Created
June 18, 2026 21:31
-
-
Save AtkinsSJ/506a66edb8aa796b20ce24fd930f693b to your computer and use it in GitHub Desktop.
IndexedDB test page
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
| <!DOCTYPE html> | |
| <meta charset="utf-8"> | |
| <title>IndexedDB DevTools Manual Test</title> | |
| <style> | |
| :root { | |
| color-scheme: light dark; | |
| --bg: #f7f7f4; | |
| --panel: #ffffff; | |
| --panel-2: #f0f3f5; | |
| --text: #15181b; | |
| --muted: #5f6a72; | |
| --line: #cfd6dc; | |
| --accent: #0f6b63; | |
| --accent-2: #285f9f; | |
| --danger: #b3261e; | |
| --code: #eef3f6; | |
| } | |
| @media (prefers-color-scheme: dark) { | |
| :root { | |
| --bg: #151719; | |
| --panel: #202427; | |
| --panel-2: #292f33; | |
| --text: #edf1f2; | |
| --muted: #a5afb6; | |
| --line: #3d464d; | |
| --accent: #5db4a9; | |
| --accent-2: #78a8d8; | |
| --danger: #ff8f86; | |
| --code: #151b1f; | |
| } | |
| } | |
| * { | |
| box-sizing: border-box; | |
| } | |
| body { | |
| margin: 0; | |
| min-height: 100vh; | |
| background: var(--bg); | |
| color: var(--text); | |
| font: 14px/1.45 system-ui, sans-serif; | |
| } | |
| header { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 16px; | |
| padding: 14px 18px; | |
| border-bottom: 1px solid var(--line); | |
| background: var(--panel); | |
| } | |
| h1 { | |
| margin: 0; | |
| font-size: 18px; | |
| font-weight: 700; | |
| } | |
| main { | |
| display: grid; | |
| grid-template-columns: minmax(220px, 0.9fr) minmax(260px, 1fr) minmax(360px, 1.6fr); | |
| gap: 12px; | |
| padding: 12px; | |
| } | |
| section { | |
| min-width: 0; | |
| border: 1px solid var(--line); | |
| border-radius: 8px; | |
| background: var(--panel); | |
| overflow: hidden; | |
| } | |
| section > h2 { | |
| margin: 0; | |
| padding: 10px 12px; | |
| border-bottom: 1px solid var(--line); | |
| background: var(--panel-2); | |
| font-size: 14px; | |
| } | |
| .content { | |
| padding: 12px; | |
| } | |
| .toolbar, | |
| .row { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| flex-wrap: wrap; | |
| } | |
| .toolbar { | |
| margin-bottom: 10px; | |
| } | |
| label { | |
| display: grid; | |
| gap: 4px; | |
| min-width: 0; | |
| color: var(--muted); | |
| font-size: 12px; | |
| font-weight: 600; | |
| } | |
| input, | |
| textarea, | |
| select, | |
| button { | |
| color: var(--text); | |
| font: inherit; | |
| } | |
| input, | |
| textarea, | |
| select { | |
| width: 100%; | |
| min-width: 0; | |
| border: 1px solid var(--line); | |
| border-radius: 6px; | |
| background: var(--panel); | |
| padding: 7px 8px; | |
| } | |
| textarea { | |
| min-height: 140px; | |
| resize: vertical; | |
| font-family: ui-monospace, monospace; | |
| font-size: 13px; | |
| line-height: 1.35; | |
| tab-size: 2; | |
| } | |
| button { | |
| border: 1px solid var(--line); | |
| border-radius: 6px; | |
| background: var(--panel-2); | |
| padding: 7px 10px; | |
| cursor: pointer; | |
| } | |
| button.primary { | |
| border-color: var(--accent); | |
| background: var(--accent); | |
| color: white; | |
| } | |
| button.danger { | |
| border-color: var(--danger); | |
| color: var(--danger); | |
| } | |
| button:disabled { | |
| cursor: default; | |
| opacity: 0.55; | |
| } | |
| .list { | |
| display: grid; | |
| gap: 6px; | |
| margin-top: 10px; | |
| } | |
| .item { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 8px; | |
| width: 100%; | |
| border: 1px solid var(--line); | |
| border-radius: 6px; | |
| background: var(--panel); | |
| padding: 8px 10px; | |
| text-align: left; | |
| } | |
| .item.selected { | |
| border-color: var(--accent); | |
| box-shadow: inset 3px 0 0 var(--accent); | |
| } | |
| .item-name { | |
| overflow-wrap: anywhere; | |
| font-weight: 600; | |
| } | |
| .item-meta { | |
| color: var(--muted); | |
| font-size: 12px; | |
| white-space: nowrap; | |
| } | |
| .grid { | |
| display: grid; | |
| gap: 10px; | |
| } | |
| .two { | |
| grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); | |
| } | |
| .records { | |
| width: 100%; | |
| border-collapse: collapse; | |
| table-layout: fixed; | |
| } | |
| .records th, | |
| .records td { | |
| border-bottom: 1px solid var(--line); | |
| padding: 8px; | |
| text-align: left; | |
| vertical-align: top; | |
| } | |
| .records th { | |
| color: var(--muted); | |
| font-size: 12px; | |
| font-weight: 700; | |
| } | |
| .records pre, | |
| #log { | |
| overflow: auto; | |
| margin: 0; | |
| border-radius: 6px; | |
| background: var(--code); | |
| padding: 8px; | |
| font-family: ui-monospace, monospace; | |
| font-size: 12px; | |
| line-height: 1.35; | |
| white-space: pre-wrap; | |
| overflow-wrap: anywhere; | |
| } | |
| #status { | |
| color: var(--muted); | |
| font-size: 12px; | |
| } | |
| .split { | |
| display: grid; | |
| grid-template-columns: minmax(0, 1fr) auto; | |
| gap: 8px; | |
| align-items: end; | |
| } | |
| @media (max-width: 980px) { | |
| main { | |
| grid-template-columns: 1fr; | |
| } | |
| } | |
| </style> | |
| <header> | |
| <h1>IndexedDB DevTools Manual Test</h1> | |
| <div class="toolbar"> | |
| <button id="refreshButton">Refresh</button> | |
| <button id="seedButton" class="primary">Seed</button> | |
| <button id="clearLogButton">Clear Log</button> | |
| <span id="status"></span> | |
| </div> | |
| </header> | |
| <main> | |
| <section> | |
| <h2>Databases</h2> | |
| <div class="content"> | |
| <form id="databaseForm" class="split"> | |
| <label> | |
| Name | |
| <input id="databaseName" autocomplete="off" value="devtools-fixture"> | |
| </label> | |
| <button class="primary">Add</button> | |
| </form> | |
| <form id="renameDatabaseForm" class="split"> | |
| <label> | |
| Rename to | |
| <input id="renameDatabaseName" autocomplete="off"> | |
| </label> | |
| <button disabled>Rename</button> | |
| </form> | |
| <div class="toolbar"> | |
| <button id="deleteDatabaseButton" class="danger" disabled>Delete</button> | |
| <button id="reloadDatabaseButton" disabled>Reload</button> | |
| </div> | |
| <div id="databaseList" class="list"></div> | |
| </div> | |
| </section> | |
| <section> | |
| <h2>Tables</h2> | |
| <div class="content"> | |
| <form id="storeForm" class="split"> | |
| <label> | |
| Name | |
| <input id="storeName" autocomplete="off" value="people"> | |
| </label> | |
| <button class="primary" disabled>Add</button> | |
| </form> | |
| <form id="renameStoreForm" class="split"> | |
| <label> | |
| Rename to | |
| <input id="renameStoreName" autocomplete="off"> | |
| </label> | |
| <button disabled>Rename</button> | |
| </form> | |
| <div class="toolbar"> | |
| <button id="deleteStoreButton" class="danger" disabled>Delete</button> | |
| <button id="clearStoreButton" disabled>Clear</button> | |
| </div> | |
| <div id="storeList" class="list"></div> | |
| </div> | |
| </section> | |
| <section> | |
| <h2>Records</h2> | |
| <div class="content grid"> | |
| <form id="recordForm" class="grid"> | |
| <div class="two"> | |
| <label> | |
| Key JSON | |
| <input id="recordKey" autocomplete="off" value="1"> | |
| </label> | |
| <label> | |
| Mode | |
| <select id="recordMode"> | |
| <option value="put">Put</option> | |
| <option value="add">Add</option> | |
| </select> | |
| </label> | |
| </div> | |
| <label> | |
| Value JSON | |
| <textarea id="recordValue">{ | |
| "name": "Ada", | |
| "role": "Engineer" | |
| }</textarea> | |
| </label> | |
| <div class="toolbar"> | |
| <button class="primary" disabled>Save</button> | |
| <button id="deleteRecordButton" class="danger" type="button" disabled>Delete</button> | |
| <button id="reloadRecordsButton" type="button" disabled>Reload Records</button> | |
| </div> | |
| </form> | |
| <table class="records"> | |
| <thead> | |
| <tr> | |
| <th style="width: 35%">Key</th> | |
| <th>Value</th> | |
| </tr> | |
| </thead> | |
| <tbody id="recordRows"></tbody> | |
| </table> | |
| <pre id="log"></pre> | |
| </div> | |
| </section> | |
| </main> | |
| <script> | |
| const state = { | |
| databases: [], | |
| selectedDatabase: "", | |
| selectedStore: "", | |
| selectedRecordKey: undefined, | |
| pollTimer: 0, | |
| }; | |
| const elements = {}; | |
| for (const id of [ | |
| "status", "databaseForm", "databaseName", "renameDatabaseForm", "renameDatabaseName", | |
| "databaseList", "deleteDatabaseButton", "reloadDatabaseButton", "storeForm", | |
| "storeName", "renameStoreForm", "renameStoreName", "storeList", "deleteStoreButton", | |
| "clearStoreButton", "recordForm", "recordKey", "recordMode", "recordValue", | |
| "deleteRecordButton", "reloadRecordsButton", "recordRows", "log", | |
| "refreshButton", "seedButton", "clearLogButton" | |
| ]) { | |
| elements[id] = document.getElementById(id); | |
| } | |
| function log(message) { | |
| const time = new Date().toLocaleTimeString(); | |
| elements.log.textContent = `[${time}] ${message}\n${elements.log.textContent}`; | |
| } | |
| function setStatus(message) { | |
| elements.status.textContent = message; | |
| } | |
| function serialize(value) { | |
| return JSON.stringify(value, null, 2); | |
| } | |
| function parseJsonInput(input, label) { | |
| try { | |
| return JSON.parse(input.value); | |
| } catch (error) { | |
| input.focus(); | |
| throw new Error(`${label}: ${error.message}`); | |
| } | |
| } | |
| function transactionDone(transaction) { | |
| return new Promise((resolve, reject) => { | |
| transaction.oncomplete = () => resolve(); | |
| transaction.onabort = () => reject(transaction.error || new Error("IndexedDB transaction aborted")); | |
| transaction.onerror = () => reject(transaction.error || new Error("IndexedDB transaction failed")); | |
| }); | |
| } | |
| function stringListToArray(list) { | |
| const result = []; | |
| for (let i = 0; i < list.length; ++i) | |
| result.push(list.item(i)); | |
| return result; | |
| } | |
| function openDatabase(name, version, upgradeCallback) { | |
| return new Promise((resolve, reject) => { | |
| const request = version === undefined ? indexedDB.open(name) : indexedDB.open(name, version); | |
| request.onupgradeneeded = event => upgradeCallback && upgradeCallback(request.result, request.transaction, event.oldVersion, event.newVersion); | |
| request.onsuccess = () => resolve(request.result); | |
| request.onerror = () => reject(request.error || new Error("Could not open database")); | |
| request.onblocked = () => log(`Open blocked for ${name}`); | |
| }); | |
| } | |
| async function withDatabase(name, callback) { | |
| const db = await openDatabase(name); | |
| try { | |
| return await callback(db); | |
| } finally { | |
| db.close(); | |
| } | |
| } | |
| async function listDatabases() { | |
| if (indexedDB.databases) { | |
| const databases = await indexedDB.databases(); | |
| return databases.map(database => ({ | |
| name: database.name || "", | |
| version: database.version || 0, | |
| })).filter(database => database.name).sort((a, b) => a.name.localeCompare(b.name)); | |
| } | |
| return []; | |
| } | |
| async function createDatabase(name) { | |
| const db = await openDatabase(name, 1); | |
| db.close(); | |
| } | |
| async function deleteDatabase(name) { | |
| await new Promise((resolve, reject) => { | |
| const request = indexedDB.deleteDatabase(name); | |
| request.onsuccess = () => resolve(); | |
| request.onerror = () => reject(request.error || new Error("Could not delete database")); | |
| request.onblocked = () => log(`Delete blocked for ${name}`); | |
| }); | |
| } | |
| async function copyDatabase(sourceName, destinationName) { | |
| if (sourceName === destinationName) | |
| return; | |
| const source = state.databases.find(database => database.name === sourceName); | |
| if (!source) | |
| throw new Error(`Unknown database: ${sourceName}`); | |
| await deleteDatabase(destinationName); | |
| const destination = await openDatabase(destinationName, 1, database => { | |
| for (const store of source.stores) | |
| database.createObjectStore(store.name); | |
| }); | |
| destination.close(); | |
| for (const store of source.stores) { | |
| for (const record of store.records) | |
| await putRecord(destinationName, store.name, record.key, record.value, "put"); | |
| } | |
| } | |
| async function renameDatabase(sourceName, destinationName) { | |
| await copyDatabase(sourceName, destinationName); | |
| await deleteDatabase(sourceName); | |
| } | |
| async function createStore(databaseName, storeName) { | |
| await withDatabase(databaseName, async db => { | |
| if (db.objectStoreNames.contains(storeName)) | |
| return; | |
| const nextVersion = db.version + 1; | |
| db.close(); | |
| const upgraded = await openDatabase(databaseName, nextVersion, database => { | |
| if (!database.objectStoreNames.contains(storeName)) | |
| database.createObjectStore(storeName); | |
| }); | |
| upgraded.close(); | |
| }); | |
| } | |
| async function renameStore(databaseName, oldName, newName) { | |
| if (oldName === newName) | |
| return; | |
| await withDatabase(databaseName, async db => { | |
| if (!db.objectStoreNames.contains(oldName)) | |
| throw new Error(`Unknown table: ${oldName}`); | |
| if (db.objectStoreNames.contains(newName)) | |
| throw new Error(`Table already exists: ${newName}`); | |
| const nextVersion = db.version + 1; | |
| db.close(); | |
| const upgraded = await openDatabase(databaseName, nextVersion, (database, transaction) => { | |
| transaction.objectStore(oldName).name = newName; | |
| }); | |
| upgraded.close(); | |
| }); | |
| } | |
| async function deleteStore(databaseName, storeName) { | |
| await withDatabase(databaseName, async db => { | |
| if (!db.objectStoreNames.contains(storeName)) | |
| return; | |
| const nextVersion = db.version + 1; | |
| db.close(); | |
| const upgraded = await openDatabase(databaseName, nextVersion, database => { | |
| if (database.objectStoreNames.contains(storeName)) | |
| database.deleteObjectStore(storeName); | |
| }); | |
| upgraded.close(); | |
| }); | |
| } | |
| async function listStores(databaseName) { | |
| if (!databaseName) | |
| return []; | |
| return await withDatabase(databaseName, db => stringListToArray(db.objectStoreNames)); | |
| } | |
| async function putRecord(databaseName, storeName, key, value, mode) { | |
| await withDatabase(databaseName, async db => { | |
| const transaction = db.transaction(storeName, "readwrite"); | |
| const store = transaction.objectStore(storeName); | |
| if (mode === "add") | |
| store.add(value, key); | |
| else | |
| store.put(value, key); | |
| await transactionDone(transaction); | |
| }); | |
| } | |
| async function deleteRecord(databaseName, storeName, key) { | |
| await withDatabase(databaseName, async db => { | |
| const transaction = db.transaction(storeName, "readwrite"); | |
| transaction.objectStore(storeName).delete(key); | |
| await transactionDone(transaction); | |
| }); | |
| } | |
| async function clearStore(databaseName, storeName) { | |
| await withDatabase(databaseName, async db => { | |
| const transaction = db.transaction(storeName, "readwrite"); | |
| transaction.objectStore(storeName).clear(); | |
| await transactionDone(transaction); | |
| }); | |
| } | |
| async function listRecords(databaseName, storeName) { | |
| if (!databaseName || !storeName) | |
| return []; | |
| return await withDatabase(databaseName, db => { | |
| return new Promise((resolve, reject) => { | |
| const rows = []; | |
| const transaction = db.transaction(storeName, "readonly"); | |
| const request = transaction.objectStore(storeName).openCursor(); | |
| request.onsuccess = () => { | |
| const cursor = request.result; | |
| if (!cursor) { | |
| resolve(rows); | |
| return; | |
| } | |
| rows.push({ key: cursor.key, value: cursor.value }); | |
| cursor.continue(); | |
| }; | |
| request.onerror = () => reject(request.error || new Error("Could not read records")); | |
| transaction.onerror = () => reject(transaction.error || new Error("Read transaction failed")); | |
| }); | |
| }); | |
| } | |
| function selectDatabase(name) { | |
| state.selectedDatabase = name; | |
| state.selectedStore = ""; | |
| state.selectedRecordKey = undefined; | |
| elements.renameDatabaseName.value = name; | |
| elements.renameStoreName.value = ""; | |
| render(); | |
| refresh(); | |
| } | |
| function selectStore(name) { | |
| state.selectedStore = name; | |
| state.selectedRecordKey = undefined; | |
| elements.renameStoreName.value = name; | |
| render(); | |
| refresh(); | |
| } | |
| function selectRecord(key, value) { | |
| state.selectedRecordKey = key; | |
| elements.recordKey.value = serialize(key); | |
| elements.recordValue.value = serialize(value); | |
| render(); | |
| } | |
| function currentDatabase() { | |
| return state.databases.find(database => database.name === state.selectedDatabase); | |
| } | |
| function render() { | |
| const database = currentDatabase(); | |
| const databaseSelected = !!database; | |
| const storeSelected = databaseSelected && !!state.selectedStore; | |
| elements.deleteDatabaseButton.disabled = !databaseSelected; | |
| elements.reloadDatabaseButton.disabled = !databaseSelected; | |
| elements.renameDatabaseForm.querySelector("button").disabled = !databaseSelected; | |
| elements.storeForm.querySelector("button").disabled = !databaseSelected; | |
| elements.renameStoreForm.querySelector("button").disabled = !storeSelected; | |
| elements.deleteStoreButton.disabled = !storeSelected; | |
| elements.clearStoreButton.disabled = !storeSelected; | |
| elements.recordForm.querySelector("button").disabled = !storeSelected; | |
| elements.deleteRecordButton.disabled = !storeSelected || state.selectedRecordKey === undefined; | |
| elements.reloadRecordsButton.disabled = !storeSelected; | |
| elements.databaseList.replaceChildren(...state.databases.map(database => { | |
| const button = document.createElement("button"); | |
| button.type = "button"; | |
| button.className = `item${database.name === state.selectedDatabase ? " selected" : ""}`; | |
| button.onclick = () => selectDatabase(database.name); | |
| const name = document.createElement("span"); | |
| name.className = "item-name"; | |
| name.textContent = database.name; | |
| const meta = document.createElement("span"); | |
| meta.className = "item-meta"; | |
| meta.textContent = `v${database.version}`; | |
| button.append(name, meta); | |
| return button; | |
| })); | |
| const stores = database ? database.stores : []; | |
| elements.storeList.replaceChildren(...stores.map(store => { | |
| const button = document.createElement("button"); | |
| button.type = "button"; | |
| button.className = `item${store.name === state.selectedStore ? " selected" : ""}`; | |
| button.onclick = () => selectStore(store.name); | |
| const name = document.createElement("span"); | |
| name.className = "item-name"; | |
| name.textContent = store.name; | |
| const meta = document.createElement("span"); | |
| meta.className = "item-meta"; | |
| meta.textContent = `${store.records.length}`; | |
| button.append(name, meta); | |
| return button; | |
| })); | |
| const selectedStore = stores.find(store => store.name === state.selectedStore); | |
| const records = selectedStore ? selectedStore.records : []; | |
| elements.recordRows.replaceChildren(...records.map(record => { | |
| const row = document.createElement("tr"); | |
| row.onclick = () => selectRecord(record.key, record.value); | |
| const key = document.createElement("td"); | |
| const keyText = document.createElement("pre"); | |
| keyText.textContent = serialize(record.key); | |
| key.append(keyText); | |
| const value = document.createElement("td"); | |
| const valueText = document.createElement("pre"); | |
| valueText.textContent = serialize(record.value); | |
| value.append(valueText); | |
| row.append(key, value); | |
| return row; | |
| })); | |
| } | |
| async function refresh() { | |
| try { | |
| const selectedDatabase = state.selectedDatabase; | |
| const selectedStore = state.selectedStore; | |
| const databases = await listDatabases(); | |
| for (const database of databases) { | |
| database.stores = []; | |
| try { | |
| const stores = await listStores(database.name); | |
| for (const storeName of stores) { | |
| let records = []; | |
| try { | |
| records = await listRecords(database.name, storeName); | |
| } catch (error) { | |
| log(`Read ${database.name}/${storeName}: ${error.message}`); | |
| } | |
| database.stores.push({ name: storeName, records }); | |
| } | |
| } catch (error) { | |
| log(`Read ${database.name}: ${error.message}`); | |
| } | |
| } | |
| state.databases = databases; | |
| if (selectedDatabase && databases.some(database => database.name === selectedDatabase)) | |
| state.selectedDatabase = selectedDatabase; | |
| else | |
| state.selectedDatabase = databases[0] ? databases[0].name : ""; | |
| if (document.activeElement !== elements.renameDatabaseName) | |
| elements.renameDatabaseName.value = state.selectedDatabase; | |
| const database = currentDatabase(); | |
| const stores = database ? database.stores : []; | |
| if (selectedStore && stores.some(store => store.name === selectedStore)) | |
| state.selectedStore = selectedStore; | |
| else | |
| state.selectedStore = stores[0] ? stores[0].name : ""; | |
| if (document.activeElement !== elements.renameStoreName) | |
| elements.renameStoreName.value = state.selectedStore; | |
| setStatus(`${databases.length} database${databases.length === 1 ? "" : "s"}`); | |
| render(); | |
| } catch (error) { | |
| setStatus(error.message); | |
| log(error.message); | |
| } | |
| } | |
| async function runAction(label, callback) { | |
| try { | |
| await callback(); | |
| log(label); | |
| await refresh(); | |
| } catch (error) { | |
| log(`${label}: ${error.message}`); | |
| } | |
| } | |
| elements.databaseForm.onsubmit = event => { | |
| event.preventDefault(); | |
| const name = elements.databaseName.value.trim(); | |
| if (!name) | |
| return; | |
| runAction(`Created database ${name}`, async () => { | |
| await createDatabase(name); | |
| state.selectedDatabase = name; | |
| elements.renameDatabaseName.value = name; | |
| }); | |
| }; | |
| elements.renameDatabaseForm.onsubmit = event => { | |
| event.preventDefault(); | |
| const oldName = state.selectedDatabase; | |
| const newName = elements.renameDatabaseName.value.trim(); | |
| if (!oldName || !newName) | |
| return; | |
| runAction(`Renamed database ${oldName} to ${newName}`, async () => { | |
| await renameDatabase(oldName, newName); | |
| state.selectedDatabase = newName; | |
| }); | |
| }; | |
| elements.deleteDatabaseButton.onclick = () => { | |
| const name = state.selectedDatabase; | |
| if (!name) | |
| return; | |
| runAction(`Deleted database ${name}`, async () => { | |
| await deleteDatabase(name); | |
| state.selectedDatabase = ""; | |
| state.selectedStore = ""; | |
| state.selectedRecordKey = undefined; | |
| }); | |
| }; | |
| elements.reloadDatabaseButton.onclick = () => refresh(); | |
| elements.storeForm.onsubmit = event => { | |
| event.preventDefault(); | |
| const databaseName = state.selectedDatabase; | |
| const storeName = elements.storeName.value.trim(); | |
| if (!databaseName || !storeName) | |
| return; | |
| runAction(`Created table ${databaseName}/${storeName}`, async () => { | |
| await createStore(databaseName, storeName); | |
| state.selectedStore = storeName; | |
| elements.renameStoreName.value = storeName; | |
| }); | |
| }; | |
| elements.renameStoreForm.onsubmit = event => { | |
| event.preventDefault(); | |
| const databaseName = state.selectedDatabase; | |
| const oldName = state.selectedStore; | |
| const newName = elements.renameStoreName.value.trim(); | |
| if (!databaseName || !oldName || !newName) | |
| return; | |
| runAction(`Renamed table ${databaseName}/${oldName} to ${newName}`, async () => { | |
| await renameStore(databaseName, oldName, newName); | |
| state.selectedStore = newName; | |
| }); | |
| }; | |
| elements.deleteStoreButton.onclick = () => { | |
| const databaseName = state.selectedDatabase; | |
| const storeName = state.selectedStore; | |
| if (!databaseName || !storeName) | |
| return; | |
| runAction(`Deleted table ${databaseName}/${storeName}`, async () => { | |
| await deleteStore(databaseName, storeName); | |
| state.selectedStore = ""; | |
| state.selectedRecordKey = undefined; | |
| }); | |
| }; | |
| elements.clearStoreButton.onclick = () => { | |
| const databaseName = state.selectedDatabase; | |
| const storeName = state.selectedStore; | |
| if (!databaseName || !storeName) | |
| return; | |
| runAction(`Cleared table ${databaseName}/${storeName}`, () => clearStore(databaseName, storeName)); | |
| }; | |
| elements.recordForm.onsubmit = event => { | |
| event.preventDefault(); | |
| const databaseName = state.selectedDatabase; | |
| const storeName = state.selectedStore; | |
| if (!databaseName || !storeName) | |
| return; | |
| runAction(`Saved record ${databaseName}/${storeName}`, async () => { | |
| const key = parseJsonInput(elements.recordKey, "Key JSON"); | |
| const value = parseJsonInput(elements.recordValue, "Value JSON"); | |
| await putRecord(databaseName, storeName, key, value, elements.recordMode.value); | |
| state.selectedRecordKey = key; | |
| }); | |
| }; | |
| elements.deleteRecordButton.onclick = () => { | |
| const databaseName = state.selectedDatabase; | |
| const storeName = state.selectedStore; | |
| if (!databaseName || !storeName || state.selectedRecordKey === undefined) | |
| return; | |
| const key = state.selectedRecordKey; | |
| runAction(`Deleted record ${databaseName}/${storeName}`, async () => { | |
| await deleteRecord(databaseName, storeName, key); | |
| state.selectedRecordKey = undefined; | |
| }); | |
| }; | |
| elements.reloadRecordsButton.onclick = () => refresh(); | |
| elements.refreshButton.onclick = () => refresh(); | |
| elements.clearLogButton.onclick = () => elements.log.textContent = ""; | |
| elements.seedButton.onclick = () => { | |
| runAction("Seeded fixture data", async () => { | |
| await deleteDatabase("devtools-fixture"); | |
| await createDatabase("devtools-fixture"); | |
| await createStore("devtools-fixture", "people"); | |
| await createStore("devtools-fixture", "tasks"); | |
| await putRecord("devtools-fixture", "people", 1, { name: "Ada", role: "Engineer" }, "put"); | |
| await putRecord("devtools-fixture", "people", 2, { name: "Grace", role: "Debugger" }, "put"); | |
| await putRecord("devtools-fixture", "tasks", "alpha", { title: "Inspect storage", done: false }, "put"); | |
| state.selectedDatabase = "devtools-fixture"; | |
| state.selectedStore = "people"; | |
| }); | |
| }; | |
| refresh(); | |
| state.pollTimer = setInterval(refresh, 1000); | |
| window.onpagehide = () => clearInterval(state.pollTimer); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment