@rickcedwhat/playwright-smart-table 6.7.1 → 6.7.4

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.
@@ -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 = `
@@ -231,20 +231,30 @@ export interface PaginationPrimitives {
231
231
  /** Classic "Previous Page" or "Scroll Up" */
232
232
  goPrevious?: (context: TableContext) => Promise<boolean>;
233
233
 
234
- /** Bulk skip forward multiple pages at once */
235
- goNextBulk?: (context: TableContext) => Promise<boolean>;
234
+ /** Bulk skip forward multiple pages at once. Returns number of pages skipped. */
235
+ goNextBulk?: (context: TableContext) => Promise<boolean | number>;
236
236
 
237
- /** Bulk skip backward multiple pages at once */
238
- goPreviousBulk?: (context: TableContext) => Promise<boolean>;
237
+ /** Bulk skip backward multiple pages at once. Returns number of pages skipped. */
238
+ goPreviousBulk?: (context: TableContext) => Promise<boolean | number>;
239
239
 
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>) | PaginationPrimitives;
257
+ export type PaginationStrategy = PaginationPrimitives;
248
258
 
249
259
  export type DedupeStrategy = (row: SmartRow) => string | number | Promise<string | number>;
250
260
 
@@ -417,6 +427,11 @@ export type RowIterationOptions = {
417
427
  * (e.g. infinite scroll tables). Returns a unique key per row.
418
428
  */
419
429
  dedupe?: DedupeStrategy;
430
+ /**
431
+ * When true, use goNextBulk (if present) to advance pages during iteration.
432
+ * @default false — uses goNext for one-page-at-a-time advancement
433
+ */
434
+ useBulkPagination?: boolean;
420
435
  };
421
436
 
422
437
  export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>; rowIndex: number }> {
@@ -451,10 +466,9 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
451
466
  ) => SmartRow;
452
467
 
453
468
  /**
454
- * Gets a row by 1-based index on the current page.
469
+ * Gets a row by 0-based index on the current page.
455
470
  * Throws error if table is not initialized.
456
- * @param index 1-based row index
457
- * @param options Optional settings including bringIntoView
471
+ * @param index 0-based row index
458
472
  */
459
473
  getRowByIndex: (
460
474
  index: number
@@ -581,6 +595,11 @@ export interface TableResult<T = any> extends AsyncIterable<{ row: SmartRow<T>;
581
595
  * Outputs table HTML and TypeScript definitions to help AI assistants generate config.
582
596
  * Automatically throws an Error containing the prompt.
583
597
  */
598
+ generateConfig: () => Promise<void>;
599
+
600
+ /**
601
+ * @deprecated Use \`generateConfig()\` instead. Will be removed in v7.0.0.
602
+ */
584
603
  generateConfigPrompt: () => Promise<void>;
585
604
  }
586
605
  `;
package/dist/types.d.ts CHANGED
@@ -205,16 +205,24 @@ export interface PaginationPrimitives {
205
205
  goNext?: (context: TableContext) => Promise<boolean>;
206
206
  /** Classic "Previous Page" or "Scroll Up" */
207
207
  goPrevious?: (context: TableContext) => Promise<boolean>;
208
- /** Bulk skip forward multiple pages at once */
209
- goNextBulk?: (context: TableContext) => Promise<boolean>;
210
- /** Bulk skip backward multiple pages at once */
211
- goPreviousBulk?: (context: TableContext) => Promise<boolean>;
208
+ /** Bulk skip forward multiple pages at once. Returns number of pages skipped. */
209
+ goNextBulk?: (context: TableContext) => Promise<boolean | number>;
210
+ /** Bulk skip backward multiple pages at once. Returns number of pages skipped. */
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>) | 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;
@@ -387,6 +395,11 @@ export type RowIterationOptions = {
387
395
  * (e.g. infinite scroll tables). Returns a unique key per row.
388
396
  */
389
397
  dedupe?: DedupeStrategy;
398
+ /**
399
+ * When true, use goNextBulk (if present) to advance pages during iteration.
400
+ * @default false — uses goNext for one-page-at-a-time advancement
401
+ */
402
+ useBulkPagination?: boolean;
390
403
  };
391
404
  export interface TableResult<T = any> extends AsyncIterable<{
392
405
  row: SmartRow<T>;
@@ -419,10 +432,9 @@ export interface TableResult<T = any> extends AsyncIterable<{
419
432
  exact?: boolean;
420
433
  }) => SmartRow;
421
434
  /**
422
- * Gets a row by 1-based index on the current page.
435
+ * Gets a row by 0-based index on the current page.
423
436
  * Throws error if table is not initialized.
424
- * @param index 1-based row index
425
- * @param options Optional settings including bringIntoView
437
+ * @param index 0-based row index
426
438
  */
427
439
  getRowByIndex: (index: number) => SmartRow;
428
440
  /**
@@ -526,5 +538,9 @@ export interface TableResult<T = any> extends AsyncIterable<{
526
538
  * Outputs table HTML and TypeScript definitions to help AI assistants generate config.
527
539
  * Automatically throws an Error containing the prompt.
528
540
  */
541
+ generateConfig: () => Promise<void>;
542
+ /**
543
+ * @deprecated Use `generateConfig()` instead. Will be removed in v7.0.0.
544
+ */
529
545
  generateConfigPrompt: () => Promise<void>;
530
546
  }
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");
@@ -33,6 +34,7 @@ const tableMapper_1 = require("./engine/tableMapper");
33
34
  const rowFinder_1 = require("./engine/rowFinder");
34
35
  const debugUtils_1 = require("./utils/debugUtils");
35
36
  const smartRowArray_1 = require("./utils/smartRowArray");
37
+ const elementTracker_1 = require("./utils/elementTracker");
36
38
  /**
37
39
  * Main hook to interact with a table.
38
40
  */
@@ -44,7 +46,7 @@ const useTable = (rootLocator, configOptions = {}) => {
44
46
  const defaultStrategies = {
45
47
  fill: fill_1.FillStrategies.default,
46
48
  header: headers_1.HeaderStrategies.visible,
47
- pagination: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
49
+ pagination: {},
48
50
  loading: {
49
51
  isHeaderLoading: loading_1.LoadingStrategies.Headers.stable(200)
50
52
  }
@@ -124,20 +126,26 @@ const useTable = (rootLocator, configOptions = {}) => {
124
126
  const _autoInit = () => __awaiter(void 0, void 0, void 0, function* () {
125
127
  yield tableMapper.getMap();
126
128
  });
127
- const _advancePage = () => __awaiter(void 0, void 0, void 0, function* () {
128
- var _a;
129
+ // Default: goNext (one page). Pass useBulk true to prefer goNextBulk. "How far" uses numeric return when strategy provides it.
130
+ const _advancePage = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (useBulk = false) {
129
131
  const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell, getHeaders: result.getHeaders, scrollToColumn: result.scrollToColumn };
130
- let advanced;
131
- if (typeof config.strategies.pagination === 'function') {
132
- advanced = !!(yield config.strategies.pagination(context));
132
+ const pagination = config.strategies.pagination;
133
+ let rawResult;
134
+ if (useBulk && (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk)) {
135
+ rawResult = yield pagination.goNextBulk(context);
133
136
  }
134
- else {
135
- advanced = !!(((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goNext) && (yield config.strategies.pagination.goNext(context)));
137
+ else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNext) {
138
+ rawResult = yield pagination.goNext(context);
136
139
  }
137
- if (advanced) {
138
- tableState.currentPageIndex++;
140
+ else if (pagination === null || pagination === void 0 ? void 0 : pagination.goNextBulk) {
141
+ rawResult = yield pagination.goNextBulk(context);
139
142
  }
140
- return advanced;
143
+ const didAdvance = rawResult !== undefined && (0, validation_1.validatePaginationResult)(rawResult, 'Pagination Strategy');
144
+ const pagesJumped = typeof rawResult === 'number' ? rawResult : (didAdvance ? 1 : 0);
145
+ if (pagesJumped > 0) {
146
+ tableState.currentPageIndex += pagesJumped;
147
+ }
148
+ return didAdvance;
141
149
  });
142
150
  const result = {
143
151
  get currentPageIndex() { return tableState.currentPageIndex; },
@@ -177,7 +185,7 @@ const useTable = (rootLocator, configOptions = {}) => {
177
185
  log("Resetting table...");
178
186
  const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
179
187
  yield config.onReset(context);
180
- if (typeof config.strategies.pagination !== 'function' && ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst)) {
188
+ if ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst) {
181
189
  log("Auto-navigating to first page...");
182
190
  yield config.strategies.pagination.goToFirst(context);
183
191
  }
@@ -266,185 +274,225 @@ const useTable = (rootLocator, configOptions = {}) => {
266
274
  yield __await(_autoInit());
267
275
  const map = tableMapper.getMapSync();
268
276
  const effectiveMaxPages = config.maxPages;
269
- let rowIndex = 0;
270
- let pagesScanned = 1;
271
- while (true) {
272
- const pageRows = yield __await(resolve(config.rowSelector, rootLocator).all());
273
- for (const rowLocator of pageRows) {
274
- yield yield __await({ row: _makeSmart(rowLocator, map, rowIndex), rowIndex });
275
- rowIndex++;
277
+ const tracker = new elementTracker_1.ElementTracker('iterator');
278
+ const useBulk = false; // iterator has no options; default goNext
279
+ try {
280
+ let rowIndex = 0;
281
+ let pagesScanned = 1;
282
+ while (true) {
283
+ const rowLocators = resolve(config.rowSelector, rootLocator);
284
+ const newIndices = yield __await(tracker.getUnseenIndices(rowLocators));
285
+ const pageRows = yield __await(rowLocators.all());
286
+ for (const idx of newIndices) {
287
+ yield yield __await({ row: _makeSmart(pageRows[idx], map, rowIndex), rowIndex });
288
+ rowIndex++;
289
+ }
290
+ if (pagesScanned >= effectiveMaxPages)
291
+ break;
292
+ if (!(yield __await(_advancePage(useBulk))))
293
+ break;
294
+ pagesScanned++;
276
295
  }
277
- if (pagesScanned >= effectiveMaxPages)
278
- break;
279
- if (!(yield __await(_advancePage())))
280
- break;
281
- pagesScanned++;
296
+ }
297
+ finally {
298
+ yield __await(tracker.cleanup(rootLocator.page()));
282
299
  }
283
300
  });
284
301
  },
285
302
  // ─── Private row-iteration engine ────────────────────────────────────────
286
303
  forEach: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
287
- var _a, _b, _c;
304
+ var _a, _b, _c, _d;
288
305
  yield _autoInit();
289
306
  const map = tableMapper.getMapSync();
290
307
  const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
291
308
  const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
292
309
  const dedupeKeys = dedupeStrategy ? new Set() : null;
293
310
  const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
294
- let rowIndex = 0;
295
- let stopped = false;
296
- let pagesScanned = 1;
297
- const stop = () => { stopped = true; };
298
- while (!stopped) {
299
- const pageRows = yield resolve(config.rowSelector, rootLocator).all();
300
- const smartRows = pageRows.map((r, i) => _makeSmart(r, map, rowIndex + i));
301
- if (parallel) {
302
- yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
303
- if (stopped)
304
- return;
305
- if (dedupeKeys) {
306
- const key = yield dedupeStrategy(row);
307
- if (dedupeKeys.has(key))
311
+ const useBulk = (_d = options.useBulkPagination) !== null && _d !== void 0 ? _d : false;
312
+ const tracker = new elementTracker_1.ElementTracker('forEach');
313
+ try {
314
+ let rowIndex = 0;
315
+ let stopped = false;
316
+ let pagesScanned = 1;
317
+ const stop = () => { stopped = true; };
318
+ while (!stopped) {
319
+ const rowLocators = resolve(config.rowSelector, rootLocator);
320
+ const newIndices = yield tracker.getUnseenIndices(rowLocators);
321
+ const pageRows = yield rowLocators.all();
322
+ const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i));
323
+ if (parallel) {
324
+ yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
325
+ if (stopped)
308
326
  return;
309
- dedupeKeys.add(key);
310
- }
311
- yield callback({ row, rowIndex: row.rowIndex, stop });
312
- })));
313
- }
314
- else {
315
- for (const row of smartRows) {
316
- if (stopped)
317
- break;
318
- if (dedupeKeys) {
319
- const key = yield dedupeStrategy(row);
320
- if (dedupeKeys.has(key))
321
- continue;
322
- dedupeKeys.add(key);
327
+ if (dedupeKeys) {
328
+ const key = yield dedupeStrategy(row);
329
+ if (dedupeKeys.has(key))
330
+ return;
331
+ dedupeKeys.add(key);
332
+ }
333
+ yield callback({ row, rowIndex: row.rowIndex, stop });
334
+ })));
335
+ }
336
+ else {
337
+ for (const row of smartRows) {
338
+ if (stopped)
339
+ break;
340
+ if (dedupeKeys) {
341
+ const key = yield dedupeStrategy(row);
342
+ if (dedupeKeys.has(key))
343
+ continue;
344
+ dedupeKeys.add(key);
345
+ }
346
+ yield callback({ row, rowIndex: row.rowIndex, stop });
323
347
  }
324
- yield callback({ row, rowIndex: row.rowIndex, stop });
325
348
  }
349
+ rowIndex += smartRows.length;
350
+ if (stopped || pagesScanned >= effectiveMaxPages)
351
+ break;
352
+ if (!(yield _advancePage(useBulk)))
353
+ break;
354
+ pagesScanned++;
326
355
  }
327
- rowIndex += smartRows.length;
328
- if (stopped || pagesScanned >= effectiveMaxPages)
329
- break;
330
- if (!(yield _advancePage()))
331
- break;
332
- pagesScanned++;
356
+ }
357
+ finally {
358
+ yield tracker.cleanup(rootLocator.page());
333
359
  }
334
360
  }),
335
361
  map: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
336
- var _a, _b, _c;
362
+ var _a, _b, _c, _d;
337
363
  yield _autoInit();
338
364
  const map = tableMapper.getMapSync();
339
365
  const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
340
366
  const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
341
367
  const dedupeKeys = dedupeStrategy ? new Set() : null;
342
368
  const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : true;
369
+ const useBulk = (_d = options.useBulkPagination) !== null && _d !== void 0 ? _d : false;
370
+ const tracker = new elementTracker_1.ElementTracker('map');
343
371
  const results = [];
344
- let rowIndex = 0;
345
- let stopped = false;
346
- let pagesScanned = 1;
347
- const stop = () => { stopped = true; };
348
- while (!stopped) {
349
- const pageRows = yield resolve(config.rowSelector, rootLocator).all();
350
- const smartRows = pageRows.map((r, i) => _makeSmart(r, map, rowIndex + i));
351
- if (parallel) {
352
- const SKIP = Symbol('skip');
353
- const pageResults = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
354
- if (dedupeKeys) {
355
- const key = yield dedupeStrategy(row);
356
- if (dedupeKeys.has(key))
357
- return SKIP;
358
- dedupeKeys.add(key);
372
+ try {
373
+ let rowIndex = 0;
374
+ let stopped = false;
375
+ let pagesScanned = 1;
376
+ const stop = () => { stopped = true; };
377
+ while (!stopped) {
378
+ const rowLocators = resolve(config.rowSelector, rootLocator);
379
+ const newIndices = yield tracker.getUnseenIndices(rowLocators);
380
+ const pageRows = yield rowLocators.all();
381
+ const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i));
382
+ if (parallel) {
383
+ const SKIP = Symbol('skip');
384
+ const pageResults = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
385
+ if (dedupeKeys) {
386
+ const key = yield dedupeStrategy(row);
387
+ if (dedupeKeys.has(key))
388
+ return SKIP;
389
+ dedupeKeys.add(key);
390
+ }
391
+ return callback({ row, rowIndex: row.rowIndex, stop });
392
+ })));
393
+ for (const r of pageResults) {
394
+ if (r !== SKIP)
395
+ results.push(r);
359
396
  }
360
- return callback({ row, rowIndex: row.rowIndex, stop });
361
- })));
362
- for (const r of pageResults) {
363
- if (r !== SKIP)
364
- results.push(r);
365
397
  }
366
- }
367
- else {
368
- for (const row of smartRows) {
369
- if (stopped)
370
- break;
371
- if (dedupeKeys) {
372
- const key = yield dedupeStrategy(row);
373
- if (dedupeKeys.has(key))
374
- continue;
375
- dedupeKeys.add(key);
398
+ else {
399
+ for (const row of smartRows) {
400
+ if (stopped)
401
+ break;
402
+ if (dedupeKeys) {
403
+ const key = yield dedupeStrategy(row);
404
+ if (dedupeKeys.has(key))
405
+ continue;
406
+ dedupeKeys.add(key);
407
+ }
408
+ results.push(yield callback({ row, rowIndex: row.rowIndex, stop }));
376
409
  }
377
- results.push(yield callback({ row, rowIndex: row.rowIndex, stop }));
378
410
  }
411
+ rowIndex += smartRows.length;
412
+ if (stopped || pagesScanned >= effectiveMaxPages)
413
+ break;
414
+ if (!(yield _advancePage(useBulk)))
415
+ break;
416
+ pagesScanned++;
379
417
  }
380
- rowIndex += smartRows.length;
381
- if (stopped || pagesScanned >= effectiveMaxPages)
382
- break;
383
- if (!(yield _advancePage()))
384
- break;
385
- pagesScanned++;
418
+ }
419
+ finally {
420
+ yield tracker.cleanup(rootLocator.page());
386
421
  }
387
422
  return results;
388
423
  }),
389
424
  filter: (predicate_1, ...args_1) => __awaiter(void 0, [predicate_1, ...args_1], void 0, function* (predicate, options = {}) {
390
- var _a, _b, _c;
425
+ var _a, _b, _c, _d;
391
426
  yield _autoInit();
392
427
  const map = tableMapper.getMapSync();
393
428
  const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
394
429
  const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
395
430
  const dedupeKeys = dedupeStrategy ? new Set() : null;
396
431
  const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
432
+ const useBulk = (_d = options.useBulkPagination) !== null && _d !== void 0 ? _d : false;
433
+ const tracker = new elementTracker_1.ElementTracker('filter');
397
434
  const matched = [];
398
- let rowIndex = 0;
399
- let stopped = false;
400
- let pagesScanned = 1;
401
- const stop = () => { stopped = true; };
402
- while (!stopped) {
403
- const pageRows = yield resolve(config.rowSelector, rootLocator).all();
404
- const smartRows = pageRows.map((r, i) => _makeSmart(r, map, rowIndex + i, pagesScanned - 1));
405
- if (parallel) {
406
- const flags = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
407
- if (dedupeKeys) {
408
- const key = yield dedupeStrategy(row);
409
- if (dedupeKeys.has(key))
410
- return false;
411
- dedupeKeys.add(key);
412
- }
413
- return predicate({ row, rowIndex: row.rowIndex, stop });
414
- })));
415
- smartRows.forEach((row, i) => { if (flags[i])
416
- matched.push(row); });
417
- }
418
- else {
419
- for (const row of smartRows) {
420
- if (stopped)
421
- break;
422
- if (dedupeKeys) {
423
- const key = yield dedupeStrategy(row);
424
- if (dedupeKeys.has(key))
425
- continue;
426
- dedupeKeys.add(key);
427
- }
428
- if (yield predicate({ row, rowIndex: row.rowIndex, stop })) {
429
- matched.push(row);
435
+ try {
436
+ let rowIndex = 0;
437
+ let stopped = false;
438
+ let pagesScanned = 1;
439
+ const stop = () => { stopped = true; };
440
+ while (!stopped) {
441
+ const rowLocators = resolve(config.rowSelector, rootLocator);
442
+ const newIndices = yield tracker.getUnseenIndices(rowLocators);
443
+ const pageRows = yield rowLocators.all();
444
+ const smartRows = newIndices.map((idx, i) => _makeSmart(pageRows[idx], map, rowIndex + i, pagesScanned - 1));
445
+ if (parallel) {
446
+ const flags = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
447
+ if (dedupeKeys) {
448
+ const key = yield dedupeStrategy(row);
449
+ if (dedupeKeys.has(key))
450
+ return false;
451
+ dedupeKeys.add(key);
452
+ }
453
+ return predicate({ row, rowIndex: row.rowIndex, stop });
454
+ })));
455
+ smartRows.forEach((row, i) => { if (flags[i])
456
+ matched.push(row); });
457
+ }
458
+ else {
459
+ for (const row of smartRows) {
460
+ if (stopped)
461
+ break;
462
+ if (dedupeKeys) {
463
+ const key = yield dedupeStrategy(row);
464
+ if (dedupeKeys.has(key))
465
+ continue;
466
+ dedupeKeys.add(key);
467
+ }
468
+ if (yield predicate({ row, rowIndex: row.rowIndex, stop })) {
469
+ matched.push(row);
470
+ }
430
471
  }
431
472
  }
473
+ rowIndex += smartRows.length;
474
+ if (stopped || pagesScanned >= effectiveMaxPages)
475
+ break;
476
+ if (!(yield _advancePage(useBulk)))
477
+ break;
478
+ pagesScanned++;
432
479
  }
433
- rowIndex += smartRows.length;
434
- if (stopped || pagesScanned >= effectiveMaxPages)
435
- break;
436
- if (!(yield _advancePage()))
437
- break;
438
- pagesScanned++;
480
+ }
481
+ finally {
482
+ yield tracker.cleanup(rootLocator.page());
439
483
  }
440
484
  return (0, smartRowArray_1.createSmartRowArray)(matched);
441
485
  }),
442
- generateConfigPrompt: () => __awaiter(void 0, void 0, void 0, function* () {
486
+ generateConfig: () => __awaiter(void 0, void 0, void 0, function* () {
443
487
  const html = yield _getCleanHtml(rootLocator);
444
488
  const separator = "=".repeat(50);
445
489
  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`;
446
490
  yield _handlePrompt('Smart Table Config', content);
447
491
  }),
492
+ generateConfigPrompt: () => __awaiter(void 0, void 0, void 0, function* () {
493
+ console.warn('āš ļø [playwright-smart-table] generateConfigPrompt() is deprecated and will be removed in v7.0.0. Please use generateConfig() instead.');
494
+ return result.generateConfig();
495
+ }),
448
496
  };
449
497
  finalTable = result;
450
498
  return result;
@@ -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>;