// The Android half of spike 2 (org #36) — runs INSIDE a bare-kit Worklet on a real Android userspace.
//
// The Linux half (spike/worklet-storage-probe.mjs) already proved rocksdb works off the main thread.
// What that could NOT touch is everything Android-specific: Bionic instead of glibc, JNI, bare-kit's own
// worklet lifecycle, and Android's app-private filesystem. This is that half.
//
// Reports every step over BareKit.IPC rather than console, because a worklet's stdout goes nowhere useful
// — a failure has to name itself or it is just a blank screen.
//
// Build: bare-pack --preset android --defer bare-abstract-socket -o app.bundle spike/android-storage-probe.mjs
import '../lib/bare-globals.js'
const IPC = BareKit.IPC
const say = (s) => { try { IPC.write(Buffer.from(s + '\n')) } catch { /* nothing else to do */ } }
// The storage path is handed in by MainActivity (getFilesDir). There is no $HOME and no env on a phone,
// so favela's FAVELA_STORE_ROOT convention has to be replaced by exactly this argv handoff.
const storage = Bare.argv[Bare.argv.length - 1]
async function main () {
say('js: alive, bare ' + (Bare.version || '?'))
say('js: argv storage = ' + storage)
// 1. Can we even touch the filesystem? Android app-private dirs are the only writable place.
const fs = await import('bare-fs')
const path = await import('bare-path')
const dir = path.default.join(storage, 'spike-store')
fs.default.mkdirSync(dir, { recursive: true })
say('ok: bare-fs mkdir (addon loaded, Bionic)')
// 2. The addons that matter. Each import is a dlopen of an android-arm64 prebuild — the `linked:`
// model spike 1 produced. Named individually so a failure says WHICH one.
await import('sodium-native'); say('ok: sodium-native')
await import('quickbit-native'); say('ok: quickbit-native')
await import('udx-native'); say('ok: udx-native')
await import('rocksdb-native'); say('ok: rocksdb-native (dlopen)')
// 3. The real question: a Corestore that actually opens RocksDB on this filesystem.
const Corestore = (await import('corestore')).default
const Hyperbee = (await import('hyperbee')).default
const t0 = Date.now()
const store = new Corestore(dir)
await store.ready()
say('ok: Corestore.ready ' + (Date.now() - t0) + 'ms')
const bee = new Hyperbee(store.get({ name: 'probe' }), { keyEncoding: 'utf-8', valueEncoding: 'json' })
await bee.ready()
const b = bee.batch()
for (let i = 0; i < 500; i++) await b.put('k' + String(i).padStart(4, '0'), { i })
await b.flush()
let seen = 0
for await (const _ of bee.createReadStream()) seen++
say('ok: wrote+read 500 records, saw ' + seen)
// 4. What it costs on THIS filesystem — spike 4 measured 0.09 MB empty / ~68 MB live WAL on ext4.
// Android may use f2fs or ext4 with different preallocation behaviour, so measure rather than assume.
const usage = (d) => {
let app = 0; let alloc = 0
for (const e of fs.default.readdirSync(d)) {
const p = path.default.join(d, e)
const st = fs.default.statSync(p)
if (st.isDirectory()) { const u = usage(p); app += u.app; alloc += u.alloc; continue }
app += st.size
alloc += (st.blocks !== undefined ? st.blocks : Math.ceil(st.size / 512)) * 512
}
return { app, alloc }
}
const u = usage(dir)
say(`ok: store while OPEN apparent=${(u.app / 1048576).toFixed(2)}MB allocated=${(u.alloc / 1048576).toFixed(2)}MB`)
await store.close()
const c = usage(dir)
say(`ok: after close apparent=${(c.app / 1048576).toFixed(2)}MB allocated=${(c.alloc / 1048576).toFixed(2)}MB`)
// 5. Swarm construction — not a connection, just proof udx/sodium link up under Bionic. Connecting
// is spike 5 and needs a network, not a device.
const Hyperswarm = (await import('hyperswarm')).default
const sw = new Hyperswarm()
say('ok: Hyperswarm constructed (udx+sodium linked)')
await sw.destroy()
say('DONE: all checks passed')
}
main().catch((e) => say('FAIL: ' + ((e && e.stack) || e)))