@rickcedwhat/playwright-smart-table 6.6.0 → 6.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/useTable.js CHANGED
@@ -24,7 +24,6 @@ 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");
28
27
  const loading_1 = require("./strategies/loading");
29
28
  const fill_1 = require("./strategies/fill");
30
29
  const headers_1 = require("./strategies/headers");
@@ -116,21 +115,30 @@ const useTable = (rootLocator, configOptions = {}) => {
116
115
  return clone.outerHTML;
117
116
  });
118
117
  });
119
- const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
120
- const { output = 'console', includeTypes = true } = options;
118
+ const _handlePrompt = (promptName, content) => __awaiter(void 0, void 0, void 0, function* () {
121
119
  let finalPrompt = content;
122
- if (includeTypes) {
123
- finalPrompt += `\n\nšŸ‘‡ Useful TypeScript Definitions šŸ‘‡\n\`\`\`typescript\n${minimalConfigContext_1.MINIMAL_CONFIG_CONTEXT}\n\`\`\`\n`;
124
- }
125
- if (output === 'error') {
126
- console.log(`āš ļø Throwing error to display [${promptName}] cleanly...`);
127
- throw new Error(finalPrompt);
128
- }
129
- console.log(finalPrompt);
120
+ finalPrompt += `\n\nšŸ‘‡ Useful TypeScript Definitions šŸ‘‡\n\`\`\`typescript\n${minimalConfigContext_1.MINIMAL_CONFIG_CONTEXT}\n\`\`\`\n`;
121
+ console.log(`āš ļø Throwing error to display [${promptName}] cleanly...`);
122
+ throw new Error(finalPrompt);
130
123
  });
131
- const _ensureInitialized = () => __awaiter(void 0, void 0, void 0, function* () {
124
+ const _autoInit = () => __awaiter(void 0, void 0, void 0, function* () {
132
125
  yield tableMapper.getMap();
133
126
  });
127
+ const _advancePage = () => __awaiter(void 0, void 0, void 0, function* () {
128
+ var _a;
129
+ 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));
133
+ }
134
+ else {
135
+ advanced = !!(((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goNext) && (yield config.strategies.pagination.goNext(context)));
136
+ }
137
+ if (advanced) {
138
+ tableState.currentPageIndex++;
139
+ }
140
+ return advanced;
141
+ });
134
142
  const result = {
135
143
  get currentPageIndex() { return tableState.currentPageIndex; },
136
144
  set currentPageIndex(v) { tableState.currentPageIndex = v; },
@@ -165,74 +173,41 @@ const useTable = (rootLocator, configOptions = {}) => {
165
173
  return resolve(config.headerSelector, rootLocator).nth(idx);
166
174
  }),
167
175
  reset: () => __awaiter(void 0, void 0, void 0, function* () {
176
+ var _a;
168
177
  log("Resetting table...");
169
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
178
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
170
179
  yield config.onReset(context);
180
+ if (typeof config.strategies.pagination !== 'function' && ((_a = config.strategies.pagination) === null || _a === void 0 ? void 0 : _a.goToFirst)) {
181
+ log("Auto-navigating to first page...");
182
+ yield config.strategies.pagination.goToFirst(context);
183
+ }
184
+ else if (hasPaginationInConfig) {
185
+ log("No goToFirst strategy configured. Table may not be on page 1.");
186
+ }
171
187
  _hasPaginated = false;
188
+ tableState.currentPageIndex = 0;
172
189
  tableMapper.clear();
173
- log("Table reset complete.");
190
+ log("Table reset complete. Calling autoInit to restore state.");
191
+ yield _autoInit();
174
192
  }),
175
193
  revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
176
194
  log("Revalidating table structure...");
177
195
  yield tableMapper.remapHeaders();
178
196
  log("Table revalidated.");
179
197
  }),
180
- getColumnValues: (column, options) => __awaiter(void 0, void 0, void 0, function* () {
181
- var _a, _b;
182
- const map = yield tableMapper.getMap();
183
- const colIdx = map.get(column);
184
- if (colIdx === undefined)
185
- throw _createColumnError(column, map);
186
- const mapper = (_a = options === null || options === void 0 ? void 0 : options.mapper) !== null && _a !== void 0 ? _a : ((c) => c.innerText());
187
- const effectiveMaxPages = (_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages;
188
- let pagesScanned = 1;
189
- const results = [];
190
- log(`Getting column values for '${column}' (Pages: ${effectiveMaxPages})`);
191
- while (true) {
192
- const rows = yield resolve(config.rowSelector, rootLocator).all();
193
- for (const row of rows) {
194
- const cell = typeof config.cellSelector === 'string'
195
- ? row.locator(config.cellSelector).nth(colIdx)
196
- : resolve(config.cellSelector, row).nth(colIdx);
197
- results.push(yield mapper(cell));
198
- }
199
- if (pagesScanned < effectiveMaxPages) {
200
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
201
- let pageRes;
202
- if (typeof config.strategies.pagination === 'function') {
203
- pageRes = yield config.strategies.pagination(context);
204
- }
205
- else {
206
- if (!config.strategies.pagination.goNext) {
207
- log('Cannot paginate: no goNext primitive found.');
208
- break;
209
- }
210
- pageRes = yield config.strategies.pagination.goNext(context);
211
- }
212
- if (yield (0, validation_1.validatePaginationResult)(pageRes, 'Pagination Strategy')) {
213
- _hasPaginated = true;
214
- tableState.currentPageIndex++;
215
- pagesScanned++;
216
- continue;
217
- }
218
- }
219
- break;
220
- }
221
- return results;
222
- }),
223
198
  getRow: (filters, options = { exact: false }) => {
224
199
  const map = tableMapper.getMapSync();
225
200
  if (!map)
226
- throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
201
+ throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.findRows() which auto-initialize.');
227
202
  const allRows = resolve(config.rowSelector, rootLocator);
228
203
  const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page());
229
204
  const rowLocator = matchedRows.first();
230
205
  return _makeSmart(rowLocator, map, 0); // fallback index 0
231
206
  },
232
- getRowByIndex: (index, options = {}) => {
207
+ getRowByIndex: (index) => {
233
208
  const map = tableMapper.getMapSync();
234
209
  if (!map)
235
- throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
210
+ throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.findRows() which auto-initialize.');
236
211
  const rowLocator = resolve(config.rowSelector, rootLocator).nth(index);
237
212
  return _makeSmart(rowLocator, map, index);
238
213
  },
@@ -248,26 +223,47 @@ const useTable = (rootLocator, configOptions = {}) => {
248
223
  },
249
224
  sorting: {
250
225
  apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
251
- yield _ensureInitialized();
226
+ var _a;
227
+ yield _autoInit();
252
228
  if (!config.strategies.sorting)
253
229
  throw new Error('No sorting strategy has been configured.');
254
230
  log(`Applying sort for column "${columnName}" (${direction})`);
255
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
256
- yield config.strategies.sorting.doSort({ columnName, direction, context });
231
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
232
+ const maxRetries = 3;
233
+ for (let i = 0; i < maxRetries; i++) {
234
+ const currentState = yield config.strategies.sorting.getSortState({ columnName, context });
235
+ if (currentState === direction) {
236
+ log(`Sort for "${columnName}" is already "${direction}".`);
237
+ return;
238
+ }
239
+ yield config.strategies.sorting.doSort({ columnName, direction, context });
240
+ if ((_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isTableLoading) {
241
+ yield config.strategies.loading.isTableLoading(context);
242
+ }
243
+ else {
244
+ yield rootLocator.page().waitForTimeout(200);
245
+ }
246
+ yield (0, debugUtils_1.debugDelay)(config, 'default');
247
+ const newState = yield config.strategies.sorting.getSortState({ columnName, context });
248
+ if (newState === direction) {
249
+ log(`Successfully sorted "${columnName}" to "${direction}".`);
250
+ return;
251
+ }
252
+ }
253
+ throw new Error(`Failed to sort column "${columnName}" to "${direction}" after ${maxRetries} attempts.`);
257
254
  }),
258
255
  getState: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
259
- yield _ensureInitialized();
256
+ yield _autoInit();
260
257
  if (!config.strategies.sorting)
261
258
  throw new Error('No sorting strategy has been configured.');
262
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
259
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve, getHeaderCell: result.getHeaderCell };
263
260
  return config.strategies.sorting.getSortState({ columnName, context });
264
261
  })
265
262
  },
266
263
  // ─── Shared async row iterator ───────────────────────────────────────────
267
264
  [Symbol.asyncIterator]() {
268
265
  return __asyncGenerator(this, arguments, function* _a() {
269
- var _b;
270
- yield __await(_ensureInitialized());
266
+ yield __await(_autoInit());
271
267
  const map = tableMapper.getMapSync();
272
268
  const effectiveMaxPages = config.maxPages;
273
269
  let rowIndex = 0;
@@ -280,17 +276,8 @@ const useTable = (rootLocator, configOptions = {}) => {
280
276
  }
281
277
  if (pagesScanned >= effectiveMaxPages)
282
278
  break;
283
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
284
- let advanced;
285
- if (typeof config.strategies.pagination === 'function') {
286
- advanced = !!(yield __await(config.strategies.pagination(context)));
287
- }
288
- else {
289
- advanced = !!(((_b = config.strategies.pagination) === null || _b === void 0 ? void 0 : _b.goNext) && (yield __await(config.strategies.pagination.goNext(context))));
290
- }
291
- if (!advanced)
279
+ if (!(yield __await(_advancePage())))
292
280
  break;
293
- tableState.currentPageIndex++;
294
281
  pagesScanned++;
295
282
  }
296
283
  });
@@ -298,11 +285,12 @@ const useTable = (rootLocator, configOptions = {}) => {
298
285
  // ─── Private row-iteration engine ────────────────────────────────────────
299
286
  forEach: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
300
287
  var _a, _b, _c;
301
- yield _ensureInitialized();
288
+ yield _autoInit();
302
289
  const map = tableMapper.getMapSync();
303
290
  const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
304
- const dedupeKeys = options.dedupe ? new Set() : null;
305
- const parallel = (_b = options.parallel) !== null && _b !== void 0 ? _b : false;
291
+ const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
292
+ const dedupeKeys = dedupeStrategy ? new Set() : null;
293
+ const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
306
294
  let rowIndex = 0;
307
295
  let stopped = false;
308
296
  let pagesScanned = 1;
@@ -315,7 +303,7 @@ const useTable = (rootLocator, configOptions = {}) => {
315
303
  if (stopped)
316
304
  return;
317
305
  if (dedupeKeys) {
318
- const key = yield options.dedupe(row);
306
+ const key = yield dedupeStrategy(row);
319
307
  if (dedupeKeys.has(key))
320
308
  return;
321
309
  dedupeKeys.add(key);
@@ -328,7 +316,7 @@ const useTable = (rootLocator, configOptions = {}) => {
328
316
  if (stopped)
329
317
  break;
330
318
  if (dedupeKeys) {
331
- const key = yield options.dedupe(row);
319
+ const key = yield dedupeStrategy(row);
332
320
  if (dedupeKeys.has(key))
333
321
  continue;
334
322
  dedupeKeys.add(key);
@@ -339,27 +327,19 @@ const useTable = (rootLocator, configOptions = {}) => {
339
327
  rowIndex += smartRows.length;
340
328
  if (stopped || pagesScanned >= effectiveMaxPages)
341
329
  break;
342
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
343
- let advanced;
344
- if (typeof config.strategies.pagination === 'function') {
345
- advanced = !!(yield config.strategies.pagination(context));
346
- }
347
- else {
348
- advanced = !!(((_c = config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNext) && (yield config.strategies.pagination.goNext(context)));
349
- }
350
- if (!advanced)
330
+ if (!(yield _advancePage()))
351
331
  break;
352
- tableState.currentPageIndex++;
353
332
  pagesScanned++;
354
333
  }
355
334
  }),
356
335
  map: (callback_1, ...args_1) => __awaiter(void 0, [callback_1, ...args_1], void 0, function* (callback, options = {}) {
357
336
  var _a, _b, _c;
358
- yield _ensureInitialized();
337
+ yield _autoInit();
359
338
  const map = tableMapper.getMapSync();
360
339
  const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
361
- const dedupeKeys = options.dedupe ? new Set() : null;
362
- const parallel = (_b = options.parallel) !== null && _b !== void 0 ? _b : true;
340
+ const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
341
+ const dedupeKeys = dedupeStrategy ? new Set() : null;
342
+ const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : true;
363
343
  const results = [];
364
344
  let rowIndex = 0;
365
345
  let stopped = false;
@@ -372,7 +352,7 @@ const useTable = (rootLocator, configOptions = {}) => {
372
352
  const SKIP = Symbol('skip');
373
353
  const pageResults = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
374
354
  if (dedupeKeys) {
375
- const key = yield options.dedupe(row);
355
+ const key = yield dedupeStrategy(row);
376
356
  if (dedupeKeys.has(key))
377
357
  return SKIP;
378
358
  dedupeKeys.add(key);
@@ -389,7 +369,7 @@ const useTable = (rootLocator, configOptions = {}) => {
389
369
  if (stopped)
390
370
  break;
391
371
  if (dedupeKeys) {
392
- const key = yield options.dedupe(row);
372
+ const key = yield dedupeStrategy(row);
393
373
  if (dedupeKeys.has(key))
394
374
  continue;
395
375
  dedupeKeys.add(key);
@@ -400,28 +380,20 @@ const useTable = (rootLocator, configOptions = {}) => {
400
380
  rowIndex += smartRows.length;
401
381
  if (stopped || pagesScanned >= effectiveMaxPages)
402
382
  break;
403
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
404
- let advanced;
405
- if (typeof config.strategies.pagination === 'function') {
406
- advanced = !!(yield config.strategies.pagination(context));
407
- }
408
- else {
409
- advanced = !!(((_c = config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNext) && (yield config.strategies.pagination.goNext(context)));
410
- }
411
- if (!advanced)
383
+ if (!(yield _advancePage()))
412
384
  break;
413
- tableState.currentPageIndex++;
414
385
  pagesScanned++;
415
386
  }
416
387
  return results;
417
388
  }),
418
389
  filter: (predicate_1, ...args_1) => __awaiter(void 0, [predicate_1, ...args_1], void 0, function* (predicate, options = {}) {
419
390
  var _a, _b, _c;
420
- yield _ensureInitialized();
391
+ yield _autoInit();
421
392
  const map = tableMapper.getMapSync();
422
393
  const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
423
- const dedupeKeys = options.dedupe ? new Set() : null;
424
- const parallel = (_b = options.parallel) !== null && _b !== void 0 ? _b : false;
394
+ const dedupeStrategy = (_b = options.dedupe) !== null && _b !== void 0 ? _b : config.strategies.dedupe;
395
+ const dedupeKeys = dedupeStrategy ? new Set() : null;
396
+ const parallel = (_c = options.parallel) !== null && _c !== void 0 ? _c : false;
425
397
  const matched = [];
426
398
  let rowIndex = 0;
427
399
  let stopped = false;
@@ -433,7 +405,7 @@ const useTable = (rootLocator, configOptions = {}) => {
433
405
  if (parallel) {
434
406
  const flags = yield Promise.all(smartRows.map((row) => __awaiter(void 0, void 0, void 0, function* () {
435
407
  if (dedupeKeys) {
436
- const key = yield options.dedupe(row);
408
+ const key = yield dedupeStrategy(row);
437
409
  if (dedupeKeys.has(key))
438
410
  return false;
439
411
  dedupeKeys.add(key);
@@ -448,7 +420,7 @@ const useTable = (rootLocator, configOptions = {}) => {
448
420
  if (stopped)
449
421
  break;
450
422
  if (dedupeKeys) {
451
- const key = yield options.dedupe(row);
423
+ const key = yield dedupeStrategy(row);
452
424
  if (dedupeKeys.has(key))
453
425
  continue;
454
426
  dedupeKeys.add(key);
@@ -461,223 +433,17 @@ const useTable = (rootLocator, configOptions = {}) => {
461
433
  rowIndex += smartRows.length;
462
434
  if (stopped || pagesScanned >= effectiveMaxPages)
463
435
  break;
464
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
465
- let advanced;
466
- if (typeof config.strategies.pagination === 'function') {
467
- advanced = !!(yield config.strategies.pagination(context));
468
- }
469
- else {
470
- advanced = !!(((_c = config.strategies.pagination) === null || _c === void 0 ? void 0 : _c.goNext) && (yield config.strategies.pagination.goNext(context)));
471
- }
472
- if (!advanced)
436
+ if (!(yield _advancePage()))
473
437
  break;
474
- tableState.currentPageIndex++;
475
438
  pagesScanned++;
476
439
  }
477
440
  return (0, smartRowArray_1.createSmartRowArray)(matched);
478
441
  }),
479
- iterateThroughTable: (callback, options) => __awaiter(void 0, void 0, void 0, function* () {
480
- var _a, _b, _c, _d, _e, _f, _g;
481
- yield _ensureInitialized();
482
- const paginationStrategy = (_a = options === null || options === void 0 ? void 0 : options.pagination) !== null && _a !== void 0 ? _a : config.strategies.pagination;
483
- const hasPaginationInOptions = (options === null || options === void 0 ? void 0 : options.pagination) !== undefined;
484
- if (!hasPaginationInOptions && !hasPaginationInConfig)
485
- throw new Error('No pagination strategy provided.');
486
- yield result.reset();
487
- yield result.init();
488
- const map = tableMapper.getMapSync();
489
- const restrictedTable = {
490
- get currentPageIndex() { return tableState.currentPageIndex; },
491
- init: result.init,
492
- getHeaders: result.getHeaders,
493
- getHeaderCell: result.getHeaderCell,
494
- getRow: result.getRow,
495
- getRowByIndex: result.getRowByIndex,
496
- findRow: result.findRow,
497
- findRows: result.findRows,
498
- getColumnValues: result.getColumnValues,
499
- isInitialized: result.isInitialized,
500
- sorting: result.sorting,
501
- scrollToColumn: result.scrollToColumn,
502
- revalidate: result.revalidate,
503
- generateConfigPrompt: result.generateConfigPrompt,
504
- forEach: result.forEach,
505
- map: result.map,
506
- filter: result.filter,
507
- [Symbol.asyncIterator]: result[Symbol.asyncIterator].bind(result),
508
- };
509
- const getIsFirst = (_b = options === null || options === void 0 ? void 0 : options.getIsFirst) !== null && _b !== void 0 ? _b : (({ index }) => index === 0);
510
- const getIsLast = (_c = options === null || options === void 0 ? void 0 : options.getIsLast) !== null && _c !== void 0 ? _c : (() => false);
511
- const allData = [];
512
- const effectiveMaxIterations = (_d = options === null || options === void 0 ? void 0 : options.maxIterations) !== null && _d !== void 0 ? _d : config.maxPages;
513
- const batchSize = options === null || options === void 0 ? void 0 : options.batchSize;
514
- const isBatching = batchSize !== undefined && batchSize > 1;
515
- const autoFlatten = (_e = options === null || options === void 0 ? void 0 : options.autoFlatten) !== null && _e !== void 0 ? _e : false;
516
- let index = 0;
517
- let paginationResult = true;
518
- let seenKeys = null;
519
- let batchRows = [];
520
- let batchStartIndex = 0;
521
- log(`Starting iterateThroughTable (maxIterations: ${effectiveMaxIterations}, batchSize: ${batchSize !== null && batchSize !== void 0 ? batchSize : 'none'})`);
522
- while (index < effectiveMaxIterations) {
523
- const rowLocators = yield resolve(config.rowSelector, rootLocator).all();
524
- const smartRowsArray = [];
525
- const isRowLoading = (_f = config.strategies.loading) === null || _f === void 0 ? void 0 : _f.isRowLoading;
526
- for (let i = 0; i < rowLocators.length; i++) {
527
- const smartRow = _makeSmart(rowLocators[i], map, i);
528
- if (isRowLoading && (yield isRowLoading(smartRow)))
529
- continue;
530
- smartRowsArray.push(smartRow);
531
- }
532
- let rows = (0, smartRowArray_1.createSmartRowArray)(smartRowsArray);
533
- const dedupeStrategy = (_g = options === null || options === void 0 ? void 0 : options.dedupeStrategy) !== null && _g !== void 0 ? _g : config.strategies.dedupe;
534
- if (dedupeStrategy && rows.length > 0) {
535
- if (!seenKeys)
536
- seenKeys = new Set();
537
- const deduplicated = [];
538
- for (const row of rows) {
539
- const key = yield dedupeStrategy(row);
540
- if (!seenKeys.has(key)) {
541
- seenKeys.add(key);
542
- deduplicated.push(row);
543
- }
544
- }
545
- rows = (0, smartRowArray_1.createSmartRowArray)(deduplicated);
546
- log(`Deduplicated ${rowLocators.length} rows to ${rows.length} unique rows (total seen: ${seenKeys.size})`);
547
- }
548
- // Add rows to batch if batching is enabled
549
- if (isBatching) {
550
- batchRows.push(...rows);
551
- }
552
- const isLastIteration = index === effectiveMaxIterations - 1;
553
- // Determine if we should invoke the callback
554
- const batchComplete = isBatching && (index - batchStartIndex + 1) >= batchSize;
555
- const shouldInvokeCallback = !isBatching || batchComplete || isLastIteration;
556
- if (shouldInvokeCallback) {
557
- const callbackRows = isBatching ? batchRows : rows;
558
- const callbackIndex = isBatching ? batchStartIndex : index;
559
- const isFirst = getIsFirst({ index: callbackIndex });
560
- let isLast = getIsLast({ index: callbackIndex, paginationResult });
561
- const isLastDueToMax = index === effectiveMaxIterations - 1;
562
- if (isFirst && (options === null || options === void 0 ? void 0 : options.beforeFirst)) {
563
- yield options.beforeFirst({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows), allData });
564
- }
565
- const batchInfo = isBatching ? {
566
- startIndex: batchStartIndex,
567
- endIndex: index,
568
- size: index - batchStartIndex + 1
569
- } : undefined;
570
- const returnValue = yield callback({
571
- index: callbackIndex,
572
- isFirst,
573
- isLast,
574
- rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows),
575
- allData,
576
- table: restrictedTable,
577
- batchInfo
578
- });
579
- if (autoFlatten && Array.isArray(returnValue)) {
580
- allData.push(...returnValue);
581
- }
582
- else {
583
- allData.push(returnValue);
584
- }
585
- // Determine if this is truly the last iteration
586
- let finalIsLast = isLastDueToMax;
587
- if (!isLastIteration) {
588
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
589
- if (typeof paginationStrategy === 'function') {
590
- paginationResult = yield paginationStrategy(context);
591
- }
592
- else {
593
- const pageObj = paginationStrategy;
594
- if (!pageObj.goNext)
595
- break;
596
- paginationResult = yield pageObj.goNext(context);
597
- }
598
- (0, debugUtils_1.logDebug)(config, 'info', `Pagination ${paginationResult ? 'succeeded' : 'failed'}`);
599
- yield (0, debugUtils_1.debugDelay)(config, 'pagination');
600
- finalIsLast = getIsLast({ index: callbackIndex, paginationResult }) || !paginationResult;
601
- if (paginationResult)
602
- tableState.currentPageIndex++;
603
- }
604
- if (finalIsLast && (options === null || options === void 0 ? void 0 : options.afterLast)) {
605
- yield options.afterLast({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows), allData });
606
- }
607
- if (finalIsLast || !paginationResult) {
608
- log(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult})`);
609
- break;
610
- }
611
- // Reset batch
612
- if (isBatching) {
613
- batchRows = [];
614
- batchStartIndex = index + 1;
615
- }
616
- }
617
- else {
618
- // Continue paginating even when batching
619
- const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
620
- if (typeof paginationStrategy === 'function') {
621
- paginationResult = yield paginationStrategy(context);
622
- }
623
- else {
624
- const pageObj = paginationStrategy;
625
- if (!pageObj.goNext) {
626
- log(`Cannot paginate: no goNext primitive found.`);
627
- break;
628
- }
629
- paginationResult = yield pageObj.goNext(context);
630
- }
631
- (0, debugUtils_1.logDebug)(config, 'info', `Pagination ${paginationResult ? 'succeeded' : 'failed'} (batching mode)`);
632
- yield (0, debugUtils_1.debugDelay)(config, 'pagination');
633
- if (paginationResult)
634
- tableState.currentPageIndex++;
635
- if (!paginationResult) {
636
- // Pagination failed, invoke callback with current batch
637
- const callbackIndex = batchStartIndex;
638
- const isFirst = getIsFirst({ index: callbackIndex });
639
- const isLast = true;
640
- if (isFirst && (options === null || options === void 0 ? void 0 : options.beforeFirst)) {
641
- yield options.beforeFirst({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(batchRows), allData });
642
- }
643
- const batchInfo = {
644
- startIndex: batchStartIndex,
645
- endIndex: index,
646
- size: index - batchStartIndex + 1
647
- };
648
- const returnValue = yield callback({
649
- index: callbackIndex,
650
- isFirst,
651
- isLast,
652
- rows: (0, smartRowArray_1.createSmartRowArray)(batchRows),
653
- allData,
654
- table: restrictedTable,
655
- batchInfo
656
- });
657
- if (autoFlatten && Array.isArray(returnValue)) {
658
- allData.push(...returnValue);
659
- }
660
- else {
661
- allData.push(returnValue);
662
- }
663
- if (options === null || options === void 0 ? void 0 : options.afterLast) {
664
- yield options.afterLast({ index: callbackIndex, rows: (0, smartRowArray_1.createSmartRowArray)(batchRows), allData });
665
- }
666
- log(`Pagination failed mid-batch (index: ${index})`);
667
- break;
668
- }
669
- }
670
- index++;
671
- log(`Iteration ${index} completed, continuing...`);
672
- }
673
- log(`iterateThroughTable completed after ${index + 1} iterations, collected ${allData.length} items`);
674
- return allData;
675
- }),
676
- generateConfigPrompt: (options) => __awaiter(void 0, void 0, void 0, function* () {
442
+ generateConfigPrompt: () => __awaiter(void 0, void 0, void 0, function* () {
677
443
  const html = yield _getCleanHtml(rootLocator);
678
444
  const separator = "=".repeat(50);
679
445
  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`;
680
- yield _handlePrompt('Smart Table Config', content, options);
446
+ yield _handlePrompt('Smart Table Config', content);
681
447
  }),
682
448
  };
683
449
  finalTable = result;
@@ -0,0 +1,15 @@
1
+ import { Locator } from '@playwright/test';
2
+ export declare class ElementTracker {
3
+ readonly id: string;
4
+ constructor(prefix?: string);
5
+ /**
6
+ * Finds the indices of newly seen elements in the browser, storing their text signature
7
+ * in a WeakMap. This gracefully handles both append-only DOMs (by identity) and
8
+ * virtualized DOMs (by text signature if nodes are recycled).
9
+ */
10
+ getUnseenIndices(locators: Locator): Promise<number[]>;
11
+ /**
12
+ * Cleans up the tracking map from the browser window object.
13
+ */
14
+ cleanup(page: any): Promise<void>;
15
+ }
@@ -0,0 +1,60 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ElementTracker = void 0;
13
+ class ElementTracker {
14
+ constructor(prefix = 'tracker') {
15
+ this.id = `__smartTable_${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
16
+ }
17
+ /**
18
+ * Finds the indices of newly seen elements in the browser, storing their text signature
19
+ * in a WeakMap. This gracefully handles both append-only DOMs (by identity) and
20
+ * virtualized DOMs (by text signature if nodes are recycled).
21
+ */
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();
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
+ });
43
+ }
44
+ /**
45
+ * Cleans up the tracking map from the browser window object.
46
+ */
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
+ });
58
+ }
59
+ }
60
+ exports.ElementTracker = ElementTracker;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rickcedwhat/playwright-smart-table",
3
- "version": "6.6.0",
3
+ "version": "6.7.1",
4
4
  "description": "Smart, column-aware table interactions for Playwright",
5
5
  "author": "Cedrick Catalan",
6
6
  "license": "MIT",