@rickcedwhat/playwright-smart-table 5.3.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +78 -957
  2. package/dist/examples/glide-strategies/columns.d.ts +13 -0
  3. package/dist/examples/glide-strategies/columns.js +43 -0
  4. package/dist/examples/glide-strategies/headers.d.ts +9 -0
  5. package/dist/examples/glide-strategies/headers.js +68 -0
  6. package/dist/src/filterEngine.d.ts +11 -0
  7. package/dist/src/filterEngine.js +39 -0
  8. package/dist/src/index.d.ts +2 -0
  9. package/dist/src/index.js +18 -0
  10. package/dist/src/plugins.d.ts +32 -0
  11. package/dist/src/plugins.js +13 -0
  12. package/dist/src/smartRow.d.ts +7 -0
  13. package/dist/src/smartRow.js +160 -0
  14. package/dist/src/strategies/columns.d.ts +18 -0
  15. package/dist/src/strategies/columns.js +21 -0
  16. package/dist/src/strategies/dedupe.d.ts +9 -0
  17. package/dist/src/strategies/dedupe.js +27 -0
  18. package/dist/src/strategies/fill.d.ts +7 -0
  19. package/dist/src/strategies/fill.js +88 -0
  20. package/dist/src/strategies/glide.d.ts +29 -0
  21. package/dist/src/strategies/glide.js +98 -0
  22. package/dist/src/strategies/headers.d.ts +13 -0
  23. package/dist/src/strategies/headers.js +30 -0
  24. package/dist/src/strategies/index.d.ts +54 -0
  25. package/dist/src/strategies/index.js +43 -0
  26. package/dist/src/strategies/loading.d.ts +48 -0
  27. package/dist/src/strategies/loading.js +82 -0
  28. package/dist/src/strategies/pagination.d.ts +33 -0
  29. package/dist/src/strategies/pagination.js +79 -0
  30. package/dist/src/strategies/rdg.d.ts +25 -0
  31. package/dist/src/strategies/rdg.js +100 -0
  32. package/dist/src/strategies/resolution.d.ts +22 -0
  33. package/dist/src/strategies/resolution.js +30 -0
  34. package/dist/src/strategies/sorting.d.ts +12 -0
  35. package/dist/src/strategies/sorting.js +68 -0
  36. package/dist/src/strategies/stabilization.d.ts +29 -0
  37. package/dist/src/strategies/stabilization.js +91 -0
  38. package/dist/src/strategies/validation.d.ts +22 -0
  39. package/dist/src/strategies/validation.js +54 -0
  40. package/dist/src/strategies/virtualizedPagination.d.ts +32 -0
  41. package/dist/src/strategies/virtualizedPagination.js +80 -0
  42. package/dist/src/typeContext.d.ts +6 -0
  43. package/dist/src/typeContext.js +465 -0
  44. package/dist/src/types.d.ts +458 -0
  45. package/dist/src/types.js +2 -0
  46. package/dist/src/useTable.d.ts +44 -0
  47. package/dist/src/useTable.js +642 -0
  48. package/dist/src/utils/debugUtils.d.ts +17 -0
  49. package/dist/src/utils/debugUtils.js +62 -0
  50. package/dist/src/utils/smartRowArray.d.ts +14 -0
  51. package/dist/src/utils/smartRowArray.js +22 -0
  52. package/dist/src/utils/stringUtils.d.ts +22 -0
  53. package/dist/src/utils/stringUtils.js +73 -0
  54. package/dist/src/utils.d.ts +7 -0
  55. package/dist/src/utils.js +29 -0
  56. package/dist/typeContext.d.ts +1 -1
  57. package/dist/typeContext.js +27 -5
  58. package/dist/types.d.ts +27 -6
  59. package/dist/useTable.js +21 -16
  60. package/dist/utils/smartRowArray.d.ts +14 -0
  61. package/dist/utils/smartRowArray.js +22 -0
  62. package/package.json +16 -20
@@ -0,0 +1,642 @@
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.Strategies = exports.ResolutionStrategies = exports.CellNavigationStrategies = exports.HeaderStrategies = exports.FillStrategies = exports.DedupeStrategies = exports.SortingStrategies = exports.LoadingStrategies = exports.PaginationStrategies = exports.useTable = void 0;
13
+ const typeContext_1 = require("./typeContext");
14
+ const sorting_1 = require("./strategies/sorting");
15
+ const pagination_1 = require("./strategies/pagination");
16
+ const dedupe_1 = require("./strategies/dedupe");
17
+ const loading_1 = require("./strategies/loading");
18
+ const fill_1 = require("./strategies/fill");
19
+ Object.defineProperty(exports, "FillStrategies", { enumerable: true, get: function () { return fill_1.FillStrategies; } });
20
+ const headers_1 = require("./strategies/headers");
21
+ Object.defineProperty(exports, "HeaderStrategies", { enumerable: true, get: function () { return headers_1.HeaderStrategies; } });
22
+ const columns_1 = require("./strategies/columns");
23
+ Object.defineProperty(exports, "CellNavigationStrategies", { enumerable: true, get: function () { return columns_1.CellNavigationStrategies; } });
24
+ const smartRow_1 = require("./smartRow");
25
+ const filterEngine_1 = require("./filterEngine");
26
+ const resolution_1 = require("./strategies/resolution");
27
+ Object.defineProperty(exports, "ResolutionStrategies", { enumerable: true, get: function () { return resolution_1.ResolutionStrategies; } });
28
+ const strategies_1 = require("./strategies");
29
+ Object.defineProperty(exports, "Strategies", { enumerable: true, get: function () { return strategies_1.Strategies; } });
30
+ const validation_1 = require("./strategies/validation");
31
+ const debugUtils_1 = require("./utils/debugUtils");
32
+ const smartRowArray_1 = require("./utils/smartRowArray");
33
+ /**
34
+ * Main hook to interact with a table.
35
+ */
36
+ const useTable = (rootLocator, configOptions = {}) => {
37
+ var _a;
38
+ // Store whether pagination was explicitly provided in config
39
+ const hasPaginationInConfig = ((_a = configOptions.strategies) === null || _a === void 0 ? void 0 : _a.pagination) !== undefined;
40
+ // Default strategies
41
+ const defaultStrategies = {
42
+ fill: fill_1.FillStrategies.default,
43
+ header: headers_1.HeaderStrategies.visible,
44
+ cellNavigation: columns_1.CellNavigationStrategies.default,
45
+ pagination: () => __awaiter(void 0, void 0, void 0, function* () { return false; }),
46
+ };
47
+ const config = Object.assign(Object.assign({ rowSelector: "tbody tr", headerSelector: "thead th", cellSelector: "td", maxPages: 1, headerTransformer: ({ text }) => text, autoScroll: true, onReset: () => __awaiter(void 0, void 0, void 0, function* () { }) }, configOptions), { strategies: Object.assign(Object.assign({}, defaultStrategies), configOptions.strategies) });
48
+ const resolve = (item, parent) => {
49
+ if (typeof item === 'string')
50
+ return parent.locator(item);
51
+ if (typeof item === 'function')
52
+ return item(parent);
53
+ return item;
54
+ };
55
+ // Internal State
56
+ let _headerMap = null;
57
+ let _hasPaginated = false;
58
+ let _isInitialized = false;
59
+ // Helpers
60
+ const log = (msg) => {
61
+ (0, debugUtils_1.logDebug)(config, 'verbose', msg); // Legacy(`šŸ”Ž [SmartTable Debug] ${msg}`);
62
+ };
63
+ const _createColumnError = (colName, map, context) => {
64
+ const availableColumns = Array.from(map.keys());
65
+ // Use Suggestion Logic from ResolutionStrategy (if we had a fuzzy one, for now manual suggest)
66
+ const lowerCol = colName.toLowerCase();
67
+ const suggestions = availableColumns.filter(col => col.toLowerCase().includes(lowerCol) ||
68
+ lowerCol.includes(col.toLowerCase()) ||
69
+ col.toLowerCase().replace(/\s+/g, '') === lowerCol.replace(/\s+/g, ''));
70
+ let suggestion = '.';
71
+ if (suggestions.length > 0 && suggestions[0] !== colName) {
72
+ suggestion = `. Did you mean "${suggestions[0]}"?`;
73
+ }
74
+ else if (availableColumns.length > 0 && availableColumns.length <= 10) {
75
+ suggestion = `. Available columns: ${availableColumns.map(c => `"${c}"`).join(', ')}`;
76
+ }
77
+ else if (availableColumns.length > 0) {
78
+ suggestion = `. Available columns (first 10 of ${availableColumns.length}): ${availableColumns.slice(0, 10).map(c => `"${c}"`).join(', ')}, ...`;
79
+ }
80
+ const contextMsg = context ? ` (${context})` : '';
81
+ return new Error(`Column "${colName}" not found${contextMsg}${suggestion}`);
82
+ };
83
+ const _getMap = (timeout) => __awaiter(void 0, void 0, void 0, function* () {
84
+ if (_headerMap)
85
+ return _headerMap;
86
+ log('Mapping headers...');
87
+ const headerTimeout = timeout !== null && timeout !== void 0 ? timeout : 3000;
88
+ if (config.autoScroll) {
89
+ try {
90
+ yield rootLocator.scrollIntoViewIfNeeded({ timeout: 1000 });
91
+ }
92
+ catch (e) { }
93
+ }
94
+ const headerLoc = resolve(config.headerSelector, rootLocator);
95
+ try {
96
+ yield headerLoc.first().waitFor({ state: 'visible', timeout: headerTimeout });
97
+ }
98
+ catch (e) { /* Ignore hydration */ }
99
+ const strategy = config.strategies.header || headers_1.HeaderStrategies.visible;
100
+ const context = {
101
+ root: rootLocator,
102
+ config: config,
103
+ page: rootLocator.page(),
104
+ resolve: resolve
105
+ };
106
+ const rawHeaders = yield strategy(context);
107
+ const seenHeaders = new Set();
108
+ const entries = [];
109
+ for (let i = 0; i < rawHeaders.length; i++) {
110
+ let text = rawHeaders[i].trim() || `__col_${i}`;
111
+ if (config.headerTransformer) {
112
+ text = yield config.headerTransformer({
113
+ text,
114
+ index: i,
115
+ locator: rootLocator.locator(config.headerSelector).nth(i),
116
+ seenHeaders
117
+ });
118
+ }
119
+ entries.push([text, i]);
120
+ seenHeaders.add(text);
121
+ }
122
+ // Validation: Check for empty table
123
+ if (entries.length === 0) {
124
+ throw new Error(`Initialization Error: No columns found using selector "${config.headerSelector}". Check your selector or ensure the table is visible.`);
125
+ }
126
+ // Validation: Check for duplicates
127
+ const seen = new Set();
128
+ const duplicates = new Set();
129
+ for (const [name] of entries) {
130
+ if (seen.has(name)) {
131
+ duplicates.add(name);
132
+ }
133
+ seen.add(name);
134
+ }
135
+ if (duplicates.size > 0) {
136
+ const dupList = Array.from(duplicates).map(d => `"${d}"`).join(', ');
137
+ throw new Error(`Initialization Error: Duplicate column names found: ${dupList}. Use 'headerTransformer' to rename duplicate columns.`);
138
+ }
139
+ _headerMap = new Map(entries);
140
+ log(`Mapped ${entries.length} columns: ${JSON.stringify(entries.map(e => e[0]))}`);
141
+ return _headerMap;
142
+ });
143
+ // Placeholder for the final table object
144
+ let finalTable = null;
145
+ const filterEngine = new filterEngine_1.FilterEngine(config, resolve);
146
+ // Helper factory
147
+ const _makeSmart = (rowLocator, map, rowIndex) => {
148
+ // Use the wrapped SmartRow logic
149
+ return (0, smartRow_1.createSmartRow)(rowLocator, map, rowIndex, config, rootLocator, resolve, finalTable);
150
+ };
151
+ const _findRowLocator = (filters_1, ...args_1) => __awaiter(void 0, [filters_1, ...args_1], void 0, function* (filters, options = {}) {
152
+ var _a, _b;
153
+ const map = yield _getMap();
154
+ const effectiveMaxPages = (_a = options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages;
155
+ let currentPage = 1;
156
+ log(`Looking for row: ${JSON.stringify(filters)} (MaxPages: ${effectiveMaxPages})`);
157
+ while (true) {
158
+ // Check for table loading
159
+ if ((_b = config.strategies.loading) === null || _b === void 0 ? void 0 : _b.isTableLoading) {
160
+ const isLoading = yield config.strategies.loading.isTableLoading({ root: rootLocator, config, page: rootLocator.page(), resolve });
161
+ if (isLoading) {
162
+ log('Table is loading... waiting');
163
+ yield rootLocator.page().waitForTimeout(200);
164
+ continue;
165
+ }
166
+ }
167
+ const allRows = resolve(config.rowSelector, rootLocator);
168
+ // Use FilterEngine
169
+ const matchedRows = filterEngine.applyFilters(allRows, filters, map, options.exact || false, rootLocator.page());
170
+ const count = yield matchedRows.count();
171
+ log(`Page ${currentPage}: Found ${count} matches.`);
172
+ if (count > 1) {
173
+ // Sample data logic (simplified for refactor, kept inline or moved to util if needed)
174
+ const sampleData = [];
175
+ try {
176
+ const firstFewRows = yield matchedRows.all();
177
+ const sampleCount = Math.min(firstFewRows.length, 3);
178
+ for (let i = 0; i < sampleCount; i++) {
179
+ const rowData = yield _makeSmart(firstFewRows[i], map).toJSON();
180
+ sampleData.push(JSON.stringify(rowData));
181
+ }
182
+ }
183
+ catch (e) { }
184
+ const sampleMsg = sampleData.length > 0 ? `\nSample matching rows:\n${sampleData.map((d, i) => ` ${i + 1}. ${d}`).join('\n')}` : '';
185
+ throw new Error(`Ambiguous Row: Found ${count} rows matching ${JSON.stringify(filters)} on page ${currentPage}. ` +
186
+ `Expected exactly one match. Try adding more filters to make your query unique.${sampleMsg}`);
187
+ }
188
+ if (count === 1)
189
+ return matchedRows.first();
190
+ if (currentPage < effectiveMaxPages) {
191
+ log(`Page ${currentPage}: Not found. Attempting pagination...`);
192
+ const context = {
193
+ root: rootLocator,
194
+ config: config,
195
+ page: rootLocator.page(),
196
+ resolve: resolve
197
+ };
198
+ const paginationResult = yield config.strategies.pagination(context);
199
+ const didLoadMore = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
200
+ if (didLoadMore) {
201
+ _hasPaginated = true;
202
+ currentPage++;
203
+ continue;
204
+ }
205
+ else {
206
+ log(`Page ${currentPage}: Pagination failed (end of data).`);
207
+ }
208
+ }
209
+ if (_hasPaginated) {
210
+ console.warn(`āš ļø [SmartTable] Row not found. The table has been paginated (Current Page: ${currentPage}). You may need to call 'await table.reset()' if the target row is on a previous page.`);
211
+ }
212
+ return null;
213
+ }
214
+ });
215
+ const _getCleanHtml = (loc) => __awaiter(void 0, void 0, void 0, function* () {
216
+ return loc.evaluate((el) => {
217
+ const clone = el.cloneNode(true);
218
+ const removeSelectors = 'script, style, svg, path, circle, rect, noscript, [hidden]';
219
+ clone.querySelectorAll(removeSelectors).forEach(n => n.remove());
220
+ const walker = document.createTreeWalker(clone, NodeFilter.SHOW_ELEMENT);
221
+ let currentNode = walker.currentNode;
222
+ while (currentNode) {
223
+ currentNode.removeAttribute('style');
224
+ currentNode.removeAttribute('data-reactid');
225
+ const cls = currentNode.getAttribute('class');
226
+ if (cls && cls.length > 80) {
227
+ const tokens = cls.split(' ');
228
+ if (tokens.length > 5) {
229
+ currentNode.setAttribute('class', tokens.slice(0, 4).join(' ') + ' ...');
230
+ }
231
+ }
232
+ currentNode = walker.nextNode();
233
+ }
234
+ return clone.outerHTML;
235
+ });
236
+ });
237
+ const _handlePrompt = (promptName_1, content_1, ...args_1) => __awaiter(void 0, [promptName_1, content_1, ...args_1], void 0, function* (promptName, content, options = {}) {
238
+ const { output = 'console', includeTypes = true } = options;
239
+ let finalPrompt = content;
240
+ if (includeTypes) {
241
+ finalPrompt += `\n\nšŸ‘‡ Useful TypeScript Definitions šŸ‘‡\n\`\`\`typescript\n${typeContext_1.TYPE_CONTEXT}\n\`\`\`\n`;
242
+ }
243
+ if (output === 'error') {
244
+ console.log(`āš ļø Throwing error to display [${promptName}] cleanly...`);
245
+ throw new Error(finalPrompt);
246
+ }
247
+ console.log(finalPrompt);
248
+ });
249
+ const _ensureInitialized = () => __awaiter(void 0, void 0, void 0, function* () {
250
+ if (!_isInitialized) {
251
+ yield _getMap();
252
+ _isInitialized = true;
253
+ }
254
+ });
255
+ const result = {
256
+ init: (options) => __awaiter(void 0, void 0, void 0, function* () {
257
+ if (_isInitialized && _headerMap)
258
+ return result;
259
+ (0, debugUtils_1.warnIfDebugInCI)(config);
260
+ (0, debugUtils_1.logDebug)(config, 'info', 'Initializing table');
261
+ yield _getMap(options === null || options === void 0 ? void 0 : options.timeout);
262
+ _isInitialized = true;
263
+ if (_headerMap) {
264
+ (0, debugUtils_1.logDebug)(config, 'info', `Table initialized with ${_headerMap.size} columns`, Array.from(_headerMap.keys()));
265
+ // Trace event removed - redundant with debug logging
266
+ }
267
+ yield (0, debugUtils_1.debugDelay)(config, 'default');
268
+ return result;
269
+ }),
270
+ scrollToColumn: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
271
+ const map = yield _getMap();
272
+ const idx = map.get(columnName);
273
+ if (idx === undefined)
274
+ throw _createColumnError(columnName, map);
275
+ yield config.strategies.cellNavigation({
276
+ config: config,
277
+ root: rootLocator,
278
+ page: rootLocator.page(),
279
+ resolve,
280
+ column: columnName,
281
+ index: idx
282
+ });
283
+ }),
284
+ getHeaders: () => __awaiter(void 0, void 0, void 0, function* () {
285
+ yield _ensureInitialized();
286
+ return Array.from(_headerMap.keys());
287
+ }),
288
+ getHeaderCell: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
289
+ yield _ensureInitialized();
290
+ const idx = _headerMap.get(columnName);
291
+ if (idx === undefined)
292
+ throw _createColumnError(columnName, _headerMap, 'header cell');
293
+ return resolve(config.headerSelector, rootLocator).nth(idx);
294
+ }),
295
+ reset: () => __awaiter(void 0, void 0, void 0, function* () {
296
+ log("Resetting table...");
297
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
298
+ yield config.onReset(context);
299
+ _hasPaginated = false;
300
+ _headerMap = null;
301
+ _isInitialized = false;
302
+ log("Table reset complete.");
303
+ }),
304
+ revalidate: () => __awaiter(void 0, void 0, void 0, function* () {
305
+ log("Revalidating table structure...");
306
+ _headerMap = null; // Clear the map to force re-scanning
307
+ yield _getMap(); // Re-scan headers
308
+ log("Table revalidated.");
309
+ }),
310
+ getColumnValues: (column, options) => __awaiter(void 0, void 0, void 0, function* () {
311
+ var _a, _b;
312
+ yield _ensureInitialized();
313
+ const colIdx = _headerMap.get(column);
314
+ if (colIdx === undefined)
315
+ throw _createColumnError(column, _headerMap);
316
+ const mapper = (_a = options === null || options === void 0 ? void 0 : options.mapper) !== null && _a !== void 0 ? _a : ((c) => c.innerText());
317
+ const effectiveMaxPages = (_b = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _b !== void 0 ? _b : config.maxPages;
318
+ let currentPage = 1;
319
+ const results = [];
320
+ log(`Getting column values for '${column}' (Pages: ${effectiveMaxPages})`);
321
+ while (true) {
322
+ const rows = yield resolve(config.rowSelector, rootLocator).all();
323
+ for (const row of rows) {
324
+ const cell = typeof config.cellSelector === 'string'
325
+ ? row.locator(config.cellSelector).nth(colIdx)
326
+ : resolve(config.cellSelector, row).nth(colIdx);
327
+ results.push(yield mapper(cell));
328
+ }
329
+ if (currentPage < effectiveMaxPages) {
330
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
331
+ if (yield config.strategies.pagination(context)) {
332
+ _hasPaginated = true;
333
+ currentPage++;
334
+ continue;
335
+ }
336
+ }
337
+ break;
338
+ }
339
+ return results;
340
+ }),
341
+ getRow: (filters, options = { exact: false }) => {
342
+ if (!_isInitialized || !_headerMap)
343
+ throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
344
+ const allRows = resolve(config.rowSelector, rootLocator);
345
+ const matchedRows = filterEngine.applyFilters(allRows, filters, _headerMap, options.exact || false, rootLocator.page());
346
+ const rowLocator = matchedRows.first();
347
+ return _makeSmart(rowLocator, _headerMap, 0); // fallback index 0
348
+ },
349
+ getRowByIndex: (index, options = {}) => {
350
+ if (!_isInitialized || !_headerMap)
351
+ throw new Error('Table not initialized. Call await table.init() first, or use async methods like table.findRow() or table.getRows() which auto-initialize.');
352
+ const rowIndex = index - 1; // Convert 1-based to 0-based
353
+ const rowLocator = resolve(config.rowSelector, rootLocator).nth(rowIndex);
354
+ return _makeSmart(rowLocator, _headerMap, rowIndex);
355
+ },
356
+ findRow: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
357
+ (0, debugUtils_1.logDebug)(config, 'info', 'Searching for row', filters);
358
+ yield _ensureInitialized();
359
+ let row = yield _findRowLocator(filters, options);
360
+ if (row) {
361
+ (0, debugUtils_1.logDebug)(config, 'info', 'Row found');
362
+ yield (0, debugUtils_1.debugDelay)(config, 'findRow');
363
+ return _makeSmart(row, _headerMap, 0);
364
+ }
365
+ (0, debugUtils_1.logDebug)(config, 'error', 'Row not found', filters);
366
+ yield (0, debugUtils_1.debugDelay)(config, 'findRow');
367
+ // Return sentinel row
368
+ row = resolve(config.rowSelector, rootLocator).filter({ hasText: "___SENTINEL_ROW_NOT_FOUND___" + Date.now() });
369
+ return _makeSmart(row, _headerMap, 0);
370
+ }),
371
+ getRows: (options) => __awaiter(void 0, void 0, void 0, function* () {
372
+ var _a;
373
+ yield _ensureInitialized();
374
+ let rowLocators = resolve(config.rowSelector, rootLocator);
375
+ if (options === null || options === void 0 ? void 0 : options.filter) {
376
+ rowLocators = filterEngine.applyFilters(rowLocators, options.filter, _headerMap, options.exact || false, rootLocator.page());
377
+ }
378
+ const allRowLocs = yield rowLocators.all();
379
+ const smartRows = [];
380
+ const isRowLoading = (_a = config.strategies.loading) === null || _a === void 0 ? void 0 : _a.isRowLoading;
381
+ for (let i = 0; i < allRowLocs.length; i++) {
382
+ const smartRow = _makeSmart(allRowLocs[i], _headerMap, i);
383
+ if (isRowLoading) {
384
+ const loading = yield isRowLoading(smartRow);
385
+ if (loading)
386
+ continue;
387
+ }
388
+ smartRows.push(smartRow);
389
+ }
390
+ return (0, smartRowArray_1.createSmartRowArray)(smartRows);
391
+ }),
392
+ findRows: (filters, options) => __awaiter(void 0, void 0, void 0, function* () {
393
+ var _a, _b, _c, _d, _e;
394
+ yield _ensureInitialized();
395
+ const allRows = [];
396
+ const effectiveMaxPages = (_b = (_a = options === null || options === void 0 ? void 0 : options.maxPages) !== null && _a !== void 0 ? _a : config.maxPages) !== null && _b !== void 0 ? _b : Infinity;
397
+ let pageCount = 0;
398
+ // Collect rows from current page
399
+ let rowLocators = resolve(config.rowSelector, rootLocator);
400
+ rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_c = options === null || options === void 0 ? void 0 : options.exact) !== null && _c !== void 0 ? _c : false, rootLocator.page());
401
+ let currentRows = yield rowLocators.all();
402
+ const isRowLoading = (_d = config.strategies.loading) === null || _d === void 0 ? void 0 : _d.isRowLoading;
403
+ for (let i = 0; i < currentRows.length; i++) {
404
+ const smartRow = _makeSmart(currentRows[i], _headerMap, i);
405
+ if (isRowLoading && (yield isRowLoading(smartRow)))
406
+ continue;
407
+ allRows.push(smartRow);
408
+ }
409
+ // Paginate and collect more rows
410
+ while (pageCount < effectiveMaxPages && config.strategies.pagination) {
411
+ const paginationResult = yield config.strategies.pagination({
412
+ root: rootLocator,
413
+ config,
414
+ resolve,
415
+ page: rootLocator.page()
416
+ });
417
+ const didPaginate = (0, validation_1.validatePaginationResult)(paginationResult, 'Pagination Strategy');
418
+ if (!didPaginate)
419
+ break;
420
+ pageCount++;
421
+ _hasPaginated = true;
422
+ // Collect rows from new page
423
+ rowLocators = resolve(config.rowSelector, rootLocator);
424
+ rowLocators = filterEngine.applyFilters(rowLocators, filters, _headerMap, (_e = options === null || options === void 0 ? void 0 : options.exact) !== null && _e !== void 0 ? _e : false, rootLocator.page());
425
+ const newRows = yield rowLocators.all();
426
+ for (let i = 0; i < newRows.length; i++) {
427
+ const smartRow = _makeSmart(newRows[i], _headerMap, i);
428
+ if (isRowLoading && (yield isRowLoading(smartRow)))
429
+ continue;
430
+ allRows.push(smartRow);
431
+ }
432
+ }
433
+ if (options === null || options === void 0 ? void 0 : options.asJSON) {
434
+ return Promise.all(allRows.map(r => r.toJSON()));
435
+ }
436
+ return allRows;
437
+ }),
438
+ isInitialized: () => {
439
+ return _isInitialized;
440
+ },
441
+ sorting: {
442
+ apply: (columnName, direction) => __awaiter(void 0, void 0, void 0, function* () {
443
+ yield _ensureInitialized();
444
+ if (!config.strategies.sorting)
445
+ throw new Error('No sorting strategy has been configured.');
446
+ log(`Applying sort for column "${columnName}" (${direction})`);
447
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
448
+ yield config.strategies.sorting.doSort({ columnName, direction, context });
449
+ }),
450
+ getState: (columnName) => __awaiter(void 0, void 0, void 0, function* () {
451
+ yield _ensureInitialized();
452
+ if (!config.strategies.sorting)
453
+ throw new Error('No sorting strategy has been configured.');
454
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
455
+ return config.strategies.sorting.getSortState({ columnName, context });
456
+ })
457
+ },
458
+ iterateThroughTable: (callback, options) => __awaiter(void 0, void 0, void 0, function* () {
459
+ var _a, _b, _c, _d, _e, _f, _g;
460
+ yield _ensureInitialized();
461
+ const paginationStrategy = (_a = options === null || options === void 0 ? void 0 : options.pagination) !== null && _a !== void 0 ? _a : config.strategies.pagination;
462
+ const hasPaginationInOptions = (options === null || options === void 0 ? void 0 : options.pagination) !== undefined;
463
+ if (!hasPaginationInOptions && !hasPaginationInConfig)
464
+ throw new Error('No pagination strategy provided.');
465
+ yield result.reset();
466
+ yield result.init();
467
+ const restrictedTable = {
468
+ init: result.init,
469
+ getHeaders: result.getHeaders,
470
+ getHeaderCell: result.getHeaderCell,
471
+ getRow: result.getRow,
472
+ getRowByIndex: result.getRowByIndex,
473
+ findRow: result.findRow,
474
+ getRows: result.getRows,
475
+ findRows: result.findRows,
476
+ getColumnValues: result.getColumnValues,
477
+ isInitialized: result.isInitialized,
478
+ sorting: result.sorting,
479
+ scrollToColumn: result.scrollToColumn,
480
+ revalidate: result.revalidate,
481
+ generateConfigPrompt: result.generateConfigPrompt,
482
+ };
483
+ const getIsFirst = (_b = options === null || options === void 0 ? void 0 : options.getIsFirst) !== null && _b !== void 0 ? _b : (({ index }) => index === 0);
484
+ const getIsLast = (_c = options === null || options === void 0 ? void 0 : options.getIsLast) !== null && _c !== void 0 ? _c : (() => false);
485
+ const allData = [];
486
+ const effectiveMaxIterations = (_d = options === null || options === void 0 ? void 0 : options.maxIterations) !== null && _d !== void 0 ? _d : config.maxPages;
487
+ const batchSize = options === null || options === void 0 ? void 0 : options.batchSize;
488
+ const isBatching = batchSize !== undefined && batchSize > 1;
489
+ const autoFlatten = (_e = options === null || options === void 0 ? void 0 : options.autoFlatten) !== null && _e !== void 0 ? _e : false;
490
+ let index = 0;
491
+ let paginationResult = true;
492
+ let seenKeys = null;
493
+ let batchRows = [];
494
+ let batchStartIndex = 0;
495
+ log(`Starting iterateThroughTable (maxIterations: ${effectiveMaxIterations}, batchSize: ${batchSize !== null && batchSize !== void 0 ? batchSize : 'none'})`);
496
+ while (index < effectiveMaxIterations) {
497
+ const rowLocators = yield resolve(config.rowSelector, rootLocator).all();
498
+ const smartRowsArray = [];
499
+ const isRowLoading = (_f = config.strategies.loading) === null || _f === void 0 ? void 0 : _f.isRowLoading;
500
+ for (let i = 0; i < rowLocators.length; i++) {
501
+ const smartRow = _makeSmart(rowLocators[i], _headerMap, i);
502
+ if (isRowLoading && (yield isRowLoading(smartRow)))
503
+ continue;
504
+ smartRowsArray.push(smartRow);
505
+ }
506
+ let rows = (0, smartRowArray_1.createSmartRowArray)(smartRowsArray);
507
+ const dedupeStrategy = (_g = options === null || options === void 0 ? void 0 : options.dedupeStrategy) !== null && _g !== void 0 ? _g : config.strategies.dedupe;
508
+ if (dedupeStrategy && rows.length > 0) {
509
+ if (!seenKeys)
510
+ seenKeys = new Set();
511
+ const deduplicated = [];
512
+ for (const row of rows) {
513
+ const key = yield dedupeStrategy(row);
514
+ if (!seenKeys.has(key)) {
515
+ seenKeys.add(key);
516
+ deduplicated.push(row);
517
+ }
518
+ }
519
+ rows = (0, smartRowArray_1.createSmartRowArray)(deduplicated);
520
+ log(`Deduplicated ${rowLocators.length} rows to ${rows.length} unique rows (total seen: ${seenKeys.size})`);
521
+ }
522
+ // Add rows to batch if batching is enabled
523
+ if (isBatching) {
524
+ batchRows.push(...rows);
525
+ }
526
+ const isLastIteration = index === effectiveMaxIterations - 1;
527
+ // Determine if we should invoke the callback
528
+ const batchComplete = isBatching && (index - batchStartIndex + 1) >= batchSize;
529
+ const shouldInvokeCallback = !isBatching || batchComplete || isLastIteration;
530
+ if (shouldInvokeCallback) {
531
+ const callbackRows = isBatching ? batchRows : rows;
532
+ const callbackIndex = isBatching ? batchStartIndex : index;
533
+ const isFirst = getIsFirst({ index: callbackIndex });
534
+ let isLast = getIsLast({ index: callbackIndex, paginationResult });
535
+ const isLastDueToMax = index === effectiveMaxIterations - 1;
536
+ if (isFirst && (options === null || options === void 0 ? void 0 : options.beforeFirst)) {
537
+ yield options.beforeFirst({ index: callbackIndex, rows: callbackRows, allData });
538
+ }
539
+ const batchInfo = isBatching ? {
540
+ startIndex: batchStartIndex,
541
+ endIndex: index,
542
+ size: index - batchStartIndex + 1
543
+ } : undefined;
544
+ const returnValue = yield callback({
545
+ index: callbackIndex,
546
+ isFirst,
547
+ isLast,
548
+ rows: (0, smartRowArray_1.createSmartRowArray)(callbackRows),
549
+ allData,
550
+ table: restrictedTable,
551
+ batchInfo
552
+ });
553
+ if (autoFlatten && Array.isArray(returnValue)) {
554
+ allData.push(...returnValue);
555
+ }
556
+ else {
557
+ allData.push(returnValue);
558
+ }
559
+ // Determine if this is truly the last iteration
560
+ let finalIsLast = isLastDueToMax;
561
+ if (!isLastIteration) {
562
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
563
+ paginationResult = yield paginationStrategy(context);
564
+ (0, debugUtils_1.logDebug)(config, 'info', `Pagination ${paginationResult ? 'succeeded' : 'failed'}`);
565
+ yield (0, debugUtils_1.debugDelay)(config, 'pagination');
566
+ finalIsLast = getIsLast({ index: callbackIndex, paginationResult }) || !paginationResult;
567
+ }
568
+ if (finalIsLast && (options === null || options === void 0 ? void 0 : options.afterLast)) {
569
+ yield options.afterLast({ index: callbackIndex, rows: callbackRows, allData });
570
+ }
571
+ if (finalIsLast || !paginationResult) {
572
+ log(`Reached last iteration (index: ${index}, paginationResult: ${paginationResult})`);
573
+ break;
574
+ }
575
+ // Reset batch
576
+ if (isBatching) {
577
+ batchRows = [];
578
+ batchStartIndex = index + 1;
579
+ }
580
+ }
581
+ else {
582
+ // Continue paginating even when batching
583
+ const context = { root: rootLocator, config, page: rootLocator.page(), resolve };
584
+ paginationResult = yield paginationStrategy(context);
585
+ (0, debugUtils_1.logDebug)(config, 'info', `Pagination ${paginationResult ? 'succeeded' : 'failed'} (batching mode)`);
586
+ yield (0, debugUtils_1.debugDelay)(config, 'pagination');
587
+ if (!paginationResult) {
588
+ // Pagination failed, invoke callback with current batch
589
+ const callbackIndex = batchStartIndex;
590
+ const isFirst = getIsFirst({ index: callbackIndex });
591
+ const isLast = true;
592
+ if (isFirst && (options === null || options === void 0 ? void 0 : options.beforeFirst)) {
593
+ yield options.beforeFirst({ index: callbackIndex, rows: batchRows, allData });
594
+ }
595
+ const batchInfo = {
596
+ startIndex: batchStartIndex,
597
+ endIndex: index,
598
+ size: index - batchStartIndex + 1
599
+ };
600
+ const returnValue = yield callback({
601
+ index: callbackIndex,
602
+ isFirst,
603
+ isLast,
604
+ rows: (0, smartRowArray_1.createSmartRowArray)(batchRows),
605
+ allData,
606
+ table: restrictedTable,
607
+ batchInfo
608
+ });
609
+ if (autoFlatten && Array.isArray(returnValue)) {
610
+ allData.push(...returnValue);
611
+ }
612
+ else {
613
+ allData.push(returnValue);
614
+ }
615
+ if (options === null || options === void 0 ? void 0 : options.afterLast) {
616
+ yield options.afterLast({ index: callbackIndex, rows: batchRows, allData });
617
+ }
618
+ log(`Pagination failed mid-batch (index: ${index})`);
619
+ break;
620
+ }
621
+ }
622
+ index++;
623
+ log(`Iteration ${index} completed, continuing...`);
624
+ }
625
+ log(`iterateThroughTable completed after ${index + 1} iterations, collected ${allData.length} items`);
626
+ return allData;
627
+ }),
628
+ generateConfigPrompt: (options) => __awaiter(void 0, void 0, void 0, function* () {
629
+ const html = yield _getCleanHtml(rootLocator);
630
+ const separator = "=".repeat(50);
631
+ 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`;
632
+ yield _handlePrompt('Smart Table Config', content, options);
633
+ }),
634
+ };
635
+ finalTable = result;
636
+ return result;
637
+ };
638
+ exports.useTable = useTable;
639
+ exports.PaginationStrategies = Object.assign({}, pagination_1.PaginationStrategies);
640
+ exports.LoadingStrategies = loading_1.LoadingStrategies;
641
+ exports.SortingStrategies = sorting_1.SortingStrategies;
642
+ exports.DedupeStrategies = dedupe_1.DedupeStrategies;
@@ -0,0 +1,17 @@
1
+ import type { FinalTableConfig } from '../types';
2
+ /**
3
+ * Get delay for specific action type
4
+ */
5
+ export declare function getDebugDelay(config: FinalTableConfig, actionType: 'pagination' | 'getCell' | 'findRow' | 'default'): number;
6
+ /**
7
+ * Add debug delay for specific action type
8
+ */
9
+ export declare function debugDelay(config: FinalTableConfig, actionType: 'pagination' | 'getCell' | 'findRow' | 'default'): Promise<void>;
10
+ /**
11
+ * Log debug message based on log level
12
+ */
13
+ export declare function logDebug(config: FinalTableConfig, level: 'error' | 'info' | 'verbose', message: string, data?: any): void;
14
+ /**
15
+ * Warn if debug.slow is enabled in CI environment
16
+ */
17
+ export declare function warnIfDebugInCI(config: FinalTableConfig): void;