@btraut/browser-bridge 0.7.3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -355,6 +355,71 @@
355
355
  window.location.href = url;
356
356
  return ok();
357
357
  }
358
+ case "drive.locator_point": {
359
+ const { locator } = parseParams();
360
+ const target = resolveLocator(locator);
361
+ if (!target) {
362
+ return buildError("LOCATOR_NOT_FOUND", "Failed to resolve locator.");
363
+ }
364
+ const rect = target.getBoundingClientRect();
365
+ return ok({
366
+ x: rect.left + rect.width / 2,
367
+ y: rect.top + rect.height / 2
368
+ });
369
+ }
370
+ case "drive.snapshot_html": {
371
+ const html = document.documentElement?.outerHTML ?? "";
372
+ return ok({ format: "html", snapshot: html });
373
+ }
374
+ case "drive.type_target_point": {
375
+ const { locator } = parseParams();
376
+ let target = resolveLocator(
377
+ locator
378
+ );
379
+ if (!target) {
380
+ target = activeEditableElement();
381
+ }
382
+ if (!target || !(target instanceof HTMLElement)) {
383
+ return buildError("LOCATOR_NOT_FOUND", "Failed to resolve locator.");
384
+ }
385
+ const tag = target.tagName.toLowerCase();
386
+ if (tag !== "input" && tag !== "textarea" && !target.isContentEditable) {
387
+ return buildError(
388
+ "INVALID_ARGUMENT",
389
+ "Target is not editable (input, textarea, or contenteditable)."
390
+ );
391
+ }
392
+ const rect = target.getBoundingClientRect();
393
+ return ok({
394
+ x: rect.left + rect.width / 2,
395
+ y: rect.top + rect.height / 2
396
+ });
397
+ }
398
+ case "drive.clear_active_editable": {
399
+ const target = activeEditableElement();
400
+ if (!target) {
401
+ return buildError(
402
+ "INVALID_ARGUMENT",
403
+ "No active editable target to clear."
404
+ );
405
+ }
406
+ const tag = target.tagName.toLowerCase();
407
+ if (tag === "input" || tag === "textarea") {
408
+ const input = target;
409
+ input.value = "";
410
+ dispatchValueEvents(input);
411
+ return ok({ ok: true });
412
+ }
413
+ if (target.isContentEditable) {
414
+ target.textContent = "";
415
+ dispatchValueEvents(target);
416
+ return ok({ ok: true });
417
+ }
418
+ return buildError(
419
+ "INVALID_ARGUMENT",
420
+ "Active target is not editable (input, textarea, or contenteditable)."
421
+ );
422
+ }
358
423
  case "drive.click": {
359
424
  const { locator, click_count } = parseParams();
360
425
  const target = resolveLocator(locator);
@@ -493,6 +558,19 @@
493
558
  }
494
559
  return ok();
495
560
  }
561
+ case "drive.detect_field_type": {
562
+ const { locator, selector } = parseParams();
563
+ let target = resolveLocator(
564
+ locator
565
+ );
566
+ if (!target && typeof selector === "string" && selector.length > 0) {
567
+ target = document.querySelector(selector);
568
+ }
569
+ if (!target) {
570
+ return buildError("LOCATOR_NOT_FOUND", "Failed to resolve locator.");
571
+ }
572
+ return ok({ fieldType: detectFieldType(target) });
573
+ }
496
574
  case "drive.fill_form": {
497
575
  const { fields } = parseParams();
498
576
  if (!Array.isArray(fields) || fields.length === 0) {
@@ -815,12 +893,16 @@
815
893
  }
816
894
  case "drive.go_back":
817
895
  case "drive.back": {
818
- history.back();
896
+ window.setTimeout(() => {
897
+ history.back();
898
+ }, 0);
819
899
  return ok();
820
900
  }
821
901
  case "drive.go_forward":
822
902
  case "drive.forward": {
823
- history.forward();
903
+ window.setTimeout(() => {
904
+ history.forward();
905
+ }, 0);
824
906
  return ok();
825
907
  }
826
908
  default:
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/content.ts"],
4
- "sourcesContent": ["type DriveErrorInfo = {\n code: string;\n message: string;\n retryable: boolean;\n details?: Record<string, unknown>;\n};\n\ntype ContentResult =\n | { ok: true; result?: unknown }\n | { ok: false; error: DriveErrorInfo };\n\nexport const runDriveAction = async (\n action: string,\n params: Record<string, unknown> | undefined\n): Promise<ContentResult> => {\n const buildError = (\n code: string,\n message: string,\n details?: Record<string, unknown>\n ): ContentResult => ({\n ok: false,\n error: {\n code,\n message,\n retryable: false,\n ...(details ? { details } : {}),\n },\n });\n\n const ok = (result?: unknown): ContentResult => ({ ok: true, result });\n\n const escapeSelector = (value: string): string => {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value);\n }\n return value.replace(/[\\\\\"']/g, '\\\\$&');\n };\n\n const isVisible = (element: Element): boolean => {\n if (!(element instanceof HTMLElement)) {\n return false;\n }\n const style = window.getComputedStyle(element);\n if (style.visibility === 'hidden' || style.display === 'none') {\n return false;\n }\n const rect = element.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) {\n return false;\n }\n if (\n element.offsetWidth === 0 &&\n element.offsetHeight === 0 &&\n element.getClientRects().length === 0\n ) {\n return false;\n }\n let current: HTMLElement | null = element;\n while (current) {\n const style = window.getComputedStyle(current);\n if (style.display === 'none') {\n return false;\n }\n if (style.visibility === 'hidden' || style.visibility === 'collapse') {\n return false;\n }\n const opacity = Number.parseFloat(style.opacity ?? '1');\n if (Number.isFinite(opacity) && opacity <= 0) {\n return false;\n }\n current = current.parentElement;\n }\n return true;\n };\n\n // Heuristic guard against unsafe regex patterns to avoid ReDoS in url_matches.\n const buildUrlMatcher = (\n pattern: string\n ):\n | { ok: true; matcher: (url: string) => boolean }\n | { ok: false; error: ContentResult } => {\n const maxLength = 256;\n if (pattern.length > maxLength) {\n return {\n ok: false,\n error: buildError(\n 'INVALID_ARGUMENT',\n `url_matches pattern exceeds ${maxLength} characters.`\n ),\n };\n }\n const nestedQuantifiers =\n /\\((?:[^()\\\\]|\\\\.)*[+*{](?:[^()\\\\]|\\\\.)*\\)[+*{]/.test(pattern);\n const repeatedWildcards = /(\\.\\*.*\\.\\*)|(\\.\\+.*\\.\\+)/.test(pattern);\n if (nestedQuantifiers || repeatedWildcards) {\n return {\n ok: false,\n error: buildError(\n 'INVALID_ARGUMENT',\n 'url_matches pattern rejected due to unsafe regex complexity.'\n ),\n };\n }\n try {\n const regex = new RegExp(pattern);\n return { ok: true, matcher: (url: string) => regex.test(url) };\n } catch {\n return { ok: true, matcher: (url: string) => url.includes(pattern) };\n }\n };\n\n const findByText = (text: string): Element | null => {\n const tree = document.createTreeWalker(\n document.body,\n NodeFilter.SHOW_ELEMENT\n );\n let node = tree.nextNode();\n while (node) {\n const element = node as Element;\n if (element.textContent && element.textContent.includes(text)) {\n return element;\n }\n node = tree.nextNode();\n }\n return null;\n };\n\n const findByRole = (locator: Record<string, unknown>): Element | null => {\n const role = locator.role;\n if (!role || typeof role !== 'object') {\n return null;\n }\n const roleName = (role as Record<string, unknown>).name;\n const roleValue = (role as Record<string, unknown>).value;\n if (typeof roleName !== 'string' || roleName.length === 0) {\n return null;\n }\n const candidates = Array.from(\n document.querySelectorAll(`[role=\"${escapeSelector(roleName)}\"]`)\n );\n if (typeof roleValue !== 'string' || roleValue.length === 0) {\n return candidates[0] ?? null;\n }\n return (\n candidates.find((candidate) => {\n const label = candidate.getAttribute('aria-label') ?? '';\n const text = candidate.textContent ?? '';\n return label.includes(roleValue) || text.includes(roleValue);\n }) ?? null\n );\n };\n\n const resolveLocator = (\n locator: Record<string, unknown> | undefined\n ): Element | null => {\n if (!locator) {\n return null;\n }\n const ref = locator.ref;\n if (typeof ref === 'string' && ref.length > 0) {\n const normalized = ref.startsWith('@') ? ref : `@${ref}`;\n const selector = `[data-bv-ref=\"${escapeSelector(normalized)}\"]`;\n const found = document.querySelector(selector);\n if (found) {\n return found;\n }\n }\n const testid = locator.testid;\n if (typeof testid === 'string' && testid.length > 0) {\n const selector = `[data-testid=\"${escapeSelector(testid)}\"]`;\n const found = document.querySelector(selector);\n if (found) {\n return found;\n }\n }\n const css = locator.css;\n if (typeof css === 'string' && css.length > 0) {\n const found = document.querySelector(css);\n if (found) {\n return found;\n }\n }\n const byRole = findByRole(locator);\n if (byRole) {\n return byRole;\n }\n const text = locator.text;\n if (typeof text === 'string' && text.length > 0) {\n return findByText(text);\n }\n return null;\n };\n\n const coerceBoolean = (value: string | boolean): boolean => {\n if (typeof value === 'boolean') {\n return value;\n }\n const normalized = value.trim().toLowerCase();\n return ['true', '1', 'yes', 'y', 'on', 'checked'].includes(normalized);\n };\n\n const dispatchValueEvents = (element: HTMLElement): void => {\n element.dispatchEvent(new Event('input', { bubbles: true }));\n element.dispatchEvent(new Event('change', { bubbles: true }));\n };\n\n const selectOption = (select: HTMLSelectElement, value: string): boolean => {\n const option = Array.from(select.options).find(\n (entry) => entry.value === value || entry.text === value\n );\n if (!option) {\n return false;\n }\n select.value = option.value;\n dispatchValueEvents(select);\n return true;\n };\n\n const selectOptionByIndex = (\n select: HTMLSelectElement,\n index: number\n ): boolean => {\n if (!Number.isInteger(index)) {\n return false;\n }\n if (index < 0 || index >= select.options.length) {\n return false;\n }\n select.selectedIndex = index;\n dispatchValueEvents(select);\n return true;\n };\n\n const setTextValue = (\n element: HTMLElement,\n value: string,\n clear: boolean\n ): boolean => {\n const tag = element.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea') {\n const input = element as HTMLInputElement | HTMLTextAreaElement;\n if (clear) {\n input.value = '';\n }\n input.value = `${input.value}${value}`;\n dispatchValueEvents(input);\n return true;\n }\n if (element.isContentEditable) {\n if (clear) {\n element.textContent = '';\n }\n element.textContent = `${element.textContent ?? ''}${value}`;\n dispatchValueEvents(element);\n return true;\n }\n return false;\n };\n\n const detectFieldType = (\n element: Element\n ): 'text' | 'select' | 'checkbox' | 'radio' | 'contentEditable' => {\n if (element instanceof HTMLSelectElement) {\n return 'select';\n }\n if (element instanceof HTMLInputElement) {\n const type = element.type.toLowerCase();\n if (type === 'checkbox') {\n return 'checkbox';\n }\n if (type === 'radio') {\n return 'radio';\n }\n return 'text';\n }\n if (element instanceof HTMLTextAreaElement) {\n return 'text';\n }\n if (element instanceof HTMLElement && element.isContentEditable) {\n return 'contentEditable';\n }\n return 'text';\n };\n\n const submitIfRequested = (element: Element): void => {\n const form = element.closest('form');\n if (form && form instanceof HTMLFormElement) {\n form.requestSubmit();\n } else if (element instanceof HTMLElement) {\n element.dispatchEvent(\n new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })\n );\n }\n };\n\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => window.setTimeout(resolve, ms));\n\n const dispatchPointer = (\n element: Element,\n type: string,\n x: number,\n y: number\n ): void => {\n element.dispatchEvent(\n new PointerEvent(type, {\n bubbles: true,\n cancelable: true,\n clientX: x,\n clientY: y,\n button: 0,\n })\n );\n };\n\n const dispatchDrag = (\n element: Element,\n type: string,\n x: number,\n y: number,\n dataTransfer?: DataTransfer\n ): void => {\n try {\n element.dispatchEvent(\n new DragEvent(type, {\n bubbles: true,\n cancelable: true,\n clientX: x,\n clientY: y,\n dataTransfer,\n })\n );\n } catch {\n element.dispatchEvent(\n new Event(type, { bubbles: true, cancelable: true })\n );\n }\n };\n\n const keyToCode = (key: string): string => {\n const map: Record<string, string> = {\n Enter: 'Enter',\n Tab: 'Tab',\n Escape: 'Escape',\n Esc: 'Escape',\n Backspace: 'Backspace',\n Delete: 'Delete',\n ArrowUp: 'ArrowUp',\n ArrowDown: 'ArrowDown',\n ArrowLeft: 'ArrowLeft',\n ArrowRight: 'ArrowRight',\n Home: 'Home',\n End: 'End',\n PageUp: 'PageUp',\n PageDown: 'PageDown',\n ' ': 'Space',\n Space: 'Space',\n };\n if (map[key]) {\n return map[key];\n }\n if (key.length === 1) {\n if (/[a-zA-Z]/.test(key)) {\n return `Key${key.toUpperCase()}`;\n }\n if (/[0-9]/.test(key)) {\n return `Digit${key}`;\n }\n }\n return key;\n };\n\n const normalizeModifiers = (\n modifiers: unknown\n ): { ctrl: boolean; alt: boolean; shift: boolean; meta: boolean } => {\n const state = { ctrl: false, alt: false, shift: false, meta: false };\n if (Array.isArray(modifiers)) {\n modifiers.forEach((modifier) => {\n if (typeof modifier !== 'string') {\n return;\n }\n const normalized = modifier.toLowerCase();\n if (normalized === 'ctrl') {\n state.ctrl = true;\n } else if (normalized === 'alt') {\n state.alt = true;\n } else if (normalized === 'shift') {\n state.shift = true;\n } else if (normalized === 'meta') {\n state.meta = true;\n }\n });\n return state;\n }\n if (modifiers && typeof modifiers === 'object') {\n const record = modifiers as Record<string, unknown>;\n state.ctrl = Boolean(record.ctrl);\n state.alt = Boolean(record.alt);\n state.shift = Boolean(record.shift);\n state.meta = Boolean(record.meta);\n }\n return state;\n };\n\n const activeEditableElement = (): HTMLElement | null => {\n const active = document.activeElement;\n if (!active || !(active instanceof HTMLElement)) {\n return null;\n }\n const tag = active.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea' || active.isContentEditable) {\n return active;\n }\n return null;\n };\n\n const parseParams = (): Record<string, unknown> => params ?? {};\n\n try {\n switch (action) {\n case 'drive.navigate': {\n const { url } = parseParams();\n if (typeof url !== 'string' || url.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'url must be a non-empty string.'\n );\n }\n window.location.href = url;\n return ok();\n }\n case 'drive.click': {\n const { locator, click_count } = parseParams();\n const target = resolveLocator(locator as Record<string, unknown>);\n if (!target) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n const count =\n typeof click_count === 'number' && Number.isFinite(click_count)\n ? Math.max(1, Math.floor(click_count))\n : 1;\n // Clicking elements that trigger JS dialogs (alert/confirm/prompt) can\n // block the renderer thread before we can reply to the background script,\n // causing the caller to time out. Defer the click to the next tick so\n // we can respond immediately.\n window.setTimeout(() => {\n try {\n if (target instanceof HTMLElement) {\n try {\n target.focus({ preventScroll: true });\n } catch {\n target.focus();\n }\n }\n for (let i = 0; i < count; i += 1) {\n (target as HTMLElement).click();\n }\n } catch {\n // Best-effort: the element may have disappeared or navigation occurred.\n }\n }, 0);\n return ok();\n }\n case 'drive.hover': {\n const { locator, delay_ms } = parseParams();\n const target = resolveLocator(locator as Record<string, unknown>);\n if (!target) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n const rect = target.getBoundingClientRect();\n const centerX = rect.left + rect.width / 2;\n const centerY = rect.top + rect.height / 2;\n const eventInit = {\n bubbles: true,\n cancelable: true,\n clientX: centerX,\n clientY: centerY,\n };\n target.dispatchEvent(new MouseEvent('mouseover', eventInit));\n target.dispatchEvent(\n new MouseEvent('mouseenter', { ...eventInit, bubbles: false })\n );\n target.dispatchEvent(new MouseEvent('mousemove', eventInit));\n if (typeof delay_ms === 'number' && Number.isFinite(delay_ms)) {\n const waitMs = Math.min(Math.max(delay_ms, 0), 10000);\n if (waitMs > 0) {\n await sleep(waitMs);\n }\n }\n const html = document.documentElement?.outerHTML ?? '';\n return ok({ format: 'html', snapshot: html });\n }\n case 'drive.select': {\n const { locator, value, text, index } = parseParams();\n const target = resolveLocator(locator as Record<string, unknown>);\n if (!target) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n if (!(target instanceof HTMLSelectElement)) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'Target is not a select element.'\n );\n }\n let applied = false;\n if (typeof index === 'number' && Number.isFinite(index)) {\n applied = selectOptionByIndex(target, Math.trunc(index));\n }\n if (!applied && typeof value === 'string') {\n applied = selectOption(target, value);\n }\n if (!applied && typeof text === 'string') {\n applied = selectOption(target, text);\n }\n if (!applied) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'No matching option found for select.'\n );\n }\n return ok();\n }\n case 'drive.type': {\n const { locator, text, clear, submit } = parseParams();\n if (typeof text !== 'string') {\n return buildError('INVALID_ARGUMENT', 'text must be a string.');\n }\n let target = resolveLocator(\n locator as Record<string, unknown> | undefined\n );\n if (!target) {\n const active = activeEditableElement();\n if (active) {\n target = active;\n }\n }\n if (!target || !(target instanceof HTMLElement)) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n target.focus();\n const tag = target.tagName.toLowerCase();\n const shouldClear = Boolean(clear);\n if (tag === 'input' || tag === 'textarea') {\n const input = target as HTMLInputElement | HTMLTextAreaElement;\n if (shouldClear) {\n input.value = '';\n }\n input.value = `${input.value}${text}`;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n } else if (target.isContentEditable) {\n if (shouldClear) {\n target.textContent = '';\n }\n target.textContent = `${target.textContent ?? ''}${text}`;\n target.dispatchEvent(new Event('input', { bubbles: true }));\n } else {\n return buildError(\n 'INVALID_ARGUMENT',\n 'Target is not editable (input, textarea, or contenteditable).'\n );\n }\n\n if (submit) {\n const form = target.closest('form');\n if (form && form instanceof HTMLFormElement) {\n form.requestSubmit();\n } else {\n target.dispatchEvent(\n new KeyboardEvent('keydown', {\n key: 'Enter',\n bubbles: true,\n })\n );\n }\n }\n\n return ok();\n }\n case 'drive.fill_form': {\n const { fields } = parseParams();\n if (!Array.isArray(fields) || fields.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'fields must be a non-empty array.'\n );\n }\n let filled = 0;\n const errors: string[] = [];\n fields.forEach((field, index) => {\n if (!field || typeof field !== 'object') {\n errors.push(`Field ${index} is not an object.`);\n return;\n }\n const record = field as Record<string, unknown>;\n const selector = record.selector;\n const locator =\n record.locator && typeof record.locator === 'object'\n ? (record.locator as Record<string, unknown>)\n : undefined;\n let element: Element | null = null;\n if (locator) {\n element = resolveLocator(locator);\n }\n if (!element && typeof selector === 'string' && selector.length > 0) {\n element = document.querySelector(selector);\n }\n if (!element) {\n errors.push(`Field ${index} could not be resolved.`);\n return;\n }\n\n const value = record.value;\n if (typeof value !== 'string' && typeof value !== 'boolean') {\n errors.push(`Field ${index} has invalid value.`);\n return;\n }\n\n const type =\n typeof record.type === 'string' && record.type.length > 0\n ? record.type\n : 'auto';\n const resolvedType =\n type === 'auto' ? detectFieldType(element) : type;\n const submit = Boolean(record.submit);\n\n let applied = false;\n if (resolvedType === 'select') {\n if (element instanceof HTMLSelectElement) {\n applied = selectOption(element, String(value));\n }\n } else if (resolvedType === 'checkbox' || resolvedType === 'radio') {\n if (element instanceof HTMLInputElement) {\n const shouldCheck =\n typeof value === 'boolean' ? value : coerceBoolean(value);\n element.checked = shouldCheck;\n dispatchValueEvents(element);\n applied = true;\n }\n } else {\n if (element instanceof HTMLElement) {\n applied = setTextValue(element, String(value), true);\n }\n }\n\n if (!applied) {\n errors.push(`Field ${index} could not be filled.`);\n return;\n }\n\n if (submit) {\n submitIfRequested(element);\n }\n filled += 1;\n });\n\n return ok({\n filled,\n attempted: fields.length,\n errors: errors.length > 0 ? errors : [],\n });\n }\n case 'drive.drag': {\n const { from, to, steps } = parseParams();\n const fromEl = resolveLocator(from as Record<string, unknown>);\n if (!fromEl) {\n return buildError(\n 'LOCATOR_NOT_FOUND',\n 'Failed to resolve drag source.'\n );\n }\n const toEl = resolveLocator(to as Record<string, unknown>);\n if (!toEl) {\n return buildError(\n 'LOCATOR_NOT_FOUND',\n 'Failed to resolve drag target.'\n );\n }\n\n const fromRect = fromEl.getBoundingClientRect();\n const toRect = toEl.getBoundingClientRect();\n const startX = fromRect.left + fromRect.width / 2;\n const startY = fromRect.top + fromRect.height / 2;\n const endX = toRect.left + toRect.width / 2;\n const endY = toRect.top + toRect.height / 2;\n // Defensive bounds in case content script receives unvalidated inputs.\n const totalSteps =\n typeof steps === 'number' && Number.isFinite(steps)\n ? Math.max(1, Math.min(50, Math.floor(steps)))\n : 12;\n let dataTransfer: DataTransfer | undefined;\n try {\n dataTransfer = new DataTransfer();\n } catch {\n dataTransfer = undefined;\n }\n\n dispatchPointer(fromEl, 'pointerdown', startX, startY);\n dispatchDrag(fromEl, 'dragstart', startX, startY, dataTransfer);\n\n for (let i = 1; i <= totalSteps; i += 1) {\n const progress = i / totalSteps;\n const x = startX + (endX - startX) * progress;\n const y = startY + (endY - startY) * progress;\n const target = document.elementFromPoint(x, y) ?? toEl;\n dispatchPointer(target, 'pointermove', x, y);\n dispatchDrag(target, 'dragover', x, y, dataTransfer);\n await sleep(10);\n }\n\n const dropTarget = document.elementFromPoint(endX, endY) ?? toEl;\n dispatchDrag(dropTarget, 'drop', endX, endY, dataTransfer);\n dispatchPointer(dropTarget, 'pointerup', endX, endY);\n dispatchDrag(fromEl, 'dragend', endX, endY, dataTransfer);\n return ok();\n }\n case 'drive.key_press': {\n const { key, modifiers } = parseParams();\n if (typeof key !== 'string' || key.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'key must be a non-empty string.'\n );\n }\n const target =\n document.activeElement instanceof HTMLElement\n ? document.activeElement\n : document.body;\n if (!target) {\n return buildError('INVALID_ARGUMENT', 'No target for key press.');\n }\n const mods = normalizeModifiers(modifiers);\n const eventInit = {\n key,\n code: keyToCode(key),\n bubbles: true,\n cancelable: true,\n ctrlKey: mods.ctrl,\n altKey: mods.alt,\n shiftKey: mods.shift,\n metaKey: mods.meta,\n };\n target.dispatchEvent(new KeyboardEvent('keydown', eventInit));\n target.dispatchEvent(new KeyboardEvent('keyup', eventInit));\n return ok();\n }\n case 'drive.key': {\n const { key, modifiers, repeat } = parseParams();\n if (typeof key !== 'string' || key.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'key must be a non-empty string.'\n );\n }\n const target =\n document.activeElement instanceof HTMLElement\n ? document.activeElement\n : document.body;\n if (!target) {\n return buildError('INVALID_ARGUMENT', 'No target for key press.');\n }\n const mods = normalizeModifiers(modifiers);\n const eventInit = {\n key,\n code: keyToCode(key),\n bubbles: true,\n cancelable: true,\n ctrlKey: mods.ctrl,\n altKey: mods.alt,\n shiftKey: mods.shift,\n metaKey: mods.meta,\n };\n const count =\n typeof repeat === 'number' && Number.isFinite(repeat)\n ? Math.max(1, Math.min(50, Math.floor(repeat)))\n : 1;\n for (let i = 0; i < count; i += 1) {\n target.dispatchEvent(new KeyboardEvent('keydown', eventInit));\n target.dispatchEvent(new KeyboardEvent('keyup', eventInit));\n }\n return ok();\n }\n case 'drive.scroll': {\n const { delta_x, delta_y, top, left, behavior } = parseParams();\n const scrollBehavior =\n behavior === 'smooth' || behavior === 'auto' ? behavior : undefined;\n if (typeof top === 'number' || typeof left === 'number') {\n window.scrollTo({\n top: typeof top === 'number' ? top : undefined,\n left: typeof left === 'number' ? left : undefined,\n behavior: scrollBehavior,\n });\n return ok();\n }\n if (typeof delta_x === 'number' || typeof delta_y === 'number') {\n window.scrollBy({\n left: typeof delta_x === 'number' ? delta_x : 0,\n top: typeof delta_y === 'number' ? delta_y : 0,\n behavior: scrollBehavior,\n });\n return ok();\n }\n return buildError(\n 'INVALID_ARGUMENT',\n 'scroll requires delta_x/delta_y or top/left.'\n );\n }\n case 'drive.screenshot_meta': {\n const root = document.documentElement;\n const body = document.body;\n const scrollWidth = Math.max(\n root?.scrollWidth ?? 0,\n root?.clientWidth ?? 0,\n body?.scrollWidth ?? 0,\n body?.clientWidth ?? 0\n );\n const scrollHeight = Math.max(\n root?.scrollHeight ?? 0,\n root?.clientHeight ?? 0,\n body?.scrollHeight ?? 0,\n body?.clientHeight ?? 0\n );\n\n return ok({\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n scrollWidth,\n scrollHeight,\n devicePixelRatio:\n typeof window.devicePixelRatio === 'number' &&\n Number.isFinite(window.devicePixelRatio) &&\n window.devicePixelRatio > 0\n ? window.devicePixelRatio\n : 1,\n });\n }\n case 'drive.screenshot_element': {\n const { selector } = parseParams();\n if (typeof selector !== 'string' || selector.trim().length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'selector must be a non-empty string.'\n );\n }\n\n let element: Element | null = null;\n try {\n element = document.querySelector(selector);\n } catch {\n return buildError(\n 'INVALID_ARGUMENT',\n 'selector must be a valid CSS selector.'\n );\n }\n\n if (!element) {\n return buildError('INVALID_ARGUMENT', 'No element matched selector.');\n }\n\n try {\n (element as HTMLElement).scrollIntoView?.({\n block: 'center',\n inline: 'center',\n });\n } catch {\n // Ignore scroll failures; capture whatever is visible.\n }\n\n const rect = element.getBoundingClientRect();\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n const dpr =\n typeof window.devicePixelRatio === 'number' &&\n Number.isFinite(window.devicePixelRatio) &&\n window.devicePixelRatio > 0\n ? window.devicePixelRatio\n : 1;\n\n return ok({\n selector,\n pageX: rect.left + scrollX,\n pageY: rect.top + scrollY,\n width: rect.width,\n height: rect.height,\n viewportLeft: rect.left,\n viewportTop: rect.top,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n scrollX,\n scrollY,\n devicePixelRatio: dpr,\n });\n }\n case 'drive.wait_for': {\n const { condition, timeout_ms } = parseParams();\n if (!condition || typeof condition !== 'object') {\n return buildError('INVALID_ARGUMENT', 'condition must be an object.');\n }\n const kind = (condition as Record<string, unknown>).kind;\n const value = (condition as Record<string, unknown>).value;\n if (\n typeof kind !== 'string' ||\n !['locator_visible', 'text_present', 'url_matches'].includes(kind)\n ) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'condition.kind must be locator_visible, text_present, or url_matches.'\n );\n }\n if (typeof value !== 'string' || value.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'condition.value must be a non-empty string.'\n );\n }\n const timeout =\n typeof timeout_ms === 'number' && Number.isFinite(timeout_ms)\n ? Math.max(0, timeout_ms)\n : 30000;\n const start = Date.now();\n const urlMatcher =\n kind === 'url_matches' ? buildUrlMatcher(value) : null;\n if (urlMatcher && !urlMatcher.ok) {\n return urlMatcher.error;\n }\n\n const checkCondition = (): boolean => {\n if (kind === 'text_present') {\n return (document.body?.innerText ?? '').includes(value);\n }\n if (kind === 'url_matches') {\n return urlMatcher\n ? urlMatcher.matcher(window.location.href)\n : window.location.href.includes(value);\n }\n const selector = value;\n const element = document.querySelector(selector);\n return Boolean(element && isVisible(element));\n };\n\n return await new Promise<ContentResult>((resolve) => {\n const tick = () => {\n if (checkCondition()) {\n resolve(ok());\n return;\n }\n if (Date.now() - start >= timeout) {\n resolve(\n buildError('TIMEOUT', `wait_for timed out after ${timeout}ms.`)\n );\n return;\n }\n window.setTimeout(tick, 100);\n };\n tick();\n });\n }\n case 'drive.go_back':\n case 'drive.back': {\n // chrome.tabs.goBack appears unreliable in some Chrome builds/profiles.\n // Driving history from within the page is more consistent.\n history.back();\n return ok();\n }\n case 'drive.go_forward':\n case 'drive.forward': {\n history.forward();\n return ok();\n }\n default:\n return buildError('INVALID_ARGUMENT', `Unsupported action ${action}.`);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return buildError('EVALUATION_FAILED', message);\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nif (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {\n chrome.runtime.onMessage.addListener(\n (\n message: Record<string, unknown>,\n _sender: unknown,\n sendResponse: (response: ContentResult) => void\n ) => {\n if (!isRecord(message) || typeof message.action !== 'string') {\n sendResponse({\n ok: false,\n error: {\n code: 'INVALID_ARGUMENT',\n message: 'Invalid content script request.',\n retryable: false,\n },\n });\n return;\n }\n\n void runDriveAction(\n message.action,\n message.params as Record<string, unknown>\n )\n .then(sendResponse)\n .catch((error) => {\n const messageText =\n error instanceof Error ? error.message : 'Unknown error';\n sendResponse({\n ok: false,\n error: {\n code: 'EVALUATION_FAILED',\n message: messageText,\n retryable: false,\n },\n });\n });\n\n return true;\n }\n );\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["type DriveErrorInfo = {\n code: string;\n message: string;\n retryable: boolean;\n details?: Record<string, unknown>;\n};\n\ntype ContentResult =\n | { ok: true; result?: unknown }\n | { ok: false; error: DriveErrorInfo };\n\nexport const runDriveAction = async (\n action: string,\n params: Record<string, unknown> | undefined\n): Promise<ContentResult> => {\n const buildError = (\n code: string,\n message: string,\n details?: Record<string, unknown>\n ): ContentResult => ({\n ok: false,\n error: {\n code,\n message,\n retryable: false,\n ...(details ? { details } : {}),\n },\n });\n\n const ok = (result?: unknown): ContentResult => ({ ok: true, result });\n\n const escapeSelector = (value: string): string => {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value);\n }\n return value.replace(/[\\\\\"']/g, '\\\\$&');\n };\n\n const isVisible = (element: Element): boolean => {\n if (!(element instanceof HTMLElement)) {\n return false;\n }\n const style = window.getComputedStyle(element);\n if (style.visibility === 'hidden' || style.display === 'none') {\n return false;\n }\n const rect = element.getBoundingClientRect();\n if (rect.width === 0 && rect.height === 0) {\n return false;\n }\n if (\n element.offsetWidth === 0 &&\n element.offsetHeight === 0 &&\n element.getClientRects().length === 0\n ) {\n return false;\n }\n let current: HTMLElement | null = element;\n while (current) {\n const style = window.getComputedStyle(current);\n if (style.display === 'none') {\n return false;\n }\n if (style.visibility === 'hidden' || style.visibility === 'collapse') {\n return false;\n }\n const opacity = Number.parseFloat(style.opacity ?? '1');\n if (Number.isFinite(opacity) && opacity <= 0) {\n return false;\n }\n current = current.parentElement;\n }\n return true;\n };\n\n // Heuristic guard against unsafe regex patterns to avoid ReDoS in url_matches.\n const buildUrlMatcher = (\n pattern: string\n ):\n | { ok: true; matcher: (url: string) => boolean }\n | { ok: false; error: ContentResult } => {\n const maxLength = 256;\n if (pattern.length > maxLength) {\n return {\n ok: false,\n error: buildError(\n 'INVALID_ARGUMENT',\n `url_matches pattern exceeds ${maxLength} characters.`\n ),\n };\n }\n const nestedQuantifiers =\n /\\((?:[^()\\\\]|\\\\.)*[+*{](?:[^()\\\\]|\\\\.)*\\)[+*{]/.test(pattern);\n const repeatedWildcards = /(\\.\\*.*\\.\\*)|(\\.\\+.*\\.\\+)/.test(pattern);\n if (nestedQuantifiers || repeatedWildcards) {\n return {\n ok: false,\n error: buildError(\n 'INVALID_ARGUMENT',\n 'url_matches pattern rejected due to unsafe regex complexity.'\n ),\n };\n }\n try {\n const regex = new RegExp(pattern);\n return { ok: true, matcher: (url: string) => regex.test(url) };\n } catch {\n return { ok: true, matcher: (url: string) => url.includes(pattern) };\n }\n };\n\n const findByText = (text: string): Element | null => {\n const tree = document.createTreeWalker(\n document.body,\n NodeFilter.SHOW_ELEMENT\n );\n let node = tree.nextNode();\n while (node) {\n const element = node as Element;\n if (element.textContent && element.textContent.includes(text)) {\n return element;\n }\n node = tree.nextNode();\n }\n return null;\n };\n\n const findByRole = (locator: Record<string, unknown>): Element | null => {\n const role = locator.role;\n if (!role || typeof role !== 'object') {\n return null;\n }\n const roleName = (role as Record<string, unknown>).name;\n const roleValue = (role as Record<string, unknown>).value;\n if (typeof roleName !== 'string' || roleName.length === 0) {\n return null;\n }\n const candidates = Array.from(\n document.querySelectorAll(`[role=\"${escapeSelector(roleName)}\"]`)\n );\n if (typeof roleValue !== 'string' || roleValue.length === 0) {\n return candidates[0] ?? null;\n }\n return (\n candidates.find((candidate) => {\n const label = candidate.getAttribute('aria-label') ?? '';\n const text = candidate.textContent ?? '';\n return label.includes(roleValue) || text.includes(roleValue);\n }) ?? null\n );\n };\n\n const resolveLocator = (\n locator: Record<string, unknown> | undefined\n ): Element | null => {\n if (!locator) {\n return null;\n }\n const ref = locator.ref;\n if (typeof ref === 'string' && ref.length > 0) {\n const normalized = ref.startsWith('@') ? ref : `@${ref}`;\n const selector = `[data-bv-ref=\"${escapeSelector(normalized)}\"]`;\n const found = document.querySelector(selector);\n if (found) {\n return found;\n }\n }\n const testid = locator.testid;\n if (typeof testid === 'string' && testid.length > 0) {\n const selector = `[data-testid=\"${escapeSelector(testid)}\"]`;\n const found = document.querySelector(selector);\n if (found) {\n return found;\n }\n }\n const css = locator.css;\n if (typeof css === 'string' && css.length > 0) {\n const found = document.querySelector(css);\n if (found) {\n return found;\n }\n }\n const byRole = findByRole(locator);\n if (byRole) {\n return byRole;\n }\n const text = locator.text;\n if (typeof text === 'string' && text.length > 0) {\n return findByText(text);\n }\n return null;\n };\n\n const coerceBoolean = (value: string | boolean): boolean => {\n if (typeof value === 'boolean') {\n return value;\n }\n const normalized = value.trim().toLowerCase();\n return ['true', '1', 'yes', 'y', 'on', 'checked'].includes(normalized);\n };\n\n const dispatchValueEvents = (element: HTMLElement): void => {\n element.dispatchEvent(new Event('input', { bubbles: true }));\n element.dispatchEvent(new Event('change', { bubbles: true }));\n };\n\n const selectOption = (select: HTMLSelectElement, value: string): boolean => {\n const option = Array.from(select.options).find(\n (entry) => entry.value === value || entry.text === value\n );\n if (!option) {\n return false;\n }\n select.value = option.value;\n dispatchValueEvents(select);\n return true;\n };\n\n const selectOptionByIndex = (\n select: HTMLSelectElement,\n index: number\n ): boolean => {\n if (!Number.isInteger(index)) {\n return false;\n }\n if (index < 0 || index >= select.options.length) {\n return false;\n }\n select.selectedIndex = index;\n dispatchValueEvents(select);\n return true;\n };\n\n const setTextValue = (\n element: HTMLElement,\n value: string,\n clear: boolean\n ): boolean => {\n const tag = element.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea') {\n const input = element as HTMLInputElement | HTMLTextAreaElement;\n if (clear) {\n input.value = '';\n }\n input.value = `${input.value}${value}`;\n dispatchValueEvents(input);\n return true;\n }\n if (element.isContentEditable) {\n if (clear) {\n element.textContent = '';\n }\n element.textContent = `${element.textContent ?? ''}${value}`;\n dispatchValueEvents(element);\n return true;\n }\n return false;\n };\n\n const detectFieldType = (\n element: Element\n ): 'text' | 'select' | 'checkbox' | 'radio' | 'contentEditable' => {\n if (element instanceof HTMLSelectElement) {\n return 'select';\n }\n if (element instanceof HTMLInputElement) {\n const type = element.type.toLowerCase();\n if (type === 'checkbox') {\n return 'checkbox';\n }\n if (type === 'radio') {\n return 'radio';\n }\n return 'text';\n }\n if (element instanceof HTMLTextAreaElement) {\n return 'text';\n }\n if (element instanceof HTMLElement && element.isContentEditable) {\n return 'contentEditable';\n }\n return 'text';\n };\n\n const submitIfRequested = (element: Element): void => {\n const form = element.closest('form');\n if (form && form instanceof HTMLFormElement) {\n form.requestSubmit();\n } else if (element instanceof HTMLElement) {\n element.dispatchEvent(\n new KeyboardEvent('keydown', { key: 'Enter', bubbles: true })\n );\n }\n };\n\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => window.setTimeout(resolve, ms));\n\n const dispatchPointer = (\n element: Element,\n type: string,\n x: number,\n y: number\n ): void => {\n element.dispatchEvent(\n new PointerEvent(type, {\n bubbles: true,\n cancelable: true,\n clientX: x,\n clientY: y,\n button: 0,\n })\n );\n };\n\n const dispatchDrag = (\n element: Element,\n type: string,\n x: number,\n y: number,\n dataTransfer?: DataTransfer\n ): void => {\n try {\n element.dispatchEvent(\n new DragEvent(type, {\n bubbles: true,\n cancelable: true,\n clientX: x,\n clientY: y,\n dataTransfer,\n })\n );\n } catch {\n element.dispatchEvent(\n new Event(type, { bubbles: true, cancelable: true })\n );\n }\n };\n\n const keyToCode = (key: string): string => {\n const map: Record<string, string> = {\n Enter: 'Enter',\n Tab: 'Tab',\n Escape: 'Escape',\n Esc: 'Escape',\n Backspace: 'Backspace',\n Delete: 'Delete',\n ArrowUp: 'ArrowUp',\n ArrowDown: 'ArrowDown',\n ArrowLeft: 'ArrowLeft',\n ArrowRight: 'ArrowRight',\n Home: 'Home',\n End: 'End',\n PageUp: 'PageUp',\n PageDown: 'PageDown',\n ' ': 'Space',\n Space: 'Space',\n };\n if (map[key]) {\n return map[key];\n }\n if (key.length === 1) {\n if (/[a-zA-Z]/.test(key)) {\n return `Key${key.toUpperCase()}`;\n }\n if (/[0-9]/.test(key)) {\n return `Digit${key}`;\n }\n }\n return key;\n };\n\n const normalizeModifiers = (\n modifiers: unknown\n ): { ctrl: boolean; alt: boolean; shift: boolean; meta: boolean } => {\n const state = { ctrl: false, alt: false, shift: false, meta: false };\n if (Array.isArray(modifiers)) {\n modifiers.forEach((modifier) => {\n if (typeof modifier !== 'string') {\n return;\n }\n const normalized = modifier.toLowerCase();\n if (normalized === 'ctrl') {\n state.ctrl = true;\n } else if (normalized === 'alt') {\n state.alt = true;\n } else if (normalized === 'shift') {\n state.shift = true;\n } else if (normalized === 'meta') {\n state.meta = true;\n }\n });\n return state;\n }\n if (modifiers && typeof modifiers === 'object') {\n const record = modifiers as Record<string, unknown>;\n state.ctrl = Boolean(record.ctrl);\n state.alt = Boolean(record.alt);\n state.shift = Boolean(record.shift);\n state.meta = Boolean(record.meta);\n }\n return state;\n };\n\n const activeEditableElement = (): HTMLElement | null => {\n const active = document.activeElement;\n if (!active || !(active instanceof HTMLElement)) {\n return null;\n }\n const tag = active.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea' || active.isContentEditable) {\n return active;\n }\n return null;\n };\n\n const parseParams = (): Record<string, unknown> => params ?? {};\n\n try {\n switch (action) {\n case 'drive.navigate': {\n const { url } = parseParams();\n if (typeof url !== 'string' || url.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'url must be a non-empty string.'\n );\n }\n window.location.href = url;\n return ok();\n }\n case 'drive.locator_point': {\n const { locator } = parseParams();\n const target = resolveLocator(locator as Record<string, unknown>);\n if (!target) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n const rect = target.getBoundingClientRect();\n return ok({\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n });\n }\n case 'drive.snapshot_html': {\n const html = document.documentElement?.outerHTML ?? '';\n return ok({ format: 'html', snapshot: html });\n }\n case 'drive.type_target_point': {\n const { locator } = parseParams();\n let target = resolveLocator(\n locator as Record<string, unknown> | undefined\n );\n if (!target) {\n target = activeEditableElement();\n }\n if (!target || !(target instanceof HTMLElement)) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n const tag = target.tagName.toLowerCase();\n if (\n tag !== 'input' &&\n tag !== 'textarea' &&\n !target.isContentEditable\n ) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'Target is not editable (input, textarea, or contenteditable).'\n );\n }\n const rect = target.getBoundingClientRect();\n return ok({\n x: rect.left + rect.width / 2,\n y: rect.top + rect.height / 2,\n });\n }\n case 'drive.clear_active_editable': {\n const target = activeEditableElement();\n if (!target) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'No active editable target to clear.'\n );\n }\n const tag = target.tagName.toLowerCase();\n if (tag === 'input' || tag === 'textarea') {\n const input = target as HTMLInputElement | HTMLTextAreaElement;\n input.value = '';\n dispatchValueEvents(input);\n return ok({ ok: true });\n }\n if (target.isContentEditable) {\n target.textContent = '';\n dispatchValueEvents(target);\n return ok({ ok: true });\n }\n return buildError(\n 'INVALID_ARGUMENT',\n 'Active target is not editable (input, textarea, or contenteditable).'\n );\n }\n case 'drive.click': {\n const { locator, click_count } = parseParams();\n const target = resolveLocator(locator as Record<string, unknown>);\n if (!target) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n const count =\n typeof click_count === 'number' && Number.isFinite(click_count)\n ? Math.max(1, Math.floor(click_count))\n : 1;\n // Clicking elements that trigger JS dialogs (alert/confirm/prompt) can\n // block the renderer thread before we can reply to the background script,\n // causing the caller to time out. Defer the click to the next tick so\n // we can respond immediately.\n window.setTimeout(() => {\n try {\n if (target instanceof HTMLElement) {\n try {\n target.focus({ preventScroll: true });\n } catch {\n target.focus();\n }\n }\n for (let i = 0; i < count; i += 1) {\n (target as HTMLElement).click();\n }\n } catch {\n // Best-effort: the element may have disappeared or navigation occurred.\n }\n }, 0);\n return ok();\n }\n case 'drive.hover': {\n const { locator, delay_ms } = parseParams();\n const target = resolveLocator(locator as Record<string, unknown>);\n if (!target) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n const rect = target.getBoundingClientRect();\n const centerX = rect.left + rect.width / 2;\n const centerY = rect.top + rect.height / 2;\n const eventInit = {\n bubbles: true,\n cancelable: true,\n clientX: centerX,\n clientY: centerY,\n };\n target.dispatchEvent(new MouseEvent('mouseover', eventInit));\n target.dispatchEvent(\n new MouseEvent('mouseenter', { ...eventInit, bubbles: false })\n );\n target.dispatchEvent(new MouseEvent('mousemove', eventInit));\n if (typeof delay_ms === 'number' && Number.isFinite(delay_ms)) {\n const waitMs = Math.min(Math.max(delay_ms, 0), 10000);\n if (waitMs > 0) {\n await sleep(waitMs);\n }\n }\n const html = document.documentElement?.outerHTML ?? '';\n return ok({ format: 'html', snapshot: html });\n }\n case 'drive.select': {\n const { locator, value, text, index } = parseParams();\n const target = resolveLocator(locator as Record<string, unknown>);\n if (!target) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n if (!(target instanceof HTMLSelectElement)) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'Target is not a select element.'\n );\n }\n let applied = false;\n if (typeof index === 'number' && Number.isFinite(index)) {\n applied = selectOptionByIndex(target, Math.trunc(index));\n }\n if (!applied && typeof value === 'string') {\n applied = selectOption(target, value);\n }\n if (!applied && typeof text === 'string') {\n applied = selectOption(target, text);\n }\n if (!applied) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'No matching option found for select.'\n );\n }\n return ok();\n }\n case 'drive.type': {\n const { locator, text, clear, submit } = parseParams();\n if (typeof text !== 'string') {\n return buildError('INVALID_ARGUMENT', 'text must be a string.');\n }\n let target = resolveLocator(\n locator as Record<string, unknown> | undefined\n );\n if (!target) {\n const active = activeEditableElement();\n if (active) {\n target = active;\n }\n }\n if (!target || !(target instanceof HTMLElement)) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n target.focus();\n const tag = target.tagName.toLowerCase();\n const shouldClear = Boolean(clear);\n if (tag === 'input' || tag === 'textarea') {\n const input = target as HTMLInputElement | HTMLTextAreaElement;\n if (shouldClear) {\n input.value = '';\n }\n input.value = `${input.value}${text}`;\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n } else if (target.isContentEditable) {\n if (shouldClear) {\n target.textContent = '';\n }\n target.textContent = `${target.textContent ?? ''}${text}`;\n target.dispatchEvent(new Event('input', { bubbles: true }));\n } else {\n return buildError(\n 'INVALID_ARGUMENT',\n 'Target is not editable (input, textarea, or contenteditable).'\n );\n }\n\n if (submit) {\n const form = target.closest('form');\n if (form && form instanceof HTMLFormElement) {\n form.requestSubmit();\n } else {\n target.dispatchEvent(\n new KeyboardEvent('keydown', {\n key: 'Enter',\n bubbles: true,\n })\n );\n }\n }\n\n return ok();\n }\n case 'drive.detect_field_type': {\n const { locator, selector } = parseParams();\n let target = resolveLocator(\n locator as Record<string, unknown> | undefined\n );\n if (!target && typeof selector === 'string' && selector.length > 0) {\n target = document.querySelector(selector);\n }\n if (!target) {\n return buildError('LOCATOR_NOT_FOUND', 'Failed to resolve locator.');\n }\n return ok({ fieldType: detectFieldType(target) });\n }\n case 'drive.fill_form': {\n const { fields } = parseParams();\n if (!Array.isArray(fields) || fields.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'fields must be a non-empty array.'\n );\n }\n let filled = 0;\n const errors: string[] = [];\n fields.forEach((field, index) => {\n if (!field || typeof field !== 'object') {\n errors.push(`Field ${index} is not an object.`);\n return;\n }\n const record = field as Record<string, unknown>;\n const selector = record.selector;\n const locator =\n record.locator && typeof record.locator === 'object'\n ? (record.locator as Record<string, unknown>)\n : undefined;\n let element: Element | null = null;\n if (locator) {\n element = resolveLocator(locator);\n }\n if (!element && typeof selector === 'string' && selector.length > 0) {\n element = document.querySelector(selector);\n }\n if (!element) {\n errors.push(`Field ${index} could not be resolved.`);\n return;\n }\n\n const value = record.value;\n if (typeof value !== 'string' && typeof value !== 'boolean') {\n errors.push(`Field ${index} has invalid value.`);\n return;\n }\n\n const type =\n typeof record.type === 'string' && record.type.length > 0\n ? record.type\n : 'auto';\n const resolvedType =\n type === 'auto' ? detectFieldType(element) : type;\n const submit = Boolean(record.submit);\n\n let applied = false;\n if (resolvedType === 'select') {\n if (element instanceof HTMLSelectElement) {\n applied = selectOption(element, String(value));\n }\n } else if (resolvedType === 'checkbox' || resolvedType === 'radio') {\n if (element instanceof HTMLInputElement) {\n const shouldCheck =\n typeof value === 'boolean' ? value : coerceBoolean(value);\n element.checked = shouldCheck;\n dispatchValueEvents(element);\n applied = true;\n }\n } else {\n if (element instanceof HTMLElement) {\n applied = setTextValue(element, String(value), true);\n }\n }\n\n if (!applied) {\n errors.push(`Field ${index} could not be filled.`);\n return;\n }\n\n if (submit) {\n submitIfRequested(element);\n }\n filled += 1;\n });\n\n return ok({\n filled,\n attempted: fields.length,\n errors: errors.length > 0 ? errors : [],\n });\n }\n case 'drive.drag': {\n const { from, to, steps } = parseParams();\n const fromEl = resolveLocator(from as Record<string, unknown>);\n if (!fromEl) {\n return buildError(\n 'LOCATOR_NOT_FOUND',\n 'Failed to resolve drag source.'\n );\n }\n const toEl = resolveLocator(to as Record<string, unknown>);\n if (!toEl) {\n return buildError(\n 'LOCATOR_NOT_FOUND',\n 'Failed to resolve drag target.'\n );\n }\n\n const fromRect = fromEl.getBoundingClientRect();\n const toRect = toEl.getBoundingClientRect();\n const startX = fromRect.left + fromRect.width / 2;\n const startY = fromRect.top + fromRect.height / 2;\n const endX = toRect.left + toRect.width / 2;\n const endY = toRect.top + toRect.height / 2;\n // Defensive bounds in case content script receives unvalidated inputs.\n const totalSteps =\n typeof steps === 'number' && Number.isFinite(steps)\n ? Math.max(1, Math.min(50, Math.floor(steps)))\n : 12;\n let dataTransfer: DataTransfer | undefined;\n try {\n dataTransfer = new DataTransfer();\n } catch {\n dataTransfer = undefined;\n }\n\n dispatchPointer(fromEl, 'pointerdown', startX, startY);\n dispatchDrag(fromEl, 'dragstart', startX, startY, dataTransfer);\n\n for (let i = 1; i <= totalSteps; i += 1) {\n const progress = i / totalSteps;\n const x = startX + (endX - startX) * progress;\n const y = startY + (endY - startY) * progress;\n const target = document.elementFromPoint(x, y) ?? toEl;\n dispatchPointer(target, 'pointermove', x, y);\n dispatchDrag(target, 'dragover', x, y, dataTransfer);\n await sleep(10);\n }\n\n const dropTarget = document.elementFromPoint(endX, endY) ?? toEl;\n dispatchDrag(dropTarget, 'drop', endX, endY, dataTransfer);\n dispatchPointer(dropTarget, 'pointerup', endX, endY);\n dispatchDrag(fromEl, 'dragend', endX, endY, dataTransfer);\n return ok();\n }\n case 'drive.key_press': {\n const { key, modifiers } = parseParams();\n if (typeof key !== 'string' || key.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'key must be a non-empty string.'\n );\n }\n const target =\n document.activeElement instanceof HTMLElement\n ? document.activeElement\n : document.body;\n if (!target) {\n return buildError('INVALID_ARGUMENT', 'No target for key press.');\n }\n const mods = normalizeModifiers(modifiers);\n const eventInit = {\n key,\n code: keyToCode(key),\n bubbles: true,\n cancelable: true,\n ctrlKey: mods.ctrl,\n altKey: mods.alt,\n shiftKey: mods.shift,\n metaKey: mods.meta,\n };\n target.dispatchEvent(new KeyboardEvent('keydown', eventInit));\n target.dispatchEvent(new KeyboardEvent('keyup', eventInit));\n return ok();\n }\n case 'drive.key': {\n const { key, modifiers, repeat } = parseParams();\n if (typeof key !== 'string' || key.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'key must be a non-empty string.'\n );\n }\n const target =\n document.activeElement instanceof HTMLElement\n ? document.activeElement\n : document.body;\n if (!target) {\n return buildError('INVALID_ARGUMENT', 'No target for key press.');\n }\n const mods = normalizeModifiers(modifiers);\n const eventInit = {\n key,\n code: keyToCode(key),\n bubbles: true,\n cancelable: true,\n ctrlKey: mods.ctrl,\n altKey: mods.alt,\n shiftKey: mods.shift,\n metaKey: mods.meta,\n };\n const count =\n typeof repeat === 'number' && Number.isFinite(repeat)\n ? Math.max(1, Math.min(50, Math.floor(repeat)))\n : 1;\n for (let i = 0; i < count; i += 1) {\n target.dispatchEvent(new KeyboardEvent('keydown', eventInit));\n target.dispatchEvent(new KeyboardEvent('keyup', eventInit));\n }\n return ok();\n }\n case 'drive.scroll': {\n const { delta_x, delta_y, top, left, behavior } = parseParams();\n const scrollBehavior =\n behavior === 'smooth' || behavior === 'auto' ? behavior : undefined;\n if (typeof top === 'number' || typeof left === 'number') {\n window.scrollTo({\n top: typeof top === 'number' ? top : undefined,\n left: typeof left === 'number' ? left : undefined,\n behavior: scrollBehavior,\n });\n return ok();\n }\n if (typeof delta_x === 'number' || typeof delta_y === 'number') {\n window.scrollBy({\n left: typeof delta_x === 'number' ? delta_x : 0,\n top: typeof delta_y === 'number' ? delta_y : 0,\n behavior: scrollBehavior,\n });\n return ok();\n }\n return buildError(\n 'INVALID_ARGUMENT',\n 'scroll requires delta_x/delta_y or top/left.'\n );\n }\n case 'drive.screenshot_meta': {\n const root = document.documentElement;\n const body = document.body;\n const scrollWidth = Math.max(\n root?.scrollWidth ?? 0,\n root?.clientWidth ?? 0,\n body?.scrollWidth ?? 0,\n body?.clientWidth ?? 0\n );\n const scrollHeight = Math.max(\n root?.scrollHeight ?? 0,\n root?.clientHeight ?? 0,\n body?.scrollHeight ?? 0,\n body?.clientHeight ?? 0\n );\n\n return ok({\n scrollX: window.scrollX,\n scrollY: window.scrollY,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n scrollWidth,\n scrollHeight,\n devicePixelRatio:\n typeof window.devicePixelRatio === 'number' &&\n Number.isFinite(window.devicePixelRatio) &&\n window.devicePixelRatio > 0\n ? window.devicePixelRatio\n : 1,\n });\n }\n case 'drive.screenshot_element': {\n const { selector } = parseParams();\n if (typeof selector !== 'string' || selector.trim().length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'selector must be a non-empty string.'\n );\n }\n\n let element: Element | null = null;\n try {\n element = document.querySelector(selector);\n } catch {\n return buildError(\n 'INVALID_ARGUMENT',\n 'selector must be a valid CSS selector.'\n );\n }\n\n if (!element) {\n return buildError('INVALID_ARGUMENT', 'No element matched selector.');\n }\n\n try {\n (element as HTMLElement).scrollIntoView?.({\n block: 'center',\n inline: 'center',\n });\n } catch {\n // Ignore scroll failures; capture whatever is visible.\n }\n\n const rect = element.getBoundingClientRect();\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n const dpr =\n typeof window.devicePixelRatio === 'number' &&\n Number.isFinite(window.devicePixelRatio) &&\n window.devicePixelRatio > 0\n ? window.devicePixelRatio\n : 1;\n\n return ok({\n selector,\n pageX: rect.left + scrollX,\n pageY: rect.top + scrollY,\n width: rect.width,\n height: rect.height,\n viewportLeft: rect.left,\n viewportTop: rect.top,\n viewportWidth: window.innerWidth,\n viewportHeight: window.innerHeight,\n scrollX,\n scrollY,\n devicePixelRatio: dpr,\n });\n }\n case 'drive.wait_for': {\n const { condition, timeout_ms } = parseParams();\n if (!condition || typeof condition !== 'object') {\n return buildError('INVALID_ARGUMENT', 'condition must be an object.');\n }\n const kind = (condition as Record<string, unknown>).kind;\n const value = (condition as Record<string, unknown>).value;\n if (\n typeof kind !== 'string' ||\n !['locator_visible', 'text_present', 'url_matches'].includes(kind)\n ) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'condition.kind must be locator_visible, text_present, or url_matches.'\n );\n }\n if (typeof value !== 'string' || value.length === 0) {\n return buildError(\n 'INVALID_ARGUMENT',\n 'condition.value must be a non-empty string.'\n );\n }\n const timeout =\n typeof timeout_ms === 'number' && Number.isFinite(timeout_ms)\n ? Math.max(0, timeout_ms)\n : 30000;\n const start = Date.now();\n const urlMatcher =\n kind === 'url_matches' ? buildUrlMatcher(value) : null;\n if (urlMatcher && !urlMatcher.ok) {\n return urlMatcher.error;\n }\n\n const checkCondition = (): boolean => {\n if (kind === 'text_present') {\n return (document.body?.innerText ?? '').includes(value);\n }\n if (kind === 'url_matches') {\n return urlMatcher\n ? urlMatcher.matcher(window.location.href)\n : window.location.href.includes(value);\n }\n const selector = value;\n const element = document.querySelector(selector);\n return Boolean(element && isVisible(element));\n };\n\n return await new Promise<ContentResult>((resolve) => {\n const tick = () => {\n if (checkCondition()) {\n resolve(ok());\n return;\n }\n if (Date.now() - start >= timeout) {\n resolve(\n buildError('TIMEOUT', `wait_for timed out after ${timeout}ms.`)\n );\n return;\n }\n window.setTimeout(tick, 100);\n };\n tick();\n });\n }\n case 'drive.go_back':\n case 'drive.back': {\n // Trigger history changes on the next tick so the background script gets\n // our response before this page unloads.\n window.setTimeout(() => {\n history.back();\n }, 0);\n return ok();\n }\n case 'drive.go_forward':\n case 'drive.forward': {\n window.setTimeout(() => {\n history.forward();\n }, 0);\n return ok();\n }\n default:\n return buildError('INVALID_ARGUMENT', `Unsupported action ${action}.`);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n return buildError('EVALUATION_FAILED', message);\n }\n};\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nif (typeof chrome !== 'undefined' && chrome.runtime?.onMessage) {\n chrome.runtime.onMessage.addListener(\n (\n message: Record<string, unknown>,\n _sender: unknown,\n sendResponse: (response: ContentResult) => void\n ) => {\n if (!isRecord(message) || typeof message.action !== 'string') {\n sendResponse({\n ok: false,\n error: {\n code: 'INVALID_ARGUMENT',\n message: 'Invalid content script request.',\n retryable: false,\n },\n });\n return;\n }\n\n void runDriveAction(\n message.action,\n message.params as Record<string, unknown>\n )\n .then(sendResponse)\n .catch((error) => {\n const messageText =\n error instanceof Error ? error.message : 'Unknown error';\n sendResponse({\n ok: false,\n error: {\n code: 'EVALUATION_FAILED',\n message: messageText,\n retryable: false,\n },\n });\n });\n\n return true;\n }\n );\n}\n"],
5
+ "mappings": ";;;AAWO,MAAM,iBAAiB,OAC5B,QACA,WAC2B;AAC3B,UAAM,aAAa,CACjB,MACA,SACA,aACmB;AAAA,MACnB,IAAI;AAAA,MACJ,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,WAAW;AAAA,QACX,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,KAAK,CAAC,YAAqC,EAAE,IAAI,MAAM,OAAO;AAEpE,UAAM,iBAAiB,CAAC,UAA0B;AAChD,UAAI,OAAO,QAAQ,eAAe,OAAO,IAAI,WAAW,YAAY;AAClE,eAAO,IAAI,OAAO,KAAK;AAAA,MACzB;AACA,aAAO,MAAM,QAAQ,WAAW,MAAM;AAAA,IACxC;AAEA,UAAM,YAAY,CAAC,YAA8B;AAC/C,UAAI,EAAE,mBAAmB,cAAc;AACrC,eAAO;AAAA,MACT;AACA,YAAM,QAAQ,OAAO,iBAAiB,OAAO;AAC7C,UAAI,MAAM,eAAe,YAAY,MAAM,YAAY,QAAQ;AAC7D,eAAO;AAAA,MACT;AACA,YAAM,OAAO,QAAQ,sBAAsB;AAC3C,UAAI,KAAK,UAAU,KAAK,KAAK,WAAW,GAAG;AACzC,eAAO;AAAA,MACT;AACA,UACE,QAAQ,gBAAgB,KACxB,QAAQ,iBAAiB,KACzB,QAAQ,eAAe,EAAE,WAAW,GACpC;AACA,eAAO;AAAA,MACT;AACA,UAAI,UAA8B;AAClC,aAAO,SAAS;AACd,cAAMA,SAAQ,OAAO,iBAAiB,OAAO;AAC7C,YAAIA,OAAM,YAAY,QAAQ;AAC5B,iBAAO;AAAA,QACT;AACA,YAAIA,OAAM,eAAe,YAAYA,OAAM,eAAe,YAAY;AACpE,iBAAO;AAAA,QACT;AACA,cAAM,UAAU,OAAO,WAAWA,OAAM,WAAW,GAAG;AACtD,YAAI,OAAO,SAAS,OAAO,KAAK,WAAW,GAAG;AAC5C,iBAAO;AAAA,QACT;AACA,kBAAU,QAAQ;AAAA,MACpB;AACA,aAAO;AAAA,IACT;AAGA,UAAM,kBAAkB,CACtB,YAGyC;AACzC,YAAM,YAAY;AAClB,UAAI,QAAQ,SAAS,WAAW;AAC9B,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,OAAO;AAAA,YACL;AAAA,YACA,+BAA+B,SAAS;AAAA,UAC1C;AAAA,QACF;AAAA,MACF;AACA,YAAM,oBACJ,iDAAiD,KAAK,OAAO;AAC/D,YAAM,oBAAoB,4BAA4B,KAAK,OAAO;AAClE,UAAI,qBAAqB,mBAAmB;AAC1C,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,OAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AACA,UAAI;AACF,cAAM,QAAQ,IAAI,OAAO,OAAO;AAChC,eAAO,EAAE,IAAI,MAAM,SAAS,CAAC,QAAgB,MAAM,KAAK,GAAG,EAAE;AAAA,MAC/D,QAAQ;AACN,eAAO,EAAE,IAAI,MAAM,SAAS,CAAC,QAAgB,IAAI,SAAS,OAAO,EAAE;AAAA,MACrE;AAAA,IACF;AAEA,UAAM,aAAa,CAAC,SAAiC;AACnD,YAAM,OAAO,SAAS;AAAA,QACpB,SAAS;AAAA,QACT,WAAW;AAAA,MACb;AACA,UAAI,OAAO,KAAK,SAAS;AACzB,aAAO,MAAM;AACX,cAAM,UAAU;AAChB,YAAI,QAAQ,eAAe,QAAQ,YAAY,SAAS,IAAI,GAAG;AAC7D,iBAAO;AAAA,QACT;AACA,eAAO,KAAK,SAAS;AAAA,MACvB;AACA,aAAO;AAAA,IACT;AAEA,UAAM,aAAa,CAAC,YAAqD;AACvE,YAAM,OAAO,QAAQ;AACrB,UAAI,CAAC,QAAQ,OAAO,SAAS,UAAU;AACrC,eAAO;AAAA,MACT;AACA,YAAM,WAAY,KAAiC;AACnD,YAAM,YAAa,KAAiC;AACpD,UAAI,OAAO,aAAa,YAAY,SAAS,WAAW,GAAG;AACzD,eAAO;AAAA,MACT;AACA,YAAM,aAAa,MAAM;AAAA,QACvB,SAAS,iBAAiB,UAAU,eAAe,QAAQ,CAAC,IAAI;AAAA,MAClE;AACA,UAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG;AAC3D,eAAO,WAAW,CAAC,KAAK;AAAA,MAC1B;AACA,aACE,WAAW,KAAK,CAAC,cAAc;AAC7B,cAAM,QAAQ,UAAU,aAAa,YAAY,KAAK;AACtD,cAAM,OAAO,UAAU,eAAe;AACtC,eAAO,MAAM,SAAS,SAAS,KAAK,KAAK,SAAS,SAAS;AAAA,MAC7D,CAAC,KAAK;AAAA,IAEV;AAEA,UAAM,iBAAiB,CACrB,YACmB;AACnB,UAAI,CAAC,SAAS;AACZ,eAAO;AAAA,MACT;AACA,YAAM,MAAM,QAAQ;AACpB,UAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG;AAC7C,cAAM,aAAa,IAAI,WAAW,GAAG,IAAI,MAAM,IAAI,GAAG;AACtD,cAAM,WAAW,iBAAiB,eAAe,UAAU,CAAC;AAC5D,cAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,YAAI,OAAO;AACT,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,SAAS,QAAQ;AACvB,UAAI,OAAO,WAAW,YAAY,OAAO,SAAS,GAAG;AACnD,cAAM,WAAW,iBAAiB,eAAe,MAAM,CAAC;AACxD,cAAM,QAAQ,SAAS,cAAc,QAAQ;AAC7C,YAAI,OAAO;AACT,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,MAAM,QAAQ;AACpB,UAAI,OAAO,QAAQ,YAAY,IAAI,SAAS,GAAG;AAC7C,cAAM,QAAQ,SAAS,cAAc,GAAG;AACxC,YAAI,OAAO;AACT,iBAAO;AAAA,QACT;AAAA,MACF;AACA,YAAM,SAAS,WAAW,OAAO;AACjC,UAAI,QAAQ;AACV,eAAO;AAAA,MACT;AACA,YAAM,OAAO,QAAQ;AACrB,UAAI,OAAO,SAAS,YAAY,KAAK,SAAS,GAAG;AAC/C,eAAO,WAAW,IAAI;AAAA,MACxB;AACA,aAAO;AAAA,IACT;AAEA,UAAM,gBAAgB,CAAC,UAAqC;AAC1D,UAAI,OAAO,UAAU,WAAW;AAC9B,eAAO;AAAA,MACT;AACA,YAAM,aAAa,MAAM,KAAK,EAAE,YAAY;AAC5C,aAAO,CAAC,QAAQ,KAAK,OAAO,KAAK,MAAM,SAAS,EAAE,SAAS,UAAU;AAAA,IACvE;AAEA,UAAM,sBAAsB,CAAC,YAA+B;AAC1D,cAAQ,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AAC3D,cAAQ,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;AAAA,IAC9D;AAEA,UAAM,eAAe,CAAC,QAA2B,UAA2B;AAC1E,YAAM,SAAS,MAAM,KAAK,OAAO,OAAO,EAAE;AAAA,QACxC,CAAC,UAAU,MAAM,UAAU,SAAS,MAAM,SAAS;AAAA,MACrD;AACA,UAAI,CAAC,QAAQ;AACX,eAAO;AAAA,MACT;AACA,aAAO,QAAQ,OAAO;AACtB,0BAAoB,MAAM;AAC1B,aAAO;AAAA,IACT;AAEA,UAAM,sBAAsB,CAC1B,QACA,UACY;AACZ,UAAI,CAAC,OAAO,UAAU,KAAK,GAAG;AAC5B,eAAO;AAAA,MACT;AACA,UAAI,QAAQ,KAAK,SAAS,OAAO,QAAQ,QAAQ;AAC/C,eAAO;AAAA,MACT;AACA,aAAO,gBAAgB;AACvB,0BAAoB,MAAM;AAC1B,aAAO;AAAA,IACT;AAEA,UAAM,eAAe,CACnB,SACA,OACA,UACY;AACZ,YAAM,MAAM,QAAQ,QAAQ,YAAY;AACxC,UAAI,QAAQ,WAAW,QAAQ,YAAY;AACzC,cAAM,QAAQ;AACd,YAAI,OAAO;AACT,gBAAM,QAAQ;AAAA,QAChB;AACA,cAAM,QAAQ,GAAG,MAAM,KAAK,GAAG,KAAK;AACpC,4BAAoB,KAAK;AACzB,eAAO;AAAA,MACT;AACA,UAAI,QAAQ,mBAAmB;AAC7B,YAAI,OAAO;AACT,kBAAQ,cAAc;AAAA,QACxB;AACA,gBAAQ,cAAc,GAAG,QAAQ,eAAe,EAAE,GAAG,KAAK;AAC1D,4BAAoB,OAAO;AAC3B,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,kBAAkB,CACtB,YACiE;AACjE,UAAI,mBAAmB,mBAAmB;AACxC,eAAO;AAAA,MACT;AACA,UAAI,mBAAmB,kBAAkB;AACvC,cAAM,OAAO,QAAQ,KAAK,YAAY;AACtC,YAAI,SAAS,YAAY;AACvB,iBAAO;AAAA,QACT;AACA,YAAI,SAAS,SAAS;AACpB,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AACA,UAAI,mBAAmB,qBAAqB;AAC1C,eAAO;AAAA,MACT;AACA,UAAI,mBAAmB,eAAe,QAAQ,mBAAmB;AAC/D,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,oBAAoB,CAAC,YAA2B;AACpD,YAAM,OAAO,QAAQ,QAAQ,MAAM;AACnC,UAAI,QAAQ,gBAAgB,iBAAiB;AAC3C,aAAK,cAAc;AAAA,MACrB,WAAW,mBAAmB,aAAa;AACzC,gBAAQ;AAAA,UACN,IAAI,cAAc,WAAW,EAAE,KAAK,SAAS,SAAS,KAAK,CAAC;AAAA,QAC9D;AAAA,MACF;AAAA,IACF;AAEA,UAAM,QAAQ,CAAC,OACb,IAAI,QAAQ,CAAC,YAAY,OAAO,WAAW,SAAS,EAAE,CAAC;AAEzD,UAAM,kBAAkB,CACtB,SACA,MACA,GACA,MACS;AACT,cAAQ;AAAA,QACN,IAAI,aAAa,MAAM;AAAA,UACrB,SAAS;AAAA,UACT,YAAY;AAAA,UACZ,SAAS;AAAA,UACT,SAAS;AAAA,UACT,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,eAAe,CACnB,SACA,MACA,GACA,GACA,iBACS;AACT,UAAI;AACF,gBAAQ;AAAA,UACN,IAAI,UAAU,MAAM;AAAA,YAClB,SAAS;AAAA,YACT,YAAY;AAAA,YACZ,SAAS;AAAA,YACT,SAAS;AAAA,YACT;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AACN,gBAAQ;AAAA,UACN,IAAI,MAAM,MAAM,EAAE,SAAS,MAAM,YAAY,KAAK,CAAC;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAEA,UAAM,YAAY,CAAC,QAAwB;AACzC,YAAM,MAA8B;AAAA,QAClC,OAAO;AAAA,QACP,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,KAAK;AAAA,QACL,WAAW;AAAA,QACX,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,WAAW;AAAA,QACX,WAAW;AAAA,QACX,YAAY;AAAA,QACZ,MAAM;AAAA,QACN,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,KAAK;AAAA,QACL,OAAO;AAAA,MACT;AACA,UAAI,IAAI,GAAG,GAAG;AACZ,eAAO,IAAI,GAAG;AAAA,MAChB;AACA,UAAI,IAAI,WAAW,GAAG;AACpB,YAAI,WAAW,KAAK,GAAG,GAAG;AACxB,iBAAO,MAAM,IAAI,YAAY,CAAC;AAAA,QAChC;AACA,YAAI,QAAQ,KAAK,GAAG,GAAG;AACrB,iBAAO,QAAQ,GAAG;AAAA,QACpB;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,UAAM,qBAAqB,CACzB,cACmE;AACnE,YAAM,QAAQ,EAAE,MAAM,OAAO,KAAK,OAAO,OAAO,OAAO,MAAM,MAAM;AACnE,UAAI,MAAM,QAAQ,SAAS,GAAG;AAC5B,kBAAU,QAAQ,CAAC,aAAa;AAC9B,cAAI,OAAO,aAAa,UAAU;AAChC;AAAA,UACF;AACA,gBAAM,aAAa,SAAS,YAAY;AACxC,cAAI,eAAe,QAAQ;AACzB,kBAAM,OAAO;AAAA,UACf,WAAW,eAAe,OAAO;AAC/B,kBAAM,MAAM;AAAA,UACd,WAAW,eAAe,SAAS;AACjC,kBAAM,QAAQ;AAAA,UAChB,WAAW,eAAe,QAAQ;AAChC,kBAAM,OAAO;AAAA,UACf;AAAA,QACF,CAAC;AACD,eAAO;AAAA,MACT;AACA,UAAI,aAAa,OAAO,cAAc,UAAU;AAC9C,cAAM,SAAS;AACf,cAAM,OAAO,QAAQ,OAAO,IAAI;AAChC,cAAM,MAAM,QAAQ,OAAO,GAAG;AAC9B,cAAM,QAAQ,QAAQ,OAAO,KAAK;AAClC,cAAM,OAAO,QAAQ,OAAO,IAAI;AAAA,MAClC;AACA,aAAO;AAAA,IACT;AAEA,UAAM,wBAAwB,MAA0B;AACtD,YAAM,SAAS,SAAS;AACxB,UAAI,CAAC,UAAU,EAAE,kBAAkB,cAAc;AAC/C,eAAO;AAAA,MACT;AACA,YAAM,MAAM,OAAO,QAAQ,YAAY;AACvC,UAAI,QAAQ,WAAW,QAAQ,cAAc,OAAO,mBAAmB;AACrE,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,MAA+B,UAAU,CAAC;AAE9D,QAAI;AACF,cAAQ,QAAQ;AAAA,QACd,KAAK,kBAAkB;AACrB,gBAAM,EAAE,IAAI,IAAI,YAAY;AAC5B,cAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG;AAC/C,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,iBAAO,SAAS,OAAO;AACvB,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA,KAAK,uBAAuB;AAC1B,gBAAM,EAAE,QAAQ,IAAI,YAAY;AAChC,gBAAM,SAAS,eAAe,OAAkC;AAChE,cAAI,CAAC,QAAQ;AACX,mBAAO,WAAW,qBAAqB,4BAA4B;AAAA,UACrE;AACA,gBAAM,OAAO,OAAO,sBAAsB;AAC1C,iBAAO,GAAG;AAAA,YACR,GAAG,KAAK,OAAO,KAAK,QAAQ;AAAA,YAC5B,GAAG,KAAK,MAAM,KAAK,SAAS;AAAA,UAC9B,CAAC;AAAA,QACH;AAAA,QACA,KAAK,uBAAuB;AAC1B,gBAAM,OAAO,SAAS,iBAAiB,aAAa;AACpD,iBAAO,GAAG,EAAE,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,QAC9C;AAAA,QACA,KAAK,2BAA2B;AAC9B,gBAAM,EAAE,QAAQ,IAAI,YAAY;AAChC,cAAI,SAAS;AAAA,YACX;AAAA,UACF;AACA,cAAI,CAAC,QAAQ;AACX,qBAAS,sBAAsB;AAAA,UACjC;AACA,cAAI,CAAC,UAAU,EAAE,kBAAkB,cAAc;AAC/C,mBAAO,WAAW,qBAAqB,4BAA4B;AAAA,UACrE;AACA,gBAAM,MAAM,OAAO,QAAQ,YAAY;AACvC,cACE,QAAQ,WACR,QAAQ,cACR,CAAC,OAAO,mBACR;AACA,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,gBAAM,OAAO,OAAO,sBAAsB;AAC1C,iBAAO,GAAG;AAAA,YACR,GAAG,KAAK,OAAO,KAAK,QAAQ;AAAA,YAC5B,GAAG,KAAK,MAAM,KAAK,SAAS;AAAA,UAC9B,CAAC;AAAA,QACH;AAAA,QACA,KAAK,+BAA+B;AAClC,gBAAM,SAAS,sBAAsB;AACrC,cAAI,CAAC,QAAQ;AACX,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,gBAAM,MAAM,OAAO,QAAQ,YAAY;AACvC,cAAI,QAAQ,WAAW,QAAQ,YAAY;AACzC,kBAAM,QAAQ;AACd,kBAAM,QAAQ;AACd,gCAAoB,KAAK;AACzB,mBAAO,GAAG,EAAE,IAAI,KAAK,CAAC;AAAA,UACxB;AACA,cAAI,OAAO,mBAAmB;AAC5B,mBAAO,cAAc;AACrB,gCAAoB,MAAM;AAC1B,mBAAO,GAAG,EAAE,IAAI,KAAK,CAAC;AAAA,UACxB;AACA,iBAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,QACA,KAAK,eAAe;AAClB,gBAAM,EAAE,SAAS,YAAY,IAAI,YAAY;AAC7C,gBAAM,SAAS,eAAe,OAAkC;AAChE,cAAI,CAAC,QAAQ;AACX,mBAAO,WAAW,qBAAqB,4BAA4B;AAAA,UACrE;AACA,gBAAM,QACJ,OAAO,gBAAgB,YAAY,OAAO,SAAS,WAAW,IAC1D,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,CAAC,IACnC;AAKN,iBAAO,WAAW,MAAM;AACtB,gBAAI;AACF,kBAAI,kBAAkB,aAAa;AACjC,oBAAI;AACF,yBAAO,MAAM,EAAE,eAAe,KAAK,CAAC;AAAA,gBACtC,QAAQ;AACN,yBAAO,MAAM;AAAA,gBACf;AAAA,cACF;AACA,uBAAS,IAAI,GAAG,IAAI,OAAO,KAAK,GAAG;AACjC,gBAAC,OAAuB,MAAM;AAAA,cAChC;AAAA,YACF,QAAQ;AAAA,YAER;AAAA,UACF,GAAG,CAAC;AACJ,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA,KAAK,eAAe;AAClB,gBAAM,EAAE,SAAS,SAAS,IAAI,YAAY;AAC1C,gBAAM,SAAS,eAAe,OAAkC;AAChE,cAAI,CAAC,QAAQ;AACX,mBAAO,WAAW,qBAAqB,4BAA4B;AAAA,UACrE;AACA,gBAAM,OAAO,OAAO,sBAAsB;AAC1C,gBAAM,UAAU,KAAK,OAAO,KAAK,QAAQ;AACzC,gBAAM,UAAU,KAAK,MAAM,KAAK,SAAS;AACzC,gBAAM,YAAY;AAAA,YAChB,SAAS;AAAA,YACT,YAAY;AAAA,YACZ,SAAS;AAAA,YACT,SAAS;AAAA,UACX;AACA,iBAAO,cAAc,IAAI,WAAW,aAAa,SAAS,CAAC;AAC3D,iBAAO;AAAA,YACL,IAAI,WAAW,cAAc,EAAE,GAAG,WAAW,SAAS,MAAM,CAAC;AAAA,UAC/D;AACA,iBAAO,cAAc,IAAI,WAAW,aAAa,SAAS,CAAC;AAC3D,cAAI,OAAO,aAAa,YAAY,OAAO,SAAS,QAAQ,GAAG;AAC7D,kBAAM,SAAS,KAAK,IAAI,KAAK,IAAI,UAAU,CAAC,GAAG,GAAK;AACpD,gBAAI,SAAS,GAAG;AACd,oBAAM,MAAM,MAAM;AAAA,YACpB;AAAA,UACF;AACA,gBAAM,OAAO,SAAS,iBAAiB,aAAa;AACpD,iBAAO,GAAG,EAAE,QAAQ,QAAQ,UAAU,KAAK,CAAC;AAAA,QAC9C;AAAA,QACA,KAAK,gBAAgB;AACnB,gBAAM,EAAE,SAAS,OAAO,MAAM,MAAM,IAAI,YAAY;AACpD,gBAAM,SAAS,eAAe,OAAkC;AAChE,cAAI,CAAC,QAAQ;AACX,mBAAO,WAAW,qBAAqB,4BAA4B;AAAA,UACrE;AACA,cAAI,EAAE,kBAAkB,oBAAoB;AAC1C,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,cAAI,UAAU;AACd,cAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,GAAG;AACvD,sBAAU,oBAAoB,QAAQ,KAAK,MAAM,KAAK,CAAC;AAAA,UACzD;AACA,cAAI,CAAC,WAAW,OAAO,UAAU,UAAU;AACzC,sBAAU,aAAa,QAAQ,KAAK;AAAA,UACtC;AACA,cAAI,CAAC,WAAW,OAAO,SAAS,UAAU;AACxC,sBAAU,aAAa,QAAQ,IAAI;AAAA,UACrC;AACA,cAAI,CAAC,SAAS;AACZ,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA,KAAK,cAAc;AACjB,gBAAM,EAAE,SAAS,MAAM,OAAO,OAAO,IAAI,YAAY;AACrD,cAAI,OAAO,SAAS,UAAU;AAC5B,mBAAO,WAAW,oBAAoB,wBAAwB;AAAA,UAChE;AACA,cAAI,SAAS;AAAA,YACX;AAAA,UACF;AACA,cAAI,CAAC,QAAQ;AACX,kBAAM,SAAS,sBAAsB;AACrC,gBAAI,QAAQ;AACV,uBAAS;AAAA,YACX;AAAA,UACF;AACA,cAAI,CAAC,UAAU,EAAE,kBAAkB,cAAc;AAC/C,mBAAO,WAAW,qBAAqB,4BAA4B;AAAA,UACrE;AACA,iBAAO,MAAM;AACb,gBAAM,MAAM,OAAO,QAAQ,YAAY;AACvC,gBAAM,cAAc,QAAQ,KAAK;AACjC,cAAI,QAAQ,WAAW,QAAQ,YAAY;AACzC,kBAAM,QAAQ;AACd,gBAAI,aAAa;AACf,oBAAM,QAAQ;AAAA,YAChB;AACA,kBAAM,QAAQ,GAAG,MAAM,KAAK,GAAG,IAAI;AACnC,kBAAM,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AACzD,kBAAM,cAAc,IAAI,MAAM,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;AAAA,UAC5D,WAAW,OAAO,mBAAmB;AACnC,gBAAI,aAAa;AACf,qBAAO,cAAc;AAAA,YACvB;AACA,mBAAO,cAAc,GAAG,OAAO,eAAe,EAAE,GAAG,IAAI;AACvD,mBAAO,cAAc,IAAI,MAAM,SAAS,EAAE,SAAS,KAAK,CAAC,CAAC;AAAA,UAC5D,OAAO;AACL,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAEA,cAAI,QAAQ;AACV,kBAAM,OAAO,OAAO,QAAQ,MAAM;AAClC,gBAAI,QAAQ,gBAAgB,iBAAiB;AAC3C,mBAAK,cAAc;AAAA,YACrB,OAAO;AACL,qBAAO;AAAA,gBACL,IAAI,cAAc,WAAW;AAAA,kBAC3B,KAAK;AAAA,kBACL,SAAS;AAAA,gBACX,CAAC;AAAA,cACH;AAAA,YACF;AAAA,UACF;AAEA,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA,KAAK,2BAA2B;AAC9B,gBAAM,EAAE,SAAS,SAAS,IAAI,YAAY;AAC1C,cAAI,SAAS;AAAA,YACX;AAAA,UACF;AACA,cAAI,CAAC,UAAU,OAAO,aAAa,YAAY,SAAS,SAAS,GAAG;AAClE,qBAAS,SAAS,cAAc,QAAQ;AAAA,UAC1C;AACA,cAAI,CAAC,QAAQ;AACX,mBAAO,WAAW,qBAAqB,4BAA4B;AAAA,UACrE;AACA,iBAAO,GAAG,EAAE,WAAW,gBAAgB,MAAM,EAAE,CAAC;AAAA,QAClD;AAAA,QACA,KAAK,mBAAmB;AACtB,gBAAM,EAAE,OAAO,IAAI,YAAY;AAC/B,cAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW,GAAG;AACjD,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,cAAI,SAAS;AACb,gBAAM,SAAmB,CAAC;AAC1B,iBAAO,QAAQ,CAAC,OAAO,UAAU;AAC/B,gBAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,qBAAO,KAAK,SAAS,KAAK,oBAAoB;AAC9C;AAAA,YACF;AACA,kBAAM,SAAS;AACf,kBAAM,WAAW,OAAO;AACxB,kBAAM,UACJ,OAAO,WAAW,OAAO,OAAO,YAAY,WACvC,OAAO,UACR;AACN,gBAAI,UAA0B;AAC9B,gBAAI,SAAS;AACX,wBAAU,eAAe,OAAO;AAAA,YAClC;AACA,gBAAI,CAAC,WAAW,OAAO,aAAa,YAAY,SAAS,SAAS,GAAG;AACnE,wBAAU,SAAS,cAAc,QAAQ;AAAA,YAC3C;AACA,gBAAI,CAAC,SAAS;AACZ,qBAAO,KAAK,SAAS,KAAK,yBAAyB;AACnD;AAAA,YACF;AAEA,kBAAM,QAAQ,OAAO;AACrB,gBAAI,OAAO,UAAU,YAAY,OAAO,UAAU,WAAW;AAC3D,qBAAO,KAAK,SAAS,KAAK,qBAAqB;AAC/C;AAAA,YACF;AAEA,kBAAM,OACJ,OAAO,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,IACpD,OAAO,OACP;AACN,kBAAM,eACJ,SAAS,SAAS,gBAAgB,OAAO,IAAI;AAC/C,kBAAM,SAAS,QAAQ,OAAO,MAAM;AAEpC,gBAAI,UAAU;AACd,gBAAI,iBAAiB,UAAU;AAC7B,kBAAI,mBAAmB,mBAAmB;AACxC,0BAAU,aAAa,SAAS,OAAO,KAAK,CAAC;AAAA,cAC/C;AAAA,YACF,WAAW,iBAAiB,cAAc,iBAAiB,SAAS;AAClE,kBAAI,mBAAmB,kBAAkB;AACvC,sBAAM,cACJ,OAAO,UAAU,YAAY,QAAQ,cAAc,KAAK;AAC1D,wBAAQ,UAAU;AAClB,oCAAoB,OAAO;AAC3B,0BAAU;AAAA,cACZ;AAAA,YACF,OAAO;AACL,kBAAI,mBAAmB,aAAa;AAClC,0BAAU,aAAa,SAAS,OAAO,KAAK,GAAG,IAAI;AAAA,cACrD;AAAA,YACF;AAEA,gBAAI,CAAC,SAAS;AACZ,qBAAO,KAAK,SAAS,KAAK,uBAAuB;AACjD;AAAA,YACF;AAEA,gBAAI,QAAQ;AACV,gCAAkB,OAAO;AAAA,YAC3B;AACA,sBAAU;AAAA,UACZ,CAAC;AAED,iBAAO,GAAG;AAAA,YACR;AAAA,YACA,WAAW,OAAO;AAAA,YAClB,QAAQ,OAAO,SAAS,IAAI,SAAS,CAAC;AAAA,UACxC,CAAC;AAAA,QACH;AAAA,QACA,KAAK,cAAc;AACjB,gBAAM,EAAE,MAAM,IAAI,MAAM,IAAI,YAAY;AACxC,gBAAM,SAAS,eAAe,IAA+B;AAC7D,cAAI,CAAC,QAAQ;AACX,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,gBAAM,OAAO,eAAe,EAA6B;AACzD,cAAI,CAAC,MAAM;AACT,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAEA,gBAAM,WAAW,OAAO,sBAAsB;AAC9C,gBAAM,SAAS,KAAK,sBAAsB;AAC1C,gBAAM,SAAS,SAAS,OAAO,SAAS,QAAQ;AAChD,gBAAM,SAAS,SAAS,MAAM,SAAS,SAAS;AAChD,gBAAM,OAAO,OAAO,OAAO,OAAO,QAAQ;AAC1C,gBAAM,OAAO,OAAO,MAAM,OAAO,SAAS;AAE1C,gBAAM,aACJ,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAC9C,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,IAC3C;AACN,cAAI;AACJ,cAAI;AACF,2BAAe,IAAI,aAAa;AAAA,UAClC,QAAQ;AACN,2BAAe;AAAA,UACjB;AAEA,0BAAgB,QAAQ,eAAe,QAAQ,MAAM;AACrD,uBAAa,QAAQ,aAAa,QAAQ,QAAQ,YAAY;AAE9D,mBAAS,IAAI,GAAG,KAAK,YAAY,KAAK,GAAG;AACvC,kBAAM,WAAW,IAAI;AACrB,kBAAM,IAAI,UAAU,OAAO,UAAU;AACrC,kBAAM,IAAI,UAAU,OAAO,UAAU;AACrC,kBAAM,SAAS,SAAS,iBAAiB,GAAG,CAAC,KAAK;AAClD,4BAAgB,QAAQ,eAAe,GAAG,CAAC;AAC3C,yBAAa,QAAQ,YAAY,GAAG,GAAG,YAAY;AACnD,kBAAM,MAAM,EAAE;AAAA,UAChB;AAEA,gBAAM,aAAa,SAAS,iBAAiB,MAAM,IAAI,KAAK;AAC5D,uBAAa,YAAY,QAAQ,MAAM,MAAM,YAAY;AACzD,0BAAgB,YAAY,aAAa,MAAM,IAAI;AACnD,uBAAa,QAAQ,WAAW,MAAM,MAAM,YAAY;AACxD,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA,KAAK,mBAAmB;AACtB,gBAAM,EAAE,KAAK,UAAU,IAAI,YAAY;AACvC,cAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG;AAC/C,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,gBAAM,SACJ,SAAS,yBAAyB,cAC9B,SAAS,gBACT,SAAS;AACf,cAAI,CAAC,QAAQ;AACX,mBAAO,WAAW,oBAAoB,0BAA0B;AAAA,UAClE;AACA,gBAAM,OAAO,mBAAmB,SAAS;AACzC,gBAAM,YAAY;AAAA,YAChB;AAAA,YACA,MAAM,UAAU,GAAG;AAAA,YACnB,SAAS;AAAA,YACT,YAAY;AAAA,YACZ,SAAS,KAAK;AAAA,YACd,QAAQ,KAAK;AAAA,YACb,UAAU,KAAK;AAAA,YACf,SAAS,KAAK;AAAA,UAChB;AACA,iBAAO,cAAc,IAAI,cAAc,WAAW,SAAS,CAAC;AAC5D,iBAAO,cAAc,IAAI,cAAc,SAAS,SAAS,CAAC;AAC1D,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA,KAAK,aAAa;AAChB,gBAAM,EAAE,KAAK,WAAW,OAAO,IAAI,YAAY;AAC/C,cAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG;AAC/C,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,gBAAM,SACJ,SAAS,yBAAyB,cAC9B,SAAS,gBACT,SAAS;AACf,cAAI,CAAC,QAAQ;AACX,mBAAO,WAAW,oBAAoB,0BAA0B;AAAA,UAClE;AACA,gBAAM,OAAO,mBAAmB,SAAS;AACzC,gBAAM,YAAY;AAAA,YAChB;AAAA,YACA,MAAM,UAAU,GAAG;AAAA,YACnB,SAAS;AAAA,YACT,YAAY;AAAA,YACZ,SAAS,KAAK;AAAA,YACd,QAAQ,KAAK;AAAA,YACb,UAAU,KAAK;AAAA,YACf,SAAS,KAAK;AAAA,UAChB;AACA,gBAAM,QACJ,OAAO,WAAW,YAAY,OAAO,SAAS,MAAM,IAChD,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,MAAM,MAAM,CAAC,CAAC,IAC5C;AACN,mBAAS,IAAI,GAAG,IAAI,OAAO,KAAK,GAAG;AACjC,mBAAO,cAAc,IAAI,cAAc,WAAW,SAAS,CAAC;AAC5D,mBAAO,cAAc,IAAI,cAAc,SAAS,SAAS,CAAC;AAAA,UAC5D;AACA,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA,KAAK,gBAAgB;AACnB,gBAAM,EAAE,SAAS,SAAS,KAAK,MAAM,SAAS,IAAI,YAAY;AAC9D,gBAAM,iBACJ,aAAa,YAAY,aAAa,SAAS,WAAW;AAC5D,cAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,UAAU;AACvD,mBAAO,SAAS;AAAA,cACd,KAAK,OAAO,QAAQ,WAAW,MAAM;AAAA,cACrC,MAAM,OAAO,SAAS,WAAW,OAAO;AAAA,cACxC,UAAU;AAAA,YACZ,CAAC;AACD,mBAAO,GAAG;AAAA,UACZ;AACA,cAAI,OAAO,YAAY,YAAY,OAAO,YAAY,UAAU;AAC9D,mBAAO,SAAS;AAAA,cACd,MAAM,OAAO,YAAY,WAAW,UAAU;AAAA,cAC9C,KAAK,OAAO,YAAY,WAAW,UAAU;AAAA,cAC7C,UAAU;AAAA,YACZ,CAAC;AACD,mBAAO,GAAG;AAAA,UACZ;AACA,iBAAO;AAAA,YACL;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,QACA,KAAK,yBAAyB;AAC5B,gBAAM,OAAO,SAAS;AACtB,gBAAM,OAAO,SAAS;AACtB,gBAAM,cAAc,KAAK;AAAA,YACvB,MAAM,eAAe;AAAA,YACrB,MAAM,eAAe;AAAA,YACrB,MAAM,eAAe;AAAA,YACrB,MAAM,eAAe;AAAA,UACvB;AACA,gBAAM,eAAe,KAAK;AAAA,YACxB,MAAM,gBAAgB;AAAA,YACtB,MAAM,gBAAgB;AAAA,YACtB,MAAM,gBAAgB;AAAA,YACtB,MAAM,gBAAgB;AAAA,UACxB;AAEA,iBAAO,GAAG;AAAA,YACR,SAAS,OAAO;AAAA,YAChB,SAAS,OAAO;AAAA,YAChB,eAAe,OAAO;AAAA,YACtB,gBAAgB,OAAO;AAAA,YACvB;AAAA,YACA;AAAA,YACA,kBACE,OAAO,OAAO,qBAAqB,YACnC,OAAO,SAAS,OAAO,gBAAgB,KACvC,OAAO,mBAAmB,IACtB,OAAO,mBACP;AAAA,UACR,CAAC;AAAA,QACH;AAAA,QACA,KAAK,4BAA4B;AAC/B,gBAAM,EAAE,SAAS,IAAI,YAAY;AACjC,cAAI,OAAO,aAAa,YAAY,SAAS,KAAK,EAAE,WAAW,GAAG;AAChE,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAEA,cAAI,UAA0B;AAC9B,cAAI;AACF,sBAAU,SAAS,cAAc,QAAQ;AAAA,UAC3C,QAAQ;AACN,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAEA,cAAI,CAAC,SAAS;AACZ,mBAAO,WAAW,oBAAoB,8BAA8B;AAAA,UACtE;AAEA,cAAI;AACF,YAAC,QAAwB,iBAAiB;AAAA,cACxC,OAAO;AAAA,cACP,QAAQ;AAAA,YACV,CAAC;AAAA,UACH,QAAQ;AAAA,UAER;AAEA,gBAAM,OAAO,QAAQ,sBAAsB;AAC3C,gBAAM,UAAU,OAAO;AACvB,gBAAM,UAAU,OAAO;AACvB,gBAAM,MACJ,OAAO,OAAO,qBAAqB,YACnC,OAAO,SAAS,OAAO,gBAAgB,KACvC,OAAO,mBAAmB,IACtB,OAAO,mBACP;AAEN,iBAAO,GAAG;AAAA,YACR;AAAA,YACA,OAAO,KAAK,OAAO;AAAA,YACnB,OAAO,KAAK,MAAM;AAAA,YAClB,OAAO,KAAK;AAAA,YACZ,QAAQ,KAAK;AAAA,YACb,cAAc,KAAK;AAAA,YACnB,aAAa,KAAK;AAAA,YAClB,eAAe,OAAO;AAAA,YACtB,gBAAgB,OAAO;AAAA,YACvB;AAAA,YACA;AAAA,YACA,kBAAkB;AAAA,UACpB,CAAC;AAAA,QACH;AAAA,QACA,KAAK,kBAAkB;AACrB,gBAAM,EAAE,WAAW,WAAW,IAAI,YAAY;AAC9C,cAAI,CAAC,aAAa,OAAO,cAAc,UAAU;AAC/C,mBAAO,WAAW,oBAAoB,8BAA8B;AAAA,UACtE;AACA,gBAAM,OAAQ,UAAsC;AACpD,gBAAM,QAAS,UAAsC;AACrD,cACE,OAAO,SAAS,YAChB,CAAC,CAAC,mBAAmB,gBAAgB,aAAa,EAAE,SAAS,IAAI,GACjE;AACA,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,cAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,mBAAO;AAAA,cACL;AAAA,cACA;AAAA,YACF;AAAA,UACF;AACA,gBAAM,UACJ,OAAO,eAAe,YAAY,OAAO,SAAS,UAAU,IACxD,KAAK,IAAI,GAAG,UAAU,IACtB;AACN,gBAAM,QAAQ,KAAK,IAAI;AACvB,gBAAM,aACJ,SAAS,gBAAgB,gBAAgB,KAAK,IAAI;AACpD,cAAI,cAAc,CAAC,WAAW,IAAI;AAChC,mBAAO,WAAW;AAAA,UACpB;AAEA,gBAAM,iBAAiB,MAAe;AACpC,gBAAI,SAAS,gBAAgB;AAC3B,sBAAQ,SAAS,MAAM,aAAa,IAAI,SAAS,KAAK;AAAA,YACxD;AACA,gBAAI,SAAS,eAAe;AAC1B,qBAAO,aACH,WAAW,QAAQ,OAAO,SAAS,IAAI,IACvC,OAAO,SAAS,KAAK,SAAS,KAAK;AAAA,YACzC;AACA,kBAAM,WAAW;AACjB,kBAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,mBAAO,QAAQ,WAAW,UAAU,OAAO,CAAC;AAAA,UAC9C;AAEA,iBAAO,MAAM,IAAI,QAAuB,CAAC,YAAY;AACnD,kBAAM,OAAO,MAAM;AACjB,kBAAI,eAAe,GAAG;AACpB,wBAAQ,GAAG,CAAC;AACZ;AAAA,cACF;AACA,kBAAI,KAAK,IAAI,IAAI,SAAS,SAAS;AACjC;AAAA,kBACE,WAAW,WAAW,4BAA4B,OAAO,KAAK;AAAA,gBAChE;AACA;AAAA,cACF;AACA,qBAAO,WAAW,MAAM,GAAG;AAAA,YAC7B;AACA,iBAAK;AAAA,UACP,CAAC;AAAA,QACH;AAAA,QACA,KAAK;AAAA,QACL,KAAK,cAAc;AAGjB,iBAAO,WAAW,MAAM;AACtB,oBAAQ,KAAK;AAAA,UACf,GAAG,CAAC;AACJ,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA,KAAK;AAAA,QACL,KAAK,iBAAiB;AACpB,iBAAO,WAAW,MAAM;AACtB,oBAAQ,QAAQ;AAAA,UAClB,GAAG,CAAC;AACJ,iBAAO,GAAG;AAAA,QACZ;AAAA,QACA;AACE,iBAAO,WAAW,oBAAoB,sBAAsB,MAAM,GAAG;AAAA,MACzE;AAAA,IACF,SAAS,OAAO;AACd,YAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,aAAO,WAAW,qBAAqB,OAAO;AAAA,IAChD;AAAA,EACF;AAEA,MAAM,WAAW,CAAC,UAChB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAErE,MAAI,OAAO,WAAW,eAAe,OAAO,SAAS,WAAW;AAC9D,WAAO,QAAQ,UAAU;AAAA,MACvB,CACE,SACA,SACA,iBACG;AACH,YAAI,CAAC,SAAS,OAAO,KAAK,OAAO,QAAQ,WAAW,UAAU;AAC5D,uBAAa;AAAA,YACX,IAAI;AAAA,YACJ,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,cACT,WAAW;AAAA,YACb;AAAA,UACF,CAAC;AACD;AAAA,QACF;AAEA,aAAK;AAAA,UACH,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV,EACG,KAAK,YAAY,EACjB,MAAM,CAAC,UAAU;AAChB,gBAAM,cACJ,iBAAiB,QAAQ,MAAM,UAAU;AAC3C,uBAAa;AAAA,YACX,IAAI;AAAA,YACJ,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,cACT,WAAW;AAAA,YACb;AAAA,UACF,CAAC;AAAA,QACH,CAAC;AAEH,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;",
6
6
  "names": ["style"]
7
7
  }
@@ -117,6 +117,8 @@
117
117
  };
118
118
 
119
119
  // packages/extension/src/options-ui.ts
120
+ var ACTIVATION_FLAG_PARAM = "bb_activate";
121
+ var ACTIVATION_PORT_PARAM = "corePort";
120
122
  var byId = (id) => {
121
123
  const el = document.getElementById(id);
122
124
  if (!el) {
@@ -150,6 +152,59 @@
150
152
  return iso;
151
153
  }
152
154
  };
155
+ var parseActivationPort = (value) => {
156
+ if (!value) {
157
+ return null;
158
+ }
159
+ const parsed = Number.parseInt(value, 10);
160
+ if (!Number.isFinite(parsed) || parsed <= 0) {
161
+ return null;
162
+ }
163
+ return Math.floor(parsed);
164
+ };
165
+ var clearActivationQueryParams = () => {
166
+ const url = new URL(window.location.href);
167
+ if (!url.search) {
168
+ return;
169
+ }
170
+ url.search = "";
171
+ window.history.replaceState(
172
+ null,
173
+ document.title,
174
+ `${url.pathname}${url.hash}`
175
+ );
176
+ };
177
+ var writeCorePort = async (corePort) => {
178
+ await new Promise((resolve, reject) => {
179
+ chrome.storage.local.set({ corePort }, () => {
180
+ const error = chrome.runtime.lastError;
181
+ if (error) {
182
+ reject(new Error(error.message));
183
+ return;
184
+ }
185
+ resolve();
186
+ });
187
+ });
188
+ };
189
+ var applyActivationQueryParams = async () => {
190
+ const url = new URL(window.location.href);
191
+ const params = url.searchParams;
192
+ if (params.get(ACTIVATION_FLAG_PARAM) !== "1") {
193
+ return;
194
+ }
195
+ const corePort = parseActivationPort(params.get(ACTIVATION_PORT_PARAM));
196
+ try {
197
+ if (corePort !== null) {
198
+ await writeCorePort(corePort);
199
+ } else {
200
+ console.warn("Ignoring dev activation request with invalid corePort.");
201
+ }
202
+ } catch (error) {
203
+ console.warn("Failed to apply dev activation corePort.", error);
204
+ } finally {
205
+ clearActivationQueryParams();
206
+ }
207
+ };
153
208
  var createToast = () => {
154
209
  const wrap = document.createElement("div");
155
210
  wrap.className = "bb-toast-wrap";
@@ -356,7 +411,10 @@ Last used: ${formatTime(
356
411
  await refresh();
357
412
  };
358
413
  var main = () => {
359
- void refreshAll();
414
+ void (async () => {
415
+ await applyActivationQueryParams();
416
+ await refreshAll();
417
+ })();
360
418
  const { granular, bypass } = getModeEls();
361
419
  granular.addEventListener("change", () => {
362
420
  if (!granular.checked) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/site-permissions.ts", "../src/options-ui.ts"],
4
- "sourcesContent": ["export const SITE_ALLOWLIST_KEY = 'siteAllowlist';\nexport const PERMISSION_PROMPT_WAIT_MS_KEY = 'permissionPromptWaitMs';\nexport const DEFAULT_PERMISSION_PROMPT_WAIT_MS = 30_000;\nexport const SITE_PERMISSIONS_MODE_KEY = 'sitePermissionsMode';\n\nexport type SitePermissionsMode = 'granular' | 'bypass';\nexport const DEFAULT_SITE_PERMISSIONS_MODE: SitePermissionsMode = 'granular';\n\nexport type SiteAllowlistEntry = {\n createdAt: string; // ISO\n lastUsedAt: string; // ISO\n};\n\nexport type SiteAllowlist = Record<string, SiteAllowlistEntry>;\n\nexport const siteKeyFromUrl = (rawUrl: string): string | null => {\n if (!rawUrl || typeof rawUrl !== 'string') {\n return null;\n }\n\n try {\n const parsed = new URL(rawUrl);\n // Only gate \"real web\" pages for now.\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return null;\n }\n\n // URL.hostname is lowercased by the platform.\n if (!parsed.hostname) {\n return null;\n }\n\n return parsed.port ? `${parsed.hostname}:${parsed.port}` : parsed.hostname;\n } catch {\n return null;\n }\n};\n\nconst isAllowlistEntry = (value: unknown): value is SiteAllowlistEntry => {\n if (!value || typeof value !== 'object') {\n return false;\n }\n const v = value as Record<string, unknown>;\n return typeof v.createdAt === 'string' && typeof v.lastUsedAt === 'string';\n};\n\nconst normalizeSiteKey = (siteKey: string): string => siteKey.toLowerCase();\n\nconst readAllowlistRaw = async (): Promise<SiteAllowlist> => {\n return await new Promise<SiteAllowlist>((resolve) => {\n chrome.storage.local.get(\n [SITE_ALLOWLIST_KEY],\n (result: Record<string, unknown>) => {\n const raw = result?.[SITE_ALLOWLIST_KEY];\n if (!raw || typeof raw !== 'object') {\n resolve({});\n return;\n }\n\n const out: SiteAllowlist = {};\n for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {\n if (typeof k !== 'string') {\n continue;\n }\n if (!isAllowlistEntry(v)) {\n continue;\n }\n out[normalizeSiteKey(k)] = v;\n }\n\n resolve(out);\n }\n );\n });\n};\n\nconst writeAllowlistRaw = async (allowlist: SiteAllowlist): Promise<void> => {\n return await new Promise<void>((resolve) => {\n chrome.storage.local.set({ [SITE_ALLOWLIST_KEY]: allowlist }, () =>\n resolve()\n );\n });\n};\n\nexport const readSitePermissionsMode =\n async (): Promise<SitePermissionsMode> => {\n return await new Promise<SitePermissionsMode>((resolve) => {\n chrome.storage.local.get(\n [SITE_PERMISSIONS_MODE_KEY],\n (result: Record<string, unknown>) => {\n const raw = result?.[SITE_PERMISSIONS_MODE_KEY];\n if (raw === 'granular' || raw === 'bypass') {\n resolve(raw);\n return;\n }\n\n // Self-heal legacy/invalid storage to a safe default, so UIs never\n // render with \"no mode selected\" and other callers don't have to\n // special-case missing values.\n try {\n chrome.storage.local.set({\n [SITE_PERMISSIONS_MODE_KEY]: DEFAULT_SITE_PERMISSIONS_MODE,\n });\n } catch {\n // ignore\n }\n resolve(DEFAULT_SITE_PERMISSIONS_MODE);\n }\n );\n });\n };\n\nexport const writeSitePermissionsMode = async (\n mode: SitePermissionsMode\n): Promise<void> => {\n return await new Promise<void>((resolve) => {\n chrome.storage.local.set({ [SITE_PERMISSIONS_MODE_KEY]: mode }, () =>\n resolve()\n );\n });\n};\n\nexport const readPermissionPromptWaitMs = async (): Promise<number> => {\n return await new Promise<number>((resolve) => {\n chrome.storage.local.get(\n [PERMISSION_PROMPT_WAIT_MS_KEY],\n (result: Record<string, unknown>) => {\n const raw = result?.[PERMISSION_PROMPT_WAIT_MS_KEY];\n if (typeof raw === 'number' && Number.isFinite(raw) && raw > 0) {\n resolve(raw);\n return;\n }\n if (typeof raw === 'string') {\n const parsed = Number(raw);\n if (Number.isFinite(parsed) && parsed > 0) {\n resolve(parsed);\n return;\n }\n }\n\n resolve(DEFAULT_PERMISSION_PROMPT_WAIT_MS);\n }\n );\n });\n};\n\nexport const getAllowlistedSites = async (): Promise<SiteAllowlist> => {\n return await readAllowlistRaw();\n};\n\nexport const isSiteAllowed = async (siteKey: string): Promise<boolean> => {\n const key = normalizeSiteKey(siteKey);\n const allowlist = await readAllowlistRaw();\n return Boolean(allowlist[key]);\n};\n\nexport const allowSiteAlways = async (\n siteKey: string,\n now: Date = new Date()\n): Promise<void> => {\n const key = normalizeSiteKey(siteKey);\n const allowlist = await readAllowlistRaw();\n const nowIso = now.toISOString();\n\n const existing = allowlist[key];\n allowlist[key] = {\n createdAt: existing?.createdAt ?? nowIso,\n lastUsedAt: nowIso,\n };\n\n await writeAllowlistRaw(allowlist);\n};\n\nexport const upsertAllowlistedSites = async (\n entries: SiteAllowlist\n): Promise<void> => {\n const allowlist = await readAllowlistRaw();\n let changed = false;\n\n for (const [k, v] of Object.entries(entries ?? {})) {\n if (typeof k !== 'string') {\n continue;\n }\n if (!isAllowlistEntry(v)) {\n continue;\n }\n allowlist[normalizeSiteKey(k)] = v;\n changed = true;\n }\n\n if (!changed) {\n return;\n }\n\n await writeAllowlistRaw(allowlist);\n};\n\nexport const touchSiteLastUsed = async (\n siteKey: string,\n now: Date = new Date()\n): Promise<void> => {\n const key = normalizeSiteKey(siteKey);\n const allowlist = await readAllowlistRaw();\n const existing = allowlist[key];\n if (!existing) {\n return;\n }\n\n allowlist[key] = { ...existing, lastUsedAt: now.toISOString() };\n await writeAllowlistRaw(allowlist);\n};\n\nexport const revokeSite = async (siteKey: string): Promise<void> => {\n const key = normalizeSiteKey(siteKey);\n const allowlist = await readAllowlistRaw();\n if (!allowlist[key]) {\n return;\n }\n delete allowlist[key];\n await writeAllowlistRaw(allowlist);\n};\n", "import {\n allowSiteAlways,\n getAllowlistedSites,\n readSitePermissionsMode,\n revokeSite,\n type SitePermissionsMode,\n upsertAllowlistedSites,\n writeSitePermissionsMode,\n} from './site-permissions.js';\n\ntype Row = {\n site: string;\n createdAt: string;\n lastUsedAt: string;\n};\n\ntype ModeEls = {\n granular: HTMLInputElement;\n bypass: HTMLInputElement;\n sitesDetails: HTMLDetailsElement;\n sitesSummary: HTMLElement;\n};\n\nconst byId = (id: string): HTMLElement => {\n const el = document.getElementById(id);\n if (!el) {\n throw new Error(`Missing element: ${id}`);\n }\n return el;\n};\n\nconst elFromHtml = (html: string): HTMLElement => {\n const tpl = document.createElement('template');\n tpl.innerHTML = html.trim();\n const node = tpl.content.firstElementChild;\n if (!node) {\n throw new Error('Expected element from template.');\n }\n return node as HTMLElement;\n};\n\nconst formatTime = (iso: string): string => {\n const d = new Date(iso);\n if (!Number.isFinite(d.getTime())) {\n return iso;\n }\n try {\n return new Intl.DateTimeFormat(undefined, {\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n }).format(d);\n } catch {\n return iso;\n }\n};\n\nconst createToast = (): {\n showUndo: (opts: { message: string; onUndo: () => Promise<void> }) => void;\n} => {\n const wrap = document.createElement('div');\n wrap.className = 'bb-toast-wrap';\n document.body.appendChild(wrap);\n\n let activeTimer: ReturnType<typeof globalThis.setTimeout> | null = null;\n\n const clear = (): void => {\n if (activeTimer !== null) {\n globalThis.clearTimeout(activeTimer);\n activeTimer = null;\n }\n wrap.innerHTML = '';\n };\n\n return {\n showUndo: ({ message, onUndo }): void => {\n clear();\n\n const toast = elFromHtml(`\n <div class=\"bb-toast\" role=\"status\" aria-live=\"polite\">\n <div class=\"bb-toast-msg\"></div>\n <button class=\"bb-link-button\" type=\"button\">Undo</button>\n </div>\n `);\n const msgEl = toast.querySelector('.bb-toast-msg') as HTMLElement | null;\n const undoBtn = toast.querySelector('button') as HTMLButtonElement | null;\n if (!msgEl || !undoBtn) {\n throw new Error('Toast missing required elements.');\n }\n\n msgEl.textContent = message;\n undoBtn.addEventListener('click', () => {\n undoBtn.disabled = true;\n void (async () => {\n try {\n await onUndo();\n } finally {\n clear();\n }\n })();\n });\n\n wrap.appendChild(toast);\n activeTimer = globalThis.setTimeout(() => clear(), 6000);\n },\n };\n};\n\nconst toast = createToast();\n\nconst getModeEls = (): ModeEls => {\n const granular = byId('bb-mode-granular') as HTMLInputElement;\n const bypass = byId('bb-mode-bypass') as HTMLInputElement;\n const sitesDetails = byId('bb-sites-details') as HTMLDetailsElement;\n const sitesSummary = byId('bb-sites-summary');\n\n if (granular.type !== 'radio' || bypass.type !== 'radio') {\n throw new Error('Expected radio inputs for permissions mode.');\n }\n if (sitesDetails.tagName.toLowerCase() !== 'details') {\n throw new Error('Expected a <details> for the sites disclosure.');\n }\n\n return {\n granular,\n bypass,\n sitesDetails,\n sitesSummary,\n };\n};\n\nlet lastMode: SitePermissionsMode | null = null;\nlet modeWriteInProgress = false;\n\nconst applyMode = (mode: SitePermissionsMode): void => {\n const els = getModeEls();\n els.granular.checked = mode === 'granular';\n els.bypass.checked = mode === 'bypass';\n\n // Always show the disclosure + allowlist UI in both modes.\n els.sitesSummary.textContent = 'Approved sites';\n if (mode === 'bypass') {\n if (lastMode !== 'bypass') {\n els.sitesDetails.open = false;\n }\n } else if (lastMode !== 'granular') {\n els.sitesDetails.open = true;\n }\n\n lastMode = mode;\n};\n\nconst refreshMode = async (): Promise<void> => {\n applyMode(await readSitePermissionsMode());\n};\n\nconst focusSiteRow = (site: string): void => {\n const container = byId('bb-sites');\n const rows = Array.from(container.querySelectorAll('.bb-site-row'));\n for (const rowEl of rows) {\n const el = rowEl as HTMLElement;\n if (el.dataset.site !== site) {\n continue;\n }\n\n try {\n el.scrollIntoView({ block: 'nearest' });\n } catch {\n // ignore\n }\n\n const btn = el.querySelector('button') as HTMLButtonElement | null;\n btn?.focus();\n return;\n }\n};\n\nconst render = (rows: Row[]): void => {\n const container = byId('bb-sites');\n container.innerHTML = '';\n container.hidden = false;\n\n if (rows.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'bb-site-empty';\n\n const line1 = document.createElement('div');\n const title = document.createElement('strong');\n title.textContent = 'No approved sites yet.';\n line1.appendChild(title);\n empty.appendChild(line1);\n\n const line2 = document.createElement('div');\n line2.textContent =\n 'Sites show up here after you approve them in a permission prompt.';\n empty.appendChild(line2);\n container.appendChild(empty);\n return;\n }\n\n for (const row of rows) {\n const item = elFromHtml(`\n <div class=\"bb-site-row\" role=\"listitem\">\n <div class=\"bb-site-main\">\n <div class=\"bb-site-key\"></div>\n <div class=\"bb-site-meta\"></div>\n </div>\n <button class=\"bb-link-button bb-link-button-danger\" type=\"button\">\n Revoke\n </button>\n </div>\n `);\n (item as HTMLElement).dataset.site = row.site;\n\n const key = item.querySelector('.bb-site-key') as HTMLElement | null;\n const meta = item.querySelector('.bb-site-meta') as HTMLElement | null;\n const revokeBtn = item.querySelector('button') as HTMLButtonElement | null;\n if (!key || !meta || !revokeBtn) {\n throw new Error('List row missing required elements.');\n }\n\n key.textContent = row.site;\n meta.textContent = `Last used: ${formatTime(row.lastUsedAt)}`;\n meta.title = `Approved: ${formatTime(row.createdAt)}\\nLast used: ${formatTime(\n row.lastUsedAt\n )}`;\n\n revokeBtn.addEventListener('click', () => {\n revokeBtn.disabled = true;\n void (async () => {\n const before = await getAllowlistedSites();\n const entry = before[row.site] ?? before[row.site.toLowerCase()];\n\n try {\n await revokeSite(row.site);\n } finally {\n await refresh();\n revokeBtn.disabled = false;\n }\n\n if (entry) {\n toast.showUndo({\n message: `Revoked ${row.site}.`,\n onUndo: async () => {\n try {\n await upsertAllowlistedSites({ [row.site]: entry });\n const after = await getAllowlistedSites();\n if (!after[row.site] && !after[row.site.toLowerCase()]) {\n await allowSiteAlways(row.site);\n }\n } catch (err) {\n // If restore fails for any reason, fall back to re-adding the site.\n // This preserves the intended user outcome even if timestamps change.\n console.warn(\n 'Undo revoke failed; falling back to allowSiteAlways.',\n err\n );\n await allowSiteAlways(row.site);\n }\n await refresh();\n focusSiteRow(row.site);\n },\n });\n }\n })();\n });\n\n container.appendChild(item);\n }\n};\n\nconst refresh = async (): Promise<void> => {\n const allowlist = await getAllowlistedSites();\n const rows: Row[] = Object.entries(allowlist).map(([site, entry]) => ({\n site,\n createdAt: entry.createdAt,\n lastUsedAt: entry.lastUsedAt,\n }));\n\n rows.sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt));\n render(rows);\n};\n\nconst setMode = async (mode: SitePermissionsMode): Promise<void> => {\n if (modeWriteInProgress) {\n return;\n }\n\n modeWriteInProgress = true;\n try {\n await writeSitePermissionsMode(mode);\n applyMode(mode);\n await refresh();\n } finally {\n modeWriteInProgress = false;\n }\n};\n\nconst refreshAll = async (): Promise<void> => {\n // Mode impacts how we want to render the empty state, so apply it first.\n await refreshMode();\n await refresh();\n};\n\nconst main = (): void => {\n void refreshAll();\n\n const { granular, bypass } = getModeEls();\n granular.addEventListener('change', () => {\n if (!granular.checked) {\n return;\n }\n void setMode('granular');\n });\n bypass.addEventListener('change', () => {\n if (!bypass.checked) {\n return;\n }\n void setMode('bypass');\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (chrome as any).storage?.onChanged?.addListener?.(() => {\n void refreshAll();\n });\n};\n\nmain();\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["export const SITE_ALLOWLIST_KEY = 'siteAllowlist';\nexport const PERMISSION_PROMPT_WAIT_MS_KEY = 'permissionPromptWaitMs';\nexport const DEFAULT_PERMISSION_PROMPT_WAIT_MS = 30_000;\nexport const SITE_PERMISSIONS_MODE_KEY = 'sitePermissionsMode';\n\nexport type SitePermissionsMode = 'granular' | 'bypass';\nexport const DEFAULT_SITE_PERMISSIONS_MODE: SitePermissionsMode = 'granular';\n\nexport type SiteAllowlistEntry = {\n createdAt: string; // ISO\n lastUsedAt: string; // ISO\n};\n\nexport type SiteAllowlist = Record<string, SiteAllowlistEntry>;\n\nexport const siteKeyFromUrl = (rawUrl: string): string | null => {\n if (!rawUrl || typeof rawUrl !== 'string') {\n return null;\n }\n\n try {\n const parsed = new URL(rawUrl);\n // Only gate \"real web\" pages for now.\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return null;\n }\n\n // URL.hostname is lowercased by the platform.\n if (!parsed.hostname) {\n return null;\n }\n\n return parsed.port ? `${parsed.hostname}:${parsed.port}` : parsed.hostname;\n } catch {\n return null;\n }\n};\n\nconst isAllowlistEntry = (value: unknown): value is SiteAllowlistEntry => {\n if (!value || typeof value !== 'object') {\n return false;\n }\n const v = value as Record<string, unknown>;\n return typeof v.createdAt === 'string' && typeof v.lastUsedAt === 'string';\n};\n\nconst normalizeSiteKey = (siteKey: string): string => siteKey.toLowerCase();\n\nconst readAllowlistRaw = async (): Promise<SiteAllowlist> => {\n return await new Promise<SiteAllowlist>((resolve) => {\n chrome.storage.local.get(\n [SITE_ALLOWLIST_KEY],\n (result: Record<string, unknown>) => {\n const raw = result?.[SITE_ALLOWLIST_KEY];\n if (!raw || typeof raw !== 'object') {\n resolve({});\n return;\n }\n\n const out: SiteAllowlist = {};\n for (const [k, v] of Object.entries(raw as Record<string, unknown>)) {\n if (typeof k !== 'string') {\n continue;\n }\n if (!isAllowlistEntry(v)) {\n continue;\n }\n out[normalizeSiteKey(k)] = v;\n }\n\n resolve(out);\n }\n );\n });\n};\n\nconst writeAllowlistRaw = async (allowlist: SiteAllowlist): Promise<void> => {\n return await new Promise<void>((resolve) => {\n chrome.storage.local.set({ [SITE_ALLOWLIST_KEY]: allowlist }, () =>\n resolve()\n );\n });\n};\n\nexport const readSitePermissionsMode =\n async (): Promise<SitePermissionsMode> => {\n return await new Promise<SitePermissionsMode>((resolve) => {\n chrome.storage.local.get(\n [SITE_PERMISSIONS_MODE_KEY],\n (result: Record<string, unknown>) => {\n const raw = result?.[SITE_PERMISSIONS_MODE_KEY];\n if (raw === 'granular' || raw === 'bypass') {\n resolve(raw);\n return;\n }\n\n // Self-heal legacy/invalid storage to a safe default, so UIs never\n // render with \"no mode selected\" and other callers don't have to\n // special-case missing values.\n try {\n chrome.storage.local.set({\n [SITE_PERMISSIONS_MODE_KEY]: DEFAULT_SITE_PERMISSIONS_MODE,\n });\n } catch {\n // ignore\n }\n resolve(DEFAULT_SITE_PERMISSIONS_MODE);\n }\n );\n });\n };\n\nexport const writeSitePermissionsMode = async (\n mode: SitePermissionsMode\n): Promise<void> => {\n return await new Promise<void>((resolve) => {\n chrome.storage.local.set({ [SITE_PERMISSIONS_MODE_KEY]: mode }, () =>\n resolve()\n );\n });\n};\n\nexport const readPermissionPromptWaitMs = async (): Promise<number> => {\n return await new Promise<number>((resolve) => {\n chrome.storage.local.get(\n [PERMISSION_PROMPT_WAIT_MS_KEY],\n (result: Record<string, unknown>) => {\n const raw = result?.[PERMISSION_PROMPT_WAIT_MS_KEY];\n if (typeof raw === 'number' && Number.isFinite(raw) && raw > 0) {\n resolve(raw);\n return;\n }\n if (typeof raw === 'string') {\n const parsed = Number(raw);\n if (Number.isFinite(parsed) && parsed > 0) {\n resolve(parsed);\n return;\n }\n }\n\n resolve(DEFAULT_PERMISSION_PROMPT_WAIT_MS);\n }\n );\n });\n};\n\nexport const getAllowlistedSites = async (): Promise<SiteAllowlist> => {\n return await readAllowlistRaw();\n};\n\nexport const isSiteAllowed = async (siteKey: string): Promise<boolean> => {\n const key = normalizeSiteKey(siteKey);\n const allowlist = await readAllowlistRaw();\n return Boolean(allowlist[key]);\n};\n\nexport const allowSiteAlways = async (\n siteKey: string,\n now: Date = new Date()\n): Promise<void> => {\n const key = normalizeSiteKey(siteKey);\n const allowlist = await readAllowlistRaw();\n const nowIso = now.toISOString();\n\n const existing = allowlist[key];\n allowlist[key] = {\n createdAt: existing?.createdAt ?? nowIso,\n lastUsedAt: nowIso,\n };\n\n await writeAllowlistRaw(allowlist);\n};\n\nexport const upsertAllowlistedSites = async (\n entries: SiteAllowlist\n): Promise<void> => {\n const allowlist = await readAllowlistRaw();\n let changed = false;\n\n for (const [k, v] of Object.entries(entries ?? {})) {\n if (typeof k !== 'string') {\n continue;\n }\n if (!isAllowlistEntry(v)) {\n continue;\n }\n allowlist[normalizeSiteKey(k)] = v;\n changed = true;\n }\n\n if (!changed) {\n return;\n }\n\n await writeAllowlistRaw(allowlist);\n};\n\nexport const touchSiteLastUsed = async (\n siteKey: string,\n now: Date = new Date()\n): Promise<void> => {\n const key = normalizeSiteKey(siteKey);\n const allowlist = await readAllowlistRaw();\n const existing = allowlist[key];\n if (!existing) {\n return;\n }\n\n allowlist[key] = { ...existing, lastUsedAt: now.toISOString() };\n await writeAllowlistRaw(allowlist);\n};\n\nexport const revokeSite = async (siteKey: string): Promise<void> => {\n const key = normalizeSiteKey(siteKey);\n const allowlist = await readAllowlistRaw();\n if (!allowlist[key]) {\n return;\n }\n delete allowlist[key];\n await writeAllowlistRaw(allowlist);\n};\n", "import {\n allowSiteAlways,\n getAllowlistedSites,\n readSitePermissionsMode,\n revokeSite,\n type SitePermissionsMode,\n upsertAllowlistedSites,\n writeSitePermissionsMode,\n} from './site-permissions.js';\n\ntype Row = {\n site: string;\n createdAt: string;\n lastUsedAt: string;\n};\n\ntype ModeEls = {\n granular: HTMLInputElement;\n bypass: HTMLInputElement;\n sitesDetails: HTMLDetailsElement;\n sitesSummary: HTMLElement;\n};\n\nconst ACTIVATION_FLAG_PARAM = 'bb_activate';\nconst ACTIVATION_PORT_PARAM = 'corePort';\n\nconst byId = (id: string): HTMLElement => {\n const el = document.getElementById(id);\n if (!el) {\n throw new Error(`Missing element: ${id}`);\n }\n return el;\n};\n\nconst elFromHtml = (html: string): HTMLElement => {\n const tpl = document.createElement('template');\n tpl.innerHTML = html.trim();\n const node = tpl.content.firstElementChild;\n if (!node) {\n throw new Error('Expected element from template.');\n }\n return node as HTMLElement;\n};\n\nconst formatTime = (iso: string): string => {\n const d = new Date(iso);\n if (!Number.isFinite(d.getTime())) {\n return iso;\n }\n try {\n return new Intl.DateTimeFormat(undefined, {\n year: 'numeric',\n month: 'numeric',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n }).format(d);\n } catch {\n return iso;\n }\n};\n\nconst parseActivationPort = (value: string | null): number | null => {\n if (!value) {\n return null;\n }\n const parsed = Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed <= 0) {\n return null;\n }\n return Math.floor(parsed);\n};\n\nconst clearActivationQueryParams = (): void => {\n const url = new URL(window.location.href);\n if (!url.search) {\n return;\n }\n url.search = '';\n window.history.replaceState(\n null,\n document.title,\n `${url.pathname}${url.hash}`\n );\n};\n\nconst writeCorePort = async (corePort: number): Promise<void> => {\n await new Promise<void>((resolve, reject) => {\n chrome.storage.local.set({ corePort }, () => {\n const error = chrome.runtime.lastError;\n if (error) {\n reject(new Error(error.message));\n return;\n }\n resolve();\n });\n });\n};\n\nconst applyActivationQueryParams = async (): Promise<void> => {\n const url = new URL(window.location.href);\n const params = url.searchParams;\n if (params.get(ACTIVATION_FLAG_PARAM) !== '1') {\n return;\n }\n\n const corePort = parseActivationPort(params.get(ACTIVATION_PORT_PARAM));\n try {\n if (corePort !== null) {\n await writeCorePort(corePort);\n } else {\n console.warn('Ignoring dev activation request with invalid corePort.');\n }\n } catch (error) {\n console.warn('Failed to apply dev activation corePort.', error);\n } finally {\n clearActivationQueryParams();\n }\n};\n\nconst createToast = (): {\n showUndo: (opts: { message: string; onUndo: () => Promise<void> }) => void;\n} => {\n const wrap = document.createElement('div');\n wrap.className = 'bb-toast-wrap';\n document.body.appendChild(wrap);\n\n let activeTimer: ReturnType<typeof globalThis.setTimeout> | null = null;\n\n const clear = (): void => {\n if (activeTimer !== null) {\n globalThis.clearTimeout(activeTimer);\n activeTimer = null;\n }\n wrap.innerHTML = '';\n };\n\n return {\n showUndo: ({ message, onUndo }): void => {\n clear();\n\n const toast = elFromHtml(`\n <div class=\"bb-toast\" role=\"status\" aria-live=\"polite\">\n <div class=\"bb-toast-msg\"></div>\n <button class=\"bb-link-button\" type=\"button\">Undo</button>\n </div>\n `);\n const msgEl = toast.querySelector('.bb-toast-msg') as HTMLElement | null;\n const undoBtn = toast.querySelector('button') as HTMLButtonElement | null;\n if (!msgEl || !undoBtn) {\n throw new Error('Toast missing required elements.');\n }\n\n msgEl.textContent = message;\n undoBtn.addEventListener('click', () => {\n undoBtn.disabled = true;\n void (async () => {\n try {\n await onUndo();\n } finally {\n clear();\n }\n })();\n });\n\n wrap.appendChild(toast);\n activeTimer = globalThis.setTimeout(() => clear(), 6000);\n },\n };\n};\n\nconst toast = createToast();\n\nconst getModeEls = (): ModeEls => {\n const granular = byId('bb-mode-granular') as HTMLInputElement;\n const bypass = byId('bb-mode-bypass') as HTMLInputElement;\n const sitesDetails = byId('bb-sites-details') as HTMLDetailsElement;\n const sitesSummary = byId('bb-sites-summary');\n\n if (granular.type !== 'radio' || bypass.type !== 'radio') {\n throw new Error('Expected radio inputs for permissions mode.');\n }\n if (sitesDetails.tagName.toLowerCase() !== 'details') {\n throw new Error('Expected a <details> for the sites disclosure.');\n }\n\n return {\n granular,\n bypass,\n sitesDetails,\n sitesSummary,\n };\n};\n\nlet lastMode: SitePermissionsMode | null = null;\nlet modeWriteInProgress = false;\n\nconst applyMode = (mode: SitePermissionsMode): void => {\n const els = getModeEls();\n els.granular.checked = mode === 'granular';\n els.bypass.checked = mode === 'bypass';\n\n // Always show the disclosure + allowlist UI in both modes.\n els.sitesSummary.textContent = 'Approved sites';\n if (mode === 'bypass') {\n if (lastMode !== 'bypass') {\n els.sitesDetails.open = false;\n }\n } else if (lastMode !== 'granular') {\n els.sitesDetails.open = true;\n }\n\n lastMode = mode;\n};\n\nconst refreshMode = async (): Promise<void> => {\n applyMode(await readSitePermissionsMode());\n};\n\nconst focusSiteRow = (site: string): void => {\n const container = byId('bb-sites');\n const rows = Array.from(container.querySelectorAll('.bb-site-row'));\n for (const rowEl of rows) {\n const el = rowEl as HTMLElement;\n if (el.dataset.site !== site) {\n continue;\n }\n\n try {\n el.scrollIntoView({ block: 'nearest' });\n } catch {\n // ignore\n }\n\n const btn = el.querySelector('button') as HTMLButtonElement | null;\n btn?.focus();\n return;\n }\n};\n\nconst render = (rows: Row[]): void => {\n const container = byId('bb-sites');\n container.innerHTML = '';\n container.hidden = false;\n\n if (rows.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'bb-site-empty';\n\n const line1 = document.createElement('div');\n const title = document.createElement('strong');\n title.textContent = 'No approved sites yet.';\n line1.appendChild(title);\n empty.appendChild(line1);\n\n const line2 = document.createElement('div');\n line2.textContent =\n 'Sites show up here after you approve them in a permission prompt.';\n empty.appendChild(line2);\n container.appendChild(empty);\n return;\n }\n\n for (const row of rows) {\n const item = elFromHtml(`\n <div class=\"bb-site-row\" role=\"listitem\">\n <div class=\"bb-site-main\">\n <div class=\"bb-site-key\"></div>\n <div class=\"bb-site-meta\"></div>\n </div>\n <button class=\"bb-link-button bb-link-button-danger\" type=\"button\">\n Revoke\n </button>\n </div>\n `);\n (item as HTMLElement).dataset.site = row.site;\n\n const key = item.querySelector('.bb-site-key') as HTMLElement | null;\n const meta = item.querySelector('.bb-site-meta') as HTMLElement | null;\n const revokeBtn = item.querySelector('button') as HTMLButtonElement | null;\n if (!key || !meta || !revokeBtn) {\n throw new Error('List row missing required elements.');\n }\n\n key.textContent = row.site;\n meta.textContent = `Last used: ${formatTime(row.lastUsedAt)}`;\n meta.title = `Approved: ${formatTime(row.createdAt)}\\nLast used: ${formatTime(\n row.lastUsedAt\n )}`;\n\n revokeBtn.addEventListener('click', () => {\n revokeBtn.disabled = true;\n void (async () => {\n const before = await getAllowlistedSites();\n const entry = before[row.site] ?? before[row.site.toLowerCase()];\n\n try {\n await revokeSite(row.site);\n } finally {\n await refresh();\n revokeBtn.disabled = false;\n }\n\n if (entry) {\n toast.showUndo({\n message: `Revoked ${row.site}.`,\n onUndo: async () => {\n try {\n await upsertAllowlistedSites({ [row.site]: entry });\n const after = await getAllowlistedSites();\n if (!after[row.site] && !after[row.site.toLowerCase()]) {\n await allowSiteAlways(row.site);\n }\n } catch (err) {\n // If restore fails for any reason, fall back to re-adding the site.\n // This preserves the intended user outcome even if timestamps change.\n console.warn(\n 'Undo revoke failed; falling back to allowSiteAlways.',\n err\n );\n await allowSiteAlways(row.site);\n }\n await refresh();\n focusSiteRow(row.site);\n },\n });\n }\n })();\n });\n\n container.appendChild(item);\n }\n};\n\nconst refresh = async (): Promise<void> => {\n const allowlist = await getAllowlistedSites();\n const rows: Row[] = Object.entries(allowlist).map(([site, entry]) => ({\n site,\n createdAt: entry.createdAt,\n lastUsedAt: entry.lastUsedAt,\n }));\n\n rows.sort((a, b) => b.lastUsedAt.localeCompare(a.lastUsedAt));\n render(rows);\n};\n\nconst setMode = async (mode: SitePermissionsMode): Promise<void> => {\n if (modeWriteInProgress) {\n return;\n }\n\n modeWriteInProgress = true;\n try {\n await writeSitePermissionsMode(mode);\n applyMode(mode);\n await refresh();\n } finally {\n modeWriteInProgress = false;\n }\n};\n\nconst refreshAll = async (): Promise<void> => {\n // Mode impacts how we want to render the empty state, so apply it first.\n await refreshMode();\n await refresh();\n};\n\nconst main = (): void => {\n void (async () => {\n await applyActivationQueryParams();\n await refreshAll();\n })();\n\n const { granular, bypass } = getModeEls();\n granular.addEventListener('change', () => {\n if (!granular.checked) {\n return;\n }\n void setMode('granular');\n });\n bypass.addEventListener('change', () => {\n if (!bypass.checked) {\n return;\n }\n void setMode('bypass');\n });\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (chrome as any).storage?.onChanged?.addListener?.(() => {\n void refreshAll();\n });\n};\n\nmain();\n"],
5
+ "mappings": "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",
6
6
  "names": ["toast"]
7
7
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Browser Bridge",
4
- "version": "0.7.3",
4
+ "version": "0.8.1",
5
5
  "description": "Control Chrome for Browser Bridge: drive tabs and inspect pages for coding agents.",
6
6
  "icons": {
7
7
  "16": "assets/icons/icon-16.png",
@@ -25,12 +25,24 @@
25
25
  "page": "options.html",
26
26
  "open_in_tab": true
27
27
  },
28
- "permissions": ["tabs", "storage", "webNavigation", "debugger", "tabGroups"],
29
- "host_permissions": ["<all_urls>"],
28
+ "permissions": [
29
+ "tabs",
30
+ "storage",
31
+ "webNavigation",
32
+ "debugger",
33
+ "tabGroups"
34
+ ],
35
+ "host_permissions": [
36
+ "<all_urls>"
37
+ ],
30
38
  "content_scripts": [
31
39
  {
32
- "matches": ["<all_urls>"],
33
- "js": ["dist/content.js"],
40
+ "matches": [
41
+ "<all_urls>"
42
+ ],
43
+ "js": [
44
+ "dist/content.js"
45
+ ],
34
46
  "run_at": "document_idle"
35
47
  }
36
48
  ]