UNPKG

everything-dev

Version:

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

871 lines (869 loc) 32.7 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const require_runtime = require('../_virtual/_rolldown/runtime.cjs'); const require_fastkv = require('../fastkv.cjs'); const require_merge = require('../merge.cjs'); const require_manifest_normalizer = require('../internal/manifest-normalizer.cjs'); const require_save_config = require('../utils/save-config.cjs'); const require_snapshot = require('./snapshot.cjs'); let node_fs = require("node:fs"); let node_path = require("node:path"); let node_crypto = require("node:crypto"); let node_module = require("node:module"); let node_os = require("node:os"); let node_stream_promises = require("node:stream/promises"); let execa = require("execa"); let glob = require("glob"); //#region src/cli/init.ts const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href); const INIT_ROOT_PATTERNS = [ "bos.config.json", "package.json", ".env.example", ".gitignore", "biome.json", "bunfig.toml", "Dockerfile", "railway.json", "AGENTS.md", ".changeset/config.json", ".changeset/README.md", "README.md", "CONTRIBUTING.md", ".github/templates/**" ]; const OVERRIDE_WORKSPACE_MAP = { ui: ["ui"], api: ["api"], host: ["host"], plugins: [] }; function getExtendsRef(config) { if (typeof config.extends === "string") return config.extends; if (config.extends && typeof config.extends === "object") return require_merge.resolveExtendsRef(config.extends, "production"); } function parseBosRef(ref) { const match = ref.match(/^bos:\/\/([^/]+)\/(.+)$/); if (!match?.[1] || !match[2]) return null; return { account: match[1], gateway: match[2] }; } function readWorkspaceCatalog(sourceDir) { const pkgPath = (0, node_path.join)(sourceDir, "package.json"); if (!(0, node_fs.existsSync)(pkgPath)) return {}; return { ...readJsonFile(pkgPath).workspaces?.catalog ?? {} }; } async function resolveCatalogChainSource(opts) { const catalogs = []; const cleanups = []; const extendsChain = []; const visited = /* @__PURE__ */ new Set(); let repository; let currentRef = `bos://${opts.extendsAccount}/${opts.extendsGateway}`; let sourceDir = opts.sourceDir ? (0, node_path.resolve)(opts.sourceDir) : void 0; let configPath = sourceDir ? (0, node_path.join)(sourceDir, "bos.config.json") : void 0; try { while (true) { if (visited.has(currentRef)) throw new Error(`Circular extends detected while resolving catalog source: ${currentRef}`); visited.add(currentRef); extendsChain.push(currentRef); let config; let currentSourceDir = sourceDir; let cleanup = async () => {}; if (configPath) { config = readJsonFile(configPath); currentSourceDir = (0, node_path.dirname)(configPath); } else { const parsed = parseBosRef(currentRef); if (!parsed) break; const sourceResult = await resolveSourceDir({ extendsAccount: parsed.account, extendsGateway: parsed.gateway }); config = sourceResult.parentConfig; currentSourceDir = sourceResult.sourceDir || void 0; cleanup = sourceResult.cleanup; } cleanups.push(cleanup); catalogs.push(currentSourceDir ? readWorkspaceCatalog(currentSourceDir) : {}); if (typeof config.repository === "string") repository = config.repository; const nextExtendsRef = getExtendsRef(config); if (!nextExtendsRef) break; if (nextExtendsRef.startsWith("bos://")) { currentRef = nextExtendsRef; sourceDir = void 0; configPath = void 0; continue; } if (!currentSourceDir) break; const nextConfigPath = (0, node_path.resolve)(currentSourceDir, nextExtendsRef); if (!(0, node_fs.existsSync)(nextConfigPath)) break; currentRef = nextConfigPath; sourceDir = (0, node_path.dirname)(nextConfigPath); configPath = nextConfigPath; } } finally { for (const cleanup of cleanups.reverse()) await cleanup(); } return { catalog: Object.assign({}, ...catalogs.reverse()), repository, extendsChain }; } async function resolveSourceDir(opts) { if (opts.source) { const sourceDir = (0, node_path.resolve)(opts.source); if (!(0, node_fs.existsSync)((0, node_path.join)(sourceDir, "bos.config.json"))) throw new Error(`No bos.config.json found in source directory: ${sourceDir}`); return { sourceDir, parentConfig: JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(sourceDir, "bos.config.json"), "utf-8")), cleanup: async () => {} }; } const parentConfig = await fetchParentConfig(opts.extendsAccount, opts.extendsGateway); if (parentConfig.repository) { const { dir: sourceDir, cleanup } = await downloadTarball(parentConfig.repository); return { sourceDir, parentConfig, cleanup }; } const chainResult = await resolveRepositoryViaExtendsChain(opts.extendsAccount, opts.extendsGateway); if (chainResult?.repository) { const { dir: sourceDir, cleanup } = await downloadTarball(chainResult.repository); return { sourceDir, parentConfig: chainResult.config, cleanup }; } return { sourceDir: "", parentConfig, cleanup: async () => {} }; } function buildInitPatterns(overrides, plugins) { const has = (section) => overrides.includes(section); const patterns = [...INIT_ROOT_PATTERNS]; if (has("ui")) patterns.push("ui/**"); if (has("api")) patterns.push("api/**"); if (has("host")) patterns.push("host/**"); if (has("plugins")) for (const plugin of plugins ?? []) patterns.push(`plugins/${plugin}/**`); return patterns; } function sourcePathToDestinationPath(filePath) { return filePath.startsWith(".github/templates/") ? filePath.replace(/^\.github\/templates\//, ".github/") : filePath; } async function fetchParentConfig(extendsAccount, extendsGateway) { return require_fastkv.fetchBosConfigFromFastKv(`bos://${extendsAccount}/${extendsGateway}`); } 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 = getExtendsRef(config); if (extendsRef) { const parsed = parseBosRef(extendsRef.startsWith("bos://") ? extendsRef : `bos://${extendsRef}`); if (parsed) { const result = await resolveRepositoryViaExtendsChain(parsed.account, parsed.gateway, visited); if (result) return result; } } return null; } catch { return null; } } async function detectGitRemoteUrl(directory) { try { const { stdout } = await (0, execa.execa)("git", [ "remote", "get-url", "origin" ], { cwd: directory, stdio: "pipe" }); const url = stdout.trim(); if (!url) return void 0; return normalizeGitUrl(url); } catch { return; } } function normalizeGitUrl(url) { const sshMatch = url.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/); if (sshMatch) return `https://github.com/${sshMatch[1]}/${sshMatch[2]}`; const httpsMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/); if (httpsMatch) return `https://github.com/${httpsMatch[1]}/${httpsMatch[2]}`; return url.endsWith(".git") ? url.slice(0, -4) : url; } async function downloadTarball(repoUrl) { const parsed = parseGitHubUrl(repoUrl); if (!parsed) throw new Error(`Cannot parse repository URL: ${repoUrl}`); const { owner, repo } = parsed; let response = null; for (const branch of ["main", "master"]) { const candidate = await fetch(`https://api.github.com/repos/${owner}/${repo}/tarball/${branch}`, { headers: { "User-Agent": "everything-dev" }, redirect: "follow" }); if (candidate.ok) { response = candidate; break; } if (candidate.status !== 404) throw new Error(`GitHub tarball download failed: ${candidate.status} ${candidate.statusText}`); } if (!response) throw new Error(`GitHub tarball download failed for ${repoUrl}: tried main and master`); if (!response.body) throw new Error("GitHub tarball download returned empty body"); 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 require$1("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, cleanup: async () => { (0, node_fs.rmSync)(extractDir, { recursive: true, force: true }); } }; } function parseGitHubUrl(url) { const httpsMatch = url.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/.*)?$/); if (httpsMatch) return { owner: httpsMatch[1], repo: httpsMatch[2] }; const sshMatch = url.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/); if (sshMatch) return { owner: sshMatch[1], repo: sshMatch[2] }; return null; } async function copyFilteredFiles(sourceDir, destination, patterns, _options) { if (patterns.length === 0) return 0; const allFiles = /* @__PURE__ */ new Set(); for (const pattern of patterns) { const matches = await (0, glob.glob)(pattern, { cwd: sourceDir, nodir: true, dot: true, absolute: false, ignore: [ "**/node_modules/**", "**/.git/**", "**/dist/**", "**/.bos/**" ] }); for (const match of matches) allFiles.add(match); } (0, node_fs.mkdirSync)(destination, { recursive: true }); let count = 0; for (const filePath of allFiles) { const src = (0, node_path.join)(sourceDir, filePath); if (!(0, node_fs.lstatSync)(src).isFile()) continue; const dest = (0, node_path.join)(destination, sourcePathToDestinationPath(filePath)); (0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true }); (0, node_fs.writeFileSync)(dest, (0, node_fs.readFileSync)(src)); count++; } return count; } function stripProductionFields(entry) { delete entry.production; delete entry.integrity; delete entry.ssr; delete entry.ssrIntegrity; } function buildRootTypecheckScript(sections) { const commands = ["bun run types:gen"]; if (sections.ui) commands.push("if [ -d ui ]; then bun run --cwd ui typecheck; fi"); if (sections.api) commands.push("if [ -d api ]; then bun run --cwd api typecheck; fi"); if (sections.host) commands.push("if [ -d host ]; then bun run --cwd host typecheck; fi"); if (sections.plugins) commands.push("if [ -d plugins ]; then for dir in plugins/*; do if [ -f \"$dir/package.json\" ]; then bun run --cwd \"$dir\" typecheck; fi; done; fi"); return commands.join(" && "); } function buildChildRootScripts(sections) { const scripts = { dev: "node_modules/.bin/bos dev --host remote", "dev:proxy": "node_modules/.bin/bos dev --proxy", build: "node_modules/.bin/bos build", deploy: "node_modules/.bin/bos build --deploy", publish: "node_modules/.bin/bos publish", start: "node_modules/.bin/bos start", typecheck: buildRootTypecheckScript(sections), lint: "biome check .", "lint:fix": "biome check --write .", format: "biome format --write .", "format:check": "biome format .", changeset: "changeset", version: "changeset version", release: "echo 'Packages versioned - app release handled by workflow'", postinstall: "node_modules/.bin/bos types gen || true", "types:gen": "node_modules/.bin/bos types gen", bos: "node_modules/.bin/bos" }; if (sections.api) { scripts["db:push"] = "bun run --cwd api drizzle-kit push"; scripts["db:studio"] = "bun run --cwd api drizzle-kit studio"; scripts["db:generate"] = "bun run --cwd api drizzle-kit generate"; scripts["db:migrate"] = "bun run --cwd api drizzle-kit migrate"; scripts["test:api"] = "cd api && bun run test tests/integration/ tests/unit/"; scripts["test:integration"] = "cd api && bun run test tests/integration/"; } if (sections.host) scripts["test:e2e"] = "bun run --cwd host test:e2e"; if (sections.api && sections.host) scripts.test = "bun run test:api && bun run test:e2e"; else if (sections.api) scripts.test = "bun run test:api"; else if (sections.host) scripts.test = "bun run test:e2e"; if (sections.api || sections.host) { scripts["dev:postgres"] = "docker compose up -d --wait && bun run dev"; scripts["dev:postgres:down"] = "docker compose down"; scripts["dev:postgres:reset"] = "docker compose down -v && docker compose up -d --wait"; } if (sections.ui) scripts["dev:ui"] = "node_modules/.bin/bos dev --ui local --api remote"; if (sections.api) scripts["dev:api"] = "node_modules/.bin/bos dev --ui remote --api local"; return scripts; } async function personalizeConfig(destination, opts) { const has = (section) => opts.overrides.includes(section); const preservedAuth = (opts.mode === "sync" && opts.existingConfig?.app && typeof opts.existingConfig.app === "object" ? opts.existingConfig.app : void 0)?.auth; const explicitRootKeys = new Set(Object.entries(opts).filter(([key, value]) => value !== void 0 && ![ "extendsAccount", "extendsGateway", "plugins", "overrides", "pluginRoutes", "workspaceOpts", "mode", "existingConfig" ].includes(key)).map(([key]) => key)); const configPath = (0, node_path.join)(destination, "bos.config.json"); if ((0, node_fs.existsSync)(configPath)) { const config = JSON.parse((0, node_fs.readFileSync)(configPath, "utf-8")); config.extends = `bos://${opts.extendsAccount}/${opts.extendsGateway}`; if (opts.account) config.account = opts.account; if (opts.domain) config.domain = opts.domain; if (opts.repository) config.repository = opts.repository; else delete config.repository; for (const field of [ "title", "description", "testnet", "staging" ]) if (!(field in opts)) delete config[field]; if (config.app && typeof config.app === "object") { const app = config.app; for (const entryKey of Object.keys(app)) { if (!has(entryKey) && (entryKey === "host" || entryKey === "ui" || entryKey === "api")) { delete app[entryKey]; continue; } if (entryKey === "auth") { delete app[entryKey]; continue; } const entry = app[entryKey]; if (entry && typeof entry === "object") stripProductionFields(entry); } if (preservedAuth !== void 0) app.auth = preservedAuth; if (Object.keys(app).length === 0) delete config.app; } if (has("plugins")) { if (config.plugins && typeof config.plugins === "object") { const plugins = config.plugins; if (opts.plugins !== void 0) { for (const pluginKey of Object.keys(plugins)) if (!opts.plugins.includes(pluginKey)) delete plugins[pluginKey]; } for (const pluginKey of Object.keys(plugins)) { const plugin = plugins[pluginKey]; let pluginObj; if (typeof plugin === "string") { pluginObj = { extends: plugin }; plugins[pluginKey] = pluginObj; } else if (plugin && typeof plugin === "object") { pluginObj = { ...plugin }; plugins[pluginKey] = pluginObj; } else continue; stripProductionFields(pluginObj); } if (Object.keys(plugins).length === 0) delete config.plugins; } } else delete config.plugins; if (opts.mode === "sync" && opts.existingConfig) { const managedRootKeys = new Set([ "extends", "account", "domain", "app", "plugins", "shared" ]); const preservedRootKeys = new Set([ ...managedRootKeys, ...Object.keys(opts.existingConfig), ...explicitRootKeys ]); for (const key of Object.keys(config)) if (!preservedRootKeys.has(key)) delete config[key]; for (const [key, value] of Object.entries(opts.existingConfig)) if (!(key in config) && !managedRootKeys.has(key) && !explicitRootKeys.has(key)) config[key] = value; } await require_save_config.saveBosConfig(destination, config); } const pkgPath = (0, node_path.join)(destination, "package.json"); if ((0, node_fs.existsSync)(pkgPath)) { const pkg = JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf-8")); const childScripts = buildChildRootScripts({ ui: has("ui"), api: has("api"), host: has("host"), plugins: has("plugins") }); if (typeof pkg.name !== "string" || pkg.name.length === 0) pkg.name = "monorepo"; pkg.private = true; pkg.type = "module"; delete pkg.module; delete pkg.peerDependencies; if (pkg.workspaces && typeof pkg.workspaces === "object") { const ws = pkg.workspaces; if (Array.isArray(ws.packages)) { ws.packages = ws.packages.filter((p) => { if (p.startsWith("packages/")) return false; if (p === "ui") return has("ui"); if (p === "api") return has("api"); if (p === "host") return has("host"); if (p.startsWith("plugins/")) return false; return true; }); if (has("plugins")) { if (!ws.packages.includes("plugins/*")) ws.packages.push("plugins/*"); } } } if (!pkg.scripts || typeof pkg.scripts !== "object") pkg.scripts = {}; const scripts = pkg.scripts; for (const [key, value] of Object.entries(childScripts)) scripts[key] = value; for (const obsoleteScript of [ "init", "sync-catalog", "db:push", "db:studio", "db:generate", "db:migrate", "test", "test:api", "test:integration", "test:e2e", "dev:postgres", "dev:postgres:down", "dev:postgres:reset", "dev:ui", "dev:api" ]) if (!(obsoleteScript in childScripts)) delete scripts[obsoleteScript]; if (pkg.devDependencies && typeof pkg.devDependencies === "object") { const deps = pkg.devDependencies; delete deps["every-plugin"]; delete deps["everything-dev"]; } if (!pkg.workspaces || typeof pkg.workspaces !== "object") pkg.workspaces = { packages: [], catalog: {} }; const workspaces = pkg.workspaces; if (!workspaces.catalog || typeof workspaces.catalog !== "object") workspaces.catalog = {}; if (!pkg.dependencies) pkg.dependencies = {}; const deps = pkg.dependencies; const spec = opts.workspaceOpts?.sourceDir ? require_manifest_normalizer.loadManifestNormalizationSpec(opts.workspaceOpts.sourceDir) : null; if (spec) { workspaces.catalog["everything-dev"] = spec.rootCatalog["everything-dev"]; workspaces.catalog["every-plugin"] = spec.rootCatalog["every-plugin"]; } const frameworkCatalog = (await resolveCatalogChainSource({ extendsAccount: opts.extendsAccount, extendsGateway: opts.extendsGateway, sourceDir: opts.workspaceOpts?.sourceDir })).catalog; for (const [name, version] of Object.entries(frameworkCatalog)) workspaces.catalog[name] = version; if (!deps["everything-dev"]) deps["everything-dev"] = "catalog:"; if (!deps["every-plugin"]) deps["every-plugin"] = "catalog:"; (0, node_fs.writeFileSync)(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`); } const apiTsConfigPath = (0, node_path.join)(destination, "api", "tsconfig.json"); if ((0, node_fs.existsSync)(apiTsConfigPath)) { const apiTsConfig = JSON.parse((0, node_fs.readFileSync)(apiTsConfigPath, "utf-8")); if (apiTsConfig.files) { const validFiles = apiTsConfig.files.filter((f) => (0, node_fs.existsSync)((0, node_path.join)(destination, "api", f))); if (validFiles.length !== apiTsConfig.files.length) { if (validFiles.length === 0) delete apiTsConfig.files; else apiTsConfig.files = validFiles; (0, node_fs.writeFileSync)(apiTsConfigPath, `${JSON.stringify(apiTsConfig, null, 2)}\n`); } } } await resolveWorkspaceRefs(destination, opts.workspaceOpts); if (has("ui")) { const genContractPath = (0, node_path.join)(destination, "ui", "src", "lib", "api-types.gen.ts"); if (!(0, node_fs.existsSync)(genContractPath)) { (0, node_fs.mkdirSync)((0, node_path.dirname)(genContractPath), { recursive: true }); (0, node_fs.writeFileSync)(genContractPath, `export type ApiContract = Record<string, never>;\n`); } } if (has("api")) { const pluginsClientGenPath = (0, node_path.join)(destination, "api", "src", "lib", "plugins-types.gen.ts"); if (!(0, node_fs.existsSync)(pluginsClientGenPath)) { (0, node_fs.mkdirSync)((0, node_path.dirname)(pluginsClientGenPath), { recursive: true }); (0, node_fs.writeFileSync)(pluginsClientGenPath, `import type { ContractRouterClient, AnyContractRouter } from "@orpc/contract";\ntype ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\nexport type PluginsClient = Record<string, never>;\n`); } } const authTypesContent = generateAuthTypesTemplate(); const authTypesPaths = []; if (has("ui")) authTypesPaths.push((0, node_path.join)(destination, "ui", "src", "lib", "auth-types.gen.ts")); if (has("api")) authTypesPaths.push((0, node_path.join)(destination, "api", "src", "lib", "auth-types.gen.ts")); if (has("host") && (0, node_fs.existsSync)((0, node_path.join)(destination, "host", "src"))) authTypesPaths.push((0, node_path.join)(destination, "host", "src", "lib", "auth-types.gen.ts")); for (const authTypesGenPath of authTypesPaths) if (!(0, node_fs.existsSync)(authTypesGenPath)) { (0, node_fs.mkdirSync)((0, node_path.dirname)(authTypesGenPath), { recursive: true }); (0, node_fs.writeFileSync)(authTypesGenPath, authTypesContent); } if (has("plugins")) for (const plugin of opts.plugins ?? []) { const pluginSrcDir = (0, node_path.join)(destination, "plugins", plugin, "src"); const pluginIndexPath = (0, node_path.join)(pluginSrcDir, "index.ts"); const pluginClientGenPath = (0, node_path.join)(pluginSrcDir, "plugins-client.gen.ts"); if (!(0, node_fs.existsSync)(pluginIndexPath) || (0, node_fs.existsSync)(pluginClientGenPath)) continue; if (!(0, node_fs.readFileSync)(pluginIndexPath, "utf-8").includes("./plugins-client.gen")) continue; (0, node_fs.writeFileSync)(pluginClientGenPath, "export type PluginsClient = Record<string, never>;\n"); } } function generateAuthTypesTemplate() { return `import type { Auth } from "better-auth"; export type { Auth } from "better-auth"; export type AuthSessionUser = NonNullable<Auth["$Infer"]["Session"]["user"]> & { role?: string | null; isAnonymous?: boolean | null; walletAddress?: string | null; banned?: boolean | null; }; export type AuthSessionData = NonNullable<Auth["$Infer"]["Session"]["session"]> & { activeOrganizationId?: string | null; }; export type AuthSession = { user: AuthSessionUser | null; session: AuthSessionData | null; }; export interface AuthOrganizationContext { activeOrganizationId: string | null; organization: { id: string; name: string; slug: string; logo?: string | null; metadata?: Record<string, unknown> } | null; member: { id: string; role: string } | null; isPersonal: boolean; hasOrganization: boolean; } export interface AuthRequestContext { user: AuthSessionUser | null; userId: string | null; isAuthenticated: boolean; authMethod: "session" | "apiKey" | "anonymous" | "none"; near: { primaryAccountId: string | null; linkedAccounts: Array<{ accountId: string; network: string; publicKey: string; isPrimary: boolean }>; hasNearAccount: boolean; }; organization: AuthOrganizationContext; organizations?: Array<{ id: string; role: string; name?: string; slug?: string }>; } export type AuthActiveMember = { id: string | null; role: string | null; organizationId: string | null }; export type AuthOrganization = NonNullable<AuthOrganizationContext["organization"]>; export type AuthOrganizationMember = NonNullable<AuthOrganizationContext["member"]>; export type AuthOrganizationSummary = NonNullable<AuthRequestContext["organizations"]>[number]; export type AuthBaseSession = Auth["$Infer"]["Session"]; export type createAuthInstance = never; export interface AuthServices { auth: Auth; db: unknown; driver: { close(): Promise<void> }; handler: (req: Request) => Promise<Response>; } `; } async function runBunInstall(destination, spinner) { await runWithProgress("bun", ["install", "--ignore-scripts"], destination, spinner, "Installing dependencies"); } async function runBunInstallForUpgrade(destination, spinner) { await runWithProgress("bun", ["install", "--force"], destination, spinner, "Installing dependencies"); } async function runTypesGen(destination, spinner) { if ((0, node_fs.existsSync)((0, node_path.join)(destination, "node_modules", ".bin", "bos"))) { await runWithProgress("node_modules/.bin/bos", ["types", "gen"], destination, spinner, "Generating types"); return; } throw new Error("Unable to locate bos CLI for types generation"); } async function runDockerComposeUp(destination) { await execCommand("docker", [ "compose", "up", "-d", "--wait" ], destination, { stdio: "inherit" }); } async function runWithProgress(command, args, cwd, spinner, label) { const child = (0, execa.execa)(command, args, { cwd, stdio: "inherit", timeout: COMMAND_TIMEOUTS[command] ?? 2 * 6e4 }); if (spinner) { const start = Date.now(); const interval = setInterval(() => { const elapsed = Math.round((Date.now() - start) / 1e3); spinner.message(`${label}... (${elapsed}s)`); }, 2e3); try { await child; } finally { clearInterval(interval); } } else await child; } function stripOrphanedWorkspacesFromLockfile(lockfilePath, allowedWorkspaces) { if (!(0, node_fs.existsSync)(lockfilePath)) return; const content = (0, node_fs.readFileSync)(lockfilePath, "utf-8"); let lockfile; try { lockfile = JSON.parse(content); } catch { return; } const workspaces = lockfile.workspaces; if (!workspaces || typeof workspaces !== "object") return; const workspaceMap = workspaces; const allowed = new Set(["", ...allowedWorkspaces]); const keys = Object.keys(workspaceMap); let changed = false; for (const key of keys) { if (allowed.has(key)) continue; if (allowedWorkspaces.some((pattern) => pattern.endsWith("/*") && key.startsWith(pattern.slice(0, -1)))) continue; delete workspaceMap[key]; changed = true; } if (changed) (0, node_fs.writeFileSync)(lockfilePath, `${JSON.stringify(lockfile, null, 2)}\n`); } function removeInitLockfile(lockfilePath) { if (!(0, node_fs.existsSync)(lockfilePath)) return; (0, node_fs.rmSync)(lockfilePath, { force: true }); } function readJsonFile(filePath) { return JSON.parse((0, node_fs.readFileSync)(filePath, "utf-8")); } async function scaffoldMinimalProject(destination, parentConfig, opts) { (0, node_fs.mkdirSync)(destination, { recursive: true }); const has = (section) => opts.overrides.includes(section); const config = { extends: `bos://${opts.extendsAccount}/${opts.extendsGateway}`, account: opts.account || opts.extendsAccount, ...opts.domain ? { domain: opts.domain } : {}, ...opts.repository ? { repository: opts.repository } : {}, ...opts.title ? { title: opts.title } : {}, ...opts.description ? { description: opts.description } : {} }; if (parentConfig.app && typeof parentConfig.app === "object") { const app = {}; const parentApp = parentConfig.app; if (has("host") && parentApp.host) { app.host = { ...parentApp.host }; stripProductionFields(app.host); } if (has("ui") && parentApp.ui) { app.ui = { ...parentApp.ui }; stripProductionFields(app.ui); } if (has("api") && parentApp.api) { app.api = { ...parentApp.api }; stripProductionFields(app.api); } if (Object.keys(app).length > 0) config.app = app; } if (has("plugins") && opts.plugins && opts.plugins.length > 0 && parentConfig.plugins) { const plugins = {}; for (const key of opts.plugins) { const parentPlugin = parentConfig.plugins?.[key]; if (parentPlugin) if (typeof parentPlugin === "string") plugins[key] = { extends: parentPlugin }; else { const pluginCopy = { ...parentPlugin }; stripProductionFields(pluginCopy); plugins[key] = pluginCopy; } } config.plugins = plugins; } await require_save_config.saveBosConfig(destination, config); const workspacePackages = []; for (const section of opts.overrides) workspacePackages.push(...OVERRIDE_WORKSPACE_MAP[section]); if (has("plugins")) workspacePackages.push("plugins/*"); const catalog = (await resolveCatalogChainSource({ extendsAccount: opts.extendsAccount, extendsGateway: opts.extendsGateway })).catalog; const pkg = { name: "monorepo", private: true, type: "module", scripts: buildChildRootScripts({ ui: has("ui"), api: has("api"), host: has("host"), plugins: has("plugins") }), dependencies: { "everything-dev": "catalog:", "every-plugin": "catalog:" }, devDependencies: {}, workspaces: { packages: workspacePackages, catalog } }; (0, node_fs.writeFileSync)((0, node_path.join)(destination, "package.json"), `${JSON.stringify(pkg, null, 2)}\n`); (0, node_fs.writeFileSync)((0, node_path.join)(destination, ".gitignore"), generateGitignore()); return 4; } async function resolveWorkspaceRefs(destination, options) { await require_manifest_normalizer.normalizePackageManifestsInTree({ sourceRootDir: options?.sourceDir ?? destination, targetDir: destination, resolveCatalogRefs: false, preserveCatalogRefs: true, removeWorkspaceDeps: ["host"] }); } async function writeInitSnapshot(destination, extendsAccount, extendsGateway, sourceDir, patterns, _options) { const allFiles = /* @__PURE__ */ new Set(); for (const pattern of patterns) { const matches = await (0, glob.glob)(pattern, { cwd: sourceDir, nodir: true, dot: true, absolute: false, ignore: [ "**/node_modules/**", "**/.git/**", "**/dist/**", "**/.bos/**" ] }); for (const match of matches) allFiles.add(match); } const fileHashes = {}; for (const filePath of allFiles) { const src = (0, node_path.join)(sourceDir, filePath); if (!(0, node_fs.lstatSync)(src).isFile()) continue; const content = (0, node_fs.readFileSync)(src); const destPath = sourcePathToDestinationPath(filePath); fileHashes[destPath] = computeHash(content); } await require_snapshot.writeSnapshot(destination, { parentRef: `bos://${extendsAccount}/${extendsGateway}`, files: fileHashes }); } function computeHash(data) { return (0, node_crypto.createHash)("sha256").update(data).digest("hex").substring(0, 16); } function mkTmpDir(prefix) { return (0, node_fs.mkdtempSync)((0, node_path.join)((0, node_os.tmpdir)(), `${prefix}-`)); } async function generateDatabaseMigrations(destination) { const drizzleConfigs = await (0, glob.glob)("**/drizzle.config.ts", { cwd: destination, nodir: true, dot: false, absolute: false, ignore: ["**/node_modules/**"] }); for (const configPath of drizzleConfigs) { const workspaceDir = (0, node_path.dirname)(configPath); const pkgPath = (0, node_path.join)(destination, workspaceDir, "package.json"); if (!(0, node_fs.existsSync)(pkgPath)) continue; if (!JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf-8")).scripts?.["db:generate"]) continue; await execCommand("bun", ["run", "db:generate"], (0, node_path.join)(destination, workspaceDir)); } } const COMMAND_TIMEOUTS = { bun: 5 * 6e4, docker: 5 * 6e4, node_modules: 2 * 6e4, tar: 6e4 }; async function execCommand(command, args, cwd, options) { const timeout = COMMAND_TIMEOUTS[command] ?? 2 * 6e4; await (0, execa.execa)(command, args, { cwd, stdio: options?.stdio ?? "pipe", timeout }); } function generateGitignore() { return `node_modules/ dist/ .env .bos/ *.gen.ts *.gen.tsx `; } //#endregion exports.INIT_ROOT_PATTERNS = INIT_ROOT_PATTERNS; exports.buildChildRootScripts = buildChildRootScripts; exports.buildInitPatterns = buildInitPatterns; exports.copyFilteredFiles = copyFilteredFiles; exports.detectGitRemoteUrl = detectGitRemoteUrl; exports.downloadTarball = downloadTarball; exports.execCommand = execCommand; exports.fetchParentConfig = fetchParentConfig; exports.generateDatabaseMigrations = generateDatabaseMigrations; exports.personalizeConfig = personalizeConfig; exports.removeInitLockfile = removeInitLockfile; exports.resolveCatalogChainSource = resolveCatalogChainSource; exports.resolveRepositoryViaExtendsChain = resolveRepositoryViaExtendsChain; exports.resolveSourceDir = resolveSourceDir; exports.runBunInstall = runBunInstall; exports.runBunInstallForUpgrade = runBunInstallForUpgrade; exports.runDockerComposeUp = runDockerComposeUp; exports.runTypesGen = runTypesGen; exports.scaffoldMinimalProject = scaffoldMinimalProject; exports.sourcePathToDestinationPath = sourcePathToDestinationPath; exports.stripOrphanedWorkspacesFromLockfile = stripOrphanedWorkspacesFromLockfile; exports.writeInitSnapshot = writeInitSnapshot; //# sourceMappingURL=init.cjs.map