@flrande/bak-extension 0.6.15 → 0.6.16

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.
@@ -1 +1 @@
1
- 2026-03-14T15:19:38.952Z
1
+ 2026-03-15T05:31:19.065Z
@@ -302,7 +302,7 @@
302
302
  detail: `Shared sample values: ${distinctOverlappingValues.join(", ")}`
303
303
  });
304
304
  }
305
- const explicitReferenceHit = table.table.name.toLowerCase().includes(source.source.label.toLowerCase()) || (table.table.selector ?? "").toLowerCase().includes(source.source.label.toLowerCase()) || source.source.label.toLowerCase().includes(table.table.name.toLowerCase());
305
+ const explicitReferenceHit = table.table.label.toLowerCase().includes(source.source.label.toLowerCase()) || (table.table.selector ?? "").toLowerCase().includes(source.source.label.toLowerCase()) || source.source.label.toLowerCase().includes(table.table.label.toLowerCase());
306
306
  if (explicitReferenceHit) {
307
307
  basis.push({
308
308
  type: "explicitReference",
@@ -533,9 +533,6 @@
533
533
  function shouldRedactHeader(name) {
534
534
  return SENSITIVE_HEADER_PATTERNS.some((pattern) => pattern.test(name));
535
535
  }
536
- function containsRedactionMarker(raw) {
537
- return typeof raw === "string" && raw.includes("[REDACTED");
538
- }
539
536
  function redactTransportText(raw) {
540
537
  if (!raw) {
541
538
  return "";
@@ -556,7 +553,7 @@
556
553
  // package.json
557
554
  var package_default = {
558
555
  name: "@flrande/bak-extension",
559
- version: "0.6.15",
556
+ version: "0.6.16",
560
557
  type: "module",
561
558
  scripts: {
562
559
  build: "tsup src/background.ts src/content.ts src/popup.ts --format iife --out-dir dist --clean && node scripts/copy-assets.mjs",
@@ -664,6 +661,17 @@
664
661
  const normalized = contentType.toLowerCase();
665
662
  return normalized.startsWith("text/") || normalized.includes("json") || normalized.includes("javascript") || normalized.includes("xml") || normalized.includes("html") || normalized.includes("urlencoded") || normalized.includes("graphql");
666
663
  }
664
+ function sanitizeEntry(entry) {
665
+ const { rawRequestHeaders, rawRequestBody, rawRequestBodyTruncated, ...publicEntry } = entry;
666
+ void rawRequestHeaders;
667
+ void rawRequestBody;
668
+ void rawRequestBodyTruncated;
669
+ return {
670
+ ...publicEntry,
671
+ requestHeaders: typeof entry.requestHeaders === "object" && entry.requestHeaders !== null ? { ...entry.requestHeaders } : void 0,
672
+ responseHeaders: typeof entry.responseHeaders === "object" && entry.responseHeaders !== null ? { ...entry.responseHeaders } : void 0
673
+ };
674
+ }
667
675
  function pushEntry(state, entry, requestId) {
668
676
  state.entries.push(entry);
669
677
  state.entriesById.set(entry.id, entry);
@@ -737,7 +745,8 @@
737
745
  return;
738
746
  }
739
747
  const request = typeof params.request === "object" && params.request !== null ? params.request : {};
740
- const headers = redactHeaderMap(normalizeHeaders(request.headers));
748
+ const rawHeaders = normalizeHeaders(request.headers);
749
+ const headers = redactHeaderMap(rawHeaders);
741
750
  const truncatedRequest = truncateText(typeof request.postData === "string" ? request.postData : void 0, DEFAULT_BODY_BYTES);
742
751
  const entry = {
743
752
  id: `net_${tabId}_${requestId}`,
@@ -754,6 +763,9 @@
754
763
  requestHeaders: headers,
755
764
  requestBodyPreview: truncatedRequest.text ? redactTransportText(truncatedRequest.text) : void 0,
756
765
  requestBodyTruncated: truncatedRequest.truncated,
766
+ rawRequestHeaders: rawHeaders,
767
+ rawRequestBody: typeof request.postData === "string" ? request.postData : void 0,
768
+ rawRequestBodyTruncated: false,
757
769
  initiatorUrl: typeof params.initiator === "object" && params.initiator !== null && typeof params.initiator.url === "string" ? String(params.initiator.url) : void 0,
758
770
  tabId,
759
771
  source: "debugger"
@@ -857,12 +869,32 @@
857
869
  function listNetworkEntries(tabId, filters = {}) {
858
870
  const state = getState(tabId);
859
871
  const limit = typeof filters.limit === "number" ? Math.max(1, Math.min(500, Math.floor(filters.limit))) : 50;
860
- return state.entries.filter((entry) => entryMatchesFilters(entry, filters)).slice(-limit).reverse().map((entry) => ({ ...entry }));
872
+ return state.entries.filter((entry) => entryMatchesFilters(entry, filters)).slice(-limit).reverse().map((entry) => sanitizeEntry(entry));
861
873
  }
862
874
  function getNetworkEntry(tabId, id) {
863
875
  const state = getState(tabId);
864
876
  const entry = state.entriesById.get(id);
865
- return entry ? { ...entry } : null;
877
+ return entry ? sanitizeEntry(entry) : null;
878
+ }
879
+ function getReplayableNetworkRequest(tabId, id) {
880
+ const state = getState(tabId);
881
+ const entry = state.entriesById.get(id);
882
+ if (!entry) {
883
+ return null;
884
+ }
885
+ const publicEntry = sanitizeEntry(entry);
886
+ if (entry.rawRequestBodyTruncated === true) {
887
+ return {
888
+ entry: publicEntry,
889
+ degradedReason: "live replay unavailable because the captured request body was truncated in memory"
890
+ };
891
+ }
892
+ return {
893
+ entry: publicEntry,
894
+ headers: entry.rawRequestHeaders ? { ...entry.rawRequestHeaders } : void 0,
895
+ body: entry.rawRequestBody,
896
+ contentType: headerValue(entry.rawRequestHeaders, "content-type")
897
+ };
866
898
  }
867
899
  async function waitForNetworkEntry(tabId, filters = {}) {
868
900
  const timeoutMs = typeof filters.timeoutMs === "number" ? Math.max(1, Math.floor(filters.timeoutMs)) : 5e3;
@@ -873,7 +905,7 @@
873
905
  const nextState = getState(tabId);
874
906
  const matched = nextState.entries.find((entry) => !seenIds.has(entry.id) && entryMatchesFilters(entry, filters));
875
907
  if (matched) {
876
- return { ...matched };
908
+ return sanitizeEntry(matched);
877
909
  }
878
910
  await new Promise((resolve) => setTimeout(resolve, 75));
879
911
  }
@@ -883,14 +915,30 @@
883
915
  };
884
916
  }
885
917
  function searchNetworkEntries(tabId, pattern, limit = 50) {
918
+ const state = getState(tabId);
886
919
  const normalized = pattern.toLowerCase();
887
- return listNetworkEntries(tabId, { limit: Math.max(limit, 1) }).filter((entry) => {
920
+ const matchedEntries = state.entries.filter((entry) => {
888
921
  const headerText = JSON.stringify({
889
922
  requestHeaders: entry.requestHeaders,
890
923
  responseHeaders: entry.responseHeaders
891
924
  }).toLowerCase();
892
925
  return entry.url.toLowerCase().includes(normalized) || (entry.requestBodyPreview ?? "").toLowerCase().includes(normalized) || (entry.responseBodyPreview ?? "").toLowerCase().includes(normalized) || headerText.includes(normalized);
893
926
  });
927
+ const scannedEntries = state.entries.filter((entry) => entryMatchesFilters(entry, {}));
928
+ const toCoverage = (entries, key, truncatedKey) => ({
929
+ full: entries.filter((entry) => typeof entry[key] === "string" && entry[truncatedKey] !== true).length,
930
+ partial: entries.filter((entry) => typeof entry[key] === "string" && entry[truncatedKey] === true).length,
931
+ none: entries.filter((entry) => typeof entry[key] !== "string").length
932
+ });
933
+ return {
934
+ entries: matchedEntries.slice(-Math.max(limit, 1)).reverse().map((entry) => sanitizeEntry(entry)),
935
+ scanned: scannedEntries.length,
936
+ matched: matchedEntries.length,
937
+ bodyCoverage: {
938
+ request: toCoverage(scannedEntries, "requestBodyPreview", "requestBodyTruncated"),
939
+ response: toCoverage(scannedEntries, "responseBodyPreview", "responseBodyTruncated")
940
+ }
941
+ };
894
942
  }
895
943
  function latestNetworkTimestamp(tabId) {
896
944
  const entries = listNetworkEntries(tabId, { limit: MAX_ENTRIES });
@@ -2507,7 +2555,18 @@
2507
2555
  await new Promise((resolve) => setTimeout(resolve, 80));
2508
2556
  }
2509
2557
  try {
2510
- return await chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" });
2558
+ return {
2559
+ captureStatus: "complete",
2560
+ imageData: await chrome.tabs.captureVisibleTab(tab.windowId, { format: "png" })
2561
+ };
2562
+ } catch (error) {
2563
+ return {
2564
+ captureStatus: "degraded",
2565
+ captureError: {
2566
+ code: "E_CAPTURE_FAILED",
2567
+ message: error instanceof Error ? error.message : String(error)
2568
+ }
2569
+ };
2511
2570
  } finally {
2512
2571
  if (shouldSwitch && typeof activeTab?.id === "number") {
2513
2572
  try {
@@ -2638,7 +2697,9 @@
2638
2697
  contentType: typeof params.contentType === "string" ? params.contentType : void 0,
2639
2698
  mode: params.mode === "json" ? "json" : "raw",
2640
2699
  maxBytes: typeof params.maxBytes === "number" ? params.maxBytes : void 0,
2641
- timeoutMs: typeof params.timeoutMs === "number" ? params.timeoutMs : void 0
2700
+ timeoutMs: typeof params.timeoutMs === "number" ? params.timeoutMs : void 0,
2701
+ fullResponse: params.fullResponse === true,
2702
+ auth: params.auth === "manual" || params.auth === "off" ? params.auth : "auto"
2642
2703
  }
2643
2704
  ],
2644
2705
  func: async (payload) => {
@@ -2761,6 +2822,69 @@
2761
2822
  }
2762
2823
  return { resolver: "lexical", value: readLexical() };
2763
2824
  };
2825
+ const findHeaderName = (headers, name) => Object.keys(headers).find((key) => key.toLowerCase() === name.toLowerCase());
2826
+ const findCookieValue = (cookieString, name) => {
2827
+ const targetName = `${name}=`;
2828
+ for (const segment of cookieString.split(";")) {
2829
+ const trimmed = segment.trim();
2830
+ if (trimmed.toLowerCase().startsWith(targetName.toLowerCase())) {
2831
+ return trimmed.slice(targetName.length);
2832
+ }
2833
+ }
2834
+ return void 0;
2835
+ };
2836
+ const buildJsonSummary = (value) => {
2837
+ const rowsCandidate = (() => {
2838
+ if (Array.isArray(value)) {
2839
+ return value;
2840
+ }
2841
+ if (typeof value !== "object" || value === null) {
2842
+ return null;
2843
+ }
2844
+ const record = value;
2845
+ for (const key of ["data", "rows", "results", "items"]) {
2846
+ if (Array.isArray(record[key])) {
2847
+ return record[key];
2848
+ }
2849
+ }
2850
+ return null;
2851
+ })();
2852
+ if (Array.isArray(rowsCandidate) && rowsCandidate.length > 0) {
2853
+ const objectRows = rowsCandidate.filter((row) => typeof row === "object" && row !== null && !Array.isArray(row)).slice(0, 25);
2854
+ if (objectRows.length > 0) {
2855
+ const columns = [...new Set(objectRows.flatMap((row) => Object.keys(row)))].slice(0, 20);
2856
+ return {
2857
+ schema: {
2858
+ columns: columns.map((label, index) => ({
2859
+ key: `col_${index + 1}`,
2860
+ label
2861
+ }))
2862
+ },
2863
+ mappedRows: objectRows.map((row) => {
2864
+ const mapped = {};
2865
+ for (const column of columns) {
2866
+ mapped[column] = row[column];
2867
+ }
2868
+ return mapped;
2869
+ })
2870
+ };
2871
+ }
2872
+ }
2873
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
2874
+ const columns = Object.keys(value).slice(0, 20);
2875
+ if (columns.length > 0) {
2876
+ return {
2877
+ schema: {
2878
+ columns: columns.map((label, index) => ({
2879
+ key: `col_${index + 1}`,
2880
+ label
2881
+ }))
2882
+ }
2883
+ };
2884
+ }
2885
+ }
2886
+ return {};
2887
+ };
2764
2888
  try {
2765
2889
  const targetWindow = payload.scope === "main" ? window : payload.scope === "current" ? resolveFrameWindow(payload.framePath ?? []) : window;
2766
2890
  if (payload.action === "eval") {
@@ -2781,9 +2905,48 @@
2781
2905
  }
2782
2906
  if (payload.action === "fetch") {
2783
2907
  const headers = { ...payload.headers ?? {} };
2784
- if (payload.contentType && !headers["Content-Type"]) {
2908
+ if (payload.contentType && !findHeaderName(headers, "Content-Type")) {
2785
2909
  headers["Content-Type"] = payload.contentType;
2786
2910
  }
2911
+ const fullResponse = payload.fullResponse === true;
2912
+ const authApplied = [];
2913
+ const authSources = /* @__PURE__ */ new Set();
2914
+ const requestUrl = new URL(payload.url, targetWindow.location.href);
2915
+ const sameOrigin = requestUrl.origin === targetWindow.location.origin;
2916
+ const authMode = payload.auth === "manual" || payload.auth === "off" ? payload.auth : "auto";
2917
+ const maybeApplyHeader = (name, value, source) => {
2918
+ if (!value || findHeaderName(headers, name)) {
2919
+ return;
2920
+ }
2921
+ headers[name] = value;
2922
+ authApplied.push(name);
2923
+ authSources.add(source);
2924
+ };
2925
+ if (sameOrigin && authMode === "auto") {
2926
+ const xsrfCookie = findCookieValue(targetWindow.document.cookie ?? "", "XSRF-TOKEN");
2927
+ if (xsrfCookie) {
2928
+ maybeApplyHeader("X-XSRF-TOKEN", decodeURIComponent(xsrfCookie), "cookie:XSRF-TOKEN");
2929
+ }
2930
+ const metaTokens = [
2931
+ {
2932
+ selector: 'meta[name="xsrf-token"], meta[name="x-xsrf-token"]',
2933
+ header: "X-XSRF-TOKEN",
2934
+ source: "meta:xsrf-token"
2935
+ },
2936
+ {
2937
+ selector: 'meta[name="csrf-token"], meta[name="csrf_token"], meta[name="_csrf"]',
2938
+ header: "X-CSRF-TOKEN",
2939
+ source: "meta:csrf-token"
2940
+ }
2941
+ ];
2942
+ for (const token of metaTokens) {
2943
+ const meta = targetWindow.document.querySelector(token.selector);
2944
+ const content = meta?.content?.trim();
2945
+ if (content) {
2946
+ maybeApplyHeader(token.header, content, token.source);
2947
+ }
2948
+ }
2949
+ }
2787
2950
  const controller = typeof AbortController === "function" ? new AbortController() : null;
2788
2951
  const timeoutId = controller && typeof payload.timeoutMs === "number" && payload.timeoutMs > 0 ? window.setTimeout(() => controller.abort(), payload.timeoutMs) : null;
2789
2952
  let response;
@@ -2810,32 +2973,43 @@
2810
2973
  value: (() => {
2811
2974
  const encoder = typeof TextEncoder === "function" ? new TextEncoder() : null;
2812
2975
  const decoder = typeof TextDecoder === "function" ? new TextDecoder() : null;
2813
- const previewLimit = typeof payload.maxBytes === "number" && payload.maxBytes > 0 ? payload.maxBytes : 8192;
2976
+ const previewLimit = !fullResponse && typeof payload.maxBytes === "number" && payload.maxBytes > 0 ? payload.maxBytes : 8192;
2814
2977
  const encodedBody = encoder ? encoder.encode(bodyText) : null;
2815
2978
  const bodyBytes = encodedBody ? encodedBody.byteLength : bodyText.length;
2816
- const truncated = bodyBytes > previewLimit;
2817
- if (payload.mode === "json" && truncated) {
2818
- throw {
2819
- code: "E_BODY_TOO_LARGE",
2820
- message: "JSON response exceeds max-bytes",
2821
- details: {
2822
- bytes: bodyBytes,
2823
- maxBytes: previewLimit
2824
- }
2825
- };
2826
- }
2827
- const previewText = encodedBody && decoder ? decoder.decode(encodedBody.subarray(0, Math.min(encodedBody.byteLength, previewLimit))) : truncated ? bodyText.slice(0, previewLimit) : bodyText;
2828
- return {
2979
+ const truncated = !fullResponse && bodyBytes > previewLimit;
2980
+ const previewText = fullResponse ? bodyText : encodedBody && decoder ? decoder.decode(encodedBody.subarray(0, Math.min(encodedBody.byteLength, previewLimit))) : truncated ? bodyText.slice(0, previewLimit) : bodyText;
2981
+ const result = {
2829
2982
  url: response.url,
2830
2983
  status: response.status,
2831
2984
  ok: response.ok,
2832
2985
  headers: headerMap,
2833
2986
  contentType: response.headers.get("content-type") ?? void 0,
2834
- bodyText: payload.mode === "json" ? void 0 : previewText,
2835
- json: payload.mode === "json" && bodyText ? JSON.parse(bodyText) : void 0,
2836
2987
  bytes: bodyBytes,
2837
- truncated
2988
+ truncated,
2989
+ authApplied: authApplied.length > 0 ? authApplied : void 0,
2990
+ authSources: authSources.size > 0 ? [...authSources] : void 0
2838
2991
  };
2992
+ if (payload.mode === "json") {
2993
+ const parsedJson = bodyText ? JSON.parse(bodyText) : void 0;
2994
+ const summary = buildJsonSummary(parsedJson);
2995
+ if (fullResponse || !truncated) {
2996
+ result.json = parsedJson;
2997
+ } else {
2998
+ result.degradedReason = "response body exceeded max-bytes and was summarized";
2999
+ }
3000
+ if (summary.schema) {
3001
+ result.schema = summary.schema;
3002
+ }
3003
+ if (summary.mappedRows) {
3004
+ result.mappedRows = summary.mappedRows;
3005
+ }
3006
+ } else {
3007
+ result.bodyText = previewText;
3008
+ if (truncated) {
3009
+ result.degradedReason = "response body exceeded max-bytes and was truncated";
3010
+ }
3011
+ }
3012
+ return result;
2839
3013
  })()
2840
3014
  };
2841
3015
  }
@@ -2908,19 +3082,16 @@
2908
3082
  }
2909
3083
  return clone;
2910
3084
  }
2911
- function replayHeadersFromEntry(entry) {
2912
- if (!entry.requestHeaders) {
3085
+ function replayHeadersFromRequestHeaders(requestHeaders) {
3086
+ if (!requestHeaders) {
2913
3087
  return void 0;
2914
3088
  }
2915
3089
  const headers = {};
2916
- for (const [name, value] of Object.entries(entry.requestHeaders)) {
3090
+ for (const [name, value] of Object.entries(requestHeaders)) {
2917
3091
  const normalizedName = name.toLowerCase();
2918
3092
  if (REPLAY_FORBIDDEN_HEADER_NAMES.has(normalizedName) || normalizedName.startsWith("sec-")) {
2919
3093
  continue;
2920
3094
  }
2921
- if (containsRedactionMarker(value)) {
2922
- continue;
2923
- }
2924
3095
  headers[name] = value;
2925
3096
  }
2926
3097
  return Object.keys(headers).length > 0 ? headers : void 0;
@@ -3051,6 +3222,64 @@
3051
3222
  }
3052
3223
  return "unknown";
3053
3224
  }
3225
+ function freshnessCategoryPriority(category) {
3226
+ switch (category) {
3227
+ case "data":
3228
+ return 0;
3229
+ case "unknown":
3230
+ return 1;
3231
+ case "event":
3232
+ return 2;
3233
+ case "contract":
3234
+ return 3;
3235
+ default:
3236
+ return 4;
3237
+ }
3238
+ }
3239
+ function freshnessSourcePriority(source) {
3240
+ switch (source) {
3241
+ case "network":
3242
+ return 0;
3243
+ case "page-data":
3244
+ return 1;
3245
+ case "visible":
3246
+ return 2;
3247
+ case "inline":
3248
+ return 3;
3249
+ default:
3250
+ return 4;
3251
+ }
3252
+ }
3253
+ function rankFreshnessEvidence(candidates, now = Date.now()) {
3254
+ return candidates.slice().sort((left, right) => {
3255
+ const byCategory = freshnessCategoryPriority(left.category) - freshnessCategoryPriority(right.category);
3256
+ if (byCategory !== 0) {
3257
+ return byCategory;
3258
+ }
3259
+ const bySource = freshnessSourcePriority(left.source) - freshnessSourcePriority(right.source);
3260
+ if (bySource !== 0) {
3261
+ return bySource;
3262
+ }
3263
+ const leftTimestamp = parseTimestampCandidate(left.value, now) ?? Number.NEGATIVE_INFINITY;
3264
+ const rightTimestamp = parseTimestampCandidate(right.value, now) ?? Number.NEGATIVE_INFINITY;
3265
+ if (leftTimestamp !== rightTimestamp) {
3266
+ return rightTimestamp - leftTimestamp;
3267
+ }
3268
+ return left.value.localeCompare(right.value);
3269
+ });
3270
+ }
3271
+ function deriveFreshnessConfidence(primary) {
3272
+ if (!primary) {
3273
+ return "low";
3274
+ }
3275
+ if (primary.category === "data" && (primary.source === "network" || primary.source === "page-data")) {
3276
+ return "high";
3277
+ }
3278
+ if (primary.category === "data") {
3279
+ return "medium";
3280
+ }
3281
+ return "low";
3282
+ }
3054
3283
  async function collectPageInspection(tabId, params = {}) {
3055
3284
  return await forwardContentRpc(tabId, "bak.internal.inspectState", params);
3056
3285
  }
@@ -3172,7 +3401,9 @@
3172
3401
  const domVisibleTimestamp = latestTimestampFromCandidates(visibleCandidates, now);
3173
3402
  const latestNetworkTs = latestNetworkTimestamp(tabId);
3174
3403
  const lastMutationAt = typeof inspection.lastMutationAt === "number" ? inspection.lastMutationAt : null;
3175
- const allCandidates = [...visibleCandidates, ...inlineCandidates, ...pageDataCandidates, ...networkCandidates];
3404
+ const allCandidates = rankFreshnessEvidence([...visibleCandidates, ...inlineCandidates, ...pageDataCandidates, ...networkCandidates], now);
3405
+ const primaryEvidence = allCandidates.find((candidate) => parseTimestampCandidate(candidate.value, now) !== null) ?? null;
3406
+ const primaryTimestamp = primaryEvidence ? parseTimestampCandidate(primaryEvidence.value, now) : null;
3176
3407
  return {
3177
3408
  pageLoadedAt: typeof inspection.pageLoadedAt === "number" ? inspection.pageLoadedAt : null,
3178
3409
  lastMutationAt,
@@ -3181,6 +3412,11 @@
3181
3412
  latestPageDataTimestamp,
3182
3413
  latestNetworkDataTimestamp,
3183
3414
  domVisibleTimestamp,
3415
+ primaryTimestamp,
3416
+ primaryCategory: primaryEvidence?.category ?? null,
3417
+ primarySource: primaryEvidence?.source ?? null,
3418
+ confidence: deriveFreshnessConfidence(primaryEvidence),
3419
+ suppressedEvidenceCount: Math.max(0, allCandidates.length - (primaryEvidence ? 1 : 0)),
3184
3420
  assessment: computeFreshnessAssessment({
3185
3421
  latestInlineDataTimestamp,
3186
3422
  latestPageDataTimestamp,
@@ -3613,9 +3849,11 @@
3613
3849
  type: "bak.collectElements",
3614
3850
  debugRichText: config.debugRichText
3615
3851
  });
3616
- const imageData = await captureAlignedTabScreenshot(tab);
3852
+ const screenshot = params.capture === false ? { captureStatus: "skipped" } : await captureAlignedTabScreenshot(tab);
3617
3853
  return {
3618
- imageBase64: includeBase64 ? imageData.replace(/^data:image\/png;base64,/, "") : "",
3854
+ captureStatus: screenshot.captureStatus,
3855
+ captureError: screenshot.captureError,
3856
+ imageBase64: includeBase64 && typeof screenshot.imageData === "string" ? screenshot.imageData.replace(/^data:image\/png;base64,/, "") : void 0,
3619
3857
  elements: elements.elements,
3620
3858
  tabId: tab.id,
3621
3859
  url: tab.url ?? ""
@@ -3739,13 +3977,7 @@
3739
3977
  return await preserveHumanFocus(typeof target.tabId !== "number", async () => {
3740
3978
  const tab = await withTab(target);
3741
3979
  await ensureTabNetworkCapture(tab.id);
3742
- return {
3743
- entries: searchNetworkEntries(
3744
- tab.id,
3745
- String(params.pattern ?? ""),
3746
- typeof params.limit === "number" ? params.limit : 50
3747
- )
3748
- };
3980
+ return searchNetworkEntries(tab.id, String(params.pattern ?? ""), typeof params.limit === "number" ? params.limit : 50);
3749
3981
  });
3750
3982
  }
3751
3983
  case "network.waitFor": {
@@ -3779,34 +4011,32 @@
3779
4011
  return await preserveHumanFocus(typeof target.tabId !== "number", async () => {
3780
4012
  const tab = await withTab(target);
3781
4013
  await ensureTabNetworkCapture(tab.id);
3782
- const entry = getNetworkEntry(tab.id, String(params.id ?? ""));
3783
- if (!entry) {
4014
+ const replayable = getReplayableNetworkRequest(tab.id, String(params.id ?? ""));
4015
+ if (!replayable) {
3784
4016
  throw toError("E_NOT_FOUND", `network entry not found: ${String(params.id ?? "")}`);
3785
4017
  }
3786
- if (entry.requestBodyTruncated === true) {
3787
- throw toError("E_BODY_TOO_LARGE", "captured request body was truncated and cannot be replayed safely", {
3788
- requestId: entry.id,
3789
- requestBytes: entry.requestBytes
3790
- });
3791
- }
3792
- if (containsRedactionMarker(entry.requestBodyPreview)) {
3793
- throw toError("E_EXECUTION", "captured request body was redacted and cannot be replayed safely", {
3794
- requestId: entry.id
3795
- });
4018
+ if (replayable.degradedReason) {
4019
+ return {
4020
+ url: replayable.entry.url,
4021
+ status: 0,
4022
+ ok: false,
4023
+ headers: {},
4024
+ bytes: replayable.entry.requestBytes,
4025
+ truncated: true,
4026
+ degradedReason: replayable.degradedReason
4027
+ };
3796
4028
  }
3797
4029
  const replayed = await executePageWorld(tab.id, "fetch", {
3798
- url: entry.url,
3799
- method: entry.method,
3800
- headers: replayHeadersFromEntry(entry),
3801
- body: entry.requestBodyPreview,
3802
- contentType: (() => {
3803
- const requestHeaders = entry.requestHeaders ?? {};
3804
- const contentTypeHeader = Object.keys(requestHeaders).find((name) => name.toLowerCase() === "content-type");
3805
- return contentTypeHeader ? requestHeaders[contentTypeHeader] : void 0;
3806
- })(),
4030
+ url: replayable.entry.url,
4031
+ method: replayable.entry.method,
4032
+ headers: replayHeadersFromRequestHeaders(replayable.headers),
4033
+ body: replayable.body,
4034
+ contentType: replayable.contentType,
3807
4035
  mode: params.mode,
3808
4036
  timeoutMs: params.timeoutMs,
3809
4037
  maxBytes: params.maxBytes,
4038
+ fullResponse: params.fullResponse === true,
4039
+ auth: params.auth,
3810
4040
  scope: "current"
3811
4041
  });
3812
4042
  const frameResult = replayed.result ?? replayed.results?.find((candidate) => candidate.value || candidate.error);
@@ -3815,7 +4045,13 @@
3815
4045
  }
3816
4046
  const first = frameResult?.value;
3817
4047
  if (!first) {
3818
- throw toError("E_EXECUTION", "network replay returned no response payload");
4048
+ return {
4049
+ url: replayable.entry.url,
4050
+ status: 0,
4051
+ ok: false,
4052
+ headers: {},
4053
+ degradedReason: "network replay returned no response payload"
4054
+ };
3819
4055
  }
3820
4056
  return params.withSchema === "auto" && params.mode === "json" ? await enrichReplayWithSchema(tab.id, String(params.id ?? ""), first) : first;
3821
4057
  });
@@ -2001,7 +2001,7 @@
2001
2001
  for (const [index, table] of htmlTables.entries()) {
2002
2002
  const handle = {
2003
2003
  id: buildTableId(table.closest(".dataTables_wrapper") ? "dataTables" : "html", index),
2004
- name: (table.getAttribute("aria-label") || table.getAttribute("data-testid") || table.id || `table-${index + 1}`).trim(),
2004
+ label: (table.getAttribute("aria-label") || table.getAttribute("data-testid") || table.id || `table-${index + 1}`).trim(),
2005
2005
  kind: table.closest(".dataTables_wrapper") ? "dataTables" : "html",
2006
2006
  selector: table.id ? `#${table.id}` : void 0,
2007
2007
  rowCount: table.querySelectorAll("tbody tr").length || table.querySelectorAll("tr").length,
@@ -2014,7 +2014,7 @@
2014
2014
  const kind = grid.className.includes("ag-") ? "ag-grid" : "aria-grid";
2015
2015
  const handle = {
2016
2016
  id: buildTableId(kind, index),
2017
- name: (grid.getAttribute("aria-label") || grid.getAttribute("data-testid") || grid.id || `grid-${index + 1}`).trim(),
2017
+ label: (grid.getAttribute("aria-label") || grid.getAttribute("data-testid") || grid.id || `grid-${index + 1}`).trim(),
2018
2018
  kind,
2019
2019
  selector: grid.id ? `#${grid.id}` : void 0,
2020
2020
  rowCount: gridRowNodes(grid).length,
@@ -2026,7 +2026,13 @@
2026
2026
  }
2027
2027
  function resolveTable(handleId) {
2028
2028
  const tables = describeTables();
2029
- const handle = tables.find((candidate) => candidate.id === handleId);
2029
+ const normalizedHandleId = handleId.trim().toLowerCase();
2030
+ const handle = tables.find((candidate) => {
2031
+ if (candidate.id === handleId) {
2032
+ return true;
2033
+ }
2034
+ return typeof candidate.label === "string" && candidate.label.trim().toLowerCase() === normalizedHandleId;
2035
+ });
2030
2036
  if (!handle) {
2031
2037
  return null;
2032
2038
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Browser Agent Kit",
4
- "version": "0.6.15",
4
+ "version": "0.6.16",
5
5
  "action": {
6
6
  "default_popup": "popup.html"
7
7
  },
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@flrande/bak-extension",
3
- "version": "0.6.15",
3
+ "version": "0.6.16",
4
4
  "type": "module",
5
5
  "dependencies": {
6
- "@flrande/bak-protocol": "0.6.15"
6
+ "@flrande/bak-protocol": "0.6.16"
7
7
  },
8
8
  "devDependencies": {
9
9
  "@types/chrome": "^0.1.14",