everything-dev 1.7.2 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +14 -6
  26. package/dist/contract.d.cts.map +1 -1
  27. package/dist/contract.d.mts +14 -6
  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 +19 -5
  74. package/dist/plugin.d.cts.map +1 -1
  75. package/dist/plugin.d.mts +19 -5
  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 +18 -3
  90. package/dist/types.d.cts.map +1 -1
  91. package/dist/types.d.mts +18 -3
  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/src/plugin.ts CHANGED
@@ -23,8 +23,6 @@ import {
23
23
  getHostDevelopmentPort,
24
24
  getProjectRoot,
25
25
  loadConfig,
26
- parsePort,
27
- resolveDevelopmentHostUrl,
28
26
  resolveLocalDevelopmentPath,
29
27
  } from "./config";
30
28
  import {
@@ -43,7 +41,7 @@ import {
43
41
  type SyncOptions,
44
42
  type UpgradeOptions,
45
43
  } from "./contract";
46
- import { type AppConfig, type AppOrchestrator, startApp } from "./dev-session";
44
+ import { devApp, startApp } from "./dev-session";
47
45
  import {
48
46
  buildRegistryConfigUrlForNetwork,
49
47
  fetchBosConfigFromFastKv,
@@ -58,6 +56,11 @@ import { computeSriHashForUrl } from "./integrity";
58
56
  import { addFunctionCallAccessKey, ensureNearCli, executeTransaction } from "./near-cli";
59
57
  import { getNetworkIdForAccount } from "./network";
60
58
  import { createPlugin, z } from "./sdk";
59
+ import {
60
+ type AppOrchestrator,
61
+ buildDescription,
62
+ buildServiceDescriptorMap,
63
+ } from "./service-descriptor";
61
64
  import { syncAndGenerateSharedUi } from "./shared";
62
65
  import type { BosConfig, RuntimeConfig, SourceMode } from "./types";
63
66
  import { run } from "./utils/run";
@@ -91,13 +94,6 @@ function ensureEnvFile(configDir: string): void {
91
94
  console.log(`[CLI] Created .env from .env.example with generated BETTER_AUTH_SECRET`);
92
95
  }
93
96
 
94
- const DEFAULT_DEV_CONFIG: AppConfig = {
95
- host: "local",
96
- ui: "local",
97
- api: "local",
98
- ssr: false,
99
- };
100
-
101
97
  const buildCommands: Record<string, { cmd: string; args: string[] }> = {
102
98
  host: { cmd: "bun", args: ["run", "build"] },
103
99
  ui: { cmd: "bun", args: ["run", "build"] },
@@ -119,35 +115,6 @@ function parseSourceMode(value: string | undefined, defaultValue: SourceMode): S
119
115
  return defaultValue;
120
116
  }
121
117
 
122
- function buildAppConfig(options: {
123
- host?: string;
124
- ui?: string;
125
- api?: string;
126
- proxy?: boolean;
127
- ssr?: boolean;
128
- }): AppConfig {
129
- return {
130
- host: parseSourceMode(options.host, DEFAULT_DEV_CONFIG.host),
131
- ui: parseSourceMode(options.ui, DEFAULT_DEV_CONFIG.ui),
132
- api: parseSourceMode(options.api, DEFAULT_DEV_CONFIG.api),
133
- proxy: options.proxy,
134
- ssr: options.ssr ?? DEFAULT_DEV_CONFIG.ssr,
135
- };
136
- }
137
-
138
- function buildDescription(config: AppConfig): string {
139
- if (config.host === "local" && config.ui === "local" && config.api === "local" && !config.proxy) {
140
- return "Full Local Development";
141
- }
142
-
143
- const parts: string[] = [];
144
- parts.push(config.host === "remote" ? "Remote Host" : "Local Host");
145
- if (config.ui === "remote") parts.push("Remote UI");
146
- if (config.proxy) parts.push("Proxy API → Production");
147
- else if (config.api === "remote") parts.push("Remote API");
148
- return parts.join(" + ");
149
- }
150
-
151
118
  function buildConfigResult(bosConfig: BosConfig | null): BosConfigResult {
152
119
  const packages = bosConfig ? Object.keys(bosConfig.app) : [];
153
120
  const remotes = packages.filter((name) => name !== "host");
@@ -194,30 +161,6 @@ function resolveWorkspaceTarget(
194
161
  return null;
195
162
  }
196
163
 
197
- function determineProcesses(
198
- config: AppConfig,
199
- localPackages: string[],
200
- runtimeConfig?: RuntimeConfig | null,
201
- ): string[] {
202
- const processes: string[] = [];
203
- if (config.ssr && config.ui === "local") processes.push("ui-ssr");
204
- if (config.ui === "local") processes.push("ui");
205
- if (localPackages.includes("auth") && runtimeConfig?.auth?.source === "local") {
206
- processes.push("auth");
207
- }
208
- if (config.api === "local" && !config.proxy) processes.push("api");
209
- for (const pkg of localPackages) {
210
- if (pkg.startsWith("plugin:")) {
211
- const pluginId = pkg.slice("plugin:".length);
212
- if (runtimeConfig?.plugins?.[pluginId]?.source === "local") {
213
- processes.push(pkg);
214
- }
215
- }
216
- }
217
- processes.push("host");
218
- return processes;
219
- }
220
-
221
164
  function isValidProxyUrl(url: string): boolean {
222
165
  try {
223
166
  const parsed = new URL(url);
@@ -318,39 +261,6 @@ function extractPublishedUrl(output: string): string | null {
318
261
  return match[match.length - 1] ?? null;
319
262
  }
320
263
 
321
- async function buildEnvVars(
322
- config: AppConfig,
323
- bosConfig?: BosConfig | null,
324
- ): Promise<Record<string, string>> {
325
- const env: Record<string, string> = {
326
- HOST_SOURCE: config.host,
327
- UI_SOURCE: config.ui,
328
- API_SOURCE: config.api,
329
- };
330
-
331
- if (config.host === "remote") {
332
- const remoteUrl = bosConfig?.app.host.production;
333
- if (remoteUrl) env.HOST_REMOTE_URL = remoteUrl;
334
- }
335
-
336
- if (config.ui === "remote") {
337
- const remoteUrl = bosConfig?.app.ui.production;
338
- if (remoteUrl) env.UI_REMOTE_URL = remoteUrl;
339
- }
340
-
341
- if (config.api === "remote") {
342
- const remoteUrl = bosConfig?.app.api.production;
343
- if (remoteUrl) env.API_REMOTE_URL = remoteUrl;
344
- }
345
-
346
- if (config.proxy && bosConfig) {
347
- const proxyUrl = resolveProxyUrl(bosConfig);
348
- if (proxyUrl) env.API_PROXY = proxyUrl;
349
- }
350
-
351
- return env;
352
- }
353
-
354
264
  async function buildEveryPluginQuietly(cwd: string) {
355
265
  const packageDir = `${cwd}/packages/every-plugin`;
356
266
  const packageExists = await Bun.file(`${packageDir}/package.json`).exists();
@@ -876,24 +786,31 @@ export default createPlugin({
876
786
  deps.runtimeConfig ?? undefined,
877
787
  );
878
788
 
879
- const appConfig = buildAppConfig({
880
- host: localPackages.includes("host") ? (input.host as string) : "remote",
881
- ui: localPackages.includes("ui") ? (input.ui as string) : "remote",
882
- api: localPackages.includes("api") ? (input.api as string) : "remote",
883
- proxy: input.proxy,
884
- ssr: input.ssr,
885
- });
789
+ const hostSource: SourceMode = localPackages.includes("host")
790
+ ? parseSourceMode(input.host as string, "local")
791
+ : "remote";
792
+ const uiSource: SourceMode = localPackages.includes("ui")
793
+ ? parseSourceMode(input.ui as string, "local")
794
+ : "remote";
795
+ const apiSource: SourceMode = localPackages.includes("api")
796
+ ? parseSourceMode(input.api as string, "local")
797
+ : "remote";
798
+ const authSource: SourceMode = localPackages.includes("auth")
799
+ ? parseSourceMode(input.auth as string, "local")
800
+ : "remote";
801
+ const ssr = input.ssr ?? false;
802
+ const proxy = input.proxy ?? false;
886
803
 
887
804
  const sharedSync = await syncAndGenerateSharedUi({
888
805
  configDir: deps.configDir,
889
- hostMode: appConfig.host,
806
+ hostMode: hostSource,
890
807
  bosConfig: deps.bosConfig ?? undefined,
891
808
  });
892
809
  if (sharedSync.catalogChanged) {
893
810
  await run("bun", ["install"], { cwd: deps.configDir });
894
811
  }
895
812
  if (
896
- (appConfig.api === "local" && !appConfig.proxy) ||
813
+ (apiSource === "local" && !proxy) ||
897
814
  localPackages.some((pkg) => pkg.startsWith("plugin:"))
898
815
  ) {
899
816
  await buildEveryPluginQuietly(deps.configDir);
@@ -913,7 +830,7 @@ export default createPlugin({
913
830
  };
914
831
  }
915
832
 
916
- if (appConfig.proxy && !resolveProxyUrl(deps.bosConfig)) {
833
+ if (proxy && !resolveProxyUrl(deps.bosConfig)) {
917
834
  return {
918
835
  status: "error" as const,
919
836
  description: "No valid proxy URL configured in bos.config.json",
@@ -921,27 +838,29 @@ export default createPlugin({
921
838
  };
922
839
  }
923
840
 
924
- const refreshedLocalPackages = detectLocalPackages(
925
- deps.bosConfig ?? undefined,
926
- deps.runtimeConfig ?? undefined,
927
- );
928
- const processes = determineProcesses(appConfig, refreshedLocalPackages, deps.runtimeConfig);
929
- const env = await buildEnvVars(appConfig, deps.bosConfig);
930
841
  const hostPort = input.port ?? getHostDevelopmentPort(deps.bosConfig.app.host.development);
931
842
  const developmentRuntime = buildRuntimeConfig(deps.bosConfig, {
932
- uiSource: appConfig.ui,
933
- apiSource: appConfig.api,
934
- authSource: refreshedLocalPackages.includes("auth") ? "local" : "remote",
935
- hostUrl: `http://localhost:${hostPort}`,
936
- proxy: env.API_PROXY,
843
+ uiSource,
844
+ apiSource,
845
+ authSource,
846
+ hostSource,
937
847
  env: "development",
938
848
  plugins: deps.runtimeConfig?.plugins,
939
849
  });
940
850
  const runtimeConfig = await prepareDevelopmentRuntimeConfig(developmentRuntime, {
941
851
  hostPort,
942
- ssr: appConfig.ssr,
852
+ ssr,
943
853
  });
944
854
 
855
+ const services = buildServiceDescriptorMap(runtimeConfig, { ssr, proxy });
856
+ const packages = [...services.keys()];
857
+ const displayEnv: Record<string, string> = {};
858
+ const apiDescriptor = services.get("api");
859
+ if (apiDescriptor?.proxy) {
860
+ const proxyUrl = resolveProxyUrl(deps.bosConfig);
861
+ if (proxyUrl) displayEnv.API_PROXY = proxyUrl;
862
+ }
863
+
945
864
  await syncApiContractBridge({
946
865
  configDir: deps.configDir,
947
866
  runtimeConfig: runtimeConfig,
@@ -949,22 +868,19 @@ export default createPlugin({
949
868
  });
950
869
 
951
870
  const orchestrator: AppOrchestrator = {
952
- packages: processes,
953
- env,
954
- description: buildDescription(appConfig),
955
- appConfig,
956
- bosConfig: deps.bosConfig,
957
- runtimeConfig,
958
- port: parsePort(runtimeConfig.hostUrl),
871
+ packages,
872
+ env: displayEnv,
873
+ description: buildDescription(services),
874
+ port: runtimeConfig.host.port,
959
875
  interactive: input.interactive,
960
876
  };
961
877
 
962
- startApp(orchestrator);
878
+ devApp(orchestrator, services, runtimeConfig);
963
879
 
964
880
  return {
965
881
  status: "started" as const,
966
882
  description: orchestrator.description,
967
- processes,
883
+ processes: packages,
968
884
  };
969
885
  }),
970
886
 
@@ -992,8 +908,6 @@ export default createPlugin({
992
908
  }
993
909
 
994
910
  const port = input.port ?? getHostDevelopmentPort(config.app.host.development);
995
- const appConfig: AppConfig = { host: "remote", ui: "remote", api: "remote" };
996
- const env = await buildEnvVars(appConfig, config);
997
911
  const isStaging = input.env === "staging";
998
912
  const runtimePlugins = remoteConfig
999
913
  ? await buildRuntimePluginsForConfig(config, deps.configDir, "production")
@@ -1002,11 +916,13 @@ export default createPlugin({
1002
916
  uiSource: "remote",
1003
917
  apiSource: "remote",
1004
918
  authSource: "remote",
1005
- hostUrl: `http://localhost:${port}`,
919
+ hostSource: "remote",
1006
920
  env: "production",
1007
921
  plugins: runtimePlugins,
1008
922
  });
1009
923
 
924
+ const services = buildServiceDescriptorMap(runtimeConfig);
925
+
1010
926
  await syncApiContractBridge({
1011
927
  configDir: deps.configDir,
1012
928
  runtimeConfig: runtimeConfig,
@@ -1021,19 +937,15 @@ export default createPlugin({
1021
937
  packages: ["host"],
1022
938
  env: {
1023
939
  NODE_ENV: "production",
1024
- ...env,
1025
940
  ...stagingEnvVars,
1026
941
  },
1027
942
  description: `${isStaging ? "Staging" : "Production"} Mode (${config.account})`,
1028
- appConfig,
1029
- bosConfig: config,
1030
- runtimeConfig,
1031
943
  port,
1032
944
  interactive: input.interactive,
1033
945
  noLogs: true,
1034
946
  };
1035
947
 
1036
- startApp(orchestrator);
948
+ startApp(orchestrator, services, runtimeConfig);
1037
949
  return {
1038
950
  status: "running" as const,
1039
951
  url: `http://localhost:${port}`,
@@ -1062,7 +974,7 @@ export default createPlugin({
1062
974
  uiSource: deps.bosConfig.app.ui?.development ? "local" : "remote",
1063
975
  apiSource: deps.bosConfig.app.api?.development ? "local" : "remote",
1064
976
  authSource: deps.bosConfig.app.auth?.development ? "local" : "remote",
1065
- hostUrl: resolveDevelopmentHostUrl(deps.bosConfig.app.host.development),
977
+ hostSource: deps.bosConfig.app.host?.development ? "local" : "remote",
1066
978
  env: "development",
1067
979
  plugins: deps.runtimeConfig?.plugins,
1068
980
  });
@@ -0,0 +1,258 @@
1
+ import { Context, Layer } from "effect";
2
+ import type { RuntimeConfig, SourceMode } from "./types";
3
+
4
+ export interface ServiceDescriptor {
5
+ key: string;
6
+ source: SourceMode;
7
+ url: string;
8
+ remoteUrl?: string;
9
+ entry: string;
10
+ name: string;
11
+ localPath?: string;
12
+ port?: number;
13
+ readinessPath: string;
14
+ defaultPort: number;
15
+ integrity?: string;
16
+ proxy?: string;
17
+ variables?: Record<string, string>;
18
+ secrets?: string[];
19
+ ssr?: boolean;
20
+ command?: string;
21
+ args?: string[];
22
+ readyPatterns?: RegExp[];
23
+ errorPatterns?: RegExp[];
24
+ }
25
+
26
+ export class ServiceDescriptorMap extends Context.Tag("ServiceDescriptorMap")<
27
+ ServiceDescriptorMap,
28
+ Map<string, ServiceDescriptor>
29
+ >() {}
30
+
31
+ export class DevRuntimeConfig extends Context.Tag("DevRuntimeConfig")<
32
+ DevRuntimeConfig,
33
+ RuntimeConfig
34
+ >() {}
35
+
36
+ const PLUGIN_READY_PATTERNS = [/ready in/i, /compiled.*successfully/i, /listening/i, /started/i];
37
+
38
+ const PLUGIN_ERROR_PATTERNS = [/error/i, /failed/i];
39
+
40
+ const SERVICE_CONFIGS: Record<
41
+ string,
42
+ Pick<
43
+ ServiceDescriptor,
44
+ "command" | "args" | "readyPatterns" | "errorPatterns" | "defaultPort" | "readinessPath"
45
+ >
46
+ > = {
47
+ host: {
48
+ command: "bun",
49
+ args: ["run", "dev"],
50
+ readyPatterns: [/Host (dev|production) server running at/i, /Server running at/i],
51
+ errorPatterns: [/error:/i, /failed/i, /exception/i],
52
+ defaultPort: 3000,
53
+ readinessPath: "/health",
54
+ },
55
+ auth: {
56
+ command: "bun",
57
+ args: ["run", "dev"],
58
+ readyPatterns: PLUGIN_READY_PATTERNS,
59
+ errorPatterns: PLUGIN_ERROR_PATTERNS,
60
+ defaultPort: 3002,
61
+ readinessPath: "/remoteEntry.js",
62
+ },
63
+ ui: {
64
+ command: "bun",
65
+ args: ["run", "dev"],
66
+ readyPatterns: [/\bready\s+built in\b/i, /\bLocal:\b/i, /\bcompiled\b.*successfully/i],
67
+ errorPatterns: [/error/i, /failed to compile/i],
68
+ defaultPort: 3003,
69
+ readinessPath: "/remoteEntry.js",
70
+ },
71
+ "ui-ssr": {
72
+ command: "bun",
73
+ args: ["run", "dev:ssr"],
74
+ readyPatterns: [/\bready\s+built in\b/i, /\bcompiled\b.*successfully/i],
75
+ errorPatterns: [/error/i, /failed/i],
76
+ defaultPort: 3004,
77
+ readinessPath: "/",
78
+ },
79
+ api: {
80
+ command: "bun",
81
+ args: ["run", "dev"],
82
+ readyPatterns: PLUGIN_READY_PATTERNS,
83
+ errorPatterns: PLUGIN_ERROR_PATTERNS,
84
+ defaultPort: 3001,
85
+ readinessPath: "/remoteEntry.js",
86
+ },
87
+ };
88
+
89
+ export function buildServiceDescriptorMap(
90
+ runtimeConfig: RuntimeConfig,
91
+ options?: { ssr?: boolean; proxy?: boolean },
92
+ ): Map<string, ServiceDescriptor> {
93
+ const map = new Map<string, ServiceDescriptor>();
94
+ const ssr = options?.ssr ?? false;
95
+
96
+ const hostIsRemote = runtimeConfig.host.source === "remote";
97
+ const hostProbeUrl = hostIsRemote
98
+ ? (runtimeConfig.host.remoteUrl ?? runtimeConfig.host.url)
99
+ : runtimeConfig.host.url;
100
+ map.set("host", {
101
+ key: "host",
102
+ source: runtimeConfig.host.source,
103
+ url: hostProbeUrl,
104
+ remoteUrl: runtimeConfig.host.remoteUrl,
105
+ entry: hostIsRemote
106
+ ? hostProbeUrl
107
+ ? `${hostProbeUrl}/mf-manifest.json`
108
+ : "/mf-manifest.json"
109
+ : runtimeConfig.host.entry,
110
+ name: runtimeConfig.host.name,
111
+ localPath: runtimeConfig.host.localPath,
112
+ port: runtimeConfig.host.port,
113
+ integrity: runtimeConfig.host.integrity,
114
+ secrets: runtimeConfig.host.secrets,
115
+ ...SERVICE_CONFIGS.host,
116
+ });
117
+
118
+ map.set("ui", {
119
+ key: "ui",
120
+ source: runtimeConfig.ui.source,
121
+ url: runtimeConfig.ui.url,
122
+ remoteUrl: runtimeConfig.ui.source === "remote" ? runtimeConfig.ui.url : undefined,
123
+ entry: runtimeConfig.ui.entry,
124
+ name: runtimeConfig.ui.name,
125
+ localPath: runtimeConfig.ui.localPath,
126
+ port: runtimeConfig.ui.port,
127
+ integrity: runtimeConfig.ui.integrity,
128
+ ssr,
129
+ ...SERVICE_CONFIGS.ui,
130
+ });
131
+
132
+ if (ssr && runtimeConfig.ui.source === "local") {
133
+ map.set("ui-ssr", {
134
+ key: "ui-ssr",
135
+ source: runtimeConfig.ui.source,
136
+ url: runtimeConfig.ui.ssrUrl ?? "",
137
+ entry: "",
138
+ name: "ui-ssr",
139
+ localPath: runtimeConfig.ui.localPath,
140
+ port: runtimeConfig.ui.ssrUrl
141
+ ? Number.parseInt(new URL(runtimeConfig.ui.ssrUrl).port, 10)
142
+ : (runtimeConfig.ui.port ?? 3003) + 1,
143
+ ...SERVICE_CONFIGS["ui-ssr"],
144
+ });
145
+ }
146
+
147
+ map.set("api", {
148
+ key: "api",
149
+ source: runtimeConfig.api.source,
150
+ url: runtimeConfig.api.url,
151
+ remoteUrl: runtimeConfig.api.source === "remote" ? runtimeConfig.api.url : undefined,
152
+ entry: runtimeConfig.api.entry,
153
+ name: runtimeConfig.api.name,
154
+ localPath: runtimeConfig.api.localPath,
155
+ port: runtimeConfig.api.port,
156
+ integrity: runtimeConfig.api.integrity,
157
+ proxy: runtimeConfig.api.proxy,
158
+ variables: runtimeConfig.api.variables,
159
+ secrets: runtimeConfig.api.secrets,
160
+ ...SERVICE_CONFIGS.api,
161
+ });
162
+
163
+ if (runtimeConfig.auth) {
164
+ map.set("auth", {
165
+ key: "auth",
166
+ source: runtimeConfig.auth.source,
167
+ url: runtimeConfig.auth.url,
168
+ remoteUrl: runtimeConfig.auth.source === "remote" ? runtimeConfig.auth.url : undefined,
169
+ entry: runtimeConfig.auth.entry,
170
+ name: runtimeConfig.auth.name,
171
+ localPath: runtimeConfig.auth.localPath,
172
+ port: runtimeConfig.auth.port,
173
+ integrity: runtimeConfig.auth.integrity,
174
+ proxy: runtimeConfig.auth.proxy,
175
+ variables: runtimeConfig.auth.variables,
176
+ secrets: runtimeConfig.auth.secrets,
177
+ ...SERVICE_CONFIGS.auth,
178
+ });
179
+ }
180
+
181
+ if (runtimeConfig.plugins) {
182
+ let pluginBasePort = 3010;
183
+ for (const [pluginId, pluginConfig] of Object.entries(runtimeConfig.plugins)) {
184
+ const pluginKey = `plugin:${pluginId}`;
185
+ const resolvedPort = pluginConfig.port ?? pluginBasePort;
186
+ pluginBasePort = resolvedPort + 1;
187
+
188
+ map.set(pluginKey, {
189
+ key: pluginKey,
190
+ source: pluginConfig.source,
191
+ url: pluginConfig.url,
192
+ remoteUrl: pluginConfig.source === "remote" ? pluginConfig.url : undefined,
193
+ entry: pluginConfig.entry,
194
+ name: pluginConfig.name,
195
+ localPath: pluginConfig.localPath,
196
+ port: resolvedPort,
197
+ integrity: pluginConfig.integrity,
198
+ proxy: pluginConfig.proxy,
199
+ variables: pluginConfig.variables,
200
+ secrets: pluginConfig.secrets,
201
+ command: "bun",
202
+ args: ["run", "dev"],
203
+ readyPatterns: PLUGIN_READY_PATTERNS,
204
+ errorPatterns: PLUGIN_ERROR_PATTERNS,
205
+ defaultPort: resolvedPort,
206
+ readinessPath: "/remoteEntry.js",
207
+ });
208
+ }
209
+ }
210
+
211
+ return map;
212
+ }
213
+
214
+ export interface AppOrchestrator {
215
+ packages: string[];
216
+ description: string;
217
+ env: Record<string, string>;
218
+ port?: number;
219
+ interactive?: boolean;
220
+ noLogs?: boolean;
221
+ }
222
+
223
+ export const ServiceDescriptorMapLive = (map: Map<string, ServiceDescriptor>) =>
224
+ Layer.succeed(ServiceDescriptorMap, map);
225
+
226
+ export const DevRuntimeConfigLive = (config: RuntimeConfig) =>
227
+ Layer.succeed(DevRuntimeConfig, config);
228
+
229
+ export function buildDescription(map: Map<string, ServiceDescriptor>): string {
230
+ const descriptors = [...map.values()].filter(
231
+ (d) => d.key !== "ui-ssr" && !d.key.startsWith("plugin:"),
232
+ );
233
+
234
+ const allLocal = descriptors.every((d) => d.source === "local");
235
+ const hasProxy = [...map.values()].some((d) => d.proxy && d.source === "local");
236
+ if (allLocal && !hasProxy) return "Full Local Development";
237
+
238
+ const parts: string[] = [];
239
+ for (const d of descriptors) {
240
+ if (d.source === "remote") {
241
+ const label =
242
+ d.key === "host"
243
+ ? "Remote Host"
244
+ : d.key === "ui"
245
+ ? "Remote UI"
246
+ : d.key === "api"
247
+ ? hasProxy
248
+ ? undefined
249
+ : "Remote API"
250
+ : d.key === "auth"
251
+ ? "Remote Auth"
252
+ : undefined;
253
+ if (label) parts.push(label);
254
+ }
255
+ }
256
+ if (hasProxy) parts.push("Proxy API → Production");
257
+ return parts.join(" + ") || "Remote Mode";
258
+ }
package/src/types.ts CHANGED
@@ -144,7 +144,12 @@ export const RuntimeConfigSchema = z.object({
144
144
  networkId: z.enum(["mainnet", "testnet"]),
145
145
  title: z.string().optional(),
146
146
  repository: z.string().optional(),
147
- hostUrl: z.string(),
147
+ host: FederationEntrySchema.extend({
148
+ localPath: z.string().optional(),
149
+ port: z.number().optional(),
150
+ secrets: z.array(z.string()).optional(),
151
+ remoteUrl: z.string().optional(),
152
+ }),
148
153
  shared: z
149
154
  .object({
150
155
  ui: z.record(z.string(), SharedConfigSchema).optional(),
@@ -175,6 +180,7 @@ export const RuntimeConfigSchema = z.object({
175
180
  export type RuntimeConfig = z.infer<typeof RuntimeConfigSchema>;
176
181
 
177
182
  export const ClientRuntimeConfigSchema = z.object({
183
+ cspNonce: z.string().optional(),
178
184
  env: z.enum(["development", "production"]),
179
185
  account: z.string(),
180
186
  networkId: z.enum(["mainnet", "testnet"]),
@@ -183,6 +189,7 @@ export const ClientRuntimeConfigSchema = z.object({
183
189
  apiBase: z.string(),
184
190
  rpcBase: z.string(),
185
191
  repository: z.string().optional(),
192
+ authAvailable: z.boolean().optional(),
186
193
  runtime: ClientRuntimeInfoSchema.optional(),
187
194
  ui: z
188
195
  .object({
package/src/ui/runtime.ts CHANGED
@@ -82,3 +82,8 @@ export function getRepository(config?: Partial<ClientRuntimeConfig>): string | u
82
82
  const cfg = config ?? getRuntimeConfig();
83
83
  return cfg?.repository;
84
84
  }
85
+
86
+ export function getCspNonce(config?: Partial<ClientRuntimeConfig>): string | undefined {
87
+ const cfg = config ?? getRuntimeConfig();
88
+ return cfg?.cspNonce;
89
+ }