UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

201 lines (199 loc) • 8.61 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const require_runtime = require('./_virtual/_rolldown/runtime.cjs'); const require_bundle_path = require('./bundle-path.cjs'); const require_config = require('./config.cjs'); const require_bundle_cache = require('./bundle-cache.cjs'); let node_fs = require("node:fs"); let node_path = require("node:path"); node_path = require_runtime.__toESM(node_path, 1); let effect = require("effect"); let node_fs_promises = require("node:fs/promises"); let every_plugin_build_artifact_names = require("every-plugin/build/artifact-names"); //#region src/bundle-fs-resolve.ts var BundleReadError = class extends effect.Schema.TaggedError()("BundleReadError", { path: effect.Schema.String, cause: effect.Schema.optional(effect.Schema.Unknown) }) {}; const MIME_TYPES = { ".js": "text/javascript", ".mjs": "text/javascript", ".cjs": "text/javascript", ".css": "text/css", ".json": "application/json", ".map": "application/json", ".svg": "image/svg+xml", ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp", ".avif": "image/avif", ".ico": "image/x-icon", ".html": "text/html", ".htm": "text/html", ".webmanifest": "application/manifest+json", ".md": "text/markdown", ".ts": "text/plain", ".woff": "font/woff", ".woff2": "font/woff2", ".ttf": "font/ttf", ".otf": "font/otf", ".eot": "application/vnd.ms-fontobject", ".wasm": "application/wasm", ".txt": "text/plain", ".xml": "application/xml" }; /** * Own-namespace path for a bundle URL: the shared URL→staged-path mapping, * narrowed to the runtime identity — a URL whose encoded namespace differs * from `namespace.account`/`namespace.gateway` must fall to the network/cache * path, never serve a sibling namespace's staged bytes. */ function bundleUrlToLocalPath(url, namespace) { const filePath = require_bundle_path.bundleUrlToStagedPath(url, namespace.bundleDir); if (!filePath) return null; const nsDir = node_path.default.resolve(namespace.bundleDir, namespace.account, namespace.gateway); if (filePath !== nsDir && !filePath.startsWith(nsDir + node_path.default.sep)) return null; return filePath; } var BundleResolver = class BundleResolver extends effect.Context.Service()("everything-dev/bundle-fs-resolve/BundleResolver") { static layer(namespace) { const respond = effect.Effect.fn("BundleResolver.respond")(function* (url) { const filePath = bundleUrlToLocalPath(url, namespace); if (!filePath) return null; const bytes = yield* effect.Effect.tryPromise({ try: () => (0, node_fs_promises.readFile)(filePath), catch: (cause) => new BundleReadError({ path: filePath, cause }) }).pipe(effect.Effect.catchTag("BundleReadError", () => effect.Effect.succeed(null))); if (bytes === null) return null; const name = node_path.default.basename(filePath); const contentType = MIME_TYPES[node_path.default.extname(name).toLowerCase()] ?? "application/octet-stream"; return new Response(new Uint8Array(bytes), { headers: { "content-type": contentType, "cache-control": (0, every_plugin_build_artifact_names.cacheControlOf)(name), etag: `"${Buffer.from(bytes.subarray(0, 4096)).toString("base64").slice(0, 24)}"` } }); }); return effect.Layer.effect(BundleResolver, effect.Effect.succeed(BundleResolver.of({ lookup: (input) => { const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url; return respond(url); } }))); } }; const staleResponse = (bytes, url) => { const name = url.split("?")[0].split("/").pop() ?? ""; const contentType = MIME_TYPES[node_path.default.extname(name).toLowerCase()] ?? "application/octet-stream"; return new Response(bytes.slice().buffer, { status: 200, headers: { "content-type": contentType, "x-bundle-cache": "stale" } }); }; /** * Installs the resolver as a global fetch interceptor — the one seam every * boot-time consumer shares (config manifest discovery, contract-type * fetches, orchestrator host loading, MF remoteEntry/identity probes). * One layered resolution policy: staged disk → network → stale-if-error. * Own-namespace URLs resolve from the staged directory and fall through to * the original fetch on a miss (a partially-staged namespace boots over the * wire instead of failing); a miss never touches the write-through cache — * for the own namespace the staged disk IS the resilience artifact. Foreign * `/bundles/…` URLs get the network with the stale-if-error cache; every * other URL falls through to the original fetch untouched. Inert when * neither a staged namespace nor a cache is configured. */ function installGlobalBundleFetch(options) { const namespace = options.namespace; const cacheDir = options.cacheDir; const runtime = namespace ? effect.ManagedRuntime.make(BundleResolver.layer(namespace)) : null; const original = globalThis.fetch; const patched = (async (input, init) => { const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url; if (namespace && bundleUrlToLocalPath(url, namespace)) { if (!runtime) throw new Error("unreachable: resolver runtime missing"); const response = await runtime.runPromise(effect.Effect.gen(function* () { return yield* (yield* BundleResolver).lookup(input); })); if (response !== null) return response; return original(input, init); } if (cacheDir !== void 0 && require_bundle_cache.bundleCachePath(url, { cacheDir })) return (async () => { try { const response = await original(input, init); if (response.ok) try { const bytes = new Uint8Array(await response.clone().arrayBuffer()); await require_bundle_cache.writeBundleCache(url, bytes, { cacheDir }); } catch {} else { const cached = await require_bundle_cache.readBundleCache(url, { cacheDir }); if (cached) return staleResponse(cached, url); } return response; } catch (error) { const cached = await require_bundle_cache.readBundleCache(url, { cacheDir }); if (cached) return staleResponse(cached, url); throw error; } })(); return original(input, init); }); const originalPreconnect = original.preconnect; if (originalPreconnect) patched.preconnect = originalPreconnect.bind(original); globalThis.fetch = patched; return { uninstall: async () => { if (globalThis.fetch === patched) globalThis.fetch = original; if (runtime) await runtime.dispose(); } }; } /** * Boot-time installer for the CLI: derives the own namespace from * `BOS_BUNDLE_DIR` plus the runtime identity (`BOS_ACCOUNT`/`BOS_GATEWAY` * registry env taking precedence over the bos.config.json fields), and the * foreign-namespace cache from `BOS_BUNDLE_CACHE_DIR`. Tier auto-detection * (ADR 0021): an identity whose namespace is not actually staged under the * bundle dir drops to the registry tier — plain network fetch — instead of * deterministically 404ing own-namespace URLs against a foreign image's * staged bytes. Never throws — a missing or malformed identity leaves the * default network fetch in place. */ function installBundleFetchFromEnv(input) { const bundleDir = process.env.BOS_BUNDLE_DIR; const cacheDir = process.env.BOS_BUNDLE_CACHE_DIR; if (!bundleDir && cacheDir === void 0) return null; let configAccount; let configGateway; const configPath = input.configPath; if (configPath && !require_config.isAppDescriptorPath(configPath)) try { const parsed = JSON.parse((0, node_fs.readFileSync)(configPath, "utf8")); configAccount = parsed.account; configGateway = parsed.domain; } catch {} const account = process.env.BOS_ACCOUNT ?? configAccount; const gateway = process.env.BOS_GATEWAY ?? configGateway; if (bundleDir && (!account || !gateway)) return null; let namespace; if (bundleDir && account && gateway) { if ((0, node_fs.existsSync)(node_path.default.join(bundleDir, account, gateway))) namespace = { bundleDir, account, gateway }; else console.warn(`[bundles] staged namespace ${account}/${gateway} not found under ${bundleDir} — registry tier (network fetch)`); } return installGlobalBundleFetch({ namespace, cacheDir: cacheDir ?? (namespace ? require_bundle_cache.bundleCacheRoot() : void 0) }); } //#endregion exports.BundleReadError = BundleReadError; exports.BundleResolver = BundleResolver; exports.bundleUrlToLocalPath = bundleUrlToLocalPath; exports.installBundleFetchFromEnv = installBundleFetchFromEnv; exports.installGlobalBundleFetch = installGlobalBundleFetch; //# sourceMappingURL=bundle-fs-resolve.cjs.map