diff --git a/static/client.js b/static/client.js index a6f4a6f..7ab0fa4 100644 --- a/static/client.js +++ b/static/client.js @@ -1,8 +1,12 @@ export { bootstrap }; -// 1. Record a start time. -// 2. Load the bootstrap document. -// 3. Load all the content_addresses from the bootstrap. -// 4. Record the end time. + +/** + * @typedef {Object} Reference + * @property {Array} dependents + * @property {string} path + * @property {string} object_id + * @property {string} content_address + */ async function load_bootstrap() { let response = await fetch("/api/v1/ref/all/username"); @@ -43,18 +47,14 @@ async function openDatabase(dbName, storeNames) { } /** + * Stores a reference object in the IndexedDB. * @param {IDBObjectStore} store * @param {Object} reference -*/ -function load_reference_paths(store, reference) { - let root_path = reference.path; - return new Promise(async (resolve, reject) => { - if (reference.dependents) { - for (var dep of reference.dependents) { - await load_reference_paths(store, dep); - } - } - + * @param {string} root_path + * @returns {Promise} + */ +function storeObject(store, reference, root_path) { + return new Promise((resolve, reject) => { const request = store.put(JSON.stringify(reference), root_path); request.onerror = (evt) => { reject(evt.target.error); @@ -66,16 +66,87 @@ function load_reference_paths(store, reference) { }); } +/** + * @param {IDBObjectStore} refStore + * @param {Object} reference + * @returns {Promise>} An array of references +*/ +function load_reference_paths(refStore, reference) { + return new Promise(async (resolve, reject) => { + let references = []; + references.push(reference); + if (reference.dependents) { + for (var dep of reference.dependents) { + references = references.concat(await load_reference_paths(refStore, dep)); + } + } + + await storeObject(refStore, reference, reference.path); + resolve(references); + }); +} + +/** + * @param {IDBDatabase} db + * @param {string} storeName + * @param {Array} references +*/ +async function load_objects_and_store(db, references, storeName) { + let objects = [] + for (var ref of references) { + /** @type {Response} */ + let response = await fetch("/api/v1/object/" + ref.content_address); + if (!response.ok) { + throw new Error("Network response was not ok: " + response.statusText); + } + let object = await response.text(); + objects.push({ id: ref.content_address, content: object }); + } + const objectTrxAndStore = await getStoreAndTransaction(db, storeName); + for (var obj of objects) { + await storeObject(objectTrxAndStore.store, obj.content, obj.id); + } + await new Promise((resolve, reject) => { + objectTrxAndStore.trx.oncomplete = () => resolve(); + objectTrxAndStore.trx.onerror = (event) => reject(event.target.error); + }); +} + +/** + * @param {string} storeName + * @param {IDBDatabase} db + * @returns {Promise<{trx: IDBTransaction, store: IDBObjectStore}>} The transaction and object store. + */ +async function getStoreAndTransaction(db, storeName) { + const transaction = db.transaction([storeName], "readwrite"); + return { trx: transaction, store: transaction.objectStore(storeName) }; +} + +/** +* @returns {Number} The number of milliseconds it took to bootstrap. +*/ async function bootstrap() { - const refStoreName = "references" - const objectStoreName = "objects" + const refStoreName = "references"; + const objectStoreName = "objects"; const databaseName = "MerkleStore"; const start = new Date().getTime(); let root = await load_bootstrap(); - let db = await openDatabase(databaseName, [refStoreName, objectStoreName]); - const transaction = db.transaction([refStoreName], "readwrite"); - const store = transaction.objectStore(refStoreName); - load_reference_paths(store, root); + const db = await openDatabase(databaseName, [refStoreName, objectStoreName]); + const refTrxAndStore = await getStoreAndTransaction(db, refStoreName); + + // Use a promise to wait for the transaction to complete + const transactionComplete = new Promise((resolve, reject) => { + refTrxAndStore.trx.oncomplete = () => resolve(); + refTrxAndStore.trx.onerror = (event) => reject(event.target.error); + }); + + let refs = await load_reference_paths(refTrxAndStore.store, root); + + // Wait for the transaction to complete + await transactionComplete; + + await load_objects_and_store(db, refs, objectStoreName); + var end = new Date().getTime(); return end - start; }