// upload-blob over the UDS accepts a PATH as well as hex bytes.
//
// favela-bridge (lib/bsky.js uploadBlob) hands hpp a picture BY PATH — both ends share one machine, and
// hex-encoding it doubles the transfer and needs a buffer library the client may not have. Until this
// handler took `path`, every such upload failed with "bytes (hex) required" and the bridge logged
// blob-upload-failed and posted without the image.
//
// The socket is 0775 fleeky:fleeky: only the daemon's own user can connect, so reading a path grants no
// access that caller does not already have.
//
// Run: bare test/upload-blob-path-uds.test.mjs
import { serveDeviceUds } from '../lib/serve-device-uds.js'
import { Client } from 'hyper-svc'
import b4a from 'b4a'
import fs from 'bare-fs'
import process from 'bare-process'
import { join } from 'bare-path'
const assert = (cond, msg) => { if (!cond) { console.error('FAIL:', msg); process.exit(1) } console.log(' ok -', msg) }
const tmp = join(process.env.TMPDIR || '/tmp', 'hpp-upload-path-' + process.pid)
fs.mkdirSync(tmp, { recursive: true })
// The handler is only registered when a blob store is passed; a stub records exactly what it was given.
const stored = []
const blobs = {
async put (buf, mimeType) { stored.push({ buf, mimeType }); return { cid: 'bafystub' + stored.length, size: buf.length } }
}
const device = { state: () => ({ ok: true }), membersDiag: async () => ({}), did: 'did:plc:test' }
const sock = join(tmp, 'u.sock')
const served = await serveDeviceUds(device, { uds: sock, blobs })
const c = await Client.connectUds(sock)
const rejects = async (params) => { try { await c.call('upload-blob', params); return null } catch (e) { return e.message } }
console.log('# a path uploads the file\'s exact bytes')
{
const file = join(tmp, 'pic.png')
const bytes = b4a.from('not really a png, but exact bytes are the point')
fs.writeFileSync(file, bytes)
const r = await c.call('upload-blob', { path: file, mimeType: 'image/png' })
assert(r && r.cid === 'bafystub1', 'upload-blob by path returns the store\'s CID')
assert(r.size === bytes.length && r.mimeType === 'image/png', 'size and mimeType are what was stored')
assert(b4a.equals(stored[0].buf, bytes), 'the store received the file\'s bytes, unchanged')
}
console.log('\n# hex bytes still work — a remote client has no path to offer')
{
const bytes = b4a.from('bytes over the wire')
const r = await c.call('upload-blob', { bytes: b4a.toString(bytes, 'hex'), mimeType: 'image/jpeg' })
assert(r && r.size === bytes.length, 'upload-blob by bytes still stores them')
assert(b4a.equals(stored[1].buf, bytes), 'and they decode exactly')
}
console.log('\n# failures say what is wrong, and store nothing')
{
const before = stored.length
const missing = await rejects({ path: join(tmp, 'nope.png') })
assert(missing && /no file at/.test(missing), `a missing path is named (${missing})`)
const empty = join(tmp, 'empty.png'); fs.writeFileSync(empty, b4a.alloc(0))
const e2 = await rejects({ path: empty })
assert(e2 && /empty/.test(e2), `an empty file is refused (${e2})`)
const e3 = await rejects({})
assert(e3 && /bytes \(hex\) or path required/.test(e3), `neither bytes nor path is refused (${e3})`)
assert(stored.length === before, 'no failed call reached the store')
}
try { await c.close() } catch {}
try { await served.close() } catch {}
fs.rmSync(tmp, { recursive: true, force: true })
console.log('\nupload-blob path: all assertions passed')