@vercel/microfrontends 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/bin/cli.cjs +392 -1620
  2. package/dist/config.cjs +478 -1001
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.d.ts +4 -25
  5. package/dist/config.js +474 -980
  6. package/dist/config.js.map +1 -1
  7. package/dist/{v2/microfrontends → microfrontends}/server.cjs +91 -150
  8. package/dist/microfrontends/server.cjs.map +1 -0
  9. package/dist/{v2/microfrontends → microfrontends}/server.d.ts +4 -4
  10. package/dist/{v2/microfrontends → microfrontends}/server.js +91 -150
  11. package/dist/microfrontends/server.js.map +1 -0
  12. package/dist/{v2/microfrontends.cjs → microfrontends.cjs} +49 -28
  13. package/dist/microfrontends.cjs.map +1 -0
  14. package/dist/{v2/microfrontends.d.ts → microfrontends.d.ts} +4 -4
  15. package/dist/{v2/microfrontends.js → microfrontends.js} +48 -27
  16. package/dist/microfrontends.js.map +1 -0
  17. package/dist/next/client.cjs +1 -1
  18. package/dist/next/client.cjs.map +1 -1
  19. package/dist/next/client.js +1 -1
  20. package/dist/next/client.js.map +1 -1
  21. package/dist/next/config.cjs +1344 -1024
  22. package/dist/next/config.cjs.map +1 -1
  23. package/dist/next/config.d.ts +1 -1
  24. package/dist/next/config.js +1343 -1023
  25. package/dist/next/config.js.map +1 -1
  26. package/dist/next/endpoints.cjs +77 -18
  27. package/dist/next/endpoints.cjs.map +1 -1
  28. package/dist/next/endpoints.d.ts +13 -2
  29. package/dist/next/endpoints.js +77 -18
  30. package/dist/next/endpoints.js.map +1 -1
  31. package/dist/next/middleware.cjs +765 -395
  32. package/dist/next/middleware.cjs.map +1 -1
  33. package/dist/next/middleware.d.ts +10 -5
  34. package/dist/next/middleware.js +765 -395
  35. package/dist/next/middleware.js.map +1 -1
  36. package/dist/next/testing.cjs +615 -1031
  37. package/dist/next/testing.cjs.map +1 -1
  38. package/dist/next/testing.d.ts +14 -12
  39. package/dist/next/testing.js +609 -1015
  40. package/dist/next/testing.js.map +1 -1
  41. package/dist/overrides.cjs +40 -108
  42. package/dist/overrides.cjs.map +1 -1
  43. package/dist/overrides.d.ts +24 -2
  44. package/dist/overrides.js +36 -106
  45. package/dist/overrides.js.map +1 -1
  46. package/dist/{v2/routing.cjs → routing.cjs} +3 -3
  47. package/dist/routing.cjs.map +1 -0
  48. package/dist/{v2/schema.cjs → schema.cjs} +1 -1
  49. package/dist/schema.cjs.map +1 -0
  50. package/dist/schema.d.ts +1 -0
  51. package/dist/utils/mfe-port.cjs +237 -1338
  52. package/dist/utils/mfe-port.cjs.map +1 -1
  53. package/dist/utils/mfe-port.js +230 -1331
  54. package/dist/utils/mfe-port.js.map +1 -1
  55. package/dist/validation.cjs +31 -361
  56. package/dist/validation.cjs.map +1 -1
  57. package/dist/validation.d.ts +3 -146
  58. package/dist/validation.js +30 -359
  59. package/dist/validation.js.map +1 -1
  60. package/package.json +29 -92
  61. package/schema/schema.json +174 -244
  62. package/dist/config/client.cjs +0 -54
  63. package/dist/config/client.cjs.map +0 -1
  64. package/dist/config/client.d.ts +0 -23
  65. package/dist/config/client.js +0 -28
  66. package/dist/config/client.js.map +0 -1
  67. package/dist/config/edge.cjs +0 -508
  68. package/dist/config/edge.cjs.map +0 -1
  69. package/dist/config/edge.d.ts +0 -20
  70. package/dist/config/edge.js +0 -481
  71. package/dist/config/edge.js.map +0 -1
  72. package/dist/microfrontend-config-983a5139.d.ts +0 -154
  73. package/dist/schema-2922d49e.d.ts +0 -182
  74. package/dist/v2/config.cjs +0 -709
  75. package/dist/v2/config.cjs.map +0 -1
  76. package/dist/v2/config.d.ts +0 -4
  77. package/dist/v2/config.js +0 -684
  78. package/dist/v2/config.js.map +0 -1
  79. package/dist/v2/microfrontends/server.cjs.map +0 -1
  80. package/dist/v2/microfrontends/server.js.map +0 -1
  81. package/dist/v2/microfrontends.cjs.map +0 -1
  82. package/dist/v2/microfrontends.js.map +0 -1
  83. package/dist/v2/next/client.cjs +0 -3
  84. package/dist/v2/next/client.cjs.map +0 -1
  85. package/dist/v2/next/client.d.ts +0 -45
  86. package/dist/v2/next/client.js +0 -3
  87. package/dist/v2/next/client.js.map +0 -1
  88. package/dist/v2/next/config.cjs +0 -2178
  89. package/dist/v2/next/config.cjs.map +0 -1
  90. package/dist/v2/next/config.d.ts +0 -22
  91. package/dist/v2/next/config.js +0 -2143
  92. package/dist/v2/next/config.js.map +0 -1
  93. package/dist/v2/next/endpoints.cjs +0 -141
  94. package/dist/v2/next/endpoints.cjs.map +0 -1
  95. package/dist/v2/next/endpoints.d.ts +0 -26
  96. package/dist/v2/next/endpoints.js +0 -116
  97. package/dist/v2/next/endpoints.js.map +0 -1
  98. package/dist/v2/next/middleware.cjs +0 -1099
  99. package/dist/v2/next/middleware.cjs.map +0 -1
  100. package/dist/v2/next/middleware.d.ts +0 -34
  101. package/dist/v2/next/middleware.js +0 -1071
  102. package/dist/v2/next/middleware.js.map +0 -1
  103. package/dist/v2/next/testing.cjs +0 -992
  104. package/dist/v2/next/testing.cjs.map +0 -1
  105. package/dist/v2/next/testing.d.ts +0 -55
  106. package/dist/v2/next/testing.js +0 -961
  107. package/dist/v2/next/testing.js.map +0 -1
  108. package/dist/v2/overrides.cjs +0 -75
  109. package/dist/v2/overrides.cjs.map +0 -1
  110. package/dist/v2/overrides.d.ts +0 -24
  111. package/dist/v2/overrides.js +0 -45
  112. package/dist/v2/overrides.js.map +0 -1
  113. package/dist/v2/routing.cjs.map +0 -1
  114. package/dist/v2/schema.cjs.map +0 -1
  115. package/dist/v2/schema.d.ts +0 -1
  116. package/schema/schema-v2.json +0 -266
  117. /package/dist/{v2/routing.d.ts → routing.d.ts} +0 -0
  118. /package/dist/{v2/routing.js → routing.js} +0 -0
  119. /package/dist/{v2/routing.js.map → routing.js.map} +0 -0
  120. /package/dist/{v2/schema.js → schema.js} +0 -0
  121. /package/dist/{v2/schema.js.map → schema.js.map} +0 -0
package/dist/config.cjs CHANGED
@@ -1,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,117 +15,15 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
- // src/config/index.ts
31
- var config_exports = {};
32
- __export(config_exports, {
33
- MicrofrontendConfig: () => MicrofrontendConfig,
34
- getClientConfigFromEnv: () => getClientConfigFromEnv,
35
- getConfigForClient: () => getConfigForClient,
36
- getWellKnownClientData: () => getWellKnownClientData,
37
- isCommonApplicationConfig: () => isCommonApplicationConfig,
38
- isDefaultApplicationConfig: () => isDefaultApplicationConfig
20
+ // src/config/microfrontends-config/isomorphic/index.ts
21
+ var isomorphic_exports = {};
22
+ __export(isomorphic_exports, {
23
+ MicrofrontendConfigIsomorphic: () => MicrofrontendConfigIsomorphic
39
24
  });
40
- module.exports = __toCommonJS(config_exports);
41
-
42
- // src/config/client/client-config.ts
43
- function getConfigForClient(config) {
44
- return {
45
- applications: Object.fromEntries(
46
- Object.entries(config.applications).map(([name, application]) => [
47
- name,
48
- {
49
- default: application.default,
50
- routing: application.routing
51
- }
52
- ])
53
- )
54
- };
55
- }
56
- function getClientConfigFromEnv() {
57
- const clientConfigJson = process.env.NEXT_PUBLIC_MFE_CLIENT_CONFIG;
58
- if (!clientConfigJson) {
59
- throw new Error(
60
- "Could not find client micro-frontends config in the environment"
61
- );
62
- }
63
- return JSON.parse(clientConfigJson);
64
- }
65
-
66
- // src/config/well-known/endpoints.ts
67
- async function getWellKnownClientData(flagValues = {}) {
68
- var _a;
69
- const config = getClientConfigFromEnv();
70
- for (const [applicationName, application] of Object.entries(
71
- config.applications
72
- )) {
73
- if (!application.routing) {
74
- continue;
75
- }
76
- const newRoutingMatches = [];
77
- for (const pathGroup of application.routing.matches) {
78
- if ((_a = pathGroup.options) == null ? void 0 : _a.flag) {
79
- const flagName = pathGroup.options.flag;
80
- const flagFn = flagValues[flagName];
81
- if (!flagFn) {
82
- throw new Error(
83
- `Flag "${flagName}" was specified to control routing for path group "${pathGroup.group}" in application ${applicationName} but not found in provided flag values.`
84
- );
85
- }
86
- const flagEnabled = await flagFn();
87
- if (flagEnabled) {
88
- newRoutingMatches.push(pathGroup);
89
- }
90
- } else {
91
- newRoutingMatches.push(pathGroup);
92
- }
93
- }
94
- application.routing.matches = newRoutingMatches;
95
- }
96
- return {
97
- config
98
- };
99
- }
100
-
101
- // src/config/types.ts
102
- var isCommonApplicationConfig = (app) => !app.default && typeof app.routing === "object";
103
- var isDefaultApplicationConfig = (app) => app.default && typeof app.routing === "undefined";
104
-
105
- // src/config/microfrontend-config.ts
106
- var import_node_fs2 = __toESM(require("fs"), 1);
107
-
108
- // src/config-v2/microfrontends/server/utils/get-output-file-path.ts
109
- var import_node_path = __toESM(require("path"), 1);
110
-
111
- // src/config-v2/microfrontends/server/constants.ts
112
- var MFE_CONFIG_DEFAULT_FILE_PATH = "microfrontends";
113
- var MFE_CONFIG_DEFAULT_FILE_NAME = "microfrontends.json";
114
-
115
- // src/utils/is-vercel.ts
116
- function isVercel() {
117
- return process.env.VERCEL === "1";
118
- }
119
-
120
- // src/config-v2/microfrontends/server/utils/get-output-file-path.ts
121
- function getOutputFilePath() {
122
- if (isVercel()) {
123
- return import_node_path.default.join(
124
- ".vercel",
125
- MFE_CONFIG_DEFAULT_FILE_PATH,
126
- MFE_CONFIG_DEFAULT_FILE_NAME
127
- );
128
- }
129
- return import_node_path.default.join(MFE_CONFIG_DEFAULT_FILE_PATH, MFE_CONFIG_DEFAULT_FILE_NAME);
130
- }
25
+ module.exports = __toCommonJS(isomorphic_exports);
26
+ var import_jsonc_parser = require("jsonc-parser");
131
27
 
132
28
  // src/config/errors.ts
133
29
  var MicrofrontendError = class extends Error {
@@ -221,812 +117,164 @@ var MicrofrontendError = class extends Error {
221
117
  }
222
118
  };
223
119
 
224
- // src/routing/url.ts
225
- function buildUrlSafeString(givenOpts = {}) {
226
- const options = {
227
- joinString: "-",
228
- lowercaseOnly: true,
229
- maxLen: 100,
230
- regexRemovePattern: /(?:(?!(?:[a-z0-9])).)/gi,
231
- trimWhitespace: true,
232
- ...givenOpts
233
- };
234
- return {
235
- generate: (...args) => {
236
- const reJoinString = new RegExp(`${options.joinString}+`, "g");
237
- let tag;
238
- if (args.length === 0) {
239
- throw new Error("generate method must be passed at least one argument");
240
- }
241
- for (let i = 0; i < args.length; i++) {
242
- const arg = args[i];
243
- if (typeof arg !== "string")
244
- throw new Error("all supplied arguments must be Strings");
245
- if (options.trimWhitespace) {
246
- args[i] = arg.trim();
247
- }
248
- }
249
- tag = args.join(options.joinString);
250
- tag = tag.replace(/\s/g, options.joinString);
251
- if (options.lowercaseOnly)
252
- tag = tag.toLowerCase();
253
- tag = tag.replace(options.regexRemovePattern, (match) => {
254
- if (match === options.joinString)
255
- return match;
256
- return "";
257
- });
258
- if (tag.length > options.maxLen)
259
- tag = tag.substring(0, options.maxLen);
260
- tag = tag.replace(reJoinString, options.joinString);
261
- return tag;
262
- }
263
- };
264
- }
265
- var urlSafeString = buildUrlSafeString().generate;
266
- function makeUrlSafe(name) {
267
- return urlSafeString(name.replace(/\//g, "-")).replace(/^-*/g, "").replace(/-*$/g, "");
268
- }
269
-
270
- // src/config/overrides/config.ts
271
- var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
272
- var _Overrides = class {
273
- constructor(config) {
274
- this.config = config;
275
- }
276
- static getAppEnvOverrideCookieName(zone) {
277
- return `${_Overrides.overrideEnvCookiePrefix}${zone}`;
278
- }
279
- static isOverrideCookie(cookie) {
280
- var _a;
281
- return Boolean((_a = cookie.name) == null ? void 0 : _a.startsWith(OVERRIDES_COOKIE_PREFIX));
282
- }
283
- static getOverrideFromCookie(cookie) {
284
- if (!_Overrides.isOverrideCookie(cookie) || !cookie.value)
285
- return;
286
- return {
287
- zone: cookie.name.replace(_Overrides.overrideEnvCookiePrefix, ""),
288
- host: cookie.value
289
- };
290
- }
291
- static parseOverrides(cookies) {
292
- const overridesConfig = { applications: {} };
293
- cookies.forEach((cookie) => {
294
- const override = _Overrides.getOverrideFromCookie(cookie);
295
- if (!override)
296
- return;
297
- overridesConfig.applications[override.zone] = {
298
- environment: { host: override.host }
299
- };
120
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
121
+ function getConfigStringFromEnv() {
122
+ const config = process.env.MFE_CONFIG;
123
+ if (!config) {
124
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
125
+ type: "config",
126
+ subtype: "not_found_in_env"
300
127
  });
301
- return overridesConfig;
302
- }
303
- static validOverrideDomainsForZone(microfrontendConfig, zone) {
304
- var _a, _b, _c, _d, _e;
305
- const projectName = (_a = microfrontendConfig.getZone(zone).vercel) == null ? void 0 : _a.projectName;
306
- if (!projectName) {
307
- return [microfrontendConfig.getZone(zone).production.host];
308
- }
309
- const parsedProjectName = makeUrlSafe(projectName);
310
- const previewDeploymentSuffix = (_c = (_b = microfrontendConfig.options) == null ? void 0 : _b.vercel) == null ? void 0 : _c.previewDeploymentSuffix;
311
- const teamSlug = (_e = (_d = microfrontendConfig.options) == null ? void 0 : _d.vercel) == null ? void 0 : _e.teamSlug;
312
- if (!teamSlug && !previewDeploymentSuffix) {
313
- return [microfrontendConfig.getZone(zone).production.host];
314
- }
315
- const suffix = previewDeploymentSuffix ? `.${previewDeploymentSuffix}` : `-${teamSlug}.vercel.app`;
316
- return [
317
- `${parsedProjectName}-git-([a-zA-Z0-9-]+)${suffix}`,
318
- microfrontendConfig.getZone(zone).production.host
319
- ];
320
- }
321
- static validateOverrideDomain(microfrontendConfig, zone, domain) {
322
- return new RegExp(
323
- `^${_Overrides.validOverrideDomainsForZone(microfrontendConfig, zone).join(
324
- "|"
325
- )}$`
326
- ).test(domain);
327
128
  }
328
- serialize() {
329
- return this.config;
330
- }
331
- };
332
- var Overrides = _Overrides;
333
- Overrides.overrideEnvCookiePrefix = `${OVERRIDES_COOKIE_PREFIX}:env:`;
129
+ return config;
130
+ }
334
131
 
335
- // src/config/common/host.ts
336
- var Host = class {
337
- constructor({ protocol, host, port }) {
338
- this.protocol = protocol || "https";
339
- this.host = host;
340
- this.port = Host.getPort({ port, protocol: this.protocol });
341
- this.serialized = {
342
- protocol,
343
- host,
344
- ...port ? { port } : void 0
345
- };
346
- }
347
- isLocal() {
348
- return this.host === "localhost" || this.host === "127.0.0.1";
349
- }
350
- static getPort({
351
- protocol,
352
- port
353
- }) {
354
- if (!port) {
355
- if (protocol === "http") {
356
- return 80;
357
- }
358
- return 443;
359
- }
360
- return port;
361
- }
362
- isDefaultPort() {
363
- return this.port === Host.getPort({ protocol: this.protocol });
364
- }
365
- toString(opts = {}) {
366
- const url = this.toUrl(opts);
367
- return url.toString().replace(/\/$/, "");
368
- }
369
- toUrl(opts = {}) {
370
- const { includeDefaultPort } = opts;
371
- const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
372
- return new URL(url);
373
- }
374
- serialize() {
375
- return this.serialized;
376
- }
377
- };
132
+ // src/config/schema/utils/is-main-config.ts
133
+ function isMainConfig(c) {
134
+ return !("partOf" in c);
135
+ }
378
136
 
379
- // src/config/common/application.ts
380
- var Application = class {
381
- constructor(name, {
382
- app,
383
- overrides
384
- }) {
385
- Application.validate(name, app);
386
- this.name = name;
387
- this.default = app.default;
388
- this.routing = app.routing;
389
- this.development = {
390
- local: new Host(app.development.local),
391
- fallback: app.development.fallback ? new Host(app.development.fallback) : void 0
392
- };
393
- this.production = new Host(app.production);
394
- this.vercel = app.vercel;
395
- this.overrides = (overrides == null ? void 0 : overrides.environment) ? {
396
- environment: new Host(overrides.environment)
397
- } : void 0;
398
- }
399
- isDefault() {
400
- return this.default;
401
- }
402
- static validate(name, app) {
403
- var _a, _b, _c, _d, _e;
404
- 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("/"))) {
405
- throw new MicrofrontendError(
406
- `Invalid assetPrefix for application "${name}". Must not start or end with a slash.`,
407
- { type: "zone", subtype: "invalid_asset_prefix" }
408
- );
409
- }
410
- for (const group of ((_e = app.routing) == null ? void 0 : _e.matches) ?? []) {
411
- for (const p of group.paths) {
412
- if (p === "/") {
413
- continue;
414
- }
415
- if (p.endsWith("/")) {
416
- throw new MicrofrontendError(
417
- `Invalid path for application "${name}". ${p} must not end with a slash.`,
418
- { type: "zone", subtype: "invalid_path" }
419
- );
420
- }
421
- if (!p.startsWith("/")) {
422
- throw new MicrofrontendError(
423
- `Invalid path for application "${name}". ${p} must start with a slash.`,
424
- { type: "zone", subtype: "invalid_path" }
425
- );
137
+ // src/config/schema/utils/is-default-app.ts
138
+ function isDefaultApp(a) {
139
+ return !("routing" in a);
140
+ }
141
+
142
+ // src/config/microfrontends-config/client/index.ts
143
+ var import_path_to_regexp = require("path-to-regexp");
144
+ var MicrofrontendConfigClient = class {
145
+ constructor(config, opts) {
146
+ this.pathCache = {};
147
+ this.serialized = config;
148
+ if (opts == null ? void 0 : opts.removeFlaggedPaths) {
149
+ for (const app of Object.values(config.applications)) {
150
+ if (app.routing) {
151
+ app.routing = app.routing.filter((match) => !match.flag);
426
152
  }
427
153
  }
428
154
  }
155
+ this.applications = config.applications;
429
156
  }
430
- serialize() {
431
- var _a, _b;
432
- if (this.routing === void 0 || this.default) {
433
- return {
434
- default: true,
435
- development: {
436
- local: this.development.local.serialize(),
437
- fallback: (_a = this.development.fallback) == null ? void 0 : _a.serialize()
438
- },
439
- production: this.production.serialize(),
440
- vercel: this.vercel
441
- };
442
- }
443
- return {
444
- default: false,
445
- routing: this.routing,
446
- development: {
447
- local: this.development.local.serialize(),
448
- fallback: (_b = this.development.fallback) == null ? void 0 : _b.serialize()
449
- },
450
- production: this.production.serialize(),
451
- vercel: this.vercel
452
- };
453
- }
454
- };
455
-
456
- // src/config/common/microfrontend-config.ts
457
- var SUPPORTED_VERSIONS = ["1"];
458
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
459
- var MicrofrontendConfigCommon = class {
460
- constructor({
461
- config,
462
- overrides
463
- }) {
464
- this.zones = {};
465
- var _a, _b, _c;
466
- if (!SUPPORTED_VERSIONS.includes(config.version)) {
467
- throw new MicrofrontendError(
468
- `Unsupported version: ${config.version}. Supported versions are: ${SUPPORTED_VERSIONS.join(
469
- ", "
470
- )}`,
471
- { type: "config", subtype: "unsupported_version" }
472
- );
473
- }
474
- const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
475
- this.overrides = overrides && !disableOverrides ? new Overrides(overrides) : void 0;
476
- for (const [zoneName, zoneConfig] of Object.entries(config.applications)) {
477
- this.zones[zoneName] = new Application(zoneName, {
478
- app: zoneConfig,
479
- overrides: !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.config.applications[zoneName] : void 0
480
- });
481
- }
482
- this.config = config;
483
- this.name = config.name;
484
- this.version = config.version;
485
- this.options = config.options;
486
- this.$schema = config.$schema;
487
- }
488
- isOverridesDisabled() {
489
- var _a, _b;
490
- return ((_b = (_a = this.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
491
- }
492
- static getConfigFromEnv() {
493
- const config = process.env.MFE_CONFIG;
157
+ /**
158
+ * Create a new `MicrofrontendConfigClient` from a JSON string.
159
+ * Config must be passed in to remain framework agnostic
160
+ */
161
+ static fromEnv(config, opts) {
494
162
  if (!config) {
495
- throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
496
- type: "config",
497
- subtype: "not_found_in_env"
498
- });
499
- }
500
- return config;
501
- }
502
- static fromEnv(_) {
503
- throw new Error("Not implemented");
504
- }
505
- getConfig() {
506
- return this.config;
507
- }
508
- getAllApplications() {
509
- return Object.values(this.zones);
510
- }
511
- getZone(name) {
512
- const zone = this.zones[name];
513
- if (!zone) {
514
- throw new MicrofrontendError(
515
- `Could not find microfrontends configuration for application "${name}"`,
516
- {
517
- type: "zone",
518
- subtype: "not_found"
519
- }
520
- );
163
+ throw new Error("No microfrontends configuration found");
521
164
  }
522
- return zone;
523
- }
524
- getApplicationByProjectId(projectId) {
525
- return Object.values(this.zones).find(
526
- (zone) => {
527
- var _a;
528
- return ((_a = zone.vercel) == null ? void 0 : _a.projectId) === projectId;
529
- }
165
+ return new MicrofrontendConfigClient(
166
+ JSON.parse(config),
167
+ opts
530
168
  );
531
169
  }
532
- getDefaultZone() {
533
- const zone = Object.values(this.zones).find((z) => z.default);
534
- if (!zone) {
535
- throw new MicrofrontendError(
536
- `Could not find default zone in microfrontends configuration`,
537
- {
538
- type: "zone",
539
- subtype: "not_found"
170
+ isEqual(other) {
171
+ return JSON.stringify(this.applications) === JSON.stringify(other.applications);
172
+ }
173
+ getApplicationNameForPath(path) {
174
+ if (!path.startsWith("/")) {
175
+ throw new Error(`Path must start with a /`);
176
+ }
177
+ if (this.pathCache[path]) {
178
+ return this.pathCache[path];
179
+ }
180
+ const pathname = new URL(path, "https://example.com").pathname;
181
+ for (const [name, application] of Object.entries(this.applications)) {
182
+ if (application.routing) {
183
+ for (const group of application.routing) {
184
+ for (const childPath of group.paths) {
185
+ const regexp = (0, import_path_to_regexp.pathToRegexp)(childPath);
186
+ if (regexp.test(pathname)) {
187
+ this.pathCache[path] = name;
188
+ return name;
189
+ }
190
+ }
540
191
  }
541
- );
192
+ }
542
193
  }
543
- return zone;
544
- }
545
- /**
546
- * Returns the configured port for the local proxy
547
- */
548
- getLocalProxyPort() {
549
- var _a, _b;
550
- return ((_b = (_a = this.config.options) == null ? void 0 : _a.localProxy) == null ? void 0 : _b.port) ?? DEFAULT_LOCAL_PROXY_PORT;
551
- }
552
- /**
553
- * Serializes the class back to the Schema type.
554
- *
555
- * NOTE: This is used when writing the config to disk and must always match the input Schema
556
- */
557
- toSchemaJson() {
558
- const applications = {};
559
- for (const [name, zone] of Object.entries(this.zones)) {
560
- applications[name] = zone.serialize();
194
+ const defaultApplication = Object.entries(this.applications).find(
195
+ ([, application]) => application.default
196
+ );
197
+ if (!defaultApplication) {
198
+ return null;
561
199
  }
562
- return {
563
- $schema: this.$schema,
564
- name: this.name,
565
- version: this.version,
566
- options: this.options,
567
- applications
568
- };
200
+ this.pathCache[path] = defaultApplication[0];
201
+ return defaultApplication[0];
569
202
  }
570
203
  serialize() {
571
- var _a;
572
- const applications = {};
573
- for (const [name, zone] of Object.entries(this.zones)) {
574
- applications[name] = zone.serialize();
575
- }
576
- return {
577
- config: {
578
- name: this.name,
579
- version: this.version,
580
- applications,
581
- options: this.options,
582
- $schema: this.$schema
583
- },
584
- overrides: (_a = this.overrides) == null ? void 0 : _a.serialize()
585
- };
586
- }
587
- write(_) {
588
- throw new MicrofrontendError(
589
- `Writing to file to disk requires using an instance of "MicrofrontendConfig".`,
590
- { type: "config", subtype: "unsupported_operation" }
591
- );
204
+ return this.serialized;
592
205
  }
593
206
  };
594
207
 
595
- // src/config/utils/get-output-file-path.ts
596
- var import_node_path2 = __toESM(require("path"), 1);
597
-
598
- // src/config/constants.ts
599
- var MFE_CONFIG_DEFAULT_FILE_PATH2 = "micro-frontends";
600
- var MFE_CONFIG_DEFAULT_FILE_NAME2 = "micro-frontends.config.json";
208
+ // src/config/overrides/constants.ts
209
+ var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
210
+ var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
601
211
 
602
- // src/config/utils/get-output-file-path.ts
603
- function getOutputFilePath2() {
604
- if (isVercel()) {
605
- return import_node_path2.default.join(
606
- ".vercel",
607
- MFE_CONFIG_DEFAULT_FILE_PATH2,
608
- MFE_CONFIG_DEFAULT_FILE_NAME2
609
- );
610
- }
611
- return import_node_path2.default.join(MFE_CONFIG_DEFAULT_FILE_PATH2, MFE_CONFIG_DEFAULT_FILE_NAME2);
212
+ // src/config/overrides/is-override-cookie.ts
213
+ function isOverrideCookie(cookie) {
214
+ var _a;
215
+ return Boolean((_a = cookie.name) == null ? void 0 : _a.startsWith(OVERRIDES_COOKIE_PREFIX));
612
216
  }
613
217
 
614
- // src/config/validation.ts
615
- var import_jsonc_parser = require("jsonc-parser");
616
- var import_path_to_regexp = require("path-to-regexp");
617
- var import_ajv = require("ajv");
618
-
619
- // schema/schema.json
620
- var schema_default = {
621
- $schema: "http://json-schema.org/draft-07/schema#",
622
- $ref: "#/definitions/Config",
623
- definitions: {
624
- Config: {
625
- type: "object",
626
- properties: {
627
- version: {
628
- type: "string"
629
- },
630
- $schema: {
631
- type: "string"
632
- },
633
- name: {
634
- type: "string",
635
- description: 'Name for the micro-frontend site (eg. "vercel.com", "vercel-site" etc.).'
636
- },
637
- applications: {
638
- $ref: "#/definitions/ApplicationConfigsById"
639
- },
640
- options: {
641
- $ref: "#/definitions/Options",
642
- description: "Optional configuration for the entire micro-frontends setup."
643
- }
644
- },
645
- required: ["version", "applications"],
646
- description: "Configuration for micro-frontend applications\n\nTODO: Add proxy configuration"
647
- },
648
- ApplicationConfigsById: {
649
- type: "object",
650
- additionalProperties: {
651
- $ref: "#/definitions/ApplicationConfig"
652
- }
653
- },
654
- ApplicationConfig: {
655
- anyOf: [
656
- {
657
- $ref: "#/definitions/DefaultApplicationConfig"
658
- },
659
- {
660
- $ref: "#/definitions/CommonApplicationConfig"
661
- }
662
- ],
663
- description: "A Micro-Frontend Deployment Target"
664
- },
665
- DefaultApplicationConfig: {
666
- type: "object",
667
- properties: {
668
- default: {
669
- type: "boolean",
670
- const: true,
671
- description: "The default application is used no other application is matched via the routing config"
672
- },
673
- routing: {
674
- $ref: "#/definitions/Routing"
675
- },
676
- development: {
677
- type: "object",
678
- properties: {
679
- local: {
680
- $ref: "#/definitions/HostConfig"
681
- },
682
- fallback: {
683
- $ref: "#/definitions/HostConfig",
684
- 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."
685
- },
686
- task: {
687
- type: "string",
688
- description: "Optional task to run when starting the development server. Should reference a script in the package.json of the application."
689
- }
690
- },
691
- required: ["local"]
692
- },
693
- production: {
694
- $ref: "#/definitions/HostConfig"
695
- },
696
- metadata: {
697
- type: "object",
698
- additionalProperties: {
699
- type: "string"
700
- }
701
- },
702
- federation: {
703
- type: "object",
704
- properties: {
705
- exposes: {
706
- type: "array",
707
- items: {
708
- type: "object",
709
- properties: {
710
- name: {
711
- type: "string",
712
- description: "The name of the module - should be used when importing the module from another application"
713
- },
714
- path: {
715
- type: "string",
716
- description: "Relative path to the module within its `application`"
717
- }
718
- },
719
- required: ["name", "path"]
720
- },
721
- description: "Modules that are exposed by this application"
722
- },
723
- uses: {
724
- type: "array",
725
- items: {
726
- type: "string"
727
- },
728
- description: "Modules that are used by this application. Only the name of the module is required."
729
- }
730
- }
731
- },
732
- vercel: {
733
- $ref: "#/definitions/Vercel"
734
- }
735
- },
736
- required: ["default", "development", "production"]
737
- },
738
- Routing: {
739
- type: "object",
740
- properties: {
741
- assetPrefix: {
742
- type: "string",
743
- description: "[assetPrefix] for the application"
744
- },
745
- matches: {
746
- type: "array",
747
- items: {
748
- $ref: "#/definitions/PathGroup"
749
- },
750
- description: "Path expressions that are routed to this application."
751
- }
752
- },
753
- required: ["matches"]
754
- },
755
- PathGroup: {
756
- type: "object",
757
- properties: {
758
- group: {
759
- type: "string",
760
- description: "Optional group name for the paths"
761
- },
762
- options: {
763
- type: "object",
764
- properties: {
765
- flag: {
766
- type: "string",
767
- description: "flag name that can be used to enable/disable all paths in the group"
768
- }
769
- }
770
- },
771
- paths: {
772
- type: "array",
773
- items: {
774
- type: "string"
775
- }
776
- }
777
- },
778
- required: ["paths"]
779
- },
780
- HostConfig: {
781
- type: "object",
782
- properties: {
783
- protocol: {
784
- type: "string",
785
- enum: ["http", "https"],
786
- 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"'
787
- },
788
- host: {
789
- type: "string",
790
- 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`)."
791
- },
792
- port: {
793
- type: "number",
794
- 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"
795
- }
796
- },
797
- required: ["host"]
798
- },
799
- Vercel: {
800
- type: "object",
801
- properties: {
802
- projectId: {
803
- type: "string",
804
- description: "Vercel project ID"
805
- },
806
- projectName: {
807
- type: "string",
808
- description: "Vercel project name (temporary until we can use project ID)"
809
- },
810
- defaultRoute: {
811
- type: "string",
812
- description: "The default route for the application. Used to render screenshots, favicons, and provide direct zone links"
813
- },
814
- routeSpeedInsightsToDefaultZone: {
815
- type: "boolean",
816
- description: "Whether to route Speed Insights to the default zone or each individual microfrontend."
817
- }
818
- },
819
- required: ["projectId"]
820
- },
821
- CommonApplicationConfig: {
822
- type: "object",
823
- properties: {
824
- default: {
825
- type: "boolean",
826
- const: false,
827
- description: "The default application is used no other application is matched via the routing config"
828
- },
829
- routing: {
830
- $ref: "#/definitions/Routing"
831
- },
832
- development: {
833
- type: "object",
834
- properties: {
835
- local: {
836
- $ref: "#/definitions/HostConfig"
837
- },
838
- fallback: {
839
- $ref: "#/definitions/HostConfig",
840
- 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."
841
- },
842
- task: {
843
- type: "string",
844
- description: "Optional task to run when starting the development server. Should reference a script in the package.json of the application."
845
- }
846
- },
847
- required: ["local"]
848
- },
849
- production: {
850
- $ref: "#/definitions/HostConfig"
851
- },
852
- metadata: {
853
- type: "object",
854
- additionalProperties: {
855
- type: "string"
856
- }
857
- },
858
- federation: {
859
- type: "object",
860
- properties: {
861
- exposes: {
862
- type: "array",
863
- items: {
864
- type: "object",
865
- properties: {
866
- name: {
867
- type: "string",
868
- description: "The name of the module - should be used when importing the module from another application"
869
- },
870
- path: {
871
- type: "string",
872
- description: "Relative path to the module within its `application`"
873
- }
874
- },
875
- required: ["name", "path"]
876
- },
877
- description: "Modules that are exposed by this application"
878
- },
879
- uses: {
880
- type: "array",
881
- items: {
882
- type: "string"
883
- },
884
- description: "Modules that are used by this application. Only the name of the module is required."
885
- }
886
- }
887
- },
888
- vercel: {
889
- $ref: "#/definitions/Vercel"
890
- }
891
- },
892
- required: ["default", "development", "production", "routing"]
893
- },
894
- Options: {
895
- type: "object",
896
- properties: {
897
- vercel: {
898
- $ref: "#/definitions/VercelOptions",
899
- description: "Micro-Frontends wide options for Vercel."
900
- },
901
- localProxy: {
902
- $ref: "#/definitions/LocalProxyOptions",
903
- description: "Options for local proxy."
904
- }
905
- }
906
- },
907
- VercelOptions: {
908
- type: "object",
909
- properties: {
910
- previewDeploymentSuffix: {
911
- type: "string",
912
- 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`."
913
- },
914
- teamSlug: {
915
- type: "string",
916
- description: "Team slug for the Vercel team"
917
- },
918
- disableOverrides: {
919
- type: "boolean",
920
- 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."
921
- }
922
- }
923
- },
924
- LocalProxyOptions: {
925
- type: "object",
926
- properties: {
927
- port: {
928
- type: "number",
929
- description: "The port number used by the local proxy server.\n\nThe default is `3024`."
930
- }
931
- }
932
- }
933
- }
934
- };
218
+ // src/config/overrides/get-override-from-cookie.ts
219
+ function getOverrideFromCookie(cookie) {
220
+ if (!isOverrideCookie(cookie) || !cookie.value)
221
+ return;
222
+ return {
223
+ application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
224
+ host: cookie.value
225
+ };
226
+ }
935
227
 
936
- // src/config/utils/load-schema.ts
937
- var SCHEMA = schema_default;
228
+ // src/config/overrides/parse-overrides.ts
229
+ function parseOverrides(cookies) {
230
+ const overridesConfig = { applications: {} };
231
+ cookies.forEach((cookie) => {
232
+ const override = getOverrideFromCookie(cookie);
233
+ if (!override)
234
+ return;
235
+ overridesConfig.applications[override.application] = {
236
+ environment: { host: override.host }
237
+ };
238
+ });
239
+ return overridesConfig;
240
+ }
938
241
 
939
- // src/config/validation.ts
940
- var validateSchema = (configString) => {
941
- const parsedConfig = (0, import_jsonc_parser.parse)(configString);
942
- const ajv = new import_ajv.Ajv();
943
- const validate = ajv.compile(SCHEMA);
944
- const isValid = validate(parsedConfig);
945
- if (!isValid) {
242
+ // src/config/microfrontends-config/isomorphic/validation.ts
243
+ var import_path_to_regexp2 = require("path-to-regexp");
244
+ var SUPPORTED_VERSIONS = ["2"];
245
+ var validateConfigVersion = (version) => {
246
+ if (!SUPPORTED_VERSIONS.includes(version)) {
946
247
  throw new MicrofrontendError(
947
- `Invalid config: ${ajv.errorsText(validate.errors)}`,
948
- { type: "config", subtype: "does_not_match_schema" }
949
- );
950
- }
951
- return parsedConfig;
952
- };
953
- var SUPPORTED_VERSIONS2 = ["1"];
954
- var validateVersion = (version) => {
955
- if (!SUPPORTED_VERSIONS2.includes(version)) {
956
- throw new MicrofrontendError(
957
- `Unsupported version: ${version}. Supported versions are: ${SUPPORTED_VERSIONS2.join(
248
+ `Unsupported version: ${version}. Supported versions are: ${SUPPORTED_VERSIONS.join(
958
249
  ", "
959
250
  )}`,
960
251
  { type: "config", subtype: "unsupported_version" }
961
252
  );
962
253
  }
963
254
  };
964
- function validateMainPath(applicationConfigsById) {
965
- for (const [id, app] of Object.entries(applicationConfigsById)) {
966
- const { defaultRoute } = app.vercel ?? {};
967
- if (!defaultRoute) {
968
- continue;
969
- }
970
- if (isDefaultApplicationConfig(app)) {
971
- const pathsWithApp = [];
972
- for (const [otherId, otherApp] of Object.entries(
973
- applicationConfigsById
974
- )) {
975
- if (isDefaultApplicationConfig(otherApp)) {
976
- continue;
977
- }
978
- pathsWithApp.push({
979
- id: otherId,
980
- paths: otherApp.routing.matches.flatMap((match) => match.paths)
981
- });
982
- }
983
- for (const { id: otherId, paths } of pathsWithApp) {
984
- const isValid = paths.every((path3) => {
985
- const matcher = (0, import_path_to_regexp.pathToRegexp)(path3);
986
- return !matcher.test(defaultRoute);
987
- });
988
- if (!isValid) {
989
- throw new MicrofrontendError(
990
- `default route "${defaultRoute}" cannot be used for "${id}" because it is matched by "${otherId}"`,
991
- { type: "config", subtype: "invalid_main_path" }
992
- );
993
- }
994
- }
995
- } else {
996
- const allPaths = app.routing.matches.flatMap((match) => match.paths);
997
- const isValid = allPaths.some((path3) => {
998
- const matcher = (0, import_path_to_regexp.pathToRegexp)(path3);
999
- return matcher.test(defaultRoute);
1000
- });
1001
- if (!isValid) {
1002
- throw new MicrofrontendError(
1003
- `default route "${defaultRoute}" is not included by the routing config for application "${id}"`,
1004
- { type: "config", subtype: "invalid_main_path" }
1005
- );
1006
- }
1007
- }
255
+ var validateConfigPaths = (applicationConfigsById) => {
256
+ if (!applicationConfigsById) {
257
+ return;
1008
258
  }
1009
- }
1010
- var validatePaths = (applicationConfigsById) => {
1011
259
  const pathsByApplicationId = /* @__PURE__ */ new Map();
1012
260
  const errors = [];
1013
261
  for (const [id, app] of Object.entries(applicationConfigsById)) {
1014
- if (isDefaultApplicationConfig(app)) {
262
+ if (isDefaultApp(app)) {
1015
263
  continue;
1016
264
  }
1017
- for (const pathMatch of app.routing.matches) {
1018
- for (const path3 of pathMatch.paths) {
1019
- const maybeError = validatePathExpression(path3);
265
+ for (const pathMatch of app.routing) {
266
+ for (const path of pathMatch.paths) {
267
+ const maybeError = validatePathExpression(path);
1020
268
  if (maybeError) {
1021
269
  errors.push(maybeError);
1022
270
  }
1023
- const existing = pathsByApplicationId.get(path3);
271
+ const existing = pathsByApplicationId.get(path);
1024
272
  if (existing) {
1025
273
  existing.applications.push(id);
1026
274
  } else {
1027
- pathsByApplicationId.set(path3, {
275
+ pathsByApplicationId.set(path, {
1028
276
  applications: [id],
1029
- matcher: (0, import_path_to_regexp.pathToRegexp)(path3),
277
+ matcher: (0, import_path_to_regexp2.pathToRegexp)(path),
1030
278
  applicationId: id
1031
279
  });
1032
280
  }
@@ -1034,10 +282,10 @@ var validatePaths = (applicationConfigsById) => {
1034
282
  }
1035
283
  }
1036
284
  const entries = Array.from(pathsByApplicationId.entries());
1037
- entries.forEach(([path3, { applications: ids, matcher, applicationId }]) => {
285
+ entries.forEach(([path, { applications: ids, matcher, applicationId }]) => {
1038
286
  if (ids.length > 1) {
1039
287
  errors.push(
1040
- `Duplicate path "${path3}" for applications "${ids.join(", ")}"`
288
+ `Duplicate path "${path}" for applications "${ids.join(", ")}"`
1041
289
  );
1042
290
  }
1043
291
  entries.forEach(
@@ -1045,14 +293,14 @@ var validatePaths = (applicationConfigsById) => {
1045
293
  matchPath,
1046
294
  { applications: matchIds, applicationId: matchApplicationId }
1047
295
  ]) => {
1048
- if (path3 === matchPath) {
296
+ if (path === matchPath) {
1049
297
  return;
1050
298
  }
1051
299
  if (applicationId === matchApplicationId) {
1052
300
  return;
1053
301
  }
1054
302
  if (matcher.test(matchPath)) {
1055
- const source = `"${path3}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
303
+ const source = `"${path}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
1056
304
  const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
1057
305
  errors.push(
1058
306
  `Overlapping path detected between ${source} and ${destination}`
@@ -1069,185 +317,414 @@ var validatePaths = (applicationConfigsById) => {
1069
317
  }
1070
318
  };
1071
319
  var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
1072
- function validatePathExpression(path3) {
1073
- const tokens = (0, import_path_to_regexp.parse)(path3);
320
+ function validatePathExpression(path) {
321
+ const tokens = (0, import_path_to_regexp2.parse)(path);
1074
322
  for (let i = 0; i < tokens.length; i++) {
1075
323
  const token = tokens[i];
1076
324
  if (token === void 0) {
1077
- return `token ${i} in ${path3} is undefined, this shouldn't happen`;
325
+ return `token ${i} in ${path} is undefined, this shouldn't happen`;
1078
326
  }
1079
327
  if (typeof token !== "string") {
1080
328
  if (token.pattern !== PATH_DEFAULT_PATTERN) {
1081
- return `Path ${path3} cannot use a regular expression wildcard`;
329
+ return `Path ${path} cannot use a regular expression wildcard`;
1082
330
  }
1083
331
  if (token.prefix !== "/") {
1084
- return `Wildcard :${token.name} must be immediately after a / in ${path3}`;
332
+ return `Wildcard :${token.name} must be immediately after a / in ${path}`;
1085
333
  }
1086
334
  if (token.suffix) {
1087
335
  return `Wildcard suffix on :${token.name} is not allowed. Suffixes are not supported`;
1088
336
  }
1089
337
  if (token.modifier && i !== tokens.length - 1) {
1090
- return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path3}. Modifiers are only allowed in the last path component`;
338
+ return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path}. Modifiers are only allowed in the last path component`;
1091
339
  }
1092
340
  }
1093
341
  }
1094
342
  return void 0;
1095
343
  }
1096
- var validateDefaults = (applicationConfigsById) => {
1097
- const defaultApplicationIds = Object.entries(applicationConfigsById).reduce((acc, [id, app]) => app.default ? [...acc, id] : acc, []);
1098
- if (defaultApplicationIds.length === 0) {
344
+ var validateAppPaths = (name, app) => {
345
+ for (const group of app.routing) {
346
+ for (const p of group.paths) {
347
+ if (p === "/") {
348
+ continue;
349
+ }
350
+ if (p.endsWith("/")) {
351
+ throw new MicrofrontendError(
352
+ `Invalid path for application "${name}". ${p} must not end with a slash.`,
353
+ { type: "application", subtype: "invalid_path" }
354
+ );
355
+ }
356
+ if (!p.startsWith("/")) {
357
+ throw new MicrofrontendError(
358
+ `Invalid path for application "${name}". ${p} must start with a slash.`,
359
+ { type: "application", subtype: "invalid_path" }
360
+ );
361
+ }
362
+ }
363
+ }
364
+ };
365
+ var validateConfigDefaultApplication = (applicationConfigsById) => {
366
+ if (!applicationConfigsById) {
367
+ return;
368
+ }
369
+ const applicationsWithRouting = Object.entries(applicationConfigsById).filter(
370
+ ([, app]) => !isDefaultApp(app)
371
+ );
372
+ const applicationsWithRoutingNames = applicationsWithRouting.map(
373
+ ([key]) => key
374
+ );
375
+ const numApplications = Object.keys(applicationConfigsById).length;
376
+ const numApplicationsWithRouting = applicationsWithRoutingNames.length;
377
+ const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
378
+ if (numApplicationsWithoutRouting === 0) {
1099
379
  throw new MicrofrontendError(
1100
- `No default application found. At least one application must be marked as default.`,
380
+ `No default application found. At least one application needs to be the default by omitting routing.`,
1101
381
  { type: "config", subtype: "no_default_application" }
1102
382
  );
1103
383
  }
1104
- if (defaultApplicationIds.length > 1) {
384
+ if (numApplicationsWithoutRouting > 1) {
1105
385
  throw new MicrofrontendError(
1106
- `Only one default application is allowed. Found ${defaultApplicationIds.join(", ")}.`,
386
+ `Only one application can omit "routing". Found ${applicationsWithRoutingNames.length - Object.keys(applicationConfigsById).length > 1}.`,
1107
387
  { type: "config", subtype: "multiple_default_applications" }
1108
388
  );
1109
389
  }
1110
390
  };
1111
- var validateOptions = (options) => {
1112
- var _a;
1113
- if ((_a = options == null ? void 0 : options.vercel) == null ? void 0 : _a.previewDeploymentSuffix) {
1114
- if (!/^[a-zA-Z]{2,}\.[a-zA-Z]{2,}$/.test(
1115
- options.vercel.previewDeploymentSuffix
1116
- )) {
1117
- throw new MicrofrontendError(
1118
- `Invalid preview deployment suffix: ${options.vercel.previewDeploymentSuffix}. Should have be formatted like "vercel.app".`,
1119
- { type: "config", subtype: "invalid_preview_deployment_suffix" }
1120
- );
391
+
392
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
393
+ var PREFIX = "vc-ap";
394
+ function generateAssetPrefixFromName({
395
+ name
396
+ }) {
397
+ if (!name) {
398
+ throw new Error("Name is required to generate an asset prefix");
399
+ }
400
+ return `${PREFIX}-${name}`;
401
+ }
402
+
403
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
404
+ function generatePortFromName({
405
+ name,
406
+ minPort = 3e3,
407
+ maxPort = 8e3
408
+ }) {
409
+ if (!name) {
410
+ throw new Error("Name is required to generate a port");
411
+ }
412
+ let hash = 0;
413
+ for (let i = 0; i < name.length; i++) {
414
+ hash = (hash << 5) - hash + name.charCodeAt(i);
415
+ hash |= 0;
416
+ }
417
+ hash = Math.abs(hash);
418
+ const range = maxPort - minPort;
419
+ const port = minPort + hash % range;
420
+ return port;
421
+ }
422
+
423
+ // src/config/microfrontends-config/isomorphic/host.ts
424
+ var Host = class {
425
+ constructor(hostConfig, options) {
426
+ const { protocol = "https", host, port } = hostConfig;
427
+ this.protocol = protocol;
428
+ this.host = host;
429
+ this.port = Host.getPort({ port, protocol: this.protocol });
430
+ this.local = options == null ? void 0 : options.isLocal;
431
+ }
432
+ isLocal() {
433
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
434
+ }
435
+ static getPort({
436
+ protocol,
437
+ port
438
+ }) {
439
+ if (!port) {
440
+ if (protocol === "http") {
441
+ return 80;
442
+ }
443
+ return 443;
1121
444
  }
445
+ return port;
446
+ }
447
+ isDefaultPort() {
448
+ return this.port === Host.getPort({ protocol: this.protocol });
449
+ }
450
+ toString(opts = {}) {
451
+ const url = this.toUrl(opts);
452
+ return url.toString().replace(/\/$/, "");
453
+ }
454
+ toUrl(opts = {}) {
455
+ const { includeDefaultPort } = opts;
456
+ const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
457
+ return new URL(url);
458
+ }
459
+ };
460
+ var LocalHost = class extends Host {
461
+ constructor({
462
+ appName,
463
+ ...hostConfig
464
+ }) {
465
+ const host = hostConfig.host ?? "localhost";
466
+ const port = hostConfig.port ?? generatePortFromName({ name: appName });
467
+ const protocol = hostConfig.protocol ?? "http";
468
+ super({ protocol, host, port });
1122
469
  }
1123
470
  };
1124
471
 
1125
- // src/config/utils/convert.ts
1126
- function convertV1RoutingToV2Routing(routing) {
1127
- return routing.matches.map((group) => {
1128
- var _a;
1129
- return {
1130
- group: group.group,
1131
- flag: (_a = group.options) == null ? void 0 : _a.flag,
1132
- paths: group.paths
472
+ // src/config/microfrontends-config/isomorphic/application.ts
473
+ var Application = class {
474
+ constructor(name, {
475
+ app,
476
+ overrides,
477
+ isDefault
478
+ }) {
479
+ var _a, _b;
480
+ this.name = name;
481
+ this.development = {
482
+ local: new LocalHost({
483
+ appName: name,
484
+ ...(_a = app.development) == null ? void 0 : _a.local
485
+ }),
486
+ fallback: ((_b = app.development) == null ? void 0 : _b.fallback) ? new Host(app.development.fallback) : void 0
1133
487
  };
1134
- });
1135
- }
1136
- function convertV1ApplicationToV2Application(application) {
1137
- const common = {
1138
- production: application.production,
1139
- development: application.development,
1140
- vercel: application.vercel
1141
- };
1142
- if (application.default) {
1143
- return common;
488
+ this.production = app.production ? new Host(app.production) : void 0;
489
+ this.vercel = app.vercel;
490
+ this.overrides = (overrides == null ? void 0 : overrides.environment) ? {
491
+ environment: new Host(overrides.environment)
492
+ } : void 0;
493
+ this.default = isDefault ?? false;
494
+ this.serialized = app;
1144
495
  }
1145
- return {
1146
- ...common,
1147
- routing: convertV1RoutingToV2Routing(application.routing)
1148
- };
1149
- }
1150
- function convertV1ConfigToV2Config(config, fromApp) {
1151
- if (!config.applications[fromApp]) {
1152
- throw new Error(`Application "${fromApp}" not found in the config`);
496
+ isDefault() {
497
+ return this.default;
1153
498
  }
1154
- const common = {
1155
- version: "2",
1156
- options: config.options
1157
- };
1158
- if (config.applications[fromApp].default) {
1159
- return {
1160
- ...common,
1161
- applications: Object.fromEntries(
1162
- Object.entries(config.applications).map(([id, application]) => [
1163
- id,
1164
- convertV1ApplicationToV2Application(application)
1165
- ])
1166
- )
1167
- };
499
+ getAssetPrefix() {
500
+ return generateAssetPrefixFromName({ name: this.name });
1168
501
  }
1169
- const defaultApplication = Object.entries(config.applications).find(
1170
- ([, application]) => application.default
1171
- );
1172
- if (!defaultApplication) {
1173
- throw new Error("No default application found in the config");
502
+ serialize() {
503
+ return this.serialized;
1174
504
  }
1175
- return {
1176
- ...common,
1177
- partOf: defaultApplication[0]
1178
- };
1179
- }
505
+ };
506
+ var DefaultApplication = class extends Application {
507
+ constructor(name, {
508
+ app,
509
+ overrides
510
+ }) {
511
+ super(name, {
512
+ app,
513
+ overrides,
514
+ isDefault: true
515
+ });
516
+ this.default = true;
517
+ this.production = new Host(app.production);
518
+ }
519
+ getAssetPrefix() {
520
+ return "";
521
+ }
522
+ };
523
+ var ChildApplication = class extends Application {
524
+ constructor(name, {
525
+ app,
526
+ overrides
527
+ }) {
528
+ ChildApplication.validate(name, app);
529
+ super(name, {
530
+ app,
531
+ overrides,
532
+ isDefault: false
533
+ });
534
+ this.default = false;
535
+ this.routing = app.routing;
536
+ }
537
+ static validate(name, app) {
538
+ validateAppPaths(name, app);
539
+ }
540
+ };
1180
541
 
1181
- // src/config/utils/write-file.ts
1182
- var import_node_fs = __toESM(require("fs"), 1);
1183
- var import_node_path3 = require("path");
1184
- function writeFile(outputPath, config, prettify) {
1185
- import_node_fs.default.mkdirSync((0, import_node_path3.dirname)(outputPath), { recursive: true });
1186
- import_node_fs.default.writeFileSync(
1187
- outputPath,
1188
- JSON.stringify(config, null, prettify ? 2 : void 0)
1189
- );
1190
- }
542
+ // src/config/microfrontends-config/isomorphic/constants.ts
543
+ var DEFAULT_LOCAL_PROXY_PORT = 3024;
1191
544
 
1192
- // src/config/microfrontend-config.ts
1193
- var MicrofrontendConfig = class extends MicrofrontendConfigCommon {
1194
- static validate(configString) {
1195
- const config = validateSchema(configString);
1196
- validateVersion(config.version);
1197
- validatePaths(config.applications);
1198
- validateMainPath(config.applications);
1199
- validateDefaults(config.applications);
1200
- validateOptions(config.options);
1201
- return config;
545
+ // src/config/microfrontends-config/isomorphic/index.ts
546
+ var MicrofrontendConfigIsomorphic = class {
547
+ constructor({
548
+ config,
549
+ overrides,
550
+ meta
551
+ }) {
552
+ this.childApplications = {};
553
+ var _a, _b, _c, _d;
554
+ MicrofrontendConfigIsomorphic.validate(config);
555
+ const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
556
+ this.overrides = overrides && !disableOverrides ? overrides : void 0;
557
+ this.isMainConfig = isMainConfig(config);
558
+ if (isMainConfig(config)) {
559
+ for (const [appId, appConfig] of Object.entries(config.applications)) {
560
+ const appOverrides = !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.applications[appId] : void 0;
561
+ if (isDefaultApp(appConfig)) {
562
+ this.defaultApplication = new DefaultApplication(appId, {
563
+ app: appConfig,
564
+ overrides: appOverrides
565
+ });
566
+ } else {
567
+ this.childApplications[appId] = new ChildApplication(appId, {
568
+ app: appConfig,
569
+ overrides: appOverrides
570
+ });
571
+ }
572
+ }
573
+ } else {
574
+ this.partOf = config.partOf;
575
+ const appOverrides = !disableOverrides ? (_d = this.overrides) == null ? void 0 : _d.applications[meta.fromApp] : void 0;
576
+ this.childApplications[meta.fromApp] = new ChildApplication(
577
+ meta.fromApp,
578
+ {
579
+ // we don't know routing because we're not in the main config
580
+ app: { routing: [] },
581
+ overrides: appOverrides
582
+ }
583
+ );
584
+ }
585
+ if (isMainConfig(config) && !this.defaultApplication) {
586
+ throw new MicrofrontendError(
587
+ `Could not find default application in microfrontends configuration`,
588
+ {
589
+ type: "application",
590
+ subtype: "not_found"
591
+ }
592
+ );
593
+ }
594
+ this.config = config;
595
+ this.options = config.options;
596
+ this.serialized = {
597
+ config,
598
+ overrides,
599
+ meta
600
+ };
601
+ }
602
+ static validate(config) {
603
+ const c = typeof config === "string" ? (0, import_jsonc_parser.parse)(config) : config;
604
+ if (isMainConfig(c)) {
605
+ validateConfigVersion(c.version);
606
+ validateConfigPaths(c.applications);
607
+ validateConfigDefaultApplication(c.applications);
608
+ }
609
+ return c;
1202
610
  }
1203
611
  static fromEnv({
612
+ meta,
1204
613
  cookies
1205
614
  }) {
1206
- return new MicrofrontendConfigCommon({
1207
- config: MicrofrontendConfig.validate(
1208
- MicrofrontendConfigCommon.getConfigFromEnv()
1209
- ),
1210
- overrides: Overrides.parseOverrides(cookies)
615
+ return new MicrofrontendConfigIsomorphic({
616
+ config: (0, import_jsonc_parser.parse)(getConfigStringFromEnv()),
617
+ overrides: parseOverrides(cookies ?? []),
618
+ meta
1211
619
  });
1212
620
  }
1213
- static fromFile({
1214
- filePath
1215
- }) {
1216
- try {
1217
- const config = import_node_fs2.default.readFileSync(filePath, "utf-8");
1218
- return new MicrofrontendConfig({
1219
- config: MicrofrontendConfig.validate(config)
1220
- });
1221
- } catch (e) {
1222
- throw MicrofrontendError.handle(e, {
1223
- fileName: filePath
1224
- });
621
+ isOverridesDisabled() {
622
+ var _a, _b;
623
+ return ((_b = (_a = this.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
624
+ }
625
+ getConfig() {
626
+ return this.config;
627
+ }
628
+ getApplicationsByType() {
629
+ return {
630
+ defaultApplication: this.defaultApplication,
631
+ applications: Object.values(this.childApplications)
632
+ };
633
+ }
634
+ getChildApplications() {
635
+ return Object.values(this.childApplications);
636
+ }
637
+ getAllApplications() {
638
+ return [
639
+ this.defaultApplication,
640
+ ...Object.values(this.childApplications)
641
+ ].filter(Boolean);
642
+ }
643
+ getApplication(name) {
644
+ var _a;
645
+ if (((_a = this.defaultApplication) == null ? void 0 : _a.name) === name) {
646
+ return this.defaultApplication;
647
+ }
648
+ const app = this.childApplications[name];
649
+ if (!app) {
650
+ throw new MicrofrontendError(
651
+ `Could not find microfrontends configuration for application "${name}"`,
652
+ {
653
+ type: "application",
654
+ subtype: "not_found"
655
+ }
656
+ );
1225
657
  }
658
+ return app;
659
+ }
660
+ getApplicationByProjectId(projectId) {
661
+ var _a, _b;
662
+ if (((_b = (_a = this.defaultApplication) == null ? void 0 : _a.vercel) == null ? void 0 : _b.projectId) === projectId) {
663
+ return this.defaultApplication;
664
+ }
665
+ return Object.values(this.childApplications).find(
666
+ (app) => {
667
+ var _a2;
668
+ return ((_a2 = app.vercel) == null ? void 0 : _a2.projectId) === projectId;
669
+ }
670
+ );
1226
671
  }
1227
672
  /**
1228
- * Writes the configuration to a file.
673
+ * Returns the default application. This can throw if the default application
674
+ * is undefined ( )
1229
675
  */
1230
- write(fromApp, opts = {}) {
1231
- const { pretty = true, versions = ["v1", "v2"] } = opts;
1232
- const config = this.toSchemaJson();
1233
- if (versions.includes("v1")) {
1234
- const outputPath = getOutputFilePath2();
1235
- writeFile(outputPath, config, pretty);
676
+ getDefaultApplication() {
677
+ if (!this.defaultApplication) {
678
+ throw new MicrofrontendError(
679
+ `Could not find default application in microfrontends configuration`,
680
+ {
681
+ type: "application",
682
+ subtype: "not_found"
683
+ }
684
+ );
1236
685
  }
1237
- if (versions.includes("v2")) {
1238
- const outputPath = getOutputFilePath();
1239
- const v2Config = convertV1ConfigToV2Config(config, fromApp);
1240
- writeFile(outputPath, v2Config, pretty);
686
+ return this.defaultApplication;
687
+ }
688
+ /**
689
+ * Returns the configured port for the local proxy
690
+ */
691
+ getLocalProxyPort() {
692
+ var _a, _b;
693
+ return ((_b = (_a = this.config.options) == null ? void 0 : _a.localProxy) == null ? void 0 : _b.port) ?? DEFAULT_LOCAL_PROXY_PORT;
694
+ }
695
+ /**
696
+ * Serializes the class back to the Schema type.
697
+ *
698
+ * NOTE: This is used when writing the config to disk and must always match the input Schema
699
+ */
700
+ toSchemaJson() {
701
+ return this.serialized.config;
702
+ }
703
+ toClientConfig() {
704
+ const applications = Object.fromEntries(
705
+ Object.entries(this.childApplications).map(([name, application]) => [
706
+ name,
707
+ {
708
+ default: false,
709
+ routing: application.routing
710
+ }
711
+ ])
712
+ );
713
+ if (this.defaultApplication) {
714
+ applications[this.defaultApplication.name] = {
715
+ default: true
716
+ };
1241
717
  }
718
+ return new MicrofrontendConfigClient({
719
+ applications
720
+ });
721
+ }
722
+ serialize() {
723
+ return this.serialized;
1242
724
  }
1243
725
  };
1244
726
  // Annotate the CommonJS export names for ESM import in node:
1245
727
  0 && (module.exports = {
1246
- MicrofrontendConfig,
1247
- getClientConfigFromEnv,
1248
- getConfigForClient,
1249
- getWellKnownClientData,
1250
- isCommonApplicationConfig,
1251
- isDefaultApplicationConfig
728
+ MicrofrontendConfigIsomorphic
1252
729
  });
1253
730
  //# sourceMappingURL=config.cjs.map