ipfs-car
Version:
Convert files to content-addressed archives (.car) and back
68 lines (61 loc) • 2.04 kB
JavaScript
import fs from 'fs'
import { pipeline } from 'stream/promises'
import { CarIndexedReader } from '@ipld/car/indexed-reader'
import { recursive as exporter } from 'ipfs-unixfs-exporter'
import { validateBlock } from '@web3-storage/car-block-validator'
import { tmpPath } from './lib/tmp.js'
import { getRoots } from './lib/car.js'
/**
* @param {string} carPath
* @param {object} [opts]
* @param {string} [opts.root]
* @param {boolean} [opts.verify]
* @param {string} [opts.output]
*/
export default async function unpack (carPath, opts = {}) {
if (!carPath) {
carPath = tmpPath()
await pipeline(process.stdin, fs.createWriteStream(carPath))
}
const reader = await CarIndexedReader.fromFile(carPath)
const roots = await getRoots(reader, opts)
// @ts-expect-error blockstore not got `has` or `put` but they are unused
const entries = exporter(roots[0], {
async get (cid) {
const block = await reader.get(cid)
if (!block) {
console.error(`Missing block: ${cid}`)
process.exit(1)
}
/* c8 ignore next */
if (opts.verify || opts.verify == null) {
// @ts-expect-error
await validateBlock(block)
}
return block.bytes
}
})
for await (const entry of entries) {
if (!opts.output) {
if (entry.type === 'file' || entry.type === 'raw') {
await pipeline(entry.content, process.stdout)
break
}
console.error('Not a file - specify output path with --output')
process.exit(1)
}
let filePath = entry.path
const parts = entry.path.split('/')
parts[0] = opts.output
filePath = parts.join('/')
if (entry.type === 'file' || entry.type === 'raw') {
await pipeline(entry.content, fs.createWriteStream(filePath))
} else if (entry.type === 'directory') {
await fs.promises.mkdir(filePath, { recursive: true })
/* c8 ignore next 4 */
} else {
console.error(`Unsupported UnixFS type ${entry.type} for path: ${entry.path}`)
process.exit(1)
}
}
}