teamshare-bridge 0.26.13 → 0.26.14
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/lib/browser/batch.d.ts +15 -0
- package/dist/lib/browser/batch.js +42 -0
- package/dist/lib/browser/batch.js.map +1 -1
- package/dist/lib/browser/cdp.d.ts +9 -0
- package/dist/lib/browser/cdp.js +28 -0
- package/dist/lib/browser/cdp.js.map +1 -1
- package/dist/lib/browser/network-log.d.ts +31 -0
- package/dist/lib/browser/network-log.js +94 -12
- package/dist/lib/browser/network-log.js.map +1 -1
- package/dist/lib/browser/page-context.d.ts +22 -0
- package/dist/lib/browser/page-context.js +167 -0
- package/dist/lib/browser/page-context.js.map +1 -1
- package/dist/lib/browser/session.d.ts +59 -3
- package/dist/lib/browser/session.js +275 -5
- package/dist/lib/browser/session.js.map +1 -1
- package/dist/lib/orchestrator/handlers/browser.js +100 -0
- package/dist/lib/orchestrator/handlers/browser.js.map +1 -1
- package/package.json +1 -1
|
@@ -43,6 +43,159 @@ const MAX_ERRORS = 8;
|
|
|
43
43
|
exports.MAX_ERRORS = MAX_ERRORS;
|
|
44
44
|
const MAX_TOASTS = 10;
|
|
45
45
|
exports.MAX_TOASTS = MAX_TOASTS;
|
|
46
|
+
/**
|
|
47
|
+
* In-page pagination detector (2026-10), shared by the full and compact state
|
|
48
|
+
* scripts. It reads the pager controls, page-size selector, page indicator,
|
|
49
|
+
* row count and total, so the agent can traverse a table instead of guessing.
|
|
50
|
+
*/
|
|
51
|
+
function paginationDetectSource() {
|
|
52
|
+
return `
|
|
53
|
+
function detectPagination(scope) {
|
|
54
|
+
const clean = (v, cap) => String(v == null ? '' : v)
|
|
55
|
+
.replace(/\\s+/g, ' ')
|
|
56
|
+
.trim()
|
|
57
|
+
.slice(0, cap || 200);
|
|
58
|
+
const vis = (el) => {
|
|
59
|
+
if (!el || el.nodeType !== 1) return false;
|
|
60
|
+
const r = el.getBoundingClientRect();
|
|
61
|
+
if (r.width === 0 || r.height === 0) return false;
|
|
62
|
+
const cs = getComputedStyle(el);
|
|
63
|
+
return cs.visibility !== 'hidden' && cs.display !== 'none' && cs.opacity !== '0';
|
|
64
|
+
};
|
|
65
|
+
const rect = (el) => {
|
|
66
|
+
const r = el.getBoundingClientRect();
|
|
67
|
+
return { x: Math.round(r.x + r.width / 2), y: Math.round(r.y + r.height / 2) };
|
|
68
|
+
};
|
|
69
|
+
const controls = [];
|
|
70
|
+
const candidates = [...scope.querySelectorAll('button,a,[role="button"]')]
|
|
71
|
+
.filter(vis)
|
|
72
|
+
.slice(0, 200);
|
|
73
|
+
for (const el of candidates) {
|
|
74
|
+
const label = clean(
|
|
75
|
+
el.getAttribute('aria-label') ||
|
|
76
|
+
el.getAttribute('title') ||
|
|
77
|
+
el.innerText ||
|
|
78
|
+
el.textContent ||
|
|
79
|
+
'',
|
|
80
|
+
60,
|
|
81
|
+
);
|
|
82
|
+
const cls = String(el.className || '');
|
|
83
|
+
const aria = String(el.getAttribute('aria-current') || '');
|
|
84
|
+
const disabled = el.disabled === true ||
|
|
85
|
+
el.getAttribute('aria-disabled') === 'true' ||
|
|
86
|
+
/(?:^|[\\s_-])disabled(?:$|[\\s_-])/i.test(cls);
|
|
87
|
+
const pageMatch = label.match(/^(?:page\\s*)?(\\d{1,5})$/i);
|
|
88
|
+
let kind = null;
|
|
89
|
+
if (/^(next|next page|›|»|older|forward)$/i.test(label) || /next page/i.test(label)) kind = 'next';
|
|
90
|
+
else if (/^(prev|prev(ious)?|‹|«|newer|back)$/i.test(label) || /previous page/i.test(label)) kind = 'prev';
|
|
91
|
+
else if (/load more|show more|more results|see more/i.test(label)) kind = 'loadMore';
|
|
92
|
+
else if (pageMatch) kind = 'page';
|
|
93
|
+
if (!kind) continue;
|
|
94
|
+
controls.push({
|
|
95
|
+
label,
|
|
96
|
+
kind,
|
|
97
|
+
...(pageMatch ? { page: Number(pageMatch[1]) } : {}),
|
|
98
|
+
disabled,
|
|
99
|
+
current: aria === 'page' || /(?:^|[\\s_-])current(?:$|[\\s_-])/i.test(cls),
|
|
100
|
+
...rect(el),
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let pageSize;
|
|
105
|
+
let pageSizeOptions = [];
|
|
106
|
+
for (const sel of [...scope.querySelectorAll('select')].filter(vis)) {
|
|
107
|
+
const opts = [...sel.options]
|
|
108
|
+
.map((o) => Number(String(o.textContent || o.label || '').trim()))
|
|
109
|
+
.filter((n) => Number.isFinite(n) && n > 0);
|
|
110
|
+
if (!opts.length) continue;
|
|
111
|
+
pageSize = Number(sel.value) || opts[0];
|
|
112
|
+
pageSizeOptions = [...new Set(opts)].sort((a, b) => a - b);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const rows = [...scope.querySelectorAll('tbody tr,[role="row"]')]
|
|
117
|
+
.filter(vis)
|
|
118
|
+
.filter((r) => r.querySelectorAll('td').length > 0 || !r.querySelector('th'));
|
|
119
|
+
const rowCount = rows.length;
|
|
120
|
+
|
|
121
|
+
const pager = scope.querySelector(
|
|
122
|
+
'nav[aria-label*="pagination" i],[class*="pagination"],[class*="pager"],[role="navigation"]',
|
|
123
|
+
) || scope;
|
|
124
|
+
const text = clean(pager.innerText || pager.textContent || '', 3000);
|
|
125
|
+
let page;
|
|
126
|
+
let total;
|
|
127
|
+
let totalPages;
|
|
128
|
+
let m = text.match(/showing\\s+(\\d+)\\s*(?:to|-|–)\\s*(\\d+)\\s+of\\s+(\\d+)/i);
|
|
129
|
+
if (m) {
|
|
130
|
+
const start = Number(m[1]);
|
|
131
|
+
const end = Number(m[2]);
|
|
132
|
+
total = Number(m[3]);
|
|
133
|
+
pageSize = pageSize || Math.max(1, end - start + 1);
|
|
134
|
+
page = Math.floor((start - 1) / pageSize) + 1;
|
|
135
|
+
}
|
|
136
|
+
if (!total) {
|
|
137
|
+
m = text.match(/(\\d+)\\s*(?:-|–|to)\\s*(\\d+)\\s+of\\s+(\\d+)/i);
|
|
138
|
+
if (m) {
|
|
139
|
+
const start = Number(m[1]);
|
|
140
|
+
const end = Number(m[2]);
|
|
141
|
+
total = Number(m[3]);
|
|
142
|
+
pageSize = pageSize || Math.max(1, end - start + 1);
|
|
143
|
+
page = page || Math.floor((start - 1) / pageSize) + 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!total) {
|
|
147
|
+
m = text.match(/(\\d+)\\s+results?\\b/i);
|
|
148
|
+
if (m) total = Number(m[1]);
|
|
149
|
+
}
|
|
150
|
+
if (!page) {
|
|
151
|
+
m = text.match(/page\\s+(\\d+)\\s+of\\s+(\\d+)/i);
|
|
152
|
+
if (m) {
|
|
153
|
+
page = Number(m[1]);
|
|
154
|
+
totalPages = Number(m[2]);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (!page) {
|
|
158
|
+
const currentPage = controls.find((c) => c.kind === 'page' && c.current);
|
|
159
|
+
if (currentPage) page = currentPage.page;
|
|
160
|
+
}
|
|
161
|
+
if (!totalPages && total && pageSize) totalPages = Math.ceil(total / pageSize);
|
|
162
|
+
|
|
163
|
+
const next = controls.find((c) => c.kind === 'next');
|
|
164
|
+
const prev = controls.find((c) => c.kind === 'prev');
|
|
165
|
+
const loadMore = controls.find((c) => c.kind === 'loadMore');
|
|
166
|
+
const hasNext = !!(
|
|
167
|
+
(next && !next.disabled) ||
|
|
168
|
+
loadMore ||
|
|
169
|
+
(totalPages && page && page < totalPages)
|
|
170
|
+
);
|
|
171
|
+
const hasPrev = !!(
|
|
172
|
+
(prev && !prev.disabled) ||
|
|
173
|
+
(page && page > 1)
|
|
174
|
+
);
|
|
175
|
+
const kind = loadMore
|
|
176
|
+
? 'load-more'
|
|
177
|
+
: controls.some((c) => c.kind === 'page')
|
|
178
|
+
? 'numbered'
|
|
179
|
+
: next || prev
|
|
180
|
+
? 'prev-next'
|
|
181
|
+
: scope.querySelector('[class*="infinite"],[data-infinite]')
|
|
182
|
+
? 'infinite'
|
|
183
|
+
: 'none';
|
|
184
|
+
return {
|
|
185
|
+
kind,
|
|
186
|
+
...(page ? { page } : {}),
|
|
187
|
+
...(pageSize ? { pageSize } : {}),
|
|
188
|
+
...(total ? { total } : {}),
|
|
189
|
+
...(totalPages ? { totalPages } : {}),
|
|
190
|
+
hasNext,
|
|
191
|
+
hasPrev,
|
|
192
|
+
rowCount,
|
|
193
|
+
pageSizeOptions,
|
|
194
|
+
controls: controls.slice(0, 20),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
`;
|
|
198
|
+
}
|
|
46
199
|
/**
|
|
47
200
|
* Selectors that surface user-visible errors/alerts/validation messages.
|
|
48
201
|
* Deliberately broad (sites use every naming scheme) but every match is
|
|
@@ -84,6 +237,7 @@ function buildPageStateScript(opts = {}) {
|
|
|
84
237
|
const MAX_TOASTS = ${MAX_TOASTS};
|
|
85
238
|
const INCLUDE_TEXT = ${includeText ? 'true' : 'false'};
|
|
86
239
|
const ERROR_SEL = ${errorSelectors};
|
|
240
|
+
const detectPagination = ${paginationDetectSource()};
|
|
87
241
|
|
|
88
242
|
const clean = (v, cap) => String(v == null ? '' : v)
|
|
89
243
|
.replace(/\\s+/g, ' ')
|
|
@@ -230,6 +384,7 @@ function buildPageStateScript(opts = {}) {
|
|
|
230
384
|
const top = loginTop ?? (dialogs.length ? dialogs[dialogs.length - 1] : null);
|
|
231
385
|
const topEl = dialogEls[top ? top.index : -1] || null;
|
|
232
386
|
const scope = topEl || document;
|
|
387
|
+
const pagination = detectPagination(scope);
|
|
233
388
|
|
|
234
389
|
const fields = (top ? top.fields : [...document.querySelectorAll('input,textarea,select')]
|
|
235
390
|
.slice(0, 200)
|
|
@@ -305,6 +460,7 @@ function buildPageStateScript(opts = {}) {
|
|
|
305
460
|
toasts: toasts,
|
|
306
461
|
busy: busy,
|
|
307
462
|
scroll: { x: Math.round(window.scrollX || 0), y: Math.round(window.scrollY || 0) },
|
|
463
|
+
pagination: pagination,
|
|
308
464
|
text: text,
|
|
309
465
|
textTruncated: textTruncated,
|
|
310
466
|
maxTextChars: MAX_TEXT,
|
|
@@ -326,6 +482,14 @@ function emptyState() {
|
|
|
326
482
|
toasts: [],
|
|
327
483
|
busy: false,
|
|
328
484
|
scroll: { x: 0, y: 0 },
|
|
485
|
+
pagination: {
|
|
486
|
+
kind: 'none',
|
|
487
|
+
hasNext: false,
|
|
488
|
+
hasPrev: false,
|
|
489
|
+
rowCount: 0,
|
|
490
|
+
pageSizeOptions: [],
|
|
491
|
+
controls: [],
|
|
492
|
+
},
|
|
329
493
|
text: '',
|
|
330
494
|
textTruncated: false,
|
|
331
495
|
maxTextChars: DEFAULT_TEXT_CAP,
|
|
@@ -372,6 +536,7 @@ function compactPageState(state) {
|
|
|
372
536
|
toasts: state.toasts.slice(-4),
|
|
373
537
|
busy: state.busy,
|
|
374
538
|
scroll: state.scroll,
|
|
539
|
+
pagination: state.pagination,
|
|
375
540
|
};
|
|
376
541
|
}
|
|
377
542
|
/**
|
|
@@ -386,6 +551,7 @@ function buildCompactStateScript() {
|
|
|
386
551
|
const errorSelectors = JSON.stringify('[role="alert"],[aria-invalid="true"],[class*="field-error"],[class*="form-error"],[class*="error-message"],[class*="invalid-feedback"],[data-sonner-toast]');
|
|
387
552
|
return `
|
|
388
553
|
(() => {
|
|
554
|
+
const detectPagination = ${paginationDetectSource()};
|
|
389
555
|
const clean = (v, cap) => String(v == null ? '' : v).replace(/\\s+/g, ' ').trim().slice(0, cap || 200);
|
|
390
556
|
const vis = (el) => {
|
|
391
557
|
if (!el || el.nodeType !== 1) return false;
|
|
@@ -499,6 +665,7 @@ function buildCompactStateScript() {
|
|
|
499
665
|
toasts: (window.__tsPageEvents || []).slice(-4),
|
|
500
666
|
busy: [...document.querySelectorAll('[aria-busy="true"],[data-loading="true"],[class*="loading"],[class*="spinner"]')].some(vis),
|
|
501
667
|
scroll: { x: Math.round(window.scrollX || 0), y: Math.round(window.scrollY || 0) },
|
|
668
|
+
pagination: detectPagination(topEl || document),
|
|
502
669
|
};
|
|
503
670
|
})()
|
|
504
671
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"page-context.js","sourceRoot":"","sources":["../../../src/lib/browser/page-context.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"page-context.js","sourceRoot":"","sources":["../../../src/lib/browser/page-context.ts"],"names":[],"mappings":";;;AAuUA,oDA0PC;AA+BD,sCAgBC;AAOD,4CAsBC;AAUD,0DA4HC;AAGD,4CAWC;AAGD,kDA8DC;AA2CD,oDA+CC;AAGD,8CAWC;AA18BD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,+BAAiD;AAoHjD,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAi0B0B,4CAAgB;AAh0B1E,MAAM,UAAU,GAAG,EAAE,CAAC;AAg0Bb,gCAAU;AA/zBnB,MAAM,WAAW,GAAG,CAAC,CAAC;AA+zBD,kCAAW;AA9zBhC,MAAM,UAAU,GAAG,CAAC,CAAC;AA8zBa,gCAAU;AA7zB5C,MAAM,UAAU,GAAG,EAAE,CAAC;AA6zBwB,gCAAU;AA3zBxD;;;;GAIG;AACH,SAAS,sBAAsB;IAC7B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiJN,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACU,QAAA,oBAAoB,GAAG;IAClC,gBAAgB;IAChB,iBAAiB;IACjB,uBAAuB;IACvB,kBAAkB;IAClB,kBAAkB;IAClB,mBAAmB;IACnB,oBAAoB;IACpB,oBAAoB;IACpB,wBAAwB;IACxB,uBAAuB;IACvB,6BAA6B;IAC7B,0BAA0B;IAC1B,WAAW;IACX,cAAc;IACd,aAAa;IACb,qBAAqB;CACtB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ;;;GAGG;AACH,SAAgB,oBAAoB,CAAC,OAAyB,EAAE;IAC9D,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,KAAK,KAAK,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CACtB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,gBAAgB,EAAE,GAAG,CAAC,EAC5D,MAAM,CACP,CAAC;IACF,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CACxB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC,EACjD,GAAG,CACJ,CAAC;IACF,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,4BAAoB,CAAC,CAAC;IAC5D,OAAO;;yBAEgB,OAAO;2BACL,SAAS;4BACR,WAAW;2BACZ,UAAU;2BACV,UAAU;6BACR,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;0BACjC,cAAc;iCACP,sBAAsB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqOtD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU;IACjB,OAAO;QACL,GAAG,EAAE,EAAE;QACP,KAAK,EAAE,EAAE;QACT,UAAU,EAAE,SAAS;QACrB,QAAQ,EAAE,EAAE;QACZ,OAAO,EAAE,EAAE;QACX,KAAK,EAAE,KAAK;QACZ,MAAM,EAAE,EAAE;QACV,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;QACV,IAAI,EAAE,KAAK;QACX,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;QACtB,UAAU,EAAE;YACV,IAAI,EAAE,MAAM;YACZ,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,KAAK;YACd,QAAQ,EAAE,CAAC;YACX,eAAe,EAAE,EAAE;YACnB,QAAQ,EAAE,EAAE;SACb;QACD,IAAI,EAAE,EAAE;QACR,aAAa,EAAE,KAAK;QACpB,YAAY,EAAE,gBAAgB;KAC/B,CAAC;AACJ,CAAC;AAED,0EAA0E;AACnE,KAAK,UAAU,aAAa,CACjC,IAAa,EACb,OAAyB,EAAE;IAE3B,MAAM,GAAG,GAAG,MAAM,IAAA,gBAAU,EAAC,IAAI,EAAE,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/D,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,UAAU,EAAE,CAAC;IACzD,MAAM,CAAC,GAAG,GAAyB,CAAC;IACpC,OAAO;QACL,GAAG,UAAU,EAAE;QACf,GAAG,CAAC;QACJ,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;QAClD,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;QAC/C,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;QAC/C,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;QAC/C,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;KACtD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,gBAAgB,CAAC,KAAgB;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAClD,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC1B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;KAC/B,CAAC,CAAC,CAAC;IACJ,OAAO;QACL,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,OAAO,EAAE,OAAO;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,UAAU,EAAE,KAAK,CAAC,UAAU;KAC7B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,uBAAuB;IACrC,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CACnC,4JAA4J,CAC7J,CAAC;IACF,OAAO;;iCAEwB,sBAAsB,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oDA0FL,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2B/D,CAAC;AACJ,CAAC;AAED,+DAA+D;AACxD,KAAK,UAAU,gBAAgB,CACpC,IAAa;IAEb,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,IAAA,gBAAU,EAAC,IAAI,EAAE,uBAAuB,EAAE,CAAC,CAAC;QAC9D,OAAO,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YACnC,CAAC,CAAE,GAA+B;YAClC,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,SAAgB,mBAAmB,CAAC,KAAgB;IAClD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,KAAK,IAAI,SAAS,EAAE,CAAC,CAAC;IACjD,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,GAAG,IAAI,SAAS,EAAE,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM;QACvB,KAAK,CAAC,IAAI,CAAC,aAAa,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACpD,IAAI,GAAG,EAAE,CAAC;QACR,KAAK,CAAC,IAAI,CACR,WAAW,GAAG,CAAC,IAAI,IAAI,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CACjG,CAAC;QACF,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CACR,kBAAkB,GAAG,CAAC,MAAM;iBACzB,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,CACxK;iBACA,IAAI,CAAC,KAAK,CAAC,EAAE,CACjB,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CACR,mBAAmB,GAAG,CAAC,OAAO;iBAC3B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;iBACnE,IAAI,CAAC,KAAK,CAAC,EAAE,CACjB,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CACR,WAAW,KAAK,CAAC,MAAM;aACpB,GAAG,CACF,CAAC,CAAC,EAAE,EAAE,CACJ,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,WAAW,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,GAAG,CACtG;aACA,IAAI,CAAC,KAAK,CAAC,EAAE,CACjB,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,KAAK,CAAC,IAAI,CACR,YAAY,KAAK,CAAC,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,CAAC,WAAW,SAAS,CAC7F,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CACR,qBAAqB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CACnE,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CACR,yBAAyB,KAAK,CAAC,MAAM;aAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;aACnC,IAAI,CAAC,KAAK,CAAC,EAAE,CACjB,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,IAAI;QAAE,KAAK,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;IACpE,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;QACzE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvB,IAAI,KAAK,CAAC,aAAa;YAAE,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAcY,QAAA,eAAe,GAA2B;IACrD,UAAU;IACV,MAAM;IACN,QAAQ;IACR,OAAO;IACP,KAAK;IACL,aAAa;IACb,WAAW;IACX,QAAQ;CACA,CAAC;AAeX;;;;GAIG;AACH,SAAgB,oBAAoB,CAAC,IAAoB;IACvD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/D,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC;QACjB,KAAK,UAAU,CAAC,CAAC,CAAC;YAChB,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;YAChD,OAAO;;4CAE+B,GAAG;;WAEpC,CAAC;QACR,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAC/D,OAAO;;;;4BAIe,MAAM;WACvB,CAAC;QACR,CAAC;QACD,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC;YAC3C,OAAO;;;iBAGI,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;WACtC,CAAC;QACR,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;YAC/D,OAAO;8EACiE,KAAK;;yBAE1D,MAAM;;;WAGpB,CAAC;QACR,CAAC;QACD,KAAK,KAAK,CAAC,CAAC,CAAC;YACX,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;YACtD,OAAO,iCAAiC,MAAM,MAAM,CAAC;QACvD,CAAC;QACD,KAAK,QAAQ;YACX,OAAO,qEAAqE,KAAK,MAAM,CAAC;QAC1F;YACE,OAAO,OAAO,CAAC;IACnB,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,SAAgB,iBAAiB,CAC/B,YAAsC,EACtC,eAAuB,EACvB,gBAAoE,EAAE;IAEtE,OAAO;QACL,GAAG,UAAU,EAAE;QACf,YAAY;QACZ,eAAe;QACf,aAAa;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { CdpPage, ElementInfo, PageInfo, PageText, PageLink } from './cdp';
|
|
2
|
-
import { type NetworkEntry, type ConsoleEntry } from './network-log';
|
|
2
|
+
import { type NetworkEntry, type ConsoleEntry, type WebSocketFrame, type NetworkLogStatus } from './network-log';
|
|
3
3
|
import { type ReadStagedResult, type FillExcelResult } from './xlsx-template';
|
|
4
4
|
import { type LoginResult, type PerformLoginOptions } from './login';
|
|
5
|
-
import { type NativeDialogState, type PageSnapshot, type WaitForKind } from './page-context';
|
|
5
|
+
import { type NativeDialogState, type PagePaginationState, type PageSnapshot, type WaitForKind } from './page-context';
|
|
6
6
|
import { type PageEvent } from './page-events';
|
|
7
7
|
/**
|
|
8
8
|
* Persistent Chrome profile for the AUTO-ANSWER browser (research reads,
|
|
@@ -176,8 +176,62 @@ export declare class BrowserSession {
|
|
|
176
176
|
error?: string;
|
|
177
177
|
}>;
|
|
178
178
|
/**
|
|
179
|
-
*
|
|
179
|
+
* Current pagination state (controls, page, page size, total, rows).
|
|
180
|
+
* Scoped to the top dialog by the page-state script.
|
|
181
|
+
*/
|
|
182
|
+
pagination(): Promise<PagePaginationState>;
|
|
183
|
+
/** Stable-ish signature used to prove a page actually changed. */
|
|
184
|
+
private paginationSignature;
|
|
185
|
+
private waitPaginationChange;
|
|
186
|
+
private clickPagination;
|
|
187
|
+
/** Move to the next page (or Load more) and wait for the content to change. */
|
|
188
|
+
nextPage(timeoutMs?: number): Promise<{
|
|
189
|
+
moved: boolean;
|
|
190
|
+
elapsedMs: number;
|
|
191
|
+
state: PagePaginationState;
|
|
192
|
+
reason?: string;
|
|
193
|
+
}>;
|
|
194
|
+
/** Move to the previous page. */
|
|
195
|
+
prevPage(timeoutMs?: number): Promise<{
|
|
196
|
+
moved: boolean;
|
|
197
|
+
elapsedMs: number;
|
|
198
|
+
state: PagePaginationState;
|
|
199
|
+
reason?: string;
|
|
200
|
+
}>;
|
|
201
|
+
/** Go to a numbered page; walks next if the page control is not rendered. */
|
|
202
|
+
gotoPage(page: number, timeoutMs?: number): Promise<{
|
|
203
|
+
moved: boolean;
|
|
204
|
+
elapsedMs: number;
|
|
205
|
+
state: PagePaginationState;
|
|
206
|
+
reason?: string;
|
|
207
|
+
}>;
|
|
208
|
+
/** Select a native page-size option (custom comboboxes use click + waitFor). */
|
|
209
|
+
setPageSize(size: number): Promise<{
|
|
210
|
+
ok: boolean;
|
|
211
|
+
state: PagePaginationState;
|
|
212
|
+
error?: string;
|
|
213
|
+
}>;
|
|
214
|
+
/**
|
|
215
|
+
* Read the current table (headers + rows), then walk every page and return
|
|
216
|
+
* the deduplicated rows. Bounded by maxPages/maxRows; stops on a repeated
|
|
217
|
+
* page signature, a disabled pager, or an unchanged table.
|
|
180
218
|
*/
|
|
219
|
+
collectTable(opts?: {
|
|
220
|
+
selector?: string;
|
|
221
|
+
maxPages?: number;
|
|
222
|
+
maxRows?: number;
|
|
223
|
+
pageSize?: number;
|
|
224
|
+
}): Promise<{
|
|
225
|
+
headers: string[];
|
|
226
|
+
rows: string[][];
|
|
227
|
+
rowCount: number;
|
|
228
|
+
pagesVisited: number;
|
|
229
|
+
total?: number;
|
|
230
|
+
complete: boolean;
|
|
231
|
+
duplicates: number;
|
|
232
|
+
truncated: boolean;
|
|
233
|
+
stoppedBecause: string;
|
|
234
|
+
}>;
|
|
181
235
|
navigate(url: string): Promise<PageInfo>;
|
|
182
236
|
/**
|
|
183
237
|
* Capture a screenshot. Returns base64-encoded data.
|
|
@@ -196,6 +250,8 @@ export declare class BrowserSession {
|
|
|
196
250
|
}): Promise<{
|
|
197
251
|
requests: NetworkEntry[];
|
|
198
252
|
console: ConsoleEntry[];
|
|
253
|
+
websockets: WebSocketFrame[];
|
|
254
|
+
status: NetworkLogStatus;
|
|
199
255
|
}>;
|
|
200
256
|
/**
|
|
201
257
|
* Click at (x, y) coordinates.
|
|
@@ -397,8 +397,281 @@ class BrowserSession {
|
|
|
397
397
|
}
|
|
398
398
|
}
|
|
399
399
|
/**
|
|
400
|
-
*
|
|
400
|
+
* Current pagination state (controls, page, page size, total, rows).
|
|
401
|
+
* Scoped to the top dialog by the page-state script.
|
|
401
402
|
*/
|
|
403
|
+
async pagination() {
|
|
404
|
+
this.ensureConnected();
|
|
405
|
+
const state = await (0, page_context_1.readPageState)(this.page, { includeText: false });
|
|
406
|
+
return state.pagination;
|
|
407
|
+
}
|
|
408
|
+
/** Stable-ish signature used to prove a page actually changed. */
|
|
409
|
+
async paginationSignature() {
|
|
410
|
+
const state = await (0, page_context_1.readPageState)(this.page, { includeText: false });
|
|
411
|
+
let firstRow = '';
|
|
412
|
+
try {
|
|
413
|
+
firstRow = String(await (0, cdp_1.evaluateJs)(this.page, `(() => {
|
|
414
|
+
const vis = (el) => { if (!el) return false; const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0; };
|
|
415
|
+
const rows = [...document.querySelectorAll('tbody tr,[role="row"]')].filter(vis);
|
|
416
|
+
const row = rows.find((r) => r.querySelectorAll('td').length > 0);
|
|
417
|
+
return row ? String(row.innerText || row.textContent || '').replace(/\\s+/g, ' ').trim().slice(0, 80) : '';
|
|
418
|
+
})()`));
|
|
419
|
+
}
|
|
420
|
+
catch {
|
|
421
|
+
/* navigation / context loss */
|
|
422
|
+
}
|
|
423
|
+
const p = state.pagination;
|
|
424
|
+
return [
|
|
425
|
+
p.page ?? '',
|
|
426
|
+
p.rowCount,
|
|
427
|
+
p.total ?? '',
|
|
428
|
+
firstRow,
|
|
429
|
+
p.controls.map((c) => `${c.kind}:${c.disabled ? 1 : 0}`).join(','),
|
|
430
|
+
].join('|');
|
|
431
|
+
}
|
|
432
|
+
async waitPaginationChange(before, timeoutMs = 8000) {
|
|
433
|
+
const started = Date.now();
|
|
434
|
+
const deadline = started + Math.min(Math.max(timeoutMs, 500), 30_000);
|
|
435
|
+
while (Date.now() < deadline) {
|
|
436
|
+
await new Promise((resolve) => setTimeout(resolve, 220));
|
|
437
|
+
try {
|
|
438
|
+
if ((await this.paginationSignature()) !== before) {
|
|
439
|
+
return {
|
|
440
|
+
moved: true,
|
|
441
|
+
state: await this.pagination(),
|
|
442
|
+
elapsedMs: Date.now() - started,
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
catch {
|
|
447
|
+
/* navigation during the poll - retry */
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
moved: false,
|
|
452
|
+
state: await this.pagination().catch(() => ({
|
|
453
|
+
kind: 'none',
|
|
454
|
+
hasNext: false,
|
|
455
|
+
hasPrev: false,
|
|
456
|
+
rowCount: 0,
|
|
457
|
+
pageSizeOptions: [],
|
|
458
|
+
controls: [],
|
|
459
|
+
})),
|
|
460
|
+
elapsedMs: Date.now() - started,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
async clickPagination(kind, page) {
|
|
464
|
+
const state = await this.pagination();
|
|
465
|
+
const control = state.controls.find((c) => c.kind === kind && (page === undefined || c.page === page)) ??
|
|
466
|
+
(kind === 'next'
|
|
467
|
+
? state.controls.find((c) => c.kind === 'loadMore')
|
|
468
|
+
: undefined);
|
|
469
|
+
if (!control || control.disabled)
|
|
470
|
+
return { clicked: false };
|
|
471
|
+
await (0, cdp_1.clickAt)(this.page, control.x, control.y);
|
|
472
|
+
return { clicked: true, control };
|
|
473
|
+
}
|
|
474
|
+
/** Move to the next page (or Load more) and wait for the content to change. */
|
|
475
|
+
async nextPage(timeoutMs = 8000) {
|
|
476
|
+
this.ensureConnected();
|
|
477
|
+
const before = await this.paginationSignature();
|
|
478
|
+
const clicked = await this.clickPagination('next');
|
|
479
|
+
if (!clicked.clicked) {
|
|
480
|
+
return {
|
|
481
|
+
moved: false,
|
|
482
|
+
elapsedMs: 0,
|
|
483
|
+
state: await this.pagination(),
|
|
484
|
+
reason: 'no enabled next/load-more control',
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const result = await this.waitPaginationChange(before, timeoutMs);
|
|
488
|
+
return {
|
|
489
|
+
...result,
|
|
490
|
+
...(result.moved ? {} : { reason: 'page did not change' }),
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
/** Move to the previous page. */
|
|
494
|
+
async prevPage(timeoutMs = 8000) {
|
|
495
|
+
this.ensureConnected();
|
|
496
|
+
const before = await this.paginationSignature();
|
|
497
|
+
const clicked = await this.clickPagination('prev');
|
|
498
|
+
if (!clicked.clicked) {
|
|
499
|
+
return {
|
|
500
|
+
moved: false,
|
|
501
|
+
elapsedMs: 0,
|
|
502
|
+
state: await this.pagination(),
|
|
503
|
+
reason: 'no enabled previous control',
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
const result = await this.waitPaginationChange(before, timeoutMs);
|
|
507
|
+
return {
|
|
508
|
+
...result,
|
|
509
|
+
...(result.moved ? {} : { reason: 'page did not change' }),
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
/** Go to a numbered page; walks next if the page control is not rendered. */
|
|
513
|
+
async gotoPage(page, timeoutMs = 8000) {
|
|
514
|
+
this.ensureConnected();
|
|
515
|
+
const target = Math.max(1, Math.floor(page));
|
|
516
|
+
const current = await this.pagination();
|
|
517
|
+
if (current.page === target) {
|
|
518
|
+
return {
|
|
519
|
+
moved: false,
|
|
520
|
+
elapsedMs: 0,
|
|
521
|
+
state: current,
|
|
522
|
+
reason: 'already on page',
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
const before = await this.paginationSignature();
|
|
526
|
+
const direct = await this.clickPagination('page', target);
|
|
527
|
+
if (direct.clicked) {
|
|
528
|
+
const result = await this.waitPaginationChange(before, timeoutMs);
|
|
529
|
+
return {
|
|
530
|
+
...result,
|
|
531
|
+
...(result.moved ? {} : { reason: 'page did not change' }),
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
// Walk forward (bounded) when only prev/next controls exist.
|
|
535
|
+
for (let i = 0; i < Math.min(target, 100); i += 1) {
|
|
536
|
+
const next = await this.nextPage(timeoutMs);
|
|
537
|
+
if (!next.moved)
|
|
538
|
+
return { ...next, reason: next.reason ?? 'page did not change' };
|
|
539
|
+
if (next.state.page === target)
|
|
540
|
+
return next;
|
|
541
|
+
}
|
|
542
|
+
return {
|
|
543
|
+
moved: false,
|
|
544
|
+
elapsedMs: 0,
|
|
545
|
+
state: await this.pagination(),
|
|
546
|
+
reason: 'page control not found and the target was not reached',
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
/** Select a native page-size option (custom comboboxes use click + waitFor). */
|
|
550
|
+
async setPageSize(size) {
|
|
551
|
+
this.ensureConnected();
|
|
552
|
+
const before = await this.paginationSignature();
|
|
553
|
+
const changed = await (0, cdp_1.evaluateJs)(this.page, `(() => {
|
|
554
|
+
const vis = (el) => { if (!el) return false; const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0; };
|
|
555
|
+
const target = ${Math.max(1, Math.floor(size))};
|
|
556
|
+
for (const sel of [...document.querySelectorAll('select')].filter(vis)) {
|
|
557
|
+
const options = [...sel.options];
|
|
558
|
+
const option = options.find((o) => Number(String(o.textContent || o.label || '').trim()) === target);
|
|
559
|
+
if (!option) continue;
|
|
560
|
+
const proto = HTMLSelectElement.prototype;
|
|
561
|
+
const desc = Object.getOwnPropertyDescriptor(proto, 'value');
|
|
562
|
+
if (desc && desc.set) desc.set.call(sel, option.value);
|
|
563
|
+
else sel.value = option.value;
|
|
564
|
+
sel.dispatchEvent(new Event('input', { bubbles: true }));
|
|
565
|
+
sel.dispatchEvent(new Event('change', { bubbles: true }));
|
|
566
|
+
return true;
|
|
567
|
+
}
|
|
568
|
+
return false;
|
|
569
|
+
})()`);
|
|
570
|
+
if (changed !== true) {
|
|
571
|
+
return {
|
|
572
|
+
ok: false,
|
|
573
|
+
state: await this.pagination(),
|
|
574
|
+
error: 'page size is not a native select; use getElements + click on the custom combobox',
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
const result = await this.waitPaginationChange(before);
|
|
578
|
+
return {
|
|
579
|
+
ok: true,
|
|
580
|
+
state: result.state,
|
|
581
|
+
...(result.moved ? {} : { error: 'page did not visibly change' }),
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
/**
|
|
585
|
+
* Read the current table (headers + rows), then walk every page and return
|
|
586
|
+
* the deduplicated rows. Bounded by maxPages/maxRows; stops on a repeated
|
|
587
|
+
* page signature, a disabled pager, or an unchanged table.
|
|
588
|
+
*/
|
|
589
|
+
async collectTable(opts) {
|
|
590
|
+
this.ensureConnected();
|
|
591
|
+
if (opts?.pageSize)
|
|
592
|
+
await this.setPageSize(opts.pageSize);
|
|
593
|
+
const maxPages = Math.min(Math.max(opts?.maxPages ?? 50, 1), 200);
|
|
594
|
+
const maxRows = Math.min(Math.max(opts?.maxRows ?? 2000, 1), 10_000);
|
|
595
|
+
const seen = new Set();
|
|
596
|
+
const rows = [];
|
|
597
|
+
let headers = [];
|
|
598
|
+
let duplicates = 0;
|
|
599
|
+
let pagesVisited = 0;
|
|
600
|
+
let stoppedBecause = 'unknown';
|
|
601
|
+
let truncated = false;
|
|
602
|
+
const readTable = async () => {
|
|
603
|
+
try {
|
|
604
|
+
const raw = await (0, cdp_1.evaluateJs)(this.page, `(() => {
|
|
605
|
+
const vis = (el) => { if (!el) return false; const r = el.getBoundingClientRect(); return r.width > 0 && r.height > 0; };
|
|
606
|
+
const selector = ${JSON.stringify(opts?.selector ?? '')};
|
|
607
|
+
let table = selector ? document.querySelector(selector) : null;
|
|
608
|
+
if (!table) {
|
|
609
|
+
const tables = [...document.querySelectorAll('table,[role="table"],[role="grid"]')].filter(vis);
|
|
610
|
+
table = tables.sort((a, b) => b.querySelectorAll('tr,[role="row"]').length - a.querySelectorAll('tr,[role="row"]').length)[0] || null;
|
|
611
|
+
}
|
|
612
|
+
if (!table) return null;
|
|
613
|
+
const headers = [...table.querySelectorAll('thead th,[role="columnheader"]')].map((h) => String(h.innerText || h.textContent || '').replace(/\\s+/g, ' ').trim()).filter(Boolean);
|
|
614
|
+
const bodyRows = [...table.querySelectorAll('tbody tr,[role="row"]')].filter(vis)
|
|
615
|
+
.filter((r) => r.querySelectorAll('td,[role="gridcell"],[role="cell"]').length > 0);
|
|
616
|
+
return {
|
|
617
|
+
headers,
|
|
618
|
+
rows: bodyRows.map((r) => [...r.querySelectorAll('td,[role="gridcell"],[role="cell"]')].map((c) => String(c.innerText || c.textContent || '').replace(/\\s+/g, ' ').trim())),
|
|
619
|
+
};
|
|
620
|
+
})()`);
|
|
621
|
+
if (!raw || typeof raw !== 'object')
|
|
622
|
+
return null;
|
|
623
|
+
return raw;
|
|
624
|
+
}
|
|
625
|
+
catch {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
for (let page = 0; page < maxPages; page += 1) {
|
|
630
|
+
const table = await readTable();
|
|
631
|
+
if (!table) {
|
|
632
|
+
stoppedBecause = 'no table found';
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
pagesVisited += 1;
|
|
636
|
+
if (table.headers.length)
|
|
637
|
+
headers = table.headers;
|
|
638
|
+
for (const row of table.rows) {
|
|
639
|
+
const key = row.join('\u0001');
|
|
640
|
+
if (seen.has(key)) {
|
|
641
|
+
duplicates += 1;
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
seen.add(key);
|
|
645
|
+
rows.push(row);
|
|
646
|
+
if (rows.length >= maxRows) {
|
|
647
|
+
truncated = true;
|
|
648
|
+
stoppedBecause = 'maxRows';
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
if (truncated)
|
|
653
|
+
break;
|
|
654
|
+
const next = await this.nextPage();
|
|
655
|
+
if (!next.moved) {
|
|
656
|
+
stoppedBecause = next.reason ?? 'end of pages';
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
const state = await this.pagination().catch(() => null);
|
|
661
|
+
return {
|
|
662
|
+
headers,
|
|
663
|
+
rows,
|
|
664
|
+
rowCount: rows.length,
|
|
665
|
+
pagesVisited,
|
|
666
|
+
...(state?.total ? { total: state.total } : {}),
|
|
667
|
+
complete: !truncated &&
|
|
668
|
+
(stoppedBecause === 'end of pages' ||
|
|
669
|
+
stoppedBecause === 'no enabled next/load-more control'),
|
|
670
|
+
duplicates,
|
|
671
|
+
truncated,
|
|
672
|
+
stoppedBecause,
|
|
673
|
+
};
|
|
674
|
+
}
|
|
402
675
|
async navigate(url) {
|
|
403
676
|
this.ensureConnected();
|
|
404
677
|
return (0, cdp_1.navigateTo)(this.page, url);
|
|
@@ -408,10 +681,7 @@ class BrowserSession {
|
|
|
408
681
|
*/
|
|
409
682
|
async screenshot(format = 'jpeg', quality = 70) {
|
|
410
683
|
this.ensureConnected();
|
|
411
|
-
|
|
412
|
-
if (!data)
|
|
413
|
-
throw new Error('Screenshot failed');
|
|
414
|
-
return data;
|
|
684
|
+
return (0, cdp_1.captureScreenshotChecked)(this.page, format, quality);
|
|
415
685
|
}
|
|
416
686
|
/**
|
|
417
687
|
* Read the network + console evidence collected for this session (QA).
|