128 lines
3.8 KiB
JavaScript
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 */
|
|
}
|
|
}
|