// In-memory ActorStorage and SharedStorage ports, enough to run a // PersonalDataServer without a database. Shared by the tests that exercise // repo mechanics: what a commit carries, and rebuilding the tree. export function createMemoryStorage() { /** @type {Map} */ const blocks = new Map(); /** @type {Map} */ const records = new Map(); /** @type {Array<{seq: number, cid: string, rev: string, prev: string|null}>} */ const commits = []; /** @type {Array<{seq: number, commit_cid: string, evt: Uint8Array}>} */ const events = []; /** @type {Record} */ const meta = {}; return { async getRecord(/** @type {string} */ uri) { return records.get(uri) || null; }, async putRecord( /** @type {string} */ uri, /** @type {string} */ cid, /** @type {string} */ _collection, /** @type {string} */ _rkey, /** @type {any} */ value, ) { records.set(uri, { cid, value }); }, async listRecords() { return { records: [], cursor: null }; }, async listAllRecords() { return [...records.entries()] .map(([uri, rec]) => { const parts = uri.replace('at://', '').split('/'); return { key: `${parts[1]}/${parts[2]}`, cid: rec.cid }; }) .sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0)); }, async deleteRecord(/** @type {string} */ uri) { records.delete(uri); }, async getBlock(/** @type {string} */ cid) { return blocks.get(cid) || null; }, async putBlock(/** @type {string} */ cid, /** @type {Uint8Array} */ data) { if (!blocks.has(cid)) blocks.set(cid, data); }, async deleteBlocks(/** @type {string[]} */ cids) { for (const cid of cids) blocks.delete(cid); }, async listBlocks( /** @type {string|null} */ cursor, /** @type {number} */ limit, ) { const page = [...blocks.keys()] .sort() .filter((cid) => !cursor || cid > cursor) .slice(0, limit) .map((cid) => ({ cid, data: /** @type {Uint8Array} */ (blocks.get(cid)), })); const last = page.length === limit ? page[limit - 1].cid : null; return { blocks: page, cursor: last }; }, async recordCidsAmong(/** @type {string[]} */ cids) { const held = new Set([...records.values()].map((r) => r.cid)); return cids.filter((cid) => held.has(cid)); }, async getBlocksCollected() { return meta.blocksCollected === true; }, async setBlocksCollected(/** @type {boolean} */ collected) { meta.blocksCollected = collected; }, async getLatestCommit() { return commits.length ? commits[commits.length - 1] : null; }, async putCommit( /** @type {number} */ seq, /** @type {string} */ cid, /** @type {string} */ rev, /** @type {string|null} */ prev, ) { commits.push({ seq, cid, rev, prev }); }, async getEvents() { return { events, cursor: null }; }, async putEvent( /** @type {number} */ seq, /** @type {string} */ _did, /** @type {string} */ commitCid, /** @type {Uint8Array} */ evt, ) { events.push({ seq, commit_cid: commitCid, evt }); }, async getLatestSeq() { return events.length ? events[events.length - 1].seq : 0; }, async getBlob() { return null; }, async putBlob() {}, async deleteBlob() {}, async listBlobs() { return { cids: [], cursor: null }; }, async getOrphanedBlobs() { return []; }, async linkBlobToRecord() {}, async unlinkBlobsFromRecord() {}, async getDid() { return meta.did ?? null; }, async setDid(/** @type {string} */ did) { meta.did = did; }, async getPrivateKey() { return meta.privateKey ?? null; }, async setPrivateKey(/** @type {any} */ key) { meta.privateKey = key; }, async getKemKey() { return meta.kemKey ?? null; }, async setKemKey(/** @type {any} */ key) { meta.kemKey = key; }, async getHandle() { return meta.handle ?? null; }, async setHandle(/** @type {string} */ handle) { meta.handle = handle; }, async getAccountStatus() { return meta.accountStatus ?? null; }, async setAccountStatus(/** @type {any} */ status) { meta.accountStatus = status; }, async getPreferences() { return []; }, async setPreferences() {}, _events: events, _blocks: blocks, }; } export function createMemorySharedStorage() { /** @type {Map} */ const actors = new Map(); return { async putActor( /** @type {string} */ did, /** @type {string} */ handle, /** @type {any} */ createdAt, ) { actors.set(did, { did, handle, createdAt }); }, async getActor(/** @type {string} */ did) { return actors.get(did) || null; }, async resolveHandle() { return null; }, async checkAndStoreDpopJti() { return true; }, async putOAuthRequest() {}, async getOAuthRequest() { return null; }, async deleteOAuthRequest() {}, async putOAuthToken() {}, async getOAuthToken() { return null; }, async deleteOAuthToken() {}, }; }