everything-dev 1.8.8 → 1.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-contract.cjs +4 -0
- package/dist/api-contract.cjs.map +1 -1
- package/dist/api-contract.mjs +4 -0
- package/dist/api-contract.mjs.map +1 -1
- package/dist/cli/init.cjs +3 -2
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +1 -0
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +1 -0
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +3 -2
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/sync.cjs +48 -1
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +48 -1
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/plugin.d.cts +1 -1
- package/dist/plugin.d.mts +1 -1
- package/dist/service-descriptor.d.cts +1 -1
- package/dist/service-descriptor.d.mts +1 -1
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/package.json +1 -1
- package/src/api-contract.ts +4 -0
- package/src/cli/init.ts +11 -7
- package/src/cli/sync.ts +84 -0
package/dist/api-contract.cjs
CHANGED
|
@@ -187,6 +187,10 @@ async function syncApiContractBridge(opts) {
|
|
|
187
187
|
if (authSource.generatedPath) generatedPath = authSource.generatedPath;
|
|
188
188
|
}
|
|
189
189
|
for (const [key, plugin] of pluginEntries) {
|
|
190
|
+
if (!plugin.url) {
|
|
191
|
+
console.warn(`[API Contract] Skipping plugin "${key}" — no URL resolved (local path missing and no production URL configured)`);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
190
194
|
const source = await resolveContractSource({
|
|
191
195
|
configDir: opts.configDir,
|
|
192
196
|
runtimeDir,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-contract.cjs","names":[],"sources":["../src/api-contract.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport type { RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface ApiPluginManifest {\n schemaVersion: 1;\n kind: \"every-plugin/manifest\";\n plugin: {\n name: string;\n version: string;\n };\n runtime: {\n remoteEntry: string;\n };\n contract?: {\n kind: \"orpc\";\n types: {\n path: string;\n exportName: string;\n typeName: string;\n sha256?: string;\n };\n };\n}\n\ninterface ContractSource {\n key: string;\n importName: string;\n sourceFilePath: string;\n generatedPath?: string;\n}\n\nfunction sha256(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction trimTrailingSlash(input: string): string {\n return input.replace(/\\/$/, \"\");\n}\n\nfunction sanitizeIdentifier(input: string): string {\n return input.replace(/[^A-Za-z0-9_]/g, \"_\").replace(/^[^A-Za-z_]+/, \"_\");\n}\n\nfunction toImportPath(fromFile: string, targetFile: string): string {\n const rel = relative(dirname(fromFile), targetFile).replace(/\\\\/g, \"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction writeFileIfChanged(filePath: string, content: string) {\n try {\n if (readFileSync(filePath, \"utf8\") === content) return false;\n } catch {\n // file does not exist yet\n }\n\n writeFileSync(filePath, content);\n return true;\n}\n\nfunction getApiPluginManifestUrl(apiBaseUrl: string): string {\n return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;\n}\n\nasync function fetchApiPluginManifest(apiBaseUrl: string): Promise<ApiPluginManifest> {\n const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));\n if (!response.ok) {\n throw new Error(\n `Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`,\n );\n }\n\n const manifest = (await response.json()) as ApiPluginManifest;\n if (manifest.schemaVersion !== 1 || manifest.kind !== \"every-plugin/manifest\") {\n throw new Error(\"Unsupported API plugin manifest format\");\n }\n\n return manifest;\n}\n\nfunction localApiContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"api\", \"src\", \"contract.ts\");\n return {\n key: \"api\",\n importName: \"BaseApiContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nfunction localAuthContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"plugins\", \"auth\", \"src\", \"contract.ts\");\n return {\n key: \"auth\",\n importName: \"authContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nasync function remoteContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n name: string;\n baseUrl: string;\n generatedSubdir: string;\n}): Promise<ContractSource> {\n const manifest = await fetchApiPluginManifest(opts.baseUrl);\n if (!manifest.contract) {\n throw new Error(\n `Plugin manifest for ${manifest.plugin.name} does not advertise contract types`,\n );\n }\n\n const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\\.\\//, \"\")}`;\n const contractResponse = await fetch(contractUrl);\n if (!contractResponse.ok) {\n throw new Error(\n `Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`,\n );\n }\n\n const contractTypes = await contractResponse.text();\n if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) {\n throw new Error(\"Fetched contract types failed checksum verification\");\n }\n\n const generatedPath = join(opts.runtimeDir, opts.generatedSubdir, \"contract.d.ts\");\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, contractTypes);\n\n return {\n key: opts.name,\n importName: `${sanitizeIdentifier(opts.name)}Contract`,\n sourceFilePath: generatedPath,\n generatedPath,\n };\n}\n\nasync function resolveContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n key: string;\n source: RuntimePluginConfig | { url: string; localPath?: string; name: string } | null;\n baseUrl: string;\n generatedSubdir: string;\n localSourceFactory?: (configDir: string) => ContractSource;\n}): Promise<ContractSource> {\n if (opts.key === \"api\") {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath != null && localPath !== \"\") {\n return {\n key: opts.key,\n importName: \"BaseApiContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return localApiContractSource(opts.configDir);\n }\n }\n\n if (opts.key === \"auth\" && opts.localSourceFactory) {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath != null && localPath !== \"\") {\n return {\n key: opts.key,\n importName: \"authContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return opts.localSourceFactory(opts.configDir);\n }\n }\n\n if (\n opts.source &&\n \"localPath\" in opts.source &&\n opts.source.localPath != null &&\n opts.source.localPath !== \"\"\n ) {\n return {\n key: opts.key,\n importName: `${sanitizeIdentifier(opts.key)}Contract`,\n sourceFilePath: join(opts.source.localPath, \"src\", \"contract.ts\"),\n };\n }\n\n return remoteContractSource({\n configDir: opts.configDir,\n runtimeDir: opts.runtimeDir,\n name: opts.key,\n baseUrl: opts.baseUrl,\n generatedSubdir: opts.generatedSubdir,\n });\n}\n\nfunction writeGeneratedFiles(opts: {\n configDir: string;\n sources: ContractSource[];\n pluginKeys: string[];\n authSource: ContractSource | null;\n}) {\n const baseSource = opts.sources.find((source) => source.key === \"api\");\n const pluginSources = opts.pluginKeys\n .map((key) => opts.sources.find((entry) => entry.key === key))\n .filter((source): source is ContractSource => Boolean(source));\n\n if (!baseSource) {\n throw new Error(\"API contract source is required to generate the aggregate contract\");\n }\n\n // --- Generate ui/src/api-contract.gen.ts ---\n const uiContractPath = join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\");\n const uiLines: string[] = [];\n\n for (const source of opts.sources) {\n const importPath = toImportPath(uiContractPath, source.sourceFilePath);\n uiLines.push(`import type { ContractType as ${source.importName} } from \"${importPath}\";`);\n }\n\n uiLines.push(\"\");\n\n const compositeParts: string[] = [];\n if (opts.authSource) {\n compositeParts.push(`auth: ${opts.authSource.importName}`);\n }\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key) ? source.key : JSON.stringify(source.key);\n compositeParts.push(`${key}: ${source.importName}`);\n }\n\n if (compositeParts.length === 0) {\n uiLines.push(`export type ApiContract = ${baseSource.importName};`);\n } else {\n uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);\n for (const part of compositeParts) {\n uiLines.push(` ${part};`);\n }\n uiLines.push(\"};\");\n }\n mkdirSync(dirname(uiContractPath), { recursive: true });\n writeFileIfChanged(uiContractPath, `${uiLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/plugins-client.gen.ts ---\n const pluginsClientPath = join(opts.configDir, \"api\", \"src\", \"plugins-client.gen.ts\");\n const pluginsClientLines: string[] = [];\n\n for (const source of pluginSources) {\n const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);\n pluginsClientLines.push(\n `import type { ContractType as ${source.importName} } from \"${importPath}\";`,\n );\n }\n\n pluginsClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n pluginsClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n pluginsClientLines.push(\"\");\n\n if (pluginSources.length === 0) {\n pluginsClientLines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n pluginsClientLines.push(\"export type PluginsClient = {\");\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);\n }\n pluginsClientLines.push(\"};\");\n }\n\n mkdirSync(dirname(pluginsClientPath), { recursive: true });\n writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/auth-client.gen.ts ---\n if (opts.authSource) {\n const authClientPath = join(opts.configDir, \"api\", \"src\", \"auth-client.gen.ts\");\n const authClientLines: string[] = [];\n\n const importPath = toImportPath(authClientPath, opts.authSource.sourceFilePath);\n authClientLines.push(\n `import type { ContractType as ${opts.authSource.importName} } from \"${importPath}\";`,\n );\n authClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n authClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n authClientLines.push(\"\");\n authClientLines.push(`export type AuthClient = ClientFactory<${opts.authSource.importName}>;`);\n\n mkdirSync(dirname(authClientPath), { recursive: true });\n writeFileIfChanged(authClientPath, `${authClientLines.join(\"\\n\")}\\n`);\n }\n\n return uiContractPath;\n}\n\nexport async function syncApiContractBridge(opts: {\n configDir: string;\n runtimeConfig: RuntimeConfig;\n apiBaseUrl: string;\n}): Promise<{\n bridgePath: string;\n generatedPath: string | null;\n manifest: ApiPluginManifest | null;\n source: \"local\" | \"remote\";\n}> {\n const runtimeDir = join(opts.configDir, \".bos\", \"generated\");\n const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n );\n const sources: ContractSource[] = [];\n let manifest: ApiPluginManifest | null = null;\n let generatedPath: string | null = null;\n let authSource: ContractSource | null = null;\n\n const baseSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"api\",\n source: opts.runtimeConfig.api,\n baseUrl: opts.apiBaseUrl,\n generatedSubdir: \"api\",\n });\n sources.push(baseSource);\n\n if (opts.runtimeConfig.auth) {\n authSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"auth\",\n source: opts.runtimeConfig.auth,\n baseUrl: opts.runtimeConfig.auth.url,\n generatedSubdir: \"auth\",\n localSourceFactory: localAuthContractSource,\n });\n sources.push(authSource);\n if (authSource.generatedPath) {\n generatedPath = authSource.generatedPath;\n }\n }\n\n for (const [key, plugin] of pluginEntries) {\n const source = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key,\n source: plugin,\n baseUrl: plugin.url,\n generatedSubdir: `plugins/${key}`,\n });\n sources.push(source);\n if (source.generatedPath) {\n generatedPath = source.generatedPath;\n }\n }\n\n const allPluginKeys = pluginEntries.map(([key]) => key);\n\n writeGeneratedFiles({\n configDir: opts.configDir,\n sources,\n pluginKeys: allPluginKeys,\n authSource,\n });\n\n if (opts.runtimeConfig.api.source !== \"local\") {\n manifest = await fetchApiPluginManifest(opts.apiBaseUrl);\n }\n\n return {\n bridgePath: join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\"),\n generatedPath,\n manifest,\n source: opts.runtimeConfig.api.source,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api-contract.cjs","names":[],"sources":["../src/api-contract.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport type { RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface ApiPluginManifest {\n schemaVersion: 1;\n kind: \"every-plugin/manifest\";\n plugin: {\n name: string;\n version: string;\n };\n runtime: {\n remoteEntry: string;\n };\n contract?: {\n kind: \"orpc\";\n types: {\n path: string;\n exportName: string;\n typeName: string;\n sha256?: string;\n };\n };\n}\n\ninterface ContractSource {\n key: string;\n importName: string;\n sourceFilePath: string;\n generatedPath?: string;\n}\n\nfunction sha256(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction trimTrailingSlash(input: string): string {\n return input.replace(/\\/$/, \"\");\n}\n\nfunction sanitizeIdentifier(input: string): string {\n return input.replace(/[^A-Za-z0-9_]/g, \"_\").replace(/^[^A-Za-z_]+/, \"_\");\n}\n\nfunction toImportPath(fromFile: string, targetFile: string): string {\n const rel = relative(dirname(fromFile), targetFile).replace(/\\\\/g, \"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction writeFileIfChanged(filePath: string, content: string) {\n try {\n if (readFileSync(filePath, \"utf8\") === content) return false;\n } catch {\n // file does not exist yet\n }\n\n writeFileSync(filePath, content);\n return true;\n}\n\nfunction getApiPluginManifestUrl(apiBaseUrl: string): string {\n return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;\n}\n\nasync function fetchApiPluginManifest(apiBaseUrl: string): Promise<ApiPluginManifest> {\n const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));\n if (!response.ok) {\n throw new Error(\n `Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`,\n );\n }\n\n const manifest = (await response.json()) as ApiPluginManifest;\n if (manifest.schemaVersion !== 1 || manifest.kind !== \"every-plugin/manifest\") {\n throw new Error(\"Unsupported API plugin manifest format\");\n }\n\n return manifest;\n}\n\nfunction localApiContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"api\", \"src\", \"contract.ts\");\n return {\n key: \"api\",\n importName: \"BaseApiContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nfunction localAuthContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"plugins\", \"auth\", \"src\", \"contract.ts\");\n return {\n key: \"auth\",\n importName: \"authContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nasync function remoteContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n name: string;\n baseUrl: string;\n generatedSubdir: string;\n}): Promise<ContractSource> {\n const manifest = await fetchApiPluginManifest(opts.baseUrl);\n if (!manifest.contract) {\n throw new Error(\n `Plugin manifest for ${manifest.plugin.name} does not advertise contract types`,\n );\n }\n\n const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\\.\\//, \"\")}`;\n const contractResponse = await fetch(contractUrl);\n if (!contractResponse.ok) {\n throw new Error(\n `Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`,\n );\n }\n\n const contractTypes = await contractResponse.text();\n if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) {\n throw new Error(\"Fetched contract types failed checksum verification\");\n }\n\n const generatedPath = join(opts.runtimeDir, opts.generatedSubdir, \"contract.d.ts\");\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, contractTypes);\n\n return {\n key: opts.name,\n importName: `${sanitizeIdentifier(opts.name)}Contract`,\n sourceFilePath: generatedPath,\n generatedPath,\n };\n}\n\nasync function resolveContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n key: string;\n source: RuntimePluginConfig | { url: string; localPath?: string; name: string } | null;\n baseUrl: string;\n generatedSubdir: string;\n localSourceFactory?: (configDir: string) => ContractSource;\n}): Promise<ContractSource> {\n if (opts.key === \"api\") {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath != null && localPath !== \"\") {\n return {\n key: opts.key,\n importName: \"BaseApiContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return localApiContractSource(opts.configDir);\n }\n }\n\n if (opts.key === \"auth\" && opts.localSourceFactory) {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath != null && localPath !== \"\") {\n return {\n key: opts.key,\n importName: \"authContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return opts.localSourceFactory(opts.configDir);\n }\n }\n\n if (\n opts.source &&\n \"localPath\" in opts.source &&\n opts.source.localPath != null &&\n opts.source.localPath !== \"\"\n ) {\n return {\n key: opts.key,\n importName: `${sanitizeIdentifier(opts.key)}Contract`,\n sourceFilePath: join(opts.source.localPath, \"src\", \"contract.ts\"),\n };\n }\n\n return remoteContractSource({\n configDir: opts.configDir,\n runtimeDir: opts.runtimeDir,\n name: opts.key,\n baseUrl: opts.baseUrl,\n generatedSubdir: opts.generatedSubdir,\n });\n}\n\nfunction writeGeneratedFiles(opts: {\n configDir: string;\n sources: ContractSource[];\n pluginKeys: string[];\n authSource: ContractSource | null;\n}) {\n const baseSource = opts.sources.find((source) => source.key === \"api\");\n const pluginSources = opts.pluginKeys\n .map((key) => opts.sources.find((entry) => entry.key === key))\n .filter((source): source is ContractSource => Boolean(source));\n\n if (!baseSource) {\n throw new Error(\"API contract source is required to generate the aggregate contract\");\n }\n\n // --- Generate ui/src/api-contract.gen.ts ---\n const uiContractPath = join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\");\n const uiLines: string[] = [];\n\n for (const source of opts.sources) {\n const importPath = toImportPath(uiContractPath, source.sourceFilePath);\n uiLines.push(`import type { ContractType as ${source.importName} } from \"${importPath}\";`);\n }\n\n uiLines.push(\"\");\n\n const compositeParts: string[] = [];\n if (opts.authSource) {\n compositeParts.push(`auth: ${opts.authSource.importName}`);\n }\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key) ? source.key : JSON.stringify(source.key);\n compositeParts.push(`${key}: ${source.importName}`);\n }\n\n if (compositeParts.length === 0) {\n uiLines.push(`export type ApiContract = ${baseSource.importName};`);\n } else {\n uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);\n for (const part of compositeParts) {\n uiLines.push(` ${part};`);\n }\n uiLines.push(\"};\");\n }\n mkdirSync(dirname(uiContractPath), { recursive: true });\n writeFileIfChanged(uiContractPath, `${uiLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/plugins-client.gen.ts ---\n const pluginsClientPath = join(opts.configDir, \"api\", \"src\", \"plugins-client.gen.ts\");\n const pluginsClientLines: string[] = [];\n\n for (const source of pluginSources) {\n const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);\n pluginsClientLines.push(\n `import type { ContractType as ${source.importName} } from \"${importPath}\";`,\n );\n }\n\n pluginsClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n pluginsClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n pluginsClientLines.push(\"\");\n\n if (pluginSources.length === 0) {\n pluginsClientLines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n pluginsClientLines.push(\"export type PluginsClient = {\");\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);\n }\n pluginsClientLines.push(\"};\");\n }\n\n mkdirSync(dirname(pluginsClientPath), { recursive: true });\n writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/auth-client.gen.ts ---\n if (opts.authSource) {\n const authClientPath = join(opts.configDir, \"api\", \"src\", \"auth-client.gen.ts\");\n const authClientLines: string[] = [];\n\n const importPath = toImportPath(authClientPath, opts.authSource.sourceFilePath);\n authClientLines.push(\n `import type { ContractType as ${opts.authSource.importName} } from \"${importPath}\";`,\n );\n authClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n authClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n authClientLines.push(\"\");\n authClientLines.push(`export type AuthClient = ClientFactory<${opts.authSource.importName}>;`);\n\n mkdirSync(dirname(authClientPath), { recursive: true });\n writeFileIfChanged(authClientPath, `${authClientLines.join(\"\\n\")}\\n`);\n }\n\n return uiContractPath;\n}\n\nexport async function syncApiContractBridge(opts: {\n configDir: string;\n runtimeConfig: RuntimeConfig;\n apiBaseUrl: string;\n}): Promise<{\n bridgePath: string;\n generatedPath: string | null;\n manifest: ApiPluginManifest | null;\n source: \"local\" | \"remote\";\n}> {\n const runtimeDir = join(opts.configDir, \".bos\", \"generated\");\n const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n );\n const sources: ContractSource[] = [];\n let manifest: ApiPluginManifest | null = null;\n let generatedPath: string | null = null;\n let authSource: ContractSource | null = null;\n\n const baseSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"api\",\n source: opts.runtimeConfig.api,\n baseUrl: opts.apiBaseUrl,\n generatedSubdir: \"api\",\n });\n sources.push(baseSource);\n\n if (opts.runtimeConfig.auth) {\n authSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"auth\",\n source: opts.runtimeConfig.auth,\n baseUrl: opts.runtimeConfig.auth.url,\n generatedSubdir: \"auth\",\n localSourceFactory: localAuthContractSource,\n });\n sources.push(authSource);\n if (authSource.generatedPath) {\n generatedPath = authSource.generatedPath;\n }\n }\n\n for (const [key, plugin] of pluginEntries) {\n if (!plugin.url) {\n console.warn(`[API Contract] Skipping plugin \"${key}\" — no URL resolved (local path missing and no production URL configured)`);\n continue;\n }\n const source = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key,\n source: plugin,\n baseUrl: plugin.url,\n generatedSubdir: `plugins/${key}`,\n });\n sources.push(source);\n if (source.generatedPath) {\n generatedPath = source.generatedPath;\n }\n }\n\n const allPluginKeys = pluginEntries.map(([key]) => key);\n\n writeGeneratedFiles({\n configDir: opts.configDir,\n sources,\n pluginKeys: allPluginKeys,\n authSource,\n });\n\n if (opts.runtimeConfig.api.source !== \"local\") {\n manifest = await fetchApiPluginManifest(opts.apiBaseUrl);\n }\n\n return {\n bridgePath: join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\"),\n generatedPath,\n manifest,\n source: opts.runtimeConfig.api.source,\n };\n}\n"],"mappings":"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"}
|
package/dist/api-contract.mjs
CHANGED
|
@@ -186,6 +186,10 @@ async function syncApiContractBridge(opts) {
|
|
|
186
186
|
if (authSource.generatedPath) generatedPath = authSource.generatedPath;
|
|
187
187
|
}
|
|
188
188
|
for (const [key, plugin] of pluginEntries) {
|
|
189
|
+
if (!plugin.url) {
|
|
190
|
+
console.warn(`[API Contract] Skipping plugin "${key}" — no URL resolved (local path missing and no production URL configured)`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
189
193
|
const source = await resolveContractSource({
|
|
190
194
|
configDir: opts.configDir,
|
|
191
195
|
runtimeDir,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-contract.mjs","names":[],"sources":["../src/api-contract.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport type { RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface ApiPluginManifest {\n schemaVersion: 1;\n kind: \"every-plugin/manifest\";\n plugin: {\n name: string;\n version: string;\n };\n runtime: {\n remoteEntry: string;\n };\n contract?: {\n kind: \"orpc\";\n types: {\n path: string;\n exportName: string;\n typeName: string;\n sha256?: string;\n };\n };\n}\n\ninterface ContractSource {\n key: string;\n importName: string;\n sourceFilePath: string;\n generatedPath?: string;\n}\n\nfunction sha256(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction trimTrailingSlash(input: string): string {\n return input.replace(/\\/$/, \"\");\n}\n\nfunction sanitizeIdentifier(input: string): string {\n return input.replace(/[^A-Za-z0-9_]/g, \"_\").replace(/^[^A-Za-z_]+/, \"_\");\n}\n\nfunction toImportPath(fromFile: string, targetFile: string): string {\n const rel = relative(dirname(fromFile), targetFile).replace(/\\\\/g, \"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction writeFileIfChanged(filePath: string, content: string) {\n try {\n if (readFileSync(filePath, \"utf8\") === content) return false;\n } catch {\n // file does not exist yet\n }\n\n writeFileSync(filePath, content);\n return true;\n}\n\nfunction getApiPluginManifestUrl(apiBaseUrl: string): string {\n return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;\n}\n\nasync function fetchApiPluginManifest(apiBaseUrl: string): Promise<ApiPluginManifest> {\n const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));\n if (!response.ok) {\n throw new Error(\n `Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`,\n );\n }\n\n const manifest = (await response.json()) as ApiPluginManifest;\n if (manifest.schemaVersion !== 1 || manifest.kind !== \"every-plugin/manifest\") {\n throw new Error(\"Unsupported API plugin manifest format\");\n }\n\n return manifest;\n}\n\nfunction localApiContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"api\", \"src\", \"contract.ts\");\n return {\n key: \"api\",\n importName: \"BaseApiContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nfunction localAuthContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"plugins\", \"auth\", \"src\", \"contract.ts\");\n return {\n key: \"auth\",\n importName: \"authContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nasync function remoteContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n name: string;\n baseUrl: string;\n generatedSubdir: string;\n}): Promise<ContractSource> {\n const manifest = await fetchApiPluginManifest(opts.baseUrl);\n if (!manifest.contract) {\n throw new Error(\n `Plugin manifest for ${manifest.plugin.name} does not advertise contract types`,\n );\n }\n\n const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\\.\\//, \"\")}`;\n const contractResponse = await fetch(contractUrl);\n if (!contractResponse.ok) {\n throw new Error(\n `Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`,\n );\n }\n\n const contractTypes = await contractResponse.text();\n if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) {\n throw new Error(\"Fetched contract types failed checksum verification\");\n }\n\n const generatedPath = join(opts.runtimeDir, opts.generatedSubdir, \"contract.d.ts\");\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, contractTypes);\n\n return {\n key: opts.name,\n importName: `${sanitizeIdentifier(opts.name)}Contract`,\n sourceFilePath: generatedPath,\n generatedPath,\n };\n}\n\nasync function resolveContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n key: string;\n source: RuntimePluginConfig | { url: string; localPath?: string; name: string } | null;\n baseUrl: string;\n generatedSubdir: string;\n localSourceFactory?: (configDir: string) => ContractSource;\n}): Promise<ContractSource> {\n if (opts.key === \"api\") {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath != null && localPath !== \"\") {\n return {\n key: opts.key,\n importName: \"BaseApiContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return localApiContractSource(opts.configDir);\n }\n }\n\n if (opts.key === \"auth\" && opts.localSourceFactory) {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath != null && localPath !== \"\") {\n return {\n key: opts.key,\n importName: \"authContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return opts.localSourceFactory(opts.configDir);\n }\n }\n\n if (\n opts.source &&\n \"localPath\" in opts.source &&\n opts.source.localPath != null &&\n opts.source.localPath !== \"\"\n ) {\n return {\n key: opts.key,\n importName: `${sanitizeIdentifier(opts.key)}Contract`,\n sourceFilePath: join(opts.source.localPath, \"src\", \"contract.ts\"),\n };\n }\n\n return remoteContractSource({\n configDir: opts.configDir,\n runtimeDir: opts.runtimeDir,\n name: opts.key,\n baseUrl: opts.baseUrl,\n generatedSubdir: opts.generatedSubdir,\n });\n}\n\nfunction writeGeneratedFiles(opts: {\n configDir: string;\n sources: ContractSource[];\n pluginKeys: string[];\n authSource: ContractSource | null;\n}) {\n const baseSource = opts.sources.find((source) => source.key === \"api\");\n const pluginSources = opts.pluginKeys\n .map((key) => opts.sources.find((entry) => entry.key === key))\n .filter((source): source is ContractSource => Boolean(source));\n\n if (!baseSource) {\n throw new Error(\"API contract source is required to generate the aggregate contract\");\n }\n\n // --- Generate ui/src/api-contract.gen.ts ---\n const uiContractPath = join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\");\n const uiLines: string[] = [];\n\n for (const source of opts.sources) {\n const importPath = toImportPath(uiContractPath, source.sourceFilePath);\n uiLines.push(`import type { ContractType as ${source.importName} } from \"${importPath}\";`);\n }\n\n uiLines.push(\"\");\n\n const compositeParts: string[] = [];\n if (opts.authSource) {\n compositeParts.push(`auth: ${opts.authSource.importName}`);\n }\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key) ? source.key : JSON.stringify(source.key);\n compositeParts.push(`${key}: ${source.importName}`);\n }\n\n if (compositeParts.length === 0) {\n uiLines.push(`export type ApiContract = ${baseSource.importName};`);\n } else {\n uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);\n for (const part of compositeParts) {\n uiLines.push(` ${part};`);\n }\n uiLines.push(\"};\");\n }\n mkdirSync(dirname(uiContractPath), { recursive: true });\n writeFileIfChanged(uiContractPath, `${uiLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/plugins-client.gen.ts ---\n const pluginsClientPath = join(opts.configDir, \"api\", \"src\", \"plugins-client.gen.ts\");\n const pluginsClientLines: string[] = [];\n\n for (const source of pluginSources) {\n const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);\n pluginsClientLines.push(\n `import type { ContractType as ${source.importName} } from \"${importPath}\";`,\n );\n }\n\n pluginsClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n pluginsClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n pluginsClientLines.push(\"\");\n\n if (pluginSources.length === 0) {\n pluginsClientLines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n pluginsClientLines.push(\"export type PluginsClient = {\");\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);\n }\n pluginsClientLines.push(\"};\");\n }\n\n mkdirSync(dirname(pluginsClientPath), { recursive: true });\n writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/auth-client.gen.ts ---\n if (opts.authSource) {\n const authClientPath = join(opts.configDir, \"api\", \"src\", \"auth-client.gen.ts\");\n const authClientLines: string[] = [];\n\n const importPath = toImportPath(authClientPath, opts.authSource.sourceFilePath);\n authClientLines.push(\n `import type { ContractType as ${opts.authSource.importName} } from \"${importPath}\";`,\n );\n authClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n authClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n authClientLines.push(\"\");\n authClientLines.push(`export type AuthClient = ClientFactory<${opts.authSource.importName}>;`);\n\n mkdirSync(dirname(authClientPath), { recursive: true });\n writeFileIfChanged(authClientPath, `${authClientLines.join(\"\\n\")}\\n`);\n }\n\n return uiContractPath;\n}\n\nexport async function syncApiContractBridge(opts: {\n configDir: string;\n runtimeConfig: RuntimeConfig;\n apiBaseUrl: string;\n}): Promise<{\n bridgePath: string;\n generatedPath: string | null;\n manifest: ApiPluginManifest | null;\n source: \"local\" | \"remote\";\n}> {\n const runtimeDir = join(opts.configDir, \".bos\", \"generated\");\n const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n );\n const sources: ContractSource[] = [];\n let manifest: ApiPluginManifest | null = null;\n let generatedPath: string | null = null;\n let authSource: ContractSource | null = null;\n\n const baseSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"api\",\n source: opts.runtimeConfig.api,\n baseUrl: opts.apiBaseUrl,\n generatedSubdir: \"api\",\n });\n sources.push(baseSource);\n\n if (opts.runtimeConfig.auth) {\n authSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"auth\",\n source: opts.runtimeConfig.auth,\n baseUrl: opts.runtimeConfig.auth.url,\n generatedSubdir: \"auth\",\n localSourceFactory: localAuthContractSource,\n });\n sources.push(authSource);\n if (authSource.generatedPath) {\n generatedPath = authSource.generatedPath;\n }\n }\n\n for (const [key, plugin] of pluginEntries) {\n const source = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key,\n source: plugin,\n baseUrl: plugin.url,\n generatedSubdir: `plugins/${key}`,\n });\n sources.push(source);\n if (source.generatedPath) {\n generatedPath = source.generatedPath;\n }\n }\n\n const allPluginKeys = pluginEntries.map(([key]) => key);\n\n writeGeneratedFiles({\n configDir: opts.configDir,\n sources,\n pluginKeys: allPluginKeys,\n authSource,\n });\n\n if (opts.runtimeConfig.api.source !== \"local\") {\n manifest = await fetchApiPluginManifest(opts.apiBaseUrl);\n }\n\n return {\n bridgePath: join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\"),\n generatedPath,\n manifest,\n source: opts.runtimeConfig.api.source,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api-contract.mjs","names":[],"sources":["../src/api-contract.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport type { RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface ApiPluginManifest {\n schemaVersion: 1;\n kind: \"every-plugin/manifest\";\n plugin: {\n name: string;\n version: string;\n };\n runtime: {\n remoteEntry: string;\n };\n contract?: {\n kind: \"orpc\";\n types: {\n path: string;\n exportName: string;\n typeName: string;\n sha256?: string;\n };\n };\n}\n\ninterface ContractSource {\n key: string;\n importName: string;\n sourceFilePath: string;\n generatedPath?: string;\n}\n\nfunction sha256(input: string): string {\n return createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nfunction trimTrailingSlash(input: string): string {\n return input.replace(/\\/$/, \"\");\n}\n\nfunction sanitizeIdentifier(input: string): string {\n return input.replace(/[^A-Za-z0-9_]/g, \"_\").replace(/^[^A-Za-z_]+/, \"_\");\n}\n\nfunction toImportPath(fromFile: string, targetFile: string): string {\n const rel = relative(dirname(fromFile), targetFile).replace(/\\\\/g, \"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\nfunction writeFileIfChanged(filePath: string, content: string) {\n try {\n if (readFileSync(filePath, \"utf8\") === content) return false;\n } catch {\n // file does not exist yet\n }\n\n writeFileSync(filePath, content);\n return true;\n}\n\nfunction getApiPluginManifestUrl(apiBaseUrl: string): string {\n return `${trimTrailingSlash(apiBaseUrl)}/plugin.manifest.json`;\n}\n\nasync function fetchApiPluginManifest(apiBaseUrl: string): Promise<ApiPluginManifest> {\n const response = await fetch(getApiPluginManifestUrl(apiBaseUrl));\n if (!response.ok) {\n throw new Error(\n `Failed to fetch API plugin manifest: ${response.status} ${response.statusText}`,\n );\n }\n\n const manifest = (await response.json()) as ApiPluginManifest;\n if (manifest.schemaVersion !== 1 || manifest.kind !== \"every-plugin/manifest\") {\n throw new Error(\"Unsupported API plugin manifest format\");\n }\n\n return manifest;\n}\n\nfunction localApiContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"api\", \"src\", \"contract.ts\");\n return {\n key: \"api\",\n importName: \"BaseApiContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nfunction localAuthContractSource(configDir: string): ContractSource {\n const sourcePath = join(configDir, \"plugins\", \"auth\", \"src\", \"contract.ts\");\n return {\n key: \"auth\",\n importName: \"authContract\",\n sourceFilePath: sourcePath,\n };\n}\n\nasync function remoteContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n name: string;\n baseUrl: string;\n generatedSubdir: string;\n}): Promise<ContractSource> {\n const manifest = await fetchApiPluginManifest(opts.baseUrl);\n if (!manifest.contract) {\n throw new Error(\n `Plugin manifest for ${manifest.plugin.name} does not advertise contract types`,\n );\n }\n\n const contractUrl = `${trimTrailingSlash(opts.baseUrl)}/${manifest.contract.types.path.replace(/^\\.\\//, \"\")}`;\n const contractResponse = await fetch(contractUrl);\n if (!contractResponse.ok) {\n throw new Error(\n `Failed to fetch contract types: ${contractResponse.status} ${contractResponse.statusText}`,\n );\n }\n\n const contractTypes = await contractResponse.text();\n if (manifest.contract.types.sha256 && manifest.contract.types.sha256 !== sha256(contractTypes)) {\n throw new Error(\"Fetched contract types failed checksum verification\");\n }\n\n const generatedPath = join(opts.runtimeDir, opts.generatedSubdir, \"contract.d.ts\");\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, contractTypes);\n\n return {\n key: opts.name,\n importName: `${sanitizeIdentifier(opts.name)}Contract`,\n sourceFilePath: generatedPath,\n generatedPath,\n };\n}\n\nasync function resolveContractSource(opts: {\n configDir: string;\n runtimeDir: string;\n key: string;\n source: RuntimePluginConfig | { url: string; localPath?: string; name: string } | null;\n baseUrl: string;\n generatedSubdir: string;\n localSourceFactory?: (configDir: string) => ContractSource;\n}): Promise<ContractSource> {\n if (opts.key === \"api\") {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath != null && localPath !== \"\") {\n return {\n key: opts.key,\n importName: \"BaseApiContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return localApiContractSource(opts.configDir);\n }\n }\n\n if (opts.key === \"auth\" && opts.localSourceFactory) {\n const localPath = opts.source && \"localPath\" in opts.source ? opts.source.localPath : undefined;\n if (localPath != null && localPath !== \"\") {\n return {\n key: opts.key,\n importName: \"authContract\",\n sourceFilePath: join(localPath, \"src\", \"contract.ts\"),\n };\n }\n\n if (!opts.baseUrl) {\n return opts.localSourceFactory(opts.configDir);\n }\n }\n\n if (\n opts.source &&\n \"localPath\" in opts.source &&\n opts.source.localPath != null &&\n opts.source.localPath !== \"\"\n ) {\n return {\n key: opts.key,\n importName: `${sanitizeIdentifier(opts.key)}Contract`,\n sourceFilePath: join(opts.source.localPath, \"src\", \"contract.ts\"),\n };\n }\n\n return remoteContractSource({\n configDir: opts.configDir,\n runtimeDir: opts.runtimeDir,\n name: opts.key,\n baseUrl: opts.baseUrl,\n generatedSubdir: opts.generatedSubdir,\n });\n}\n\nfunction writeGeneratedFiles(opts: {\n configDir: string;\n sources: ContractSource[];\n pluginKeys: string[];\n authSource: ContractSource | null;\n}) {\n const baseSource = opts.sources.find((source) => source.key === \"api\");\n const pluginSources = opts.pluginKeys\n .map((key) => opts.sources.find((entry) => entry.key === key))\n .filter((source): source is ContractSource => Boolean(source));\n\n if (!baseSource) {\n throw new Error(\"API contract source is required to generate the aggregate contract\");\n }\n\n // --- Generate ui/src/api-contract.gen.ts ---\n const uiContractPath = join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\");\n const uiLines: string[] = [];\n\n for (const source of opts.sources) {\n const importPath = toImportPath(uiContractPath, source.sourceFilePath);\n uiLines.push(`import type { ContractType as ${source.importName} } from \"${importPath}\";`);\n }\n\n uiLines.push(\"\");\n\n const compositeParts: string[] = [];\n if (opts.authSource) {\n compositeParts.push(`auth: ${opts.authSource.importName}`);\n }\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key) ? source.key : JSON.stringify(source.key);\n compositeParts.push(`${key}: ${source.importName}`);\n }\n\n if (compositeParts.length === 0) {\n uiLines.push(`export type ApiContract = ${baseSource.importName};`);\n } else {\n uiLines.push(`export type ApiContract = ${baseSource.importName} & {`);\n for (const part of compositeParts) {\n uiLines.push(` ${part};`);\n }\n uiLines.push(\"};\");\n }\n mkdirSync(dirname(uiContractPath), { recursive: true });\n writeFileIfChanged(uiContractPath, `${uiLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/plugins-client.gen.ts ---\n const pluginsClientPath = join(opts.configDir, \"api\", \"src\", \"plugins-client.gen.ts\");\n const pluginsClientLines: string[] = [];\n\n for (const source of pluginSources) {\n const importPath = toImportPath(pluginsClientPath, source.sourceFilePath);\n pluginsClientLines.push(\n `import type { ContractType as ${source.importName} } from \"${importPath}\";`,\n );\n }\n\n pluginsClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n pluginsClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n pluginsClientLines.push(\"\");\n\n if (pluginSources.length === 0) {\n pluginsClientLines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n pluginsClientLines.push(\"export type PluginsClient = {\");\n for (const source of pluginSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n pluginsClientLines.push(` ${key}: ClientFactory<${source.importName}>;`);\n }\n pluginsClientLines.push(\"};\");\n }\n\n mkdirSync(dirname(pluginsClientPath), { recursive: true });\n writeFileIfChanged(pluginsClientPath, `${pluginsClientLines.join(\"\\n\")}\\n`);\n\n // --- Generate api/src/auth-client.gen.ts ---\n if (opts.authSource) {\n const authClientPath = join(opts.configDir, \"api\", \"src\", \"auth-client.gen.ts\");\n const authClientLines: string[] = [];\n\n const importPath = toImportPath(authClientPath, opts.authSource.sourceFilePath);\n authClientLines.push(\n `import type { ContractType as ${opts.authSource.importName} } from \"${importPath}\";`,\n );\n authClientLines.push(\n 'import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";',\n );\n authClientLines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n authClientLines.push(\"\");\n authClientLines.push(`export type AuthClient = ClientFactory<${opts.authSource.importName}>;`);\n\n mkdirSync(dirname(authClientPath), { recursive: true });\n writeFileIfChanged(authClientPath, `${authClientLines.join(\"\\n\")}\\n`);\n }\n\n return uiContractPath;\n}\n\nexport async function syncApiContractBridge(opts: {\n configDir: string;\n runtimeConfig: RuntimeConfig;\n apiBaseUrl: string;\n}): Promise<{\n bridgePath: string;\n generatedPath: string | null;\n manifest: ApiPluginManifest | null;\n source: \"local\" | \"remote\";\n}> {\n const runtimeDir = join(opts.configDir, \".bos\", \"generated\");\n const pluginEntries = Object.entries(opts.runtimeConfig.plugins ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n );\n const sources: ContractSource[] = [];\n let manifest: ApiPluginManifest | null = null;\n let generatedPath: string | null = null;\n let authSource: ContractSource | null = null;\n\n const baseSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"api\",\n source: opts.runtimeConfig.api,\n baseUrl: opts.apiBaseUrl,\n generatedSubdir: \"api\",\n });\n sources.push(baseSource);\n\n if (opts.runtimeConfig.auth) {\n authSource = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key: \"auth\",\n source: opts.runtimeConfig.auth,\n baseUrl: opts.runtimeConfig.auth.url,\n generatedSubdir: \"auth\",\n localSourceFactory: localAuthContractSource,\n });\n sources.push(authSource);\n if (authSource.generatedPath) {\n generatedPath = authSource.generatedPath;\n }\n }\n\n for (const [key, plugin] of pluginEntries) {\n if (!plugin.url) {\n console.warn(`[API Contract] Skipping plugin \"${key}\" — no URL resolved (local path missing and no production URL configured)`);\n continue;\n }\n const source = await resolveContractSource({\n configDir: opts.configDir,\n runtimeDir,\n key,\n source: plugin,\n baseUrl: plugin.url,\n generatedSubdir: `plugins/${key}`,\n });\n sources.push(source);\n if (source.generatedPath) {\n generatedPath = source.generatedPath;\n }\n }\n\n const allPluginKeys = pluginEntries.map(([key]) => key);\n\n writeGeneratedFiles({\n configDir: opts.configDir,\n sources,\n pluginKeys: allPluginKeys,\n authSource,\n });\n\n if (opts.runtimeConfig.api.source !== \"local\") {\n manifest = await fetchApiPluginManifest(opts.apiBaseUrl);\n }\n\n return {\n bridgePath: join(opts.configDir, \"ui\", \"src\", \"api-contract.gen.ts\"),\n generatedPath,\n manifest,\n source: opts.runtimeConfig.api.source,\n };\n}\n"],"mappings":"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"}
|
package/dist/cli/init.cjs
CHANGED
|
@@ -179,13 +179,14 @@ function isRouteExcluded(filePath, excludedPatterns) {
|
|
|
179
179
|
return false;
|
|
180
180
|
}
|
|
181
181
|
async function personalizeConfig(destination, opts) {
|
|
182
|
+
const isInit = opts.mode !== "sync";
|
|
182
183
|
const configPath = (0, node_path.join)(destination, "bos.config.json");
|
|
183
184
|
if ((0, node_fs.existsSync)(configPath)) {
|
|
184
185
|
const config = JSON.parse((0, node_fs.readFileSync)(configPath, "utf-8"));
|
|
185
186
|
config.extends = `bos://${opts.extendsAccount}/${opts.extendsGateway}`;
|
|
186
187
|
if (opts.account) config.account = opts.account;
|
|
187
188
|
if (opts.domain) config.domain = opts.domain;
|
|
188
|
-
if (config.app && typeof config.app === "object") {
|
|
189
|
+
if (isInit && config.app && typeof config.app === "object") {
|
|
189
190
|
const app = config.app;
|
|
190
191
|
for (const entryKey of Object.keys(app)) {
|
|
191
192
|
const entry = app[entryKey];
|
|
@@ -203,7 +204,7 @@ async function personalizeConfig(destination, opts) {
|
|
|
203
204
|
if (opts.plugins && opts.plugins.length > 0) {
|
|
204
205
|
for (const pluginKey of Object.keys(plugins)) if (!opts.plugins.includes(pluginKey)) delete plugins[pluginKey];
|
|
205
206
|
}
|
|
206
|
-
for (const pluginKey of Object.keys(plugins)) {
|
|
207
|
+
if (isInit) for (const pluginKey of Object.keys(plugins)) {
|
|
207
208
|
const plugin = plugins[pluginKey];
|
|
208
209
|
if (plugin && typeof plugin === "object") {
|
|
209
210
|
const p = plugin;
|
package/dist/cli/init.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.cjs","names":["require","fetchBosConfigFromFastKv","loadManifestNormalizationSpec","normalizePackageManifestsInTree","writeSnapshot"],"sources":["../../src/cli/init.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport {\n createWriteStream,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { glob } from \"glob\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport {\n loadManifestNormalizationSpec,\n normalizePackageManifestsInTree,\n} from \"../internal/manifest-normalizer\";\nimport type { BosConfig } from \"../types\";\nimport { writeSnapshot } from \"./snapshot\";\n\nconst require = createRequire(import.meta.url);\n\ninterface SourceResult {\n sourceDir: string;\n parentConfig: BosConfig;\n cleanup: () => Promise<void>;\n}\n\nexport async function resolveSourceDir(opts: {\n extendsAccount: string;\n extendsGateway: string;\n source?: string;\n}): Promise<SourceResult> {\n if (opts.source) {\n const sourceDir = resolve(opts.source);\n if (!existsSync(join(sourceDir, \"bos.config.json\"))) {\n throw new Error(`No bos.config.json found in source directory: ${sourceDir}`);\n }\n const parentConfig = JSON.parse(\n readFileSync(join(sourceDir, \"bos.config.json\"), \"utf-8\"),\n ) as BosConfig;\n return { sourceDir, parentConfig, cleanup: async () => {} };\n }\n\n const parentConfig = await fetchParentConfig(opts.extendsAccount, opts.extendsGateway);\n\n if (!parentConfig.repository) {\n throw new Error(\"Parent config has no repository field — cannot locate template source\");\n }\n\n const { dir: sourceDir, cleanup } = await downloadTarball(parentConfig.repository);\n return { sourceDir, parentConfig, cleanup };\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 downloadTarball(\n repoUrl: string,\n): Promise<{ dir: string; cleanup: () => Promise<void> }> {\n const parsed = parseGitHubUrl(repoUrl);\n if (!parsed) {\n throw new Error(`Cannot parse repository URL: ${repoUrl}`);\n }\n\n const { owner, repo, branch } = parsed;\n const tarballUrl = `https://api.github.com/repos/${owner}/${repo}/tarball/${branch}`;\n\n const tmpDir = mkTmpDir(\"bos-init-tarball-\");\n const tarballPath = join(tmpDir, \"source.tar.gz\");\n\n const response = await fetch(tarballUrl, {\n headers: { \"User-Agent\": \"everything-dev\" },\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n rmSync(tmpDir, { recursive: true, force: true });\n throw new Error(`GitHub tarball download failed: ${response.status} ${response.statusText}`);\n }\n\n if (!response.body) {\n rmSync(tmpDir, { recursive: true, force: true });\n throw new Error(\"GitHub tarball download returned empty body\");\n }\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 const tar = require(\"tar\") as {\n extract: (opts: { cwd: string; file: string; strip: number }) => Promise<void>;\n };\n await tar.extract({ 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 cleanup: async () => {\n rmSync(extractDir, { recursive: true, force: true });\n },\n };\n}\n\nfunction parseGitHubUrl(url: string): { owner: string; repo: string; branch: string } | null {\n const httpsMatch = url.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?(?:\\/.*)?$/);\n if (httpsMatch) {\n return { owner: httpsMatch[1], repo: httpsMatch[2], branch: \"main\" };\n }\n\n const sshMatch = url.match(/^git@github\\.com:([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (sshMatch) {\n return { owner: sshMatch[1], repo: sshMatch[2], branch: \"main\" };\n }\n\n return null;\n}\n\nexport async function readTemplatekeep(sourceDir: string): Promise<string[]> {\n const keepFile = join(sourceDir, \".templatekeep\");\n if (!existsSync(keepFile)) {\n return [];\n }\n\n const content = readFileSync(keepFile, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function copyFilteredFiles(\n sourceDir: string,\n destination: string,\n patterns: string[],\n options: { withHost: boolean; plugins?: string[]; pluginRoutes?: Record<string, string[]> },\n): Promise<number> {\n if (patterns.length === 0) {\n return 0;\n }\n\n const effectivePatterns = options.withHost\n ? [...patterns, \"host/**\"]\n : patterns.filter((p) => !p.startsWith(\"host/\") && p !== \"host/**\");\n\n const filteredPatterns = effectivePatterns.filter((p) => {\n const pluginMatch = p.match(/^plugins\\/([^/]+)/);\n if (!pluginMatch) return true;\n const pluginName = pluginMatch[1];\n return options.plugins?.includes(pluginName) ?? true;\n });\n\n const excludedRoutePatterns: string[] = [];\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n }\n\n const allFiles = new Set<string>();\n for (const pattern of filteredPatterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n const pluginMatch = match.match(/^plugins\\/([^/]+)/);\n if (pluginMatch) {\n const pluginName = pluginMatch[1];\n if (!(options.plugins?.includes(pluginName) ?? true)) continue;\n }\n if (isRouteExcluded(match, excludedRoutePatterns)) continue;\n allFiles.add(match);\n }\n }\n\n const routeFiles = new Set<string>();\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isRouteExcluded(match, excludedRoutePatterns)) {\n routeFiles.add(match);\n }\n }\n }\n }\n }\n\n for (const f of routeFiles) allFiles.add(f);\n\n mkdirSync(destination, { recursive: true });\n\n let count = 0;\n for (const filePath of allFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(destination, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n const content = readFileSync(src);\n writeFileSync(dest, content);\n count++;\n }\n\n return count;\n}\n\nfunction isRouteExcluded(filePath: string, excludedPatterns: string[]): boolean {\n if (excludedPatterns.length === 0) return false;\n for (const pattern of excludedPatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nexport async function personalizeConfig(\n destination: string,\n opts: {\n extendsAccount: string;\n extendsGateway: string;\n account?: string;\n domain?: string;\n plugins?: string[];\n pluginRoutes?: Record<string, string[]>;\n workspaceOpts?: { localOverrides?: boolean; sourceDir?: string };\n },\n): Promise<void> {\n const configPath = join(destination, \"bos.config.json\");\n if (existsSync(configPath)) {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>;\n\n config.extends = `bos://${opts.extendsAccount}/${opts.extendsGateway}`;\n\n if (opts.account) {\n config.account = opts.account;\n }\n if (opts.domain) {\n config.domain = opts.domain;\n }\n\n if (config.app && typeof config.app === \"object\") {\n const app = config.app as Record<string, unknown>;\n for (const entryKey of Object.keys(app)) {\n const entry = app[entryKey];\n if (entry && typeof entry === \"object\") {\n const e = entry as Record<string, unknown>;\n delete e.production;\n delete e.integrity;\n delete e.ssr;\n delete e.ssrIntegrity;\n }\n }\n }\n\n if (config.plugins && typeof config.plugins === \"object\") {\n const plugins = config.plugins as Record<string, unknown>;\n\n if (opts.plugins && opts.plugins.length > 0) {\n for (const pluginKey of Object.keys(plugins)) {\n if (!opts.plugins.includes(pluginKey)) {\n delete plugins[pluginKey];\n }\n }\n }\n\n for (const pluginKey of Object.keys(plugins)) {\n const plugin = plugins[pluginKey];\n if (plugin && typeof plugin === \"object\") {\n const p = plugin as Record<string, unknown>;\n delete p.production;\n delete p.integrity;\n }\n }\n\n if (Object.keys(plugins).length === 0) {\n delete config.plugins;\n }\n }\n\n writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`);\n }\n\n const pkgPath = join(destination, \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n\n if (pkg.workspaces && typeof pkg.workspaces === \"object\") {\n const ws = pkg.workspaces as { packages?: string[] };\n if (Array.isArray(ws.packages)) {\n ws.packages = ws.packages\n .filter((p: string) => p !== \"host\" && !p.startsWith(\"packages/\"))\n .filter((p: string) => {\n const pluginMatch = p.match(/^plugins\\/([^/]+)/);\n if (!pluginMatch) return true;\n return opts.plugins?.includes(pluginMatch[1]) ?? true;\n });\n }\n }\n\n if (pkg.scripts && typeof pkg.scripts === \"object\") {\n const scripts = pkg.scripts as Record<string, string>;\n const rewrite = (key: string, from: string, to: string) => {\n if (scripts[key]?.includes(from)) {\n scripts[key] = scripts[key].replaceAll(from, to);\n }\n };\n rewrite(\"dev\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:ui\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:api\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:proxy\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"build\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"deploy\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"publish\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"start\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n\n if (scripts[\"sync:api-contract\"]) {\n delete scripts[\"sync:api-contract\"];\n }\n if (scripts.postinstall) {\n delete scripts.postinstall;\n }\n if (scripts.typecheck?.includes(\"sync:api-contract\")) {\n scripts.typecheck = scripts.typecheck.replace(\"bun run sync:api-contract && \", \"\");\n }\n }\n\n if (pkg.devDependencies && typeof pkg.devDependencies === \"object\") {\n const deps = pkg.devDependencies as Record<string, string>;\n delete deps[\"every-plugin\"];\n delete deps[\"everything-dev\"];\n }\n\n if (!pkg.dependencies) pkg.dependencies = {};\n const deps = pkg.dependencies as Record<string, string>;\n const spec = opts.workspaceOpts?.sourceDir\n ? loadManifestNormalizationSpec(opts.workspaceOpts.sourceDir)\n : null;\n if (!deps[\"everything-dev\"] && spec)\n deps[\"everything-dev\"] = spec.rootCatalog[\"everything-dev\"];\n if (!deps[\"every-plugin\"] && spec) deps[\"every-plugin\"] = spec.rootCatalog[\"every-plugin\"];\n\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n\n await resolveWorkspaceRefs(destination, opts.workspaceOpts);\n\n const genContractPath = join(destination, \"ui\", \"src\", \"api-contract.gen.ts\");\n if (!existsSync(genContractPath)) {\n mkdirSync(dirname(genContractPath), { recursive: true });\n writeFileSync(genContractPath, `export type ApiContract = Record<string, never>;\\n`);\n }\n}\n\nexport async function runBunInstall(destination: string): Promise<void> {\n await execCommand(\"bun\", [\"install\"], destination);\n}\n\nconst WORKSPACE_LOCAL_PATHS: Record<string, string> = {\n \"everything-dev\": \"packages/everything-dev\",\n \"every-plugin\": \"packages/every-plugin\",\n};\n\nasync function resolveWorkspaceRefs(\n destination: string,\n options?: { localOverrides?: boolean; sourceDir?: string },\n): Promise<void> {\n await normalizePackageManifestsInTree({\n sourceRootDir: options?.sourceDir ?? destination,\n targetDir: destination,\n resolveCatalogRefs: true,\n removeWorkspaceDeps: [\"host\"],\n });\n\n if (options?.localOverrides && options.sourceDir) {\n const rootPkgPath = join(destination, \"package.json\");\n if (existsSync(rootPkgPath)) {\n const pkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n if (!pkg.overrides) pkg.overrides = {};\n const overrides = pkg.overrides as Record<string, string>;\n\n const rootWorkspaces = ((pkg.workspaces as Record<string, string[]>)?.packages ?? []).filter(\n Boolean,\n );\n\n for (const [name, relPath] of Object.entries(WORKSPACE_LOCAL_PATHS)) {\n if (!rootWorkspaces.some((ws) => ws === relPath || ws === `plugins/${name}`)) {\n const srcPkgPath = join(options.sourceDir, relPath, \"package.json\");\n if (existsSync(srcPkgPath)) {\n overrides[name] = `file:${relPath}`;\n rootWorkspaces.push(relPath);\n }\n }\n }\n\n if (rootWorkspaces.length > 0) {\n if (!pkg.workspaces) pkg.workspaces = {};\n (pkg.workspaces as Record<string, string[]>).packages = rootWorkspaces;\n }\n\n writeFileSync(rootPkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n }\n}\n\nexport async function writeInitSnapshot(\n destination: string,\n extendsAccount: string,\n extendsGateway: string,\n sourceDir: string,\n patterns: string[],\n options: { withHost: boolean; plugins?: string[]; pluginRoutes?: Record<string, string[]> },\n): Promise<void> {\n const effectivePatterns = options.withHost\n ? [...patterns, \"host/**\"]\n : patterns.filter((p) => !p.startsWith(\"host/\") && p !== \"host/**\");\n\n const excludedRoutePatterns: string[] = [];\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n }\n\n const allFiles = new Set<string>();\n for (const pattern of effectivePatterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n const pluginMatch = match.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !(options.plugins?.includes(pluginMatch[1]) ?? true)) continue;\n if (isRouteExcluded(match, excludedRoutePatterns)) continue;\n allFiles.add(match);\n }\n }\n\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isRouteExcluded(match, excludedRoutePatterns)) {\n allFiles.add(match);\n }\n }\n }\n }\n }\n\n const fileHashes: Record<string, string> = {};\n for (const filePath of allFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n fileHashes[destPath] = computeHash(content);\n }\n\n await writeSnapshot(destination, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: fileHashes,\n });\n}\n\nfunction computeHash(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\").substring(0, 16);\n}\n\nfunction mkTmpDir(prefix: string): string {\n const base = join(tmpdir(), prefix);\n let attempt = 0;\n while (true) {\n const dir = `${base}-${Date.now()}-${attempt}`;\n try {\n mkdirSync(dir, { recursive: true });\n return dir;\n } catch {\n attempt++;\n if (attempt > 10) throw new Error(\"Failed to create temp directory\");\n }\n }\n}\n\nfunction execCommand(command: string, args: string[], cwd?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: \"pipe\",\n shell: true,\n });\n let stderr = \"\";\n child.stderr?.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(\n new Error(\n `Command '${command} ${args.join(\" \")}' failed with exit code ${code}: ${stderr}`,\n ),\n );\n });\n child.on(\"error\", reject);\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"init.cjs","names":["require","fetchBosConfigFromFastKv","loadManifestNormalizationSpec","normalizePackageManifestsInTree","writeSnapshot"],"sources":["../../src/cli/init.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport {\n createWriteStream,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { glob } from \"glob\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport {\n loadManifestNormalizationSpec,\n normalizePackageManifestsInTree,\n} from \"../internal/manifest-normalizer\";\nimport type { BosConfig } from \"../types\";\nimport { writeSnapshot } from \"./snapshot\";\n\nconst require = createRequire(import.meta.url);\n\ninterface SourceResult {\n sourceDir: string;\n parentConfig: BosConfig;\n cleanup: () => Promise<void>;\n}\n\nexport async function resolveSourceDir(opts: {\n extendsAccount: string;\n extendsGateway: string;\n source?: string;\n}): Promise<SourceResult> {\n if (opts.source) {\n const sourceDir = resolve(opts.source);\n if (!existsSync(join(sourceDir, \"bos.config.json\"))) {\n throw new Error(`No bos.config.json found in source directory: ${sourceDir}`);\n }\n const parentConfig = JSON.parse(\n readFileSync(join(sourceDir, \"bos.config.json\"), \"utf-8\"),\n ) as BosConfig;\n return { sourceDir, parentConfig, cleanup: async () => {} };\n }\n\n const parentConfig = await fetchParentConfig(opts.extendsAccount, opts.extendsGateway);\n\n if (!parentConfig.repository) {\n throw new Error(\"Parent config has no repository field — cannot locate template source\");\n }\n\n const { dir: sourceDir, cleanup } = await downloadTarball(parentConfig.repository);\n return { sourceDir, parentConfig, cleanup };\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 downloadTarball(\n repoUrl: string,\n): Promise<{ dir: string; cleanup: () => Promise<void> }> {\n const parsed = parseGitHubUrl(repoUrl);\n if (!parsed) {\n throw new Error(`Cannot parse repository URL: ${repoUrl}`);\n }\n\n const { owner, repo, branch } = parsed;\n const tarballUrl = `https://api.github.com/repos/${owner}/${repo}/tarball/${branch}`;\n\n const tmpDir = mkTmpDir(\"bos-init-tarball-\");\n const tarballPath = join(tmpDir, \"source.tar.gz\");\n\n const response = await fetch(tarballUrl, {\n headers: { \"User-Agent\": \"everything-dev\" },\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n rmSync(tmpDir, { recursive: true, force: true });\n throw new Error(`GitHub tarball download failed: ${response.status} ${response.statusText}`);\n }\n\n if (!response.body) {\n rmSync(tmpDir, { recursive: true, force: true });\n throw new Error(\"GitHub tarball download returned empty body\");\n }\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 const tar = require(\"tar\") as {\n extract: (opts: { cwd: string; file: string; strip: number }) => Promise<void>;\n };\n await tar.extract({ 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 cleanup: async () => {\n rmSync(extractDir, { recursive: true, force: true });\n },\n };\n}\n\nfunction parseGitHubUrl(url: string): { owner: string; repo: string; branch: string } | null {\n const httpsMatch = url.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?(?:\\/.*)?$/);\n if (httpsMatch) {\n return { owner: httpsMatch[1], repo: httpsMatch[2], branch: \"main\" };\n }\n\n const sshMatch = url.match(/^git@github\\.com:([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (sshMatch) {\n return { owner: sshMatch[1], repo: sshMatch[2], branch: \"main\" };\n }\n\n return null;\n}\n\nexport async function readTemplatekeep(sourceDir: string): Promise<string[]> {\n const keepFile = join(sourceDir, \".templatekeep\");\n if (!existsSync(keepFile)) {\n return [];\n }\n\n const content = readFileSync(keepFile, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function copyFilteredFiles(\n sourceDir: string,\n destination: string,\n patterns: string[],\n options: { withHost: boolean; plugins?: string[]; pluginRoutes?: Record<string, string[]> },\n): Promise<number> {\n if (patterns.length === 0) {\n return 0;\n }\n\n const effectivePatterns = options.withHost\n ? [...patterns, \"host/**\"]\n : patterns.filter((p) => !p.startsWith(\"host/\") && p !== \"host/**\");\n\n const filteredPatterns = effectivePatterns.filter((p) => {\n const pluginMatch = p.match(/^plugins\\/([^/]+)/);\n if (!pluginMatch) return true;\n const pluginName = pluginMatch[1];\n return options.plugins?.includes(pluginName) ?? true;\n });\n\n const excludedRoutePatterns: string[] = [];\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n }\n\n const allFiles = new Set<string>();\n for (const pattern of filteredPatterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n const pluginMatch = match.match(/^plugins\\/([^/]+)/);\n if (pluginMatch) {\n const pluginName = pluginMatch[1];\n if (!(options.plugins?.includes(pluginName) ?? true)) continue;\n }\n if (isRouteExcluded(match, excludedRoutePatterns)) continue;\n allFiles.add(match);\n }\n }\n\n const routeFiles = new Set<string>();\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isRouteExcluded(match, excludedRoutePatterns)) {\n routeFiles.add(match);\n }\n }\n }\n }\n }\n\n for (const f of routeFiles) allFiles.add(f);\n\n mkdirSync(destination, { recursive: true });\n\n let count = 0;\n for (const filePath of allFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(destination, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n const content = readFileSync(src);\n writeFileSync(dest, content);\n count++;\n }\n\n return count;\n}\n\nfunction isRouteExcluded(filePath: string, excludedPatterns: string[]): boolean {\n if (excludedPatterns.length === 0) return false;\n for (const pattern of excludedPatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nexport async function personalizeConfig(\n destination: string,\n opts: {\n extendsAccount: string;\n extendsGateway: string;\n account?: string;\n domain?: string;\n plugins?: string[];\n pluginRoutes?: Record<string, string[]>;\n workspaceOpts?: { localOverrides?: boolean; sourceDir?: string };\n mode?: \"init\" | \"sync\";\n },\n): Promise<void> {\n const isInit = opts.mode !== \"sync\";\n const configPath = join(destination, \"bos.config.json\");\n if (existsSync(configPath)) {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>;\n\n config.extends = `bos://${opts.extendsAccount}/${opts.extendsGateway}`;\n\n if (opts.account) {\n config.account = opts.account;\n }\n if (opts.domain) {\n config.domain = opts.domain;\n }\n\n if (isInit && config.app && typeof config.app === \"object\") {\n const app = config.app as Record<string, unknown>;\n for (const entryKey of Object.keys(app)) {\n const entry = app[entryKey];\n if (entry && typeof entry === \"object\") {\n const e = entry as Record<string, unknown>;\n delete e.production;\n delete e.integrity;\n delete e.ssr;\n delete e.ssrIntegrity;\n }\n }\n }\n\n if (config.plugins && typeof config.plugins === \"object\") {\n const plugins = config.plugins as Record<string, unknown>;\n\n if (opts.plugins && opts.plugins.length > 0) {\n for (const pluginKey of Object.keys(plugins)) {\n if (!opts.plugins.includes(pluginKey)) {\n delete plugins[pluginKey];\n }\n }\n }\n\n if (isInit) {\n for (const pluginKey of Object.keys(plugins)) {\n const plugin = plugins[pluginKey];\n if (plugin && typeof plugin === \"object\") {\n const p = plugin as Record<string, unknown>;\n delete p.production;\n delete p.integrity;\n }\n }\n }\n\n if (Object.keys(plugins).length === 0) {\n delete config.plugins;\n }\n }\n\n writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`);\n }\n\n const pkgPath = join(destination, \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n\n if (pkg.workspaces && typeof pkg.workspaces === \"object\") {\n const ws = pkg.workspaces as { packages?: string[] };\n if (Array.isArray(ws.packages)) {\n ws.packages = ws.packages\n .filter((p: string) => p !== \"host\" && !p.startsWith(\"packages/\"))\n .filter((p: string) => {\n const pluginMatch = p.match(/^plugins\\/([^/]+)/);\n if (!pluginMatch) return true;\n return opts.plugins?.includes(pluginMatch[1]) ?? true;\n });\n }\n }\n\n if (pkg.scripts && typeof pkg.scripts === \"object\") {\n const scripts = pkg.scripts as Record<string, string>;\n const rewrite = (key: string, from: string, to: string) => {\n if (scripts[key]?.includes(from)) {\n scripts[key] = scripts[key].replaceAll(from, to);\n }\n };\n rewrite(\"dev\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:ui\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:api\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:proxy\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"build\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"deploy\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"publish\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"start\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n\n if (scripts[\"sync:api-contract\"]) {\n delete scripts[\"sync:api-contract\"];\n }\n if (scripts.postinstall) {\n delete scripts.postinstall;\n }\n if (scripts.typecheck?.includes(\"sync:api-contract\")) {\n scripts.typecheck = scripts.typecheck.replace(\"bun run sync:api-contract && \", \"\");\n }\n }\n\n if (pkg.devDependencies && typeof pkg.devDependencies === \"object\") {\n const deps = pkg.devDependencies as Record<string, string>;\n delete deps[\"every-plugin\"];\n delete deps[\"everything-dev\"];\n }\n\n if (!pkg.dependencies) pkg.dependencies = {};\n const deps = pkg.dependencies as Record<string, string>;\n const spec = opts.workspaceOpts?.sourceDir\n ? loadManifestNormalizationSpec(opts.workspaceOpts.sourceDir)\n : null;\n if (!deps[\"everything-dev\"] && spec)\n deps[\"everything-dev\"] = spec.rootCatalog[\"everything-dev\"];\n if (!deps[\"every-plugin\"] && spec) deps[\"every-plugin\"] = spec.rootCatalog[\"every-plugin\"];\n\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n\n await resolveWorkspaceRefs(destination, opts.workspaceOpts);\n\n const genContractPath = join(destination, \"ui\", \"src\", \"api-contract.gen.ts\");\n if (!existsSync(genContractPath)) {\n mkdirSync(dirname(genContractPath), { recursive: true });\n writeFileSync(genContractPath, `export type ApiContract = Record<string, never>;\\n`);\n }\n}\n\nexport async function runBunInstall(destination: string): Promise<void> {\n await execCommand(\"bun\", [\"install\"], destination);\n}\n\nconst WORKSPACE_LOCAL_PATHS: Record<string, string> = {\n \"everything-dev\": \"packages/everything-dev\",\n \"every-plugin\": \"packages/every-plugin\",\n};\n\nasync function resolveWorkspaceRefs(\n destination: string,\n options?: { localOverrides?: boolean; sourceDir?: string },\n): Promise<void> {\n await normalizePackageManifestsInTree({\n sourceRootDir: options?.sourceDir ?? destination,\n targetDir: destination,\n resolveCatalogRefs: true,\n removeWorkspaceDeps: [\"host\"],\n });\n\n if (options?.localOverrides && options.sourceDir) {\n const rootPkgPath = join(destination, \"package.json\");\n if (existsSync(rootPkgPath)) {\n const pkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n if (!pkg.overrides) pkg.overrides = {};\n const overrides = pkg.overrides as Record<string, string>;\n\n const rootWorkspaces = ((pkg.workspaces as Record<string, string[]>)?.packages ?? []).filter(\n Boolean,\n );\n\n for (const [name, relPath] of Object.entries(WORKSPACE_LOCAL_PATHS)) {\n if (!rootWorkspaces.some((ws) => ws === relPath || ws === `plugins/${name}`)) {\n const srcPkgPath = join(options.sourceDir, relPath, \"package.json\");\n if (existsSync(srcPkgPath)) {\n overrides[name] = `file:${relPath}`;\n rootWorkspaces.push(relPath);\n }\n }\n }\n\n if (rootWorkspaces.length > 0) {\n if (!pkg.workspaces) pkg.workspaces = {};\n (pkg.workspaces as Record<string, string[]>).packages = rootWorkspaces;\n }\n\n writeFileSync(rootPkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n }\n}\n\nexport async function writeInitSnapshot(\n destination: string,\n extendsAccount: string,\n extendsGateway: string,\n sourceDir: string,\n patterns: string[],\n options: { withHost: boolean; plugins?: string[]; pluginRoutes?: Record<string, string[]> },\n): Promise<void> {\n const effectivePatterns = options.withHost\n ? [...patterns, \"host/**\"]\n : patterns.filter((p) => !p.startsWith(\"host/\") && p !== \"host/**\");\n\n const excludedRoutePatterns: string[] = [];\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n }\n\n const allFiles = new Set<string>();\n for (const pattern of effectivePatterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n const pluginMatch = match.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !(options.plugins?.includes(pluginMatch[1]) ?? true)) continue;\n if (isRouteExcluded(match, excludedRoutePatterns)) continue;\n allFiles.add(match);\n }\n }\n\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isRouteExcluded(match, excludedRoutePatterns)) {\n allFiles.add(match);\n }\n }\n }\n }\n }\n\n const fileHashes: Record<string, string> = {};\n for (const filePath of allFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n fileHashes[destPath] = computeHash(content);\n }\n\n await writeSnapshot(destination, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: fileHashes,\n });\n}\n\nfunction computeHash(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\").substring(0, 16);\n}\n\nfunction mkTmpDir(prefix: string): string {\n const base = join(tmpdir(), prefix);\n let attempt = 0;\n while (true) {\n const dir = `${base}-${Date.now()}-${attempt}`;\n try {\n mkdirSync(dir, { recursive: true });\n return dir;\n } catch {\n attempt++;\n if (attempt > 10) throw new Error(\"Failed to create temp directory\");\n }\n }\n}\n\nfunction execCommand(command: string, args: string[], cwd?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: \"pipe\",\n shell: true,\n });\n let stderr = \"\";\n child.stderr?.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(\n new Error(\n `Command '${command} ${args.join(\" \")}' failed with exit code ${code}: ${stderr}`,\n ),\n );\n });\n child.on(\"error\", reject);\n });\n}\n"],"mappings":"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"}
|
package/dist/cli/init.d.cts
CHANGED
|
@@ -33,6 +33,7 @@ declare function personalizeConfig(destination: string, opts: {
|
|
|
33
33
|
localOverrides?: boolean;
|
|
34
34
|
sourceDir?: string;
|
|
35
35
|
};
|
|
36
|
+
mode?: "init" | "sync";
|
|
36
37
|
}): Promise<void>;
|
|
37
38
|
declare function runBunInstall(destination: string): Promise<void>;
|
|
38
39
|
declare function writeInitSnapshot(destination: string, extendsAccount: string, extendsGateway: string, sourceDir: string, patterns: string[], options: {
|
package/dist/cli/init.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.cts","names":[],"sources":["../../src/cli/init.ts"],"mappings":";;;UA0BU,YAAA;EACR,SAAA;EACA,YAAA,EAAc,SAAA;EACd,OAAA,QAAe,OAAA;AAAA;AAAA,iBAGK,gBAAA,CAAiB,IAAA;EACrC,cAAA;EACA,cAAA;EACA,MAAA;AAAA,IACE,OAAA,CAAQ,YAAA;AAAA,iBAsBU,iBAAA,CACpB,cAAA,UACA,cAAA,WACC,OAAA,CAAQ,SAAA;AAAA,iBAKW,eAAA,CACpB,OAAA,WACC,OAAA;EAAU,GAAA;EAAa,OAAA,QAAe,OAAA;AAAA;AAAA,iBAiEnB,gBAAA,CAAiB,SAAA,WAAoB,OAAA;AAAA,iBAarC,iBAAA,CACpB,SAAA,UACA,WAAA,UACA,QAAA,YACA,OAAA;EAAW,QAAA;EAAmB,OAAA;EAAoB,YAAA,GAAe,MAAA;AAAA,IAChE,OAAA;AAAA,iBAoGmB,iBAAA,CACpB,WAAA,UACA,IAAA;EACE,cAAA;EACA,cAAA;EACA,OAAA;EACA,MAAA;EACA,OAAA;EACA,YAAA,GAAe,MAAA;EACf,aAAA;IAAkB,cAAA;IAA0B,SAAA;EAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"init.d.cts","names":[],"sources":["../../src/cli/init.ts"],"mappings":";;;UA0BU,YAAA;EACR,SAAA;EACA,YAAA,EAAc,SAAA;EACd,OAAA,QAAe,OAAA;AAAA;AAAA,iBAGK,gBAAA,CAAiB,IAAA;EACrC,cAAA;EACA,cAAA;EACA,MAAA;AAAA,IACE,OAAA,CAAQ,YAAA;AAAA,iBAsBU,iBAAA,CACpB,cAAA,UACA,cAAA,WACC,OAAA,CAAQ,SAAA;AAAA,iBAKW,eAAA,CACpB,OAAA,WACC,OAAA;EAAU,GAAA;EAAa,OAAA,QAAe,OAAA;AAAA;AAAA,iBAiEnB,gBAAA,CAAiB,SAAA,WAAoB,OAAA;AAAA,iBAarC,iBAAA,CACpB,SAAA,UACA,WAAA,UACA,QAAA,YACA,OAAA;EAAW,QAAA;EAAmB,OAAA;EAAoB,YAAA,GAAe,MAAA;AAAA,IAChE,OAAA;AAAA,iBAoGmB,iBAAA,CACpB,WAAA,UACA,IAAA;EACE,cAAA;EACA,cAAA;EACA,OAAA;EACA,MAAA;EACA,OAAA;EACA,YAAA,GAAe,MAAA;EACf,aAAA;IAAkB,cAAA;IAA0B,SAAA;EAAA;EAC5C,IAAA;AAAA,IAED,OAAA;AAAA,iBAkImB,aAAA,CAAc,WAAA,WAAsB,OAAA;AAAA,iBAmDpC,iBAAA,CACpB,WAAA,UACA,cAAA,UACA,cAAA,UACA,SAAA,UACA,QAAA,YACA,OAAA;EAAW,QAAA;EAAmB,OAAA;EAAoB,YAAA,GAAe,MAAA;AAAA,IAChE,OAAA"}
|
package/dist/cli/init.d.mts
CHANGED
|
@@ -33,6 +33,7 @@ declare function personalizeConfig(destination: string, opts: {
|
|
|
33
33
|
localOverrides?: boolean;
|
|
34
34
|
sourceDir?: string;
|
|
35
35
|
};
|
|
36
|
+
mode?: "init" | "sync";
|
|
36
37
|
}): Promise<void>;
|
|
37
38
|
declare function runBunInstall(destination: string): Promise<void>;
|
|
38
39
|
declare function writeInitSnapshot(destination: string, extendsAccount: string, extendsGateway: string, sourceDir: string, patterns: string[], options: {
|
package/dist/cli/init.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.mts","names":[],"sources":["../../src/cli/init.ts"],"mappings":";;;UA0BU,YAAA;EACR,SAAA;EACA,YAAA,EAAc,SAAA;EACd,OAAA,QAAe,OAAA;AAAA;AAAA,iBAGK,gBAAA,CAAiB,IAAA;EACrC,cAAA;EACA,cAAA;EACA,MAAA;AAAA,IACE,OAAA,CAAQ,YAAA;AAAA,iBAsBU,iBAAA,CACpB,cAAA,UACA,cAAA,WACC,OAAA,CAAQ,SAAA;AAAA,iBAKW,eAAA,CACpB,OAAA,WACC,OAAA;EAAU,GAAA;EAAa,OAAA,QAAe,OAAA;AAAA;AAAA,iBAiEnB,gBAAA,CAAiB,SAAA,WAAoB,OAAA;AAAA,iBAarC,iBAAA,CACpB,SAAA,UACA,WAAA,UACA,QAAA,YACA,OAAA;EAAW,QAAA;EAAmB,OAAA;EAAoB,YAAA,GAAe,MAAA;AAAA,IAChE,OAAA;AAAA,iBAoGmB,iBAAA,CACpB,WAAA,UACA,IAAA;EACE,cAAA;EACA,cAAA;EACA,OAAA;EACA,MAAA;EACA,OAAA;EACA,YAAA,GAAe,MAAA;EACf,aAAA;IAAkB,cAAA;IAA0B,SAAA;EAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"init.d.mts","names":[],"sources":["../../src/cli/init.ts"],"mappings":";;;UA0BU,YAAA;EACR,SAAA;EACA,YAAA,EAAc,SAAA;EACd,OAAA,QAAe,OAAA;AAAA;AAAA,iBAGK,gBAAA,CAAiB,IAAA;EACrC,cAAA;EACA,cAAA;EACA,MAAA;AAAA,IACE,OAAA,CAAQ,YAAA;AAAA,iBAsBU,iBAAA,CACpB,cAAA,UACA,cAAA,WACC,OAAA,CAAQ,SAAA;AAAA,iBAKW,eAAA,CACpB,OAAA,WACC,OAAA;EAAU,GAAA;EAAa,OAAA,QAAe,OAAA;AAAA;AAAA,iBAiEnB,gBAAA,CAAiB,SAAA,WAAoB,OAAA;AAAA,iBAarC,iBAAA,CACpB,SAAA,UACA,WAAA,UACA,QAAA,YACA,OAAA;EAAW,QAAA;EAAmB,OAAA;EAAoB,YAAA,GAAe,MAAA;AAAA,IAChE,OAAA;AAAA,iBAoGmB,iBAAA,CACpB,WAAA,UACA,IAAA;EACE,cAAA;EACA,cAAA;EACA,OAAA;EACA,MAAA;EACA,OAAA;EACA,YAAA,GAAe,MAAA;EACf,aAAA;IAAkB,cAAA;IAA0B,SAAA;EAAA;EAC5C,IAAA;AAAA,IAED,OAAA;AAAA,iBAkImB,aAAA,CAAc,WAAA,WAAsB,OAAA;AAAA,iBAmDpC,iBAAA,CACpB,WAAA,UACA,cAAA,UACA,cAAA,UACA,SAAA,UACA,QAAA,YACA,OAAA;EAAW,QAAA;EAAmB,OAAA;EAAoB,YAAA,GAAe,MAAA;AAAA,IAChE,OAAA"}
|
package/dist/cli/init.mjs
CHANGED
|
@@ -177,13 +177,14 @@ function isRouteExcluded(filePath, excludedPatterns) {
|
|
|
177
177
|
return false;
|
|
178
178
|
}
|
|
179
179
|
async function personalizeConfig(destination, opts) {
|
|
180
|
+
const isInit = opts.mode !== "sync";
|
|
180
181
|
const configPath = join(destination, "bos.config.json");
|
|
181
182
|
if (existsSync(configPath)) {
|
|
182
183
|
const config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
183
184
|
config.extends = `bos://${opts.extendsAccount}/${opts.extendsGateway}`;
|
|
184
185
|
if (opts.account) config.account = opts.account;
|
|
185
186
|
if (opts.domain) config.domain = opts.domain;
|
|
186
|
-
if (config.app && typeof config.app === "object") {
|
|
187
|
+
if (isInit && config.app && typeof config.app === "object") {
|
|
187
188
|
const app = config.app;
|
|
188
189
|
for (const entryKey of Object.keys(app)) {
|
|
189
190
|
const entry = app[entryKey];
|
|
@@ -201,7 +202,7 @@ async function personalizeConfig(destination, opts) {
|
|
|
201
202
|
if (opts.plugins && opts.plugins.length > 0) {
|
|
202
203
|
for (const pluginKey of Object.keys(plugins)) if (!opts.plugins.includes(pluginKey)) delete plugins[pluginKey];
|
|
203
204
|
}
|
|
204
|
-
for (const pluginKey of Object.keys(plugins)) {
|
|
205
|
+
if (isInit) for (const pluginKey of Object.keys(plugins)) {
|
|
205
206
|
const plugin = plugins[pluginKey];
|
|
206
207
|
if (plugin && typeof plugin === "object") {
|
|
207
208
|
const p = plugin;
|
package/dist/cli/init.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.mjs","names":[],"sources":["../../src/cli/init.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport {\n createWriteStream,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { glob } from \"glob\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport {\n loadManifestNormalizationSpec,\n normalizePackageManifestsInTree,\n} from \"../internal/manifest-normalizer\";\nimport type { BosConfig } from \"../types\";\nimport { writeSnapshot } from \"./snapshot\";\n\nconst require = createRequire(import.meta.url);\n\ninterface SourceResult {\n sourceDir: string;\n parentConfig: BosConfig;\n cleanup: () => Promise<void>;\n}\n\nexport async function resolveSourceDir(opts: {\n extendsAccount: string;\n extendsGateway: string;\n source?: string;\n}): Promise<SourceResult> {\n if (opts.source) {\n const sourceDir = resolve(opts.source);\n if (!existsSync(join(sourceDir, \"bos.config.json\"))) {\n throw new Error(`No bos.config.json found in source directory: ${sourceDir}`);\n }\n const parentConfig = JSON.parse(\n readFileSync(join(sourceDir, \"bos.config.json\"), \"utf-8\"),\n ) as BosConfig;\n return { sourceDir, parentConfig, cleanup: async () => {} };\n }\n\n const parentConfig = await fetchParentConfig(opts.extendsAccount, opts.extendsGateway);\n\n if (!parentConfig.repository) {\n throw new Error(\"Parent config has no repository field — cannot locate template source\");\n }\n\n const { dir: sourceDir, cleanup } = await downloadTarball(parentConfig.repository);\n return { sourceDir, parentConfig, cleanup };\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 downloadTarball(\n repoUrl: string,\n): Promise<{ dir: string; cleanup: () => Promise<void> }> {\n const parsed = parseGitHubUrl(repoUrl);\n if (!parsed) {\n throw new Error(`Cannot parse repository URL: ${repoUrl}`);\n }\n\n const { owner, repo, branch } = parsed;\n const tarballUrl = `https://api.github.com/repos/${owner}/${repo}/tarball/${branch}`;\n\n const tmpDir = mkTmpDir(\"bos-init-tarball-\");\n const tarballPath = join(tmpDir, \"source.tar.gz\");\n\n const response = await fetch(tarballUrl, {\n headers: { \"User-Agent\": \"everything-dev\" },\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n rmSync(tmpDir, { recursive: true, force: true });\n throw new Error(`GitHub tarball download failed: ${response.status} ${response.statusText}`);\n }\n\n if (!response.body) {\n rmSync(tmpDir, { recursive: true, force: true });\n throw new Error(\"GitHub tarball download returned empty body\");\n }\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 const tar = require(\"tar\") as {\n extract: (opts: { cwd: string; file: string; strip: number }) => Promise<void>;\n };\n await tar.extract({ 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 cleanup: async () => {\n rmSync(extractDir, { recursive: true, force: true });\n },\n };\n}\n\nfunction parseGitHubUrl(url: string): { owner: string; repo: string; branch: string } | null {\n const httpsMatch = url.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?(?:\\/.*)?$/);\n if (httpsMatch) {\n return { owner: httpsMatch[1], repo: httpsMatch[2], branch: \"main\" };\n }\n\n const sshMatch = url.match(/^git@github\\.com:([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (sshMatch) {\n return { owner: sshMatch[1], repo: sshMatch[2], branch: \"main\" };\n }\n\n return null;\n}\n\nexport async function readTemplatekeep(sourceDir: string): Promise<string[]> {\n const keepFile = join(sourceDir, \".templatekeep\");\n if (!existsSync(keepFile)) {\n return [];\n }\n\n const content = readFileSync(keepFile, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function copyFilteredFiles(\n sourceDir: string,\n destination: string,\n patterns: string[],\n options: { withHost: boolean; plugins?: string[]; pluginRoutes?: Record<string, string[]> },\n): Promise<number> {\n if (patterns.length === 0) {\n return 0;\n }\n\n const effectivePatterns = options.withHost\n ? [...patterns, \"host/**\"]\n : patterns.filter((p) => !p.startsWith(\"host/\") && p !== \"host/**\");\n\n const filteredPatterns = effectivePatterns.filter((p) => {\n const pluginMatch = p.match(/^plugins\\/([^/]+)/);\n if (!pluginMatch) return true;\n const pluginName = pluginMatch[1];\n return options.plugins?.includes(pluginName) ?? true;\n });\n\n const excludedRoutePatterns: string[] = [];\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n }\n\n const allFiles = new Set<string>();\n for (const pattern of filteredPatterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n const pluginMatch = match.match(/^plugins\\/([^/]+)/);\n if (pluginMatch) {\n const pluginName = pluginMatch[1];\n if (!(options.plugins?.includes(pluginName) ?? true)) continue;\n }\n if (isRouteExcluded(match, excludedRoutePatterns)) continue;\n allFiles.add(match);\n }\n }\n\n const routeFiles = new Set<string>();\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isRouteExcluded(match, excludedRoutePatterns)) {\n routeFiles.add(match);\n }\n }\n }\n }\n }\n\n for (const f of routeFiles) allFiles.add(f);\n\n mkdirSync(destination, { recursive: true });\n\n let count = 0;\n for (const filePath of allFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(destination, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n const content = readFileSync(src);\n writeFileSync(dest, content);\n count++;\n }\n\n return count;\n}\n\nfunction isRouteExcluded(filePath: string, excludedPatterns: string[]): boolean {\n if (excludedPatterns.length === 0) return false;\n for (const pattern of excludedPatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nexport async function personalizeConfig(\n destination: string,\n opts: {\n extendsAccount: string;\n extendsGateway: string;\n account?: string;\n domain?: string;\n plugins?: string[];\n pluginRoutes?: Record<string, string[]>;\n workspaceOpts?: { localOverrides?: boolean; sourceDir?: string };\n },\n): Promise<void> {\n const configPath = join(destination, \"bos.config.json\");\n if (existsSync(configPath)) {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>;\n\n config.extends = `bos://${opts.extendsAccount}/${opts.extendsGateway}`;\n\n if (opts.account) {\n config.account = opts.account;\n }\n if (opts.domain) {\n config.domain = opts.domain;\n }\n\n if (config.app && typeof config.app === \"object\") {\n const app = config.app as Record<string, unknown>;\n for (const entryKey of Object.keys(app)) {\n const entry = app[entryKey];\n if (entry && typeof entry === \"object\") {\n const e = entry as Record<string, unknown>;\n delete e.production;\n delete e.integrity;\n delete e.ssr;\n delete e.ssrIntegrity;\n }\n }\n }\n\n if (config.plugins && typeof config.plugins === \"object\") {\n const plugins = config.plugins as Record<string, unknown>;\n\n if (opts.plugins && opts.plugins.length > 0) {\n for (const pluginKey of Object.keys(plugins)) {\n if (!opts.plugins.includes(pluginKey)) {\n delete plugins[pluginKey];\n }\n }\n }\n\n for (const pluginKey of Object.keys(plugins)) {\n const plugin = plugins[pluginKey];\n if (plugin && typeof plugin === \"object\") {\n const p = plugin as Record<string, unknown>;\n delete p.production;\n delete p.integrity;\n }\n }\n\n if (Object.keys(plugins).length === 0) {\n delete config.plugins;\n }\n }\n\n writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`);\n }\n\n const pkgPath = join(destination, \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n\n if (pkg.workspaces && typeof pkg.workspaces === \"object\") {\n const ws = pkg.workspaces as { packages?: string[] };\n if (Array.isArray(ws.packages)) {\n ws.packages = ws.packages\n .filter((p: string) => p !== \"host\" && !p.startsWith(\"packages/\"))\n .filter((p: string) => {\n const pluginMatch = p.match(/^plugins\\/([^/]+)/);\n if (!pluginMatch) return true;\n return opts.plugins?.includes(pluginMatch[1]) ?? true;\n });\n }\n }\n\n if (pkg.scripts && typeof pkg.scripts === \"object\") {\n const scripts = pkg.scripts as Record<string, string>;\n const rewrite = (key: string, from: string, to: string) => {\n if (scripts[key]?.includes(from)) {\n scripts[key] = scripts[key].replaceAll(from, to);\n }\n };\n rewrite(\"dev\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:ui\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:api\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:proxy\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"build\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"deploy\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"publish\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"start\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n\n if (scripts[\"sync:api-contract\"]) {\n delete scripts[\"sync:api-contract\"];\n }\n if (scripts.postinstall) {\n delete scripts.postinstall;\n }\n if (scripts.typecheck?.includes(\"sync:api-contract\")) {\n scripts.typecheck = scripts.typecheck.replace(\"bun run sync:api-contract && \", \"\");\n }\n }\n\n if (pkg.devDependencies && typeof pkg.devDependencies === \"object\") {\n const deps = pkg.devDependencies as Record<string, string>;\n delete deps[\"every-plugin\"];\n delete deps[\"everything-dev\"];\n }\n\n if (!pkg.dependencies) pkg.dependencies = {};\n const deps = pkg.dependencies as Record<string, string>;\n const spec = opts.workspaceOpts?.sourceDir\n ? loadManifestNormalizationSpec(opts.workspaceOpts.sourceDir)\n : null;\n if (!deps[\"everything-dev\"] && spec)\n deps[\"everything-dev\"] = spec.rootCatalog[\"everything-dev\"];\n if (!deps[\"every-plugin\"] && spec) deps[\"every-plugin\"] = spec.rootCatalog[\"every-plugin\"];\n\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n\n await resolveWorkspaceRefs(destination, opts.workspaceOpts);\n\n const genContractPath = join(destination, \"ui\", \"src\", \"api-contract.gen.ts\");\n if (!existsSync(genContractPath)) {\n mkdirSync(dirname(genContractPath), { recursive: true });\n writeFileSync(genContractPath, `export type ApiContract = Record<string, never>;\\n`);\n }\n}\n\nexport async function runBunInstall(destination: string): Promise<void> {\n await execCommand(\"bun\", [\"install\"], destination);\n}\n\nconst WORKSPACE_LOCAL_PATHS: Record<string, string> = {\n \"everything-dev\": \"packages/everything-dev\",\n \"every-plugin\": \"packages/every-plugin\",\n};\n\nasync function resolveWorkspaceRefs(\n destination: string,\n options?: { localOverrides?: boolean; sourceDir?: string },\n): Promise<void> {\n await normalizePackageManifestsInTree({\n sourceRootDir: options?.sourceDir ?? destination,\n targetDir: destination,\n resolveCatalogRefs: true,\n removeWorkspaceDeps: [\"host\"],\n });\n\n if (options?.localOverrides && options.sourceDir) {\n const rootPkgPath = join(destination, \"package.json\");\n if (existsSync(rootPkgPath)) {\n const pkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n if (!pkg.overrides) pkg.overrides = {};\n const overrides = pkg.overrides as Record<string, string>;\n\n const rootWorkspaces = ((pkg.workspaces as Record<string, string[]>)?.packages ?? []).filter(\n Boolean,\n );\n\n for (const [name, relPath] of Object.entries(WORKSPACE_LOCAL_PATHS)) {\n if (!rootWorkspaces.some((ws) => ws === relPath || ws === `plugins/${name}`)) {\n const srcPkgPath = join(options.sourceDir, relPath, \"package.json\");\n if (existsSync(srcPkgPath)) {\n overrides[name] = `file:${relPath}`;\n rootWorkspaces.push(relPath);\n }\n }\n }\n\n if (rootWorkspaces.length > 0) {\n if (!pkg.workspaces) pkg.workspaces = {};\n (pkg.workspaces as Record<string, string[]>).packages = rootWorkspaces;\n }\n\n writeFileSync(rootPkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n }\n}\n\nexport async function writeInitSnapshot(\n destination: string,\n extendsAccount: string,\n extendsGateway: string,\n sourceDir: string,\n patterns: string[],\n options: { withHost: boolean; plugins?: string[]; pluginRoutes?: Record<string, string[]> },\n): Promise<void> {\n const effectivePatterns = options.withHost\n ? [...patterns, \"host/**\"]\n : patterns.filter((p) => !p.startsWith(\"host/\") && p !== \"host/**\");\n\n const excludedRoutePatterns: string[] = [];\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n }\n\n const allFiles = new Set<string>();\n for (const pattern of effectivePatterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n const pluginMatch = match.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !(options.plugins?.includes(pluginMatch[1]) ?? true)) continue;\n if (isRouteExcluded(match, excludedRoutePatterns)) continue;\n allFiles.add(match);\n }\n }\n\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isRouteExcluded(match, excludedRoutePatterns)) {\n allFiles.add(match);\n }\n }\n }\n }\n }\n\n const fileHashes: Record<string, string> = {};\n for (const filePath of allFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n fileHashes[destPath] = computeHash(content);\n }\n\n await writeSnapshot(destination, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: fileHashes,\n });\n}\n\nfunction computeHash(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\").substring(0, 16);\n}\n\nfunction mkTmpDir(prefix: string): string {\n const base = join(tmpdir(), prefix);\n let attempt = 0;\n while (true) {\n const dir = `${base}-${Date.now()}-${attempt}`;\n try {\n mkdirSync(dir, { recursive: true });\n return dir;\n } catch {\n attempt++;\n if (attempt > 10) throw new Error(\"Failed to create temp directory\");\n }\n }\n}\n\nfunction execCommand(command: string, args: string[], cwd?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: \"pipe\",\n shell: true,\n });\n let stderr = \"\";\n child.stderr?.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(\n new Error(\n `Command '${command} ${args.join(\" \")}' failed with exit code ${code}: ${stderr}`,\n ),\n );\n });\n child.on(\"error\", reject);\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"init.mjs","names":[],"sources":["../../src/cli/init.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { createHash } from \"node:crypto\";\nimport {\n createWriteStream,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n rmSync,\n writeFileSync,\n} from \"node:fs\";\nimport { createRequire } from \"node:module\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pipeline } from \"node:stream/promises\";\nimport { glob } from \"glob\";\nimport { fetchBosConfigFromFastKv } from \"../fastkv\";\nimport {\n loadManifestNormalizationSpec,\n normalizePackageManifestsInTree,\n} from \"../internal/manifest-normalizer\";\nimport type { BosConfig } from \"../types\";\nimport { writeSnapshot } from \"./snapshot\";\n\nconst require = createRequire(import.meta.url);\n\ninterface SourceResult {\n sourceDir: string;\n parentConfig: BosConfig;\n cleanup: () => Promise<void>;\n}\n\nexport async function resolveSourceDir(opts: {\n extendsAccount: string;\n extendsGateway: string;\n source?: string;\n}): Promise<SourceResult> {\n if (opts.source) {\n const sourceDir = resolve(opts.source);\n if (!existsSync(join(sourceDir, \"bos.config.json\"))) {\n throw new Error(`No bos.config.json found in source directory: ${sourceDir}`);\n }\n const parentConfig = JSON.parse(\n readFileSync(join(sourceDir, \"bos.config.json\"), \"utf-8\"),\n ) as BosConfig;\n return { sourceDir, parentConfig, cleanup: async () => {} };\n }\n\n const parentConfig = await fetchParentConfig(opts.extendsAccount, opts.extendsGateway);\n\n if (!parentConfig.repository) {\n throw new Error(\"Parent config has no repository field — cannot locate template source\");\n }\n\n const { dir: sourceDir, cleanup } = await downloadTarball(parentConfig.repository);\n return { sourceDir, parentConfig, cleanup };\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 downloadTarball(\n repoUrl: string,\n): Promise<{ dir: string; cleanup: () => Promise<void> }> {\n const parsed = parseGitHubUrl(repoUrl);\n if (!parsed) {\n throw new Error(`Cannot parse repository URL: ${repoUrl}`);\n }\n\n const { owner, repo, branch } = parsed;\n const tarballUrl = `https://api.github.com/repos/${owner}/${repo}/tarball/${branch}`;\n\n const tmpDir = mkTmpDir(\"bos-init-tarball-\");\n const tarballPath = join(tmpDir, \"source.tar.gz\");\n\n const response = await fetch(tarballUrl, {\n headers: { \"User-Agent\": \"everything-dev\" },\n redirect: \"follow\",\n });\n\n if (!response.ok) {\n rmSync(tmpDir, { recursive: true, force: true });\n throw new Error(`GitHub tarball download failed: ${response.status} ${response.statusText}`);\n }\n\n if (!response.body) {\n rmSync(tmpDir, { recursive: true, force: true });\n throw new Error(\"GitHub tarball download returned empty body\");\n }\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 const tar = require(\"tar\") as {\n extract: (opts: { cwd: string; file: string; strip: number }) => Promise<void>;\n };\n await tar.extract({ 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 cleanup: async () => {\n rmSync(extractDir, { recursive: true, force: true });\n },\n };\n}\n\nfunction parseGitHubUrl(url: string): { owner: string; repo: string; branch: string } | null {\n const httpsMatch = url.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?(?:\\/.*)?$/);\n if (httpsMatch) {\n return { owner: httpsMatch[1], repo: httpsMatch[2], branch: \"main\" };\n }\n\n const sshMatch = url.match(/^git@github\\.com:([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (sshMatch) {\n return { owner: sshMatch[1], repo: sshMatch[2], branch: \"main\" };\n }\n\n return null;\n}\n\nexport async function readTemplatekeep(sourceDir: string): Promise<string[]> {\n const keepFile = join(sourceDir, \".templatekeep\");\n if (!existsSync(keepFile)) {\n return [];\n }\n\n const content = readFileSync(keepFile, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function copyFilteredFiles(\n sourceDir: string,\n destination: string,\n patterns: string[],\n options: { withHost: boolean; plugins?: string[]; pluginRoutes?: Record<string, string[]> },\n): Promise<number> {\n if (patterns.length === 0) {\n return 0;\n }\n\n const effectivePatterns = options.withHost\n ? [...patterns, \"host/**\"]\n : patterns.filter((p) => !p.startsWith(\"host/\") && p !== \"host/**\");\n\n const filteredPatterns = effectivePatterns.filter((p) => {\n const pluginMatch = p.match(/^plugins\\/([^/]+)/);\n if (!pluginMatch) return true;\n const pluginName = pluginMatch[1];\n return options.plugins?.includes(pluginName) ?? true;\n });\n\n const excludedRoutePatterns: string[] = [];\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n }\n\n const allFiles = new Set<string>();\n for (const pattern of filteredPatterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n const pluginMatch = match.match(/^plugins\\/([^/]+)/);\n if (pluginMatch) {\n const pluginName = pluginMatch[1];\n if (!(options.plugins?.includes(pluginName) ?? true)) continue;\n }\n if (isRouteExcluded(match, excludedRoutePatterns)) continue;\n allFiles.add(match);\n }\n }\n\n const routeFiles = new Set<string>();\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isRouteExcluded(match, excludedRoutePatterns)) {\n routeFiles.add(match);\n }\n }\n }\n }\n }\n\n for (const f of routeFiles) allFiles.add(f);\n\n mkdirSync(destination, { recursive: true });\n\n let count = 0;\n for (const filePath of allFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(destination, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n const content = readFileSync(src);\n writeFileSync(dest, content);\n count++;\n }\n\n return count;\n}\n\nfunction isRouteExcluded(filePath: string, excludedPatterns: string[]): boolean {\n if (excludedPatterns.length === 0) return false;\n for (const pattern of excludedPatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nexport async function personalizeConfig(\n destination: string,\n opts: {\n extendsAccount: string;\n extendsGateway: string;\n account?: string;\n domain?: string;\n plugins?: string[];\n pluginRoutes?: Record<string, string[]>;\n workspaceOpts?: { localOverrides?: boolean; sourceDir?: string };\n mode?: \"init\" | \"sync\";\n },\n): Promise<void> {\n const isInit = opts.mode !== \"sync\";\n const configPath = join(destination, \"bos.config.json\");\n if (existsSync(configPath)) {\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as Record<string, unknown>;\n\n config.extends = `bos://${opts.extendsAccount}/${opts.extendsGateway}`;\n\n if (opts.account) {\n config.account = opts.account;\n }\n if (opts.domain) {\n config.domain = opts.domain;\n }\n\n if (isInit && config.app && typeof config.app === \"object\") {\n const app = config.app as Record<string, unknown>;\n for (const entryKey of Object.keys(app)) {\n const entry = app[entryKey];\n if (entry && typeof entry === \"object\") {\n const e = entry as Record<string, unknown>;\n delete e.production;\n delete e.integrity;\n delete e.ssr;\n delete e.ssrIntegrity;\n }\n }\n }\n\n if (config.plugins && typeof config.plugins === \"object\") {\n const plugins = config.plugins as Record<string, unknown>;\n\n if (opts.plugins && opts.plugins.length > 0) {\n for (const pluginKey of Object.keys(plugins)) {\n if (!opts.plugins.includes(pluginKey)) {\n delete plugins[pluginKey];\n }\n }\n }\n\n if (isInit) {\n for (const pluginKey of Object.keys(plugins)) {\n const plugin = plugins[pluginKey];\n if (plugin && typeof plugin === \"object\") {\n const p = plugin as Record<string, unknown>;\n delete p.production;\n delete p.integrity;\n }\n }\n }\n\n if (Object.keys(plugins).length === 0) {\n delete config.plugins;\n }\n }\n\n writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\\n`);\n }\n\n const pkgPath = join(destination, \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n\n if (pkg.workspaces && typeof pkg.workspaces === \"object\") {\n const ws = pkg.workspaces as { packages?: string[] };\n if (Array.isArray(ws.packages)) {\n ws.packages = ws.packages\n .filter((p: string) => p !== \"host\" && !p.startsWith(\"packages/\"))\n .filter((p: string) => {\n const pluginMatch = p.match(/^plugins\\/([^/]+)/);\n if (!pluginMatch) return true;\n return opts.plugins?.includes(pluginMatch[1]) ?? true;\n });\n }\n }\n\n if (pkg.scripts && typeof pkg.scripts === \"object\") {\n const scripts = pkg.scripts as Record<string, string>;\n const rewrite = (key: string, from: string, to: string) => {\n if (scripts[key]?.includes(from)) {\n scripts[key] = scripts[key].replaceAll(from, to);\n }\n };\n rewrite(\"dev\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:ui\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:api\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"dev:proxy\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"build\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"deploy\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"publish\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n rewrite(\"start\", \"packages/everything-dev/cli.js\", \"node_modules/.bin/bos\");\n\n if (scripts[\"sync:api-contract\"]) {\n delete scripts[\"sync:api-contract\"];\n }\n if (scripts.postinstall) {\n delete scripts.postinstall;\n }\n if (scripts.typecheck?.includes(\"sync:api-contract\")) {\n scripts.typecheck = scripts.typecheck.replace(\"bun run sync:api-contract && \", \"\");\n }\n }\n\n if (pkg.devDependencies && typeof pkg.devDependencies === \"object\") {\n const deps = pkg.devDependencies as Record<string, string>;\n delete deps[\"every-plugin\"];\n delete deps[\"everything-dev\"];\n }\n\n if (!pkg.dependencies) pkg.dependencies = {};\n const deps = pkg.dependencies as Record<string, string>;\n const spec = opts.workspaceOpts?.sourceDir\n ? loadManifestNormalizationSpec(opts.workspaceOpts.sourceDir)\n : null;\n if (!deps[\"everything-dev\"] && spec)\n deps[\"everything-dev\"] = spec.rootCatalog[\"everything-dev\"];\n if (!deps[\"every-plugin\"] && spec) deps[\"every-plugin\"] = spec.rootCatalog[\"every-plugin\"];\n\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n\n await resolveWorkspaceRefs(destination, opts.workspaceOpts);\n\n const genContractPath = join(destination, \"ui\", \"src\", \"api-contract.gen.ts\");\n if (!existsSync(genContractPath)) {\n mkdirSync(dirname(genContractPath), { recursive: true });\n writeFileSync(genContractPath, `export type ApiContract = Record<string, never>;\\n`);\n }\n}\n\nexport async function runBunInstall(destination: string): Promise<void> {\n await execCommand(\"bun\", [\"install\"], destination);\n}\n\nconst WORKSPACE_LOCAL_PATHS: Record<string, string> = {\n \"everything-dev\": \"packages/everything-dev\",\n \"every-plugin\": \"packages/every-plugin\",\n};\n\nasync function resolveWorkspaceRefs(\n destination: string,\n options?: { localOverrides?: boolean; sourceDir?: string },\n): Promise<void> {\n await normalizePackageManifestsInTree({\n sourceRootDir: options?.sourceDir ?? destination,\n targetDir: destination,\n resolveCatalogRefs: true,\n removeWorkspaceDeps: [\"host\"],\n });\n\n if (options?.localOverrides && options.sourceDir) {\n const rootPkgPath = join(destination, \"package.json\");\n if (existsSync(rootPkgPath)) {\n const pkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n if (!pkg.overrides) pkg.overrides = {};\n const overrides = pkg.overrides as Record<string, string>;\n\n const rootWorkspaces = ((pkg.workspaces as Record<string, string[]>)?.packages ?? []).filter(\n Boolean,\n );\n\n for (const [name, relPath] of Object.entries(WORKSPACE_LOCAL_PATHS)) {\n if (!rootWorkspaces.some((ws) => ws === relPath || ws === `plugins/${name}`)) {\n const srcPkgPath = join(options.sourceDir, relPath, \"package.json\");\n if (existsSync(srcPkgPath)) {\n overrides[name] = `file:${relPath}`;\n rootWorkspaces.push(relPath);\n }\n }\n }\n\n if (rootWorkspaces.length > 0) {\n if (!pkg.workspaces) pkg.workspaces = {};\n (pkg.workspaces as Record<string, string[]>).packages = rootWorkspaces;\n }\n\n writeFileSync(rootPkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n }\n}\n\nexport async function writeInitSnapshot(\n destination: string,\n extendsAccount: string,\n extendsGateway: string,\n sourceDir: string,\n patterns: string[],\n options: { withHost: boolean; plugins?: string[]; pluginRoutes?: Record<string, string[]> },\n): Promise<void> {\n const effectivePatterns = options.withHost\n ? [...patterns, \"host/**\"]\n : patterns.filter((p) => !p.startsWith(\"host/\") && p !== \"host/**\");\n\n const excludedRoutePatterns: string[] = [];\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n }\n\n const allFiles = new Set<string>();\n for (const pattern of effectivePatterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n const pluginMatch = match.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !(options.plugins?.includes(pluginMatch[1]) ?? true)) continue;\n if (isRouteExcluded(match, excludedRoutePatterns)) continue;\n allFiles.add(match);\n }\n }\n\n if (options.pluginRoutes) {\n for (const [pluginKey, routePatterns] of Object.entries(options.pluginRoutes)) {\n if (!(options.plugins?.includes(pluginKey) ?? true)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isRouteExcluded(match, excludedRoutePatterns)) {\n allFiles.add(match);\n }\n }\n }\n }\n }\n\n const fileHashes: Record<string, string> = {};\n for (const filePath of allFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n fileHashes[destPath] = computeHash(content);\n }\n\n await writeSnapshot(destination, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: fileHashes,\n });\n}\n\nfunction computeHash(data: Uint8Array): string {\n return createHash(\"sha256\").update(data).digest(\"hex\").substring(0, 16);\n}\n\nfunction mkTmpDir(prefix: string): string {\n const base = join(tmpdir(), prefix);\n let attempt = 0;\n while (true) {\n const dir = `${base}-${Date.now()}-${attempt}`;\n try {\n mkdirSync(dir, { recursive: true });\n return dir;\n } catch {\n attempt++;\n if (attempt > 10) throw new Error(\"Failed to create temp directory\");\n }\n }\n}\n\nfunction execCommand(command: string, args: string[], cwd?: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(command, args, {\n cwd,\n stdio: \"pipe\",\n shell: true,\n });\n let stderr = \"\";\n child.stderr?.on(\"data\", (data: Buffer) => {\n stderr += data.toString();\n });\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(\n new Error(\n `Command '${command} ${args.join(\" \")}' failed with exit code ${code}: ${stderr}`,\n ),\n );\n });\n child.on(\"error\", reject);\n });\n}\n"],"mappings":";;;;;;;;;;;;;AAwBA,MAAM,UAAU,cAAc,OAAO,KAAK,IAAI;AAQ9C,eAAsB,iBAAiB,MAIb;AACxB,KAAI,KAAK,QAAQ;EACf,MAAM,YAAY,QAAQ,KAAK,OAAO;AACtC,MAAI,CAAC,WAAW,KAAK,WAAW,kBAAkB,CAAC,CACjD,OAAM,IAAI,MAAM,iDAAiD,YAAY;AAK/E,SAAO;GAAE;GAAW,cAHC,KAAK,MACxB,aAAa,KAAK,WAAW,kBAAkB,EAAE,QAAQ,CAC1D;GACiC,SAAS,YAAY;GAAI;;CAG7D,MAAM,eAAe,MAAM,kBAAkB,KAAK,gBAAgB,KAAK,eAAe;AAEtF,KAAI,CAAC,aAAa,WAChB,OAAM,IAAI,MAAM,wEAAwE;CAG1F,MAAM,EAAE,KAAK,WAAW,YAAY,MAAM,gBAAgB,aAAa,WAAW;AAClF,QAAO;EAAE;EAAW;EAAc;EAAS;;AAG7C,eAAsB,kBACpB,gBACA,gBACoB;AAEpB,QAAO,yBADQ,SAAS,eAAe,GAAG,iBACQ;;AAGpD,eAAsB,gBACpB,SACwD;CACxD,MAAM,SAAS,eAAe,QAAQ;AACtC,KAAI,CAAC,OACH,OAAM,IAAI,MAAM,gCAAgC,UAAU;CAG5D,MAAM,EAAE,OAAO,MAAM,WAAW;CAChC,MAAM,aAAa,gCAAgC,MAAM,GAAG,KAAK,WAAW;CAE5E,MAAM,SAAS,SAAS,oBAAoB;CAC5C,MAAM,cAAc,KAAK,QAAQ,gBAAgB;CAEjD,MAAM,WAAW,MAAM,MAAM,YAAY;EACvC,SAAS,EAAE,cAAc,kBAAkB;EAC3C,UAAU;EACX,CAAC;AAEF,KAAI,CAAC,SAAS,IAAI;AAChB,SAAO,QAAQ;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;AAChD,QAAM,IAAI,MAAM,mCAAmC,SAAS,OAAO,GAAG,SAAS,aAAa;;AAG9F,KAAI,CAAC,SAAS,MAAM;AAClB,SAAO,QAAQ;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;AAChD,QAAM,IAAI,MAAM,8CAA8C;;CAGhE,MAAM,aAAa,kBAAkB,YAAY;CACjD,MAAM,SAAS,SAAS;AACxB,OAAM,SAAS,QAAQ,WAAW;CAElC,MAAM,aAAa,SAAS,oBAAoB;AAChD,KAAI;AAIF,QAHY,QAAQ,MAAM,CAGhB,QAAQ;GAAE,KAAK;GAAY,MAAM;GAAa,OAAO;GAAG,CAAC;SAC7D;AACN,QAAM,YAAY,OAAO;GAAC;GAAQ;GAAa;GAAwB;GAAM;GAAW,CAAC;;AAG3F,QAAO,QAAQ;EAAE,WAAW;EAAM,OAAO;EAAM,CAAC;AAEhD,QAAO;EACL,KAAK;EACL,SAAS,YAAY;AACnB,UAAO,YAAY;IAAE,WAAW;IAAM,OAAO;IAAM,CAAC;;EAEvD;;AAGH,SAAS,eAAe,KAAqE;CAC3F,MAAM,aAAa,IAAI,MAAM,iEAAiE;AAC9F,KAAI,WACF,QAAO;EAAE,OAAO,WAAW;EAAI,MAAM,WAAW;EAAI,QAAQ;EAAQ;CAGtE,MAAM,WAAW,IAAI,MAAM,gDAAgD;AAC3E,KAAI,SACF,QAAO;EAAE,OAAO,SAAS;EAAI,MAAM,SAAS;EAAI,QAAQ;EAAQ;AAGlE,QAAO;;AAGT,eAAsB,iBAAiB,WAAsC;CAC3E,MAAM,WAAW,KAAK,WAAW,gBAAgB;AACjD,KAAI,CAAC,WAAW,SAAS,CACvB,QAAO,EAAE;AAIX,QADgB,aAAa,UAAU,QAAQ,CAE5C,MAAM,KAAK,CACX,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,QAAQ,SAAS,KAAK,SAAS,KAAK,CAAC,KAAK,WAAW,IAAI,CAAC;;AAG/D,eAAsB,kBACpB,WACA,aACA,UACA,SACiB;AACjB,KAAI,SAAS,WAAW,EACtB,QAAO;CAOT,MAAM,oBAJoB,QAAQ,WAC9B,CAAC,GAAG,UAAU,UAAU,GACxB,SAAS,QAAQ,MAAM,CAAC,EAAE,WAAW,QAAQ,IAAI,MAAM,UAAU,EAE1B,QAAQ,MAAM;EACvD,MAAM,cAAc,EAAE,MAAM,oBAAoB;AAChD,MAAI,CAAC,YAAa,QAAO;EACzB,MAAM,aAAa,YAAY;AAC/B,SAAO,QAAQ,SAAS,SAAS,WAAW,IAAI;GAChD;CAEF,MAAM,wBAAkC,EAAE;AAC1C,KAAI,QAAQ,cACV;OAAK,MAAM,CAAC,WAAW,kBAAkB,OAAO,QAAQ,QAAQ,aAAa,CAC3E,KAAI,EAAE,QAAQ,SAAS,SAAS,UAAU,IAAI,MAC5C,uBAAsB,KAAK,GAAG,cAAc;;CAKlD,MAAM,2BAAW,IAAI,KAAa;AAClC,MAAK,MAAM,WAAW,kBAAkB;EACtC,MAAM,UAAU,MAAM,KAAK,SAAS;GAClC,KAAK;GACL,OAAO;GACP,KAAK;GACL,UAAU;GACX,CAAC;AACF,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,cAAc,MAAM,MAAM,oBAAoB;AACpD,OAAI,aAAa;IACf,MAAM,aAAa,YAAY;AAC/B,QAAI,EAAE,QAAQ,SAAS,SAAS,WAAW,IAAI,MAAO;;AAExD,OAAI,gBAAgB,OAAO,sBAAsB,CAAE;AACnD,YAAS,IAAI,MAAM;;;CAIvB,MAAM,6BAAa,IAAI,KAAa;AACpC,KAAI,QAAQ,aACV,MAAK,MAAM,CAAC,WAAW,kBAAkB,OAAO,QAAQ,QAAQ,aAAa,EAAE;AAC7E,MAAI,EAAE,QAAQ,SAAS,SAAS,UAAU,IAAI,MAAO;AACrD,OAAK,MAAM,MAAM,eAAe;GAC9B,MAAM,UAAU,MAAM,KAAK,IAAI;IAC7B,KAAK;IACL,OAAO;IACP,KAAK;IACL,UAAU;IACX,CAAC;AACF,QAAK,MAAM,SAAS,QAClB,KAAI,CAAC,gBAAgB,OAAO,sBAAsB,CAChD,YAAW,IAAI,MAAM;;;AAO/B,MAAK,MAAM,KAAK,WAAY,UAAS,IAAI,EAAE;AAE3C,WAAU,aAAa,EAAE,WAAW,MAAM,CAAC;CAE3C,IAAI,QAAQ;AACZ,MAAK,MAAM,YAAY,UAAU;EAC/B,MAAM,MAAM,KAAK,WAAW,SAAS;AAErC,MAAI,CADS,UAAU,IAAI,CACjB,QAAQ,CAAE;EAKpB,MAAM,OAAO,KAAK,aAHD,SAAS,WAAW,qBAAqB,GACtD,SAAS,QAAQ,0BAA0B,WAAW,GACtD,SACoC;AACxC,YAAU,QAAQ,KAAK,EAAE,EAAE,WAAW,MAAM,CAAC;AAE7C,gBAAc,MADE,aAAa,IAAI,CACL;AAC5B;;AAGF,QAAO;;AAGT,SAAS,gBAAgB,UAAkB,kBAAqC;AAC9E,KAAI,iBAAiB,WAAW,EAAG,QAAO;AAC1C,MAAK,MAAM,WAAW,iBACpB,KAAI,QAAQ,SAAS,MAAM,EAAE;EAC3B,MAAM,SAAS,QAAQ,MAAM,GAAG,GAAG;AACnC,MAAI,SAAS,WAAW,GAAG,OAAO,GAAG,IAAI,aAAa,OAAQ,QAAO;YAC5D,aAAa,WAAW,SAAS,WAAW,GAAG,QAAQ,GAAG,CACnE,QAAO;AAGX,QAAO;;AAGT,eAAsB,kBACpB,aACA,MAUe;CACf,MAAM,SAAS,KAAK,SAAS;CAC7B,MAAM,aAAa,KAAK,aAAa,kBAAkB;AACvD,KAAI,WAAW,WAAW,EAAE;EAC1B,MAAM,SAAS,KAAK,MAAM,aAAa,YAAY,QAAQ,CAAC;AAE5D,SAAO,UAAU,SAAS,KAAK,eAAe,GAAG,KAAK;AAEtD,MAAI,KAAK,QACP,QAAO,UAAU,KAAK;AAExB,MAAI,KAAK,OACP,QAAO,SAAS,KAAK;AAGvB,MAAI,UAAU,OAAO,OAAO,OAAO,OAAO,QAAQ,UAAU;GAC1D,MAAM,MAAM,OAAO;AACnB,QAAK,MAAM,YAAY,OAAO,KAAK,IAAI,EAAE;IACvC,MAAM,QAAQ,IAAI;AAClB,QAAI,SAAS,OAAO,UAAU,UAAU;KACtC,MAAM,IAAI;AACV,YAAO,EAAE;AACT,YAAO,EAAE;AACT,YAAO,EAAE;AACT,YAAO,EAAE;;;;AAKf,MAAI,OAAO,WAAW,OAAO,OAAO,YAAY,UAAU;GACxD,MAAM,UAAU,OAAO;AAEvB,OAAI,KAAK,WAAW,KAAK,QAAQ,SAAS,GACxC;SAAK,MAAM,aAAa,OAAO,KAAK,QAAQ,CAC1C,KAAI,CAAC,KAAK,QAAQ,SAAS,UAAU,CACnC,QAAO,QAAQ;;AAKrB,OAAI,OACF,MAAK,MAAM,aAAa,OAAO,KAAK,QAAQ,EAAE;IAC5C,MAAM,SAAS,QAAQ;AACvB,QAAI,UAAU,OAAO,WAAW,UAAU;KACxC,MAAM,IAAI;AACV,YAAO,EAAE;AACT,YAAO,EAAE;;;AAKf,OAAI,OAAO,KAAK,QAAQ,CAAC,WAAW,EAClC,QAAO,OAAO;;AAIlB,gBAAc,YAAY,GAAG,KAAK,UAAU,QAAQ,MAAM,EAAE,CAAC,IAAI;;CAGnE,MAAM,UAAU,KAAK,aAAa,eAAe;AACjD,KAAI,WAAW,QAAQ,EAAE;EACvB,MAAM,MAAM,KAAK,MAAM,aAAa,SAAS,QAAQ,CAAC;AAEtD,MAAI,IAAI,cAAc,OAAO,IAAI,eAAe,UAAU;GACxD,MAAM,KAAK,IAAI;AACf,OAAI,MAAM,QAAQ,GAAG,SAAS,CAC5B,IAAG,WAAW,GAAG,SACd,QAAQ,MAAc,MAAM,UAAU,CAAC,EAAE,WAAW,YAAY,CAAC,CACjE,QAAQ,MAAc;IACrB,MAAM,cAAc,EAAE,MAAM,oBAAoB;AAChD,QAAI,CAAC,YAAa,QAAO;AACzB,WAAO,KAAK,SAAS,SAAS,YAAY,GAAG,IAAI;KACjD;;AAIR,MAAI,IAAI,WAAW,OAAO,IAAI,YAAY,UAAU;GAClD,MAAM,UAAU,IAAI;GACpB,MAAM,WAAW,KAAa,MAAc,OAAe;AACzD,QAAI,QAAQ,MAAM,SAAS,KAAK,CAC9B,SAAQ,OAAO,QAAQ,KAAK,WAAW,MAAM,GAAG;;AAGpD,WAAQ,OAAO,kCAAkC,wBAAwB;AACzE,WAAQ,UAAU,kCAAkC,wBAAwB;AAC5E,WAAQ,WAAW,kCAAkC,wBAAwB;AAC7E,WAAQ,aAAa,kCAAkC,wBAAwB;AAC/E,WAAQ,SAAS,kCAAkC,wBAAwB;AAC3E,WAAQ,UAAU,kCAAkC,wBAAwB;AAC5E,WAAQ,WAAW,kCAAkC,wBAAwB;AAC7E,WAAQ,SAAS,kCAAkC,wBAAwB;AAE3E,OAAI,QAAQ,qBACV,QAAO,QAAQ;AAEjB,OAAI,QAAQ,YACV,QAAO,QAAQ;AAEjB,OAAI,QAAQ,WAAW,SAAS,oBAAoB,CAClD,SAAQ,YAAY,QAAQ,UAAU,QAAQ,iCAAiC,GAAG;;AAItF,MAAI,IAAI,mBAAmB,OAAO,IAAI,oBAAoB,UAAU;GAClE,MAAM,OAAO,IAAI;AACjB,UAAO,KAAK;AACZ,UAAO,KAAK;;AAGd,MAAI,CAAC,IAAI,aAAc,KAAI,eAAe,EAAE;EAC5C,MAAM,OAAO,IAAI;EACjB,MAAM,OAAO,KAAK,eAAe,YAC7B,8BAA8B,KAAK,cAAc,UAAU,GAC3D;AACJ,MAAI,CAAC,KAAK,qBAAqB,KAC7B,MAAK,oBAAoB,KAAK,YAAY;AAC5C,MAAI,CAAC,KAAK,mBAAmB,KAAM,MAAK,kBAAkB,KAAK,YAAY;AAE3E,gBAAc,SAAS,GAAG,KAAK,UAAU,KAAK,MAAM,EAAE,CAAC,IAAI;;AAG7D,OAAM,qBAAqB,aAAa,KAAK,cAAc;CAE3D,MAAM,kBAAkB,KAAK,aAAa,MAAM,OAAO,sBAAsB;AAC7E,KAAI,CAAC,WAAW,gBAAgB,EAAE;AAChC,YAAU,QAAQ,gBAAgB,EAAE,EAAE,WAAW,MAAM,CAAC;AACxD,gBAAc,iBAAiB,qDAAqD;;;AAIxF,eAAsB,cAAc,aAAoC;AACtE,OAAM,YAAY,OAAO,CAAC,UAAU,EAAE,YAAY;;AAGpD,MAAM,wBAAgD;CACpD,kBAAkB;CAClB,gBAAgB;CACjB;AAED,eAAe,qBACb,aACA,SACe;AACf,OAAM,gCAAgC;EACpC,eAAe,SAAS,aAAa;EACrC,WAAW;EACX,oBAAoB;EACpB,qBAAqB,CAAC,OAAO;EAC9B,CAAC;AAEF,KAAI,SAAS,kBAAkB,QAAQ,WAAW;EAChD,MAAM,cAAc,KAAK,aAAa,eAAe;AACrD,MAAI,WAAW,YAAY,EAAE;GAC3B,MAAM,MAAM,KAAK,MAAM,aAAa,aAAa,QAAQ,CAAC;AAC1D,OAAI,CAAC,IAAI,UAAW,KAAI,YAAY,EAAE;GACtC,MAAM,YAAY,IAAI;GAEtB,MAAM,kBAAmB,IAAI,YAAyC,YAAY,EAAE,EAAE,OACpF,QACD;AAED,QAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,sBAAsB,CACjE,KAAI,CAAC,eAAe,MAAM,OAAO,OAAO,WAAW,OAAO,WAAW,OAAO,EAE1E;QAAI,WADe,KAAK,QAAQ,WAAW,SAAS,eAAe,CACzC,EAAE;AAC1B,eAAU,QAAQ,QAAQ;AAC1B,oBAAe,KAAK,QAAQ;;;AAKlC,OAAI,eAAe,SAAS,GAAG;AAC7B,QAAI,CAAC,IAAI,WAAY,KAAI,aAAa,EAAE;AACxC,IAAC,IAAI,WAAwC,WAAW;;AAG1D,iBAAc,aAAa,GAAG,KAAK,UAAU,KAAK,MAAM,EAAE,CAAC,IAAI;;;;AAKrE,eAAsB,kBACpB,aACA,gBACA,gBACA,WACA,UACA,SACe;CACf,MAAM,oBAAoB,QAAQ,WAC9B,CAAC,GAAG,UAAU,UAAU,GACxB,SAAS,QAAQ,MAAM,CAAC,EAAE,WAAW,QAAQ,IAAI,MAAM,UAAU;CAErE,MAAM,wBAAkC,EAAE;AAC1C,KAAI,QAAQ,cACV;OAAK,MAAM,CAAC,WAAW,kBAAkB,OAAO,QAAQ,QAAQ,aAAa,CAC3E,KAAI,EAAE,QAAQ,SAAS,SAAS,UAAU,IAAI,MAC5C,uBAAsB,KAAK,GAAG,cAAc;;CAKlD,MAAM,2BAAW,IAAI,KAAa;AAClC,MAAK,MAAM,WAAW,mBAAmB;EACvC,MAAM,UAAU,MAAM,KAAK,SAAS;GAClC,KAAK;GACL,OAAO;GACP,KAAK;GACL,UAAU;GACX,CAAC;AACF,OAAK,MAAM,SAAS,SAAS;GAC3B,MAAM,cAAc,MAAM,MAAM,oBAAoB;AACpD,OAAI,eAAe,EAAE,QAAQ,SAAS,SAAS,YAAY,GAAG,IAAI,MAAO;AACzE,OAAI,gBAAgB,OAAO,sBAAsB,CAAE;AACnD,YAAS,IAAI,MAAM;;;AAIvB,KAAI,QAAQ,aACV,MAAK,MAAM,CAAC,WAAW,kBAAkB,OAAO,QAAQ,QAAQ,aAAa,EAAE;AAC7E,MAAI,EAAE,QAAQ,SAAS,SAAS,UAAU,IAAI,MAAO;AACrD,OAAK,MAAM,MAAM,eAAe;GAC9B,MAAM,UAAU,MAAM,KAAK,IAAI;IAC7B,KAAK;IACL,OAAO;IACP,KAAK;IACL,UAAU;IACX,CAAC;AACF,QAAK,MAAM,SAAS,QAClB,KAAI,CAAC,gBAAgB,OAAO,sBAAsB,CAChD,UAAS,IAAI,MAAM;;;CAO7B,MAAM,aAAqC,EAAE;AAC7C,MAAK,MAAM,YAAY,UAAU;EAC/B,MAAM,MAAM,KAAK,WAAW,SAAS;AAErC,MAAI,CADS,UAAU,IAAI,CACjB,QAAQ,CAAE;EACpB,MAAM,UAAU,aAAa,IAAI;EACjC,MAAM,WAAW,SAAS,WAAW,qBAAqB,GACtD,SAAS,QAAQ,0BAA0B,WAAW,GACtD;AACJ,aAAW,YAAY,YAAY,QAAQ;;AAG7C,OAAM,cAAc,aAAa;EAC/B,WAAW,SAAS,eAAe,GAAG;EACtC,OAAO;EACR,CAAC;;AAGJ,SAAS,YAAY,MAA0B;AAC7C,QAAO,WAAW,SAAS,CAAC,OAAO,KAAK,CAAC,OAAO,MAAM,CAAC,UAAU,GAAG,GAAG;;AAGzE,SAAS,SAAS,QAAwB;CACxC,MAAM,OAAO,KAAK,QAAQ,EAAE,OAAO;CACnC,IAAI,UAAU;AACd,QAAO,MAAM;EACX,MAAM,MAAM,GAAG,KAAK,GAAG,KAAK,KAAK,CAAC,GAAG;AACrC,MAAI;AACF,aAAU,KAAK,EAAE,WAAW,MAAM,CAAC;AACnC,UAAO;UACD;AACN;AACA,OAAI,UAAU,GAAI,OAAM,IAAI,MAAM,kCAAkC;;;;AAK1E,SAAS,YAAY,SAAiB,MAAgB,KAA6B;AACjF,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,QAAQ,MAAM,SAAS,MAAM;GACjC;GACA,OAAO;GACP,OAAO;GACR,CAAC;EACF,IAAI,SAAS;AACb,QAAM,QAAQ,GAAG,SAAS,SAAiB;AACzC,aAAU,KAAK,UAAU;IACzB;AACF,QAAM,GAAG,UAAU,SAAS;AAC1B,OAAI,SAAS,EAAG,UAAS;OAEvB,wBACE,IAAI,MACF,YAAY,QAAQ,GAAG,KAAK,KAAK,IAAI,CAAC,0BAA0B,KAAK,IAAI,SAC1E,CACF;IACH;AACF,QAAM,GAAG,SAAS,OAAO;GACzB"}
|
package/dist/cli/sync.cjs
CHANGED
|
@@ -50,6 +50,43 @@ function backupFiles(projectDir, filePaths) {
|
|
|
50
50
|
}
|
|
51
51
|
return backupDir;
|
|
52
52
|
}
|
|
53
|
+
function isPlainObject(value) {
|
|
54
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
55
|
+
}
|
|
56
|
+
function mergeJsonValues(local, template) {
|
|
57
|
+
if (isPlainObject(local) && isPlainObject(template)) {
|
|
58
|
+
const merged = {};
|
|
59
|
+
for (const key of Object.keys(local)) merged[key] = mergeJsonValues(local[key], template[key]);
|
|
60
|
+
for (const key of Object.keys(template)) if (!(key in merged)) merged[key] = template[key];
|
|
61
|
+
return merged;
|
|
62
|
+
}
|
|
63
|
+
return local ?? template;
|
|
64
|
+
}
|
|
65
|
+
function mergeBosConfig(local, template) {
|
|
66
|
+
const merged = {};
|
|
67
|
+
if (local.extends !== void 0) merged.extends = local.extends;
|
|
68
|
+
else if (template.extends !== void 0) merged.extends = template.extends;
|
|
69
|
+
const TRAIL_GROUP = [
|
|
70
|
+
"app",
|
|
71
|
+
"plugins",
|
|
72
|
+
"shared"
|
|
73
|
+
];
|
|
74
|
+
const localKeys = Object.keys(local).filter((k) => k !== "extends");
|
|
75
|
+
const templateKeys = Object.keys(template).filter((k) => k !== "extends" && !localKeys.includes(k));
|
|
76
|
+
const firstTrailIndex = localKeys.findIndex((k) => TRAIL_GROUP.includes(k));
|
|
77
|
+
const orderedKeys = [];
|
|
78
|
+
if (firstTrailIndex >= 0) {
|
|
79
|
+
orderedKeys.push(...localKeys.slice(0, firstTrailIndex));
|
|
80
|
+
orderedKeys.push(...templateKeys);
|
|
81
|
+
for (const trailKey of TRAIL_GROUP) if (localKeys.includes(trailKey)) orderedKeys.push(trailKey);
|
|
82
|
+
} else {
|
|
83
|
+
orderedKeys.push(...localKeys);
|
|
84
|
+
orderedKeys.push(...templateKeys);
|
|
85
|
+
for (const trailKey of TRAIL_GROUP) if (templateKeys.includes(trailKey)) orderedKeys.push(trailKey);
|
|
86
|
+
}
|
|
87
|
+
for (const key of orderedKeys) merged[key] = mergeJsonValues(local[key], template[key]);
|
|
88
|
+
return merged;
|
|
89
|
+
}
|
|
53
90
|
function mergePackageJson(local, template) {
|
|
54
91
|
const merged = { ...template };
|
|
55
92
|
for (const depField of [
|
|
@@ -80,6 +117,15 @@ function writeSyncedFile(sourceDir, projectDir, filePath) {
|
|
|
80
117
|
const src = (0, node_path.join)(sourceDir, filePath);
|
|
81
118
|
const dest = (0, node_path.join)(projectDir, filePath.startsWith(".github/templates/") ? filePath.replace(/^\.github\/templates\//, ".github/") : filePath);
|
|
82
119
|
(0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
|
|
120
|
+
if (filePath.endsWith("bos.config.json")) {
|
|
121
|
+
const localContent = (0, node_fs.existsSync)(dest) ? (0, node_fs.readFileSync)(dest, "utf-8") : null;
|
|
122
|
+
const templateContent = (0, node_fs.readFileSync)(src, "utf-8");
|
|
123
|
+
if (localContent) {
|
|
124
|
+
const merged = mergeBosConfig(JSON.parse(localContent), JSON.parse(templateContent));
|
|
125
|
+
(0, node_fs.writeFileSync)(dest, `${JSON.stringify(merged, null, 2)}\n`);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
83
129
|
if (filePath.endsWith("package.json")) {
|
|
84
130
|
const localContent = (0, node_fs.existsSync)(dest) ? (0, node_fs.readFileSync)(dest, "utf-8") : null;
|
|
85
131
|
const templateContent = (0, node_fs.readFileSync)(src, "utf-8");
|
|
@@ -218,7 +264,8 @@ async function syncTemplate(projectDir, options) {
|
|
|
218
264
|
domain: localConfig.domain || extendsGateway,
|
|
219
265
|
plugins: childPlugins,
|
|
220
266
|
pluginRoutes,
|
|
221
|
-
workspaceOpts: { sourceDir }
|
|
267
|
+
workspaceOpts: { sourceDir },
|
|
268
|
+
mode: "sync"
|
|
222
269
|
});
|
|
223
270
|
if (!options.noInstall) await require_cli_init.runBunInstall(projectDir);
|
|
224
271
|
return {
|
package/dist/cli/sync.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.cjs","names":["resolveSourceDir","readTemplatekeep","readSnapshot","writeSnapshot","personalizeConfig","runBunInstall"],"sources":["../../src/cli/sync.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { SyncOptions, SyncResult } from \"../contract\";\nimport { personalizeConfig, readTemplatekeep, resolveSourceDir, runBunInstall } from \"./init\";\nimport { readSnapshot, writeSnapshot } from \"./snapshot\";\n\nfunction readExcludeFile(filePath: string): string[] {\n if (!existsSync(filePath)) return [];\n const content = readFileSync(filePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function readTemplatesyncExclude(sourceDir: string): Promise<string[]> {\n return readExcludeFile(join(sourceDir, \".templatesync-exclude\"));\n}\n\nexport function readLocalSyncExcludes(projectDir: string): string[] {\n return readExcludeFile(join(projectDir, \".bos\", \"sync-local-exclude\"));\n}\n\nfunction isExcluded(filePath: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n const slashIdx = filePath.indexOf(\"/\", prefix.length + 1);\n if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nfunction computeLocalHash(projectDir: string, filePath: string): string | null {\n const fullPath = join(projectDir, filePath);\n if (!existsSync(fullPath)) return null;\n try {\n const content = readFileSync(fullPath);\n return createHash(\"sha256\").update(content).digest(\"hex\").substring(0, 16);\n } catch {\n return null;\n }\n}\n\nfunction backupFiles(projectDir: string, filePaths: string[]): string | null {\n const filesToBackup = filePaths.filter((f) => existsSync(join(projectDir, f)));\n if (filesToBackup.length === 0) return null;\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(projectDir, \".bos\", \"sync-backup\", timestamp);\n\n for (const filePath of filesToBackup) {\n const src = join(projectDir, filePath);\n const dest = join(backupDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n copyFileSync(src, dest);\n }\n\n return backupDir;\n}\n\nfunction mergePackageJson(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...template };\n\n for (const depField of [\n \"dependencies\",\n \"devDependencies\",\n \"peerDependencies\",\n \"overrides\",\n ] as const) {\n const localDeps = local[depField] as Record<string, string> | undefined;\n const templateDeps = template[depField] as Record<string, string> | undefined;\n\n if (!localDeps && !templateDeps) continue;\n\n const mergedDeps: Record<string, string> = { ...(templateDeps ?? {}) };\n\n if (localDeps) {\n for (const [name, version] of Object.entries(localDeps)) {\n if (!(name in mergedDeps)) {\n mergedDeps[name] = version;\n }\n }\n }\n\n if (Object.keys(mergedDeps).length > 0) {\n merged[depField] = mergedDeps;\n }\n }\n\n if (local.scripts && typeof local.scripts === \"object\") {\n merged.scripts = {\n ...((template.scripts as Record<string, string>) ?? {}),\n ...(local.scripts as Record<string, string>),\n };\n }\n\n return merged;\n}\n\nfunction toDestPath(filePath: string): string {\n return filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n}\n\nfunction writeSyncedFile(sourceDir: string, projectDir: string, filePath: string): void {\n const src = join(sourceDir, filePath);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(projectDir, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n\n if (filePath.endsWith(\"package.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergePackageJson(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n writeFileSync(dest, readFileSync(src));\n}\n\nexport async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {\n const localConfig = JSON.parse(\n readFileSync(join(projectDir, \"bos.config.json\"), \"utf-8\"),\n ) as Record<string, unknown>;\n\n const extendsRef = localConfig.extends as string | undefined;\n if (!extendsRef?.startsWith(\"bos://\")) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No extends field found in bos.config.json — cannot determine parent\",\n };\n }\n\n const extendsMatch = extendsRef.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!extendsMatch) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: `Invalid extends reference: ${extendsRef}`,\n };\n }\n\n const extendsAccount = extendsMatch[1];\n const extendsGateway = extendsMatch[2];\n\n const { sourceDir, parentConfig, cleanup } = await resolveSourceDir({\n extendsAccount,\n extendsGateway,\n });\n\n try {\n const patterns = await readTemplatekeep(sourceDir);\n if (patterns.length === 0) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No .templatekeep found in template source\",\n };\n }\n\n const parentExcludes = await readTemplatesyncExclude(sourceDir);\n const localExcludes = readLocalSyncExcludes(projectDir);\n const excludePatterns = [...parentExcludes, ...localExcludes];\n\n const allTemplateFiles = new Set<string>();\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n allTemplateFiles.add(match);\n }\n }\n\n const childPlugins =\n localConfig.plugins && typeof localConfig.plugins === \"object\"\n ? Object.keys(localConfig.plugins as Record<string, unknown>)\n : [];\n\n const pluginRoutes: Record<string, string[]> = {};\n if (parentConfig.plugins) {\n for (const [key, ref] of Object.entries(parentConfig.plugins)) {\n if (ref.routes && ref.routes.length > 0) {\n pluginRoutes[key] = ref.routes;\n }\n }\n }\n\n const excludedRoutePatterns: string[] = [];\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n\n const filteredFiles = new Set<string>();\n for (const filePath of allTemplateFiles) {\n const pluginMatch = filePath.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;\n if (isExcluded(filePath, excludedRoutePatterns)) continue;\n filteredFiles.add(filePath);\n }\n\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isExcluded(match, excludedRoutePatterns)) {\n filteredFiles.add(match);\n }\n }\n }\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const updated: string[] = [];\n const skipped: string[] = [];\n const added: string[] = [];\n\n for (const filePath of filteredFiles) {\n const destPath = toDestPath(filePath);\n if (isExcluded(destPath, excludePatterns)) continue;\n\n const localHash = computeLocalHash(projectDir, destPath);\n const sourceContent = readFileSync(join(sourceDir, filePath));\n const sourceHash = createHash(\"sha256\").update(sourceContent).digest(\"hex\").substring(0, 16);\n\n if (localHash === null) {\n added.push(destPath);\n continue;\n }\n\n if (localHash === sourceHash) continue;\n\n const snapshotHash = snapshot?.files[destPath];\n\n if (snapshotHash === undefined) {\n updated.push(destPath);\n continue;\n }\n\n if (localHash === snapshotHash) {\n updated.push(destPath);\n } else {\n if (options.force) {\n updated.push(destPath);\n } else {\n skipped.push(destPath);\n }\n }\n }\n\n if (options.dryRun) {\n return {\n status: \"dry-run\",\n updated,\n skipped,\n added,\n };\n }\n\n const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));\n\n const destToSource = new Map<string, string>();\n for (const filePath of filteredFiles) {\n destToSource.set(toDestPath(filePath), filePath);\n }\n\n if (filesToWrite.length > 0) {\n backupFiles(projectDir, filesToWrite);\n\n for (const destPath of filesToWrite) {\n const sourcePath = destToSource.get(destPath) ?? destPath;\n writeSyncedFile(sourceDir, projectDir, sourcePath);\n }\n }\n\n const newSnapshotFiles: Record<string, string> = {};\n for (const filePath of filteredFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n newSnapshotFiles[toDestPath(filePath)] = createHash(\"sha256\")\n .update(content)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n await writeSnapshot(projectDir, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: newSnapshotFiles,\n });\n\n const account = (localConfig.account as string) || extendsAccount;\n const domain = (localConfig.domain as string) || extendsGateway;\n\n await personalizeConfig(projectDir, {\n extendsAccount,\n extendsGateway,\n account,\n domain,\n plugins: childPlugins,\n pluginRoutes,\n workspaceOpts: { sourceDir },\n });\n\n if (!options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n return {\n status: \"synced\",\n updated,\n skipped,\n added,\n };\n } finally {\n await cleanup();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sync.cjs","names":["resolveSourceDir","readTemplatekeep","readSnapshot","writeSnapshot","personalizeConfig","runBunInstall"],"sources":["../../src/cli/sync.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { SyncOptions, SyncResult } from \"../contract\";\nimport { personalizeConfig, readTemplatekeep, resolveSourceDir, runBunInstall } from \"./init\";\nimport { readSnapshot, writeSnapshot } from \"./snapshot\";\n\nfunction readExcludeFile(filePath: string): string[] {\n if (!existsSync(filePath)) return [];\n const content = readFileSync(filePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function readTemplatesyncExclude(sourceDir: string): Promise<string[]> {\n return readExcludeFile(join(sourceDir, \".templatesync-exclude\"));\n}\n\nexport function readLocalSyncExcludes(projectDir: string): string[] {\n return readExcludeFile(join(projectDir, \".bos\", \"sync-local-exclude\"));\n}\n\nfunction isExcluded(filePath: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n const slashIdx = filePath.indexOf(\"/\", prefix.length + 1);\n if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nfunction computeLocalHash(projectDir: string, filePath: string): string | null {\n const fullPath = join(projectDir, filePath);\n if (!existsSync(fullPath)) return null;\n try {\n const content = readFileSync(fullPath);\n return createHash(\"sha256\").update(content).digest(\"hex\").substring(0, 16);\n } catch {\n return null;\n }\n}\n\nfunction backupFiles(projectDir: string, filePaths: string[]): string | null {\n const filesToBackup = filePaths.filter((f) => existsSync(join(projectDir, f)));\n if (filesToBackup.length === 0) return null;\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(projectDir, \".bos\", \"sync-backup\", timestamp);\n\n for (const filePath of filesToBackup) {\n const src = join(projectDir, filePath);\n const dest = join(backupDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n copyFileSync(src, dest);\n }\n\n return backupDir;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction mergeJsonValues(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n // Preserve local key order first\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValues(local[key], template[key]);\n }\n // Append any new template keys at the end\n for (const key of Object.keys(template)) {\n if (!(key in merged)) {\n merged[key] = template[key];\n }\n }\n return merged;\n }\n // For arrays and primitives, local always wins\n return local ?? template;\n}\n\nfunction mergeBosConfig(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n\n // 1. extends always first\n if (local.extends !== undefined) merged.extends = local.extends;\n else if (template.extends !== undefined) merged.extends = template.extends;\n\n // 2. Fixed trailing group: app, plugins, shared\n const TRAIL_GROUP = [\"app\", \"plugins\", \"shared\"];\n\n const localKeys = Object.keys(local).filter((k) => k !== \"extends\");\n const templateKeys = Object.keys(template).filter((k) => k !== \"extends\" && !localKeys.includes(k));\n\n // Find the first trailing-group key present locally (\"app\" comes first in the group)\n const firstTrailIndex = localKeys.findIndex((k) => TRAIL_GROUP.includes(k));\n\n const orderedKeys: string[] = [];\n\n if (firstTrailIndex >= 0) {\n // Keys before the trail group stay in local order\n orderedKeys.push(...localKeys.slice(0, firstTrailIndex));\n // New template keys inserted right before the trail group\n orderedKeys.push(...templateKeys);\n // Trail group keys (app, plugins, shared) in canonical order, preserving local if present\n for (const trailKey of TRAIL_GROUP) {\n if (localKeys.includes(trailKey)) orderedKeys.push(trailKey);\n }\n } else {\n // No trail group found locally — keep local order then append new keys and trail group\n orderedKeys.push(...localKeys);\n orderedKeys.push(...templateKeys);\n for (const trailKey of TRAIL_GROUP) {\n if (templateKeys.includes(trailKey)) orderedKeys.push(trailKey);\n }\n }\n\n // 3. Merge values for each key\n for (const key of orderedKeys) {\n merged[key] = mergeJsonValues(local[key], template[key]);\n }\n\n return merged;\n}\n\nfunction mergePackageJson(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...template };\n\n for (const depField of [\n \"dependencies\",\n \"devDependencies\",\n \"peerDependencies\",\n \"overrides\",\n ] as const) {\n const localDeps = local[depField] as Record<string, string> | undefined;\n const templateDeps = template[depField] as Record<string, string> | undefined;\n\n if (!localDeps && !templateDeps) continue;\n\n const mergedDeps: Record<string, string> = { ...(templateDeps ?? {}) };\n\n if (localDeps) {\n for (const [name, version] of Object.entries(localDeps)) {\n if (!(name in mergedDeps)) {\n mergedDeps[name] = version;\n }\n }\n }\n\n if (Object.keys(mergedDeps).length > 0) {\n merged[depField] = mergedDeps;\n }\n }\n\n if (local.scripts && typeof local.scripts === \"object\") {\n merged.scripts = {\n ...((template.scripts as Record<string, string>) ?? {}),\n ...(local.scripts as Record<string, string>),\n };\n }\n\n return merged;\n}\n\nfunction toDestPath(filePath: string): string {\n return filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n}\n\nfunction writeSyncedFile(sourceDir: string, projectDir: string, filePath: string): void {\n const src = join(sourceDir, filePath);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(projectDir, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n\n if (filePath.endsWith(\"bos.config.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergeBosConfig(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n if (filePath.endsWith(\"package.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergePackageJson(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n writeFileSync(dest, readFileSync(src));\n}\n\nexport async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {\n const localConfig = JSON.parse(\n readFileSync(join(projectDir, \"bos.config.json\"), \"utf-8\"),\n ) as Record<string, unknown>;\n\n const extendsRef = localConfig.extends as string | undefined;\n if (!extendsRef?.startsWith(\"bos://\")) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No extends field found in bos.config.json — cannot determine parent\",\n };\n }\n\n const extendsMatch = extendsRef.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!extendsMatch) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: `Invalid extends reference: ${extendsRef}`,\n };\n }\n\n const extendsAccount = extendsMatch[1];\n const extendsGateway = extendsMatch[2];\n\n const { sourceDir, parentConfig, cleanup } = await resolveSourceDir({\n extendsAccount,\n extendsGateway,\n });\n\n try {\n const patterns = await readTemplatekeep(sourceDir);\n if (patterns.length === 0) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No .templatekeep found in template source\",\n };\n }\n\n const parentExcludes = await readTemplatesyncExclude(sourceDir);\n const localExcludes = readLocalSyncExcludes(projectDir);\n const excludePatterns = [...parentExcludes, ...localExcludes];\n\n const allTemplateFiles = new Set<string>();\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n allTemplateFiles.add(match);\n }\n }\n\n const childPlugins =\n localConfig.plugins && typeof localConfig.plugins === \"object\"\n ? Object.keys(localConfig.plugins as Record<string, unknown>)\n : [];\n\n const pluginRoutes: Record<string, string[]> = {};\n if (parentConfig.plugins) {\n for (const [key, ref] of Object.entries(parentConfig.plugins)) {\n if (ref.routes && ref.routes.length > 0) {\n pluginRoutes[key] = ref.routes;\n }\n }\n }\n\n const excludedRoutePatterns: string[] = [];\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n\n const filteredFiles = new Set<string>();\n for (const filePath of allTemplateFiles) {\n const pluginMatch = filePath.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;\n if (isExcluded(filePath, excludedRoutePatterns)) continue;\n filteredFiles.add(filePath);\n }\n\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isExcluded(match, excludedRoutePatterns)) {\n filteredFiles.add(match);\n }\n }\n }\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const updated: string[] = [];\n const skipped: string[] = [];\n const added: string[] = [];\n\n for (const filePath of filteredFiles) {\n const destPath = toDestPath(filePath);\n if (isExcluded(destPath, excludePatterns)) continue;\n\n const localHash = computeLocalHash(projectDir, destPath);\n const sourceContent = readFileSync(join(sourceDir, filePath));\n const sourceHash = createHash(\"sha256\").update(sourceContent).digest(\"hex\").substring(0, 16);\n\n if (localHash === null) {\n added.push(destPath);\n continue;\n }\n\n if (localHash === sourceHash) continue;\n\n const snapshotHash = snapshot?.files[destPath];\n\n if (snapshotHash === undefined) {\n updated.push(destPath);\n continue;\n }\n\n if (localHash === snapshotHash) {\n updated.push(destPath);\n } else {\n if (options.force) {\n updated.push(destPath);\n } else {\n skipped.push(destPath);\n }\n }\n }\n\n if (options.dryRun) {\n return {\n status: \"dry-run\",\n updated,\n skipped,\n added,\n };\n }\n\n const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));\n\n const destToSource = new Map<string, string>();\n for (const filePath of filteredFiles) {\n destToSource.set(toDestPath(filePath), filePath);\n }\n\n if (filesToWrite.length > 0) {\n backupFiles(projectDir, filesToWrite);\n\n for (const destPath of filesToWrite) {\n const sourcePath = destToSource.get(destPath) ?? destPath;\n writeSyncedFile(sourceDir, projectDir, sourcePath);\n }\n }\n\n const newSnapshotFiles: Record<string, string> = {};\n for (const filePath of filteredFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n newSnapshotFiles[toDestPath(filePath)] = createHash(\"sha256\")\n .update(content)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n await writeSnapshot(projectDir, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: newSnapshotFiles,\n });\n\n const account = (localConfig.account as string) || extendsAccount;\n const domain = (localConfig.domain as string) || extendsGateway;\n\n await personalizeConfig(projectDir, {\n extendsAccount,\n extendsGateway,\n account,\n domain,\n plugins: childPlugins,\n pluginRoutes,\n workspaceOpts: { sourceDir },\n mode: \"sync\",\n });\n\n if (!options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n return {\n status: \"synced\",\n updated,\n skipped,\n added,\n };\n } finally {\n await cleanup();\n }\n}\n"],"mappings":"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"}
|
package/dist/cli/sync.mjs
CHANGED
|
@@ -49,6 +49,43 @@ function backupFiles(projectDir, filePaths) {
|
|
|
49
49
|
}
|
|
50
50
|
return backupDir;
|
|
51
51
|
}
|
|
52
|
+
function isPlainObject(value) {
|
|
53
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
54
|
+
}
|
|
55
|
+
function mergeJsonValues(local, template) {
|
|
56
|
+
if (isPlainObject(local) && isPlainObject(template)) {
|
|
57
|
+
const merged = {};
|
|
58
|
+
for (const key of Object.keys(local)) merged[key] = mergeJsonValues(local[key], template[key]);
|
|
59
|
+
for (const key of Object.keys(template)) if (!(key in merged)) merged[key] = template[key];
|
|
60
|
+
return merged;
|
|
61
|
+
}
|
|
62
|
+
return local ?? template;
|
|
63
|
+
}
|
|
64
|
+
function mergeBosConfig(local, template) {
|
|
65
|
+
const merged = {};
|
|
66
|
+
if (local.extends !== void 0) merged.extends = local.extends;
|
|
67
|
+
else if (template.extends !== void 0) merged.extends = template.extends;
|
|
68
|
+
const TRAIL_GROUP = [
|
|
69
|
+
"app",
|
|
70
|
+
"plugins",
|
|
71
|
+
"shared"
|
|
72
|
+
];
|
|
73
|
+
const localKeys = Object.keys(local).filter((k) => k !== "extends");
|
|
74
|
+
const templateKeys = Object.keys(template).filter((k) => k !== "extends" && !localKeys.includes(k));
|
|
75
|
+
const firstTrailIndex = localKeys.findIndex((k) => TRAIL_GROUP.includes(k));
|
|
76
|
+
const orderedKeys = [];
|
|
77
|
+
if (firstTrailIndex >= 0) {
|
|
78
|
+
orderedKeys.push(...localKeys.slice(0, firstTrailIndex));
|
|
79
|
+
orderedKeys.push(...templateKeys);
|
|
80
|
+
for (const trailKey of TRAIL_GROUP) if (localKeys.includes(trailKey)) orderedKeys.push(trailKey);
|
|
81
|
+
} else {
|
|
82
|
+
orderedKeys.push(...localKeys);
|
|
83
|
+
orderedKeys.push(...templateKeys);
|
|
84
|
+
for (const trailKey of TRAIL_GROUP) if (templateKeys.includes(trailKey)) orderedKeys.push(trailKey);
|
|
85
|
+
}
|
|
86
|
+
for (const key of orderedKeys) merged[key] = mergeJsonValues(local[key], template[key]);
|
|
87
|
+
return merged;
|
|
88
|
+
}
|
|
52
89
|
function mergePackageJson(local, template) {
|
|
53
90
|
const merged = { ...template };
|
|
54
91
|
for (const depField of [
|
|
@@ -79,6 +116,15 @@ function writeSyncedFile(sourceDir, projectDir, filePath) {
|
|
|
79
116
|
const src = join(sourceDir, filePath);
|
|
80
117
|
const dest = join(projectDir, filePath.startsWith(".github/templates/") ? filePath.replace(/^\.github\/templates\//, ".github/") : filePath);
|
|
81
118
|
mkdirSync(dirname(dest), { recursive: true });
|
|
119
|
+
if (filePath.endsWith("bos.config.json")) {
|
|
120
|
+
const localContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
|
|
121
|
+
const templateContent = readFileSync(src, "utf-8");
|
|
122
|
+
if (localContent) {
|
|
123
|
+
const merged = mergeBosConfig(JSON.parse(localContent), JSON.parse(templateContent));
|
|
124
|
+
writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\n`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
82
128
|
if (filePath.endsWith("package.json")) {
|
|
83
129
|
const localContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
|
|
84
130
|
const templateContent = readFileSync(src, "utf-8");
|
|
@@ -217,7 +263,8 @@ async function syncTemplate(projectDir, options) {
|
|
|
217
263
|
domain: localConfig.domain || extendsGateway,
|
|
218
264
|
plugins: childPlugins,
|
|
219
265
|
pluginRoutes,
|
|
220
|
-
workspaceOpts: { sourceDir }
|
|
266
|
+
workspaceOpts: { sourceDir },
|
|
267
|
+
mode: "sync"
|
|
221
268
|
});
|
|
222
269
|
if (!options.noInstall) await runBunInstall(projectDir);
|
|
223
270
|
return {
|
package/dist/cli/sync.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.mjs","names":[],"sources":["../../src/cli/sync.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { SyncOptions, SyncResult } from \"../contract\";\nimport { personalizeConfig, readTemplatekeep, resolveSourceDir, runBunInstall } from \"./init\";\nimport { readSnapshot, writeSnapshot } from \"./snapshot\";\n\nfunction readExcludeFile(filePath: string): string[] {\n if (!existsSync(filePath)) return [];\n const content = readFileSync(filePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function readTemplatesyncExclude(sourceDir: string): Promise<string[]> {\n return readExcludeFile(join(sourceDir, \".templatesync-exclude\"));\n}\n\nexport function readLocalSyncExcludes(projectDir: string): string[] {\n return readExcludeFile(join(projectDir, \".bos\", \"sync-local-exclude\"));\n}\n\nfunction isExcluded(filePath: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n const slashIdx = filePath.indexOf(\"/\", prefix.length + 1);\n if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nfunction computeLocalHash(projectDir: string, filePath: string): string | null {\n const fullPath = join(projectDir, filePath);\n if (!existsSync(fullPath)) return null;\n try {\n const content = readFileSync(fullPath);\n return createHash(\"sha256\").update(content).digest(\"hex\").substring(0, 16);\n } catch {\n return null;\n }\n}\n\nfunction backupFiles(projectDir: string, filePaths: string[]): string | null {\n const filesToBackup = filePaths.filter((f) => existsSync(join(projectDir, f)));\n if (filesToBackup.length === 0) return null;\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(projectDir, \".bos\", \"sync-backup\", timestamp);\n\n for (const filePath of filesToBackup) {\n const src = join(projectDir, filePath);\n const dest = join(backupDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n copyFileSync(src, dest);\n }\n\n return backupDir;\n}\n\nfunction mergePackageJson(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...template };\n\n for (const depField of [\n \"dependencies\",\n \"devDependencies\",\n \"peerDependencies\",\n \"overrides\",\n ] as const) {\n const localDeps = local[depField] as Record<string, string> | undefined;\n const templateDeps = template[depField] as Record<string, string> | undefined;\n\n if (!localDeps && !templateDeps) continue;\n\n const mergedDeps: Record<string, string> = { ...(templateDeps ?? {}) };\n\n if (localDeps) {\n for (const [name, version] of Object.entries(localDeps)) {\n if (!(name in mergedDeps)) {\n mergedDeps[name] = version;\n }\n }\n }\n\n if (Object.keys(mergedDeps).length > 0) {\n merged[depField] = mergedDeps;\n }\n }\n\n if (local.scripts && typeof local.scripts === \"object\") {\n merged.scripts = {\n ...((template.scripts as Record<string, string>) ?? {}),\n ...(local.scripts as Record<string, string>),\n };\n }\n\n return merged;\n}\n\nfunction toDestPath(filePath: string): string {\n return filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n}\n\nfunction writeSyncedFile(sourceDir: string, projectDir: string, filePath: string): void {\n const src = join(sourceDir, filePath);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(projectDir, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n\n if (filePath.endsWith(\"package.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergePackageJson(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n writeFileSync(dest, readFileSync(src));\n}\n\nexport async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {\n const localConfig = JSON.parse(\n readFileSync(join(projectDir, \"bos.config.json\"), \"utf-8\"),\n ) as Record<string, unknown>;\n\n const extendsRef = localConfig.extends as string | undefined;\n if (!extendsRef?.startsWith(\"bos://\")) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No extends field found in bos.config.json — cannot determine parent\",\n };\n }\n\n const extendsMatch = extendsRef.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!extendsMatch) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: `Invalid extends reference: ${extendsRef}`,\n };\n }\n\n const extendsAccount = extendsMatch[1];\n const extendsGateway = extendsMatch[2];\n\n const { sourceDir, parentConfig, cleanup } = await resolveSourceDir({\n extendsAccount,\n extendsGateway,\n });\n\n try {\n const patterns = await readTemplatekeep(sourceDir);\n if (patterns.length === 0) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No .templatekeep found in template source\",\n };\n }\n\n const parentExcludes = await readTemplatesyncExclude(sourceDir);\n const localExcludes = readLocalSyncExcludes(projectDir);\n const excludePatterns = [...parentExcludes, ...localExcludes];\n\n const allTemplateFiles = new Set<string>();\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n allTemplateFiles.add(match);\n }\n }\n\n const childPlugins =\n localConfig.plugins && typeof localConfig.plugins === \"object\"\n ? Object.keys(localConfig.plugins as Record<string, unknown>)\n : [];\n\n const pluginRoutes: Record<string, string[]> = {};\n if (parentConfig.plugins) {\n for (const [key, ref] of Object.entries(parentConfig.plugins)) {\n if (ref.routes && ref.routes.length > 0) {\n pluginRoutes[key] = ref.routes;\n }\n }\n }\n\n const excludedRoutePatterns: string[] = [];\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n\n const filteredFiles = new Set<string>();\n for (const filePath of allTemplateFiles) {\n const pluginMatch = filePath.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;\n if (isExcluded(filePath, excludedRoutePatterns)) continue;\n filteredFiles.add(filePath);\n }\n\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isExcluded(match, excludedRoutePatterns)) {\n filteredFiles.add(match);\n }\n }\n }\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const updated: string[] = [];\n const skipped: string[] = [];\n const added: string[] = [];\n\n for (const filePath of filteredFiles) {\n const destPath = toDestPath(filePath);\n if (isExcluded(destPath, excludePatterns)) continue;\n\n const localHash = computeLocalHash(projectDir, destPath);\n const sourceContent = readFileSync(join(sourceDir, filePath));\n const sourceHash = createHash(\"sha256\").update(sourceContent).digest(\"hex\").substring(0, 16);\n\n if (localHash === null) {\n added.push(destPath);\n continue;\n }\n\n if (localHash === sourceHash) continue;\n\n const snapshotHash = snapshot?.files[destPath];\n\n if (snapshotHash === undefined) {\n updated.push(destPath);\n continue;\n }\n\n if (localHash === snapshotHash) {\n updated.push(destPath);\n } else {\n if (options.force) {\n updated.push(destPath);\n } else {\n skipped.push(destPath);\n }\n }\n }\n\n if (options.dryRun) {\n return {\n status: \"dry-run\",\n updated,\n skipped,\n added,\n };\n }\n\n const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));\n\n const destToSource = new Map<string, string>();\n for (const filePath of filteredFiles) {\n destToSource.set(toDestPath(filePath), filePath);\n }\n\n if (filesToWrite.length > 0) {\n backupFiles(projectDir, filesToWrite);\n\n for (const destPath of filesToWrite) {\n const sourcePath = destToSource.get(destPath) ?? destPath;\n writeSyncedFile(sourceDir, projectDir, sourcePath);\n }\n }\n\n const newSnapshotFiles: Record<string, string> = {};\n for (const filePath of filteredFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n newSnapshotFiles[toDestPath(filePath)] = createHash(\"sha256\")\n .update(content)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n await writeSnapshot(projectDir, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: newSnapshotFiles,\n });\n\n const account = (localConfig.account as string) || extendsAccount;\n const domain = (localConfig.domain as string) || extendsGateway;\n\n await personalizeConfig(projectDir, {\n extendsAccount,\n extendsGateway,\n account,\n domain,\n plugins: childPlugins,\n pluginRoutes,\n workspaceOpts: { sourceDir },\n });\n\n if (!options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n return {\n status: \"synced\",\n updated,\n skipped,\n added,\n };\n } finally {\n await cleanup();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sync.mjs","names":[],"sources":["../../src/cli/sync.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { SyncOptions, SyncResult } from \"../contract\";\nimport { personalizeConfig, readTemplatekeep, resolveSourceDir, runBunInstall } from \"./init\";\nimport { readSnapshot, writeSnapshot } from \"./snapshot\";\n\nfunction readExcludeFile(filePath: string): string[] {\n if (!existsSync(filePath)) return [];\n const content = readFileSync(filePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function readTemplatesyncExclude(sourceDir: string): Promise<string[]> {\n return readExcludeFile(join(sourceDir, \".templatesync-exclude\"));\n}\n\nexport function readLocalSyncExcludes(projectDir: string): string[] {\n return readExcludeFile(join(projectDir, \".bos\", \"sync-local-exclude\"));\n}\n\nfunction isExcluded(filePath: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n const slashIdx = filePath.indexOf(\"/\", prefix.length + 1);\n if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nfunction computeLocalHash(projectDir: string, filePath: string): string | null {\n const fullPath = join(projectDir, filePath);\n if (!existsSync(fullPath)) return null;\n try {\n const content = readFileSync(fullPath);\n return createHash(\"sha256\").update(content).digest(\"hex\").substring(0, 16);\n } catch {\n return null;\n }\n}\n\nfunction backupFiles(projectDir: string, filePaths: string[]): string | null {\n const filesToBackup = filePaths.filter((f) => existsSync(join(projectDir, f)));\n if (filesToBackup.length === 0) return null;\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(projectDir, \".bos\", \"sync-backup\", timestamp);\n\n for (const filePath of filesToBackup) {\n const src = join(projectDir, filePath);\n const dest = join(backupDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n copyFileSync(src, dest);\n }\n\n return backupDir;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction mergeJsonValues(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n // Preserve local key order first\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValues(local[key], template[key]);\n }\n // Append any new template keys at the end\n for (const key of Object.keys(template)) {\n if (!(key in merged)) {\n merged[key] = template[key];\n }\n }\n return merged;\n }\n // For arrays and primitives, local always wins\n return local ?? template;\n}\n\nfunction mergeBosConfig(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged: Record<string, unknown> = {};\n\n // 1. extends always first\n if (local.extends !== undefined) merged.extends = local.extends;\n else if (template.extends !== undefined) merged.extends = template.extends;\n\n // 2. Fixed trailing group: app, plugins, shared\n const TRAIL_GROUP = [\"app\", \"plugins\", \"shared\"];\n\n const localKeys = Object.keys(local).filter((k) => k !== \"extends\");\n const templateKeys = Object.keys(template).filter((k) => k !== \"extends\" && !localKeys.includes(k));\n\n // Find the first trailing-group key present locally (\"app\" comes first in the group)\n const firstTrailIndex = localKeys.findIndex((k) => TRAIL_GROUP.includes(k));\n\n const orderedKeys: string[] = [];\n\n if (firstTrailIndex >= 0) {\n // Keys before the trail group stay in local order\n orderedKeys.push(...localKeys.slice(0, firstTrailIndex));\n // New template keys inserted right before the trail group\n orderedKeys.push(...templateKeys);\n // Trail group keys (app, plugins, shared) in canonical order, preserving local if present\n for (const trailKey of TRAIL_GROUP) {\n if (localKeys.includes(trailKey)) orderedKeys.push(trailKey);\n }\n } else {\n // No trail group found locally — keep local order then append new keys and trail group\n orderedKeys.push(...localKeys);\n orderedKeys.push(...templateKeys);\n for (const trailKey of TRAIL_GROUP) {\n if (templateKeys.includes(trailKey)) orderedKeys.push(trailKey);\n }\n }\n\n // 3. Merge values for each key\n for (const key of orderedKeys) {\n merged[key] = mergeJsonValues(local[key], template[key]);\n }\n\n return merged;\n}\n\nfunction mergePackageJson(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...template };\n\n for (const depField of [\n \"dependencies\",\n \"devDependencies\",\n \"peerDependencies\",\n \"overrides\",\n ] as const) {\n const localDeps = local[depField] as Record<string, string> | undefined;\n const templateDeps = template[depField] as Record<string, string> | undefined;\n\n if (!localDeps && !templateDeps) continue;\n\n const mergedDeps: Record<string, string> = { ...(templateDeps ?? {}) };\n\n if (localDeps) {\n for (const [name, version] of Object.entries(localDeps)) {\n if (!(name in mergedDeps)) {\n mergedDeps[name] = version;\n }\n }\n }\n\n if (Object.keys(mergedDeps).length > 0) {\n merged[depField] = mergedDeps;\n }\n }\n\n if (local.scripts && typeof local.scripts === \"object\") {\n merged.scripts = {\n ...((template.scripts as Record<string, string>) ?? {}),\n ...(local.scripts as Record<string, string>),\n };\n }\n\n return merged;\n}\n\nfunction toDestPath(filePath: string): string {\n return filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n}\n\nfunction writeSyncedFile(sourceDir: string, projectDir: string, filePath: string): void {\n const src = join(sourceDir, filePath);\n const destPath = filePath.startsWith(\".github/templates/\")\n ? filePath.replace(/^\\.github\\/templates\\//, \".github/\")\n : filePath;\n const dest = join(projectDir, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n\n if (filePath.endsWith(\"bos.config.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergeBosConfig(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n if (filePath.endsWith(\"package.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergePackageJson(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n writeFileSync(dest, readFileSync(src));\n}\n\nexport async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {\n const localConfig = JSON.parse(\n readFileSync(join(projectDir, \"bos.config.json\"), \"utf-8\"),\n ) as Record<string, unknown>;\n\n const extendsRef = localConfig.extends as string | undefined;\n if (!extendsRef?.startsWith(\"bos://\")) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No extends field found in bos.config.json — cannot determine parent\",\n };\n }\n\n const extendsMatch = extendsRef.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!extendsMatch) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: `Invalid extends reference: ${extendsRef}`,\n };\n }\n\n const extendsAccount = extendsMatch[1];\n const extendsGateway = extendsMatch[2];\n\n const { sourceDir, parentConfig, cleanup } = await resolveSourceDir({\n extendsAccount,\n extendsGateway,\n });\n\n try {\n const patterns = await readTemplatekeep(sourceDir);\n if (patterns.length === 0) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No .templatekeep found in template source\",\n };\n }\n\n const parentExcludes = await readTemplatesyncExclude(sourceDir);\n const localExcludes = readLocalSyncExcludes(projectDir);\n const excludePatterns = [...parentExcludes, ...localExcludes];\n\n const allTemplateFiles = new Set<string>();\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n allTemplateFiles.add(match);\n }\n }\n\n const childPlugins =\n localConfig.plugins && typeof localConfig.plugins === \"object\"\n ? Object.keys(localConfig.plugins as Record<string, unknown>)\n : [];\n\n const pluginRoutes: Record<string, string[]> = {};\n if (parentConfig.plugins) {\n for (const [key, ref] of Object.entries(parentConfig.plugins)) {\n if (ref.routes && ref.routes.length > 0) {\n pluginRoutes[key] = ref.routes;\n }\n }\n }\n\n const excludedRoutePatterns: string[] = [];\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n\n const filteredFiles = new Set<string>();\n for (const filePath of allTemplateFiles) {\n const pluginMatch = filePath.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;\n if (isExcluded(filePath, excludedRoutePatterns)) continue;\n filteredFiles.add(filePath);\n }\n\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isExcluded(match, excludedRoutePatterns)) {\n filteredFiles.add(match);\n }\n }\n }\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const updated: string[] = [];\n const skipped: string[] = [];\n const added: string[] = [];\n\n for (const filePath of filteredFiles) {\n const destPath = toDestPath(filePath);\n if (isExcluded(destPath, excludePatterns)) continue;\n\n const localHash = computeLocalHash(projectDir, destPath);\n const sourceContent = readFileSync(join(sourceDir, filePath));\n const sourceHash = createHash(\"sha256\").update(sourceContent).digest(\"hex\").substring(0, 16);\n\n if (localHash === null) {\n added.push(destPath);\n continue;\n }\n\n if (localHash === sourceHash) continue;\n\n const snapshotHash = snapshot?.files[destPath];\n\n if (snapshotHash === undefined) {\n updated.push(destPath);\n continue;\n }\n\n if (localHash === snapshotHash) {\n updated.push(destPath);\n } else {\n if (options.force) {\n updated.push(destPath);\n } else {\n skipped.push(destPath);\n }\n }\n }\n\n if (options.dryRun) {\n return {\n status: \"dry-run\",\n updated,\n skipped,\n added,\n };\n }\n\n const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));\n\n const destToSource = new Map<string, string>();\n for (const filePath of filteredFiles) {\n destToSource.set(toDestPath(filePath), filePath);\n }\n\n if (filesToWrite.length > 0) {\n backupFiles(projectDir, filesToWrite);\n\n for (const destPath of filesToWrite) {\n const sourcePath = destToSource.get(destPath) ?? destPath;\n writeSyncedFile(sourceDir, projectDir, sourcePath);\n }\n }\n\n const newSnapshotFiles: Record<string, string> = {};\n for (const filePath of filteredFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n newSnapshotFiles[toDestPath(filePath)] = createHash(\"sha256\")\n .update(content)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n await writeSnapshot(projectDir, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: newSnapshotFiles,\n });\n\n const account = (localConfig.account as string) || extendsAccount;\n const domain = (localConfig.domain as string) || extendsGateway;\n\n await personalizeConfig(projectDir, {\n extendsAccount,\n extendsGateway,\n account,\n domain,\n plugins: childPlugins,\n pluginRoutes,\n workspaceOpts: { sourceDir },\n mode: \"sync\",\n });\n\n if (!options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n return {\n status: \"synced\",\n updated,\n skipped,\n added,\n };\n } finally {\n await cleanup();\n }\n}\n"],"mappings":"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"}
|
package/dist/plugin.d.cts
CHANGED
|
@@ -393,7 +393,7 @@ declare const _default: _$every_plugin0.LoadedPluginWithBinding<{
|
|
|
393
393
|
}> | undefined;
|
|
394
394
|
} | null;
|
|
395
395
|
runtimeConfig: {
|
|
396
|
-
env: "
|
|
396
|
+
env: "development" | "production";
|
|
397
397
|
account: string;
|
|
398
398
|
networkId: "testnet" | "mainnet";
|
|
399
399
|
host: {
|
package/dist/plugin.d.mts
CHANGED
|
@@ -393,7 +393,7 @@ declare const _default: _$every_plugin0.LoadedPluginWithBinding<{
|
|
|
393
393
|
}> | undefined;
|
|
394
394
|
} | null;
|
|
395
395
|
runtimeConfig: {
|
|
396
|
-
env: "
|
|
396
|
+
env: "development" | "production";
|
|
397
397
|
account: string;
|
|
398
398
|
networkId: "testnet" | "mainnet";
|
|
399
399
|
host: {
|
|
@@ -26,7 +26,7 @@ interface ServiceDescriptor {
|
|
|
26
26
|
declare const ServiceDescriptorMap_base: Context.TagClass<ServiceDescriptorMap, "ServiceDescriptorMap", Map<string, ServiceDescriptor>>;
|
|
27
27
|
declare class ServiceDescriptorMap extends ServiceDescriptorMap_base {}
|
|
28
28
|
declare const DevRuntimeConfig_base: Context.TagClass<DevRuntimeConfig, "DevRuntimeConfig", {
|
|
29
|
-
env: "
|
|
29
|
+
env: "development" | "production";
|
|
30
30
|
account: string;
|
|
31
31
|
networkId: "testnet" | "mainnet";
|
|
32
32
|
host: {
|
|
@@ -26,7 +26,7 @@ interface ServiceDescriptor {
|
|
|
26
26
|
declare const ServiceDescriptorMap_base: Context.TagClass<ServiceDescriptorMap, "ServiceDescriptorMap", Map<string, ServiceDescriptor>>;
|
|
27
27
|
declare class ServiceDescriptorMap extends ServiceDescriptorMap_base {}
|
|
28
28
|
declare const DevRuntimeConfig_base: Context.TagClass<DevRuntimeConfig, "DevRuntimeConfig", {
|
|
29
|
-
env: "
|
|
29
|
+
env: "development" | "production";
|
|
30
30
|
account: string;
|
|
31
31
|
networkId: "testnet" | "mainnet";
|
|
32
32
|
host: {
|
package/dist/types.d.cts
CHANGED
|
@@ -192,8 +192,8 @@ declare const BosConfigSchema: z.ZodObject<{
|
|
|
192
192
|
type BosConfig = z.infer<typeof BosConfigSchema>;
|
|
193
193
|
declare const RuntimeConfigSchema: z.ZodObject<{
|
|
194
194
|
env: z.ZodEnum<{
|
|
195
|
-
production: "production";
|
|
196
195
|
development: "development";
|
|
196
|
+
production: "production";
|
|
197
197
|
}>;
|
|
198
198
|
account: z.ZodString;
|
|
199
199
|
domain: z.ZodOptional<z.ZodString>;
|
|
@@ -299,8 +299,8 @@ type RuntimeConfig = z.infer<typeof RuntimeConfigSchema>;
|
|
|
299
299
|
declare const ClientRuntimeConfigSchema: z.ZodObject<{
|
|
300
300
|
cspNonce: z.ZodOptional<z.ZodString>;
|
|
301
301
|
env: z.ZodEnum<{
|
|
302
|
-
production: "production";
|
|
303
302
|
development: "development";
|
|
303
|
+
production: "production";
|
|
304
304
|
}>;
|
|
305
305
|
account: z.ZodString;
|
|
306
306
|
networkId: z.ZodEnum<{
|
package/dist/types.d.mts
CHANGED
|
@@ -192,8 +192,8 @@ declare const BosConfigSchema: z.ZodObject<{
|
|
|
192
192
|
type BosConfig = z.infer<typeof BosConfigSchema>;
|
|
193
193
|
declare const RuntimeConfigSchema: z.ZodObject<{
|
|
194
194
|
env: z.ZodEnum<{
|
|
195
|
-
production: "production";
|
|
196
195
|
development: "development";
|
|
196
|
+
production: "production";
|
|
197
197
|
}>;
|
|
198
198
|
account: z.ZodString;
|
|
199
199
|
domain: z.ZodOptional<z.ZodString>;
|
|
@@ -299,8 +299,8 @@ type RuntimeConfig = z.infer<typeof RuntimeConfigSchema>;
|
|
|
299
299
|
declare const ClientRuntimeConfigSchema: z.ZodObject<{
|
|
300
300
|
cspNonce: z.ZodOptional<z.ZodString>;
|
|
301
301
|
env: z.ZodEnum<{
|
|
302
|
-
production: "production";
|
|
303
302
|
development: "development";
|
|
303
|
+
production: "production";
|
|
304
304
|
}>;
|
|
305
305
|
account: z.ZodString;
|
|
306
306
|
networkId: z.ZodEnum<{
|
package/package.json
CHANGED
package/src/api-contract.ts
CHANGED
|
@@ -350,6 +350,10 @@ export async function syncApiContractBridge(opts: {
|
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
for (const [key, plugin] of pluginEntries) {
|
|
353
|
+
if (!plugin.url) {
|
|
354
|
+
console.warn(`[API Contract] Skipping plugin "${key}" — no URL resolved (local path missing and no production URL configured)`);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
353
357
|
const source = await resolveContractSource({
|
|
354
358
|
configDir: opts.configDir,
|
|
355
359
|
runtimeDir,
|
package/src/cli/init.ts
CHANGED
|
@@ -259,8 +259,10 @@ export async function personalizeConfig(
|
|
|
259
259
|
plugins?: string[];
|
|
260
260
|
pluginRoutes?: Record<string, string[]>;
|
|
261
261
|
workspaceOpts?: { localOverrides?: boolean; sourceDir?: string };
|
|
262
|
+
mode?: "init" | "sync";
|
|
262
263
|
},
|
|
263
264
|
): Promise<void> {
|
|
265
|
+
const isInit = opts.mode !== "sync";
|
|
264
266
|
const configPath = join(destination, "bos.config.json");
|
|
265
267
|
if (existsSync(configPath)) {
|
|
266
268
|
const config = JSON.parse(readFileSync(configPath, "utf-8")) as Record<string, unknown>;
|
|
@@ -274,7 +276,7 @@ export async function personalizeConfig(
|
|
|
274
276
|
config.domain = opts.domain;
|
|
275
277
|
}
|
|
276
278
|
|
|
277
|
-
if (config.app && typeof config.app === "object") {
|
|
279
|
+
if (isInit && config.app && typeof config.app === "object") {
|
|
278
280
|
const app = config.app as Record<string, unknown>;
|
|
279
281
|
for (const entryKey of Object.keys(app)) {
|
|
280
282
|
const entry = app[entryKey];
|
|
@@ -299,12 +301,14 @@ export async function personalizeConfig(
|
|
|
299
301
|
}
|
|
300
302
|
}
|
|
301
303
|
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
304
|
+
if (isInit) {
|
|
305
|
+
for (const pluginKey of Object.keys(plugins)) {
|
|
306
|
+
const plugin = plugins[pluginKey];
|
|
307
|
+
if (plugin && typeof plugin === "object") {
|
|
308
|
+
const p = plugin as Record<string, unknown>;
|
|
309
|
+
delete p.production;
|
|
310
|
+
delete p.integrity;
|
|
311
|
+
}
|
|
308
312
|
}
|
|
309
313
|
}
|
|
310
314
|
|
package/src/cli/sync.ts
CHANGED
|
@@ -74,6 +74,76 @@ function backupFiles(projectDir: string, filePaths: string[]): string | null {
|
|
|
74
74
|
return backupDir;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
78
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function mergeJsonValues(local: unknown, template: unknown): unknown {
|
|
82
|
+
if (isPlainObject(local) && isPlainObject(template)) {
|
|
83
|
+
const merged: Record<string, unknown> = {};
|
|
84
|
+
// Preserve local key order first
|
|
85
|
+
for (const key of Object.keys(local)) {
|
|
86
|
+
merged[key] = mergeJsonValues(local[key], template[key]);
|
|
87
|
+
}
|
|
88
|
+
// Append any new template keys at the end
|
|
89
|
+
for (const key of Object.keys(template)) {
|
|
90
|
+
if (!(key in merged)) {
|
|
91
|
+
merged[key] = template[key];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return merged;
|
|
95
|
+
}
|
|
96
|
+
// For arrays and primitives, local always wins
|
|
97
|
+
return local ?? template;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function mergeBosConfig(
|
|
101
|
+
local: Record<string, unknown>,
|
|
102
|
+
template: Record<string, unknown>,
|
|
103
|
+
): Record<string, unknown> {
|
|
104
|
+
const merged: Record<string, unknown> = {};
|
|
105
|
+
|
|
106
|
+
// 1. extends always first
|
|
107
|
+
if (local.extends !== undefined) merged.extends = local.extends;
|
|
108
|
+
else if (template.extends !== undefined) merged.extends = template.extends;
|
|
109
|
+
|
|
110
|
+
// 2. Fixed trailing group: app, plugins, shared
|
|
111
|
+
const TRAIL_GROUP = ["app", "plugins", "shared"];
|
|
112
|
+
|
|
113
|
+
const localKeys = Object.keys(local).filter((k) => k !== "extends");
|
|
114
|
+
const templateKeys = Object.keys(template).filter((k) => k !== "extends" && !localKeys.includes(k));
|
|
115
|
+
|
|
116
|
+
// Find the first trailing-group key present locally ("app" comes first in the group)
|
|
117
|
+
const firstTrailIndex = localKeys.findIndex((k) => TRAIL_GROUP.includes(k));
|
|
118
|
+
|
|
119
|
+
const orderedKeys: string[] = [];
|
|
120
|
+
|
|
121
|
+
if (firstTrailIndex >= 0) {
|
|
122
|
+
// Keys before the trail group stay in local order
|
|
123
|
+
orderedKeys.push(...localKeys.slice(0, firstTrailIndex));
|
|
124
|
+
// New template keys inserted right before the trail group
|
|
125
|
+
orderedKeys.push(...templateKeys);
|
|
126
|
+
// Trail group keys (app, plugins, shared) in canonical order, preserving local if present
|
|
127
|
+
for (const trailKey of TRAIL_GROUP) {
|
|
128
|
+
if (localKeys.includes(trailKey)) orderedKeys.push(trailKey);
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
// No trail group found locally — keep local order then append new keys and trail group
|
|
132
|
+
orderedKeys.push(...localKeys);
|
|
133
|
+
orderedKeys.push(...templateKeys);
|
|
134
|
+
for (const trailKey of TRAIL_GROUP) {
|
|
135
|
+
if (templateKeys.includes(trailKey)) orderedKeys.push(trailKey);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// 3. Merge values for each key
|
|
140
|
+
for (const key of orderedKeys) {
|
|
141
|
+
merged[key] = mergeJsonValues(local[key], template[key]);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return merged;
|
|
145
|
+
}
|
|
146
|
+
|
|
77
147
|
function mergePackageJson(
|
|
78
148
|
local: Record<string, unknown>,
|
|
79
149
|
template: Record<string, unknown>,
|
|
@@ -130,6 +200,19 @@ function writeSyncedFile(sourceDir: string, projectDir: string, filePath: string
|
|
|
130
200
|
const dest = join(projectDir, destPath);
|
|
131
201
|
mkdirSync(dirname(dest), { recursive: true });
|
|
132
202
|
|
|
203
|
+
if (filePath.endsWith("bos.config.json")) {
|
|
204
|
+
const localContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
|
|
205
|
+
const templateContent = readFileSync(src, "utf-8");
|
|
206
|
+
|
|
207
|
+
if (localContent) {
|
|
208
|
+
const local = JSON.parse(localContent) as Record<string, unknown>;
|
|
209
|
+
const template = JSON.parse(templateContent) as Record<string, unknown>;
|
|
210
|
+
const merged = mergeBosConfig(local, template);
|
|
211
|
+
writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\n`);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
133
216
|
if (filePath.endsWith("package.json")) {
|
|
134
217
|
const localContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
|
|
135
218
|
const templateContent = readFileSync(src, "utf-8");
|
|
@@ -348,6 +431,7 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr
|
|
|
348
431
|
plugins: childPlugins,
|
|
349
432
|
pluginRoutes,
|
|
350
433
|
workspaceOpts: { sourceDir },
|
|
434
|
+
mode: "sync",
|
|
351
435
|
});
|
|
352
436
|
|
|
353
437
|
if (!options.noInstall) {
|