Created
February 20, 2026 09:42
-
-
Save katagaki/a2487ea1c25c05a633d08154176052ca to your computer and use it in GitHub Desktop.
Export site data from one browser and import it into another.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| async function massiveExport() { | |
| console.log("Starting chunked export..."); | |
| const chunks = []; | |
| chunks.push('{\n"localStorage": ' + JSON.stringify({ ...localStorage }) + ',\n'); | |
| chunks.push('"sessionStorage": ' + JSON.stringify({ ...sessionStorage }) + ',\n'); | |
| chunks.push('"cookies": ' + JSON.stringify(document.cookie) + ',\n'); | |
| chunks.push('"indexedDB": {\n'); | |
| const dbs = await indexedDB.databases(); | |
| let dbFirst = true; | |
| for (const dbInfo of dbs) { | |
| if (!dbFirst) chunks.push(',\n'); | |
| dbFirst = false; | |
| const dbName = dbInfo.name; | |
| chunks.push(`"${dbName}": {"version": ${dbInfo.version}, "stores": {\n`); | |
| await new Promise((resolve) => { | |
| const req = indexedDB.open(dbName); | |
| req.onsuccess = async (e) => { | |
| const db = e.target.result; | |
| if (!db.objectStoreNames.length) { | |
| db.close(); | |
| return resolve(); | |
| } | |
| let storeFirst = true; | |
| for (const storeName of Array.from(db.objectStoreNames)) { | |
| if (!storeFirst) chunks.push(',\n'); | |
| storeFirst = false; | |
| const tx = db.transaction(storeName, 'readonly'); | |
| const store = tx.objectStore(storeName); | |
| chunks.push(`"${storeName}": {`); | |
| chunks.push(`"keyPath": ${JSON.stringify(store.keyPath)}, `); | |
| chunks.push(`"autoIncrement": ${store.autoIncrement}, `); | |
| chunks.push(`"records": [\n`); | |
| await new Promise((resolveStore) => { | |
| let recordFirst = true; | |
| const cursorReq = store.openCursor(); | |
| cursorReq.onsuccess = (ce) => { | |
| const cursor = ce.target.result; | |
| if (cursor) { | |
| if (!recordFirst) chunks.push(',\n'); | |
| recordFirst = false; | |
| try { | |
| chunks.push(JSON.stringify({ key: cursor.key, value: cursor.value })); | |
| } catch (err) { | |
| console.warn(`Skipped un-stringifiable record in ${storeName}`); | |
| } | |
| cursor.continue(); | |
| } else { | |
| resolveStore(); | |
| } | |
| }; | |
| cursorReq.onerror = resolveStore; | |
| }); | |
| chunks.push('\n]}\n'); | |
| } | |
| chunks.push('}}\n'); | |
| db.close(); | |
| resolve(); | |
| }; | |
| req.onerror = () => { | |
| chunks.push('}}\n'); | |
| resolve(); | |
| }; | |
| }); | |
| } | |
| chunks.push('}\n}'); | |
| console.log("Compiling file..."); | |
| const blob = new Blob(chunks, { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = `massive_export_${window.location.hostname}.json`; | |
| a.click(); | |
| console.log("Download triggered!"); | |
| } | |
| massiveExport(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| function createImporterUI() { | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = '.json'; | |
| input.style.cssText = ` | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| z-index: 999999; | |
| background: #000000; | |
| color: #ffffff; | |
| padding: 15px; | |
| border: 2px solid #ffffff; | |
| border-radius: 8px; | |
| font-family: monospace; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.5); | |
| cursor: pointer; | |
| `; | |
| document.body.appendChild(input); | |
| input.addEventListener('change', async (e) => { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| console.log("Reading file into memory..."); | |
| const text = await file.text(); | |
| console.log("Parsing data..."); | |
| const data = JSON.parse(text); | |
| if (data.localStorage) { | |
| Object.entries(data.localStorage).forEach(([k, v]) => localStorage.setItem(k, v)); | |
| } | |
| if (data.sessionStorage) { | |
| Object.entries(data.sessionStorage).forEach(([k, v]) => sessionStorage.setItem(k, v)); | |
| } | |
| if (data.cookies) { | |
| data.cookies.split(';').forEach(cookie => { | |
| if (cookie.trim()) { | |
| document.cookie = `${cookie.trim()}; path=/; max-age=31536000; SameSite=Lax`; | |
| } | |
| }); | |
| } | |
| console.log("Restoring IndexedDB databases..."); | |
| for (const dbName in data.indexedDB) { | |
| const dbData = data.indexedDB[dbName]; | |
| await new Promise(resolve => { | |
| const delReq = indexedDB.deleteDatabase(dbName); | |
| delReq.onsuccess = delReq.onerror = resolve; | |
| }); | |
| await new Promise((resolve) => { | |
| const req = indexedDB.open(dbName, dbData.version); | |
| req.onupgradeneeded = (e) => { | |
| const db = e.target.result; | |
| for (const storeName in dbData.stores) { | |
| const storeInfo = dbData.stores[storeName]; | |
| const options = { autoIncrement: storeInfo.autoIncrement }; | |
| if (storeInfo.keyPath !== null) options.keyPath = storeInfo.keyPath; | |
| db.createObjectStore(storeName, options); | |
| } | |
| }; | |
| req.onsuccess = (e) => { | |
| const db = e.target.result; | |
| if (!db.objectStoreNames.length) { | |
| db.close(); | |
| return resolve(); | |
| } | |
| const tx = db.transaction(db.objectStoreNames, 'readwrite'); | |
| for (const storeName in dbData.stores) { | |
| const store = tx.objectStore(storeName); | |
| const storeInfo = dbData.stores[storeName]; | |
| storeInfo.records.forEach(record => { | |
| if (storeInfo.keyPath !== null) { | |
| store.put(record.value); | |
| } else { | |
| store.put(record.value, record.key); | |
| } | |
| }); | |
| } | |
| tx.oncomplete = () => { | |
| console.log(`[IndexedDB] Restored: ${dbName}`); | |
| db.close(); | |
| resolve(); | |
| }; | |
| }; | |
| }); | |
| } | |
| console.log("Import completely successful. You can refresh the page now!"); | |
| input.style.background = "#28a745"; | |
| input.style.borderColor = "#28a745"; | |
| setTimeout(() => input.remove(), 3000); | |
| }); | |
| } | |
| createImporterUI(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment