@vercel/microfrontends 0.14.0 → 0.15.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 +377 -1626
  2. package/dist/config.cjs +460 -1004
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.d.ts +4 -25
  5. package/dist/config.js +456 -983
  6. package/dist/config.js.map +1 -1
  7. package/dist/{v2/microfrontends → microfrontends}/server.cjs +63 -143
  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 +63 -143
  11. package/dist/microfrontends/server.js.map +1 -0
  12. package/dist/{v2/microfrontends.cjs → microfrontends.cjs} +21 -21
  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} +20 -20
  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 +1323 -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 +1327 -1028
  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 +745 -396
  32. package/dist/next/middleware.cjs.map +1 -1
  33. package/dist/next/middleware.d.ts +10 -5
  34. package/dist/next/middleware.js +745 -396
  35. package/dist/next/middleware.js.map +1 -1
  36. package/dist/next/testing.cjs +595 -1032
  37. package/dist/next/testing.cjs.map +1 -1
  38. package/dist/next/testing.d.ts +14 -12
  39. package/dist/next/testing.js +589 -1016
  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 +211 -1333
  52. package/dist/utils/mfe-port.cjs.map +1 -1
  53. package/dist/utils/mfe-port.js +204 -1326
  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,268 @@ 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
- };
199
- });
200
- return overridesConfig;
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 tokens = parsePathRegexp(path5);
251
+ for (const token of tokens.slice(0, -1)) {
252
+ if (typeof token !== "string") {
253
+ errors.push(
254
+ `Path ${path5} may only have a :wildcard in the last path component`
255
+ );
256
+ }
257
+ }
258
+ const existing = pathsByApplicationId.get(path5);
259
+ if (existing) {
260
+ existing.applications.push(id);
261
+ } else {
262
+ pathsByApplicationId.set(path5, {
263
+ applications: [id],
264
+ matcher: pathToRegexp2(path5),
265
+ applicationId: id
266
+ });
267
+ }
268
+ }
269
+ }
201
270
  }
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];
271
+ const entries = Array.from(pathsByApplicationId.entries());
272
+ entries.forEach(([path5, { applications: ids, matcher, applicationId }]) => {
273
+ if (ids.length > 1) {
274
+ errors.push(
275
+ `Duplicate path "${path5}" for applications "${ids.join(", ")}"`
276
+ );
207
277
  }
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];
278
+ entries.forEach(
279
+ ([
280
+ matchPath,
281
+ { applications: matchIds, applicationId: matchApplicationId }
282
+ ]) => {
283
+ if (path5 === matchPath) {
284
+ return;
285
+ }
286
+ if (applicationId === matchApplicationId) {
287
+ return;
288
+ }
289
+ if (matcher.test(matchPath)) {
290
+ const source = `"${path5}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
291
+ const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
292
+ errors.push(
293
+ `Overlapping path detected between ${source} and ${destination}`
294
+ );
295
+ }
296
+ }
297
+ );
298
+ });
299
+ if (errors.length) {
300
+ throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
301
+ type: "config",
302
+ subtype: "conflicting_paths"
303
+ });
304
+ }
305
+ };
306
+ var validateAppPaths = (name, app) => {
307
+ for (const group of app.routing) {
308
+ for (const p of group.paths) {
309
+ if (p === "/") {
310
+ continue;
311
+ }
312
+ if (p.endsWith("/")) {
313
+ throw new MicrofrontendError(
314
+ `Invalid path for application "${name}". ${p} must not end with a slash.`,
315
+ { type: "application", subtype: "invalid_path" }
316
+ );
317
+ }
318
+ if (!p.startsWith("/")) {
319
+ throw new MicrofrontendError(
320
+ `Invalid path for application "${name}". ${p} must start with a slash.`,
321
+ { type: "application", subtype: "invalid_path" }
322
+ );
323
+ }
213
324
  }
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
325
  }
220
- static validateOverrideDomain(microfrontendConfig, zone, domain) {
221
- return new RegExp(
222
- `^${_Overrides.validOverrideDomainsForZone(microfrontendConfig, zone).join(
223
- "|"
224
- )}$`
225
- ).test(domain);
326
+ };
327
+ var validateConfigDefaultApplication = (applicationConfigsById) => {
328
+ if (!applicationConfigsById) {
329
+ return;
226
330
  }
227
- serialize() {
228
- return this.config;
331
+ const applicationsWithRouting = Object.entries(applicationConfigsById).filter(
332
+ ([, app]) => !isDefaultApp(app)
333
+ );
334
+ const applicationsWithRoutingNames = applicationsWithRouting.map(
335
+ ([key]) => key
336
+ );
337
+ const numApplications = Object.keys(applicationConfigsById).length;
338
+ const numApplicationsWithRouting = applicationsWithRoutingNames.length;
339
+ const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
340
+ if (numApplicationsWithoutRouting === 0) {
341
+ throw new MicrofrontendError(
342
+ `No default application found. At least one application needs to be the default by omitting routing.`,
343
+ { type: "config", subtype: "no_default_application" }
344
+ );
345
+ }
346
+ if (numApplicationsWithoutRouting > 1) {
347
+ throw new MicrofrontendError(
348
+ `Only one application can omit "routing". Found ${applicationsWithRoutingNames.length - Object.keys(applicationConfigsById).length > 1}.`,
349
+ { type: "config", subtype: "multiple_default_applications" }
350
+ );
229
351
  }
230
352
  };
231
- var Overrides = _Overrides;
232
- Overrides.overrideEnvCookiePrefix = `${OVERRIDES_COOKIE_PREFIX}:env:`;
233
353
 
234
- // src/config/common/host.ts
354
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
355
+ var PREFIX = "vc-ap";
356
+ function generateAssetPrefixFromName({
357
+ name
358
+ }) {
359
+ if (!name) {
360
+ throw new Error("Name is required to generate an asset prefix");
361
+ }
362
+ return `${PREFIX}-${name}`;
363
+ }
364
+
365
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
366
+ function generatePortFromName({
367
+ name,
368
+ minPort = 3e3,
369
+ maxPort = 8e3
370
+ }) {
371
+ if (!name) {
372
+ throw new Error("Name is required to generate a port");
373
+ }
374
+ let hash = 0;
375
+ for (let i = 0; i < name.length; i++) {
376
+ hash = (hash << 5) - hash + name.charCodeAt(i);
377
+ hash |= 0;
378
+ }
379
+ hash = Math.abs(hash);
380
+ const range = maxPort - minPort;
381
+ const port = minPort + hash % range;
382
+ return port;
383
+ }
384
+
385
+ // src/config/microfrontends-config/isomorphic/host.ts
235
386
  var Host = class {
236
- constructor({ protocol, host, port }) {
237
- this.protocol = protocol || "https";
387
+ constructor(hostConfig, options) {
388
+ const { protocol = "https", host, port } = hostConfig;
389
+ this.protocol = protocol;
238
390
  this.host = host;
239
391
  this.port = Host.getPort({ port, protocol: this.protocol });
240
- this.serialized = {
241
- protocol,
242
- host,
243
- ...port ? { port } : void 0
244
- };
392
+ this.local = options == null ? void 0 : options.isLocal;
245
393
  }
246
394
  isLocal() {
247
- return this.host === "localhost" || this.host === "127.0.0.1";
395
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
248
396
  }
249
397
  static getPort({
250
398
  protocol,
@@ -270,176 +418,234 @@ var Host = class {
270
418
  const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
271
419
  return new URL(url);
272
420
  }
273
- serialize() {
274
- return this.serialized;
421
+ };
422
+ var LocalHost = class extends Host {
423
+ constructor({
424
+ appName,
425
+ ...hostConfig
426
+ }) {
427
+ const host = hostConfig.host ?? "localhost";
428
+ const port = hostConfig.port ?? generatePortFromName({ name: appName });
429
+ const protocol = hostConfig.protocol ?? "http";
430
+ super({ protocol, host, port });
275
431
  }
276
432
  };
277
433
 
278
- // src/config/common/application.ts
434
+ // src/config/microfrontends-config/isomorphic/application.ts
279
435
  var Application = class {
280
436
  constructor(name, {
281
437
  app,
282
- overrides
438
+ overrides,
439
+ isDefault
283
440
  }) {
284
- Application.validate(name, app);
441
+ var _a, _b;
285
442
  this.name = name;
286
- this.default = app.default;
287
- this.routing = app.routing;
288
443
  this.development = {
289
- local: new Host(app.development.local),
290
- fallback: app.development.fallback ? new Host(app.development.fallback) : void 0
444
+ local: new LocalHost({
445
+ appName: name,
446
+ ...(_a = app.development) == null ? void 0 : _a.local
447
+ }),
448
+ fallback: ((_b = app.development) == null ? void 0 : _b.fallback) ? new Host(app.development.fallback) : void 0
291
449
  };
292
- this.production = new Host(app.production);
450
+ this.production = app.production ? new Host(app.production) : void 0;
293
451
  this.vercel = app.vercel;
294
452
  this.overrides = (overrides == null ? void 0 : overrides.environment) ? {
295
453
  environment: new Host(overrides.environment)
296
454
  } : void 0;
455
+ this.default = isDefault ?? false;
456
+ this.serialized = app;
297
457
  }
298
458
  isDefault() {
299
459
  return this.default;
300
460
  }
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
- }
461
+ getAssetPrefix() {
462
+ return generateAssetPrefixFromName({ name: this.name });
328
463
  }
329
464
  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
- };
465
+ return this.serialized;
466
+ }
467
+ };
468
+ var DefaultApplication = class extends Application {
469
+ constructor(name, {
470
+ app,
471
+ overrides
472
+ }) {
473
+ super(name, {
474
+ app,
475
+ overrides,
476
+ isDefault: true
477
+ });
478
+ this.default = true;
479
+ this.production = new Host(app.production);
480
+ }
481
+ getAssetPrefix() {
482
+ return "";
483
+ }
484
+ };
485
+ var ChildApplication = class extends Application {
486
+ constructor(name, {
487
+ app,
488
+ overrides
489
+ }) {
490
+ ChildApplication.validate(name, app);
491
+ super(name, {
492
+ app,
493
+ overrides,
494
+ isDefault: false
495
+ });
496
+ this.default = false;
497
+ this.routing = app.routing;
498
+ }
499
+ static validate(name, app) {
500
+ validateAppPaths(name, app);
352
501
  }
353
502
  };
354
503
 
355
- // src/config/common/microfrontend-config.ts
356
- var SUPPORTED_VERSIONS = ["1"];
504
+ // src/config/microfrontends-config/isomorphic/constants.ts
357
505
  var DEFAULT_LOCAL_PROXY_PORT = 3024;
358
- var MicrofrontendConfigCommon = class {
506
+
507
+ // src/config/microfrontends-config/isomorphic/index.ts
508
+ var MicrofrontendConfigIsomorphic = class {
359
509
  constructor({
360
510
  config,
361
- overrides
511
+ overrides,
512
+ meta
362
513
  }) {
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" }
514
+ this.childApplications = {};
515
+ var _a, _b, _c, _d;
516
+ MicrofrontendConfigIsomorphic.validate(config);
517
+ const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
518
+ this.overrides = overrides && !disableOverrides ? overrides : void 0;
519
+ this.isMainConfig = isMainConfig(config);
520
+ if (isMainConfig(config)) {
521
+ for (const [appId, appConfig] of Object.entries(config.applications)) {
522
+ const appOverrides = !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.applications[appId] : void 0;
523
+ if (isDefaultApp(appConfig)) {
524
+ this.defaultApplication = new DefaultApplication(appId, {
525
+ app: appConfig,
526
+ overrides: appOverrides
527
+ });
528
+ } else {
529
+ this.childApplications[appId] = new ChildApplication(appId, {
530
+ app: appConfig,
531
+ overrides: appOverrides
532
+ });
533
+ }
534
+ }
535
+ } else {
536
+ this.partOf = config.partOf;
537
+ const appOverrides = !disableOverrides ? (_d = this.overrides) == null ? void 0 : _d.applications[meta.fromApp] : void 0;
538
+ this.childApplications[meta.fromApp] = new ChildApplication(
539
+ meta.fromApp,
540
+ {
541
+ // we don't know routing because we're not in the main config
542
+ app: { routing: [] },
543
+ overrides: appOverrides
544
+ }
371
545
  );
372
546
  }
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
- });
547
+ if (isMainConfig(config) && !this.defaultApplication) {
548
+ throw new MicrofrontendError(
549
+ `Could not find default application in microfrontends configuration`,
550
+ {
551
+ type: "application",
552
+ subtype: "not_found"
553
+ }
554
+ );
380
555
  }
381
556
  this.config = config;
382
- this.name = config.name;
383
- this.version = config.version;
384
557
  this.options = config.options;
385
- this.$schema = config.$schema;
558
+ this.serialized = {
559
+ config,
560
+ overrides,
561
+ meta
562
+ };
563
+ }
564
+ static validate(config) {
565
+ const c = typeof config === "string" ? parse(config) : config;
566
+ if (isMainConfig(c)) {
567
+ validateConfigVersion(c.version);
568
+ validateConfigPaths(c.applications);
569
+ validateConfigDefaultApplication(c.applications);
570
+ }
571
+ return c;
572
+ }
573
+ static fromEnv({
574
+ meta,
575
+ cookies
576
+ }) {
577
+ return new MicrofrontendConfigIsomorphic({
578
+ config: parse(getConfigStringFromEnv()),
579
+ overrides: parseOverrides(cookies ?? []),
580
+ meta
581
+ });
386
582
  }
387
583
  isOverridesDisabled() {
388
584
  var _a, _b;
389
585
  return ((_b = (_a = this.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
390
586
  }
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
587
  getConfig() {
405
588
  return this.config;
406
589
  }
590
+ getApplicationsByType() {
591
+ return {
592
+ defaultApplication: this.defaultApplication,
593
+ applications: Object.values(this.childApplications)
594
+ };
595
+ }
596
+ getChildApplications() {
597
+ return Object.values(this.childApplications);
598
+ }
407
599
  getAllApplications() {
408
- return Object.values(this.zones);
600
+ return [
601
+ this.defaultApplication,
602
+ ...Object.values(this.childApplications)
603
+ ].filter(Boolean);
409
604
  }
410
- getZone(name) {
411
- const zone = this.zones[name];
412
- if (!zone) {
605
+ getApplication(name) {
606
+ var _a;
607
+ if (((_a = this.defaultApplication) == null ? void 0 : _a.name) === name) {
608
+ return this.defaultApplication;
609
+ }
610
+ const app = this.childApplications[name];
611
+ if (!app) {
413
612
  throw new MicrofrontendError(
414
613
  `Could not find microfrontends configuration for application "${name}"`,
415
614
  {
416
- type: "zone",
615
+ type: "application",
417
616
  subtype: "not_found"
418
617
  }
419
618
  );
420
619
  }
421
- return zone;
620
+ return app;
422
621
  }
423
622
  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;
623
+ var _a, _b;
624
+ if (((_b = (_a = this.defaultApplication) == null ? void 0 : _a.vercel) == null ? void 0 : _b.projectId) === projectId) {
625
+ return this.defaultApplication;
626
+ }
627
+ return Object.values(this.childApplications).find(
628
+ (app) => {
629
+ var _a2;
630
+ return ((_a2 = app.vercel) == null ? void 0 : _a2.projectId) === projectId;
428
631
  }
429
632
  );
430
633
  }
431
- getDefaultZone() {
432
- const zone = Object.values(this.zones).find((z) => z.default);
433
- if (!zone) {
634
+ /**
635
+ * Returns the default application. This can throw if the default application
636
+ * is undefined ( )
637
+ */
638
+ getDefaultApplication() {
639
+ if (!this.defaultApplication) {
434
640
  throw new MicrofrontendError(
435
- `Could not find default zone in microfrontends configuration`,
641
+ `Could not find default application in microfrontends configuration`,
436
642
  {
437
- type: "zone",
643
+ type: "application",
438
644
  subtype: "not_found"
439
645
  }
440
646
  );
441
647
  }
442
- return zone;
648
+ return this.defaultApplication;
443
649
  }
444
650
  /**
445
651
  * Returns the configured port for the local proxy
@@ -454,671 +660,801 @@ var MicrofrontendConfigCommon = class {
454
660
  * NOTE: This is used when writing the config to disk and must always match the input Schema
455
661
  */
456
662
  toSchemaJson() {
457
- const applications = {};
458
- for (const [name, zone] of Object.entries(this.zones)) {
459
- applications[name] = zone.serialize();
460
- }
461
- return {
462
- $schema: this.$schema,
463
- name: this.name,
464
- version: this.version,
465
- options: this.options,
466
- applications
467
- };
663
+ return this.serialized.config;
468
664
  }
469
- serialize() {
470
- var _a;
471
- const applications = {};
472
- for (const [name, zone] of Object.entries(this.zones)) {
473
- applications[name] = zone.serialize();
474
- }
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
- };
485
- }
486
- write(_) {
487
- throw new MicrofrontendError(
488
- `Writing to file to disk requires using an instance of "MicrofrontendConfig".`,
489
- { type: "config", subtype: "unsupported_operation" }
665
+ toClientConfig() {
666
+ const applications = Object.fromEntries(
667
+ Object.entries(this.childApplications).map(([name, application]) => [
668
+ name,
669
+ {
670
+ default: false,
671
+ routing: application.routing
672
+ }
673
+ ])
490
674
  );
675
+ if (this.defaultApplication) {
676
+ applications[this.defaultApplication.name] = {
677
+ default: true
678
+ };
679
+ }
680
+ return new MicrofrontendConfigClient({
681
+ applications
682
+ });
683
+ }
684
+ serialize() {
685
+ return this.serialized;
491
686
  }
492
687
  };
493
688
 
494
- // src/config/utils/get-output-file-path.ts
495
- import path2 from "node:path";
689
+ // src/config/microfrontends-config/isomorphic/child.ts
690
+ var MicrofrontendChildConfig = class extends MicrofrontendConfigIsomorphic {
691
+ constructor({
692
+ config,
693
+ overrides,
694
+ meta
695
+ }) {
696
+ super({ config, overrides, meta });
697
+ this.isMainConfig = false;
698
+ this.partOf = config.partOf;
699
+ }
700
+ };
701
+
702
+ // src/config/microfrontends-config/isomorphic/main.ts
703
+ var MicrofrontendMainConfig = class extends MicrofrontendConfigIsomorphic {
704
+ constructor({
705
+ config,
706
+ overrides,
707
+ meta
708
+ }) {
709
+ var _a, _b, _c;
710
+ super({ config, overrides, meta });
711
+ this.isMainConfig = true;
712
+ const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
713
+ let defaultApplication;
714
+ for (const [appId, appConfig] of Object.entries(config.applications)) {
715
+ const appOverrides = !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.applications[appId] : void 0;
716
+ if (isDefaultApp(appConfig)) {
717
+ defaultApplication = new DefaultApplication(appId, {
718
+ app: appConfig,
719
+ overrides: appOverrides
720
+ });
721
+ } else {
722
+ this.childApplications[appId] = new ChildApplication(appId, {
723
+ app: appConfig,
724
+ overrides: appOverrides
725
+ });
726
+ }
727
+ }
728
+ if (!defaultApplication) {
729
+ throw new MicrofrontendError(
730
+ `Could not find default application in microfrontends configuration`,
731
+ {
732
+ type: "application",
733
+ subtype: "not_found"
734
+ }
735
+ );
736
+ }
737
+ this.defaultApplication = defaultApplication;
738
+ }
739
+ };
740
+
741
+ // src/config/microfrontends/isomorphic/index.ts
742
+ var Microfrontends = class {
743
+ constructor({
744
+ config,
745
+ overrides,
746
+ meta
747
+ }) {
748
+ if (isMainConfig(config)) {
749
+ this.config = new MicrofrontendMainConfig({ config, overrides, meta });
750
+ } else {
751
+ this.config = new MicrofrontendChildConfig({ config, overrides, meta });
752
+ }
753
+ }
754
+ isChildConfig() {
755
+ return this.config instanceof MicrofrontendChildConfig;
756
+ }
757
+ static fromEnv({
758
+ cookies,
759
+ meta
760
+ }) {
761
+ const config = MicrofrontendConfigIsomorphic.fromEnv({
762
+ cookies,
763
+ meta
764
+ });
765
+ return new Microfrontends(config.serialize());
766
+ }
767
+ };
768
+
769
+ // src/config/microfrontends/utils/find-repository-root.ts
770
+ import fs from "node:fs";
771
+ import path from "node:path";
772
+ var GIT_DIRECTORY = ".git";
773
+ function findRepositoryRoot(startDir) {
774
+ let currentDir = startDir || process.cwd();
775
+ while (currentDir !== path.parse(currentDir).root) {
776
+ const gitPath = path.join(currentDir, GIT_DIRECTORY);
777
+ if (fs.existsSync(gitPath) && fs.statSync(gitPath).isDirectory()) {
778
+ return currentDir;
779
+ }
780
+ currentDir = path.dirname(currentDir);
781
+ }
782
+ throw new Error(
783
+ "Repository root not found. Specify the root of the repository with the `repository.root` option."
784
+ );
785
+ }
786
+
787
+ // src/config/microfrontends/utils/find-package-path.ts
788
+ import { dirname } from "node:path";
789
+ import { readFileSync } from "node:fs";
790
+ import fg from "fast-glob";
791
+ var configCache = {};
792
+ function findPackagePathWithGlob({
793
+ repositoryRoot,
794
+ name
795
+ }) {
796
+ try {
797
+ const packageJsonPaths = fg.globSync("**/package.json", {
798
+ cwd: repositoryRoot,
799
+ absolute: true,
800
+ onlyFiles: true,
801
+ followSymbolicLinks: false,
802
+ ignore: ["**/node_modules/**", "**/.git/**"]
803
+ });
804
+ const matchingPaths = [];
805
+ for (const packageJsonPath2 of packageJsonPaths) {
806
+ const packageJsonContent = readFileSync(packageJsonPath2, "utf-8");
807
+ const packageJson = JSON.parse(packageJsonContent);
808
+ if (packageJson.name === name) {
809
+ matchingPaths.push(packageJsonPath2);
810
+ }
811
+ }
812
+ if (matchingPaths.length > 1) {
813
+ throw new Error(
814
+ `Found multiple packages with the name "${name}" in the repository: ${matchingPaths.join(", ")}`
815
+ );
816
+ }
817
+ if (matchingPaths.length === 0) {
818
+ throw new Error(
819
+ `Could not find package with the name "${name}" in the repository`
820
+ );
821
+ }
822
+ const [packageJsonPath] = matchingPaths;
823
+ return dirname(packageJsonPath);
824
+ } catch (error) {
825
+ return null;
826
+ }
827
+ }
828
+ function findPackagePath(opts) {
829
+ const cacheKey = `${opts.repositoryRoot}-${opts.name}`;
830
+ if (configCache[cacheKey]) {
831
+ return configCache[cacheKey];
832
+ }
833
+ const result = findPackagePathWithGlob(opts);
834
+ if (!result) {
835
+ throw new Error(
836
+ `Could not find package with the name "${opts.name}" in the repository`
837
+ );
838
+ }
839
+ configCache[cacheKey] = result;
840
+ return result;
841
+ }
842
+
843
+ // src/config/microfrontends/utils/find-default-package.ts
844
+ import { dirname as dirname2 } from "node:path";
845
+ import { readFileSync as readFileSync2 } from "node:fs";
846
+ import { parse as parse2 } from "jsonc-parser";
847
+ import fg2 from "fast-glob";
496
848
 
497
849
  // src/config/constants.ts
498
- var MFE_CONFIG_DEFAULT_FILE_PATH2 = "micro-frontends";
499
- var MFE_CONFIG_DEFAULT_FILE_NAME2 = "micro-frontends.config.json";
850
+ var CONFIGURATION_FILENAMES = [
851
+ "microfrontends.jsonc",
852
+ "microfrontends.json"
853
+ ];
854
+
855
+ // src/config/microfrontends/utils/find-default-package.ts
856
+ var configCache2 = {};
857
+ function findDefaultMicrofrontendsPackages({
858
+ repositoryRoot,
859
+ applicationName
860
+ }) {
861
+ try {
862
+ const microfrontendsJsonPaths = fg2.globSync(
863
+ `**/{${CONFIGURATION_FILENAMES.join(",")}}`,
864
+ {
865
+ cwd: repositoryRoot,
866
+ absolute: true,
867
+ onlyFiles: true,
868
+ followSymbolicLinks: false,
869
+ ignore: ["**/node_modules/**", "**/.git/**"]
870
+ }
871
+ );
872
+ const matchingPaths = [];
873
+ for (const microfrontendsJsonPath of microfrontendsJsonPaths) {
874
+ const microfrontendsJsonContent = readFileSync2(
875
+ microfrontendsJsonPath,
876
+ "utf-8"
877
+ );
878
+ const microfrontendsJson = parse2(microfrontendsJsonContent);
879
+ if (isMainConfig(microfrontendsJson) && microfrontendsJson.applications[applicationName]) {
880
+ matchingPaths.push(microfrontendsJsonPath);
881
+ }
882
+ }
883
+ if (matchingPaths.length > 1) {
884
+ throw new Error(
885
+ `Found multiple default applications referencing "${applicationName}" in the repository, this is not yet supported.
886
+ ${matchingPaths.join("\n \u2022 ")}`
887
+ );
888
+ }
889
+ if (matchingPaths.length === 0) {
890
+ throw new Error(
891
+ `Could not find default application with "applications.${applicationName}"`
892
+ );
893
+ }
894
+ const [packageJsonPath] = matchingPaths;
895
+ return dirname2(packageJsonPath);
896
+ } catch (error) {
897
+ return null;
898
+ }
899
+ }
900
+ function findDefaultMicrofrontendsPackage(opts) {
901
+ const cacheKey = `${opts.repositoryRoot}-${opts.applicationName}`;
902
+ if (configCache2[cacheKey]) {
903
+ return configCache2[cacheKey];
904
+ }
905
+ const result = findDefaultMicrofrontendsPackages(opts);
906
+ if (!result) {
907
+ throw new Error(
908
+ `Error trying to resolve the main microfrontends configuration`
909
+ );
910
+ }
911
+ configCache2[cacheKey] = result;
912
+ return result;
913
+ }
914
+
915
+ // src/config/microfrontends/utils/is-monorepo.ts
916
+ import fs2 from "node:fs";
917
+ import path2 from "node:path";
918
+ function isMonorepo({
919
+ repositoryRoot
920
+ }) {
921
+ try {
922
+ if (fs2.existsSync(path2.join(repositoryRoot, "pnpm-workspace.yaml"))) {
923
+ return true;
924
+ }
925
+ if (fs2.existsSync(path2.join(repositoryRoot, "vlt-workspaces.json"))) {
926
+ return true;
927
+ }
928
+ const packageJsonPath = path2.join(repositoryRoot, "package.json");
929
+ if (!fs2.existsSync(packageJsonPath)) {
930
+ return false;
931
+ }
932
+ const packageJson = JSON.parse(
933
+ fs2.readFileSync(packageJsonPath, "utf-8")
934
+ );
935
+ return packageJson.workspaces !== void 0;
936
+ } catch (error) {
937
+ console.error("Error determining if repository is a monorepo", error);
938
+ return false;
939
+ }
940
+ }
941
+
942
+ // src/config/microfrontends/utils/find-package-root.ts
943
+ import fs3 from "node:fs";
944
+ import path3 from "node:path";
945
+ var PACKAGE_JSON = "package.json";
946
+ function findPackageRoot(startDir) {
947
+ let currentDir = startDir || process.cwd();
948
+ while (currentDir !== path3.parse(currentDir).root) {
949
+ const pkgJsonPath = path3.join(currentDir, PACKAGE_JSON);
950
+ if (fs3.existsSync(pkgJsonPath)) {
951
+ return currentDir;
952
+ }
953
+ currentDir = path3.dirname(currentDir);
954
+ }
955
+ throw new Error(
956
+ "Package root not found. Specify the root of the package with the `package.root` option."
957
+ );
958
+ }
959
+
960
+ // src/config/microfrontends/utils/find-config.ts
961
+ import fs4 from "node:fs";
962
+ import { join } from "node:path";
963
+ function findConfig({ dir }) {
964
+ for (const filename of CONFIGURATION_FILENAMES) {
965
+ const maybeConfig = join(dir, filename);
966
+ if (fs4.existsSync(maybeConfig)) {
967
+ return maybeConfig;
968
+ }
969
+ }
970
+ return null;
971
+ }
500
972
 
501
- // src/config/utils/get-output-file-path.ts
502
- function getOutputFilePath2() {
973
+ // src/config/microfrontends/server/utils/get-output-file-path.ts
974
+ import path4 from "node:path";
975
+
976
+ // src/config/microfrontends/server/constants.ts
977
+ var MFE_CONFIG_DEFAULT_FILE_PATH = "microfrontends";
978
+ var MFE_CONFIG_DEFAULT_FILE_NAME = "microfrontends.json";
979
+
980
+ // src/utils/is-vercel.ts
981
+ function isVercel() {
982
+ return process.env.VERCEL === "1";
983
+ }
984
+
985
+ // src/config/microfrontends/server/utils/get-output-file-path.ts
986
+ function getOutputFilePath() {
503
987
  if (isVercel()) {
504
- return path2.join(
988
+ return path4.join(
505
989
  ".vercel",
506
- MFE_CONFIG_DEFAULT_FILE_PATH2,
507
- MFE_CONFIG_DEFAULT_FILE_NAME2
990
+ MFE_CONFIG_DEFAULT_FILE_PATH,
991
+ MFE_CONFIG_DEFAULT_FILE_NAME
508
992
  );
509
993
  }
510
- return path2.join(MFE_CONFIG_DEFAULT_FILE_PATH2, MFE_CONFIG_DEFAULT_FILE_NAME2);
994
+ return path4.join(MFE_CONFIG_DEFAULT_FILE_PATH, MFE_CONFIG_DEFAULT_FILE_NAME);
511
995
  }
512
996
 
513
- // src/config/validation.ts
514
- import { parse } from "jsonc-parser";
515
- import { pathToRegexp, parse as parsePathRegexp } from "path-to-regexp";
997
+ // src/config/microfrontends/server/validation.ts
998
+ import { parse as parse3 } from "jsonc-parser";
516
999
  import { Ajv } from "ajv";
517
1000
 
518
- // src/config/types.ts
519
- var isDefaultApplicationConfig = (app) => app.default && typeof app.routing === "undefined";
520
-
521
1001
  // schema/schema.json
522
1002
  var schema_default = {
523
1003
  $schema: "http://json-schema.org/draft-07/schema#",
524
1004
  $ref: "#/definitions/Config",
525
1005
  definitions: {
526
1006
  Config: {
1007
+ anyOf: [
1008
+ {
1009
+ $ref: "#/definitions/MainConfig"
1010
+ },
1011
+ {
1012
+ $ref: "#/definitions/ChildConfig"
1013
+ }
1014
+ ]
1015
+ },
1016
+ MainConfig: {
527
1017
  type: "object",
528
1018
  properties: {
529
- version: {
530
- type: "string"
531
- },
532
1019
  $schema: {
533
1020
  type: "string"
534
1021
  },
535
- name: {
1022
+ version: {
536
1023
  type: "string",
537
- description: 'Name for the micro-frontend site (eg. "vercel.com", "vercel-site" etc.).'
1024
+ const: "2"
1025
+ },
1026
+ options: {
1027
+ $ref: "#/definitions/Options"
1028
+ },
1029
+ remotes: {
1030
+ type: "object",
1031
+ additionalProperties: {
1032
+ $ref: "#/definitions/Application"
1033
+ },
1034
+ 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
1035
  },
539
1036
  applications: {
540
- $ref: "#/definitions/ApplicationConfigsById"
1037
+ $ref: "#/definitions/ApplicationRouting",
1038
+ 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"
1039
+ }
1040
+ },
1041
+ required: ["applications", "version"],
1042
+ additionalProperties: false
1043
+ },
1044
+ Options: {
1045
+ type: "object",
1046
+ properties: {
1047
+ vercel: {
1048
+ $ref: "#/definitions/VercelOptions",
1049
+ description: "Micro-Frontends wide options for Vercel."
541
1050
  },
542
- options: {
543
- $ref: "#/definitions/Options",
544
- description: "Optional configuration for the entire micro-frontends setup."
1051
+ localProxy: {
1052
+ $ref: "#/definitions/LocalProxyOptions",
1053
+ description: "Options for local proxy."
545
1054
  }
546
1055
  },
547
- required: ["version", "applications"],
548
- description: "Configuration for micro-frontend applications\n\nTODO: Add proxy configuration"
1056
+ additionalProperties: false
549
1057
  },
550
- ApplicationConfigsById: {
1058
+ VercelOptions: {
551
1059
  type: "object",
552
- additionalProperties: {
553
- $ref: "#/definitions/ApplicationConfig"
554
- }
1060
+ properties: {
1061
+ teamSlug: {
1062
+ type: "string",
1063
+ description: "Team slug for the Vercel team"
1064
+ },
1065
+ disableOverrides: {
1066
+ type: "boolean",
1067
+ 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."
1068
+ }
1069
+ },
1070
+ additionalProperties: false
555
1071
  },
556
- ApplicationConfig: {
1072
+ LocalProxyOptions: {
1073
+ type: "object",
1074
+ properties: {
1075
+ port: {
1076
+ type: "number",
1077
+ description: "The port number used by the local proxy server.\n\nThe default is `3024`."
1078
+ }
1079
+ },
1080
+ additionalProperties: false
1081
+ },
1082
+ Application: {
557
1083
  anyOf: [
558
1084
  {
559
- $ref: "#/definitions/DefaultApplicationConfig"
1085
+ $ref: "#/definitions/DefaultApplication"
560
1086
  },
561
1087
  {
562
- $ref: "#/definitions/CommonApplicationConfig"
1088
+ $ref: "#/definitions/ChildApplication"
563
1089
  }
564
- ],
565
- description: "A Micro-Frontend Deployment Target"
1090
+ ]
566
1091
  },
567
- DefaultApplicationConfig: {
1092
+ DefaultApplication: {
568
1093
  type: "object",
569
1094
  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"
1095
+ vercel: {
1096
+ $ref: "#/definitions/Vercel"
577
1097
  },
578
1098
  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"]
1099
+ $ref: "#/definitions/Development"
594
1100
  },
595
1101
  production: {
596
1102
  $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
1103
  }
637
1104
  },
638
- required: ["default", "development", "production"]
1105
+ required: ["production"],
1106
+ additionalProperties: false
639
1107
  },
640
- Routing: {
1108
+ Vercel: {
641
1109
  type: "object",
642
1110
  properties: {
643
- assetPrefix: {
1111
+ projectId: {
644
1112
  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."
1113
+ description: "Vercel project ID"
653
1114
  }
654
1115
  },
655
- required: ["matches"]
1116
+ required: ["projectId"],
1117
+ additionalProperties: false
656
1118
  },
657
- PathGroup: {
1119
+ Development: {
658
1120
  type: "object",
659
1121
  properties: {
660
- group: {
661
- type: "string",
662
- description: "Optional group name for the paths"
1122
+ local: {
1123
+ $ref: "#/definitions/LocalHostConfig"
663
1124
  },
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
- }
1125
+ fallback: {
1126
+ $ref: "#/definitions/HostConfig",
1127
+ 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
1128
  },
673
- paths: {
674
- type: "array",
675
- items: {
676
- type: "string"
677
- }
1129
+ task: {
1130
+ type: "string",
1131
+ description: "Optional task to run when starting the development server. Should reference a script in the package.json of the application."
678
1132
  }
679
1133
  },
680
- required: ["paths"]
1134
+ additionalProperties: false
681
1135
  },
682
- HostConfig: {
1136
+ LocalHostConfig: {
683
1137
  type: "object",
1138
+ additionalProperties: false,
684
1139
  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
1140
  host: {
691
1141
  type: "string",
692
1142
  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
1143
  },
1144
+ protocol: {
1145
+ type: "string",
1146
+ enum: ["http", "https"],
1147
+ 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'
1148
+ },
694
1149
  port: {
695
1150
  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"
1151
+ description: "The port number to be used for the connection. Common values include `80` for HTTP and `443` for HTTPS."
697
1152
  }
698
- },
699
- required: ["host"]
1153
+ }
700
1154
  },
701
- Vercel: {
1155
+ HostConfig: {
702
1156
  type: "object",
703
1157
  properties: {
704
- projectId: {
705
- type: "string",
706
- description: "Vercel project ID"
707
- },
708
- projectName: {
1158
+ protocol: {
709
1159
  type: "string",
710
- description: "Vercel project name (temporary until we can use project ID)"
1160
+ enum: ["http", "https"],
1161
+ 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
1162
  },
712
- defaultRoute: {
1163
+ host: {
713
1164
  type: "string",
714
- description: "The default route for the application. Used to render screenshots, favicons, and provide direct zone links"
1165
+ 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
1166
  },
716
- routeSpeedInsightsToDefaultZone: {
717
- type: "boolean",
718
- description: "Whether to route Speed Insights to the default zone or each individual microfrontend."
1167
+ port: {
1168
+ type: "number",
1169
+ description: "The port number to be used for the connection. Common values include `80` for HTTP and `443` for HTTPS."
719
1170
  }
720
1171
  },
721
- required: ["projectId"]
1172
+ required: ["host"],
1173
+ additionalProperties: false
722
1174
  },
723
- CommonApplicationConfig: {
1175
+ ChildApplication: {
724
1176
  type: "object",
725
1177
  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"
1178
+ vercel: {
1179
+ $ref: "#/definitions/Vercel"
733
1180
  },
734
1181
  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"]
1182
+ $ref: "#/definitions/Development"
1183
+ },
1184
+ routing: {
1185
+ $ref: "#/definitions/Routing",
1186
+ description: "Groups of path expressions that are routed to this application."
750
1187
  },
751
1188
  production: {
752
1189
  $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
1190
  }
793
1191
  },
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
- }
808
- },
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`;
1192
+ required: ["routing"],
1193
+ additionalProperties: false
1194
+ },
1195
+ Routing: {
1196
+ type: "array",
1197
+ items: {
1198
+ $ref: "#/definitions/PathGroup"
990
1199
  }
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`;
1200
+ },
1201
+ PathGroup: {
1202
+ type: "object",
1203
+ properties: {
1204
+ group: {
1205
+ type: "string",
1206
+ description: "Optional group name for the paths"
1207
+ },
1208
+ flag: {
1209
+ type: "string",
1210
+ description: "flag name that can be used to enable/disable all paths in the group"
1211
+ },
1212
+ paths: {
1213
+ type: "array",
1214
+ items: {
1215
+ type: "string"
1216
+ }
1217
+ }
1218
+ },
1219
+ required: ["paths"],
1220
+ additionalProperties: false
1221
+ },
1222
+ ApplicationRouting: {
1223
+ type: "object",
1224
+ additionalProperties: {
1225
+ $ref: "#/definitions/Application"
993
1226
  }
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
- );
1227
+ },
1228
+ ChildConfig: {
1229
+ type: "object",
1230
+ properties: {
1231
+ $schema: {
1232
+ type: "string"
1233
+ },
1234
+ version: {
1235
+ type: "string",
1236
+ const: "2"
1237
+ },
1238
+ options: {
1239
+ $ref: "#/definitions/Options"
1240
+ },
1241
+ remotes: {
1242
+ type: "object",
1243
+ additionalProperties: {
1244
+ $ref: "#/definitions/Application"
1245
+ },
1246
+ 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."
1247
+ },
1248
+ partOf: {
1249
+ type: "string",
1250
+ 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."
1251
+ }
1252
+ },
1253
+ required: ["partOf", "version"],
1254
+ additionalProperties: false
1023
1255
  }
1024
1256
  }
1025
1257
  };
1026
1258
 
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;
1259
+ // src/config/schema/utils/load.ts
1260
+ var SCHEMA = schema_default;
1261
+
1262
+ // src/config/microfrontends/server/validation.ts
1263
+ function validateSchema(configString) {
1264
+ const parsedConfig = parse3(configString);
1265
+ const ajv = new Ajv();
1266
+ const validate = ajv.compile(SCHEMA);
1267
+ const isValid = validate(parsedConfig);
1268
+ if (!isValid) {
1269
+ throw new MicrofrontendError(
1270
+ `Invalid config: ${ajv.errorsText(validate.errors)}`,
1271
+ { type: "config", subtype: "does_not_match_schema" }
1272
+ );
1046
1273
  }
1047
- return {
1048
- ...common,
1049
- routing: convertV1RoutingToV2Routing(application.routing)
1050
- };
1274
+ return parsedConfig;
1051
1275
  }
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
- ])
1276
+
1277
+ // src/config/microfrontends/server/index.ts
1278
+ var MicrofrontendsServer = class extends Microfrontends {
1279
+ /**
1280
+ * Writes the configuration to a file.
1281
+ */
1282
+ writeConfig(opts = {
1283
+ pretty: true
1284
+ }) {
1285
+ const outputPath = getOutputFilePath();
1286
+ fs5.mkdirSync(dirname3(outputPath), { recursive: true });
1287
+ fs5.writeFileSync(
1288
+ outputPath,
1289
+ JSON.stringify(
1290
+ this.config.toSchemaJson(),
1291
+ null,
1292
+ opts.pretty ?? true ? 2 : void 0
1068
1293
  )
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");
1294
+ );
1076
1295
  }
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;
1296
+ // --------- Static Methods ---------
1297
+ /**
1298
+ * Generates a MicrofrontendsServer instance from an unknown object.
1299
+ */
1300
+ static fromUnknown({
1301
+ config,
1302
+ cookies,
1303
+ meta
1304
+ }) {
1305
+ const overrides = cookies ? parseOverrides(cookies) : void 0;
1306
+ if (typeof config === "string") {
1307
+ return new MicrofrontendsServer({
1308
+ config: MicrofrontendsServer.validate(config),
1309
+ overrides,
1310
+ meta
1311
+ });
1312
+ }
1313
+ if (typeof config === "object") {
1314
+ return new MicrofrontendsServer({
1315
+ config,
1316
+ overrides,
1317
+ meta
1318
+ });
1319
+ }
1320
+ throw new MicrofrontendError(
1321
+ "Invalid config: must be a string or an object",
1322
+ { type: "config", subtype: "does_not_match_schema" }
1323
+ );
1104
1324
  }
1325
+ /**
1326
+ * Generates a MicrofrontendsServer instance from the environment.
1327
+ * Uses additional validation that is only available when in a node runtime
1328
+ */
1105
1329
  static fromEnv({
1106
- cookies
1330
+ cookies,
1331
+ meta
1107
1332
  }) {
1108
- return new MicrofrontendConfigCommon({
1109
- config: MicrofrontendConfig.validate(
1110
- MicrofrontendConfigCommon.getConfigFromEnv()
1111
- ),
1112
- overrides: Overrides.parseOverrides(cookies)
1333
+ return new MicrofrontendsServer({
1334
+ config: MicrofrontendsServer.validate(getConfigStringFromEnv()),
1335
+ overrides: parseOverrides(cookies),
1336
+ meta
1113
1337
  });
1114
1338
  }
1339
+ /**
1340
+ * Validates the configuration against the JSON schema
1341
+ */
1342
+ static validate(config) {
1343
+ if (typeof config === "string") {
1344
+ const c = validateSchema(config);
1345
+ return c;
1346
+ }
1347
+ return config;
1348
+ }
1349
+ /**
1350
+ * Looks up the configuration by inferring the package root and looking for a microfrontends config file. If a file is not found,
1351
+ * it will look for a package in the repository with a microfrontends file that contains the current application
1352
+ * and use that configuration.
1353
+ *
1354
+ * This can return either a Child or Main configuration.
1355
+ */
1356
+ static infer({
1357
+ directory,
1358
+ filePath,
1359
+ meta,
1360
+ cookies,
1361
+ options
1362
+ } = {}) {
1363
+ if (filePath && meta) {
1364
+ return MicrofrontendsServer.fromFile({
1365
+ filePath,
1366
+ cookies,
1367
+ meta,
1368
+ options
1369
+ });
1370
+ }
1371
+ try {
1372
+ const packageRoot = findPackageRoot(directory);
1373
+ const packageJsonPath = join2(packageRoot, "package.json");
1374
+ const packageJson = JSON.parse(
1375
+ fs5.readFileSync(packageJsonPath, "utf-8")
1376
+ );
1377
+ if (!packageJson.name) {
1378
+ throw new Error(`No name found in package.json at ${packageJsonPath}`);
1379
+ }
1380
+ const configMeta = meta ?? { fromApp: packageJson.name };
1381
+ const maybeConfig = findConfig({ dir: packageRoot });
1382
+ if (maybeConfig) {
1383
+ return MicrofrontendsServer.fromFile({
1384
+ filePath: maybeConfig,
1385
+ cookies,
1386
+ meta: configMeta,
1387
+ options
1388
+ });
1389
+ }
1390
+ const repositoryRoot = findRepositoryRoot();
1391
+ const isMonorepo2 = isMonorepo({ repositoryRoot });
1392
+ if (isMonorepo2) {
1393
+ const defaultPackage = findDefaultMicrofrontendsPackage({
1394
+ repositoryRoot,
1395
+ applicationName: packageJson.name
1396
+ });
1397
+ const maybeConfigFromDefault = findConfig({ dir: defaultPackage });
1398
+ if (maybeConfigFromDefault) {
1399
+ return MicrofrontendsServer.fromFile({
1400
+ filePath: maybeConfigFromDefault,
1401
+ cookies,
1402
+ meta: configMeta,
1403
+ options
1404
+ });
1405
+ }
1406
+ }
1407
+ throw new Error("Unable to infer");
1408
+ } catch (e) {
1409
+ throw new MicrofrontendError(
1410
+ "Unable to infer microfrontends configuration",
1411
+ { type: "config", subtype: "inference_failed" }
1412
+ );
1413
+ }
1414
+ }
1415
+ /*
1416
+ * Generates a MicrofrontendsServer instance from a file.
1417
+ */
1115
1418
  static fromFile({
1116
- filePath
1419
+ filePath,
1420
+ cookies,
1421
+ meta,
1422
+ options
1117
1423
  }) {
1118
1424
  try {
1119
- const config = fs2.readFileSync(filePath, "utf-8");
1120
- return new MicrofrontendConfig({
1121
- config: MicrofrontendConfig.validate(config)
1425
+ const configJson = fs5.readFileSync(filePath, "utf-8");
1426
+ const config = MicrofrontendsServer.validate(configJson);
1427
+ if (!isMainConfig(config) && (options == null ? void 0 : options.resolveMainConfig)) {
1428
+ const repositoryRoot = findRepositoryRoot();
1429
+ const isMonorepo2 = isMonorepo({ repositoryRoot });
1430
+ if (isMonorepo2) {
1431
+ const packagePath = findPackagePath({
1432
+ repositoryRoot,
1433
+ name: config.partOf
1434
+ });
1435
+ if (!packagePath) {
1436
+ throw new MicrofrontendError(
1437
+ `Could not find default application "${config.partOf}" in the repository`,
1438
+ { type: "config", subtype: "not_found" }
1439
+ );
1440
+ }
1441
+ const maybeConfig = findConfig({ dir: packagePath });
1442
+ if (!maybeConfig) {
1443
+ throw new MicrofrontendError(
1444
+ `Could not find microfrontends configuration in ${packagePath}`,
1445
+ { type: "config", subtype: "not_found" }
1446
+ );
1447
+ }
1448
+ return MicrofrontendsServer.fromMainConfigFile({
1449
+ filePath: maybeConfig,
1450
+ overrides: cookies ? parseOverrides(cookies) : void 0
1451
+ });
1452
+ }
1453
+ }
1454
+ return new MicrofrontendsServer({
1455
+ config,
1456
+ overrides: cookies ? parseOverrides(cookies) : void 0,
1457
+ meta
1122
1458
  });
1123
1459
  } catch (e) {
1124
1460
  throw MicrofrontendError.handle(e, {
@@ -1126,20 +1462,41 @@ var MicrofrontendConfig = class extends MicrofrontendConfigCommon {
1126
1462
  });
1127
1463
  }
1128
1464
  }
1129
- /**
1130
- * Writes the configuration to a file.
1465
+ /*
1466
+ * Generates a MicrofrontendMainConfig instance from a file.
1131
1467
  */
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);
1468
+ static fromMainConfigFile({
1469
+ filePath,
1470
+ overrides
1471
+ }) {
1472
+ try {
1473
+ const config = fs5.readFileSync(filePath, "utf-8");
1474
+ const validatedConfig = MicrofrontendsServer.validate(config);
1475
+ if (!isMainConfig(validatedConfig)) {
1476
+ throw new MicrofrontendError(
1477
+ `${filePath} is not a main microfrontend config`,
1478
+ {
1479
+ type: "config",
1480
+ subtype: "invalid_main_path"
1481
+ }
1482
+ );
1483
+ }
1484
+ const [defaultApplication] = Object.entries(validatedConfig.applications).filter(([, app]) => isDefaultApp(app)).map(([name]) => name);
1485
+ if (!defaultApplication) {
1486
+ throw new MicrofrontendError(
1487
+ `No default application found. At least one application needs to be the default by omitting routing.`,
1488
+ { type: "config", subtype: "no_default_application" }
1489
+ );
1490
+ }
1491
+ return new MicrofrontendsServer({
1492
+ config: validatedConfig,
1493
+ overrides,
1494
+ meta: { fromApp: defaultApplication }
1495
+ });
1496
+ } catch (e) {
1497
+ throw MicrofrontendError.handle(e, {
1498
+ fileName: filePath
1499
+ });
1143
1500
  }
1144
1501
  }
1145
1502
  };
@@ -1155,19 +1512,19 @@ function displayLocalProxyInfo(port) {
1155
1512
 
1156
1513
  // src/next/config/transforms/asset-prefix.ts
1157
1514
  function transform(args) {
1158
- var _a;
1159
- const { next, zone } = args;
1160
- if (!((_a = zone.routing) == null ? void 0 : _a.assetPrefix)) {
1515
+ const { next, app } = args;
1516
+ if (app.isDefault()) {
1161
1517
  return {
1162
1518
  next
1163
1519
  };
1164
1520
  }
1165
- if (next.assetPrefix !== void 0 && next.assetPrefix !== zone.routing.assetPrefix) {
1166
- throw new Error(
1167
- `"assetPrefix" already set. Overwriting with "${zone.name}"`
1521
+ if (next.assetPrefix !== void 0 && next.assetPrefix !== app.getAssetPrefix()) {
1522
+ console.log(
1523
+ `"assetPrefix" already set. This route must be manually configured in your microfrontend.json file.`
1168
1524
  );
1525
+ } else {
1526
+ next.assetPrefix = `/${app.getAssetPrefix()}`;
1169
1527
  }
1170
- next.assetPrefix = `/${zone.routing.assetPrefix}`;
1171
1528
  return {
1172
1529
  next
1173
1530
  };
@@ -1191,8 +1548,8 @@ function transform2(args) {
1191
1548
 
1192
1549
  // src/next/config/transforms/headers.ts
1193
1550
  function transform3(args) {
1194
- const { next, zone } = args;
1195
- if (zone.isDefault()) {
1551
+ const { next, app } = args;
1552
+ if (app.isDefault()) {
1196
1553
  return {
1197
1554
  next
1198
1555
  };
@@ -1203,7 +1560,11 @@ function transform3(args) {
1203
1560
  headers: [
1204
1561
  {
1205
1562
  key: "X-Vercel-Zone",
1206
- value: zone.name
1563
+ value: app.name
1564
+ },
1565
+ {
1566
+ key: "X-Vercel-MFE-App",
1567
+ value: app.name
1207
1568
  }
1208
1569
  ]
1209
1570
  },
@@ -1242,82 +1603,39 @@ function transform3(args) {
1242
1603
  };
1243
1604
  }
1244
1605
 
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}"`);
1606
+ // src/routing/get-domain-from-environment.ts
1607
+ function getDomainFromEnvironment({
1608
+ app,
1609
+ target
1610
+ }) {
1611
+ var _a;
1612
+ const mfeProjects = JSON.parse(
1613
+ process.env.VERCEL_MICROFRONTENDS_PROJECTS ?? "[]"
1614
+ );
1615
+ if (mfeProjects.length === 0) {
1616
+ throw new Error("Missing related microfrontends project information");
1256
1617
  }
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
- }
1618
+ if (!((_a = app.vercel) == null ? void 0 : _a.projectId)) {
1619
+ throw new Error(`Missing applications[${app.name}].vercel.projectId`);
1272
1620
  }
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) {
1621
+ const vercelProject = mfeProjects.find(
1622
+ (p) => {
1623
+ var _a2;
1624
+ return p.project.id === ((_a2 = app.vercel) == null ? void 0 : _a2.projectId);
1625
+ }
1626
+ );
1627
+ if (!vercelProject) {
1279
1628
  throw new Error(
1280
- `Missing Vercel project configuration for application "${zone.name}"`
1629
+ `Missing related microfrontends project information for application "${app.name}"`
1281
1630
  );
1282
1631
  }
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}`;
1632
+ const domain = target === "preview" && vercelProject.preview.branch ? vercelProject.preview.branch : vercelProject.production.alias ?? vercelProject.production.url;
1633
+ if (!domain) {
1634
+ throw new Error(
1635
+ `Missing domain for target "${target}" in application "${app.name}"`
1636
+ );
1319
1637
  }
1320
- return zone.production.toString();
1638
+ return domain.startsWith("https://") ? domain : `https://${domain}`;
1321
1639
  }
1322
1640
 
1323
1641
  // src/routing/get-domain-for-current-environment.ts
@@ -1333,34 +1651,45 @@ ${line}
1333
1651
  `);
1334
1652
  }
1335
1653
  }
1336
- function getDomainForCurrentEnvironment(zone, opts = {}) {
1654
+ function getCurrentEnvironment() {
1655
+ const isDevelopment = !process.env.VERCEL_ENV || process.env.VERCEL_ENV === "development";
1656
+ const isPreview = process.env.VERCEL_ENV === "preview";
1657
+ const isProduction2 = process.env.VERCEL_ENV === "production";
1658
+ if (isDevelopment) {
1659
+ return { group: "development" };
1660
+ } else if (isProduction2) {
1661
+ return { group: "production" };
1662
+ } else if (isPreview) {
1663
+ return { group: "preview" };
1664
+ }
1665
+ return { group: "custom", name: process.env.VERCEL_ENV };
1666
+ }
1667
+ function getDomainForCurrentEnvironment(config, appName, opts = {}) {
1337
1668
  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];
1669
+ const app = config.getApplication(appName);
1670
+ if (!opts.ignoreOverride && ((_a = app.overrides) == null ? void 0 : _a.environment)) {
1671
+ return app.overrides.environment.toString();
1672
+ }
1673
+ const { group } = getCurrentEnvironment();
1674
+ const productionHost = config.getDefaultApplication().production.toString();
1675
+ switch (group) {
1676
+ case "development": {
1677
+ const domain = ["test", "development"].includes(process.env.NODE_ENV) ? app.development.local.toString() : productionHost;
1678
+ debugDomains(appName, "development", domain);
1679
+ return domain;
1357
1680
  }
1358
- throw new Error(
1359
- `Could not find preview domain for application "${zoneName}"`
1360
- );
1681
+ case "preview": {
1682
+ return getDomainFromEnvironment({ app, target: "preview" });
1683
+ }
1684
+ case "production": {
1685
+ return getDomainFromEnvironment({ app, target: "production" });
1686
+ }
1687
+ case "custom":
1688
+ console.warn(
1689
+ `Custom environments are not supported in getDomainForCurrentEnvironment`
1690
+ );
1691
+ return productionHost;
1361
1692
  }
1362
- debugDomains(zoneName, process.env.VERCEL_ENV, zone.production.toString());
1363
- return zone.production.toString();
1364
1693
  }
1365
1694
 
1366
1695
  // src/next/config/transforms/rewrites.ts
@@ -1382,11 +1711,11 @@ ${table}
1382
1711
  `);
1383
1712
  }
1384
1713
  }
1385
- function pathToRewrites(path3) {
1714
+ function pathToRewrites(path5) {
1386
1715
  var _a;
1387
1716
  const regex = /(?<base>^.+)\/:.+\*$/;
1388
- const match = regex.exec(path3);
1389
- const paths = [path3];
1717
+ const match = regex.exec(path5);
1718
+ const paths = [path5];
1390
1719
  if ((_a = match == null ? void 0 : match.groups) == null ? void 0 : _a.base) {
1391
1720
  paths.unshift(match.groups.base);
1392
1721
  }
@@ -1415,42 +1744,39 @@ function rewritesMapToArr(rewrites) {
1415
1744
  });
1416
1745
  }
1417
1746
  function transform4(args) {
1418
- const { next, microfrontend, zone } = args;
1747
+ const { next, microfrontend, app } = args;
1419
1748
  const buildBeforeFiles = () => {
1420
- var _a, _b, _c, _d;
1421
1749
  const rewrites = /* @__PURE__ */ new Map();
1422
- if ((_a = zone.routing) == null ? void 0 : _a.assetPrefix) {
1423
- rewrites.set(`/${zone.routing.assetPrefix}/_next/:path+`, {
1750
+ if (!app.isDefault()) {
1751
+ rewrites.set(`/${app.getAssetPrefix()}/_next/:path+`, {
1424
1752
  destination: {
1425
1753
  pathname: `/_next/:path+`
1426
1754
  }
1427
1755
  });
1428
- if (!((_b = zone.vercel) == null ? void 0 : _b.routeSpeedInsightsToDefaultZone)) {
1429
- rewrites.set(`/${zone.routing.assetPrefix}/_vercel/:path*`, {
1756
+ rewrites.set(`/${app.getAssetPrefix()}/.well-known/vercel/flags`, {
1757
+ destination: {
1758
+ pathname: `/.well-known/vercel/flags`
1759
+ }
1760
+ });
1761
+ if (process.env.VERCEL_MICROFRONTENDS_CONSOLIDATE_SPEED_INSIGHTS === "1") {
1762
+ rewrites.set(`/${app.getAssetPrefix()}/_vercel/:path*`, {
1430
1763
  destination: { pathname: "/_vercel/:path*" }
1431
1764
  });
1432
1765
  }
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) {
1766
+ } else if (microfrontend instanceof MicrofrontendMainConfig) {
1767
+ for (const [_, a] of Object.entries(
1768
+ microfrontend.getChildApplications()
1769
+ )) {
1770
+ const { routing } = a;
1771
+ const domain = getDomainForCurrentEnvironment(microfrontend, a.name);
1772
+ rewrites.set(`/${a.getAssetPrefix()}/:path+`, {
1773
+ destination: {
1774
+ domain,
1775
+ pathname: `/${a.getAssetPrefix()}/:path+`
1776
+ }
1777
+ });
1778
+ for (const group of routing) {
1779
+ if (group.flag) {
1454
1780
  continue;
1455
1781
  } else {
1456
1782
  for (const source of group.paths) {
@@ -1464,7 +1790,6 @@ function transform4(args) {
1464
1790
  }
1465
1791
  }
1466
1792
  }
1467
- } else {
1468
1793
  }
1469
1794
  return rewritesMapToArr(rewrites);
1470
1795
  };
@@ -1501,17 +1826,44 @@ function transform4(args) {
1501
1826
  }
1502
1827
 
1503
1828
  // src/next/config/transforms/server-actions.ts
1829
+ function debugRewrites2(allowedOrigins) {
1830
+ if (process.env.MFE_DEBUG === "true" && allowedOrigins) {
1831
+ const indent = " ".repeat(4);
1832
+ const header = "server actions allowed origins";
1833
+ const separator = "\u23AF".repeat(header.length);
1834
+ const maxSourceLength = Math.max(
1835
+ ...allowedOrigins.map((key) => key.length)
1836
+ );
1837
+ const table = allowedOrigins.map((origin, idx) => {
1838
+ const paddedSource = origin.padEnd(maxSourceLength);
1839
+ return `${indent} ${idx + 1}. ${paddedSource}`;
1840
+ }).join("\n");
1841
+ console.log(`${indent}${header}
1842
+ ${indent}${separator}
1843
+ ${table}
1844
+ `);
1845
+ }
1846
+ }
1504
1847
  var formatDomainForServerAction = (domain) => domain.replace(/https?:\/\//, "");
1505
1848
  function transform5(args) {
1506
- var _a;
1507
- const { next, zone, microfrontend } = args;
1508
- const zonesToAllow = [
1849
+ var _a, _b;
1850
+ const { next, app, microfrontend } = args;
1851
+ if (microfrontend instanceof MicrofrontendChildConfig) {
1852
+ console.warn(
1853
+ "server actions transform requires the full config - skipping"
1854
+ );
1855
+ return {
1856
+ next
1857
+ };
1858
+ }
1859
+ const defaultApplication = microfrontend.getDefaultApplication();
1860
+ const appsToAllow = [
1509
1861
  // this zone - this is included by default unless allowedOrigins is overridden (which we are)
1510
1862
  // so we re-add it here.
1511
- zone,
1863
+ app,
1512
1864
  // this is the default zone for the microfrontend. Allow child zones to call server actions
1513
1865
  // that are in the default zone.
1514
- microfrontend.getDefaultZone()
1866
+ defaultApplication
1515
1867
  ];
1516
1868
  const existingServerActionConfig = (_a = next.experimental) == null ? void 0 : _a.serverActions;
1517
1869
  next.experimental = {
@@ -1520,15 +1872,23 @@ function transform5(args) {
1520
1872
  ...existingServerActionConfig,
1521
1873
  allowedOrigins: Array.from(
1522
1874
  /* @__PURE__ */ new Set([
1875
+ // existing
1523
1876
  ...(existingServerActionConfig == null ? void 0 : existingServerActionConfig.allowedOrigins) ?? [],
1524
- ...zonesToAllow.flatMap((z) => [
1525
- formatDomainForServerAction(z.production.toString()),
1526
- formatDomainForServerAction(getDomainForCurrentEnvironment(z))
1877
+ // this deployments host
1878
+ ...process.env.VERCEL_URL ? [formatDomainForServerAction(process.env.VERCEL_URL)] : [],
1879
+ // default application host
1880
+ formatDomainForServerAction(defaultApplication.production.toString()),
1881
+ // environment specific microfrontend hosts
1882
+ ...appsToAllow.flatMap((a) => [
1883
+ formatDomainForServerAction(
1884
+ getDomainForCurrentEnvironment(microfrontend, a.name)
1885
+ )
1527
1886
  ])
1528
1887
  ])
1529
1888
  )
1530
1889
  }
1531
1890
  };
1891
+ debugRewrites2((_b = next.experimental.serverActions) == null ? void 0 : _b.allowedOrigins);
1532
1892
  return {
1533
1893
  next
1534
1894
  };
@@ -1537,14 +1897,6 @@ function transform5(args) {
1537
1897
  // src/next/config/transforms/webpack.ts
1538
1898
  function transform6(args) {
1539
1899
  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
1900
  const configWithWebpack = {
1549
1901
  ...next,
1550
1902
  webpack(cfg, context) {
@@ -1553,8 +1905,7 @@ function transform6(args) {
1553
1905
  if (isServer || nextRuntime === "edge") {
1554
1906
  config.plugins.push(
1555
1907
  new wpFromNext.EnvironmentPlugin({
1556
- MFE_CONFIG: JSON.stringify(microfrontend.serialize()),
1557
- MFE_PREVIEW_DOMAINS: JSON.stringify(previewDomains)
1908
+ MFE_CONFIG: JSON.stringify(microfrontend.serialize())
1558
1909
  })
1559
1910
  );
1560
1911
  }
@@ -1594,21 +1945,6 @@ var transforms = {
1594
1945
  webpack: transform6
1595
1946
  };
1596
1947
 
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
1948
  // src/next/config/env.ts
1613
1949
  function debugEnv(env) {
1614
1950
  if (process.env.MFE_DEBUG === "true") {
@@ -1627,32 +1963,18 @@ ${table}
1627
1963
  }
1628
1964
  }
1629
1965
  function setEnvironment({
1630
- zone,
1631
- config
1966
+ app,
1967
+ microfrontends
1632
1968
  }) {
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
1969
  const clientEnvs = {
1643
- NEXT_PUBLIC_MFE_CURRENT_APPLICATION: zone.name,
1970
+ NEXT_PUBLIC_MFE_CURRENT_APPLICATION: app.name,
1644
1971
  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
- }
1972
+ microfrontends.config.toClientConfig().serialize()
1973
+ )
1651
1974
  };
1652
1975
  const serverEnvs = {
1653
- MFE_CURRENT_APPLICATION: zone.name,
1654
- MFE_PREVIEW_DOMAINS: JSON.stringify(previewDomains),
1655
- MFE_CONFIG: JSON.stringify(config.getConfig())
1976
+ MFE_CURRENT_APPLICATION: app.name,
1977
+ MFE_CONFIG: JSON.stringify(microfrontends.config.getConfig())
1656
1978
  };
1657
1979
  const allEnvs = { ...clientEnvs, ...serverEnvs };
1658
1980
  for (const [key, value] of Object.entries(allEnvs)) {
@@ -1671,37 +1993,12 @@ function isProduction(opts) {
1671
1993
  }
1672
1994
  return process.env.VERCEL_ENV === "production";
1673
1995
  }
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
1996
  function getApplicationContext(opts) {
1700
1997
  if (opts == null ? void 0 : opts.appName) {
1701
1998
  return { name: opts.appName };
1702
1999
  }
1703
2000
  try {
1704
- const packageJsonString = fs3.readFileSync("./package.json", "utf-8");
2001
+ const packageJsonString = fs6.readFileSync("./package.json", "utf-8");
1705
2002
  const packageJson = JSON.parse(packageJsonString);
1706
2003
  if (!packageJson.name) {
1707
2004
  throw new MicrofrontendError(
@@ -1725,13 +2022,15 @@ function withMicrofrontends(nextConfig, opts) {
1725
2022
  if (opts == null ? void 0 : opts.debug) {
1726
2023
  process.env.MFE_DEBUG = "true";
1727
2024
  }
1728
- const { name } = getApplicationContext(opts);
1729
- const { configPath } = getConfigPath(opts);
1730
- const mfConfig = MicrofrontendConfig.fromFile({
1731
- filePath: configPath
2025
+ const { name: fromApp } = getApplicationContext(opts);
2026
+ const microfrontends = MicrofrontendsServer.infer({
2027
+ filePath: opts == null ? void 0 : opts.configPath,
2028
+ meta: {
2029
+ fromApp
2030
+ }
1732
2031
  });
1733
- const zone = mfConfig.getZone(name);
1734
- setEnvironment({ zone, config: mfConfig });
2032
+ const app = microfrontends.config.getApplication(fromApp);
2033
+ setEnvironment({ app, microfrontends });
1735
2034
  let next = { ...nextConfig };
1736
2035
  for (const [key, transform7] of typedEntries(transforms)) {
1737
2036
  if ((_a = opts == null ? void 0 : opts.skipTransforms) == null ? void 0 : _a.includes(key)) {
@@ -1740,9 +2039,9 @@ function withMicrofrontends(nextConfig, opts) {
1740
2039
  }
1741
2040
  try {
1742
2041
  const transformedConfig = transform7({
1743
- zone,
2042
+ app,
1744
2043
  next,
1745
- microfrontend: mfConfig,
2044
+ microfrontend: microfrontends.config,
1746
2045
  opts: {
1747
2046
  isProduction: isProduction(opts)
1748
2047
  }
@@ -1752,9 +2051,9 @@ function withMicrofrontends(nextConfig, opts) {
1752
2051
  console.error("Error transforming next config", e);
1753
2052
  }
1754
2053
  }
1755
- displayLocalProxyInfo(mfConfig.getLocalProxyPort());
2054
+ displayLocalProxyInfo(microfrontends.config.getLocalProxyPort());
1756
2055
  if (isVercel()) {
1757
- mfConfig.write(zone.name, { versions: ["v1", "v2"] });
2056
+ microfrontends.writeConfig();
1758
2057
  }
1759
2058
  return next;
1760
2059
  }