UNPKG

everything-dev

Version:

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

211 lines (209 loc) • 7.92 kB
const require_http_client = require('../../utils/http-client.cjs'); const require_services_registry_fastkv = require('../registry/fastkv.cjs'); const require_load = require('./load.cjs'); const require_bos_ref = require('../../utils/bos-ref.cjs'); const require_catalog = require('../workspace/catalog.cjs'); const require_services_resolution_session = require('./session.cjs'); let node_fs = require("node:fs"); let node_path = require("node:path"); let execa = require("execa"); let node_os = require("node:os"); let node_stream_promises = require("node:stream/promises"); let tar = require("tar"); //#region src/services/resolution/init-source.ts async function resolveCatalogChainSource(opts) { const entry = require_load.findConfigPath((0, node_path.resolve)(opts.sourceDir)) ?? (0, node_path.join)((0, node_path.resolve)(opts.sourceDir), "bos.config.json"); const catalogs = []; const cleanups = []; const resolvedRefs = []; let repository; try { await require_services_resolution_session.walkExtendsChain(entry, { env: "production", collectCatalogs: true, registerCleanup: (fn) => cleanups.push(fn), visit: async (link) => { resolvedRefs.unshift(link.ref.startsWith("bos://") ? link.ref : (0, node_path.join)(link.baseDir, (0, node_path.basename)(link.ref))); catalogs.push(require_catalog.readWorkspaceCatalog(link.sourceDir ?? link.baseDir)); const repositoryValue = link.config.repository; if (typeof repositoryValue === "string" && repository === void 0) repository = repositoryValue; } }); } finally { for (const cleanup of [...cleanups].reverse()) await cleanup(); } const originRef = opts.originRef ?? (opts.extendsAccount !== void 0 && opts.extendsGateway !== void 0 ? `bos://${opts.extendsAccount}/${opts.extendsGateway}` : void 0); return { catalog: Object.assign({}, ...catalogs), repository, extendsChain: originRef ? [originRef, ...resolvedRefs.slice(1)] : resolvedRefs }; } async function resolveSourceDir(opts) { if (opts.source) { const sourceDir = (0, node_path.resolve)(opts.source); const configPath = require_load.findConfigPath(sourceDir); if (!configPath) throw new Error(`No authored config (bos.app.ts or bos.config.json) found in source directory: ${sourceDir}`); return { sourceDir, parentConfig: require_load.isAppDescriptorPath(configPath) ? await require_load.loadAppDescriptorConfig(configPath) : JSON.parse((0, node_fs.readFileSync)(configPath, "utf-8")), cleanup: async () => {} }; } const parentConfig = await fetchParentConfig(opts.extendsAccount, opts.extendsGateway); if (opts.ref) { if (!parentConfig.repository) throw new Error(`Release ${opts.ref} requested but the config at bos://${opts.extendsAccount}/${opts.extendsGateway} names no repository`); const { dir: sourceDir, cleanup, resolvedRef, resolvedSha } = await downloadTarball(parentConfig.repository, opts.ref); return { sourceDir, parentConfig, cleanup, resolvedRef, resolvedSha }; } const chainResult = await resolveRepositoryViaExtendsChain(opts.extendsAccount, opts.extendsGateway); if (chainResult?.repository) { const { dir: sourceDir, cleanup, resolvedRef, resolvedSha } = await downloadTarball(chainResult.repository, opts.ref); return { sourceDir, parentConfig: chainResult.config, cleanup, resolvedRef, resolvedSha }; } return { sourceDir: "", parentConfig, cleanup: async () => {} }; } async function fetchParentConfig(extendsAccount, extendsGateway) { const bosUrl = `bos://${extendsAccount}/${extendsGateway}`; return require_services_registry_fastkv.fetchBosConfigFromFastKv(bosUrl); } async function resolveRepositoryViaExtendsChain(extendsAccount, extendsGateway, visited = /* @__PURE__ */ new Set()) { const key = `bos://${extendsAccount}/${extendsGateway}`; if (visited.has(key)) return null; visited.add(key); try { const config = await fetchParentConfig(extendsAccount, extendsGateway); if (config.repository) return { repository: config.repository, config }; const extendsRef = require_bos_ref.getExtendsRef(config); if (extendsRef) { const normalized = extendsRef.startsWith("bos://") ? extendsRef : `bos://${extendsRef}`; const parsed = require_bos_ref.parseBosRef(normalized); if (parsed) { const result = await resolveRepositoryViaExtendsChain(parsed.account, parsed.gateway, visited); if (result) return result; } } return null; } catch { return null; } } /** * The release tarball ref (ADR 0034 pt 4): the canonical `v<version>` tag — * sync's workflow pins and the release script both target it. One ref, one * resolution: a version names exactly one release. */ function releaseRefCandidates(frameworkVersion) { return `v${frameworkVersion}`; } async function downloadTarball(repoUrl, ref) { const parsed = parseGitHubUrl(repoUrl); if (!parsed) throw new Error(`Cannot parse repository URL: ${repoUrl}`); const { owner, repo } = parsed; let response = null; let resolvedRef; const candidates = ref ? Array.isArray(ref) ? [...ref] : [ref] : ["main", "master"]; for (const candidateRef of candidates) { const candidate = await require_http_client.fetchResponse(`https://api.github.com/repos/${owner}/${repo}/tarball/${encodeURIComponent(candidateRef)}`, { headers: { "User-Agent": "everything-dev" }, redirect: "follow", timeout: "60 seconds" }); if (candidate.ok) { response = candidate; resolvedRef = candidateRef; break; } if (candidate.status !== 404) throw new Error(`GitHub tarball download failed: ${candidate.status} ${candidate.statusText}`); } if (!response) throw new Error(ref ? `GitHub tarball download failed for ${repoUrl}: ref not found (tried ${candidates.join(", ")})` : `GitHub tarball download failed for ${repoUrl}: tried main and master`); if (!response.body) throw new Error("GitHub tarball download returned empty body"); const resolvedSha = response.url.match(/\/([0-9a-f]{40})(?:\.tar\.gz)?\b/)?.[1]; const tmpDir = mkTmpDir("bos-init-tarball-"); const tarballPath = (0, node_path.join)(tmpDir, "source.tar.gz"); const fileStream = (0, node_fs.createWriteStream)(tarballPath); const reader = response.body; await (0, node_stream_promises.pipeline)(reader, fileStream); const extractDir = mkTmpDir("bos-init-extract-"); try { await (0, tar.extract)({ cwd: extractDir, file: tarballPath, strip: 1 }); } catch { await execCommand("tar", [ "-xzf", tarballPath, "--strip-components=1", "-C", extractDir ]); } (0, node_fs.rmSync)(tmpDir, { recursive: true, force: true }); return { dir: extractDir, resolvedRef, resolvedSha, cleanup: async () => { (0, node_fs.rmSync)(extractDir, { recursive: true, force: true }); } }; } function parseGitHubUrl(url) { const httpsMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/); if (httpsMatch?.[1] && httpsMatch[2]) return { owner: httpsMatch[1], repo: httpsMatch[2] }; const sshMatch = url.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/); if (sshMatch?.[1] && sshMatch[2]) return { owner: sshMatch[1], repo: sshMatch[2] }; return null; } function mkTmpDir(prefix) { return (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), `${prefix}-`)); } async function execCommand(command, args) { await (0, execa.execa)(command, args, { stdio: "pipe", timeout: 12e4 }); } //#endregion exports.downloadTarball = downloadTarball; exports.fetchParentConfig = fetchParentConfig; exports.parseGitHubUrl = parseGitHubUrl; exports.readWorkspaceCatalog = require_catalog.readWorkspaceCatalog; exports.releaseRefCandidates = releaseRefCandidates; exports.resolveCatalogChainSource = resolveCatalogChainSource; exports.resolveRepositoryViaExtendsChain = resolveRepositoryViaExtendsChain; exports.resolveSourceDir = resolveSourceDir; //# sourceMappingURL=init-source.cjs.map