everything-dev 1.7.2 → 1.8.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 (134) hide show
  1. package/dist/api.cjs +1 -1
  2. package/dist/api.mjs +1 -1
  3. package/dist/app.cjs +82 -51
  4. package/dist/app.cjs.map +1 -1
  5. package/dist/app.mjs +82 -51
  6. package/dist/app.mjs.map +1 -1
  7. package/dist/cli/upgrade.cjs.map +1 -1
  8. package/dist/cli/upgrade.mjs.map +1 -1
  9. package/dist/components/dev-view.cjs +6 -3
  10. package/dist/components/dev-view.cjs.map +1 -1
  11. package/dist/components/dev-view.mjs +6 -3
  12. package/dist/components/dev-view.mjs.map +1 -1
  13. package/dist/components/streaming-view.cjs +5 -2
  14. package/dist/components/streaming-view.cjs.map +1 -1
  15. package/dist/components/streaming-view.mjs +5 -2
  16. package/dist/components/streaming-view.mjs.map +1 -1
  17. package/dist/config.cjs +28 -5
  18. package/dist/config.cjs.map +1 -1
  19. package/dist/config.d.cts.map +1 -1
  20. package/dist/config.d.mts.map +1 -1
  21. package/dist/config.mjs +28 -5
  22. package/dist/config.mjs.map +1 -1
  23. package/dist/contract.cjs +1 -0
  24. package/dist/contract.cjs.map +1 -1
  25. package/dist/contract.d.cts +10 -2
  26. package/dist/contract.d.cts.map +1 -1
  27. package/dist/contract.d.mts +10 -2
  28. package/dist/contract.d.mts.map +1 -1
  29. package/dist/contract.mjs +1 -0
  30. package/dist/contract.mjs.map +1 -1
  31. package/dist/dev-logs.cjs +6 -2
  32. package/dist/dev-logs.cjs.map +1 -1
  33. package/dist/dev-logs.mjs +7 -2
  34. package/dist/dev-logs.mjs.map +1 -1
  35. package/dist/dev-session.cjs +27 -23
  36. package/dist/dev-session.cjs.map +1 -1
  37. package/dist/dev-session.mjs +27 -24
  38. package/dist/dev-session.mjs.map +1 -1
  39. package/dist/federation.server.cjs +1 -1
  40. package/dist/federation.server.mjs +1 -1
  41. package/dist/host.cjs +4 -3
  42. package/dist/host.cjs.map +1 -1
  43. package/dist/host.d.cts.map +1 -1
  44. package/dist/host.d.mts.map +1 -1
  45. package/dist/host.mjs +4 -3
  46. package/dist/host.mjs.map +1 -1
  47. package/dist/integrity.cjs +68 -2
  48. package/dist/integrity.cjs.map +1 -1
  49. package/dist/integrity.d.cts +14 -1
  50. package/dist/integrity.d.cts.map +1 -1
  51. package/dist/integrity.d.mts +14 -1
  52. package/dist/integrity.d.mts.map +1 -1
  53. package/dist/integrity.mjs +66 -3
  54. package/dist/integrity.mjs.map +1 -1
  55. package/dist/mf.cjs +32 -0
  56. package/dist/mf.cjs.map +1 -1
  57. package/dist/mf.d.cts +3 -1
  58. package/dist/mf.d.cts.map +1 -1
  59. package/dist/mf.d.mts +3 -1
  60. package/dist/mf.d.mts.map +1 -1
  61. package/dist/mf.mjs +32 -1
  62. package/dist/mf.mjs.map +1 -1
  63. package/dist/orchestrator.cjs +167 -317
  64. package/dist/orchestrator.cjs.map +1 -1
  65. package/dist/orchestrator.d.cts +24 -21
  66. package/dist/orchestrator.d.cts.map +1 -1
  67. package/dist/orchestrator.d.mts +24 -21
  68. package/dist/orchestrator.d.mts.map +1 -1
  69. package/dist/orchestrator.mjs +168 -316
  70. package/dist/orchestrator.mjs.map +1 -1
  71. package/dist/plugin.cjs +38 -107
  72. package/dist/plugin.cjs.map +1 -1
  73. package/dist/plugin.d.cts +16 -2
  74. package/dist/plugin.d.cts.map +1 -1
  75. package/dist/plugin.d.mts +16 -2
  76. package/dist/plugin.d.mts.map +1 -1
  77. package/dist/plugin.mjs +39 -108
  78. package/dist/plugin.mjs.map +1 -1
  79. package/dist/service-descriptor.cjs +188 -0
  80. package/dist/service-descriptor.cjs.map +1 -0
  81. package/dist/service-descriptor.d.cts +107 -0
  82. package/dist/service-descriptor.d.cts.map +1 -0
  83. package/dist/service-descriptor.d.mts +107 -0
  84. package/dist/service-descriptor.d.mts.map +1 -0
  85. package/dist/service-descriptor.mjs +182 -0
  86. package/dist/service-descriptor.mjs.map +1 -0
  87. package/dist/types.cjs +8 -1
  88. package/dist/types.cjs.map +1 -1
  89. package/dist/types.d.cts +16 -1
  90. package/dist/types.d.cts.map +1 -1
  91. package/dist/types.d.mts +16 -1
  92. package/dist/types.d.mts.map +1 -1
  93. package/dist/types.mjs +8 -1
  94. package/dist/types.mjs.map +1 -1
  95. package/dist/ui/index.cjs +1 -0
  96. package/dist/ui/index.d.cts +2 -2
  97. package/dist/ui/index.d.mts +2 -2
  98. package/dist/ui/index.mjs +2 -2
  99. package/dist/ui/runtime.cjs +4 -0
  100. package/dist/ui/runtime.cjs.map +1 -1
  101. package/dist/ui/runtime.d.cts +2 -1
  102. package/dist/ui/runtime.d.cts.map +1 -1
  103. package/dist/ui/runtime.d.mts +2 -1
  104. package/dist/ui/runtime.d.mts.map +1 -1
  105. package/dist/ui/runtime.mjs +4 -1
  106. package/dist/ui/runtime.mjs.map +1 -1
  107. package/package.json +12 -4
  108. package/skills/dev-workflow/SKILL.md +105 -0
  109. package/skills/publish-sync/SKILL.md +130 -0
  110. package/src/app.ts +98 -204
  111. package/src/cli/upgrade.ts +20 -4
  112. package/src/components/dev-view.tsx +8 -3
  113. package/src/components/streaming-view.ts +7 -2
  114. package/src/config.ts +40 -8
  115. package/src/contract.ts +1 -0
  116. package/src/dev-logs.ts +8 -1
  117. package/src/dev-session.ts +56 -79
  118. package/src/host.ts +4 -3
  119. package/src/integrity.ts +96 -10
  120. package/src/mf.ts +42 -0
  121. package/src/orchestrator.ts +232 -411
  122. package/src/plugin.ts +48 -136
  123. package/src/service-descriptor.ts +258 -0
  124. package/src/types.ts +8 -1
  125. package/src/ui/runtime.ts +5 -0
  126. package/dist/process-registry.cjs +0 -120
  127. package/dist/process-registry.cjs.map +0 -1
  128. package/dist/process-registry.d.cts +0 -25
  129. package/dist/process-registry.d.cts.map +0 -1
  130. package/dist/process-registry.d.mts +0 -25
  131. package/dist/process-registry.d.mts.map +0 -1
  132. package/dist/process-registry.mjs +0 -119
  133. package/dist/process-registry.mjs.map +0 -1
  134. package/src/process-registry.ts +0 -154
package/dist/api.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
3
3
  require('./sdk.cjs');
4
- const require_mf = require('./mf.cjs');
5
4
  const require_integrity = require('./integrity.cjs');
5
+ const require_mf = require('./mf.cjs');
6
6
  let every_plugin = require("every-plugin");
7
7
 
8
8
  //#region src/api.ts
package/dist/api.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createPluginRuntime } from "./sdk.mjs";
2
- import { ensureNodeRuntimePlugin, registerRemote } from "./mf.mjs";
3
2
  import { verifySriForUrl } from "./integrity.mjs";
3
+ import { ensureNodeRuntimePlugin, registerRemote } from "./mf.mjs";
4
4
 
5
5
  //#region src/api.ts
6
6
  async function loadApiPlugin(opts) {
package/dist/app.cjs CHANGED
@@ -1,18 +1,16 @@
1
1
  const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
2
2
  const require_network = require('./network.cjs');
3
3
  const require_config = require('./config.cjs');
4
- require('./orchestrator.cjs');
5
4
  let node_fs = require("node:fs");
6
5
  let node_path = require("node:path");
7
- let effect = require("effect");
8
6
  let node_net = require("node:net");
9
7
 
10
8
  //#region src/app.ts
11
9
  const DEFAULT_HOST_PORT = 3e3;
12
- const DEFAULT_UI_PORT = 3002;
13
- const DEFAULT_API_PORT = 3014;
14
- const DEFAULT_AUTH_PORT = 3020;
15
- const DEFAULT_PLUGIN_PORT_START = 3021;
10
+ const DEFAULT_API_PORT = 3001;
11
+ const DEFAULT_AUTH_PORT = 3002;
12
+ const DEFAULT_UI_PORT = 3003;
13
+ const DEFAULT_PLUGIN_PORT_START = 3010;
16
14
  function detectLocalPackages(bosConfig, runtimeConfig) {
17
15
  const packages = [];
18
16
  const configDir = require_config.getProjectRoot();
@@ -20,7 +18,7 @@ function detectLocalPackages(bosConfig, runtimeConfig) {
20
18
  if (uiLocalPath && (0, node_fs.existsSync)((0, node_path.join)(uiLocalPath, "package.json"))) packages.push("ui");
21
19
  const apiLocalPath = runtimeConfig?.api.localPath ?? require_config.resolveLocalDevelopmentPath(bosConfig?.app.api.development, configDir);
22
20
  if (apiLocalPath && (0, node_fs.existsSync)((0, node_path.join)(apiLocalPath, "package.json"))) packages.push("api");
23
- const hostLocalPath = require_config.resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);
21
+ const hostLocalPath = runtimeConfig?.host?.localPath ?? require_config.resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);
24
22
  if (hostLocalPath && (0, node_fs.existsSync)((0, node_path.join)(hostLocalPath, "package.json"))) packages.push("host");
25
23
  else if ((0, node_fs.existsSync)((0, node_path.join)(configDir, "host", "package.json"))) packages.push("host");
26
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}`);
@@ -30,69 +28,98 @@ function detectLocalPackages(bosConfig, runtimeConfig) {
30
28
  }
31
29
  function buildRuntimeConfig(bosConfig, options) {
32
30
  const configDir = require_config.getProjectRoot();
31
+ const hostConfig = bosConfig.app.host;
33
32
  const uiConfig = bosConfig.app.ui;
34
33
  const apiConfig = bosConfig.app.api;
35
34
  const authConfig = bosConfig.app.auth;
36
- const uiSource = options.uiSource ?? "local";
37
- const apiSource = options.apiSource ?? "local";
38
- const authSource = options.authSource ?? "local";
39
- const uiLocalPath = require_config.resolveLocalDevelopmentPath(uiConfig.development, configDir);
40
- const apiLocalPath = require_config.resolveLocalDevelopmentPath(apiConfig.development, configDir);
41
- const authLocalPath = authConfig ? require_config.resolveLocalDevelopmentPath(authConfig.development, configDir) : null;
42
- const uiLocalUrl = !uiLocalPath && uiConfig.development && !require_config.isLocalDevelopmentTarget(uiConfig.development) ? uiConfig.development : "";
43
- const apiLocalUrl = !apiLocalPath && apiConfig.development && !require_config.isLocalDevelopmentTarget(apiConfig.development) ? apiConfig.development : "";
44
- const authLocalUrl = authConfig && !authLocalPath && authConfig.development && !require_config.isLocalDevelopmentTarget(authConfig.development) ? authConfig.development : "";
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}`;
45
62
  return {
46
63
  env: options.env ?? "development",
47
64
  account: bosConfig.account,
48
65
  domain: bosConfig.domain,
49
66
  networkId: require_network.getNetworkIdForAccount(bosConfig.account),
50
- hostUrl: options.hostUrl,
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
+ },
51
78
  shared: bosConfig.shared,
52
79
  ui: uiConfig ? {
53
80
  name: uiConfig.name,
54
- url: uiSource === "remote" ? uiConfig.production ?? "" : uiLocalUrl,
55
- entry: uiSource === "remote" ? `${uiConfig.production ?? ""}/mf-manifest.json` : uiLocalUrl ? `${uiLocalUrl}/mf-manifest.json` : "/mf-manifest.json",
56
- localPath: uiSource === "local" ? uiLocalPath ?? void 0 : void 0,
57
- port: uiSource === "local" && uiLocalUrl ? require_config.parsePort(uiLocalUrl) : void 0,
58
- ssrUrl: uiSource === "remote" ? uiConfig.ssr : void 0,
59
- ssrIntegrity: uiSource === "remote" ? uiConfig.ssrIntegrity : void 0,
60
- integrity: uiSource === "remote" ? uiConfig.integrity : void 0,
61
- source: uiSource === "local" ? uiLocalPath ? "local" : "remote" : "remote"
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
62
89
  } : {
63
90
  name: "ui",
64
91
  url: "",
65
92
  entry: "/mf-manifest.json",
66
- source: uiSource
93
+ source: uiEntry.source
67
94
  },
68
95
  api: apiConfig ? {
69
96
  name: apiConfig.name,
70
- url: apiSource === "remote" ? apiConfig.production ?? "" : apiLocalUrl,
71
- entry: apiSource === "remote" ? `${apiConfig.production ?? ""}/mf-manifest.json` : apiLocalUrl ? `${apiLocalUrl}/mf-manifest.json` : "/mf-manifest.json",
72
- localPath: apiSource === "local" ? apiLocalPath ?? void 0 : void 0,
73
- port: apiSource === "local" && apiLocalUrl ? require_config.parsePort(apiLocalUrl) : void 0,
74
- source: apiSource === "local" ? apiLocalPath ? "local" : "remote" : "remote",
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,
75
102
  proxy: options.proxy ?? apiConfig.proxy,
76
103
  variables: apiConfig.variables,
77
104
  secrets: apiConfig.secrets,
78
- integrity: apiSource === "remote" ? apiConfig.integrity : void 0
105
+ integrity: apiEntry.source === "remote" ? apiConfig.integrity : void 0
79
106
  } : {
80
107
  name: "api",
81
108
  url: "",
82
109
  entry: "/mf-manifest.json",
83
- source: apiSource
110
+ source: apiEntry.source
84
111
  },
85
- auth: authConfig ? {
86
- name: require_config.resolvePluginRuntimeName(void 0, authSource === "local" ? authLocalPath ?? void 0 : void 0, authConfig.name),
87
- url: authSource === "remote" ? authConfig.production ?? "" : authLocalUrl,
88
- entry: authSource === "remote" ? `${authConfig.production ?? ""}/mf-manifest.json` : authLocalUrl ? `${authLocalUrl}/mf-manifest.json` : "/mf-manifest.json",
89
- localPath: authSource === "local" ? authLocalPath ?? void 0 : void 0,
90
- port: authSource === "local" && authLocalUrl ? require_config.parsePort(authLocalUrl) : void 0,
91
- source: authSource === "local" ? authLocalPath ? "local" : "remote" : "remote",
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,
92
119
  proxy: authConfig.proxy,
93
120
  variables: authConfig.variables,
94
121
  secrets: authConfig.secrets,
95
- integrity: authSource === "remote" ? authConfig.integrity : void 0
122
+ integrity: authEntry.source === "remote" ? authConfig.integrity : void 0
96
123
  } : void 0,
97
124
  plugins: options.plugins
98
125
  };
@@ -135,15 +162,27 @@ function withLocalRuntimeUrl(entry, port) {
135
162
  }
136
163
  async function prepareDevelopmentRuntimeConfig(runtimeConfig, options) {
137
164
  const usedPorts = /* @__PURE__ */ new Set();
138
- const hostPort = await pickAvailablePort(options?.hostPort ?? (runtimeConfig.hostUrl ? require_config.parsePort(runtimeConfig.hostUrl) : DEFAULT_HOST_PORT), usedPorts);
165
+ const hostPort = await pickAvailablePort(options?.hostPort ?? DEFAULT_HOST_PORT, usedPorts);
139
166
  const next = {
140
167
  ...runtimeConfig,
141
- hostUrl: `http://localhost:${hostPort}`,
168
+ host: {
169
+ ...runtimeConfig.host,
170
+ url: `http://localhost:${hostPort}`,
171
+ port: hostPort
172
+ },
142
173
  ui: { ...runtimeConfig.ui },
143
174
  api: { ...runtimeConfig.api },
144
175
  auth: runtimeConfig.auth ? { ...runtimeConfig.auth } : void 0,
145
176
  plugins: runtimeConfig.plugins ? { ...runtimeConfig.plugins } : void 0
146
177
  };
178
+ if (next.api.source === "local" && next.api.localPath) {
179
+ const apiPort = await pickAvailablePort(next.api.port ?? DEFAULT_API_PORT, usedPorts);
180
+ next.api = withLocalRuntimeUrl(next.api, apiPort);
181
+ }
182
+ if (next.auth?.source === "local" && next.auth.localPath) {
183
+ const authPort = await pickAvailablePort(next.auth.port ?? DEFAULT_AUTH_PORT, usedPorts);
184
+ next.auth = withLocalRuntimeUrl(next.auth, authPort);
185
+ }
147
186
  if (next.ui.source === "local" && next.ui.localPath) {
148
187
  const uiPort = await pickAvailablePort(next.ui.port ?? DEFAULT_UI_PORT, usedPorts);
149
188
  next.ui = withLocalRuntimeUrl(next.ui, uiPort);
@@ -152,10 +191,6 @@ async function prepareDevelopmentRuntimeConfig(runtimeConfig, options) {
152
191
  next.ui.ssrUrl = `http://localhost:${ssrPort}`;
153
192
  } else next.ui.ssrUrl = void 0;
154
193
  }
155
- if (next.api.source === "local" && next.api.localPath) {
156
- const apiPort = await pickAvailablePort(next.api.port ?? DEFAULT_API_PORT, usedPorts);
157
- next.api = withLocalRuntimeUrl(next.api, apiPort);
158
- }
159
194
  if (next.plugins) {
160
195
  const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));
161
196
  let pluginBasePort = DEFAULT_PLUGIN_PORT_START;
@@ -166,10 +201,6 @@ async function prepareDevelopmentRuntimeConfig(runtimeConfig, options) {
166
201
  pluginBasePort = pluginPort + 1;
167
202
  }
168
203
  }
169
- if (next.auth?.source === "local" && next.auth.localPath) {
170
- const authPort = await pickAvailablePort(next.auth.port ?? DEFAULT_AUTH_PORT, usedPorts);
171
- next.auth = withLocalRuntimeUrl(next.auth, authPort);
172
- }
173
204
  return next;
174
205
  }
175
206
 
package/dist/app.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.cjs","names":["getProjectRoot","resolveLocalDevelopmentPath","isLocalDevelopmentTarget","getNetworkIdForAccount","parsePort","resolvePluginRuntimeName"],"sources":["../src/app.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { createConnection } from \"node:net\";\nimport { join } from \"node:path\";\nimport { Effect } from \"effect\";\nimport {\n getProjectRoot,\n isLocalDevelopmentTarget,\n parsePort,\n resolveLocalDevelopmentPath,\n resolvePluginRuntimeName,\n} from \"./config\";\nimport { getNetworkIdForAccount } from \"./network\";\nimport { makeDevProcess, type ProcessCallbacks, type ProcessHandle } from \"./orchestrator\";\nimport type { ProcessRegistry } from \"./process-registry\";\nimport type { BosConfig, RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface AppOrchestrator {\n packages: string[];\n env: Record<string, string>;\n description: string;\n bosConfig: BosConfig;\n runtimeConfig: RuntimeConfig;\n port?: number;\n interactive?: boolean;\n}\n\nconst STARTUP_ORDER = [\"ui-ssr\", \"ui\", \"auth\", \"api\", \"plugin\", \"host-build\", \"host\"];\nconst DEFAULT_HOST_PORT = 3000;\nconst DEFAULT_UI_PORT = 3002;\nconst DEFAULT_API_PORT = 3014;\nconst DEFAULT_AUTH_PORT = 3020;\nconst DEFAULT_PLUGIN_PORT_START = 3021;\n\nconst sortByOrder = (packages: string[]): string[] => {\n return [...packages].sort((a, b) => {\n const aIdx = a.startsWith(\"plugin:\")\n ? STARTUP_ORDER.indexOf(\"plugin\")\n : STARTUP_ORDER.indexOf(a);\n const bIdx = b.startsWith(\"plugin:\")\n ? STARTUP_ORDER.indexOf(\"plugin\")\n : STARTUP_ORDER.indexOf(b);\n if (aIdx === -1 && bIdx === -1) return 0;\n if (aIdx === -1) return 1;\n if (bIdx === -1) return -1;\n return aIdx - bIdx;\n });\n};\n\n// Note: log filtering and persistence lives at the CLI layer.\n\nexport interface DevServersHandle {\n handles: ProcessHandle[];\n shutdown: Effect.Effect<void>;\n}\n\nexport const startDevServers = (\n orchestrator: AppOrchestrator,\n callbacks: ProcessCallbacks,\n registry?: ProcessRegistry,\n) => {\n const run = Effect.gen(function* () {\n const orderedPackages = sortByOrder(orchestrator.packages);\n const handles: ProcessHandle[] = [];\n\n const startProcess = (pkg: string) => {\n const portOverride = pkg === \"host\" ? orchestrator.port : undefined;\n return makeDevProcess(\n pkg,\n orchestrator.env,\n callbacks,\n portOverride,\n orchestrator.bosConfig,\n orchestrator.runtimeConfig,\n registry,\n );\n };\n\n const startGroup = (packages: string[]) =>\n Effect.forEach(packages, startProcess, { concurrency: \"unbounded\" });\n\n const awaitReady = (pkg: string, handle: ProcessHandle) =>\n Effect.race(\n handle.waitForReady,\n Effect.sleep(\"30 seconds\").pipe(\n Effect.andThen(\n Effect.sync(() => {\n callbacks.onLog(pkg, \"Timeout waiting for ready, continuing...\", true);\n }),\n ),\n ),\n );\n\n const nonHostPackages = orderedPackages.filter((pkg) => pkg !== \"host\");\n const hostPackages = orderedPackages.filter((pkg) => pkg === \"host\");\n\n const nonHostHandles = yield* startGroup(nonHostPackages);\n handles.push(...nonHostHandles);\n\n yield* Effect.forEach(\n nonHostHandles.map((handle, index) => ({\n handle,\n pkg: nonHostPackages[index] ?? handle.name,\n })),\n ({ handle, pkg }) => awaitReady(pkg, handle),\n { concurrency: \"unbounded\" },\n );\n\n const hostHandles = yield* startGroup(hostPackages);\n handles.push(...hostHandles);\n\n yield* Effect.forEach(\n hostHandles.map((handle, index) => ({ handle, pkg: hostPackages[index] ?? handle.name })),\n ({ handle, pkg }) => awaitReady(pkg, handle),\n { concurrency: \"unbounded\" },\n );\n\n const shutdown = Effect.gen(function* () {\n const reversed = [...handles].reverse();\n for (const handle of reversed) {\n yield* handle.kill.pipe(Effect.ignore);\n }\n });\n\n return { handles, shutdown } satisfies DevServersHandle;\n });\n\n return run;\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 = 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 uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n hostUrl: string;\n proxy?: string;\n env?: \"development\" | \"production\";\n plugins?: Record<string, RuntimePluginConfig>;\n },\n): RuntimeConfig {\n const configDir = getProjectRoot();\n const uiConfig = bosConfig.app.ui;\n const apiConfig = bosConfig.app.api;\n const authConfig = bosConfig.app.auth;\n const uiSource = options.uiSource ?? \"local\";\n const apiSource = options.apiSource ?? \"local\";\n const authSource = options.authSource ?? \"local\";\n const uiLocalPath = resolveLocalDevelopmentPath(uiConfig.development, configDir);\n const apiLocalPath = resolveLocalDevelopmentPath(apiConfig.development, configDir);\n const authLocalPath = authConfig\n ? resolveLocalDevelopmentPath(authConfig.development, configDir)\n : null;\n const uiLocalUrl =\n !uiLocalPath && uiConfig.development && !isLocalDevelopmentTarget(uiConfig.development)\n ? uiConfig.development\n : \"\";\n const apiLocalUrl =\n !apiLocalPath && apiConfig.development && !isLocalDevelopmentTarget(apiConfig.development)\n ? apiConfig.development\n : \"\";\n const authLocalUrl =\n authConfig &&\n !authLocalPath &&\n authConfig.development &&\n !isLocalDevelopmentTarget(authConfig.development)\n ? authConfig.development\n : \"\";\n\n return {\n env: options.env ?? \"development\",\n account: bosConfig.account,\n domain: bosConfig.domain,\n networkId: getNetworkIdForAccount(bosConfig.account),\n hostUrl: options.hostUrl,\n shared: bosConfig.shared,\n ui: uiConfig\n ? {\n name: uiConfig.name,\n url: uiSource === \"remote\" ? (uiConfig.production ?? \"\") : uiLocalUrl,\n entry:\n uiSource === \"remote\"\n ? `${uiConfig.production ?? \"\"}/mf-manifest.json`\n : uiLocalUrl\n ? `${uiLocalUrl}/mf-manifest.json`\n : \"/mf-manifest.json\",\n localPath: uiSource === \"local\" ? (uiLocalPath ?? undefined) : undefined,\n port: uiSource === \"local\" && uiLocalUrl ? parsePort(uiLocalUrl) : undefined,\n ssrUrl: uiSource === \"remote\" ? uiConfig.ssr : undefined,\n ssrIntegrity: uiSource === \"remote\" ? uiConfig.ssrIntegrity : undefined,\n integrity: uiSource === \"remote\" ? uiConfig.integrity : undefined,\n source: uiSource === \"local\" ? (uiLocalPath ? \"local\" : \"remote\") : \"remote\",\n }\n : {\n name: \"ui\",\n url: \"\",\n entry: \"/mf-manifest.json\",\n source: uiSource,\n },\n api: apiConfig\n ? {\n name: apiConfig.name,\n url: apiSource === \"remote\" ? (apiConfig.production ?? \"\") : apiLocalUrl,\n entry:\n apiSource === \"remote\"\n ? `${apiConfig.production ?? \"\"}/mf-manifest.json`\n : apiLocalUrl\n ? `${apiLocalUrl}/mf-manifest.json`\n : \"/mf-manifest.json\",\n localPath: apiSource === \"local\" ? (apiLocalPath ?? undefined) : undefined,\n port: apiSource === \"local\" && apiLocalUrl ? parsePort(apiLocalUrl) : undefined,\n source: apiSource === \"local\" ? (apiLocalPath ? \"local\" : \"remote\") : \"remote\",\n proxy: options.proxy ?? apiConfig.proxy,\n variables: apiConfig.variables,\n secrets: apiConfig.secrets,\n integrity: apiSource === \"remote\" ? apiConfig.integrity : undefined,\n }\n : {\n name: \"api\",\n url: \"\",\n entry: \"/mf-manifest.json\",\n source: apiSource,\n },\n auth: authConfig\n ? {\n name: resolvePluginRuntimeName(\n undefined,\n authSource === \"local\" ? (authLocalPath ?? undefined) : undefined,\n authConfig.name,\n ),\n url: authSource === \"remote\" ? (authConfig.production ?? \"\") : authLocalUrl,\n entry:\n authSource === \"remote\"\n ? `${authConfig.production ?? \"\"}/mf-manifest.json`\n : authLocalUrl\n ? `${authLocalUrl}/mf-manifest.json`\n : \"/mf-manifest.json\",\n localPath: authSource === \"local\" ? (authLocalPath ?? undefined) : undefined,\n port: authSource === \"local\" && authLocalUrl ? parsePort(authLocalUrl) : undefined,\n source: authSource === \"local\" ? (authLocalPath ? \"local\" : \"remote\") : \"remote\",\n proxy: authConfig.proxy,\n variables: authConfig.variables,\n secrets: authConfig.secrets,\n integrity: authSource === \"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(\n options?.hostPort ??\n (runtimeConfig.hostUrl ? parsePort(runtimeConfig.hostUrl) : DEFAULT_HOST_PORT),\n usedPorts,\n );\n\n const next: RuntimeConfig = {\n ...runtimeConfig,\n hostUrl: `http://localhost:${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.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.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.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 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 return next;\n}\n"],"mappings":"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"}
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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"}
package/dist/app.mjs CHANGED
@@ -1,17 +1,15 @@
1
1
  import { getNetworkIdForAccount } from "./network.mjs";
2
2
  import { getProjectRoot, isLocalDevelopmentTarget, parsePort, resolveLocalDevelopmentPath, resolvePluginRuntimeName } from "./config.mjs";
3
- import "./orchestrator.mjs";
4
3
  import { existsSync } from "node:fs";
5
4
  import { join } from "node:path";
6
- import { Effect } from "effect";
7
5
  import { createConnection } from "node:net";
8
6
 
9
7
  //#region src/app.ts
10
8
  const DEFAULT_HOST_PORT = 3e3;
11
- const DEFAULT_UI_PORT = 3002;
12
- const DEFAULT_API_PORT = 3014;
13
- const DEFAULT_AUTH_PORT = 3020;
14
- const DEFAULT_PLUGIN_PORT_START = 3021;
9
+ const DEFAULT_API_PORT = 3001;
10
+ const DEFAULT_AUTH_PORT = 3002;
11
+ const DEFAULT_UI_PORT = 3003;
12
+ const DEFAULT_PLUGIN_PORT_START = 3010;
15
13
  function detectLocalPackages(bosConfig, runtimeConfig) {
16
14
  const packages = [];
17
15
  const configDir = getProjectRoot();
@@ -19,7 +17,7 @@ function detectLocalPackages(bosConfig, runtimeConfig) {
19
17
  if (uiLocalPath && existsSync(join(uiLocalPath, "package.json"))) packages.push("ui");
20
18
  const apiLocalPath = runtimeConfig?.api.localPath ?? resolveLocalDevelopmentPath(bosConfig?.app.api.development, configDir);
21
19
  if (apiLocalPath && existsSync(join(apiLocalPath, "package.json"))) packages.push("api");
22
- const hostLocalPath = resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);
20
+ const hostLocalPath = runtimeConfig?.host?.localPath ?? resolveLocalDevelopmentPath(bosConfig?.app.host.development, configDir);
23
21
  if (hostLocalPath && existsSync(join(hostLocalPath, "package.json"))) packages.push("host");
24
22
  else if (existsSync(join(configDir, "host", "package.json"))) packages.push("host");
25
23
  for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig?.plugins ?? {})) if (pluginConfig.localPath && existsSync(join(pluginConfig.localPath, "package.json"))) packages.push(`plugin:${pluginId}`);
@@ -29,69 +27,98 @@ function detectLocalPackages(bosConfig, runtimeConfig) {
29
27
  }
30
28
  function buildRuntimeConfig(bosConfig, options) {
31
29
  const configDir = getProjectRoot();
30
+ const hostConfig = bosConfig.app.host;
32
31
  const uiConfig = bosConfig.app.ui;
33
32
  const apiConfig = bosConfig.app.api;
34
33
  const authConfig = bosConfig.app.auth;
35
- const uiSource = options.uiSource ?? "local";
36
- const apiSource = options.apiSource ?? "local";
37
- const authSource = options.authSource ?? "local";
38
- const uiLocalPath = resolveLocalDevelopmentPath(uiConfig.development, configDir);
39
- const apiLocalPath = resolveLocalDevelopmentPath(apiConfig.development, configDir);
40
- const authLocalPath = authConfig ? resolveLocalDevelopmentPath(authConfig.development, configDir) : null;
41
- const uiLocalUrl = !uiLocalPath && uiConfig.development && !isLocalDevelopmentTarget(uiConfig.development) ? uiConfig.development : "";
42
- const apiLocalUrl = !apiLocalPath && apiConfig.development && !isLocalDevelopmentTarget(apiConfig.development) ? apiConfig.development : "";
43
- const authLocalUrl = authConfig && !authLocalPath && authConfig.development && !isLocalDevelopmentTarget(authConfig.development) ? authConfig.development : "";
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}`;
44
61
  return {
45
62
  env: options.env ?? "development",
46
63
  account: bosConfig.account,
47
64
  domain: bosConfig.domain,
48
65
  networkId: getNetworkIdForAccount(bosConfig.account),
49
- hostUrl: options.hostUrl,
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
+ },
50
77
  shared: bosConfig.shared,
51
78
  ui: uiConfig ? {
52
79
  name: uiConfig.name,
53
- url: uiSource === "remote" ? uiConfig.production ?? "" : uiLocalUrl,
54
- entry: uiSource === "remote" ? `${uiConfig.production ?? ""}/mf-manifest.json` : uiLocalUrl ? `${uiLocalUrl}/mf-manifest.json` : "/mf-manifest.json",
55
- localPath: uiSource === "local" ? uiLocalPath ?? void 0 : void 0,
56
- port: uiSource === "local" && uiLocalUrl ? parsePort(uiLocalUrl) : void 0,
57
- ssrUrl: uiSource === "remote" ? uiConfig.ssr : void 0,
58
- ssrIntegrity: uiSource === "remote" ? uiConfig.ssrIntegrity : void 0,
59
- integrity: uiSource === "remote" ? uiConfig.integrity : void 0,
60
- source: uiSource === "local" ? uiLocalPath ? "local" : "remote" : "remote"
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
61
88
  } : {
62
89
  name: "ui",
63
90
  url: "",
64
91
  entry: "/mf-manifest.json",
65
- source: uiSource
92
+ source: uiEntry.source
66
93
  },
67
94
  api: apiConfig ? {
68
95
  name: apiConfig.name,
69
- url: apiSource === "remote" ? apiConfig.production ?? "" : apiLocalUrl,
70
- entry: apiSource === "remote" ? `${apiConfig.production ?? ""}/mf-manifest.json` : apiLocalUrl ? `${apiLocalUrl}/mf-manifest.json` : "/mf-manifest.json",
71
- localPath: apiSource === "local" ? apiLocalPath ?? void 0 : void 0,
72
- port: apiSource === "local" && apiLocalUrl ? parsePort(apiLocalUrl) : void 0,
73
- source: apiSource === "local" ? apiLocalPath ? "local" : "remote" : "remote",
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,
74
101
  proxy: options.proxy ?? apiConfig.proxy,
75
102
  variables: apiConfig.variables,
76
103
  secrets: apiConfig.secrets,
77
- integrity: apiSource === "remote" ? apiConfig.integrity : void 0
104
+ integrity: apiEntry.source === "remote" ? apiConfig.integrity : void 0
78
105
  } : {
79
106
  name: "api",
80
107
  url: "",
81
108
  entry: "/mf-manifest.json",
82
- source: apiSource
109
+ source: apiEntry.source
83
110
  },
84
- auth: authConfig ? {
85
- name: resolvePluginRuntimeName(void 0, authSource === "local" ? authLocalPath ?? void 0 : void 0, authConfig.name),
86
- url: authSource === "remote" ? authConfig.production ?? "" : authLocalUrl,
87
- entry: authSource === "remote" ? `${authConfig.production ?? ""}/mf-manifest.json` : authLocalUrl ? `${authLocalUrl}/mf-manifest.json` : "/mf-manifest.json",
88
- localPath: authSource === "local" ? authLocalPath ?? void 0 : void 0,
89
- port: authSource === "local" && authLocalUrl ? parsePort(authLocalUrl) : void 0,
90
- source: authSource === "local" ? authLocalPath ? "local" : "remote" : "remote",
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,
91
118
  proxy: authConfig.proxy,
92
119
  variables: authConfig.variables,
93
120
  secrets: authConfig.secrets,
94
- integrity: authSource === "remote" ? authConfig.integrity : void 0
121
+ integrity: authEntry.source === "remote" ? authConfig.integrity : void 0
95
122
  } : void 0,
96
123
  plugins: options.plugins
97
124
  };
@@ -134,15 +161,27 @@ function withLocalRuntimeUrl(entry, port) {
134
161
  }
135
162
  async function prepareDevelopmentRuntimeConfig(runtimeConfig, options) {
136
163
  const usedPorts = /* @__PURE__ */ new Set();
137
- const hostPort = await pickAvailablePort(options?.hostPort ?? (runtimeConfig.hostUrl ? parsePort(runtimeConfig.hostUrl) : DEFAULT_HOST_PORT), usedPorts);
164
+ const hostPort = await pickAvailablePort(options?.hostPort ?? DEFAULT_HOST_PORT, usedPorts);
138
165
  const next = {
139
166
  ...runtimeConfig,
140
- hostUrl: `http://localhost:${hostPort}`,
167
+ host: {
168
+ ...runtimeConfig.host,
169
+ url: `http://localhost:${hostPort}`,
170
+ port: hostPort
171
+ },
141
172
  ui: { ...runtimeConfig.ui },
142
173
  api: { ...runtimeConfig.api },
143
174
  auth: runtimeConfig.auth ? { ...runtimeConfig.auth } : void 0,
144
175
  plugins: runtimeConfig.plugins ? { ...runtimeConfig.plugins } : void 0
145
176
  };
177
+ if (next.api.source === "local" && next.api.localPath) {
178
+ const apiPort = await pickAvailablePort(next.api.port ?? DEFAULT_API_PORT, usedPorts);
179
+ next.api = withLocalRuntimeUrl(next.api, apiPort);
180
+ }
181
+ if (next.auth?.source === "local" && next.auth.localPath) {
182
+ const authPort = await pickAvailablePort(next.auth.port ?? DEFAULT_AUTH_PORT, usedPorts);
183
+ next.auth = withLocalRuntimeUrl(next.auth, authPort);
184
+ }
146
185
  if (next.ui.source === "local" && next.ui.localPath) {
147
186
  const uiPort = await pickAvailablePort(next.ui.port ?? DEFAULT_UI_PORT, usedPorts);
148
187
  next.ui = withLocalRuntimeUrl(next.ui, uiPort);
@@ -151,10 +190,6 @@ async function prepareDevelopmentRuntimeConfig(runtimeConfig, options) {
151
190
  next.ui.ssrUrl = `http://localhost:${ssrPort}`;
152
191
  } else next.ui.ssrUrl = void 0;
153
192
  }
154
- if (next.api.source === "local" && next.api.localPath) {
155
- const apiPort = await pickAvailablePort(next.api.port ?? DEFAULT_API_PORT, usedPorts);
156
- next.api = withLocalRuntimeUrl(next.api, apiPort);
157
- }
158
193
  if (next.plugins) {
159
194
  const entries = Object.entries(next.plugins).sort(([a], [b]) => a.localeCompare(b));
160
195
  let pluginBasePort = DEFAULT_PLUGIN_PORT_START;
@@ -165,10 +200,6 @@ async function prepareDevelopmentRuntimeConfig(runtimeConfig, options) {
165
200
  pluginBasePort = pluginPort + 1;
166
201
  }
167
202
  }
168
- if (next.auth?.source === "local" && next.auth.localPath) {
169
- const authPort = await pickAvailablePort(next.auth.port ?? DEFAULT_AUTH_PORT, usedPorts);
170
- next.auth = withLocalRuntimeUrl(next.auth, authPort);
171
- }
172
203
  return next;
173
204
  }
174
205
 
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 { Effect } from \"effect\";\nimport {\n getProjectRoot,\n isLocalDevelopmentTarget,\n parsePort,\n resolveLocalDevelopmentPath,\n resolvePluginRuntimeName,\n} from \"./config\";\nimport { getNetworkIdForAccount } from \"./network\";\nimport { makeDevProcess, type ProcessCallbacks, type ProcessHandle } from \"./orchestrator\";\nimport type { ProcessRegistry } from \"./process-registry\";\nimport type { BosConfig, RuntimeConfig, RuntimePluginConfig } from \"./types\";\n\nexport interface AppOrchestrator {\n packages: string[];\n env: Record<string, string>;\n description: string;\n bosConfig: BosConfig;\n runtimeConfig: RuntimeConfig;\n port?: number;\n interactive?: boolean;\n}\n\nconst STARTUP_ORDER = [\"ui-ssr\", \"ui\", \"auth\", \"api\", \"plugin\", \"host-build\", \"host\"];\nconst DEFAULT_HOST_PORT = 3000;\nconst DEFAULT_UI_PORT = 3002;\nconst DEFAULT_API_PORT = 3014;\nconst DEFAULT_AUTH_PORT = 3020;\nconst DEFAULT_PLUGIN_PORT_START = 3021;\n\nconst sortByOrder = (packages: string[]): string[] => {\n return [...packages].sort((a, b) => {\n const aIdx = a.startsWith(\"plugin:\")\n ? STARTUP_ORDER.indexOf(\"plugin\")\n : STARTUP_ORDER.indexOf(a);\n const bIdx = b.startsWith(\"plugin:\")\n ? STARTUP_ORDER.indexOf(\"plugin\")\n : STARTUP_ORDER.indexOf(b);\n if (aIdx === -1 && bIdx === -1) return 0;\n if (aIdx === -1) return 1;\n if (bIdx === -1) return -1;\n return aIdx - bIdx;\n });\n};\n\n// Note: log filtering and persistence lives at the CLI layer.\n\nexport interface DevServersHandle {\n handles: ProcessHandle[];\n shutdown: Effect.Effect<void>;\n}\n\nexport const startDevServers = (\n orchestrator: AppOrchestrator,\n callbacks: ProcessCallbacks,\n registry?: ProcessRegistry,\n) => {\n const run = Effect.gen(function* () {\n const orderedPackages = sortByOrder(orchestrator.packages);\n const handles: ProcessHandle[] = [];\n\n const startProcess = (pkg: string) => {\n const portOverride = pkg === \"host\" ? orchestrator.port : undefined;\n return makeDevProcess(\n pkg,\n orchestrator.env,\n callbacks,\n portOverride,\n orchestrator.bosConfig,\n orchestrator.runtimeConfig,\n registry,\n );\n };\n\n const startGroup = (packages: string[]) =>\n Effect.forEach(packages, startProcess, { concurrency: \"unbounded\" });\n\n const awaitReady = (pkg: string, handle: ProcessHandle) =>\n Effect.race(\n handle.waitForReady,\n Effect.sleep(\"30 seconds\").pipe(\n Effect.andThen(\n Effect.sync(() => {\n callbacks.onLog(pkg, \"Timeout waiting for ready, continuing...\", true);\n }),\n ),\n ),\n );\n\n const nonHostPackages = orderedPackages.filter((pkg) => pkg !== \"host\");\n const hostPackages = orderedPackages.filter((pkg) => pkg === \"host\");\n\n const nonHostHandles = yield* startGroup(nonHostPackages);\n handles.push(...nonHostHandles);\n\n yield* Effect.forEach(\n nonHostHandles.map((handle, index) => ({\n handle,\n pkg: nonHostPackages[index] ?? handle.name,\n })),\n ({ handle, pkg }) => awaitReady(pkg, handle),\n { concurrency: \"unbounded\" },\n );\n\n const hostHandles = yield* startGroup(hostPackages);\n handles.push(...hostHandles);\n\n yield* Effect.forEach(\n hostHandles.map((handle, index) => ({ handle, pkg: hostPackages[index] ?? handle.name })),\n ({ handle, pkg }) => awaitReady(pkg, handle),\n { concurrency: \"unbounded\" },\n );\n\n const shutdown = Effect.gen(function* () {\n const reversed = [...handles].reverse();\n for (const handle of reversed) {\n yield* handle.kill.pipe(Effect.ignore);\n }\n });\n\n return { handles, shutdown } satisfies DevServersHandle;\n });\n\n return run;\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 = 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 uiSource?: \"local\" | \"remote\";\n apiSource?: \"local\" | \"remote\";\n authSource?: \"local\" | \"remote\";\n hostUrl: string;\n proxy?: string;\n env?: \"development\" | \"production\";\n plugins?: Record<string, RuntimePluginConfig>;\n },\n): RuntimeConfig {\n const configDir = getProjectRoot();\n const uiConfig = bosConfig.app.ui;\n const apiConfig = bosConfig.app.api;\n const authConfig = bosConfig.app.auth;\n const uiSource = options.uiSource ?? \"local\";\n const apiSource = options.apiSource ?? \"local\";\n const authSource = options.authSource ?? \"local\";\n const uiLocalPath = resolveLocalDevelopmentPath(uiConfig.development, configDir);\n const apiLocalPath = resolveLocalDevelopmentPath(apiConfig.development, configDir);\n const authLocalPath = authConfig\n ? resolveLocalDevelopmentPath(authConfig.development, configDir)\n : null;\n const uiLocalUrl =\n !uiLocalPath && uiConfig.development && !isLocalDevelopmentTarget(uiConfig.development)\n ? uiConfig.development\n : \"\";\n const apiLocalUrl =\n !apiLocalPath && apiConfig.development && !isLocalDevelopmentTarget(apiConfig.development)\n ? apiConfig.development\n : \"\";\n const authLocalUrl =\n authConfig &&\n !authLocalPath &&\n authConfig.development &&\n !isLocalDevelopmentTarget(authConfig.development)\n ? authConfig.development\n : \"\";\n\n return {\n env: options.env ?? \"development\",\n account: bosConfig.account,\n domain: bosConfig.domain,\n networkId: getNetworkIdForAccount(bosConfig.account),\n hostUrl: options.hostUrl,\n shared: bosConfig.shared,\n ui: uiConfig\n ? {\n name: uiConfig.name,\n url: uiSource === \"remote\" ? (uiConfig.production ?? \"\") : uiLocalUrl,\n entry:\n uiSource === \"remote\"\n ? `${uiConfig.production ?? \"\"}/mf-manifest.json`\n : uiLocalUrl\n ? `${uiLocalUrl}/mf-manifest.json`\n : \"/mf-manifest.json\",\n localPath: uiSource === \"local\" ? (uiLocalPath ?? undefined) : undefined,\n port: uiSource === \"local\" && uiLocalUrl ? parsePort(uiLocalUrl) : undefined,\n ssrUrl: uiSource === \"remote\" ? uiConfig.ssr : undefined,\n ssrIntegrity: uiSource === \"remote\" ? uiConfig.ssrIntegrity : undefined,\n integrity: uiSource === \"remote\" ? uiConfig.integrity : undefined,\n source: uiSource === \"local\" ? (uiLocalPath ? \"local\" : \"remote\") : \"remote\",\n }\n : {\n name: \"ui\",\n url: \"\",\n entry: \"/mf-manifest.json\",\n source: uiSource,\n },\n api: apiConfig\n ? {\n name: apiConfig.name,\n url: apiSource === \"remote\" ? (apiConfig.production ?? \"\") : apiLocalUrl,\n entry:\n apiSource === \"remote\"\n ? `${apiConfig.production ?? \"\"}/mf-manifest.json`\n : apiLocalUrl\n ? `${apiLocalUrl}/mf-manifest.json`\n : \"/mf-manifest.json\",\n localPath: apiSource === \"local\" ? (apiLocalPath ?? undefined) : undefined,\n port: apiSource === \"local\" && apiLocalUrl ? parsePort(apiLocalUrl) : undefined,\n source: apiSource === \"local\" ? (apiLocalPath ? \"local\" : \"remote\") : \"remote\",\n proxy: options.proxy ?? apiConfig.proxy,\n variables: apiConfig.variables,\n secrets: apiConfig.secrets,\n integrity: apiSource === \"remote\" ? apiConfig.integrity : undefined,\n }\n : {\n name: \"api\",\n url: \"\",\n entry: \"/mf-manifest.json\",\n source: apiSource,\n },\n auth: authConfig\n ? {\n name: resolvePluginRuntimeName(\n undefined,\n authSource === \"local\" ? (authLocalPath ?? undefined) : undefined,\n authConfig.name,\n ),\n url: authSource === \"remote\" ? (authConfig.production ?? \"\") : authLocalUrl,\n entry:\n authSource === \"remote\"\n ? `${authConfig.production ?? \"\"}/mf-manifest.json`\n : authLocalUrl\n ? `${authLocalUrl}/mf-manifest.json`\n : \"/mf-manifest.json\",\n localPath: authSource === \"local\" ? (authLocalPath ?? undefined) : undefined,\n port: authSource === \"local\" && authLocalUrl ? parsePort(authLocalUrl) : undefined,\n source: authSource === \"local\" ? (authLocalPath ? \"local\" : \"remote\") : \"remote\",\n proxy: authConfig.proxy,\n variables: authConfig.variables,\n secrets: authConfig.secrets,\n integrity: authSource === \"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(\n options?.hostPort ??\n (runtimeConfig.hostUrl ? parsePort(runtimeConfig.hostUrl) : DEFAULT_HOST_PORT),\n usedPorts,\n );\n\n const next: RuntimeConfig = {\n ...runtimeConfig,\n hostUrl: `http://localhost:${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.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.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.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 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 return next;\n}\n"],"mappings":"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"}
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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"}