@vercel/microfrontends 1.5.0-canary.2 → 2.0.0-canary.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 (59) hide show
  1. package/CHANGELOG.md +10 -18
  2. package/dist/bin/cli.cjs +103 -117
  3. package/dist/config.cjs +48 -101
  4. package/dist/config.cjs.map +1 -1
  5. package/dist/config.d.ts +2 -29
  6. package/dist/config.js +37 -100
  7. package/dist/config.js.map +1 -1
  8. package/dist/experimental/sveltekit.cjs +93 -109
  9. package/dist/experimental/sveltekit.cjs.map +1 -1
  10. package/dist/experimental/sveltekit.js +92 -108
  11. package/dist/experimental/sveltekit.js.map +1 -1
  12. package/dist/experimental/vite.cjs +93 -109
  13. package/dist/experimental/vite.cjs.map +1 -1
  14. package/dist/experimental/vite.js +92 -108
  15. package/dist/experimental/vite.js.map +1 -1
  16. package/dist/get-application-context-e8a5a0e2.d.ts +14 -0
  17. package/dist/microfrontends/server.cjs +93 -109
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +4 -13
  20. package/dist/microfrontends/server.js +92 -108
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/microfrontends/utils.cjs +24 -4
  23. package/dist/microfrontends/utils.cjs.map +1 -1
  24. package/dist/microfrontends/utils.d.ts +3 -1
  25. package/dist/microfrontends/utils.js +24 -4
  26. package/dist/microfrontends/utils.js.map +1 -1
  27. package/dist/next/client.cjs +1 -1
  28. package/dist/next/client.cjs.map +1 -1
  29. package/dist/next/client.js +1 -1
  30. package/dist/next/client.js.map +1 -1
  31. package/dist/next/config.cjs +244 -117
  32. package/dist/next/config.cjs.map +1 -1
  33. package/dist/next/config.d.ts +1 -1
  34. package/dist/next/config.js +243 -116
  35. package/dist/next/config.js.map +1 -1
  36. package/dist/next/middleware.cjs +88 -44
  37. package/dist/next/middleware.cjs.map +1 -1
  38. package/dist/next/middleware.js +78 -44
  39. package/dist/next/middleware.js.map +1 -1
  40. package/dist/next/testing.cjs +51 -104
  41. package/dist/next/testing.cjs.map +1 -1
  42. package/dist/next/testing.d.ts +2 -2
  43. package/dist/next/testing.js +39 -102
  44. package/dist/next/testing.js.map +1 -1
  45. package/dist/overrides.d.ts +3 -3
  46. package/dist/schema.d.ts +2 -2
  47. package/dist/{types-1cec43e6.d.ts → types-0deb756b.d.ts} +16 -1
  48. package/dist/{types-1fb60496.d.ts → types-4299bff1.d.ts} +1 -1
  49. package/dist/utils/mfe-port.cjs +93 -109
  50. package/dist/utils/mfe-port.cjs.map +1 -1
  51. package/dist/utils/mfe-port.js +92 -108
  52. package/dist/utils/mfe-port.js.map +1 -1
  53. package/dist/validation.cjs +4 -0
  54. package/dist/validation.cjs.map +1 -1
  55. package/dist/validation.d.ts +1 -1
  56. package/dist/validation.js +4 -0
  57. package/dist/validation.js.map +1 -1
  58. package/package.json +3 -1
  59. package/schema/schema.json +4 -0
package/CHANGELOG.md CHANGED
@@ -1,30 +1,22 @@
1
1
  # @vercel/microfrontends
2
2
 
3
- ## 1.5.0-canary.2
3
+ ## 2.0.0-canary.0
4
4
 
5
- ### Patch Changes
6
-
7
- - add back asset prefix image change
8
-
9
- ## 1.5.0-canary.1
5
+ > **Check out our [Public Beta](https://vercel.com/changelog/microfrontends-are-now-in-public-beta) changelog to learn more about this release.**
10
6
 
11
- ### Patch Changes
7
+ ### Major Changes
12
8
 
13
- - Revert asset prefix image change
9
+ - This release removes the project name and flag names from being visible to client side code.
10
+ - Modify the auto-generated asset prefix to use a hash of the project name instead of the project name itself.
11
+ - Allow users to specify a custom asset prefix in the `microfrontends.json`
12
+ - Remove project names and flag names from the Microfrontends client configuration.
14
13
 
15
- ## 1.5.0-canary.0
14
+ ## 1.5.0
16
15
 
17
16
  ### Minor Changes
18
17
 
19
- - Add asset prefix to next.js images using `images.path` in the next.config
20
- - Remove deprecated fields `projectId` and `localPort` from the package
21
-
22
- ## 1.4.2
23
-
24
- ### Patch Changes
25
-
26
- - df9826a: Improve repository root inference for CLI deployments. For repositories missing `.git` and `pnpm-workspace.yaml`, `@vercel/microfrontends` will now use the directory
27
- closest to the root that contains a `package.json` as the root of the repository.
18
+ - 003d24b: - Automatically add asset prefix to Next.js images using `images.path` in `next.config.js`.
19
+ - Remove deprecated fields `projectId` and `localPort` from the package
28
20
 
29
21
  ## 1.4.1
30
22
 
package/dist/bin/cli.cjs CHANGED
@@ -30,7 +30,7 @@ var import_env = require("@next/env");
30
30
  // package.json
31
31
  var package_default = {
32
32
  name: "@vercel/microfrontends",
33
- version: "1.5.0-canary.2",
33
+ version: "2.0.0-canary.0",
34
34
  private: false,
35
35
  description: "Defines configuration and utilities for microfrontends development",
36
36
  keywords: [
@@ -165,12 +165,14 @@ var package_default = {
165
165
  },
166
166
  dependencies: {
167
167
  "@next/env": "15.4.0-canary.41",
168
+ "@types/md5": "^2.3.5",
168
169
  ajv: "^8.17.1",
169
170
  commander: "^12.1.0",
170
171
  cookie: "0.4.0",
171
172
  "fast-glob": "^3.3.2",
172
173
  "http-proxy": "^1.18.1",
173
174
  "jsonc-parser": "^3.3.1",
175
+ md5: "^2.3.0",
174
176
  nanoid: "^3.3.9",
175
177
  "path-to-regexp": "6.2.1",
176
178
  semver: "^7.7.2"
@@ -242,7 +244,7 @@ var http = __toESM(require("http"), 1);
242
244
  var https = __toESM(require("https"), 1);
243
245
  var import_node_url = require("url");
244
246
  var import_cookie = require("cookie");
245
- var import_path_to_regexp3 = require("path-to-regexp");
247
+ var import_path_to_regexp2 = require("path-to-regexp");
246
248
  var import_http_proxy = __toESM(require("http-proxy"), 1);
247
249
 
248
250
  // src/config/microfrontends-config/isomorphic/index.ts
@@ -357,84 +359,6 @@ function isDefaultApp(a) {
357
359
  return !("routing" in a);
358
360
  }
359
361
 
360
- // src/config/microfrontends-config/client/index.ts
361
- var import_path_to_regexp = require("path-to-regexp");
362
- var regexpCache = /* @__PURE__ */ new Map();
363
- var getRegexp = (path7) => {
364
- const existing = regexpCache.get(path7);
365
- if (existing) {
366
- return existing;
367
- }
368
- const regexp = (0, import_path_to_regexp.pathToRegexp)(path7);
369
- regexpCache.set(path7, regexp);
370
- return regexp;
371
- };
372
- var MicrofrontendConfigClient = class {
373
- constructor(config, opts) {
374
- this.pathCache = {};
375
- this.serialized = config;
376
- if (opts?.removeFlaggedPaths) {
377
- for (const app of Object.values(config.applications)) {
378
- if (app.routing) {
379
- app.routing = app.routing.filter((match) => !match.flag);
380
- }
381
- }
382
- }
383
- this.applications = config.applications;
384
- }
385
- /**
386
- * Create a new `MicrofrontendConfigClient` from a JSON string.
387
- * Config must be passed in to remain framework agnostic
388
- */
389
- static fromEnv(config, opts) {
390
- if (!config) {
391
- throw new Error(
392
- "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
393
- );
394
- }
395
- return new MicrofrontendConfigClient(
396
- JSON.parse(config),
397
- opts
398
- );
399
- }
400
- isEqual(other) {
401
- return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
402
- }
403
- getApplicationNameForPath(path7) {
404
- if (!path7.startsWith("/")) {
405
- throw new Error(`Path must start with a /`);
406
- }
407
- if (this.pathCache[path7]) {
408
- return this.pathCache[path7];
409
- }
410
- const pathname = new URL(path7, "https://example.com").pathname;
411
- for (const [name, application] of Object.entries(this.applications)) {
412
- if (application.routing) {
413
- for (const group of application.routing) {
414
- for (const childPath of group.paths) {
415
- const regexp = getRegexp(childPath);
416
- if (regexp.test(pathname)) {
417
- this.pathCache[path7] = name;
418
- return name;
419
- }
420
- }
421
- }
422
- }
423
- }
424
- const defaultApplication = Object.entries(this.applications).find(
425
- ([, application]) => application.default
426
- );
427
- if (!defaultApplication) {
428
- return null;
429
- }
430
- this.pathCache[path7] = defaultApplication[0];
431
- return defaultApplication[0];
432
- }
433
- serialize() {
434
- return this.serialized;
435
- }
436
- };
437
-
438
362
  // src/config/overrides/constants.ts
439
363
  var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
440
364
  var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
@@ -469,11 +393,12 @@ function parseOverrides(cookies) {
469
393
  }
470
394
 
471
395
  // src/config/microfrontends-config/isomorphic/validation.ts
472
- var import_path_to_regexp2 = require("path-to-regexp");
396
+ var import_path_to_regexp = require("path-to-regexp");
473
397
  var LIST_FORMATTER = new Intl.ListFormat("en", {
474
398
  style: "long",
475
399
  type: "conjunction"
476
400
  });
401
+ var VALID_ASSET_PREFIX_REGEXP = /^[a-z](?:[a-z0-9-]*[a-z0-9])?$/;
477
402
  var validateConfigPaths = (applicationConfigsById) => {
478
403
  if (!applicationConfigsById) {
479
404
  return;
@@ -496,7 +421,7 @@ var validateConfigPaths = (applicationConfigsById) => {
496
421
  } else {
497
422
  pathsByApplicationId.set(path7, {
498
423
  applications: [id],
499
- matcher: (0, import_path_to_regexp2.pathToRegexp)(path7),
424
+ matcher: (0, import_path_to_regexp.pathToRegexp)(path7),
500
425
  applicationId: id
501
426
  });
502
427
  }
@@ -543,7 +468,7 @@ var validateConfigPaths = (applicationConfigsById) => {
543
468
  var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
544
469
  function validatePathExpression(path7) {
545
470
  try {
546
- const tokens = (0, import_path_to_regexp2.parse)(path7);
471
+ const tokens = (0, import_path_to_regexp.parse)(path7);
547
472
  if (/(?<!\\)\{/.test(path7)) {
548
473
  return `Optional paths are not supported: ${path7}`;
549
474
  }
@@ -600,6 +525,22 @@ var validateAppPaths = (name, app) => {
600
525
  }
601
526
  }
602
527
  }
528
+ if (app.assetPrefix) {
529
+ if (!VALID_ASSET_PREFIX_REGEXP.test(app.assetPrefix)) {
530
+ throw new MicrofrontendError(
531
+ `Invalid asset prefix for application "${name}". ${app.assetPrefix} must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`,
532
+ { type: "application", subtype: "invalid_asset_prefix" }
533
+ );
534
+ }
535
+ if (app.assetPrefix !== `vc-ap-${name}` && !app.routing.some(
536
+ (group) => group.paths.includes(`/${app.assetPrefix}/:path*`) && !group.flag
537
+ )) {
538
+ throw new MicrofrontendError(
539
+ `When \`assetPrefix\` is specified, \`/${app.assetPrefix}/:path*\` must be added the routing paths for the application. Changing the asset prefix is not a forwards and backwards compatible change, and the custom asset prefix should be added to \`paths\` and deployed before setting the \`assetPrefix\` field.`,
540
+ { type: "application", subtype: "invalid_asset_prefix" }
541
+ );
542
+ }
543
+ }
603
544
  };
604
545
  var validateConfigDefaultApplication = (applicationConfigsById) => {
605
546
  if (!applicationConfigsById) {
@@ -631,6 +572,15 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
631
572
  }
632
573
  };
633
574
 
575
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
576
+ var import_md5 = __toESM(require("md5"), 1);
577
+ function hashApplicationName(name) {
578
+ if (!name) {
579
+ throw new Error("Application name is required to generate hash");
580
+ }
581
+ return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
582
+ }
583
+
634
584
  // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
635
585
  var PREFIX = "vc-ap";
636
586
  function generateAssetPrefixFromName({
@@ -639,7 +589,7 @@ function generateAssetPrefixFromName({
639
589
  if (!name) {
640
590
  throw new Error("Name is required to generate an asset prefix");
641
591
  }
642
- return `${PREFIX}-${name}`;
592
+ return `${PREFIX}-${hashApplicationName(name)}`;
643
593
  }
644
594
 
645
595
  // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
@@ -799,7 +749,13 @@ var Application = class {
799
749
  return this.default;
800
750
  }
801
751
  getAssetPrefix() {
802
- return generateAssetPrefixFromName({ name: this.name });
752
+ const generatedAssetPrefix = generateAssetPrefixFromName({
753
+ name: this.name
754
+ });
755
+ if ("assetPrefix" in this.serialized) {
756
+ return this.serialized.assetPrefix ?? generatedAssetPrefix;
757
+ }
758
+ return generatedAssetPrefix;
803
759
  }
804
760
  getAutomationBypassEnvVarName() {
805
761
  return generateAutomationBypassEnvVarName({ name: this.name });
@@ -933,7 +889,7 @@ var MicrofrontendConfigIsomorphic = class {
933
889
  );
934
890
  if (!app) {
935
891
  throw new MicrofrontendError(
936
- `Could not find microfrontends configuration for application "${name}"`,
892
+ `Could not find microfrontends configuration for application "${name}". If the name in package.json differs from your Vercel Project name, set the \`packageName\` field for the application in \`microfrontends.json\` to ensure that the configuration can be found locally.`,
937
893
  {
938
894
  type: "application",
939
895
  subtype: "not_found"
@@ -978,23 +934,6 @@ var MicrofrontendConfigIsomorphic = class {
978
934
  toSchemaJson() {
979
935
  return this.serialized.config;
980
936
  }
981
- toClientConfig() {
982
- const applications = Object.fromEntries(
983
- Object.entries(this.childApplications).map(([name, application]) => [
984
- name,
985
- {
986
- default: false,
987
- routing: application.routing
988
- }
989
- ])
990
- );
991
- applications[this.defaultApplication.name] = {
992
- default: true
993
- };
994
- return new MicrofrontendConfigClient({
995
- applications
996
- });
997
- }
998
937
  serialize() {
999
938
  return this.serialized;
1000
939
  }
@@ -1057,8 +996,9 @@ var CONFIGURATION_FILENAMES = [
1057
996
  var configCache = {};
1058
997
  function findPackageWithMicrofrontendsConfig({
1059
998
  repositoryRoot,
1060
- applicationName
999
+ applicationContext
1061
1000
  }) {
1001
+ const applicationName = applicationContext.name;
1062
1002
  try {
1063
1003
  const microfrontendsJsonPaths = import_fast_glob.default.globSync(
1064
1004
  `**/{${CONFIGURATION_FILENAMES.join(",")}}`,
@@ -1100,26 +1040,45 @@ ${matchingPaths.join("\n \u2022 ")}`,
1100
1040
  );
1101
1041
  }
1102
1042
  if (matchingPaths.length === 0) {
1043
+ let additionalErrorMessage = "";
1044
+ if (microfrontendsJsonPaths.length > 0) {
1045
+ if (!applicationContext.projectName) {
1046
+ additionalErrorMessage = `
1047
+
1048
+ If the name in package.json (${applicationContext.packageJsonName}) differs from your Vercel Project name, set the \`packageName\` field for the application in \`microfrontends.json\` to ensure that the configuration can be found locally.`;
1049
+ } else {
1050
+ additionalErrorMessage = `
1051
+
1052
+ Names of applications in \`microfrontends.json\` must match the Vercel Project name (${applicationContext.projectName}).`;
1053
+ }
1054
+ }
1103
1055
  throw new MicrofrontendError(
1104
- `Could not find a \`microfrontends.json\` file in the repository that contains "applications.${applicationName}". If your Vercel Microfrontends configuration is not in this repository, you can use the Vercel CLI to pull the Vercel Microfrontends configuration using the "vercel microfrontends pull" command, or you can specify the path manually using the VC_MICROFRONTENDS_CONFIG environment variable. If you suspect this is thrown in error, please reach out to the Vercel team.`,
1056
+ `Could not find a \`microfrontends.json\` file in the repository that contains the "${applicationName}" application.${additionalErrorMessage}
1057
+
1058
+ If your Vercel Microfrontends configuration is not in this repository, you can use the Vercel CLI to pull the Vercel Microfrontends configuration using the "vercel microfrontends pull" command, or you can specify the path manually using the VC_MICROFRONTENDS_CONFIG environment variable.
1059
+
1060
+ If you suspect this is thrown in error, please reach out to the Vercel team.`,
1105
1061
  { type: "config", subtype: "inference_failed" }
1106
1062
  );
1107
1063
  }
1108
1064
  const [packageJsonPath] = matchingPaths;
1109
1065
  return (0, import_node_path2.dirname)(packageJsonPath);
1110
1066
  } catch (error) {
1067
+ if (error instanceof MicrofrontendError) {
1068
+ throw error;
1069
+ }
1111
1070
  return null;
1112
1071
  }
1113
1072
  }
1114
1073
  function inferMicrofrontendsLocation(opts) {
1115
- const cacheKey = `${opts.repositoryRoot}-${opts.applicationName}`;
1074
+ const cacheKey = `${opts.repositoryRoot}-${opts.applicationContext.name}`;
1116
1075
  if (configCache[cacheKey]) {
1117
1076
  return configCache[cacheKey];
1118
1077
  }
1119
1078
  const result = findPackageWithMicrofrontendsConfig(opts);
1120
1079
  if (!result) {
1121
1080
  throw new MicrofrontendError(
1122
- `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationName}" starting in directory "${opts.repositoryRoot}".`,
1081
+ `Could not infer the location of the \`microfrontends.json\` file for application "${opts.applicationContext.name}" starting in directory "${opts.repositoryRoot}".`,
1123
1082
  { type: "config", subtype: "inference_failed" }
1124
1083
  );
1125
1084
  }
@@ -1195,8 +1154,31 @@ function getApplicationContext(opts) {
1195
1154
  if (opts?.appName) {
1196
1155
  return { name: opts.appName };
1197
1156
  }
1157
+ if (process.env.VERCEL_PROJECT_NAME) {
1158
+ return {
1159
+ name: process.env.VERCEL_PROJECT_NAME,
1160
+ projectName: process.env.VERCEL_PROJECT_NAME
1161
+ };
1162
+ }
1198
1163
  if (process.env.NX_TASK_TARGET_PROJECT) {
1199
- return { name: process.env.NX_TASK_TARGET_PROJECT };
1164
+ return {
1165
+ name: process.env.NX_TASK_TARGET_PROJECT,
1166
+ packageJsonName: process.env.NX_TASK_TARGET_PROJECT
1167
+ };
1168
+ }
1169
+ try {
1170
+ const vercelProjectJsonPath = import_node_fs6.default.readFileSync(
1171
+ import_node_path6.default.join(opts?.packageRoot || ".", ".vercel", "project.json"),
1172
+ "utf-8"
1173
+ );
1174
+ const projectJson = JSON.parse(vercelProjectJsonPath);
1175
+ if (projectJson.projectName) {
1176
+ return {
1177
+ name: projectJson.projectName,
1178
+ projectName: projectJson.projectName
1179
+ };
1180
+ }
1181
+ } catch (_) {
1200
1182
  }
1201
1183
  try {
1202
1184
  const packageJsonString = import_node_fs6.default.readFileSync(
@@ -1214,7 +1196,7 @@ function getApplicationContext(opts) {
1214
1196
  }
1215
1197
  );
1216
1198
  }
1217
- return { name: packageJson.name };
1199
+ return { name: packageJson.name, packageJsonName: packageJson.name };
1218
1200
  } catch (err) {
1219
1201
  throw MicrofrontendError.handle(err, {
1220
1202
  fileName: "package.json"
@@ -1354,6 +1336,10 @@ var schema_default = {
1354
1336
  routing: {
1355
1337
  $ref: "#/definitions/Routing",
1356
1338
  description: "Groups of path expressions that are routed to this application."
1339
+ },
1340
+ assetPrefix: {
1341
+ type: "string",
1342
+ description: "The name of the asset prefix to use instead of the auto-generated name.\n\nThe asset prefix is used to prefix all paths to static assets, such as JS, CSS, or images that are served by a specific application. It is necessary to ensure there are no conflicts with other applications on the same domain.\n\nAn auto-generated asset prefix of the form `vc-ap-<hash>` is used when this field is not provided.\n\nWhen this field is provided, `/${assetPrefix}/:path*` must also be added to the list of paths in the `routing` field. Changing the asset prefix after a microfrontend application has already been deployed is not a forwards and backwards compatible change, and the asset prefix should be added to the `routing` field and deployed before setting the `assetPrefix` field."
1357
1343
  }
1358
1344
  },
1359
1345
  required: [
@@ -1579,7 +1565,7 @@ var MicrofrontendsServer = class {
1579
1565
  }
1580
1566
  try {
1581
1567
  const packageRoot = findPackageRoot(directory);
1582
- const { name: appName } = getApplicationContext({ packageRoot });
1568
+ const applicationContext = getApplicationContext({ packageRoot });
1583
1569
  const maybeConfig = findConfig({ dir: packageRoot });
1584
1570
  if (maybeConfig) {
1585
1571
  return MicrofrontendsServer.fromFile({
@@ -1613,7 +1599,7 @@ var MicrofrontendsServer = class {
1613
1599
  if (isMonorepo2) {
1614
1600
  const defaultPackage = inferMicrofrontendsLocation({
1615
1601
  repositoryRoot,
1616
- applicationName: appName
1602
+ applicationContext
1617
1603
  });
1618
1604
  const maybeConfigFromDefault = findConfig({ dir: defaultPackage });
1619
1605
  if (maybeConfigFromDefault) {
@@ -2145,7 +2131,7 @@ var ProxyRequestRouter = class {
2145
2131
  }
2146
2132
  for (const group of application.routing) {
2147
2133
  for (const childPath of group.paths) {
2148
- const regexp = (0, import_path_to_regexp3.pathToRegexp)(childPath);
2134
+ const regexp = (0, import_path_to_regexp2.pathToRegexp)(childPath);
2149
2135
  if (regexp.test(url.pathname)) {
2150
2136
  mfeDebug(
2151
2137
  `routing ${path7} to '${target.application}' at ${target.hostname}`
@@ -2188,7 +2174,7 @@ var ProxyRequestRouter = class {
2188
2174
  const pathname = url.pathname;
2189
2175
  const target = this.getApplicationTarget(app);
2190
2176
  for (const rewrite of rewrites) {
2191
- if ((0, import_path_to_regexp3.pathToRegexp)(`/${app.getAssetPrefix()}${rewrite}`).test(pathname)) {
2177
+ if ((0, import_path_to_regexp2.pathToRegexp)(`/${app.getAssetPrefix()}${rewrite}`).test(pathname)) {
2192
2178
  mfeDebug(
2193
2179
  `routing ${pathname} to '${target.application}' at ${target.hostname}`
2194
2180
  );
@@ -2205,8 +2191,8 @@ var ProxyRequestRouter = class {
2205
2191
  referer = void 0,
2206
2192
  applications
2207
2193
  }) {
2208
- const isStackFrame = (0, import_path_to_regexp3.pathToRegexp)("/__nextjs_original-stack-frame").test(url.pathname) || // Plural form was introduced in https://github.com/vercel/next.js/pull/75557
2209
- (0, import_path_to_regexp3.pathToRegexp)("/__nextjs_original-stack-frames").test(url.pathname);
2194
+ const isStackFrame = (0, import_path_to_regexp2.pathToRegexp)("/__nextjs_original-stack-frame").test(url.pathname) || // Plural form was introduced in https://github.com/vercel/next.js/pull/75557
2195
+ (0, import_path_to_regexp2.pathToRegexp)("/__nextjs_original-stack-frames").test(url.pathname);
2210
2196
  if (!referer || !isStackFrame) {
2211
2197
  return null;
2212
2198
  }
@@ -2226,7 +2212,7 @@ var ProxyRequestRouter = class {
2226
2212
  } : null;
2227
2213
  }
2228
2214
  checkNextSourceMap({ url }) {
2229
- const isSourceMap = (0, import_path_to_regexp3.pathToRegexp)("/__nextjs_source-map").test(url.pathname);
2215
+ const isSourceMap = (0, import_path_to_regexp2.pathToRegexp)("/__nextjs_source-map").test(url.pathname);
2230
2216
  if (!isSourceMap) {
2231
2217
  return null;
2232
2218
  }
@@ -2246,7 +2232,7 @@ var ProxyRequestRouter = class {
2246
2232
  url,
2247
2233
  applications
2248
2234
  }) {
2249
- const isNextImage = (0, import_path_to_regexp3.pathToRegexp)("/_next/image").test(url.pathname);
2235
+ const isNextImage = (0, import_path_to_regexp2.pathToRegexp)("/_next/image").test(url.pathname);
2250
2236
  if (!isNextImage) {
2251
2237
  return null;
2252
2238
  }
package/dist/config.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
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
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/config/microfrontends-config/isomorphic/index.ts
@@ -134,84 +144,6 @@ function isDefaultApp(a) {
134
144
  return !("routing" in a);
135
145
  }
136
146
 
137
- // src/config/microfrontends-config/client/index.ts
138
- var import_path_to_regexp = require("path-to-regexp");
139
- var regexpCache = /* @__PURE__ */ new Map();
140
- var getRegexp = (path) => {
141
- const existing = regexpCache.get(path);
142
- if (existing) {
143
- return existing;
144
- }
145
- const regexp = (0, import_path_to_regexp.pathToRegexp)(path);
146
- regexpCache.set(path, regexp);
147
- return regexp;
148
- };
149
- var MicrofrontendConfigClient = class {
150
- constructor(config, opts) {
151
- this.pathCache = {};
152
- this.serialized = config;
153
- if (opts?.removeFlaggedPaths) {
154
- for (const app of Object.values(config.applications)) {
155
- if (app.routing) {
156
- app.routing = app.routing.filter((match) => !match.flag);
157
- }
158
- }
159
- }
160
- this.applications = config.applications;
161
- }
162
- /**
163
- * Create a new `MicrofrontendConfigClient` from a JSON string.
164
- * Config must be passed in to remain framework agnostic
165
- */
166
- static fromEnv(config, opts) {
167
- if (!config) {
168
- throw new Error(
169
- "Could not construct MicrofrontendConfigClient: configuration is empty or undefined. Did you set up your application with `withMicrofrontends`?"
170
- );
171
- }
172
- return new MicrofrontendConfigClient(
173
- JSON.parse(config),
174
- opts
175
- );
176
- }
177
- isEqual(other) {
178
- return this === other || JSON.stringify(this.applications) === JSON.stringify(other.applications);
179
- }
180
- getApplicationNameForPath(path) {
181
- if (!path.startsWith("/")) {
182
- throw new Error(`Path must start with a /`);
183
- }
184
- if (this.pathCache[path]) {
185
- return this.pathCache[path];
186
- }
187
- const pathname = new URL(path, "https://example.com").pathname;
188
- for (const [name, application] of Object.entries(this.applications)) {
189
- if (application.routing) {
190
- for (const group of application.routing) {
191
- for (const childPath of group.paths) {
192
- const regexp = getRegexp(childPath);
193
- if (regexp.test(pathname)) {
194
- this.pathCache[path] = name;
195
- return name;
196
- }
197
- }
198
- }
199
- }
200
- }
201
- const defaultApplication = Object.entries(this.applications).find(
202
- ([, application]) => application.default
203
- );
204
- if (!defaultApplication) {
205
- return null;
206
- }
207
- this.pathCache[path] = defaultApplication[0];
208
- return defaultApplication[0];
209
- }
210
- serialize() {
211
- return this.serialized;
212
- }
213
- };
214
-
215
147
  // src/config/overrides/constants.ts
216
148
  var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
217
149
  var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
@@ -246,11 +178,12 @@ function parseOverrides(cookies) {
246
178
  }
247
179
 
248
180
  // src/config/microfrontends-config/isomorphic/validation.ts
249
- var import_path_to_regexp2 = require("path-to-regexp");
181
+ var import_path_to_regexp = require("path-to-regexp");
250
182
  var LIST_FORMATTER = new Intl.ListFormat("en", {
251
183
  style: "long",
252
184
  type: "conjunction"
253
185
  });
186
+ var VALID_ASSET_PREFIX_REGEXP = /^[a-z](?:[a-z0-9-]*[a-z0-9])?$/;
254
187
  var validateConfigPaths = (applicationConfigsById) => {
255
188
  if (!applicationConfigsById) {
256
189
  return;
@@ -273,7 +206,7 @@ var validateConfigPaths = (applicationConfigsById) => {
273
206
  } else {
274
207
  pathsByApplicationId.set(path, {
275
208
  applications: [id],
276
- matcher: (0, import_path_to_regexp2.pathToRegexp)(path),
209
+ matcher: (0, import_path_to_regexp.pathToRegexp)(path),
277
210
  applicationId: id
278
211
  });
279
212
  }
@@ -320,7 +253,7 @@ var validateConfigPaths = (applicationConfigsById) => {
320
253
  var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
321
254
  function validatePathExpression(path) {
322
255
  try {
323
- const tokens = (0, import_path_to_regexp2.parse)(path);
256
+ const tokens = (0, import_path_to_regexp.parse)(path);
324
257
  if (/(?<!\\)\{/.test(path)) {
325
258
  return `Optional paths are not supported: ${path}`;
326
259
  }
@@ -377,6 +310,22 @@ var validateAppPaths = (name, app) => {
377
310
  }
378
311
  }
379
312
  }
313
+ if (app.assetPrefix) {
314
+ if (!VALID_ASSET_PREFIX_REGEXP.test(app.assetPrefix)) {
315
+ throw new MicrofrontendError(
316
+ `Invalid asset prefix for application "${name}". ${app.assetPrefix} must start with a lowercase letter and contain only lowercase letters, numbers, and hyphens.`,
317
+ { type: "application", subtype: "invalid_asset_prefix" }
318
+ );
319
+ }
320
+ if (app.assetPrefix !== `vc-ap-${name}` && !app.routing.some(
321
+ (group) => group.paths.includes(`/${app.assetPrefix}/:path*`) && !group.flag
322
+ )) {
323
+ throw new MicrofrontendError(
324
+ `When \`assetPrefix\` is specified, \`/${app.assetPrefix}/:path*\` must be added the routing paths for the application. Changing the asset prefix is not a forwards and backwards compatible change, and the custom asset prefix should be added to \`paths\` and deployed before setting the \`assetPrefix\` field.`,
325
+ { type: "application", subtype: "invalid_asset_prefix" }
326
+ );
327
+ }
328
+ }
380
329
  };
381
330
  var validateConfigDefaultApplication = (applicationConfigsById) => {
382
331
  if (!applicationConfigsById) {
@@ -408,6 +357,15 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
408
357
  }
409
358
  };
410
359
 
360
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
361
+ var import_md5 = __toESM(require("md5"), 1);
362
+ function hashApplicationName(name) {
363
+ if (!name) {
364
+ throw new Error("Application name is required to generate hash");
365
+ }
366
+ return (0, import_md5.default)(name).substring(0, 6).padStart(6, "0");
367
+ }
368
+
411
369
  // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
412
370
  var PREFIX = "vc-ap";
413
371
  function generateAssetPrefixFromName({
@@ -416,7 +374,7 @@ function generateAssetPrefixFromName({
416
374
  if (!name) {
417
375
  throw new Error("Name is required to generate an asset prefix");
418
376
  }
419
- return `${PREFIX}-${name}`;
377
+ return `${PREFIX}-${hashApplicationName(name)}`;
420
378
  }
421
379
 
422
380
  // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
@@ -576,7 +534,13 @@ var Application = class {
576
534
  return this.default;
577
535
  }
578
536
  getAssetPrefix() {
579
- return generateAssetPrefixFromName({ name: this.name });
537
+ const generatedAssetPrefix = generateAssetPrefixFromName({
538
+ name: this.name
539
+ });
540
+ if ("assetPrefix" in this.serialized) {
541
+ return this.serialized.assetPrefix ?? generatedAssetPrefix;
542
+ }
543
+ return generatedAssetPrefix;
580
544
  }
581
545
  getAutomationBypassEnvVarName() {
582
546
  return generateAutomationBypassEnvVarName({ name: this.name });
@@ -710,7 +674,7 @@ var MicrofrontendConfigIsomorphic = class {
710
674
  );
711
675
  if (!app) {
712
676
  throw new MicrofrontendError(
713
- `Could not find microfrontends configuration for application "${name}"`,
677
+ `Could not find microfrontends configuration for application "${name}". If the name in package.json differs from your Vercel Project name, set the \`packageName\` field for the application in \`microfrontends.json\` to ensure that the configuration can be found locally.`,
714
678
  {
715
679
  type: "application",
716
680
  subtype: "not_found"
@@ -755,23 +719,6 @@ var MicrofrontendConfigIsomorphic = class {
755
719
  toSchemaJson() {
756
720
  return this.serialized.config;
757
721
  }
758
- toClientConfig() {
759
- const applications = Object.fromEntries(
760
- Object.entries(this.childApplications).map(([name, application]) => [
761
- name,
762
- {
763
- default: false,
764
- routing: application.routing
765
- }
766
- ])
767
- );
768
- applications[this.defaultApplication.name] = {
769
- default: true
770
- };
771
- return new MicrofrontendConfigClient({
772
- applications
773
- });
774
- }
775
722
  serialize() {
776
723
  return this.serialized;
777
724
  }