--- id: cdp_save_storage_state_go_browser name: cdp_save_storage_state kind: function lang: go domain: browser purity: impure version: 1.1.0 tested: true tests: ["TestIsStorageAccessDenied", "TestCookieDomainMatchesHost"] test_file_path: "functions/browser/cdp_save_storage_state_test.go" description: "Captura cookies, localStorage y sessionStorage de la página activa y los serializa a un archivo JSON para restaurar la sesión sin repetir el login. Distingue 'origen sin storage accesible' (vacío legítimo) de un error real de evaluación, que aborta el guardado en vez de escribir una sesión incompleta en silencio." tags: [cdp, browser, storage, session, cookies, localStorage, auth, navegator] signature: "func CdpSaveStorageState(c *CDPConn, outPath string) error" uses_functions: - cdp_evaluate_go_browser uses_types: [] returns: [] returns_optional: false error_type: error_go_core imports: [] file_path: "functions/browser/cdp_save_storage_state.go" example: | conn, _ := CdpConnect(9222) defer CdpClose(conn) CdpNavigate(conn, "https://app.example.com") err := CdpSaveStorageState(conn, "/tmp/session.json") params: - name: c desc: "Conexión CDP activa apuntando a la pestaña con la sesión autenticada." - name: outPath desc: "Ruta del archivo JSON de salida donde se escribirá el estado (cookies + localStorage)." output: "nil si el archivo se escribió correctamente; error con contexto en caso de fallo de red, CDP o escritura." --- ## Ejemplo ```go conn, err := CdpConnect(9222) if err != nil { log.Fatal(err) } defer CdpClose(conn) // Navegar y autenticarse manualmente o con scraping CdpNavigate(conn, "https://app.example.com/dashboard") // Guardar estado de la sesión if err := CdpSaveStorageState(conn, "/tmp/session.json"); err != nil { log.Fatal(err) } // /tmp/session.json contiene cookies + localStorage listos para restaurar ``` ## Cuando usarla Tras completar un login en el browser (manual o automatizado), antes de cerrar la sesión o como paso final del script de autenticación. En la próxima ejecución, llama a `CdpLoadStorageState` en vez de repetir el flujo de login. ## Gotchas - **localStorage es por-origen**: solo captura el localStorage del origen actualmente cargado en la pestaña. Si necesitas preservar localStorage de múltiples dominios, guarda un estado por cada dominio navegado. - **Cookies globales del perfil**: `Network.getAllCookies` devuelve todas las cookies del perfil de Chrome, no solo las del origen activo. El JSON puede ser grande si el perfil tiene muchas cookies. - **Páginas especiales** (`about:blank`, `chrome://`, `data:`, extensiones): acceder a `window.localStorage` lanza `SecurityError`. La función lo detecta (`isStorageAccessDenied`) y devuelve `{}` legítimo, no error — el storage queda vacío en el JSON. **Pero** un error REAL (conexión caída, JS roto, JSON inválido) ahora SÍ se propaga y aborta el guardado: antes se tragaba en silencio y escribía una sesión incompleta que parecía válida. - **Permisos**: el archivo se escribe con `0644`; asegúrate de que el directorio de destino existe antes de llamar a la función. ## Capability growth log - v1.1.0 (2026-06-06) — `readWebStorage` distingue "origen sin storage accesible" (SecurityError → `{}`) de "error real de evaluación" (se propaga); `CdpSaveStorageState` aborta en error real en vez de guardar sesión incompleta en silencio; captura también sessionStorage; test del discriminador `isStorageAccessDenied` + del matcher `cookieDomainMatchesHost`.