@condorinst/mobile-bridge 0.13.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/keys.ts","../src/internal/dev-panel.ts","../src/internal/mock.ts","../src/internal/native.ts","../src/internal/subscribers.ts","../src/bridge.ts","../src/bridge-client.ts","../src/bridge-shell.ts","../src/shell-messages.ts"],"sourcesContent":["/**\n * `condor-mobile-bridge` — SDK do bridge JS↔Native do app white-label Condor.\n *\n * Public API: `Bridge` singleton + tipos.\n *\n * Import side effect: instala `window.CondorBridge.emit` no primeiro\n * carregamento do módulo, para que o nativo possa chamar via\n * `evaluateJavascript(\"window.CondorBridge.emit('key', payload)\")`. Para\n * que esta instalação aconteça **antes** do nativo tentar emitir, o\n * import deste módulo deve ser um dos primeiros statements do bundle do\n * cliente.\n *\n * **v0.2.0:** mock mode — quando `window.flutter_inappwebview` não é\n * detectado (browser comum, Storybook, Vite dev server), o adapter ativa\n * automaticamente. `Bridge.isMock`/`mockEmit`/`getMockState`/`installDevPanel`\n * expostos para suporte ao desenvolvimento.\n *\n * **v0.4.0:** tipos estritos por chave em `PayloadOf<K>`. `Bridge.on<K>`\n * infere o payload tipado por chave catalogada. Chaves não-catalogadas\n * caem no fallback permissivo (`Record<string, unknown>`).\n *\n * **v0.5.0:** introduz `BridgeClient` (API para código de cliente externo\n * dentro de `<iframe sandbox=\"allow-scripts\">`) e `BridgeShell` (API\n * para o shell Condor-controlado que envolve o iframe). Comunicação\n * shell↔client via `window.postMessage` com schema Valibot + policy\n * default-deny. Defesa em 4 camadas contra propagação do\n * `window.flutter_inappwebview` ao iframe (vide\n * `condor_wiki/cross-cutting/sleep-white-label-content-transport-and-security.md`\n * §Risco 1).\n */\n\nexport type { CondorBridge, MockState } from './bridge';\nexport { Bridge } from './bridge';\nexport type { BridgeClientHandshakeAck, CondorBridgeClient } from './bridge-client';\nexport { BridgeClient } from './bridge-client';\nexport type { BridgeShellOptions } from './bridge-shell';\nexport { BridgeShell } from './bridge-shell';\nexport type { AutoRespondResponse, DevPanelOptions } from './internal/dev-panel';\nexport type { MockMessage } from './internal/mock';\nexport type { BridgeKey } from './keys';\nexport { BRIDGE_KEYS } from './keys';\nexport type {\n BridgeIncomingMessage,\n BridgeOutgoingEvent,\n HandshakeAck,\n HandshakeOptions,\n} from './messages';\nexport type {\n DeleteMemoryAckPayload,\n DeviceConnectionPayload,\n DeviceLightSensorsPayload,\n DeviceNotificationPayload,\n KeyPayloads,\n PayloadOf,\n PingPongPayload,\n RealTimeAckPayload,\n RequestPermissionPayload,\n SensorLogPayload,\n SyncTimeAckPayload,\n SystemStatusPayload,\n UserDevicePayload,\n} from './payloads';\nexport type {\n ClientToShellMessage,\n ConfirmationRequest,\n SandboxVerifyResult,\n ShellPolicy,\n ShellToClientMessage,\n} from './shell-messages';\n","/**\n * Catálogo das chaves do bridge JS↔Native.\n *\n * Espelha o lado JS-facing de `ForegroundComCoreKeys` do `condor_core`\n * (monorepo `mobile_applications`). As strings aqui são o que o JS recebe\n * via `window.CondorBridge.emit(key, payload)`.\n *\n * Camada 1 (v0.1.0): catálogo declarativo. Apenas `pingPong` está\n * efetivamente emitida pelo lado Dart no Trio B; as outras 8 são\n * reservadas para o Trio C (`sleep-whitelabel-bridge-keys`) e ficam\n * aqui como contrato forward-looking.\n */\nexport const BRIDGE_KEYS = {\n pingPong: 'pingPong',\n userDevice: 'userDevice',\n deviceLightSensors: 'deviceLightSensors',\n syncTime: 'syncTime',\n realTime: 'realTime',\n deleteMemory: 'deleteMemory',\n deviceNotification: 'deviceNotification',\n deviceConnection: 'deviceConnection',\n sensorLog: 'sensorLog',\n dfu: 'dfu',\n systemStatus: 'systemStatus',\n /**\n * Native → client: o back do SO foi pressionado e o cliente reportou\n * ter histórico interno (`reportNavigation(true)`). O cliente deve voltar\n * UMA página interna. Emitido só quando o nativo decidiu delegar o back\n * ao cliente. Vide `BridgeClient.onBack`.\n */\n navigationBack: 'navigationBack',\n} as const;\n\nexport type BridgeKey = (typeof BRIDGE_KEYS)[keyof typeof BRIDGE_KEYS];\n","/**\n * Dev panel — UI flutuante para inspecionar o bridge em mock mode.\n *\n * **Opt-in:** chamar `Bridge.installDevPanel(opts?)` quando quiser ver\n * o painel. Só funciona em mock mode (throws em modo real). Injeta no\n * `document.body`; idempotente (re-install remove o anterior).\n *\n * Conteúdo:\n * - Header com badge \"MOCK\", contador de sends, botão minimizar/maximizar.\n * - Sent log — últimas N mensagens (timestamp, type, payload truncado).\n * - Trigger emit — dropdown com `BRIDGE_KEYS` + textarea payload + botão.\n *\n * Estilos inline + classes prefixadas `condor-mock-panel-*` para reduzir\n * conflito com CSS do cliente.\n */\n\nimport { BRIDGE_KEYS } from '../keys';\nimport type { MockAdapter, MockMessage } from './mock';\n\nconst PANEL_ID = 'condor-mock-panel-root';\nconst REFRESH_INTERVAL_MS = 500;\n\n/**\n * Resposta auto-emitida em reação a um `Bridge.send`, retornada pela\n * policy `onSendForAutoRespond` do cliente. `null` significa \"não\n * responder a este send\".\n *\n * @since v0.3.0\n */\nexport interface AutoRespondResponse {\n key: string;\n payload: unknown;\n}\n\nexport interface DevPanelOptions {\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n maxMessages?: number;\n theme?: 'dark' | 'light';\n startCollapsed?: boolean;\n\n /**\n * Policy de auto-respond: invocada a cada `Bridge.send` quando o\n * toggle \"Auto-respond\" está ligado no painel. Retorna\n * `{key, payload}` para disparar `Bridge.mockEmit(key, payload)`\n * após delay, ou `null` para não responder.\n *\n * Sem esta callback, o checkbox \"Auto-respond\" não aparece no painel.\n *\n * @since v0.3.0\n */\n onSendForAutoRespond?: (msg: MockMessage) => AutoRespondResponse | null;\n\n /**\n * Estado inicial do toggle \"Auto-respond\". Default: `false`\n * (manual permanece padrão; cliente decide quando ligar).\n *\n * @since v0.3.0\n */\n startAutoRespondEnabled?: boolean;\n\n /**\n * Delay em ms entre o `send` e o `mockEmit` da auto-resposta.\n * Default: `50` — aproxima latência de IPC real de native bridge.\n *\n * @since v0.3.0\n */\n autoRespondDelayMs?: number;\n}\n\ninterface InstallParams {\n adapter: MockAdapter;\n options?: DevPanelOptions;\n}\n\ninterface ResolvedOptions {\n position: NonNullable<DevPanelOptions['position']>;\n maxMessages: number;\n theme: NonNullable<DevPanelOptions['theme']>;\n startCollapsed: boolean;\n startAutoRespondEnabled: boolean;\n autoRespondDelayMs: number;\n}\n\nconst DEFAULTS: ResolvedOptions = {\n position: 'bottom-right',\n maxMessages: 20,\n theme: 'dark',\n startCollapsed: false,\n startAutoRespondEnabled: false,\n autoRespondDelayMs: 50,\n};\n\nfunction resolveOptions(opts?: DevPanelOptions): ResolvedOptions {\n return {\n position: opts?.position ?? DEFAULTS.position,\n maxMessages: opts?.maxMessages ?? DEFAULTS.maxMessages,\n theme: opts?.theme ?? DEFAULTS.theme,\n startCollapsed: opts?.startCollapsed ?? DEFAULTS.startCollapsed,\n startAutoRespondEnabled: opts?.startAutoRespondEnabled ?? DEFAULTS.startAutoRespondEnabled,\n autoRespondDelayMs: opts?.autoRespondDelayMs ?? DEFAULTS.autoRespondDelayMs,\n };\n}\n\nfunction applyPanelStyles(el: HTMLElement, opts: ResolvedOptions): void {\n const isDark = opts.theme === 'dark';\n const positions: Record<ResolvedOptions['position'], Partial<CSSStyleDeclaration>> = {\n 'bottom-right': { bottom: '12px', right: '12px' },\n 'bottom-left': { bottom: '12px', left: '12px' },\n 'top-right': { top: '12px', right: '12px' },\n 'top-left': { top: '12px', left: '12px' },\n };\n Object.assign(el.style, {\n position: 'fixed',\n width: '340px',\n maxHeight: '420px',\n zIndex: '2147483647',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: '12px',\n color: isDark ? '#e0e0e0' : '#222',\n background: isDark ? '#1a1a1aee' : '#ffffffee',\n border: isDark ? '1px solid #444' : '1px solid #ccc',\n borderRadius: '6px',\n boxShadow: '0 4px 12px rgba(0,0,0,0.3)',\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column',\n ...positions[opts.position],\n });\n}\n\nfunction buildHeader(badge: HTMLElement, counter: HTMLElement, toggle: HTMLElement): HTMLElement {\n const header = document.createElement('div');\n header.className = 'condor-mock-panel-header';\n Object.assign(header.style, {\n padding: '6px 10px',\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n background: '#0a84ff',\n color: '#fff',\n fontWeight: '600',\n cursor: 'pointer',\n });\n Object.assign(badge.style, {\n background: '#fff',\n color: '#0a84ff',\n padding: '1px 6px',\n borderRadius: '3px',\n fontSize: '10px',\n letterSpacing: '0.5px',\n });\n badge.textContent = 'MOCK';\n Object.assign(counter.style, {\n marginLeft: 'auto',\n fontSize: '11px',\n opacity: '0.9',\n });\n Object.assign(toggle.style, {\n background: 'transparent',\n border: '1px solid rgba(255,255,255,0.5)',\n color: '#fff',\n cursor: 'pointer',\n padding: '0 6px',\n borderRadius: '3px',\n fontFamily: 'inherit',\n });\n header.append(badge, document.createTextNode('condor-bridge'), counter, toggle);\n return header;\n}\n\nfunction buildSentSection(): { container: HTMLElement; list: HTMLElement } {\n const container = document.createElement('div');\n container.className = 'condor-mock-panel-sent';\n Object.assign(container.style, {\n padding: '6px 10px',\n borderBottom: '1px solid rgba(255,255,255,0.08)',\n maxHeight: '180px',\n overflowY: 'auto',\n });\n const title = document.createElement('div');\n title.textContent = 'Sent';\n Object.assign(title.style, { opacity: '0.7', marginBottom: '4px', fontSize: '11px' });\n const list = document.createElement('div');\n list.className = 'condor-mock-panel-sent-list';\n container.append(title, list);\n return { container, list };\n}\n\nfunction buildTriggerSection(\n bridgeKeys: readonly string[],\n onEmit: (key: string, payloadJson: string) => void,\n onClear: () => void,\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'condor-mock-panel-trigger';\n Object.assign(container.style, {\n padding: '6px 10px',\n display: 'flex',\n flexDirection: 'column',\n gap: '4px',\n });\n const title = document.createElement('div');\n title.textContent = 'Trigger emit';\n Object.assign(title.style, { opacity: '0.7', fontSize: '11px' });\n\n const select = document.createElement('select');\n select.className = 'condor-mock-panel-key-select';\n Object.assign(select.style, {\n background: 'transparent',\n color: 'inherit',\n border: '1px solid rgba(255,255,255,0.2)',\n padding: '4px',\n borderRadius: '3px',\n fontFamily: 'inherit',\n fontSize: '11px',\n });\n for (const key of bridgeKeys) {\n const opt = document.createElement('option');\n opt.value = key;\n opt.textContent = key;\n select.appendChild(opt);\n }\n const ackOpt = document.createElement('option');\n ackOpt.value = 'handshake-ack';\n ackOpt.textContent = 'handshake-ack';\n select.appendChild(ackOpt);\n\n const textarea = document.createElement('textarea');\n textarea.className = 'condor-mock-panel-payload';\n textarea.placeholder = '{ \"data\": \"pong\" }';\n Object.assign(textarea.style, {\n background: 'transparent',\n color: 'inherit',\n border: '1px solid rgba(255,255,255,0.2)',\n padding: '4px',\n borderRadius: '3px',\n fontFamily: 'inherit',\n fontSize: '11px',\n minHeight: '50px',\n resize: 'vertical',\n });\n\n const buttons = document.createElement('div');\n Object.assign(buttons.style, { display: 'flex', gap: '6px' });\n\n const emitBtn = document.createElement('button');\n emitBtn.type = 'button';\n emitBtn.textContent = 'Emit';\n emitBtn.className = 'condor-mock-panel-emit-btn';\n Object.assign(emitBtn.style, {\n background: '#0a84ff',\n color: '#fff',\n border: 'none',\n padding: '4px 10px',\n borderRadius: '3px',\n cursor: 'pointer',\n fontFamily: 'inherit',\n fontSize: '11px',\n flex: '1',\n });\n emitBtn.addEventListener('click', () => {\n onEmit(select.value, textarea.value);\n });\n\n const clearBtn = document.createElement('button');\n clearBtn.type = 'button';\n clearBtn.textContent = 'Clear sent';\n clearBtn.className = 'condor-mock-panel-clear-btn';\n Object.assign(clearBtn.style, {\n background: 'transparent',\n color: 'inherit',\n border: '1px solid rgba(255,255,255,0.3)',\n padding: '4px 10px',\n borderRadius: '3px',\n cursor: 'pointer',\n fontFamily: 'inherit',\n fontSize: '11px',\n });\n clearBtn.addEventListener('click', onClear);\n\n buttons.append(emitBtn, clearBtn);\n container.append(title, select, textarea, buttons);\n return container;\n}\n\nfunction buildAutoRespondSection(\n initialEnabled: boolean,\n onChange: (next: boolean) => void,\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'condor-mock-panel-auto-respond';\n Object.assign(container.style, {\n padding: '6px 10px',\n borderBottom: '1px solid rgba(255,255,255,0.08)',\n display: 'flex',\n flexDirection: 'column',\n gap: '2px',\n });\n\n const title = document.createElement('div');\n title.textContent = 'Auto-respond';\n Object.assign(title.style, { opacity: '0.7', fontSize: '11px' });\n\n const row = document.createElement('label');\n Object.assign(row.style, {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: 'pointer',\n fontSize: '12px',\n });\n\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.className = 'condor-mock-panel-auto-respond-toggle';\n checkbox.checked = initialEnabled;\n Object.assign(checkbox.style, {\n cursor: 'pointer',\n });\n\n const label = document.createElement('span');\n label.textContent = 'Enabled — mockEmit em resposta a cada send (policy do cliente)';\n\n checkbox.addEventListener('change', () => {\n onChange(checkbox.checked);\n });\n\n row.append(checkbox, label);\n container.append(title, row);\n return container;\n}\n\nfunction renderSentList(list: HTMLElement, adapter: MockAdapter, max: number): void {\n list.innerHTML = '';\n const all = adapter.getMessages();\n const tail = all.slice(-max).reverse();\n for (const msg of tail) {\n const row = document.createElement('div');\n Object.assign(row.style, {\n padding: '3px 0',\n borderBottom: '1px solid rgba(255,255,255,0.06)',\n wordBreak: 'break-all',\n });\n const ts = new Date(msg.timestamp).toLocaleTimeString();\n const payload = msg.payload as { type?: string };\n const type = payload?.type ?? '?';\n row.textContent = `${ts} → ${type} ${JSON.stringify(msg.payload).slice(0, 80)}`;\n list.appendChild(row);\n }\n}\n\ninterface InstalledPanel {\n uninstall(): void;\n refresh(): void;\n}\n\nlet currentPanel: InstalledPanel | null = null;\n\nexport function installDevPanel(params: InstallParams): InstalledPanel {\n if (typeof document === 'undefined') {\n throw new Error('condor-mobile-bridge dev panel: document is undefined (Node-only env).');\n }\n\n // Idempotente: remove painel anterior se existir.\n if (currentPanel) {\n currentPanel.uninstall();\n currentPanel = null;\n }\n\n const opts = resolveOptions(params.options);\n const root = document.createElement('div');\n root.id = PANEL_ID;\n applyPanelStyles(root, opts);\n\n const badge = document.createElement('span');\n const counter = document.createElement('span');\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.textContent = opts.startCollapsed ? '+' : '−';\n const header = buildHeader(badge, counter, toggle);\n\n const body = document.createElement('div');\n Object.assign(body.style, {\n display: opts.startCollapsed ? 'none' : 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n });\n\n const { container: sentContainer, list: sentList } = buildSentSection();\n\n // Auto-respond section — conditional render: só aparece se policy fornecida.\n const policy = params.options?.onSendForAutoRespond;\n let autoRespondEnabled = opts.startAutoRespondEnabled;\n let unsubscribeSendListener: (() => void) | null = null;\n\n let autoRespondSection: HTMLElement | null = null;\n if (policy) {\n autoRespondSection = buildAutoRespondSection(autoRespondEnabled, (next) => {\n autoRespondEnabled = next;\n });\n unsubscribeSendListener = params.adapter.onCallHandler((msg) => {\n if (!autoRespondEnabled) return;\n let response: AutoRespondResponse | null;\n try {\n response = policy(msg);\n } catch (err) {\n console.error('[condor-mobile-bridge dev-panel] onSendForAutoRespond threw:', err);\n return;\n }\n if (!response) return;\n setTimeout(() => {\n try {\n params.adapter.triggerEmit(response.key, response.payload);\n } catch (err) {\n console.error(\n '[condor-mobile-bridge dev-panel] triggerEmit failed in auto-respond:',\n err,\n );\n }\n }, opts.autoRespondDelayMs);\n });\n }\n\n const trigger = buildTriggerSection(\n Object.values(BRIDGE_KEYS),\n (key, payloadJson) => {\n let payload: unknown = {};\n const trimmed = payloadJson.trim();\n if (trimmed.length > 0) {\n try {\n payload = JSON.parse(trimmed);\n } catch (err) {\n console.error('[condor-mobile-bridge dev-panel] invalid JSON payload', err);\n return;\n }\n }\n params.adapter.triggerEmit(key, payload);\n },\n () => {\n params.adapter.clearMessages();\n renderSentList(sentList, params.adapter, opts.maxMessages);\n counter.textContent = '0 sent';\n },\n );\n\n if (autoRespondSection) {\n body.append(sentContainer, autoRespondSection, trigger);\n } else {\n body.append(sentContainer, trigger);\n }\n root.append(header, body);\n\n toggle.addEventListener('click', (e) => {\n e.stopPropagation();\n const collapsed = body.style.display === 'none';\n body.style.display = collapsed ? 'flex' : 'none';\n toggle.textContent = collapsed ? '−' : '+';\n });\n\n document.body.appendChild(root);\n\n const refresh = () => {\n const count = params.adapter.getMessages().length;\n counter.textContent = `${count} sent`;\n renderSentList(sentList, params.adapter, opts.maxMessages);\n };\n refresh();\n\n const interval = setInterval(refresh, REFRESH_INTERVAL_MS);\n\n const panel: InstalledPanel = {\n uninstall() {\n clearInterval(interval);\n unsubscribeSendListener?.();\n unsubscribeSendListener = null;\n root.remove();\n if (currentPanel === panel) currentPanel = null;\n },\n refresh,\n };\n currentPanel = panel;\n return panel;\n}\n\n/**\n * Inspeção (testes/debug).\n */\nexport function getInstalledDevPanel(): InstalledPanel | null {\n return currentPanel;\n}\n","/**\n * Mock adapter — ativo quando `window.flutter_inappwebview` não é\n * detectado (browser dev, jsdom sem host instalado, Storybook etc.).\n *\n * Comportamento:\n * - `callHandler` grava `{handlerName, payload, timestamp}` em buffer\n * interno (cap 100, FIFO), loga via `console.debug` e dispatcha\n * listeners registrados via `onCallHandler`.\n * - `triggerEmit` dispatcha via `window.CondorBridge.emit` — bridge real\n * trata como se viesse do nativo (handshake-ack resolve handshake;\n * demais chaves vão pros subscribers).\n * - `getMessages` retorna snapshot read-only do buffer.\n * - `clearMessages` esvazia o buffer (útil em testes).\n * - `onCallHandler(listener)` — registra listener invocado a cada send.\n * Retorna função de unsubscribe. Útil para dev panel reagir a sends\n * (v0.3.0+ auto-respond toggle) ou análise em testes.\n */\n\nconst MAX_BUFFER = 100;\n\nexport interface MockMessage {\n handlerName: string;\n payload: unknown;\n timestamp: number;\n}\n\nexport interface MockAdapter {\n readonly isMock: true;\n callHandler(handlerName: string, payload: unknown): void;\n getMessages(): readonly MockMessage[];\n clearMessages(): void;\n triggerEmit(key: string, payload: unknown): void;\n /**\n * Registra listener invocado a cada `callHandler` (mensagem JS→Native).\n * Retorna função de unsubscribe.\n *\n * Erros em listeners são capturados + logados; não afetam outros\n * listeners nem o pipeline do bridge.\n *\n * @since v0.3.0\n */\n onCallHandler(listener: (msg: MockMessage) => void): () => void;\n}\n\nexport function createMockAdapter(): MockAdapter {\n const messages: MockMessage[] = [];\n const sendListeners = new Set<(msg: MockMessage) => void>();\n\n return {\n isMock: true,\n\n callHandler(handlerName, payload) {\n const msg: MockMessage = { handlerName, payload, timestamp: Date.now() };\n messages.push(msg);\n if (messages.length > MAX_BUFFER) {\n messages.shift();\n }\n console.debug('[condor-mobile-bridge mock] →', handlerName, payload);\n for (const listener of sendListeners) {\n try {\n listener(msg);\n } catch (err) {\n console.error('[condor-mobile-bridge mock] send listener threw:', err);\n }\n }\n },\n\n getMessages() {\n return messages;\n },\n\n clearMessages() {\n messages.length = 0;\n },\n\n triggerEmit(key, payload) {\n const bridge = typeof window !== 'undefined' ? window.CondorBridge : undefined;\n if (!bridge || typeof bridge.emit !== 'function') {\n throw new Error(\n 'condor-mobile-bridge mock: window.CondorBridge not bootstrapped; cannot emit.',\n );\n }\n bridge.emit(key, payload);\n },\n\n onCallHandler(listener) {\n sendListeners.add(listener);\n return () => {\n sendListeners.delete(listener);\n };\n },\n };\n}\n","/**\n * Detecção do host nativo + adapter pattern.\n *\n * **v0.1.0:** lançava erro quando `window.flutter_inappwebview` ausente.\n * **v0.2.0:** ativa mock adapter (`createMockAdapter`) — destrava dev em\n * browser comum sem buildar APK.\n *\n * Adapter ativo é cached na primeira chamada de `getActiveAdapter()`.\n * Tests devem chamar `__resetAdapterForTests()` no `beforeEach` para\n * forçar re-detecção (ex.: após instalar/remover `window.flutter_inappwebview`).\n */\n\nimport { createMockAdapter, type MockAdapter } from './mock';\n\ninterface FlutterInAppWebView {\n callHandler(name: string, ...args: unknown[]): Promise<unknown>;\n}\n\ndeclare global {\n interface Window {\n flutter_inappwebview?: FlutterInAppWebView;\n }\n}\n\nexport interface RealAdapter {\n readonly isMock: false;\n callHandler(handlerName: string, payload: unknown): void;\n}\n\nexport type NativeAdapter = RealAdapter | MockAdapter;\n\nlet activeAdapter: NativeAdapter | null = null;\n\nfunction createRealAdapter(host: FlutterInAppWebView): RealAdapter {\n return {\n isMock: false,\n callHandler(handlerName, payload) {\n void host.callHandler(handlerName, payload);\n },\n };\n}\n\nfunction detectAdapter(): NativeAdapter {\n if (typeof window === 'undefined') {\n return createMockAdapter();\n }\n const host = window.flutter_inappwebview;\n if (host && typeof host.callHandler === 'function') {\n return createRealAdapter(host);\n }\n return createMockAdapter();\n}\n\nexport function getActiveAdapter(): NativeAdapter {\n if (!activeAdapter) {\n activeAdapter = detectAdapter();\n }\n return activeAdapter;\n}\n\nexport function hasNativeHost(): boolean {\n return !getActiveAdapter().isMock;\n}\n\n/**\n * Envia mensagem ao native handler via adapter ativo.\n *\n * Em modo real: fire-and-forget `flutter_inappwebview.callHandler`.\n * Em modo mock: grava no buffer + `console.debug`.\n */\nexport function callNative(handlerName: string, payload: unknown): void {\n getActiveAdapter().callHandler(handlerName, payload);\n}\n\n/**\n * Reseta cache do adapter. **Exportado para uso em testes apenas.**\n *\n * Cliente NUNCA deve chamar isso em código de aplicação — o adapter é\n * detectado uma vez no startup e mantido.\n */\nexport function __resetAdapterForTests(): void {\n activeAdapter = null;\n}\n","import type { BridgeKey } from '../keys';\nimport type { PayloadOf } from '../payloads';\n\ntype Callback = (payload: unknown) => void;\n\nconst subscribers = new Map<string, Set<Callback>>();\n\nexport function addSubscriber<K extends BridgeKey>(\n key: K,\n cb: (payload: PayloadOf<K>) => void,\n): () => void {\n let set = subscribers.get(key);\n if (!set) {\n set = new Set<Callback>();\n subscribers.set(key, set);\n }\n set.add(cb as Callback);\n return () => {\n const current = subscribers.get(key);\n if (!current) return;\n current.delete(cb as Callback);\n if (current.size === 0) {\n subscribers.delete(key);\n }\n };\n}\n\nexport function dispatchToSubscribers(key: string, payload: unknown): void {\n const set = subscribers.get(key);\n if (!set) return;\n for (const cb of set) {\n try {\n cb(payload);\n } catch (err) {\n // Subscriber bugado não deve afetar os outros nem o bridge.\n console.error(`[condor-mobile-bridge] subscriber for \"${key}\" threw:`, err);\n }\n }\n}\n\n/**\n * Limpa todos os subscribers. Útil em testes para isolar casos.\n */\nexport function clearAllSubscribers(): void {\n subscribers.clear();\n}\n\n/**\n * Inspeção (testes/debug).\n */\nexport function getSubscriberCount(key: string): number {\n return subscribers.get(key)?.size ?? 0;\n}\n\n/**\n * Lista das chaves com subscribers ativos (testes/debug/dev panel).\n */\nexport function getSubscriberKeys(): string[] {\n return Array.from(subscribers.keys());\n}\n","import type { DevPanelOptions } from './internal/dev-panel';\nimport { installDevPanel as installDevPanelInternal } from './internal/dev-panel';\nimport type { MockAdapter, MockMessage } from './internal/mock';\nimport { callNative, getActiveAdapter } from './internal/native';\nimport { addSubscriber, dispatchToSubscribers, getSubscriberKeys } from './internal/subscribers';\nimport type { BridgeKey } from './keys';\nimport type { HandshakeAck, HandshakeOptions } from './messages';\nimport type { PayloadOf } from './payloads';\n\nconst HANDSHAKE_ACK_KEY = 'handshake-ack';\nconst NATIVE_HANDLER = 'CondorBridge';\nconst PROTOCOL_VERSION = '0.1.0';\n\ninterface PendingHandshake {\n resolve: (ack: HandshakeAck) => void;\n reject: (err: Error) => void;\n}\n\ninterface WindowCondorBridge {\n emit(key: string, payload: unknown): void;\n}\n\ndeclare global {\n interface Window {\n CondorBridge?: WindowCondorBridge;\n }\n}\n\nlet pendingHandshake: PendingHandshake | null = null;\nlet bridgeBootstrapped = false;\n\nfunction emit(key: string, payload: unknown): void {\n if (key === HANDSHAKE_ACK_KEY && pendingHandshake) {\n const ack = (payload ?? {}) as Partial<HandshakeAck>;\n const resolver = pendingHandshake;\n pendingHandshake = null;\n resolver.resolve({\n nativeVersion: ack.nativeVersion ?? 'unknown',\n accepted: ack.accepted ?? false,\n });\n return;\n }\n dispatchToSubscribers(key, payload);\n}\n\nfunction bootstrapWindowBridge(): void {\n if (bridgeBootstrapped) return;\n if (typeof window === 'undefined') return;\n bridgeBootstrapped = true;\n\n if (window.CondorBridge && typeof window.CondorBridge.emit === 'function') {\n // Já instalado (HMR / double import / mock instalado por testes).\n // Substitui apenas o emit para garantir que o dispatcher mais recente\n // esteja conectado aos subscribers desta instância do módulo.\n window.CondorBridge.emit = emit;\n } else {\n window.CondorBridge = { emit };\n }\n}\n\nbootstrapWindowBridge();\n\nfunction requireMockAdapter(): MockAdapter {\n const adapter = getActiveAdapter();\n if (!adapter.isMock) {\n throw new Error(\n 'condor-mobile-bridge: this API is available only in mock mode (no native host detected).',\n );\n }\n return adapter;\n}\n\nexport interface MockState {\n readonly isMock: true;\n readonly messages: readonly MockMessage[];\n readonly subscriberKeys: readonly string[];\n}\n\nexport interface CondorBridge {\n /**\n * `true` quando o adapter ativo é mock (browser dev, sem\n * `window.flutter_inappwebview`). `false` quando rodando dentro do\n * WebView Flutter.\n */\n readonly isMock: boolean;\n\n /**\n * Envia handshake versionado ao nativo e aguarda `handshake-ack`.\n *\n * Apenas um handshake pode estar pendente por vez. Chamadas paralelas\n * antes do primeiro `ack` chegar são rejeitadas com erro claro.\n *\n * **Em mock mode:** NÃO resolve sozinho. Cliente/teste deve chamar\n * `Bridge.mockEmit('handshake-ack', {nativeVersion, accepted})` para\n * destravar a Promise. Esta decisão (`Explícito`) força testes a serem\n * intencionais sobre o estado nativo.\n */\n handshake(opts: HandshakeOptions): Promise<HandshakeAck>;\n\n /**\n * Subscreve a uma chave observável. Retorna função `unsubscribe`.\n *\n * Múltiplos subscribers por chave são permitidos. Erro em um subscriber\n * não afeta os outros (capturado e logado em `console.error`).\n */\n on<K extends BridgeKey>(key: K, cb: (payload: PayloadOf<K>) => void): () => void;\n\n /**\n * Envia mensagem genérica ao nativo. Use para comandos UI→BG que não\n * tenham helper dedicado. `type` é a string que o native usa para\n * roteamento no `_onJsMessage`.\n *\n * Em mock mode: grava no buffer interno + `console.debug`.\n */\n send(type: string, payload?: Record<string, unknown>): void;\n\n /**\n * Abre a tela nativa de Settings do app. Equivalente a\n * `Bridge.send('openSettings')`.\n */\n openSettings(): void;\n\n /**\n * Dispara `window.CondorBridge.emit(key, payload)` programaticamente —\n * útil para simular eventos nativos em testes e dev em browser.\n *\n * **Throws** se o adapter ativo não for mock.\n */\n mockEmit(key: string, payload?: unknown): void;\n\n /**\n * Snapshot do estado do mock adapter (mensagens enviadas + chaves com\n * subscribers ativos). Useful para asserts em testes de cliente.\n *\n * **Throws** se o adapter ativo não for mock.\n */\n getMockState(): MockState;\n\n /**\n * Injeta painel HTML flutuante no `document.body` para inspecionar o\n * bridge em mock mode. Idempotente — re-install substitui o anterior.\n *\n * **Throws** se o adapter ativo não for mock OU se `document` não existir.\n */\n installDevPanel(options?: DevPanelOptions): void;\n}\n\nexport const Bridge: CondorBridge = {\n get isMock(): boolean {\n return getActiveAdapter().isMock;\n },\n\n handshake(opts: HandshakeOptions): Promise<HandshakeAck> {\n bootstrapWindowBridge();\n if (pendingHandshake) {\n return Promise.reject(\n new Error(\n 'condor-mobile-bridge: handshake already in flight. Await the previous call before issuing another.',\n ),\n );\n }\n return new Promise<HandshakeAck>((resolve, reject) => {\n pendingHandshake = { resolve, reject };\n try {\n callNative(NATIVE_HANDLER, {\n type: 'handshake',\n version: PROTOCOL_VERSION,\n payload: { clientVersion: opts.clientVersion },\n });\n } catch (err) {\n pendingHandshake = null;\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n },\n\n on<K extends BridgeKey>(key: K, cb: (payload: PayloadOf<K>) => void): () => void {\n bootstrapWindowBridge();\n return addSubscriber(key, cb);\n },\n\n send(type: string, payload?: Record<string, unknown>): void {\n callNative(NATIVE_HANDLER, {\n type,\n version: PROTOCOL_VERSION,\n payload: payload ?? {},\n });\n },\n\n openSettings(): void {\n this.send('openSettings');\n },\n\n mockEmit(key: string, payload?: unknown): void {\n requireMockAdapter().triggerEmit(key, payload ?? {});\n },\n\n getMockState(): MockState {\n const adapter = requireMockAdapter();\n return {\n isMock: true,\n messages: adapter.getMessages(),\n subscriberKeys: getSubscriberKeys(),\n };\n },\n\n installDevPanel(options?: DevPanelOptions): void {\n const adapter = requireMockAdapter();\n installDevPanelInternal({ adapter, options });\n },\n};\n\n/**\n * Reseta o estado interno do bridge (handshake pendente).\n *\n * **Exportado para uso em testes apenas.** Não chamar em código de\n * aplicação — quebra a semântica de \"apenas um handshake pendente\".\n */\nexport function __resetBridgeForTests(): void {\n pendingHandshake = null;\n}\n","/**\n * `BridgeClient` (v0.5.0) — API para código do cliente externo rodando\n * dentro de `<iframe sandbox=\"allow-scripts\">` no `sleep_whitelabel`.\n *\n * Surface idêntica ao `Bridge` legado (`on`/`send`/`openSettings`/`handshake`)\n * — cliente importa e não percebe que está em sandbox. Internamente,\n * todas as chamadas viram `window.parent.postMessage(...)` com schema\n * validado pelo shell antes de chegar ao nativo.\n *\n * **Não expõe** `installDevPanel`, `mockEmit`, `isMock`, `getMockState` —\n * essas APIs são responsabilidade do shell (`BridgeShell`).\n *\n * **Threat model & Camada 2 do Risco 1** (vide\n * `sleep-white-label-content-transport-and-security.md`): este módulo\n * também responde ao `verify-sandbox` do shell — função de attestation\n * do próprio iframe sobre se ainda enxerga `window.flutter_inappwebview`\n * ou `window.CondorBridge` (que deveriam ter sido deletados pela Camada 1).\n * Se vier `true` em algum, o shell aborta o boot do app.\n */\n\nimport { BRIDGE_KEYS, type BridgeKey } from './keys';\nimport type { PayloadOf } from './payloads';\nimport type { ClientToShellMessage, ShellToClientMessage } from './shell-messages';\n\nconst PROTOCOL_VERSION = '0.5.0';\nconst HANDSHAKE_TIMEOUT_MS = 5000;\n\ninterface PendingHandshake {\n resolve: (ack: { shellVersion: string; accepted: boolean }) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\ninterface Subscriber {\n // Stored as (unknown) — typed inference comes from public `on<K>`.\n cb: (payload: unknown) => void;\n}\n\nconst subscribers = new Map<string, Subscriber[]>();\nlet pendingHandshake: PendingHandshake | null = null;\nlet messageListenerInstalled = false;\n\nfunction postToShell(msg: ClientToShellMessage): void {\n if (typeof window === 'undefined' || window.parent === window) {\n // No parent — not in iframe. Caller bug; silently drop.\n console.warn('[BridgeClient] no parent window; message dropped:', msg.type);\n return;\n }\n window.parent.postMessage(msg, '*');\n}\n\nfunction dispatchEvent(key: string, payload: unknown): void {\n const arr = subscribers.get(key);\n if (!arr || arr.length === 0) return;\n for (const sub of arr) {\n try {\n sub.cb(payload);\n } catch (err) {\n console.error('[BridgeClient] subscriber error:', err);\n }\n }\n}\n\nfunction onMessage(event: MessageEvent): void {\n if (event.source !== window.parent) return;\n // Não validamos com Valibot aqui — o shell é trusted (Condor-controlado).\n // Validamos só o `type` para evitar crashes por payload malformado.\n const data = event.data as Partial<ShellToClientMessage>;\n if (!data || typeof data !== 'object' || typeof data.type !== 'string') return;\n\n switch (data.type) {\n case 'event': {\n const msg = data as Extract<ShellToClientMessage, { type: 'event' }>;\n if (typeof msg.key === 'string') dispatchEvent(msg.key, msg.payload);\n break;\n }\n case 'handshake-ack': {\n const msg = data as Extract<ShellToClientMessage, { type: 'handshake-ack' }>;\n if (pendingHandshake) {\n clearTimeout(pendingHandshake.timer);\n const { resolve } = pendingHandshake;\n pendingHandshake = null;\n resolve({\n shellVersion: msg.ack?.shellVersion ?? 'unknown',\n accepted: msg.ack?.accepted ?? false,\n });\n }\n break;\n }\n case 'nack':\n // Cliente pode ouvir nacks via `Bridge.on('__nack', ...)` em futuras versões.\n // v0.5.0: só loga, sem dispatcher dedicado.\n console.warn('[BridgeClient] command nacked by shell:', data);\n break;\n case 'verify-sandbox': {\n // Camada 2 do Risco 1: shell pergunta se ainda enxergamos os bridges\n // nativos. Resposta autoritativa do iframe sobre seu próprio estado.\n const w = window as unknown as Record<string, unknown>;\n postToShell({\n type: 'sandbox-verify-response',\n flutterBridgeReachable: typeof w.flutter_inappwebview !== 'undefined',\n condorBridgeReachable: typeof w.CondorBridge !== 'undefined',\n });\n break;\n }\n }\n}\n\nfunction ensureMessageListener(): void {\n if (messageListenerInstalled) return;\n if (typeof window === 'undefined') return;\n messageListenerInstalled = true;\n window.addEventListener('message', onMessage);\n}\n\nensureMessageListener();\n\nexport interface BridgeClientHandshakeAck {\n shellVersion: string;\n accepted: boolean;\n}\n\nexport interface CondorBridgeClient {\n /**\n * Versão do protocolo do client.\n */\n readonly protocolVersion: string;\n\n /**\n * Subscreve a uma chave observável. Retorna função `unsubscribe`.\n * Múltiplos subscribers por chave permitidos. Erro em um subscriber\n * não afeta os outros.\n */\n on<K extends BridgeKey>(key: K, cb: (payload: PayloadOf<K>) => void): () => void;\n\n /**\n * Envia comando para o shell. Shell valida contra `ShellPolicy` antes\n * de tocar o nativo. Comandos não permitidos → `nack` (logado em\n * `console.warn` na v0.5.0).\n */\n send(command: string, args?: Record<string, unknown>): void;\n\n /**\n * Abre Settings nativa. Equivalente a `send('openSettings')`.\n */\n openSettings(): void;\n\n /**\n * Solicita UMA permissão via **diálogo nativo in-app** (não abre Settings).\n * Equivalente a `send('requestPermission', { permission })`. Use no botão\n * \"Conceder\" do card Necessárias, com um nome vindo de\n * `systemStatus.missingEssentialPermissions` (ex.: `'bluetoothScan'`).\n * O resultado chega via re-emissão de `systemStatus` (sem chave dedicada).\n */\n requestPermission(permission: string): void;\n\n /**\n * Handshake com o shell. Resolve com `{ shellVersion, accepted }`.\n * Timeout em 5s se shell não responder.\n *\n * Apenas um handshake pode estar pendente por vez.\n */\n handshake(opts: { clientVersion: string }): Promise<BridgeClientHandshakeAck>;\n\n /**\n * Reporta ao nativo se o cliente tem histórico interno para voltar.\n * **Chame a cada mudança de rota interna** (e no boot). O nativo cacheia\n * este estado e o usa para decidir o botão/gesture de voltar do SO:\n * - `true` → o back do SO pede uma volta interna (evento `navigationBack`,\n * vide `onBack`).\n * - `false` → o back do SO fica na home (não sai da página).\n *\n * Equivalente a `send('navigationState', { canGoBack })`.\n */\n reportNavigation(canGoBack: boolean): void;\n\n /**\n * Assina o pedido de back do SO (evento `navigationBack`). O callback deve\n * voltar **uma** página interna do cliente e então chamar\n * `reportNavigation(...)` com o novo estado. Só é chamado quando o cliente\n * havia reportado `reportNavigation(true)`. Retorna função `unsubscribe`.\n */\n onBack(cb: () => void): () => void;\n}\n\nexport const BridgeClient: CondorBridgeClient = {\n protocolVersion: PROTOCOL_VERSION,\n\n on<K extends BridgeKey>(key: K, cb: (payload: PayloadOf<K>) => void): () => void {\n ensureMessageListener();\n const arr = subscribers.get(key) ?? [];\n const sub: Subscriber = { cb: cb as (payload: unknown) => void };\n arr.push(sub);\n subscribers.set(key, arr);\n // Notifica shell para registrar subscription no native (idempotente\n // no shell — múltiplos `subscribe` para a mesma chave são tratados).\n postToShell({ type: 'subscribe', key });\n return () => {\n const remaining = (subscribers.get(key) ?? []).filter((s) => s !== sub);\n subscribers.set(key, remaining);\n if (remaining.length === 0) postToShell({ type: 'unsubscribe', key });\n };\n },\n\n send(command: string, args: Record<string, unknown> = {}): void {\n postToShell({ type: 'send', command, args });\n },\n\n openSettings(): void {\n this.send('openSettings');\n },\n\n requestPermission(permission: string): void {\n this.send('requestPermission', { permission });\n },\n\n handshake(opts: { clientVersion: string }): Promise<BridgeClientHandshakeAck> {\n ensureMessageListener();\n if (pendingHandshake) {\n return Promise.reject(\n new Error(\n 'BridgeClient: handshake already in flight. Await previous before issuing another.',\n ),\n );\n }\n return new Promise<BridgeClientHandshakeAck>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingHandshake = null;\n reject(new Error(`BridgeClient: handshake timeout after ${HANDSHAKE_TIMEOUT_MS}ms`));\n }, HANDSHAKE_TIMEOUT_MS);\n pendingHandshake = { resolve, reject, timer };\n postToShell({\n type: 'handshake',\n payload: { clientVersion: opts.clientVersion },\n });\n });\n },\n\n reportNavigation(canGoBack: boolean): void {\n this.send('navigationState', { canGoBack });\n },\n\n onBack(cb: () => void): () => void {\n return this.on(BRIDGE_KEYS.navigationBack, () => cb());\n },\n};\n\n/**\n * Reseta estado interno (subscribers + handshake pendente).\n *\n * **Exportado para uso em testes apenas.** Listener `message` permanece\n * instalado entre testes — é idempotente (checa `event.source ===\n * window.parent` per-call) e o `window` do jsdom persiste entre tests.\n */\nexport function __resetBridgeClientForTests(): void {\n if (pendingHandshake) {\n clearTimeout(pendingHandshake.timer);\n pendingHandshake = null;\n }\n subscribers.clear();\n}\n","/**\n * `BridgeShell` (v0.5.0) — API para o shell Condor-controlado que envolve\n * o conteúdo do cliente externo dentro de `<iframe sandbox=\"allow-scripts\">`.\n *\n * Responsabilidades:\n * 1. **Camada 1 do Risco 1** — `bootstrapClientIframe(iframe, clientPath)`:\n * busca HTML do cliente via `fetch`, prepende script de override que\n * deleta `window.flutter_inappwebview` e `window.CondorBridge` do\n * iframe, e seta como `srcdoc`. Override roda em parse-time, antes\n * de qualquer outro script.\n * 2. **Camada 2 do Risco 1** — `verifySandbox(iframe)`: round-trip\n * `postMessage` que pede ao iframe atestation autoritativa de que\n * `window.flutter_inappwebview` e `window.CondorBridge` são `undefined`.\n * 3. **Roteamento postMessage iframe ↔ nativo:** escuta `message` events\n * do iframe, valida com Valibot, aplica `ShellPolicy` default-deny,\n * e propaga para o `Bridge` legado (que fala com o nativo). Eventos\n * nativos voltam para o iframe via `postMessage`.\n * 4. **Confirmação humana para comandos sensíveis:** comandos em\n * `policy.requiresConfirmation` disparam callback\n * `onConfirmationRequest` antes de tocar o nativo — shell apresenta UI.\n */\n\nimport * as v from 'valibot';\nimport { Bridge } from './bridge';\nimport type { BridgeKey } from './keys';\nimport {\n ClientToShellMessage,\n type ConfirmationRequest,\n type SandboxVerifyResult,\n type ShellPolicy,\n type ShellToClientMessage,\n} from './shell-messages';\n\nconst SHELL_VERSION = '0.5.1';\nconst VERIFY_TIMEOUT_MS = 2000;\n\n/**\n * **Camada 1 do Risco 1 — override prefix.**\n *\n * Script prependado ao srcdoc do iframe pelo `bootstrapClientIframe*`.\n * Roda em parse-time, antes de qualquer outro script do cliente, no\n * próprio documento do iframe (origem null — sem cross-origin block).\n *\n * Usa **getter/setter no-op** + `configurable: false` (não\n * `value/writable: false`). Razão: bundles ESM modernos rodam em strict\n * mode; qualquer `window.X = …` em propriedade não-writable **throws**,\n * o que quebra bootstrap de módulos legados que tentam registrar APIs no\n * window (ex.: `condor-mobile-bridge` legacy faz\n * `window.CondorBridge = {emit}` como side-effect no module load).\n * Com getter/setter no-op, a atribuição passa silenciosamente mas o\n * getter sempre retorna undefined — bridges nativos permanecem\n * inacessíveis no iframe sem quebrar fluxos JS legítimos.\n *\n * Validado empiricamente via pen test ofensivo em `moto g 9 plus`\n * durante o trio sandbox iframe (2026-06-09).\n */\nconst OVERRIDE_PREFIX = `<script>\n(function(){\n try { delete window.flutter_inappwebview; } catch (_) {}\n try { delete window.CondorBridge; } catch (_) {}\n try {\n Object.defineProperty(window, 'flutter_inappwebview', {\n get: function(){ return undefined; },\n set: function(_){ /* no-op: bloqueia injeção do plugin no iframe */ },\n configurable: false,\n });\n } catch (_) {}\n try {\n Object.defineProperty(window, 'CondorBridge', {\n get: function(){ return undefined; },\n set: function(_){ /* no-op: bloqueia bootstrap do package legado */ },\n configurable: false,\n });\n } catch (_) {}\n try {\n // Neutraliza navigator.sendBeacon: canal de exfil que a CSP connect-src\n // não barra de forma confiável em alguns WebViews (pentest 2026-06-24,\n // PT-10). O cliente comunica só via BridgeClient/postMessage — não tem\n // uso legítimo de sendBeacon. Getter no-op retorna função que devolve\n // false; setter no-op ignora tentativa de restaurar.\n Object.defineProperty(navigator, 'sendBeacon', {\n get: function(){ return function(){ return false; }; },\n set: function(_){ /* no-op */ },\n configurable: false,\n });\n } catch (_) {}\n})();\n</script>`;\n\nexport interface BridgeShellOptions {\n /**\n * Iframe que hospeda o cliente. Pode estar vazio no momento da\n * construção — `bootstrapClientIframe` será chamado depois para popular.\n */\n iframe: HTMLIFrameElement;\n\n /**\n * Policy default-deny. Toda chave/comando vindo do iframe é validado\n * contra estas listas. Vide `ShellPolicy`.\n */\n policy: ShellPolicy;\n\n /**\n * Callback síncrono ou assíncrono chamado quando o cliente pede um\n * comando em `policy.requiresConfirmation`. Shell apresenta UI de\n * confirmação; resolve com `true` (aprovar) ou `false` (recusar).\n * Se não fornecido, comandos `requiresConfirmation` são **negados**\n * automaticamente — fail-safe.\n */\n onConfirmationRequest?: (req: ConfirmationRequest) => boolean | Promise<boolean>;\n}\n\nexport class BridgeShell {\n private readonly iframe: HTMLIFrameElement;\n private readonly policy: ShellPolicy;\n private readonly onConfirm?: BridgeShellOptions['onConfirmationRequest'];\n private readonly subscriptions = new Map<BridgeKey, () => void>();\n private detached = false;\n\n // Map de `verify-sandbox` round-trips pendentes — key gerada por nonce.\n private pendingVerifies = new Map<\n string,\n {\n resolve: (r: SandboxVerifyResult) => void;\n reject: (e: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n\n constructor(opts: BridgeShellOptions) {\n this.iframe = opts.iframe;\n this.policy = opts.policy;\n this.onConfirm = opts.onConfirmationRequest;\n if (typeof window !== 'undefined') {\n window.addEventListener('message', this.onMessage);\n }\n }\n\n /**\n * **Camada 1 do Risco 1.** Bootstrap via fetch de URL.\n *\n * Atalho para o caso comum HTTP/HTTPS: faz `fetch(clientPath)` e\n * delega para `bootstrapClientIframeWithHtml`.\n *\n * **Não funciona em scheme `file://`** — `fetch` em WebView Android\n * (Chromium) bloqueia o scheme com \"URL scheme 'file' is not supported\".\n * Para asset bundled em APK ou bundle inlined no build-time, use\n * `bootstrapClientIframeWithHtml(htmlString)`.\n *\n * **Devo ser chamado uma vez no boot do shell**, antes de qualquer\n * outra interação. Após retornar, `verifySandbox()` deve ser invocado\n * para atestation (Camada 2).\n */\n async bootstrapClientIframe(clientPath = 'client.html'): Promise<void> {\n const res = await fetch(clientPath);\n if (!res.ok) throw new Error(`BridgeShell: falha ao carregar ${clientPath} (${res.status})`);\n const clientHtml = await res.text();\n await this.bootstrapClientIframeWithHtml(clientHtml);\n }\n\n /**\n * **Camada 1 do Risco 1.** Bootstrap a partir de HTML string.\n *\n * Aceita HTML do cliente diretamente (já lido de arquivo, embedado em\n * build-time via `esbuild.define`, lido de IndexedDB, etc.) e seta\n * como `srcdoc` do iframe prefixado pelo override script. Override\n * roda em parse-time, antes de qualquer outro script do cliente.\n *\n * Use quando `fetch` não é aplicável (asset bundled em `file://`,\n * bundle inlined no build, qualquer cenário sem rede). Para HTTP\n * use `bootstrapClientIframe(clientPath)`.\n *\n * **Devo ser chamado uma vez no boot do shell**, antes de qualquer\n * outra interação. Após retornar, `verifySandbox()` deve ser invocado\n * para atestation (Camada 2).\n */\n async bootstrapClientIframeWithHtml(clientHtml: string): Promise<void> {\n this.iframe.srcdoc = OVERRIDE_PREFIX + clientHtml;\n await this.waitForIframeLoad();\n }\n\n private waitForIframeLoad(): Promise<void> {\n return new Promise<void>((resolve) => {\n const onLoad = (): void => {\n this.iframe.removeEventListener('load', onLoad);\n resolve();\n };\n this.iframe.addEventListener('load', onLoad);\n });\n }\n\n /**\n * **Camada 2 do Risco 1.** Round-trip `postMessage` que pede ao iframe\n * atestation autoritativa do estado de `window.flutter_inappwebview`\n * e `window.CondorBridge`. Timeout em 2s.\n *\n * `ok` é `true` apenas se ambos forem `undefined` no iframe. Se vier\n * `false`, **shell deve abortar o boot do app** — Camada 1 falhou e\n * Camada 4 (dual WebView) precisa ser acionada manualmente.\n */\n verifySandbox(): Promise<SandboxVerifyResult> {\n if (!this.iframe.contentWindow) {\n return Promise.reject(new Error('BridgeShell: iframe sem contentWindow'));\n }\n return new Promise<SandboxVerifyResult>((resolve, reject) => {\n const nonce = Math.random().toString(36).slice(2);\n const timer = setTimeout(() => {\n this.pendingVerifies.delete(nonce);\n reject(new Error(`BridgeShell: verify-sandbox timeout após ${VERIFY_TIMEOUT_MS}ms`));\n }, VERIFY_TIMEOUT_MS);\n this.pendingVerifies.set(nonce, { resolve, reject, timer });\n this.postToClient({ type: 'verify-sandbox' });\n });\n }\n\n /**\n * Encerra o shell — remove listener `message`, cancela subscriptions\n * pendentes no `Bridge` nativo, limpa estado interno.\n */\n detach(): void {\n if (this.detached) return;\n this.detached = true;\n if (typeof window !== 'undefined') {\n window.removeEventListener('message', this.onMessage);\n }\n for (const unsub of this.subscriptions.values()) unsub();\n this.subscriptions.clear();\n for (const pending of this.pendingVerifies.values()) {\n clearTimeout(pending.timer);\n pending.reject(new Error('BridgeShell: detached'));\n }\n this.pendingVerifies.clear();\n }\n\n private onMessage = async (event: MessageEvent): Promise<void> => {\n if (this.detached) return;\n if (event.source !== this.iframe.contentWindow) return;\n\n const parsed = v.safeParse(ClientToShellMessage, event.data);\n if (!parsed.success) {\n // Drop silently — possivelmente noise (extensão de browser, dev tool).\n // Loga só em modo dev para diagnose.\n if (typeof console !== 'undefined') {\n console.debug('[BridgeShell] dropped malformed message:', parsed.issues?.[0]?.message);\n }\n return;\n }\n\n const msg = parsed.output;\n switch (msg.type) {\n case 'subscribe':\n await this.handleSubscribe(msg.key as BridgeKey);\n break;\n case 'unsubscribe':\n this.handleUnsubscribe(msg.key as BridgeKey);\n break;\n case 'send':\n await this.handleSend(msg.command, msg.args ?? {});\n break;\n case 'handshake':\n // Handshake do cliente é com o shell (não com o nativo diretamente).\n // Shell responde com sua versão. Se quiser propagar pro nativo, o\n // shell faz isso por iniciativa própria via `Bridge.handshake`.\n this.postToClient({\n type: 'handshake-ack',\n ack: { shellVersion: SHELL_VERSION, accepted: true },\n });\n break;\n case 'jsError':\n // Forward erro JS do iframe para o nativo (roteia para Logs).\n Bridge.send('jsError', {\n origin: 'iframe',\n message: msg.payload.message,\n stack: msg.payload.stack,\n source: msg.payload.source,\n lineno: msg.payload.lineno,\n colno: msg.payload.colno,\n });\n break;\n case 'sandbox-verify-response': {\n // Camada 2: cliente respondeu ao verify-sandbox. Resolve o pending\n // mais antigo (em prática só há um por vez — mantemos map por\n // robustez).\n const first = this.pendingVerifies.keys().next().value;\n if (first !== undefined) {\n const pending = this.pendingVerifies.get(first);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingVerifies.delete(first);\n const ok = !msg.flutterBridgeReachable && !msg.condorBridgeReachable;\n pending.resolve({\n flutterBridgeReachable: msg.flutterBridgeReachable,\n condorBridgeReachable: msg.condorBridgeReachable,\n ok,\n });\n }\n }\n break;\n }\n }\n };\n\n private async handleSubscribe(key: BridgeKey): Promise<void> {\n if (!this.policy.readOnlyKeys.includes(key)) {\n this.postToClient({ type: 'nack', key, reason: 'not-read-only' });\n return;\n }\n if (this.subscriptions.has(key)) return; // idempotent\n const unsub = Bridge.on(key, (payload) => {\n if (this.detached) return;\n this.postToClient({ type: 'event', key, payload });\n });\n this.subscriptions.set(key, unsub);\n }\n\n private handleUnsubscribe(key: BridgeKey): void {\n const unsub = this.subscriptions.get(key);\n if (!unsub) return;\n unsub();\n this.subscriptions.delete(key);\n }\n\n private async handleSend(command: string, args: Record<string, unknown>): Promise<void> {\n if (this.policy.deniedCommands.includes(command)) {\n this.postToClient({ type: 'nack', command, reason: 'denied' });\n return;\n }\n if (this.policy.requiresConfirmation.includes(command)) {\n if (!this.onConfirm) {\n // Fail-safe: sem callback, comando que requer confirmação é negado.\n this.postToClient({ type: 'nack', command, reason: 'no-confirmation-handler' });\n return;\n }\n let approved = false;\n try {\n approved = await this.onConfirm({ command, args });\n } catch (err) {\n this.postToClient({\n type: 'nack',\n command,\n reason: `confirmation-error: ${err instanceof Error ? err.message : String(err)}`,\n });\n return;\n }\n if (!approved) {\n this.postToClient({ type: 'nack', command, reason: 'user-rejected' });\n return;\n }\n } else if (!this.policy.allowedCommands.includes(command)) {\n // Comando fora de allowed/denied/requiresConfirmation — drop por default.\n this.postToClient({ type: 'nack', command, reason: 'not-allowed' });\n return;\n }\n Bridge.send(command, args);\n }\n\n private postToClient(msg: ShellToClientMessage): void {\n if (this.detached) return;\n this.iframe.contentWindow?.postMessage(msg, '*');\n }\n}\n","/**\n * Schemas Valibot dos `postMessage` shell↔client (v0.5.0).\n *\n * Quando o conteúdo do cliente roda dentro de `<iframe sandbox=\"allow-scripts\">`\n * (origem null), a comunicação com o shell Condor-controlado acontece via\n * `window.postMessage`. Estes schemas são o **único contrato aceito** —\n * qualquer mensagem mal-formada é descartada silenciosamente pelo shell.\n *\n * **Threat model:** o iframe pode ser hostil (supply-chain comprometida,\n * XSS no código do cliente). Estes schemas + a `ShellPolicy` aplicada\n * no `BridgeShell` formam o boundary estrutural — vide\n * `condor_wiki/cross-cutting/sleep-white-label-content-transport-and-security.md`.\n */\n\nimport * as v from 'valibot';\nimport { BRIDGE_KEYS } from './keys';\n\nconst BridgeKeySchema = v.picklist(Object.values(BRIDGE_KEYS) as [string, ...string[]]);\n\n// ─── Client → Shell ──────────────────────────────────────────────────\n\nexport const SubscribeMessage = v.object({\n type: v.literal('subscribe'),\n key: BridgeKeySchema,\n});\n\nexport const UnsubscribeMessage = v.object({\n type: v.literal('unsubscribe'),\n key: BridgeKeySchema,\n});\n\nexport const SendMessage = v.object({\n type: v.literal('send'),\n command: v.string(),\n args: v.optional(v.record(v.string(), v.unknown()), {}),\n});\n\nexport const HandshakeMessage = v.object({\n type: v.literal('handshake'),\n payload: v.object({ clientVersion: v.string() }),\n});\n\nexport const JsErrorMessage = v.object({\n type: v.literal('jsError'),\n payload: v.object({\n message: v.string(),\n stack: v.optional(v.string()),\n source: v.optional(v.string()),\n lineno: v.optional(v.number()),\n colno: v.optional(v.number()),\n }),\n});\n\nexport const SandboxVerifyResponseMessage = v.object({\n type: v.literal('sandbox-verify-response'),\n flutterBridgeReachable: v.boolean(),\n condorBridgeReachable: v.boolean(),\n});\n\nexport const ClientToShellMessage = v.variant('type', [\n SubscribeMessage,\n UnsubscribeMessage,\n SendMessage,\n HandshakeMessage,\n JsErrorMessage,\n SandboxVerifyResponseMessage,\n]);\n\nexport type ClientToShellMessage = v.InferOutput<typeof ClientToShellMessage>;\n\n// ─── Shell → Client ──────────────────────────────────────────────────\n\nexport const EventMessage = v.object({\n type: v.literal('event'),\n key: BridgeKeySchema,\n payload: v.unknown(),\n});\n\nexport const HandshakeAckMessage = v.object({\n type: v.literal('handshake-ack'),\n ack: v.object({\n shellVersion: v.string(),\n accepted: v.boolean(),\n }),\n});\n\nexport const NackMessage = v.object({\n type: v.literal('nack'),\n key: v.optional(v.string()),\n command: v.optional(v.string()),\n reason: v.string(),\n});\n\nexport const VerifySandboxMessage = v.object({\n type: v.literal('verify-sandbox'),\n});\n\nexport const ShellToClientMessage = v.variant('type', [\n EventMessage,\n HandshakeAckMessage,\n NackMessage,\n VerifySandboxMessage,\n]);\n\nexport type ShellToClientMessage = v.InferOutput<typeof ShellToClientMessage>;\n\n// ─── Policy ──────────────────────────────────────────────────────────\n\n/**\n * Policy default-deny aplicada pelo `BridgeShell`. Toda chave/comando\n * vindo do iframe é validado contra estas listas antes de tocar o\n * bridge nativo.\n *\n * - `readOnlyKeys`: chaves que o cliente pode subscrever (observáveis).\n * - `allowedCommands`: comandos pass-through (sem confirmação humana).\n * - `requiresConfirmation`: comandos que disparam callback `onConfirmationRequest`\n * no shell — usuário decide aprovar/recusar via UI nativa do shell.\n * - `deniedCommands`: bloqueio explícito (defesa em profundidade — comandos\n * destrutivos como `dfu` que nunca devem fluir do iframe).\n *\n * **Não embedada no package** — cliente do `BridgeShell` (no monorepo\n * `sleep_whitelabel`) provê a policy concreta. Mantém o package agnóstico\n * ao catálogo e permite policy evoluir sem novo release.\n */\nexport interface ShellPolicy {\n readOnlyKeys: string[];\n allowedCommands: string[];\n requiresConfirmation: string[];\n deniedCommands: string[];\n}\n\n/**\n * Resposta do callback `onConfirmationRequest` do `BridgeShell`.\n *\n * `approve: true` → comando flui para o nativo.\n * `approve: false` → shell envia `nack` ao iframe com `reason: 'user-rejected'`.\n */\nexport interface ConfirmationRequest {\n command: string;\n args: Record<string, unknown>;\n}\n\n/**\n * Estado retornado pelo `verifySandbox()` do `BridgeShell` após\n * round-trip com o iframe. Se algum dos `*Reachable` vier `true`, o\n * boundary está furado — Camada 1 (`bootstrapClientIframe` com override\n * prefix) falhou e a Camada 4 (dual WebView) deve ser acionada.\n */\nexport interface SandboxVerifyResult {\n flutterBridgeReachable: boolean;\n condorBridgeReachable: boolean;\n ok: boolean; // !flutterBridgeReachable && !condorBridgeReachable\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYO,IAAM,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,oBAAoB;AAAA,EACpB,UAAU;AAAA,EACV,UAAU;AAAA,EACV,cAAc;AAAA,EACd,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,WAAW;AAAA,EACX,KAAK;AAAA,EACL,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,gBAAgB;AAClB;;;ACZA,IAAM,WAAW;AACjB,IAAM,sBAAsB;AA+D5B,IAAM,WAA4B;AAAA,EAChC,UAAU;AAAA,EACV,aAAa;AAAA,EACb,OAAO;AAAA,EACP,gBAAgB;AAAA,EAChB,yBAAyB;AAAA,EACzB,oBAAoB;AACtB;AAEA,SAAS,eAAe,MAAyC;AAC/D,SAAO;AAAA,IACL,UAAU,MAAM,YAAY,SAAS;AAAA,IACrC,aAAa,MAAM,eAAe,SAAS;AAAA,IAC3C,OAAO,MAAM,SAAS,SAAS;AAAA,IAC/B,gBAAgB,MAAM,kBAAkB,SAAS;AAAA,IACjD,yBAAyB,MAAM,2BAA2B,SAAS;AAAA,IACnE,oBAAoB,MAAM,sBAAsB,SAAS;AAAA,EAC3D;AACF;AAEA,SAAS,iBAAiB,IAAiB,MAA6B;AACtE,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,YAA+E;AAAA,IACnF,gBAAgB,EAAE,QAAQ,QAAQ,OAAO,OAAO;AAAA,IAChD,eAAe,EAAE,QAAQ,QAAQ,MAAM,OAAO;AAAA,IAC9C,aAAa,EAAE,KAAK,QAAQ,OAAO,OAAO;AAAA,IAC1C,YAAY,EAAE,KAAK,QAAQ,MAAM,OAAO;AAAA,EAC1C;AACA,SAAO,OAAO,GAAG,OAAO;AAAA,IACtB,UAAU;AAAA,IACV,OAAO;AAAA,IACP,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,OAAO,SAAS,YAAY;AAAA,IAC5B,YAAY,SAAS,cAAc;AAAA,IACnC,QAAQ,SAAS,mBAAmB;AAAA,IACpC,cAAc;AAAA,IACd,WAAW;AAAA,IACX,UAAU;AAAA,IACV,SAAS;AAAA,IACT,eAAe;AAAA,IACf,GAAG,UAAU,KAAK,QAAQ;AAAA,EAC5B,CAAC;AACH;AAEA,SAAS,YAAY,OAAoB,SAAsB,QAAkC;AAC/F,QAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,SAAO,YAAY;AACnB,SAAO,OAAO,OAAO,OAAO;AAAA,IAC1B,SAAS;AAAA,IACT,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV,CAAC;AACD,SAAO,OAAO,MAAM,OAAO;AAAA,IACzB,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,SAAS;AAAA,IACT,cAAc;AAAA,IACd,UAAU;AAAA,IACV,eAAe;AAAA,EACjB,CAAC;AACD,QAAM,cAAc;AACpB,SAAO,OAAO,QAAQ,OAAO;AAAA,IAC3B,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,SAAS;AAAA,EACX,CAAC;AACD,SAAO,OAAO,OAAO,OAAO;AAAA,IAC1B,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,EACd,CAAC;AACD,SAAO,OAAO,OAAO,SAAS,eAAe,eAAe,GAAG,SAAS,MAAM;AAC9E,SAAO;AACT;AAEA,SAAS,mBAAkE;AACzE,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY;AACtB,SAAO,OAAO,UAAU,OAAO;AAAA,IAC7B,SAAS;AAAA,IACT,cAAc;AAAA,IACd,WAAW;AAAA,IACX,WAAW;AAAA,EACb,CAAC;AACD,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,cAAc;AACpB,SAAO,OAAO,MAAM,OAAO,EAAE,SAAS,OAAO,cAAc,OAAO,UAAU,OAAO,CAAC;AACpF,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,YAAY;AACjB,YAAU,OAAO,OAAO,IAAI;AAC5B,SAAO,EAAE,WAAW,KAAK;AAC3B;AAEA,SAAS,oBACP,YACA,QACA,SACa;AACb,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY;AACtB,SAAO,OAAO,UAAU,OAAO;AAAA,IAC7B,SAAS;AAAA,IACT,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,EACP,CAAC;AACD,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,cAAc;AACpB,SAAO,OAAO,MAAM,OAAO,EAAE,SAAS,OAAO,UAAU,OAAO,CAAC;AAE/D,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,YAAY;AACnB,SAAO,OAAO,OAAO,OAAO;AAAA,IAC1B,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ,CAAC;AACD,aAAW,OAAO,YAAY;AAC5B,UAAM,MAAM,SAAS,cAAc,QAAQ;AAC3C,QAAI,QAAQ;AACZ,QAAI,cAAc;AAClB,WAAO,YAAY,GAAG;AAAA,EACxB;AACA,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,QAAQ;AACf,SAAO,cAAc;AACrB,SAAO,YAAY,MAAM;AAEzB,QAAM,WAAW,SAAS,cAAc,UAAU;AAClD,WAAS,YAAY;AACrB,WAAS,cAAc;AACvB,SAAO,OAAO,SAAS,OAAO;AAAA,IAC5B,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,WAAW;AAAA,IACX,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,UAAU,SAAS,cAAc,KAAK;AAC5C,SAAO,OAAO,QAAQ,OAAO,EAAE,SAAS,QAAQ,KAAK,MAAM,CAAC;AAE5D,QAAM,UAAU,SAAS,cAAc,QAAQ;AAC/C,UAAQ,OAAO;AACf,UAAQ,cAAc;AACtB,UAAQ,YAAY;AACpB,SAAO,OAAO,QAAQ,OAAO;AAAA,IAC3B,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,MAAM;AAAA,EACR,CAAC;AACD,UAAQ,iBAAiB,SAAS,MAAM;AACtC,WAAO,OAAO,OAAO,SAAS,KAAK;AAAA,EACrC,CAAC;AAED,QAAM,WAAW,SAAS,cAAc,QAAQ;AAChD,WAAS,OAAO;AAChB,WAAS,cAAc;AACvB,WAAS,YAAY;AACrB,SAAO,OAAO,SAAS,OAAO;AAAA,IAC5B,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,UAAU;AAAA,EACZ,CAAC;AACD,WAAS,iBAAiB,SAAS,OAAO;AAE1C,UAAQ,OAAO,SAAS,QAAQ;AAChC,YAAU,OAAO,OAAO,QAAQ,UAAU,OAAO;AACjD,SAAO;AACT;AAEA,SAAS,wBACP,gBACA,UACa;AACb,QAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,YAAU,YAAY;AACtB,SAAO,OAAO,UAAU,OAAO;AAAA,IAC7B,SAAS;AAAA,IACT,cAAc;AAAA,IACd,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,EACP,CAAC;AAED,QAAM,QAAQ,SAAS,cAAc,KAAK;AAC1C,QAAM,cAAc;AACpB,SAAO,OAAO,MAAM,OAAO,EAAE,SAAS,OAAO,UAAU,OAAO,CAAC;AAE/D,QAAM,MAAM,SAAS,cAAc,OAAO;AAC1C,SAAO,OAAO,IAAI,OAAO;AAAA,IACvB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,WAAW,SAAS,cAAc,OAAO;AAC/C,WAAS,OAAO;AAChB,WAAS,YAAY;AACrB,WAAS,UAAU;AACnB,SAAO,OAAO,SAAS,OAAO;AAAA,IAC5B,QAAQ;AAAA,EACV,CAAC;AAED,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,cAAc;AAEpB,WAAS,iBAAiB,UAAU,MAAM;AACxC,aAAS,SAAS,OAAO;AAAA,EAC3B,CAAC;AAED,MAAI,OAAO,UAAU,KAAK;AAC1B,YAAU,OAAO,OAAO,GAAG;AAC3B,SAAO;AACT;AAEA,SAAS,eAAe,MAAmB,SAAsB,KAAmB;AAClF,OAAK,YAAY;AACjB,QAAM,MAAM,QAAQ,YAAY;AAChC,QAAM,OAAO,IAAI,MAAM,CAAC,GAAG,EAAE,QAAQ;AACrC,aAAW,OAAO,MAAM;AACtB,UAAM,MAAM,SAAS,cAAc,KAAK;AACxC,WAAO,OAAO,IAAI,OAAO;AAAA,MACvB,SAAS;AAAA,MACT,cAAc;AAAA,MACd,WAAW;AAAA,IACb,CAAC;AACD,UAAM,KAAK,IAAI,KAAK,IAAI,SAAS,EAAE,mBAAmB;AACtD,UAAM,UAAU,IAAI;AACpB,UAAM,OAAO,SAAS,QAAQ;AAC9B,QAAI,cAAc,GAAG,EAAE,aAAQ,IAAI,KAAK,KAAK,UAAU,IAAI,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AAChF,SAAK,YAAY,GAAG;AAAA,EACtB;AACF;AAOA,IAAI,eAAsC;AAEnC,SAAS,gBAAgB,QAAuC;AACrE,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,IAAI,MAAM,wEAAwE;AAAA,EAC1F;AAGA,MAAI,cAAc;AAChB,iBAAa,UAAU;AACvB,mBAAe;AAAA,EACjB;AAEA,QAAM,OAAO,eAAe,OAAO,OAAO;AAC1C,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,OAAK,KAAK;AACV,mBAAiB,MAAM,IAAI;AAE3B,QAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,QAAM,UAAU,SAAS,cAAc,MAAM;AAC7C,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,OAAO;AACd,SAAO,cAAc,KAAK,iBAAiB,MAAM;AACjD,QAAM,SAAS,YAAY,OAAO,SAAS,MAAM;AAEjD,QAAM,OAAO,SAAS,cAAc,KAAK;AACzC,SAAO,OAAO,KAAK,OAAO;AAAA,IACxB,SAAS,KAAK,iBAAiB,SAAS;AAAA,IACxC,eAAe;AAAA,IACf,UAAU;AAAA,EACZ,CAAC;AAED,QAAM,EAAE,WAAW,eAAe,MAAM,SAAS,IAAI,iBAAiB;AAGtE,QAAM,SAAS,OAAO,SAAS;AAC/B,MAAI,qBAAqB,KAAK;AAC9B,MAAI,0BAA+C;AAEnD,MAAI,qBAAyC;AAC7C,MAAI,QAAQ;AACV,yBAAqB,wBAAwB,oBAAoB,CAAC,SAAS;AACzE,2BAAqB;AAAA,IACvB,CAAC;AACD,8BAA0B,OAAO,QAAQ,cAAc,CAAC,QAAQ;AAC9D,UAAI,CAAC,mBAAoB;AACzB,UAAI;AACJ,UAAI;AACF,mBAAW,OAAO,GAAG;AAAA,MACvB,SAAS,KAAK;AACZ,gBAAQ,MAAM,gEAAgE,GAAG;AACjF;AAAA,MACF;AACA,UAAI,CAAC,SAAU;AACf,iBAAW,MAAM;AACf,YAAI;AACF,iBAAO,QAAQ,YAAY,SAAS,KAAK,SAAS,OAAO;AAAA,QAC3D,SAAS,KAAK;AACZ,kBAAQ;AAAA,YACN;AAAA,YACA;AAAA,UACF;AAAA,QACF;AAAA,MACF,GAAG,KAAK,kBAAkB;AAAA,IAC5B,CAAC;AAAA,EACH;AAEA,QAAM,UAAU;AAAA,IACd,OAAO,OAAO,WAAW;AAAA,IACzB,CAAC,KAAK,gBAAgB;AACpB,UAAI,UAAmB,CAAC;AACxB,YAAM,UAAU,YAAY,KAAK;AACjC,UAAI,QAAQ,SAAS,GAAG;AACtB,YAAI;AACF,oBAAU,KAAK,MAAM,OAAO;AAAA,QAC9B,SAAS,KAAK;AACZ,kBAAQ,MAAM,yDAAyD,GAAG;AAC1E;AAAA,QACF;AAAA,MACF;AACA,aAAO,QAAQ,YAAY,KAAK,OAAO;AAAA,IACzC;AAAA,IACA,MAAM;AACJ,aAAO,QAAQ,cAAc;AAC7B,qBAAe,UAAU,OAAO,SAAS,KAAK,WAAW;AACzD,cAAQ,cAAc;AAAA,IACxB;AAAA,EACF;AAEA,MAAI,oBAAoB;AACtB,SAAK,OAAO,eAAe,oBAAoB,OAAO;AAAA,EACxD,OAAO;AACL,SAAK,OAAO,eAAe,OAAO;AAAA,EACpC;AACA,OAAK,OAAO,QAAQ,IAAI;AAExB,SAAO,iBAAiB,SAAS,CAAC,MAAM;AACtC,MAAE,gBAAgB;AAClB,UAAM,YAAY,KAAK,MAAM,YAAY;AACzC,SAAK,MAAM,UAAU,YAAY,SAAS;AAC1C,WAAO,cAAc,YAAY,WAAM;AAAA,EACzC,CAAC;AAED,WAAS,KAAK,YAAY,IAAI;AAE9B,QAAM,UAAU,MAAM;AACpB,UAAM,QAAQ,OAAO,QAAQ,YAAY,EAAE;AAC3C,YAAQ,cAAc,GAAG,KAAK;AAC9B,mBAAe,UAAU,OAAO,SAAS,KAAK,WAAW;AAAA,EAC3D;AACA,UAAQ;AAER,QAAM,WAAW,YAAY,SAAS,mBAAmB;AAEzD,QAAM,QAAwB;AAAA,IAC5B,YAAY;AACV,oBAAc,QAAQ;AACtB,gCAA0B;AAC1B,gCAA0B;AAC1B,WAAK,OAAO;AACZ,UAAI,iBAAiB,MAAO,gBAAe;AAAA,IAC7C;AAAA,IACA;AAAA,EACF;AACA,iBAAe;AACf,SAAO;AACT;;;AChdA,IAAM,aAAa;AA0BZ,SAAS,oBAAiC;AAC/C,QAAM,WAA0B,CAAC;AACjC,QAAM,gBAAgB,oBAAI,IAAgC;AAE1D,SAAO;AAAA,IACL,QAAQ;AAAA,IAER,YAAY,aAAa,SAAS;AAChC,YAAM,MAAmB,EAAE,aAAa,SAAS,WAAW,KAAK,IAAI,EAAE;AACvE,eAAS,KAAK,GAAG;AACjB,UAAI,SAAS,SAAS,YAAY;AAChC,iBAAS,MAAM;AAAA,MACjB;AACA,cAAQ,MAAM,sCAAiC,aAAa,OAAO;AACnE,iBAAW,YAAY,eAAe;AACpC,YAAI;AACF,mBAAS,GAAG;AAAA,QACd,SAAS,KAAK;AACZ,kBAAQ,MAAM,oDAAoD,GAAG;AAAA,QACvE;AAAA,MACF;AAAA,IACF;AAAA,IAEA,cAAc;AACZ,aAAO;AAAA,IACT;AAAA,IAEA,gBAAgB;AACd,eAAS,SAAS;AAAA,IACpB;AAAA,IAEA,YAAY,KAAK,SAAS;AACxB,YAAM,SAAS,OAAO,WAAW,cAAc,OAAO,eAAe;AACrE,UAAI,CAAC,UAAU,OAAO,OAAO,SAAS,YAAY;AAChD,cAAM,IAAI;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,aAAO,KAAK,KAAK,OAAO;AAAA,IAC1B;AAAA,IAEA,cAAc,UAAU;AACtB,oBAAc,IAAI,QAAQ;AAC1B,aAAO,MAAM;AACX,sBAAc,OAAO,QAAQ;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AACF;;;AC7DA,IAAI,gBAAsC;AAE1C,SAAS,kBAAkB,MAAwC;AACjE,SAAO;AAAA,IACL,QAAQ;AAAA,IACR,YAAY,aAAa,SAAS;AAChC,WAAK,KAAK,YAAY,aAAa,OAAO;AAAA,IAC5C;AAAA,EACF;AACF;AAEA,SAAS,gBAA+B;AACtC,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO,kBAAkB;AAAA,EAC3B;AACA,QAAM,OAAO,OAAO;AACpB,MAAI,QAAQ,OAAO,KAAK,gBAAgB,YAAY;AAClD,WAAO,kBAAkB,IAAI;AAAA,EAC/B;AACA,SAAO,kBAAkB;AAC3B;AAEO,SAAS,mBAAkC;AAChD,MAAI,CAAC,eAAe;AAClB,oBAAgB,cAAc;AAAA,EAChC;AACA,SAAO;AACT;AAYO,SAAS,WAAW,aAAqB,SAAwB;AACtE,mBAAiB,EAAE,YAAY,aAAa,OAAO;AACrD;;;ACnEA,IAAM,cAAc,oBAAI,IAA2B;AAE5C,SAAS,cACd,KACA,IACY;AACZ,MAAI,MAAM,YAAY,IAAI,GAAG;AAC7B,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAc;AACxB,gBAAY,IAAI,KAAK,GAAG;AAAA,EAC1B;AACA,MAAI,IAAI,EAAc;AACtB,SAAO,MAAM;AACX,UAAM,UAAU,YAAY,IAAI,GAAG;AACnC,QAAI,CAAC,QAAS;AACd,YAAQ,OAAO,EAAc;AAC7B,QAAI,QAAQ,SAAS,GAAG;AACtB,kBAAY,OAAO,GAAG;AAAA,IACxB;AAAA,EACF;AACF;AAEO,SAAS,sBAAsB,KAAa,SAAwB;AACzE,QAAM,MAAM,YAAY,IAAI,GAAG;AAC/B,MAAI,CAAC,IAAK;AACV,aAAW,MAAM,KAAK;AACpB,QAAI;AACF,SAAG,OAAO;AAAA,IACZ,SAAS,KAAK;AAEZ,cAAQ,MAAM,0CAA0C,GAAG,YAAY,GAAG;AAAA,IAC5E;AAAA,EACF;AACF;AAmBO,SAAS,oBAA8B;AAC5C,SAAO,MAAM,KAAK,YAAY,KAAK,CAAC;AACtC;;;AClDA,IAAM,oBAAoB;AAC1B,IAAM,iBAAiB;AACvB,IAAM,mBAAmB;AAiBzB,IAAI,mBAA4C;AAChD,IAAI,qBAAqB;AAEzB,SAAS,KAAK,KAAa,SAAwB;AACjD,MAAI,QAAQ,qBAAqB,kBAAkB;AACjD,UAAM,MAAO,WAAW,CAAC;AACzB,UAAM,WAAW;AACjB,uBAAmB;AACnB,aAAS,QAAQ;AAAA,MACf,eAAe,IAAI,iBAAiB;AAAA,MACpC,UAAU,IAAI,YAAY;AAAA,IAC5B,CAAC;AACD;AAAA,EACF;AACA,wBAAsB,KAAK,OAAO;AACpC;AAEA,SAAS,wBAA8B;AACrC,MAAI,mBAAoB;AACxB,MAAI,OAAO,WAAW,YAAa;AACnC,uBAAqB;AAErB,MAAI,OAAO,gBAAgB,OAAO,OAAO,aAAa,SAAS,YAAY;AAIzE,WAAO,aAAa,OAAO;AAAA,EAC7B,OAAO;AACL,WAAO,eAAe,EAAE,KAAK;AAAA,EAC/B;AACF;AAEA,sBAAsB;AAEtB,SAAS,qBAAkC;AACzC,QAAM,UAAU,iBAAiB;AACjC,MAAI,CAAC,QAAQ,QAAQ;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AA6EO,IAAM,SAAuB;AAAA,EAClC,IAAI,SAAkB;AACpB,WAAO,iBAAiB,EAAE;AAAA,EAC5B;AAAA,EAEA,UAAU,MAA+C;AACvD,0BAAsB;AACtB,QAAI,kBAAkB;AACpB,aAAO,QAAQ;AAAA,QACb,IAAI;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,IAAI,QAAsB,CAAC,SAAS,WAAW;AACpD,yBAAmB,EAAE,SAAS,OAAO;AACrC,UAAI;AACF,mBAAW,gBAAgB;AAAA,UACzB,MAAM;AAAA,UACN,SAAS;AAAA,UACT,SAAS,EAAE,eAAe,KAAK,cAAc;AAAA,QAC/C,CAAC;AAAA,MACH,SAAS,KAAK;AACZ,2BAAmB;AACnB,eAAO,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,MAC5D;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,GAAwB,KAAQ,IAAiD;AAC/E,0BAAsB;AACtB,WAAO,cAAc,KAAK,EAAE;AAAA,EAC9B;AAAA,EAEA,KAAK,MAAc,SAAyC;AAC1D,eAAW,gBAAgB;AAAA,MACzB;AAAA,MACA,SAAS;AAAA,MACT,SAAS,WAAW,CAAC;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEA,eAAqB;AACnB,SAAK,KAAK,cAAc;AAAA,EAC1B;AAAA,EAEA,SAAS,KAAa,SAAyB;AAC7C,uBAAmB,EAAE,YAAY,KAAK,WAAW,CAAC,CAAC;AAAA,EACrD;AAAA,EAEA,eAA0B;AACxB,UAAM,UAAU,mBAAmB;AACnC,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,UAAU,QAAQ,YAAY;AAAA,MAC9B,gBAAgB,kBAAkB;AAAA,IACpC;AAAA,EACF;AAAA,EAEA,gBAAgB,SAAiC;AAC/C,UAAM,UAAU,mBAAmB;AACnC,oBAAwB,EAAE,SAAS,QAAQ,CAAC;AAAA,EAC9C;AACF;;;AC1LA,IAAMA,oBAAmB;AACzB,IAAM,uBAAuB;AAa7B,IAAMC,eAAc,oBAAI,IAA0B;AAClD,IAAIC,oBAA4C;AAChD,IAAI,2BAA2B;AAE/B,SAAS,YAAY,KAAiC;AACpD,MAAI,OAAO,WAAW,eAAe,OAAO,WAAW,QAAQ;AAE7D,YAAQ,KAAK,qDAAqD,IAAI,IAAI;AAC1E;AAAA,EACF;AACA,SAAO,OAAO,YAAY,KAAK,GAAG;AACpC;AAEA,SAAS,cAAc,KAAa,SAAwB;AAC1D,QAAM,MAAMD,aAAY,IAAI,GAAG;AAC/B,MAAI,CAAC,OAAO,IAAI,WAAW,EAAG;AAC9B,aAAW,OAAO,KAAK;AACrB,QAAI;AACF,UAAI,GAAG,OAAO;AAAA,IAChB,SAAS,KAAK;AACZ,cAAQ,MAAM,oCAAoC,GAAG;AAAA,IACvD;AAAA,EACF;AACF;AAEA,SAAS,UAAU,OAA2B;AAC5C,MAAI,MAAM,WAAW,OAAO,OAAQ;AAGpC,QAAM,OAAO,MAAM;AACnB,MAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,OAAO,KAAK,SAAS,SAAU;AAExE,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK,SAAS;AACZ,YAAM,MAAM;AACZ,UAAI,OAAO,IAAI,QAAQ,SAAU,eAAc,IAAI,KAAK,IAAI,OAAO;AACnE;AAAA,IACF;AAAA,IACA,KAAK,iBAAiB;AACpB,YAAM,MAAM;AACZ,UAAIC,mBAAkB;AACpB,qBAAaA,kBAAiB,KAAK;AACnC,cAAM,EAAE,QAAQ,IAAIA;AACpB,QAAAA,oBAAmB;AACnB,gBAAQ;AAAA,UACN,cAAc,IAAI,KAAK,gBAAgB;AAAA,UACvC,UAAU,IAAI,KAAK,YAAY;AAAA,QACjC,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAAA,IACA,KAAK;AAGH,cAAQ,KAAK,2CAA2C,IAAI;AAC5D;AAAA,IACF,KAAK,kBAAkB;AAGrB,YAAM,IAAI;AACV,kBAAY;AAAA,QACV,MAAM;AAAA,QACN,wBAAwB,OAAO,EAAE,yBAAyB;AAAA,QAC1D,uBAAuB,OAAO,EAAE,iBAAiB;AAAA,MACnD,CAAC;AACD;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,wBAA8B;AACrC,MAAI,yBAA0B;AAC9B,MAAI,OAAO,WAAW,YAAa;AACnC,6BAA2B;AAC3B,SAAO,iBAAiB,WAAW,SAAS;AAC9C;AAEA,sBAAsB;AAsEf,IAAM,eAAmC;AAAA,EAC9C,iBAAiBF;AAAA,EAEjB,GAAwB,KAAQ,IAAiD;AAC/E,0BAAsB;AACtB,UAAM,MAAMC,aAAY,IAAI,GAAG,KAAK,CAAC;AACrC,UAAM,MAAkB,EAAE,GAAqC;AAC/D,QAAI,KAAK,GAAG;AACZ,IAAAA,aAAY,IAAI,KAAK,GAAG;AAGxB,gBAAY,EAAE,MAAM,aAAa,IAAI,CAAC;AACtC,WAAO,MAAM;AACX,YAAM,aAAaA,aAAY,IAAI,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,MAAM,MAAM,GAAG;AACtE,MAAAA,aAAY,IAAI,KAAK,SAAS;AAC9B,UAAI,UAAU,WAAW,EAAG,aAAY,EAAE,MAAM,eAAe,IAAI,CAAC;AAAA,IACtE;AAAA,EACF;AAAA,EAEA,KAAK,SAAiB,OAAgC,CAAC,GAAS;AAC9D,gBAAY,EAAE,MAAM,QAAQ,SAAS,KAAK,CAAC;AAAA,EAC7C;AAAA,EAEA,eAAqB;AACnB,SAAK,KAAK,cAAc;AAAA,EAC1B;AAAA,EAEA,kBAAkB,YAA0B;AAC1C,SAAK,KAAK,qBAAqB,EAAE,WAAW,CAAC;AAAA,EAC/C;AAAA,EAEA,UAAU,MAAoE;AAC5E,0BAAsB;AACtB,QAAIC,mBAAkB;AACpB,aAAO,QAAQ;AAAA,QACb,IAAI;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,WAAO,IAAI,QAAkC,CAAC,SAAS,WAAW;AAChE,YAAM,QAAQ,WAAW,MAAM;AAC7B,QAAAA,oBAAmB;AACnB,eAAO,IAAI,MAAM,yCAAyC,oBAAoB,IAAI,CAAC;AAAA,MACrF,GAAG,oBAAoB;AACvB,MAAAA,oBAAmB,EAAE,SAAS,QAAQ,MAAM;AAC5C,kBAAY;AAAA,QACV,MAAM;AAAA,QACN,SAAS,EAAE,eAAe,KAAK,cAAc;AAAA,MAC/C,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,iBAAiB,WAA0B;AACzC,SAAK,KAAK,mBAAmB,EAAE,UAAU,CAAC;AAAA,EAC5C;AAAA,EAEA,OAAO,IAA4B;AACjC,WAAO,KAAK,GAAG,YAAY,gBAAgB,MAAM,GAAG,CAAC;AAAA,EACvD;AACF;;;AC/NA,IAAAC,KAAmB;;;ACRnB,QAAmB;AAGnB,IAAM,kBAAoB,WAAS,OAAO,OAAO,WAAW,CAA0B;AAI/E,IAAM,mBAAqB,SAAO;AAAA,EACvC,MAAQ,UAAQ,WAAW;AAAA,EAC3B,KAAK;AACP,CAAC;AAEM,IAAM,qBAAuB,SAAO;AAAA,EACzC,MAAQ,UAAQ,aAAa;AAAA,EAC7B,KAAK;AACP,CAAC;AAEM,IAAM,cAAgB,SAAO;AAAA,EAClC,MAAQ,UAAQ,MAAM;AAAA,EACtB,SAAW,SAAO;AAAA,EAClB,MAAQ,WAAW,SAAS,SAAO,GAAK,UAAQ,CAAC,GAAG,CAAC,CAAC;AACxD,CAAC;AAEM,IAAM,mBAAqB,SAAO;AAAA,EACvC,MAAQ,UAAQ,WAAW;AAAA,EAC3B,SAAW,SAAO,EAAE,eAAiB,SAAO,EAAE,CAAC;AACjD,CAAC;AAEM,IAAM,iBAAmB,SAAO;AAAA,EACrC,MAAQ,UAAQ,SAAS;AAAA,EACzB,SAAW,SAAO;AAAA,IAChB,SAAW,SAAO;AAAA,IAClB,OAAS,WAAW,SAAO,CAAC;AAAA,IAC5B,QAAU,WAAW,SAAO,CAAC;AAAA,IAC7B,QAAU,WAAW,SAAO,CAAC;AAAA,IAC7B,OAAS,WAAW,SAAO,CAAC;AAAA,EAC9B,CAAC;AACH,CAAC;AAEM,IAAM,+BAAiC,SAAO;AAAA,EACnD,MAAQ,UAAQ,yBAAyB;AAAA,EACzC,wBAA0B,UAAQ;AAAA,EAClC,uBAAyB,UAAQ;AACnC,CAAC;AAEM,IAAM,uBAAyB,UAAQ,QAAQ;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAMM,IAAM,eAAiB,SAAO;AAAA,EACnC,MAAQ,UAAQ,OAAO;AAAA,EACvB,KAAK;AAAA,EACL,SAAW,UAAQ;AACrB,CAAC;AAEM,IAAM,sBAAwB,SAAO;AAAA,EAC1C,MAAQ,UAAQ,eAAe;AAAA,EAC/B,KAAO,SAAO;AAAA,IACZ,cAAgB,SAAO;AAAA,IACvB,UAAY,UAAQ;AAAA,EACtB,CAAC;AACH,CAAC;AAEM,IAAM,cAAgB,SAAO;AAAA,EAClC,MAAQ,UAAQ,MAAM;AAAA,EACtB,KAAO,WAAW,SAAO,CAAC;AAAA,EAC1B,SAAW,WAAW,SAAO,CAAC;AAAA,EAC9B,QAAU,SAAO;AACnB,CAAC;AAEM,IAAM,uBAAyB,SAAO;AAAA,EAC3C,MAAQ,UAAQ,gBAAgB;AAClC,CAAC;AAEM,IAAM,uBAAyB,UAAQ,QAAQ;AAAA,EACpD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;;;ADrED,IAAM,gBAAgB;AACtB,IAAM,oBAAoB;AAsB1B,IAAM,kBAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwDjB,IAAM,cAAN,MAAkB;AAAA,EAiBvB,YAAY,MAA0B;AAbtC,SAAiB,gBAAgB,oBAAI,IAA2B;AAChE,SAAQ,WAAW;AAGnB;AAAA,SAAQ,kBAAkB,oBAAI,IAO5B;AA2GF,SAAQ,YAAY,OAAO,UAAuC;AAChE,UAAI,KAAK,SAAU;AACnB,UAAI,MAAM,WAAW,KAAK,OAAO,cAAe;AAEhD,YAAM,SAAW,aAAU,sBAAsB,MAAM,IAAI;AAC3D,UAAI,CAAC,OAAO,SAAS;AAGnB,YAAI,OAAO,YAAY,aAAa;AAClC,kBAAQ,MAAM,4CAA4C,OAAO,SAAS,CAAC,GAAG,OAAO;AAAA,QACvF;AACA;AAAA,MACF;AAEA,YAAM,MAAM,OAAO;AACnB,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,gBAAM,KAAK,gBAAgB,IAAI,GAAgB;AAC/C;AAAA,QACF,KAAK;AACH,eAAK,kBAAkB,IAAI,GAAgB;AAC3C;AAAA,QACF,KAAK;AACH,gBAAM,KAAK,WAAW,IAAI,SAAS,IAAI,QAAQ,CAAC,CAAC;AACjD;AAAA,QACF,KAAK;AAIH,eAAK,aAAa;AAAA,YAChB,MAAM;AAAA,YACN,KAAK,EAAE,cAAc,eAAe,UAAU,KAAK;AAAA,UACrD,CAAC;AACD;AAAA,QACF,KAAK;AAEH,iBAAO,KAAK,WAAW;AAAA,YACrB,QAAQ;AAAA,YACR,SAAS,IAAI,QAAQ;AAAA,YACrB,OAAO,IAAI,QAAQ;AAAA,YACnB,QAAQ,IAAI,QAAQ;AAAA,YACpB,QAAQ,IAAI,QAAQ;AAAA,YACpB,OAAO,IAAI,QAAQ;AAAA,UACrB,CAAC;AACD;AAAA,QACF,KAAK,2BAA2B;AAI9B,gBAAM,QAAQ,KAAK,gBAAgB,KAAK,EAAE,KAAK,EAAE;AACjD,cAAI,UAAU,QAAW;AACvB,kBAAM,UAAU,KAAK,gBAAgB,IAAI,KAAK;AAC9C,gBAAI,SAAS;AACX,2BAAa,QAAQ,KAAK;AAC1B,mBAAK,gBAAgB,OAAO,KAAK;AACjC,oBAAM,KAAK,CAAC,IAAI,0BAA0B,CAAC,IAAI;AAC/C,sBAAQ,QAAQ;AAAA,gBACd,wBAAwB,IAAI;AAAA,gBAC5B,uBAAuB,IAAI;AAAA,gBAC3B;AAAA,cACF,CAAC;AAAA,YACH;AAAA,UACF;AACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AA1KE,SAAK,SAAS,KAAK;AACnB,SAAK,SAAS,KAAK;AACnB,SAAK,YAAY,KAAK;AACtB,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,iBAAiB,WAAW,KAAK,SAAS;AAAA,IACnD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,sBAAsB,aAAa,eAA8B;AACrE,UAAM,MAAM,MAAM,MAAM,UAAU;AAClC,QAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,kCAAkC,UAAU,KAAK,IAAI,MAAM,GAAG;AAC3F,UAAM,aAAa,MAAM,IAAI,KAAK;AAClC,UAAM,KAAK,8BAA8B,UAAU;AAAA,EACrD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,8BAA8B,YAAmC;AACrE,SAAK,OAAO,SAAS,kBAAkB;AACvC,UAAM,KAAK,kBAAkB;AAAA,EAC/B;AAAA,EAEQ,oBAAmC;AACzC,WAAO,IAAI,QAAc,CAAC,YAAY;AACpC,YAAM,SAAS,MAAY;AACzB,aAAK,OAAO,oBAAoB,QAAQ,MAAM;AAC9C,gBAAQ;AAAA,MACV;AACA,WAAK,OAAO,iBAAiB,QAAQ,MAAM;AAAA,IAC7C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,gBAA8C;AAC5C,QAAI,CAAC,KAAK,OAAO,eAAe;AAC9B,aAAO,QAAQ,OAAO,IAAI,MAAM,uCAAuC,CAAC;AAAA,IAC1E;AACA,WAAO,IAAI,QAA6B,CAAC,SAAS,WAAW;AAC3D,YAAM,QAAQ,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC;AAChD,YAAM,QAAQ,WAAW,MAAM;AAC7B,aAAK,gBAAgB,OAAO,KAAK;AACjC,eAAO,IAAI,MAAM,+CAA4C,iBAAiB,IAAI,CAAC;AAAA,MACrF,GAAG,iBAAiB;AACpB,WAAK,gBAAgB,IAAI,OAAO,EAAE,SAAS,QAAQ,MAAM,CAAC;AAC1D,WAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAe;AACb,QAAI,KAAK,SAAU;AACnB,SAAK,WAAW;AAChB,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,oBAAoB,WAAW,KAAK,SAAS;AAAA,IACtD;AACA,eAAW,SAAS,KAAK,cAAc,OAAO,EAAG,OAAM;AACvD,SAAK,cAAc,MAAM;AACzB,eAAW,WAAW,KAAK,gBAAgB,OAAO,GAAG;AACnD,mBAAa,QAAQ,KAAK;AAC1B,cAAQ,OAAO,IAAI,MAAM,uBAAuB,CAAC;AAAA,IACnD;AACA,SAAK,gBAAgB,MAAM;AAAA,EAC7B;AAAA,EAsEA,MAAc,gBAAgB,KAA+B;AAC3D,QAAI,CAAC,KAAK,OAAO,aAAa,SAAS,GAAG,GAAG;AAC3C,WAAK,aAAa,EAAE,MAAM,QAAQ,KAAK,QAAQ,gBAAgB,CAAC;AAChE;AAAA,IACF;AACA,QAAI,KAAK,cAAc,IAAI,GAAG,EAAG;AACjC,UAAM,QAAQ,OAAO,GAAG,KAAK,CAAC,YAAY;AACxC,UAAI,KAAK,SAAU;AACnB,WAAK,aAAa,EAAE,MAAM,SAAS,KAAK,QAAQ,CAAC;AAAA,IACnD,CAAC;AACD,SAAK,cAAc,IAAI,KAAK,KAAK;AAAA,EACnC;AAAA,EAEQ,kBAAkB,KAAsB;AAC9C,UAAM,QAAQ,KAAK,cAAc,IAAI,GAAG;AACxC,QAAI,CAAC,MAAO;AACZ,UAAM;AACN,SAAK,cAAc,OAAO,GAAG;AAAA,EAC/B;AAAA,EAEA,MAAc,WAAW,SAAiB,MAA8C;AACtF,QAAI,KAAK,OAAO,eAAe,SAAS,OAAO,GAAG;AAChD,WAAK,aAAa,EAAE,MAAM,QAAQ,SAAS,QAAQ,SAAS,CAAC;AAC7D;AAAA,IACF;AACA,QAAI,KAAK,OAAO,qBAAqB,SAAS,OAAO,GAAG;AACtD,UAAI,CAAC,KAAK,WAAW;AAEnB,aAAK,aAAa,EAAE,MAAM,QAAQ,SAAS,QAAQ,0BAA0B,CAAC;AAC9E;AAAA,MACF;AACA,UAAI,WAAW;AACf,UAAI;AACF,mBAAW,MAAM,KAAK,UAAU,EAAE,SAAS,KAAK,CAAC;AAAA,MACnD,SAAS,KAAK;AACZ,aAAK,aAAa;AAAA,UAChB,MAAM;AAAA,UACN;AAAA,UACA,QAAQ,uBAAuB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QACjF,CAAC;AACD;AAAA,MACF;AACA,UAAI,CAAC,UAAU;AACb,aAAK,aAAa,EAAE,MAAM,QAAQ,SAAS,QAAQ,gBAAgB,CAAC;AACpE;AAAA,MACF;AAAA,IACF,WAAW,CAAC,KAAK,OAAO,gBAAgB,SAAS,OAAO,GAAG;AAEzD,WAAK,aAAa,EAAE,MAAM,QAAQ,SAAS,QAAQ,cAAc,CAAC;AAClE;AAAA,IACF;AACA,WAAO,KAAK,SAAS,IAAI;AAAA,EAC3B;AAAA,EAEQ,aAAa,KAAiC;AACpD,QAAI,KAAK,SAAU;AACnB,SAAK,OAAO,eAAe,YAAY,KAAK,GAAG;AAAA,EACjD;AACF;","names":["PROTOCOL_VERSION","subscribers","pendingHandshake","v"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/keys.ts","../src/internal/dev-panel.ts","../src/internal/mock.ts","../src/internal/native.ts","../src/internal/subscribers.ts","../src/bridge.ts","../src/bridge-client.ts","../src/bridge-shell.ts","../src/shell-messages.ts"],"sourcesContent":["/**\n * `condor-mobile-bridge` — SDK do bridge JS↔Native do app white-label Condor.\n *\n * Public API: `Bridge` singleton + tipos.\n *\n * Import side effect: instala `window.CondorBridge.emit` no primeiro\n * carregamento do módulo, para que o nativo possa chamar via\n * `evaluateJavascript(\"window.CondorBridge.emit('key', payload)\")`. Para\n * que esta instalação aconteça **antes** do nativo tentar emitir, o\n * import deste módulo deve ser um dos primeiros statements do bundle do\n * cliente.\n *\n * **v0.2.0:** mock mode — quando `window.flutter_inappwebview` não é\n * detectado (browser comum, Storybook, Vite dev server), o adapter ativa\n * automaticamente. `Bridge.isMock`/`mockEmit`/`getMockState`/`installDevPanel`\n * expostos para suporte ao desenvolvimento.\n *\n * **v0.4.0:** tipos estritos por chave em `PayloadOf<K>`. `Bridge.on<K>`\n * infere o payload tipado por chave catalogada. Chaves não-catalogadas\n * caem no fallback permissivo (`Record<string, unknown>`).\n *\n * **v0.5.0:** introduz `BridgeClient` (API para código de cliente externo\n * dentro de `<iframe sandbox=\"allow-scripts\">`) e `BridgeShell` (API\n * para o shell Condor-controlado que envolve o iframe). Comunicação\n * shell↔client via `window.postMessage` com schema Valibot + policy\n * default-deny. Defesa em 4 camadas contra propagação do\n * `window.flutter_inappwebview` ao iframe (vide\n * `condor_wiki/cross-cutting/sleep-white-label-content-transport-and-security.md`\n * §Risco 1).\n *\n * **v0.14.0:** chave observável `language` — o nativo informa o locale cru do\n * aparelho **e** o idioma escolhido nos ajustes do app (ou `null` quando não\n * houve escolha), para o cliente aplicar a própria regra de precedência e poder\n * renderizar idiomas que a camada nativa não suporta. Vide `LanguagePayload`.\n */\n\nexport type { CondorBridge, MockState } from './bridge';\nexport { Bridge } from './bridge';\nexport type { BridgeClientHandshakeAck, CondorBridgeClient } from './bridge-client';\nexport { BridgeClient } from './bridge-client';\nexport type { BridgeShellOptions } from './bridge-shell';\nexport { BridgeShell } from './bridge-shell';\nexport type { AutoRespondResponse, DevPanelOptions } from './internal/dev-panel';\nexport type { MockMessage } from './internal/mock';\nexport type { BridgeKey } from './keys';\nexport { BRIDGE_KEYS } from './keys';\nexport type {\n BridgeIncomingMessage,\n BridgeOutgoingEvent,\n HandshakeAck,\n HandshakeOptions,\n} from './messages';\nexport type {\n DeleteMemoryAckPayload,\n DeviceConnectionPayload,\n DeviceLightSensorsPayload,\n DeviceNotificationPayload,\n KeyPayloads,\n LanguagePayload,\n PayloadOf,\n PingPongPayload,\n RealTimeAckPayload,\n RequestPermissionPayload,\n SensorLogPayload,\n SensorLogQueryPayload,\n SyncTimeAckPayload,\n SystemStatusPayload,\n UserDevicePayload,\n} from './payloads';\nexport type {\n ClientToShellMessage,\n ConfirmationRequest,\n SandboxVerifyResult,\n ShellPolicy,\n ShellToClientMessage,\n} from './shell-messages';\n","/**\n * Catálogo das chaves do bridge JS↔Native.\n *\n * Espelha o lado JS-facing de `ForegroundComCoreKeys` do `condor_core`\n * (monorepo `mobile_applications`). As strings aqui são o que o JS recebe\n * via `window.CondorBridge.emit(key, payload)`.\n *\n * Camada 1 (v0.1.0): catálogo declarativo. Apenas `pingPong` está\n * efetivamente emitida pelo lado Dart no Trio B; as outras 8 são\n * reservadas para o Trio C (`sleep-whitelabel-bridge-keys`) e ficam\n * aqui como contrato forward-looking.\n */\nexport const BRIDGE_KEYS = {\n pingPong: 'pingPong',\n userDevice: 'userDevice',\n deviceLightSensors: 'deviceLightSensors',\n syncTime: 'syncTime',\n realTime: 'realTime',\n deleteMemory: 'deleteMemory',\n deviceNotification: 'deviceNotification',\n deviceConnection: 'deviceConnection',\n sensorLog: 'sensorLog',\n dfu: 'dfu',\n systemStatus: 'systemStatus',\n /**\n * Native → client: o back do SO foi pressionado e o cliente reportou\n * ter histórico interno (`reportNavigation(true)`). O cliente deve voltar\n * UMA página interna. Emitido só quando o nativo decidiu delegar o back\n * ao cliente. Vide `BridgeClient.onBack`.\n */\n navigationBack: 'navigationBack',\n /**\n * Native → client: the app's language state. Carries the raw device locale\n * AND the language explicitly chosen in the app Settings, so the client can\n * apply its own precedence rule — including rendering a language the native\n * chrome does not support. Emitted on attach, after `handshake-ack`, and on\n * every change in Settings. Vide `LanguagePayload`.\n */\n language: 'language',\n} as const;\n\nexport type BridgeKey = (typeof BRIDGE_KEYS)[keyof typeof BRIDGE_KEYS];\n","/**\n * Dev panel — UI flutuante para inspecionar o bridge em mock mode.\n *\n * **Opt-in:** chamar `Bridge.installDevPanel(opts?)` quando quiser ver\n * o painel. Só funciona em mock mode (throws em modo real). Injeta no\n * `document.body`; idempotente (re-install remove o anterior).\n *\n * Conteúdo:\n * - Header com badge \"MOCK\", contador de sends, botão minimizar/maximizar.\n * - Sent log — últimas N mensagens (timestamp, type, payload truncado).\n * - Trigger emit — dropdown com `BRIDGE_KEYS` + textarea payload + botão.\n *\n * Estilos inline + classes prefixadas `condor-mock-panel-*` para reduzir\n * conflito com CSS do cliente.\n */\n\nimport { BRIDGE_KEYS } from '../keys';\nimport type { MockAdapter, MockMessage } from './mock';\n\nconst PANEL_ID = 'condor-mock-panel-root';\nconst REFRESH_INTERVAL_MS = 500;\n\n/**\n * Resposta auto-emitida em reação a um `Bridge.send`, retornada pela\n * policy `onSendForAutoRespond` do cliente. `null` significa \"não\n * responder a este send\".\n *\n * @since v0.3.0\n */\nexport interface AutoRespondResponse {\n key: string;\n payload: unknown;\n}\n\nexport interface DevPanelOptions {\n position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';\n maxMessages?: number;\n theme?: 'dark' | 'light';\n startCollapsed?: boolean;\n\n /**\n * Policy de auto-respond: invocada a cada `Bridge.send` quando o\n * toggle \"Auto-respond\" está ligado no painel. Retorna\n * `{key, payload}` para disparar `Bridge.mockEmit(key, payload)`\n * após delay, ou `null` para não responder.\n *\n * Sem esta callback, o checkbox \"Auto-respond\" não aparece no painel.\n *\n * @since v0.3.0\n */\n onSendForAutoRespond?: (msg: MockMessage) => AutoRespondResponse | null;\n\n /**\n * Estado inicial do toggle \"Auto-respond\". Default: `false`\n * (manual permanece padrão; cliente decide quando ligar).\n *\n * @since v0.3.0\n */\n startAutoRespondEnabled?: boolean;\n\n /**\n * Delay em ms entre o `send` e o `mockEmit` da auto-resposta.\n * Default: `50` — aproxima latência de IPC real de native bridge.\n *\n * @since v0.3.0\n */\n autoRespondDelayMs?: number;\n}\n\ninterface InstallParams {\n adapter: MockAdapter;\n options?: DevPanelOptions;\n}\n\ninterface ResolvedOptions {\n position: NonNullable<DevPanelOptions['position']>;\n maxMessages: number;\n theme: NonNullable<DevPanelOptions['theme']>;\n startCollapsed: boolean;\n startAutoRespondEnabled: boolean;\n autoRespondDelayMs: number;\n}\n\nconst DEFAULTS: ResolvedOptions = {\n position: 'bottom-right',\n maxMessages: 20,\n theme: 'dark',\n startCollapsed: false,\n startAutoRespondEnabled: false,\n autoRespondDelayMs: 50,\n};\n\nfunction resolveOptions(opts?: DevPanelOptions): ResolvedOptions {\n return {\n position: opts?.position ?? DEFAULTS.position,\n maxMessages: opts?.maxMessages ?? DEFAULTS.maxMessages,\n theme: opts?.theme ?? DEFAULTS.theme,\n startCollapsed: opts?.startCollapsed ?? DEFAULTS.startCollapsed,\n startAutoRespondEnabled: opts?.startAutoRespondEnabled ?? DEFAULTS.startAutoRespondEnabled,\n autoRespondDelayMs: opts?.autoRespondDelayMs ?? DEFAULTS.autoRespondDelayMs,\n };\n}\n\nfunction applyPanelStyles(el: HTMLElement, opts: ResolvedOptions): void {\n const isDark = opts.theme === 'dark';\n const positions: Record<ResolvedOptions['position'], Partial<CSSStyleDeclaration>> = {\n 'bottom-right': { bottom: '12px', right: '12px' },\n 'bottom-left': { bottom: '12px', left: '12px' },\n 'top-right': { top: '12px', right: '12px' },\n 'top-left': { top: '12px', left: '12px' },\n };\n Object.assign(el.style, {\n position: 'fixed',\n width: '340px',\n maxHeight: '420px',\n zIndex: '2147483647',\n fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace',\n fontSize: '12px',\n color: isDark ? '#e0e0e0' : '#222',\n background: isDark ? '#1a1a1aee' : '#ffffffee',\n border: isDark ? '1px solid #444' : '1px solid #ccc',\n borderRadius: '6px',\n boxShadow: '0 4px 12px rgba(0,0,0,0.3)',\n overflow: 'hidden',\n display: 'flex',\n flexDirection: 'column',\n ...positions[opts.position],\n });\n}\n\nfunction buildHeader(badge: HTMLElement, counter: HTMLElement, toggle: HTMLElement): HTMLElement {\n const header = document.createElement('div');\n header.className = 'condor-mock-panel-header';\n Object.assign(header.style, {\n padding: '6px 10px',\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n background: '#0a84ff',\n color: '#fff',\n fontWeight: '600',\n cursor: 'pointer',\n });\n Object.assign(badge.style, {\n background: '#fff',\n color: '#0a84ff',\n padding: '1px 6px',\n borderRadius: '3px',\n fontSize: '10px',\n letterSpacing: '0.5px',\n });\n badge.textContent = 'MOCK';\n Object.assign(counter.style, {\n marginLeft: 'auto',\n fontSize: '11px',\n opacity: '0.9',\n });\n Object.assign(toggle.style, {\n background: 'transparent',\n border: '1px solid rgba(255,255,255,0.5)',\n color: '#fff',\n cursor: 'pointer',\n padding: '0 6px',\n borderRadius: '3px',\n fontFamily: 'inherit',\n });\n header.append(badge, document.createTextNode('condor-bridge'), counter, toggle);\n return header;\n}\n\nfunction buildSentSection(): { container: HTMLElement; list: HTMLElement } {\n const container = document.createElement('div');\n container.className = 'condor-mock-panel-sent';\n Object.assign(container.style, {\n padding: '6px 10px',\n borderBottom: '1px solid rgba(255,255,255,0.08)',\n maxHeight: '180px',\n overflowY: 'auto',\n });\n const title = document.createElement('div');\n title.textContent = 'Sent';\n Object.assign(title.style, { opacity: '0.7', marginBottom: '4px', fontSize: '11px' });\n const list = document.createElement('div');\n list.className = 'condor-mock-panel-sent-list';\n container.append(title, list);\n return { container, list };\n}\n\nfunction buildTriggerSection(\n bridgeKeys: readonly string[],\n onEmit: (key: string, payloadJson: string) => void,\n onClear: () => void,\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'condor-mock-panel-trigger';\n Object.assign(container.style, {\n padding: '6px 10px',\n display: 'flex',\n flexDirection: 'column',\n gap: '4px',\n });\n const title = document.createElement('div');\n title.textContent = 'Trigger emit';\n Object.assign(title.style, { opacity: '0.7', fontSize: '11px' });\n\n const select = document.createElement('select');\n select.className = 'condor-mock-panel-key-select';\n Object.assign(select.style, {\n background: 'transparent',\n color: 'inherit',\n border: '1px solid rgba(255,255,255,0.2)',\n padding: '4px',\n borderRadius: '3px',\n fontFamily: 'inherit',\n fontSize: '11px',\n });\n for (const key of bridgeKeys) {\n const opt = document.createElement('option');\n opt.value = key;\n opt.textContent = key;\n select.appendChild(opt);\n }\n const ackOpt = document.createElement('option');\n ackOpt.value = 'handshake-ack';\n ackOpt.textContent = 'handshake-ack';\n select.appendChild(ackOpt);\n\n const textarea = document.createElement('textarea');\n textarea.className = 'condor-mock-panel-payload';\n textarea.placeholder = '{ \"data\": \"pong\" }';\n Object.assign(textarea.style, {\n background: 'transparent',\n color: 'inherit',\n border: '1px solid rgba(255,255,255,0.2)',\n padding: '4px',\n borderRadius: '3px',\n fontFamily: 'inherit',\n fontSize: '11px',\n minHeight: '50px',\n resize: 'vertical',\n });\n\n const buttons = document.createElement('div');\n Object.assign(buttons.style, { display: 'flex', gap: '6px' });\n\n const emitBtn = document.createElement('button');\n emitBtn.type = 'button';\n emitBtn.textContent = 'Emit';\n emitBtn.className = 'condor-mock-panel-emit-btn';\n Object.assign(emitBtn.style, {\n background: '#0a84ff',\n color: '#fff',\n border: 'none',\n padding: '4px 10px',\n borderRadius: '3px',\n cursor: 'pointer',\n fontFamily: 'inherit',\n fontSize: '11px',\n flex: '1',\n });\n emitBtn.addEventListener('click', () => {\n onEmit(select.value, textarea.value);\n });\n\n const clearBtn = document.createElement('button');\n clearBtn.type = 'button';\n clearBtn.textContent = 'Clear sent';\n clearBtn.className = 'condor-mock-panel-clear-btn';\n Object.assign(clearBtn.style, {\n background: 'transparent',\n color: 'inherit',\n border: '1px solid rgba(255,255,255,0.3)',\n padding: '4px 10px',\n borderRadius: '3px',\n cursor: 'pointer',\n fontFamily: 'inherit',\n fontSize: '11px',\n });\n clearBtn.addEventListener('click', onClear);\n\n buttons.append(emitBtn, clearBtn);\n container.append(title, select, textarea, buttons);\n return container;\n}\n\nfunction buildAutoRespondSection(\n initialEnabled: boolean,\n onChange: (next: boolean) => void,\n): HTMLElement {\n const container = document.createElement('div');\n container.className = 'condor-mock-panel-auto-respond';\n Object.assign(container.style, {\n padding: '6px 10px',\n borderBottom: '1px solid rgba(255,255,255,0.08)',\n display: 'flex',\n flexDirection: 'column',\n gap: '2px',\n });\n\n const title = document.createElement('div');\n title.textContent = 'Auto-respond';\n Object.assign(title.style, { opacity: '0.7', fontSize: '11px' });\n\n const row = document.createElement('label');\n Object.assign(row.style, {\n display: 'flex',\n alignItems: 'center',\n gap: '8px',\n cursor: 'pointer',\n fontSize: '12px',\n });\n\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.className = 'condor-mock-panel-auto-respond-toggle';\n checkbox.checked = initialEnabled;\n Object.assign(checkbox.style, {\n cursor: 'pointer',\n });\n\n const label = document.createElement('span');\n label.textContent = 'Enabled — mockEmit em resposta a cada send (policy do cliente)';\n\n checkbox.addEventListener('change', () => {\n onChange(checkbox.checked);\n });\n\n row.append(checkbox, label);\n container.append(title, row);\n return container;\n}\n\nfunction renderSentList(list: HTMLElement, adapter: MockAdapter, max: number): void {\n list.innerHTML = '';\n const all = adapter.getMessages();\n const tail = all.slice(-max).reverse();\n for (const msg of tail) {\n const row = document.createElement('div');\n Object.assign(row.style, {\n padding: '3px 0',\n borderBottom: '1px solid rgba(255,255,255,0.06)',\n wordBreak: 'break-all',\n });\n const ts = new Date(msg.timestamp).toLocaleTimeString();\n const payload = msg.payload as { type?: string };\n const type = payload?.type ?? '?';\n row.textContent = `${ts} → ${type} ${JSON.stringify(msg.payload).slice(0, 80)}`;\n list.appendChild(row);\n }\n}\n\ninterface InstalledPanel {\n uninstall(): void;\n refresh(): void;\n}\n\nlet currentPanel: InstalledPanel | null = null;\n\nexport function installDevPanel(params: InstallParams): InstalledPanel {\n if (typeof document === 'undefined') {\n throw new Error('condor-mobile-bridge dev panel: document is undefined (Node-only env).');\n }\n\n // Idempotente: remove painel anterior se existir.\n if (currentPanel) {\n currentPanel.uninstall();\n currentPanel = null;\n }\n\n const opts = resolveOptions(params.options);\n const root = document.createElement('div');\n root.id = PANEL_ID;\n applyPanelStyles(root, opts);\n\n const badge = document.createElement('span');\n const counter = document.createElement('span');\n const toggle = document.createElement('button');\n toggle.type = 'button';\n toggle.textContent = opts.startCollapsed ? '+' : '−';\n const header = buildHeader(badge, counter, toggle);\n\n const body = document.createElement('div');\n Object.assign(body.style, {\n display: opts.startCollapsed ? 'none' : 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n });\n\n const { container: sentContainer, list: sentList } = buildSentSection();\n\n // Auto-respond section — conditional render: só aparece se policy fornecida.\n const policy = params.options?.onSendForAutoRespond;\n let autoRespondEnabled = opts.startAutoRespondEnabled;\n let unsubscribeSendListener: (() => void) | null = null;\n\n let autoRespondSection: HTMLElement | null = null;\n if (policy) {\n autoRespondSection = buildAutoRespondSection(autoRespondEnabled, (next) => {\n autoRespondEnabled = next;\n });\n unsubscribeSendListener = params.adapter.onCallHandler((msg) => {\n if (!autoRespondEnabled) return;\n let response: AutoRespondResponse | null;\n try {\n response = policy(msg);\n } catch (err) {\n console.error('[condor-mobile-bridge dev-panel] onSendForAutoRespond threw:', err);\n return;\n }\n if (!response) return;\n setTimeout(() => {\n try {\n params.adapter.triggerEmit(response.key, response.payload);\n } catch (err) {\n console.error(\n '[condor-mobile-bridge dev-panel] triggerEmit failed in auto-respond:',\n err,\n );\n }\n }, opts.autoRespondDelayMs);\n });\n }\n\n const trigger = buildTriggerSection(\n Object.values(BRIDGE_KEYS),\n (key, payloadJson) => {\n let payload: unknown = {};\n const trimmed = payloadJson.trim();\n if (trimmed.length > 0) {\n try {\n payload = JSON.parse(trimmed);\n } catch (err) {\n console.error('[condor-mobile-bridge dev-panel] invalid JSON payload', err);\n return;\n }\n }\n params.adapter.triggerEmit(key, payload);\n },\n () => {\n params.adapter.clearMessages();\n renderSentList(sentList, params.adapter, opts.maxMessages);\n counter.textContent = '0 sent';\n },\n );\n\n if (autoRespondSection) {\n body.append(sentContainer, autoRespondSection, trigger);\n } else {\n body.append(sentContainer, trigger);\n }\n root.append(header, body);\n\n toggle.addEventListener('click', (e) => {\n e.stopPropagation();\n const collapsed = body.style.display === 'none';\n body.style.display = collapsed ? 'flex' : 'none';\n toggle.textContent = collapsed ? '−' : '+';\n });\n\n document.body.appendChild(root);\n\n const refresh = () => {\n const count = params.adapter.getMessages().length;\n counter.textContent = `${count} sent`;\n renderSentList(sentList, params.adapter, opts.maxMessages);\n };\n refresh();\n\n const interval = setInterval(refresh, REFRESH_INTERVAL_MS);\n\n const panel: InstalledPanel = {\n uninstall() {\n clearInterval(interval);\n unsubscribeSendListener?.();\n unsubscribeSendListener = null;\n root.remove();\n if (currentPanel === panel) currentPanel = null;\n },\n refresh,\n };\n currentPanel = panel;\n return panel;\n}\n\n/**\n * Inspeção (testes/debug).\n */\nexport function getInstalledDevPanel(): InstalledPanel | null {\n return currentPanel;\n}\n","/**\n * Mock adapter — ativo quando `window.flutter_inappwebview` não é\n * detectado (browser dev, jsdom sem host instalado, Storybook etc.).\n *\n * Comportamento:\n * - `callHandler` grava `{handlerName, payload, timestamp}` em buffer\n * interno (cap 100, FIFO), loga via `console.debug` e dispatcha\n * listeners registrados via `onCallHandler`.\n * - `triggerEmit` dispatcha via `window.CondorBridge.emit` — bridge real\n * trata como se viesse do nativo (handshake-ack resolve handshake;\n * demais chaves vão pros subscribers).\n * - `getMessages` retorna snapshot read-only do buffer.\n * - `clearMessages` esvazia o buffer (útil em testes).\n * - `onCallHandler(listener)` — registra listener invocado a cada send.\n * Retorna função de unsubscribe. Útil para dev panel reagir a sends\n * (v0.3.0+ auto-respond toggle) ou análise em testes.\n */\n\nconst MAX_BUFFER = 100;\n\nexport interface MockMessage {\n handlerName: string;\n payload: unknown;\n timestamp: number;\n}\n\nexport interface MockAdapter {\n readonly isMock: true;\n callHandler(handlerName: string, payload: unknown): void;\n getMessages(): readonly MockMessage[];\n clearMessages(): void;\n triggerEmit(key: string, payload: unknown): void;\n /**\n * Registra listener invocado a cada `callHandler` (mensagem JS→Native).\n * Retorna função de unsubscribe.\n *\n * Erros em listeners são capturados + logados; não afetam outros\n * listeners nem o pipeline do bridge.\n *\n * @since v0.3.0\n */\n onCallHandler(listener: (msg: MockMessage) => void): () => void;\n}\n\nexport function createMockAdapter(): MockAdapter {\n const messages: MockMessage[] = [];\n const sendListeners = new Set<(msg: MockMessage) => void>();\n\n return {\n isMock: true,\n\n callHandler(handlerName, payload) {\n const msg: MockMessage = { handlerName, payload, timestamp: Date.now() };\n messages.push(msg);\n if (messages.length > MAX_BUFFER) {\n messages.shift();\n }\n console.debug('[condor-mobile-bridge mock] →', handlerName, payload);\n for (const listener of sendListeners) {\n try {\n listener(msg);\n } catch (err) {\n console.error('[condor-mobile-bridge mock] send listener threw:', err);\n }\n }\n },\n\n getMessages() {\n return messages;\n },\n\n clearMessages() {\n messages.length = 0;\n },\n\n triggerEmit(key, payload) {\n const bridge = typeof window !== 'undefined' ? window.CondorBridge : undefined;\n if (!bridge || typeof bridge.emit !== 'function') {\n throw new Error(\n 'condor-mobile-bridge mock: window.CondorBridge not bootstrapped; cannot emit.',\n );\n }\n bridge.emit(key, payload);\n },\n\n onCallHandler(listener) {\n sendListeners.add(listener);\n return () => {\n sendListeners.delete(listener);\n };\n },\n };\n}\n","/**\n * Detecção do host nativo + adapter pattern.\n *\n * **v0.1.0:** lançava erro quando `window.flutter_inappwebview` ausente.\n * **v0.2.0:** ativa mock adapter (`createMockAdapter`) — destrava dev em\n * browser comum sem buildar APK.\n *\n * Adapter ativo é cached na primeira chamada de `getActiveAdapter()`.\n * Tests devem chamar `__resetAdapterForTests()` no `beforeEach` para\n * forçar re-detecção (ex.: após instalar/remover `window.flutter_inappwebview`).\n */\n\nimport { createMockAdapter, type MockAdapter } from './mock';\n\ninterface FlutterInAppWebView {\n callHandler(name: string, ...args: unknown[]): Promise<unknown>;\n}\n\ndeclare global {\n interface Window {\n flutter_inappwebview?: FlutterInAppWebView;\n }\n}\n\nexport interface RealAdapter {\n readonly isMock: false;\n callHandler(handlerName: string, payload: unknown): void;\n}\n\nexport type NativeAdapter = RealAdapter | MockAdapter;\n\nlet activeAdapter: NativeAdapter | null = null;\n\nfunction createRealAdapter(host: FlutterInAppWebView): RealAdapter {\n return {\n isMock: false,\n callHandler(handlerName, payload) {\n void host.callHandler(handlerName, payload);\n },\n };\n}\n\nfunction detectAdapter(): NativeAdapter {\n if (typeof window === 'undefined') {\n return createMockAdapter();\n }\n const host = window.flutter_inappwebview;\n if (host && typeof host.callHandler === 'function') {\n return createRealAdapter(host);\n }\n return createMockAdapter();\n}\n\nexport function getActiveAdapter(): NativeAdapter {\n if (!activeAdapter) {\n activeAdapter = detectAdapter();\n }\n return activeAdapter;\n}\n\nexport function hasNativeHost(): boolean {\n return !getActiveAdapter().isMock;\n}\n\n/**\n * Envia mensagem ao native handler via adapter ativo.\n *\n * Em modo real: fire-and-forget `flutter_inappwebview.callHandler`.\n * Em modo mock: grava no buffer + `console.debug`.\n */\nexport function callNative(handlerName: string, payload: unknown): void {\n getActiveAdapter().callHandler(handlerName, payload);\n}\n\n/**\n * Reseta cache do adapter. **Exportado para uso em testes apenas.**\n *\n * Cliente NUNCA deve chamar isso em código de aplicação — o adapter é\n * detectado uma vez no startup e mantido.\n */\nexport function __resetAdapterForTests(): void {\n activeAdapter = null;\n}\n","import type { BridgeKey } from '../keys';\nimport type { PayloadOf } from '../payloads';\n\ntype Callback = (payload: unknown) => void;\n\nconst subscribers = new Map<string, Set<Callback>>();\n\nexport function addSubscriber<K extends BridgeKey>(\n key: K,\n cb: (payload: PayloadOf<K>) => void,\n): () => void {\n let set = subscribers.get(key);\n if (!set) {\n set = new Set<Callback>();\n subscribers.set(key, set);\n }\n set.add(cb as Callback);\n return () => {\n const current = subscribers.get(key);\n if (!current) return;\n current.delete(cb as Callback);\n if (current.size === 0) {\n subscribers.delete(key);\n }\n };\n}\n\nexport function dispatchToSubscribers(key: string, payload: unknown): void {\n const set = subscribers.get(key);\n if (!set) return;\n for (const cb of set) {\n try {\n cb(payload);\n } catch (err) {\n // Subscriber bugado não deve afetar os outros nem o bridge.\n console.error(`[condor-mobile-bridge] subscriber for \"${key}\" threw:`, err);\n }\n }\n}\n\n/**\n * Limpa todos os subscribers. Útil em testes para isolar casos.\n */\nexport function clearAllSubscribers(): void {\n subscribers.clear();\n}\n\n/**\n * Inspeção (testes/debug).\n */\nexport function getSubscriberCount(key: string): number {\n return subscribers.get(key)?.size ?? 0;\n}\n\n/**\n * Lista das chaves com subscribers ativos (testes/debug/dev panel).\n */\nexport function getSubscriberKeys(): string[] {\n return Array.from(subscribers.keys());\n}\n","import type { DevPanelOptions } from './internal/dev-panel';\nimport { installDevPanel as installDevPanelInternal } from './internal/dev-panel';\nimport type { MockAdapter, MockMessage } from './internal/mock';\nimport { callNative, getActiveAdapter } from './internal/native';\nimport { addSubscriber, dispatchToSubscribers, getSubscriberKeys } from './internal/subscribers';\nimport type { BridgeKey } from './keys';\nimport type { HandshakeAck, HandshakeOptions } from './messages';\nimport type { PayloadOf } from './payloads';\n\nconst HANDSHAKE_ACK_KEY = 'handshake-ack';\nconst NATIVE_HANDLER = 'CondorBridge';\nconst PROTOCOL_VERSION = '0.1.0';\n\ninterface PendingHandshake {\n resolve: (ack: HandshakeAck) => void;\n reject: (err: Error) => void;\n}\n\ninterface WindowCondorBridge {\n emit(key: string, payload: unknown): void;\n}\n\ndeclare global {\n interface Window {\n CondorBridge?: WindowCondorBridge;\n }\n}\n\nlet pendingHandshake: PendingHandshake | null = null;\nlet bridgeBootstrapped = false;\n\nfunction emit(key: string, payload: unknown): void {\n if (key === HANDSHAKE_ACK_KEY && pendingHandshake) {\n const ack = (payload ?? {}) as Partial<HandshakeAck>;\n const resolver = pendingHandshake;\n pendingHandshake = null;\n resolver.resolve({\n nativeVersion: ack.nativeVersion ?? 'unknown',\n accepted: ack.accepted ?? false,\n });\n return;\n }\n dispatchToSubscribers(key, payload);\n}\n\nfunction bootstrapWindowBridge(): void {\n if (bridgeBootstrapped) return;\n if (typeof window === 'undefined') return;\n bridgeBootstrapped = true;\n\n if (window.CondorBridge && typeof window.CondorBridge.emit === 'function') {\n // Já instalado (HMR / double import / mock instalado por testes).\n // Substitui apenas o emit para garantir que o dispatcher mais recente\n // esteja conectado aos subscribers desta instância do módulo.\n window.CondorBridge.emit = emit;\n } else {\n window.CondorBridge = { emit };\n }\n}\n\nbootstrapWindowBridge();\n\nfunction requireMockAdapter(): MockAdapter {\n const adapter = getActiveAdapter();\n if (!adapter.isMock) {\n throw new Error(\n 'condor-mobile-bridge: this API is available only in mock mode (no native host detected).',\n );\n }\n return adapter;\n}\n\nexport interface MockState {\n readonly isMock: true;\n readonly messages: readonly MockMessage[];\n readonly subscriberKeys: readonly string[];\n}\n\nexport interface CondorBridge {\n /**\n * `true` quando o adapter ativo é mock (browser dev, sem\n * `window.flutter_inappwebview`). `false` quando rodando dentro do\n * WebView Flutter.\n */\n readonly isMock: boolean;\n\n /**\n * Envia handshake versionado ao nativo e aguarda `handshake-ack`.\n *\n * Apenas um handshake pode estar pendente por vez. Chamadas paralelas\n * antes do primeiro `ack` chegar são rejeitadas com erro claro.\n *\n * **Em mock mode:** NÃO resolve sozinho. Cliente/teste deve chamar\n * `Bridge.mockEmit('handshake-ack', {nativeVersion, accepted})` para\n * destravar a Promise. Esta decisão (`Explícito`) força testes a serem\n * intencionais sobre o estado nativo.\n */\n handshake(opts: HandshakeOptions): Promise<HandshakeAck>;\n\n /**\n * Subscreve a uma chave observável. Retorna função `unsubscribe`.\n *\n * Múltiplos subscribers por chave são permitidos. Erro em um subscriber\n * não afeta os outros (capturado e logado em `console.error`).\n */\n on<K extends BridgeKey>(key: K, cb: (payload: PayloadOf<K>) => void): () => void;\n\n /**\n * Envia mensagem genérica ao nativo. Use para comandos UI→BG que não\n * tenham helper dedicado. `type` é a string que o native usa para\n * roteamento no `_onJsMessage`.\n *\n * Em mock mode: grava no buffer interno + `console.debug`.\n */\n send(type: string, payload?: Record<string, unknown>): void;\n\n /**\n * Abre a tela nativa de Settings do app. Equivalente a\n * `Bridge.send('openSettings')`.\n */\n openSettings(): void;\n\n /**\n * Dispara `window.CondorBridge.emit(key, payload)` programaticamente —\n * útil para simular eventos nativos em testes e dev em browser.\n *\n * **Throws** se o adapter ativo não for mock.\n */\n mockEmit(key: string, payload?: unknown): void;\n\n /**\n * Snapshot do estado do mock adapter (mensagens enviadas + chaves com\n * subscribers ativos). Useful para asserts em testes de cliente.\n *\n * **Throws** se o adapter ativo não for mock.\n */\n getMockState(): MockState;\n\n /**\n * Injeta painel HTML flutuante no `document.body` para inspecionar o\n * bridge em mock mode. Idempotente — re-install substitui o anterior.\n *\n * **Throws** se o adapter ativo não for mock OU se `document` não existir.\n */\n installDevPanel(options?: DevPanelOptions): void;\n}\n\nexport const Bridge: CondorBridge = {\n get isMock(): boolean {\n return getActiveAdapter().isMock;\n },\n\n handshake(opts: HandshakeOptions): Promise<HandshakeAck> {\n bootstrapWindowBridge();\n if (pendingHandshake) {\n return Promise.reject(\n new Error(\n 'condor-mobile-bridge: handshake already in flight. Await the previous call before issuing another.',\n ),\n );\n }\n return new Promise<HandshakeAck>((resolve, reject) => {\n pendingHandshake = { resolve, reject };\n try {\n callNative(NATIVE_HANDLER, {\n type: 'handshake',\n version: PROTOCOL_VERSION,\n payload: { clientVersion: opts.clientVersion },\n });\n } catch (err) {\n pendingHandshake = null;\n reject(err instanceof Error ? err : new Error(String(err)));\n }\n });\n },\n\n on<K extends BridgeKey>(key: K, cb: (payload: PayloadOf<K>) => void): () => void {\n bootstrapWindowBridge();\n return addSubscriber(key, cb);\n },\n\n send(type: string, payload?: Record<string, unknown>): void {\n callNative(NATIVE_HANDLER, {\n type,\n version: PROTOCOL_VERSION,\n payload: payload ?? {},\n });\n },\n\n openSettings(): void {\n this.send('openSettings');\n },\n\n mockEmit(key: string, payload?: unknown): void {\n requireMockAdapter().triggerEmit(key, payload ?? {});\n },\n\n getMockState(): MockState {\n const adapter = requireMockAdapter();\n return {\n isMock: true,\n messages: adapter.getMessages(),\n subscriberKeys: getSubscriberKeys(),\n };\n },\n\n installDevPanel(options?: DevPanelOptions): void {\n const adapter = requireMockAdapter();\n installDevPanelInternal({ adapter, options });\n },\n};\n\n/**\n * Reseta o estado interno do bridge (handshake pendente).\n *\n * **Exportado para uso em testes apenas.** Não chamar em código de\n * aplicação — quebra a semântica de \"apenas um handshake pendente\".\n */\nexport function __resetBridgeForTests(): void {\n pendingHandshake = null;\n}\n","/**\n * `BridgeClient` (v0.5.0) — API para código do cliente externo rodando\n * dentro de `<iframe sandbox=\"allow-scripts\">` no `sleep_whitelabel`.\n *\n * Surface idêntica ao `Bridge` legado (`on`/`send`/`openSettings`/`handshake`)\n * — cliente importa e não percebe que está em sandbox. Internamente,\n * todas as chamadas viram `window.parent.postMessage(...)` com schema\n * validado pelo shell antes de chegar ao nativo.\n *\n * **Não expõe** `installDevPanel`, `mockEmit`, `isMock`, `getMockState` —\n * essas APIs são responsabilidade do shell (`BridgeShell`).\n *\n * **Threat model & Camada 2 do Risco 1** (vide\n * `sleep-white-label-content-transport-and-security.md`): este módulo\n * também responde ao `verify-sandbox` do shell — função de attestation\n * do próprio iframe sobre se ainda enxerga `window.flutter_inappwebview`\n * ou `window.CondorBridge` (que deveriam ter sido deletados pela Camada 1).\n * Se vier `true` em algum, o shell aborta o boot do app.\n */\n\nimport { BRIDGE_KEYS, type BridgeKey } from './keys';\nimport type { PayloadOf, SensorLogPayload, SensorLogQueryPayload } from './payloads';\nimport type { ClientToShellMessage, ShellToClientMessage } from './shell-messages';\n\nconst PROTOCOL_VERSION = '0.5.0';\nconst HANDSHAKE_TIMEOUT_MS = 5000;\n/**\n * Timeout padrão de uma página de `sensorLog`. Uma página de 1000 logs custa\n * ~2,7 s no pior caso medido (device gen2), então a margem é deliberada.\n */\nconst SENSOR_LOG_TIMEOUT_MS = 10000;\n\ninterface PendingHandshake {\n resolve: (ack: { shellVersion: string; accepted: boolean }) => void;\n reject: (err: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\ninterface Subscriber {\n // Stored as (unknown) — typed inference comes from public `on<K>`.\n cb: (payload: unknown) => void;\n}\n\nconst subscribers = new Map<string, Subscriber[]>();\nlet pendingHandshake: PendingHandshake | null = null;\nlet messageListenerInstalled = false;\n\nfunction postToShell(msg: ClientToShellMessage): void {\n if (typeof window === 'undefined' || window.parent === window) {\n // No parent — not in iframe. Caller bug; silently drop.\n console.warn('[BridgeClient] no parent window; message dropped:', msg.type);\n return;\n }\n window.parent.postMessage(msg, '*');\n}\n\nfunction dispatchEvent(key: string, payload: unknown): void {\n const arr = subscribers.get(key);\n if (!arr || arr.length === 0) return;\n for (const sub of arr) {\n try {\n sub.cb(payload);\n } catch (err) {\n console.error('[BridgeClient] subscriber error:', err);\n }\n }\n}\n\nfunction onMessage(event: MessageEvent): void {\n if (event.source !== window.parent) return;\n // Não validamos com Valibot aqui — o shell é trusted (Condor-controlado).\n // Validamos só o `type` para evitar crashes por payload malformado.\n const data = event.data as Partial<ShellToClientMessage>;\n if (!data || typeof data !== 'object' || typeof data.type !== 'string') return;\n\n switch (data.type) {\n case 'event': {\n const msg = data as Extract<ShellToClientMessage, { type: 'event' }>;\n if (typeof msg.key === 'string') dispatchEvent(msg.key, msg.payload);\n break;\n }\n case 'handshake-ack': {\n const msg = data as Extract<ShellToClientMessage, { type: 'handshake-ack' }>;\n if (pendingHandshake) {\n clearTimeout(pendingHandshake.timer);\n const { resolve } = pendingHandshake;\n pendingHandshake = null;\n resolve({\n shellVersion: msg.ack?.shellVersion ?? 'unknown',\n accepted: msg.ack?.accepted ?? false,\n });\n }\n break;\n }\n case 'nack':\n // Cliente pode ouvir nacks via `Bridge.on('__nack', ...)` em futuras versões.\n // v0.5.0: só loga, sem dispatcher dedicado.\n console.warn('[BridgeClient] command nacked by shell:', data);\n break;\n case 'verify-sandbox': {\n // Camada 2 do Risco 1: shell pergunta se ainda enxergamos os bridges\n // nativos. Resposta autoritativa do iframe sobre seu próprio estado.\n const w = window as unknown as Record<string, unknown>;\n postToShell({\n type: 'sandbox-verify-response',\n flutterBridgeReachable: typeof w.flutter_inappwebview !== 'undefined',\n condorBridgeReachable: typeof w.CondorBridge !== 'undefined',\n });\n break;\n }\n }\n}\n\nfunction ensureMessageListener(): void {\n if (messageListenerInstalled) return;\n if (typeof window === 'undefined') return;\n messageListenerInstalled = true;\n window.addEventListener('message', onMessage);\n}\n\nensureMessageListener();\n\nexport interface BridgeClientHandshakeAck {\n shellVersion: string;\n accepted: boolean;\n}\n\nexport interface CondorBridgeClient {\n /**\n * Versão do protocolo do client.\n */\n readonly protocolVersion: string;\n\n /**\n * Subscreve a uma chave observável. Retorna função `unsubscribe`.\n * Múltiplos subscribers por chave permitidos. Erro em um subscriber\n * não afeta os outros.\n */\n on<K extends BridgeKey>(key: K, cb: (payload: PayloadOf<K>) => void): () => void;\n\n /**\n * Envia comando para o shell. Shell valida contra `ShellPolicy` antes\n * de tocar o nativo. Comandos não permitidos → `nack` (logado em\n * `console.warn` na v0.5.0).\n */\n send(command: string, args?: Record<string, unknown>): void;\n\n /**\n * Abre Settings nativa. Equivalente a `send('openSettings')`.\n */\n openSettings(): void;\n\n /**\n * Pede UMA página de sensor logs e resolve com a resposta correlacionada\n * pelo `requestId` — inclusive quando ela traz `success: false`, que é\n * resposta e não falha de transporte. A Promise só **rejeita** por timeout.\n *\n * Dispensa o cliente de manter o mapa `requestId → resolve` à mão. Para\n * varrer um período, encadeie as chamadas usando o `startingAfter` devolvido\n * até `hasMore` virar `false`: os cursores são dependentes, então as páginas\n * **não** podem ser buscadas em paralelo.\n */\n fetchSensorLog(\n query: SensorLogQueryPayload,\n opts?: { timeoutMs?: number },\n ): Promise<SensorLogPayload>;\n\n /**\n * Solicita UMA permissão via **diálogo nativo in-app** (não abre Settings).\n * Equivalente a `send('requestPermission', { permission })`. Use no botão\n * \"Conceder\" do card Necessárias, com um nome vindo de\n * `systemStatus.missingEssentialPermissions` (ex.: `'bluetoothScan'`).\n * O resultado chega via re-emissão de `systemStatus` (sem chave dedicada).\n */\n requestPermission(permission: string): void;\n\n /**\n * Handshake com o shell. Resolve com `{ shellVersion, accepted }`.\n * Timeout em 5s se shell não responder.\n *\n * Apenas um handshake pode estar pendente por vez.\n */\n handshake(opts: { clientVersion: string }): Promise<BridgeClientHandshakeAck>;\n\n /**\n * Reporta ao nativo se o cliente tem histórico interno para voltar.\n * **Chame a cada mudança de rota interna** (e no boot). O nativo cacheia\n * este estado e o usa para decidir o botão/gesture de voltar do SO:\n * - `true` → o back do SO pede uma volta interna (evento `navigationBack`,\n * vide `onBack`).\n * - `false` → o back do SO fica na home (não sai da página).\n *\n * Equivalente a `send('navigationState', { canGoBack })`.\n */\n reportNavigation(canGoBack: boolean): void;\n\n /**\n * Assina o pedido de back do SO (evento `navigationBack`). O callback deve\n * voltar **uma** página interna do cliente e então chamar\n * `reportNavigation(...)` com o novo estado. Só é chamado quando o cliente\n * havia reportado `reportNavigation(true)`. Retorna função `unsubscribe`.\n */\n onBack(cb: () => void): () => void;\n}\n\nexport const BridgeClient: CondorBridgeClient = {\n protocolVersion: PROTOCOL_VERSION,\n\n on<K extends BridgeKey>(key: K, cb: (payload: PayloadOf<K>) => void): () => void {\n ensureMessageListener();\n const arr = subscribers.get(key) ?? [];\n const sub: Subscriber = { cb: cb as (payload: unknown) => void };\n arr.push(sub);\n subscribers.set(key, arr);\n // Notifica shell para registrar subscription no native (idempotente\n // no shell — múltiplos `subscribe` para a mesma chave são tratados).\n postToShell({ type: 'subscribe', key });\n return () => {\n const remaining = (subscribers.get(key) ?? []).filter((s) => s !== sub);\n subscribers.set(key, remaining);\n if (remaining.length === 0) postToShell({ type: 'unsubscribe', key });\n };\n },\n\n send(command: string, args: Record<string, unknown> = {}): void {\n postToShell({ type: 'send', command, args });\n },\n\n openSettings(): void {\n this.send('openSettings');\n },\n\n fetchSensorLog(\n query: SensorLogQueryPayload,\n opts?: { timeoutMs?: number },\n ): Promise<SensorLogPayload> {\n const timeoutMs = opts?.timeoutMs ?? SENSOR_LOG_TIMEOUT_MS;\n return new Promise<SensorLogPayload>((resolve, reject) => {\n let settled = false;\n const unsubscribe = this.on(BRIDGE_KEYS.sensorLog, (payload) => {\n // Outras páginas em voo compartilham a chave: só a nossa interessa.\n if (settled || payload.requestId !== query.requestId) return;\n settled = true;\n clearTimeout(timer);\n unsubscribe();\n resolve(payload);\n });\n const timer = setTimeout(() => {\n if (settled) return;\n settled = true;\n unsubscribe();\n reject(\n new Error(\n `BridgeClient: sensorLog timeout after ${timeoutMs}ms (requestId=${query.requestId})`,\n ),\n );\n }, timeoutMs);\n this.send('sensorLog', { ...query });\n });\n },\n\n requestPermission(permission: string): void {\n this.send('requestPermission', { permission });\n },\n\n handshake(opts: { clientVersion: string }): Promise<BridgeClientHandshakeAck> {\n ensureMessageListener();\n if (pendingHandshake) {\n return Promise.reject(\n new Error(\n 'BridgeClient: handshake already in flight. Await previous before issuing another.',\n ),\n );\n }\n return new Promise<BridgeClientHandshakeAck>((resolve, reject) => {\n const timer = setTimeout(() => {\n pendingHandshake = null;\n reject(new Error(`BridgeClient: handshake timeout after ${HANDSHAKE_TIMEOUT_MS}ms`));\n }, HANDSHAKE_TIMEOUT_MS);\n pendingHandshake = { resolve, reject, timer };\n postToShell({\n type: 'handshake',\n payload: { clientVersion: opts.clientVersion },\n });\n });\n },\n\n reportNavigation(canGoBack: boolean): void {\n this.send('navigationState', { canGoBack });\n },\n\n onBack(cb: () => void): () => void {\n return this.on(BRIDGE_KEYS.navigationBack, () => cb());\n },\n};\n\n/**\n * Reseta estado interno (subscribers + handshake pendente).\n *\n * **Exportado para uso em testes apenas.** Listener `message` permanece\n * instalado entre testes — é idempotente (checa `event.source ===\n * window.parent` per-call) e o `window` do jsdom persiste entre tests.\n */\nexport function __resetBridgeClientForTests(): void {\n if (pendingHandshake) {\n clearTimeout(pendingHandshake.timer);\n pendingHandshake = null;\n }\n subscribers.clear();\n}\n","/**\n * `BridgeShell` (v0.5.0) — API para o shell Condor-controlado que envolve\n * o conteúdo do cliente externo dentro de `<iframe sandbox=\"allow-scripts\">`.\n *\n * Responsabilidades:\n * 1. **Camada 1 do Risco 1** — `bootstrapClientIframe(iframe, clientPath)`:\n * busca HTML do cliente via `fetch`, prepende script de override que\n * deleta `window.flutter_inappwebview` e `window.CondorBridge` do\n * iframe, e seta como `srcdoc`. Override roda em parse-time, antes\n * de qualquer outro script.\n * 2. **Camada 2 do Risco 1** — `verifySandbox(iframe)`: round-trip\n * `postMessage` que pede ao iframe atestation autoritativa de que\n * `window.flutter_inappwebview` e `window.CondorBridge` são `undefined`.\n * 3. **Roteamento postMessage iframe ↔ nativo:** escuta `message` events\n * do iframe, valida com Valibot, aplica `ShellPolicy` default-deny,\n * e propaga para o `Bridge` legado (que fala com o nativo). Eventos\n * nativos voltam para o iframe via `postMessage`.\n * 4. **Confirmação humana para comandos sensíveis:** comandos em\n * `policy.requiresConfirmation` disparam callback\n * `onConfirmationRequest` antes de tocar o nativo — shell apresenta UI.\n */\n\nimport * as v from 'valibot';\nimport { Bridge } from './bridge';\nimport type { BridgeKey } from './keys';\nimport {\n ClientToShellMessage,\n type ConfirmationRequest,\n type SandboxVerifyResult,\n type ShellPolicy,\n type ShellToClientMessage,\n} from './shell-messages';\n\nconst SHELL_VERSION = '0.5.1';\nconst VERIFY_TIMEOUT_MS = 2000;\n\n/**\n * **Camada 1 do Risco 1 — override prefix.**\n *\n * Script prependado ao srcdoc do iframe pelo `bootstrapClientIframe*`.\n * Roda em parse-time, antes de qualquer outro script do cliente, no\n * próprio documento do iframe (origem null — sem cross-origin block).\n *\n * Usa **getter/setter no-op** + `configurable: false` (não\n * `value/writable: false`). Razão: bundles ESM modernos rodam em strict\n * mode; qualquer `window.X = …` em propriedade não-writable **throws**,\n * o que quebra bootstrap de módulos legados que tentam registrar APIs no\n * window (ex.: `condor-mobile-bridge` legacy faz\n * `window.CondorBridge = {emit}` como side-effect no module load).\n * Com getter/setter no-op, a atribuição passa silenciosamente mas o\n * getter sempre retorna undefined — bridges nativos permanecem\n * inacessíveis no iframe sem quebrar fluxos JS legítimos.\n *\n * Validado empiricamente via pen test ofensivo em `moto g 9 plus`\n * durante o trio sandbox iframe (2026-06-09).\n */\nconst OVERRIDE_PREFIX = `<script>\n(function(){\n try { delete window.flutter_inappwebview; } catch (_) {}\n try { delete window.CondorBridge; } catch (_) {}\n try {\n Object.defineProperty(window, 'flutter_inappwebview', {\n get: function(){ return undefined; },\n set: function(_){ /* no-op: bloqueia injeção do plugin no iframe */ },\n configurable: false,\n });\n } catch (_) {}\n try {\n Object.defineProperty(window, 'CondorBridge', {\n get: function(){ return undefined; },\n set: function(_){ /* no-op: bloqueia bootstrap do package legado */ },\n configurable: false,\n });\n } catch (_) {}\n try {\n // Neutraliza navigator.sendBeacon: canal de exfil que a CSP connect-src\n // não barra de forma confiável em alguns WebViews (pentest 2026-06-24,\n // PT-10). O cliente comunica só via BridgeClient/postMessage — não tem\n // uso legítimo de sendBeacon. Getter no-op retorna função que devolve\n // false; setter no-op ignora tentativa de restaurar.\n Object.defineProperty(navigator, 'sendBeacon', {\n get: function(){ return function(){ return false; }; },\n set: function(_){ /* no-op */ },\n configurable: false,\n });\n } catch (_) {}\n})();\n</script>`;\n\nexport interface BridgeShellOptions {\n /**\n * Iframe que hospeda o cliente. Pode estar vazio no momento da\n * construção — `bootstrapClientIframe` será chamado depois para popular.\n */\n iframe: HTMLIFrameElement;\n\n /**\n * Policy default-deny. Toda chave/comando vindo do iframe é validado\n * contra estas listas. Vide `ShellPolicy`.\n */\n policy: ShellPolicy;\n\n /**\n * Callback síncrono ou assíncrono chamado quando o cliente pede um\n * comando em `policy.requiresConfirmation`. Shell apresenta UI de\n * confirmação; resolve com `true` (aprovar) ou `false` (recusar).\n * Se não fornecido, comandos `requiresConfirmation` são **negados**\n * automaticamente — fail-safe.\n */\n onConfirmationRequest?: (req: ConfirmationRequest) => boolean | Promise<boolean>;\n}\n\nexport class BridgeShell {\n private readonly iframe: HTMLIFrameElement;\n private readonly policy: ShellPolicy;\n private readonly onConfirm?: BridgeShellOptions['onConfirmationRequest'];\n private readonly subscriptions = new Map<BridgeKey, () => void>();\n private detached = false;\n\n // Map de `verify-sandbox` round-trips pendentes — key gerada por nonce.\n private pendingVerifies = new Map<\n string,\n {\n resolve: (r: SandboxVerifyResult) => void;\n reject: (e: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n }\n >();\n\n constructor(opts: BridgeShellOptions) {\n this.iframe = opts.iframe;\n this.policy = opts.policy;\n this.onConfirm = opts.onConfirmationRequest;\n if (typeof window !== 'undefined') {\n window.addEventListener('message', this.onMessage);\n }\n }\n\n /**\n * **Camada 1 do Risco 1.** Bootstrap via fetch de URL.\n *\n * Atalho para o caso comum HTTP/HTTPS: faz `fetch(clientPath)` e\n * delega para `bootstrapClientIframeWithHtml`.\n *\n * **Não funciona em scheme `file://`** — `fetch` em WebView Android\n * (Chromium) bloqueia o scheme com \"URL scheme 'file' is not supported\".\n * Para asset bundled em APK ou bundle inlined no build-time, use\n * `bootstrapClientIframeWithHtml(htmlString)`.\n *\n * **Devo ser chamado uma vez no boot do shell**, antes de qualquer\n * outra interação. Após retornar, `verifySandbox()` deve ser invocado\n * para atestation (Camada 2).\n */\n async bootstrapClientIframe(clientPath = 'client.html'): Promise<void> {\n const res = await fetch(clientPath);\n if (!res.ok) throw new Error(`BridgeShell: falha ao carregar ${clientPath} (${res.status})`);\n const clientHtml = await res.text();\n await this.bootstrapClientIframeWithHtml(clientHtml);\n }\n\n /**\n * **Camada 1 do Risco 1.** Bootstrap a partir de HTML string.\n *\n * Aceita HTML do cliente diretamente (já lido de arquivo, embedado em\n * build-time via `esbuild.define`, lido de IndexedDB, etc.) e seta\n * como `srcdoc` do iframe prefixado pelo override script. Override\n * roda em parse-time, antes de qualquer outro script do cliente.\n *\n * Use quando `fetch` não é aplicável (asset bundled em `file://`,\n * bundle inlined no build, qualquer cenário sem rede). Para HTTP\n * use `bootstrapClientIframe(clientPath)`.\n *\n * **Devo ser chamado uma vez no boot do shell**, antes de qualquer\n * outra interação. Após retornar, `verifySandbox()` deve ser invocado\n * para atestation (Camada 2).\n */\n async bootstrapClientIframeWithHtml(clientHtml: string): Promise<void> {\n this.iframe.srcdoc = OVERRIDE_PREFIX + clientHtml;\n await this.waitForIframeLoad();\n }\n\n private waitForIframeLoad(): Promise<void> {\n return new Promise<void>((resolve) => {\n const onLoad = (): void => {\n this.iframe.removeEventListener('load', onLoad);\n resolve();\n };\n this.iframe.addEventListener('load', onLoad);\n });\n }\n\n /**\n * **Camada 2 do Risco 1.** Round-trip `postMessage` que pede ao iframe\n * atestation autoritativa do estado de `window.flutter_inappwebview`\n * e `window.CondorBridge`. Timeout em 2s.\n *\n * `ok` é `true` apenas se ambos forem `undefined` no iframe. Se vier\n * `false`, **shell deve abortar o boot do app** — Camada 1 falhou e\n * Camada 4 (dual WebView) precisa ser acionada manualmente.\n */\n verifySandbox(): Promise<SandboxVerifyResult> {\n if (!this.iframe.contentWindow) {\n return Promise.reject(new Error('BridgeShell: iframe sem contentWindow'));\n }\n return new Promise<SandboxVerifyResult>((resolve, reject) => {\n const nonce = Math.random().toString(36).slice(2);\n const timer = setTimeout(() => {\n this.pendingVerifies.delete(nonce);\n reject(new Error(`BridgeShell: verify-sandbox timeout após ${VERIFY_TIMEOUT_MS}ms`));\n }, VERIFY_TIMEOUT_MS);\n this.pendingVerifies.set(nonce, { resolve, reject, timer });\n this.postToClient({ type: 'verify-sandbox' });\n });\n }\n\n /**\n * Encerra o shell — remove listener `message`, cancela subscriptions\n * pendentes no `Bridge` nativo, limpa estado interno.\n */\n detach(): void {\n if (this.detached) return;\n this.detached = true;\n if (typeof window !== 'undefined') {\n window.removeEventListener('message', this.onMessage);\n }\n for (const unsub of this.subscriptions.values()) unsub();\n this.subscriptions.clear();\n for (const pending of this.pendingVerifies.values()) {\n clearTimeout(pending.timer);\n pending.reject(new Error('BridgeShell: detached'));\n }\n this.pendingVerifies.clear();\n }\n\n private onMessage = async (event: MessageEvent): Promise<void> => {\n if (this.detached) return;\n if (event.source !== this.iframe.contentWindow) return;\n\n const parsed = v.safeParse(ClientToShellMessage, event.data);\n if (!parsed.success) {\n // Drop silently — possivelmente noise (extensão de browser, dev tool).\n // Loga só em modo dev para diagnose.\n if (typeof console !== 'undefined') {\n console.debug('[BridgeShell] dropped malformed message:', parsed.issues?.[0]?.message);\n }\n return;\n }\n\n const msg = parsed.output;\n switch (msg.type) {\n case 'subscribe':\n await this.handleSubscribe(msg.key as BridgeKey);\n break;\n case 'unsubscribe':\n this.handleUnsubscribe(msg.key as BridgeKey);\n break;\n case 'send':\n await this.handleSend(msg.command, msg.args ?? {});\n break;\n case 'handshake':\n // Handshake do cliente é com o shell (não com o nativo diretamente).\n // Shell responde com sua versão. Se quiser propagar pro nativo, o\n // shell faz isso por iniciativa própria via `Bridge.handshake`.\n this.postToClient({\n type: 'handshake-ack',\n ack: { shellVersion: SHELL_VERSION, accepted: true },\n });\n break;\n case 'jsError':\n // Forward erro JS do iframe para o nativo (roteia para Logs).\n Bridge.send('jsError', {\n origin: 'iframe',\n message: msg.payload.message,\n stack: msg.payload.stack,\n source: msg.payload.source,\n lineno: msg.payload.lineno,\n colno: msg.payload.colno,\n });\n break;\n case 'sandbox-verify-response': {\n // Camada 2: cliente respondeu ao verify-sandbox. Resolve o pending\n // mais antigo (em prática só há um por vez — mantemos map por\n // robustez).\n const first = this.pendingVerifies.keys().next().value;\n if (first !== undefined) {\n const pending = this.pendingVerifies.get(first);\n if (pending) {\n clearTimeout(pending.timer);\n this.pendingVerifies.delete(first);\n const ok = !msg.flutterBridgeReachable && !msg.condorBridgeReachable;\n pending.resolve({\n flutterBridgeReachable: msg.flutterBridgeReachable,\n condorBridgeReachable: msg.condorBridgeReachable,\n ok,\n });\n }\n }\n break;\n }\n }\n };\n\n private async handleSubscribe(key: BridgeKey): Promise<void> {\n if (!this.policy.readOnlyKeys.includes(key)) {\n this.postToClient({ type: 'nack', key, reason: 'not-read-only' });\n return;\n }\n if (this.subscriptions.has(key)) return; // idempotent\n const unsub = Bridge.on(key, (payload) => {\n if (this.detached) return;\n this.postToClient({ type: 'event', key, payload });\n });\n this.subscriptions.set(key, unsub);\n }\n\n private handleUnsubscribe(key: BridgeKey): void {\n const unsub = this.subscriptions.get(key);\n if (!unsub) return;\n unsub();\n this.subscriptions.delete(key);\n }\n\n private async handleSend(command: string, args: Record<string, unknown>): Promise<void> {\n if (this.policy.deniedCommands.includes(command)) {\n this.postToClient({ type: 'nack', command, reason: 'denied' });\n return;\n }\n if (this.policy.requiresConfirmation.includes(command)) {\n if (!this.onConfirm) {\n // Fail-safe: sem callback, comando que requer confirmação é negado.\n this.postToClient({ type: 'nack', command, reason: 'no-confirmation-handler' });\n return;\n }\n let approved = false;\n try {\n approved = await this.onConfirm({ command, args });\n } catch (err) {\n this.postToClient({\n type: 'nack',\n command,\n reason: `confirmation-error: ${err instanceof Error ? err.message : String(err)}`,\n });\n return;\n }\n if (!approved) {\n this.postToClient({ type: 'nack', command, reason: 'user-rejected' });\n return;\n }\n } else if (!this.policy.allowedCommands.includes(command)) {\n // Comando fora de allowed/denied/requiresConfirmation — drop por default.\n this.postToClient({ type: 'nack', command, reason: 'not-allowed' });\n return;\n }\n Bridge.send(command, args);\n }\n\n private postToClient(msg: ShellToClientMessage): void {\n if (this.detached) return;\n this.iframe.contentWindow?.postMessage(msg, '*');\n }\n}\n","/**\n * Schemas Valibot dos `postMessage` shell↔client (v0.5.0).\n *\n * Quando o conteúdo do cliente roda dentro de `<iframe sandbox=\"allow-scripts\">`\n * (origem null), a comunicação com o shell Condor-controlado acontece via\n * `window.postMessage`. Estes schemas são o **único contrato aceito** —\n * qualquer mensagem mal-formada é descartada silenciosamente pelo shell.\n *\n * **Threat model:** o iframe pode ser hostil (supply-chain comprometida,\n * XSS no código do cliente). Estes schemas + a `ShellPolicy` aplicada\n * no `BridgeShell` formam o boundary estrutural — vide\n * `condor_wiki/cross-cutting/sleep-white-label-content-transport-and-security.md`.\n */\n\nimport * as v from 'valibot';\nimport { BRIDGE_KEYS } from './keys';\n\nconst BridgeKeySchema = v.picklist(Object.values(BRIDGE_KEYS) as [string, ...string[]]);\n\n// ─── Client → Shell ──────────────────────────────────────────────────\n\nexport const SubscribeMessage = v.object({\n type: v.literal('subscribe'),\n key: BridgeKeySchema,\n});\n\nexport const UnsubscribeMessage = v.object({\n type: v.literal('unsubscribe'),\n key: BridgeKeySchema,\n});\n\nexport const SendMessage = v.object({\n type: v.literal('send'),\n command: v.string(),\n args: v.optional(v.record(v.string(), v.unknown()), {}),\n});\n\nexport const HandshakeMessage = v.object({\n type: v.literal('handshake'),\n payload: v.object({ clientVersion: v.string() }),\n});\n\nexport const JsErrorMessage = v.object({\n type: v.literal('jsError'),\n payload: v.object({\n message: v.string(),\n stack: v.optional(v.string()),\n source: v.optional(v.string()),\n lineno: v.optional(v.number()),\n colno: v.optional(v.number()),\n }),\n});\n\nexport const SandboxVerifyResponseMessage = v.object({\n type: v.literal('sandbox-verify-response'),\n flutterBridgeReachable: v.boolean(),\n condorBridgeReachable: v.boolean(),\n});\n\nexport const ClientToShellMessage = v.variant('type', [\n SubscribeMessage,\n UnsubscribeMessage,\n SendMessage,\n HandshakeMessage,\n JsErrorMessage,\n SandboxVerifyResponseMessage,\n]);\n\nexport type ClientToShellMessage = v.InferOutput<typeof ClientToShellMessage>;\n\n// ─── Shell → Client ──────────────────────────────────────────────────\n\nexport const EventMessage = v.object({\n type: v.literal('event'),\n key: BridgeKeySchema,\n payload: v.unknown(),\n});\n\nexport const HandshakeAckMessage = v.object({\n type: v.literal('handshake-ack'),\n ack: v.object({\n shellVersion: v.string(),\n accepted: v.boolean(),\n }),\n});\n\nexport const NackMessage = v.object({\n type: v.literal('nack'),\n key: v.optional(v.string()),\n command: v.optional(v.string()),\n reason: v.string(),\n});\n\nexport const VerifySandboxMessage = v.object({\n type: v.literal('verify-sandbox'),\n});\n\nexport const ShellToClientMessage = v.variant('type', [\n EventMessage,\n HandshakeAckMessage,\n NackMessage,\n VerifySandboxMessage,\n]);\n\nexport type ShellToClientMessage = v.InferOutput<typeof ShellToClientMessage>;\n\n// ─── Policy ──────────────────────────────────────────────────────────\n\n/**\n * Policy default-deny aplicada pelo `BridgeShell`. Toda chave/comando\n * vindo do iframe é validado contra estas listas antes de tocar o\n * bridge nativo.\n *\n * - `readOnlyKeys`: chaves que o cliente pode subscrever (observáveis).\n * - `allowedCommands`: comandos pass-through (sem confirmação humana).\n * - `requiresConfirmation`: comandos que disparam callback `onConfirmationRequest`\n * no shell — usuário decide aprovar/recusar via UI nativa do shell.\n * - `deniedCommands`: bloqueio explícito (defesa em profundidade — comandos\n * destrutivos como `dfu` que nunca devem fluir do iframe).\n *\n * **Não embedada no package** — cliente do `BridgeShell` (no monorepo\n * `sleep_whitelabel`) provê a policy concreta. Mantém o package agnóstico\n * ao catálogo e permite policy evoluir sem novo release.\n */\nexport interface ShellPolicy {\n readOnlyKeys: string[];\n allowedCommands: string[];\n requiresConfirmation: string[];\n deniedCommands: string[];\n}\n\n/**\n * Resposta do callback `onConfirmationRequest` do `BridgeShell`.\n *\n * `approve: true` → comando flui para o nativo.\n * `approve: false` → shell envia `nack` ao iframe com `reason: 'user-rejected'`.\n */\nexport interface ConfirmationRequest {\n command: string;\n args: Record<string, unknown>;\n}\n\n/**\n * Estado retornado pelo `verifySandbox()` do `BridgeShell` após\n * round-trip com o iframe. Se algum dos `*Reachable` vier `true`, o\n * boundary está furado — Camada 1 (`bootstrapClientIframe` com override\n * prefix) falhou e a Camada 4 (dual WebView) deve ser acionada.\n */\nexport interface SandboxVerifyResult {\n flutterBridgeReachable: boolean;\n condorBridgeReachable: boolean;\n ok: boolean; // !flutterBridgeReachable && !condorBridgeReachable\n}\n"],"mappings":"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","names":["PROTOCOL_VERSION","subscribers","pendingHandshake","v"]}
package/dist/index.d.cts CHANGED
@@ -113,6 +113,14 @@ declare const BRIDGE_KEYS: {
113
113
  * ao cliente. Vide `BridgeClient.onBack`.
114
114
  */
115
115
  readonly navigationBack: "navigationBack";
116
+ /**
117
+ * Native → client: the app's language state. Carries the raw device locale
118
+ * AND the language explicitly chosen in the app Settings, so the client can
119
+ * apply its own precedence rule — including rendering a language the native
120
+ * chrome does not support. Emitted on attach, after `handshake-ack`, and on
121
+ * every change in Settings. Vide `LanguagePayload`.
122
+ */
123
+ readonly language: "language";
116
124
  };
117
125
  type BridgeKey = (typeof BRIDGE_KEYS)[keyof typeof BRIDGE_KEYS];
118
126
 
@@ -234,13 +242,47 @@ interface DeviceNotificationPayload {
234
242
  messageCode: string | null;
235
243
  isError: boolean;
236
244
  }
245
+ /**
246
+ * Parâmetros do comando `sensorLog` (GET `user_cmd=log` do Mobile Lambda).
247
+ *
248
+ * `startTime`/`endTime` delimitam um período — `startTime` é **inclusivo** e
249
+ * `endTime` **exclusivo**, ambos no formato estrito `YYYY-MM-DD HH:MM:SS`
250
+ * (sem `T`, sem offset, sem date-only).
251
+ *
252
+ * ⚠️ Os timestamps do contrato são **hora local do dispositivo**, não UTC:
253
+ * `log_time` é passthrough do RTC do aparelho. Monte o range com os getters
254
+ * locais do `Date` (`getFullYear()`, `getHours()`, …) e **nunca** com
255
+ * `toISOString()` — converter para UTC desloca as séries pelo offset do fuso,
256
+ * com dados plausíveis e nenhum erro visível.
257
+ *
258
+ * Os cursores `startingAfter`/`endingBefore` são **opacos** e mutuamente
259
+ * exclusivos: ecoe o valor recebido, nunca construa um.
260
+ */
261
+ interface SensorLogQueryPayload {
262
+ /** Ecoado na resposta — correlaciona requisições concorrentes. */
263
+ requestId: string;
264
+ serialNo: number;
265
+ /** `wrist` ou `necklace`. Omita para não filtrar. */
266
+ bodyPart?: string;
267
+ /** 1..1000 (default do backend = 10). */
268
+ limit?: number;
269
+ startingAfter?: string;
270
+ endingBefore?: string;
271
+ /** Inclusivo, `YYYY-MM-DD HH:MM:SS` em hora local do dispositivo. */
272
+ startTime?: string;
273
+ /** Exclusivo, `YYYY-MM-DD HH:MM:SS` em hora local do dispositivo. */
274
+ endTime?: string;
275
+ }
237
276
  /**
238
277
  * Resposta do comando `sensorLog` (GET `user_cmd=log` do Mobile Lambda).
239
278
  * Envelope com `requestId` ecoado para correlação JS (suporta paginação
240
279
  * keyset concorrente) + `success`/`error`. No sucesso traz a página de logs
241
- * (ordem cronológica ASC, temperatura em °C, timestamps UTC naive) e os
242
- * cursores opacos. No erro, `error` carrega o código do contrato (ex.:
243
- * `CONFLICTING_CURSORS`, `invalid session`).
280
+ * (ordem cronológica ASC, temperatura em °C, timestamps naive em **hora local
281
+ * do dispositivo**) e os cursores opacos. Com range, `hasMore`/`hasPrevious`
282
+ * são calculados **dentro do período**, então `hasMore: false` significa
283
+ * "período completo". No erro, `error` carrega o código do contrato (ex.:
284
+ * `CONFLICTING_CURSORS`, `INVALID_TIME_RANGE`, `invalid session`) — código de
285
+ * diagnóstico, **nunca** texto para exibir ao usuário.
244
286
  */
245
287
  interface SensorLogPayload {
246
288
  requestId: string;
@@ -308,6 +350,63 @@ interface RequestPermissionPayload {
308
350
  * back não carrega dados; o cliente só reage voltando uma página interna.
309
351
  */
310
352
  type NavigationBackPayload = Record<string, never>;
353
+ /**
354
+ * Language state of the native app (native → client).
355
+ *
356
+ * Emitted on attach, again right after `handshake-ack` (the client may
357
+ * subscribe late), and on every language change in the app Settings — so a
358
+ * client already loaded re-renders without a restart.
359
+ *
360
+ * **The channel carries two independent sources on purpose.** The native app
361
+ * ships four languages (`en`, `pt`, `de`, `fr`) and collapses anything else to
362
+ * English. Your bundle has no such limit: it may well ship Spanish, Italian or
363
+ * Japanese. Were this channel to send only the resolved language, a device set
364
+ * to Spanish would reach you as `en` and you would render English while holding
365
+ * a Spanish bundle — the information would have been destroyed on our side
366
+ * before reaching you. So we send both raw values and let you decide.
367
+ *
368
+ * **The precedence rule is yours.** The one thing we guarantee is that no
369
+ * information is destroyed: `appLanguage` is `null` when — and only when — the
370
+ * user never made an explicit choice. It is never a fallback in disguise.
371
+ *
372
+ * | `deviceLocale` | `appLanguage` | a reasonable client decision |
373
+ * | --- | --- | --- |
374
+ * | `es-ES` | `null` | render **Spanish** — you have it, we don't. Our chrome stays English; your content is Spanish, which is better, not worse |
375
+ * | `es-ES` | `"pt"` | render **Portuguese** — the user said what they want, and an explicit choice outranks the device |
376
+ * | `pt-BR` | `null` | render Portuguese |
377
+ * | `ja-JP` | `"de"` | render German (explicit choice) — or Japanese if you'd rather honour the device. Your call, with both values in hand |
378
+ *
379
+ * Use `resolvedLanguage` when you'd rather stay visually consistent with the
380
+ * native chrome (splash, Settings, loading/error screens) than follow the
381
+ * device.
382
+ *
383
+ * **Detecting an older native host:** there is no version negotiation. A native
384
+ * app older than `0.14.0` never emits this key, so the event simply never
385
+ * arrives. Read `nativeVersion` from the handshake ack if you need to branch on
386
+ * it, and keep a default language for that case.
387
+ */
388
+ interface LanguagePayload {
389
+ /**
390
+ * Raw device locale as a BCP-47 tag, exactly as the OS reports it, region
391
+ * included when there is one (e.g. `"pt-BR"`, `"es-419"`, `"ja-JP"`). Never
392
+ * passed through the app's supported-language fallback.
393
+ */
394
+ deviceLocale: string;
395
+ /**
396
+ * Language explicitly chosen by the user in the app Settings, as a bare
397
+ * language code (`"en" | "pt" | "de" | "fr"`), or `null` when the user never
398
+ * chose one. Region is not carried here — the app stores only the language
399
+ * code for the user's choice.
400
+ */
401
+ appLanguage: string | null;
402
+ /**
403
+ * Language the native chrome is currently rendering, as a bare language code
404
+ * (`"en" | "pt" | "de" | "fr"`). This is the app's own resolution — the
405
+ * user's choice when there is one, otherwise the device language collapsed
406
+ * into the four supported languages.
407
+ */
408
+ resolvedLanguage: string;
409
+ }
311
410
  /**
312
411
  * Mapa discriminado de payloads por chave do catálogo. Usado por
313
412
  * `PayloadOf<K>` para inferência automática no `Bridge.on`.
@@ -327,6 +426,7 @@ interface KeyPayloads {
327
426
  dfu: Record<string, unknown>;
328
427
  systemStatus: SystemStatusPayload;
329
428
  navigationBack: NavigationBackPayload;
429
+ language: LanguagePayload;
330
430
  }
331
431
  /**
332
432
  * Payload tipado por chave. Distributivo:
@@ -462,6 +562,19 @@ interface CondorBridgeClient {
462
562
  * Abre Settings nativa. Equivalente a `send('openSettings')`.
463
563
  */
464
564
  openSettings(): void;
565
+ /**
566
+ * Pede UMA página de sensor logs e resolve com a resposta correlacionada
567
+ * pelo `requestId` — inclusive quando ela traz `success: false`, que é
568
+ * resposta e não falha de transporte. A Promise só **rejeita** por timeout.
569
+ *
570
+ * Dispensa o cliente de manter o mapa `requestId → resolve` à mão. Para
571
+ * varrer um período, encadeie as chamadas usando o `startingAfter` devolvido
572
+ * até `hasMore` virar `false`: os cursores são dependentes, então as páginas
573
+ * **não** podem ser buscadas em paralelo.
574
+ */
575
+ fetchSensorLog(query: SensorLogQueryPayload, opts?: {
576
+ timeoutMs?: number;
577
+ }): Promise<SensorLogPayload>;
465
578
  /**
466
579
  * Solicita UMA permissão via **diálogo nativo in-app** (não abre Settings).
467
580
  * Equivalente a `send('requestPermission', { permission })`. Use no botão
@@ -713,4 +826,4 @@ declare class BridgeShell {
713
826
  private postToClient;
714
827
  }
715
828
 
716
- export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type MockMessage, type MockState, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogPayload, type ShellPolicy, ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload };
829
+ export { type AutoRespondResponse, BRIDGE_KEYS, Bridge, BridgeClient, type BridgeClientHandshakeAck, type BridgeIncomingMessage, type BridgeKey, type BridgeOutgoingEvent, BridgeShell, type BridgeShellOptions, ClientToShellMessage, type CondorBridge, type CondorBridgeClient, type ConfirmationRequest, type DeleteMemoryAckPayload, type DevPanelOptions, type DeviceConnectionPayload, type DeviceLightSensorsPayload, type DeviceNotificationPayload, type HandshakeAck, type HandshakeOptions, type KeyPayloads, type LanguagePayload, type MockMessage, type MockState, type PayloadOf, type PingPongPayload, type RealTimeAckPayload, type RequestPermissionPayload, type SandboxVerifyResult, type SensorLogPayload, type SensorLogQueryPayload, type ShellPolicy, ShellToClientMessage, type SyncTimeAckPayload, type SystemStatusPayload, type UserDevicePayload };