// T5 build — publish a pear-install-shaped app into a VIRTUAL drive (no blob bytes).
// Layout required by pear-install/index.js:
// /package.json (name, version, bin)
// /by-arch/<platform>-<arch>/app/<bin>
import { createRequire } from 'module'
const require = createRequire(import.meta.url)
const fs = require('fs')
const path = require('path')
const process = require('process')
const crypto = require('hypercore-crypto')
const idEnc = require('hypercore-id-encoding')
const Corestore = require('corestore')
const Hyperdrive = require('hyperdrive')
const { ContentStore, build } = require('../lib/storage.cjs')
const { chunk } = await import('../../hyper-overlay/lib/chunker.js')
const ROOT = process.argv[2]
const APP = 't5app'
const HOST = process.platform + '-' + process.arch
const srcDir = ROOT + '/src'
const dir = ROOT + '/virtual'
// The "overlay": real files on disk that stay canonical.
fs.mkdirSync(srcDir, { recursive: true })
const binBytes = Buffer.concat([
Buffer.from('#!/bin/sh\necho t5app-ok\n'),
crypto.randomBytes(6 * 1024 * 1024) // payload so replication is real work
])
const pkgBytes = Buffer.from(
JSON.stringify({ name: APP, version: '1.0.0', bin: { [APP]: APP } }, null, 2)
)
fs.writeFileSync(path.join(srcDir, APP), binBytes)
fs.writeFileSync(path.join(srcDir, 'package.json'), pkgBytes)
const files = [
{ name: '/package.json', file: path.join(srcDir, 'package.json'), buf: pkgBytes },
{ name: '/by-arch/' + HOST + '/app/' + APP, file: path.join(srcDir, APP), buf: binBytes }
]
const content = new ContentStore()
for (const f of files) {
f.chunks = chunk(f.buf)
for (const c of f.chunks) content.add(c.hash, f.file, c.offset, c.length)
}
const Storage = build(content)
const store = new Corestore(new Storage(dir), { primaryKey: Buffer.alloc(32, 7), unsafe: true })
const drive = new Hyperdrive(store)
await drive.ready()
function write (ws, buf) {
return new Promise((resolve) => {
if (ws.write(buf)) return resolve()
ws.once('drain', resolve)
})
}
for (const f of files) {
const ws = drive.createWriteStream(f.name, { dedup: true })
for (const c of f.chunks) await write(ws, c.data)
await new Promise((resolve, reject) => {
ws.once('error', reject)
ws.once('close', resolve)
ws.end()
})
}
await drive.getBlobs()
const key = drive.key
const info = {
key: key.toString('hex'),
link: 'pear://' + idEnc.normalize(key),
blobsLength: drive.blobs.core.length,
binSha: crypto.hash(binBytes).toString('hex'),
binBytes: binBytes.length,
host: HOST
}
await drive.close()
await store.close()
content.close()
fs.writeFileSync(ROOT + '/drive.json', JSON.stringify(info, null, 2))
console.log(JSON.stringify(info, null, 2))