@vercel/microfrontends 2.2.1 → 2.3.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 +25 -0
  2. package/cli/index.cjs +0 -1
  3. package/dist/bin/cli.cjs +486 -397
  4. package/dist/config.cjs +193 -171
  5. package/dist/config.cjs.map +1 -1
  6. package/dist/config.d.ts +3 -2
  7. package/dist/config.js +194 -172
  8. package/dist/config.js.map +1 -1
  9. package/dist/experimental/sveltekit.cjs +585 -513
  10. package/dist/experimental/sveltekit.cjs.map +1 -1
  11. package/dist/experimental/sveltekit.js +591 -519
  12. package/dist/experimental/sveltekit.js.map +1 -1
  13. package/dist/experimental/vite.cjs +607 -535
  14. package/dist/experimental/vite.cjs.map +1 -1
  15. package/dist/experimental/vite.js +616 -544
  16. package/dist/experimental/vite.js.map +1 -1
  17. package/dist/microfrontends/server.cjs +603 -531
  18. package/dist/microfrontends/server.cjs.map +1 -1
  19. package/dist/microfrontends/server.d.ts +2 -2
  20. package/dist/microfrontends/server.js +609 -537
  21. package/dist/microfrontends/server.js.map +1 -1
  22. package/dist/microfrontends/utils.cjs +101 -50
  23. package/dist/microfrontends/utils.cjs.map +1 -1
  24. package/dist/microfrontends/utils.d.ts +4 -4
  25. package/dist/microfrontends/utils.js +102 -51
  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.d.ts +8 -8
  30. package/dist/next/client.js +1 -1
  31. package/dist/next/client.js.map +1 -1
  32. package/dist/next/config.cjs +725 -649
  33. package/dist/next/config.cjs.map +1 -1
  34. package/dist/next/config.js +722 -646
  35. package/dist/next/config.js.map +1 -1
  36. package/dist/next/middleware.cjs +246 -224
  37. package/dist/next/middleware.cjs.map +1 -1
  38. package/dist/next/middleware.js +247 -225
  39. package/dist/next/middleware.js.map +1 -1
  40. package/dist/next/testing.cjs +194 -172
  41. package/dist/next/testing.cjs.map +1 -1
  42. package/dist/next/testing.d.ts +1 -1
  43. package/dist/next/testing.js +195 -173
  44. package/dist/next/testing.js.map +1 -1
  45. package/dist/overrides.cjs +5 -5
  46. package/dist/overrides.cjs.map +1 -1
  47. package/dist/overrides.d.ts +9 -9
  48. package/dist/overrides.js +5 -5
  49. package/dist/overrides.js.map +1 -1
  50. package/dist/utils/mfe-port.cjs +622 -535
  51. package/dist/utils/mfe-port.cjs.map +1 -1
  52. package/dist/utils/mfe-port.d.ts +9 -1
  53. package/dist/utils/mfe-port.js +634 -548
  54. package/dist/utils/mfe-port.js.map +1 -1
  55. package/dist/validation.cjs +8 -24
  56. package/dist/validation.cjs.map +1 -1
  57. package/dist/validation.js +8 -24
  58. package/dist/validation.js.map +1 -1
  59. package/package.json +5 -7
package/dist/config.js CHANGED
@@ -93,23 +93,6 @@ var MicrofrontendError = class extends Error {
93
93
  }
94
94
  };
95
95
 
96
- // src/config/microfrontends-config/utils/get-config-from-env.ts
97
- function getConfigStringFromEnv() {
98
- const config = process.env.MFE_CONFIG;
99
- if (!config) {
100
- throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
101
- type: "config",
102
- subtype: "not_found_in_env"
103
- });
104
- }
105
- return config;
106
- }
107
-
108
- // src/config/schema/utils/is-default-app.ts
109
- function isDefaultApp(a) {
110
- return !("routing" in a);
111
- }
112
-
113
96
  // src/config/overrides/constants.ts
114
97
  var OVERRIDES_COOKIE_PREFIX = "vercel-micro-frontends-override";
115
98
  var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX}:env:`;
@@ -143,6 +126,11 @@ function parseOverrides(cookies) {
143
126
  return overridesConfig;
144
127
  }
145
128
 
129
+ // src/config/schema/utils/is-default-app.ts
130
+ function isDefaultApp(a) {
131
+ return !("routing" in a);
132
+ }
133
+
146
134
  // src/config/microfrontends-config/client/index.ts
147
135
  import { pathToRegexp } from "path-to-regexp";
148
136
  var regexpCache = /* @__PURE__ */ new Map();
@@ -241,8 +229,185 @@ var MicrofrontendConfigClient = class {
241
229
  }
242
230
  };
243
231
 
232
+ // src/config/microfrontends-config/utils/get-config-from-env.ts
233
+ function getConfigStringFromEnv() {
234
+ const config = process.env.MFE_CONFIG;
235
+ if (!config) {
236
+ throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
237
+ type: "config",
238
+ subtype: "not_found_in_env"
239
+ });
240
+ }
241
+ return config;
242
+ }
243
+
244
+ // src/config/microfrontends-config/isomorphic/constants.ts
245
+ var DEFAULT_LOCAL_PROXY_PORT = 3024;
246
+ var MFE_APP_PORT_ENV = "MFE_APP_PORT";
247
+ var MFE_LOCAL_PROXY_PORT_ENV = "MFE_LOCAL_PROXY_PORT";
248
+
249
+ // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
250
+ function generatePortFromName({
251
+ name,
252
+ minPort = 3e3,
253
+ maxPort = 8e3
254
+ }) {
255
+ if (!name) {
256
+ throw new Error("Name is required to generate a port");
257
+ }
258
+ let hash = 0;
259
+ for (let i = 0; i < name.length; i++) {
260
+ hash = (hash << 5) - hash + name.charCodeAt(i);
261
+ hash |= 0;
262
+ }
263
+ hash = Math.abs(hash);
264
+ const range = maxPort - minPort;
265
+ const port = minPort + hash % range;
266
+ return port;
267
+ }
268
+
269
+ // src/config/microfrontends-config/isomorphic/host.ts
270
+ var Host = class {
271
+ constructor(hostConfig, options) {
272
+ if (typeof hostConfig === "string") {
273
+ ({
274
+ protocol: this.protocol,
275
+ host: this.host,
276
+ port: this.port
277
+ } = Host.parseUrl(hostConfig));
278
+ } else {
279
+ const { protocol = "https", host, port } = hostConfig;
280
+ this.protocol = protocol;
281
+ this.host = host;
282
+ this.port = port;
283
+ }
284
+ this.local = options?.isLocal;
285
+ }
286
+ static parseUrl(url, defaultProtocol = "https") {
287
+ let hostToParse = url;
288
+ if (!/^https?:\/\//.exec(hostToParse)) {
289
+ hostToParse = `${defaultProtocol}://${hostToParse}`;
290
+ }
291
+ const parsed = new URL(hostToParse);
292
+ if (!parsed.hostname) {
293
+ throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
294
+ }
295
+ if (parsed.hash) {
296
+ throw new Error(
297
+ Host.getMicrofrontendsError(url, "cannot have a fragment")
298
+ );
299
+ }
300
+ if (parsed.username || parsed.password) {
301
+ throw new Error(
302
+ Host.getMicrofrontendsError(
303
+ url,
304
+ "cannot have authentication credentials (username and/or password)"
305
+ )
306
+ );
307
+ }
308
+ if (parsed.pathname !== "/") {
309
+ throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
310
+ }
311
+ if (parsed.search) {
312
+ throw new Error(
313
+ Host.getMicrofrontendsError(url, "cannot have query parameters")
314
+ );
315
+ }
316
+ const protocol = parsed.protocol.slice(0, -1);
317
+ return {
318
+ protocol,
319
+ host: parsed.hostname,
320
+ port: parsed.port ? Number.parseInt(parsed.port, 10) : void 0
321
+ };
322
+ }
323
+ static getMicrofrontendsError(url, message) {
324
+ return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
325
+ }
326
+ isLocal() {
327
+ return this.local || this.host === "localhost" || this.host === "127.0.0.1";
328
+ }
329
+ toString() {
330
+ const url = this.toUrl();
331
+ return url.toString().replace(/\/$/, "");
332
+ }
333
+ toUrl() {
334
+ const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
335
+ return new URL(url);
336
+ }
337
+ };
338
+ var LocalHost = class extends Host {
339
+ constructor({
340
+ appName,
341
+ local
342
+ }) {
343
+ const portOverride = process.env[MFE_APP_PORT_ENV];
344
+ if (portOverride) {
345
+ const overridePort = Number.parseInt(portOverride, 10);
346
+ if (!Number.isNaN(overridePort) && overridePort > 0 && overridePort < 65536) {
347
+ super({
348
+ protocol: "http",
349
+ host: "localhost",
350
+ port: overridePort
351
+ });
352
+ return;
353
+ }
354
+ }
355
+ let protocol;
356
+ let host;
357
+ let port;
358
+ if (typeof local === "number") {
359
+ port = local;
360
+ } else if (typeof local === "string") {
361
+ if (/^\d+$/.test(local)) {
362
+ port = Number.parseInt(local, 10);
363
+ } else {
364
+ const parsed = Host.parseUrl(local, "http");
365
+ protocol = parsed.protocol;
366
+ host = parsed.host;
367
+ port = parsed.port;
368
+ }
369
+ } else if (local) {
370
+ protocol = local.protocol;
371
+ host = local.host;
372
+ port = local.port;
373
+ }
374
+ super({
375
+ protocol: protocol ?? "http",
376
+ host: host ?? "localhost",
377
+ port: port ?? generatePortFromName({ name: appName })
378
+ });
379
+ }
380
+ };
381
+
382
+ // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
383
+ import md5 from "md5";
384
+ function hashApplicationName(name) {
385
+ if (!name) {
386
+ throw new Error("Application name is required to generate hash");
387
+ }
388
+ return md5(name).substring(0, 6).padStart(6, "0");
389
+ }
390
+
391
+ // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
392
+ var PREFIX = "vc-ap";
393
+ function generateAssetPrefixFromName({
394
+ name
395
+ }) {
396
+ if (!name) {
397
+ throw new Error("Name is required to generate an asset prefix");
398
+ }
399
+ return `${PREFIX}-${hashApplicationName(name)}`;
400
+ }
401
+
402
+ // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
403
+ function generateAutomationBypassEnvVarName({
404
+ name
405
+ }) {
406
+ return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
407
+ }
408
+
244
409
  // src/config/microfrontends-config/isomorphic/validation.ts
245
- import { pathToRegexp as pathToRegexp2, parse as parsePathRegexp } from "path-to-regexp";
410
+ import { parse as parsePathRegexp, pathToRegexp as pathToRegexp2 } from "path-to-regexp";
246
411
  var LIST_FORMATTER = new Intl.ListFormat("en", {
247
412
  style: "long",
248
413
  type: "conjunction"
@@ -314,7 +479,7 @@ var validateConfigPaths = (applicationConfigsById) => {
314
479
  );
315
480
  }
316
481
  };
317
- var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
482
+ var PATH_DEFAULT_PATTERNS = ["[^\\/#\\?]+?", "(?:(?!\\.)[^\\/#\\?])+?"];
318
483
  function validatePathExpression(path) {
319
484
  try {
320
485
  const tokens = parsePathRegexp(path);
@@ -336,7 +501,7 @@ function validatePathExpression(path) {
336
501
  if (!token.name) {
337
502
  return `Only named wildcards are allowed: ${path} (hint: add ":path" to the wildcard)`;
338
503
  }
339
- if (token.pattern !== PATH_DEFAULT_PATTERN && // Allows (a|b|c) and ((?!a|b|c).*) regex
504
+ if (!PATH_DEFAULT_PATTERNS.includes(token.pattern) && // Allows (a|b|c) and ((?!a|b|c).*) regex
340
505
  // Only limited regex is supported for now, due to performance considerations
341
506
  // Allows all letters, numbers, and hyphens. Other characters must be escaped.
342
507
  !/^(?<allowed>[\w-~]+(?:\|[^:|()]+)+)$|^\(\?!(?<disallowed>[\w-~]+(?:\|[^:|()]+)*)\)\.\*$/.test(
@@ -422,154 +587,6 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
422
587
  }
423
588
  };
424
589
 
425
- // src/config/microfrontends-config/isomorphic/utils/hash-application-name.ts
426
- import md5 from "md5";
427
- function hashApplicationName(name) {
428
- if (!name) {
429
- throw new Error("Application name is required to generate hash");
430
- }
431
- return md5(name).substring(0, 6).padStart(6, "0");
432
- }
433
-
434
- // src/config/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
435
- var PREFIX = "vc-ap";
436
- function generateAssetPrefixFromName({
437
- name
438
- }) {
439
- if (!name) {
440
- throw new Error("Name is required to generate an asset prefix");
441
- }
442
- return `${PREFIX}-${hashApplicationName(name)}`;
443
- }
444
-
445
- // src/config/microfrontends-config/isomorphic/utils/generate-port.ts
446
- function generatePortFromName({
447
- name,
448
- minPort = 3e3,
449
- maxPort = 8e3
450
- }) {
451
- if (!name) {
452
- throw new Error("Name is required to generate a port");
453
- }
454
- let hash = 0;
455
- for (let i = 0; i < name.length; i++) {
456
- hash = (hash << 5) - hash + name.charCodeAt(i);
457
- hash |= 0;
458
- }
459
- hash = Math.abs(hash);
460
- const range = maxPort - minPort;
461
- const port = minPort + hash % range;
462
- return port;
463
- }
464
-
465
- // src/config/microfrontends-config/isomorphic/host.ts
466
- var Host = class {
467
- constructor(hostConfig, options) {
468
- if (typeof hostConfig === "string") {
469
- ({
470
- protocol: this.protocol,
471
- host: this.host,
472
- port: this.port
473
- } = Host.parseUrl(hostConfig));
474
- } else {
475
- const { protocol = "https", host, port } = hostConfig;
476
- this.protocol = protocol;
477
- this.host = host;
478
- this.port = port;
479
- }
480
- this.local = options?.isLocal;
481
- }
482
- static parseUrl(url, defaultProtocol = "https") {
483
- let hostToParse = url;
484
- if (!/^https?:\/\//.exec(hostToParse)) {
485
- hostToParse = `${defaultProtocol}://${hostToParse}`;
486
- }
487
- const parsed = new URL(hostToParse);
488
- if (!parsed.hostname) {
489
- throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
490
- }
491
- if (parsed.hash) {
492
- throw new Error(
493
- Host.getMicrofrontendsError(url, "cannot have a fragment")
494
- );
495
- }
496
- if (parsed.username || parsed.password) {
497
- throw new Error(
498
- Host.getMicrofrontendsError(
499
- url,
500
- "cannot have authentication credentials (username and/or password)"
501
- )
502
- );
503
- }
504
- if (parsed.pathname !== "/") {
505
- throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
506
- }
507
- if (parsed.search) {
508
- throw new Error(
509
- Host.getMicrofrontendsError(url, "cannot have query parameters")
510
- );
511
- }
512
- const protocol = parsed.protocol.slice(0, -1);
513
- return {
514
- protocol,
515
- host: parsed.hostname,
516
- port: parsed.port ? Number.parseInt(parsed.port) : void 0
517
- };
518
- }
519
- static getMicrofrontendsError(url, message) {
520
- return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
521
- }
522
- isLocal() {
523
- return this.local || this.host === "localhost" || this.host === "127.0.0.1";
524
- }
525
- toString() {
526
- const url = this.toUrl();
527
- return url.toString().replace(/\/$/, "");
528
- }
529
- toUrl() {
530
- const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
531
- return new URL(url);
532
- }
533
- };
534
- var LocalHost = class extends Host {
535
- constructor({
536
- appName,
537
- local
538
- }) {
539
- let protocol;
540
- let host;
541
- let port;
542
- if (typeof local === "number") {
543
- port = local;
544
- } else if (typeof local === "string") {
545
- if (/^\d+$/.test(local)) {
546
- port = Number.parseInt(local);
547
- } else {
548
- const parsed = Host.parseUrl(local, "http");
549
- protocol = parsed.protocol;
550
- host = parsed.host;
551
- port = parsed.port;
552
- }
553
- } else if (local) {
554
- protocol = local.protocol;
555
- host = local.host;
556
- port = local.port;
557
- }
558
- super({
559
- protocol: protocol ?? "http",
560
- host: host ?? "localhost",
561
- port: port ?? generatePortFromName({ name: appName })
562
- });
563
- }
564
- };
565
-
566
- // src/config/microfrontends-config/isomorphic/utils/generate-automation-bypass-env-var-name.ts
567
- function generateAutomationBypassEnvVarName({
568
- name
569
- }) {
570
- return `AUTOMATION_BYPASS_${name.toUpperCase().replace(/[^a-zA-Z0-9]/g, "_")}`;
571
- }
572
-
573
590
  // src/config/microfrontends-config/isomorphic/application.ts
574
591
  var Application = class {
575
592
  constructor(name, {
@@ -650,9 +667,6 @@ var ChildApplication = class extends Application {
650
667
  }
651
668
  };
652
669
 
653
- // src/config/microfrontends-config/isomorphic/constants.ts
654
- var DEFAULT_LOCAL_PROXY_PORT = 3024;
655
-
656
670
  // src/config/microfrontends-config/isomorphic/index.ts
657
671
  var MicrofrontendConfigIsomorphic = class {
658
672
  constructor({
@@ -771,9 +785,17 @@ var MicrofrontendConfigIsomorphic = class {
771
785
  return this.defaultApplication;
772
786
  }
773
787
  /**
774
- * Returns the configured port for the local proxy
788
+ * Returns the configured port for the local proxy.
789
+ * Can be overridden via MFE_LOCAL_PROXY_PORT environment variable.
775
790
  */
776
791
  getLocalProxyPort() {
792
+ const portOverride = process.env[MFE_LOCAL_PROXY_PORT_ENV];
793
+ if (portOverride) {
794
+ const port = Number.parseInt(portOverride, 10);
795
+ if (!Number.isNaN(port) && port > 0 && port < 65536) {
796
+ return port;
797
+ }
798
+ }
777
799
  return this.config.options?.localProxyPort ?? DEFAULT_LOCAL_PROXY_PORT;
778
800
  }
779
801
  toClientConfig(options) {