Files
GlowTrack/packages/storage/poc/import-benchmark.html
Danilo Reyes 2f096d0265 feat: Add GlowTrack mood and habit wellbeing grid specifications
- Introduced export schema for JSON data structure.
- Created renderer contract detailing canvas/SVG rendering requirements.
- Defined IndexedDB storage schema and migration strategies.
- Documented data model including entities and relationships.
- Developed implementation plan outlining execution flow and project structure.
- Provided quickstart guide for development environment setup.
- Compiled research documentation on performance, accessibility, and theming.
- Established feature specification with user scenarios and functional requirements.
2025-09-18 00:36:13 -06:00

131 lines
6.3 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>GlowTrack IndexedDB Import Benchmark</title>
<style> body { font-family: system-ui, sans-serif; padding: 1rem; } textarea { width: 100%; height: 120px; } </style>
</head>
<body>
<h1>IndexedDB Import Benchmark</h1>
<button id="seed">Generate 3-year JSON</button>
<label>Chunk size: <input id="chunk" type="number" min="10" max="5000" value="500" /></label>
<button id="import">Import</button>
<button id="runMatrix">Run Matrix</button>
<button id="download">Download JSON Report</button>
<button id="wipe">Wipe DB</button>
<pre id="log"></pre>
<script>
const log = (...args) => (document.getElementById('log').textContent += args.join(' ') + '\n');
function openDB(version = 1) {
return new Promise((resolve, reject) => {
const req = indexedDB.open('glowtrack', version);
req.onupgradeneeded = (e) => {
const db = req.result;
if (!db.objectStoreNames.contains('settings')) db.createObjectStore('settings');
if (!db.objectStoreNames.contains('habits')) db.createObjectStore('habits', { keyPath: 'id' });
if (!db.objectStoreNames.contains('days')) db.createObjectStore('days', { keyPath: 'date' });
if (!db.objectStoreNames.contains('entries')) {
const s = db.createObjectStore('entries', { keyPath: 'id' });
s.createIndex('by_date', 'date');
s.createIndex('by_habit', 'habitId');
}
};
req.onsuccess = () => resolve(req.result);
req.onerror = () => reject(req.error);
});
}
function randomUUID(){return crypto.randomUUID?crypto.randomUUID():('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(c){var r=Math.random()*16|0,v=c=='x'?r:(r&0x3|0x8);return v.toString(16)}))}
function generateData(years=3){
const today = new Date();
const start = new Date(today);
start.setDate(start.getDate() - years*365);
const days = [];
const habits = [
{ id: 'h1', type: 'positive', label: 'Walk', defaultWeight: 1, archived: false },
{ id: 'h2', type: 'negative', label: 'Late snack', defaultWeight: 1, archived: false }
];
for(let d=new Date(start); d<=today; d.setDate(d.getDate()+1)){
const date = d.toISOString().slice(0,10);
const entries = [];
if (Math.random() < 0.6) entries.push({ id: randomUUID(), type:'positive', habitId:'h1', label:'Walk', weight:1, timestamp:new Date(d).toISOString(), date });
if (Math.random() < 0.3) entries.push({ id: randomUUID(), type:'negative', habitId:'h2', label:'Late snack', weight:1, timestamp:new Date(d).toISOString(), date });
const hue = Math.floor(Math.random()*360);
const intensity = Math.random();
days.push({ date, mood:{hue,intensity}, entries });
}
return { version: '1.0.0', app:{name:'GlowTrack', version:'0.0.0'}, exportedAt: new Date().toISOString(), data:{ settings:{}, habits, days } };
}
async function importJSON(db, json, chunk=Infinity){
const tx1 = db.transaction(['days'], 'readwrite');
const daysStore = tx1.objectStore('days');
const t0 = performance.now();
let i=0; const days = json.data.days;
while(i < days.length){
const slice = days.slice(i, Math.min(i+chunk, days.length));
for(const day of slice){ await new Promise((res, rej)=>{ const r = daysStore.put({date:day.date, mood:day.mood}); r.onsuccess=()=>res(); r.onerror=()=>rej(r.error); }); }
i += slice.length;
}
await new Promise((res, rej)=>{ tx1.oncomplete=()=>res(); tx1.onerror=()=>rej(tx1.error); });
const t1 = performance.now();
const tx2 = db.transaction(['entries'], 'readwrite');
const entriesStore = tx2.objectStore('entries');
i=0; const entries = json.data.days.flatMap(d=>d.entries);
while(i < entries.length){
const slice = entries.slice(i, Math.min(i+chunk, entries.length));
for(const e of slice){ await new Promise((res, rej)=>{ const r = entriesStore.put(e); r.onsuccess=()=>res(); r.onerror=()=>rej(r.error); }); }
i += slice.length;
}
await new Promise((res, rej)=>{ tx2.oncomplete=()=>res(); tx2.onerror=()=>rej(tx2.error); });
const t2 = performance.now();
return { daysMs: t1 - t0, entriesMs: t2 - t1 };
}
document.getElementById('seed').onclick = () => {
window.generated = generateData(3);
log('Generated days:', window.generated.data.days.length);
};
const report = { runs: [] };
document.getElementById('import').onclick = async () => {
if (!window.generated) { log('Generate first.'); return; }
const db = await openDB(1);
const chunk = +document.getElementById('chunk').value || Infinity;
const res = await importJSON(db, window.generated, chunk);
log('Imported. Chunk:', chunk, 'Days(ms):', res.daysMs.toFixed(0), 'Entries(ms):', res.entriesMs.toFixed(0));
report.runs.push({ date: new Date().toISOString(), chunk, ...res });
db.close();
};
document.getElementById('runMatrix').onclick = async () => {
if (!window.generated) { log('Generate first.'); return; }
const chunks = [100, 250, 500, 1000, 2000, 5000];
for(const ch of chunks){
await new Promise((r)=>{ const del = indexedDB.deleteDatabase('glowtrack'); del.onsuccess=r; del.onerror=r; });
const db2 = await openDB(1);
const res = await importJSON(db2, window.generated, ch);
report.runs.push({ date: new Date().toISOString(), chunk: ch, ...res });
log('Matrix:', ch, 'Days(ms):', res.daysMs.toFixed(0), 'Entries(ms):', res.entriesMs.toFixed(0));
db2.close();
}
alert('Matrix complete');
};
document.getElementById('download').onclick = () => {
const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = 'storage-benchmark.json'; a.click();
URL.revokeObjectURL(url);
};
document.getElementById('wipe').onclick = async () => {
await new Promise((res, rej)=>{ const r = indexedDB.deleteDatabase('glowtrack'); r.onsuccess=res; r.onerror=()=>rej(r.error); });
log('DB wiped');
};
</script>
</body>
</html>