@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
@@ -3,7 +3,7 @@ import { readFileSync } from "node:fs";
3
3
  import {
4
4
  NextRequest
5
5
  } from "next/server.js";
6
- import { match } from "path-to-regexp";
6
+ import { match, pathToRegexp as pathToRegexp3 } from "path-to-regexp";
7
7
  import { parse as parse2 } from "jsonc-parser";
8
8
 
9
9
  // src/config/microfrontends-config/isomorphic/index.ts
@@ -233,8 +233,7 @@ var validateConfigPaths = (applicationConfigsById) => {
233
233
  if (isDefaultApp(app)) {
234
234
  continue;
235
235
  }
236
- const childApp = app;
237
- for (const pathMatch of childApp.routing) {
236
+ for (const pathMatch of app.routing) {
238
237
  for (const path of pathMatch.paths) {
239
238
  const maybeError = validatePathExpression(path);
240
239
  if (maybeError) {
@@ -382,64 +381,15 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
382
381
  };
383
382
  var validateDeprecatedFields = (config) => {
384
383
  const errors = [];
385
- if (config.options?.vercel) {
386
- errors.push(
387
- `Configuration cannot contain deprecated field 'options.vercel'. Use 'options.disableOverrides' instead.`
388
- );
389
- }
390
- if (config.options?.localProxy) {
391
- errors.push(
392
- `Configuration cannot contain deprecated field 'options.localProxy'. Use 'options.localProxyPort' instead.`
393
- );
394
- }
395
384
  for (const [applicationId, application] of Object.entries(
396
385
  config.applications
397
386
  )) {
398
- if (application.vercel) {
399
- errors.push(
400
- `Application '${applicationId}' cannot contain deprecated field 'vercel'. Use 'projectId' instead.`
401
- );
402
- }
403
- if (application.production) {
404
- errors.push(
405
- `Application '${applicationId}' cannot contain deprecated field 'production'. Use 'development.fallback' instead.`
406
- );
407
- }
408
387
  if (application.development?.localPort) {
409
388
  errors.push(
410
389
  `Application '${applicationId}' cannot contain deprecated field 'development.localPort'. Use 'developement.local' instead.`
411
390
  );
412
391
  }
413
- if (application.development?.fallback && typeof application.development.fallback !== "string") {
414
- const fallback = application.development.fallback;
415
- let asString = fallback.host;
416
- if (fallback.protocol) {
417
- asString = `${fallback.protocol}://${asString}`;
418
- }
419
- if (fallback.port) {
420
- asString = `${asString}:${fallback.port}`;
421
- }
422
- errors.push(
423
- `Application '${applicationId}' requires a string (not an object) for the 'development.fallback' field. Please set 'development.fallback' to '${asString}'.`
424
- );
425
- }
426
- if (application.development?.local && typeof application.development.local !== "string" && typeof application.development.local !== "number") {
427
- const local = application.development.local;
428
- let asString;
429
- if (local.port && !local.protocol && !local.host) {
430
- asString = String(local.port);
431
- } else {
432
- asString = local.host ?? "localhost";
433
- if (local.protocol) {
434
- asString = `${local.protocol}://${asString}`;
435
- }
436
- if (local.port) {
437
- asString = `${asString}:${local.port}`;
438
- }
439
- }
440
- errors.push(
441
- `Application '${applicationId}' requires a string or number (not an object) for the 'development.local' field. Please set 'development.local' to '${asString}'.`
442
- );
392
+ if (application.projectId) {
443
393
  }
444
394
  }
445
395
  if (errors.length) {
@@ -594,6 +544,13 @@ var LocalHost = class extends Host {
594
544
  }
595
545
  };
596
546
 
547
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
548
+ function generateAutomationBypassEnvVarName({
549
+ name
550
+ }) {
551
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
552
+ }
553
+
597
554
  // src/config/microfrontends-config/isomorphic/application.ts
598
555
  var Application = class {
599
556
  constructor(name, {
@@ -612,10 +569,8 @@ var Application = class {
612
569
  };
613
570
  if (app.development?.fallback) {
614
571
  this.fallback = new Host(app.development.fallback);
615
- } else if (app.production) {
616
- this.fallback = new Host(app.production);
617
572
  }
618
- this.projectId = app.projectId ?? app.vercel?.projectId;
573
+ this.projectId = app.projectId;
619
574
  this.packageName = app.packageName;
620
575
  this.overrides = overrides?.environment ? {
621
576
  environment: new Host(overrides.environment)
@@ -629,6 +584,9 @@ var Application = class {
629
584
  getAssetPrefix() {
630
585
  return generateAssetPrefixFromName({ name: this.name });
631
586
  }
587
+ getAutomationBypassEnvVarName() {
588
+ return generateAutomationBypassEnvVarName({ name: this.name });
589
+ }
632
590
  serialize() {
633
591
  return this.serialized;
634
592
  }
@@ -644,16 +602,7 @@ var DefaultApplication = class extends Application {
644
602
  isDefault: true
645
603
  });
646
604
  this.default = true;
647
- const fallbackHost = app.development?.fallback ?? app.production;
648
- if (fallbackHost === void 0) {
649
- throw new Error(
650
- "`app.production` or `app.development.fallback` must be set in the default application in microfrontends.json."
651
- );
652
- }
653
- this.fallback = new Host(fallbackHost);
654
- if (app.production) {
655
- this.production = new Host(app.production);
656
- }
605
+ this.fallback = new Host(app.development.fallback);
657
606
  }
658
607
  getAssetPrefix() {
659
608
  return "";
@@ -690,7 +639,7 @@ var MicrofrontendConfigIsomorphic = class {
690
639
  }) {
691
640
  this.childApplications = {};
692
641
  MicrofrontendConfigIsomorphic.validate(config, opts);
693
- const disableOverrides = config.options?.disableOverrides ?? config.options?.vercel?.disableOverrides ?? false;
642
+ const disableOverrides = config.options?.disableOverrides ?? false;
694
643
  this.overrides = overrides && !disableOverrides ? overrides : void 0;
695
644
  let defaultApplication;
696
645
  for (const [appId, appConfig] of Object.entries(config.applications)) {
@@ -743,7 +692,7 @@ var MicrofrontendConfigIsomorphic = class {
743
692
  });
744
693
  }
745
694
  isOverridesDisabled() {
746
- return this.options?.vercel?.disableOverrides ?? false;
695
+ return this.options?.disableOverrides ?? false;
747
696
  }
748
697
  getConfig() {
749
698
  return this.config;
@@ -781,6 +730,14 @@ var MicrofrontendConfigIsomorphic = class {
781
730
  }
782
731
  return app;
783
732
  }
733
+ hasApplication(name) {
734
+ try {
735
+ this.getApplication(name);
736
+ return true;
737
+ } catch {
738
+ return false;
739
+ }
740
+ }
784
741
  getApplicationByProjectId(projectId) {
785
742
  if (this.defaultApplication.projectId === projectId) {
786
743
  return this.defaultApplication;
@@ -799,7 +756,7 @@ var MicrofrontendConfigIsomorphic = class {
799
756
  * Returns the configured port for the local proxy
800
757
  */
801
758
  getLocalProxyPort() {
802
- return this.config.options?.localProxyPort ?? this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
759
+ return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
803
760
  }
804
761
  /**
805
762
  * Serializes the class back to the Schema type.
@@ -831,34 +788,6 @@ var MicrofrontendConfigIsomorphic = class {
831
788
  }
832
789
  };
833
790
 
834
- // src/routing/get-domain-from-environment.ts
835
- function getDomainFromEnvironment({
836
- app,
837
- target
838
- }) {
839
- const mfeProjects = JSON.parse(
840
- process.env.VERCEL_MICROFRONTENDS_PROJECTS ?? "[]"
841
- );
842
- if (mfeProjects.length === 0) {
843
- throw new Error("Missing related microfrontends project information");
844
- }
845
- const vercelProject = mfeProjects.find(
846
- (p) => p.project.name === app.name || p.project.id === app.projectId
847
- );
848
- if (!vercelProject) {
849
- throw new Error(
850
- `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.`
851
- );
852
- }
853
- const domain = target === "preview" && vercelProject.preview.branch ? vercelProject.preview.branch : vercelProject.production.alias ?? vercelProject.production.url;
854
- if (!domain) {
855
- throw new Error(
856
- `Missing domain for target "${target}" in application "${app.name}"`
857
- );
858
- }
859
- return domain.startsWith("https://") ? domain : `https://${domain}`;
860
- }
861
-
862
791
  // src/next/testing/index.ts
863
792
  function expandWildcards(path) {
864
793
  if (path.includes("/:path*") || path.includes("/:slug*")) {
@@ -905,18 +834,6 @@ function getFlaggedPathsForApp(mfConfig, appName) {
905
834
  }
906
835
  return app.routing.filter((group) => Boolean(group.flag)).flatMap((group) => group.paths.flatMap(expandWildcards));
907
836
  }
908
- function getExpectedDomainForApp(mfConfig, appName, env) {
909
- const app = mfConfig.getApplication(appName);
910
- const defaultApp = mfConfig.getDefaultApplication();
911
- if (env === "development") {
912
- return app.development.local.toString();
913
- }
914
- if (["preview", "production"].includes(env)) {
915
- const target = env;
916
- return getDomainFromEnvironment({ app, target });
917
- }
918
- return defaultApp.fallback.toString();
919
- }
920
837
  function getAllMicrofrontendPaths(mfConfig) {
921
838
  return mfConfig.getChildApplications().flatMap((app) => {
922
839
  return app.routing.flatMap((group) => group.paths.flatMap(expandWildcards));
@@ -1000,41 +917,29 @@ async function validateMiddlewareOnFlaggedPaths(microfrontendConfigOrPath, middl
1000
917
  for (const appName of allAppNames) {
1001
918
  const flaggedPaths = getFlaggedPathsForApp(microfrontendConfig, appName);
1002
919
  if (flaggedPaths.length) {
1003
- for (const env of ["preview", "production"]) {
1004
- process.env.VERCEL_ENV = env;
1005
- for (const path of flaggedPaths) {
1006
- const expectedHost = getExpectedDomainForApp(
1007
- microfrontendConfig,
1008
- appName,
1009
- env
920
+ for (const path of flaggedPaths) {
921
+ const host = microfrontendConfig.defaultApplication.fallback.toString();
922
+ const requestPath = `${host}${path}`;
923
+ const request = new NextRequest(requestPath);
924
+ const response = await middleware(
925
+ request,
926
+ {}
927
+ );
928
+ const routedZone = response?.headers.get(
929
+ "x-middleware-request-x-vercel-mfe-zone"
930
+ );
931
+ if (!response) {
932
+ errors.push(
933
+ `middleware did not action for ${requestPath}. Expected to route to ${appName}`
1010
934
  );
1011
- const expectedDefaultHost = getExpectedDomainForApp(
1012
- microfrontendConfig,
1013
- microfrontendConfig.getDefaultApplication().name,
1014
- env
935
+ } else if (response.status !== 200) {
936
+ errors.push(
937
+ `expected 200 status for ${requestPath} but got ${response.status}`
1015
938
  );
1016
- const requestPath = `${expectedDefaultHost}${path}`;
1017
- const request = new NextRequest(requestPath, {
1018
- headers: { "x-vercel-skip-deployment-existence-check": "1" }
1019
- });
1020
- const response = await middleware(
1021
- request,
1022
- {}
939
+ } else if (routedZone !== appName) {
940
+ errors.push(
941
+ `expected ${requestPath} to route to ${appName}, but got ${routedZone}`
1023
942
  );
1024
- const expectedUrl = `${expectedHost}${path}`;
1025
- if (!response) {
1026
- errors.push(
1027
- `middleware did not action for ${requestPath} in ${env}. Expected a rewrite to ${expectedUrl}`
1028
- );
1029
- } else if (response.status !== 200) {
1030
- errors.push(
1031
- `expected 200 status for ${requestPath} in ${env} but got ${response.status}`
1032
- );
1033
- } else if (response.headers.get("x-middleware-request-x-vercel-mfe-zone") !== appName) {
1034
- errors.push(
1035
- `Expected microfrontends middleware to return rewrite to ${appName} in ${env}, but got ${response.headers.get("x-middleware-request-x-vercel-mfe-zone")} instead`
1036
- );
1037
- }
1038
943
  }
1039
944
  }
1040
945
  }
@@ -1047,15 +952,89 @@ async function validateMiddlewareOnFlaggedPaths(microfrontendConfigOrPath, middl
1047
952
  process.env.MFE_PREVIEW_DOMAINS = initialMfePreviewDomains;
1048
953
  }
1049
954
  }
955
+ function validateRouting(microfrontendConfigOrPath, routesToTest) {
956
+ const microfrontendConfig = typeof microfrontendConfigOrPath === "string" ? loadMicrofrontendConfigForEdge(microfrontendConfigOrPath) : microfrontendConfigOrPath;
957
+ const matches = /* @__PURE__ */ new Map();
958
+ for (const application of microfrontendConfig.getChildApplications()) {
959
+ for (const route of application.routing) {
960
+ for (const path of route.paths) {
961
+ matches.set({ path, flag: route.flag }, application.name);
962
+ }
963
+ }
964
+ }
965
+ const errors = [];
966
+ for (const [applicationName, paths] of Object.entries(routesToTest)) {
967
+ if (!microfrontendConfig.hasApplication(applicationName)) {
968
+ errors.push(
969
+ `Application ${applicationName} does not exist in the microfrontends config. The applications in the config are: ${microfrontendConfig.getAllApplications().map((app) => app.name).join(", ")}`
970
+ );
971
+ continue;
972
+ }
973
+ for (const expected of paths) {
974
+ const path = typeof expected === "string" ? expected : expected.path;
975
+ const flag = typeof expected === "string" ? void 0 : expected.flag;
976
+ const matchedApplications = /* @__PURE__ */ new Map();
977
+ const matchesWithoutFlags = /* @__PURE__ */ new Map();
978
+ for (const [matcher, applicationMatched] of matches.entries()) {
979
+ if (pathToRegexp3(matcher.path).test(path)) {
980
+ if (!matcher.flag || matcher.flag === flag) {
981
+ const existingMatches = matchedApplications.get(applicationMatched) ?? [];
982
+ existingMatches.push(matcher.path);
983
+ matchedApplications.set(applicationMatched, existingMatches);
984
+ } else {
985
+ matchesWithoutFlags.set(applicationName, matcher.flag);
986
+ }
987
+ }
988
+ }
989
+ if (matchedApplications.size === 0) {
990
+ matchedApplications.set(
991
+ microfrontendConfig.getDefaultApplication().name,
992
+ ["fallback to default application"]
993
+ );
994
+ }
995
+ if (matchedApplications.size > 1) {
996
+ const formattedMatches = Array.from(
997
+ matchedApplications.entries().map(
998
+ ([matchedApplication, matchers]) => `${matchedApplication} (on ${matchers.join(", ")})`
999
+ )
1000
+ ).join(", ");
1001
+ errors.push(
1002
+ `${path} can only match one application, but it matched multiple: ${formattedMatches}`
1003
+ );
1004
+ } else if (!matchedApplications.has(applicationName)) {
1005
+ const actualMatch = matchedApplications.entries().next().value;
1006
+ if (!actualMatch) {
1007
+ throw new Error("this shouldn't happen");
1008
+ }
1009
+ const [matchedApplication, matchers] = actualMatch;
1010
+ let extraMessage = "";
1011
+ if (matchesWithoutFlags.has(applicationName)) {
1012
+ const flagToSet = matchesWithoutFlags.get(applicationName);
1013
+ 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}'}.`;
1014
+ }
1015
+ errors.push(
1016
+ `Expected ${path}${flag ? ` (with flag ${flag})` : ""} to match ${applicationName}, but it matched ${matchedApplication} (on ${matchers.join(", ")}).${extraMessage}`
1017
+ );
1018
+ }
1019
+ }
1020
+ }
1021
+ if (errors.length) {
1022
+ throw new Error(
1023
+ `Incorrect microfrontends routing detected:
1024
+
1025
+ - ${errors.join("\n- ")}`
1026
+ );
1027
+ }
1028
+ }
1050
1029
  export {
1051
1030
  expandWildcards,
1052
1031
  getAllChildApplicationNames,
1053
1032
  getAllMicrofrontendPaths,
1054
- getExpectedDomainForApp,
1055
1033
  getFlaggedPathsForApp,
1056
1034
  getLaunchedPathsForApp,
1057
1035
  loadMicrofrontendConfigForEdge,
1058
1036
  validateMiddlewareConfig,
1059
- validateMiddlewareOnFlaggedPaths
1037
+ validateMiddlewareOnFlaggedPaths,
1038
+ validateRouting
1060
1039
  };
1061
1040
  //# sourceMappingURL=testing.js.map