@flrande/bak-extension 0.6.11 → 0.6.12

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.
@@ -30,6 +30,215 @@
30
30
  };
31
31
  }
32
32
 
33
+ // src/dynamic-data-tools.ts
34
+ var DATA_PATTERN = /\b(updated|update|updatedat|asof|timestamp|generated|generatedat|refresh|freshness|latest|last|quote|trade|price|flow|market|time|snapshot|signal)\b/i;
35
+ var CONTRACT_PATTERN = /\b(expiry|expiration|expires|option|contract|strike|maturity|dte|call|put|exercise)\b/i;
36
+ var EVENT_PATTERN = /\b(earnings|event|report|dividend|split|meeting|fomc|release|filing)\b/i;
37
+ var ROW_CANDIDATE_KEYS = ["data", "rows", "results", "items", "records", "entries"];
38
+ function sampleValue(value, depth = 0) {
39
+ if (depth >= 2 || value === null || value === void 0 || typeof value !== "object") {
40
+ if (typeof value === "string") {
41
+ return value.length > 160 ? value.slice(0, 160) : value;
42
+ }
43
+ if (typeof value === "function") {
44
+ return "[Function]";
45
+ }
46
+ return value;
47
+ }
48
+ if (Array.isArray(value)) {
49
+ return value.slice(0, 3).map((item) => sampleValue(item, depth + 1));
50
+ }
51
+ const sampled = {};
52
+ for (const key of Object.keys(value).slice(0, 8)) {
53
+ try {
54
+ sampled[key] = sampleValue(value[key], depth + 1);
55
+ } catch {
56
+ sampled[key] = "[Unreadable]";
57
+ }
58
+ }
59
+ return sampled;
60
+ }
61
+ function estimateSampleSize(value) {
62
+ if (Array.isArray(value)) {
63
+ return value.length;
64
+ }
65
+ if (value && typeof value === "object") {
66
+ return Object.keys(value).length;
67
+ }
68
+ return null;
69
+ }
70
+ function classifyTimestamp(path, value, now = Date.now()) {
71
+ const normalized = path.toLowerCase();
72
+ if (DATA_PATTERN.test(normalized)) {
73
+ return "data";
74
+ }
75
+ if (CONTRACT_PATTERN.test(normalized)) {
76
+ return "contract";
77
+ }
78
+ if (EVENT_PATTERN.test(normalized)) {
79
+ return "event";
80
+ }
81
+ const parsed = Date.parse(value.trim());
82
+ return Number.isFinite(parsed) && parsed > now + 36 * 60 * 60 * 1e3 ? "contract" : "unknown";
83
+ }
84
+ function collectTimestampProbes(value, path, options = {}) {
85
+ const collected = [];
86
+ const now = typeof options.now === "number" ? options.now : Date.now();
87
+ const limit = typeof options.limit === "number" ? Math.max(1, Math.floor(options.limit)) : 16;
88
+ const visit = (candidate, candidatePath, depth) => {
89
+ if (collected.length >= limit) {
90
+ return;
91
+ }
92
+ if (typeof candidate === "string" && candidate.trim().length > 0) {
93
+ const parsed = Date.parse(candidate.trim());
94
+ if (Number.isFinite(parsed)) {
95
+ collected.push({
96
+ path: candidatePath,
97
+ value: candidate,
98
+ category: classifyTimestamp(candidatePath, candidate, now)
99
+ });
100
+ }
101
+ return;
102
+ }
103
+ if (depth >= 3 || candidate === null || candidate === void 0) {
104
+ return;
105
+ }
106
+ if (Array.isArray(candidate)) {
107
+ candidate.slice(0, 3).forEach((entry, index) => visit(entry, `${candidatePath}[${index}]`, depth + 1));
108
+ return;
109
+ }
110
+ if (typeof candidate === "object") {
111
+ Object.keys(candidate).slice(0, 8).forEach((key) => {
112
+ try {
113
+ visit(candidate[key], candidatePath ? `${candidatePath}.${key}` : key, depth + 1);
114
+ } catch {
115
+ }
116
+ });
117
+ }
118
+ };
119
+ visit(value, path, 0);
120
+ return collected;
121
+ }
122
+ function inferSchemaHint(value) {
123
+ const rowsCandidate = extractStructuredRows(value);
124
+ if (rowsCandidate) {
125
+ if (rowsCandidate.rowType === "object") {
126
+ const firstRecord = rowsCandidate.rows.find(
127
+ (row) => typeof row === "object" && row !== null && !Array.isArray(row)
128
+ );
129
+ return {
130
+ kind: "rows-object",
131
+ columns: firstRecord ? Object.keys(firstRecord).slice(0, 12) : []
132
+ };
133
+ }
134
+ if (rowsCandidate.rowType === "array") {
135
+ const firstRow = rowsCandidate.rows.find((row) => Array.isArray(row));
136
+ return {
137
+ kind: "rows-array",
138
+ columns: firstRow ? firstRow.map((_, index) => `Column ${index + 1}`) : []
139
+ };
140
+ }
141
+ }
142
+ if (Array.isArray(value)) {
143
+ return { kind: "array" };
144
+ }
145
+ if (value && typeof value === "object") {
146
+ return {
147
+ kind: "object",
148
+ columns: Object.keys(value).slice(0, 12)
149
+ };
150
+ }
151
+ if (value === null || value === void 0) {
152
+ return null;
153
+ }
154
+ return { kind: "scalar" };
155
+ }
156
+ function extractStructuredRows(value, path = "$") {
157
+ if (Array.isArray(value)) {
158
+ if (value.length === 0) {
159
+ return { rows: value, path, rowType: "object" };
160
+ }
161
+ const first = value.find((item) => item !== null && item !== void 0);
162
+ if (Array.isArray(first)) {
163
+ return { rows: value, path, rowType: "array" };
164
+ }
165
+ if (first && typeof first === "object") {
166
+ return { rows: value, path, rowType: "object" };
167
+ }
168
+ return { rows: value, path, rowType: "scalar" };
169
+ }
170
+ if (!value || typeof value !== "object") {
171
+ return null;
172
+ }
173
+ const record = value;
174
+ for (const key of ROW_CANDIDATE_KEYS) {
175
+ if (Array.isArray(record[key])) {
176
+ return extractStructuredRows(record[key], `${path}.${key}`);
177
+ }
178
+ }
179
+ return null;
180
+ }
181
+ function buildTableIntelligence(input) {
182
+ const signals = [];
183
+ const visibleRowCount = Math.max(0, Math.floor(input.visibleRowCount));
184
+ const estimatedTotalRows = typeof input.estimatedTotalRows === "number" && Number.isFinite(input.estimatedTotalRows) && input.estimatedTotalRows > 0 ? Math.max(visibleRowCount, Math.floor(input.estimatedTotalRows)) : void 0;
185
+ const maxObservedRowIndex = typeof input.maxObservedRowIndex === "number" && Number.isFinite(input.maxObservedRowIndex) ? Math.max(0, Math.floor(input.maxObservedRowIndex)) : void 0;
186
+ const minObservedRowIndex = typeof input.minObservedRowIndex === "number" && Number.isFinite(input.minObservedRowIndex) ? Math.max(0, Math.floor(input.minObservedRowIndex)) : void 0;
187
+ if (input.knownGridKind) {
188
+ signals.push({
189
+ code: "known-grid-kind",
190
+ detail: `Detected ${input.kind} container semantics`
191
+ });
192
+ }
193
+ if (input.hasScrollContainer) {
194
+ signals.push({
195
+ code: "scroll-container",
196
+ detail: "Scrollable container detected for the table region"
197
+ });
198
+ }
199
+ if (input.hasTranslatedRows) {
200
+ signals.push({
201
+ code: "row-transform-offsets",
202
+ detail: "Row transform offsets indicate viewport-based row reuse"
203
+ });
204
+ }
205
+ if (maxObservedRowIndex !== void 0 && maxObservedRowIndex > visibleRowCount) {
206
+ signals.push({
207
+ code: "row-index-gap",
208
+ detail: `Observed row indexes reach ${maxObservedRowIndex} while only ${visibleRowCount} rows are mounted`
209
+ });
210
+ }
211
+ if (estimatedTotalRows !== void 0 && estimatedTotalRows > visibleRowCount) {
212
+ signals.push({
213
+ code: "dom-rows-less-than-expected",
214
+ detail: `Estimated ${estimatedTotalRows} rows with ${visibleRowCount} currently mounted`
215
+ });
216
+ }
217
+ const virtualized = input.hasTranslatedRows || input.hasScrollContainer && (estimatedTotalRows !== void 0 && estimatedTotalRows > visibleRowCount || maxObservedRowIndex !== void 0 && maxObservedRowIndex > visibleRowCount + 1);
218
+ const lazyLoaded = input.hasScrollContainer && !virtualized && estimatedTotalRows !== void 0 && estimatedTotalRows > visibleRowCount;
219
+ const preferredExtractionMode = input.kind === "html" || input.kind === "dataTables" ? "dataSource" : input.hasScrollContainer ? "scroll" : "visibleOnly";
220
+ const completeness = preferredExtractionMode === "dataSource" ? "complete" : estimatedTotalRows !== void 0 && estimatedTotalRows > visibleRowCount ? "partial" : minObservedRowIndex !== void 0 && minObservedRowIndex > 1 ? "partial" : "unknown";
221
+ return {
222
+ virtualized,
223
+ lazyLoaded,
224
+ preferredExtractionMode,
225
+ estimatedTotalRows,
226
+ completeness,
227
+ signals
228
+ };
229
+ }
230
+ function buildExtractionMetadata(mode, rows, intelligence, warnings = [], options = {}) {
231
+ const estimatedTotalRows = intelligence?.estimatedTotalRows;
232
+ const complete = options.limitApplied ? false : mode === "dataSource" ? true : options.reachedEnd === true ? true : intelligence?.completeness === "complete";
233
+ return {
234
+ mode,
235
+ complete: complete === true,
236
+ observedRows: rows.length,
237
+ estimatedTotalRows,
238
+ warnings
239
+ };
240
+ }
241
+
33
242
  // src/privacy.ts
34
243
  var MAX_SAFE_TEXT_LENGTH = 120;
35
244
  var MAX_DEBUG_TEXT_LENGTH = 320;
@@ -1676,6 +1885,111 @@
1676
1885
  function buildTableId(kind, index) {
1677
1886
  return `${kind}:${index + 1}`;
1678
1887
  }
1888
+ function gridRowNodes(grid) {
1889
+ return Array.from(grid.querySelectorAll('[role="row"]')).filter(
1890
+ (row) => row.querySelector('[role="gridcell"], [role="cell"]') !== null
1891
+ );
1892
+ }
1893
+ function tableSchemaFromElement(element) {
1894
+ return element instanceof HTMLTableElement ? htmlTableSchema(element) : gridSchema(element);
1895
+ }
1896
+ function tableRowsFromElement(element, limit) {
1897
+ return element instanceof HTMLTableElement ? htmlTableRows(element, limit) : gridRows(element, limit);
1898
+ }
1899
+ function findScrollableTableContainer(element) {
1900
+ const explicitCandidate = isHtmlElement(element) && (element.matches("[data-bak-scroll-root], [data-virtual-scroll]") ? element : null) || ("querySelector" in element ? element.querySelector("[data-bak-scroll-root], [data-virtual-scroll]") ?? null : null);
1901
+ if (explicitCandidate) {
1902
+ return explicitCandidate;
1903
+ }
1904
+ const candidates = [];
1905
+ if (isHtmlElement(element)) {
1906
+ candidates.push(element);
1907
+ }
1908
+ if ("querySelectorAll" in element) {
1909
+ candidates.push(...Array.from(element.querySelectorAll('[role="rowgroup"], *')));
1910
+ }
1911
+ let current = element.parentElement;
1912
+ while (current && candidates.length < 80) {
1913
+ candidates.push(current);
1914
+ current = current.parentElement;
1915
+ }
1916
+ const uniqueCandidates = [...new Set(candidates)];
1917
+ return uniqueCandidates.find((candidate) => {
1918
+ const style = getComputedStyle(candidate);
1919
+ return (style.overflowY === "auto" || style.overflowY === "scroll") && candidate.scrollHeight > candidate.clientHeight + 4 && candidate.clientHeight > 0;
1920
+ }) ?? null;
1921
+ }
1922
+ function observedRowIndexes(rows) {
1923
+ return rows.map((row) => {
1924
+ const raw = row.getAttribute("aria-rowindex") || row.getAttribute("data-row-index") || row.dataset.rowIndex;
1925
+ const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
1926
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
1927
+ }).filter((value) => value !== null);
1928
+ }
1929
+ function inferEstimatedTotalRows(element, rows, scrollContainer) {
1930
+ const attrCandidates = [
1931
+ element.getAttribute("aria-rowcount"),
1932
+ (scrollContainer ?? void 0)?.getAttribute("aria-rowcount"),
1933
+ element.getAttribute("data-total-rows"),
1934
+ (scrollContainer ?? void 0)?.getAttribute("data-total-rows")
1935
+ ].filter((candidate) => typeof candidate === "string" && candidate.trim().length > 0).map((candidate) => Number.parseInt(candidate, 10)).filter((candidate) => Number.isFinite(candidate) && candidate > 0);
1936
+ if (attrCandidates.length > 0) {
1937
+ return Math.max(...attrCandidates);
1938
+ }
1939
+ const indexes = observedRowIndexes(rows);
1940
+ if (indexes.length > 0) {
1941
+ return Math.max(...indexes);
1942
+ }
1943
+ const rowHeight = rows[0]?.getBoundingClientRect().height ?? 0;
1944
+ if (scrollContainer && rowHeight > 0) {
1945
+ const estimate = Math.round(scrollContainer.scrollHeight / rowHeight);
1946
+ if (estimate > rows.length) {
1947
+ return estimate;
1948
+ }
1949
+ }
1950
+ return void 0;
1951
+ }
1952
+ function tableIntelligenceForElement(table, element) {
1953
+ if (element instanceof HTMLTableElement) {
1954
+ return buildTableIntelligence({
1955
+ kind: table.kind,
1956
+ visibleRowCount: htmlTableRows(element, Number.MAX_SAFE_INTEGER).length,
1957
+ rowCount: table.rowCount,
1958
+ estimatedTotalRows: table.rowCount,
1959
+ hasScrollContainer: false,
1960
+ hasTranslatedRows: false,
1961
+ knownGridKind: false
1962
+ });
1963
+ }
1964
+ const rows = gridRowNodes(element);
1965
+ const scrollContainer = findScrollableTableContainer(element);
1966
+ const rowIndexes = observedRowIndexes(rows);
1967
+ const estimatedTotalRows = inferEstimatedTotalRows(element, rows, scrollContainer);
1968
+ const hasTranslatedRows = rows.some((row) => {
1969
+ const style = row.style.transform || getComputedStyle(row).transform;
1970
+ return typeof style === "string" && style !== "" && style !== "none";
1971
+ });
1972
+ return buildTableIntelligence({
1973
+ kind: table.kind,
1974
+ visibleRowCount: rows.length,
1975
+ rowCount: table.rowCount,
1976
+ estimatedTotalRows,
1977
+ hasScrollContainer: scrollContainer !== null,
1978
+ hasTranslatedRows,
1979
+ maxObservedRowIndex: rowIndexes.length > 0 ? Math.max(...rowIndexes) : void 0,
1980
+ minObservedRowIndex: rowIndexes.length > 0 ? Math.min(...rowIndexes) : void 0,
1981
+ knownGridKind: table.kind !== "aria-grid"
1982
+ });
1983
+ }
1984
+ function withTableIntelligence(table, element) {
1985
+ if (!(element instanceof Element)) {
1986
+ return table;
1987
+ }
1988
+ return {
1989
+ ...table,
1990
+ intelligence: tableIntelligenceForElement(table, element)
1991
+ };
1992
+ }
1679
1993
  function describeTables() {
1680
1994
  const rootResult = resolveRootForLocator();
1681
1995
  if (!rootResult.ok) {
@@ -1685,26 +1999,28 @@
1685
1999
  const tables = [];
1686
2000
  const htmlTables = Array.from(root.querySelectorAll("table"));
1687
2001
  for (const [index, table] of htmlTables.entries()) {
1688
- tables.push({
2002
+ const handle = {
1689
2003
  id: buildTableId(table.closest(".dataTables_wrapper") ? "dataTables" : "html", index),
1690
2004
  name: (table.getAttribute("aria-label") || table.getAttribute("data-testid") || table.id || `table-${index + 1}`).trim(),
1691
2005
  kind: table.closest(".dataTables_wrapper") ? "dataTables" : "html",
1692
2006
  selector: table.id ? `#${table.id}` : void 0,
1693
2007
  rowCount: table.querySelectorAll("tbody tr").length || table.querySelectorAll("tr").length,
1694
2008
  columnCount: table.querySelectorAll("thead th").length || table.querySelectorAll("tr:first-child th, tr:first-child td").length
1695
- });
2009
+ };
2010
+ tables.push(withTableIntelligence(handle, table));
1696
2011
  }
1697
2012
  const gridRoots = Array.from(root.querySelectorAll('[role="grid"], [role="table"], .ag-root, .ag-root-wrapper'));
1698
2013
  for (const [index, grid] of gridRoots.entries()) {
1699
2014
  const kind = grid.className.includes("ag-") ? "ag-grid" : "aria-grid";
1700
- tables.push({
2015
+ const handle = {
1701
2016
  id: buildTableId(kind, index),
1702
2017
  name: (grid.getAttribute("aria-label") || grid.getAttribute("data-testid") || grid.id || `grid-${index + 1}`).trim(),
1703
2018
  kind,
1704
2019
  selector: grid.id ? `#${grid.id}` : void 0,
1705
- rowCount: grid.querySelectorAll('[role="row"]').length,
2020
+ rowCount: gridRowNodes(grid).length,
1706
2021
  columnCount: grid.querySelectorAll('[role="columnheader"]').length
1707
- });
2022
+ };
2023
+ tables.push(withTableIntelligence(handle, grid));
1708
2024
  }
1709
2025
  return tables;
1710
2026
  }
@@ -1764,15 +2080,171 @@
1764
2080
  }
1765
2081
  function gridRows(grid, limit) {
1766
2082
  const columns = gridSchema(grid);
1767
- return Array.from(grid.querySelectorAll('[role="row"]')).slice(0, limit).map((row) => {
2083
+ return gridRowNodes(grid).slice(0, limit).map((row) => {
1768
2084
  const record = {};
1769
- Array.from(row.querySelectorAll('[role="gridcell"], [role="cell"], [role="columnheader"]')).forEach((cell, index) => {
2085
+ Array.from(row.querySelectorAll('[role="gridcell"], [role="cell"]')).forEach((cell, index) => {
1770
2086
  const key = columns[index]?.label ?? `Column ${index + 1}`;
1771
2087
  record[key] = (cell.textContent ?? "").trim();
1772
2088
  });
1773
2089
  return record;
1774
2090
  }).filter((row) => Object.values(row).some((value) => String(value).trim().length > 0));
1775
2091
  }
2092
+ function rowSignature(row) {
2093
+ return JSON.stringify(
2094
+ Object.entries(row).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, value === void 0 ? null : value])
2095
+ );
2096
+ }
2097
+ function mergeUniqueRows(target, rows, limit) {
2098
+ let added = 0;
2099
+ for (const row of rows) {
2100
+ const signature = rowSignature(row);
2101
+ if (target.has(signature)) {
2102
+ continue;
2103
+ }
2104
+ target.set(signature, row);
2105
+ added += 1;
2106
+ if (target.size >= limit) {
2107
+ break;
2108
+ }
2109
+ }
2110
+ return added;
2111
+ }
2112
+ function waitForPaint(frames = 2) {
2113
+ return new Promise((resolve) => {
2114
+ const schedule = (remaining) => {
2115
+ if (remaining <= 0) {
2116
+ window.setTimeout(() => resolve(), 24);
2117
+ return;
2118
+ }
2119
+ requestAnimationFrame(() => schedule(remaining - 1));
2120
+ };
2121
+ schedule(frames);
2122
+ });
2123
+ }
2124
+ function currentTableWindowSignature(element) {
2125
+ const rowIndexes = observedRowIndexes(gridRowNodes(element));
2126
+ if (rowIndexes.length > 0) {
2127
+ return `indexes:${rowIndexes.join(",")}`;
2128
+ }
2129
+ const visibleRows = tableRowsFromElement(element, 24);
2130
+ return `rows:${visibleRows.map((row) => rowSignature(row)).join("|")}`;
2131
+ }
2132
+ async function waitForTableWindowChange(element, previousSignature, timeoutMs = 200) {
2133
+ const start = Date.now();
2134
+ let currentSignature = previousSignature;
2135
+ while (Date.now() - start < timeoutMs) {
2136
+ await waitForPaint(1);
2137
+ currentSignature = currentTableWindowSignature(element);
2138
+ if (currentSignature !== previousSignature) {
2139
+ return currentSignature;
2140
+ }
2141
+ await new Promise((resolve) => window.setTimeout(resolve, 16));
2142
+ }
2143
+ return currentSignature;
2144
+ }
2145
+ async function scrollExtractRows(element, limit, intelligence) {
2146
+ const scrollContainer = findScrollableTableContainer(element);
2147
+ if (!scrollContainer) {
2148
+ const rows2 = tableRowsFromElement(element, limit);
2149
+ return {
2150
+ rows: rows2,
2151
+ extractionMode: "visibleOnly",
2152
+ extraction: buildExtractionMetadata("visibleOnly", rows2, intelligence, [
2153
+ "Fell back to visible rows because no scroll container was detected."
2154
+ ])
2155
+ };
2156
+ }
2157
+ const originalScrollTop = scrollContainer.scrollTop;
2158
+ const collected = /* @__PURE__ */ new Map();
2159
+ const seenRowIndexes = /* @__PURE__ */ new Set();
2160
+ let stagnantPasses = 0;
2161
+ let reachedEnd = false;
2162
+ let limitApplied = false;
2163
+ const rowHeight = gridRowNodes(element)[0]?.getBoundingClientRect().height ?? 0;
2164
+ const step = Math.max(80, Math.round(scrollContainer.clientHeight * 0.8), rowHeight > 0 ? Math.round(rowHeight * 4) : 0);
2165
+ const initialMaxScrollTop = Math.max(0, scrollContainer.scrollHeight - scrollContainer.clientHeight);
2166
+ const maxIterations = Math.max(8, Math.min(80, Math.ceil(initialMaxScrollTop / Math.max(step, 1)) + 6, limit * 2));
2167
+ try {
2168
+ scrollContainer.scrollTop = 0;
2169
+ scrollContainer.dispatchEvent(new Event("scroll"));
2170
+ await waitForPaint();
2171
+ let currentWindowSignature = currentTableWindowSignature(element);
2172
+ for (let iteration = 0; iteration < maxIterations; iteration += 1) {
2173
+ const currentRows = tableRowsFromElement(element, limit);
2174
+ const added = mergeUniqueRows(collected, currentRows, limit);
2175
+ observedRowIndexes(gridRowNodes(element)).forEach((rowIndex) => {
2176
+ seenRowIndexes.add(rowIndex);
2177
+ });
2178
+ const estimatedTotalRows = intelligence?.estimatedTotalRows;
2179
+ if (typeof estimatedTotalRows === "number" && (collected.size >= estimatedTotalRows || seenRowIndexes.size >= estimatedTotalRows)) {
2180
+ reachedEnd = true;
2181
+ break;
2182
+ }
2183
+ if (collected.size >= limit) {
2184
+ limitApplied = true;
2185
+ break;
2186
+ }
2187
+ if (added === 0) {
2188
+ stagnantPasses += 1;
2189
+ } else {
2190
+ stagnantPasses = 0;
2191
+ }
2192
+ const maxScrollTop = Math.max(0, scrollContainer.scrollHeight - scrollContainer.clientHeight);
2193
+ if (scrollContainer.scrollTop >= maxScrollTop - 2) {
2194
+ reachedEnd = true;
2195
+ if (stagnantPasses >= 1) {
2196
+ break;
2197
+ }
2198
+ }
2199
+ if (stagnantPasses >= 2) {
2200
+ break;
2201
+ }
2202
+ const nextScrollTop = Math.min(maxScrollTop, scrollContainer.scrollTop + step);
2203
+ if (nextScrollTop === scrollContainer.scrollTop) {
2204
+ reachedEnd = true;
2205
+ break;
2206
+ }
2207
+ scrollContainer.scrollTop = nextScrollTop;
2208
+ scrollContainer.dispatchEvent(new Event("scroll"));
2209
+ currentWindowSignature = await waitForTableWindowChange(element, currentWindowSignature);
2210
+ }
2211
+ if (!reachedEnd) {
2212
+ const maxScrollTop = Math.max(0, scrollContainer.scrollHeight - scrollContainer.clientHeight);
2213
+ if (scrollContainer.scrollTop < maxScrollTop) {
2214
+ const previousWindowSignature = currentTableWindowSignature(element);
2215
+ scrollContainer.scrollTop = maxScrollTop;
2216
+ scrollContainer.dispatchEvent(new Event("scroll"));
2217
+ await waitForTableWindowChange(element, previousWindowSignature);
2218
+ mergeUniqueRows(collected, tableRowsFromElement(element, limit), limit);
2219
+ observedRowIndexes(gridRowNodes(element)).forEach((rowIndex) => {
2220
+ seenRowIndexes.add(rowIndex);
2221
+ });
2222
+ }
2223
+ }
2224
+ } finally {
2225
+ scrollContainer.scrollTop = originalScrollTop;
2226
+ await waitForPaint();
2227
+ }
2228
+ const rows = [...collected.values()].slice(0, limit);
2229
+ if (typeof intelligence?.estimatedTotalRows === "number" && (rows.length >= intelligence.estimatedTotalRows || seenRowIndexes.size >= intelligence.estimatedTotalRows)) {
2230
+ reachedEnd = true;
2231
+ }
2232
+ const warnings = [];
2233
+ if (limitApplied) {
2234
+ warnings.push("Stopped after reaching maxRows before confirming the end of the table.");
2235
+ }
2236
+ if (!reachedEnd && (intelligence?.estimatedTotalRows ?? rows.length) > rows.length) {
2237
+ warnings.push("Could not confirm the end of scrollable content; result may be partial.");
2238
+ }
2239
+ return {
2240
+ rows,
2241
+ extractionMode: "scroll",
2242
+ extraction: buildExtractionMetadata("scroll", rows, intelligence, warnings, {
2243
+ reachedEnd,
2244
+ limitApplied
2245
+ })
2246
+ };
2247
+ }
1776
2248
  function runPageWorldRequest(action, payload) {
1777
2249
  const requestId = `bak_page_world_${Date.now()}_${Math.random().toString(16).slice(2)}`;
1778
2250
  return new Promise((resolve, reject) => {
@@ -2150,6 +2622,31 @@
2150
2622
  async function globalsPreview() {
2151
2623
  return Object.keys(window).filter((key) => DISCOVERY_GLOBAL_PATTERN.test(key)).slice(0, 50);
2152
2624
  }
2625
+ function collectInlineJsonSources(root) {
2626
+ const scripts = Array.from(root.querySelectorAll('script[type="application/json"], script[type="application/ld+json"]'));
2627
+ return scripts.map((script, index) => {
2628
+ const raw = script.textContent?.trim() ?? "";
2629
+ if (!raw) {
2630
+ return null;
2631
+ }
2632
+ try {
2633
+ const parsed = JSON.parse(raw);
2634
+ const path = script.id ? `#${script.id}` : `inline-json:${index + 1}`;
2635
+ const timestamps = collectTimestampProbes(parsed, path);
2636
+ return {
2637
+ label: script.id || script.getAttribute("data-testid") || script.type || `inline-json-${index + 1}`,
2638
+ path,
2639
+ sample: sampleValue(parsed),
2640
+ sampleSize: estimateSampleSize(parsed),
2641
+ schemaHint: inferSchemaHint(parsed),
2642
+ lastObservedAt: timestamps.length > 0 ? Math.max(...timestamps.map((item) => Date.parse(item.value)).filter(Number.isFinite)) : null,
2643
+ timestamps
2644
+ };
2645
+ } catch {
2646
+ return null;
2647
+ }
2648
+ }).filter((item) => item !== null);
2649
+ }
2153
2650
  async function collectInspectionState(params = {}) {
2154
2651
  const rootResult = resolveRootForLocator();
2155
2652
  if (!rootResult.ok) {
@@ -2170,6 +2667,7 @@
2170
2667
  );
2171
2668
  const previewGlobals = await globalsPreview();
2172
2669
  const suspiciousGlobals = [...new Set(scripts.flatMap((script) => script.suspectedVars))].slice(0, 50);
2670
+ const inlineJsonSources = collectInlineJsonSources(root);
2173
2671
  return {
2174
2672
  url: metadata.url,
2175
2673
  title: metadata.title,
@@ -2188,6 +2686,7 @@
2188
2686
  storage: storageMetadata(),
2189
2687
  cookies: cookieMetadata(),
2190
2688
  frames: collectFrames(),
2689
+ inlineJsonSources,
2191
2690
  tables: describeTables(),
2192
2691
  timers: {
2193
2692
  timeouts: 0,
@@ -2887,9 +3386,10 @@
2887
3386
  if (!resolved || !(resolved.element instanceof Element)) {
2888
3387
  throw { code: "E_NOT_FOUND", message: `table not found: ${String(params.table ?? "")}` };
2889
3388
  }
2890
- const schema = resolved.element instanceof HTMLTableElement ? { columns: htmlTableSchema(resolved.element) } : { columns: gridSchema(resolved.element) };
3389
+ const table = withTableIntelligence(resolved.table, resolved.element);
3390
+ const schema = { columns: tableSchemaFromElement(resolved.element) };
2891
3391
  return {
2892
- table: resolved.table,
3392
+ table,
2893
3393
  schema
2894
3394
  };
2895
3395
  }
@@ -2900,11 +3400,29 @@
2900
3400
  throw { code: "E_NOT_FOUND", message: `table not found: ${String(params.table ?? "")}` };
2901
3401
  }
2902
3402
  const requestedLimit = params.all === true ? typeof params.maxRows === "number" ? Math.max(1, Math.floor(params.maxRows)) : 1e4 : typeof params.limit === "number" ? Math.max(1, Math.floor(params.limit)) : 100;
2903
- const extractionMode = resolved.table.kind === "html" || resolved.table.kind === "dataTables" ? "dataSource" : "visibleOnly";
2904
- const rows = resolved.element instanceof HTMLTableElement ? htmlTableRows(resolved.element, requestedLimit) : gridRows(resolved.element, requestedLimit);
3403
+ const table = withTableIntelligence(resolved.table, resolved.element);
3404
+ let rows = tableRowsFromElement(resolved.element, requestedLimit);
3405
+ let extractionMode = table.intelligence?.preferredExtractionMode ?? (resolved.element instanceof HTMLTableElement ? "dataSource" : "visibleOnly");
3406
+ let extraction = buildExtractionMetadata(
3407
+ extractionMode,
3408
+ rows,
3409
+ table.intelligence,
3410
+ params.all === true && extractionMode === "visibleOnly" ? ["Only visible rows could be read from this table region."] : [],
3411
+ {
3412
+ reachedEnd: extractionMode === "dataSource",
3413
+ limitApplied: params.all === true && rows.length >= requestedLimit
3414
+ }
3415
+ );
3416
+ if (params.all === true && extractionMode === "scroll") {
3417
+ const scrolled = await scrollExtractRows(resolved.element, requestedLimit, table.intelligence);
3418
+ rows = scrolled.rows;
3419
+ extractionMode = scrolled.extractionMode;
3420
+ extraction = scrolled.extraction;
3421
+ }
2905
3422
  return {
2906
- table: resolved.table,
3423
+ table,
2907
3424
  extractionMode,
3425
+ extraction,
2908
3426
  rows
2909
3427
  };
2910
3428
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Browser Agent Kit",
4
- "version": "0.6.11",
4
+ "version": "0.6.12",
5
5
  "action": {
6
6
  "default_popup": "popup.html"
7
7
  },