/** * Host exit and ordered teardown of swarm/Hyperdrive/Corestore (Pear-safe ordering). * @module bare-os-lifecycle-manager */ /** * On Pear/Bare, `process.exit` is often missing or ineffective; use `Bare.exit` * (see holepunch `pear-prerelease`, `pear-terminal`, `bare-process`). * @param {number} code */ export function exitHostProcess(code) { const Bare = globalThis.Bare if (Bare && typeof Bare.exit === 'function') { /* Defer so Hyperdrive/swarm/native stdio teardown is not on the same stack as exit * (avoids malloc "pointer being freed was not allocated" under Pear on macOS). */ const run = () => Bare.exit(code) if (typeof setImmediate === 'function') setImmediate(run) else Promise.resolve().then(run) return } const p = globalThis.process if (p && typeof p.exit === 'function') p.exit(code) } /** * Stop replication before closing drives — closing Hyperdrive while Protomux streams * are still live can corrupt native heaps under Pear. * * Boot budget / bare-stdlib telemetry is recorded by the booter before initd (**`BARE_OS_BOOT_BARE_STDLIB_RESOLUTION_MS`**, **`bootReadyStateRef.subsystems.bareStdlib`**) and by the guest in **`/run/bare-os/boot-perf.json`** (**schema 5**); **`metrics_live.json`** **`bootBudgetTelemetry`** (**schema 2**) mirrors the same env keys for dashboards. * * @param {{ * hdmsController?: { deactivate(): Promise }, * personalDrive?: { close(): Promise }, * drive?: { close(): Promise } * }} disk * @param {{ destroy(): Promise }} swarm * @param {{ close(): Promise }} store */ export async function teardownBareOsBootResources(disk, swarm, store) { try { if (disk.hdmsController) await disk.hdmsController.deactivate() } catch (_) {} try { await swarm.destroy() } catch (_) {} try { if (disk.personalDrive) await disk.personalDrive.close() } catch (_) {} try { if (disk.drive) await disk.drive.close() } catch (_) {} try { await store.close() } catch (_) {} } /** * @returns {{ state: string, canTransitionTo: (next: string) => boolean, transition: (next: string) => boolean, snapshot: () => { state: string, atMs: number, transitions: Array<{ from: string, to: string, atMs: number }> } }} */ export function createBareOsLifecycleStateMachine() { /** @type {'opening'|'ready'|'suspending'|'resuming'|'closing'|'closed'} */ let state = 'opening' /** @type {Array<{ from: string, to: string, atMs: number }>} */ const transitions = [] const ok = (from, to) => (from === 'opening' && (to === 'ready' || to === 'closing')) || (from === 'ready' && (to === 'suspending' || to === 'closing')) || (from === 'suspending' && (to === 'resuming' || to === 'closing')) || (from === 'resuming' && (to === 'ready' || to === 'closing')) || (from === 'closing' && to === 'closed') return { get state() { return state }, canTransitionTo(next) { return ok(state, String(next || '')) }, transition(next) { const n = String(next || '') if (!ok(state, n)) return false transitions.push({ from: state, to: n, atMs: Date.now() }) state = /** @type {typeof state} */ (n) return true }, snapshot() { return { state, atMs: Date.now(), transitions: [...transitions] } } } } /** * @param {Array<{ name: string, run: () => Promise }>} hooks * @returns {Promise<{ schema: 1, ok: boolean, rows: Array<{ name: string, ok: boolean, error?: string, atMs: number }> }>} */ export async function runTransactionalLifecycleHooks(hooks) { /** @type {Array<{ name: string, ok: boolean, error?: string, atMs: number }>} */ const rows = [] let ok = true for (const h of Array.isArray(hooks) ? hooks : []) { const name = String(h && h.name ? h.name : 'hook') try { await Promise.resolve(h.run()) rows.push({ name, ok: true, atMs: Date.now() }) } catch (e) { ok = false rows.push({ name, ok: false, error: e && e.message ? e.message : String(e), atMs: Date.now() }) } } return { schema: 1, ok, rows } } /** * Best-effort settle delay before teardown. * @param {{ timeoutMs?: number }} [opts] */ export async function quiesceReplicationBeforeTeardown(opts = {}) { const t = Math.max(0, Math.min(10000, Number(opts.timeoutMs) || 200)) if (t <= 0) return await new Promise((resolve) => setTimeout(resolve, t)) }