carbites
Version:
Chunking for CAR files. Split a single CAR into multiple CARs.
65 lines (59 loc) • 2.03 kB
JavaScript
import { CarWriter } from '@ipld/car'
import { decode, code } from '@ipld/dag-cbor'
import { Block } from 'multiformats/block'
import { isRootNode } from './root-node.js'
import { SimpleCarJoiner } from '../simple/joiner.js'
/**
* @typedef {import('@ipld/car/api').BlockReader & import('@ipld/car/api').RootsReader} ICarReader
*/
export class RootedCarJoiner extends SimpleCarJoiner {
async * car () {
const reader = this._cars[0]
const rblk = await getRootedBlock(reader)
const roots = rblk.value[1]
const { writer, out } = CarWriter.create(roots)
const writeCar = async () => {
try {
for await (const b of filterBlock(rblk.cid, reader)) {
await writer.put(b)
}
for (const reader of this._cars.slice(1)) {
const rblk = await getRootedBlock(reader)
for await (const b of filterBlock(rblk.cid, reader)) {
await writer.put(b)
}
}
} catch (err) {
console.error(err) // TODO: how to forward this on?
} finally {
await writer.close()
}
}
writeCar()
yield * out
}
}
/**
* @param {ICarReader} reader
* @returns {Promise<import('multiformats/block').Block<import('./root-node').RootNode>>}
*/
async function getRootedBlock (reader) {
const roots = await reader.getRoots()
if (roots.length !== 1) throw new Error(`invalid number of roots: ${roots.length}`)
if (roots[0].code !== code) throw new Error(`invalid root block code: ${roots[0].code}`)
const block = await reader.get(roots[0])
if (!block) throw new Error(`missing root block: ${roots[0]}`)
const node = decode(block.bytes)
if (!isRootNode(node)) throw new Error(`invalid root node: ${block.cid}`)
return new Block({ cid: block.cid, bytes: block.bytes, value: node })
}
/**
* @param {import('multiformats').CID} cid
* @param {ICarReader} reader
*/
async function * filterBlock (cid, reader) {
for await (const b of reader.blocks()) {
if (b.cid.equals(cid)) continue
yield b
}
}