Bare worklet & bundling
This page covers how the client's embedded P2P node (a
Bare worklet via
react-native-bare-kit) is bundled and debugged. Companion to
Client build.
bare-pack essentials
- Bundle with
bare-pack --preset android(linked addons + all Android hosts), plus--builtins/--imports/--encoding base64 --out backend/app.bundle.js. The bundle is a build artifact — regenerate it whenever backend/SDK code changes, before the next APK build, or the app runs stale engine code. - The backend package needs its own
node_modules(it is not an npm workspace of the app) — runnpm installthere before bundling, and run the bundling itself from the main checkout so everyfile:dep resolves. --importsis a global import-override map — it remaps arbitrary specifiers. This is hownode:cryptois redirected to a sodium-backed WebCrypto shim. Don't put node core names in--builtinsfor the worklet: the 0.13.x worklet runtime has no builtins table, and anybuiltin:reference aborts the worklet at load.
Worklet runtime gotchas
- Missing globals: there is no
TextEncoder/TextDecoder/globalThis.crypto— polyfill them in a module that is the first import of your entrypoint (crypto libraries hitTextEncoderat module-evaluation time). - cwd is
/on Android (no HOME/env either) — relative store pathsENOENT. Derive the app sandbox (from/proc/self/cmdline→ package name →/data/data/<pkg>/files, for example) and create the dir — after a data clear it doesn't exist, and a bare probe strands you on a relative path. - Any uncaught exception SIGABRTs the whole app process. Install an
uncaughtExceptionguard that reports over IPC as the last resort, and treat every HTTP response your code writes as abortable (players cancel requests constantly) — an unhandled "write after close" stream error was exactly such a crash. - Debug loop: worklet errors reach logcat as
E <package>: Uncaught …with abare:/worklet.bundle/...stack, then the abort. App-level errors after boot come back over IPC as{type:'error'}.
Native addons
- The Holepunch stack's native addons ship Android prebuilds in their npm
packages. The bare-kit Gradle link task collects every addon reachable
from the app's package.json dependency graph into the APK (versioned
.sonames, so two majors of the same addon coexist). If an addon is only reachable through afile:dep, make sure that dep is declared from the app side. - To audit a bundle: decode the base64 export — the header is
«length»\n«JSON»— and compare itslinked:addon references against the.sofiles the APK ships.
Hyper-stack API traps (health probes, discovery)
hyperdrive.get(path)blocks indefinitely waiting for blob blocks when peers exist but data hasn't replicated — never use it in a poll/health probe. Usedrive.entry(path)(metadata-only, returns fast); the entry's beeseqbumps on every rewrite, which doubles as a "live playlist is advancing" signal.- A peer that announces a topic after you joined it is only
discovered on hyperswarm's slow periodic refresh (this can exceed a
minute). If you're waiting for a seeder to appear (a broadcaster
restart, or a CDN→P2P auto-return), keep the
PeerDiscoveryobject fromswarm.join()and call.refresh()on your poll interval — discovery then lands in seconds. - If the panel socket drops, clear your RPC binding on
closeso the next swarm connection re-arms it — otherwise every later call failsCHANNEL_CLOSEDforever.