{"version":3,"file":"app.cjs","names":["getProjectRoot","resolveLocalDevelopmentPath","configBuildRuntimeConfig"],"sources":["../src/app.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { createConnection } from \"node:net\";\nimport { join } from \"node:path\";\nimport {\n  buildRuntimeConfig as configBuildRuntimeConfig,\n  getProjectRoot,\n  resolveLocalDevelopmentPath,\n} from \"./config\";\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\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 probeTcpOpen(port: number, timeoutMs = 250): Promise<boolean> {\n  return new Promise((resolve) => {\n    const socket = createConnection({ host: \"127.0.0.1\", port });\n    const timer = setTimeout(() => {\n      socket.destroy();\n      resolve(false);\n    }, timeoutMs);\n\n    socket.once(\"connect\", () => {\n      clearTimeout(timer);\n      socket.destroy();\n      resolve(true);\n    });\n\n    socket.once(\"error\", () => {\n      clearTimeout(timer);\n      resolve(false);\n    });\n  });\n}\n\nasync function pickAvailablePort(preferred: number, usedPorts: Set<number>): Promise<number> {\n  let port = preferred;\n  while (usedPorts.has(port) || (await probeTcpOpen(port))) {\n    port += 1;\n  }\n  usedPorts.add(port);\n  return port;\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 async function prepareDevelopmentRuntimeConfig(\n  runtimeConfig: RuntimeConfig,\n  options?: { hostPort?: number; ssr?: boolean },\n): Promise<RuntimeConfig> {\n  const usedPorts = new Set<number>();\n  const hostPort = await pickAvailablePort(options?.hostPort ?? DEFAULT_HOST_PORT, usedPorts);\n\n  const next: RuntimeConfig = {\n    ...runtimeConfig,\n    host: { ...runtimeConfig.host, url: `http://localhost:${hostPort}`, port: hostPort },\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  if (next.api.source === \"local\" && next.api.localPath) {\n    const apiPort = await pickAvailablePort(next.api.port ?? DEFAULT_API_PORT, usedPorts);\n    next.api = withLocalRuntimeUrl(next.api, apiPort);\n  }\n\n  if (next.auth?.source === \"local\" && next.auth.localPath) {\n    const authPort = await pickAvailablePort(next.auth.port ?? DEFAULT_AUTH_PORT, usedPorts);\n    next.auth = withLocalRuntimeUrl(next.auth, authPort);\n  }\n\n  if (next.ui.source === \"local\" && next.ui.localPath) {\n    const uiPort = await pickAvailablePort(next.ui.port ?? DEFAULT_UI_PORT, usedPorts);\n    next.ui = withLocalRuntimeUrl(next.ui, uiPort);\n    if (options?.ssr) {\n      const ssrPort = await pickAvailablePort(uiPort + 1, usedPorts);\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 = DEFAULT_PLUGIN_PORT_START;\n\n    for (const [pluginId, plugin] of entries) {\n      if (plugin.source === \"local\" && plugin.localPath) {\n        const pluginPort = await pickAvailablePort(plugin.port ?? pluginBasePort, usedPorts);\n        next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);\n        pluginBasePort = pluginPort + 1;\n      }\n\n      if (plugin.ui?.source === \"local\" && plugin.ui.localPath) {\n        const uiPort = await pickAvailablePort(plugin.ui.port ?? pluginBasePort, usedPorts);\n        next.plugins[pluginId] = {\n          ...next.plugins[pluginId]!,\n          ui: withLocalRuntimeUrl(plugin.ui, uiPort),\n        };\n        pluginBasePort = uiPort + 1;\n      }\n    }\n  }\n\n  return next;\n}\n"],"mappings":";;;;;;;AAaA,MAAM,oBAAoB;AAC1B,MAAM,mBAAmB;AACzB,MAAM,oBAAoB;AAC1B,MAAM,kBAAkB;AACxB,MAAM,4BAA4B;AAElC,SAAgB,oBACd,WACA,eACU;CACV,MAAM,WAAqB,EAAE;CAC7B,MAAM,YAAYA,+BAAgB;CAElC,MAAM,cACJ,eAAe,GAAG,aAClBC,2CAA4B,WAAW,IAAI,GAAG,aAAa,UAAU;AACvE,KAAI,2DAA+B,aAAa,eAAe,CAAC,CAC9D,UAAS,KAAK,KAAK;CAGrB,MAAM,eACJ,eAAe,IAAI,aACnBA,2CAA4B,WAAW,IAAI,IAAI,aAAa,UAAU;AACxE,KAAI,4DAAgC,cAAc,eAAe,CAAC,CAChE,UAAS,KAAK,MAAM;CAGtB,MAAM,gBACJ,eAAe,MAAM,aACrBA,2CAA4B,WAAW,IAAI,KAAK,aAAa,UAAU;AACzE,KAAI,6DAAiC,eAAe,eAAe,CAAC,CAClE,UAAS,KAAK,OAAO;sDACI,WAAW,QAAQ,eAAe,CAAC,CAC5D,UAAS,KAAK,OAAO;AAGvB,MAAK,MAAM,CAAC,UAAU,iBAAiB,OAAO,QAAQ,eAAe,WAAW,EAAE,CAAC,EAAE;AACnF,MAAI,aAAa,yDAA6B,aAAa,WAAW,eAAe,CAAC,CACpF,UAAS,KAAK,UAAU,WAAW;AAErC,MAAI,aAAa,IAAI,yDAA6B,aAAa,GAAG,WAAW,eAAe,CAAC,CAC3F,UAAS,KAAK,aAAa,WAAW;;CAI1C,MAAM,gBACJ,eAAe,MAAM,aACrBA,2CAA4B,WAAW,IAAI,MAAM,aAAa,UAAU;AAC1E,KAAI,6DAAiC,eAAe,eAAe,CAAC,CAClE,UAAS,KAAK,OAAO;AAGvB,QAAO;;AAGT,SAAgB,mBACd,WACA,SASe;AACf,QAAOC,kCAAyB,WAAWF,+BAAgB,EAAE,QAAQ,OAAO,eAAe;EACzF,YAAY,QAAQ;EACpB,UAAU,QAAQ;EAClB,WAAW,QAAQ;EACnB,YAAY,QAAQ;EACpB,OAAO,QAAQ;EACf,SAAS,QAAQ;EAClB,CAAC;;AAGJ,SAAS,aAAa,MAAc,YAAY,KAAuB;AACrE,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,wCAA0B;GAAE,MAAM;GAAa;GAAM,CAAC;EAC5D,MAAM,QAAQ,iBAAiB;AAC7B,UAAO,SAAS;AAChB,WAAQ,MAAM;KACb,UAAU;AAEb,SAAO,KAAK,iBAAiB;AAC3B,gBAAa,MAAM;AACnB,UAAO,SAAS;AAChB,WAAQ,KAAK;IACb;AAEF,SAAO,KAAK,eAAe;AACzB,gBAAa,MAAM;AACnB,WAAQ,MAAM;IACd;GACF;;AAGJ,eAAe,kBAAkB,WAAmB,WAAyC;CAC3F,IAAI,OAAO;AACX,QAAO,UAAU,IAAI,KAAK,IAAK,MAAM,aAAa,KAAK,CACrD,SAAQ;AAEV,WAAU,IAAI,KAAK;AACnB,QAAO;;AAGT,SAAS,oBAEP,OAAU,MAAiB;CAC3B,MAAM,MAAM,oBAAoB;AAChC,QAAO;EACL,GAAG;EACH;EACA,OAAO,GAAG,IAAI;EACd;EACD;;AAGH,eAAsB,gCACpB,eACA,SACwB;CACxB,MAAM,4BAAY,IAAI,KAAa;CACnC,MAAM,WAAW,MAAM,kBAAkB,SAAS,YAAY,mBAAmB,UAAU;CAE3F,MAAM,OAAsB;EAC1B,GAAG;EACH,MAAM;GAAE,GAAG,cAAc;GAAM,KAAK,oBAAoB;GAAY,MAAM;GAAU;EACpF,IAAI,EAAE,GAAG,cAAc,IAAI;EAC3B,KAAK,EAAE,GAAG,cAAc,KAAK;EAC7B,MAAM,cAAc,OAAO,EAAE,GAAG,cAAc,MAAM,GAAG;EACvD,SAAS,cAAc,UAAU,EAAE,GAAG,cAAc,SAAS,GAAG;EACjE;AAED,KAAI,KAAK,IAAI,WAAW,WAAW,KAAK,IAAI,WAAW;EACrD,MAAM,UAAU,MAAM,kBAAkB,KAAK,IAAI,QAAQ,kBAAkB,UAAU;AACrF,OAAK,MAAM,oBAAoB,KAAK,KAAK,QAAQ;;AAGnD,KAAI,KAAK,MAAM,WAAW,WAAW,KAAK,KAAK,WAAW;EACxD,MAAM,WAAW,MAAM,kBAAkB,KAAK,KAAK,QAAQ,mBAAmB,UAAU;AACxF,OAAK,OAAO,oBAAoB,KAAK,MAAM,SAAS;;AAGtD,KAAI,KAAK,GAAG,WAAW,WAAW,KAAK,GAAG,WAAW;EACnD,MAAM,SAAS,MAAM,kBAAkB,KAAK,GAAG,QAAQ,iBAAiB,UAAU;AAClF,OAAK,KAAK,oBAAoB,KAAK,IAAI,OAAO;AAC9C,MAAI,SAAS,KAAK;GAChB,MAAM,UAAU,MAAM,kBAAkB,SAAS,GAAG,UAAU;AAC9D,QAAK,GAAG,SAAS,oBAAoB;QAErC,MAAK,GAAG,SAAS;;AAIrB,KAAI,KAAK,SAAS;EAChB,MAAM,UAAU,OAAO,QAAQ,KAAK,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;EACnF,IAAI,iBAAiB;AAErB,OAAK,MAAM,CAAC,UAAU,WAAW,SAAS;AACxC,OAAI,OAAO,WAAW,WAAW,OAAO,WAAW;IACjD,MAAM,aAAa,MAAM,kBAAkB,OAAO,QAAQ,gBAAgB,UAAU;AACpF,SAAK,QAAQ,YAAY,oBAAoB,QAAQ,WAAW;AAChE,qBAAiB,aAAa;;AAGhC,OAAI,OAAO,IAAI,WAAW,WAAW,OAAO,GAAG,WAAW;IACxD,MAAM,SAAS,MAAM,kBAAkB,OAAO,GAAG,QAAQ,gBAAgB,UAAU;AACnF,SAAK,QAAQ,YAAY;KACvB,GAAG,KAAK,QAAQ;KAChB,IAAI,oBAAoB,OAAO,IAAI,OAAO;KAC3C;AACD,qBAAiB,SAAS;;;;AAKhC,QAAO"}