@vercel/microfrontends 0.14.0 → 0.16.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 (121) hide show
  1. package/dist/bin/cli.cjs +392 -1620
  2. package/dist/config.cjs +478 -1001
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.d.ts +4 -25
  5. package/dist/config.js +474 -980
  6. package/dist/config.js.map +1 -1
  7. package/dist/{v2/microfrontends → microfrontends}/server.cjs +91 -150
  8. package/dist/microfrontends/server.cjs.map +1 -0
  9. package/dist/{v2/microfrontends → microfrontends}/server.d.ts +4 -4
  10. package/dist/{v2/microfrontends → microfrontends}/server.js +91 -150
  11. package/dist/microfrontends/server.js.map +1 -0
  12. package/dist/{v2/microfrontends.cjs → microfrontends.cjs} +49 -28
  13. package/dist/microfrontends.cjs.map +1 -0
  14. package/dist/{v2/microfrontends.d.ts → microfrontends.d.ts} +4 -4
  15. package/dist/{v2/microfrontends.js → microfrontends.js} +48 -27
  16. package/dist/microfrontends.js.map +1 -0
  17. package/dist/next/client.cjs +1 -1
  18. package/dist/next/client.cjs.map +1 -1
  19. package/dist/next/client.js +1 -1
  20. package/dist/next/client.js.map +1 -1
  21. package/dist/next/config.cjs +1344 -1024
  22. package/dist/next/config.cjs.map +1 -1
  23. package/dist/next/config.d.ts +1 -1
  24. package/dist/next/config.js +1343 -1023
  25. package/dist/next/config.js.map +1 -1
  26. package/dist/next/endpoints.cjs +77 -18
  27. package/dist/next/endpoints.cjs.map +1 -1
  28. package/dist/next/endpoints.d.ts +13 -2
  29. package/dist/next/endpoints.js +77 -18
  30. package/dist/next/endpoints.js.map +1 -1
  31. package/dist/next/middleware.cjs +765 -395
  32. package/dist/next/middleware.cjs.map +1 -1
  33. package/dist/next/middleware.d.ts +10 -5
  34. package/dist/next/middleware.js +765 -395
  35. package/dist/next/middleware.js.map +1 -1
  36. package/dist/next/testing.cjs +615 -1031
  37. package/dist/next/testing.cjs.map +1 -1
  38. package/dist/next/testing.d.ts +14 -12
  39. package/dist/next/testing.js +609 -1015
  40. package/dist/next/testing.js.map +1 -1
  41. package/dist/overrides.cjs +40 -108
  42. package/dist/overrides.cjs.map +1 -1
  43. package/dist/overrides.d.ts +24 -2
  44. package/dist/overrides.js +36 -106
  45. package/dist/overrides.js.map +1 -1
  46. package/dist/{v2/routing.cjs → routing.cjs} +3 -3
  47. package/dist/routing.cjs.map +1 -0
  48. package/dist/{v2/schema.cjs → schema.cjs} +1 -1
  49. package/dist/schema.cjs.map +1 -0
  50. package/dist/schema.d.ts +1 -0
  51. package/dist/utils/mfe-port.cjs +237 -1338
  52. package/dist/utils/mfe-port.cjs.map +1 -1
  53. package/dist/utils/mfe-port.js +230 -1331
  54. package/dist/utils/mfe-port.js.map +1 -1
  55. package/dist/validation.cjs +31 -361
  56. package/dist/validation.cjs.map +1 -1
  57. package/dist/validation.d.ts +3 -146
  58. package/dist/validation.js +30 -359
  59. package/dist/validation.js.map +1 -1
  60. package/package.json +29 -92
  61. package/schema/schema.json +174 -244
  62. package/dist/config/client.cjs +0 -54
  63. package/dist/config/client.cjs.map +0 -1
  64. package/dist/config/client.d.ts +0 -23
  65. package/dist/config/client.js +0 -28
  66. package/dist/config/client.js.map +0 -1
  67. package/dist/config/edge.cjs +0 -508
  68. package/dist/config/edge.cjs.map +0 -1
  69. package/dist/config/edge.d.ts +0 -20
  70. package/dist/config/edge.js +0 -481
  71. package/dist/config/edge.js.map +0 -1
  72. package/dist/microfrontend-config-983a5139.d.ts +0 -154
  73. package/dist/schema-2922d49e.d.ts +0 -182
  74. package/dist/v2/config.cjs +0 -709
  75. package/dist/v2/config.cjs.map +0 -1
  76. package/dist/v2/config.d.ts +0 -4
  77. package/dist/v2/config.js +0 -684
  78. package/dist/v2/config.js.map +0 -1
  79. package/dist/v2/microfrontends/server.cjs.map +0 -1
  80. package/dist/v2/microfrontends/server.js.map +0 -1
  81. package/dist/v2/microfrontends.cjs.map +0 -1
  82. package/dist/v2/microfrontends.js.map +0 -1
  83. package/dist/v2/next/client.cjs +0 -3
  84. package/dist/v2/next/client.cjs.map +0 -1
  85. package/dist/v2/next/client.d.ts +0 -45
  86. package/dist/v2/next/client.js +0 -3
  87. package/dist/v2/next/client.js.map +0 -1
  88. package/dist/v2/next/config.cjs +0 -2178
  89. package/dist/v2/next/config.cjs.map +0 -1
  90. package/dist/v2/next/config.d.ts +0 -22
  91. package/dist/v2/next/config.js +0 -2143
  92. package/dist/v2/next/config.js.map +0 -1
  93. package/dist/v2/next/endpoints.cjs +0 -141
  94. package/dist/v2/next/endpoints.cjs.map +0 -1
  95. package/dist/v2/next/endpoints.d.ts +0 -26
  96. package/dist/v2/next/endpoints.js +0 -116
  97. package/dist/v2/next/endpoints.js.map +0 -1
  98. package/dist/v2/next/middleware.cjs +0 -1099
  99. package/dist/v2/next/middleware.cjs.map +0 -1
  100. package/dist/v2/next/middleware.d.ts +0 -34
  101. package/dist/v2/next/middleware.js +0 -1071
  102. package/dist/v2/next/middleware.js.map +0 -1
  103. package/dist/v2/next/testing.cjs +0 -992
  104. package/dist/v2/next/testing.cjs.map +0 -1
  105. package/dist/v2/next/testing.d.ts +0 -55
  106. package/dist/v2/next/testing.js +0 -961
  107. package/dist/v2/next/testing.js.map +0 -1
  108. package/dist/v2/overrides.cjs +0 -75
  109. package/dist/v2/overrides.cjs.map +0 -1
  110. package/dist/v2/overrides.d.ts +0 -24
  111. package/dist/v2/overrides.js +0 -45
  112. package/dist/v2/overrides.js.map +0 -1
  113. package/dist/v2/routing.cjs.map +0 -1
  114. package/dist/v2/schema.cjs.map +0 -1
  115. package/dist/v2/schema.d.ts +0 -1
  116. package/schema/schema-v2.json +0 -266
  117. /package/dist/{v2/routing.d.ts → routing.d.ts} +0 -0
  118. /package/dist/{v2/routing.js → routing.js} +0 -0
  119. /package/dist/{v2/routing.js.map → routing.js.map} +0 -0
  120. /package/dist/{v2/schema.js → schema.js} +0 -0
  121. /package/dist/{v2/schema.js.map → schema.js.map} +0 -0
@@ -1,31 +1,42 @@
1
1
  // src/next/config/index.ts
2
- import fs3 from "node:fs";
2
+ import fs6 from "node:fs";
3
3
 
4
- // src/config/microfrontend-config.ts
5
- import fs2 from "node:fs";
4
+ // src/config/microfrontends/server/index.ts
5
+ import fs5 from "node:fs";
6
+ import { dirname as dirname3, join as join2 } from "node:path";
6
7
 
7
- // src/config-v2/microfrontends/server/utils/get-output-file-path.ts
8
- import path from "node:path";
8
+ // src/config/overrides/constants.ts
9
+ var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
10
+ var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
9
11
 
10
- // src/config-v2/microfrontends/server/constants.ts
11
- var MFE_CONFIG_DEFAULT_FILE_PATH = "microfrontends";
12
- var MFE_CONFIG_DEFAULT_FILE_NAME = "microfrontends.json";
12
+ // src/config/overrides/is-override-cookie.ts
13
+ function isOverrideCookie(cookie) {
14
+ var _a;
15
+ return Boolean((_a = cookie.name) == null ? void 0 : _a.startsWith(OVERRIDES_COOKIE_PREFIX));
16
+ }
13
17
 
14
- // src/utils/is-vercel.ts
15
- function isVercel() {
16
- return process.env.VERCEL === "1";
18
+ // src/config/overrides/get-override-from-cookie.ts
19
+ function getOverrideFromCookie(cookie) {
20
+ if (!isOverrideCookie(cookie) || !cookie.value)
21
+ return;
22
+ return {
23
+ application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
24
+ host: cookie.value
25
+ };
17
26
  }
18
27
 
19
- // src/config-v2/microfrontends/server/utils/get-output-file-path.ts
20
- function getOutputFilePath() {
21
- if (isVercel()) {
22
- return path.join(
23
- ".vercel",
24
- MFE_CONFIG_DEFAULT_FILE_PATH,
25
- MFE_CONFIG_DEFAULT_FILE_NAME
26
- );
27
- }
28
- return path.join(MFE_CONFIG_DEFAULT_FILE_PATH, MFE_CONFIG_DEFAULT_FILE_NAME);
28
+ // src/config/overrides/parse-overrides.ts
29
+ function parseOverrides(cookies) {
30
+ const overridesConfig = { applications: {} };
31
+ cookies.forEach((cookie) => {
32
+ const override = getOverrideFromCookie(cookie);
33
+ if (!override)
34
+ return;
35
+ overridesConfig.applications[override.application] = {
36
+ environment: { host: override.host }
37
+ };
38
+ });
39
+ return overridesConfig;
29
40
  }
30
41
 
31
42
  // src/config/errors.ts
@@ -120,131 +131,289 @@ var MicrofrontendError = class extends Error {
120
131
  }
121
132
  };
122
133
 
123
- // src/routing/url.ts
124
- function buildUrlSafeString(givenOpts = {}) {
125
- const options = {
126
- joinString: "-",
127
- lowercaseOnly: true,
128
- maxLen: 100,
129
- regexRemovePattern: /(?:(?!(?:[a-z0-9])).)/gi,
130
- trimWhitespace: true,
131
- ...givenOpts
132
- };
133
- return {
134
- generate: (...args) => {
135
- const reJoinString = new RegExp(`${options.joinString}+`, "g");
136
- let tag;
137
- if (args.length === 0) {
138
- throw new Error("generate method must be passed at least one argument");
134
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
135
+ function getConfigStringFromEnv() {
136
+ const config = process.env.MFE_CONFIG;
137
+ if (!config) {
138
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
139
+ type: "config",
140
+ subtype: "not_found_in_env"
141
+ });
142
+ }
143
+ return config;
144
+ }
145
+
146
+ // src/config/microfrontends-config/isomorphic/index.ts
147
+ import { parse } from "jsonc-parser";
148
+
149
+ // src/config/schema/utils/is-main-config.ts
150
+ function isMainConfig(c) {
151
+ return !("partOf" in c);
152
+ }
153
+
154
+ // src/config/schema/utils/is-default-app.ts
155
+ function isDefaultApp(a) {
156
+ return !("routing" in a);
157
+ }
158
+
159
+ // src/config/microfrontends-config/client/index.ts
160
+ import { pathToRegexp } from "path-to-regexp";
161
+ var MicrofrontendConfigClient = class {
162
+ constructor(config, opts) {
163
+ this.pathCache = {};
164
+ this.serialized = config;
165
+ if (opts == null ? void 0 : opts.removeFlaggedPaths) {
166
+ for (const app of Object.values(config.applications)) {
167
+ if (app.routing) {
168
+ app.routing = app.routing.filter((match) => !match.flag);
169
+ }
139
170
  }
140
- for (let i = 0; i < args.length; i++) {
141
- const arg = args[i];
142
- if (typeof arg !== "string")
143
- throw new Error("all supplied arguments must be Strings");
144
- if (options.trimWhitespace) {
145
- args[i] = arg.trim();
171
+ }
172
+ this.applications = config.applications;
173
+ }
174
+ /**
175
+ * Create a new `MicrofrontendConfigClient` from a JSON string.
176
+ * Config must be passed in to remain framework agnostic
177
+ */
178
+ static fromEnv(config, opts) {
179
+ if (!config) {
180
+ throw new Error("No microfrontends configuration found");
181
+ }
182
+ return new MicrofrontendConfigClient(
183
+ JSON.parse(config),
184
+ opts
185
+ );
186
+ }
187
+ isEqual(other) {
188
+ return JSON.stringify(this.applications) === JSON.stringify(other.applications);
189
+ }
190
+ getApplicationNameForPath(path5) {
191
+ if (!path5.startsWith("/")) {
192
+ throw new Error(`Path must start with a /`);
193
+ }
194
+ if (this.pathCache[path5]) {
195
+ return this.pathCache[path5];
196
+ }
197
+ const pathname = new URL(path5, "https://example.com").pathname;
198
+ for (const [name, application] of Object.entries(this.applications)) {
199
+ if (application.routing) {
200
+ for (const group of application.routing) {
201
+ for (const childPath of group.paths) {
202
+ const regexp = pathToRegexp(childPath);
203
+ if (regexp.test(pathname)) {
204
+ this.pathCache[path5] = name;
205
+ return name;
206
+ }
207
+ }
146
208
  }
147
209
  }
148
- tag = args.join(options.joinString);
149
- tag = tag.replace(/\s/g, options.joinString);
150
- if (options.lowercaseOnly)
151
- tag = tag.toLowerCase();
152
- tag = tag.replace(options.regexRemovePattern, (match) => {
153
- if (match === options.joinString)
154
- return match;
155
- return "";
156
- });
157
- if (tag.length > options.maxLen)
158
- tag = tag.substring(0, options.maxLen);
159
- tag = tag.replace(reJoinString, options.joinString);
160
- return tag;
161
210
  }
162
- };
163
- }
164
- var urlSafeString = buildUrlSafeString().generate;
165
- function makeUrlSafe(name) {
166
- return urlSafeString(name.replace(/\//g, "-")).replace(/^-*/g, "").replace(/-*$/g, "");
167
- }
168
-
169
- // src/config/overrides/config.ts
170
- var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
171
- var _Overrides = class {
172
- constructor(config) {
173
- this.config = config;
211
+ const defaultApplication = Object.entries(this.applications).find(
212
+ ([, application]) => application.default
213
+ );
214
+ if (!defaultApplication) {
215
+ return null;
216
+ }
217
+ this.pathCache[path5] = defaultApplication[0];
218
+ return defaultApplication[0];
174
219
  }
175
- static getAppEnvOverrideCookieName(zone) {
176
- return `${_Overrides.overrideEnvCookiePrefix}${zone}`;
220
+ serialize() {
221
+ return this.serialized;
177
222
  }
178
- static isOverrideCookie(cookie) {
179
- var _a;
180
- return Boolean((_a = cookie.name) == null ? void 0 : _a.startsWith(OVERRIDES_COOKIE_PREFIX));
223
+ };
224
+
225
+ // src/config/microfrontends-config/isomorphic/validation.ts
226
+ import { pathToRegexp as pathToRegexp2, parse as parsePathRegexp } from "path-to-regexp";
227
+ var SUPPORTED_VERSIONS = ["2"];
228
+ var validateConfigVersion = (version) => {
229
+ if (!SUPPORTED_VERSIONS.includes(version)) {
230
+ throw new MicrofrontendError(
231
+ `Unsupported version: ${version}. Supported versions are: ${SUPPORTED_VERSIONS.join(
232
+ ", "
233
+ )}`,
234
+ { type: "config", subtype: "unsupported_version" }
235
+ );
181
236
  }
182
- static getOverrideFromCookie(cookie) {
183
- if (!_Overrides.isOverrideCookie(cookie) || !cookie.value)
184
- return;
185
- return {
186
- zone: cookie.name.replace(_Overrides.overrideEnvCookiePrefix, ""),
187
- host: cookie.value
188
- };
237
+ };
238
+ var validateConfigPaths = (applicationConfigsById) => {
239
+ if (!applicationConfigsById) {
240
+ return;
189
241
  }
190
- static parseOverrides(cookies) {
191
- const overridesConfig = { applications: {} };
192
- cookies.forEach((cookie) => {
193
- const override = _Overrides.getOverrideFromCookie(cookie);
194
- if (!override)
195
- return;
196
- overridesConfig.applications[override.zone] = {
197
- environment: { host: override.host }
198
- };
242
+ const pathsByApplicationId = /* @__PURE__ */ new Map();
243
+ const errors = [];
244
+ for (const [id, app] of Object.entries(applicationConfigsById)) {
245
+ if (isDefaultApp(app)) {
246
+ continue;
247
+ }
248
+ for (const pathMatch of app.routing) {
249
+ for (const path5 of pathMatch.paths) {
250
+ const maybeError = validatePathExpression(path5);
251
+ if (maybeError) {
252
+ errors.push(maybeError);
253
+ }
254
+ const existing = pathsByApplicationId.get(path5);
255
+ if (existing) {
256
+ existing.applications.push(id);
257
+ } else {
258
+ pathsByApplicationId.set(path5, {
259
+ applications: [id],
260
+ matcher: pathToRegexp2(path5),
261
+ applicationId: id
262
+ });
263
+ }
264
+ }
265
+ }
266
+ }
267
+ const entries = Array.from(pathsByApplicationId.entries());
268
+ entries.forEach(([path5, { applications: ids, matcher, applicationId }]) => {
269
+ if (ids.length > 1) {
270
+ errors.push(
271
+ `Duplicate path "${path5}" for applications "${ids.join(", ")}"`
272
+ );
273
+ }
274
+ entries.forEach(
275
+ ([
276
+ matchPath,
277
+ { applications: matchIds, applicationId: matchApplicationId }
278
+ ]) => {
279
+ if (path5 === matchPath) {
280
+ return;
281
+ }
282
+ if (applicationId === matchApplicationId) {
283
+ return;
284
+ }
285
+ if (matcher.test(matchPath)) {
286
+ const source = `"${path5}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
287
+ const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
288
+ errors.push(
289
+ `Overlapping path detected between ${source} and ${destination}`
290
+ );
291
+ }
292
+ }
293
+ );
294
+ });
295
+ if (errors.length) {
296
+ throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
297
+ type: "config",
298
+ subtype: "conflicting_paths"
199
299
  });
200
- return overridesConfig;
201
300
  }
202
- static validOverrideDomainsForZone(microfrontendConfig, zone) {
203
- var _a, _b, _c, _d, _e;
204
- const projectName = (_a = microfrontendConfig.getZone(zone).vercel) == null ? void 0 : _a.projectName;
205
- if (!projectName) {
206
- return [microfrontendConfig.getZone(zone).production.host];
301
+ };
302
+ var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
303
+ function validatePathExpression(path5) {
304
+ const tokens = parsePathRegexp(path5);
305
+ for (let i = 0; i < tokens.length; i++) {
306
+ const token = tokens[i];
307
+ if (token === void 0) {
308
+ return `token ${i} in ${path5} is undefined, this shouldn't happen`;
309
+ }
310
+ if (typeof token !== "string") {
311
+ if (token.pattern !== PATH_DEFAULT_PATTERN) {
312
+ return `Path ${path5} cannot use a regular expression wildcard`;
313
+ }
314
+ if (token.prefix !== "/") {
315
+ return `Wildcard :${token.name} must be immediately after a / in ${path5}`;
316
+ }
317
+ if (token.suffix) {
318
+ return `Wildcard suffix on :${token.name} is not allowed. Suffixes are not supported`;
319
+ }
320
+ if (token.modifier && i !== tokens.length - 1) {
321
+ return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path5}. Modifiers are only allowed in the last path component`;
322
+ }
207
323
  }
208
- const parsedProjectName = makeUrlSafe(projectName);
209
- const previewDeploymentSuffix = (_c = (_b = microfrontendConfig.options) == null ? void 0 : _b.vercel) == null ? void 0 : _c.previewDeploymentSuffix;
210
- const teamSlug = (_e = (_d = microfrontendConfig.options) == null ? void 0 : _d.vercel) == null ? void 0 : _e.teamSlug;
211
- if (!teamSlug && !previewDeploymentSuffix) {
212
- return [microfrontendConfig.getZone(zone).production.host];
324
+ }
325
+ return void 0;
326
+ }
327
+ var validateAppPaths = (name, app) => {
328
+ for (const group of app.routing) {
329
+ for (const p of group.paths) {
330
+ if (p === "/") {
331
+ continue;
332
+ }
333
+ if (p.endsWith("/")) {
334
+ throw new MicrofrontendError(
335
+ `Invalid path for application "${name}". ${p} must not end with a slash.`,
336
+ { type: "application", subtype: "invalid_path" }
337
+ );
338
+ }
339
+ if (!p.startsWith("/")) {
340
+ throw new MicrofrontendError(
341
+ `Invalid path for application "${name}". ${p} must start with a slash.`,
342
+ { type: "application", subtype: "invalid_path" }
343
+ );
344
+ }
213
345
  }
214
- const suffix = previewDeploymentSuffix ? `.${previewDeploymentSuffix}` : `-${teamSlug}.vercel.app`;
215
- return [
216
- `${parsedProjectName}-git-([a-zA-Z0-9-]+)${suffix}`,
217
- microfrontendConfig.getZone(zone).production.host
218
- ];
219
346
  }
220
- static validateOverrideDomain(microfrontendConfig, zone, domain) {
221
- return new RegExp(
222
- `^${_Overrides.validOverrideDomainsForZone(microfrontendConfig, zone).join(
223
- "|"
224
- )}$`
225
- ).test(domain);
347
+ };
348
+ var validateConfigDefaultApplication = (applicationConfigsById) => {
349
+ if (!applicationConfigsById) {
350
+ return;
226
351
  }
227
- serialize() {
228
- return this.config;
352
+ const applicationsWithRouting = Object.entries(applicationConfigsById).filter(
353
+ ([, app]) => !isDefaultApp(app)
354
+ );
355
+ const applicationsWithRoutingNames = applicationsWithRouting.map(
356
+ ([key]) => key
357
+ );
358
+ const numApplications = Object.keys(applicationConfigsById).length;
359
+ const numApplicationsWithRouting = applicationsWithRoutingNames.length;
360
+ const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
361
+ if (numApplicationsWithoutRouting === 0) {
362
+ throw new MicrofrontendError(
363
+ `No default application found. At least one application needs to be the default by omitting routing.`,
364
+ { type: "config", subtype: "no_default_application" }
365
+ );
366
+ }
367
+ if (numApplicationsWithoutRouting > 1) {
368
+ throw new MicrofrontendError(
369
+ `Only one application can omit "routing". Found ${applicationsWithRoutingNames.length - Object.keys(applicationConfigsById).length > 1}.`,
370
+ { type: "config", subtype: "multiple_default_applications" }
371
+ );
229
372
  }
230
373
  };
231
- var Overrides = _Overrides;
232
- Overrides.overrideEnvCookiePrefix = `${OVERRIDES_COOKIE_PREFIX}:env:`;
233
374
 
234
- // src/config/common/host.ts
375
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
376
+ var PREFIX = "vc-ap";
377
+ function generateAssetPrefixFromName({
378
+ name
379
+ }) {
380
+ if (!name) {
381
+ throw new Error("Name is required to generate an asset prefix");
382
+ }
383
+ return `${PREFIX}-${name}`;
384
+ }
385
+
386
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
387
+ function generatePortFromName({
388
+ name,
389
+ minPort = 3e3,
390
+ maxPort = 8e3
391
+ }) {
392
+ if (!name) {
393
+ throw new Error("Name is required to generate a port");
394
+ }
395
+ let hash = 0;
396
+ for (let i = 0; i < name.length; i++) {
397
+ hash = (hash << 5) - hash + name.charCodeAt(i);
398
+ hash |= 0;
399
+ }
400
+ hash = Math.abs(hash);
401
+ const range = maxPort - minPort;
402
+ const port = minPort + hash % range;
403
+ return port;
404
+ }
405
+
406
+ // src/config/microfrontends-config/isomorphic/host.ts
235
407
  var Host = class {
236
- constructor({ protocol, host, port }) {
237
- this.protocol = protocol || "https";
408
+ constructor(hostConfig, options) {
409
+ const { protocol = "https", host, port } = hostConfig;
410
+ this.protocol = protocol;
238
411
  this.host = host;
239
412
  this.port = Host.getPort({ port, protocol: this.protocol });
240
- this.serialized = {
241
- protocol,
242
- host,
243
- ...port ? { port } : void 0
244
- };
413
+ this.local = options == null ? void 0 : options.isLocal;
245
414
  }
246
415
  isLocal() {
247
- return this.host === "localhost" || this.host === "127.0.0.1";
416
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
248
417
  }
249
418
  static getPort({
250
419
  protocol,
@@ -270,176 +439,234 @@ var Host = class {
270
439
  const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
271
440
  return new URL(url);
272
441
  }
273
- serialize() {
274
- return this.serialized;
442
+ };
443
+ var LocalHost = class extends Host {
444
+ constructor({
445
+ appName,
446
+ ...hostConfig
447
+ }) {
448
+ const host = hostConfig.host ?? "localhost";
449
+ const port = hostConfig.port ?? generatePortFromName({ name: appName });
450
+ const protocol = hostConfig.protocol ?? "http";
451
+ super({ protocol, host, port });
275
452
  }
276
453
  };
277
454
 
278
- // src/config/common/application.ts
455
+ // src/config/microfrontends-config/isomorphic/application.ts
279
456
  var Application = class {
280
457
  constructor(name, {
281
458
  app,
282
- overrides
459
+ overrides,
460
+ isDefault
283
461
  }) {
284
- Application.validate(name, app);
462
+ var _a, _b;
285
463
  this.name = name;
286
- this.default = app.default;
287
- this.routing = app.routing;
288
464
  this.development = {
289
- local: new Host(app.development.local),
290
- fallback: app.development.fallback ? new Host(app.development.fallback) : void 0
465
+ local: new LocalHost({
466
+ appName: name,
467
+ ...(_a = app.development) == null ? void 0 : _a.local
468
+ }),
469
+ fallback: ((_b = app.development) == null ? void 0 : _b.fallback) ? new Host(app.development.fallback) : void 0
291
470
  };
292
- this.production = new Host(app.production);
471
+ this.production = app.production ? new Host(app.production) : void 0;
293
472
  this.vercel = app.vercel;
294
473
  this.overrides = (overrides == null ? void 0 : overrides.environment) ? {
295
474
  environment: new Host(overrides.environment)
296
475
  } : void 0;
476
+ this.default = isDefault ?? false;
477
+ this.serialized = app;
297
478
  }
298
479
  isDefault() {
299
480
  return this.default;
300
481
  }
301
- static validate(name, app) {
302
- var _a, _b, _c, _d, _e;
303
- if (((_b = (_a = app.routing) == null ? void 0 : _a.assetPrefix) == null ? void 0 : _b.startsWith("/")) || ((_d = (_c = app.routing) == null ? void 0 : _c.assetPrefix) == null ? void 0 : _d.endsWith("/"))) {
304
- throw new MicrofrontendError(
305
- `Invalid assetPrefix for application "${name}". Must not start or end with a slash.`,
306
- { type: "zone", subtype: "invalid_asset_prefix" }
307
- );
308
- }
309
- for (const group of ((_e = app.routing) == null ? void 0 : _e.matches) ?? []) {
310
- for (const p of group.paths) {
311
- if (p === "/") {
312
- continue;
313
- }
314
- if (p.endsWith("/")) {
315
- throw new MicrofrontendError(
316
- `Invalid path for application "${name}". ${p} must not end with a slash.`,
317
- { type: "zone", subtype: "invalid_path" }
318
- );
319
- }
320
- if (!p.startsWith("/")) {
321
- throw new MicrofrontendError(
322
- `Invalid path for application "${name}". ${p} must start with a slash.`,
323
- { type: "zone", subtype: "invalid_path" }
324
- );
325
- }
326
- }
327
- }
482
+ getAssetPrefix() {
483
+ return generateAssetPrefixFromName({ name: this.name });
328
484
  }
329
485
  serialize() {
330
- var _a, _b;
331
- if (this.routing === void 0 || this.default) {
332
- return {
333
- default: true,
334
- development: {
335
- local: this.development.local.serialize(),
336
- fallback: (_a = this.development.fallback) == null ? void 0 : _a.serialize()
337
- },
338
- production: this.production.serialize(),
339
- vercel: this.vercel
340
- };
341
- }
342
- return {
343
- default: false,
344
- routing: this.routing,
345
- development: {
346
- local: this.development.local.serialize(),
347
- fallback: (_b = this.development.fallback) == null ? void 0 : _b.serialize()
348
- },
349
- production: this.production.serialize(),
350
- vercel: this.vercel
351
- };
486
+ return this.serialized;
487
+ }
488
+ };
489
+ var DefaultApplication = class extends Application {
490
+ constructor(name, {
491
+ app,
492
+ overrides
493
+ }) {
494
+ super(name, {
495
+ app,
496
+ overrides,
497
+ isDefault: true
498
+ });
499
+ this.default = true;
500
+ this.production = new Host(app.production);
501
+ }
502
+ getAssetPrefix() {
503
+ return "";
504
+ }
505
+ };
506
+ var ChildApplication = class extends Application {
507
+ constructor(name, {
508
+ app,
509
+ overrides
510
+ }) {
511
+ ChildApplication.validate(name, app);
512
+ super(name, {
513
+ app,
514
+ overrides,
515
+ isDefault: false
516
+ });
517
+ this.default = false;
518
+ this.routing = app.routing;
519
+ }
520
+ static validate(name, app) {
521
+ validateAppPaths(name, app);
352
522
  }
353
523
  };
354
524
 
355
- // src/config/common/microfrontend-config.ts
356
- var SUPPORTED_VERSIONS = ["1"];
525
+ // src/config/microfrontends-config/isomorphic/constants.ts
357
526
  var DEFAULT_LOCAL_PROXY_PORT = 3024;
358
- var MicrofrontendConfigCommon = class {
527
+
528
+ // src/config/microfrontends-config/isomorphic/index.ts
529
+ var MicrofrontendConfigIsomorphic = class {
359
530
  constructor({
360
531
  config,
361
- overrides
532
+ overrides,
533
+ meta
362
534
  }) {
363
- this.zones = {};
364
- var _a, _b, _c;
365
- if (!SUPPORTED_VERSIONS.includes(config.version)) {
366
- throw new MicrofrontendError(
367
- `Unsupported version: ${config.version}. Supported versions are: ${SUPPORTED_VERSIONS.join(
368
- ", "
369
- )}`,
370
- { type: "config", subtype: "unsupported_version" }
535
+ this.childApplications = {};
536
+ var _a, _b, _c, _d;
537
+ MicrofrontendConfigIsomorphic.validate(config);
538
+ const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
539
+ this.overrides = overrides && !disableOverrides ? overrides : void 0;
540
+ this.isMainConfig = isMainConfig(config);
541
+ if (isMainConfig(config)) {
542
+ for (const [appId, appConfig] of Object.entries(config.applications)) {
543
+ const appOverrides = !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.applications[appId] : void 0;
544
+ if (isDefaultApp(appConfig)) {
545
+ this.defaultApplication = new DefaultApplication(appId, {
546
+ app: appConfig,
547
+ overrides: appOverrides
548
+ });
549
+ } else {
550
+ this.childApplications[appId] = new ChildApplication(appId, {
551
+ app: appConfig,
552
+ overrides: appOverrides
553
+ });
554
+ }
555
+ }
556
+ } else {
557
+ this.partOf = config.partOf;
558
+ const appOverrides = !disableOverrides ? (_d = this.overrides) == null ? void 0 : _d.applications[meta.fromApp] : void 0;
559
+ this.childApplications[meta.fromApp] = new ChildApplication(
560
+ meta.fromApp,
561
+ {
562
+ // we don't know routing because we're not in the main config
563
+ app: { routing: [] },
564
+ overrides: appOverrides
565
+ }
371
566
  );
372
567
  }
373
- const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
374
- this.overrides = overrides && !disableOverrides ? new Overrides(overrides) : void 0;
375
- for (const [zoneName, zoneConfig] of Object.entries(config.applications)) {
376
- this.zones[zoneName] = new Application(zoneName, {
377
- app: zoneConfig,
378
- overrides: !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.config.applications[zoneName] : void 0
379
- });
568
+ if (isMainConfig(config) && !this.defaultApplication) {
569
+ throw new MicrofrontendError(
570
+ `Could not find default application in microfrontends configuration`,
571
+ {
572
+ type: "application",
573
+ subtype: "not_found"
574
+ }
575
+ );
380
576
  }
381
577
  this.config = config;
382
- this.name = config.name;
383
- this.version = config.version;
384
578
  this.options = config.options;
385
- this.$schema = config.$schema;
579
+ this.serialized = {
580
+ config,
581
+ overrides,
582
+ meta
583
+ };
584
+ }
585
+ static validate(config) {
586
+ const c = typeof config === "string" ? parse(config) : config;
587
+ if (isMainConfig(c)) {
588
+ validateConfigVersion(c.version);
589
+ validateConfigPaths(c.applications);
590
+ validateConfigDefaultApplication(c.applications);
591
+ }
592
+ return c;
593
+ }
594
+ static fromEnv({
595
+ meta,
596
+ cookies
597
+ }) {
598
+ return new MicrofrontendConfigIsomorphic({
599
+ config: parse(getConfigStringFromEnv()),
600
+ overrides: parseOverrides(cookies ?? []),
601
+ meta
602
+ });
386
603
  }
387
604
  isOverridesDisabled() {
388
605
  var _a, _b;
389
606
  return ((_b = (_a = this.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
390
607
  }
391
- static getConfigFromEnv() {
392
- const config = process.env.MFE_CONFIG;
393
- if (!config) {
394
- throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
395
- type: "config",
396
- subtype: "not_found_in_env"
397
- });
398
- }
399
- return config;
400
- }
401
- static fromEnv(_) {
402
- throw new Error("Not implemented");
403
- }
404
608
  getConfig() {
405
609
  return this.config;
406
610
  }
407
- getAllApplications() {
408
- return Object.values(this.zones);
611
+ getApplicationsByType() {
612
+ return {
613
+ defaultApplication: this.defaultApplication,
614
+ applications: Object.values(this.childApplications)
615
+ };
409
616
  }
410
- getZone(name) {
411
- const zone = this.zones[name];
412
- if (!zone) {
617
+ getChildApplications() {
618
+ return Object.values(this.childApplications);
619
+ }
620
+ getAllApplications() {
621
+ return [
622
+ this.defaultApplication,
623
+ ...Object.values(this.childApplications)
624
+ ].filter(Boolean);
625
+ }
626
+ getApplication(name) {
627
+ var _a;
628
+ if (((_a = this.defaultApplication) == null ? void 0 : _a.name) === name) {
629
+ return this.defaultApplication;
630
+ }
631
+ const app = this.childApplications[name];
632
+ if (!app) {
413
633
  throw new MicrofrontendError(
414
634
  `Could not find microfrontends configuration for application "${name}"`,
415
635
  {
416
- type: "zone",
636
+ type: "application",
417
637
  subtype: "not_found"
418
638
  }
419
639
  );
420
640
  }
421
- return zone;
641
+ return app;
422
642
  }
423
643
  getApplicationByProjectId(projectId) {
424
- return Object.values(this.zones).find(
425
- (zone) => {
426
- var _a;
427
- return ((_a = zone.vercel) == null ? void 0 : _a.projectId) === projectId;
644
+ var _a, _b;
645
+ if (((_b = (_a = this.defaultApplication) == null ? void 0 : _a.vercel) == null ? void 0 : _b.projectId) === projectId) {
646
+ return this.defaultApplication;
647
+ }
648
+ return Object.values(this.childApplications).find(
649
+ (app) => {
650
+ var _a2;
651
+ return ((_a2 = app.vercel) == null ? void 0 : _a2.projectId) === projectId;
428
652
  }
429
653
  );
430
654
  }
431
- getDefaultZone() {
432
- const zone = Object.values(this.zones).find((z) => z.default);
433
- if (!zone) {
655
+ /**
656
+ * Returns the default application. This can throw if the default application
657
+ * is undefined ( )
658
+ */
659
+ getDefaultApplication() {
660
+ if (!this.defaultApplication) {
434
661
  throw new MicrofrontendError(
435
- `Could not find default zone in microfrontends configuration`,
662
+ `Could not find default application in microfrontends configuration`,
436
663
  {
437
- type: "zone",
664
+ type: "application",
438
665
  subtype: "not_found"
439
666
  }
440
667
  );
441
668
  }
442
- return zone;
669
+ return this.defaultApplication;
443
670
  }
444
671
  /**
445
672
  * Returns the configured port for the local proxy
@@ -454,671 +681,801 @@ var MicrofrontendConfigCommon = class {
454
681
  * NOTE: This is used when writing the config to disk and must always match the input Schema
455
682
  */
456
683
  toSchemaJson() {
457
- const applications = {};
458
- for (const [name, zone] of Object.entries(this.zones)) {
459
- applications[name] = zone.serialize();
684
+ return this.serialized.config;
685
+ }
686
+ toClientConfig() {
687
+ const applications = Object.fromEntries(
688
+ Object.entries(this.childApplications).map(([name, application]) => [
689
+ name,
690
+ {
691
+ default: false,
692
+ routing: application.routing
693
+ }
694
+ ])
695
+ );
696
+ if (this.defaultApplication) {
697
+ applications[this.defaultApplication.name] = {
698
+ default: true
699
+ };
460
700
  }
461
- return {
462
- $schema: this.$schema,
463
- name: this.name,
464
- version: this.version,
465
- options: this.options,
701
+ return new MicrofrontendConfigClient({
466
702
  applications
467
- };
703
+ });
468
704
  }
469
705
  serialize() {
470
- var _a;
471
- const applications = {};
472
- for (const [name, zone] of Object.entries(this.zones)) {
473
- applications[name] = zone.serialize();
706
+ return this.serialized;
707
+ }
708
+ };
709
+
710
+ // src/config/microfrontends-config/isomorphic/child.ts
711
+ var MicrofrontendChildConfig = class extends MicrofrontendConfigIsomorphic {
712
+ constructor({
713
+ config,
714
+ overrides,
715
+ meta
716
+ }) {
717
+ super({ config, overrides, meta });
718
+ this.isMainConfig = false;
719
+ this.partOf = config.partOf;
720
+ }
721
+ };
722
+
723
+ // src/config/microfrontends-config/isomorphic/main.ts
724
+ var MicrofrontendMainConfig = class extends MicrofrontendConfigIsomorphic {
725
+ constructor({
726
+ config,
727
+ overrides,
728
+ meta
729
+ }) {
730
+ var _a, _b, _c;
731
+ super({ config, overrides, meta });
732
+ this.isMainConfig = true;
733
+ const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
734
+ let defaultApplication;
735
+ for (const [appId, appConfig] of Object.entries(config.applications)) {
736
+ const appOverrides = !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.applications[appId] : void 0;
737
+ if (isDefaultApp(appConfig)) {
738
+ defaultApplication = new DefaultApplication(appId, {
739
+ app: appConfig,
740
+ overrides: appOverrides
741
+ });
742
+ } else {
743
+ this.childApplications[appId] = new ChildApplication(appId, {
744
+ app: appConfig,
745
+ overrides: appOverrides
746
+ });
747
+ }
474
748
  }
475
- return {
476
- config: {
477
- name: this.name,
478
- version: this.version,
479
- applications,
480
- options: this.options,
481
- $schema: this.$schema
482
- },
483
- overrides: (_a = this.overrides) == null ? void 0 : _a.serialize()
484
- };
749
+ if (!defaultApplication) {
750
+ throw new MicrofrontendError(
751
+ `Could not find default application in microfrontends configuration`,
752
+ {
753
+ type: "application",
754
+ subtype: "not_found"
755
+ }
756
+ );
757
+ }
758
+ this.defaultApplication = defaultApplication;
485
759
  }
486
- write(_) {
487
- throw new MicrofrontendError(
488
- `Writing to file to disk requires using an instance of "MicrofrontendConfig".`,
489
- { type: "config", subtype: "unsupported_operation" }
490
- );
760
+ };
761
+
762
+ // src/config/microfrontends/isomorphic/index.ts
763
+ var Microfrontends = class {
764
+ constructor({
765
+ config,
766
+ overrides,
767
+ meta
768
+ }) {
769
+ if (isMainConfig(config)) {
770
+ this.config = new MicrofrontendMainConfig({ config, overrides, meta });
771
+ } else {
772
+ this.config = new MicrofrontendChildConfig({ config, overrides, meta });
773
+ }
774
+ }
775
+ isChildConfig() {
776
+ return this.config instanceof MicrofrontendChildConfig;
777
+ }
778
+ static fromEnv({
779
+ cookies,
780
+ meta
781
+ }) {
782
+ const config = MicrofrontendConfigIsomorphic.fromEnv({
783
+ cookies,
784
+ meta
785
+ });
786
+ return new Microfrontends(config.serialize());
491
787
  }
492
788
  };
493
789
 
494
- // src/config/utils/get-output-file-path.ts
495
- import path2 from "node:path";
790
+ // src/config/microfrontends/utils/find-repository-root.ts
791
+ import fs from "node:fs";
792
+ import path from "node:path";
793
+ var GIT_DIRECTORY = ".git";
794
+ function findRepositoryRoot(startDir) {
795
+ let currentDir = startDir || process.cwd();
796
+ while (currentDir !== path.parse(currentDir).root) {
797
+ const gitPath = path.join(currentDir, GIT_DIRECTORY);
798
+ if (fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory()) {
799
+ return currentDir;
800
+ }
801
+ currentDir = path.dirname(currentDir);
802
+ }
803
+ throw new Error(
804
+ "Repository root not found. Specify the root of the repository with the `repository.root` option."
805
+ );
806
+ }
807
+
808
+ // src/config/microfrontends/utils/find-package-path.ts
809
+ import { dirname } from "node:path";
810
+ import { readFileSync } from "node:fs";
811
+ import fg from "fast-glob";
812
+ var configCache = {};
813
+ function findPackagePathWithGlob({
814
+ repositoryRoot,
815
+ name
816
+ }) {
817
+ try {
818
+ const packageJsonPaths = fg.globSync("**/package.json", {
819
+ cwd: repositoryRoot,
820
+ absolute: true,
821
+ onlyFiles: true,
822
+ followSymbolicLinks: false,
823
+ ignore: ["**/node_modules/**", "**/.git/**"]
824
+ });
825
+ const matchingPaths = [];
826
+ for (const packageJsonPath2 of packageJsonPaths) {
827
+ const packageJsonContent = readFileSync(packageJsonPath2, "utf-8");
828
+ const packageJson = JSON.parse(packageJsonContent);
829
+ if (packageJson.name === name) {
830
+ matchingPaths.push(packageJsonPath2);
831
+ }
832
+ }
833
+ if (matchingPaths.length > 1) {
834
+ throw new Error(
835
+ `Found multiple packages with the name "${name}" in the repository: ${matchingPaths.join(", ")}`
836
+ );
837
+ }
838
+ if (matchingPaths.length === 0) {
839
+ throw new Error(
840
+ `Could not find package with the name "${name}" in the repository`
841
+ );
842
+ }
843
+ const [packageJsonPath] = matchingPaths;
844
+ return dirname(packageJsonPath);
845
+ } catch (error) {
846
+ return null;
847
+ }
848
+ }
849
+ function findPackagePath(opts) {
850
+ const cacheKey = `${opts.repositoryRoot}-${opts.name}`;
851
+ if (configCache[cacheKey]) {
852
+ return configCache[cacheKey];
853
+ }
854
+ const result = findPackagePathWithGlob(opts);
855
+ if (!result) {
856
+ throw new Error(
857
+ `Could not find package with the name "${opts.name}" in the repository`
858
+ );
859
+ }
860
+ configCache[cacheKey] = result;
861
+ return result;
862
+ }
863
+
864
+ // src/config/microfrontends/utils/find-default-package.ts
865
+ import { dirname as dirname2 } from "node:path";
866
+ import { readFileSync as readFileSync2 } from "node:fs";
867
+ import { parse as parse2 } from "jsonc-parser";
868
+ import fg2 from "fast-glob";
496
869
 
497
870
  // src/config/constants.ts
498
- var MFE_CONFIG_DEFAULT_FILE_PATH2 = "micro-frontends";
499
- var MFE_CONFIG_DEFAULT_FILE_NAME2 = "micro-frontends.config.json";
871
+ var CONFIGURATION_FILENAMES = [
872
+ "microfrontends.jsonc",
873
+ "microfrontends.json"
874
+ ];
875
+
876
+ // src/config/microfrontends/utils/find-default-package.ts
877
+ var configCache2 = {};
878
+ function findDefaultMicrofrontendsPackages({
879
+ repositoryRoot,
880
+ applicationName
881
+ }) {
882
+ try {
883
+ const microfrontendsJsonPaths = fg2.globSync(
884
+ `**/{${CONFIGURATION_FILENAMES.join(",")}}`,
885
+ {
886
+ cwd: repositoryRoot,
887
+ absolute: true,
888
+ onlyFiles: true,
889
+ followSymbolicLinks: false,
890
+ ignore: ["**/node_modules/**", "**/.git/**"]
891
+ }
892
+ );
893
+ const matchingPaths = [];
894
+ for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
895
+ const microfrontendsJsonContent = readFileSync2(
896
+ microfrontendsJsonPath,
897
+ "utf-8"
898
+ );
899
+ const microfrontendsJson = parse2(microfrontendsJsonContent);
900
+ if (isMainConfig(microfrontendsJson) && microfrontendsJson.applications[applicationName]) {
901
+ matchingPaths.push(microfrontendsJsonPath);
902
+ }
903
+ }
904
+ if (matchingPaths.length > 1) {
905
+ throw new Error(
906
+ `Found multiple default applications referencing "${applicationName}" in the repository, this is not yet supported.
907
+ ${matchingPaths.join("\n \u2022 ")}`
908
+ );
909
+ }
910
+ if (matchingPaths.length === 0) {
911
+ throw new Error(
912
+ `Could not find default application with "applications.${applicationName}"`
913
+ );
914
+ }
915
+ const [packageJsonPath] = matchingPaths;
916
+ return dirname2(packageJsonPath);
917
+ } catch (error) {
918
+ return null;
919
+ }
920
+ }
921
+ function findDefaultMicrofrontendsPackage(opts) {
922
+ const cacheKey = `${opts.repositoryRoot}-${opts.applicationName}`;
923
+ if (configCache2[cacheKey]) {
924
+ return configCache2[cacheKey];
925
+ }
926
+ const result = findDefaultMicrofrontendsPackages(opts);
927
+ if (!result) {
928
+ throw new Error(
929
+ `Error trying to resolve the main microfrontends configuration`
930
+ );
931
+ }
932
+ configCache2[cacheKey] = result;
933
+ return result;
934
+ }
935
+
936
+ // src/config/microfrontends/utils/is-monorepo.ts
937
+ import fs2 from "node:fs";
938
+ import path2 from "node:path";
939
+ function isMonorepo({
940
+ repositoryRoot
941
+ }) {
942
+ try {
943
+ if (fs2.existsSync(path2.join(repositoryRoot, "pnpm-workspace.yaml"))) {
944
+ return true;
945
+ }
946
+ if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
947
+ return true;
948
+ }
949
+ const packageJsonPath = path2.join(repositoryRoot, "package.json");
950
+ if (!fs2.existsSync(packageJsonPath)) {
951
+ return false;
952
+ }
953
+ const packageJson = JSON.parse(
954
+ fs2.readFileSync(packageJsonPath, "utf-8")
955
+ );
956
+ return packageJson.workspaces !== void 0;
957
+ } catch (error) {
958
+ console.error("Error determining if repository is a monorepo", error);
959
+ return false;
960
+ }
961
+ }
962
+
963
+ // src/config/microfrontends/utils/find-package-root.ts
964
+ import fs3 from "node:fs";
965
+ import path3 from "node:path";
966
+ var PACKAGE_JSON = "package.json";
967
+ function findPackageRoot(startDir) {
968
+ let currentDir = startDir || process.cwd();
969
+ while (currentDir !== path3.parse(currentDir).root) {
970
+ const pkgJsonPath = path3.join(currentDir, PACKAGE_JSON);
971
+ if (fs3.existsSync(pkgJsonPath)) {
972
+ return currentDir;
973
+ }
974
+ currentDir = path3.dirname(currentDir);
975
+ }
976
+ throw new Error(
977
+ "Package root not found. Specify the root of the package with the `package.root` option."
978
+ );
979
+ }
980
+
981
+ // src/config/microfrontends/utils/find-config.ts
982
+ import fs4 from "node:fs";
983
+ import { join } from "node:path";
984
+ function findConfig({ dir }) {
985
+ for (const filename of CONFIGURATION_FILENAMES) {
986
+ const maybeConfig = join(dir, filename);
987
+ if (fs4.existsSync(maybeConfig)) {
988
+ return maybeConfig;
989
+ }
990
+ }
991
+ return null;
992
+ }
993
+
994
+ // src/config/microfrontends/server/utils/get-output-file-path.ts
995
+ import path4 from "node:path";
500
996
 
501
- // src/config/utils/get-output-file-path.ts
502
- function getOutputFilePath2() {
997
+ // src/config/microfrontends/server/constants.ts
998
+ var MFE_CONFIG_DEFAULT_FILE_PATH = "microfrontends";
999
+ var MFE_CONFIG_DEFAULT_FILE_NAME = "microfrontends.json";
1000
+
1001
+ // src/utils/is-vercel.ts
1002
+ function isVercel() {
1003
+ return process.env.VERCEL === "1";
1004
+ }
1005
+
1006
+ // src/config/microfrontends/server/utils/get-output-file-path.ts
1007
+ function getOutputFilePath() {
503
1008
  if (isVercel()) {
504
- return path2.join(
1009
+ return path4.join(
505
1010
  ".vercel",
506
- MFE_CONFIG_DEFAULT_FILE_PATH2,
507
- MFE_CONFIG_DEFAULT_FILE_NAME2
1011
+ MFE_CONFIG_DEFAULT_FILE_PATH,
1012
+ MFE_CONFIG_DEFAULT_FILE_NAME
508
1013
  );
509
1014
  }
510
- return path2.join(MFE_CONFIG_DEFAULT_FILE_PATH2, MFE_CONFIG_DEFAULT_FILE_NAME2);
1015
+ return path4.join(MFE_CONFIG_DEFAULT_FILE_PATH, MFE_CONFIG_DEFAULT_FILE_NAME);
511
1016
  }
512
1017
 
513
- // src/config/validation.ts
514
- import { parse } from "jsonc-parser";
515
- import { pathToRegexp, parse as parsePathRegexp } from "path-to-regexp";
1018
+ // src/config/microfrontends/server/validation.ts
1019
+ import { parse as parse3 } from "jsonc-parser";
516
1020
  import { Ajv } from "ajv";
517
1021
 
518
- // src/config/types.ts
519
- var isDefaultApplicationConfig = (app) => app.default && typeof app.routing === "undefined";
520
-
521
1022
  // schema/schema.json
522
1023
  var schema_default = {
523
1024
  $schema: "http://json-schema.org/draft-07/schema#",
524
1025
  $ref: "#/definitions/Config",
525
1026
  definitions: {
526
1027
  Config: {
1028
+ anyOf: [
1029
+ {
1030
+ $ref: "#/definitions/MainConfig"
1031
+ },
1032
+ {
1033
+ $ref: "#/definitions/ChildConfig"
1034
+ }
1035
+ ]
1036
+ },
1037
+ MainConfig: {
527
1038
  type: "object",
528
1039
  properties: {
529
- version: {
530
- type: "string"
531
- },
532
1040
  $schema: {
533
1041
  type: "string"
534
1042
  },
535
- name: {
1043
+ version: {
536
1044
  type: "string",
537
- description: 'Name for the micro-frontend site (eg. "vercel.com", "vercel-site" etc.).'
1045
+ const: "2"
1046
+ },
1047
+ options: {
1048
+ $ref: "#/definitions/Options"
1049
+ },
1050
+ remotes: {
1051
+ type: "object",
1052
+ additionalProperties: {
1053
+ $ref: "#/definitions/Application"
1054
+ },
1055
+ description: "Applications that only serve a subset of the microfrontend routes only need to reference the name of the primary application that owns the full microfrontends configuration."
538
1056
  },
539
1057
  applications: {
540
- $ref: "#/definitions/ApplicationConfigsById"
1058
+ $ref: "#/definitions/ApplicationRouting",
1059
+ description: "Mapping of application names to the routes that they host. Only needs to be defined in the application that owns the primary microfrontend domain"
1060
+ }
1061
+ },
1062
+ required: ["applications", "version"],
1063
+ additionalProperties: false
1064
+ },
1065
+ Options: {
1066
+ type: "object",
1067
+ properties: {
1068
+ vercel: {
1069
+ $ref: "#/definitions/VercelOptions",
1070
+ description: "Micro-Frontends wide options for Vercel."
541
1071
  },
542
- options: {
543
- $ref: "#/definitions/Options",
544
- description: "Optional configuration for the entire micro-frontends setup."
1072
+ localProxy: {
1073
+ $ref: "#/definitions/LocalProxyOptions",
1074
+ description: "Options for local proxy."
545
1075
  }
546
1076
  },
547
- required: ["version", "applications"],
548
- description: "Configuration for micro-frontend applications\n\nTODO: Add proxy configuration"
1077
+ additionalProperties: false
549
1078
  },
550
- ApplicationConfigsById: {
1079
+ VercelOptions: {
551
1080
  type: "object",
552
- additionalProperties: {
553
- $ref: "#/definitions/ApplicationConfig"
554
- }
1081
+ properties: {
1082
+ teamSlug: {
1083
+ type: "string",
1084
+ description: "Team slug for the Vercel team"
1085
+ },
1086
+ disableOverrides: {
1087
+ type: "boolean",
1088
+ description: "If you want to disable the overrides for the site. For example, if you are managing rewrites between applications externally, you may wish to disable the overrides on the toolbar as they will have no effect."
1089
+ }
1090
+ },
1091
+ additionalProperties: false
1092
+ },
1093
+ LocalProxyOptions: {
1094
+ type: "object",
1095
+ properties: {
1096
+ port: {
1097
+ type: "number",
1098
+ description: "The port number used by the local proxy server.\n\nThe default is `3024`."
1099
+ }
1100
+ },
1101
+ additionalProperties: false
555
1102
  },
556
- ApplicationConfig: {
1103
+ Application: {
557
1104
  anyOf: [
558
1105
  {
559
- $ref: "#/definitions/DefaultApplicationConfig"
1106
+ $ref: "#/definitions/DefaultApplication"
560
1107
  },
561
1108
  {
562
- $ref: "#/definitions/CommonApplicationConfig"
1109
+ $ref: "#/definitions/ChildApplication"
563
1110
  }
564
- ],
565
- description: "A Micro-Frontend Deployment Target"
1111
+ ]
566
1112
  },
567
- DefaultApplicationConfig: {
1113
+ DefaultApplication: {
568
1114
  type: "object",
569
1115
  properties: {
570
- default: {
571
- type: "boolean",
572
- const: true,
573
- description: "The default application is used no other application is matched via the routing config"
574
- },
575
- routing: {
576
- $ref: "#/definitions/Routing"
1116
+ vercel: {
1117
+ $ref: "#/definitions/Vercel"
577
1118
  },
578
1119
  development: {
579
- type: "object",
580
- properties: {
581
- local: {
582
- $ref: "#/definitions/HostConfig"
583
- },
584
- fallback: {
585
- $ref: "#/definitions/HostConfig",
586
- description: "Fallback for local development, could be a host config that points to any environment. If this is not provided, or the application is not running - requests to the application in local development will error."
587
- },
588
- task: {
589
- type: "string",
590
- description: "Optional task to run when starting the development server. Should reference a script in the package.json of the application."
591
- }
592
- },
593
- required: ["local"]
1120
+ $ref: "#/definitions/Development"
594
1121
  },
595
1122
  production: {
596
1123
  $ref: "#/definitions/HostConfig"
597
- },
598
- metadata: {
599
- type: "object",
600
- additionalProperties: {
601
- type: "string"
602
- }
603
- },
604
- federation: {
605
- type: "object",
606
- properties: {
607
- exposes: {
608
- type: "array",
609
- items: {
610
- type: "object",
611
- properties: {
612
- name: {
613
- type: "string",
614
- description: "The name of the module - should be used when importing the module from another application"
615
- },
616
- path: {
617
- type: "string",
618
- description: "Relative path to the module within its `application`"
619
- }
620
- },
621
- required: ["name", "path"]
622
- },
623
- description: "Modules that are exposed by this application"
624
- },
625
- uses: {
626
- type: "array",
627
- items: {
628
- type: "string"
629
- },
630
- description: "Modules that are used by this application. Only the name of the module is required."
631
- }
632
- }
633
- },
634
- vercel: {
635
- $ref: "#/definitions/Vercel"
636
1124
  }
637
1125
  },
638
- required: ["default", "development", "production"]
1126
+ required: ["production"],
1127
+ additionalProperties: false
639
1128
  },
640
- Routing: {
1129
+ Vercel: {
641
1130
  type: "object",
642
1131
  properties: {
643
- assetPrefix: {
1132
+ projectId: {
644
1133
  type: "string",
645
- description: "[assetPrefix] for the application"
646
- },
647
- matches: {
648
- type: "array",
649
- items: {
650
- $ref: "#/definitions/PathGroup"
651
- },
652
- description: "Path expressions that are routed to this application."
1134
+ description: "Vercel project ID"
653
1135
  }
654
1136
  },
655
- required: ["matches"]
1137
+ required: ["projectId"],
1138
+ additionalProperties: false
656
1139
  },
657
- PathGroup: {
1140
+ Development: {
658
1141
  type: "object",
659
1142
  properties: {
660
- group: {
661
- type: "string",
662
- description: "Optional group name for the paths"
1143
+ local: {
1144
+ $ref: "#/definitions/LocalHostConfig"
663
1145
  },
664
- options: {
665
- type: "object",
666
- properties: {
667
- flag: {
668
- type: "string",
669
- description: "flag name that can be used to enable/disable all paths in the group"
670
- }
671
- }
1146
+ fallback: {
1147
+ $ref: "#/definitions/HostConfig",
1148
+ description: "Fallback for local development, could be a host config that points to any environment. If this is not provided, or the application is not running - requests to the application in local development will error."
672
1149
  },
673
- paths: {
674
- type: "array",
675
- items: {
676
- type: "string"
677
- }
1150
+ task: {
1151
+ type: "string",
1152
+ description: "Optional task to run when starting the development server. Should reference a script in the package.json of the application."
678
1153
  }
679
1154
  },
680
- required: ["paths"]
1155
+ additionalProperties: false
681
1156
  },
682
- HostConfig: {
1157
+ LocalHostConfig: {
683
1158
  type: "object",
1159
+ additionalProperties: false,
684
1160
  properties: {
685
- protocol: {
686
- type: "string",
687
- enum: ["http", "https"],
688
- description: 'The protocol to be used for the connection.\n- `http`: Hypertext Transfer Protocol (HTTP).\n- `https`: Secure Hypertext Transfer Protocol (HTTPS).\n\n* @defaultValue "https"'
689
- },
690
1161
  host: {
691
1162
  type: "string",
692
1163
  description: "The hostname or IP address of the server. This can be a domain name (e.g., `example.com`) or an IP address (e.g., `192.168.1.1`)."
693
1164
  },
1165
+ protocol: {
1166
+ type: "string",
1167
+ enum: ["http", "https"],
1168
+ description: 'The protocol to be used for the connection.\n- `http`: Hypertext Transfer Protocol (HTTP).\n- `https`: Secure Hypertext Transfer Protocol (HTTPS).\n\n* @defaultValue "http" for local development, "https" for otherwise'
1169
+ },
694
1170
  port: {
695
1171
  type: "number",
696
- description: "The port number to be used for the connection. Common values include `80` for HTTP and `443` for HTTPS.\n\nThis field is optional and can be omitted if the default ports for the specified protocol are used"
1172
+ description: "The port number to be used for the connection. Common values include `80` for HTTP and `443` for HTTPS."
697
1173
  }
698
- },
699
- required: ["host"]
1174
+ }
700
1175
  },
701
- Vercel: {
1176
+ HostConfig: {
702
1177
  type: "object",
703
1178
  properties: {
704
- projectId: {
705
- type: "string",
706
- description: "Vercel project ID"
707
- },
708
- projectName: {
1179
+ protocol: {
709
1180
  type: "string",
710
- description: "Vercel project name (temporary until we can use project ID)"
1181
+ enum: ["http", "https"],
1182
+ description: 'The protocol to be used for the connection.\n- `http`: Hypertext Transfer Protocol (HTTP).\n- `https`: Secure Hypertext Transfer Protocol (HTTPS).\n\n* @defaultValue "http" for local development, "https" for otherwise'
711
1183
  },
712
- defaultRoute: {
1184
+ host: {
713
1185
  type: "string",
714
- description: "The default route for the application. Used to render screenshots, favicons, and provide direct zone links"
1186
+ description: "The hostname or IP address of the server. This can be a domain name (e.g., `example.com`) or an IP address (e.g., `192.168.1.1`)."
715
1187
  },
716
- routeSpeedInsightsToDefaultZone: {
717
- type: "boolean",
718
- description: "Whether to route Speed Insights to the default zone or each individual microfrontend."
1188
+ port: {
1189
+ type: "number",
1190
+ description: "The port number to be used for the connection. Common values include `80` for HTTP and `443` for HTTPS."
719
1191
  }
720
1192
  },
721
- required: ["projectId"]
1193
+ required: ["host"],
1194
+ additionalProperties: false
722
1195
  },
723
- CommonApplicationConfig: {
1196
+ ChildApplication: {
724
1197
  type: "object",
725
1198
  properties: {
726
- default: {
727
- type: "boolean",
728
- const: false,
729
- description: "The default application is used no other application is matched via the routing config"
730
- },
731
- routing: {
732
- $ref: "#/definitions/Routing"
1199
+ vercel: {
1200
+ $ref: "#/definitions/Vercel"
733
1201
  },
734
1202
  development: {
735
- type: "object",
736
- properties: {
737
- local: {
738
- $ref: "#/definitions/HostConfig"
739
- },
740
- fallback: {
741
- $ref: "#/definitions/HostConfig",
742
- description: "Fallback for local development, could be a host config that points to any environment. If this is not provided, or the application is not running - requests to the application in local development will error."
743
- },
744
- task: {
745
- type: "string",
746
- description: "Optional task to run when starting the development server. Should reference a script in the package.json of the application."
747
- }
748
- },
749
- required: ["local"]
1203
+ $ref: "#/definitions/Development"
1204
+ },
1205
+ routing: {
1206
+ $ref: "#/definitions/Routing",
1207
+ description: "Groups of path expressions that are routed to this application."
750
1208
  },
751
1209
  production: {
752
1210
  $ref: "#/definitions/HostConfig"
753
- },
754
- metadata: {
755
- type: "object",
756
- additionalProperties: {
757
- type: "string"
758
- }
759
- },
760
- federation: {
761
- type: "object",
762
- properties: {
763
- exposes: {
764
- type: "array",
765
- items: {
766
- type: "object",
767
- properties: {
768
- name: {
769
- type: "string",
770
- description: "The name of the module - should be used when importing the module from another application"
771
- },
772
- path: {
773
- type: "string",
774
- description: "Relative path to the module within its `application`"
775
- }
776
- },
777
- required: ["name", "path"]
778
- },
779
- description: "Modules that are exposed by this application"
780
- },
781
- uses: {
782
- type: "array",
783
- items: {
784
- type: "string"
785
- },
786
- description: "Modules that are used by this application. Only the name of the module is required."
787
- }
788
- }
789
- },
790
- vercel: {
791
- $ref: "#/definitions/Vercel"
792
1211
  }
793
1212
  },
794
- required: ["default", "development", "production", "routing"]
795
- },
796
- Options: {
797
- type: "object",
798
- properties: {
799
- vercel: {
800
- $ref: "#/definitions/VercelOptions",
801
- description: "Micro-Frontends wide options for Vercel."
802
- },
803
- localProxy: {
804
- $ref: "#/definitions/LocalProxyOptions",
805
- description: "Options for local proxy."
806
- }
807
- }
1213
+ required: ["routing"],
1214
+ additionalProperties: false
808
1215
  },
809
- VercelOptions: {
810
- type: "object",
811
- properties: {
812
- previewDeploymentSuffix: {
813
- type: "string",
814
- description: "If your team uses a custom Preview Deployment Suffix, please specify it here. See https://vercel.com/docs/deployments/preview-deployment-suffix. The default is `vercel.app`."
815
- },
816
- teamSlug: {
817
- type: "string",
818
- description: "Team slug for the Vercel team"
819
- },
820
- disableOverrides: {
821
- type: "boolean",
822
- description: "If you want to disable the overrides for the site. For example, if you are managing rewrites between applications externally, you may wish to disable the overrides on the toolbar as they will have no effect."
823
- }
824
- }
825
- },
826
- LocalProxyOptions: {
827
- type: "object",
828
- properties: {
829
- port: {
830
- type: "number",
831
- description: "The port number used by the local proxy server.\n\nThe default is `3024`."
832
- }
833
- }
834
- }
835
- }
836
- };
837
-
838
- // src/config/utils/load-schema.ts
839
- var SCHEMA = schema_default;
840
-
841
- // src/config/validation.ts
842
- var validateSchema = (configString) => {
843
- const parsedConfig = parse(configString);
844
- const ajv = new Ajv();
845
- const validate = ajv.compile(SCHEMA);
846
- const isValid = validate(parsedConfig);
847
- if (!isValid) {
848
- throw new MicrofrontendError(
849
- `Invalid config: ${ajv.errorsText(validate.errors)}`,
850
- { type: "config", subtype: "does_not_match_schema" }
851
- );
852
- }
853
- return parsedConfig;
854
- };
855
- var SUPPORTED_VERSIONS2 = ["1"];
856
- var validateVersion = (version) => {
857
- if (!SUPPORTED_VERSIONS2.includes(version)) {
858
- throw new MicrofrontendError(
859
- `Unsupported version: ${version}. Supported versions are: ${SUPPORTED_VERSIONS2.join(
860
- ", "
861
- )}`,
862
- { type: "config", subtype: "unsupported_version" }
863
- );
864
- }
865
- };
866
- function validateMainPath(applicationConfigsById) {
867
- for (const [id, app] of Object.entries(applicationConfigsById)) {
868
- const { defaultRoute } = app.vercel ?? {};
869
- if (!defaultRoute) {
870
- continue;
871
- }
872
- if (isDefaultApplicationConfig(app)) {
873
- const pathsWithApp = [];
874
- for (const [otherId, otherApp] of Object.entries(
875
- applicationConfigsById
876
- )) {
877
- if (isDefaultApplicationConfig(otherApp)) {
878
- continue;
879
- }
880
- pathsWithApp.push({
881
- id: otherId,
882
- paths: otherApp.routing.matches.flatMap((match) => match.paths)
883
- });
884
- }
885
- for (const { id: otherId, paths } of pathsWithApp) {
886
- const isValid = paths.every((path3) => {
887
- const matcher = pathToRegexp(path3);
888
- return !matcher.test(defaultRoute);
889
- });
890
- if (!isValid) {
891
- throw new MicrofrontendError(
892
- `default route "${defaultRoute}" cannot be used for "${id}" because it is matched by "${otherId}"`,
893
- { type: "config", subtype: "invalid_main_path" }
894
- );
895
- }
896
- }
897
- } else {
898
- const allPaths = app.routing.matches.flatMap((match) => match.paths);
899
- const isValid = allPaths.some((path3) => {
900
- const matcher = pathToRegexp(path3);
901
- return matcher.test(defaultRoute);
902
- });
903
- if (!isValid) {
904
- throw new MicrofrontendError(
905
- `default route "${defaultRoute}" is not included by the routing config for application "${id}"`,
906
- { type: "config", subtype: "invalid_main_path" }
907
- );
908
- }
909
- }
910
- }
911
- }
912
- var validatePaths = (applicationConfigsById) => {
913
- const pathsByApplicationId = /* @__PURE__ */ new Map();
914
- const errors = [];
915
- for (const [id, app] of Object.entries(applicationConfigsById)) {
916
- if (isDefaultApplicationConfig(app)) {
917
- continue;
918
- }
919
- for (const pathMatch of app.routing.matches) {
920
- for (const path3 of pathMatch.paths) {
921
- const maybeError = validatePathExpression(path3);
922
- if (maybeError) {
923
- errors.push(maybeError);
924
- }
925
- const existing = pathsByApplicationId.get(path3);
926
- if (existing) {
927
- existing.applications.push(id);
928
- } else {
929
- pathsByApplicationId.set(path3, {
930
- applications: [id],
931
- matcher: pathToRegexp(path3),
932
- applicationId: id
933
- });
934
- }
935
- }
936
- }
937
- }
938
- const entries = Array.from(pathsByApplicationId.entries());
939
- entries.forEach(([path3, { applications: ids, matcher, applicationId }]) => {
940
- if (ids.length > 1) {
941
- errors.push(
942
- `Duplicate path "${path3}" for applications "${ids.join(", ")}"`
943
- );
944
- }
945
- entries.forEach(
946
- ([
947
- matchPath,
948
- { applications: matchIds, applicationId: matchApplicationId }
949
- ]) => {
950
- if (path3 === matchPath) {
951
- return;
952
- }
953
- if (applicationId === matchApplicationId) {
954
- return;
955
- }
956
- if (matcher.test(matchPath)) {
957
- const source = `"${path3}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
958
- const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
959
- errors.push(
960
- `Overlapping path detected between ${source} and ${destination}`
961
- );
962
- }
963
- }
964
- );
965
- });
966
- if (errors.length) {
967
- throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
968
- type: "config",
969
- subtype: "conflicting_paths"
970
- });
971
- }
972
- };
973
- var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
974
- function validatePathExpression(path3) {
975
- const tokens = parsePathRegexp(path3);
976
- for (let i = 0; i < tokens.length; i++) {
977
- const token = tokens[i];
978
- if (token === void 0) {
979
- return `token ${i} in ${path3} is undefined, this shouldn't happen`;
980
- }
981
- if (typeof token !== "string") {
982
- if (token.pattern !== PATH_DEFAULT_PATTERN) {
983
- return `Path ${path3} cannot use a regular expression wildcard`;
984
- }
985
- if (token.prefix !== "/") {
986
- return `Wildcard :${token.name} must be immediately after a / in ${path3}`;
987
- }
988
- if (token.suffix) {
989
- return `Wildcard suffix on :${token.name} is not allowed. Suffixes are not supported`;
1216
+ Routing: {
1217
+ type: "array",
1218
+ items: {
1219
+ $ref: "#/definitions/PathGroup"
990
1220
  }
991
- if (token.modifier && i !== tokens.length - 1) {
992
- return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path3}. Modifiers are only allowed in the last path component`;
1221
+ },
1222
+ PathGroup: {
1223
+ type: "object",
1224
+ properties: {
1225
+ group: {
1226
+ type: "string",
1227
+ description: "Optional group name for the paths"
1228
+ },
1229
+ flag: {
1230
+ type: "string",
1231
+ description: "flag name that can be used to enable/disable all paths in the group"
1232
+ },
1233
+ paths: {
1234
+ type: "array",
1235
+ items: {
1236
+ type: "string"
1237
+ }
1238
+ }
1239
+ },
1240
+ required: ["paths"],
1241
+ additionalProperties: false
1242
+ },
1243
+ ApplicationRouting: {
1244
+ type: "object",
1245
+ additionalProperties: {
1246
+ $ref: "#/definitions/Application"
993
1247
  }
994
- }
995
- }
996
- return void 0;
997
- }
998
- var validateDefaults = (applicationConfigsById) => {
999
- const defaultApplicationIds = Object.entries(applicationConfigsById).reduce((acc, [id, app]) => app.default ? [...acc, id] : acc, []);
1000
- if (defaultApplicationIds.length === 0) {
1001
- throw new MicrofrontendError(
1002
- `No default application found. At least one application must be marked as default.`,
1003
- { type: "config", subtype: "no_default_application" }
1004
- );
1005
- }
1006
- if (defaultApplicationIds.length > 1) {
1007
- throw new MicrofrontendError(
1008
- `Only one default application is allowed. Found ${defaultApplicationIds.join(", ")}.`,
1009
- { type: "config", subtype: "multiple_default_applications" }
1010
- );
1011
- }
1012
- };
1013
- var validateOptions = (options) => {
1014
- var _a;
1015
- if ((_a = options == null ? void 0 : options.vercel) == null ? void 0 : _a.previewDeploymentSuffix) {
1016
- if (!/^[a-zA-Z]{2,}\.[a-zA-Z]{2,}$/.test(
1017
- options.vercel.previewDeploymentSuffix
1018
- )) {
1019
- throw new MicrofrontendError(
1020
- `Invalid preview deployment suffix: ${options.vercel.previewDeploymentSuffix}. Should have be formatted like "vercel.app".`,
1021
- { type: "config", subtype: "invalid_preview_deployment_suffix" }
1022
- );
1248
+ },
1249
+ ChildConfig: {
1250
+ type: "object",
1251
+ properties: {
1252
+ $schema: {
1253
+ type: "string"
1254
+ },
1255
+ version: {
1256
+ type: "string",
1257
+ const: "2"
1258
+ },
1259
+ options: {
1260
+ $ref: "#/definitions/Options"
1261
+ },
1262
+ remotes: {
1263
+ type: "object",
1264
+ additionalProperties: {
1265
+ $ref: "#/definitions/Application"
1266
+ },
1267
+ description: "Applications that only serve a subset of the microfrontend routes only need to reference the name of the primary application that owns the full microfrontends configuration."
1268
+ },
1269
+ partOf: {
1270
+ type: "string",
1271
+ description: "Applications that only serve a subset of the microfrontend routes only need to reference the name of the primary application that owns the full microfrontends configuration."
1272
+ }
1273
+ },
1274
+ required: ["partOf", "version"],
1275
+ additionalProperties: false
1023
1276
  }
1024
1277
  }
1025
1278
  };
1026
1279
 
1027
- // src/config/utils/convert.ts
1028
- function convertV1RoutingToV2Routing(routing) {
1029
- return routing.matches.map((group) => {
1030
- var _a;
1031
- return {
1032
- group: group.group,
1033
- flag: (_a = group.options) == null ? void 0 : _a.flag,
1034
- paths: group.paths
1035
- };
1036
- });
1037
- }
1038
- function convertV1ApplicationToV2Application(application) {
1039
- const common = {
1040
- production: application.production,
1041
- development: application.development,
1042
- vercel: application.vercel
1043
- };
1044
- if (application.default) {
1045
- return common;
1280
+ // src/config/schema/utils/load.ts
1281
+ var SCHEMA = schema_default;
1282
+
1283
+ // src/config/microfrontends/server/validation.ts
1284
+ function validateSchema(configString) {
1285
+ const parsedConfig = parse3(configString);
1286
+ const ajv = new Ajv();
1287
+ const validate = ajv.compile(SCHEMA);
1288
+ const isValid = validate(parsedConfig);
1289
+ if (!isValid) {
1290
+ throw new MicrofrontendError(
1291
+ `Invalid config: ${ajv.errorsText(validate.errors)}`,
1292
+ { type: "config", subtype: "does_not_match_schema" }
1293
+ );
1046
1294
  }
1047
- return {
1048
- ...common,
1049
- routing: convertV1RoutingToV2Routing(application.routing)
1050
- };
1295
+ return parsedConfig;
1051
1296
  }
1052
- function convertV1ConfigToV2Config(config, fromApp) {
1053
- if (!config.applications[fromApp]) {
1054
- throw new Error(`Application "${fromApp}" not found in the config`);
1055
- }
1056
- const common = {
1057
- version: "2",
1058
- options: config.options
1059
- };
1060
- if (config.applications[fromApp].default) {
1061
- return {
1062
- ...common,
1063
- applications: Object.fromEntries(
1064
- Object.entries(config.applications).map(([id, application]) => [
1065
- id,
1066
- convertV1ApplicationToV2Application(application)
1067
- ])
1297
+
1298
+ // src/config/microfrontends/server/index.ts
1299
+ var MicrofrontendsServer = class extends Microfrontends {
1300
+ /**
1301
+ * Writes the configuration to a file.
1302
+ */
1303
+ writeConfig(opts = {
1304
+ pretty: true
1305
+ }) {
1306
+ const outputPath = getOutputFilePath();
1307
+ fs5.mkdirSync(dirname3(outputPath), { recursive: true });
1308
+ fs5.writeFileSync(
1309
+ outputPath,
1310
+ JSON.stringify(
1311
+ this.config.toSchemaJson(),
1312
+ null,
1313
+ opts.pretty ?? true ? 2 : void 0
1068
1314
  )
1069
- };
1070
- }
1071
- const defaultApplication = Object.entries(config.applications).find(
1072
- ([, application]) => application.default
1073
- );
1074
- if (!defaultApplication) {
1075
- throw new Error("No default application found in the config");
1315
+ );
1076
1316
  }
1077
- return {
1078
- ...common,
1079
- partOf: defaultApplication[0]
1080
- };
1081
- }
1082
-
1083
- // src/config/utils/write-file.ts
1084
- import fs from "node:fs";
1085
- import { dirname } from "node:path";
1086
- function writeFile(outputPath, config, prettify) {
1087
- fs.mkdirSync(dirname(outputPath), { recursive: true });
1088
- fs.writeFileSync(
1089
- outputPath,
1090
- JSON.stringify(config, null, prettify ? 2 : void 0)
1091
- );
1092
- }
1093
-
1094
- // src/config/microfrontend-config.ts
1095
- var MicrofrontendConfig = class extends MicrofrontendConfigCommon {
1096
- static validate(configString) {
1097
- const config = validateSchema(configString);
1098
- validateVersion(config.version);
1099
- validatePaths(config.applications);
1100
- validateMainPath(config.applications);
1101
- validateDefaults(config.applications);
1102
- validateOptions(config.options);
1103
- return config;
1317
+ // --------- Static Methods ---------
1318
+ /**
1319
+ * Generates a MicrofrontendsServer instance from an unknown object.
1320
+ */
1321
+ static fromUnknown({
1322
+ config,
1323
+ cookies,
1324
+ meta
1325
+ }) {
1326
+ const overrides = cookies ? parseOverrides(cookies) : void 0;
1327
+ if (typeof config === "string") {
1328
+ return new MicrofrontendsServer({
1329
+ config: MicrofrontendsServer.validate(config),
1330
+ overrides,
1331
+ meta
1332
+ });
1333
+ }
1334
+ if (typeof config === "object") {
1335
+ return new MicrofrontendsServer({
1336
+ config,
1337
+ overrides,
1338
+ meta
1339
+ });
1340
+ }
1341
+ throw new MicrofrontendError(
1342
+ "Invalid config: must be a string or an object",
1343
+ { type: "config", subtype: "does_not_match_schema" }
1344
+ );
1104
1345
  }
1346
+ /**
1347
+ * Generates a MicrofrontendsServer instance from the environment.
1348
+ * Uses additional validation that is only available when in a node runtime
1349
+ */
1105
1350
  static fromEnv({
1106
- cookies
1351
+ cookies,
1352
+ meta
1107
1353
  }) {
1108
- return new MicrofrontendConfigCommon({
1109
- config: MicrofrontendConfig.validate(
1110
- MicrofrontendConfigCommon.getConfigFromEnv()
1111
- ),
1112
- overrides: Overrides.parseOverrides(cookies)
1354
+ return new MicrofrontendsServer({
1355
+ config: MicrofrontendsServer.validate(getConfigStringFromEnv()),
1356
+ overrides: parseOverrides(cookies),
1357
+ meta
1113
1358
  });
1114
1359
  }
1360
+ /**
1361
+ * Validates the configuration against the JSON schema
1362
+ */
1363
+ static validate(config) {
1364
+ if (typeof config === "string") {
1365
+ const c = validateSchema(config);
1366
+ return c;
1367
+ }
1368
+ return config;
1369
+ }
1370
+ /**
1371
+ * Looks up the configuration by inferring the package root and looking for a microfrontends config file. If a file is not found,
1372
+ * it will look for a package in the repository with a microfrontends file that contains the current application
1373
+ * and use that configuration.
1374
+ *
1375
+ * This can return either a Child or Main configuration.
1376
+ */
1377
+ static infer({
1378
+ directory,
1379
+ filePath,
1380
+ meta,
1381
+ cookies,
1382
+ options
1383
+ } = {}) {
1384
+ if (filePath && meta) {
1385
+ return MicrofrontendsServer.fromFile({
1386
+ filePath,
1387
+ cookies,
1388
+ meta,
1389
+ options
1390
+ });
1391
+ }
1392
+ try {
1393
+ const packageRoot = findPackageRoot(directory);
1394
+ const packageJsonPath = join2(packageRoot, "package.json");
1395
+ const packageJson = JSON.parse(
1396
+ fs5.readFileSync(packageJsonPath, "utf-8")
1397
+ );
1398
+ if (!packageJson.name) {
1399
+ throw new Error(`No name found in package.json at ${packageJsonPath}`);
1400
+ }
1401
+ const configMeta = meta ?? { fromApp: packageJson.name };
1402
+ const maybeConfig = findConfig({ dir: packageRoot });
1403
+ if (maybeConfig) {
1404
+ return MicrofrontendsServer.fromFile({
1405
+ filePath: maybeConfig,
1406
+ cookies,
1407
+ meta: configMeta,
1408
+ options
1409
+ });
1410
+ }
1411
+ const repositoryRoot = findRepositoryRoot();
1412
+ const isMonorepo2 = isMonorepo({ repositoryRoot });
1413
+ if (isMonorepo2) {
1414
+ const defaultPackage = findDefaultMicrofrontendsPackage({
1415
+ repositoryRoot,
1416
+ applicationName: packageJson.name
1417
+ });
1418
+ const maybeConfigFromDefault = findConfig({ dir: defaultPackage });
1419
+ if (maybeConfigFromDefault) {
1420
+ return MicrofrontendsServer.fromFile({
1421
+ filePath: maybeConfigFromDefault,
1422
+ cookies,
1423
+ meta: configMeta,
1424
+ options
1425
+ });
1426
+ }
1427
+ }
1428
+ throw new Error("Unable to infer");
1429
+ } catch (e) {
1430
+ throw new MicrofrontendError(
1431
+ "Unable to infer microfrontends configuration",
1432
+ { type: "config", subtype: "inference_failed" }
1433
+ );
1434
+ }
1435
+ }
1436
+ /*
1437
+ * Generates a MicrofrontendsServer instance from a file.
1438
+ */
1115
1439
  static fromFile({
1116
- filePath
1440
+ filePath,
1441
+ cookies,
1442
+ meta,
1443
+ options
1117
1444
  }) {
1118
1445
  try {
1119
- const config = fs2.readFileSync(filePath, "utf-8");
1120
- return new MicrofrontendConfig({
1121
- config: MicrofrontendConfig.validate(config)
1446
+ const configJson = fs5.readFileSync(filePath, "utf-8");
1447
+ const config = MicrofrontendsServer.validate(configJson);
1448
+ if (!isMainConfig(config) && (options == null ? void 0 : options.resolveMainConfig)) {
1449
+ const repositoryRoot = findRepositoryRoot();
1450
+ const isMonorepo2 = isMonorepo({ repositoryRoot });
1451
+ if (isMonorepo2) {
1452
+ const packagePath = findPackagePath({
1453
+ repositoryRoot,
1454
+ name: config.partOf
1455
+ });
1456
+ if (!packagePath) {
1457
+ throw new MicrofrontendError(
1458
+ `Could not find default application "${config.partOf}" in the repository`,
1459
+ { type: "config", subtype: "not_found" }
1460
+ );
1461
+ }
1462
+ const maybeConfig = findConfig({ dir: packagePath });
1463
+ if (!maybeConfig) {
1464
+ throw new MicrofrontendError(
1465
+ `Could not find microfrontends configuration in ${packagePath}`,
1466
+ { type: "config", subtype: "not_found" }
1467
+ );
1468
+ }
1469
+ return MicrofrontendsServer.fromMainConfigFile({
1470
+ filePath: maybeConfig,
1471
+ overrides: cookies ? parseOverrides(cookies) : void 0
1472
+ });
1473
+ }
1474
+ }
1475
+ return new MicrofrontendsServer({
1476
+ config,
1477
+ overrides: cookies ? parseOverrides(cookies) : void 0,
1478
+ meta
1122
1479
  });
1123
1480
  } catch (e) {
1124
1481
  throw MicrofrontendError.handle(e, {
@@ -1126,20 +1483,41 @@ var MicrofrontendConfig = class extends MicrofrontendConfigCommon {
1126
1483
  });
1127
1484
  }
1128
1485
  }
1129
- /**
1130
- * Writes the configuration to a file.
1486
+ /*
1487
+ * Generates a MicrofrontendMainConfig instance from a file.
1131
1488
  */
1132
- write(fromApp, opts = {}) {
1133
- const { pretty = true, versions = ["v1", "v2"] } = opts;
1134
- const config = this.toSchemaJson();
1135
- if (versions.includes("v1")) {
1136
- const outputPath = getOutputFilePath2();
1137
- writeFile(outputPath, config, pretty);
1138
- }
1139
- if (versions.includes("v2")) {
1140
- const outputPath = getOutputFilePath();
1141
- const v2Config = convertV1ConfigToV2Config(config, fromApp);
1142
- writeFile(outputPath, v2Config, pretty);
1489
+ static fromMainConfigFile({
1490
+ filePath,
1491
+ overrides
1492
+ }) {
1493
+ try {
1494
+ const config = fs5.readFileSync(filePath, "utf-8");
1495
+ const validatedConfig = MicrofrontendsServer.validate(config);
1496
+ if (!isMainConfig(validatedConfig)) {
1497
+ throw new MicrofrontendError(
1498
+ `${filePath} is not a main microfrontend config`,
1499
+ {
1500
+ type: "config",
1501
+ subtype: "invalid_main_path"
1502
+ }
1503
+ );
1504
+ }
1505
+ const [defaultApplication] = Object.entries(validatedConfig.applications).filter(([, app]) => isDefaultApp(app)).map(([name]) => name);
1506
+ if (!defaultApplication) {
1507
+ throw new MicrofrontendError(
1508
+ `No default application found. At least one application needs to be the default by omitting routing.`,
1509
+ { type: "config", subtype: "no_default_application" }
1510
+ );
1511
+ }
1512
+ return new MicrofrontendsServer({
1513
+ config: validatedConfig,
1514
+ overrides,
1515
+ meta: { fromApp: defaultApplication }
1516
+ });
1517
+ } catch (e) {
1518
+ throw MicrofrontendError.handle(e, {
1519
+ fileName: filePath
1520
+ });
1143
1521
  }
1144
1522
  }
1145
1523
  };
@@ -1155,19 +1533,19 @@ function displayLocalProxyInfo(port) {
1155
1533
 
1156
1534
  // src/next/config/transforms/asset-prefix.ts
1157
1535
  function transform(args) {
1158
- var _a;
1159
- const { next, zone } = args;
1160
- if (!((_a = zone.routing) == null ? void 0 : _a.assetPrefix)) {
1536
+ const { next, app } = args;
1537
+ if (app.isDefault()) {
1161
1538
  return {
1162
1539
  next
1163
1540
  };
1164
1541
  }
1165
- if (next.assetPrefix !== void 0 && next.assetPrefix !== zone.routing.assetPrefix) {
1166
- throw new Error(
1167
- `"assetPrefix" already set. Overwriting with "${zone.name}"`
1542
+ if (next.assetPrefix !== void 0 && next.assetPrefix !== app.getAssetPrefix()) {
1543
+ console.log(
1544
+ `"assetPrefix" already set. This route must be manually configured in your microfrontend.json file.`
1168
1545
  );
1546
+ } else {
1547
+ next.assetPrefix = `/${app.getAssetPrefix()}`;
1169
1548
  }
1170
- next.assetPrefix = `/${zone.routing.assetPrefix}`;
1171
1549
  return {
1172
1550
  next
1173
1551
  };
@@ -1191,8 +1569,8 @@ function transform2(args) {
1191
1569
 
1192
1570
  // src/next/config/transforms/headers.ts
1193
1571
  function transform3(args) {
1194
- const { next, zone } = args;
1195
- if (zone.isDefault()) {
1572
+ const { next, app } = args;
1573
+ if (app.isDefault()) {
1196
1574
  return {
1197
1575
  next
1198
1576
  };
@@ -1203,7 +1581,11 @@ function transform3(args) {
1203
1581
  headers: [
1204
1582
  {
1205
1583
  key: "X-Vercel-Zone",
1206
- value: zone.name
1584
+ value: app.name
1585
+ },
1586
+ {
1587
+ key: "X-Vercel-MFE-App",
1588
+ value: app.name
1207
1589
  }
1208
1590
  ]
1209
1591
  },
@@ -1242,82 +1624,39 @@ function transform3(args) {
1242
1624
  };
1243
1625
  }
1244
1626
 
1245
- // src/routing/get-preview-domain.ts
1246
- import { createHash } from "node:crypto";
1247
- var DOMAIN_SIZE = 253;
1248
- var DOMAIN_LABEL_SIZE = 63;
1249
- function getMaxLabelLength(domain) {
1250
- return Math.min(DOMAIN_SIZE - domain.length - 1, DOMAIN_LABEL_SIZE);
1251
- }
1252
- function parseBranchUrl(branchUrl) {
1253
- const branchUrlParts = branchUrl.split(".");
1254
- if (branchUrlParts.length < 3) {
1255
- throw new Error(`Could not parse Vercel branch URL "${branchUrl}"`);
1627
+ // src/routing/get-domain-from-environment.ts
1628
+ function getDomainFromEnvironment({
1629
+ app,
1630
+ target
1631
+ }) {
1632
+ var _a;
1633
+ const mfeProjects = JSON.parse(
1634
+ process.env.VERCEL_MICROFRONTENDS_PROJECTS ?? "[]"
1635
+ );
1636
+ if (mfeProjects.length === 0) {
1637
+ throw new Error("Missing related microfrontends project information");
1256
1638
  }
1257
- const domain = [branchUrlParts[1], branchUrlParts[2]].join(".");
1258
- return {
1259
- // biome-ignore lint/style/noNonNullAssertion: Ignored using `--suppress`
1260
- label: branchUrlParts[0],
1261
- domain
1262
- };
1263
- }
1264
- function getDomainSuffix(branchUrl, vercelUrl) {
1265
- const branchLabelParts = parseBranchUrl(branchUrl).label.split("-");
1266
- const vercelUrlLabelParts = parseBranchUrl(vercelUrl).label.split("-");
1267
- const suffixParts = [];
1268
- for (let i = 1; i < branchLabelParts.length; i++) {
1269
- if (branchLabelParts.at(-1 * i) === vercelUrlLabelParts.at(-1 * i)) {
1270
- suffixParts.push(branchLabelParts.at(-1 * i) ?? "");
1271
- }
1639
+ if (!((_a = app.vercel) == null ? void 0 : _a.projectId)) {
1640
+ throw new Error(`Missing applications[${app.name}].vercel.projectId`);
1272
1641
  }
1273
- return suffixParts.reverse().filter(Boolean).join("-");
1274
- }
1275
- function getPreviewDomain(zone) {
1276
- var _a;
1277
- const targetVercelProjectName = (_a = zone.vercel) == null ? void 0 : _a.projectName;
1278
- if (!targetVercelProjectName) {
1642
+ const vercelProject = mfeProjects.find(
1643
+ (p) => {
1644
+ var _a2;
1645
+ return p.project.id === ((_a2 = app.vercel) == null ? void 0 : _a2.projectId);
1646
+ }
1647
+ );
1648
+ if (!vercelProject) {
1279
1649
  throw new Error(
1280
- `Missing Vercel project configuration for application "${zone.name}"`
1650
+ `Missing related microfrontends project information for application "${app.name}"`
1281
1651
  );
1282
1652
  }
1283
- const { VERCEL_GIT_COMMIT_REF, VERCEL_BRANCH_URL, VERCEL_URL } = process.env;
1284
- if (VERCEL_GIT_COMMIT_REF && VERCEL_BRANCH_URL && VERCEL_URL) {
1285
- const branchUrlParts = parseBranchUrl(VERCEL_BRANCH_URL);
1286
- const { domain } = branchUrlParts;
1287
- const stagingSuffix = domain !== "vercel.sh" ? getDomainSuffix(VERCEL_BRANCH_URL, VERCEL_URL) : "";
1288
- const gitBranch = `git-${VERCEL_GIT_COMMIT_REF.toLocaleLowerCase()}`;
1289
- const gitBranchForHash = `git-${VERCEL_GIT_COMMIT_REF}`;
1290
- const urlSafeBranch = makeUrlSafe(gitBranch);
1291
- const urlSafeProjectName = makeUrlSafe(targetVercelProjectName);
1292
- const maxLabelLength = getMaxLabelLength(domain);
1293
- const label = [urlSafeProjectName, urlSafeBranch, stagingSuffix].filter(Boolean).join("-");
1294
- if (label.length > maxLabelLength) {
1295
- let newLabel = makeUrlSafe(stagingSuffix).slice(0, maxLabelLength);
1296
- const hash = createHash("sha256").update(`${gitBranchForHash}${targetVercelProjectName}`).digest("hex").slice(0, 6);
1297
- newLabel = [hash, newLabel].filter(Boolean).join("-");
1298
- const projectNamePart = makeUrlSafe(
1299
- urlSafeProjectName.slice(0, maxLabelLength - newLabel.length - 1 - 4)
1300
- );
1301
- const branchPart = makeUrlSafe(
1302
- urlSafeBranch.slice(
1303
- 0,
1304
- Math.max(
1305
- 0,
1306
- maxLabelLength - (newLabel.length + projectNamePart.length) - 2
1307
- )
1308
- )
1309
- );
1310
- if (branchPart.length >= 3) {
1311
- newLabel = [branchPart, newLabel].filter(Boolean).join("-");
1312
- }
1313
- if (projectNamePart) {
1314
- newLabel = [projectNamePart, newLabel].filter(Boolean).join("-");
1315
- }
1316
- return `https://${newLabel}.${domain}`;
1317
- }
1318
- return `https://${label}.${domain}`;
1653
+ const domain = target === "preview" && vercelProject.preview.branch ? vercelProject.preview.branch : vercelProject.production.alias ?? vercelProject.production.url;
1654
+ if (!domain) {
1655
+ throw new Error(
1656
+ `Missing domain for target "${target}" in application "${app.name}"`
1657
+ );
1319
1658
  }
1320
- return zone.production.toString();
1659
+ return domain.startsWith("https://") ? domain : `https://${domain}`;
1321
1660
  }
1322
1661
 
1323
1662
  // src/routing/get-domain-for-current-environment.ts
@@ -1333,34 +1672,45 @@ ${line}
1333
1672
  `);
1334
1673
  }
1335
1674
  }
1336
- function getDomainForCurrentEnvironment(zone, opts = {}) {
1675
+ function getCurrentEnvironment() {
1676
+ const isDevelopment = !process.env.VERCEL_ENV || process.env.VERCEL_ENV === "development";
1677
+ const isPreview = process.env.VERCEL_ENV === "preview";
1678
+ const isProduction2 = process.env.VERCEL_ENV === "production";
1679
+ if (isDevelopment) {
1680
+ return { group: "development" };
1681
+ } else if (isProduction2) {
1682
+ return { group: "production" };
1683
+ } else if (isPreview) {
1684
+ return { group: "preview" };
1685
+ }
1686
+ return { group: "custom", name: process.env.VERCEL_ENV };
1687
+ }
1688
+ function getDomainForCurrentEnvironment(config, appName, opts = {}) {
1337
1689
  var _a;
1338
- if (!opts.ignoreOverride && ((_a = zone.overrides) == null ? void 0 : _a.environment)) {
1339
- return zone.overrides.environment.toString();
1340
- }
1341
- const zoneName = zone.name;
1342
- if (!process.env.VERCEL_ENV || process.env.VERCEL_ENV === "development") {
1343
- const domain = process.env.NODE_ENV === "test" ? zone.development.local.toString() : zone.production.toString();
1344
- debugDomains(zoneName, "development", domain);
1345
- return domain;
1346
- } else if (process.env.VERCEL_ENV === "production") {
1347
- const domain = zone.production.toString();
1348
- debugDomains(zoneName, "production", domain);
1349
- return domain;
1350
- } else if (process.env.VERCEL_ENV === "preview") {
1351
- const MFE_PREVIEW_DOMAINS = JSON.parse(
1352
- process.env.MFE_PREVIEW_DOMAINS ?? "{}"
1353
- );
1354
- if (MFE_PREVIEW_DOMAINS[zoneName]) {
1355
- debugDomains(zoneName, "preview", MFE_PREVIEW_DOMAINS[zoneName]);
1356
- return MFE_PREVIEW_DOMAINS[zoneName];
1690
+ const app = config.getApplication(appName);
1691
+ if (!opts.ignoreOverride && ((_a = app.overrides) == null ? void 0 : _a.environment)) {
1692
+ return app.overrides.environment.toString();
1693
+ }
1694
+ const { group } = getCurrentEnvironment();
1695
+ const productionHost = config.getDefaultApplication().production.toString();
1696
+ switch (group) {
1697
+ case "development": {
1698
+ const domain = ["test", "development"].includes(process.env.NODE_ENV) ? app.development.local.toString() : productionHost;
1699
+ debugDomains(appName, "development", domain);
1700
+ return domain;
1357
1701
  }
1358
- throw new Error(
1359
- `Could not find preview domain for application "${zoneName}"`
1360
- );
1702
+ case "preview": {
1703
+ return getDomainFromEnvironment({ app, target: "preview" });
1704
+ }
1705
+ case "production": {
1706
+ return getDomainFromEnvironment({ app, target: "production" });
1707
+ }
1708
+ case "custom":
1709
+ console.warn(
1710
+ `Custom environments are not supported in getDomainForCurrentEnvironment`
1711
+ );
1712
+ return productionHost;
1361
1713
  }
1362
- debugDomains(zoneName, process.env.VERCEL_ENV, zone.production.toString());
1363
- return zone.production.toString();
1364
1714
  }
1365
1715
 
1366
1716
  // src/next/config/transforms/rewrites.ts
@@ -1382,11 +1732,11 @@ ${table}
1382
1732
  `);
1383
1733
  }
1384
1734
  }
1385
- function pathToRewrites(path3) {
1735
+ function pathToRewrites(path5) {
1386
1736
  var _a;
1387
1737
  const regex = /(?<base>^.+)\/:.+\*$/;
1388
- const match = regex.exec(path3);
1389
- const paths = [path3];
1738
+ const match = regex.exec(path5);
1739
+ const paths = [path5];
1390
1740
  if ((_a = match == null ? void 0 : match.groups) == null ? void 0 : _a.base) {
1391
1741
  paths.unshift(match.groups.base);
1392
1742
  }
@@ -1415,42 +1765,39 @@ function rewritesMapToArr(rewrites) {
1415
1765
  });
1416
1766
  }
1417
1767
  function transform4(args) {
1418
- const { next, microfrontend, zone } = args;
1768
+ const { next, microfrontend, app } = args;
1419
1769
  const buildBeforeFiles = () => {
1420
- var _a, _b, _c, _d;
1421
1770
  const rewrites = /* @__PURE__ */ new Map();
1422
- if ((_a = zone.routing) == null ? void 0 : _a.assetPrefix) {
1423
- rewrites.set(`/${zone.routing.assetPrefix}/_next/:path+`, {
1771
+ if (!app.isDefault()) {
1772
+ rewrites.set(`/${app.getAssetPrefix()}/_next/:path+`, {
1424
1773
  destination: {
1425
1774
  pathname: `/_next/:path+`
1426
1775
  }
1427
1776
  });
1428
- if (!((_b = zone.vercel) == null ? void 0 : _b.routeSpeedInsightsToDefaultZone)) {
1429
- rewrites.set(`/${zone.routing.assetPrefix}/_vercel/:path*`, {
1777
+ rewrites.set(`/${app.getAssetPrefix()}/.well-known/vercel/flags`, {
1778
+ destination: {
1779
+ pathname: `/.well-known/vercel/flags`
1780
+ }
1781
+ });
1782
+ if (process.env.VERCEL_MICROFRONTENDS_CONSOLIDATE_SPEED_INSIGHTS === "1") {
1783
+ rewrites.set(`/${app.getAssetPrefix()}/_vercel/:path*`, {
1430
1784
  destination: { pathname: "/_vercel/:path*" }
1431
1785
  });
1432
1786
  }
1433
- }
1434
- if (zone.isDefault()) {
1435
- for (const [_, z] of Object.entries(microfrontend.zones)) {
1436
- if (zone.name === z.name) {
1437
- continue;
1438
- }
1439
- const { routing } = z;
1440
- if (!routing) {
1441
- continue;
1442
- }
1443
- const domain = process.env.VERCEL_ENV === "preview" ? getPreviewDomain(z) : getDomainForCurrentEnvironment(z);
1444
- if ((_c = z.routing) == null ? void 0 : _c.assetPrefix) {
1445
- rewrites.set(`/${z.routing.assetPrefix}/:path+`, {
1446
- destination: {
1447
- domain,
1448
- pathname: `/${z.routing.assetPrefix}/:path+`
1449
- }
1450
- });
1451
- }
1452
- for (const group of routing.matches) {
1453
- if ((_d = group.options) == null ? void 0 : _d.flag) {
1787
+ } else if (microfrontend instanceof MicrofrontendMainConfig) {
1788
+ for (const [_, a] of Object.entries(
1789
+ microfrontend.getChildApplications()
1790
+ )) {
1791
+ const { routing } = a;
1792
+ const domain = getDomainForCurrentEnvironment(microfrontend, a.name);
1793
+ rewrites.set(`/${a.getAssetPrefix()}/:path+`, {
1794
+ destination: {
1795
+ domain,
1796
+ pathname: `/${a.getAssetPrefix()}/:path+`
1797
+ }
1798
+ });
1799
+ for (const group of routing) {
1800
+ if (group.flag) {
1454
1801
  continue;
1455
1802
  } else {
1456
1803
  for (const source of group.paths) {
@@ -1464,7 +1811,6 @@ function transform4(args) {
1464
1811
  }
1465
1812
  }
1466
1813
  }
1467
- } else {
1468
1814
  }
1469
1815
  return rewritesMapToArr(rewrites);
1470
1816
  };
@@ -1501,17 +1847,44 @@ function transform4(args) {
1501
1847
  }
1502
1848
 
1503
1849
  // src/next/config/transforms/server-actions.ts
1850
+ function debugRewrites2(allowedOrigins) {
1851
+ if (process.env.MFE_DEBUG === "true" && allowedOrigins) {
1852
+ const indent = " ".repeat(4);
1853
+ const header = "server actions allowed origins";
1854
+ const separator = "\u23AF".repeat(header.length);
1855
+ const maxSourceLength = Math.max(
1856
+ ...allowedOrigins.map((key) => key.length)
1857
+ );
1858
+ const table = allowedOrigins.map((origin, idx) => {
1859
+ const paddedSource = origin.padEnd(maxSourceLength);
1860
+ return `${indent} ${idx + 1}. ${paddedSource}`;
1861
+ }).join("\n");
1862
+ console.log(`${indent}${header}
1863
+ ${indent}${separator}
1864
+ ${table}
1865
+ `);
1866
+ }
1867
+ }
1504
1868
  var formatDomainForServerAction = (domain) => domain.replace(/https?:\/\//, "");
1505
1869
  function transform5(args) {
1506
- var _a;
1507
- const { next, zone, microfrontend } = args;
1508
- const zonesToAllow = [
1870
+ var _a, _b;
1871
+ const { next, app, microfrontend } = args;
1872
+ if (microfrontend instanceof MicrofrontendChildConfig) {
1873
+ console.warn(
1874
+ "server actions transform requires the full config - skipping"
1875
+ );
1876
+ return {
1877
+ next
1878
+ };
1879
+ }
1880
+ const defaultApplication = microfrontend.getDefaultApplication();
1881
+ const appsToAllow = [
1509
1882
  // this zone - this is included by default unless allowedOrigins is overridden (which we are)
1510
1883
  // so we re-add it here.
1511
- zone,
1884
+ app,
1512
1885
  // this is the default zone for the microfrontend. Allow child zones to call server actions
1513
1886
  // that are in the default zone.
1514
- microfrontend.getDefaultZone()
1887
+ defaultApplication
1515
1888
  ];
1516
1889
  const existingServerActionConfig = (_a = next.experimental) == null ? void 0 : _a.serverActions;
1517
1890
  next.experimental = {
@@ -1520,15 +1893,23 @@ function transform5(args) {
1520
1893
  ...existingServerActionConfig,
1521
1894
  allowedOrigins: Array.from(
1522
1895
  /* @__PURE__ */ new Set([
1896
+ // existing
1523
1897
  ...(existingServerActionConfig == null ? void 0 : existingServerActionConfig.allowedOrigins) ?? [],
1524
- ...zonesToAllow.flatMap((z) => [
1525
- formatDomainForServerAction(z.production.toString()),
1526
- formatDomainForServerAction(getDomainForCurrentEnvironment(z))
1898
+ // this deployments host
1899
+ ...process.env.VERCEL_URL ? [formatDomainForServerAction(process.env.VERCEL_URL)] : [],
1900
+ // default application host
1901
+ formatDomainForServerAction(defaultApplication.production.toString()),
1902
+ // environment specific microfrontend hosts
1903
+ ...appsToAllow.flatMap((a) => [
1904
+ formatDomainForServerAction(
1905
+ getDomainForCurrentEnvironment(microfrontend, a.name)
1906
+ )
1527
1907
  ])
1528
1908
  ])
1529
1909
  )
1530
1910
  }
1531
1911
  };
1912
+ debugRewrites2((_b = next.experimental.serverActions) == null ? void 0 : _b.allowedOrigins);
1532
1913
  return {
1533
1914
  next
1534
1915
  };
@@ -1537,14 +1918,6 @@ function transform5(args) {
1537
1918
  // src/next/config/transforms/webpack.ts
1538
1919
  function transform6(args) {
1539
1920
  const { next, microfrontend } = args;
1540
- const previewDomains = process.env.VERCEL_ENV === "preview" ? microfrontend.getAllApplications().reduce(
1541
- (obj, app) => {
1542
- obj[app.name] = getPreviewDomain(app);
1543
- return obj;
1544
- },
1545
- // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter -- needed to satisfy TSC
1546
- {}
1547
- ) : {};
1548
1921
  const configWithWebpack = {
1549
1922
  ...next,
1550
1923
  webpack(cfg, context) {
@@ -1553,8 +1926,7 @@ function transform6(args) {
1553
1926
  if (isServer || nextRuntime === "edge") {
1554
1927
  config.plugins.push(
1555
1928
  new wpFromNext.EnvironmentPlugin({
1556
- MFE_CONFIG: JSON.stringify(microfrontend.serialize()),
1557
- MFE_PREVIEW_DOMAINS: JSON.stringify(previewDomains)
1929
+ MFE_CONFIG: JSON.stringify(microfrontend.serialize())
1558
1930
  })
1559
1931
  );
1560
1932
  }
@@ -1594,21 +1966,6 @@ var transforms = {
1594
1966
  webpack: transform6
1595
1967
  };
1596
1968
 
1597
- // src/config/client/client-config.ts
1598
- function getConfigForClient(config) {
1599
- return {
1600
- applications: Object.fromEntries(
1601
- Object.entries(config.applications).map(([name, application]) => [
1602
- name,
1603
- {
1604
- default: application.default,
1605
- routing: application.routing
1606
- }
1607
- ])
1608
- )
1609
- };
1610
- }
1611
-
1612
1969
  // src/next/config/env.ts
1613
1970
  function debugEnv(env) {
1614
1971
  if (process.env.MFE_DEBUG === "true") {
@@ -1627,32 +1984,18 @@ ${table}
1627
1984
  }
1628
1985
  }
1629
1986
  function setEnvironment({
1630
- zone,
1631
- config
1987
+ app,
1988
+ microfrontends
1632
1989
  }) {
1633
- var _a, _b, _c, _d;
1634
- const previewDomains = process.env.VERCEL_ENV === "preview" ? config.getAllApplications().reduce(
1635
- (obj, app) => {
1636
- obj[app.name] = getPreviewDomain(app);
1637
- return obj;
1638
- },
1639
- // eslint-disable-next-line @typescript-eslint/prefer-reduce-type-parameter -- needed to satisfy TSC
1640
- {}
1641
- ) : {};
1642
1990
  const clientEnvs = {
1643
- NEXT_PUBLIC_MFE_CURRENT_APPLICATION: zone.name,
1991
+ NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
1644
1992
  NEXT_PUBLIC_MFE_CLIENT_CONFIG: JSON.stringify(
1645
- getConfigForClient(config.getConfig())
1646
- ),
1647
- ...((_a = zone.vercel) == null ? void 0 : _a.routeSpeedInsightsToDefaultZone) && ((_b = zone.routing) == null ? void 0 : _b.assetPrefix) ? {} : {
1648
- NEXT_PUBLIC_SPEED_INSIGHTS_BASEPATH: (_c = zone.routing) == null ? void 0 : _c.assetPrefix,
1649
- NEXT_PUBLIC_WEB_ANALYTICS_BASEPATH: (_d = zone.routing) == null ? void 0 : _d.assetPrefix
1650
- }
1993
+ microfrontends.config.toClientConfig().serialize()
1994
+ )
1651
1995
  };
1652
1996
  const serverEnvs = {
1653
- MFE_CURRENT_APPLICATION: zone.name,
1654
- MFE_PREVIEW_DOMAINS: JSON.stringify(previewDomains),
1655
- MFE_CONFIG: JSON.stringify(config.getConfig())
1997
+ MFE_CURRENT_APPLICATION: app.name,
1998
+ MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
1656
1999
  };
1657
2000
  const allEnvs = { ...clientEnvs, ...serverEnvs };
1658
2001
  for (const [key, value] of Object.entries(allEnvs)) {
@@ -1671,37 +2014,12 @@ function isProduction(opts) {
1671
2014
  }
1672
2015
  return process.env.VERCEL_ENV === "production";
1673
2016
  }
1674
- function getConfigPath(opts) {
1675
- var _a;
1676
- if (opts == null ? void 0 : opts.configPath) {
1677
- return { configPath: opts.configPath };
1678
- }
1679
- try {
1680
- const vercelJsonString = fs3.readFileSync("./vercel.json", "utf-8");
1681
- const vercelJson = JSON.parse(vercelJsonString);
1682
- if (!((_a = vercelJson.microFrontends) == null ? void 0 : _a.config)) {
1683
- throw new MicrofrontendError(
1684
- `vercel.json file missing required field "microFrontends.config"`,
1685
- {
1686
- type: "vercelJson",
1687
- subtype: "missing_field_microFrontend_config_path",
1688
- source: "@vercel/microfrontends/next"
1689
- }
1690
- );
1691
- }
1692
- return { configPath: vercelJson.microFrontends.config };
1693
- } catch (err) {
1694
- throw MicrofrontendError.handle(err, {
1695
- fileName: "vercel.json"
1696
- });
1697
- }
1698
- }
1699
2017
  function getApplicationContext(opts) {
1700
2018
  if (opts == null ? void 0 : opts.appName) {
1701
2019
  return { name: opts.appName };
1702
2020
  }
1703
2021
  try {
1704
- const packageJsonString = fs3.readFileSync("./package.json", "utf-8");
2022
+ const packageJsonString = fs6.readFileSync("./package.json", "utf-8");
1705
2023
  const packageJson = JSON.parse(packageJsonString);
1706
2024
  if (!packageJson.name) {
1707
2025
  throw new MicrofrontendError(
@@ -1725,13 +2043,15 @@ function withMicrofrontends(nextConfig, opts) {
1725
2043
  if (opts == null ? void 0 : opts.debug) {
1726
2044
  process.env.MFE_DEBUG = "true";
1727
2045
  }
1728
- const { name } = getApplicationContext(opts);
1729
- const { configPath } = getConfigPath(opts);
1730
- const mfConfig = MicrofrontendConfig.fromFile({
1731
- filePath: configPath
2046
+ const { name: fromApp } = getApplicationContext(opts);
2047
+ const microfrontends = MicrofrontendsServer.infer({
2048
+ filePath: opts == null ? void 0 : opts.configPath,
2049
+ meta: {
2050
+ fromApp
2051
+ }
1732
2052
  });
1733
- const zone = mfConfig.getZone(name);
1734
- setEnvironment({ zone, config: mfConfig });
2053
+ const app = microfrontends.config.getApplication(fromApp);
2054
+ setEnvironment({ app, microfrontends });
1735
2055
  let next = { ...nextConfig };
1736
2056
  for (const [key, transform7] of typedEntries(transforms)) {
1737
2057
  if ((_a = opts == null ? void 0 : opts.skipTransforms) == null ? void 0 : _a.includes(key)) {
@@ -1740,9 +2060,9 @@ function withMicrofrontends(nextConfig, opts) {
1740
2060
  }
1741
2061
  try {
1742
2062
  const transformedConfig = transform7({
1743
- zone,
2063
+ app,
1744
2064
  next,
1745
- microfrontend: mfConfig,
2065
+ microfrontend: microfrontends.config,
1746
2066
  opts: {
1747
2067
  isProduction: isProduction(opts)
1748
2068
  }
@@ -1752,9 +2072,9 @@ function withMicrofrontends(nextConfig, opts) {
1752
2072
  console.error("Error transforming next config", e);
1753
2073
  }
1754
2074
  }
1755
- displayLocalProxyInfo(mfConfig.getLocalProxyPort());
2075
+ displayLocalProxyInfo(microfrontends.config.getLocalProxyPort());
1756
2076
  if (isVercel()) {
1757
- mfConfig.write(zone.name, { versions: ["v1", "v2"] });
2077
+ microfrontends.writeConfig();
1758
2078
  }
1759
2079
  return next;
1760
2080
  }