clone

a self-hosted sync node for lofi apps: jazz sync over iroh, ticket-gated, with no static ip or cloud needed

feat: deno compile packaging — one self-contained binary

Dami committed c2cfafb
deno task compile embeds the prebuilt matrix (--include); the loader
resolves repo paths via URL (the compiled vfs does not normalize '..'),
and addon staging falls back from the sibling .node shim to a
version-keyed OS-cache extraction (atomic tmp+rename) when the artifact
lives in the read-only embedded vfs — verified end to end: compiled
binary boots Jazz healthy AND mesh up from the cache-extracted addon,
clean SIGINT shutdown. jazz-napi loads fine under deno compile.
commit
c2cfafb747e91b16f9683f699c7e230583edf424
parent
8c7e9edbf680d5aba9f079947fa5a10d39ef85cf
author
Dami <dami.etoile@gmail.com>
5 files changed, +73 -23
M.gitignore3+-
Mdeno.json3+-
Dnative/iroh-js/prebuilt/aarch64-apple-darwin/libnumber0_iroh.nodebin
Msrc/native/addon.ts80+++++++++++++++++++++++-------
Msrc/native/loader.ts10+++-
M .gitignore +2 -1
M deno.json +2 -1
D native/iroh-js/prebuilt/aarch64-apple-darwin/libnumber0_iroh.node +0 -0

Binary file.

M src/native/addon.ts +62 -18
@@ -102,6 +102,59 @@ export interface IrohAddon {
102102 103103 const require = createRequire(import.meta.url);104104 105+/** Cache key for extracted artifacts — bump when the vendored tag or106+ * lofi_ext surface changes (see native/iroh-js/UPSTREAM.md). */107+export const IROH_JS_VERSION = "1.1.0-lofi.1";108+109+function osCacheDir(): string {110+ const home = Deno.env.get("HOME") ?? Deno.env.get("USERPROFILE") ?? ".";111+ switch (Deno.build.os) {112+ case "darwin":113+ return `${home}/Library/Caches`;114+ case "windows":115+ return Deno.env.get("LOCALAPPDATA") ?? `${home}/AppData/Local`;116+ default:117+ return Deno.env.get("XDG_CACHE_HOME") ?? `${home}/.cache`;118+ }119+}120+121+/** Give require() a real on-disk .node file. Prefer a sibling shim next to122+ * the artifact (dev flow: cargo output, refreshed on mtime). When the123+ * artifact lives in a read-only place — a `deno compile` binary's embedded124+ * vfs — extract to a version-keyed OS cache instead (atomic: tmp + rename,125+ * safe under concurrent launches). */126+function stageNodeArtifact(srcPath: string): string {127+ if (srcPath.endsWith(".node")) return srcPath;128+ const shim = srcPath.replace(/\.(dylib|so|dll)$/, ".node");129+ try {130+ const src = Deno.statSync(srcPath);131+ let stale = true;132+ try {133+ const dst = Deno.statSync(shim);134+ stale = (src.mtime?.getTime() ?? 1) > (dst.mtime?.getTime() ?? 0);135+ } catch {136+ // no shim yet137+ }138+ if (stale) Deno.copyFileSync(srcPath, shim);139+ return shim;140+ } catch {141+ // Sibling not writable (embedded vfs / read-only install): cache extract.142+ }143+ const dir = `${osCacheDir()}/lofi-node/iroh-js-${IROH_JS_VERSION}-${Deno.build.os}-${Deno.build.arch}`;144+ const target = `${dir}/iroh.node`;145+ try {146+ Deno.statSync(target);147+ return target; // version-keyed: presence means done148+ } catch {149+ // extract below150+ }151+ Deno.mkdirSync(dir, { recursive: true });152+ const tmp = `${target}.${Deno.pid}.tmp`;153+ Deno.writeFileSync(tmp, Deno.readFileSync(srcPath));154+ Deno.renameSync(tmp, target);155+ return target;156+}157+105158 /** Load + probe the addon. Throws MeshUnavailableError with a precise reason106159 * (no silent degradation — lofi's boot-gate ethos). */107160 export function loadIrohAddon(explicitPath?: string): IrohAddon {
@@ -116,24 +169,15 @@ export function loadIrohAddon(explicitPath?: string): IrohAddon {
116169 );117170 }118171 // require() only loads native addons from `.node` files — cargo emits119- // .dylib/.so/.dll, so maintain a sibling .node copy (refreshed on mtime).120- let loadPath = resolved.path;121- if (!loadPath.endsWith(".node")) {122- const shim = loadPath.replace(/\.(dylib|so|dll)$/, ".node");123- try {124- const src = Deno.statSync(loadPath);125- let stale = true;126- try {127- const dst = Deno.statSync(shim);128- stale = (src.mtime?.getTime() ?? 1) > (dst.mtime?.getTime() ?? 0);129- } catch {130- // no shim yet131- }132- if (stale) Deno.copyFileSync(loadPath, shim);133- loadPath = shim;134- } catch (e) {135- throw new MeshUnavailableError(`could not stage .node shim for ${loadPath}: ${(e as Error).message}`);136- }172+ // .dylib/.so/.dll, and a compiled binary's embedded artifacts aren't173+ // directly loadable at all; stage a real .node file either way.174+ let loadPath: string;175+ try {176+ loadPath = stageNodeArtifact(resolved.path);177+ } catch (e) {178+ throw new MeshUnavailableError(179+ `could not stage .node artifact for ${resolved.path}: ${(e as Error).message}`,180+ );137181 }138182 let addon: IrohAddon;139183 try {
M src/native/loader.ts +7 -3