From 9a407dac264bc8a327dc5fbe9e9b9f1cc476f1e6 Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Wed, 7 Jan 2026 20:22:22 -0500 Subject: [PATCH] refactor(example): update html and add IPFS status display --- example/index.html | 33 ++++++++--- example/main.js | 133 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 129 insertions(+), 37 deletions(-) diff --git a/example/index.html b/example/index.html index 24b6572..8254d7e 100644 --- a/example/index.html +++ b/example/index.html @@ -11,7 +11,7 @@ .container { max-width: 800px; margin: 0 auto; } .card { background: #171717; border-radius: 8px; padding: 1rem; margin-bottom: 1rem; } .card h2 { font-size: 0.875rem; color: #a3a3a3; margin-bottom: 0.5rem; font-weight: 500; } - .status { display: inline-block; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; } + .status { display: inline-block; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; margin-right: 0.5rem; } .status.ok { background: #14532d; color: #4ade80; } .status.err { background: #7f1d1d; color: #f87171; } .status.wait { background: #422006; color: #fbbf24; } @@ -32,6 +32,9 @@ .card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; } .clear-btn { background: #374151; padding: 0.25rem 0.5rem; font-size: 0.7rem; margin: 0; } .clear-btn:hover { background: #4b5563; } + .cid-display { font-family: monospace; font-size: 0.75rem; color: #60a5fa; background: #1e3a5f; padding: 0.25rem 0.5rem; border-radius: 4px; word-break: break-all; margin-top: 0.5rem; display: none; } + .cid-display.visible { display: block; } + .status-row { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; } @@ -40,8 +43,11 @@

Status

- Loading... - +
+ Loading... + IPFS... + +
@@ -56,23 +62,27 @@
-

generate(credential)

+

generate(credential) → IPFS

+
-

load(database)

+

load(cid | database)

- - + +
+ + +
@@ -103,6 +113,15 @@
+ diff --git a/example/main.js b/example/main.js index e92f33d..08b6078 100644 --- a/example/main.js +++ b/example/main.js @@ -1,6 +1,8 @@ import { createEnclave } from '../dist/enclave.js'; let enclave = null; +let helia = null; +let lastCID = null; let lastDatabase = null; const LogLevel = { INFO: 'info', OK: 'ok', ERR: 'err', DATA: 'data' }; @@ -25,10 +27,25 @@ function log(card, level, message, data = null) { console.log(`[${time}] [${card}] ${message}`, data ?? ''); } -function setStatus(ok, message) { - const el = document.getElementById('status'); - el.textContent = message; - el.className = `status ${ok ? 'ok' : 'err'}`; +function setStatus(id, ok, message) { + const el = document.getElementById(id); + if (el) { + el.textContent = message; + el.className = `status ${ok ? 'ok' : ok === false ? 'err' : 'wait'}`; + } +} + +function setCID(cid) { + const el = document.getElementById('cid-display'); + const input = document.getElementById('cid-input'); + if (el && cid) { + el.textContent = `CID: ${cid}`; + el.classList.add('visible'); + } + if (input && cid) { + input.value = cid; + } + lastCID = cid; } function arrayBufferToBase64(buffer) { @@ -40,15 +57,6 @@ function arrayBufferToBase64(buffer) { return btoa(binary); } -function base64ToArrayBuffer(base64) { - const binary = atob(base64); - const bytes = new Uint8Array(binary.length); - for (let i = 0; i < binary.length; i++) { - bytes[i] = binary.charCodeAt(i); - } - return bytes.buffer; -} - async function createWebAuthnCredential() { const userId = crypto.getRandomValues(new Uint8Array(16)); const challenge = crypto.getRandomValues(new Uint8Array(32)); @@ -92,14 +100,39 @@ async function createWebAuthnCredential() { }; } +async function initHelia() { + try { + setStatus('ipfs-status', null, 'IPFS...'); + log('generate', LogLevel.INFO, 'Initializing IPFS (Helia)...'); + + const { createHelia } = await import('helia'); + helia = await createHelia(); + + setStatus('ipfs-status', true, 'IPFS Ready'); + log('generate', LogLevel.OK, 'IPFS initialized'); + return helia; + } catch (err) { + setStatus('ipfs-status', false, 'IPFS Failed'); + log('generate', LogLevel.ERR, `IPFS init failed: ${err?.message || String(err)}`); + return null; + } +} + async function init() { try { log('generate', LogLevel.INFO, 'Loading enclave.wasm...'); - enclave = await createEnclave('./enclave.wasm', { debug: true }); - setStatus(true, 'Ready'); - log('generate', LogLevel.OK, 'Plugin loaded'); + + await initHelia(); + + enclave = await createEnclave('./enclave.wasm', { + debug: true, + ipfs: helia + }); + + setStatus('status', true, 'Ready'); + log('generate', LogLevel.OK, `Plugin loaded (IPFS: ${enclave.ipfsEnabled ? 'enabled' : 'disabled'})`); } catch (err) { - setStatus(false, 'Failed'); + setStatus('status', false, 'Failed'); log('generate', LogLevel.ERR, `Load failed: ${err?.message || String(err)}`); } } @@ -143,12 +176,18 @@ window.testGenerate = async function() { log('generate', LogLevel.INFO, 'Calling enclave.generate()...'); const result = await enclave.generate(credentialBase64); - log('generate', LogLevel.OK, `DID created: ${result.did}`, { did: result.did, dbSize: result.database?.length }); + + const logData = { did: result.did, dbSize: result.database?.length }; + if (result.cid) { + logData.cid = result.cid; + setCID(result.cid); + log('generate', LogLevel.OK, `Stored to IPFS: ${result.cid}`); + } + + log('generate', LogLevel.OK, `DID created: ${result.did}`, logData); if (result.database) { lastDatabase = result.database; - document.getElementById('database').value = btoa(String.fromCharCode(...result.database)); - log('generate', LogLevel.INFO, 'Database saved for load() test'); } return result; } catch (err) { @@ -178,12 +217,18 @@ window.testGenerateMock = async function() { try { const result = await enclave.generate(mockCredential); - log('generate', LogLevel.OK, `DID created: ${result.did}`, { did: result.did, dbSize: result.database?.length }); + + const logData = { did: result.did, dbSize: result.database?.length }; + if (result.cid) { + logData.cid = result.cid; + setCID(result.cid); + log('generate', LogLevel.OK, `Stored to IPFS: ${result.cid}`); + } + + log('generate', LogLevel.OK, `DID created: ${result.did}`, logData); if (result.database) { lastDatabase = result.database; - document.getElementById('database').value = btoa(String.fromCharCode(...result.database)); - log('generate', LogLevel.INFO, 'Database saved for load() test'); } return result; } catch (err) { @@ -192,17 +237,45 @@ window.testGenerateMock = async function() { } }; -window.testLoad = async function() { +window.testLoadFromCID = async function() { if (!enclave) return log('load', LogLevel.ERR, 'Plugin not loaded'); - const b64 = document.getElementById('database').value; - if (!b64) return log('load', LogLevel.ERR, 'No database - run generate first'); + const cid = document.getElementById('cid-input').value; + if (!cid) return log('load', LogLevel.ERR, 'No CID - run generate first'); - log('load', LogLevel.INFO, `Loading database (${b64.length} chars)...`); + if (!cid.startsWith('bafy')) { + log('load', LogLevel.ERR, 'Invalid CID format (should start with bafy...)'); + return; + } + + log('load', LogLevel.INFO, `Loading from IPFS: ${cid}`); try { - const database = Uint8Array.from(atob(b64), c => c.charCodeAt(0)); - const result = await enclave.load(database); + const result = await enclave.load(cid); + + if (result.success) { + log('load', LogLevel.OK, `Loaded DID: ${result.did}`, result); + } else { + log('load', LogLevel.ERR, result.error, result); + } + return result; + } catch (err) { + log('load', LogLevel.ERR, err?.message || String(err)); + throw err; + } +}; + +window.testLoadFromBytes = async function() { + if (!enclave) return log('load', LogLevel.ERR, 'Plugin not loaded'); + + if (!lastDatabase) { + return log('load', LogLevel.ERR, 'No database in memory - run generate first'); + } + + log('load', LogLevel.INFO, `Loading from bytes (${lastDatabase.length} bytes)...`); + + try { + const result = await enclave.load(new Uint8Array(lastDatabase)); if (result.success) { log('load', LogLevel.OK, `Loaded DID: ${result.did}`, result); @@ -273,7 +346,7 @@ window.runAllTests = async function() { try { await testPing(); await testGenerateMock(); - await testLoad(); + await testLoadFromCID(); await testExec(); await testQuery(); log('query', LogLevel.OK, '=== All tests passed ===');