everything-dev 1.12.3 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +1 -1
- package/dist/app.cjs +24 -101
- package/dist/app.cjs.map +1 -1
- package/dist/app.mjs +25 -102
- package/dist/app.mjs.map +1 -1
- package/dist/cli/init.cjs +143 -66
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +1 -1
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +1 -1
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +144 -67
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/prompts.cjs +3 -3
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +3 -3
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/sync.cjs +15 -56
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +15 -56
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli/upgrade.cjs +3 -1
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs +3 -1
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/config.cjs +223 -81
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +21 -5
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +21 -5
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +217 -83
- package/dist/config.mjs.map +1 -1
- package/dist/contract.d.cts +104 -8
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +104 -8
- package/dist/contract.d.mts.map +1 -1
- package/dist/host.cjs +34 -1
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts.map +1 -1
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +34 -1
- package/dist/host.mjs.map +1 -1
- package/dist/index.cjs +17 -0
- package/dist/index.d.cts +5 -3
- package/dist/index.d.mts +5 -3
- package/dist/index.mjs +5 -3
- package/dist/merge.cjs +113 -0
- package/dist/merge.cjs.map +1 -0
- package/dist/merge.d.cts +7 -0
- package/dist/merge.d.cts.map +1 -0
- package/dist/merge.d.mts +7 -0
- package/dist/merge.d.mts.map +1 -0
- package/dist/merge.mjs +107 -0
- package/dist/merge.mjs.map +1 -0
- package/dist/plugin.cjs +117 -105
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +114 -8
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +114 -8
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +117 -105
- package/dist/plugin.mjs.map +1 -1
- package/dist/service-descriptor.cjs +21 -0
- package/dist/service-descriptor.cjs.map +1 -1
- package/dist/service-descriptor.d.cts +23 -1
- package/dist/service-descriptor.d.cts.map +1 -1
- package/dist/service-descriptor.d.mts +23 -1
- package/dist/service-descriptor.d.mts.map +1 -1
- package/dist/service-descriptor.mjs +21 -0
- package/dist/service-descriptor.mjs.map +1 -1
- package/dist/shared.cjs +24 -2
- package/dist/shared.cjs.map +1 -1
- package/dist/shared.d.cts +3 -0
- package/dist/shared.d.cts.map +1 -1
- package/dist/shared.d.mts +3 -0
- package/dist/shared.d.mts.map +1 -1
- package/dist/shared.mjs +25 -3
- package/dist/shared.mjs.map +1 -1
- package/dist/sidebar.cjs +124 -0
- package/dist/sidebar.cjs.map +1 -0
- package/dist/sidebar.d.cts +8 -0
- package/dist/sidebar.d.cts.map +1 -0
- package/dist/sidebar.d.mts +8 -0
- package/dist/sidebar.d.mts.map +1 -0
- package/dist/sidebar.mjs +122 -0
- package/dist/sidebar.mjs.map +1 -0
- package/dist/types.cjs +104 -10
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +256 -29
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +256 -29
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +100 -11
- package/dist/types.mjs.map +1 -1
- package/dist/utils/path-match.cjs +18 -0
- package/dist/utils/path-match.cjs.map +1 -0
- package/dist/utils/path-match.mjs +17 -0
- package/dist/utils/path-match.mjs.map +1 -0
- package/dist/utils/save-config.cjs +19 -0
- package/dist/utils/save-config.cjs.map +1 -0
- package/dist/utils/save-config.mjs +18 -0
- package/dist/utils/save-config.mjs.map +1 -0
- package/package.json +3 -2
- package/skills/dev-workflow/SKILL.md +8 -0
- package/skills/extends-config/SKILL.md +132 -0
- package/skills/init-upgrade/SKILL.md +128 -0
- package/skills/publish-sync/SKILL.md +30 -0
- package/src/app.ts +23 -118
- package/src/cli/init.ts +199 -100
- package/src/cli/prompts.ts +2 -2
- package/src/cli/sync.ts +27 -96
- package/src/cli/upgrade.ts +2 -0
- package/src/config.ts +356 -132
- package/src/host.ts +45 -0
- package/src/index.ts +1 -0
- package/src/merge.ts +198 -0
- package/src/plugin.ts +340 -318
- package/src/service-descriptor.ts +23 -0
- package/src/shared.ts +48 -5
- package/src/sidebar.ts +162 -0
- package/src/types.ts +134 -28
- package/src/utils/path-match.ts +16 -0
- package/src/utils/save-config.ts +20 -0
package/src/config.ts
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
3
|
-
import { fetchBosConfigFromFastKv } from "./fastkv";
|
|
3
|
+
import { fetchBosConfigFromFastKv, fetchPluginFromRegistry, parsePluginBosUrl } from "./fastkv";
|
|
4
|
+
import {
|
|
5
|
+
type BosEnv,
|
|
6
|
+
isPlainObject,
|
|
7
|
+
mergeBosConfigWithExtends,
|
|
8
|
+
type ResolvedConfigMeta,
|
|
9
|
+
rebuildOrderedConfig,
|
|
10
|
+
resolveExtendsRef,
|
|
11
|
+
} from "./merge";
|
|
4
12
|
import { getNetworkIdForAccount } from "./network";
|
|
5
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
BosConfig,
|
|
15
|
+
BosConfigInput,
|
|
16
|
+
BosPluginRef,
|
|
17
|
+
ExtendsConfig,
|
|
18
|
+
PluginEntryValue,
|
|
19
|
+
RuntimeConfig,
|
|
20
|
+
RuntimePluginConfig,
|
|
21
|
+
SharedDepConfig,
|
|
22
|
+
} from "./types";
|
|
6
23
|
import { BosConfigSchema } from "./types";
|
|
7
24
|
|
|
8
25
|
const LOCAL_PREFIX = "local:";
|
|
9
26
|
const DEFAULT_HOST_PORT = 3000;
|
|
27
|
+
const RESOLVED_CONFIG_FILENAME = "bos.resolved-config.json";
|
|
10
28
|
|
|
11
29
|
interface RuntimeTarget {
|
|
12
30
|
source: "local" | "remote";
|
|
@@ -59,7 +77,7 @@ export interface ConfigResult {
|
|
|
59
77
|
export async function loadConfig(options?: {
|
|
60
78
|
cwd?: string;
|
|
61
79
|
path?: string;
|
|
62
|
-
env?:
|
|
80
|
+
env?: BosEnv;
|
|
63
81
|
}): Promise<ConfigResult | null> {
|
|
64
82
|
const configPath = options?.path ?? findConfigPath(options?.cwd);
|
|
65
83
|
if (!configPath) {
|
|
@@ -68,21 +86,25 @@ export async function loadConfig(options?: {
|
|
|
68
86
|
}
|
|
69
87
|
|
|
70
88
|
const baseDir = dirname(configPath);
|
|
89
|
+
const env = options?.env ?? "development";
|
|
90
|
+
const runtimeEnv: BosEnv = env === "staging" ? "production" : env;
|
|
71
91
|
|
|
72
92
|
try {
|
|
73
93
|
const extendedChain: string[] = [];
|
|
74
|
-
const parsed = await resolveConfigWithExtends(
|
|
94
|
+
const parsed = await resolveConfigWithExtends(
|
|
95
|
+
configPath,
|
|
96
|
+
baseDir,
|
|
97
|
+
new Set(),
|
|
98
|
+
extendedChain,
|
|
99
|
+
env,
|
|
100
|
+
);
|
|
75
101
|
const config = BosConfigSchema.parse(parsed);
|
|
76
102
|
|
|
77
103
|
cachedConfig = config;
|
|
78
104
|
projectRoot = baseDir;
|
|
79
105
|
|
|
80
|
-
const pluginRuntime = await resolveRuntimePlugins(
|
|
81
|
-
|
|
82
|
-
baseDir,
|
|
83
|
-
options?.env ?? "development",
|
|
84
|
-
);
|
|
85
|
-
const runtime = buildRuntimeConfig(config, baseDir, options?.env ?? "development", {
|
|
106
|
+
const pluginRuntime = await resolveRuntimePlugins(config.plugins ?? {}, baseDir, runtimeEnv);
|
|
107
|
+
const runtime = buildRuntimeConfig(config, baseDir, runtimeEnv, {
|
|
86
108
|
plugins: pluginRuntime,
|
|
87
109
|
});
|
|
88
110
|
|
|
@@ -103,7 +125,7 @@ export async function loadConfig(options?: {
|
|
|
103
125
|
export async function loadBosConfig(options?: {
|
|
104
126
|
cwd?: string;
|
|
105
127
|
path?: string;
|
|
106
|
-
env?:
|
|
128
|
+
env?: BosEnv;
|
|
107
129
|
}): Promise<RuntimeConfig> {
|
|
108
130
|
const result = await loadConfig(options);
|
|
109
131
|
if (!result) {
|
|
@@ -116,17 +138,93 @@ export async function loadBosConfig(options?: {
|
|
|
116
138
|
export async function buildRuntimePluginsForConfig(
|
|
117
139
|
config: BosConfig,
|
|
118
140
|
baseDir: string,
|
|
119
|
-
env:
|
|
141
|
+
env: BosEnv,
|
|
120
142
|
): Promise<Record<string, RuntimePluginConfig> | undefined> {
|
|
121
143
|
const plugins = await resolveRuntimePlugins(config.plugins ?? {}, baseDir, env);
|
|
122
144
|
return Object.keys(plugins).length > 0 ? plugins : undefined;
|
|
123
145
|
}
|
|
124
146
|
|
|
147
|
+
export function getResolvedConfigPath(configDir: string): string {
|
|
148
|
+
return join(configDir, ".bos", RESOLVED_CONFIG_FILENAME);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function loadResolvedConfig(configDir: string): BosConfig | null {
|
|
152
|
+
const resolvedPath = getResolvedConfigPath(configDir);
|
|
153
|
+
if (!existsSync(resolvedPath)) return null;
|
|
154
|
+
try {
|
|
155
|
+
const raw = JSON.parse(readFileSync(resolvedPath, "utf-8"));
|
|
156
|
+
if (!isPlainObject(raw)) return null;
|
|
157
|
+
const { _resolved, ...configData } = raw;
|
|
158
|
+
return BosConfigSchema.parse(configData);
|
|
159
|
+
} catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export function writeResolvedConfig(
|
|
165
|
+
configDir: string,
|
|
166
|
+
config: BosConfig,
|
|
167
|
+
env: BosEnv,
|
|
168
|
+
extendsChain?: string[],
|
|
169
|
+
source?: string,
|
|
170
|
+
): void {
|
|
171
|
+
const resolvedPath = getResolvedConfigPath(configDir);
|
|
172
|
+
const resolvedDir = dirname(resolvedPath);
|
|
173
|
+
if (!existsSync(resolvedDir)) {
|
|
174
|
+
mkdirSync(resolvedDir, { recursive: true });
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const ordered = rebuildOrderedConfig(config);
|
|
178
|
+
const meta: ResolvedConfigMeta = {
|
|
179
|
+
env,
|
|
180
|
+
resolvedAt: new Date().toISOString(),
|
|
181
|
+
extendsChain: extendsChain ?? [],
|
|
182
|
+
...(source ? { source } : {}),
|
|
183
|
+
};
|
|
184
|
+
const output = {
|
|
185
|
+
_resolved: meta,
|
|
186
|
+
...ordered,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const content = `${JSON.stringify(output, null, 2)}\n`;
|
|
190
|
+
try {
|
|
191
|
+
if (readFileSync(resolvedPath, "utf-8") === content) return;
|
|
192
|
+
} catch {
|
|
193
|
+
// file doesn't exist yet
|
|
194
|
+
}
|
|
195
|
+
writeFileSync(resolvedPath, content);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function resolveBosConfigPath(configDir: string): string {
|
|
199
|
+
const resolvedPath = getResolvedConfigPath(configDir);
|
|
200
|
+
if (existsSync(resolvedPath)) return resolvedPath;
|
|
201
|
+
return join(configDir, "bos.config.json");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function readBosConfigForBuild(configDir: string): Record<string, unknown> {
|
|
205
|
+
const resolvedPath = getResolvedConfigPath(configDir);
|
|
206
|
+
if (existsSync(resolvedPath)) {
|
|
207
|
+
try {
|
|
208
|
+
const raw = JSON.parse(readFileSync(resolvedPath, "utf-8"));
|
|
209
|
+
if (isPlainObject(raw)) {
|
|
210
|
+
const { _resolved, ...configData } = raw;
|
|
211
|
+
return configData as Record<string, unknown>;
|
|
212
|
+
}
|
|
213
|
+
} catch {}
|
|
214
|
+
}
|
|
215
|
+
const bosConfigPath = join(configDir, "bos.config.json");
|
|
216
|
+
return JSON.parse(readFileSync(bosConfigPath, "utf-8")) as Record<string, unknown>;
|
|
217
|
+
}
|
|
218
|
+
|
|
125
219
|
function resolveDevelopmentTarget(
|
|
126
220
|
development: string | undefined,
|
|
127
221
|
production: string | undefined,
|
|
128
222
|
baseDir: string,
|
|
223
|
+
forceSource?: "local" | "remote",
|
|
129
224
|
): RuntimeTarget {
|
|
225
|
+
if (forceSource === "remote") {
|
|
226
|
+
return resolveRuntimeTarget(production, baseDir, "remote");
|
|
227
|
+
}
|
|
130
228
|
const devTarget = resolveRuntimeTarget(development, baseDir);
|
|
131
229
|
if (devTarget.source === "local" && (!devTarget.localPath || !existsSync(devTarget.localPath))) {
|
|
132
230
|
return resolveRuntimeTarget(production, baseDir, "remote");
|
|
@@ -134,36 +232,72 @@ function resolveDevelopmentTarget(
|
|
|
134
232
|
return devTarget;
|
|
135
233
|
}
|
|
136
234
|
|
|
137
|
-
|
|
235
|
+
export interface BuildRuntimeConfigOptions {
|
|
236
|
+
plugins?: Record<string, RuntimePluginConfig>;
|
|
237
|
+
hostSource?: "local" | "remote";
|
|
238
|
+
uiSource?: "local" | "remote";
|
|
239
|
+
apiSource?: "local" | "remote";
|
|
240
|
+
authSource?: "local" | "remote";
|
|
241
|
+
proxy?: string;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export function buildRuntimeConfig(
|
|
138
245
|
config: BosConfig,
|
|
139
246
|
baseDir: string,
|
|
140
|
-
env:
|
|
141
|
-
options?:
|
|
247
|
+
env: BosEnv,
|
|
248
|
+
options?: BuildRuntimeConfigOptions,
|
|
142
249
|
): RuntimeConfig {
|
|
143
250
|
const uiConfig = config.app.ui;
|
|
144
251
|
const apiConfig = config.app.api;
|
|
145
252
|
const authConfig = config.app.auth;
|
|
146
253
|
const uiRuntime =
|
|
147
254
|
env === "development"
|
|
148
|
-
? resolveDevelopmentTarget(
|
|
255
|
+
? resolveDevelopmentTarget(
|
|
256
|
+
uiConfig.development,
|
|
257
|
+
uiConfig.production,
|
|
258
|
+
baseDir,
|
|
259
|
+
options?.uiSource,
|
|
260
|
+
)
|
|
149
261
|
: resolveRuntimeTarget(uiConfig.production, baseDir, "remote");
|
|
150
262
|
const apiRuntime =
|
|
151
263
|
env === "development"
|
|
152
|
-
? resolveDevelopmentTarget(
|
|
264
|
+
? resolveDevelopmentTarget(
|
|
265
|
+
apiConfig.development,
|
|
266
|
+
apiConfig.production,
|
|
267
|
+
baseDir,
|
|
268
|
+
options?.apiSource,
|
|
269
|
+
)
|
|
153
270
|
: resolveRuntimeTarget(apiConfig.production, baseDir, "remote");
|
|
154
271
|
const authRuntime = authConfig
|
|
155
272
|
? env === "development"
|
|
156
|
-
? resolveDevelopmentTarget(
|
|
273
|
+
? resolveDevelopmentTarget(
|
|
274
|
+
authConfig.development,
|
|
275
|
+
authConfig.production,
|
|
276
|
+
baseDir,
|
|
277
|
+
options?.authSource,
|
|
278
|
+
)
|
|
157
279
|
: resolveRuntimeTarget(authConfig.production, baseDir, "remote")
|
|
158
280
|
: undefined;
|
|
159
281
|
|
|
160
282
|
const hostConfig = config.app.host;
|
|
161
283
|
const hostRuntime =
|
|
162
284
|
env === "development"
|
|
163
|
-
? resolveDevelopmentTarget(
|
|
285
|
+
? resolveDevelopmentTarget(
|
|
286
|
+
hostConfig.development,
|
|
287
|
+
hostConfig.production,
|
|
288
|
+
baseDir,
|
|
289
|
+
options?.hostSource,
|
|
290
|
+
)
|
|
164
291
|
: resolveRuntimeTarget(hostConfig.production, baseDir, "remote");
|
|
165
292
|
|
|
166
|
-
const hostListeningUrl =
|
|
293
|
+
const hostListeningUrl =
|
|
294
|
+
env === "development"
|
|
295
|
+
? resolveDevelopmentHostUrl(hostConfig.development)
|
|
296
|
+
: `http://localhost:${hostRuntime.port ?? DEFAULT_HOST_PORT}`;
|
|
297
|
+
|
|
298
|
+
const hostIsRemote = hostRuntime.source === "remote";
|
|
299
|
+
const uiIsRemote = uiRuntime.source === "remote";
|
|
300
|
+
const apiIsRemote = apiRuntime.source === "remote";
|
|
167
301
|
|
|
168
302
|
return {
|
|
169
303
|
env,
|
|
@@ -178,9 +312,9 @@ function buildRuntimeConfig(
|
|
|
178
312
|
localPath: hostRuntime.localPath,
|
|
179
313
|
port: hostRuntime.port ?? DEFAULT_HOST_PORT,
|
|
180
314
|
secrets: hostConfig.secrets,
|
|
181
|
-
integrity:
|
|
315
|
+
integrity: hostIsRemote ? hostConfig.integrity : undefined,
|
|
182
316
|
source: hostRuntime.source,
|
|
183
|
-
remoteUrl:
|
|
317
|
+
remoteUrl: hostIsRemote ? hostRuntime.url : undefined,
|
|
184
318
|
},
|
|
185
319
|
shared: config.shared,
|
|
186
320
|
ui: {
|
|
@@ -189,9 +323,9 @@ function buildRuntimeConfig(
|
|
|
189
323
|
entry: uiRuntime.url ? `${uiRuntime.url}/mf-manifest.json` : "/mf-manifest.json",
|
|
190
324
|
localPath: uiRuntime.localPath,
|
|
191
325
|
port: uiRuntime.port,
|
|
192
|
-
ssrUrl: uiConfig.ssr,
|
|
193
|
-
ssrIntegrity:
|
|
194
|
-
integrity:
|
|
326
|
+
ssrUrl: uiIsRemote ? uiConfig.ssr : undefined,
|
|
327
|
+
ssrIntegrity: uiIsRemote ? uiConfig.ssrIntegrity : undefined,
|
|
328
|
+
integrity: uiIsRemote ? uiConfig.integrity : undefined,
|
|
195
329
|
source: uiRuntime.source,
|
|
196
330
|
},
|
|
197
331
|
api: {
|
|
@@ -201,25 +335,31 @@ function buildRuntimeConfig(
|
|
|
201
335
|
localPath: apiRuntime.localPath,
|
|
202
336
|
port: apiRuntime.port,
|
|
203
337
|
source: apiRuntime.source,
|
|
204
|
-
proxy: apiConfig.proxy,
|
|
338
|
+
proxy: options?.proxy ?? apiConfig.proxy,
|
|
205
339
|
variables: apiConfig.variables,
|
|
206
340
|
secrets: apiConfig.secrets,
|
|
207
|
-
integrity:
|
|
341
|
+
integrity: apiIsRemote ? apiConfig.integrity : undefined,
|
|
208
342
|
},
|
|
209
|
-
auth:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
343
|
+
auth: (() => {
|
|
344
|
+
if (!authConfig || !authRuntime) return undefined;
|
|
345
|
+
return {
|
|
346
|
+
name: resolvePluginRuntimeName(undefined, authRuntime.localPath, authConfig.name),
|
|
347
|
+
url: authRuntime.url,
|
|
348
|
+
entry: authRuntime.url ? `${authRuntime.url}/mf-manifest.json` : "/mf-manifest.json",
|
|
349
|
+
localPath: authRuntime.localPath,
|
|
350
|
+
port: authRuntime.port,
|
|
351
|
+
source: authRuntime.source,
|
|
352
|
+
proxy: authConfig.proxy,
|
|
353
|
+
variables: authConfig.variables,
|
|
354
|
+
secrets: authConfig.secrets,
|
|
355
|
+
integrity: authRuntime.source === "remote" ? authConfig.integrity : undefined,
|
|
356
|
+
sidebar: authConfig.sidebar?.map((item) => ({
|
|
357
|
+
...item,
|
|
358
|
+
to: item.to ?? "/auth",
|
|
359
|
+
roleRequired: item.roleRequired ?? ("member" as const),
|
|
360
|
+
})),
|
|
361
|
+
};
|
|
362
|
+
})(),
|
|
223
363
|
plugins:
|
|
224
364
|
options?.plugins && Object.keys(options.plugins).length > 0 ? options.plugins : undefined,
|
|
225
365
|
};
|
|
@@ -239,74 +379,123 @@ async function resolveConfigWithExtends(
|
|
|
239
379
|
baseDir: string,
|
|
240
380
|
visited: Set<string>,
|
|
241
381
|
chain: string[],
|
|
382
|
+
env: BosEnv = "development",
|
|
242
383
|
): Promise<BosConfigInput> {
|
|
243
384
|
if (visited.has(configPath)) {
|
|
244
385
|
throw new Error(`Circular extends detected: ${[...visited, configPath].join(" -> ")}`);
|
|
245
386
|
}
|
|
246
387
|
|
|
247
388
|
const config = await loadConfigFile(configPath, baseDir);
|
|
248
|
-
|
|
249
|
-
chain.push(configPath);
|
|
250
|
-
}
|
|
389
|
+
chain.push(configPath);
|
|
251
390
|
|
|
252
391
|
if (!config.extends) {
|
|
253
392
|
return config;
|
|
254
393
|
}
|
|
255
394
|
|
|
395
|
+
const extendsRef = resolveExtendsRef(config.extends as string | ExtendsConfig, env);
|
|
396
|
+
if (!extendsRef) {
|
|
397
|
+
return config;
|
|
398
|
+
}
|
|
399
|
+
|
|
256
400
|
const nextVisited = new Set(visited);
|
|
257
401
|
nextVisited.add(configPath);
|
|
258
|
-
const
|
|
259
|
-
const parentBaseDir = parentPath.startsWith("bos://")
|
|
402
|
+
const parentBaseDir = extendsRef.startsWith("bos://")
|
|
260
403
|
? baseDir
|
|
261
|
-
: isAbsolute(
|
|
262
|
-
? dirname(
|
|
404
|
+
: isAbsolute(extendsRef)
|
|
405
|
+
? dirname(extendsRef)
|
|
263
406
|
: baseDir;
|
|
264
|
-
const parent = await resolveConfigWithExtends(
|
|
407
|
+
const parent = await resolveConfigWithExtends(extendsRef, parentBaseDir, nextVisited, chain, env);
|
|
265
408
|
|
|
266
|
-
return
|
|
409
|
+
return mergeBosConfigWithExtends(parent, config);
|
|
267
410
|
}
|
|
268
411
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
412
|
+
type PluginOverrideValue = PluginEntryValue | null | false;
|
|
413
|
+
|
|
414
|
+
function normalizePluginEntry(raw: PluginOverrideValue): BosPluginRef | null | false {
|
|
415
|
+
if (raw === null || raw === false) return raw;
|
|
416
|
+
if (typeof raw === "string") {
|
|
417
|
+
return { extends: raw };
|
|
273
418
|
}
|
|
274
|
-
return
|
|
419
|
+
return raw;
|
|
275
420
|
}
|
|
276
421
|
|
|
277
422
|
async function resolveRuntimePlugins(
|
|
278
|
-
plugins: Record<string,
|
|
423
|
+
plugins: Record<string, PluginOverrideValue>,
|
|
279
424
|
baseDir: string,
|
|
280
|
-
env:
|
|
281
|
-
prefix: string[] = [],
|
|
425
|
+
env: BosEnv,
|
|
282
426
|
): Promise<Record<string, RuntimePluginConfig>> {
|
|
283
427
|
const out: Record<string, RuntimePluginConfig> = {};
|
|
284
428
|
|
|
285
|
-
for (const [pluginId,
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
)
|
|
429
|
+
for (const [pluginId, rawInput] of Object.entries(plugins)) {
|
|
430
|
+
const normalized = normalizePluginEntry(rawInput);
|
|
431
|
+
if (normalized === null || normalized === false) continue;
|
|
432
|
+
|
|
433
|
+
let resolvedConfig: BosConfigInput = {};
|
|
434
|
+
let pluginBaseDir = baseDir;
|
|
435
|
+
|
|
436
|
+
if (normalized.extends) {
|
|
437
|
+
try {
|
|
438
|
+
const extendsUrl = resolveExtendsRef(normalized.extends, env);
|
|
439
|
+
if (extendsUrl) {
|
|
440
|
+
const remoteConfig = await fetchBosConfigFromFastKv<BosConfigInput>(extendsUrl);
|
|
441
|
+
resolvedConfig = remoteConfig;
|
|
442
|
+
}
|
|
443
|
+
} catch {
|
|
444
|
+
resolvedConfig = {};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
293
447
|
|
|
294
|
-
|
|
295
|
-
|
|
448
|
+
if (normalized.development?.startsWith(LOCAL_PREFIX)) {
|
|
449
|
+
const localPath = resolve(baseDir, normalized.development.slice(LOCAL_PREFIX.length).trim());
|
|
450
|
+
if (existsSync(localPath)) {
|
|
451
|
+
const localConfigPath = join(localPath, "bos.config.json");
|
|
452
|
+
if (existsSync(localConfigPath)) {
|
|
453
|
+
try {
|
|
454
|
+
const localRaw = JSON.parse(readFileSync(localConfigPath, "utf-8")) as BosConfigInput;
|
|
455
|
+
resolvedConfig = mergeBosConfigWithExtends(resolvedConfig, localRaw);
|
|
456
|
+
pluginBaseDir = localPath;
|
|
457
|
+
} catch {}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (normalized.app && isPlainObject(normalized.app)) {
|
|
463
|
+
const mergedApp: Record<string, unknown> = {
|
|
464
|
+
...((resolvedConfig.app as Record<string, unknown>) ?? {}),
|
|
465
|
+
...(normalized.app as Record<string, unknown>),
|
|
466
|
+
};
|
|
467
|
+
resolvedConfig = { ...resolvedConfig, app: mergedApp as BosConfigInput["app"] };
|
|
468
|
+
}
|
|
469
|
+
if (normalized.shared && isPlainObject(normalized.shared)) {
|
|
470
|
+
const mergedShared: Record<string, Record<string, SharedDepConfig>> = {
|
|
471
|
+
...(resolvedConfig.shared ?? {}),
|
|
472
|
+
...(normalized.shared as Record<string, Record<string, SharedDepConfig>>),
|
|
473
|
+
};
|
|
474
|
+
resolvedConfig = { ...resolvedConfig, shared: mergedShared };
|
|
475
|
+
}
|
|
476
|
+
if (normalized.sidebar) {
|
|
477
|
+
resolvedConfig = { ...resolvedConfig, sidebar: normalized.sidebar };
|
|
478
|
+
}
|
|
479
|
+
if (normalized.routes) {
|
|
480
|
+
resolvedConfig = { ...resolvedConfig, routes: normalized.routes };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const pluginRuntime = await buildRuntimePluginConfig(
|
|
484
|
+
pluginId,
|
|
296
485
|
resolvedConfig,
|
|
297
486
|
pluginBaseDir,
|
|
298
487
|
env,
|
|
299
|
-
|
|
488
|
+
normalized,
|
|
300
489
|
);
|
|
301
490
|
if (
|
|
302
|
-
|
|
303
|
-
typeof
|
|
491
|
+
normalized.name &&
|
|
492
|
+
typeof normalized.name === "string" &&
|
|
304
493
|
!pluginRuntime.name.includes("/")
|
|
305
494
|
) {
|
|
306
|
-
pluginRuntime.name =
|
|
495
|
+
pluginRuntime.name = normalized.name;
|
|
307
496
|
}
|
|
308
497
|
|
|
309
|
-
const integrity =
|
|
498
|
+
const integrity = normalized.integrity;
|
|
310
499
|
if (env === "production" && integrity) {
|
|
311
500
|
pluginRuntime.integrity = integrity;
|
|
312
501
|
}
|
|
@@ -316,7 +505,7 @@ async function resolveRuntimePlugins(
|
|
|
316
505
|
pluginRuntime.url &&
|
|
317
506
|
!pluginRuntime.localPath &&
|
|
318
507
|
typeof resolvedConfig.app?.api?.name !== "string" &&
|
|
319
|
-
!
|
|
508
|
+
!normalized.name
|
|
320
509
|
) {
|
|
321
510
|
pluginRuntime.name = await resolveRemotePluginRuntimeName(
|
|
322
511
|
pluginRuntime.url,
|
|
@@ -324,15 +513,7 @@ async function resolveRuntimePlugins(
|
|
|
324
513
|
);
|
|
325
514
|
}
|
|
326
515
|
|
|
327
|
-
out[
|
|
328
|
-
|
|
329
|
-
if (resolvedConfig.plugins && Object.keys(resolvedConfig.plugins).length > 0) {
|
|
330
|
-
const nested = await resolveRuntimePlugins(resolvedConfig.plugins, pluginBaseDir, env, [
|
|
331
|
-
...prefix,
|
|
332
|
-
pluginId,
|
|
333
|
-
]);
|
|
334
|
-
Object.assign(out, nested);
|
|
335
|
-
}
|
|
516
|
+
out[pluginId] = pluginRuntime;
|
|
336
517
|
}
|
|
337
518
|
|
|
338
519
|
return out;
|
|
@@ -340,7 +521,13 @@ async function resolveRuntimePlugins(
|
|
|
340
521
|
|
|
341
522
|
async function resolveRemotePluginRuntimeName(baseUrl: string, fallback: string): Promise<string> {
|
|
342
523
|
try {
|
|
343
|
-
const
|
|
524
|
+
const controller = new AbortController();
|
|
525
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
526
|
+
const response = await fetch(`${baseUrl.replace(/\/$/, "")}/plugin.manifest.json`, {
|
|
527
|
+
signal: controller.signal,
|
|
528
|
+
});
|
|
529
|
+
clearTimeout(timeout);
|
|
530
|
+
|
|
344
531
|
if (!response.ok) {
|
|
345
532
|
return fallback;
|
|
346
533
|
}
|
|
@@ -357,13 +544,38 @@ async function resolveRemotePluginRuntimeName(baseUrl: string, fallback: string)
|
|
|
357
544
|
}
|
|
358
545
|
}
|
|
359
546
|
|
|
360
|
-
|
|
547
|
+
interface ResolvedBosPlugin {
|
|
548
|
+
url: string;
|
|
549
|
+
integrity?: string;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
async function resolveBosPluginUrl(bosUrl: string): Promise<ResolvedBosPlugin | null> {
|
|
553
|
+
const parsed = parsePluginBosUrl(bosUrl);
|
|
554
|
+
if (!parsed) return null;
|
|
555
|
+
|
|
556
|
+
try {
|
|
557
|
+
const entry = await fetchPluginFromRegistry(parsed.accountId, parsed.pluginName);
|
|
558
|
+
if (!entry) return null;
|
|
559
|
+
|
|
560
|
+
const cdnUrl = entry.metadata.cdnUrl;
|
|
561
|
+
if (!cdnUrl) return null;
|
|
562
|
+
|
|
563
|
+
return {
|
|
564
|
+
url: cdnUrl,
|
|
565
|
+
integrity: entry.metadata.integrity ?? undefined,
|
|
566
|
+
};
|
|
567
|
+
} catch {
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
async function buildRuntimePluginConfig(
|
|
361
573
|
pluginId: string,
|
|
362
574
|
config: BosConfigInput,
|
|
363
575
|
baseDir: string,
|
|
364
|
-
env:
|
|
365
|
-
source:
|
|
366
|
-
): RuntimePluginConfig {
|
|
576
|
+
env: BosEnv,
|
|
577
|
+
source: BosPluginRef,
|
|
578
|
+
): Promise<RuntimePluginConfig> {
|
|
367
579
|
const apiConfig = config.app?.api ?? {};
|
|
368
580
|
const apiDevelopment =
|
|
369
581
|
typeof apiConfig.development === "string" ? apiConfig.development : undefined;
|
|
@@ -372,7 +584,18 @@ function buildRuntimePluginConfig(
|
|
|
372
584
|
const sourceProduction = typeof source.production === "string" ? source.production : undefined;
|
|
373
585
|
const proxy = typeof apiConfig.proxy === "string" ? apiConfig.proxy : undefined;
|
|
374
586
|
const development = apiDevelopment ?? sourceDevelopment;
|
|
375
|
-
|
|
587
|
+
let production = apiProduction ?? sourceProduction;
|
|
588
|
+
|
|
589
|
+
if (production?.startsWith("bos://")) {
|
|
590
|
+
const resolved = await resolveBosPluginUrl(production);
|
|
591
|
+
if (resolved) {
|
|
592
|
+
production = resolved.url;
|
|
593
|
+
if (resolved.integrity && env === "production") {
|
|
594
|
+
source.integrity = resolved.integrity;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
376
599
|
const runtimeTarget =
|
|
377
600
|
env === "development"
|
|
378
601
|
? resolveDevelopmentTarget(development, production, baseDir)
|
|
@@ -383,6 +606,25 @@ function buildRuntimePluginConfig(
|
|
|
383
606
|
pluginId,
|
|
384
607
|
);
|
|
385
608
|
|
|
609
|
+
const uiConfig = config.app?.ui;
|
|
610
|
+
const uiDevelopment =
|
|
611
|
+
typeof uiConfig?.development === "string" ? uiConfig.development : undefined;
|
|
612
|
+
const uiProduction = typeof uiConfig?.production === "string" ? uiConfig.production : undefined;
|
|
613
|
+
const uiRuntime =
|
|
614
|
+
uiConfig && (uiDevelopment || uiProduction)
|
|
615
|
+
? env === "development"
|
|
616
|
+
? resolveDevelopmentTarget(uiDevelopment, uiProduction, baseDir)
|
|
617
|
+
: resolveRuntimeTarget(uiProduction, baseDir, "remote")
|
|
618
|
+
: undefined;
|
|
619
|
+
|
|
620
|
+
const sidebar = (config.sidebar ?? source.sidebar)?.map((item) => ({
|
|
621
|
+
...item,
|
|
622
|
+
to: item.to ?? `/${pluginId}`,
|
|
623
|
+
roleRequired: item.roleRequired ?? ("member" as const),
|
|
624
|
+
}));
|
|
625
|
+
|
|
626
|
+
const routes = config.routes ?? source.routes;
|
|
627
|
+
|
|
386
628
|
return {
|
|
387
629
|
name: apiName,
|
|
388
630
|
url: runtimeTarget.url,
|
|
@@ -395,6 +637,24 @@ function buildRuntimePluginConfig(
|
|
|
395
637
|
proxy: proxy ?? (typeof source.proxy === "string" ? source.proxy : undefined),
|
|
396
638
|
variables: normalizeStringRecord(apiConfig.variables ?? source.variables),
|
|
397
639
|
secrets: normalizeStringArray(apiConfig.secrets ?? source.secrets),
|
|
640
|
+
ui: uiRuntime
|
|
641
|
+
? {
|
|
642
|
+
name: typeof uiConfig?.name === "string" ? uiConfig.name : `${apiName}-ui`,
|
|
643
|
+
url: uiRuntime.url,
|
|
644
|
+
entry: uiRuntime.url
|
|
645
|
+
? `${uiRuntime.url.replace(/\/$/, "")}/mf-manifest.json`
|
|
646
|
+
: "/mf-manifest.json",
|
|
647
|
+
source: uiRuntime.source,
|
|
648
|
+
localPath: uiRuntime.localPath,
|
|
649
|
+
port: uiRuntime.port,
|
|
650
|
+
integrity:
|
|
651
|
+
uiRuntime.source === "remote" && typeof uiConfig?.integrity === "string"
|
|
652
|
+
? uiConfig.integrity
|
|
653
|
+
: undefined,
|
|
654
|
+
}
|
|
655
|
+
: undefined,
|
|
656
|
+
sidebar,
|
|
657
|
+
routes,
|
|
398
658
|
};
|
|
399
659
|
}
|
|
400
660
|
|
|
@@ -422,45 +682,6 @@ export function resolvePluginRuntimeName(
|
|
|
422
682
|
return fallback;
|
|
423
683
|
}
|
|
424
684
|
|
|
425
|
-
async function resolveBosConfigInput(
|
|
426
|
-
input: BosConfigInput,
|
|
427
|
-
baseDir: string,
|
|
428
|
-
visited: Set<string>,
|
|
429
|
-
chain: string[],
|
|
430
|
-
): Promise<{ config: BosConfigInput; baseDir: string }> {
|
|
431
|
-
if (input.extends) {
|
|
432
|
-
const parentBaseDir = input.extends.startsWith("bos://")
|
|
433
|
-
? baseDir
|
|
434
|
-
: isAbsolute(input.extends)
|
|
435
|
-
? dirname(input.extends)
|
|
436
|
-
: baseDir;
|
|
437
|
-
const config = await resolveConfigWithExtends(input.extends, parentBaseDir, visited, chain);
|
|
438
|
-
return { config: mergeConfigs(config, input), baseDir: parentBaseDir };
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return { config: input, baseDir };
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function mergeValues(parent: unknown, child: unknown): unknown {
|
|
445
|
-
if (Array.isArray(parent) && Array.isArray(child)) {
|
|
446
|
-
return child;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (isPlainObject(parent) && isPlainObject(child)) {
|
|
450
|
-
const merged: Record<string, unknown> = { ...parent };
|
|
451
|
-
for (const [key, value] of Object.entries(child)) {
|
|
452
|
-
merged[key] = key in merged ? mergeValues(merged[key], value) : value;
|
|
453
|
-
}
|
|
454
|
-
return merged;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
return child ?? parent;
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
461
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
685
|
function normalizeStringRecord(value: unknown): Record<string, string> | undefined {
|
|
465
686
|
if (!isPlainObject(value)) return undefined;
|
|
466
687
|
const out: Record<string, string> = {};
|
|
@@ -488,7 +709,7 @@ function resolveRuntimeTarget(
|
|
|
488
709
|
}
|
|
489
710
|
|
|
490
711
|
if (value.startsWith(LOCAL_PREFIX)) {
|
|
491
|
-
const localTarget = value
|
|
712
|
+
const localTarget = value?.slice(LOCAL_PREFIX.length).trim();
|
|
492
713
|
if (!localTarget) {
|
|
493
714
|
throw new Error(`Invalid local development target: ${value}`);
|
|
494
715
|
}
|
|
@@ -512,7 +733,9 @@ function resolveRuntimeTarget(
|
|
|
512
733
|
};
|
|
513
734
|
}
|
|
514
735
|
|
|
515
|
-
export function isLocalDevelopmentTarget(
|
|
736
|
+
export function isLocalDevelopmentTarget(
|
|
737
|
+
value: string | undefined,
|
|
738
|
+
): value is `${typeof LOCAL_PREFIX}${string}` {
|
|
516
739
|
return typeof value === "string" && value.startsWith(LOCAL_PREFIX);
|
|
517
740
|
}
|
|
518
741
|
|
|
@@ -524,7 +747,7 @@ export function resolveLocalDevelopmentPath(
|
|
|
524
747
|
return null;
|
|
525
748
|
}
|
|
526
749
|
|
|
527
|
-
const localTarget = value
|
|
750
|
+
const localTarget = value.slice(LOCAL_PREFIX.length).trim();
|
|
528
751
|
return localTarget ? resolve(baseDir, localTarget) : null;
|
|
529
752
|
}
|
|
530
753
|
|
|
@@ -549,5 +772,6 @@ export function parsePort(url: string): number {
|
|
|
549
772
|
}
|
|
550
773
|
}
|
|
551
774
|
|
|
775
|
+
export { BOS_CONFIG_ORDER, rebuildOrderedConfig } from "./merge";
|
|
552
776
|
export type { BosConfig, RuntimeConfig } from "./types";
|
|
553
777
|
export { BosConfigSchema } from "./types";
|