@vercel/microfrontends 1.0.1-canary.0 → 1.0.1-canary.2

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 (44) hide show
  1. package/dist/bin/cli.cjs +143 -44
  2. package/dist/config.cjs +39 -26
  3. package/dist/config.cjs.map +1 -1
  4. package/dist/config.js +39 -26
  5. package/dist/config.js.map +1 -1
  6. package/dist/{sveltekit.cjs → experimental/sveltekit.cjs} +39 -26
  7. package/dist/experimental/sveltekit.cjs.map +1 -0
  8. package/dist/{sveltekit.js → experimental/sveltekit.js} +39 -26
  9. package/dist/experimental/sveltekit.js.map +1 -0
  10. package/dist/experimental/vite.cjs +1758 -0
  11. package/dist/experimental/vite.cjs.map +1 -0
  12. package/dist/experimental/vite.d.ts +29 -0
  13. package/dist/experimental/vite.js +1723 -0
  14. package/dist/experimental/vite.js.map +1 -0
  15. package/dist/microfrontends/server.cjs +39 -26
  16. package/dist/microfrontends/server.cjs.map +1 -1
  17. package/dist/microfrontends/server.js +39 -26
  18. package/dist/microfrontends/server.js.map +1 -1
  19. package/dist/microfrontends.cjs +39 -26
  20. package/dist/microfrontends.cjs.map +1 -1
  21. package/dist/microfrontends.js +39 -26
  22. package/dist/microfrontends.js.map +1 -1
  23. package/dist/next/config.cjs +220 -135
  24. package/dist/next/config.cjs.map +1 -1
  25. package/dist/next/config.d.ts +7 -1
  26. package/dist/next/config.js +220 -135
  27. package/dist/next/config.js.map +1 -1
  28. package/dist/next/middleware.cjs +69 -31
  29. package/dist/next/middleware.cjs.map +1 -1
  30. package/dist/next/middleware.js +69 -31
  31. package/dist/next/middleware.js.map +1 -1
  32. package/dist/next/testing.cjs +39 -26
  33. package/dist/next/testing.cjs.map +1 -1
  34. package/dist/next/testing.d.ts +1 -1
  35. package/dist/next/testing.js +40 -27
  36. package/dist/next/testing.js.map +1 -1
  37. package/dist/utils/mfe-port.cjs +39 -26
  38. package/dist/utils/mfe-port.cjs.map +1 -1
  39. package/dist/utils/mfe-port.js +39 -26
  40. package/dist/utils/mfe-port.js.map +1 -1
  41. package/package.json +22 -9
  42. package/dist/sveltekit.cjs.map +0 -1
  43. package/dist/sveltekit.js.map +0 -1
  44. /package/dist/{sveltekit.d.ts → experimental/sveltekit.d.ts} +0 -0
package/dist/bin/cli.cjs CHANGED
@@ -29,7 +29,7 @@ var import_commander = require("commander");
29
29
  // package.json
30
30
  var package_default = {
31
31
  name: "@vercel/microfrontends",
32
- version: "1.0.1-canary.0",
32
+ version: "1.0.1-canary.2",
33
33
  private: false,
34
34
  description: "Defines configuration and utilities for microfrontends development",
35
35
  keywords: [
@@ -54,6 +54,14 @@ var package_default = {
54
54
  import: "./dist/config.js",
55
55
  require: "./dist/config.cjs"
56
56
  },
57
+ "./experimental/sveltekit": {
58
+ import: "./dist/experimental/sveltekit.js",
59
+ require: "./dist/experimental/sveltekit.cjs"
60
+ },
61
+ "./experimental/vite": {
62
+ import: "./dist/experimental/vite.js",
63
+ require: "./dist/experimental/vite.cjs"
64
+ },
57
65
  "./microfrontends": {
58
66
  import: "./dist/microfrontends.js",
59
67
  require: "./dist/microfrontends.cjs"
@@ -94,10 +102,6 @@ var package_default = {
94
102
  import: "./dist/next/testing.js",
95
103
  require: "./dist/next/testing.cjs"
96
104
  },
97
- "./sveltekit": {
98
- import: "./dist/sveltekit.js",
99
- require: "./dist/sveltekit.cjs"
100
- },
101
105
  "./next/client": {
102
106
  import: "./dist/next/client.js",
103
107
  require: "./dist/next/client.cjs"
@@ -115,6 +119,12 @@ var package_default = {
115
119
  config: [
116
120
  "./dist/config.d.ts"
117
121
  ],
122
+ "experimental/sveltekit": [
123
+ "./dist/experimental/sveltekit.d.ts"
124
+ ],
125
+ "experimental/vite": [
126
+ "./dist/experimental/vite.d.ts"
127
+ ],
118
128
  microfrontends: [
119
129
  "./dist/microfrontends.d.ts"
120
130
  ],
@@ -145,9 +155,6 @@ var package_default = {
145
155
  "next/testing": [
146
156
  "./dist/next/testing.d.ts"
147
157
  ],
148
- sveltekit: [
149
- "./dist/sveltekit.d.ts"
150
- ],
151
158
  "next/client": [
152
159
  "./dist/next/client.d.ts"
153
160
  ],
@@ -182,6 +189,7 @@ var package_default = {
182
189
  "fast-glob": "^3.3.2",
183
190
  "http-proxy": "^1.18.1",
184
191
  "jsonc-parser": "^3.3.1",
192
+ nanoid: "^5.1.2",
185
193
  "path-to-regexp": "6.2.1"
186
194
  },
187
195
  devDependencies: {
@@ -208,6 +216,7 @@ var package_default = {
208
216
  tsup: "^6.6.2",
209
217
  tsx: "^4.6.2",
210
218
  typescript: "5.7.3",
219
+ vite: "^5.1.6",
211
220
  webpack: "5"
212
221
  },
213
222
  peerDependencies: {
@@ -216,7 +225,8 @@ var package_default = {
216
225
  "@vercel/speed-insights": ">=1.2.0",
217
226
  next: ">=13",
218
227
  react: ">=17.0.0",
219
- "react-dom": ">=17.0.0"
228
+ "react-dom": ">=17.0.0",
229
+ vite: ">=5"
220
230
  },
221
231
  peerDependenciesMeta: {
222
232
  "@sveltejs/kit": {
@@ -236,6 +246,9 @@ var package_default = {
236
246
  },
237
247
  "react-dom": {
238
248
  optional: true
249
+ },
250
+ vite: {
251
+ optional: true
239
252
  }
240
253
  }
241
254
  };
@@ -363,16 +376,17 @@ var validateConfigPaths = (applicationConfigsById) => {
363
376
  const maybeError = validatePathExpression(path6);
364
377
  if (maybeError) {
365
378
  errors.push(maybeError);
366
- }
367
- const existing = pathsByApplicationId.get(path6);
368
- if (existing) {
369
- existing.applications.push(id);
370
379
  } else {
371
- pathsByApplicationId.set(path6, {
372
- applications: [id],
373
- matcher: (0, import_path_to_regexp.pathToRegexp)(path6),
374
- applicationId: id
375
- });
380
+ const existing = pathsByApplicationId.get(path6);
381
+ if (existing) {
382
+ existing.applications.push(id);
383
+ } else {
384
+ pathsByApplicationId.set(path6, {
385
+ applications: [id],
386
+ matcher: (0, import_path_to_regexp.pathToRegexp)(path6),
387
+ applicationId: id
388
+ });
389
+ }
376
390
  }
377
391
  }
378
392
  }
@@ -412,26 +426,38 @@ var validateConfigPaths = (applicationConfigsById) => {
412
426
  };
413
427
  var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
414
428
  function validatePathExpression(path6) {
415
- const tokens = (0, import_path_to_regexp.parse)(path6);
416
- for (let i = 0; i < tokens.length; i++) {
417
- const token = tokens[i];
418
- if (token === void 0) {
419
- return `token ${i} in ${path6} is undefined, this shouldn't happen`;
420
- }
421
- if (typeof token !== "string") {
422
- if (token.pattern !== PATH_DEFAULT_PATTERN) {
423
- return `Path ${path6} cannot use a regular expression wildcard`;
424
- }
425
- if (token.prefix !== "/") {
426
- return `Wildcard :${token.name} must be immediately after a / in ${path6}`;
427
- }
428
- if (token.suffix) {
429
- return `Wildcard suffix on :${token.name} is not allowed. Suffixes are not supported`;
429
+ try {
430
+ const tokens = (0, import_path_to_regexp.parse)(path6);
431
+ if (/(?<!\\)\{/.test(path6)) {
432
+ return `Optional paths are not supported: ${path6}`;
433
+ }
434
+ if (/(?<!\\|\()\?/.test(path6)) {
435
+ return `Optional paths are not supported: ${path6}`;
436
+ }
437
+ if (/\/[^/]*(?<!\\):[^/]*(?<!\\):[^/]*/.test(path6)) {
438
+ return `Only one wildcard is allowed per path segment: ${path6}`;
439
+ }
440
+ for (let i = 0; i < tokens.length; i++) {
441
+ const token = tokens[i];
442
+ if (token === void 0) {
443
+ return `token ${i} in ${path6} is undefined, this shouldn't happen`;
430
444
  }
431
- if (token.modifier && i !== tokens.length - 1) {
432
- return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path6}. Modifiers are only allowed in the last path component`;
445
+ if (typeof token !== "string") {
446
+ if (token.pattern !== PATH_DEFAULT_PATTERN && // Allows (a|b|c) and ((?!a|b|c).*) regex
447
+ // Only limited regex is supported for now, due to performance considerations
448
+ !/^(?<allowed>[\w]+(?:\|[^|()]+)+)$|^\(\?!(?<disallowed>[\w]+(?:\|[^|()]+)+)\)\.\*$/.test(
449
+ token.pattern
450
+ )) {
451
+ return `Path ${path6} cannot use unsupported regular expression wildcard`;
452
+ }
453
+ if (token.modifier && i !== tokens.length - 1) {
454
+ return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path6}. Modifiers are only allowed in the last path component`;
455
+ }
433
456
  }
434
457
  }
458
+ } catch (e) {
459
+ const message = e instanceof Error ? e.message : String(e);
460
+ return `Path ${path6} could not be parsed into regexp: ${message}`;
435
461
  }
436
462
  return void 0;
437
463
  }
@@ -1888,6 +1914,8 @@ var MicrofrontendsServer = class extends Microfrontends {
1888
1914
 
1889
1915
  // src/bin/local-proxy.ts
1890
1916
  var MFE_LOCAL_PROXY_HEADER = "x-vercel-mfe-local-proxy-origin";
1917
+ var MFE_FLAG_VALUE = "vercel-mfe-flag-value";
1918
+ var MFE_FLAG_VALUE_HEADER = `x-${MFE_FLAG_VALUE}`;
1891
1919
  var MFE_DEBUG = process.env.MFE_DEBUG;
1892
1920
  var mfeDebug = (message) => {
1893
1921
  if (MFE_DEBUG === "true" || MFE_DEBUG === "1") {
@@ -1979,10 +2007,10 @@ var ProxyRequestRouter = class {
1979
2007
  getTarget(request2) {
1980
2008
  const cookies = (0, import_cookie.parse)(request2.headers.cookie || "");
1981
2009
  const config = this.getConfigWithOverrides(cookies);
1982
- const path6 = request2.url;
1983
- if (!path6) {
2010
+ if (!request2.url) {
1984
2011
  return this.getDefaultHost(config);
1985
2012
  }
2013
+ const { path: path6, mfeFlagValue } = extractMfeFlagValue(request2.url);
1986
2014
  const authTarget = this.getAuthTarget(request2, config);
1987
2015
  if (authTarget) {
1988
2016
  return authTarget;
@@ -1992,7 +2020,14 @@ var ProxyRequestRouter = class {
1992
2020
  path: path6,
1993
2021
  url,
1994
2022
  applications: config.getChildApplications(),
1995
- referer: request2.headers.referer
2023
+ referer: request2.headers.referer,
2024
+ // If a request already has the local proxy header, then the request has
2025
+ // already gone through the local proxy.
2026
+ // This should only happen if middleware indicates that a flagged route
2027
+ // is enabled, so we treat this as enabling all flagged routes for this request.
2028
+ middlewareMfeZone: request2.headers["x-vercel-mfe-zone"],
2029
+ // Value to use when encountering any flagged paths instead of checking middleware
2030
+ mfeFlagValue
1996
2031
  });
1997
2032
  if (target)
1998
2033
  return target;
@@ -2006,10 +2041,21 @@ var ProxyRequestRouter = class {
2006
2041
  path: path6,
2007
2042
  url,
2008
2043
  applications,
2009
- referer = void 0
2044
+ referer = void 0,
2045
+ middlewareMfeZone = void 0,
2046
+ mfeFlagValue = void 0
2010
2047
  }) {
2011
2048
  for (const application of Object.values(applications)) {
2012
2049
  const target = this.getApplicationTarget(application);
2050
+ if (middlewareMfeZone) {
2051
+ if (middlewareMfeZone === application.name) {
2052
+ mfeDebug(
2053
+ `routing ${path6} to '${target.application}' at ${target.hostname} according to 'x-vercel-mfe-zone' header`
2054
+ );
2055
+ return { path: path6, ...target };
2056
+ }
2057
+ continue;
2058
+ }
2013
2059
  const builtInRewrite = this.checkBuiltinAssetPrefix({
2014
2060
  rewrites: ["/:path*"],
2015
2061
  path: path6,
@@ -2026,11 +2072,33 @@ var ProxyRequestRouter = class {
2026
2072
  mfeDebug(
2027
2073
  `routing ${path6} to '${target.application}' at ${target.hostname}`
2028
2074
  );
2075
+ if (group.flag) {
2076
+ if (mfeFlagValue === true) {
2077
+ } else if (mfeFlagValue === false) {
2078
+ continue;
2079
+ } else {
2080
+ mfeDebug(
2081
+ "Routing group is behind flag. Routing to default app to check flag via middleware."
2082
+ );
2083
+ if (!this.isDefaultAppLocal()) {
2084
+ const defaultApp = this.getDefaultHost(this.config);
2085
+ console.error(
2086
+ `'${path6}' is a flagged path, but the default application is not running locally. Using '${defaultApp.hostname}' to handle this request.`
2087
+ );
2088
+ }
2089
+ return null;
2090
+ }
2091
+ }
2029
2092
  return { path: path6, ...target };
2030
2093
  }
2031
2094
  }
2032
2095
  }
2033
2096
  }
2097
+ if (middlewareMfeZone) {
2098
+ console.error(
2099
+ `A request contained 'x-vercel-mfe-zone: ${middlewareMfeZone}', but no application was found with that name.`
2100
+ );
2101
+ }
2034
2102
  return null;
2035
2103
  }
2036
2104
  checkBuiltinAssetPrefix({
@@ -2079,6 +2147,9 @@ var ProxyRequestRouter = class {
2079
2147
  path: `${url.pathname}${url.search}`
2080
2148
  } : null;
2081
2149
  }
2150
+ isDefaultAppLocal() {
2151
+ return this.localApps.includes(this.config.getDefaultApplication().name);
2152
+ }
2082
2153
  };
2083
2154
  var LocalProxy = class {
2084
2155
  constructor(config, {
@@ -2141,16 +2212,14 @@ var LocalProxy = class {
2141
2212
  return;
2142
2213
  }
2143
2214
  const target = this.router.getTarget(req);
2215
+ const { req: strippedReq, mfeFlagValue } = removeMfeFlagQuery(req);
2144
2216
  if (target.protocol === "https") {
2145
2217
  const { hostname, port, path: path6 } = target;
2146
2218
  const requestOptions = {
2147
2219
  hostname,
2148
2220
  path: path6,
2149
2221
  method: req.method,
2150
- headers: {
2151
- ...req.headers,
2152
- host: hostname
2153
- },
2222
+ headers: { ...req.headers, host: hostname },
2154
2223
  port
2155
2224
  };
2156
2225
  const localhost = `http://localhost:${this.proxyPort}`;
@@ -2179,7 +2248,10 @@ var LocalProxy = class {
2179
2248
  } else {
2180
2249
  const headers = {};
2181
2250
  headers[MFE_LOCAL_PROXY_HEADER] = "1";
2182
- this.proxy.web(req, res, {
2251
+ if (mfeFlagValue !== void 0) {
2252
+ headers[MFE_FLAG_VALUE_HEADER] = mfeFlagValue.toString();
2253
+ }
2254
+ this.proxy.web(strippedReq, res, {
2183
2255
  target: target.url,
2184
2256
  headers
2185
2257
  });
@@ -2214,6 +2286,33 @@ var LocalProxy = class {
2214
2286
  return false;
2215
2287
  }
2216
2288
  };
2289
+ function extractMfeFlagValue(path6) {
2290
+ const host = "http://example.com";
2291
+ const url = new import_node_url.URL(`${host}${path6}`);
2292
+ const mfeFlagValue = stringAsBoolean(url.searchParams.get(MFE_FLAG_VALUE));
2293
+ url.searchParams.delete(MFE_FLAG_VALUE);
2294
+ const pathWithoutMfe = mfeFlagValue === void 0 ? path6 : url.toString().substring(host.length);
2295
+ return { path: pathWithoutMfe, mfeFlagValue };
2296
+ }
2297
+ function stringAsBoolean(value) {
2298
+ if (value === "true") {
2299
+ return true;
2300
+ } else if (value === "false") {
2301
+ return false;
2302
+ }
2303
+ return void 0;
2304
+ }
2305
+ function removeMfeFlagQuery(req) {
2306
+ if (!req.url) {
2307
+ return { req };
2308
+ }
2309
+ const { path: path6, mfeFlagValue } = extractMfeFlagValue(req.url);
2310
+ if (mfeFlagValue === void 0) {
2311
+ return { req };
2312
+ }
2313
+ req.url = path6;
2314
+ return { req, mfeFlagValue };
2315
+ }
2217
2316
 
2218
2317
  // src/bin/port.ts
2219
2318
  var import_node_process = require("process");
package/dist/config.cjs CHANGED
@@ -256,16 +256,17 @@ var validateConfigPaths = (applicationConfigsById) => {
256
256
  const maybeError = validatePathExpression(path);
257
257
  if (maybeError) {
258
258
  errors.push(maybeError);
259
- }
260
- const existing = pathsByApplicationId.get(path);
261
- if (existing) {
262
- existing.applications.push(id);
263
259
  } else {
264
- pathsByApplicationId.set(path, {
265
- applications: [id],
266
- matcher: (0, import_path_to_regexp2.pathToRegexp)(path),
267
- applicationId: id
268
- });
260
+ const existing = pathsByApplicationId.get(path);
261
+ if (existing) {
262
+ existing.applications.push(id);
263
+ } else {
264
+ pathsByApplicationId.set(path, {
265
+ applications: [id],
266
+ matcher: (0, import_path_to_regexp2.pathToRegexp)(path),
267
+ applicationId: id
268
+ });
269
+ }
269
270
  }
270
271
  }
271
272
  }
@@ -305,26 +306,38 @@ var validateConfigPaths = (applicationConfigsById) => {
305
306
  };
306
307
  var PATH_DEFAULT_PATTERN = "[^\\/#\\?]+?";
307
308
  function validatePathExpression(path) {
308
- const tokens = (0, import_path_to_regexp2.parse)(path);
309
- for (let i = 0; i < tokens.length; i++) {
310
- const token = tokens[i];
311
- if (token === void 0) {
312
- return `token ${i} in ${path} is undefined, this shouldn't happen`;
313
- }
314
- if (typeof token !== "string") {
315
- if (token.pattern !== PATH_DEFAULT_PATTERN) {
316
- return `Path ${path} cannot use a regular expression wildcard`;
317
- }
318
- if (token.prefix !== "/") {
319
- return `Wildcard :${token.name} must be immediately after a / in ${path}`;
320
- }
321
- if (token.suffix) {
322
- return `Wildcard suffix on :${token.name} is not allowed. Suffixes are not supported`;
309
+ try {
310
+ const tokens = (0, import_path_to_regexp2.parse)(path);
311
+ if (/(?<!\\)\{/.test(path)) {
312
+ return `Optional paths are not supported: ${path}`;
313
+ }
314
+ if (/(?<!\\|\()\?/.test(path)) {
315
+ return `Optional paths are not supported: ${path}`;
316
+ }
317
+ if (/\/[^/]*(?<!\\):[^/]*(?<!\\):[^/]*/.test(path)) {
318
+ return `Only one wildcard is allowed per path segment: ${path}`;
319
+ }
320
+ for (let i = 0; i < tokens.length; i++) {
321
+ const token = tokens[i];
322
+ if (token === void 0) {
323
+ return `token ${i} in ${path} is undefined, this shouldn't happen`;
323
324
  }
324
- if (token.modifier && i !== tokens.length - 1) {
325
- return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path}. Modifiers are only allowed in the last path component`;
325
+ if (typeof token !== "string") {
326
+ if (token.pattern !== PATH_DEFAULT_PATTERN && // Allows (a|b|c) and ((?!a|b|c).*) regex
327
+ // Only limited regex is supported for now, due to performance considerations
328
+ !/^(?<allowed>[\w]+(?:\|[^|()]+)+)$|^\(\?!(?<disallowed>[\w]+(?:\|[^|()]+)+)\)\.\*$/.test(
329
+ token.pattern
330
+ )) {
331
+ return `Path ${path} cannot use unsupported regular expression wildcard`;
332
+ }
333
+ if (token.modifier && i !== tokens.length - 1) {
334
+ return `Modifier ${token.modifier} is not allowed on wildcard :${token.name} in ${path}. Modifiers are only allowed in the last path component`;
335
+ }
326
336
  }
327
337
  }
338
+ } catch (e) {
339
+ const message = e instanceof Error ? e.message : String(e);
340
+ return `Path ${path} could not be parsed into regexp: ${message}`;
328
341
  }
329
342
  return void 0;
330
343
  }