everything-dev 1.12.3 → 1.13.1

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 (124) hide show
  1. package/cli.js +1 -1
  2. package/dist/app.cjs +24 -101
  3. package/dist/app.cjs.map +1 -1
  4. package/dist/app.mjs +25 -102
  5. package/dist/app.mjs.map +1 -1
  6. package/dist/cli/init.cjs +143 -66
  7. package/dist/cli/init.cjs.map +1 -1
  8. package/dist/cli/init.d.cts +1 -1
  9. package/dist/cli/init.d.cts.map +1 -1
  10. package/dist/cli/init.d.mts +1 -1
  11. package/dist/cli/init.d.mts.map +1 -1
  12. package/dist/cli/init.mjs +144 -67
  13. package/dist/cli/init.mjs.map +1 -1
  14. package/dist/cli/prompts.cjs +3 -3
  15. package/dist/cli/prompts.cjs.map +1 -1
  16. package/dist/cli/prompts.mjs +3 -3
  17. package/dist/cli/prompts.mjs.map +1 -1
  18. package/dist/cli/sync.cjs +15 -56
  19. package/dist/cli/sync.cjs.map +1 -1
  20. package/dist/cli/sync.mjs +15 -56
  21. package/dist/cli/sync.mjs.map +1 -1
  22. package/dist/cli/upgrade.cjs +3 -1
  23. package/dist/cli/upgrade.cjs.map +1 -1
  24. package/dist/cli/upgrade.mjs +3 -1
  25. package/dist/cli/upgrade.mjs.map +1 -1
  26. package/dist/config.cjs +223 -81
  27. package/dist/config.cjs.map +1 -1
  28. package/dist/config.d.cts +21 -5
  29. package/dist/config.d.cts.map +1 -1
  30. package/dist/config.d.mts +21 -5
  31. package/dist/config.d.mts.map +1 -1
  32. package/dist/config.mjs +217 -83
  33. package/dist/config.mjs.map +1 -1
  34. package/dist/contract.d.cts +104 -8
  35. package/dist/contract.d.cts.map +1 -1
  36. package/dist/contract.d.mts +104 -8
  37. package/dist/contract.d.mts.map +1 -1
  38. package/dist/host.cjs +34 -1
  39. package/dist/host.cjs.map +1 -1
  40. package/dist/host.d.cts.map +1 -1
  41. package/dist/host.d.mts.map +1 -1
  42. package/dist/host.mjs +34 -1
  43. package/dist/host.mjs.map +1 -1
  44. package/dist/index.cjs +17 -0
  45. package/dist/index.d.cts +5 -3
  46. package/dist/index.d.mts +5 -3
  47. package/dist/index.mjs +5 -3
  48. package/dist/merge.cjs +113 -0
  49. package/dist/merge.cjs.map +1 -0
  50. package/dist/merge.d.cts +7 -0
  51. package/dist/merge.d.cts.map +1 -0
  52. package/dist/merge.d.mts +7 -0
  53. package/dist/merge.d.mts.map +1 -0
  54. package/dist/merge.mjs +107 -0
  55. package/dist/merge.mjs.map +1 -0
  56. package/dist/plugin.cjs +117 -105
  57. package/dist/plugin.cjs.map +1 -1
  58. package/dist/plugin.d.cts +114 -8
  59. package/dist/plugin.d.cts.map +1 -1
  60. package/dist/plugin.d.mts +114 -8
  61. package/dist/plugin.d.mts.map +1 -1
  62. package/dist/plugin.mjs +117 -105
  63. package/dist/plugin.mjs.map +1 -1
  64. package/dist/service-descriptor.cjs +21 -0
  65. package/dist/service-descriptor.cjs.map +1 -1
  66. package/dist/service-descriptor.d.cts +23 -1
  67. package/dist/service-descriptor.d.cts.map +1 -1
  68. package/dist/service-descriptor.d.mts +23 -1
  69. package/dist/service-descriptor.d.mts.map +1 -1
  70. package/dist/service-descriptor.mjs +21 -0
  71. package/dist/service-descriptor.mjs.map +1 -1
  72. package/dist/shared.cjs +24 -2
  73. package/dist/shared.cjs.map +1 -1
  74. package/dist/shared.d.cts +3 -0
  75. package/dist/shared.d.cts.map +1 -1
  76. package/dist/shared.d.mts +3 -0
  77. package/dist/shared.d.mts.map +1 -1
  78. package/dist/shared.mjs +25 -3
  79. package/dist/shared.mjs.map +1 -1
  80. package/dist/sidebar.cjs +124 -0
  81. package/dist/sidebar.cjs.map +1 -0
  82. package/dist/sidebar.d.cts +8 -0
  83. package/dist/sidebar.d.cts.map +1 -0
  84. package/dist/sidebar.d.mts +8 -0
  85. package/dist/sidebar.d.mts.map +1 -0
  86. package/dist/sidebar.mjs +122 -0
  87. package/dist/sidebar.mjs.map +1 -0
  88. package/dist/types.cjs +104 -10
  89. package/dist/types.cjs.map +1 -1
  90. package/dist/types.d.cts +256 -29
  91. package/dist/types.d.cts.map +1 -1
  92. package/dist/types.d.mts +256 -29
  93. package/dist/types.d.mts.map +1 -1
  94. package/dist/types.mjs +100 -11
  95. package/dist/types.mjs.map +1 -1
  96. package/dist/utils/path-match.cjs +18 -0
  97. package/dist/utils/path-match.cjs.map +1 -0
  98. package/dist/utils/path-match.mjs +17 -0
  99. package/dist/utils/path-match.mjs.map +1 -0
  100. package/dist/utils/save-config.cjs +19 -0
  101. package/dist/utils/save-config.cjs.map +1 -0
  102. package/dist/utils/save-config.mjs +18 -0
  103. package/dist/utils/save-config.mjs.map +1 -0
  104. package/package.json +3 -2
  105. package/skills/dev-workflow/SKILL.md +8 -0
  106. package/skills/extends-config/SKILL.md +132 -0
  107. package/skills/init-upgrade/SKILL.md +128 -0
  108. package/skills/publish-sync/SKILL.md +30 -0
  109. package/src/app.ts +23 -118
  110. package/src/cli/init.ts +199 -100
  111. package/src/cli/prompts.ts +2 -2
  112. package/src/cli/sync.ts +27 -96
  113. package/src/cli/upgrade.ts +2 -0
  114. package/src/config.ts +356 -132
  115. package/src/host.ts +45 -0
  116. package/src/index.ts +1 -0
  117. package/src/merge.ts +198 -0
  118. package/src/plugin.ts +340 -318
  119. package/src/service-descriptor.ts +23 -0
  120. package/src/shared.ts +48 -5
  121. package/src/sidebar.ts +162 -0
  122. package/src/types.ts +134 -28
  123. package/src/utils/path-match.ts +16 -0
  124. package/src/utils/save-config.ts +20 -0
package/cli.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  import { existsSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
package/dist/app.cjs CHANGED
@@ -1,5 +1,4 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
- const require_network = require('./network.cjs');
3
2
  const require_config = require('./config.cjs');
4
3
  let node_fs = require("node:fs");
5
4
  let node_path = require("node:path");
@@ -21,108 +20,23 @@ function detectLocalPackages(bosConfig, runtimeConfig) {
21
20
  const hostLocalPath = runtimeConfig?.host?.localPath ?? require_config.resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);
22
21
  if (hostLocalPath && (0, node_fs.existsSync)((0, node_path.join)(hostLocalPath, "package.json"))) packages.push("host");
23
22
  else if ((0, node_fs.existsSync)((0, node_path.join)(configDir, "host", "package.json"))) packages.push("host");
24
- for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) if (pluginConfig.localPath && (0, node_fs.existsSync)((0, node_path.join)(pluginConfig.localPath, "package.json"))) packages.push(`plugin:${pluginId}`);
23
+ for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) {
24
+ if (pluginConfig.localPath && (0, node_fs.existsSync)((0, node_path.join)(pluginConfig.localPath, "package.json"))) packages.push(`plugin:${pluginId}`);
25
+ if (pluginConfig.ui?.localPath && (0, node_fs.existsSync)((0, node_path.join)(pluginConfig.ui.localPath, "package.json"))) packages.push(`plugin-ui:${pluginId}`);
26
+ }
25
27
  const authLocalPath = runtimeConfig?.auth?.localPath ?? require_config.resolveLocalDevelopmentPath(bosConfig?.app.auth?.development, configDir);
26
28
  if (authLocalPath && (0, node_fs.existsSync)((0, node_path.join)(authLocalPath, "package.json"))) packages.push("auth");
27
29
  return packages;
28
30
  }
29
31
  function buildRuntimeConfig(bosConfig, options) {
30
- const configDir = require_config.getProjectRoot();
31
- const hostConfig = bosConfig.app.host;
32
- const uiConfig = bosConfig.app.ui;
33
- const apiConfig = bosConfig.app.api;
34
- const authConfig = bosConfig.app.auth;
35
- function resolveDevelopmentEntry(entry, preferredSource) {
36
- if (preferredSource === "remote") return {
37
- source: "remote",
38
- url: entry.production ?? ""
39
- };
40
- const localPath = require_config.resolveLocalDevelopmentPath(entry.development, configDir);
41
- if (localPath && (0, node_fs.existsSync)(localPath)) return {
42
- source: "local",
43
- url: "",
44
- localPath
45
- };
46
- const devUrl = entry.development && !require_config.isLocalDevelopmentTarget(entry.development) ? entry.development.replace(/\/$/, "") : null;
47
- if (devUrl) return {
48
- source: "local",
49
- url: devUrl,
50
- port: require_config.parsePort(devUrl)
51
- };
52
- return {
53
- source: "remote",
54
- url: entry.production ?? ""
55
- };
56
- }
57
- const hostEntry = resolveDevelopmentEntry(hostConfig, options.hostSource ?? "local");
58
- const uiEntry = resolveDevelopmentEntry(uiConfig, options.uiSource ?? "local");
59
- const apiEntry = resolveDevelopmentEntry(apiConfig, options.apiSource ?? "local");
60
- const authEntry = authConfig ? resolveDevelopmentEntry(authConfig, options.authSource ?? "local") : void 0;
61
- const hostUrl = `http://localhost:${DEFAULT_HOST_PORT}`;
62
- return {
63
- env: options.env ?? "development",
64
- account: bosConfig.account,
65
- domain: bosConfig.domain,
66
- networkId: require_network.getNetworkIdForAccount(bosConfig.account),
67
- host: {
68
- name: "host",
69
- url: hostUrl,
70
- entry: `${hostUrl}/mf-manifest.json`,
71
- localPath: hostEntry.localPath,
72
- port: hostEntry.port ?? DEFAULT_HOST_PORT,
73
- secrets: hostConfig.secrets,
74
- integrity: hostEntry.source === "remote" ? hostConfig.integrity : void 0,
75
- source: hostEntry.source,
76
- remoteUrl: hostEntry.source === "remote" ? hostEntry.url : void 0
77
- },
78
- shared: bosConfig.shared,
79
- ui: uiConfig ? {
80
- name: uiConfig.name,
81
- url: uiEntry.url,
82
- entry: uiEntry.url ? `${uiEntry.url}/mf-manifest.json` : "/mf-manifest.json",
83
- localPath: uiEntry.localPath,
84
- port: uiEntry.port,
85
- ssrUrl: uiEntry.source === "remote" ? uiConfig.ssr : void 0,
86
- ssrIntegrity: uiEntry.source === "remote" ? uiConfig.ssrIntegrity : void 0,
87
- integrity: uiEntry.source === "remote" ? uiConfig.integrity : void 0,
88
- source: uiEntry.source
89
- } : {
90
- name: "ui",
91
- url: "",
92
- entry: "/mf-manifest.json",
93
- source: uiEntry.source
94
- },
95
- api: apiConfig ? {
96
- name: apiConfig.name,
97
- url: apiEntry.url,
98
- entry: apiEntry.url ? `${apiEntry.url}/mf-manifest.json` : "/mf-manifest.json",
99
- localPath: apiEntry.localPath,
100
- port: apiEntry.port,
101
- source: apiEntry.source,
102
- proxy: options.proxy ?? apiConfig.proxy,
103
- variables: apiConfig.variables,
104
- secrets: apiConfig.secrets,
105
- integrity: apiEntry.source === "remote" ? apiConfig.integrity : void 0
106
- } : {
107
- name: "api",
108
- url: "",
109
- entry: "/mf-manifest.json",
110
- source: apiEntry.source
111
- },
112
- auth: authEntry && authConfig ? {
113
- name: require_config.resolvePluginRuntimeName(void 0, authEntry.localPath, authConfig.name),
114
- url: authEntry.url,
115
- entry: authEntry.url ? `${authEntry.url}/mf-manifest.json` : "/mf-manifest.json",
116
- localPath: authEntry.localPath,
117
- port: authEntry.port,
118
- source: authEntry.source,
119
- proxy: authConfig.proxy,
120
- variables: authConfig.variables,
121
- secrets: authConfig.secrets,
122
- integrity: authEntry.source === "remote" ? authConfig.integrity : void 0
123
- } : void 0,
32
+ return require_config.buildRuntimeConfig(bosConfig, require_config.getProjectRoot(), options.env ?? "development", {
33
+ hostSource: options.hostSource,
34
+ uiSource: options.uiSource,
35
+ apiSource: options.apiSource,
36
+ authSource: options.authSource,
37
+ proxy: options.proxy,
124
38
  plugins: options.plugins
125
- };
39
+ });
126
40
  }
127
41
  function probeTcpOpen(port, timeoutMs = 250) {
128
42
  return new Promise((resolve) => {
@@ -195,10 +109,19 @@ async function prepareDevelopmentRuntimeConfig(runtimeConfig, options) {
195
109
  const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));
196
110
  let pluginBasePort = DEFAULT_PLUGIN_PORT_START;
197
111
  for (const [pluginId, plugin] of entries) {
198
- if (plugin.source !== "local" || !plugin.localPath) continue;
199
- const pluginPort = await pickAvailablePort(plugin.port ?? pluginBasePort, usedPorts);
200
- next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);
201
- pluginBasePort = pluginPort + 1;
112
+ if (plugin.source === "local" && plugin.localPath) {
113
+ const pluginPort = await pickAvailablePort(plugin.port ?? pluginBasePort, usedPorts);
114
+ next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);
115
+ pluginBasePort = pluginPort + 1;
116
+ }
117
+ if (plugin.ui?.source === "local" && plugin.ui.localPath) {
118
+ const uiPort = await pickAvailablePort(plugin.ui.port ?? pluginBasePort, usedPorts);
119
+ next.plugins[pluginId] = {
120
+ ...next.plugins[pluginId],
121
+ ui: withLocalRuntimeUrl(plugin.ui, uiPort)
122
+ };
123
+ pluginBasePort = uiPort + 1;
124
+ }
202
125
  }
203
126
  }
204
127
  return next;
package/dist/app.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.cjs","names":["getProjectRoot","resolveLocalDevelopmentPath","isLocalDevelopmentTarget","parsePort","getNetworkIdForAccount","resolvePluginRuntimeName"],"sources":["../src/app.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { createConnection } from \"node:net\";\nimport { join } from \"node:path\";\nimport {\n getProjectRoot,\n isLocalDevelopmentTarget,\n parsePort,\n resolveLocalDevelopmentPath,\n resolvePluginRuntimeName,\n} from \"./config\";\nimport { getNetworkIdForAccount } from \"./network\";\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 }\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 const configDir = getProjectRoot();\n const hostConfig = bosConfig.app.host;\n const uiConfig = bosConfig.app.ui;\n const apiConfig = bosConfig.app.api;\n const authConfig = bosConfig.app.auth;\n\n function resolveDevelopmentEntry(\n entry: { development?: string; production?: string },\n preferredSource: \"local\" | \"remote\",\n ): { source: \"local\" | \"remote\"; url: string; localPath?: string; port?: number } {\n if (preferredSource === \"remote\") {\n return { source: \"remote\", url: entry.production ?? \"\" };\n }\n\n const localPath = resolveLocalDevelopmentPath(entry.development, configDir);\n if (localPath && existsSync(localPath)) {\n return { source: \"local\", url: \"\", localPath };\n }\n\n const devUrl =\n entry.development && !isLocalDevelopmentTarget(entry.development)\n ? entry.development.replace(/\\/$/, \"\")\n : null;\n if (devUrl) {\n return { source: \"local\", url: devUrl, port: parsePort(devUrl) };\n }\n\n return { source: \"remote\", url: entry.production ?? \"\" };\n }\n\n const hostEntry = resolveDevelopmentEntry(hostConfig, options.hostSource ?? \"local\");\n const uiEntry = resolveDevelopmentEntry(uiConfig, options.uiSource ?? \"local\");\n const apiEntry = resolveDevelopmentEntry(apiConfig, options.apiSource ?? \"local\");\n const authEntry = authConfig\n ? resolveDevelopmentEntry(authConfig, options.authSource ?? \"local\")\n : undefined;\n\n const hostUrl = `http://localhost:${DEFAULT_HOST_PORT}`;\n\n return {\n env: options.env ?? \"development\",\n account: bosConfig.account,\n domain: bosConfig.domain,\n networkId: getNetworkIdForAccount(bosConfig.account),\n host: {\n name: \"host\",\n url: hostUrl,\n entry: `${hostUrl}/mf-manifest.json`,\n localPath: hostEntry.localPath,\n port: hostEntry.port ?? DEFAULT_HOST_PORT,\n secrets: hostConfig.secrets,\n integrity: hostEntry.source === \"remote\" ? hostConfig.integrity : undefined,\n source: hostEntry.source,\n remoteUrl: hostEntry.source === \"remote\" ? hostEntry.url : undefined,\n },\n shared: bosConfig.shared,\n ui: uiConfig\n ? {\n name: uiConfig.name,\n url: uiEntry.url,\n entry: uiEntry.url ? `${uiEntry.url}/mf-manifest.json` : \"/mf-manifest.json\",\n localPath: uiEntry.localPath,\n port: uiEntry.port,\n ssrUrl: uiEntry.source === \"remote\" ? uiConfig.ssr : undefined,\n ssrIntegrity: uiEntry.source === \"remote\" ? uiConfig.ssrIntegrity : undefined,\n integrity: uiEntry.source === \"remote\" ? uiConfig.integrity : undefined,\n source: uiEntry.source,\n }\n : {\n name: \"ui\",\n url: \"\",\n entry: \"/mf-manifest.json\",\n source: uiEntry.source,\n },\n api: apiConfig\n ? {\n name: apiConfig.name,\n url: apiEntry.url,\n entry: apiEntry.url ? `${apiEntry.url}/mf-manifest.json` : \"/mf-manifest.json\",\n localPath: apiEntry.localPath,\n port: apiEntry.port,\n source: apiEntry.source,\n proxy: options.proxy ?? apiConfig.proxy,\n variables: apiConfig.variables,\n secrets: apiConfig.secrets,\n integrity: apiEntry.source === \"remote\" ? apiConfig.integrity : undefined,\n }\n : {\n name: \"api\",\n url: \"\",\n entry: \"/mf-manifest.json\",\n source: apiEntry.source,\n },\n auth:\n authEntry && authConfig\n ? {\n name: resolvePluginRuntimeName(undefined, authEntry.localPath, authConfig.name),\n url: authEntry.url,\n entry: authEntry.url ? `${authEntry.url}/mf-manifest.json` : \"/mf-manifest.json\",\n localPath: authEntry.localPath,\n port: authEntry.port,\n source: authEntry.source,\n proxy: authConfig.proxy,\n variables: authConfig.variables,\n secrets: authConfig.secrets,\n integrity: authEntry.source === \"remote\" ? authConfig.integrity : undefined,\n }\n : undefined,\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 continue;\n }\n\n const pluginPort = await pickAvailablePort(plugin.port ?? pluginBasePort, usedPorts);\n next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);\n pluginBasePort = pluginPort + 1;\n }\n }\n\n return next;\n}\n"],"mappings":"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"}
1
+ {"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"}
package/dist/app.mjs CHANGED
@@ -1,5 +1,4 @@
1
- import { getNetworkIdForAccount } from "./network.mjs";
2
- import { getProjectRoot, isLocalDevelopmentTarget, parsePort, resolveLocalDevelopmentPath, resolvePluginRuntimeName } from "./config.mjs";
1
+ import { buildRuntimeConfig as buildRuntimeConfig$1, getProjectRoot, resolveLocalDevelopmentPath } from "./config.mjs";
3
2
  import { existsSync } from "node:fs";
4
3
  import { join } from "node:path";
5
4
  import { createConnection } from "node:net";
@@ -20,108 +19,23 @@ function detectLocalPackages(bosConfig, runtimeConfig) {
20
19
  const hostLocalPath = runtimeConfig?.host?.localPath ?? resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);
21
20
  if (hostLocalPath && existsSync(join(hostLocalPath, "package.json"))) packages.push("host");
22
21
  else if (existsSync(join(configDir, "host", "package.json"))) packages.push("host");
23
- for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) if (pluginConfig.localPath && existsSync(join(pluginConfig.localPath, "package.json"))) packages.push(`plugin:${pluginId}`);
22
+ for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) {
23
+ if (pluginConfig.localPath && existsSync(join(pluginConfig.localPath, "package.json"))) packages.push(`plugin:${pluginId}`);
24
+ if (pluginConfig.ui?.localPath && existsSync(join(pluginConfig.ui.localPath, "package.json"))) packages.push(`plugin-ui:${pluginId}`);
25
+ }
24
26
  const authLocalPath = runtimeConfig?.auth?.localPath ?? resolveLocalDevelopmentPath(bosConfig?.app.auth?.development, configDir);
25
27
  if (authLocalPath && existsSync(join(authLocalPath, "package.json"))) packages.push("auth");
26
28
  return packages;
27
29
  }
28
30
  function buildRuntimeConfig(bosConfig, options) {
29
- const configDir = getProjectRoot();
30
- const hostConfig = bosConfig.app.host;
31
- const uiConfig = bosConfig.app.ui;
32
- const apiConfig = bosConfig.app.api;
33
- const authConfig = bosConfig.app.auth;
34
- function resolveDevelopmentEntry(entry, preferredSource) {
35
- if (preferredSource === "remote") return {
36
- source: "remote",
37
- url: entry.production ?? ""
38
- };
39
- const localPath = resolveLocalDevelopmentPath(entry.development, configDir);
40
- if (localPath && existsSync(localPath)) return {
41
- source: "local",
42
- url: "",
43
- localPath
44
- };
45
- const devUrl = entry.development && !isLocalDevelopmentTarget(entry.development) ? entry.development.replace(/\/$/, "") : null;
46
- if (devUrl) return {
47
- source: "local",
48
- url: devUrl,
49
- port: parsePort(devUrl)
50
- };
51
- return {
52
- source: "remote",
53
- url: entry.production ?? ""
54
- };
55
- }
56
- const hostEntry = resolveDevelopmentEntry(hostConfig, options.hostSource ?? "local");
57
- const uiEntry = resolveDevelopmentEntry(uiConfig, options.uiSource ?? "local");
58
- const apiEntry = resolveDevelopmentEntry(apiConfig, options.apiSource ?? "local");
59
- const authEntry = authConfig ? resolveDevelopmentEntry(authConfig, options.authSource ?? "local") : void 0;
60
- const hostUrl = `http://localhost:${DEFAULT_HOST_PORT}`;
61
- return {
62
- env: options.env ?? "development",
63
- account: bosConfig.account,
64
- domain: bosConfig.domain,
65
- networkId: getNetworkIdForAccount(bosConfig.account),
66
- host: {
67
- name: "host",
68
- url: hostUrl,
69
- entry: `${hostUrl}/mf-manifest.json`,
70
- localPath: hostEntry.localPath,
71
- port: hostEntry.port ?? DEFAULT_HOST_PORT,
72
- secrets: hostConfig.secrets,
73
- integrity: hostEntry.source === "remote" ? hostConfig.integrity : void 0,
74
- source: hostEntry.source,
75
- remoteUrl: hostEntry.source === "remote" ? hostEntry.url : void 0
76
- },
77
- shared: bosConfig.shared,
78
- ui: uiConfig ? {
79
- name: uiConfig.name,
80
- url: uiEntry.url,
81
- entry: uiEntry.url ? `${uiEntry.url}/mf-manifest.json` : "/mf-manifest.json",
82
- localPath: uiEntry.localPath,
83
- port: uiEntry.port,
84
- ssrUrl: uiEntry.source === "remote" ? uiConfig.ssr : void 0,
85
- ssrIntegrity: uiEntry.source === "remote" ? uiConfig.ssrIntegrity : void 0,
86
- integrity: uiEntry.source === "remote" ? uiConfig.integrity : void 0,
87
- source: uiEntry.source
88
- } : {
89
- name: "ui",
90
- url: "",
91
- entry: "/mf-manifest.json",
92
- source: uiEntry.source
93
- },
94
- api: apiConfig ? {
95
- name: apiConfig.name,
96
- url: apiEntry.url,
97
- entry: apiEntry.url ? `${apiEntry.url}/mf-manifest.json` : "/mf-manifest.json",
98
- localPath: apiEntry.localPath,
99
- port: apiEntry.port,
100
- source: apiEntry.source,
101
- proxy: options.proxy ?? apiConfig.proxy,
102
- variables: apiConfig.variables,
103
- secrets: apiConfig.secrets,
104
- integrity: apiEntry.source === "remote" ? apiConfig.integrity : void 0
105
- } : {
106
- name: "api",
107
- url: "",
108
- entry: "/mf-manifest.json",
109
- source: apiEntry.source
110
- },
111
- auth: authEntry && authConfig ? {
112
- name: resolvePluginRuntimeName(void 0, authEntry.localPath, authConfig.name),
113
- url: authEntry.url,
114
- entry: authEntry.url ? `${authEntry.url}/mf-manifest.json` : "/mf-manifest.json",
115
- localPath: authEntry.localPath,
116
- port: authEntry.port,
117
- source: authEntry.source,
118
- proxy: authConfig.proxy,
119
- variables: authConfig.variables,
120
- secrets: authConfig.secrets,
121
- integrity: authEntry.source === "remote" ? authConfig.integrity : void 0
122
- } : void 0,
31
+ return buildRuntimeConfig$1(bosConfig, getProjectRoot(), options.env ?? "development", {
32
+ hostSource: options.hostSource,
33
+ uiSource: options.uiSource,
34
+ apiSource: options.apiSource,
35
+ authSource: options.authSource,
36
+ proxy: options.proxy,
123
37
  plugins: options.plugins
124
- };
38
+ });
125
39
  }
126
40
  function probeTcpOpen(port, timeoutMs = 250) {
127
41
  return new Promise((resolve) => {
@@ -194,10 +108,19 @@ async function prepareDevelopmentRuntimeConfig(runtimeConfig, options) {
194
108
  const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));
195
109
  let pluginBasePort = DEFAULT_PLUGIN_PORT_START;
196
110
  for (const [pluginId, plugin] of entries) {
197
- if (plugin.source !== "local" || !plugin.localPath) continue;
198
- const pluginPort = await pickAvailablePort(plugin.port ?? pluginBasePort, usedPorts);
199
- next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);
200
- pluginBasePort = pluginPort + 1;
111
+ if (plugin.source === "local" && plugin.localPath) {
112
+ const pluginPort = await pickAvailablePort(plugin.port ?? pluginBasePort, usedPorts);
113
+ next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);
114
+ pluginBasePort = pluginPort + 1;
115
+ }
116
+ if (plugin.ui?.source === "local" && plugin.ui.localPath) {
117
+ const uiPort = await pickAvailablePort(plugin.ui.port ?? pluginBasePort, usedPorts);
118
+ next.plugins[pluginId] = {
119
+ ...next.plugins[pluginId],
120
+ ui: withLocalRuntimeUrl(plugin.ui, uiPort)
121
+ };
122
+ pluginBasePort = uiPort + 1;
123
+ }
201
124
  }
202
125
  }
203
126
  return next;
package/dist/app.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.mjs","names":[],"sources":["../src/app.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { createConnection } from \"node:net\";\nimport { join } from \"node:path\";\nimport {\n getProjectRoot,\n isLocalDevelopmentTarget,\n parsePort,\n resolveLocalDevelopmentPath,\n resolvePluginRuntimeName,\n} from \"./config\";\nimport { getNetworkIdForAccount } from \"./network\";\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 }\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 const configDir = getProjectRoot();\n const hostConfig = bosConfig.app.host;\n const uiConfig = bosConfig.app.ui;\n const apiConfig = bosConfig.app.api;\n const authConfig = bosConfig.app.auth;\n\n function resolveDevelopmentEntry(\n entry: { development?: string; production?: string },\n preferredSource: \"local\" | \"remote\",\n ): { source: \"local\" | \"remote\"; url: string; localPath?: string; port?: number } {\n if (preferredSource === \"remote\") {\n return { source: \"remote\", url: entry.production ?? \"\" };\n }\n\n const localPath = resolveLocalDevelopmentPath(entry.development, configDir);\n if (localPath && existsSync(localPath)) {\n return { source: \"local\", url: \"\", localPath };\n }\n\n const devUrl =\n entry.development && !isLocalDevelopmentTarget(entry.development)\n ? entry.development.replace(/\\/$/, \"\")\n : null;\n if (devUrl) {\n return { source: \"local\", url: devUrl, port: parsePort(devUrl) };\n }\n\n return { source: \"remote\", url: entry.production ?? \"\" };\n }\n\n const hostEntry = resolveDevelopmentEntry(hostConfig, options.hostSource ?? \"local\");\n const uiEntry = resolveDevelopmentEntry(uiConfig, options.uiSource ?? \"local\");\n const apiEntry = resolveDevelopmentEntry(apiConfig, options.apiSource ?? \"local\");\n const authEntry = authConfig\n ? resolveDevelopmentEntry(authConfig, options.authSource ?? \"local\")\n : undefined;\n\n const hostUrl = `http://localhost:${DEFAULT_HOST_PORT}`;\n\n return {\n env: options.env ?? \"development\",\n account: bosConfig.account,\n domain: bosConfig.domain,\n networkId: getNetworkIdForAccount(bosConfig.account),\n host: {\n name: \"host\",\n url: hostUrl,\n entry: `${hostUrl}/mf-manifest.json`,\n localPath: hostEntry.localPath,\n port: hostEntry.port ?? DEFAULT_HOST_PORT,\n secrets: hostConfig.secrets,\n integrity: hostEntry.source === \"remote\" ? hostConfig.integrity : undefined,\n source: hostEntry.source,\n remoteUrl: hostEntry.source === \"remote\" ? hostEntry.url : undefined,\n },\n shared: bosConfig.shared,\n ui: uiConfig\n ? {\n name: uiConfig.name,\n url: uiEntry.url,\n entry: uiEntry.url ? `${uiEntry.url}/mf-manifest.json` : \"/mf-manifest.json\",\n localPath: uiEntry.localPath,\n port: uiEntry.port,\n ssrUrl: uiEntry.source === \"remote\" ? uiConfig.ssr : undefined,\n ssrIntegrity: uiEntry.source === \"remote\" ? uiConfig.ssrIntegrity : undefined,\n integrity: uiEntry.source === \"remote\" ? uiConfig.integrity : undefined,\n source: uiEntry.source,\n }\n : {\n name: \"ui\",\n url: \"\",\n entry: \"/mf-manifest.json\",\n source: uiEntry.source,\n },\n api: apiConfig\n ? {\n name: apiConfig.name,\n url: apiEntry.url,\n entry: apiEntry.url ? `${apiEntry.url}/mf-manifest.json` : \"/mf-manifest.json\",\n localPath: apiEntry.localPath,\n port: apiEntry.port,\n source: apiEntry.source,\n proxy: options.proxy ?? apiConfig.proxy,\n variables: apiConfig.variables,\n secrets: apiConfig.secrets,\n integrity: apiEntry.source === \"remote\" ? apiConfig.integrity : undefined,\n }\n : {\n name: \"api\",\n url: \"\",\n entry: \"/mf-manifest.json\",\n source: apiEntry.source,\n },\n auth:\n authEntry && authConfig\n ? {\n name: resolvePluginRuntimeName(undefined, authEntry.localPath, authConfig.name),\n url: authEntry.url,\n entry: authEntry.url ? `${authEntry.url}/mf-manifest.json` : \"/mf-manifest.json\",\n localPath: authEntry.localPath,\n port: authEntry.port,\n source: authEntry.source,\n proxy: authConfig.proxy,\n variables: authConfig.variables,\n secrets: authConfig.secrets,\n integrity: authEntry.source === \"remote\" ? authConfig.integrity : undefined,\n }\n : undefined,\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 continue;\n }\n\n const pluginPort = await pickAvailablePort(plugin.port ?? pluginBasePort, usedPorts);\n next.plugins[pluginId] = withLocalRuntimeUrl(plugin, pluginPort);\n pluginBasePort = pluginPort + 1;\n }\n }\n\n return next;\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 { 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":"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"}