/** * Host-resolved Bare / companion npm modules for in-image scripts (`ctx.bare`), * plus optional merge from trusted IIFE bundles on the system drive. */ import { readFileSync } from '#host-fs' import { dirname, join } from '#host-path' import { fileURLToPath } from 'url' import bareModuleManifestEmbedded from './bare-module-manifest.data.mjs' import { bareOsHostBooterWarn } from './bare-os-host-booter-log.js' /** * `fileURLToPath` only accepts `file:` URLs. Pear uses `pear://dev/...` for `import.meta.url`, * so we must not call it at load time or when running under `pear run`. * @returns {string | null} */ function bareOsBooterPackageJsonPathForCreateRequire() { const href = String(import.meta.url) if (!href.startsWith('file:')) return null try { return fileURLToPath(new URL('../package.json', import.meta.url)) } catch { return null } } /** * Drive IIFEs call `require("events")`, `require("bare-stream")`, …; `new Function` has no * lexical `require`, so they look at **`globalThis.require`**. On **`file:`** dev trees, install * Node's `createRequire` for the booter package for the duration of bundle eval. Under Pear * (`pear://`), skip — runtime `require` or `tryBareFetchImportWhenDriveMissing` covers fetch. * @param {() => void | Promise} fn */ async function withDriveBundleGlobalRequire(fn) { if (typeof globalThis.require === 'function') return fn() const pkgJson = bareOsBooterPackageJsonPathForCreateRequire() let installed = false if (pkgJson) { try { const urlMod = await import('url') const bm = await import('bare-module') const createRequire = bm.createRequire || bm.default?.createRequire if (typeof createRequire === 'function') { const href = urlMod.pathToFileURL(pkgJson).href globalThis.require = createRequire(href) installed = true } } catch { /* bare-module createRequire unavailable (e.g. some pear:// trees) */ } } try { return await fn() } finally { if (installed) { delete globalThis.require } } } export const BARE_OS_STDLIB_GLOBAL = '__bare_os_stdlib__' const MANIFEST_NAME = 'bare-module-manifest.json' /** * @param {unknown} m * @returns {typeof bareModuleManifestEmbedded} */ function cloneBareModuleManifest(m) { return /** @type {typeof bareModuleManifestEmbedded} */ ( JSON.parse(JSON.stringify(m)) ) } /** @type {{ version: number, entries: Array<{ ctxKey: string, package: string, export?: string, bundle?: boolean, optional?: boolean, tier?: string }> } | null} */ let _cachedManifest = null /** * Embedded copy of **`bare-module-manifest.json`** for Pear (`pear://` **import.meta.url**). * `bare-fs` coerces **`URL`** arguments through **`bare-url.fileURLToPath`**, which rejects * non-`file:` schemes, so **`readFileSync(new URL('pear:…'))`** cannot load the JSON from the * bundle. Static import of **`bare-module-manifest.data.mjs`** (regenerated by * **`npm run sync:bare-manifest`**) keeps the manifest in the Pear module graph. * * On **`file:`** trees, the JSON on disk is preferred so edits apply without regenerating `.data.mjs`. */ export function loadBareModuleManifest() { if (_cachedManifest) return _cachedManifest const href = String(import.meta.url) if (href.startsWith('pear:')) { _cachedManifest = cloneBareModuleManifest(bareModuleManifestEmbedded) return _cachedManifest } if (href.startsWith('file:')) { try { const libDir = dirname(fileURLToPath(href)) const raw = readFileSync(join(libDir, MANIFEST_NAME), 'utf8') _cachedManifest = JSON.parse(raw) } catch { _cachedManifest = cloneBareModuleManifest(bareModuleManifestEmbedded) } return _cachedManifest } _cachedManifest = cloneBareModuleManifest(bareModuleManifestEmbedded) return _cachedManifest } /** Exported for tests: default export object from **`bare-module-manifest.data.mjs`**. */ export function bareOsBareModuleManifestEmbeddedRef() { return bareModuleManifestEmbedded } /** * @param {Record} shellEnv */ export function bareOsBareModulesEnabled(shellEnv) { if (!shellEnv || typeof shellEnv !== 'object') return true const v = shellEnv.BARE_OS_BARE_MODULES return v !== '0' && v !== 'false' } /** * @param {Record} shellEnv */ export function bareOsBareDriveBundlesEnabled(shellEnv) { if (!shellEnv || typeof shellEnv !== 'object') return true const v = shellEnv.BARE_OS_BARE_DRIVE_BUNDLES if (v === '0' || v === 'false') return false return bareOsBareModulesEnabled(shellEnv) } /** * When false, skip host `import(pkg)` for ctx.bare (use only `/lib/bare` IIFEs). * Set **`BARE_OS_BARE_HOST_IMPORTS=0`** for a fully image-local stdlib. */ export function bareOsBareHostImportsEnabled(shellEnv) { if (!shellEnv || typeof shellEnv !== 'object') return true const v = shellEnv.BARE_OS_BARE_HOST_IMPORTS if (v === '0' || v === 'false') return false return true } /** * Populate object with host `import()` results (per manifest). Mutates `target`. * @param {Record} shellEnv * @param {Record} target */ function bareHostRuntime() { if (typeof globalThis.Bare !== 'undefined') return true const v = globalThis.process?.versions return Boolean(v && typeof v.bare === 'string') } export async function buildBareCtxObjectFromHost(shellEnv, target) { if (!bareOsBareModulesEnabled(shellEnv)) return if (!bareOsBareHostImportsEnabled(shellEnv)) return const { entries } = loadBareModuleManifest() const onBare = bareHostRuntime() const tasks = entries.map(async (ent) => { if (!onBare && ent.nativeHint === true) return null const key = ent.ctxKey if (!key || target[key] !== undefined) return null try { const mod = await import(/* webpackIgnore: true */ ent.package) const val = ent.sideEffectImport ? mod?.default !== undefined ? mod.default : true : ent.export === '*' ? mod : mod?.default !== undefined ? mod.default : mod return { key, ent, val } } catch (err) { return { key, ent, err } } }) const settled = await Promise.all(tasks) for (const r of settled) { if (!r) continue const { key, ent, val, err } = r if (err) { if (!ent.optional) { bareOsHostBooterWarn( 'ctx_bare_module_load_failed', `ctx.bare.${key}: failed to load "${ent.package}"`, err?.message || String(err) ) } continue } if (val !== undefined) target[key] = val } } /** * Execute trusted bundle sources that assign `globalThis.__bare_os_stdlib__[ctxKey]`. * Fills missing keys on `target` only. * @param {Record} shellEnv * @param {{ readFile: (p: string, opts?: unknown) => Promise }} vfs * @param {Record} target */ /** * Optional `/lib/bare/bare-module-lock.json` on the system image: `{ "pins": { "ctxKey": "semver" } }`. * Emits warnings for pins whose `ctxKey` is still missing after drive merge + host import. * @param {{ readFile: (p: string) => Promise }} vfs * @param {Record} target * @returns {Promise} */ export async function verifyBareModuleLockfile(vfs, target) { /** @type {string[]} */ const warnings = [] if (!vfs || typeof vfs.readFile !== 'function') return warnings let buf try { buf = await vfs.readFile('/lib/bare/bare-module-lock.json') } catch { return warnings } if (!buf || !buf.byteLength) return warnings let j try { j = JSON.parse(new TextDecoder().decode(buf)) } catch { warnings.push('bare-module-lock.json: invalid JSON') return warnings } const pins = j && typeof j === 'object' ? j.pins : null if (!pins || typeof pins !== 'object') return warnings for (const [k, ver] of Object.entries(pins)) { if (target[k] === undefined) { warnings.push( `bare-module-lock: ctx.bare.${k} missing (pinned ${String(ver)})` ) } } return warnings } /** * esbuild IIFE assigns `__bare_os_bundle_exports__` (CJS interop object), not the * default export itself. Entries use `export default ` → `{ default: binding }`. * @param {unknown} val */ function unwrapDriveBundleExport(val) { if (val === null || val === undefined) return val if (typeof val !== 'object') return val if (Object.prototype.hasOwnProperty.call(val, 'default')) { const d = /** @type {{ default?: unknown }} */ (val).default if (d !== undefined) return d } return val } export async function maybeMergeBareFromDrive(shellEnv, vfs, target) { if (!bareOsBareModulesEnabled(shellEnv)) return if (!bareOsBareDriveBundlesEnabled(shellEnv)) return if (!vfs || typeof vfs.readFile !== 'function') return const perfBareStdlib = shellEnv?.BARE_OS_BOOT_PERF_DETAIL === '1' || shellEnv?.BARE_OS_BOOT_PERF_DETAIL === 'true' const hrtBig = typeof globalThis.process?.hrtime?.bigint === 'function' ? () => globalThis.process.hrtime.bigint() : null const t0 = perfBareStdlib && hrtBig ? hrtBig() : null let metaBuf try { metaBuf = await vfs.readFile('/lib/bare/manifest.json') } catch { metaBuf = null } if (!metaBuf || metaBuf.byteLength === 0) return let meta try { const text = new TextDecoder().decode(metaBuf) meta = JSON.parse(text) } catch { return } const bundles = meta?.bundles if (!Array.isArray(bundles)) return const g = globalThis const sym = BARE_OS_STDLIB_GLOBAL g[sym] = g[sym] && typeof g[sym] === 'object' ? g[sym] : {} const concRaw = String( shellEnv?.BARE_OS_BARE_STDLIB_RESOLVE_CONCURRENCY ?? '' ).trim() const concParsed = Number.parseInt(concRaw, 10) const readConc = Number.isFinite(concParsed) && concParsed >= 1 ? Math.min(concParsed, 32) : 4 await withDriveBundleGlobalRequire(async () => { /** @type {{ path: string, keys: string[] }[]} */ const work = [] /** @type {Set} */ const seenPaths = new Set() let allKeysSatisfied = true for (const b of bundles) { const path = typeof b?.path === 'string' ? b.path : '' const keys = Array.isArray(b?.keys) ? b.keys : [] if (!path.startsWith('/lib/bare/')) continue if (seenPaths.has(path)) continue seenPaths.add(path) for (const k of keys) { if (typeof k === 'string' && k && target[k] === undefined) { allKeysSatisfied = false } } work.push({ path, keys }) } if (work.length > 0 && allKeysSatisfied) { await tryBareFetchImportWhenDriveMissing(target) return } /** @type {{ path: string, keys: string[], source: string | null }[]} */ const prepared = [] for (let i = 0; i < work.length; ) { const slice = work.slice(i, i + readConc) i += slice.length const chunk = await Promise.all( slice.map(async ({ path, keys }) => { let srcBuf try { srcBuf = await vfs.readFile(path) } catch { srcBuf = null } if (!srcBuf || srcBuf.byteLength === 0) { return { path, keys, source: null } } const source = new TextDecoder().decode(srcBuf) if (source.length > 12 * 1024 * 1024) { return { path, keys, source: null } } return { path, keys, source } }) ) prepared.push(...chunk) } for (const { path, keys, source } of prepared) { if (!source) continue try { const run = new Function( `"use strict"; ${source}\n//# sourceURL=bare-drive-bundle:${path}` ) run() } catch (err) { bareOsHostBooterWarn( 'drive_bundle_eval_failed', `drive bundle failed ${path}`, err?.message || String(err) ) continue } const snap = g[sym] if (!snap || typeof snap !== 'object') continue for (const k of keys) { if (typeof k !== 'string' || !k) continue if (target[k] !== undefined) continue if (Object.prototype.hasOwnProperty.call(snap, k)) { target[k] = unwrapDriveBundleExport(snap[k]) } } } }) await tryBareFetchImportWhenDriveMissing(target) if (t0 && hrtBig) { const ns = Number(hrtBig() - t0) bareOsHostBooterWarn( 'bare_stdlib_merge_ns', 'maybeMergeBareFromDrive wall (hrtime bigint delta ns)', String(ns) ) } } /** * When drive `fetch.js` eval fails (no `globalThis.require` or native addons), Bare can still * `import('bare-fetch')` from the booter — same as `buildBareCtxObjectFromHost`, but runs even * when the manifest marks the entry host-only. * @param {Record} target */ async function tryBareFetchImportWhenDriveMissing(target) { if (target.fetch !== undefined) return if (!bareHostRuntime()) return try { let mod if (typeof import.meta.resolve === 'function') { try { mod = await import(import.meta.resolve('bare-fetch')) } catch { mod = await import('bare-fetch') } } else { mod = await import('bare-fetch') } const f = mod?.default if (typeof f === 'function') target.fetch = f } catch { /* optional */ } }