/** * 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>} */ 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} 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} 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} [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 */ } }