everything-dev 1.51.7 → 1.52.0

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.
Files changed (87) hide show
  1. package/dist/api-contract.cjs +61 -16
  2. package/dist/api-contract.cjs.map +1 -1
  3. package/dist/api-contract.mjs +60 -17
  4. package/dist/api-contract.mjs.map +1 -1
  5. package/dist/app.cjs +1 -1
  6. package/dist/app.cjs.map +1 -1
  7. package/dist/app.mjs +1 -1
  8. package/dist/app.mjs.map +1 -1
  9. package/dist/cli/init.cjs +76 -52
  10. package/dist/cli/init.cjs.map +1 -1
  11. package/dist/cli/init.d.cts +15 -7
  12. package/dist/cli/init.d.cts.map +1 -1
  13. package/dist/cli/init.d.mts +15 -7
  14. package/dist/cli/init.d.mts.map +1 -1
  15. package/dist/cli/init.mjs +70 -47
  16. package/dist/cli/init.mjs.map +1 -1
  17. package/dist/cli/sync.cjs +34 -5
  18. package/dist/cli/sync.cjs.map +1 -1
  19. package/dist/cli/sync.mjs +35 -6
  20. package/dist/cli/sync.mjs.map +1 -1
  21. package/dist/cli/upgrade.cjs +151 -93
  22. package/dist/cli/upgrade.cjs.map +1 -1
  23. package/dist/cli/upgrade.mjs +138 -80
  24. package/dist/cli/upgrade.mjs.map +1 -1
  25. package/dist/cli/utils/helpers.cjs +25 -0
  26. package/dist/cli/utils/helpers.cjs.map +1 -0
  27. package/dist/cli/utils/helpers.mjs +23 -0
  28. package/dist/cli/utils/helpers.mjs.map +1 -0
  29. package/dist/cli.cjs +84 -72
  30. package/dist/cli.cjs.map +1 -1
  31. package/dist/cli.mjs +84 -72
  32. package/dist/cli.mjs.map +1 -1
  33. package/dist/code-artifacts.cjs +1 -1
  34. package/dist/code-artifacts.mjs +1 -1
  35. package/dist/config.cjs +44 -4
  36. package/dist/config.cjs.map +1 -1
  37. package/dist/config.d.cts +1 -1
  38. package/dist/config.d.cts.map +1 -1
  39. package/dist/config.d.mts +1 -1
  40. package/dist/config.d.mts.map +1 -1
  41. package/dist/config.mjs +44 -4
  42. package/dist/config.mjs.map +1 -1
  43. package/dist/contract.cjs +9 -4
  44. package/dist/contract.cjs.map +1 -1
  45. package/dist/contract.d.cts +34 -24
  46. package/dist/contract.d.cts.map +1 -1
  47. package/dist/contract.d.mts +34 -24
  48. package/dist/contract.d.mts.map +1 -1
  49. package/dist/contract.meta.cjs +2 -1
  50. package/dist/contract.meta.cjs.map +1 -1
  51. package/dist/contract.meta.d.cts +3 -0
  52. package/dist/contract.meta.d.mts +3 -0
  53. package/dist/contract.meta.mjs +2 -1
  54. package/dist/contract.meta.mjs.map +1 -1
  55. package/dist/contract.mjs +9 -4
  56. package/dist/contract.mjs.map +1 -1
  57. package/dist/dag.cjs +176 -0
  58. package/dist/dag.cjs.map +1 -0
  59. package/dist/dag.d.cts +24 -0
  60. package/dist/dag.d.cts.map +1 -0
  61. package/dist/dag.d.mts +24 -0
  62. package/dist/dag.d.mts.map +1 -0
  63. package/dist/dag.mjs +167 -0
  64. package/dist/dag.mjs.map +1 -0
  65. package/dist/index.cjs +2 -0
  66. package/dist/index.d.cts +2 -2
  67. package/dist/index.d.mts +2 -2
  68. package/dist/index.mjs +2 -2
  69. package/dist/infra/planner.cjs +1 -1
  70. package/dist/infra/planner.mjs +1 -1
  71. package/dist/near-cli.cjs +1 -1
  72. package/dist/near-cli.mjs +1 -1
  73. package/dist/plugin.cjs +27 -18
  74. package/dist/plugin.cjs.map +1 -1
  75. package/dist/plugin.d.cts +57 -11
  76. package/dist/plugin.d.mts +57 -11
  77. package/dist/plugin.mjs +28 -19
  78. package/dist/plugin.mjs.map +1 -1
  79. package/dist/types.cjs +41 -6
  80. package/dist/types.cjs.map +1 -1
  81. package/dist/types.d.cts +118 -3
  82. package/dist/types.d.cts.map +1 -1
  83. package/dist/types.d.mts +118 -3
  84. package/dist/types.d.mts.map +1 -1
  85. package/dist/types.mjs +40 -7
  86. package/dist/types.mjs.map +1 -1
  87. package/package.json +6 -1
@@ -1 +1 @@
1
- {"version":3,"file":"api-contract.mjs","names":[],"sources":["../src/api-contract.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport { fetchJsonOrNull, fetchResponse } from \"./http-client\";\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 additionalExports?: Array<{\n path: string;\n exports: string[];\n sha256?: string;\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 url = getApiPluginManifestUrl(apiBaseUrl);\n const manifest = await fetchJsonOrNull<ApiPluginManifest>(url, { retries: 0 });\n if (!manifest) {\n throw new Error(`Failed to fetch API plugin manifest from ${url}`);\n }\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 fetchResponse(contractUrl);\n if (!contractResponse.ok) {\n throw new Error(\n `Failed to fetch contract types from ${contractUrl}: ${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 fetchAuthExportTypes(opts: {\n baseUrl: string;\n runtimeDir: string;\n manifest: ApiPluginManifest;\n}): Promise<string | null> {\n if (!opts.manifest.additionalExports || opts.manifest.additionalExports.length === 0) {\n return null;\n }\n\n const authExportEntry = opts.manifest.additionalExports.find(\n (entry) => entry.path.includes(\"auth-export\") || entry.path.endsWith(\"auth-export.d.ts\"),\n );\n\n if (!authExportEntry) {\n return null;\n }\n\n const exportUrl = `${trimTrailingSlash(opts.baseUrl)}/${authExportEntry.path.replace(/^\\.\\//, \"\")}`;\n const response = await fetchResponse(exportUrl);\n if (!response.ok) {\n console.warn(\n `[API Contract] Failed to fetch auth export types from ${exportUrl}: ${response.status}`,\n );\n return null;\n }\n\n const content = await response.text();\n if (authExportEntry.sha256 && authExportEntry.sha256 !== sha256(content)) {\n console.warn(`[API Contract] Auth export types checksum mismatch for ${exportUrl}`);\n return null;\n }\n\n const generatedPath = join(opts.runtimeDir, \"auth\", \"auth-export.d.ts\");\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, content);\n\n return generatedPath;\n}\n\nfunction writeAuthTypesGen(targetPath: string, authExportPath: string) {\n const exportImportPath = toImportPath(targetPath, authExportPath);\n const content = [\n `export type {`,\n ` Auth,`,\n ` AuthOrganizationContext,`,\n ` AuthOrganization,`,\n ` AuthOrganizationSummary,`,\n ` AuthOrganizationMember,`,\n ` AuthApiKey,`,\n ` AuthInvitation,`,\n ` GetActiveMemberInput,`,\n ` GetOrganizationInput,`,\n ` ListMembersInput,`,\n ` ListInvitationsInput,`,\n ` ListApiKeysInput,`,\n ` AuthServices,`,\n ` createAuthInstance,`,\n `} from \"${exportImportPath}\";`,\n `import type { InferOutput, ContractType as AuthContract } from \"${toImportPath(targetPath, join(dirname(authExportPath), \"contract.d.ts\"))}\";`,\n `import type { Auth as BaseAuth } from \"${exportImportPath}\";`,\n \"\",\n 'type RawAuthSession = InferOutput<\"getSession\">;',\n 'type RawAuthRequestContext = InferOutput<\"getContext\">;',\n 'type RawAuthActiveMember = InferOutput<\"getActiveMember\">;',\n \"\",\n 'export type AuthSessionUser = NonNullable<RawAuthSession[\"user\"]>;',\n 'export type AuthSessionData = NonNullable<RawAuthSession[\"session\"]>;',\n \"export type AuthSession = {\",\n \" user: AuthSessionUser | null;\",\n \" session: AuthSessionData | null;\",\n \"};\",\n \"export type AuthRequestContext = RawAuthRequestContext;\",\n \"export type AuthPluginContext = Partial<AuthRequestContext> & {\",\n \" reqHeaders?: Headers;\",\n \" getRawBody?: () => Promise<string>;\",\n \"};\",\n \"export type AuthActiveMember = RawAuthActiveMember;\",\n 'export type AuthBaseSession = BaseAuth[\"$Infer\"][\"Session\"];',\n \"export type AuthContractType = AuthContract;\",\n \"\",\n ].join(\"\\n\");\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileIfChanged(targetPath, content);\n}\n\nfunction writeContractBasedAuthTypesGen(targetPath: string, configDir: string) {\n const authExportRel = toImportPath(\n targetPath,\n join(configDir, \".bos\", \"generated\", \"auth\", \"auth-export.d.ts\"),\n );\n const contractRel = toImportPath(\n targetPath,\n join(configDir, \".bos\", \"generated\", \"auth\", \"contract.d.ts\"),\n );\n\n const content = [\n `export type {`,\n ` Auth,`,\n ` AuthOrganizationContext,`,\n ` AuthOrganization,`,\n ` AuthOrganizationSummary,`,\n ` AuthOrganizationMember,`,\n ` AuthApiKey,`,\n ` AuthInvitation,`,\n ` GetActiveMemberInput,`,\n ` GetOrganizationInput,`,\n ` ListMembersInput,`,\n ` ListInvitationsInput,`,\n ` ListApiKeysInput,`,\n ` AuthServices,`,\n ` createAuthInstance,`,\n `} from \"${authExportRel}\";`,\n `import type { InferOutput, ContractType as AuthContract } from \"${contractRel}\";`,\n `import type { Auth as BaseAuth } from \"${authExportRel}\";`,\n \"\",\n 'type RawAuthSession = InferOutput<\"getSession\">;',\n 'type RawAuthRequestContext = InferOutput<\"getContext\">;',\n 'type RawAuthActiveMember = InferOutput<\"getActiveMember\">;',\n \"\",\n 'export type AuthSessionUser = NonNullable<RawAuthSession[\"user\"]>;',\n 'export type AuthSessionData = NonNullable<RawAuthSession[\"session\"]>;',\n \"export type AuthSession = {\",\n \" user: AuthSessionUser | null;\",\n \" session: AuthSessionData | null;\",\n \"};\",\n \"export type AuthRequestContext = RawAuthRequestContext;\",\n \"export type AuthPluginContext = Partial<AuthRequestContext> & {\",\n \" reqHeaders?: Headers;\",\n \" getRawBody?: () => Promise<string>;\",\n \"};\",\n \"export type AuthActiveMember = RawAuthActiveMember;\",\n 'export type AuthBaseSession = BaseAuth[\"$Infer\"][\"Session\"];',\n \"export type AuthContractType = AuthContract;\",\n \"\",\n ].join(\"\\n\");\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileIfChanged(targetPath, content);\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 authExportPath?: string | null;\n}) {\n const hasLocalApiWorkspace = existsSync(join(opts.configDir, \"api\", \"src\"));\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/lib/api-types.gen.ts ---\n const uiContractPath = join(opts.configDir, \"ui\", \"src\", \"lib\", \"api-types.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/lib/plugins-types.gen.ts ---\n // Includes both plugin contracts AND auth as a unified PluginsClient type\n if (hasLocalApiWorkspace) {\n const pluginsClientPath = join(opts.configDir, \"api\", \"src\", \"lib\", \"plugins-types.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 if (opts.authSource) {\n const authImportPath = toImportPath(pluginsClientPath, opts.authSource.sourceFilePath);\n pluginsClientLines.push(\n `import type { ContractType as ${opts.authSource.importName} } from \"${authImportPath}\";`,\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 const allPluginSources = [...pluginSources];\n if (opts.authSource) {\n allPluginSources.push({ ...opts.authSource, key: \"auth\" });\n }\n\n if (allPluginSources.length === 0) {\n pluginsClientLines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n pluginsClientLines.push(\"export type PluginsClient = {\");\n for (const source of allPluginSources) {\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\n // --- Generate */src/lib/auth-types.gen.ts ---\n const authTypeTargets = [join(opts.configDir, \"ui\", \"src\", \"lib\", \"auth-types.gen.ts\")];\n const apiLibDir = join(opts.configDir, \"api\", \"src\", \"lib\");\n if (existsSync(apiLibDir)) {\n authTypeTargets.push(join(apiLibDir, \"auth-types.gen.ts\"));\n }\n const hostLibDir = join(opts.configDir, \"host\", \"src\", \"lib\");\n if (existsSync(join(opts.configDir, \"host\", \"src\"))) {\n authTypeTargets.push(join(hostLibDir, \"auth-types.gen.ts\"));\n }\n\n // Per-plugin auth-types.gen.ts\n for (const key of opts.pluginKeys) {\n const pluginLibDir = join(opts.configDir, \"plugins\", key, \"src\", \"lib\");\n if (existsSync(join(opts.configDir, \"plugins\", key, \"src\"))) {\n authTypeTargets.push(join(pluginLibDir, \"auth-types.gen.ts\"));\n }\n }\n\n if (opts.authExportPath) {\n for (const authTypesPath of authTypeTargets) {\n writeAuthTypesGen(authTypesPath, opts.authExportPath);\n }\n } else if (opts.authSource) {\n for (const authTypesPath of authTypeTargets) {\n writeContractBasedAuthTypesGen(authTypesPath, opts.configDir);\n }\n }\n\n return uiContractPath;\n}\n\nexport interface ContractBridgeStatus {\n key: string;\n source: \"local\" | \"remote\" | \"skipped\" | \"failed\";\n url?: string;\n error?: string;\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 status: ContractBridgeStatus[];\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 const status: ContractBridgeStatus[] = [];\n let manifest: ApiPluginManifest | null = null;\n let generatedPath: string | null = null;\n let authSource: ContractSource | null = null;\n let authExportPath: string | null = null;\n const excludedPluginKeys = new Set<string>();\n\n try {\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 status.push({\n key: \"api\",\n source: opts.runtimeConfig.api.source,\n url: opts.runtimeConfig.api.source !== \"local\" ? opts.apiBaseUrl : undefined,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`[API Contract] Failed to resolve api contract: ${message}`);\n status.push({\n key: \"api\",\n source: \"failed\",\n url: opts.apiBaseUrl || undefined,\n error: message,\n });\n }\n\n if (opts.runtimeConfig.auth) {\n try {\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 status.push({\n key: \"auth\",\n source: opts.runtimeConfig.auth.source,\n url: opts.runtimeConfig.auth.source !== \"local\" ? opts.runtimeConfig.auth.url : undefined,\n });\n if (authSource.generatedPath) {\n generatedPath = authSource.generatedPath;\n }\n\n if (opts.runtimeConfig.auth.url && opts.runtimeConfig.auth.source !== \"local\") {\n try {\n const authManifest = await fetchApiPluginManifest(opts.runtimeConfig.auth.url);\n const fetchedAuthExportPath = await fetchAuthExportTypes({\n baseUrl: opts.runtimeConfig.auth.url,\n runtimeDir,\n manifest: authManifest,\n });\n if (fetchedAuthExportPath) {\n authExportPath = fetchedAuthExportPath;\n }\n } catch (error) {\n console.warn(\n `[API Contract] Failed to fetch auth additional exports: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (!authExportPath) {\n const localAuthExport = join(opts.configDir, \"plugins\", \"auth\", \"src\", \"auth-export.ts\");\n if (existsSync(localAuthExport)) {\n authExportPath = localAuthExport;\n } else {\n const generatedAuthExport = join(runtimeDir, \"auth\", \"auth-export.d.ts\");\n if (existsSync(generatedAuthExport)) {\n authExportPath = generatedAuthExport;\n }\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`[API Contract] Failed to resolve auth contract: ${message}`);\n status.push({\n key: \"auth\",\n source: \"failed\",\n url: opts.runtimeConfig.auth.url || undefined,\n error: message,\n });\n }\n }\n\n for (const [key, plugin] of pluginEntries) {\n if (!plugin.url && !plugin.localPath) {\n console.warn(\n `[API Contract] Skipping plugin \"${key}\" — no URL resolved (local path missing and no production URL configured)`,\n );\n status.push({ key, source: \"skipped\" });\n excludedPluginKeys.add(key);\n }\n }\n\n const resolvablePlugins = pluginEntries.filter(([key]) => !excludedPluginKeys.has(key));\n\n const pluginResults = await Promise.allSettled(\n resolvablePlugins.map(async ([key, plugin]) => {\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 return {\n key,\n source,\n plugin,\n };\n }),\n );\n\n pluginResults.forEach((result, index) => {\n const [key, plugin] = resolvablePlugins[index];\n if (result.status === \"fulfilled\") {\n sources.push(result.value.source);\n status.push({\n key,\n source: plugin.source,\n url: plugin.source !== \"local\" ? plugin.url : undefined,\n });\n if (result.value.source.generatedPath) {\n generatedPath = result.value.source.generatedPath;\n }\n } else {\n const message =\n result.reason instanceof Error ? result.reason.message : String(result.reason);\n console.warn(`[API Contract] Failed to resolve plugin \"${key}\": ${message}`);\n status.push({ key, source: \"failed\", url: plugin.url || undefined, error: message });\n excludedPluginKeys.add(key);\n }\n });\n\n const apiStatus = status.find((s) => s.key === \"api\");\n if (apiStatus?.source === \"failed\") {\n throw new Error(\n `Cannot generate contract types without api contract: ${apiStatus.error ?? \"unknown error\"}`,\n );\n }\n\n const allPluginKeys = pluginEntries\n .filter(([key]) => !excludedPluginKeys.has(key))\n .map(([key]) => key);\n\n writeGeneratedFiles({\n configDir: opts.configDir,\n sources,\n pluginKeys: allPluginKeys,\n authSource,\n authExportPath,\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\", \"lib\", \"api-types.gen.ts\"),\n generatedPath,\n manifest,\n source: opts.runtimeConfig.api.source,\n status,\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 { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, join, relative } from \"node:path\";\nimport { fetchJsonOrNull, fetchResponse } from \"./http-client\";\nimport type { JsonObject, 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 additionalExports?: Array<{\n path: string;\n exports: string[];\n sha256?: string;\n }>;\n plugins?: Array<{\n key: string;\n name: string;\n url: string;\n dependsOn?: string[];\n secrets?: string[];\n variables?: JsonObject;\n }>;\n dependsOn?: string[];\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\nexport async function fetchApiPluginManifest(apiBaseUrl: string): Promise<ApiPluginManifest> {\n const url = getApiPluginManifestUrl(apiBaseUrl);\n const manifest = await fetchJsonOrNull<ApiPluginManifest>(url, { retries: 0 });\n if (!manifest) {\n throw new Error(`Failed to fetch API plugin manifest from ${url}`);\n }\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 fetchResponse(contractUrl);\n if (!contractResponse.ok) {\n throw new Error(\n `Failed to fetch contract types from ${contractUrl}: ${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 fetchAuthExportTypes(opts: {\n baseUrl: string;\n runtimeDir: string;\n manifest: ApiPluginManifest;\n}): Promise<string | null> {\n if (!opts.manifest.additionalExports || opts.manifest.additionalExports.length === 0) {\n return null;\n }\n\n const authExportEntry = opts.manifest.additionalExports.find(\n (entry) => entry.path.includes(\"auth-export\") || entry.path.endsWith(\"auth-export.d.ts\"),\n );\n\n if (!authExportEntry) {\n return null;\n }\n\n const exportUrl = `${trimTrailingSlash(opts.baseUrl)}/${authExportEntry.path.replace(/^\\.\\//, \"\")}`;\n const response = await fetchResponse(exportUrl);\n if (!response.ok) {\n console.warn(\n `[API Contract] Failed to fetch auth export types from ${exportUrl}: ${response.status}`,\n );\n return null;\n }\n\n const content = await response.text();\n if (authExportEntry.sha256 && authExportEntry.sha256 !== sha256(content)) {\n console.warn(`[API Contract] Auth export types checksum mismatch for ${exportUrl}`);\n return null;\n }\n\n const generatedPath = join(opts.runtimeDir, \"auth\", \"auth-export.d.ts\");\n mkdirSync(dirname(generatedPath), { recursive: true });\n writeFileIfChanged(generatedPath, content);\n\n return generatedPath;\n}\n\nfunction writeAuthTypesGen(targetPath: string, authExportPath: string) {\n const exportImportPath = toImportPath(targetPath, authExportPath);\n const content = [\n `export type {`,\n ` Auth,`,\n ` AuthOrganizationContext,`,\n ` AuthOrganization,`,\n ` AuthOrganizationSummary,`,\n ` AuthOrganizationMember,`,\n ` AuthApiKey,`,\n ` AuthInvitation,`,\n ` GetActiveMemberInput,`,\n ` GetOrganizationInput,`,\n ` ListMembersInput,`,\n ` ListInvitationsInput,`,\n ` ListApiKeysInput,`,\n ` AuthServices,`,\n ` createAuthInstance,`,\n `} from \"${exportImportPath}\";`,\n `import type { InferOutput, ContractType as AuthContract } from \"${toImportPath(targetPath, join(dirname(authExportPath), \"contract.d.ts\"))}\";`,\n `import type { Auth as BaseAuth } from \"${exportImportPath}\";`,\n \"\",\n 'type RawAuthSession = InferOutput<\"getSession\">;',\n 'type RawAuthRequestContext = InferOutput<\"getContext\">;',\n 'type RawAuthActiveMember = InferOutput<\"getActiveMember\">;',\n \"\",\n 'export type AuthSessionUser = NonNullable<RawAuthSession[\"user\"]>;',\n 'export type AuthSessionData = NonNullable<RawAuthSession[\"session\"]>;',\n \"export type AuthSession = {\",\n \" user: AuthSessionUser | null;\",\n \" session: AuthSessionData | null;\",\n \"};\",\n \"export type AuthRequestContext = RawAuthRequestContext;\",\n \"export type AuthPluginContext = Partial<AuthRequestContext> & {\",\n \" reqHeaders?: Headers;\",\n \" getRawBody?: () => Promise<string>;\",\n \"};\",\n \"export type AuthActiveMember = RawAuthActiveMember;\",\n 'export type AuthBaseSession = BaseAuth[\"$Infer\"][\"Session\"];',\n \"export type AuthContractType = AuthContract;\",\n \"\",\n ].join(\"\\n\");\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileIfChanged(targetPath, content);\n}\n\nfunction writeContractBasedAuthTypesGen(targetPath: string, configDir: string) {\n const authExportRel = toImportPath(\n targetPath,\n join(configDir, \".bos\", \"generated\", \"auth\", \"auth-export.d.ts\"),\n );\n const contractRel = toImportPath(\n targetPath,\n join(configDir, \".bos\", \"generated\", \"auth\", \"contract.d.ts\"),\n );\n\n const content = [\n `export type {`,\n ` Auth,`,\n ` AuthOrganizationContext,`,\n ` AuthOrganization,`,\n ` AuthOrganizationSummary,`,\n ` AuthOrganizationMember,`,\n ` AuthApiKey,`,\n ` AuthInvitation,`,\n ` GetActiveMemberInput,`,\n ` GetOrganizationInput,`,\n ` ListMembersInput,`,\n ` ListInvitationsInput,`,\n ` ListApiKeysInput,`,\n ` AuthServices,`,\n ` createAuthInstance,`,\n `} from \"${authExportRel}\";`,\n `import type { InferOutput, ContractType as AuthContract } from \"${contractRel}\";`,\n `import type { Auth as BaseAuth } from \"${authExportRel}\";`,\n \"\",\n 'type RawAuthSession = InferOutput<\"getSession\">;',\n 'type RawAuthRequestContext = InferOutput<\"getContext\">;',\n 'type RawAuthActiveMember = InferOutput<\"getActiveMember\">;',\n \"\",\n 'export type AuthSessionUser = NonNullable<RawAuthSession[\"user\"]>;',\n 'export type AuthSessionData = NonNullable<RawAuthSession[\"session\"]>;',\n \"export type AuthSession = {\",\n \" user: AuthSessionUser | null;\",\n \" session: AuthSessionData | null;\",\n \"};\",\n \"export type AuthRequestContext = RawAuthRequestContext;\",\n \"export type AuthPluginContext = Partial<AuthRequestContext> & {\",\n \" reqHeaders?: Headers;\",\n \" getRawBody?: () => Promise<string>;\",\n \"};\",\n \"export type AuthActiveMember = RawAuthActiveMember;\",\n 'export type AuthBaseSession = BaseAuth[\"$Infer\"][\"Session\"];',\n \"export type AuthContractType = AuthContract;\",\n \"\",\n ].join(\"\\n\");\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileIfChanged(targetPath, content);\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 writePluginClientGen(opts: {\n configDir: string;\n pluginKey: string;\n depSources: ContractSource[];\n}) {\n const pluginSrcDir = join(opts.configDir, \"plugins\", opts.pluginKey, \"src\");\n if (!existsSync(pluginSrcDir)) return;\n\n const targetPath = join(pluginSrcDir, \"lib\", \"plugins-client.gen.ts\");\n const lines: string[] = [];\n\n for (const source of opts.depSources) {\n const importPath = toImportPath(targetPath, source.sourceFilePath);\n lines.push(`import type { ContractType as ${source.importName} } from \"${importPath}\";`);\n }\n\n lines.push('import type { ContractRouterClient, AnyContractRouter } from \"@orpc/contract\";');\n lines.push(\n \"type ClientFactory<C extends AnyContractRouter> = (context?: Record<string, unknown>) => ContractRouterClient<C>;\",\n );\n lines.push(\"\");\n\n if (opts.depSources.length === 0) {\n lines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n lines.push(\"export type PluginsClient = {\");\n for (const source of opts.depSources) {\n const key = /^[$A-Z_][0-9A-Z_$]*$/i.test(source.key)\n ? source.key\n : JSON.stringify(source.key);\n lines.push(` ${key}: ClientFactory<${source.importName}>;`);\n }\n lines.push(\"};\");\n }\n\n mkdirSync(dirname(targetPath), { recursive: true });\n writeFileIfChanged(targetPath, `${lines.join(\"\\n\")}\\n`);\n}\n\nexport function writeGeneratedFiles(opts: {\n configDir: string;\n sources: ContractSource[];\n pluginKeys: string[];\n authSource: ContractSource | null;\n authExportPath?: string | null;\n apiDependsOn?: string[];\n pluginDependsOn?: Record<string, string[]>;\n}) {\n const hasLocalApiWorkspace = existsSync(join(opts.configDir, \"api\", \"src\"));\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/lib/api-types.gen.ts ---\n const uiContractPath = join(opts.configDir, \"ui\", \"src\", \"lib\", \"api-types.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/lib/plugins-types.gen.ts ---\n // Filtered by apiDependsOn when explicit; includes all plugins + auth when implicit\n if (hasLocalApiWorkspace) {\n const pluginsClientPath = join(opts.configDir, \"api\", \"src\", \"lib\", \"plugins-types.gen.ts\");\n const pluginsClientLines: string[] = [];\n\n const allPluginSources = [...pluginSources];\n if (opts.authSource) {\n allPluginSources.push({ ...opts.authSource, key: \"auth\" });\n }\n\n const apiDepSources = opts.apiDependsOn?.length\n ? allPluginSources.filter((s) => opts.apiDependsOn!.includes(s.key))\n : allPluginSources;\n\n for (const source of apiDepSources) {\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 (apiDepSources.length === 0) {\n pluginsClientLines.push(\"export type PluginsClient = Record<string, never>;\");\n } else {\n pluginsClientLines.push(\"export type PluginsClient = {\");\n for (const source of apiDepSources) {\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\n // --- Generate per-plugin plugins-client.gen.ts ---\n const allSourcesForLookup = [...pluginSources];\n if (opts.authSource) {\n allSourcesForLookup.push({ ...opts.authSource, key: \"auth\" });\n }\n\n for (const pluginKey of opts.pluginKeys) {\n const deps = opts.pluginDependsOn?.[pluginKey];\n if (!deps?.length) continue;\n\n const depSources = deps\n .map((depKey) => allSourcesForLookup.find((s) => s.key === depKey))\n .filter((s): s is ContractSource => Boolean(s));\n\n writePluginClientGen({\n configDir: opts.configDir,\n pluginKey,\n depSources,\n });\n }\n\n // --- Generate */src/lib/auth-types.gen.ts ---\n const authTypeTargets = [join(opts.configDir, \"ui\", \"src\", \"lib\", \"auth-types.gen.ts\")];\n const apiLibDir = join(opts.configDir, \"api\", \"src\", \"lib\");\n if (existsSync(apiLibDir)) {\n authTypeTargets.push(join(apiLibDir, \"auth-types.gen.ts\"));\n }\n const hostLibDir = join(opts.configDir, \"host\", \"src\", \"lib\");\n if (existsSync(join(opts.configDir, \"host\", \"src\"))) {\n authTypeTargets.push(join(hostLibDir, \"auth-types.gen.ts\"));\n }\n\n // Per-plugin auth-types.gen.ts\n for (const key of opts.pluginKeys) {\n const pluginLibDir = join(opts.configDir, \"plugins\", key, \"src\", \"lib\");\n if (existsSync(join(opts.configDir, \"plugins\", key, \"src\"))) {\n authTypeTargets.push(join(pluginLibDir, \"auth-types.gen.ts\"));\n }\n }\n\n if (opts.authExportPath) {\n for (const authTypesPath of authTypeTargets) {\n writeAuthTypesGen(authTypesPath, opts.authExportPath);\n }\n } else if (opts.authSource) {\n for (const authTypesPath of authTypeTargets) {\n writeContractBasedAuthTypesGen(authTypesPath, opts.configDir);\n }\n }\n\n return uiContractPath;\n}\n\nexport interface ContractBridgeStatus {\n key: string;\n source: \"local\" | \"remote\" | \"skipped\" | \"failed\";\n url?: string;\n localPath?: string;\n error?: string;\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 status: ContractBridgeStatus[];\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 const status: ContractBridgeStatus[] = [];\n let manifest: ApiPluginManifest | null = null;\n let generatedPath: string | null = null;\n let authSource: ContractSource | null = null;\n let authExportPath: string | null = null;\n const excludedPluginKeys = new Set<string>();\n\n try {\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 status.push({\n key: \"api\",\n source: opts.runtimeConfig.api.source,\n url: opts.runtimeConfig.api.source !== \"local\" ? opts.apiBaseUrl : undefined,\n localPath:\n opts.runtimeConfig.api.source === \"local\" ? opts.runtimeConfig.api.localPath : undefined,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`[API Contract] Failed to resolve api contract: ${message}`);\n status.push({\n key: \"api\",\n source: \"failed\",\n url: opts.apiBaseUrl || undefined,\n error: message,\n });\n }\n\n if (opts.runtimeConfig.auth) {\n try {\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 status.push({\n key: \"auth\",\n source: opts.runtimeConfig.auth.source,\n url: opts.runtimeConfig.auth.source !== \"local\" ? opts.runtimeConfig.auth.url : undefined,\n localPath:\n opts.runtimeConfig.auth.source === \"local\"\n ? opts.runtimeConfig.auth.localPath\n : undefined,\n });\n if (authSource.generatedPath) {\n generatedPath = authSource.generatedPath;\n }\n\n if (opts.runtimeConfig.auth.url && opts.runtimeConfig.auth.source !== \"local\") {\n try {\n const authManifest = await fetchApiPluginManifest(opts.runtimeConfig.auth.url);\n const fetchedAuthExportPath = await fetchAuthExportTypes({\n baseUrl: opts.runtimeConfig.auth.url,\n runtimeDir,\n manifest: authManifest,\n });\n if (fetchedAuthExportPath) {\n authExportPath = fetchedAuthExportPath;\n }\n } catch (error) {\n console.warn(\n `[API Contract] Failed to fetch auth additional exports: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n if (!authExportPath) {\n const localAuthExport = join(opts.configDir, \"plugins\", \"auth\", \"src\", \"auth-export.ts\");\n if (existsSync(localAuthExport)) {\n authExportPath = localAuthExport;\n } else {\n const generatedAuthExport = join(runtimeDir, \"auth\", \"auth-export.d.ts\");\n if (existsSync(generatedAuthExport)) {\n authExportPath = generatedAuthExport;\n }\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.warn(`[API Contract] Failed to resolve auth contract: ${message}`);\n status.push({\n key: \"auth\",\n source: \"failed\",\n url: opts.runtimeConfig.auth.url || undefined,\n error: message,\n });\n }\n }\n\n for (const [key, plugin] of pluginEntries) {\n if (!plugin.url && !plugin.localPath) {\n console.warn(\n `[API Contract] Skipping plugin \"${key}\" — no URL resolved (local path missing and no production URL configured)`,\n );\n status.push({ key, source: \"skipped\" });\n excludedPluginKeys.add(key);\n }\n }\n\n const resolvablePlugins = pluginEntries.filter(([key]) => !excludedPluginKeys.has(key));\n\n const pluginResults = await Promise.allSettled(\n resolvablePlugins.map(async ([key, plugin]) => {\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 return {\n key,\n source,\n plugin,\n };\n }),\n );\n\n pluginResults.forEach((result, index) => {\n const [key, plugin] = resolvablePlugins[index];\n if (result.status === \"fulfilled\") {\n sources.push(result.value.source);\n status.push({\n key,\n source: plugin.source,\n url: plugin.source !== \"local\" ? plugin.url : undefined,\n localPath: plugin.source === \"local\" ? plugin.localPath : undefined,\n });\n if (result.value.source.generatedPath) {\n generatedPath = result.value.source.generatedPath;\n }\n } else {\n const message =\n result.reason instanceof Error ? result.reason.message : String(result.reason);\n console.warn(`[API Contract] Failed to resolve plugin \"${key}\": ${message}`);\n status.push({ key, source: \"failed\", url: plugin.url || undefined, error: message });\n excludedPluginKeys.add(key);\n }\n });\n\n const apiStatus = status.find((s) => s.key === \"api\");\n if (apiStatus?.source === \"failed\") {\n throw new Error(\n `Cannot generate contract types without api contract: ${apiStatus.error ?? \"unknown error\"}`,\n );\n }\n\n const allPluginKeys = pluginEntries\n .filter(([key]) => !excludedPluginKeys.has(key))\n .map(([key]) => key);\n\n const pluginDependsOn: Record<string, string[]> = {};\n for (const [key, plugin] of pluginEntries) {\n if (!excludedPluginKeys.has(key) && plugin.dependsOn?.length) {\n pluginDependsOn[key] = plugin.dependsOn;\n }\n }\n\n writeGeneratedFiles({\n configDir: opts.configDir,\n sources,\n pluginKeys: allPluginKeys,\n authSource,\n authExportPath,\n apiDependsOn: opts.runtimeConfig.api.dependsOn,\n pluginDependsOn,\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\", \"lib\", \"api-types.gen.ts\"),\n generatedPath,\n manifest,\n source: opts.runtimeConfig.api.source,\n status,\n };\n}\n"],"mappings":";;;;;;AAgDA,SAAS,OAAO,OAAuB;CACrC,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK;AACxD;AAEA,SAAS,kBAAkB,OAAuB;CAChD,OAAO,MAAM,QAAQ,OAAO,EAAE;AAChC;AAEA,SAAS,mBAAmB,OAAuB;CACjD,OAAO,MAAM,QAAQ,kBAAkB,GAAG,CAAC,CAAC,QAAQ,gBAAgB,GAAG;AACzE;AAEA,SAAS,aAAa,UAAkB,YAA4B;CAClE,MAAM,MAAM,SAAS,QAAQ,QAAQ,GAAG,UAAU,CAAC,CAAC,QAAQ,OAAO,GAAG;CACtE,OAAO,IAAI,WAAW,GAAG,IAAI,MAAM,KAAK;AAC1C;AAEA,SAAS,mBAAmB,UAAkB,SAAiB;CAC7D,IAAI;EACF,IAAI,aAAa,UAAU,MAAM,MAAM,SAAS,OAAO;CACzD,QAAQ,CAER;CAEA,cAAc,UAAU,OAAO;CAC/B,OAAO;AACT;AAEA,SAAS,wBAAwB,YAA4B;CAC3D,OAAO,GAAG,kBAAkB,UAAU,EAAE;AAC1C;AAEA,eAAsB,uBAAuB,YAAgD;CAC3F,MAAM,MAAM,wBAAwB,UAAU;CAC9C,MAAM,WAAW,MAAM,gBAAmC,KAAK,EAAE,SAAS,EAAE,CAAC;CAC7E,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,4CAA4C,KAAK;CAEnE,IAAI,SAAS,kBAAkB,KAAK,SAAS,SAAS,yBACpD,MAAM,IAAI,MAAM,wCAAwC;CAG1D,OAAO;AACT;AAEA,SAAS,uBAAuB,WAAmC;CAEjE,OAAO;EACL,KAAK;EACL,YAAY;EACZ,gBAJiB,KAAK,WAAW,OAAO,OAAO,aAItB;CAC3B;AACF;AAEA,SAAS,wBAAwB,WAAmC;CAElE,OAAO;EACL,KAAK;EACL,YAAY;EACZ,gBAJiB,KAAK,WAAW,WAAW,QAAQ,OAAO,aAIlC;CAC3B;AACF;AAEA,eAAe,qBAAqB,MAMR;CAC1B,MAAM,WAAW,MAAM,uBAAuB,KAAK,OAAO;CAC1D,IAAI,CAAC,SAAS,UACZ,MAAM,IAAI,MACR,uBAAuB,SAAS,OAAO,KAAK,mCAC9C;CAGF,MAAM,cAAc,GAAG,kBAAkB,KAAK,OAAO,EAAE,GAAG,SAAS,SAAS,MAAM,KAAK,QAAQ,SAAS,EAAE;CAC1G,MAAM,mBAAmB,MAAM,cAAc,WAAW;CACxD,IAAI,CAAC,iBAAiB,IACpB,MAAM,IAAI,MACR,uCAAuC,YAAY,IAAI,iBAAiB,OAAO,GAAG,iBAAiB,YACrG;CAGF,MAAM,gBAAgB,MAAM,iBAAiB,KAAK;CAClD,IAAI,SAAS,SAAS,MAAM,UAAU,SAAS,SAAS,MAAM,WAAW,OAAO,aAAa,GAC3F,MAAM,IAAI,MAAM,qDAAqD;CAGvE,MAAM,gBAAgB,KAAK,KAAK,YAAY,KAAK,iBAAiB,eAAe;CACjF,UAAU,QAAQ,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;CACrD,mBAAmB,eAAe,aAAa;CAE/C,OAAO;EACL,KAAK,KAAK;EACV,YAAY,GAAG,mBAAmB,KAAK,IAAI,EAAE;EAC7C,gBAAgB;EAChB;CACF;AACF;AAEA,eAAe,qBAAqB,MAIT;CACzB,IAAI,CAAC,KAAK,SAAS,qBAAqB,KAAK,SAAS,kBAAkB,WAAW,GACjF,OAAO;CAGT,MAAM,kBAAkB,KAAK,SAAS,kBAAkB,MACrD,UAAU,MAAM,KAAK,SAAS,aAAa,KAAK,MAAM,KAAK,SAAS,kBAAkB,CACzF;CAEA,IAAI,CAAC,iBACH,OAAO;CAGT,MAAM,YAAY,GAAG,kBAAkB,KAAK,OAAO,EAAE,GAAG,gBAAgB,KAAK,QAAQ,SAAS,EAAE;CAChG,MAAM,WAAW,MAAM,cAAc,SAAS;CAC9C,IAAI,CAAC,SAAS,IAAI;EAChB,QAAQ,KACN,yDAAyD,UAAU,IAAI,SAAS,QAClF;EACA,OAAO;CACT;CAEA,MAAM,UAAU,MAAM,SAAS,KAAK;CACpC,IAAI,gBAAgB,UAAU,gBAAgB,WAAW,OAAO,OAAO,GAAG;EACxE,QAAQ,KAAK,0DAA0D,WAAW;EAClF,OAAO;CACT;CAEA,MAAM,gBAAgB,KAAK,KAAK,YAAY,QAAQ,kBAAkB;CACtE,UAAU,QAAQ,aAAa,GAAG,EAAE,WAAW,KAAK,CAAC;CACrD,mBAAmB,eAAe,OAAO;CAEzC,OAAO;AACT;AAEA,SAAS,kBAAkB,YAAoB,gBAAwB;CACrE,MAAM,mBAAmB,aAAa,YAAY,cAAc;CAChE,MAAM,UAAU;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,WAAW,iBAAiB;EAC5B,mEAAmE,aAAa,YAAY,KAAK,QAAQ,cAAc,GAAG,eAAe,CAAC,EAAE;EAC5I,0CAA0C,iBAAiB;EAC3D;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;CACX,UAAU,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;CAClD,mBAAmB,YAAY,OAAO;AACxC;AAEA,SAAS,+BAA+B,YAAoB,WAAmB;CAC7E,MAAM,gBAAgB,aACpB,YACA,KAAK,WAAW,QAAQ,aAAa,QAAQ,kBAAkB,CACjE;CACA,MAAM,cAAc,aAClB,YACA,KAAK,WAAW,QAAQ,aAAa,QAAQ,eAAe,CAC9D;CAEA,MAAM,UAAU;EACd;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,WAAW,cAAc;EACzB,mEAAmE,YAAY;EAC/E,0CAA0C,cAAc;EACxD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF,CAAC,CAAC,KAAK,IAAI;CACX,UAAU,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;CAClD,mBAAmB,YAAY,OAAO;AACxC;AAEA,eAAe,sBAAsB,MAQT;CAC1B,IAAI,KAAK,QAAQ,OAAO;EACtB,MAAM,YAAY,KAAK,UAAU,eAAe,KAAK,SAAS,KAAK,OAAO,YAAY;EACtF,IAAI,aAAa,QAAQ,cAAc,IACrC,OAAO;GACL,KAAK,KAAK;GACV,YAAY;GACZ,gBAAgB,KAAK,WAAW,OAAO,aAAa;EACtD;EAGF,IAAI,CAAC,KAAK,SACR,OAAO,uBAAuB,KAAK,SAAS;CAEhD;CAEA,IAAI,KAAK,QAAQ,UAAU,KAAK,oBAAoB;EAClD,MAAM,YAAY,KAAK,UAAU,eAAe,KAAK,SAAS,KAAK,OAAO,YAAY;EACtF,IAAI,aAAa,QAAQ,cAAc,IACrC,OAAO;GACL,KAAK,KAAK;GACV,YAAY;GACZ,gBAAgB,KAAK,WAAW,OAAO,aAAa;EACtD;EAGF,IAAI,CAAC,KAAK,SACR,OAAO,KAAK,mBAAmB,KAAK,SAAS;CAEjD;CAEA,IACE,KAAK,UACL,eAAe,KAAK,UACpB,KAAK,OAAO,aAAa,QACzB,KAAK,OAAO,cAAc,IAE1B,OAAO;EACL,KAAK,KAAK;EACV,YAAY,GAAG,mBAAmB,KAAK,GAAG,EAAE;EAC5C,gBAAgB,KAAK,KAAK,OAAO,WAAW,OAAO,aAAa;CAClE;CAGF,OAAO,qBAAqB;EAC1B,WAAW,KAAK;EAChB,YAAY,KAAK;EACjB,MAAM,KAAK;EACX,SAAS,KAAK;EACd,iBAAiB,KAAK;CACxB,CAAC;AACH;AAEA,SAAS,qBAAqB,MAI3B;CACD,MAAM,eAAe,KAAK,KAAK,WAAW,WAAW,KAAK,WAAW,KAAK;CAC1E,IAAI,CAAC,WAAW,YAAY,GAAG;CAE/B,MAAM,aAAa,KAAK,cAAc,OAAO,uBAAuB;CACpE,MAAM,QAAkB,CAAC;CAEzB,KAAK,MAAM,UAAU,KAAK,YAAY;EACpC,MAAM,aAAa,aAAa,YAAY,OAAO,cAAc;EACjE,MAAM,KAAK,iCAAiC,OAAO,WAAW,WAAW,WAAW,GAAG;CACzF;CAEA,MAAM,KAAK,kFAAgF;CAC3F,MAAM,KACJ,mHACF;CACA,MAAM,KAAK,EAAE;CAEb,IAAI,KAAK,WAAW,WAAW,GAC7B,MAAM,KAAK,oDAAoD;MAC1D;EACL,MAAM,KAAK,+BAA+B;EAC1C,KAAK,MAAM,UAAU,KAAK,YAAY;GACpC,MAAM,MAAM,wBAAwB,KAAK,OAAO,GAAG,IAC/C,OAAO,MACP,KAAK,UAAU,OAAO,GAAG;GAC7B,MAAM,KAAK,KAAK,IAAI,kBAAkB,OAAO,WAAW,GAAG;EAC7D;EACA,MAAM,KAAK,IAAI;CACjB;CAEA,UAAU,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;CAClD,mBAAmB,YAAY,GAAG,MAAM,KAAK,IAAI,EAAE,GAAG;AACxD;AAEA,SAAgB,oBAAoB,MAQjC;CACD,MAAM,uBAAuB,WAAW,KAAK,KAAK,WAAW,OAAO,KAAK,CAAC;CAC1E,MAAM,aAAa,KAAK,QAAQ,MAAM,WAAW,OAAO,QAAQ,KAAK;CACrE,MAAM,gBAAgB,KAAK,WACxB,KAAK,QAAQ,KAAK,QAAQ,MAAM,UAAU,MAAM,QAAQ,GAAG,CAAC,CAAC,CAC7D,QAAQ,WAAqC,QAAQ,MAAM,CAAC;CAE/D,IAAI,CAAC,YACH,MAAM,IAAI,MAAM,oEAAoE;CAItF,MAAM,iBAAiB,KAAK,KAAK,WAAW,MAAM,OAAO,OAAO,kBAAkB;CAClF,MAAM,UAAoB,CAAC;CAE3B,KAAK,MAAM,UAAU,KAAK,SAAS;EACjC,MAAM,aAAa,aAAa,gBAAgB,OAAO,cAAc;EACrE,QAAQ,KAAK,iCAAiC,OAAO,WAAW,WAAW,WAAW,GAAG;CAC3F;CAEA,QAAQ,KAAK,EAAE;CAEf,MAAM,iBAA2B,CAAC;CAClC,IAAI,KAAK,YACP,eAAe,KAAK,SAAS,KAAK,WAAW,YAAY;CAE3D,KAAK,MAAM,UAAU,eAAe;EAClC,MAAM,MAAM,wBAAwB,KAAK,OAAO,GAAG,IAAI,OAAO,MAAM,KAAK,UAAU,OAAO,GAAG;EAC7F,eAAe,KAAK,GAAG,IAAI,IAAI,OAAO,YAAY;CACpD;CAEA,IAAI,eAAe,WAAW,GAC5B,QAAQ,KAAK,6BAA6B,WAAW,WAAW,EAAE;MAC7D;EACL,QAAQ,KAAK,6BAA6B,WAAW,WAAW,KAAK;EACrE,KAAK,MAAM,QAAQ,gBACjB,QAAQ,KAAK,KAAK,KAAK,EAAE;EAE3B,QAAQ,KAAK,IAAI;CACnB;CACA,UAAU,QAAQ,cAAc,GAAG,EAAE,WAAW,KAAK,CAAC;CACtD,mBAAmB,gBAAgB,GAAG,QAAQ,KAAK,IAAI,EAAE,GAAG;CAI5D,IAAI,sBAAsB;EACxB,MAAM,oBAAoB,KAAK,KAAK,WAAW,OAAO,OAAO,OAAO,sBAAsB;EAC1F,MAAM,qBAA+B,CAAC;EAEtC,MAAM,mBAAmB,CAAC,GAAG,aAAa;EAC1C,IAAI,KAAK,YACP,iBAAiB,KAAK;GAAE,GAAG,KAAK;GAAY,KAAK;EAAO,CAAC;EAG3D,MAAM,gBAAgB,KAAK,cAAc,SACrC,iBAAiB,QAAQ,MAAM,KAAK,aAAc,SAAS,EAAE,GAAG,CAAC,IACjE;EAEJ,KAAK,MAAM,UAAU,eAAe;GAClC,MAAM,aAAa,aAAa,mBAAmB,OAAO,cAAc;GACxE,mBAAmB,KACjB,iCAAiC,OAAO,WAAW,WAAW,WAAW,GAC3E;EACF;EAEA,mBAAmB,KACjB,kFACF;EACA,mBAAmB,KACjB,mHACF;EACA,mBAAmB,KAAK,EAAE;EAE1B,IAAI,cAAc,WAAW,GAC3B,mBAAmB,KAAK,oDAAoD;OACvE;GACL,mBAAmB,KAAK,+BAA+B;GACvD,KAAK,MAAM,UAAU,eAAe;IAClC,MAAM,MAAM,wBAAwB,KAAK,OAAO,GAAG,IAC/C,OAAO,MACP,KAAK,UAAU,OAAO,GAAG;IAC7B,mBAAmB,KAAK,KAAK,IAAI,kBAAkB,OAAO,WAAW,GAAG;GAC1E;GACA,mBAAmB,KAAK,IAAI;EAC9B;EAEA,UAAU,QAAQ,iBAAiB,GAAG,EAAE,WAAW,KAAK,CAAC;EACzD,mBAAmB,mBAAmB,GAAG,mBAAmB,KAAK,IAAI,EAAE,GAAG;CAC5E;CAGA,MAAM,sBAAsB,CAAC,GAAG,aAAa;CAC7C,IAAI,KAAK,YACP,oBAAoB,KAAK;EAAE,GAAG,KAAK;EAAY,KAAK;CAAO,CAAC;CAG9D,KAAK,MAAM,aAAa,KAAK,YAAY;EACvC,MAAM,OAAO,KAAK,kBAAkB;EACpC,IAAI,CAAC,MAAM,QAAQ;EAEnB,MAAM,aAAa,KAChB,KAAK,WAAW,oBAAoB,MAAM,MAAM,EAAE,QAAQ,MAAM,CAAC,CAAC,CAClE,QAAQ,MAA2B,QAAQ,CAAC,CAAC;EAEhD,qBAAqB;GACnB,WAAW,KAAK;GAChB;GACA;EACF,CAAC;CACH;CAGA,MAAM,kBAAkB,CAAC,KAAK,KAAK,WAAW,MAAM,OAAO,OAAO,mBAAmB,CAAC;CACtF,MAAM,YAAY,KAAK,KAAK,WAAW,OAAO,OAAO,KAAK;CAC1D,IAAI,WAAW,SAAS,GACtB,gBAAgB,KAAK,KAAK,WAAW,mBAAmB,CAAC;CAE3D,MAAM,aAAa,KAAK,KAAK,WAAW,QAAQ,OAAO,KAAK;CAC5D,IAAI,WAAW,KAAK,KAAK,WAAW,QAAQ,KAAK,CAAC,GAChD,gBAAgB,KAAK,KAAK,YAAY,mBAAmB,CAAC;CAI5D,KAAK,MAAM,OAAO,KAAK,YAAY;EACjC,MAAM,eAAe,KAAK,KAAK,WAAW,WAAW,KAAK,OAAO,KAAK;EACtE,IAAI,WAAW,KAAK,KAAK,WAAW,WAAW,KAAK,KAAK,CAAC,GACxD,gBAAgB,KAAK,KAAK,cAAc,mBAAmB,CAAC;CAEhE;CAEA,IAAI,KAAK,gBACP,KAAK,MAAM,iBAAiB,iBAC1B,kBAAkB,eAAe,KAAK,cAAc;MAEjD,IAAI,KAAK,YACd,KAAK,MAAM,iBAAiB,iBAC1B,+BAA+B,eAAe,KAAK,SAAS;CAIhE,OAAO;AACT;AAUA,eAAsB,sBAAsB,MAUzC;CACD,MAAM,aAAa,KAAK,KAAK,WAAW,QAAQ,WAAW;CAC3D,MAAM,gBAAgB,OAAO,QAAQ,KAAK,cAAc,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OACjF,EAAE,cAAc,CAAC,CACnB;CACA,MAAM,UAA4B,CAAC;CACnC,MAAM,SAAiC,CAAC;CACxC,IAAI,WAAqC;CACzC,IAAI,gBAA+B;CACnC,IAAI,aAAoC;CACxC,IAAI,iBAAgC;CACpC,MAAM,qCAAqB,IAAI,IAAY;CAE3C,IAAI;EACF,MAAM,aAAa,MAAM,sBAAsB;GAC7C,WAAW,KAAK;GAChB;GACA,KAAK;GACL,QAAQ,KAAK,cAAc;GAC3B,SAAS,KAAK;GACd,iBAAiB;EACnB,CAAC;EACD,QAAQ,KAAK,UAAU;EACvB,OAAO,KAAK;GACV,KAAK;GACL,QAAQ,KAAK,cAAc,IAAI;GAC/B,KAAK,KAAK,cAAc,IAAI,WAAW,UAAU,KAAK,aAAa;GACnE,WACE,KAAK,cAAc,IAAI,WAAW,UAAU,KAAK,cAAc,IAAI,YAAY;EACnF,CAAC;CACH,SAAS,OAAO;EACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,QAAQ,KAAK,kDAAkD,SAAS;EACxE,OAAO,KAAK;GACV,KAAK;GACL,QAAQ;GACR,KAAK,KAAK,cAAc;GACxB,OAAO;EACT,CAAC;CACH;CAEA,IAAI,KAAK,cAAc,MACrB,IAAI;EACF,aAAa,MAAM,sBAAsB;GACvC,WAAW,KAAK;GAChB;GACA,KAAK;GACL,QAAQ,KAAK,cAAc;GAC3B,SAAS,KAAK,cAAc,KAAK;GACjC,iBAAiB;GACjB,oBAAoB;EACtB,CAAC;EACD,QAAQ,KAAK,UAAU;EACvB,OAAO,KAAK;GACV,KAAK;GACL,QAAQ,KAAK,cAAc,KAAK;GAChC,KAAK,KAAK,cAAc,KAAK,WAAW,UAAU,KAAK,cAAc,KAAK,MAAM;GAChF,WACE,KAAK,cAAc,KAAK,WAAW,UAC/B,KAAK,cAAc,KAAK,YACxB;EACR,CAAC;EACD,IAAI,WAAW,eACb,gBAAgB,WAAW;EAG7B,IAAI,KAAK,cAAc,KAAK,OAAO,KAAK,cAAc,KAAK,WAAW,SACpE,IAAI;GACF,MAAM,eAAe,MAAM,uBAAuB,KAAK,cAAc,KAAK,GAAG;GAC7E,MAAM,wBAAwB,MAAM,qBAAqB;IACvD,SAAS,KAAK,cAAc,KAAK;IACjC;IACA,UAAU;GACZ,CAAC;GACD,IAAI,uBACF,iBAAiB;EAErB,SAAS,OAAO;GACd,QAAQ,KACN,2DAA2D,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAClH;EACF;EAGF,IAAI,CAAC,gBAAgB;GACnB,MAAM,kBAAkB,KAAK,KAAK,WAAW,WAAW,QAAQ,OAAO,gBAAgB;GACvF,IAAI,WAAW,eAAe,GAC5B,iBAAiB;QACZ;IACL,MAAM,sBAAsB,KAAK,YAAY,QAAQ,kBAAkB;IACvE,IAAI,WAAW,mBAAmB,GAChC,iBAAiB;GAErB;EACF;CACF,SAAS,OAAO;EACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACrE,QAAQ,KAAK,mDAAmD,SAAS;EACzE,OAAO,KAAK;GACV,KAAK;GACL,QAAQ;GACR,KAAK,KAAK,cAAc,KAAK,OAAO;GACpC,OAAO;EACT,CAAC;CACH;CAGF,KAAK,MAAM,CAAC,KAAK,WAAW,eAC1B,IAAI,CAAC,OAAO,OAAO,CAAC,OAAO,WAAW;EACpC,QAAQ,KACN,mCAAmC,IAAI,0EACzC;EACA,OAAO,KAAK;GAAE;GAAK,QAAQ;EAAU,CAAC;EACtC,mBAAmB,IAAI,GAAG;CAC5B;CAGF,MAAM,oBAAoB,cAAc,QAAQ,CAAC,SAAS,CAAC,mBAAmB,IAAI,GAAG,CAAC;CAoBtF,OAlB4B,QAAQ,WAClC,kBAAkB,IAAI,OAAO,CAAC,KAAK,YAAY;EAS7C,OAAO;GACL;GACA,cAVmB,sBAAsB;IACzC,WAAW,KAAK;IAChB;IACA;IACA,QAAQ;IACR,SAAS,OAAO;IAChB,iBAAiB,WAAW;GAC9B,CAAC;GAIC;EACF;CACF,CAAC,CACH,EAEa,CAAC,SAAS,QAAQ,UAAU;EACvC,MAAM,CAAC,KAAK,UAAU,kBAAkB;EACxC,IAAI,OAAO,WAAW,aAAa;GACjC,QAAQ,KAAK,OAAO,MAAM,MAAM;GAChC,OAAO,KAAK;IACV;IACA,QAAQ,OAAO;IACf,KAAK,OAAO,WAAW,UAAU,OAAO,MAAM;IAC9C,WAAW,OAAO,WAAW,UAAU,OAAO,YAAY;GAC5D,CAAC;GACD,IAAI,OAAO,MAAM,OAAO,eACtB,gBAAgB,OAAO,MAAM,OAAO;EAExC,OAAO;GACL,MAAM,UACJ,OAAO,kBAAkB,QAAQ,OAAO,OAAO,UAAU,OAAO,OAAO,MAAM;GAC/E,QAAQ,KAAK,4CAA4C,IAAI,KAAK,SAAS;GAC3E,OAAO,KAAK;IAAE;IAAK,QAAQ;IAAU,KAAK,OAAO,OAAO;IAAW,OAAO;GAAQ,CAAC;GACnF,mBAAmB,IAAI,GAAG;EAC5B;CACF,CAAC;CAED,MAAM,YAAY,OAAO,MAAM,MAAM,EAAE,QAAQ,KAAK;CACpD,IAAI,WAAW,WAAW,UACxB,MAAM,IAAI,MACR,wDAAwD,UAAU,SAAS,iBAC7E;CAGF,MAAM,gBAAgB,cACnB,QAAQ,CAAC,SAAS,CAAC,mBAAmB,IAAI,GAAG,CAAC,CAAC,CAC/C,KAAK,CAAC,SAAS,GAAG;CAErB,MAAM,kBAA4C,CAAC;CACnD,KAAK,MAAM,CAAC,KAAK,WAAW,eAC1B,IAAI,CAAC,mBAAmB,IAAI,GAAG,KAAK,OAAO,WAAW,QACpD,gBAAgB,OAAO,OAAO;CAIlC,oBAAoB;EAClB,WAAW,KAAK;EAChB;EACA,YAAY;EACZ;EACA;EACA,cAAc,KAAK,cAAc,IAAI;EACrC;CACF,CAAC;CAED,IAAI,KAAK,cAAc,IAAI,WAAW,SACpC,WAAW,MAAM,uBAAuB,KAAK,UAAU;CAGzD,OAAO;EACL,YAAY,KAAK,KAAK,WAAW,MAAM,OAAO,OAAO,kBAAkB;EACvE;EACA;EACA,QAAQ,KAAK,cAAc,IAAI;EAC/B;CACF;AACF"}
package/dist/app.cjs CHANGED
@@ -29,7 +29,7 @@ function detectLocalPackages(bosConfig, runtimeConfig) {
29
29
  if (authLocalPath && (0, node_fs.existsSync)((0, node_path.join)(authLocalPath, "package.json"))) packages.push("auth");
30
30
  return packages;
31
31
  }
32
- function buildRuntimeConfig(bosConfig, options) {
32
+ async function buildRuntimeConfig(bosConfig, options) {
33
33
  return require_config.buildRuntimeConfig(bosConfig, require_config.getProjectRoot(), options.env ?? "development", {
34
34
  hostSource: options.hostSource,
35
35
  uiSource: options.uiSource,
package/dist/app.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.cjs","names":["Data","Context","getProjectRoot","resolveLocalDevelopmentPath","configBuildRuntimeConfig","Effect","Layer","claimedPorts"],"sources":["../src/app.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { createServer } from \"node:net\";\nimport { join } from \"node:path\";\nimport { Context, Data, Effect, Layer } from \"effect\";\nimport type { DevPortState } from \"./cli/infra\";\nimport {\n buildRuntimeConfig as configBuildRuntimeConfig,\n getProjectRoot,\n resolveLocalDevelopmentPath,\n} from \"./config\";\nimport { claimedPorts } from \"./process-registry\";\nimport type { AppOrchestrator } from \"./service-descriptor\";\nimport type { BosConfig, RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport type { AppOrchestrator };\n\nconst DEFAULT_HOST_PORT = 3000;\nconst DEFAULT_API_PORT = 3001;\nconst DEFAULT_AUTH_PORT = 3002;\nconst DEFAULT_UI_PORT = 3003;\nconst DEFAULT_PLUGIN_PORT_START = 3010;\n\nconst PROBE_TIMEOUT_MS = 250;\nconst MAX_PORT_SCAN_STEPS = 1000;\nconst PARALLEL_PROBE_WINDOW = 8;\n\nexport type PortBudget = { min: number; max: number };\n\nexport class PortAllocationError extends Data.TaggedError(\"PortAllocationError\")<{\n preferred: number;\n budget?: PortBudget;\n cause?: unknown;\n}> {}\n\nexport class PortAllocator extends Context.Tag(\"PortAllocator\")<\n PortAllocator,\n {\n pickAvailable: (\n preferred: number,\n budget?: PortBudget,\n ) => Effect.Effect<number, PortAllocationError>;\n }\n>() {}\n\nexport function detectLocalPackages(\n bosConfig?: BosConfig,\n runtimeConfig?: RuntimeConfig,\n): string[] {\n const packages: string[] = [];\n const configDir = getProjectRoot();\n\n const uiLocalPath =\n runtimeConfig?.ui.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.ui.development, configDir);\n if (uiLocalPath && existsSync(join(uiLocalPath, \"package.json\"))) {\n packages.push(\"ui\");\n }\n\n const apiLocalPath =\n runtimeConfig?.api.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.api.development, configDir);\n if (apiLocalPath && existsSync(join(apiLocalPath, \"package.json\"))) {\n packages.push(\"api\");\n }\n\n const hostLocalPath =\n runtimeConfig?.host?.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);\n if (hostLocalPath && existsSync(join(hostLocalPath, \"package.json\"))) {\n packages.push(\"host\");\n } else if (existsSync(join(configDir, \"host\", \"package.json\"))) {\n packages.push(\"host\");\n }\n\n for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) {\n if (pluginConfig.localPath && existsSync(join(pluginConfig.localPath, \"package.json\"))) {\n packages.push(`plugin:${pluginId}`);\n }\n if (pluginConfig.ui?.localPath && existsSync(join(pluginConfig.ui.localPath, \"package.json\"))) {\n packages.push(`plugin-ui:${pluginId}`);\n }\n }\n\n const authLocalPath =\n runtimeConfig?.auth?.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.auth?.development, configDir);\n if (authLocalPath && existsSync(join(authLocalPath, \"package.json\"))) {\n packages.push(\"auth\");\n }\n\n return packages;\n}\n\nexport function buildRuntimeConfig(\n bosConfig: BosConfig,\n options: {\n hostSource?: \"local\" | \"remote\";\n uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n proxy?: string;\n env?: \"development\" | \"production\";\n plugins?: Record<string, RuntimePluginConfig>;\n },\n): RuntimeConfig {\n return configBuildRuntimeConfig(bosConfig, getProjectRoot(), options.env ?? \"development\", {\n hostSource: options.hostSource,\n uiSource: options.uiSource,\n apiSource: options.apiSource,\n authSource: options.authSource,\n proxy: options.proxy,\n plugins: options.plugins,\n });\n}\n\nfunction probePortBindable(port: number): Effect.Effect<boolean> {\n return Effect.async<boolean>((resume) => {\n const server = createServer();\n\n server.once(\"listening\", () => {\n server.close(() => {\n resume(Effect.succeed(true));\n });\n });\n\n server.once(\"error\", () => {\n server.removeAllListeners();\n // EADDRINUSE, EACCES, or any other bind error → not available\n resume(Effect.succeed(false));\n });\n\n server.listen(port, \"127.0.0.1\");\n\n const timer = setTimeout(() => {\n server.removeAllListeners();\n try {\n server.close();\n } catch {\n // ignore\n }\n resume(Effect.succeed(false));\n }, PROBE_TIMEOUT_MS);\n\n server.once(\"listening\", () => clearTimeout(timer));\n server.once(\"error\", () => clearTimeout(timer));\n });\n}\n\nfunction pickAvailablePort(\n preferred: number,\n usedPorts: Set<number>,\n budget?: PortBudget,\n): Effect.Effect<number, PortAllocationError> {\n return Effect.gen(function* () {\n const within = (candidate: number): boolean =>\n !budget || (candidate >= budget.min && candidate <= budget.max);\n\n let port = preferred;\n if (!within(port)) {\n port = budget ? budget.min : port;\n }\n\n const ceiling = budget ? budget.max + 1 : Number.MAX_SAFE_INTEGER;\n let steps = 0;\n\n const fail = () =>\n Effect.fail(\n new PortAllocationError({\n preferred,\n budget,\n cause: budget\n ? `No free port in budget [${budget.min}, ${budget.max}] starting from ${preferred}`\n : `No free port found starting from ${preferred} within ${MAX_PORT_SCAN_STEPS} steps`,\n }),\n );\n\n while (true) {\n if (port >= ceiling || steps > MAX_PORT_SCAN_STEPS) {\n yield* fail();\n }\n\n const candidates: number[] = [];\n for (let i = 0; i < PARALLEL_PROBE_WINDOW && port + i < ceiling; i++) {\n const candidate = port + i;\n if (!usedPorts.has(candidate)) {\n candidates.push(candidate);\n }\n }\n\n if (candidates.length === 0) {\n port += PARALLEL_PROBE_WINDOW;\n steps += PARALLEL_PROBE_WINDOW;\n continue;\n }\n\n const results = yield* Effect.forEach(\n candidates,\n (c) => probePortBindable(c).pipe(Effect.map((free) => ({ port: c, free }))),\n { concurrency: \"unbounded\" },\n );\n\n const firstFree = results.find((r) => r.free);\n if (firstFree) {\n usedPorts.add(firstFree.port);\n return firstFree.port;\n }\n\n port += PARALLEL_PROBE_WINDOW;\n steps += PARALLEL_PROBE_WINDOW;\n }\n });\n}\n\nexport const PortAllocatorLive: Layer.Layer<PortAllocator> = Layer.sync(PortAllocator, () => {\n const usedPorts = claimedPorts();\n return {\n pickAvailable: (preferred, budget) => pickAvailablePort(preferred, usedPorts, budget),\n };\n});\n\nfunction withLocalRuntimeUrl<\n T extends { url: string; entry: string; port?: number; localPath?: string },\n>(entry: T, port: number): T {\n const url = `http://localhost:${port}`;\n return {\n ...entry,\n url,\n entry: `${url}/mf-manifest.json`,\n port,\n };\n}\n\nexport interface DevPortOptions {\n hostPort?: number;\n apiPort?: number;\n uiPort?: number;\n authPort?: number;\n pluginPortStart?: number;\n ssr?: boolean;\n portBudget?: PortBudget;\n}\n\nexport interface PreparedDevRuntime {\n runtimeConfig: RuntimeConfig;\n devPorts: DevPortState;\n}\n\nexport function prepareDevelopmentRuntimeConfig(\n runtimeConfig: RuntimeConfig,\n options?: DevPortOptions,\n): Effect.Effect<PreparedDevRuntime, PortAllocationError, PortAllocator> {\n return Effect.gen(function* () {\n const allocator = yield* PortAllocator;\n const budget = options?.portBudget;\n\n const pickedHostPort = yield* allocator.pickAvailable(\n options?.hostPort ?? DEFAULT_HOST_PORT,\n budget,\n );\n\n const hostIsLocal = runtimeConfig.host.source === \"local\";\n const next: RuntimeConfig = {\n ...runtimeConfig,\n host: hostIsLocal\n ? {\n ...runtimeConfig.host,\n url: `http://localhost:${pickedHostPort}`,\n port: pickedHostPort,\n }\n : { ...runtimeConfig.host },\n ui: { ...runtimeConfig.ui },\n api: { ...runtimeConfig.api },\n auth: runtimeConfig.auth ? { ...runtimeConfig.auth } : undefined,\n plugins: runtimeConfig.plugins ? { ...runtimeConfig.plugins } : undefined,\n };\n\n const devPorts: DevPortState = {\n host: hostIsLocal ? pickedHostPort : undefined,\n api: undefined,\n ui: undefined,\n auth: undefined,\n pluginPortStart: undefined,\n };\n\n if (next.api.source === \"local\" && next.api.localPath) {\n const apiPort = yield* allocator.pickAvailable(\n options?.apiPort ?? next.api.port ?? DEFAULT_API_PORT,\n budget,\n );\n next.api = withLocalRuntimeUrl(next.api, apiPort);\n devPorts.api = apiPort;\n }\n\n if (next.auth?.source === \"local\" && next.auth.localPath) {\n const authPort = yield* allocator.pickAvailable(\n options?.authPort ?? next.auth.port ?? DEFAULT_AUTH_PORT,\n budget,\n );\n next.auth = withLocalRuntimeUrl(next.auth, authPort);\n devPorts.auth = authPort;\n }\n\n if (next.ui.source === \"local\" && next.ui.localPath) {\n const uiPort = yield* allocator.pickAvailable(\n options?.uiPort ?? next.ui.port ?? DEFAULT_UI_PORT,\n budget,\n );\n next.ui = withLocalRuntimeUrl(next.ui, uiPort);\n devPorts.ui = uiPort;\n if (options?.ssr) {\n const ssrPort = yield* allocator.pickAvailable(uiPort + 1, budget);\n next.ui.ssrUrl = `http://localhost:${ssrPort}`;\n } else {\n next.ui.ssrUrl = undefined;\n }\n }\n\n if (next.plugins) {\n const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));\n let pluginBasePort = options?.pluginPortStart ?? DEFAULT_PLUGIN_PORT_START;\n let firstLocalPluginPort: number | undefined;\n\n for (const [pluginId, plugin] of entries) {\n if (plugin.source === \"local\" && plugin.localPath) {\n const pluginPort = yield* allocator.pickAvailable(plugin.port ?? pluginBasePort, budget);\n next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);\n if (firstLocalPluginPort === undefined) firstLocalPluginPort = pluginPort;\n pluginBasePort = pluginPort + 1;\n }\n\n if (plugin.ui?.source === \"local\" && plugin.ui.localPath) {\n const pluginUiPort = yield* allocator.pickAvailable(\n plugin.ui.port ?? pluginBasePort,\n budget,\n );\n next.plugins[pluginId] = {\n ...next.plugins[pluginId]!,\n ui: withLocalRuntimeUrl(plugin.ui, pluginUiPort),\n };\n if (firstLocalPluginPort === undefined) firstLocalPluginPort = pluginUiPort;\n pluginBasePort = pluginUiPort + 1;\n }\n }\n\n devPorts.pluginPortStart = firstLocalPluginPort;\n }\n\n return { runtimeConfig: next, devPorts };\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app.cjs","names":["Data","Context","getProjectRoot","resolveLocalDevelopmentPath","configBuildRuntimeConfig","Effect","Layer","claimedPorts"],"sources":["../src/app.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { createServer } from \"node:net\";\nimport { join } from \"node:path\";\nimport { Context, Data, Effect, Layer } from \"effect\";\nimport type { DevPortState } from \"./cli/infra\";\nimport {\n buildRuntimeConfig as configBuildRuntimeConfig,\n getProjectRoot,\n resolveLocalDevelopmentPath,\n} from \"./config\";\nimport { claimedPorts } from \"./process-registry\";\nimport type { AppOrchestrator } from \"./service-descriptor\";\nimport type { BosConfig, RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport type { AppOrchestrator };\n\nconst DEFAULT_HOST_PORT = 3000;\nconst DEFAULT_API_PORT = 3001;\nconst DEFAULT_AUTH_PORT = 3002;\nconst DEFAULT_UI_PORT = 3003;\nconst DEFAULT_PLUGIN_PORT_START = 3010;\n\nconst PROBE_TIMEOUT_MS = 250;\nconst MAX_PORT_SCAN_STEPS = 1000;\nconst PARALLEL_PROBE_WINDOW = 8;\n\nexport type PortBudget = { min: number; max: number };\n\nexport class PortAllocationError extends Data.TaggedError(\"PortAllocationError\")<{\n preferred: number;\n budget?: PortBudget;\n cause?: unknown;\n}> {}\n\nexport class PortAllocator extends Context.Tag(\"PortAllocator\")<\n PortAllocator,\n {\n pickAvailable: (\n preferred: number,\n budget?: PortBudget,\n ) => Effect.Effect<number, PortAllocationError>;\n }\n>() {}\n\nexport function detectLocalPackages(\n bosConfig?: BosConfig,\n runtimeConfig?: RuntimeConfig,\n): string[] {\n const packages: string[] = [];\n const configDir = getProjectRoot();\n\n const uiLocalPath =\n runtimeConfig?.ui.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.ui.development, configDir);\n if (uiLocalPath && existsSync(join(uiLocalPath, \"package.json\"))) {\n packages.push(\"ui\");\n }\n\n const apiLocalPath =\n runtimeConfig?.api.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.api.development, configDir);\n if (apiLocalPath && existsSync(join(apiLocalPath, \"package.json\"))) {\n packages.push(\"api\");\n }\n\n const hostLocalPath =\n runtimeConfig?.host?.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);\n if (hostLocalPath && existsSync(join(hostLocalPath, \"package.json\"))) {\n packages.push(\"host\");\n } else if (existsSync(join(configDir, \"host\", \"package.json\"))) {\n packages.push(\"host\");\n }\n\n for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) {\n if (pluginConfig.localPath && existsSync(join(pluginConfig.localPath, \"package.json\"))) {\n packages.push(`plugin:${pluginId}`);\n }\n if (pluginConfig.ui?.localPath && existsSync(join(pluginConfig.ui.localPath, \"package.json\"))) {\n packages.push(`plugin-ui:${pluginId}`);\n }\n }\n\n const authLocalPath =\n runtimeConfig?.auth?.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.auth?.development, configDir);\n if (authLocalPath && existsSync(join(authLocalPath, \"package.json\"))) {\n packages.push(\"auth\");\n }\n\n return packages;\n}\n\nexport async function buildRuntimeConfig(\n bosConfig: BosConfig,\n options: {\n hostSource?: \"local\" | \"remote\";\n uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n proxy?: string;\n env?: \"development\" | \"production\";\n plugins?: Record<string, RuntimePluginConfig>;\n },\n): Promise<RuntimeConfig> {\n return configBuildRuntimeConfig(bosConfig, getProjectRoot(), options.env ?? \"development\", {\n hostSource: options.hostSource,\n uiSource: options.uiSource,\n apiSource: options.apiSource,\n authSource: options.authSource,\n proxy: options.proxy,\n plugins: options.plugins,\n });\n}\n\nfunction probePortBindable(port: number): Effect.Effect<boolean> {\n return Effect.async<boolean>((resume) => {\n const server = createServer();\n\n server.once(\"listening\", () => {\n server.close(() => {\n resume(Effect.succeed(true));\n });\n });\n\n server.once(\"error\", () => {\n server.removeAllListeners();\n // EADDRINUSE, EACCES, or any other bind error → not available\n resume(Effect.succeed(false));\n });\n\n server.listen(port, \"127.0.0.1\");\n\n const timer = setTimeout(() => {\n server.removeAllListeners();\n try {\n server.close();\n } catch {\n // ignore\n }\n resume(Effect.succeed(false));\n }, PROBE_TIMEOUT_MS);\n\n server.once(\"listening\", () => clearTimeout(timer));\n server.once(\"error\", () => clearTimeout(timer));\n });\n}\n\nfunction pickAvailablePort(\n preferred: number,\n usedPorts: Set<number>,\n budget?: PortBudget,\n): Effect.Effect<number, PortAllocationError> {\n return Effect.gen(function* () {\n const within = (candidate: number): boolean =>\n !budget || (candidate >= budget.min && candidate <= budget.max);\n\n let port = preferred;\n if (!within(port)) {\n port = budget ? budget.min : port;\n }\n\n const ceiling = budget ? budget.max + 1 : Number.MAX_SAFE_INTEGER;\n let steps = 0;\n\n const fail = () =>\n Effect.fail(\n new PortAllocationError({\n preferred,\n budget,\n cause: budget\n ? `No free port in budget [${budget.min}, ${budget.max}] starting from ${preferred}`\n : `No free port found starting from ${preferred} within ${MAX_PORT_SCAN_STEPS} steps`,\n }),\n );\n\n while (true) {\n if (port >= ceiling || steps > MAX_PORT_SCAN_STEPS) {\n yield* fail();\n }\n\n const candidates: number[] = [];\n for (let i = 0; i < PARALLEL_PROBE_WINDOW && port + i < ceiling; i++) {\n const candidate = port + i;\n if (!usedPorts.has(candidate)) {\n candidates.push(candidate);\n }\n }\n\n if (candidates.length === 0) {\n port += PARALLEL_PROBE_WINDOW;\n steps += PARALLEL_PROBE_WINDOW;\n continue;\n }\n\n const results = yield* Effect.forEach(\n candidates,\n (c) => probePortBindable(c).pipe(Effect.map((free) => ({ port: c, free }))),\n { concurrency: \"unbounded\" },\n );\n\n const firstFree = results.find((r) => r.free);\n if (firstFree) {\n usedPorts.add(firstFree.port);\n return firstFree.port;\n }\n\n port += PARALLEL_PROBE_WINDOW;\n steps += PARALLEL_PROBE_WINDOW;\n }\n });\n}\n\nexport const PortAllocatorLive: Layer.Layer<PortAllocator> = Layer.sync(PortAllocator, () => {\n const usedPorts = claimedPorts();\n return {\n pickAvailable: (preferred, budget) => pickAvailablePort(preferred, usedPorts, budget),\n };\n});\n\nfunction withLocalRuntimeUrl<\n T extends { url: string; entry: string; port?: number; localPath?: string },\n>(entry: T, port: number): T {\n const url = `http://localhost:${port}`;\n return {\n ...entry,\n url,\n entry: `${url}/mf-manifest.json`,\n port,\n };\n}\n\nexport interface DevPortOptions {\n hostPort?: number;\n apiPort?: number;\n uiPort?: number;\n authPort?: number;\n pluginPortStart?: number;\n ssr?: boolean;\n portBudget?: PortBudget;\n}\n\nexport interface PreparedDevRuntime {\n runtimeConfig: RuntimeConfig;\n devPorts: DevPortState;\n}\n\nexport function prepareDevelopmentRuntimeConfig(\n runtimeConfig: RuntimeConfig,\n options?: DevPortOptions,\n): Effect.Effect<PreparedDevRuntime, PortAllocationError, PortAllocator> {\n return Effect.gen(function* () {\n const allocator = yield* PortAllocator;\n const budget = options?.portBudget;\n\n const pickedHostPort = yield* allocator.pickAvailable(\n options?.hostPort ?? DEFAULT_HOST_PORT,\n budget,\n );\n\n const hostIsLocal = runtimeConfig.host.source === \"local\";\n const next: RuntimeConfig = {\n ...runtimeConfig,\n host: hostIsLocal\n ? {\n ...runtimeConfig.host,\n url: `http://localhost:${pickedHostPort}`,\n port: pickedHostPort,\n }\n : { ...runtimeConfig.host },\n ui: { ...runtimeConfig.ui },\n api: { ...runtimeConfig.api },\n auth: runtimeConfig.auth ? { ...runtimeConfig.auth } : undefined,\n plugins: runtimeConfig.plugins ? { ...runtimeConfig.plugins } : undefined,\n };\n\n const devPorts: DevPortState = {\n host: hostIsLocal ? pickedHostPort : undefined,\n api: undefined,\n ui: undefined,\n auth: undefined,\n pluginPortStart: undefined,\n };\n\n if (next.api.source === \"local\" && next.api.localPath) {\n const apiPort = yield* allocator.pickAvailable(\n options?.apiPort ?? next.api.port ?? DEFAULT_API_PORT,\n budget,\n );\n next.api = withLocalRuntimeUrl(next.api, apiPort);\n devPorts.api = apiPort;\n }\n\n if (next.auth?.source === \"local\" && next.auth.localPath) {\n const authPort = yield* allocator.pickAvailable(\n options?.authPort ?? next.auth.port ?? DEFAULT_AUTH_PORT,\n budget,\n );\n next.auth = withLocalRuntimeUrl(next.auth, authPort);\n devPorts.auth = authPort;\n }\n\n if (next.ui.source === \"local\" && next.ui.localPath) {\n const uiPort = yield* allocator.pickAvailable(\n options?.uiPort ?? next.ui.port ?? DEFAULT_UI_PORT,\n budget,\n );\n next.ui = withLocalRuntimeUrl(next.ui, uiPort);\n devPorts.ui = uiPort;\n if (options?.ssr) {\n const ssrPort = yield* allocator.pickAvailable(uiPort + 1, budget);\n next.ui.ssrUrl = `http://localhost:${ssrPort}`;\n } else {\n next.ui.ssrUrl = undefined;\n }\n }\n\n if (next.plugins) {\n const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));\n let pluginBasePort = options?.pluginPortStart ?? DEFAULT_PLUGIN_PORT_START;\n let firstLocalPluginPort: number | undefined;\n\n for (const [pluginId, plugin] of entries) {\n if (plugin.source === \"local\" && plugin.localPath) {\n const pluginPort = yield* allocator.pickAvailable(plugin.port ?? pluginBasePort, budget);\n next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);\n if (firstLocalPluginPort === undefined) firstLocalPluginPort = pluginPort;\n pluginBasePort = pluginPort + 1;\n }\n\n if (plugin.ui?.source === \"local\" && plugin.ui.localPath) {\n const pluginUiPort = yield* allocator.pickAvailable(\n plugin.ui.port ?? pluginBasePort,\n budget,\n );\n next.plugins[pluginId] = {\n ...next.plugins[pluginId]!,\n ui: withLocalRuntimeUrl(plugin.ui, pluginUiPort),\n };\n if (firstLocalPluginPort === undefined) firstLocalPluginPort = pluginUiPort;\n pluginBasePort = pluginUiPort + 1;\n }\n }\n\n devPorts.pluginPortStart = firstLocalPluginPort;\n }\n\n return { runtimeConfig: next, devPorts };\n });\n}\n"],"mappings":"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"}
package/dist/app.mjs CHANGED
@@ -29,7 +29,7 @@ function detectLocalPackages(bosConfig, runtimeConfig) {
29
29
  if (authLocalPath && existsSync(join(authLocalPath, "package.json"))) packages.push("auth");
30
30
  return packages;
31
31
  }
32
- function buildRuntimeConfig(bosConfig, options) {
32
+ async function buildRuntimeConfig(bosConfig, options) {
33
33
  return buildRuntimeConfig$1(bosConfig, getProjectRoot(), options.env ?? "development", {
34
34
  hostSource: options.hostSource,
35
35
  uiSource: options.uiSource,
package/dist/app.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.mjs","names":["configBuildRuntimeConfig"],"sources":["../src/app.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { createServer } from \"node:net\";\nimport { join } from \"node:path\";\nimport { Context, Data, Effect, Layer } from \"effect\";\nimport type { DevPortState } from \"./cli/infra\";\nimport {\n buildRuntimeConfig as configBuildRuntimeConfig,\n getProjectRoot,\n resolveLocalDevelopmentPath,\n} from \"./config\";\nimport { claimedPorts } from \"./process-registry\";\nimport type { AppOrchestrator } from \"./service-descriptor\";\nimport type { BosConfig, RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport type { AppOrchestrator };\n\nconst DEFAULT_HOST_PORT = 3000;\nconst DEFAULT_API_PORT = 3001;\nconst DEFAULT_AUTH_PORT = 3002;\nconst DEFAULT_UI_PORT = 3003;\nconst DEFAULT_PLUGIN_PORT_START = 3010;\n\nconst PROBE_TIMEOUT_MS = 250;\nconst MAX_PORT_SCAN_STEPS = 1000;\nconst PARALLEL_PROBE_WINDOW = 8;\n\nexport type PortBudget = { min: number; max: number };\n\nexport class PortAllocationError extends Data.TaggedError(\"PortAllocationError\")<{\n preferred: number;\n budget?: PortBudget;\n cause?: unknown;\n}> {}\n\nexport class PortAllocator extends Context.Tag(\"PortAllocator\")<\n PortAllocator,\n {\n pickAvailable: (\n preferred: number,\n budget?: PortBudget,\n ) => Effect.Effect<number, PortAllocationError>;\n }\n>() {}\n\nexport function detectLocalPackages(\n bosConfig?: BosConfig,\n runtimeConfig?: RuntimeConfig,\n): string[] {\n const packages: string[] = [];\n const configDir = getProjectRoot();\n\n const uiLocalPath =\n runtimeConfig?.ui.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.ui.development, configDir);\n if (uiLocalPath && existsSync(join(uiLocalPath, \"package.json\"))) {\n packages.push(\"ui\");\n }\n\n const apiLocalPath =\n runtimeConfig?.api.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.api.development, configDir);\n if (apiLocalPath && existsSync(join(apiLocalPath, \"package.json\"))) {\n packages.push(\"api\");\n }\n\n const hostLocalPath =\n runtimeConfig?.host?.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);\n if (hostLocalPath && existsSync(join(hostLocalPath, \"package.json\"))) {\n packages.push(\"host\");\n } else if (existsSync(join(configDir, \"host\", \"package.json\"))) {\n packages.push(\"host\");\n }\n\n for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) {\n if (pluginConfig.localPath && existsSync(join(pluginConfig.localPath, \"package.json\"))) {\n packages.push(`plugin:${pluginId}`);\n }\n if (pluginConfig.ui?.localPath && existsSync(join(pluginConfig.ui.localPath, \"package.json\"))) {\n packages.push(`plugin-ui:${pluginId}`);\n }\n }\n\n const authLocalPath =\n runtimeConfig?.auth?.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.auth?.development, configDir);\n if (authLocalPath && existsSync(join(authLocalPath, \"package.json\"))) {\n packages.push(\"auth\");\n }\n\n return packages;\n}\n\nexport function buildRuntimeConfig(\n bosConfig: BosConfig,\n options: {\n hostSource?: \"local\" | \"remote\";\n uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n proxy?: string;\n env?: \"development\" | \"production\";\n plugins?: Record<string, RuntimePluginConfig>;\n },\n): RuntimeConfig {\n return configBuildRuntimeConfig(bosConfig, getProjectRoot(), options.env ?? \"development\", {\n hostSource: options.hostSource,\n uiSource: options.uiSource,\n apiSource: options.apiSource,\n authSource: options.authSource,\n proxy: options.proxy,\n plugins: options.plugins,\n });\n}\n\nfunction probePortBindable(port: number): Effect.Effect<boolean> {\n return Effect.async<boolean>((resume) => {\n const server = createServer();\n\n server.once(\"listening\", () => {\n server.close(() => {\n resume(Effect.succeed(true));\n });\n });\n\n server.once(\"error\", () => {\n server.removeAllListeners();\n // EADDRINUSE, EACCES, or any other bind error → not available\n resume(Effect.succeed(false));\n });\n\n server.listen(port, \"127.0.0.1\");\n\n const timer = setTimeout(() => {\n server.removeAllListeners();\n try {\n server.close();\n } catch {\n // ignore\n }\n resume(Effect.succeed(false));\n }, PROBE_TIMEOUT_MS);\n\n server.once(\"listening\", () => clearTimeout(timer));\n server.once(\"error\", () => clearTimeout(timer));\n });\n}\n\nfunction pickAvailablePort(\n preferred: number,\n usedPorts: Set<number>,\n budget?: PortBudget,\n): Effect.Effect<number, PortAllocationError> {\n return Effect.gen(function* () {\n const within = (candidate: number): boolean =>\n !budget || (candidate >= budget.min && candidate <= budget.max);\n\n let port = preferred;\n if (!within(port)) {\n port = budget ? budget.min : port;\n }\n\n const ceiling = budget ? budget.max + 1 : Number.MAX_SAFE_INTEGER;\n let steps = 0;\n\n const fail = () =>\n Effect.fail(\n new PortAllocationError({\n preferred,\n budget,\n cause: budget\n ? `No free port in budget [${budget.min}, ${budget.max}] starting from ${preferred}`\n : `No free port found starting from ${preferred} within ${MAX_PORT_SCAN_STEPS} steps`,\n }),\n );\n\n while (true) {\n if (port >= ceiling || steps > MAX_PORT_SCAN_STEPS) {\n yield* fail();\n }\n\n const candidates: number[] = [];\n for (let i = 0; i < PARALLEL_PROBE_WINDOW && port + i < ceiling; i++) {\n const candidate = port + i;\n if (!usedPorts.has(candidate)) {\n candidates.push(candidate);\n }\n }\n\n if (candidates.length === 0) {\n port += PARALLEL_PROBE_WINDOW;\n steps += PARALLEL_PROBE_WINDOW;\n continue;\n }\n\n const results = yield* Effect.forEach(\n candidates,\n (c) => probePortBindable(c).pipe(Effect.map((free) => ({ port: c, free }))),\n { concurrency: \"unbounded\" },\n );\n\n const firstFree = results.find((r) => r.free);\n if (firstFree) {\n usedPorts.add(firstFree.port);\n return firstFree.port;\n }\n\n port += PARALLEL_PROBE_WINDOW;\n steps += PARALLEL_PROBE_WINDOW;\n }\n });\n}\n\nexport const PortAllocatorLive: Layer.Layer<PortAllocator> = Layer.sync(PortAllocator, () => {\n const usedPorts = claimedPorts();\n return {\n pickAvailable: (preferred, budget) => pickAvailablePort(preferred, usedPorts, budget),\n };\n});\n\nfunction withLocalRuntimeUrl<\n T extends { url: string; entry: string; port?: number; localPath?: string },\n>(entry: T, port: number): T {\n const url = `http://localhost:${port}`;\n return {\n ...entry,\n url,\n entry: `${url}/mf-manifest.json`,\n port,\n };\n}\n\nexport interface DevPortOptions {\n hostPort?: number;\n apiPort?: number;\n uiPort?: number;\n authPort?: number;\n pluginPortStart?: number;\n ssr?: boolean;\n portBudget?: PortBudget;\n}\n\nexport interface PreparedDevRuntime {\n runtimeConfig: RuntimeConfig;\n devPorts: DevPortState;\n}\n\nexport function prepareDevelopmentRuntimeConfig(\n runtimeConfig: RuntimeConfig,\n options?: DevPortOptions,\n): Effect.Effect<PreparedDevRuntime, PortAllocationError, PortAllocator> {\n return Effect.gen(function* () {\n const allocator = yield* PortAllocator;\n const budget = options?.portBudget;\n\n const pickedHostPort = yield* allocator.pickAvailable(\n options?.hostPort ?? DEFAULT_HOST_PORT,\n budget,\n );\n\n const hostIsLocal = runtimeConfig.host.source === \"local\";\n const next: RuntimeConfig = {\n ...runtimeConfig,\n host: hostIsLocal\n ? {\n ...runtimeConfig.host,\n url: `http://localhost:${pickedHostPort}`,\n port: pickedHostPort,\n }\n : { ...runtimeConfig.host },\n ui: { ...runtimeConfig.ui },\n api: { ...runtimeConfig.api },\n auth: runtimeConfig.auth ? { ...runtimeConfig.auth } : undefined,\n plugins: runtimeConfig.plugins ? { ...runtimeConfig.plugins } : undefined,\n };\n\n const devPorts: DevPortState = {\n host: hostIsLocal ? pickedHostPort : undefined,\n api: undefined,\n ui: undefined,\n auth: undefined,\n pluginPortStart: undefined,\n };\n\n if (next.api.source === \"local\" && next.api.localPath) {\n const apiPort = yield* allocator.pickAvailable(\n options?.apiPort ?? next.api.port ?? DEFAULT_API_PORT,\n budget,\n );\n next.api = withLocalRuntimeUrl(next.api, apiPort);\n devPorts.api = apiPort;\n }\n\n if (next.auth?.source === \"local\" && next.auth.localPath) {\n const authPort = yield* allocator.pickAvailable(\n options?.authPort ?? next.auth.port ?? DEFAULT_AUTH_PORT,\n budget,\n );\n next.auth = withLocalRuntimeUrl(next.auth, authPort);\n devPorts.auth = authPort;\n }\n\n if (next.ui.source === \"local\" && next.ui.localPath) {\n const uiPort = yield* allocator.pickAvailable(\n options?.uiPort ?? next.ui.port ?? DEFAULT_UI_PORT,\n budget,\n );\n next.ui = withLocalRuntimeUrl(next.ui, uiPort);\n devPorts.ui = uiPort;\n if (options?.ssr) {\n const ssrPort = yield* allocator.pickAvailable(uiPort + 1, budget);\n next.ui.ssrUrl = `http://localhost:${ssrPort}`;\n } else {\n next.ui.ssrUrl = undefined;\n }\n }\n\n if (next.plugins) {\n const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));\n let pluginBasePort = options?.pluginPortStart ?? DEFAULT_PLUGIN_PORT_START;\n let firstLocalPluginPort: number | undefined;\n\n for (const [pluginId, plugin] of entries) {\n if (plugin.source === \"local\" && plugin.localPath) {\n const pluginPort = yield* allocator.pickAvailable(plugin.port ?? pluginBasePort, budget);\n next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);\n if (firstLocalPluginPort === undefined) firstLocalPluginPort = pluginPort;\n pluginBasePort = pluginPort + 1;\n }\n\n if (plugin.ui?.source === \"local\" && plugin.ui.localPath) {\n const pluginUiPort = yield* allocator.pickAvailable(\n plugin.ui.port ?? pluginBasePort,\n budget,\n );\n next.plugins[pluginId] = {\n ...next.plugins[pluginId]!,\n ui: withLocalRuntimeUrl(plugin.ui, pluginUiPort),\n };\n if (firstLocalPluginPort === undefined) firstLocalPluginPort = pluginUiPort;\n pluginBasePort = pluginUiPort + 1;\n }\n }\n\n devPorts.pluginPortStart = firstLocalPluginPort;\n }\n\n return { runtimeConfig: next, devPorts };\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"app.mjs","names":["configBuildRuntimeConfig"],"sources":["../src/app.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { createServer } from \"node:net\";\nimport { join } from \"node:path\";\nimport { Context, Data, Effect, Layer } from \"effect\";\nimport type { DevPortState } from \"./cli/infra\";\nimport {\n buildRuntimeConfig as configBuildRuntimeConfig,\n getProjectRoot,\n resolveLocalDevelopmentPath,\n} from \"./config\";\nimport { claimedPorts } from \"./process-registry\";\nimport type { AppOrchestrator } from \"./service-descriptor\";\nimport type { BosConfig, RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport type { AppOrchestrator };\n\nconst DEFAULT_HOST_PORT = 3000;\nconst DEFAULT_API_PORT = 3001;\nconst DEFAULT_AUTH_PORT = 3002;\nconst DEFAULT_UI_PORT = 3003;\nconst DEFAULT_PLUGIN_PORT_START = 3010;\n\nconst PROBE_TIMEOUT_MS = 250;\nconst MAX_PORT_SCAN_STEPS = 1000;\nconst PARALLEL_PROBE_WINDOW = 8;\n\nexport type PortBudget = { min: number; max: number };\n\nexport class PortAllocationError extends Data.TaggedError(\"PortAllocationError\")<{\n preferred: number;\n budget?: PortBudget;\n cause?: unknown;\n}> {}\n\nexport class PortAllocator extends Context.Tag(\"PortAllocator\")<\n PortAllocator,\n {\n pickAvailable: (\n preferred: number,\n budget?: PortBudget,\n ) => Effect.Effect<number, PortAllocationError>;\n }\n>() {}\n\nexport function detectLocalPackages(\n bosConfig?: BosConfig,\n runtimeConfig?: RuntimeConfig,\n): string[] {\n const packages: string[] = [];\n const configDir = getProjectRoot();\n\n const uiLocalPath =\n runtimeConfig?.ui.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.ui.development, configDir);\n if (uiLocalPath && existsSync(join(uiLocalPath, \"package.json\"))) {\n packages.push(\"ui\");\n }\n\n const apiLocalPath =\n runtimeConfig?.api.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.api.development, configDir);\n if (apiLocalPath && existsSync(join(apiLocalPath, \"package.json\"))) {\n packages.push(\"api\");\n }\n\n const hostLocalPath =\n runtimeConfig?.host?.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);\n if (hostLocalPath && existsSync(join(hostLocalPath, \"package.json\"))) {\n packages.push(\"host\");\n } else if (existsSync(join(configDir, \"host\", \"package.json\"))) {\n packages.push(\"host\");\n }\n\n for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) {\n if (pluginConfig.localPath && existsSync(join(pluginConfig.localPath, \"package.json\"))) {\n packages.push(`plugin:${pluginId}`);\n }\n if (pluginConfig.ui?.localPath && existsSync(join(pluginConfig.ui.localPath, \"package.json\"))) {\n packages.push(`plugin-ui:${pluginId}`);\n }\n }\n\n const authLocalPath =\n runtimeConfig?.auth?.localPath ??\n resolveLocalDevelopmentPath(bosConfig?.app.auth?.development, configDir);\n if (authLocalPath && existsSync(join(authLocalPath, \"package.json\"))) {\n packages.push(\"auth\");\n }\n\n return packages;\n}\n\nexport async function buildRuntimeConfig(\n bosConfig: BosConfig,\n options: {\n hostSource?: \"local\" | \"remote\";\n uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n proxy?: string;\n env?: \"development\" | \"production\";\n plugins?: Record<string, RuntimePluginConfig>;\n },\n): Promise<RuntimeConfig> {\n return configBuildRuntimeConfig(bosConfig, getProjectRoot(), options.env ?? \"development\", {\n hostSource: options.hostSource,\n uiSource: options.uiSource,\n apiSource: options.apiSource,\n authSource: options.authSource,\n proxy: options.proxy,\n plugins: options.plugins,\n });\n}\n\nfunction probePortBindable(port: number): Effect.Effect<boolean> {\n return Effect.async<boolean>((resume) => {\n const server = createServer();\n\n server.once(\"listening\", () => {\n server.close(() => {\n resume(Effect.succeed(true));\n });\n });\n\n server.once(\"error\", () => {\n server.removeAllListeners();\n // EADDRINUSE, EACCES, or any other bind error → not available\n resume(Effect.succeed(false));\n });\n\n server.listen(port, \"127.0.0.1\");\n\n const timer = setTimeout(() => {\n server.removeAllListeners();\n try {\n server.close();\n } catch {\n // ignore\n }\n resume(Effect.succeed(false));\n }, PROBE_TIMEOUT_MS);\n\n server.once(\"listening\", () => clearTimeout(timer));\n server.once(\"error\", () => clearTimeout(timer));\n });\n}\n\nfunction pickAvailablePort(\n preferred: number,\n usedPorts: Set<number>,\n budget?: PortBudget,\n): Effect.Effect<number, PortAllocationError> {\n return Effect.gen(function* () {\n const within = (candidate: number): boolean =>\n !budget || (candidate >= budget.min && candidate <= budget.max);\n\n let port = preferred;\n if (!within(port)) {\n port = budget ? budget.min : port;\n }\n\n const ceiling = budget ? budget.max + 1 : Number.MAX_SAFE_INTEGER;\n let steps = 0;\n\n const fail = () =>\n Effect.fail(\n new PortAllocationError({\n preferred,\n budget,\n cause: budget\n ? `No free port in budget [${budget.min}, ${budget.max}] starting from ${preferred}`\n : `No free port found starting from ${preferred} within ${MAX_PORT_SCAN_STEPS} steps`,\n }),\n );\n\n while (true) {\n if (port >= ceiling || steps > MAX_PORT_SCAN_STEPS) {\n yield* fail();\n }\n\n const candidates: number[] = [];\n for (let i = 0; i < PARALLEL_PROBE_WINDOW && port + i < ceiling; i++) {\n const candidate = port + i;\n if (!usedPorts.has(candidate)) {\n candidates.push(candidate);\n }\n }\n\n if (candidates.length === 0) {\n port += PARALLEL_PROBE_WINDOW;\n steps += PARALLEL_PROBE_WINDOW;\n continue;\n }\n\n const results = yield* Effect.forEach(\n candidates,\n (c) => probePortBindable(c).pipe(Effect.map((free) => ({ port: c, free }))),\n { concurrency: \"unbounded\" },\n );\n\n const firstFree = results.find((r) => r.free);\n if (firstFree) {\n usedPorts.add(firstFree.port);\n return firstFree.port;\n }\n\n port += PARALLEL_PROBE_WINDOW;\n steps += PARALLEL_PROBE_WINDOW;\n }\n });\n}\n\nexport const PortAllocatorLive: Layer.Layer<PortAllocator> = Layer.sync(PortAllocator, () => {\n const usedPorts = claimedPorts();\n return {\n pickAvailable: (preferred, budget) => pickAvailablePort(preferred, usedPorts, budget),\n };\n});\n\nfunction withLocalRuntimeUrl<\n T extends { url: string; entry: string; port?: number; localPath?: string },\n>(entry: T, port: number): T {\n const url = `http://localhost:${port}`;\n return {\n ...entry,\n url,\n entry: `${url}/mf-manifest.json`,\n port,\n };\n}\n\nexport interface DevPortOptions {\n hostPort?: number;\n apiPort?: number;\n uiPort?: number;\n authPort?: number;\n pluginPortStart?: number;\n ssr?: boolean;\n portBudget?: PortBudget;\n}\n\nexport interface PreparedDevRuntime {\n runtimeConfig: RuntimeConfig;\n devPorts: DevPortState;\n}\n\nexport function prepareDevelopmentRuntimeConfig(\n runtimeConfig: RuntimeConfig,\n options?: DevPortOptions,\n): Effect.Effect<PreparedDevRuntime, PortAllocationError, PortAllocator> {\n return Effect.gen(function* () {\n const allocator = yield* PortAllocator;\n const budget = options?.portBudget;\n\n const pickedHostPort = yield* allocator.pickAvailable(\n options?.hostPort ?? DEFAULT_HOST_PORT,\n budget,\n );\n\n const hostIsLocal = runtimeConfig.host.source === \"local\";\n const next: RuntimeConfig = {\n ...runtimeConfig,\n host: hostIsLocal\n ? {\n ...runtimeConfig.host,\n url: `http://localhost:${pickedHostPort}`,\n port: pickedHostPort,\n }\n : { ...runtimeConfig.host },\n ui: { ...runtimeConfig.ui },\n api: { ...runtimeConfig.api },\n auth: runtimeConfig.auth ? { ...runtimeConfig.auth } : undefined,\n plugins: runtimeConfig.plugins ? { ...runtimeConfig.plugins } : undefined,\n };\n\n const devPorts: DevPortState = {\n host: hostIsLocal ? pickedHostPort : undefined,\n api: undefined,\n ui: undefined,\n auth: undefined,\n pluginPortStart: undefined,\n };\n\n if (next.api.source === \"local\" && next.api.localPath) {\n const apiPort = yield* allocator.pickAvailable(\n options?.apiPort ?? next.api.port ?? DEFAULT_API_PORT,\n budget,\n );\n next.api = withLocalRuntimeUrl(next.api, apiPort);\n devPorts.api = apiPort;\n }\n\n if (next.auth?.source === \"local\" && next.auth.localPath) {\n const authPort = yield* allocator.pickAvailable(\n options?.authPort ?? next.auth.port ?? DEFAULT_AUTH_PORT,\n budget,\n );\n next.auth = withLocalRuntimeUrl(next.auth, authPort);\n devPorts.auth = authPort;\n }\n\n if (next.ui.source === \"local\" && next.ui.localPath) {\n const uiPort = yield* allocator.pickAvailable(\n options?.uiPort ?? next.ui.port ?? DEFAULT_UI_PORT,\n budget,\n );\n next.ui = withLocalRuntimeUrl(next.ui, uiPort);\n devPorts.ui = uiPort;\n if (options?.ssr) {\n const ssrPort = yield* allocator.pickAvailable(uiPort + 1, budget);\n next.ui.ssrUrl = `http://localhost:${ssrPort}`;\n } else {\n next.ui.ssrUrl = undefined;\n }\n }\n\n if (next.plugins) {\n const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));\n let pluginBasePort = options?.pluginPortStart ?? DEFAULT_PLUGIN_PORT_START;\n let firstLocalPluginPort: number | undefined;\n\n for (const [pluginId, plugin] of entries) {\n if (plugin.source === \"local\" && plugin.localPath) {\n const pluginPort = yield* allocator.pickAvailable(plugin.port ?? pluginBasePort, budget);\n next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);\n if (firstLocalPluginPort === undefined) firstLocalPluginPort = pluginPort;\n pluginBasePort = pluginPort + 1;\n }\n\n if (plugin.ui?.source === \"local\" && plugin.ui.localPath) {\n const pluginUiPort = yield* allocator.pickAvailable(\n plugin.ui.port ?? pluginBasePort,\n budget,\n );\n next.plugins[pluginId] = {\n ...next.plugins[pluginId]!,\n ui: withLocalRuntimeUrl(plugin.ui, pluginUiPort),\n };\n if (firstLocalPluginPort === undefined) firstLocalPluginPort = pluginUiPort;\n pluginBasePort = pluginUiPort + 1;\n }\n }\n\n devPorts.pluginPortStart = firstLocalPluginPort;\n }\n\n return { runtimeConfig: next, devPorts };\n });\n}\n"],"mappings":"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"}
package/dist/cli/init.cjs CHANGED
@@ -1,10 +1,10 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_http_client = require('../http-client.cjs');
3
3
  const require_fastkv = require('../fastkv.cjs');
4
- const require_merge = require('../merge.cjs');
5
4
  const require_manifest_normalizer = require('../internal/manifest-normalizer.cjs');
6
5
  const require_save_config = require('../utils/save-config.cjs');
7
6
  const require_snapshot = require('./snapshot.cjs');
7
+ const require_helpers = require('./utils/helpers.cjs');
8
8
  let node_fs = require("node:fs");
9
9
  let node_path = require("node:path");
10
10
  let node_crypto = require("node:crypto");
@@ -39,22 +39,10 @@ const OVERRIDE_WORKSPACE_MAP = {
39
39
  host: ["host"],
40
40
  plugins: []
41
41
  };
42
- function getExtendsRef(config) {
43
- if (typeof config.extends === "string") return config.extends;
44
- if (config.extends && typeof config.extends === "object") return require_merge.resolveExtendsRef(config.extends, "production");
45
- }
46
- function parseBosRef(ref) {
47
- const match = ref.match(/^bos:\/\/([^/]+)\/(.+)$/);
48
- if (!match?.[1] || !match[2]) return null;
49
- return {
50
- account: match[1],
51
- gateway: match[2]
52
- };
53
- }
54
42
  function readWorkspaceCatalog(sourceDir) {
55
43
  const pkgPath = (0, node_path.join)(sourceDir, "package.json");
56
44
  if (!(0, node_fs.existsSync)(pkgPath)) return {};
57
- return { ...readJsonFile(pkgPath).workspaces?.catalog ?? {} };
45
+ return { ...require_helpers.readJsonFile(pkgPath).workspaces?.catalog ?? {} };
58
46
  }
59
47
  async function resolveCatalogChainSource(opts) {
60
48
  const catalogs = [];
@@ -74,10 +62,10 @@ async function resolveCatalogChainSource(opts) {
74
62
  let currentSourceDir = sourceDir;
75
63
  let cleanup = async () => {};
76
64
  if (configPath) {
77
- config = readJsonFile(configPath);
65
+ config = require_helpers.readJsonFile(configPath);
78
66
  currentSourceDir = (0, node_path.dirname)(configPath);
79
67
  } else {
80
- const parsed = parseBosRef(currentRef);
68
+ const parsed = require_helpers.parseBosRef(currentRef);
81
69
  if (!parsed) break;
82
70
  const sourceResult = await resolveSourceDir({
83
71
  extendsAccount: parsed.account,
@@ -90,7 +78,7 @@ async function resolveCatalogChainSource(opts) {
90
78
  cleanups.push(cleanup);
91
79
  catalogs.push(currentSourceDir ? readWorkspaceCatalog(currentSourceDir) : {});
92
80
  if (typeof config.repository === "string") repository = config.repository;
93
- const nextExtendsRef = getExtendsRef(config);
81
+ const nextExtendsRef = require_helpers.getExtendsRef(config);
94
82
  if (!nextExtendsRef) break;
95
83
  if (nextExtendsRef.startsWith("bos://")) {
96
84
  currentRef = nextExtendsRef;
@@ -195,9 +183,9 @@ async function resolveRepositoryViaExtendsChain(extendsAccount, extendsGateway,
195
183
  repository: config.repository,
196
184
  config
197
185
  };
198
- const extendsRef = getExtendsRef(config);
186
+ const extendsRef = require_helpers.getExtendsRef(config);
199
187
  if (extendsRef) {
200
- const parsed = parseBosRef(extendsRef.startsWith("bos://") ? extendsRef : `bos://${extendsRef}`);
188
+ const parsed = require_helpers.parseBosRef(extendsRef.startsWith("bos://") ? extendsRef : `bos://${extendsRef}`);
201
189
  if (parsed) {
202
190
  const result = await resolveRepositoryViaExtendsChain(parsed.account, parsed.gateway, visited);
203
191
  if (result) return result;
@@ -345,14 +333,31 @@ function buildRootTypecheckScript(sections) {
345
333
  if (sections.plugins) commands.push("if [ -d plugins ]; then for dir in plugins/*; do if [ -f \"$dir/package.json\" ]; then bun run --cwd \"$dir\" typecheck; fi; done; fi");
346
334
  return commands.join(" && ");
347
335
  }
336
+ function getParentOnlyScriptKeys(projectDir) {
337
+ const parentPkgPath = (0, node_path.join)(projectDir, "node_modules", "everything-dev", "package.json");
338
+ if (!(0, node_fs.existsSync)(parentPkgPath)) return [];
339
+ try {
340
+ const parentScripts = JSON.parse((0, node_fs.readFileSync)(parentPkgPath, "utf-8")).scripts ?? {};
341
+ const allChildKeys = new Set(Object.keys(buildChildRootScripts({
342
+ ui: true,
343
+ api: true,
344
+ host: true,
345
+ plugins: true
346
+ })));
347
+ allChildKeys.add("test:integration");
348
+ return Object.keys(parentScripts).filter((key) => !allChildKeys.has(key));
349
+ } catch {
350
+ return [];
351
+ }
352
+ }
348
353
  function buildChildRootScripts(sections) {
349
354
  const scripts = {
350
- dev: "node_modules/.bin/bos dev",
351
- "dev:proxy": "node_modules/.bin/bos dev --proxy",
352
- build: "node_modules/.bin/bos build",
353
- deploy: "node_modules/.bin/bos build --deploy",
354
- publish: "node_modules/.bin/bos publish",
355
- start: "node_modules/.bin/bos start",
355
+ dev: "bos dev",
356
+ "dev:proxy": "bos dev --proxy",
357
+ build: "bos build",
358
+ deploy: "bos build --deploy",
359
+ publish: "bos publish",
360
+ start: "bos start",
356
361
  typecheck: buildRootTypecheckScript(sections),
357
362
  lint: "biome check .",
358
363
  "lint:fix": "biome check --write .",
@@ -361,31 +366,34 @@ function buildChildRootScripts(sections) {
361
366
  changeset: "changeset",
362
367
  version: "changeset version",
363
368
  release: "echo 'Packages versioned - app release handled by workflow'",
364
- postinstall: "node_modules/.bin/bos types gen || true",
365
- "types:gen": "node_modules/.bin/bos types gen",
366
- bos: "node_modules/.bin/bos"
369
+ postinstall: "node node_modules/.bin/bos types gen || true",
370
+ "types:gen": "node node_modules/.bin/bos types gen",
371
+ bos: "bos"
367
372
  };
368
373
  if (sections.api) {
369
374
  scripts["db:push"] = "bun run --cwd api drizzle-kit push";
370
- scripts["db:studio"] = "node_modules/.bin/bos db:studio";
371
- scripts["db:doctor"] = "node_modules/.bin/bos db:doctor";
372
- scripts["db:repair"] = "node_modules/.bin/bos db:repair";
375
+ scripts["db:studio"] = "bos db:studio";
376
+ scripts["db:doctor"] = "bos db:doctor";
377
+ scripts["db:repair"] = "bos db:repair";
373
378
  scripts["db:generate"] = "bun run --cwd api drizzle-kit generate";
374
379
  scripts["db:migrate"] = "bun run --cwd api drizzle-kit migrate";
375
- scripts["test:api"] = "cd api && bun run test tests/integration/ tests/unit/";
376
- scripts["test:integration"] = "cd api && bun run test tests/integration/";
380
+ scripts["test:api"] = "bun run --cwd api test --if-present";
381
+ scripts["test:integration"] = "bun run --cwd api test tests/integration/ --if-present";
377
382
  }
378
- if (sections.host) scripts["test:e2e"] = "bun run --cwd host test:e2e";
379
- if (sections.api && sections.host) scripts.test = "bun run test:api && bun run test:e2e";
380
- else if (sections.api) scripts.test = "bun run test:api";
381
- else if (sections.host) scripts.test = "bun run test:e2e";
383
+ if (sections.host) scripts["test:e2e"] = "bun run --cwd host test --if-present";
384
+ const testTargets = [];
385
+ if (sections.api) testTargets.push("bun run --cwd api test --if-present");
386
+ if (sections.host) testTargets.push("bun run --cwd host test --if-present");
387
+ if (sections.ui) testTargets.push("bun run --cwd ui test --if-present");
388
+ if (sections.plugins) testTargets.push("for d in plugins/*; do [ -f \"$d/package.json\" ] && bun run --cwd \"$d\" test --if-present; done");
389
+ scripts.test = testTargets.length > 0 ? testTargets.join(" && ") : "echo \"No workspace directories configured\"";
382
390
  if (sections.api || sections.host) {
383
391
  scripts["dev:postgres"] = "docker compose up -d --wait && bun run dev";
384
392
  scripts["dev:postgres:down"] = "docker compose down";
385
393
  scripts["dev:postgres:reset"] = "docker compose down -v && docker compose up -d --wait";
386
394
  }
387
- if (sections.ui) scripts["dev:ui"] = "node_modules/.bin/bos dev --ui local --api remote";
388
- if (sections.api) scripts["dev:api"] = "node_modules/.bin/bos dev --ui remote --api local";
395
+ if (sections.ui) scripts["dev:ui"] = "bos dev --ui local --api remote";
396
+ if (sections.api) scripts["dev:api"] = "bos dev --ui remote --api local";
389
397
  return scripts;
390
398
  }
391
399
  async function personalizeConfig(destination, opts) {
@@ -471,6 +479,12 @@ async function personalizeConfig(destination, opts) {
471
479
  }
472
480
  await require_save_config.saveBosConfig(destination, config);
473
481
  }
482
+ for (const relPath of ["ui/src/lib/api-types.gen.ts", "api/src/lib/plugins-types.gen.ts"]) {
483
+ const absolutePath = (0, node_path.join)(destination, relPath);
484
+ try {
485
+ (0, node_fs.rmSync)(absolutePath, { force: true });
486
+ } catch {}
487
+ }
474
488
  const pkgPath = (0, node_path.join)(destination, "package.json");
475
489
  if ((0, node_fs.existsSync)(pkgPath)) {
476
490
  const pkg = JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf-8"));
@@ -505,6 +519,7 @@ async function personalizeConfig(destination, opts) {
505
519
  const scripts = pkg.scripts;
506
520
  for (const [key, value] of Object.entries(childScripts)) scripts[key] = value;
507
521
  for (const obsoleteScript of [
522
+ ...getParentOnlyScriptKeys(destination),
508
523
  "init",
509
524
  "sync-catalog",
510
525
  "db:push",
@@ -613,9 +628,9 @@ export type InferOutput<_TRoute extends string> = any;
613
628
  if (has("plugins")) for (const plugin of opts.plugins ?? []) {
614
629
  const pluginSrcDir = (0, node_path.join)(destination, "plugins", plugin, "src");
615
630
  const pluginIndexPath = (0, node_path.join)(pluginSrcDir, "index.ts");
616
- const pluginClientGenPath = (0, node_path.join)(pluginSrcDir, "plugins-client.gen.ts");
631
+ const pluginClientGenPath = (0, node_path.join)(pluginSrcDir, "lib", "plugins-client.gen.ts");
617
632
  if (!(0, node_fs.existsSync)(pluginIndexPath) || (0, node_fs.existsSync)(pluginClientGenPath)) continue;
618
- if (!(0, node_fs.readFileSync)(pluginIndexPath, "utf-8").includes("./plugins-client.gen")) continue;
633
+ if (!(0, node_fs.readFileSync)(pluginIndexPath, "utf-8").includes("./lib/plugins-client.gen")) continue;
619
634
  (0, node_fs.writeFileSync)(pluginClientGenPath, "export type PluginsClient = Record<string, never>;\n");
620
635
  }
621
636
  }
@@ -664,15 +679,22 @@ function toRelativeImportPath(fromPath, toPath) {
664
679
  const rel = (0, node_path.relative)((0, node_path.dirname)(toPath), fromPath);
665
680
  return rel.startsWith(".") ? rel : `./${rel}`;
666
681
  }
667
- async function runBunInstall(destination, spinner) {
668
- await runWithProgress("bun", ["install", "--ignore-scripts"], destination, spinner, "Installing dependencies");
682
+ async function runBunInstall(destination, opts) {
683
+ await runWithProgress("bun", ["install", "--ignore-scripts"], destination, opts?.spinner, "Installing dependencies");
669
684
  }
670
- async function runBunInstallForUpgrade(destination, spinner) {
671
- await runWithProgress("bun", ["install", "--force"], destination, spinner, "Installing dependencies");
685
+ async function runBunInstallForUpgrade(destination, opts) {
686
+ await runWithProgress("bun", ["install", "--force"], destination, opts?.spinner, "Installing dependencies");
672
687
  }
673
- async function runTypesGen(destination, spinner) {
674
- if ((0, node_fs.existsSync)((0, node_path.join)(destination, "node_modules", ".bin", "bos"))) {
675
- await runWithProgress("node_modules/.bin/bos", ["types", "gen"], destination, spinner, "Generating types");
688
+ async function runTypesGen(destination, opts) {
689
+ const bosModule = (0, node_path.join)(destination, "node_modules", "everything-dev", "dist", "cli.mjs");
690
+ if ((0, node_fs.existsSync)(bosModule)) {
691
+ const args = [
692
+ bosModule,
693
+ "types",
694
+ "gen"
695
+ ];
696
+ if (opts?.remotePlugins && opts.remotePlugins.length > 0) args.push("--remote-plugins", opts.remotePlugins.join(","));
697
+ await runWithProgress(process.execPath, args, destination, opts?.spinner, "Generating types");
676
698
  return;
677
699
  }
678
700
  throw new Error("Unable to locate bos CLI for types generation");
@@ -689,7 +711,11 @@ async function runWithProgress(command, args, cwd, spinner, label) {
689
711
  const child = (0, execa.execa)(command, args, {
690
712
  cwd,
691
713
  stdio: "inherit",
692
- timeout: COMMAND_TIMEOUTS[command] ?? 2 * 6e4
714
+ timeout: COMMAND_TIMEOUTS[command] ?? 2 * 6e4,
715
+ env: {
716
+ ...process.env,
717
+ BOS_NO_BANNER: "1"
718
+ }
693
719
  });
694
720
  if (spinner) {
695
721
  const start = Date.now();
@@ -731,9 +757,6 @@ function removeInitLockfile(lockfilePath) {
731
757
  if (!(0, node_fs.existsSync)(lockfilePath)) return;
732
758
  (0, node_fs.rmSync)(lockfilePath, { force: true });
733
759
  }
734
- function readJsonFile(filePath) {
735
- return JSON.parse((0, node_fs.readFileSync)(filePath, "utf-8"));
736
- }
737
760
  async function scaffoldMinimalProject(destination, parentConfig, opts) {
738
761
  (0, node_fs.mkdirSync)(destination, { recursive: true });
739
762
  const has = (section) => opts.overrides.includes(section);
@@ -1068,6 +1091,7 @@ exports.execCommand = execCommand;
1068
1091
  exports.extractSkillsBlock = extractSkillsBlock;
1069
1092
  exports.fetchParentConfig = fetchParentConfig;
1070
1093
  exports.generateDatabaseMigrations = generateDatabaseMigrations;
1094
+ exports.getParentOnlyScriptKeys = getParentOnlyScriptKeys;
1071
1095
  exports.personalizeAgentsMd = personalizeAgentsMd;
1072
1096
  exports.personalizeConfig = personalizeConfig;
1073
1097
  exports.removeInitLockfile = removeInitLockfile;