@rickcedwhat/playwright-smart-table 6.7.3 → 6.7.5

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/README.md +2 -0
  2. package/dist/engine/rowFinder.d.ts +1 -1
  3. package/dist/engine/rowFinder.js +20 -29
  4. package/dist/engine/tableIteration.d.ts +25 -0
  5. package/dist/engine/tableIteration.js +210 -0
  6. package/dist/minimalConfigContext.d.ts +1 -1
  7. package/dist/minimalConfigContext.js +0 -3
  8. package/dist/plugins/glide/columns.d.ts +11 -0
  9. package/dist/plugins/glide/columns.js +51 -0
  10. package/dist/plugins/glide/headers.d.ts +9 -0
  11. package/dist/plugins/glide/headers.js +65 -0
  12. package/dist/plugins/glide/index.d.ts +31 -0
  13. package/dist/plugins/glide/index.js +104 -0
  14. package/dist/plugins/glide.d.ts +31 -0
  15. package/dist/plugins/glide.js +104 -0
  16. package/dist/plugins/index.d.ts +16 -0
  17. package/dist/plugins/index.js +16 -0
  18. package/dist/plugins/mui/index.d.ts +8 -0
  19. package/dist/plugins/mui/index.js +25 -0
  20. package/dist/plugins/mui.d.ts +8 -0
  21. package/dist/plugins/mui.js +25 -0
  22. package/dist/plugins/rdg/index.d.ts +17 -0
  23. package/dist/plugins/rdg/index.js +124 -0
  24. package/dist/plugins/rdg.d.ts +17 -0
  25. package/dist/plugins/rdg.js +124 -0
  26. package/dist/plugins.d.ts +12 -40
  27. package/dist/plugins.js +9 -6
  28. package/dist/smartRow.js +42 -32
  29. package/dist/strategies/glide.d.ts +7 -21
  30. package/dist/strategies/glide.js +22 -12
  31. package/dist/strategies/mui.d.ts +8 -0
  32. package/dist/strategies/mui.js +25 -0
  33. package/dist/strategies/pagination.js +25 -4
  34. package/dist/strategies/rdg.d.ts +6 -23
  35. package/dist/strategies/rdg.js +23 -10
  36. package/dist/strategies/validation.d.ts +2 -7
  37. package/dist/strategies/validation.js +1 -12
  38. package/dist/typeContext.d.ts +2 -2
  39. package/dist/typeContext.js +37 -17
  40. package/dist/types.d.ts +33 -23
  41. package/dist/useTable.js +72 -194
  42. package/dist/utils/paginationPath.d.ts +37 -0
  43. package/dist/utils/paginationPath.js +227 -0
  44. package/dist/utils/sentinel.d.ts +5 -0
  45. package/dist/utils/sentinel.js +8 -0
  46. package/package.json +1 -1
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TYPE_CONTEXT = void 0;
4
4
  /**
5
5
  * šŸ¤– AUTO-GENERATED FILE. DO NOT EDIT.
6
- * This file is generated by scripts/embed-types.js
6
+ * This file is generated by scripts/embed-types.mjs
7
7
  * It contains the raw text of types.ts to provide context for LLM prompts.
8
8
  */
9
9
  exports.TYPE_CONTEXT = `
@@ -240,11 +240,21 @@ export interface PaginationPrimitives {
240
240
  /** Jump to first page / scroll to top */
241
241
  goToFirst?: (context: TableContext) => Promise<boolean>;
242
242
 
243
- /** Jump to specific page index (0-indexed) */
243
+ /**
244
+ * Jump to specific page index (0-indexed).
245
+ * Can be full-range (e.g. page number input: any page works) or windowed (e.g. only visible links 6–14).
246
+ * Return false when the page is not reachable in the current UI; the library will step toward the target (goNextBulk/goNext or goPreviousBulk/goPrevious) and retry goToPage until it succeeds.
247
+ */
244
248
  goToPage?: (pageIndex: number, context: TableContext) => Promise<boolean>;
249
+
250
+ /** How many pages one goNextBulk() advances. Used by navigation path planner for optimal bringIntoView. */
251
+ nextBulkPages?: number;
252
+
253
+ /** How many pages one goPreviousBulk() goes back. Used by navigation path planner for optimal bringIntoView. */
254
+ previousBulkPages?: number;
245
255
  }
246
256
 
247
- export type PaginationStrategy = ((context: TableContext) => Promise<boolean | number>) | PaginationPrimitives;
257
+ export type PaginationStrategy = PaginationPrimitives;
248
258
 
249
259
  export type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;
250
260
 
@@ -288,7 +298,8 @@ export type { HeaderStrategy } from './strategies/headers';
288
298
  export type { ColumnResolutionStrategy } from './strategies/resolution';
289
299
 
290
300
  /**
291
- * Strategy to filter rows based on criteria.
301
+ * Strategy to filter rows based on criteria. Applied when using getRow/findRow/findRows with filters.
302
+ * The default engine handles string, RegExp, number, and function (cell) => Locator filters.
292
303
  */
293
304
  export interface FilterStrategy {
294
305
  apply(options: {
@@ -300,7 +311,8 @@ export interface FilterStrategy {
300
311
  }
301
312
 
302
313
  /**
303
- * Strategy to check if the table or rows are loading.
314
+ * Strategy to check if the table or rows are loading. Used after pagination/sort to wait for content.
315
+ * E.g. isHeaderLoading for init stability; isTableLoading after sort/pagination.
304
316
  */
305
317
  export interface LoadingStrategy {
306
318
  isTableLoading?: (context: TableContext) => Promise<boolean>;
@@ -335,13 +347,10 @@ export interface TableStrategies {
335
347
  * Useful for scrolling off-screen columns into view in horizontally virtualized tables.
336
348
  */
337
349
  beforeCellRead?: BeforeCellReadFn;
338
- /** Custom helper to check if a table is fully loaded/ready */
339
- isTableLoaded?: (args: TableContext) => Promise<boolean>;
340
- /** Custom helper to check if a row is fully loaded/ready */
341
- isRowLoaded?: (args: { row: Locator, index: number }) => Promise<boolean>;
342
- /** Custom helper to check if a cell is fully loaded/ready (e.g. for editing) */
343
- isCellLoaded?: (args: { cell: Locator, column: string, row: Locator }) => Promise<boolean>;
344
- /** Strategy for detecting loading states */
350
+ /**
351
+ * Strategy for detecting loading states. Use this for table-, row-, and header-level readiness.
352
+ * E.g. after sort/pagination, the engine uses loading.isTableLoading when present.
353
+ */
345
354
  loading?: LoadingStrategy;
346
355
  }
347
356
 
@@ -417,6 +426,11 @@ export type RowIterationOptions = {
417
426
  * (e.g. infinite scroll tables). Returns a unique key per row.
418
427
  */
419
428
  dedupe?: DedupeStrategy;
429
+ /**
430
+ * When true, use goNextBulk (if present) to advance pages during iteration.
431
+ * @default false — uses goNext for one-page-at-a-time advancement
432
+ */
433
+ useBulkPagination?: boolean;
420
434
  };
421
435
 
422
436
  export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>; rowIndex: number }> {
@@ -444,6 +458,8 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
444
458
  /**
445
459
  * Finds a row by filters on the current page only. Returns immediately (sync).
446
460
  * Throws error if table is not initialized.
461
+ * @note The returned SmartRow may have \`rowIndex\` as 0 when the match is not the first row.
462
+ * Use getRowByIndex(index) when you need a known index (e.g. for bringIntoView()).
447
463
  */
448
464
  getRow: (
449
465
  filters: Record<string, FilterValue>,
@@ -451,10 +467,9 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
451
467
  ) => SmartRow;
452
468
 
453
469
  /**
454
- * Gets a row by 1-based index on the current page.
470
+ * Gets a row by 0-based index on the current page.
455
471
  * Throws error if table is not initialized.
456
- * @param index 1-based row index
457
- * @param options Optional settings including bringIntoView
472
+ * @param index 0-based row index
458
473
  */
459
474
  getRowByIndex: (
460
475
  index: number
@@ -474,11 +489,11 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
474
489
  /**
475
490
  * ASYNC: Searches for all matching rows across pages using pagination.
476
491
  * Auto-initializes the table if not already initialized.
477
- * @param filters - The filter criteria to match
492
+ * @param filters - The filter criteria to match (omit or pass {} for all rows)
478
493
  * @param options - Search options including exact match and max pages
479
494
  */
480
495
  findRows: (
481
- filters: Record<string, FilterValue>,
496
+ filters?: Record<string, FilterValue>,
482
497
  options?: { exact?: boolean, maxPages?: number }
483
498
  ) => Promise<SmartRowArray<T>>;
484
499
 
@@ -581,6 +596,11 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
581
596
  * Outputs table HTML and TypeScript definitions to help AI assistants generate config.
582
597
  * Automatically throws an Error containing the prompt.
583
598
  */
599
+ generateConfig: () => Promise<void>;
600
+
601
+ /**
602
+ * @deprecated Use \`generateConfig()\` instead. Will be removed in v7.0.0.
603
+ */
584
604
  generateConfigPrompt: () => Promise<void>;
585
605
  }
586
606
  `;
package/dist/types.d.ts CHANGED
@@ -211,10 +211,18 @@ export interface PaginationPrimitives {
211
211
  goPreviousBulk?: (context: TableContext) => Promise<boolean | number>;
212
212
  /** Jump to first page / scroll to top */
213
213
  goToFirst?: (context: TableContext) => Promise<boolean>;
214
- /** Jump to specific page index (0-indexed) */
214
+ /**
215
+ * Jump to specific page index (0-indexed).
216
+ * Can be full-range (e.g. page number input: any page works) or windowed (e.g. only visible links 6–14).
217
+ * Return false when the page is not reachable in the current UI; the library will step toward the target (goNextBulk/goNext or goPreviousBulk/goPrevious) and retry goToPage until it succeeds.
218
+ */
215
219
  goToPage?: (pageIndex: number, context: TableContext) => Promise<boolean>;
220
+ /** How many pages one goNextBulk() advances. Used by navigation path planner for optimal bringIntoView. */
221
+ nextBulkPages?: number;
222
+ /** How many pages one goPreviousBulk() goes back. Used by navigation path planner for optimal bringIntoView. */
223
+ previousBulkPages?: number;
216
224
  }
217
- export type PaginationStrategy = ((context: TableContext) => Promise<boolean | number>) | PaginationPrimitives;
225
+ export type PaginationStrategy = PaginationPrimitives;
218
226
  export type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;
219
227
  export type FillStrategy = (options: {
220
228
  row: SmartRow;
@@ -251,7 +259,8 @@ import { NavigationPrimitives } from './strategies/columns';
251
259
  */
252
260
  export type { ColumnResolutionStrategy } from './strategies/resolution';
253
261
  /**
254
- * Strategy to filter rows based on criteria.
262
+ * Strategy to filter rows based on criteria. Applied when using getRow/findRow/findRows with filters.
263
+ * The default engine handles string, RegExp, number, and function (cell) => Locator filters.
255
264
  */
256
265
  export interface FilterStrategy {
257
266
  apply(options: {
@@ -265,7 +274,8 @@ export interface FilterStrategy {
265
274
  }): Locator;
266
275
  }
267
276
  /**
268
- * Strategy to check if the table or rows are loading.
277
+ * Strategy to check if the table or rows are loading. Used after pagination/sort to wait for content.
278
+ * E.g. isHeaderLoading for init stability; isTableLoading after sort/pagination.
269
279
  */
270
280
  export interface LoadingStrategy {
271
281
  isTableLoading?: (context: TableContext) => Promise<boolean>;
@@ -298,20 +308,10 @@ export interface TableStrategies {
298
308
  * Useful for scrolling off-screen columns into view in horizontally virtualized tables.
299
309
  */
300
310
  beforeCellRead?: BeforeCellReadFn;
301
- /** Custom helper to check if a table is fully loaded/ready */
302
- isTableLoaded?: (args: TableContext) => Promise<boolean>;
303
- /** Custom helper to check if a row is fully loaded/ready */
304
- isRowLoaded?: (args: {
305
- row: Locator;
306
- index: number;
307
- }) => Promise<boolean>;
308
- /** Custom helper to check if a cell is fully loaded/ready (e.g. for editing) */
309
- isCellLoaded?: (args: {
310
- cell: Locator;
311
- column: string;
312
- row: Locator;
313
- }) => Promise<boolean>;
314
- /** Strategy for detecting loading states */
311
+ /**
312
+ * Strategy for detecting loading states. Use this for table-, row-, and header-level readiness.
313
+ * E.g. after sort/pagination, the engine uses loading.isTableLoading when present.
314
+ */
315
315
  loading?: LoadingStrategy;
316
316
  }
317
317
  export interface TableConfig<T = any> {
@@ -387,6 +387,11 @@ export type RowIterationOptions = {
387
387
  * (e.g. infinite scroll tables). Returns a unique key per row.
388
388
  */
389
389
  dedupe?: DedupeStrategy;
390
+ /**
391
+ * When true, use goNextBulk (if present) to advance pages during iteration.
392
+ * @default false — uses goNext for one-page-at-a-time advancement
393
+ */
394
+ useBulkPagination?: boolean;
390
395
  };
391
396
  export interface TableResult<T = any> extends AsyncIterable<{
392
397
  row: SmartRow<T>;
@@ -414,15 +419,16 @@ export interface TableResult<T = any> extends AsyncIterable<{
414
419
  /**
415
420
  * Finds a row by filters on the current page only. Returns immediately (sync).
416
421
  * Throws error if table is not initialized.
422
+ * @note The returned SmartRow may have `rowIndex` as 0 when the match is not the first row.
423
+ * Use getRowByIndex(index) when you need a known index (e.g. for bringIntoView()).
417
424
  */
418
425
  getRow: (filters: Record<string, FilterValue>, options?: {
419
426
  exact?: boolean;
420
427
  }) => SmartRow;
421
428
  /**
422
- * Gets a row by 1-based index on the current page.
429
+ * Gets a row by 0-based index on the current page.
423
430
  * Throws error if table is not initialized.
424
- * @param index 1-based row index
425
- * @param options Optional settings including bringIntoView
431
+ * @param index 0-based row index
426
432
  */
427
433
  getRowByIndex: (index: number) => SmartRow;
428
434
  /**
@@ -438,10 +444,10 @@ export interface TableResult<T = any> extends AsyncIterable<{
438
444
  /**
439
445
  * ASYNC: Searches for all matching rows across pages using pagination.
440
446
  * Auto-initializes the table if not already initialized.
441
- * @param filters - The filter criteria to match
447
+ * @param filters - The filter criteria to match (omit or pass {} for all rows)
442
448
  * @param options - Search options including exact match and max pages
443
449
  */
444
- findRows: (filters: Record<string, FilterValue>, options?: {
450
+ findRows: (filters?: Record<string, FilterValue>, options?: {
445
451
  exact?: boolean;
446
452
  maxPages?: number;
447
453
  }) => Promise<SmartRowArray<T>>;
@@ -526,5 +532,9 @@ export interface TableResult<T = any> extends AsyncIterable<{
526
532
  * Outputs table HTML and TypeScript definitions to help AI assistants generate config.
527
533
  * Automatically throws an Error containing the prompt.
528
534
  */
535
+ generateConfig: () => Promise<void>;
536
+ /**
537
+ * @deprecated Use `generateConfig()` instead. Will be removed in v7.0.0.
538
+ */
529
539
  generateConfigPrompt: () => Promise<void>;
530
540
  }
package/dist/useTable.js CHANGED
@@ -24,6 +24,7 @@ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _ar
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.useTable = void 0;
26
26
  const minimalConfigContext_1 = require("./minimalConfigContext");
27
+ const validation_1 = require("./strategies/validation");
27
28
  const loading_1 = require("./strategies/loading");
28
29
  const fill_1 = require("./strategies/fill");
29
30
  const headers_1 = require("./strategies/headers");
@@ -31,6 +32,7 @@ const smartRow_1 = require("./smartRow");
31
32
  const filterEngine_1 = require("./filterEngine");
32
33
  const tableMapper_1 = require("./engine/tableMapper");
33
34
  const rowFinder_1 = require("./engine/rowFinder");
35
+ const tableIteration_1 = require("./engine/tableIteration");
34
36
  const debugUtils_1 = require("./utils/debugUtils");
35
37
  const smartRowArray_1 = require("./utils/smartRowArray");
36
38
  const elementTracker_1 = require("./utils/elementTracker");
@@ -45,7 +47,7 @@ const useTable = (rootLocator, configOptions = {}) => {
45
47
  const defaultStrategies = {
46
48
  fill: fill_1.FillStrategies.default,
47
49
  header: headers_1.HeaderStrategies.visible,
48
- pagination: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
50
+ pagination: {},
49
51
  loading: {
50
52
  isHeaderLoading: loading_1.LoadingStrategies.Headers.stable(200)
51
53
  }
@@ -94,6 +96,8 @@ const useTable = (rootLocator, configOptions = {}) => {
94
96
  };
95
97
  const tableState = { currentPageIndex: 0 };
96
98
  const rowFinder = new rowFinder_1.RowFinder(rootLocator, config, resolve, filterEngine, tableMapper, _makeSmart, tableState);
99
+ /** Builds a full TableContext/StrategyContext with getHeaderCell, getHeaders, scrollToColumn. Set after result is created. */
100
+ let createStrategyContext = () => ({ root: rootLocator, config, page: rootLocator.page(), resolve });
97
101
  const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
98
102
  return loc.evaluate((el) => {
99
103
  const clone = el.cloneNode(true);
@@ -125,20 +129,26 @@ const useTable = (rootLocator, configOptions = {}) => {
125
129
  const _autoInit = () => __awaiter(void 0, void 0, void 0, function* () {
126
130
  yield tableMapper.getMap();
127
131
  });
128
- const _advancePage = () => __awaiter(void 0, void 0, void 0, function* () {
129
- var _a;
130
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell, getHeaders: result.getHeaders, scrollToColumn: result.scrollToColumn };
131
- let advanced;
132
- if (typeof config.strategies.pagination === 'function') {
133
- advanced = !!(yield config.strategies.pagination(context));
132
+ // Default: goNext (one page). Pass useBulk true to prefer goNextBulk. "How far" uses numeric return when strategy provides it.
133
+ const _advancePage = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (useBulk = false) {
134
+ const context = createStrategyContext();
135
+ const pagination = config.strategies.pagination;
136
+ let rawResult;
137
+ if (useBulk && (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk)) {
138
+ rawResult = yield pagination.goNextBulk(context);
134
139
  }
135
- else {
136
- advanced = !!(((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goNext) && (yield config.strategies.pagination.goNext(context)));
140
+ else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNext) {
141
+ rawResult = yield pagination.goNext(context);
137
142
  }
138
- if (advanced) {
139
- tableState.currentPageIndex++;
143
+ else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk) {
144
+ rawResult = yield pagination.goNextBulk(context);
140
145
  }
141
- return advanced;
146
+ const didAdvance = rawResult !== undefined && (0, validation_1.validatePaginationResult)(rawResult, 'Pagination Strategy');
147
+ const pagesJumped = typeof rawResult === 'number' ? rawResult : (didAdvance ? 1 : 0);
148
+ if (pagesJumped > 0) {
149
+ tableState.currentPageIndex += pagesJumped;
150
+ }
151
+ return didAdvance;
142
152
  });
143
153
  const result = {
144
154
  get currentPageIndex() { return tableState.currentPageIndex; },
@@ -176,11 +186,10 @@ const useTable = (rootLocator, configOptions = {}) => {
176
186
  reset: () => __awaiter(void 0, void 0, void 0, function* () {
177
187
  var _a;
178
188
  log("Resetting table...");
179
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
180
- yield config.onReset(context);
181
- if (typeof config.strategies.pagination !== 'function' && ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst)) {
189
+ yield config.onReset(createStrategyContext());
190
+ if ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst) {
182
191
  log("Auto-navigating to first page...");
183
- yield config.strategies.pagination.goToFirst(context);
192
+ yield config.strategies.pagination.goToFirst(createStrategyContext());
184
193
  }
185
194
  else if (hasPaginationInConfig) {
186
195
  log("No goToFirst strategy configured. Table may not be on page 1.");
@@ -213,11 +222,10 @@ const useTable = (rootLocator, configOptions = {}) => {
213
222
  return _makeSmart(rowLocator, map, index);
214
223
  },
215
224
  findRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
216
- // @ts-ignore
217
225
  return rowFinder.findRow(filters, options);
218
226
  }),
219
227
  findRows: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
220
- return rowFinder.findRows(filters, options);
228
+ return rowFinder.findRows(filters !== null && filters !== void 0 ? filters : {}, options);
221
229
  }),
222
230
  isInitialized: () => {
223
231
  return tableMapper.isInitialized();
@@ -229,7 +237,7 @@ const useTable = (rootLocator, configOptions = {}) => {
229
237
  if (!config.strategies.sorting)
230
238
  throw new Error('No sorting strategy has been configured.');
231
239
  log(`Applying sort for column "${columnName}" (${direction})`);
232
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
240
+ const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
233
241
  const maxRetries = 3;
234
242
  for (let i = 0; i < maxRetries; i++) {
235
243
  const currentState = yield config.strategies.sorting.getSortState({ columnName, context });
@@ -257,7 +265,7 @@ const useTable = (rootLocator, configOptions = {}) => {
257
265
  yield _autoInit();
258
266
  if (!config.strategies.sorting)
259
267
  throw new Error('No sorting strategy has been configured.');
260
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
268
+ const context = Object.assign(Object.assign({}, createStrategyContext()), { getHeaderCell: result.getHeaderCell });
261
269
  return config.strategies.sorting.getSortState({ columnName, context });
262
270
  })
263
271
  },
@@ -268,6 +276,7 @@ const useTable = (rootLocator, configOptions = {}) => {
268
276
  const map = tableMapper.getMapSync();
269
277
  const effectiveMaxPages = config.maxPages;
270
278
  const tracker = new elementTracker_1.ElementTracker('iterator');
279
+ const useBulk = false; // iterator has no options; default goNext
271
280
  try {
272
281
  let rowIndex = 0;
273
282
  let pagesScanned = 1;
@@ -281,7 +290,7 @@ const useTable = (rootLocator, configOptions = {}) => {
281
290
  }
282
291
  if (pagesScanned >= effectiveMaxPages)
283
292
  break;
284
- if (!(yield __await(_advancePage())))
293
+ if (!(yield __await(_advancePage(useBulk))))
285
294
  break;
286
295
  pagesScanned++;
287
296
  }
@@ -291,194 +300,63 @@ const useTable = (rootLocator, configOptions = {}) => {
291
300
  }
292
301
  });
293
302
  },
294
- // ─── Private row-iteration engine ────────────────────────────────────────
303
+ // ─── Row iteration (delegated to engine/tableIteration) ──────────────────
295
304
  forEach: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
296
- var _a, _b, _c;
297
305
  yield _autoInit();
298
- const map = tableMapper.getMapSync();
299
- const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
300
- const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
301
- const dedupeKeys = dedupeStrategy ? new Set() : null;
302
- const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
303
- const tracker = new elementTracker_1.ElementTracker('forEach');
304
- try {
305
- let rowIndex = 0;
306
- let stopped = false;
307
- let pagesScanned = 1;
308
- const stop = () => { stopped = true; };
309
- while (!stopped) {
310
- const rowLocators = resolve(config.rowSelector, rootLocator);
311
- const newIndices = yield tracker.getUnseenIndices(rowLocators);
312
- const pageRows = yield rowLocators.all();
313
- const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i));
314
- if (parallel) {
315
- yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
316
- if (stopped)
317
- return;
318
- if (dedupeKeys) {
319
- const key = yield dedupeStrategy(row);
320
- if (dedupeKeys.has(key))
321
- return;
322
- dedupeKeys.add(key);
323
- }
324
- yield callback({ row, rowIndex: row.rowIndex, stop });
325
- })));
326
- }
327
- else {
328
- for (const row of smartRows) {
329
- if (stopped)
330
- break;
331
- if (dedupeKeys) {
332
- const key = yield dedupeStrategy(row);
333
- if (dedupeKeys.has(key))
334
- continue;
335
- dedupeKeys.add(key);
336
- }
337
- yield callback({ row, rowIndex: row.rowIndex, stop });
338
- }
339
- }
340
- rowIndex += smartRows.length;
341
- if (stopped || pagesScanned >= effectiveMaxPages)
342
- break;
343
- if (!(yield _advancePage()))
344
- break;
345
- pagesScanned++;
346
- }
347
- }
348
- finally {
349
- yield tracker.cleanup(rootLocator.page());
350
- }
306
+ yield (0, tableIteration_1.runForEach)({
307
+ getRowLocators: () => resolve(config.rowSelector, rootLocator),
308
+ getMap: () => tableMapper.getMapSync(),
309
+ advancePage: _advancePage,
310
+ makeSmartRow: (loc, map, idx, pageIdx) => _makeSmart(loc, map, idx, pageIdx),
311
+ createSmartRowArray: smartRowArray_1.createSmartRowArray,
312
+ config,
313
+ getPage: () => rootLocator.page(),
314
+ }, callback, options);
351
315
  }),
352
316
  map: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
353
- var _a, _b, _c;
354
317
  yield _autoInit();
355
- const map = tableMapper.getMapSync();
356
- const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
357
- const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
358
- const dedupeKeys = dedupeStrategy ? new Set() : null;
359
- const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : true;
360
- const tracker = new elementTracker_1.ElementTracker('map');
361
- const results = [];
362
- try {
363
- let rowIndex = 0;
364
- let stopped = false;
365
- let pagesScanned = 1;
366
- const stop = () => { stopped = true; };
367
- while (!stopped) {
368
- const rowLocators = resolve(config.rowSelector, rootLocator);
369
- const newIndices = yield tracker.getUnseenIndices(rowLocators);
370
- const pageRows = yield rowLocators.all();
371
- const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i));
372
- if (parallel) {
373
- const SKIP = Symbol('skip');
374
- const pageResults = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
375
- if (dedupeKeys) {
376
- const key = yield dedupeStrategy(row);
377
- if (dedupeKeys.has(key))
378
- return SKIP;
379
- dedupeKeys.add(key);
380
- }
381
- return callback({ row, rowIndex: row.rowIndex, stop });
382
- })));
383
- for (const r of pageResults) {
384
- if (r !== SKIP)
385
- results.push(r);
386
- }
387
- }
388
- else {
389
- for (const row of smartRows) {
390
- if (stopped)
391
- break;
392
- if (dedupeKeys) {
393
- const key = yield dedupeStrategy(row);
394
- if (dedupeKeys.has(key))
395
- continue;
396
- dedupeKeys.add(key);
397
- }
398
- results.push(yield callback({ row, rowIndex: row.rowIndex, stop }));
399
- }
400
- }
401
- rowIndex += smartRows.length;
402
- if (stopped || pagesScanned >= effectiveMaxPages)
403
- break;
404
- if (!(yield _advancePage()))
405
- break;
406
- pagesScanned++;
407
- }
408
- }
409
- finally {
410
- yield tracker.cleanup(rootLocator.page());
411
- }
412
- return results;
318
+ return (0, tableIteration_1.runMap)({
319
+ getRowLocators: () => resolve(config.rowSelector, rootLocator),
320
+ getMap: () => tableMapper.getMapSync(),
321
+ advancePage: _advancePage,
322
+ makeSmartRow: (loc, map, idx, pageIdx) => _makeSmart(loc, map, idx, pageIdx),
323
+ createSmartRowArray: smartRowArray_1.createSmartRowArray,
324
+ config,
325
+ getPage: () => rootLocator.page(),
326
+ }, callback, options);
413
327
  }),
414
328
  filter: (predicate_1, ...args_1) => __awaiter(void 0, [predicate_1, ...args_1], void 0, function* (predicate, options = {}) {
415
- var _a, _b, _c;
416
329
  yield _autoInit();
417
- const map = tableMapper.getMapSync();
418
- const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
419
- const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
420
- const dedupeKeys = dedupeStrategy ? new Set() : null;
421
- const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
422
- const tracker = new elementTracker_1.ElementTracker('filter');
423
- const matched = [];
424
- try {
425
- let rowIndex = 0;
426
- let stopped = false;
427
- let pagesScanned = 1;
428
- const stop = () => { stopped = true; };
429
- while (!stopped) {
430
- const rowLocators = resolve(config.rowSelector, rootLocator);
431
- const newIndices = yield tracker.getUnseenIndices(rowLocators);
432
- const pageRows = yield rowLocators.all();
433
- const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i, pagesScanned - 1));
434
- if (parallel) {
435
- const flags = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
436
- if (dedupeKeys) {
437
- const key = yield dedupeStrategy(row);
438
- if (dedupeKeys.has(key))
439
- return false;
440
- dedupeKeys.add(key);
441
- }
442
- return predicate({ row, rowIndex: row.rowIndex, stop });
443
- })));
444
- smartRows.forEach((row, i) => { if (flags[i])
445
- matched.push(row); });
446
- }
447
- else {
448
- for (const row of smartRows) {
449
- if (stopped)
450
- break;
451
- if (dedupeKeys) {
452
- const key = yield dedupeStrategy(row);
453
- if (dedupeKeys.has(key))
454
- continue;
455
- dedupeKeys.add(key);
456
- }
457
- if (yield predicate({ row, rowIndex: row.rowIndex, stop })) {
458
- matched.push(row);
459
- }
460
- }
461
- }
462
- rowIndex += smartRows.length;
463
- if (stopped || pagesScanned >= effectiveMaxPages)
464
- break;
465
- if (!(yield _advancePage()))
466
- break;
467
- pagesScanned++;
468
- }
469
- }
470
- finally {
471
- yield tracker.cleanup(rootLocator.page());
472
- }
473
- return (0, smartRowArray_1.createSmartRowArray)(matched);
330
+ return (0, tableIteration_1.runFilter)({
331
+ getRowLocators: () => resolve(config.rowSelector, rootLocator),
332
+ getMap: () => tableMapper.getMapSync(),
333
+ advancePage: _advancePage,
334
+ makeSmartRow: (loc, map, idx, pageIdx) => _makeSmart(loc, map, idx, pageIdx),
335
+ createSmartRowArray: smartRowArray_1.createSmartRowArray,
336
+ config,
337
+ getPage: () => rootLocator.page(),
338
+ }, predicate, options);
474
339
  }),
475
- generateConfigPrompt: () => __awaiter(void 0, void 0, void 0, function* () {
340
+ generateConfig: () => __awaiter(void 0, void 0, void 0, function* () {
476
341
  const html = yield _getCleanHtml(rootLocator);
477
342
  const separator = "=".repeat(50);
478
343
  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`;
479
344
  yield _handlePrompt('Smart Table Config', content);
480
345
  }),
346
+ generateConfigPrompt: () => __awaiter(void 0, void 0, void 0, function* () {
347
+ console.warn('āš ļø [playwright-smart-table] generateConfigPrompt() is deprecated and will be removed in v7.0.0. Please use generateConfig() instead.');
348
+ return result.generateConfig();
349
+ }),
481
350
  };
351
+ createStrategyContext = () => ({
352
+ root: rootLocator,
353
+ config,
354
+ page: rootLocator.page(),
355
+ resolve,
356
+ getHeaderCell: result.getHeaderCell,
357
+ getHeaders: result.getHeaders,
358
+ scrollToColumn: result.scrollToColumn,
359
+ });
482
360
  finalTable = result;
483
361
  return result;
484
362
  };
@@ -0,0 +1,37 @@
1
+ import type { PaginationPrimitives, TableContext } from '../types';
2
+ /** A single step in a navigation plan (e.g. "goNextBulk 3 times"). */
3
+ export type NavigationStep = {
4
+ type: 'goToPage';
5
+ pageIndex: number;
6
+ } | {
7
+ type: 'goNextBulk';
8
+ count: number;
9
+ } | {
10
+ type: 'goNext';
11
+ count: number;
12
+ } | {
13
+ type: 'goPreviousBulk';
14
+ count: number;
15
+ } | {
16
+ type: 'goPrevious';
17
+ count: number;
18
+ };
19
+ /**
20
+ * Plans an optimal path from currentPageIndex to targetPageIndex using available primitives.
21
+ * Prefers goToPage when present; otherwise uses bulk steps (goNextBulk / goPreviousBulk) then
22
+ * single steps (goNext / goPrevious). May choose to overshoot with bulk then step back when
23
+ * that reduces total primitive calls (e.g. page 3 → 12 with bulk 10: goNextBulk once, goPrevious once).
24
+ */
25
+ export declare function planNavigationPath(currentPageIndex: number, targetPageIndex: number, primitives: PaginationPrimitives): NavigationStep[];
26
+ /**
27
+ * Navigate to targetPageIndex when goToPage is available but may be "windowed"
28
+ * (e.g. only works for visible page links 6–14). Tries goToPage(target); on false,
29
+ * steps once toward target (goNextBulk/goNext or goPreviousBulk/goPrevious), then retries.
30
+ * Example: from 3 to 38 with windowed goToPage → goToPage(38) false, goNextBulk(), goToPage(38) false, … goToPage(38) true.
31
+ */
32
+ export declare function executeNavigationWithGoToPageRetry(targetPageIndex: number, primitives: PaginationPrimitives, context: TableContext, getCurrentPage: () => number, setCurrentPage: (n: number) => void): Promise<void>;
33
+ /**
34
+ * Executes a navigation path by calling the corresponding primitives.
35
+ * Updates currentPageIndex via the provided setter as steps run.
36
+ */
37
+ export declare function executeNavigationPath(path: NavigationStep[], primitives: PaginationPrimitives, context: TableContext, getCurrentPage: () => number, setCurrentPage: (n: number) => void): Promise<void>;