refactor(example): update html and add IPFS status display
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
.container { max-width: 800px; margin: 0 auto; }
|
.container { max-width: 800px; margin: 0 auto; }
|
||||||
.card { background: #171717; border-radius: 8px; padding: 1rem; margin-bottom: 1rem; }
|
.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; }
|
.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.ok { background: #14532d; color: #4ade80; }
|
||||||
.status.err { background: #7f1d1d; color: #f87171; }
|
.status.err { background: #7f1d1d; color: #f87171; }
|
||||||
.status.wait { background: #422006; color: #fbbf24; }
|
.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; }
|
.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 { background: #374151; padding: 0.25rem 0.5rem; font-size: 0.7rem; margin: 0; }
|
||||||
.clear-btn:hover { background: #4b5563; }
|
.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; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -40,8 +43,11 @@
|
|||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<h2>Status</h2>
|
<h2>Status</h2>
|
||||||
<span id="status" class="status wait">Loading...</span>
|
<div class="status-row">
|
||||||
<button onclick="runAllTests()" style="margin-left: 1rem;">Run All Tests</button>
|
<span id="status" class="status wait">Loading...</span>
|
||||||
|
<span id="ipfs-status" class="status wait">IPFS...</span>
|
||||||
|
<button onclick="runAllTests()" style="margin-left: 0.5rem;">Run All Tests</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -56,23 +62,27 @@
|
|||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h2>generate(credential)</h2>
|
<h2>generate(credential) → IPFS</h2>
|
||||||
<button class="clear-btn" onclick="clearCardLog('generate')">Clear</button>
|
<button class="clear-btn" onclick="clearCardLog('generate')">Clear</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<button onclick="testGenerate()">Create with WebAuthn</button>
|
<button onclick="testGenerate()">Create with WebAuthn</button>
|
||||||
<button onclick="testGenerateMock()">Create with Mock</button>
|
<button onclick="testGenerateMock()">Create with Mock</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="cid-display" class="cid-display"></div>
|
||||||
<div id="log-generate" class="log"></div>
|
<div id="log-generate" class="log"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h2>load(database)</h2>
|
<h2>load(cid | database)</h2>
|
||||||
<button class="clear-btn" onclick="clearCardLog('load')">Clear</button>
|
<button class="clear-btn" onclick="clearCardLog('load')">Clear</button>
|
||||||
</div>
|
</div>
|
||||||
<input type="text" id="database" placeholder="Base64 database (auto-filled after generate)">
|
<input type="text" id="cid-input" placeholder="IPFS CID (bafybeig...) - auto-filled after generate">
|
||||||
<button onclick="testLoad()">Run</button>
|
<div class="actions">
|
||||||
|
<button onclick="testLoadFromCID()">Load from IPFS</button>
|
||||||
|
<button onclick="testLoadFromBytes()">Load from Bytes</button>
|
||||||
|
</div>
|
||||||
<div id="log-load" class="log"></div>
|
<div id="log-load" class="log"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -103,6 +113,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"helia": "https://esm.sh/helia@5",
|
||||||
|
"@helia/unixfs": "https://esm.sh/@helia/unixfs@4",
|
||||||
|
"multiformats/cid": "https://esm.sh/multiformats@13/cid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
<script type="module" src="./main.js"></script>
|
<script type="module" src="./main.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
133
example/main.js
133
example/main.js
@@ -1,6 +1,8 @@
|
|||||||
import { createEnclave } from '../dist/enclave.js';
|
import { createEnclave } from '../dist/enclave.js';
|
||||||
|
|
||||||
let enclave = null;
|
let enclave = null;
|
||||||
|
let helia = null;
|
||||||
|
let lastCID = null;
|
||||||
let lastDatabase = null;
|
let lastDatabase = null;
|
||||||
|
|
||||||
const LogLevel = { INFO: 'info', OK: 'ok', ERR: 'err', DATA: 'data' };
|
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 ?? '');
|
console.log(`[${time}] [${card}] ${message}`, data ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
function setStatus(ok, message) {
|
function setStatus(id, ok, message) {
|
||||||
const el = document.getElementById('status');
|
const el = document.getElementById(id);
|
||||||
el.textContent = message;
|
if (el) {
|
||||||
el.className = `status ${ok ? 'ok' : 'err'}`;
|
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) {
|
function arrayBufferToBase64(buffer) {
|
||||||
@@ -40,15 +57,6 @@ function arrayBufferToBase64(buffer) {
|
|||||||
return btoa(binary);
|
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() {
|
async function createWebAuthnCredential() {
|
||||||
const userId = crypto.getRandomValues(new Uint8Array(16));
|
const userId = crypto.getRandomValues(new Uint8Array(16));
|
||||||
const challenge = crypto.getRandomValues(new Uint8Array(32));
|
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() {
|
async function init() {
|
||||||
try {
|
try {
|
||||||
log('generate', LogLevel.INFO, 'Loading enclave.wasm...');
|
log('generate', LogLevel.INFO, 'Loading enclave.wasm...');
|
||||||
enclave = await createEnclave('./enclave.wasm', { debug: true });
|
|
||||||
setStatus(true, 'Ready');
|
await initHelia();
|
||||||
log('generate', LogLevel.OK, 'Plugin loaded');
|
|
||||||
|
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) {
|
} catch (err) {
|
||||||
setStatus(false, 'Failed');
|
setStatus('status', false, 'Failed');
|
||||||
log('generate', LogLevel.ERR, `Load failed: ${err?.message || String(err)}`);
|
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()...');
|
log('generate', LogLevel.INFO, 'Calling enclave.generate()...');
|
||||||
const result = await enclave.generate(credentialBase64);
|
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) {
|
if (result.database) {
|
||||||
lastDatabase = 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;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -178,12 +217,18 @@ window.testGenerateMock = async function() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await enclave.generate(mockCredential);
|
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) {
|
if (result.database) {
|
||||||
lastDatabase = 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;
|
return result;
|
||||||
} catch (err) {
|
} 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');
|
if (!enclave) return log('load', LogLevel.ERR, 'Plugin not loaded');
|
||||||
|
|
||||||
const b64 = document.getElementById('database').value;
|
const cid = document.getElementById('cid-input').value;
|
||||||
if (!b64) return log('load', LogLevel.ERR, 'No database - run generate first');
|
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 {
|
try {
|
||||||
const database = Uint8Array.from(atob(b64), c => c.charCodeAt(0));
|
const result = await enclave.load(cid);
|
||||||
const result = await enclave.load(database);
|
|
||||||
|
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) {
|
if (result.success) {
|
||||||
log('load', LogLevel.OK, `Loaded DID: ${result.did}`, result);
|
log('load', LogLevel.OK, `Loaded DID: ${result.did}`, result);
|
||||||
@@ -273,7 +346,7 @@ window.runAllTests = async function() {
|
|||||||
try {
|
try {
|
||||||
await testPing();
|
await testPing();
|
||||||
await testGenerateMock();
|
await testGenerateMock();
|
||||||
await testLoad();
|
await testLoadFromCID();
|
||||||
await testExec();
|
await testExec();
|
||||||
await testQuery();
|
await testQuery();
|
||||||
log('query', LogLevel.OK, '=== All tests passed ===');
|
log('query', LogLevel.OK, '=== All tests passed ===');
|
||||||
|
|||||||
Reference in New Issue
Block a user