UNPKG

@fireproof/database

Version:
1 lines 89.3 kB
{"version":3,"file":"fireproof.cjs","sources":["../../tsc/write-queue.js","../../tsc/loader-helpers.js","../../tsc/encrypted-block.js","../../tsc/crypto.js","../../tsc/encrypt-helpers.js","../../tsc/loader.js","../../tsc/transaction.js","../../tsc/crdt-helpers.js","../../tsc/crdt.js","../../tsc/database.js","../../tsc/indexer-helpers.js","../../tsc/index.js"],"sourcesContent":["export function writeQueue(worker, payload = Infinity) {\n const queue = [];\n let isProcessing = false;\n async function process() {\n if (isProcessing || queue.length === 0)\n return;\n isProcessing = true;\n const tasksToProcess = queue.splice(0, payload);\n const updates = tasksToProcess.map(item => item.task);\n const result = await worker(updates);\n tasksToProcess.forEach(task => task.resolve(result));\n isProcessing = false;\n void process();\n }\n return {\n push(task) {\n return new Promise((resolve) => {\n queue.push({ task, resolve });\n void process();\n });\n }\n };\n}\n","import { encode, decode } from 'multiformats/block';\nimport { sha256 as hasher } from 'multiformats/hashes/sha2';\nimport * as raw from 'multiformats/codecs/raw';\nimport * as CBW from '@ipld/car/buffer-writer';\nimport * as codec from '@ipld/dag-cbor';\nexport async function innerMakeCarFile(fp, t) {\n const { cid, bytes } = await encodeCarHeader(fp);\n await t.put(cid, bytes);\n return encodeCarFile(cid, t);\n}\nexport async function encodeCarFile(carHeaderBlockCid, t) {\n let size = 0;\n // console.log('encodeCarFile', carHeaderBlockCid.bytes.byteLength, { carHeaderBlockCid }, CBW.headerLength)\n const headerSize = CBW.headerLength({ roots: [carHeaderBlockCid] });\n size += headerSize;\n for (const { cid, bytes } of t.entries()) {\n size += CBW.blockLength({ cid, bytes });\n }\n const buffer = new Uint8Array(size);\n const writer = CBW.createWriter(buffer, { headerSize });\n writer.addRoot(carHeaderBlockCid);\n for (const { cid, bytes } of t.entries()) {\n writer.write({ cid, bytes });\n }\n writer.close();\n return await encode({ value: writer.bytes, hasher, codec: raw });\n}\nexport async function encodeCarHeader(fp) {\n return (await encode({\n value: { fp },\n hasher,\n codec\n }));\n}\nexport async function parseCarFile(reader) {\n const roots = await reader.getRoots();\n const header = await reader.get(roots[0]);\n if (!header)\n throw new Error('missing header block');\n const { value } = await decode({ bytes: header.bytes, hasher, codec });\n // @ts-ignore\n if (value && value.fp === undefined)\n throw new Error('missing fp');\n const { fp } = value;\n return fp;\n}\n","// from https://github.com/mikeal/encrypted-block\nimport { Crypto } from '@peculiar/webcrypto';\nimport { CID } from 'multiformats';\nimport { Buffer } from 'buffer';\n// const crypto = new Crypto()\nexport function getCrypto() {\n try {\n return new Crypto();\n }\n catch (e) {\n return null;\n }\n}\nconst crypto = getCrypto();\nexport function randomBytes(size) {\n const bytes = Buffer.allocUnsafe(size);\n if (size > 0) {\n crypto.getRandomValues(bytes);\n }\n return bytes;\n}\nconst enc32 = (value) => {\n value = +value;\n const buff = new Uint8Array(4);\n buff[3] = (value >>> 24);\n buff[2] = (value >>> 16);\n buff[1] = (value >>> 8);\n buff[0] = (value & 0xff);\n return buff;\n};\nconst readUInt32LE = (buffer) => {\n const offset = buffer.byteLength - 4;\n return ((buffer[offset]) |\n (buffer[offset + 1] << 8) |\n (buffer[offset + 2] << 16)) +\n (buffer[offset + 3] * 0x1000000);\n};\nconst concat = (buffers) => {\n const uint8Arrays = buffers.map(b => b instanceof ArrayBuffer ? new Uint8Array(b) : b);\n const totalLength = uint8Arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of uint8Arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n};\nconst encode = ({ iv, bytes }) => concat([iv, bytes]);\nconst decode = (bytes) => {\n const iv = bytes.subarray(0, 12);\n bytes = bytes.slice(12);\n return { iv, bytes };\n};\nconst code = 0x300000 + 1337;\nasync function subtleKey(key) {\n return await crypto.subtle.importKey('raw', // raw or jwk\n key, // raw data\n 'AES-GCM', false, // extractable\n ['encrypt', 'decrypt']);\n}\nconst decrypt = async ({ key, value }) => {\n let { bytes, iv } = value;\n const cryKey = await subtleKey(key);\n const deBytes = await crypto.subtle.decrypt({\n name: 'AES-GCM',\n iv,\n tagLength: 128\n }, cryKey, bytes);\n bytes = new Uint8Array(deBytes);\n const len = readUInt32LE(bytes.subarray(0, 4));\n const cid = CID.decode(bytes.subarray(4, 4 + len));\n bytes = bytes.subarray(4 + len);\n return { cid, bytes };\n};\nconst encrypt = async ({ key, cid, bytes }) => {\n const len = enc32(cid.bytes.byteLength);\n const iv = randomBytes(12);\n const msg = concat([len, cid.bytes, bytes]);\n try {\n const cryKey = await subtleKey(key);\n const deBytes = await crypto.subtle.encrypt({\n name: 'AES-GCM',\n iv,\n tagLength: 128\n }, cryKey, msg);\n bytes = new Uint8Array(deBytes);\n }\n catch (e) {\n console.log('e', e);\n throw e;\n }\n return { value: { bytes, iv } };\n};\nconst cryptoFn = (key) => {\n // @ts-ignore\n // eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n return { encrypt: opts => encrypt({ key, ...opts }), decrypt: opts => decrypt({ key, ...opts }) };\n};\nconst name = 'jchris@encrypted-block:aes-gcm';\nexport { encode, decode, code, name, encrypt, decrypt, cryptoFn as crypto };\n","import * as codec from './encrypted-block.js';\nimport * as dagcbor from '@ipld/dag-cbor';\n// @ts-ignore\nimport { create, load } from 'prolly-trees/cid-set';\nimport { CID } from 'multiformats';\nimport { encode, decode, create as mfCreate } from 'multiformats/block';\nconst encrypt = async function* ({ get, cids, hasher, key, cache, chunker, root }) {\n const set = new Set();\n let eroot;\n for (const cid of cids) {\n const unencrypted = await get(cid);\n if (!unencrypted)\n throw new Error('missing cid: ' + cid.toString());\n const encrypted = await codec.encrypt({ ...unencrypted, key });\n const block = await encode({ ...encrypted, codec, hasher });\n yield block;\n set.add(block.cid.toString());\n if (unencrypted.cid.equals(root))\n eroot = block.cid;\n }\n if (!eroot)\n throw new Error('cids does not include root');\n const list = [...set].map(s => CID.parse(s));\n let last;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n for await (const node of create({ list, get, cache, chunker, hasher, codec: dagcbor })) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const block = await node.block;\n yield block;\n last = block;\n }\n if (!last)\n throw new Error('missing last block');\n const head = [eroot, last.cid];\n const block = await encode({ value: head, codec: dagcbor, hasher });\n yield block;\n};\nconst decrypt = async function* ({ root, get, key, cache, chunker, hasher }) {\n const getWithDecode = async (cid) => get(cid).then(async (block) => {\n if (!block)\n return;\n const decoded = await decode({ ...block, codec: dagcbor, hasher });\n return decoded;\n });\n const getWithDecrypt = async (cid) => get(cid).then(async (block) => {\n if (!block)\n return;\n const decoded = await decode({ ...block, codec, hasher });\n return decoded;\n });\n const decodedRoot = await getWithDecode(root);\n if (!decodedRoot)\n throw new Error('missing root');\n if (!decodedRoot.bytes)\n throw new Error('missing bytes');\n const { value: [eroot, tree] } = decodedRoot;\n const rootBlock = await get(eroot);\n if (!rootBlock)\n throw new Error('missing root block');\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call\n const cidset = await load({ cid: tree, get: getWithDecode, cache, chunker, codec, hasher });\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n const { result: nodes } = await cidset.getAllEntries();\n const unwrap = async (eblock) => {\n if (!eblock)\n throw new Error('missing block');\n if (!eblock.value) {\n eblock = await decode({ ...eblock, codec, hasher });\n }\n const { bytes, cid } = await codec.decrypt({ ...eblock, key }).catch(e => {\n throw e;\n });\n const block = await mfCreate({ cid, bytes, hasher, codec });\n return block;\n };\n const promises = [];\n for (const { cid } of nodes) {\n if (!rootBlock.cid.equals(cid))\n promises.push(getWithDecrypt(cid).then(unwrap));\n }\n yield* promises;\n yield unwrap(rootBlock);\n};\nexport { encrypt, decrypt };\n","import { sha256 } from 'multiformats/hashes/sha2';\nimport { encrypt, decrypt } from './crypto';\nimport { Buffer } from 'buffer';\n// @ts-ignore\nimport { bf } from 'prolly-trees/utils';\n// @ts-ignore\nimport { nocache as cache } from 'prolly-trees/cache';\nimport { encodeCarHeader, encodeCarFile } from './loader-helpers'; // Import the existing function\nimport { MemoryBlockstore } from '@alanshaw/pail/block';\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call\nconst chunker = bf(30);\nexport async function encryptedMakeCarFile(key, fp, t) {\n const { cid, bytes } = await encodeCarHeader(fp);\n await t.put(cid, bytes);\n return encryptedEncodeCarFile(key, cid, t);\n}\nasync function encryptedEncodeCarFile(key, rootCid, t) {\n const encryptionKeyBuffer = Buffer.from(key, 'hex');\n const encryptionKey = encryptionKeyBuffer.buffer.slice(encryptionKeyBuffer.byteOffset, encryptionKeyBuffer.byteOffset + encryptionKeyBuffer.byteLength);\n const encryptedBlocks = new MemoryBlockstore();\n const cidsToEncrypt = [];\n for (const { cid } of t.entries()) {\n cidsToEncrypt.push(cid);\n }\n let last = null;\n for await (const block of encrypt({\n cids: cidsToEncrypt,\n get: t.get.bind(t),\n key: encryptionKey,\n hasher: sha256,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n chunker,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n cache,\n root: rootCid\n })) {\n await encryptedBlocks.put(block.cid, block.bytes);\n last = block;\n }\n if (!last)\n throw new Error('no blocks encrypted');\n const encryptedCar = await encodeCarFile(last.cid, encryptedBlocks);\n return encryptedCar;\n}\nexport async function decodeEncryptedCar(key, reader) {\n const roots = await reader.getRoots();\n const root = roots[0];\n return await decodeCarBlocks(root, reader.get.bind(reader), key);\n}\nasync function decodeCarBlocks(root, get, keyMaterial) {\n const decryptionKeyBuffer = Buffer.from(keyMaterial, 'hex');\n const decryptionKey = decryptionKeyBuffer.buffer.slice(decryptionKeyBuffer.byteOffset, decryptionKeyBuffer.byteOffset + decryptionKeyBuffer.byteLength);\n const decryptedBlocks = new MemoryBlockstore();\n let last = null;\n for await (const block of decrypt({\n root,\n get,\n key: decryptionKey,\n hasher: sha256,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n chunker,\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n cache\n })) {\n await decryptedBlocks.put(block.cid, block.bytes);\n last = block;\n }\n if (!last)\n throw new Error('no blocks decrypted');\n return { blocks: decryptedBlocks, root: last.cid };\n}\n","import { CarReader } from '@ipld/car';\nimport { innerMakeCarFile, parseCarFile } from './loader-helpers';\nimport { decodeEncryptedCar, encryptedMakeCarFile } from './encrypt-helpers';\nimport { getCrypto, randomBytes } from './encrypted-block';\nclass Loader {\n name;\n opts = {};\n headerStore;\n carStore;\n carLog = [];\n carReaders = new Map();\n ready;\n key;\n keyId;\n static defaultHeader;\n constructor(name, opts) {\n this.name = name;\n this.opts = opts || this.opts;\n this.ready = this.initializeStores().then(async () => {\n if (!this.headerStore || !this.carStore)\n throw new Error('stores not initialized');\n const meta = await this.headerStore.load('main');\n return await this.ingestCarHeadFromMeta(meta);\n });\n }\n async commit(t, done, compact = false) {\n await this.ready;\n const fp = this.makeCarHeader(done, this.carLog, compact);\n const { cid, bytes } = this.key ? await encryptedMakeCarFile(this.key, fp, t) : await innerMakeCarFile(fp, t);\n await this.carStore.save({ cid, bytes });\n if (compact) {\n for (const cid of this.carLog) {\n await this.carStore.remove(cid);\n }\n this.carLog.splice(0, this.carLog.length, cid);\n }\n else {\n this.carLog.push(cid);\n }\n await this.headerStore.save({ car: cid, key: this.key || null });\n return cid;\n }\n async getBlock(cid) {\n await this.ready;\n for (const [, reader] of [...this.carReaders]) {\n const block = await reader.get(cid);\n if (block) {\n return block;\n }\n }\n }\n async initializeStores() {\n const isBrowser = typeof window !== 'undefined';\n console.log('is browser?', isBrowser);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const module = isBrowser ? await require('./store-browser') : await require('./store-fs');\n if (module) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n this.headerStore = new module.HeaderStore(this.name);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access\n this.carStore = new module.CarStore(this.name);\n }\n else {\n throw new Error('Failed to initialize stores.');\n }\n }\n async loadCar(cid) {\n if (!this.headerStore || !this.carStore)\n throw new Error('stores not initialized');\n if (this.carReaders.has(cid.toString()))\n return this.carReaders.get(cid.toString());\n const car = await this.carStore.load(cid);\n if (!car)\n throw new Error(`missing car file ${cid.toString()}`);\n const reader = await this.ensureDecryptedReader(await CarReader.fromBytes(car.bytes));\n this.carReaders.set(cid.toString(), reader);\n this.carLog.push(cid);\n return reader;\n }\n async ensureDecryptedReader(reader) {\n if (!this.key)\n return reader;\n const { blocks, root } = await decodeEncryptedCar(this.key, reader);\n return {\n getRoots: () => [root],\n get: blocks.get.bind(blocks)\n };\n }\n async setKey(key) {\n if (this.key && this.key !== key)\n throw new Error('key already set');\n this.key = key;\n const crypto = getCrypto();\n if (!crypto)\n throw new Error('missing crypto module');\n const subtle = crypto.subtle;\n const encoder = new TextEncoder();\n const data = encoder.encode(key);\n const hashBuffer = await subtle.digest('SHA-256', data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n this.keyId = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n }\n async ingestCarHeadFromMeta(meta) {\n if (!this.headerStore || !this.carStore)\n throw new Error('stores not initialized');\n if (!meta) {\n // generate a random key\n if (!this.opts.public) {\n if (getCrypto()) {\n await this.setKey(randomBytes(32).toString('hex'));\n }\n else {\n console.warn('missing crypto module, using public mode');\n }\n }\n console.log('no meta, returning default header', this.name, this.keyId);\n return this.defaultHeader;\n }\n const { car: cid, key } = meta;\n console.log('ingesting car head from meta', { car: cid, key });\n if (key) {\n await this.setKey(key);\n }\n const reader = await this.loadCar(cid);\n this.carLog = [cid]; // this.carLog.push(cid)\n const carHeader = await parseCarFile(reader);\n await this.getMoreReaders(carHeader.cars);\n return carHeader;\n }\n async getMoreReaders(cids) {\n await Promise.all(cids.map(cid => this.loadCar(cid)));\n }\n}\nexport class DbLoader extends Loader {\n static defaultHeader = { cars: [], compact: [], head: [] };\n defaultHeader = DbLoader.defaultHeader;\n makeCarHeader({ head }, cars, compact = false) {\n return compact ? { head, cars: [], compact: cars } : { head, cars, compact: [] };\n }\n}\nexport class IdxLoader extends Loader {\n static defaultHeader = { cars: [], compact: [], indexes: new Map() };\n defaultHeader = IdxLoader.defaultHeader;\n makeCarHeader({ indexes }, cars, compact = false) {\n return compact ? { indexes, cars: [], compact: cars } : { indexes, cars, compact: [] };\n }\n}\n","import { MemoryBlockstore } from '@alanshaw/pail/block';\nimport { DbLoader, IdxLoader } from './loader';\nexport class Transaction extends MemoryBlockstore {\n parent;\n constructor(parent) {\n super();\n this.parent = parent;\n this.parent = parent;\n }\n async get(cid) {\n return this.parent.get(cid);\n }\n async superGet(cid) {\n return super.get(cid);\n }\n}\nclass FireproofBlockstore {\n ready;\n name = null;\n loader = null;\n opts = {};\n transactions = new Set();\n constructor(name, LoaderClass, opts) {\n this.opts = opts || this.opts;\n if (name) {\n this.name = name;\n this.loader = new LoaderClass(name, this.opts);\n this.ready = this.loader.ready;\n }\n else {\n this.ready = Promise.resolve(LoaderClass.defaultHeader);\n }\n }\n // eslint-disable-next-line @typescript-eslint/require-await\n async put() {\n throw new Error('use a transaction to put');\n }\n async get(cid) {\n for (const f of this.transactions) {\n const v = await f.superGet(cid);\n if (v)\n return v;\n }\n if (!this.loader)\n return;\n return await this.loader.getBlock(cid);\n }\n async commitCompaction(t, head) {\n this.transactions.clear();\n this.transactions.add(t);\n return await this.loader?.commit(t, { head }, true);\n }\n async *entries() {\n const seen = new Set();\n for (const t of this.transactions) {\n for await (const blk of t.entries()) {\n if (seen.has(blk.cid.toString()))\n continue;\n seen.add(blk.cid.toString());\n yield blk;\n }\n }\n }\n async executeTransaction(fn, commitHandler) {\n const t = new Transaction(this);\n this.transactions.add(t);\n const done = await fn(t);\n const { car, done: result } = await commitHandler(t, done);\n return car ? { ...result, car } : result;\n }\n}\nexport class IndexBlockstore extends FireproofBlockstore {\n constructor(name, opts) {\n super(name || null, IdxLoader, opts);\n }\n async transaction(fn, indexes) {\n return this.executeTransaction(fn, async (t, done) => {\n indexes.set(done.name, done);\n const car = await this.loader?.commit(t, { indexes });\n return { car, done };\n });\n }\n}\nexport class TransactionBlockstore extends FireproofBlockstore {\n constructor(name, opts) {\n // todo this will be a map of headers by branch name\n super(name || null, DbLoader, opts);\n }\n async transaction(fn) {\n return this.executeTransaction(fn, async (t, done) => {\n const car = await this.loader?.commit(t, done);\n return { car, done };\n });\n }\n}\n","import { encode, decode } from 'multiformats/block';\nimport { sha256 as hasher } from 'multiformats/hashes/sha2';\nimport * as codec from '@ipld/dag-cbor';\nimport { put, get, entries } from '@alanshaw/pail/crdt';\nimport { EventFetcher } from '@alanshaw/pail/clock';\nimport { Transaction } from './transaction';\nexport async function applyBulkUpdateToCrdt(tblocks, head, updates, options) {\n for (const update of updates) {\n const link = await makeLinkForDoc(tblocks, update);\n const result = await put(tblocks, head, update.key, link, options);\n for (const { cid, bytes } of [...result.additions, ...result.removals, result.event]) {\n tblocks.putSync(cid, bytes);\n }\n head = result.head;\n }\n return { head };\n}\nasync function makeLinkForDoc(blocks, update) {\n let value;\n if (update.del) {\n value = { del: true };\n }\n else {\n value = { doc: update.value };\n }\n const block = await encode({ value, hasher, codec });\n blocks.putSync(block.cid, block.bytes);\n return block.cid;\n}\nexport async function getValueFromCrdt(blocks, head, key) {\n const link = await get(blocks, head, key);\n if (!link)\n throw new Error(`Missing key ${key}`);\n return await getValueFromLink(blocks, link);\n}\nasync function getValueFromLink(blocks, link) {\n const block = await blocks.get(link);\n if (!block)\n throw new Error(`Missing block ${link.toString()}`);\n const { value } = (await decode({ bytes: block.bytes, hasher, codec }));\n return value;\n}\nexport async function clockChangesSince(blocks, head, since) {\n const eventsFetcher = new EventFetcher(blocks);\n const keys = new Set();\n const updates = await gatherUpdates(blocks, eventsFetcher, head, since, [], keys);\n return { result: updates.reverse(), head };\n}\nasync function gatherUpdates(blocks, eventsFetcher, head, since, updates = [], keys) {\n const sHead = head.map(l => l.toString());\n for (const link of since) {\n if (sHead.includes(link.toString())) {\n return updates;\n }\n }\n for (const link of head) {\n const { value: event } = await eventsFetcher.get(link);\n const { key, value } = event.data;\n if (keys.has(key))\n continue;\n keys.add(key);\n const docValue = await getValueFromLink(blocks, value);\n updates.push({ key, value: docValue.doc, del: docValue.del });\n if (event.parents) {\n updates = await gatherUpdates(blocks, eventsFetcher, event.parents, since, updates, keys);\n }\n }\n return updates;\n}\nexport async function doCompact(blocks, head) {\n const blockLog = new LoggingFetcher(blocks);\n const newBlocks = new Transaction(blocks);\n for await (const [, link] of entries(blockLog, head)) {\n const bl = await blocks.get(link);\n if (!bl)\n throw new Error('Missing block: ' + link.toString());\n await newBlocks.put(link, bl.bytes);\n }\n for (const cid of blockLog.cids) {\n const bl = await blocks.get(cid);\n if (!bl)\n throw new Error('Missing block: ' + cid.toString());\n await newBlocks.put(cid, bl.bytes);\n }\n await blocks.commitCompaction(newBlocks, head);\n}\nclass LoggingFetcher {\n blocks;\n cids = new Set();\n constructor(blocks) {\n this.blocks = blocks;\n }\n async get(cid) {\n this.cids.add(cid);\n return await this.blocks.get(cid);\n }\n}\n","import { TransactionBlockstore, IndexBlockstore } from './transaction';\nimport { clockChangesSince, applyBulkUpdateToCrdt, getValueFromCrdt, doCompact } from './crdt-helpers';\nexport class CRDT {\n name;\n opts = {};\n ready;\n blocks;\n indexBlocks;\n indexers = new Map();\n _head = [];\n constructor(name, opts) {\n this.name = name || null;\n this.opts = opts || this.opts;\n this.blocks = new TransactionBlockstore(name, this.opts);\n this.indexBlocks = new IndexBlockstore(name ? name + '.idx' : undefined, this.opts);\n this.ready = this.blocks.ready.then((header) => {\n // @ts-ignore\n if (header.indexes)\n throw new Error('cannot have indexes in crdt header');\n if (header.head) {\n this._head = header.head;\n } // todo multi head support here\n });\n }\n async bulk(updates, options) {\n await this.ready;\n const tResult = await this.blocks.transaction(async (tblocks) => {\n const { head } = await applyBulkUpdateToCrdt(tblocks, this._head, updates, options);\n this._head = head; // we need multi head support here if allowing calls to bulk in parallel\n return { head };\n });\n return tResult;\n }\n // async getAll(rootCache: any = null): Promise<{root: any, cids: CIDCounter, clockCIDs: CIDCounter, result: T[]}> {\n async get(key) {\n await this.ready;\n const result = await getValueFromCrdt(this.blocks, this._head, key);\n if (result.del)\n return null;\n return result;\n }\n async changes(since = []) {\n await this.ready;\n return await clockChangesSince(this.blocks, this._head, since);\n }\n async compact() {\n await this.ready;\n return await doCompact(this.blocks, this._head);\n }\n}\n","import { writeQueue } from './write-queue';\nimport { CRDT } from './crdt';\nexport class Database {\n static databases = new Map();\n name;\n opts = {};\n _listeners = new Set();\n _crdt;\n _writeQueue;\n constructor(name, opts) {\n this.name = name;\n this.opts = opts || this.opts;\n this._crdt = new CRDT(name, this.opts);\n this._writeQueue = writeQueue(async (updates) => {\n const r = await this._crdt.bulk(updates);\n await this._notify(updates);\n return r;\n });\n }\n async get(id) {\n const got = await this._crdt.get(id).catch(e => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n e.message = `Not found: ${id} - ` + e.message;\n throw e;\n });\n if (!got)\n throw new Error(`Not found: ${id}`);\n const { doc } = got;\n return { _id: id, ...doc };\n }\n async put(doc) {\n const { _id, ...value } = doc;\n const docId = _id || 'f' + Math.random().toString(36).slice(2); // todo uuid v7\n const result = await this._writeQueue.push({ key: docId, value });\n return { id: docId, clock: result?.head };\n }\n async del(id) {\n const result = await this._writeQueue.push({ key: id, del: true });\n return { id, clock: result?.head };\n }\n async changes(since = []) {\n const { result, head } = await this._crdt.changes(since);\n const rows = result.map(({ key, value }) => ({\n key, value: { _id: key, ...value }\n }));\n return { rows, clock: head };\n }\n subscribe(listener) {\n this._listeners.add(listener);\n return () => {\n this._listeners.delete(listener);\n };\n }\n async _notify(updates) {\n if (this._listeners.size) {\n const docs = updates.map(({ key, value }) => ({ _id: key, ...value }));\n for (const listener of this._listeners) {\n await listener(docs);\n }\n }\n }\n}\nexport function database(name, opts) {\n if (!Database.databases.has(name)) {\n Database.databases.set(name, new Database(name, opts));\n }\n return Database.databases.get(name);\n}\n","import { create } from 'multiformats/block';\nimport { sha256 as hasher } from 'multiformats/hashes/sha2';\nimport * as codec from '@ipld/dag-cbor';\n// @ts-ignore\nimport charwise from 'charwise';\n// @ts-ignore\nimport * as DbIndex from 'prolly-trees/db-index';\n// @ts-ignore\nimport { bf, simpleCompare } from 'prolly-trees/utils';\n// @ts-ignore\nimport { nocache as cache } from 'prolly-trees/cache';\nexport class IndexTree {\n cid = null;\n root = null;\n}\nconst refCompare = (aRef, bRef) => {\n if (Number.isNaN(aRef))\n return -1;\n if (Number.isNaN(bRef))\n throw new Error('ref may not be Infinity or NaN');\n if (aRef === Infinity)\n return 1;\n // if (!Number.isFinite(bRef)) throw new Error('ref may not be Infinity or NaN')\n // eslint-disable-next-line @typescript-eslint/no-unsafe-call\n return simpleCompare(aRef, bRef);\n};\nconst compare = (a, b) => {\n const [aKey, aRef] = a;\n const [bKey, bRef] = b;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call\n const comp = simpleCompare(aKey, bKey);\n if (comp !== 0)\n return comp;\n return refCompare(aRef, bRef);\n};\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call\nexport const byKeyOpts = { cache, chunker: bf(30), codec, hasher, compare };\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call\nexport const byIdOpts = { cache, chunker: bf(30), codec, hasher, compare: simpleCompare };\nexport function indexEntriesForChanges(changes, mapFn) {\n const indexEntries = [];\n changes.forEach(({ key: _id, value, del }) => {\n if (del || !value)\n return;\n let mapCalled = false;\n const mapReturn = mapFn({ _id, ...value }, (k, v) => {\n mapCalled = true;\n if (typeof k === 'undefined')\n return;\n indexEntries.push({\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n key: [charwise.encode(k), _id],\n value: v || null\n });\n });\n if (!mapCalled && mapReturn) {\n indexEntries.push({\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n key: [charwise.encode(mapReturn), _id],\n value: null\n });\n }\n });\n return indexEntries;\n}\nfunction makeProllyGetBlock(blocks) {\n return async (address) => {\n const block = await blocks.get(address);\n if (!block)\n throw new Error(`Missing block ${address.toString()}`);\n const { cid, bytes } = block;\n return create({ cid, bytes, hasher, codec });\n };\n}\nexport async function bulkIndex(tblocks, inIndex, indexEntries, opts) {\n if (!indexEntries.length)\n return inIndex;\n if (!inIndex.root) {\n if (!inIndex.cid) {\n let returnRootBlock = null;\n let returnNode = null;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n for await (const node of await DbIndex.create({ get: makeProllyGetBlock(tblocks), list: indexEntries, ...opts })) {\n const block = await node.block;\n await tblocks.put(block.cid, block.bytes);\n returnRootBlock = block;\n returnNode = node;\n }\n if (!returnNode || !returnRootBlock)\n throw new Error('failed to create index');\n return { root: returnNode, cid: returnRootBlock.cid };\n }\n else {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n inIndex.root = await DbIndex.load({ cid: inIndex.cid, get: makeProllyGetBlock(tblocks), ...opts });\n }\n }\n const { root, blocks: newBlocks } = await inIndex.root.bulk(indexEntries);\n if (root) {\n for await (const block of newBlocks) {\n await tblocks.put(block.cid, block.bytes);\n }\n return { root, cid: (await root.block).cid };\n }\n else {\n return { root: null, cid: null };\n }\n}\nexport async function loadIndex(tblocks, cid, opts) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n return await DbIndex.load({ cid, get: makeProllyGetBlock(tblocks), ...opts });\n}\nexport async function applyQuery(crdt, resp, query) {\n if (query.descending) {\n resp.result = resp.result.reverse();\n }\n if (query.limit) {\n resp.result = resp.result.slice(0, query.limit);\n }\n if (query.includeDocs) {\n resp.result = await Promise.all(resp.result.map(async (row) => {\n const val = await crdt.get(row.id);\n const doc = val ? { _id: row.id, ...val.doc } : null;\n return { ...row, doc };\n }));\n }\n return {\n rows: resp.result.map(row => {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n row.key = charwise.decode(row.key);\n return row;\n })\n };\n}\nexport function encodeRange(range) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n return range.map(key => charwise.encode(key));\n}\nexport function encodeKey(key) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n return charwise.encode(key);\n}\n","import { bulkIndex, indexEntriesForChanges, byIdOpts, byKeyOpts, IndexTree, applyQuery, encodeRange, encodeKey, loadIndex } from './indexer-helpers';\nexport function index({ _crdt }, name, mapFn, meta) {\n if (mapFn && meta)\n throw new Error('cannot provide both mapFn and meta');\n if (mapFn && mapFn.constructor.name !== 'Function')\n throw new Error('mapFn must be a function');\n if (_crdt.indexers.has(name)) {\n const idx = _crdt.indexers.get(name);\n idx.applyMapFn(name, mapFn, meta);\n }\n else {\n const idx = new Index(_crdt, name, mapFn, meta);\n _crdt.indexers.set(name, idx);\n }\n return _crdt.indexers.get(name);\n}\nexport class Index {\n blocks;\n crdt;\n name = null;\n mapFn = null;\n mapFnString = '';\n byKey = new IndexTree();\n byId = new IndexTree();\n indexHead = undefined;\n includeDocsDefault = false;\n initError = null;\n ready;\n constructor(crdt, name, mapFn, meta) {\n this.blocks = crdt.indexBlocks;\n this.crdt = crdt;\n this.applyMapFn(name, mapFn, meta);\n if (!(this.mapFnString || this.initError))\n throw new Error('missing mapFnString');\n this.ready = this.blocks.ready.then((header) => {\n // @ts-ignore\n if (header.head)\n throw new Error('cannot have head in idx header');\n if (header.indexes === undefined)\n throw new Error('missing indexes in idx header');\n for (const [name, idx] of Object.entries(header.indexes)) {\n index({ _crdt: crdt }, name, undefined, idx);\n }\n });\n }\n applyMapFn(name, mapFn, meta) {\n if (mapFn && meta)\n throw new Error('cannot provide both mapFn and meta');\n if (this.name && this.name !== name)\n throw new Error('cannot change name');\n this.name = name;\n try {\n if (meta) {\n // hydrating from header\n if (this.indexHead &&\n this.indexHead.map(c => c.toString()).join() !== meta.head.map(c => c.toString()).join()) {\n throw new Error('cannot apply meta to existing index');\n }\n this.byId.cid = meta.byId;\n this.byKey.cid = meta.byKey;\n this.indexHead = meta.head;\n if (this.mapFnString) {\n // we already initialized from application code\n if (this.mapFnString !== meta.map)\n throw new Error('cannot apply different mapFn meta');\n }\n else {\n // we are first\n this.mapFnString = meta.map;\n }\n }\n else {\n if (this.mapFn) {\n // we already initialized from application code\n if (mapFn) {\n if (this.mapFn.toString() !== mapFn.toString())\n throw new Error('cannot apply different mapFn app2');\n }\n }\n else {\n // application code is creating an index\n if (!mapFn) {\n mapFn = makeMapFnFromName(name);\n }\n if (this.mapFnString) {\n // we already loaded from a header\n if (this.mapFnString !== mapFn.toString())\n throw new Error('cannot apply different mapFn app');\n }\n else {\n // we are first\n this.mapFnString = mapFn.toString();\n }\n this.mapFn = mapFn;\n }\n }\n const matches = /=>\\s*(.*)/.test(this.mapFnString);\n this.includeDocsDefault = matches;\n }\n catch (e) {\n this.initError = e;\n }\n }\n async query(opts = {}) {\n await this._updateIndex();\n await this._hydrateIndex();\n if (!this.byKey.root)\n return await applyQuery(this.crdt, { result: [] }, opts);\n if (this.includeDocsDefault && opts.includeDocs === undefined)\n opts.includeDocs = true;\n if (opts.range) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n const { result, ...all } = await this.byKey.root.range(...encodeRange(opts.range));\n return await applyQuery(this.crdt, { result, ...all }, opts);\n }\n if (opts.key) {\n const encodedKey = encodeKey(opts.key);\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call\n return await applyQuery(this.crdt, await this.byKey.root.get(encodedKey), opts);\n }\n if (opts.prefix) {\n if (!Array.isArray(opts.prefix))\n opts.prefix = [opts.prefix];\n const start = [...opts.prefix, NaN];\n const end = [...opts.prefix, Infinity];\n const encodedR = encodeRange([start, end]);\n return await applyQuery(this.crdt, await this.byKey.root.range(...encodedR), opts);\n }\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call\n const { result, ...all } = await this.byKey.root.getAllEntries(); // funky return type\n return await applyQuery(this.crdt, {\n result: result.map(({ key: [k, id], value }) => ({ key: k, id, value })),\n ...all\n }, opts);\n }\n async _hydrateIndex() {\n if (this.byId.root && this.byKey.root)\n return;\n if (!this.byId.cid || !this.byKey.cid)\n return;\n this.byId.root = await loadIndex(this.blocks, this.byId.cid, byIdOpts);\n this.byKey.root = await loadIndex(this.blocks, this.byKey.cid, byKeyOpts);\n }\n async _updateIndex() {\n await this.ready;\n if (this.initError)\n throw this.initError;\n if (!this.mapFn)\n throw new Error('No map function defined');\n const { result, head } = await this.crdt.changes(this.indexHead);\n if (result.length === 0) {\n this.indexHead = head;\n return { byId: this.byId, byKey: this.byKey };\n }\n let staleKeyIndexEntries = [];\n let removeIdIndexEntries = [];\n if (this.byId.root) {\n const removeIds = result.map(({ key }) => key);\n const { result: oldChangeEntries } = await this.byId.root.getMany(removeIds);\n staleKeyIndexEntries = oldChangeEntries.map(key => ({ key, del: true }));\n removeIdIndexEntries = oldChangeEntries.map((key) => ({ key: key[1], del: true }));\n }\n const indexEntries = indexEntriesForChanges(result, this.mapFn); // use a getter to translate from string\n const byIdIndexEntries = indexEntries.map(({ key }) => ({ key: key[1], value: key }));\n const indexerMeta = new Map();\n for (const [name, indexer] of this.crdt.indexers) {\n if (indexer.indexHead) {\n indexerMeta.set(name, {\n byId: indexer.byId.cid,\n byKey: indexer.byKey.cid,\n head: indexer.indexHead,\n map: indexer.mapFnString,\n name: indexer.name\n });\n }\n }\n return await this.blocks.transaction(async (tblocks) => {\n this.byId = await bulkIndex(tblocks, this.byId, removeIdIndexEntries.concat(byIdIndexEntries), byIdOpts);\n this.byKey = await bulkIndex(tblocks, this.byKey, staleKeyIndexEntries.concat(indexEntries), byKeyOpts);\n this.indexHead = head;\n return { byId: this.byId.cid, byKey: this.byKey.cid, head, map: this.mapFnString, name: this.name };\n }, indexerMeta);\n }\n}\nfunction makeMapFnFromName(name) {\n return (doc) => {\n if (doc[name])\n return doc[name];\n };\n}\n"],"names":["CBW","encode","hasher","raw","codec","decode","Crypto","Buffer","decrypt","CID","encrypt","codec.encrypt","block","create","dagcbor","load","codec.decrypt","mfCreate","bf","MemoryBlockstore","sha256","cache","car","CarReader","put","get","EventFetcher","entries","simpleCompare","DbIndex"],"mappings":"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