@vercel/microfrontends 0.18.0 → 0.19.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 (47) hide show
  1. package/dist/bin/cli.cjs +151 -71
  2. package/dist/config.cjs +102 -56
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.d.ts +4 -4
  5. package/dist/config.js +102 -56
  6. package/dist/config.js.map +1 -1
  7. package/dist/{index-24024799.d.ts → index-09b1ddf9.d.ts} +16 -19
  8. package/dist/{index-ef8657e6.d.ts → index-2f78c0ca.d.ts} +44 -7
  9. package/dist/microfrontends/server.cjs +147 -67
  10. package/dist/microfrontends/server.cjs.map +1 -1
  11. package/dist/microfrontends/server.d.ts +4 -4
  12. package/dist/microfrontends/server.js +147 -67
  13. package/dist/microfrontends/server.js.map +1 -1
  14. package/dist/microfrontends.cjs +103 -57
  15. package/dist/microfrontends.cjs.map +1 -1
  16. package/dist/microfrontends.d.ts +4 -4
  17. package/dist/microfrontends.js +103 -57
  18. package/dist/microfrontends.js.map +1 -1
  19. package/dist/next/config.cjs +164 -79
  20. package/dist/next/config.cjs.map +1 -1
  21. package/dist/next/config.js +164 -79
  22. package/dist/next/config.js.map +1 -1
  23. package/dist/next/endpoints.d.ts +2 -2
  24. package/dist/next/middleware.cjs +120 -75
  25. package/dist/next/middleware.cjs.map +1 -1
  26. package/dist/next/middleware.js +120 -75
  27. package/dist/next/middleware.js.map +1 -1
  28. package/dist/next/testing.cjs +109 -62
  29. package/dist/next/testing.cjs.map +1 -1
  30. package/dist/next/testing.d.ts +4 -4
  31. package/dist/next/testing.js +109 -62
  32. package/dist/next/testing.js.map +1 -1
  33. package/dist/overrides.d.ts +3 -3
  34. package/dist/schema.d.ts +1 -1
  35. package/dist/{types-089498fd.d.ts → types-4ef2bddb.d.ts} +1 -1
  36. package/dist/{types-9f161cec.d.ts → types-b6d38aea.d.ts} +1 -1
  37. package/dist/utils/mfe-port.cjs +148 -68
  38. package/dist/utils/mfe-port.cjs.map +1 -1
  39. package/dist/utils/mfe-port.js +148 -68
  40. package/dist/utils/mfe-port.js.map +1 -1
  41. package/dist/validation.cjs +43 -9
  42. package/dist/validation.cjs.map +1 -1
  43. package/dist/validation.d.ts +1 -1
  44. package/dist/validation.js +43 -9
  45. package/dist/validation.js.map +1 -1
  46. package/package.json +1 -1
  47. package/schema/schema.json +71 -40
@@ -1,8 +1,8 @@
1
1
  import { MiddlewareConfig, NextRequest, NextFetchEvent } from 'next/server';
2
- import { M as MicrofrontendConfigIsomorphic } from '../index-24024799.js';
3
- import '../index-ef8657e6.js';
4
- import '../types-089498fd.js';
5
- import '../types-9f161cec.js';
2
+ import { M as MicrofrontendConfigIsomorphic } from '../index-09b1ddf9.js';
3
+ import '../index-2f78c0ca.js';
4
+ import '../types-4ef2bddb.js';
5
+ import '../types-b6d38aea.js';
6
6
 
7
7
  /** Replaces path wildcards (if they exist) with synthesized paths. */
8
8
  declare function expandWildcards(path: string): string[];
@@ -237,7 +237,8 @@ var validateConfigPaths = (applicationConfigsById) => {
237
237
  if (isDefaultApp(app)) {
238
238
  continue;
239
239
  }
240
- for (const pathMatch of app.routing) {
240
+ const childApp = app;
241
+ for (const pathMatch of childApp.routing) {
241
242
  for (const path of pathMatch.paths) {
242
243
  const maybeError = validatePathExpression(path);
243
244
  if (maybeError) {
@@ -257,33 +258,31 @@ var validateConfigPaths = (applicationConfigsById) => {
257
258
  }
258
259
  }
259
260
  const entries = Array.from(pathsByApplicationId.entries());
260
- entries.forEach(([path, { applications: ids, matcher, applicationId }]) => {
261
+ for (const [path, { applications: ids, matcher, applicationId }] of entries) {
261
262
  if (ids.length > 1) {
262
263
  errors.push(
263
264
  `Duplicate path "${path}" for applications "${ids.join(", ")}"`
264
265
  );
265
266
  }
266
- entries.forEach(
267
- ([
268
- matchPath,
269
- { applications: matchIds, applicationId: matchApplicationId }
270
- ]) => {
271
- if (path === matchPath) {
272
- return;
273
- }
274
- if (applicationId === matchApplicationId) {
275
- return;
276
- }
277
- if (matcher.test(matchPath)) {
278
- const source = `"${path}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
279
- const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
280
- errors.push(
281
- `Overlapping path detected between ${source} and ${destination}`
282
- );
283
- }
267
+ for (const [
268
+ matchPath,
269
+ { applications: matchIds, applicationId: matchApplicationId }
270
+ ] of entries) {
271
+ if (path === matchPath) {
272
+ continue;
284
273
  }
285
- );
286
- });
274
+ if (applicationId === matchApplicationId) {
275
+ continue;
276
+ }
277
+ if (matcher.test(matchPath)) {
278
+ const source = `"${path}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
279
+ const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
280
+ errors.push(
281
+ `Overlapping path detected between ${source} and ${destination}`
282
+ );
283
+ }
284
+ }
285
+ }
287
286
  if (errors.length) {
288
287
  throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
289
288
  type: "config",
@@ -352,7 +351,7 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
352
351
  const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
353
352
  if (numApplicationsWithoutRouting === 0) {
354
353
  throw new MicrofrontendError(
355
- `No default application found. At least one application needs to be the default by omitting routing.`,
354
+ "No default application found. At least one application needs to be the default by omitting routing.",
356
355
  { type: "config", subtype: "no_default_application" }
357
356
  );
358
357
  }
@@ -398,47 +397,80 @@ function generatePortFromName({
398
397
  // src/config/microfrontends-config/isomorphic/host.ts
399
398
  var Host = class {
400
399
  constructor(hostConfig, options) {
401
- const { protocol = "https", host, port } = hostConfig;
402
- this.protocol = protocol;
403
- this.host = host;
404
- this.port = Host.getPort({ port, protocol: this.protocol });
400
+ if (typeof hostConfig === "string") {
401
+ ({
402
+ protocol: this.protocol,
403
+ host: this.host,
404
+ port: this.port
405
+ } = Host.parseUrl(hostConfig));
406
+ } else {
407
+ const { protocol = "https", host, port } = hostConfig;
408
+ this.protocol = protocol;
409
+ this.host = host;
410
+ this.port = port;
411
+ }
405
412
  this.local = options?.isLocal;
406
413
  }
407
- isLocal() {
408
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
409
- }
410
- static getPort({
411
- protocol,
412
- port
413
- }) {
414
- if (!port) {
415
- if (protocol === "http") {
416
- return 80;
417
- }
418
- return 443;
414
+ static parseUrl(url) {
415
+ let hostToParse = url;
416
+ if (!/^https?:\/\//.exec(hostToParse)) {
417
+ hostToParse = `https://${hostToParse}`;
419
418
  }
420
- return port;
419
+ const parsed = new URL(hostToParse);
420
+ if (!parsed.hostname) {
421
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
422
+ }
423
+ if (parsed.hash) {
424
+ throw new Error(
425
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
426
+ );
427
+ }
428
+ if (parsed.username || parsed.password) {
429
+ throw new Error(
430
+ Host.getMicrofrontendsError(
431
+ url,
432
+ "cannot have authentication credentials (username and/or password)"
433
+ )
434
+ );
435
+ }
436
+ if (parsed.pathname !== "/") {
437
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
438
+ }
439
+ if (parsed.search) {
440
+ throw new Error(
441
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
442
+ );
443
+ }
444
+ const protocol = parsed.protocol.slice(0, -1);
445
+ return {
446
+ protocol,
447
+ host: parsed.hostname,
448
+ port: parsed.port ? Number.parseInt(parsed.port) : void 0
449
+ };
421
450
  }
422
- isDefaultPort() {
423
- return this.port === Host.getPort({ protocol: this.protocol });
451
+ static getMicrofrontendsError(url, message) {
452
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
424
453
  }
425
- toString(opts = {}) {
426
- const url = this.toUrl(opts);
454
+ isLocal() {
455
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
456
+ }
457
+ toString() {
458
+ const url = this.toUrl();
427
459
  return url.toString().replace(/\/$/, "");
428
460
  }
429
- toUrl(opts = {}) {
430
- const { includeDefaultPort } = opts;
431
- const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
461
+ toUrl() {
462
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
432
463
  return new URL(url);
433
464
  }
434
465
  };
435
466
  var LocalHost = class extends Host {
436
467
  constructor({
437
468
  appName,
469
+ localPort,
438
470
  ...hostConfig
439
471
  }) {
440
472
  const host = hostConfig.host ?? "localhost";
441
- const port = hostConfig.port ?? generatePortFromName({ name: appName });
473
+ const port = localPort ?? hostConfig.port ?? generatePortFromName({ name: appName });
442
474
  const protocol = hostConfig.protocol ?? "http";
443
475
  super({ protocol, host, port });
444
476
  }
@@ -455,12 +487,17 @@ var Application = class {
455
487
  this.development = {
456
488
  local: new LocalHost({
457
489
  appName: name,
490
+ localPort: app.development?.localPort,
458
491
  ...app.development?.local
459
492
  }),
460
493
  fallback: app.development?.fallback ? new Host(app.development.fallback) : void 0
461
494
  };
462
- this.production = app.production ? new Host(app.production) : void 0;
463
- this.vercel = app.vercel;
495
+ if (app.development?.fallback) {
496
+ this.fallback = new Host(app.development.fallback);
497
+ } else if (app.production) {
498
+ this.fallback = new Host(app.production);
499
+ }
500
+ this.projectId = app.projectId ?? app.vercel?.projectId;
464
501
  this.overrides = overrides?.environment ? {
465
502
  environment: new Host(overrides.environment)
466
503
  } : void 0;
@@ -488,7 +525,16 @@ var DefaultApplication = class extends Application {
488
525
  isDefault: true
489
526
  });
490
527
  this.default = true;
491
- this.production = new Host(app.production);
528
+ const fallbackHost = app.development?.fallback ?? app.production;
529
+ if (fallbackHost === void 0) {
530
+ throw new Error(
531
+ "`app.production` or `app.development.fallback` must be set in the default application in microfrontends.json."
532
+ );
533
+ }
534
+ this.fallback = new Host(fallbackHost);
535
+ if (app.production) {
536
+ this.production = new Host(app.production);
537
+ }
492
538
  }
493
539
  getAssetPrefix() {
494
540
  return "";
@@ -558,7 +604,7 @@ var MicrofrontendConfigIsomorphic = class {
558
604
  }
559
605
  if (isMainConfig(config) && !this.defaultApplication) {
560
606
  throw new MicrofrontendError(
561
- `Could not find default application in microfrontends configuration`,
607
+ "Could not find default application in microfrontends configuration",
562
608
  {
563
609
  type: "application",
564
610
  subtype: "not_found"
@@ -634,11 +680,11 @@ var MicrofrontendConfigIsomorphic = class {
634
680
  return app;
635
681
  }
636
682
  getApplicationByProjectId(projectId) {
637
- if (this.defaultApplication?.vercel?.projectId === projectId) {
683
+ if (this.defaultApplication?.projectId === projectId) {
638
684
  return this.defaultApplication;
639
685
  }
640
686
  return Object.values(this.childApplications).find(
641
- (app) => app.vercel?.projectId === projectId
687
+ (app) => app.projectId === projectId
642
688
  );
643
689
  }
644
690
  /**
@@ -648,7 +694,7 @@ var MicrofrontendConfigIsomorphic = class {
648
694
  getDefaultApplication() {
649
695
  if (!this.defaultApplication) {
650
696
  throw new MicrofrontendError(
651
- `Could not find default application in microfrontends configuration`,
697
+ "Could not find default application in microfrontends configuration",
652
698
  {
653
699
  type: "application",
654
700
  subtype: "not_found"
@@ -661,7 +707,7 @@ var MicrofrontendConfigIsomorphic = class {
661
707
  * Returns the configured port for the local proxy
662
708
  */
663
709
  getLocalProxyPort() {
664
- return this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
710
+ return this.config.options?.localProxyPort ?? this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
665
711
  }
666
712
  /**
667
713
  * Serializes the class back to the Schema type.
@@ -706,12 +752,10 @@ function getDomainFromEnvironment({
706
752
  if (mfeProjects.length === 0) {
707
753
  throw new Error("Missing related microfrontends project information");
708
754
  }
709
- if (!app.vercel?.projectId) {
755
+ if (!app.projectId) {
710
756
  throw new Error(`Missing applications[${app.name}].vercel.projectId`);
711
757
  }
712
- const vercelProject = mfeProjects.find(
713
- (p) => p.project.id === app.vercel?.projectId
714
- );
758
+ const vercelProject = mfeProjects.find((p) => p.project.id === app.projectId);
715
759
  if (!vercelProject) {
716
760
  throw new Error(
717
761
  `Missing related microfrontends project information for application "${app.name}"`
@@ -783,7 +827,7 @@ function getExpectedDomainForApp(mfConfig, appName, env) {
783
827
  const target = env;
784
828
  return getDomainFromEnvironment({ app, target });
785
829
  }
786
- return defaultApp.production.toString();
830
+ return defaultApp.fallback.toString();
787
831
  }
788
832
  function getAllMultiZonesPaths(mfConfig) {
789
833
  return mfConfig.getChildApplications().flatMap((app) => {
@@ -830,8 +874,11 @@ function validateMiddlewareConfig(middlewareConfig, microfrontendConfigOrPath, e
830
874
  for (const path of aMatch.paths) {
831
875
  const pathsToTest = expandWildcards(path);
832
876
  for (const testPath of pathsToTest) {
833
- const productionHost = microfrontendConfig.getDefaultApplication().production.host;
877
+ const productionHost = microfrontendConfig.getDefaultApplication().production?.host;
834
878
  const pathForDisplay = `${testPath}${path === testPath ? "" : ` (synthesized from ${path})`}`;
879
+ if (!productionHost) {
880
+ continue;
881
+ }
835
882
  if (!urlMatches(middlewareConfig, testPath, "test.nonproduction.host")) {
836
883
  errors.push(
837
884
  `Matcher misconfigured for ${pathForDisplay}. This path should have matched the middleware config on a non-production host, but did not. Microfrontends require a middleware config matcher that matches on this host everywhere but in production. That can be configured with a configuration like this: