Require only bare-* modules under Bare for Holesail control
CI / test (push) Successful in 10m3s

Stop falling back to Node builtin names (process/crypto/…) which
bare-module cannot resolve. Detect Bare via globalThis.Bare and drop
bare-process (use global pid instead).
This commit is contained in:
2026-07-10 23:22:10 -04:00
parent aa48876d75
commit f732cab26c
+43 -36
View File
@@ -1,40 +1,40 @@
/** /**
* Bare/Node control plane for local Holesail client sessions. * Bare/Node control plane for local Holesail client sessions.
* Runs in the pear-electron Bare entry (index.js), where require.addon works. * Runs in the pear-electron Bare entry (index.js), where require.addon works.
* The UI renderer talks to this over http://127.0.0.1:<port> (never loads bare-tcp itself). * The UI renderer talks to this over http://127.0.0.1:<port>.
* *
* Uses Bare modules under Pear (via package.json "imports" bare conditions and * Under Bare: require ONLY bare-* packages (never Node builtin names like
* which-runtime dual-load). Do not require bare Node built-ins alone on Bare. * 'process' / 'crypto' — bare-module has no Node core builtins).
* Under Node: require Node core (bare-* native addons need Bare's require.addon).
*/ */
'use strict' 'use strict'
const { isBare } = require('which-runtime') const isBare = typeof globalThis.Bare !== 'undefined'
// Prefer package.json imports maps; dual-load as fallback for older resolvers. // Strict split — no cross-fallback (Bare cannot load 'process', Node cannot load bare-abort addons).
function load (bareName, nodeName) { const http = isBare ? require('bare-http1') : require('http')
if (isBare) { const fs = isBare ? require('bare-fs') : require('fs')
try { const path = isBare ? require('bare-path') : require('path')
return require(bareName) const crypto = isBare ? require('bare-crypto') : require('crypto')
} catch { const net = isBare ? require('bare-net') : require('net')
return require(nodeName)
}
}
try {
return require(nodeName)
} catch {
return require(bareName)
}
}
const http = load('bare-http1', 'http')
const fs = load('bare-fs', 'fs')
const path = load('bare-path', 'path')
const crypto = load('bare-crypto', 'crypto')
const net = load('bare-net', 'net')
const process = load('bare-process', 'process')
const b4a = require('b4a') const b4a = require('b4a')
const Holesail = require('holesail') const Holesail = require('holesail')
// Avoid bare-process / Node 'process' entirely
function getPid() {
try {
if (typeof globalThis.process?.pid === 'number') return globalThis.process.pid
} catch {
// ignore
}
try {
if (typeof globalThis.Bare?.pid === 'number') return globalThis.Bare.pid
} catch {
// ignore
}
return 0
}
/** @type {Map<string, { instance: any, localPort: number, host: string, url: string, info: object }>} */ /** @type {Map<string, { instance: any, localPort: number, host: string, url: string, info: object }>} */
const localClients = new Map() const localClients = new Map()
@@ -74,7 +74,6 @@ async function connect(urlOrKey, opts = {}) {
log: false, log: false,
}) })
// Race ready against a timeout so the UI never hangs forever
const readyMs = Number(opts.readyTimeoutMs) || 45000 const readyMs = Number(opts.readyTimeoutMs) || 45000
await Promise.race([ await Promise.race([
instance.ready(), instance.ready(),
@@ -159,6 +158,12 @@ function sendJson(res, status, body) {
res.end(raw) res.end(raw)
} }
function makeToken() {
// bare-crypto and Node crypto both expose randomBytes
const bytes = crypto.randomBytes(16)
return b4a.toString(bytes, 'hex')
}
/** /**
* @param {{ * @param {{
* statePath?: string, * statePath?: string,
@@ -168,12 +173,11 @@ function sendJson(res, status, body) {
* }} [opts] * }} [opts]
*/ */
async function start(opts = {}) { async function start(opts = {}) {
const token = opts.token || crypto.randomBytes(16).toString('hex') const token = opts.token || makeToken()
const host = opts.host || '127.0.0.1' const host = opts.host || '127.0.0.1'
const pid = process.pid const pid = getPid()
const server = http.createServer(async (req, res) => { const server = http.createServer(async (req, res) => {
// CORS preflight (UI fetch from pear-bridge origin)
if (req.method === 'OPTIONS') { if (req.method === 'OPTIONS') {
res.statusCode = 204 res.statusCode = 204
res.setHeader('Access-Control-Allow-Origin', '*') res.setHeader('Access-Control-Allow-Origin', '*')
@@ -184,16 +188,19 @@ async function start(opts = {}) {
} }
try { try {
// bare-url / WHATWG URL
const url = new URL(req.url || '/', `http://${host}`) const url = new URL(req.url || '/', `http://${host}`)
const pathname = url.pathname const pathname = url.pathname
if (pathname === '/health' && req.method === 'GET') { if (pathname === '/health' && req.method === 'GET') {
sendJson(res, 200, { ok: true, service: 'holesail-local', clients: localClients.size }) sendJson(res, 200, {
ok: true,
service: 'holesail-local',
clients: localClients.size,
bare: isBare,
})
return return
} }
// All mutating / privileged routes need the token
if (pathname !== '/health') { if (pathname !== '/health') {
const hdr = req.headers['x-peardock-token'] const hdr = req.headers['x-peardock-token']
const q = url.searchParams.get('token') const q = url.searchParams.get('token')
@@ -245,7 +252,7 @@ async function start(opts = {}) {
const addr = server.address() const addr = server.address()
const port = typeof addr === 'object' && addr ? addr.port : 0 const port = typeof addr === 'object' && addr ? addr.port : 0
const baseUrl = `http://${host}:${port}` const baseUrl = `http://${host}:${port}`
const meta = { port, host, token, baseUrl, pid } const meta = { port, host, token, baseUrl, pid, bare: isBare }
if (opts.statePath) { if (opts.statePath) {
try { try {
@@ -262,9 +269,9 @@ async function start(opts = {}) {
...meta, ...meta,
server, server,
async close() { async close() {
for (const url of [...localClients.keys()]) { for (const u of [...localClients.keys()]) {
try { try {
await disconnect(url) await disconnect(u)
} catch { } catch {
// ignore // ignore
} }