@vercel/microfrontends 1.1.1-canary.6 → 1.2.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 (55) hide show
  1. package/dist/bin/cli.cjs +405 -204
  2. package/dist/config.cjs +25 -68
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.d.ts +5 -5
  5. package/dist/config.js +25 -68
  6. package/dist/config.js.map +1 -1
  7. package/dist/experimental/sveltekit.cjs +64 -193
  8. package/dist/experimental/sveltekit.cjs.map +1 -1
  9. package/dist/experimental/sveltekit.js +64 -193
  10. package/dist/experimental/sveltekit.js.map +1 -1
  11. package/dist/experimental/vite.cjs +64 -193
  12. package/dist/experimental/vite.cjs.map +1 -1
  13. package/dist/experimental/vite.js +64 -193
  14. package/dist/experimental/vite.js.map +1 -1
  15. package/dist/microfrontends/server.cjs +64 -193
  16. package/dist/microfrontends/server.cjs.map +1 -1
  17. package/dist/microfrontends/server.d.ts +3 -3
  18. package/dist/microfrontends/server.js +64 -193
  19. package/dist/microfrontends/server.js.map +1 -1
  20. package/dist/next/config.cjs +69 -358
  21. package/dist/next/config.cjs.map +1 -1
  22. package/dist/next/config.d.ts +1 -1
  23. package/dist/next/config.js +69 -358
  24. package/dist/next/config.js.map +1 -1
  25. package/dist/next/endpoints.d.ts +2 -2
  26. package/dist/next/middleware.cjs +25 -68
  27. package/dist/next/middleware.cjs.map +1 -1
  28. package/dist/next/middleware.js +25 -68
  29. package/dist/next/middleware.js.map +1 -1
  30. package/dist/next/testing.cjs +123 -144
  31. package/dist/next/testing.cjs.map +1 -1
  32. package/dist/next/testing.d.ts +36 -5
  33. package/dist/next/testing.js +122 -143
  34. package/dist/next/testing.js.map +1 -1
  35. package/dist/overrides.d.ts +3 -3
  36. package/dist/schema.d.ts +2 -2
  37. package/dist/{types-f1260e44.d.ts → types-b970b583.d.ts} +1 -1
  38. package/dist/{types-a4add5ab.d.ts → types-bee19651.d.ts} +11 -2
  39. package/dist/{types-54064641.d.ts → types-c9f15465.d.ts} +31 -85
  40. package/dist/utils/mfe-port.cjs +64 -193
  41. package/dist/utils/mfe-port.cjs.map +1 -1
  42. package/dist/utils/mfe-port.js +64 -193
  43. package/dist/utils/mfe-port.js.map +1 -1
  44. package/dist/validation.cjs +39 -125
  45. package/dist/validation.cjs.map +1 -1
  46. package/dist/validation.d.ts +1 -1
  47. package/dist/validation.js +39 -125
  48. package/dist/validation.js.map +1 -1
  49. package/package.json +2 -8
  50. package/schema/schema.json +45 -131
  51. package/dist/routing.cjs +0 -19
  52. package/dist/routing.cjs.map +0 -1
  53. package/dist/routing.d.ts +0 -26
  54. package/dist/routing.js +0 -1
  55. package/dist/routing.js.map +0 -1
@@ -23,12 +23,12 @@ __export(testing_exports, {
23
23
  expandWildcards: () => expandWildcards,
24
24
  getAllChildApplicationNames: () => getAllChildApplicationNames,
25
25
  getAllMicrofrontendPaths: () => getAllMicrofrontendPaths,
26
- getExpectedDomainForApp: () => getExpectedDomainForApp,
27
26
  getFlaggedPathsForApp: () => getFlaggedPathsForApp,
28
27
  getLaunchedPathsForApp: () => getLaunchedPathsForApp,
29
28
  loadMicrofrontendConfigForEdge: () => loadMicrofrontendConfigForEdge,
30
29
  validateMiddlewareConfig: () => validateMiddlewareConfig,
31
- validateMiddlewareOnFlaggedPaths: () => validateMiddlewareOnFlaggedPaths
30
+ validateMiddlewareOnFlaggedPaths: () => validateMiddlewareOnFlaggedPaths,
31
+ validateRouting: () => validateRouting
32
32
  });
33
33
  module.exports = __toCommonJS(testing_exports);
34
34
  var import_node_fs = require("fs");
@@ -263,8 +263,7 @@ var validateConfigPaths = (applicationConfigsById) => {
263
263
  if (isDefaultApp(app)) {
264
264
  continue;
265
265
  }
266
- const childApp = app;
267
- for (const pathMatch of childApp.routing) {
266
+ for (const pathMatch of app.routing) {
268
267
  for (const path of pathMatch.paths) {
269
268
  const maybeError = validatePathExpression(path);
270
269
  if (maybeError) {
@@ -412,64 +411,15 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
412
411
  };
413
412
  var validateDeprecatedFields = (config) => {
414
413
  const errors = [];
415
- if (config.options?.vercel) {
416
- errors.push(
417
- `Configuration cannot contain deprecated field 'options.vercel'. Use 'options.disableOverrides' instead.`
418
- );
419
- }
420
- if (config.options?.localProxy) {
421
- errors.push(
422
- `Configuration cannot contain deprecated field 'options.localProxy'. Use 'options.localProxyPort' instead.`
423
- );
424
- }
425
414
  for (const [applicationId, application] of Object.entries(
426
415
  config.applications
427
416
  )) {
428
- if (application.vercel) {
429
- errors.push(
430
- `Application '${applicationId}' cannot contain deprecated field 'vercel'. Use 'projectId' instead.`
431
- );
432
- }
433
- if (application.production) {
434
- errors.push(
435
- `Application '${applicationId}' cannot contain deprecated field 'production'. Use 'development.fallback' instead.`
436
- );
437
- }
438
417
  if (application.development?.localPort) {
439
418
  errors.push(
440
419
  `Application '${applicationId}' cannot contain deprecated field 'development.localPort'. Use 'developement.local' instead.`
441
420
  );
442
421
  }
443
- if (application.development?.fallback && typeof application.development.fallback !== "string") {
444
- const fallback = application.development.fallback;
445
- let asString = fallback.host;
446
- if (fallback.protocol) {
447
- asString = `${fallback.protocol}://${asString}`;
448
- }
449
- if (fallback.port) {
450
- asString = `${asString}:${fallback.port}`;
451
- }
452
- errors.push(
453
- `Application '${applicationId}' requires a string (not an object) for the 'development.fallback' field. Please set 'development.fallback' to '${asString}'.`
454
- );
455
- }
456
- if (application.development?.local && typeof application.development.local !== "string" && typeof application.development.local !== "number") {
457
- const local = application.development.local;
458
- let asString;
459
- if (local.port && !local.protocol && !local.host) {
460
- asString = String(local.port);
461
- } else {
462
- asString = local.host ?? "localhost";
463
- if (local.protocol) {
464
- asString = `${local.protocol}://${asString}`;
465
- }
466
- if (local.port) {
467
- asString = `${asString}:${local.port}`;
468
- }
469
- }
470
- errors.push(
471
- `Application '${applicationId}' requires a string or number (not an object) for the 'development.local' field. Please set 'development.local' to '${asString}'.`
472
- );
422
+ if (application.projectId) {
473
423
  }
474
424
  }
475
425
  if (errors.length) {
@@ -624,6 +574,13 @@ var LocalHost = class extends Host {
624
574
  }
625
575
  };
626
576
 
577
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
578
+ function generateAutomationBypassEnvVarName({
579
+ name
580
+ }) {
581
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
582
+ }
583
+
627
584
  // src/config/microfrontends-config/isomorphic/application.ts
628
585
  var Application = class {
629
586
  constructor(name, {
@@ -642,10 +599,8 @@ var Application = class {
642
599
  };
643
600
  if (app.development?.fallback) {
644
601
  this.fallback = new Host(app.development.fallback);
645
- } else if (app.production) {
646
- this.fallback = new Host(app.production);
647
602
  }
648
- this.projectId = app.projectId ?? app.vercel?.projectId;
603
+ this.projectId = app.projectId;
649
604
  this.packageName = app.packageName;
650
605
  this.overrides = overrides?.environment ? {
651
606
  environment: new Host(overrides.environment)
@@ -659,6 +614,9 @@ var Application = class {
659
614
  getAssetPrefix() {
660
615
  return generateAssetPrefixFromName({ name: this.name });
661
616
  }
617
+ getAutomationBypassEnvVarName() {
618
+ return generateAutomationBypassEnvVarName({ name: this.name });
619
+ }
662
620
  serialize() {
663
621
  return this.serialized;
664
622
  }
@@ -674,16 +632,7 @@ var DefaultApplication = class extends Application {
674
632
  isDefault: true
675
633
  });
676
634
  this.default = true;
677
- const fallbackHost = app.development?.fallback ?? app.production;
678
- if (fallbackHost === void 0) {
679
- throw new Error(
680
- "`app.production` or `app.development.fallback` must be set in the default application in microfrontends.json."
681
- );
682
- }
683
- this.fallback = new Host(fallbackHost);
684
- if (app.production) {
685
- this.production = new Host(app.production);
686
- }
635
+ this.fallback = new Host(app.development.fallback);
687
636
  }
688
637
  getAssetPrefix() {
689
638
  return "";
@@ -720,7 +669,7 @@ var MicrofrontendConfigIsomorphic = class {
720
669
  }) {
721
670
  this.childApplications = {};
722
671
  MicrofrontendConfigIsomorphic.validate(config, opts);
723
- const disableOverrides = config.options?.disableOverrides ?? config.options?.vercel?.disableOverrides ?? false;
672
+ const disableOverrides = config.options?.disableOverrides ?? false;
724
673
  this.overrides = overrides && !disableOverrides ? overrides : void 0;
725
674
  let defaultApplication;
726
675
  for (const [appId, appConfig] of Object.entries(config.applications)) {
@@ -773,7 +722,7 @@ var MicrofrontendConfigIsomorphic = class {
773
722
  });
774
723
  }
775
724
  isOverridesDisabled() {
776
- return this.options?.vercel?.disableOverrides ?? false;
725
+ return this.options?.disableOverrides ?? false;
777
726
  }
778
727
  getConfig() {
779
728
  return this.config;
@@ -811,6 +760,14 @@ var MicrofrontendConfigIsomorphic = class {
811
760
  }
812
761
  return app;
813
762
  }
763
+ hasApplication(name) {
764
+ try {
765
+ this.getApplication(name);
766
+ return true;
767
+ } catch {
768
+ return false;
769
+ }
770
+ }
814
771
  getApplicationByProjectId(projectId) {
815
772
  if (this.defaultApplication.projectId === projectId) {
816
773
  return this.defaultApplication;
@@ -829,7 +786,7 @@ var MicrofrontendConfigIsomorphic = class {
829
786
  * Returns the configured port for the local proxy
830
787
  */
831
788
  getLocalProxyPort() {
832
- return this.config.options?.localProxyPort ?? this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
789
+ return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
833
790
  }
834
791
  /**
835
792
  * Serializes the class back to the Schema type.
@@ -861,34 +818,6 @@ var MicrofrontendConfigIsomorphic = class {
861
818
  }
862
819
  };
863
820
 
864
- // src/routing/get-domain-from-environment.ts
865
- function getDomainFromEnvironment({
866
- app,
867
- target
868
- }) {
869
- const mfeProjects = JSON.parse(
870
- process.env.VERCEL_MICROFRONTENDS_PROJECTS ?? "[]"
871
- );
872
- if (mfeProjects.length === 0) {
873
- throw new Error("Missing related microfrontends project information");
874
- }
875
- const vercelProject = mfeProjects.find(
876
- (p) => p.project.name === app.name || p.project.id === app.projectId
877
- );
878
- if (!vercelProject) {
879
- throw new Error(
880
- `Could not find the Vercel project for application "${app.name}". If the name does not match the Vercel project name, set the \`projectId\` field in the application config.`
881
- );
882
- }
883
- const domain = target === "preview" && vercelProject.preview.branch ? vercelProject.preview.branch : vercelProject.production.alias ?? vercelProject.production.url;
884
- if (!domain) {
885
- throw new Error(
886
- `Missing domain for target "${target}" in application "${app.name}"`
887
- );
888
- }
889
- return domain.startsWith("https://") ? domain : `https://${domain}`;
890
- }
891
-
892
821
  // src/next/testing/index.ts
893
822
  function expandWildcards(path) {
894
823
  if (path.includes("/:path*") || path.includes("/:slug*")) {
@@ -935,18 +864,6 @@ function getFlaggedPathsForApp(mfConfig, appName) {
935
864
  }
936
865
  return app.routing.filter((group) => Boolean(group.flag)).flatMap((group) => group.paths.flatMap(expandWildcards));
937
866
  }
938
- function getExpectedDomainForApp(mfConfig, appName, env) {
939
- const app = mfConfig.getApplication(appName);
940
- const defaultApp = mfConfig.getDefaultApplication();
941
- if (env === "development") {
942
- return app.development.local.toString();
943
- }
944
- if (["preview", "production"].includes(env)) {
945
- const target = env;
946
- return getDomainFromEnvironment({ app, target });
947
- }
948
- return defaultApp.fallback.toString();
949
- }
950
867
  function getAllMicrofrontendPaths(mfConfig) {
951
868
  return mfConfig.getChildApplications().flatMap((app) => {
952
869
  return app.routing.flatMap((group) => group.paths.flatMap(expandWildcards));
@@ -1030,41 +947,29 @@ async function validateMiddlewareOnFlaggedPaths(microfrontendConfigOrPath, middl
1030
947
  for (const appName of allAppNames) {
1031
948
  const flaggedPaths = getFlaggedPathsForApp(microfrontendConfig, appName);
1032
949
  if (flaggedPaths.length) {
1033
- for (const env of ["preview", "production"]) {
1034
- process.env.VERCEL_ENV = env;
1035
- for (const path of flaggedPaths) {
1036
- const expectedHost = getExpectedDomainForApp(
1037
- microfrontendConfig,
1038
- appName,
1039
- env
950
+ for (const path of flaggedPaths) {
951
+ const host = microfrontendConfig.defaultApplication.fallback.toString();
952
+ const requestPath = `${host}${path}`;
953
+ const request = new import_server.NextRequest(requestPath);
954
+ const response = await middleware(
955
+ request,
956
+ {}
957
+ );
958
+ const routedZone = response?.headers.get(
959
+ "x-middleware-request-x-vercel-mfe-zone"
960
+ );
961
+ if (!response) {
962
+ errors.push(
963
+ `middleware did not action for ${requestPath}. Expected to route to ${appName}`
1040
964
  );
1041
- const expectedDefaultHost = getExpectedDomainForApp(
1042
- microfrontendConfig,
1043
- microfrontendConfig.getDefaultApplication().name,
1044
- env
965
+ } else if (response.status !== 200) {
966
+ errors.push(
967
+ `expected 200 status for ${requestPath} but got ${response.status}`
1045
968
  );
1046
- const requestPath = `${expectedDefaultHost}${path}`;
1047
- const request = new import_server.NextRequest(requestPath, {
1048
- headers: { "x-vercel-skip-deployment-existence-check": "1" }
1049
- });
1050
- const response = await middleware(
1051
- request,
1052
- {}
969
+ } else if (routedZone !== appName) {
970
+ errors.push(
971
+ `expected ${requestPath} to route to ${appName}, but got ${routedZone}`
1053
972
  );
1054
- const expectedUrl = `${expectedHost}${path}`;
1055
- if (!response) {
1056
- errors.push(
1057
- `middleware did not action for ${requestPath} in ${env}. Expected a rewrite to ${expectedUrl}`
1058
- );
1059
- } else if (response.status !== 200) {
1060
- errors.push(
1061
- `expected 200 status for ${requestPath} in ${env} but got ${response.status}`
1062
- );
1063
- } else if (response.headers.get("x-middleware-request-x-vercel-mfe-zone") !== appName) {
1064
- errors.push(
1065
- `Expected microfrontends middleware to return rewrite to ${appName} in ${env}, but got ${response.headers.get("x-middleware-request-x-vercel-mfe-zone")} instead`
1066
- );
1067
- }
1068
973
  }
1069
974
  }
1070
975
  }
@@ -1077,16 +982,90 @@ async function validateMiddlewareOnFlaggedPaths(microfrontendConfigOrPath, middl
1077
982
  process.env.MFE_PREVIEW_DOMAINS = initialMfePreviewDomains;
1078
983
  }
1079
984
  }
985
+ function validateRouting(microfrontendConfigOrPath, routesToTest) {
986
+ const microfrontendConfig = typeof microfrontendConfigOrPath === "string" ? loadMicrofrontendConfigForEdge(microfrontendConfigOrPath) : microfrontendConfigOrPath;
987
+ const matches = /* @__PURE__ */ new Map();
988
+ for (const application of microfrontendConfig.getChildApplications()) {
989
+ for (const route of application.routing) {
990
+ for (const path of route.paths) {
991
+ matches.set({ path, flag: route.flag }, application.name);
992
+ }
993
+ }
994
+ }
995
+ const errors = [];
996
+ for (const [applicationName, paths] of Object.entries(routesToTest)) {
997
+ if (!microfrontendConfig.hasApplication(applicationName)) {
998
+ errors.push(
999
+ `Application ${applicationName} does not exist in the microfrontends config. The applications in the config are: ${microfrontendConfig.getAllApplications().map((app) => app.name).join(", ")}`
1000
+ );
1001
+ continue;
1002
+ }
1003
+ for (const expected of paths) {
1004
+ const path = typeof expected === "string" ? expected : expected.path;
1005
+ const flag = typeof expected === "string" ? void 0 : expected.flag;
1006
+ const matchedApplications = /* @__PURE__ */ new Map();
1007
+ const matchesWithoutFlags = /* @__PURE__ */ new Map();
1008
+ for (const [matcher, applicationMatched] of matches.entries()) {
1009
+ if ((0, import_path_to_regexp3.pathToRegexp)(matcher.path).test(path)) {
1010
+ if (!matcher.flag || matcher.flag === flag) {
1011
+ const existingMatches = matchedApplications.get(applicationMatched) ?? [];
1012
+ existingMatches.push(matcher.path);
1013
+ matchedApplications.set(applicationMatched, existingMatches);
1014
+ } else {
1015
+ matchesWithoutFlags.set(applicationName, matcher.flag);
1016
+ }
1017
+ }
1018
+ }
1019
+ if (matchedApplications.size === 0) {
1020
+ matchedApplications.set(
1021
+ microfrontendConfig.getDefaultApplication().name,
1022
+ ["fallback to default application"]
1023
+ );
1024
+ }
1025
+ if (matchedApplications.size > 1) {
1026
+ const formattedMatches = Array.from(
1027
+ matchedApplications.entries().map(
1028
+ ([matchedApplication, matchers]) => `${matchedApplication} (on ${matchers.join(", ")})`
1029
+ )
1030
+ ).join(", ");
1031
+ errors.push(
1032
+ `${path} can only match one application, but it matched multiple: ${formattedMatches}`
1033
+ );
1034
+ } else if (!matchedApplications.has(applicationName)) {
1035
+ const actualMatch = matchedApplications.entries().next().value;
1036
+ if (!actualMatch) {
1037
+ throw new Error("this shouldn't happen");
1038
+ }
1039
+ const [matchedApplication, matchers] = actualMatch;
1040
+ let extraMessage = "";
1041
+ if (matchesWithoutFlags.has(applicationName)) {
1042
+ const flagToSet = matchesWithoutFlags.get(applicationName);
1043
+ extraMessage = ` It would've matched ${applicationName} if the ${flagToSet} flag was set. If this is what you want, replace ${path} in the paths-to-test list with {path: '${path}', flag: '${flagToSet}'}.`;
1044
+ }
1045
+ errors.push(
1046
+ `Expected ${path}${flag ? ` (with flag ${flag})` : ""} to match ${applicationName}, but it matched ${matchedApplication} (on ${matchers.join(", ")}).${extraMessage}`
1047
+ );
1048
+ }
1049
+ }
1050
+ }
1051
+ if (errors.length) {
1052
+ throw new Error(
1053
+ `Incorrect microfrontends routing detected:
1054
+
1055
+ - ${errors.join("\n- ")}`
1056
+ );
1057
+ }
1058
+ }
1080
1059
  // Annotate the CommonJS export names for ESM import in node:
1081
1060
  0 && (module.exports = {
1082
1061
  expandWildcards,
1083
1062
  getAllChildApplicationNames,
1084
1063
  getAllMicrofrontendPaths,
1085
- getExpectedDomainForApp,
1086
1064
  getFlaggedPathsForApp,
1087
1065
  getLaunchedPathsForApp,
1088
1066
  loadMicrofrontendConfigForEdge,
1089
1067
  validateMiddlewareConfig,
1090
- validateMiddlewareOnFlaggedPaths
1068
+ validateMiddlewareOnFlaggedPaths,
1069
+ validateRouting
1091
1070
  });
1092
1071
  //# sourceMappingURL=testing.cjs.map