#!/usr/bin/env node
// Extract a bare-union-bundle flat .bundle file into a readable source tree.
// Format: <decimal-len>\n <json-manifest> <binary-tail>
// manifest.files = { "<path>": { offset, length, mode } } indexed into tail.
'use strict'
const fs = require('fs')
const path = require('path')
const bundlePath = process.argv[2]
const outDir = process.argv[3]
if (!bundlePath || !outDir) { console.error('usage: extract-bundle.cjs <bundle> <outdir>'); process.exit(1) }
const buf = fs.readFileSync(bundlePath)
const nl = buf.indexOf(10)
// String-aware brace counter to find the end of the JSON manifest.
let i = nl + 1
let depth = 0, inStr = false, esc = false, start = -1
for (; i < buf.length; i++) {
const c = buf[i]
if (start === -1) { if (c === 0x7b) { start = i; depth = 1 } continue }
if (inStr) {
if (esc) esc = false
else if (c === 0x5c) esc = true
else if (c === 0x22) inStr = false
} else {
if (c === 0x22) inStr = true
else if (c === 0x7b) depth++
else if (c === 0x7d) { depth--; if (depth === 0) { i++; break } }
}
}
const manifest = JSON.parse(buf.slice(start, i).toString('utf8'))
const tailStart = i
const files = manifest.files || {}
const paths = Object.keys(files)
console.log('manifest keys:', Object.keys(manifest).join(','))
console.log('files:', paths.length, '| tailStart:', tailStart, '| sample:', paths[0], JSON.stringify(files[paths[0]]))
let written = 0
for (const p of paths) {
const f = files[p]
if (typeof f.offset !== 'number' || typeof f.length !== 'number') continue
const data = buf.slice(tailStart + f.offset, tailStart + f.offset + f.length)
const dest = path.join(outDir, p)
fs.mkdirSync(path.dirname(dest), { recursive: true })
fs.writeFileSync(dest, data)
written++
}
console.log('wrote', written, 'files to', outDir)