Skip to content

Instantly share code, notes, and snippets.

@katagaki
Created February 20, 2026 09:42
Show Gist options
  • Select an option

  • Save katagaki/a2487ea1c25c05a633d08154176052ca to your computer and use it in GitHub Desktop.

Select an option

Save katagaki/a2487ea1c25c05a633d08154176052ca to your computer and use it in GitHub Desktop.
Export site data from one browser and import it into another.
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();
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