everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 14.1 kB
Source Map (JSON)
{"version":3,"file":"init-source.mjs","names":["tarExtract"],"sources":["../../../src/services/resolution/init-source.ts"],"sourcesContent":["import { createWriteStream, mkdtempSync, readFileSync, rmSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { basename, join, resolve } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { execa } from \"execa\";\nimport { extract as tarExtract } from \"tar\";\nimport { findConfigPath, isAppDescriptorPath, loadAppDescriptorConfig } from \"../../config\";\nimport type { BosConfig } from \"../../types\";\nimport { getExtendsRef, parseBosRef } from \"../../utils/bos-ref\";\nimport { fetchResponse } from \"../../utils/http-client\";\nimport { fetchBosConfigFromFastKv } from \"../registry/fastkv\";\nimport { readWorkspaceCatalog } from \"../workspace/catalog\";\nimport { walkExtendsChain } from \"./session\";\n\nexport { readWorkspaceCatalog };\n\nexport interface SourceResult {\n sourceDir: string;\n parentConfig: BosConfig;\n cleanup: () => Promise<void>;\n /** The ref the tarball actually resolved from — release provenance (ADR 0034). */\n resolvedRef?: string;\n resolvedSha?: string;\n}\n\nexport interface CatalogChainSource {\n catalog: Record<string, string>;\n repository?: string;\n extendsChain: string[];\n}\n\nexport async function resolveCatalogChainSource(opts: {\n /** Local source root (e.g. a downloaded release tarball) — the walk is\n * always local now: the platform manifest is the chain's catalog authority\n * (ADR 0034), so no network-side catalog walk exists. */\n sourceDir: string;\n /** The bos:// ref the caller resolved this chain from — recorded as the\n * chain head for provenance (writeResolvedConfig's extendsChain) instead of\n * the entry's own file path. */\n originRef?: string;\n /** Legacy head-ref spelling: synthesized into originRef when originRef is\n * absent. */\n extendsAccount?: string;\n extendsGateway?: string;\n}): Promise<CatalogChainSource> {\n const entry =\n findConfigPath(resolve(opts.sourceDir)) ?? join(resolve(opts.sourceDir), \"bos.config.json\");\n const catalogs: Record<string, string>[] = [];\n const cleanups: Array<() => Promise<void>> = [];\n const resolvedRefs: string[] = [];\n let repository: string | undefined;\n\n try {\n await walkExtendsChain(entry, {\n env: \"production\",\n collectCatalogs: true,\n registerCleanup: (fn) => cleanups.push(fn),\n visit: async (link) => {\n resolvedRefs.unshift(\n link.ref.startsWith(\"bos://\") ? link.ref : join(link.baseDir, basename(link.ref)),\n );\n catalogs.push(readWorkspaceCatalog(link.sourceDir ?? link.baseDir));\n const repositoryValue = (link.config as Record<string, unknown>).repository;\n if (typeof repositoryValue === \"string\" && repository === undefined) {\n repository = repositoryValue;\n }\n },\n });\n } finally {\n for (const cleanup of [...cleanups].reverse()) {\n await cleanup();\n }\n }\n\n const originRef =\n opts.originRef ??\n (opts.extendsAccount !== undefined && opts.extendsGateway !== undefined\n ? `bos://${opts.extendsAccount}/${opts.extendsGateway}`\n : undefined);\n\n return {\n catalog: Object.assign({}, ...catalogs),\n repository,\n extendsChain: originRef ? [originRef, ...resolvedRefs.slice(1)] : resolvedRefs,\n };\n}\n\nexport async function resolveSourceDir(opts: {\n extendsAccount: string;\n extendsGateway: string;\n source?: string;\n /** Release ref pinning the tarball (ADR 0034): the canonical `v<version>`\n * tag. Absent → fail loud — the scaffold surface never pulls a moving\n * branch head, only released bytes. */\n ref?: string;\n}): Promise<SourceResult> {\n if (opts.source) {\n const sourceDir = resolve(opts.source);\n const configPath = findConfigPath(sourceDir);\n if (!configPath) {\n throw new Error(\n `No authored config (bos.app.ts or bos.config.json) found in source directory: ${sourceDir}`,\n );\n }\n const parentConfig = isAppDescriptorPath(configPath)\n ? ((await loadAppDescriptorConfig(configPath)) as BosConfig)\n : (JSON.parse(readFileSync(configPath, \"utf-8\")) as BosConfig);\n return { sourceDir, parentConfig, cleanup: async () => {} };\n }\n\n const parentConfig = await fetchParentConfig(opts.extendsAccount, opts.extendsGateway);\n\n if (opts.ref) {\n if (!parentConfig.repository) {\n throw new Error(\n `Release ${opts.ref} requested but the config at bos://${opts.extendsAccount}/${opts.extendsGateway} names no repository`,\n );\n }\n const {\n dir: sourceDir,\n cleanup,\n resolvedRef,\n resolvedSha,\n } = await downloadTarball(parentConfig.repository, opts.ref);\n return { sourceDir, parentConfig, cleanup, resolvedRef, resolvedSha };\n }\n\n const chainResult = await resolveRepositoryViaExtendsChain(\n opts.extendsAccount,\n opts.extendsGateway,\n );\n if (chainResult?.repository) {\n const {\n dir: sourceDir,\n cleanup,\n resolvedRef,\n resolvedSha,\n } = await downloadTarball(chainResult.repository, opts.ref);\n return { sourceDir, parentConfig: chainResult.config, cleanup, resolvedRef, resolvedSha };\n }\n\n return {\n sourceDir: \"\",\n parentConfig,\n cleanup: async () => {},\n };\n}\n\nexport async function fetchParentConfig(\n extendsAccount: string,\n extendsGateway: string,\n): Promise<BosConfig> {\n const bosUrl = `bos://${extendsAccount}/${extendsGateway}`;\n return fetchBosConfigFromFastKv<BosConfig>(bosUrl);\n}\n\nexport async function resolveRepositoryViaExtendsChain(\n extendsAccount: string,\n extendsGateway: string,\n visited = new Set<string>(),\n): Promise<{ repository: string; config: BosConfig } | null> {\n const key = `bos://${extendsAccount}/${extendsGateway}`;\n if (visited.has(key)) return null;\n visited.add(key);\n\n try {\n const config = await fetchParentConfig(extendsAccount, extendsGateway);\n if (config.repository) {\n return { repository: config.repository, config };\n }\n\n const extendsRef = getExtendsRef(config as Record<string, unknown>);\n if (extendsRef) {\n const normalized = extendsRef.startsWith(\"bos://\") ? extendsRef : `bos://${extendsRef}`;\n const parsed = parseBosRef(normalized);\n if (parsed) {\n const result = await resolveRepositoryViaExtendsChain(\n parsed.account,\n parsed.gateway,\n visited,\n );\n if (result) return result;\n }\n }\n\n return null;\n } catch {\n return null;\n }\n}\n\n/**\n * The release tarball ref (ADR 0034 pt 4): the canonical `v<version>` tag —\n * sync's workflow pins and the release script both target it. One ref, one\n * resolution: a version names exactly one release.\n */\nexport function releaseRefCandidates(frameworkVersion: string): string {\n return `v${frameworkVersion}`;\n}\n\nexport async function downloadTarball(\n repoUrl: string,\n ref?: string | string[],\n): Promise<{\n dir: string;\n cleanup: () => Promise<void>;\n resolvedRef?: string;\n resolvedSha?: string;\n}> {\n const parsed = parseGitHubUrl(repoUrl);\n if (!parsed) {\n throw new Error(`Cannot parse repository URL: ${repoUrl}`);\n }\n\n const { owner, repo } = parsed;\n let response: Response | null = null;\n let resolvedRef: string | undefined;\n\n const candidates = ref ? (Array.isArray(ref) ? [...ref] : [ref]) : [\"main\", \"master\"];\n for (const candidateRef of candidates) {\n const candidate = await fetchResponse(\n `https://api.github.com/repos/${owner}/${repo}/tarball/${encodeURIComponent(candidateRef)}`,\n {\n headers: { \"User-Agent\": \"everything-dev\" },\n redirect: \"follow\",\n timeout: \"60 seconds\",\n },\n );\n if (candidate.ok) {\n response = candidate;\n resolvedRef = candidateRef;\n break;\n }\n if (candidate.status !== 404) {\n throw new Error(\n `GitHub tarball download failed: ${candidate.status} ${candidate.statusText}`,\n );\n }\n }\n\n if (!response) {\n throw new Error(\n ref\n ? `GitHub tarball download failed for ${repoUrl}: ref not found (tried ${candidates.join(\", \")})`\n : `GitHub tarball download failed for ${repoUrl}: tried main and master`,\n );\n }\n\n if (!response.body) {\n throw new Error(\"GitHub tarball download returned empty body\");\n }\n\n const resolvedSha = response.url.match(/\\/([0-9a-f]{40})(?:\\.tar\\.gz)?\\b/)?.[1];\n\n const tmpDir = mkTmpDir(\"bos-init-tarball-\");\n const tarballPath = join(tmpDir, \"source.tar.gz\");\n\n const fileStream = createWriteStream(tarballPath);\n const reader = response.body as unknown as NodeJS.ReadableStream;\n await pipeline(reader, fileStream);\n\n const extractDir = mkTmpDir(\"bos-init-extract-\");\n try {\n await tarExtract({ cwd: extractDir, file: tarballPath, strip: 1 });\n } catch {\n await execCommand(\"tar\", [\"-xzf\", tarballPath, \"--strip-components=1\", \"-C\", extractDir]);\n }\n\n rmSync(tmpDir, { recursive: true, force: true });\n\n return {\n dir: extractDir,\n resolvedRef,\n resolvedSha,\n cleanup: async () => {\n rmSync(extractDir, { recursive: true, force: true });\n },\n };\n}\n\nexport function parseGitHubUrl(url: string): { owner: string; repo: string } | null {\n const httpsMatch = url.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?(?:\\/.*)?$/);\n if (httpsMatch?.[1] && httpsMatch[2]) {\n return { owner: httpsMatch[1], repo: httpsMatch[2] };\n }\n\n const sshMatch = url.match(/^git@github\\.com:([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (sshMatch?.[1] && sshMatch[2]) {\n return { owner: sshMatch[1], repo: sshMatch[2] };\n }\n\n return null;\n}\n\nfunction mkTmpDir(prefix: string): string {\n return mkdtempSync(join(tmpdir(), `${prefix}-`));\n}\nasync function execCommand(command: string, args: string[]): Promise<void> {\n await execa(command, args, { stdio: \"pipe\", timeout: 2 * 60_000 });\n}\n"],"mappings":"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"}