Files
bare-operating-system/packages/bare-os-booter/lib/bare-os-ensure-bare-fetch.js
T
2026-04-04 01:20:57 -04:00

128 lines
3.8 KiB
JavaScript

/**
* Install holepunch fetch on globalThis when the host has no native fetch (Pear/Bare).
* Dynamic `import('bare-fetch')` from deep modules can fail on Pear; resolving to an
* absolute URL via import.meta.resolve first avoids that.
*/
/** @type {boolean} */
let installTried = false
/**
* Legacy bundles stored the whole CJS interop object on `ctx.bare.fetch` instead of
* the default export; unwrap when needed.
* @param {unknown} x
* @returns {typeof fetch | null}
*/
export function coerceBareFetchExport(x) {
if (typeof x === 'function') return /** @type {typeof fetch} */ (x)
if (x && typeof x === 'object' && typeof /** @type {{ default?: unknown }} */ (x).default === 'function')
return /** @type {typeof fetch} */ (/** @type {{ default: typeof fetch }} */ (x).default)
return null
}
/**
* @param {any} f
*/
function applyFetchToGlobals(f) {
globalThis.fetch = f
if (f.Request) globalThis.Request = f.Request
if (f.Response) globalThis.Response = f.Response
if (f.Headers) globalThis.Headers = f.Headers
if (typeof global !== 'undefined') {
global.fetch = f
if (f.Request) global.Request = f.Request
if (f.Response) global.Response = f.Response
if (f.Headers) global.Headers = f.Headers
}
}
/**
* Dynamic `import(specifier)` from nested modules can fail on Pear ("Cannot find referrer");
* resolving to an absolute URL via `import.meta.resolve` first fixes that. Tries parent-aware
* resolve when supported, then bare specifier.
* @param {string} specifier
* @returns {Promise<Record<string, unknown>>}
*/
export async function importResolvedOrBare(specifier) {
if (typeof import.meta.resolve === 'function') {
try {
return await import(
import.meta.resolve(specifier, import.meta.url)
)
} catch {
try {
return await import(import.meta.resolve(specifier))
} catch {
/* fall through */
}
}
}
return await import(specifier)
}
/**
* Use fetch from the system image **`/lib/bare/bundles/fetch.js`** (merged into `ctx.bare`)
* when the host has no global fetch (typical Pear/Bare).
* @param {Record<string, unknown>} bareLibrary
* @returns {boolean} true if globals were updated
*/
export function primeGlobalFetchFromBareLibrary(bareLibrary) {
if (typeof globalThis.fetch === 'function') return false
const f = coerceBareFetchExport(bareLibrary && bareLibrary.fetch)
if (f) {
applyFetchToGlobals(f)
return true
}
return false
}
/**
* Prefer policy-wrapped **`ctx.httpFetch`**, then **`ctx.bare.fetch`** (drive bundle), then global fetch.
* @param {Record<string, unknown>} ctx
* @returns {typeof fetch | null}
*/
export function resolveBareOsFetchFn(ctx) {
if (typeof ctx.httpFetch === 'function')
return /** @type {typeof fetch} */ (ctx.httpFetch)
const bare = coerceBareFetchExport(ctx.bare && ctx.bare.fetch)
if (bare) return bare.bind(ctx.bare)
if (typeof globalThis.fetch === 'function')
return globalThis.fetch.bind(globalThis)
return null
}
/**
* @param {Record<string, unknown>} [ctx]
*/
export async function ensureBareFetchGlobals(ctx) {
if (typeof globalThis.fetch === 'function') return
const fromBare = coerceBareFetchExport(ctx?.bare && ctx.bare.fetch)
if (fromBare) {
applyFetchToGlobals(fromBare)
return
}
if (installTried) return
installTried = true
try {
const m = await importResolvedOrBare('bare-fetch')
const f = m.default
if (typeof f === 'function') applyFetchToGlobals(f)
} catch {
/* bare-fetch optional when not on Bare / not bundled */
}
if (typeof globalThis.fetch === 'function') return
try {
const m = await importResolvedOrBare('bare-https')
const f = m.default?.fetch || m.fetch
if (typeof f === 'function') {
globalThis.fetch = f
if (typeof global !== 'undefined') global.fetch = f
}
} catch {
/* bare-https optional */
}
}