Files
bare-operating-system/packages/bare-os-booter/lib/bare-os-ctx-bare.js
T

652 lines
20 KiB
JavaScript

/**
* Host-resolved Bare / companion npm modules for in-image scripts (`ctx.bare`),
* plus optional merge from trusted IIFE bundles on the system drive.
*/
import b4a from 'b4a'
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'
/**
* Used only by {@link tryLoadBareCtxKeyFromDriveBundlePath}: some Bare booters lack
* `TextDecoder`. Dynamic `import('b4a')` fails on Pear (no referrer); static import is fine.
* @param {Uint8Array} buf
*/
function decodeUtf8BytesBareFallback(buf) {
if (typeof TextDecoder !== 'undefined') {
try {
return new TextDecoder().decode(buf)
} catch {
/* ignore */
}
}
return b4a.toString(buf, 'utf8')
}
/**
* `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`**. Bundled code also calls
* **`__require.addon()`** (native bindings); that only works when `globalThis.require` is from
* **`bare-module`'s `createRequire`** (exposes **`require.addon`**). A bare function or Node-only
* `require` breaks eval with **`__require.addon is not a function`**.
*
* Order: if existing `require` already has **`addon`**, use it; else install **`createRequire`**
* from the booter **`file:`** package.json when available; else **`createRequire(import.meta.url)`**
* (covers **`pear://`** on Bare). If **`addon`** is still missing (e.g. Node dev), attach a stub
* for the duration of eval only.
*
* @param {() => void | Promise<void>} fn
*/
async function tryBareModuleCreateRequire(parentURL) {
try {
const bm = await import('bare-module')
const createRequire = bm.createRequire || bm.default?.createRequire
if (typeof createRequire !== 'function') return null
return createRequire(parentURL)
} catch {
return null
}
}
/**
* Pear `import('pkg')` can fail when the current referrer is a `pear://` booter URL.
* Try loading via bare-module's createRequire from the booter package root first,
* then fall back to `createRequire(import.meta.url)`.
*
* @param {string} specifier
* @returns {Promise<unknown>}
*/
export async function tryRequireFromBooter(specifier) {
if (typeof specifier !== 'string' || !specifier.trim()) return undefined
const liveRequire = globalThis.require
if (typeof liveRequire === 'function') {
try {
return liveRequire(specifier)
} catch {
/* continue */
}
}
const pkgJson = bareOsBooterPackageJsonPathForCreateRequire()
if (pkgJson) {
try {
const urlMod = await import('url')
const href = urlMod.pathToFileURL(pkgJson).href
const req = await tryBareModuleCreateRequire(href)
if (typeof req === 'function') return req(specifier)
} catch {
/* continue */
}
}
try {
const req = await tryBareModuleCreateRequire(import.meta.url)
if (typeof req === 'function') return req(specifier)
} catch {
/* continue */
}
return undefined
}
/** @returns {import('bare-module').Require['addon']} */
function driveBundleRequireAddonStub() {
const stub = function bareOsDriveBundleAddon() {
return new Proxy(
{},
{
get() {
return function bareOsDriveBundleAddonExport() {
return {}
}
}
}
)
}
stub.resolve = function bareOsDriveBundleAddonResolve() {
return ''
}
stub.host = ''
return stub
}
/**
* Install a temporary `require` wrapper for drive-bundle eval so we control both
* module resolution and the `require.addon` surface seen by esbuild IIFEs.
*
* @param {Function} delegate
* @param {Function} addon
*/
function createDriveBundleRequireWrapper(delegate, addon) {
const wrapped = function bareOsDriveBundleRequire(...args) {
return Reflect.apply(delegate, this, args)
}
for (const key of ['resolve', 'cache', 'extensions', 'main']) {
try {
const v = delegate[key]
if (v !== undefined) wrapped[key] = v
} catch {
/* ignore */
}
}
wrapped.addon = addon
return wrapped
}
async function withDriveBundleGlobalRequire(fn) {
const origRequire = globalThis.require
/** @type {Function | null} */
let delegate =
typeof origRequire === 'function' ? /** @type {Function} */ (origRequire) : null
if (!delegate) {
const pkgJson = bareOsBooterPackageJsonPathForCreateRequire()
if (pkgJson) {
try {
const urlMod = await import('url')
const href = urlMod.pathToFileURL(pkgJson).href
const req = await tryBareModuleCreateRequire(href)
if (typeof req === 'function') delegate = req
} catch {
/* continue */
}
}
}
if (!delegate) {
const req = await tryBareModuleCreateRequire(import.meta.url)
if (typeof req === 'function') delegate = req
}
if (typeof delegate === 'function') {
/** @type {Function | null} */
let addon =
typeof delegate.addon === 'function' ? /** @type {Function} */ (delegate.addon) : null
if (!addon) {
const addonReq = await tryBareModuleCreateRequire(import.meta.url)
if (addonReq && typeof addonReq.addon === 'function') addon = addonReq.addon
}
if (!addon) addon = driveBundleRequireAddonStub()
globalThis.require = createDriveBundleRequireWrapper(delegate, addon)
}
try {
return await fn()
} finally {
if (origRequire === undefined) delete globalThis.require
else globalThis.require = origRequire
}
}
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<string, string>} 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<string, string>} 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<string, string>} shellEnv
* @param {Record<string, unknown>} 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 pearBooter =
typeof import.meta !== 'undefined' &&
String(import.meta.url || '').startsWith('pear:')
const { entries } = loadBareModuleManifest()
const skipHostKeys = new Set(
String(shellEnv?.BARE_OS_BARE_HOST_SKIP_CTX_KEYS || '')
.split(/[\s,]+/)
.map((s) => s.trim())
.filter(Boolean)
)
const onlyHostKeysRaw = String(
shellEnv?.BARE_OS_BARE_HOST_ONLY_CTX_KEYS || ''
)
.split(/[\s,]+/)
.map((s) => s.trim())
.filter(Boolean)
const onlyHostKeys =
onlyHostKeysRaw.length > 0 ? new Set(onlyHostKeysRaw) : null
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
if (skipHostKeys.has(key)) return null
if (onlyHostKeys && !onlyHostKeys.has(key)) return null
// Pear (`pear:` import.meta.url): bare-module cannot resolve `import("holesail")` from
// this booter URL (MODULE_NOT_FOUND / no referrer). Drive IIFEs supply bundle:true keys.
if (pearBooter && ent.bundle === true) 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<string, string>} shellEnv
* @param {{ readFile: (p: string, opts?: unknown) => Promise<Uint8Array | null> }} vfs
* @param {Record<string, unknown>} 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<Uint8Array | null> }} vfs
* @param {Record<string, unknown>} target
* @returns {Promise<string[]>}
*/
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 <binding>` → `{ 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
}
/**
* Eval a single `/lib/bare/bundles/*.js` IIFE (same as {@link maybeMergeBareFromDrive}) and
* return one `ctxKey` from `globalThis.__bare_os_stdlib__`. Does not mutate `ctx.bare`.
* Used when Pear skipped populating a key (merge short-circuit or eval failure).
*
* @param {Record<string, unknown>} ctx
* @param {{ readFile: (p: string) => Promise<Uint8Array | null> }} vfs
* @param {string} logicalPath
* @param {string} ctxKey
* @returns {Promise<unknown>}
*/
export async function tryLoadBareCtxKeyFromDriveBundlePath(
ctx,
vfs,
logicalPath,
ctxKey
) {
void ctx
if (!vfs || typeof vfs.readFile !== 'function') return undefined
if (
typeof logicalPath !== 'string' ||
!logicalPath.startsWith('/lib/bare/') ||
typeof ctxKey !== 'string' ||
!ctxKey
) {
return undefined
}
let buf
try {
buf = await vfs.readFile(logicalPath)
} catch {
return undefined
}
if (!buf || !buf.byteLength) return undefined
const text = decodeUtf8BytesBareFallback(buf)
if (text.length > 12 * 1024 * 1024) return undefined
const g = globalThis
const sym = BARE_OS_STDLIB_GLOBAL
/** @type {string | null} */
let errMsg = null
await withDriveBundleGlobalRequire(async () => {
g[sym] = g[sym] && typeof g[sym] === 'object' ? g[sym] : {}
try {
const run = new Function(
`"use strict"; var require = globalThis.require;\n${text}\n//# sourceURL=bare-drive-bundle:${logicalPath}`
)
run()
} catch (err) {
errMsg = (err && /** @type {{ message?: string }} */ (err).message) || String(err)
}
})
if (errMsg) {
bareOsHostBooterWarn(
'drive_bundle_eval_failed',
`tryLoadBareCtxKeyFromDriveBundlePath ${logicalPath}`,
errMsg
)
return undefined
}
const snap = g[sym]
if (!snap || typeof snap !== 'object') return undefined
if (!Object.prototype.hasOwnProperty.call(snap, ctxKey)) return undefined
return unwrapDriveBundleExport(snap[ctxKey])
}
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<string>} */
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"; var require = globalThis.require;\n${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<string, unknown>} 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 */
}
}