@vercel/microfrontends 0.9.0 → 0.10.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.
- package/README.md +2 -2
- package/dist/bin/cli.cjs +1483 -186
- package/dist/config/edge.cjs +47 -47
- package/dist/config/edge.cjs.map +1 -1
- package/dist/config/edge.d.ts +6 -6
- package/dist/config/edge.js +46 -46
- package/dist/config/edge.js.map +1 -1
- package/dist/config.cjs +66 -60
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.ts +3 -3
- package/dist/config.js +65 -59
- package/dist/config.js.map +1 -1
- package/dist/{index-eff254d8.d.ts → index-05742bef.d.ts} +11 -22
- package/dist/{micro-frontend-config-42886104.d.ts → microfrontend-config-2425db74.d.ts} +12 -12
- package/dist/next/config.cjs +83 -77
- package/dist/next/config.cjs.map +1 -1
- package/dist/next/config.d.ts +3 -3
- package/dist/next/config.js +82 -76
- package/dist/next/config.js.map +1 -1
- package/dist/next/middleware.cjs +55 -55
- package/dist/next/middleware.cjs.map +1 -1
- package/dist/next/middleware.d.ts +11 -11
- package/dist/next/middleware.js +53 -53
- package/dist/next/middleware.js.map +1 -1
- package/dist/next/testing.cjs +79 -73
- package/dist/next/testing.cjs.map +1 -1
- package/dist/next/testing.d.ts +9 -9
- package/dist/next/testing.js +79 -73
- package/dist/next/testing.js.map +1 -1
- package/dist/overrides.cjs +9 -9
- package/dist/overrides.cjs.map +1 -1
- package/dist/overrides.d.ts +1 -1
- package/dist/overrides.js +9 -9
- package/dist/overrides.js.map +1 -1
- package/dist/types-13f3e535.d.ts +15 -0
- package/dist/v2/config.cjs +39 -39
- package/dist/v2/config.cjs.map +1 -1
- package/dist/v2/config.d.ts +2 -1
- package/dist/v2/config.js +38 -38
- package/dist/v2/config.js.map +1 -1
- package/dist/v2/microfrontends/server.cjs +102 -65
- package/dist/v2/microfrontends/server.cjs.map +1 -1
- package/dist/v2/microfrontends/server.d.ts +6 -1
- package/dist/v2/microfrontends/server.js +102 -65
- package/dist/v2/microfrontends/server.js.map +1 -1
- package/dist/v2/microfrontends.cjs +44 -44
- package/dist/v2/microfrontends.cjs.map +1 -1
- package/dist/v2/microfrontends.d.ts +5 -4
- package/dist/v2/microfrontends.js +44 -44
- package/dist/v2/microfrontends.js.map +1 -1
- package/dist/v2/next/client.cjs +1 -1
- package/dist/v2/next/client.cjs.map +1 -1
- package/dist/v2/next/client.js +1 -1
- package/dist/v2/next/client.js.map +1 -1
- package/dist/v2/next/config.cjs +120 -83
- package/dist/v2/next/config.cjs.map +1 -1
- package/dist/v2/next/config.d.ts +4 -4
- package/dist/v2/next/config.js +119 -82
- package/dist/v2/next/config.js.map +1 -1
- package/dist/v2/next/endpoints.cjs +5 -5
- package/dist/v2/next/endpoints.cjs.map +1 -1
- package/dist/v2/next/endpoints.js +5 -5
- package/dist/v2/next/endpoints.js.map +1 -1
- package/dist/v2/next/middleware.cjs +54 -54
- package/dist/v2/next/middleware.cjs.map +1 -1
- package/dist/v2/next/middleware.d.ts +8 -8
- package/dist/v2/next/middleware.js +52 -52
- package/dist/v2/next/middleware.js.map +1 -1
- package/dist/v2/overrides.cjs +75 -0
- package/dist/v2/overrides.cjs.map +1 -0
- package/dist/v2/overrides.d.ts +24 -0
- package/dist/v2/overrides.js +45 -0
- package/dist/v2/overrides.js.map +1 -0
- package/dist/validation.cjs +20 -20
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.js +20 -20
- package/dist/validation.js.map +1 -1
- package/package.json +10 -3
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: "0.
|
|
32
|
+
version: "0.10.0",
|
|
33
33
|
private: false,
|
|
34
34
|
description: "Defines configuration and utilities for micro-frontend development",
|
|
35
35
|
repository: {
|
|
@@ -87,6 +87,10 @@ var package_default = {
|
|
|
87
87
|
import: "./dist/v2/microfrontends.js",
|
|
88
88
|
require: "./dist/v2/microfrontends.cjs"
|
|
89
89
|
},
|
|
90
|
+
"./v2/overrides": {
|
|
91
|
+
import: "./dist/v2/overrides.js",
|
|
92
|
+
require: "./dist/v2/overrides.cjs"
|
|
93
|
+
},
|
|
90
94
|
"./v2/microfrontends/server": {
|
|
91
95
|
import: "./dist/v2/microfrontends/server.js",
|
|
92
96
|
require: "./dist/v2/microfrontends/server.cjs"
|
|
@@ -129,6 +133,7 @@ var package_default = {
|
|
|
129
133
|
"next/testing": ["./dist/next/testing.d.ts"],
|
|
130
134
|
"v2/config": ["./dist/v2/config.d.ts"],
|
|
131
135
|
"v2/microfrontends": ["./dist/v2/microfrontends.d.ts"],
|
|
136
|
+
"v2/overrides": ["./dist/v2/overrides.d.ts"],
|
|
132
137
|
"v2/microfrontends/server": ["./dist/v2/microfrontends/server.d.ts"],
|
|
133
138
|
"v2/schema": ["./dist/v2/schema.d.ts"],
|
|
134
139
|
"v2/next/config": ["./dist/v2/next/config.d.ts"],
|
|
@@ -175,7 +180,7 @@ var package_default = {
|
|
|
175
180
|
"@vercel-private/conformance": "^1.12.2-canary.0",
|
|
176
181
|
jest: "^29.7.0",
|
|
177
182
|
"jest-environment-jsdom": "29.2.2",
|
|
178
|
-
next: "15.0.4-canary.
|
|
183
|
+
next: "15.0.4-canary.26",
|
|
179
184
|
react: "19.0.0-rc-380f5d67-20241113",
|
|
180
185
|
"react-dom": "19.0.0-rc-380f5d67-20241113",
|
|
181
186
|
"ts-json-schema-generator": "^1.1.2",
|
|
@@ -185,7 +190,7 @@ var package_default = {
|
|
|
185
190
|
webpack: "5"
|
|
186
191
|
},
|
|
187
192
|
peerDependencies: {
|
|
188
|
-
next: "15.0.4-canary.
|
|
193
|
+
next: "15.0.4-canary.26",
|
|
189
194
|
react: "19.0.0-rc-380f5d67-20241113",
|
|
190
195
|
"react-dom": "19.0.0-rc-380f5d67-20241113"
|
|
191
196
|
},
|
|
@@ -197,14 +202,15 @@ var package_default = {
|
|
|
197
202
|
// src/bin/local-proxy.ts
|
|
198
203
|
var http = __toESM(require("http"), 1);
|
|
199
204
|
var https = __toESM(require("https"), 1);
|
|
205
|
+
var import_types2 = require("util/types");
|
|
200
206
|
var import_cookie = require("cookie");
|
|
201
|
-
var
|
|
207
|
+
var import_path_to_regexp4 = require("path-to-regexp");
|
|
202
208
|
var import_http_proxy = __toESM(require("http-proxy"), 1);
|
|
203
209
|
|
|
204
210
|
// src/config/types.ts
|
|
205
211
|
var isDefaultApplicationConfig = (app) => app.default && typeof app.routing === "undefined";
|
|
206
212
|
|
|
207
|
-
// src/config/
|
|
213
|
+
// src/config/microfrontend-config.ts
|
|
208
214
|
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
209
215
|
|
|
210
216
|
// src/config-v2/microfrontends/server/utils/get-output-file-path.ts
|
|
@@ -232,14 +238,14 @@ function getOutputFilePath() {
|
|
|
232
238
|
}
|
|
233
239
|
|
|
234
240
|
// src/config/errors.ts
|
|
235
|
-
var
|
|
241
|
+
var MicrofrontendError = class extends Error {
|
|
236
242
|
constructor(message, opts) {
|
|
237
243
|
super(message);
|
|
238
|
-
this.name = "
|
|
239
|
-
this.source = (opts == null ? void 0 : opts.source) ?? "@vercel/
|
|
244
|
+
this.name = "MicrofrontendsError";
|
|
245
|
+
this.source = (opts == null ? void 0 : opts.source) ?? "@vercel/microfrontends";
|
|
240
246
|
this.type = (opts == null ? void 0 : opts.type) ?? "unknown";
|
|
241
247
|
this.subtype = opts == null ? void 0 : opts.subtype;
|
|
242
|
-
Error.captureStackTrace(this,
|
|
248
|
+
Error.captureStackTrace(this, MicrofrontendError);
|
|
243
249
|
}
|
|
244
250
|
isKnown() {
|
|
245
251
|
return this.type !== "unknown";
|
|
@@ -248,13 +254,13 @@ var MicroFrontendError = class extends Error {
|
|
|
248
254
|
return !this.isKnown();
|
|
249
255
|
}
|
|
250
256
|
/**
|
|
251
|
-
* Converts an error to a
|
|
257
|
+
* Converts an error to a MicrofrontendsError.
|
|
252
258
|
* @param original - The original error to convert.
|
|
253
|
-
* @returns The converted
|
|
259
|
+
* @returns The converted MicrofrontendsError.
|
|
254
260
|
*/
|
|
255
261
|
static convert(original, opts) {
|
|
256
262
|
if (opts == null ? void 0 : opts.fileName) {
|
|
257
|
-
const err =
|
|
263
|
+
const err = MicrofrontendError.convertFSError(original, opts.fileName);
|
|
258
264
|
if (err) {
|
|
259
265
|
return err;
|
|
260
266
|
}
|
|
@@ -262,25 +268,25 @@ var MicroFrontendError = class extends Error {
|
|
|
262
268
|
if (original.message.includes(
|
|
263
269
|
"Code generation from strings disallowed for this context"
|
|
264
270
|
)) {
|
|
265
|
-
return new
|
|
271
|
+
return new MicrofrontendError(original.message, {
|
|
266
272
|
type: "config",
|
|
267
273
|
subtype: "unsupported_validation_env",
|
|
268
274
|
source: "ajv"
|
|
269
275
|
});
|
|
270
276
|
}
|
|
271
|
-
return new
|
|
277
|
+
return new MicrofrontendError(original.message);
|
|
272
278
|
}
|
|
273
279
|
static convertFSError(original, fileName) {
|
|
274
280
|
if (original instanceof Error && "code" in original) {
|
|
275
281
|
if (original.code === "ENOENT") {
|
|
276
|
-
return new
|
|
282
|
+
return new MicrofrontendError(`Could not find "${fileName}"`, {
|
|
277
283
|
type: "config",
|
|
278
284
|
subtype: "unable_to_read_file",
|
|
279
285
|
source: "fs"
|
|
280
286
|
});
|
|
281
287
|
}
|
|
282
288
|
if (original.code === "EACCES") {
|
|
283
|
-
return new
|
|
289
|
+
return new MicrofrontendError(
|
|
284
290
|
`Permission denied while accessing "${fileName}"`,
|
|
285
291
|
{
|
|
286
292
|
type: "config",
|
|
@@ -291,7 +297,7 @@ var MicroFrontendError = class extends Error {
|
|
|
291
297
|
}
|
|
292
298
|
}
|
|
293
299
|
if (original instanceof SyntaxError) {
|
|
294
|
-
return new
|
|
300
|
+
return new MicrofrontendError(
|
|
295
301
|
`Failed to parse "${fileName}": Invalid JSON format.`,
|
|
296
302
|
{
|
|
297
303
|
type: "config",
|
|
@@ -303,23 +309,23 @@ var MicroFrontendError = class extends Error {
|
|
|
303
309
|
return null;
|
|
304
310
|
}
|
|
305
311
|
/**
|
|
306
|
-
* Handles an unknown error and returns a
|
|
312
|
+
* Handles an unknown error and returns a MicrofrontendsError instance.
|
|
307
313
|
* @param err - The error to handle.
|
|
308
|
-
* @returns A
|
|
314
|
+
* @returns A MicrofrontendsError instance.
|
|
309
315
|
*/
|
|
310
316
|
static handle(err, opts) {
|
|
311
|
-
if (err instanceof
|
|
317
|
+
if (err instanceof MicrofrontendError) {
|
|
312
318
|
return err;
|
|
313
319
|
}
|
|
314
320
|
if (err instanceof Error) {
|
|
315
|
-
return
|
|
321
|
+
return MicrofrontendError.convert(err, opts);
|
|
316
322
|
}
|
|
317
323
|
if (typeof err === "object" && err !== null) {
|
|
318
324
|
if ("message" in err && typeof err.message === "string") {
|
|
319
|
-
return
|
|
325
|
+
return MicrofrontendError.convert(new Error(err.message), opts);
|
|
320
326
|
}
|
|
321
327
|
}
|
|
322
|
-
return new
|
|
328
|
+
return new MicrofrontendError("An unknown error occurred");
|
|
323
329
|
}
|
|
324
330
|
};
|
|
325
331
|
|
|
@@ -402,27 +408,27 @@ var _Overrides = class {
|
|
|
402
408
|
});
|
|
403
409
|
return overridesConfig;
|
|
404
410
|
}
|
|
405
|
-
static validOverrideDomainsForZone(
|
|
411
|
+
static validOverrideDomainsForZone(microfrontendConfig, zone) {
|
|
406
412
|
var _a, _b, _c, _d, _e;
|
|
407
|
-
const projectName = (_a =
|
|
413
|
+
const projectName = (_a = microfrontendConfig.getZone(zone).vercel) == null ? void 0 : _a.projectName;
|
|
408
414
|
if (!projectName) {
|
|
409
|
-
return [
|
|
415
|
+
return [microfrontendConfig.getZone(zone).production.host];
|
|
410
416
|
}
|
|
411
417
|
const parsedProjectName = makeUrlSafe(projectName);
|
|
412
|
-
const previewDeploymentSuffix = (_c = (_b =
|
|
413
|
-
const teamSlug = (_e = (_d =
|
|
418
|
+
const previewDeploymentSuffix = (_c = (_b = microfrontendConfig.options) == null ? void 0 : _b.vercel) == null ? void 0 : _c.previewDeploymentSuffix;
|
|
419
|
+
const teamSlug = (_e = (_d = microfrontendConfig.options) == null ? void 0 : _d.vercel) == null ? void 0 : _e.teamSlug;
|
|
414
420
|
if (!teamSlug && !previewDeploymentSuffix) {
|
|
415
|
-
return [
|
|
421
|
+
return [microfrontendConfig.getZone(zone).production.host];
|
|
416
422
|
}
|
|
417
423
|
const suffix = previewDeploymentSuffix ? `.${previewDeploymentSuffix}` : `-${teamSlug}.vercel.app`;
|
|
418
424
|
return [
|
|
419
425
|
`${parsedProjectName}-git-([a-zA-Z0-9-]+)${suffix}`,
|
|
420
|
-
|
|
426
|
+
microfrontendConfig.getZone(zone).production.host
|
|
421
427
|
];
|
|
422
428
|
}
|
|
423
|
-
static validateOverrideDomain(
|
|
429
|
+
static validateOverrideDomain(microfrontendConfig, zone, domain) {
|
|
424
430
|
return new RegExp(
|
|
425
|
-
`^${_Overrides.validOverrideDomainsForZone(
|
|
431
|
+
`^${_Overrides.validOverrideDomainsForZone(microfrontendConfig, zone).join(
|
|
426
432
|
"|"
|
|
427
433
|
)}$`
|
|
428
434
|
).test(domain);
|
|
@@ -504,7 +510,7 @@ var Application = class {
|
|
|
504
510
|
static validate(name, app) {
|
|
505
511
|
var _a, _b, _c, _d, _e;
|
|
506
512
|
if (((_b = (_a = app.routing) == null ? void 0 : _a.assetPrefix) == null ? void 0 : _b.startsWith("/")) || ((_d = (_c = app.routing) == null ? void 0 : _c.assetPrefix) == null ? void 0 : _d.endsWith("/"))) {
|
|
507
|
-
throw new
|
|
513
|
+
throw new MicrofrontendError(
|
|
508
514
|
`Invalid assetPrefix for application "${name}". Must not start or end with a slash.`,
|
|
509
515
|
{ type: "zone", subtype: "invalid_asset_prefix" }
|
|
510
516
|
);
|
|
@@ -515,13 +521,13 @@ var Application = class {
|
|
|
515
521
|
continue;
|
|
516
522
|
}
|
|
517
523
|
if (p.endsWith("/")) {
|
|
518
|
-
throw new
|
|
524
|
+
throw new MicrofrontendError(
|
|
519
525
|
`Invalid path for application "${name}". ${p} must not end with a slash.`,
|
|
520
526
|
{ type: "zone", subtype: "invalid_path" }
|
|
521
527
|
);
|
|
522
528
|
}
|
|
523
529
|
if (!p.startsWith("/")) {
|
|
524
|
-
throw new
|
|
530
|
+
throw new MicrofrontendError(
|
|
525
531
|
`Invalid path for application "${name}". ${p} must start with a slash.`,
|
|
526
532
|
{ type: "zone", subtype: "invalid_path" }
|
|
527
533
|
);
|
|
@@ -555,10 +561,10 @@ var Application = class {
|
|
|
555
561
|
}
|
|
556
562
|
};
|
|
557
563
|
|
|
558
|
-
// src/config/common/
|
|
564
|
+
// src/config/common/microfrontend-config.ts
|
|
559
565
|
var SUPPORTED_VERSIONS = ["1"];
|
|
560
566
|
var DEFAULT_LOCAL_PROXY_PORT = 3024;
|
|
561
|
-
var
|
|
567
|
+
var MicrofrontendConfigCommon = class {
|
|
562
568
|
constructor({
|
|
563
569
|
config,
|
|
564
570
|
overrides
|
|
@@ -566,7 +572,7 @@ var MicroFrontendConfigCommon = class {
|
|
|
566
572
|
this.zones = {};
|
|
567
573
|
var _a, _b, _c;
|
|
568
574
|
if (!SUPPORTED_VERSIONS.includes(config.version)) {
|
|
569
|
-
throw new
|
|
575
|
+
throw new MicrofrontendError(
|
|
570
576
|
`Unsupported version: ${config.version}. Supported versions are: ${SUPPORTED_VERSIONS.join(
|
|
571
577
|
", "
|
|
572
578
|
)}`,
|
|
@@ -594,7 +600,7 @@ var MicroFrontendConfigCommon = class {
|
|
|
594
600
|
static getConfigFromEnv() {
|
|
595
601
|
const config = process.env.MFE_CONFIG;
|
|
596
602
|
if (!config) {
|
|
597
|
-
throw new
|
|
603
|
+
throw new MicrofrontendError(`Missing "MFE_CONFIG" in environment.`, {
|
|
598
604
|
type: "config",
|
|
599
605
|
subtype: "not_found_in_env"
|
|
600
606
|
});
|
|
@@ -613,8 +619,8 @@ var MicroFrontendConfigCommon = class {
|
|
|
613
619
|
getZone(name) {
|
|
614
620
|
const zone = this.zones[name];
|
|
615
621
|
if (!zone) {
|
|
616
|
-
throw new
|
|
617
|
-
`Could not find
|
|
622
|
+
throw new MicrofrontendError(
|
|
623
|
+
`Could not find microfrontends configuration for application "${name}"`,
|
|
618
624
|
{
|
|
619
625
|
type: "zone",
|
|
620
626
|
subtype: "not_found"
|
|
@@ -634,8 +640,8 @@ var MicroFrontendConfigCommon = class {
|
|
|
634
640
|
getDefaultZone() {
|
|
635
641
|
const zone = Object.values(this.zones).find((z) => z.default);
|
|
636
642
|
if (!zone) {
|
|
637
|
-
throw new
|
|
638
|
-
`Could not find default zone in
|
|
643
|
+
throw new MicrofrontendError(
|
|
644
|
+
`Could not find default zone in microfrontends configuration`,
|
|
639
645
|
{
|
|
640
646
|
type: "zone",
|
|
641
647
|
subtype: "not_found"
|
|
@@ -687,8 +693,8 @@ var MicroFrontendConfigCommon = class {
|
|
|
687
693
|
};
|
|
688
694
|
}
|
|
689
695
|
write(_) {
|
|
690
|
-
throw new
|
|
691
|
-
`Writing to file to disk requires using an instance of "
|
|
696
|
+
throw new MicrofrontendError(
|
|
697
|
+
`Writing to file to disk requires using an instance of "MicrofrontendConfig".`,
|
|
692
698
|
{ type: "config", subtype: "unsupported_operation" }
|
|
693
699
|
);
|
|
694
700
|
}
|
|
@@ -1049,7 +1055,7 @@ var validateSchema = (configString) => {
|
|
|
1049
1055
|
const validate = ajv.compile(SCHEMA);
|
|
1050
1056
|
const isValid = validate(parsedConfig);
|
|
1051
1057
|
if (!isValid) {
|
|
1052
|
-
throw new
|
|
1058
|
+
throw new MicrofrontendError(
|
|
1053
1059
|
`Invalid config: ${ajv.errorsText(validate.errors)}`,
|
|
1054
1060
|
{ type: "config", subtype: "does_not_match_schema" }
|
|
1055
1061
|
);
|
|
@@ -1059,7 +1065,7 @@ var validateSchema = (configString) => {
|
|
|
1059
1065
|
var SUPPORTED_VERSIONS2 = ["1"];
|
|
1060
1066
|
var validateVersion = (version) => {
|
|
1061
1067
|
if (!SUPPORTED_VERSIONS2.includes(version)) {
|
|
1062
|
-
throw new
|
|
1068
|
+
throw new MicrofrontendError(
|
|
1063
1069
|
`Unsupported version: ${version}. Supported versions are: ${SUPPORTED_VERSIONS2.join(
|
|
1064
1070
|
", "
|
|
1065
1071
|
)}`,
|
|
@@ -1092,7 +1098,7 @@ function validateMainPath(applicationConfigsById) {
|
|
|
1092
1098
|
return !matcher.test(defaultRoute);
|
|
1093
1099
|
});
|
|
1094
1100
|
if (!isValid) {
|
|
1095
|
-
throw new
|
|
1101
|
+
throw new MicrofrontendError(
|
|
1096
1102
|
`default route "${defaultRoute}" cannot be used for "${id}" because it is matched by "${otherId}"`,
|
|
1097
1103
|
{ type: "config", subtype: "invalid_main_path" }
|
|
1098
1104
|
);
|
|
@@ -1105,7 +1111,7 @@ function validateMainPath(applicationConfigsById) {
|
|
|
1105
1111
|
return matcher.test(defaultRoute);
|
|
1106
1112
|
});
|
|
1107
1113
|
if (!isValid) {
|
|
1108
|
-
throw new
|
|
1114
|
+
throw new MicrofrontendError(
|
|
1109
1115
|
`default route "${defaultRoute}" is not included by the routing config for application "${id}"`,
|
|
1110
1116
|
{ type: "config", subtype: "invalid_main_path" }
|
|
1111
1117
|
);
|
|
@@ -1168,7 +1174,7 @@ var validatePaths = (applicationConfigsById) => {
|
|
|
1168
1174
|
);
|
|
1169
1175
|
});
|
|
1170
1176
|
if (errors.length) {
|
|
1171
|
-
throw new
|
|
1177
|
+
throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
|
|
1172
1178
|
type: "config",
|
|
1173
1179
|
subtype: "conflicting_paths"
|
|
1174
1180
|
});
|
|
@@ -1202,13 +1208,13 @@ function validatePathExpression(path3) {
|
|
|
1202
1208
|
var validateDefaults = (applicationConfigsById) => {
|
|
1203
1209
|
const defaultApplicationIds = Object.entries(applicationConfigsById).reduce((acc, [id, app]) => app.default ? [...acc, id] : acc, []);
|
|
1204
1210
|
if (defaultApplicationIds.length === 0) {
|
|
1205
|
-
throw new
|
|
1211
|
+
throw new MicrofrontendError(
|
|
1206
1212
|
`No default application found. At least one application must be marked as default.`,
|
|
1207
1213
|
{ type: "config", subtype: "no_default_application" }
|
|
1208
1214
|
);
|
|
1209
1215
|
}
|
|
1210
1216
|
if (defaultApplicationIds.length > 1) {
|
|
1211
|
-
throw new
|
|
1217
|
+
throw new MicrofrontendError(
|
|
1212
1218
|
`Only one default application is allowed. Found ${defaultApplicationIds.join(", ")}.`,
|
|
1213
1219
|
{ type: "config", subtype: "multiple_default_applications" }
|
|
1214
1220
|
);
|
|
@@ -1220,7 +1226,7 @@ var validateOptions = (options) => {
|
|
|
1220
1226
|
if (!/^[a-zA-Z]{2,}\.[a-zA-Z]{2,}$/.test(
|
|
1221
1227
|
options.vercel.previewDeploymentSuffix
|
|
1222
1228
|
)) {
|
|
1223
|
-
throw new
|
|
1229
|
+
throw new MicrofrontendError(
|
|
1224
1230
|
`Invalid preview deployment suffix: ${options.vercel.previewDeploymentSuffix}. Should have be formatted like "vercel.app".`,
|
|
1225
1231
|
{ type: "config", subtype: "invalid_preview_deployment_suffix" }
|
|
1226
1232
|
);
|
|
@@ -1275,9 +1281,15 @@ function convertV1ConfigToV2Config(config, fromApp) {
|
|
|
1275
1281
|
)
|
|
1276
1282
|
};
|
|
1277
1283
|
}
|
|
1284
|
+
const defaultApplication = Object.entries(config.applications).find(
|
|
1285
|
+
([, application]) => application.default
|
|
1286
|
+
);
|
|
1287
|
+
if (!defaultApplication) {
|
|
1288
|
+
throw new Error("No default application found in the config");
|
|
1289
|
+
}
|
|
1278
1290
|
return {
|
|
1279
1291
|
...common,
|
|
1280
|
-
partOf:
|
|
1292
|
+
partOf: defaultApplication[0]
|
|
1281
1293
|
};
|
|
1282
1294
|
}
|
|
1283
1295
|
|
|
@@ -1292,8 +1304,8 @@ function writeFile(outputPath, config, prettify) {
|
|
|
1292
1304
|
);
|
|
1293
1305
|
}
|
|
1294
1306
|
|
|
1295
|
-
// src/config/
|
|
1296
|
-
var
|
|
1307
|
+
// src/config/microfrontend-config.ts
|
|
1308
|
+
var MicrofrontendConfig = class extends MicrofrontendConfigCommon {
|
|
1297
1309
|
static validate(configString) {
|
|
1298
1310
|
const config = validateSchema(configString);
|
|
1299
1311
|
validateVersion(config.version);
|
|
@@ -1306,9 +1318,9 @@ var MicroFrontendConfig = class extends MicroFrontendConfigCommon {
|
|
|
1306
1318
|
static fromEnv({
|
|
1307
1319
|
cookies
|
|
1308
1320
|
}) {
|
|
1309
|
-
return new
|
|
1310
|
-
config:
|
|
1311
|
-
|
|
1321
|
+
return new MicrofrontendConfigCommon({
|
|
1322
|
+
config: MicrofrontendConfig.validate(
|
|
1323
|
+
MicrofrontendConfigCommon.getConfigFromEnv()
|
|
1312
1324
|
),
|
|
1313
1325
|
overrides: Overrides.parseOverrides(cookies)
|
|
1314
1326
|
});
|
|
@@ -1318,11 +1330,11 @@ var MicroFrontendConfig = class extends MicroFrontendConfigCommon {
|
|
|
1318
1330
|
}) {
|
|
1319
1331
|
try {
|
|
1320
1332
|
const config = import_node_fs2.default.readFileSync(filePath, "utf-8");
|
|
1321
|
-
return new
|
|
1322
|
-
config:
|
|
1333
|
+
return new MicrofrontendConfig({
|
|
1334
|
+
config: MicrofrontendConfig.validate(config)
|
|
1323
1335
|
});
|
|
1324
1336
|
} catch (e) {
|
|
1325
|
-
throw
|
|
1337
|
+
throw MicrofrontendError.handle(e, {
|
|
1326
1338
|
fileName: filePath
|
|
1327
1339
|
});
|
|
1328
1340
|
}
|
|
@@ -1345,150 +1357,1435 @@ var MicroFrontendConfig = class extends MicroFrontendConfigCommon {
|
|
|
1345
1357
|
}
|
|
1346
1358
|
};
|
|
1347
1359
|
|
|
1348
|
-
// src/
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1360
|
+
// src/config-v2/schema/utils/is-default-app.ts
|
|
1361
|
+
function isDefaultApp(a) {
|
|
1362
|
+
return !("routing" in a);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
// src/config-v2/errors.ts
|
|
1366
|
+
var MicrofrontendError2 = class extends Error {
|
|
1367
|
+
constructor(message, opts) {
|
|
1368
|
+
super(message);
|
|
1369
|
+
this.name = "MicrofrontendsError";
|
|
1370
|
+
this.source = (opts == null ? void 0 : opts.source) ?? "@vercel/microfrontends";
|
|
1371
|
+
this.type = (opts == null ? void 0 : opts.type) ?? "unknown";
|
|
1372
|
+
this.subtype = opts == null ? void 0 : opts.subtype;
|
|
1373
|
+
Error.captureStackTrace(this, MicrofrontendError2);
|
|
1353
1374
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
constructor(config, {
|
|
1357
|
-
localApps,
|
|
1358
|
-
proxyPort
|
|
1359
|
-
}) {
|
|
1360
|
-
this.config = config;
|
|
1361
|
-
this.localApps = localApps;
|
|
1362
|
-
this.proxyPort = proxyPort ?? this.config.getLocalProxyPort();
|
|
1363
|
-
this.proxy = import_http_proxy.default.createProxyServer({ secure: true });
|
|
1364
|
-
this.proxy.on("error", (err, req, res) => {
|
|
1365
|
-
if (res instanceof http.ServerResponse) {
|
|
1366
|
-
res.writeHead(500, {
|
|
1367
|
-
"Content-Type": "text/plain"
|
|
1368
|
-
});
|
|
1369
|
-
}
|
|
1370
|
-
const target = this.getTarget(req);
|
|
1371
|
-
res.end(
|
|
1372
|
-
`Error proxying request to ${target.application}. Is the server running locally on port ${target.port}?`
|
|
1373
|
-
);
|
|
1374
|
-
console.error(`Error proxying request for ${target.application}: `, err);
|
|
1375
|
-
});
|
|
1375
|
+
isKnown() {
|
|
1376
|
+
return this.type !== "unknown";
|
|
1376
1377
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
proxyPort
|
|
1380
|
-
}) {
|
|
1381
|
-
const config = MicroFrontendConfig.fromFile({ filePath });
|
|
1382
|
-
return new LocalProxy(config, { localApps, proxyPort });
|
|
1378
|
+
isUnknown() {
|
|
1379
|
+
return !this.isKnown();
|
|
1383
1380
|
}
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1381
|
+
/**
|
|
1382
|
+
* Converts an error to a MicrofrontendsError.
|
|
1383
|
+
* @param original - The original error to convert.
|
|
1384
|
+
* @returns The converted MicrofrontendsError.
|
|
1385
|
+
*/
|
|
1386
|
+
static convert(original, opts) {
|
|
1387
|
+
if (opts == null ? void 0 : opts.fileName) {
|
|
1388
|
+
const err = MicrofrontendError2.convertFSError(original, opts.fileName);
|
|
1389
|
+
if (err) {
|
|
1390
|
+
return err;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
if (original.message.includes(
|
|
1394
|
+
"Code generation from strings disallowed for this context"
|
|
1395
|
+
)) {
|
|
1396
|
+
return new MicrofrontendError2(original.message, {
|
|
1397
|
+
type: "config",
|
|
1398
|
+
subtype: "unsupported_validation_env",
|
|
1399
|
+
source: "ajv"
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
return new MicrofrontendError2(original.message);
|
|
1387
1403
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1404
|
+
static convertFSError(original, fileName) {
|
|
1405
|
+
if (original instanceof Error && "code" in original) {
|
|
1406
|
+
if (original.code === "ENOENT") {
|
|
1407
|
+
return new MicrofrontendError2(`Could not find "${fileName}"`, {
|
|
1408
|
+
type: "config",
|
|
1409
|
+
subtype: "unable_to_read_file",
|
|
1410
|
+
source: "fs"
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
if (original.code === "EACCES") {
|
|
1414
|
+
return new MicrofrontendError2(
|
|
1415
|
+
`Permission denied while accessing "${fileName}"`,
|
|
1416
|
+
{
|
|
1417
|
+
type: "config",
|
|
1418
|
+
subtype: "invalid_permissions",
|
|
1419
|
+
source: "fs"
|
|
1420
|
+
}
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1394
1423
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
}
|
|
1424
|
+
if (original instanceof SyntaxError) {
|
|
1425
|
+
return new MicrofrontendError2(
|
|
1426
|
+
`Failed to parse "${fileName}": Invalid JSON format.`,
|
|
1427
|
+
{
|
|
1428
|
+
type: "config",
|
|
1429
|
+
subtype: "invalid_syntax",
|
|
1430
|
+
source: "fs"
|
|
1431
|
+
}
|
|
1432
|
+
);
|
|
1433
|
+
}
|
|
1434
|
+
return null;
|
|
1405
1435
|
}
|
|
1406
1436
|
/**
|
|
1407
|
-
*
|
|
1408
|
-
*
|
|
1409
|
-
*
|
|
1410
|
-
* protected host.
|
|
1437
|
+
* Handles an unknown error and returns a MicrofrontendsError instance.
|
|
1438
|
+
* @param err - The error to handle.
|
|
1439
|
+
* @returns A MicrofrontendsError instance.
|
|
1411
1440
|
*/
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
const isAuthRedirect = (_a = request2.url) == null ? void 0 : _a.startsWith(
|
|
1416
|
-
"/.well-known/vercel-auth-redirect"
|
|
1417
|
-
);
|
|
1418
|
-
const isSsoRedirect = (_b = request2.url) == null ? void 0 : _b.startsWith("/sso-api");
|
|
1419
|
-
const isJWTRedirect = url.searchParams.has("_vercel_jwt");
|
|
1420
|
-
const defaultHost = this.getDefaultHost(config);
|
|
1421
|
-
let hostname = null;
|
|
1422
|
-
let path3 = request2.url;
|
|
1423
|
-
if (isAuthRedirect) {
|
|
1424
|
-
hostname = url.searchParams.get("_host_override");
|
|
1425
|
-
}
|
|
1426
|
-
if (isSsoRedirect) {
|
|
1427
|
-
hostname = "vercel.com";
|
|
1441
|
+
static handle(err, opts) {
|
|
1442
|
+
if (err instanceof MicrofrontendError2) {
|
|
1443
|
+
return err;
|
|
1428
1444
|
}
|
|
1429
|
-
if (
|
|
1430
|
-
|
|
1431
|
-
url.searchParams.delete("_host_override");
|
|
1432
|
-
path3 = `${url.pathname}${url.search}`;
|
|
1445
|
+
if (err instanceof Error) {
|
|
1446
|
+
return MicrofrontendError2.convert(err, opts);
|
|
1433
1447
|
}
|
|
1434
|
-
if (
|
|
1435
|
-
|
|
1448
|
+
if (typeof err === "object" && err !== null) {
|
|
1449
|
+
if ("message" in err && typeof err.message === "string") {
|
|
1450
|
+
return MicrofrontendError2.convert(new Error(err.message), opts);
|
|
1451
|
+
}
|
|
1436
1452
|
}
|
|
1437
|
-
return
|
|
1453
|
+
return new MicrofrontendError2("An unknown error occurred");
|
|
1438
1454
|
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1455
|
+
};
|
|
1456
|
+
|
|
1457
|
+
// src/config-v2/microfrontends-config/isomorphic/validation.ts
|
|
1458
|
+
var import_path_to_regexp2 = require("path-to-regexp");
|
|
1459
|
+
var SUPPORTED_VERSIONS3 = ["2"];
|
|
1460
|
+
var validateConfigVersion = (version) => {
|
|
1461
|
+
if (!SUPPORTED_VERSIONS3.includes(version)) {
|
|
1462
|
+
throw new MicrofrontendError2(
|
|
1463
|
+
`Unsupported version: ${version}. Supported versions are: ${SUPPORTED_VERSIONS3.join(
|
|
1464
|
+
", "
|
|
1465
|
+
)}`,
|
|
1466
|
+
{ type: "config", subtype: "unsupported_version" }
|
|
1443
1467
|
);
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
return authTarget;
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
var validateConfigPaths = (applicationConfigsById) => {
|
|
1471
|
+
if (!applicationConfigsById) {
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
const pathsByApplicationId = /* @__PURE__ */ new Map();
|
|
1475
|
+
const errors = [];
|
|
1476
|
+
for (const [id, app] of Object.entries(applicationConfigsById)) {
|
|
1477
|
+
if (isDefaultApp(app)) {
|
|
1478
|
+
continue;
|
|
1456
1479
|
}
|
|
1457
|
-
const
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
if (regexp.test(pathname)) {
|
|
1465
|
-
const target = this.getApplicationTarget(application);
|
|
1466
|
-
mfeDebug(
|
|
1467
|
-
`routing ${path3} to '${target.application}' at ${target.hostname}`
|
|
1468
|
-
);
|
|
1469
|
-
return { path: path3, ...target };
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
if (application.routing.assetPrefix) {
|
|
1473
|
-
const pattern = (0, import_path_to_regexp2.pathToRegexp)(
|
|
1474
|
-
`/${application.routing.assetPrefix}/:path+`
|
|
1480
|
+
for (const pathMatch of app.routing) {
|
|
1481
|
+
for (const path3 of pathMatch.paths) {
|
|
1482
|
+
const tokens = (0, import_path_to_regexp2.parse)(path3);
|
|
1483
|
+
for (const token of tokens.slice(0, -1)) {
|
|
1484
|
+
if (typeof token !== "string") {
|
|
1485
|
+
errors.push(
|
|
1486
|
+
`Path ${path3} may only have a :wildcard in the last path component`
|
|
1475
1487
|
);
|
|
1476
|
-
if (pattern.test(pathname)) {
|
|
1477
|
-
const target = this.getApplicationTarget(application);
|
|
1478
|
-
mfeDebug(
|
|
1479
|
-
`routing ${path3} to '${target.application}' at ${target.hostname}`
|
|
1480
|
-
);
|
|
1481
|
-
return { path: path3, ...target };
|
|
1482
|
-
}
|
|
1483
1488
|
}
|
|
1484
1489
|
}
|
|
1490
|
+
const existing = pathsByApplicationId.get(path3);
|
|
1491
|
+
if (existing) {
|
|
1492
|
+
existing.applications.push(id);
|
|
1493
|
+
} else {
|
|
1494
|
+
pathsByApplicationId.set(path3, {
|
|
1495
|
+
applications: [id],
|
|
1496
|
+
matcher: (0, import_path_to_regexp2.pathToRegexp)(path3),
|
|
1497
|
+
applicationId: id
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1485
1500
|
}
|
|
1486
1501
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1502
|
+
}
|
|
1503
|
+
const entries = Array.from(pathsByApplicationId.entries());
|
|
1504
|
+
entries.forEach(([path3, { applications: ids, matcher, applicationId }]) => {
|
|
1505
|
+
if (ids.length > 1) {
|
|
1506
|
+
errors.push(
|
|
1507
|
+
`Duplicate path "${path3}" for applications "${ids.join(", ")}"`
|
|
1508
|
+
);
|
|
1509
|
+
}
|
|
1510
|
+
entries.forEach(
|
|
1511
|
+
([
|
|
1512
|
+
matchPath,
|
|
1513
|
+
{ applications: matchIds, applicationId: matchApplicationId }
|
|
1514
|
+
]) => {
|
|
1515
|
+
if (path3 === matchPath) {
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
if (applicationId === matchApplicationId) {
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
if (matcher.test(matchPath)) {
|
|
1522
|
+
const source = `"${path3}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
|
|
1523
|
+
const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
|
|
1524
|
+
errors.push(
|
|
1525
|
+
`Overlapping path detected between ${source} and ${destination}`
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1490
1529
|
);
|
|
1491
|
-
|
|
1530
|
+
});
|
|
1531
|
+
if (errors.length) {
|
|
1532
|
+
throw new MicrofrontendError2(`Invalid paths: ${errors.join(", ")}`, {
|
|
1533
|
+
type: "config",
|
|
1534
|
+
subtype: "conflicting_paths"
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
var validateAppPaths = (name, app) => {
|
|
1539
|
+
for (const group of app.routing) {
|
|
1540
|
+
for (const p of group.paths) {
|
|
1541
|
+
if (p === "/") {
|
|
1542
|
+
continue;
|
|
1543
|
+
}
|
|
1544
|
+
if (p.endsWith("/")) {
|
|
1545
|
+
throw new MicrofrontendError2(
|
|
1546
|
+
`Invalid path for application "${name}". ${p} must not end with a slash.`,
|
|
1547
|
+
{ type: "application", subtype: "invalid_path" }
|
|
1548
|
+
);
|
|
1549
|
+
}
|
|
1550
|
+
if (!p.startsWith("/")) {
|
|
1551
|
+
throw new MicrofrontendError2(
|
|
1552
|
+
`Invalid path for application "${name}". ${p} must start with a slash.`,
|
|
1553
|
+
{ type: "application", subtype: "invalid_path" }
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1559
|
+
var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
1560
|
+
if (!applicationConfigsById) {
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
const applicationsWithRouting = Object.entries(applicationConfigsById).filter(
|
|
1564
|
+
([, app]) => !isDefaultApp(app)
|
|
1565
|
+
);
|
|
1566
|
+
const applicationsWithRoutingNames = applicationsWithRouting.map(
|
|
1567
|
+
([key]) => key
|
|
1568
|
+
);
|
|
1569
|
+
const numApplications = Object.keys(applicationConfigsById).length;
|
|
1570
|
+
const numApplicationsWithRouting = applicationsWithRoutingNames.length;
|
|
1571
|
+
const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
|
|
1572
|
+
if (numApplicationsWithoutRouting === 0) {
|
|
1573
|
+
throw new MicrofrontendError2(
|
|
1574
|
+
`No default application found. At least one application needs to be the default by omitting routing.`,
|
|
1575
|
+
{ type: "config", subtype: "no_default_application" }
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
if (numApplicationsWithoutRouting > 1) {
|
|
1579
|
+
throw new MicrofrontendError2(
|
|
1580
|
+
`Only one application can omit "routing". Found ${applicationsWithRoutingNames.length - Object.keys(applicationConfigsById).length > 1}.`,
|
|
1581
|
+
{ type: "config", subtype: "multiple_default_applications" }
|
|
1582
|
+
);
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
var validateConfigOptions = (options) => {
|
|
1586
|
+
var _a;
|
|
1587
|
+
if ((_a = options == null ? void 0 : options.vercel) == null ? void 0 : _a.previewDeploymentSuffix) {
|
|
1588
|
+
if (!/^[a-zA-Z]{2,}\.[a-zA-Z]{2,}$/.test(
|
|
1589
|
+
options.vercel.previewDeploymentSuffix
|
|
1590
|
+
)) {
|
|
1591
|
+
throw new MicrofrontendError2(
|
|
1592
|
+
`Invalid preview deployment suffix: ${options.vercel.previewDeploymentSuffix}. Should have be formatted like "vercel.app".`,
|
|
1593
|
+
{ type: "config", subtype: "invalid_preview_deployment_suffix" }
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
};
|
|
1598
|
+
|
|
1599
|
+
// src/config-v2/microfrontends-config/isomorphic/utils/generate-asset-prefix.ts
|
|
1600
|
+
var PREFIX = "vc-ap";
|
|
1601
|
+
function generateAssetPrefixFromName({
|
|
1602
|
+
name
|
|
1603
|
+
}) {
|
|
1604
|
+
if (!name) {
|
|
1605
|
+
throw new Error("Name is required to generate an asset prefix");
|
|
1606
|
+
}
|
|
1607
|
+
return `${PREFIX}-${name}`;
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// src/config-v2/microfrontends-config/isomorphic/utils/generate-port.ts
|
|
1611
|
+
function generatePortFromName({
|
|
1612
|
+
name,
|
|
1613
|
+
minPort = 3e3,
|
|
1614
|
+
maxPort = 8e3
|
|
1615
|
+
}) {
|
|
1616
|
+
if (!name) {
|
|
1617
|
+
throw new Error("Name is required to generate a port");
|
|
1618
|
+
}
|
|
1619
|
+
let hash = 0;
|
|
1620
|
+
for (let i = 0; i < name.length; i++) {
|
|
1621
|
+
hash = (hash << 5) - hash + name.charCodeAt(i);
|
|
1622
|
+
hash |= 0;
|
|
1623
|
+
}
|
|
1624
|
+
hash = Math.abs(hash);
|
|
1625
|
+
const range = maxPort - minPort;
|
|
1626
|
+
const port = minPort + hash % range;
|
|
1627
|
+
return port;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
// src/config-v2/microfrontends-config/isomorphic/host.ts
|
|
1631
|
+
var Host2 = class {
|
|
1632
|
+
constructor(hostConfig, options) {
|
|
1633
|
+
const { protocol = "https", host, port } = hostConfig;
|
|
1634
|
+
this.protocol = protocol;
|
|
1635
|
+
this.host = host;
|
|
1636
|
+
this.port = Host2.getPort({ port, protocol: this.protocol });
|
|
1637
|
+
this.local = options == null ? void 0 : options.isLocal;
|
|
1638
|
+
}
|
|
1639
|
+
isLocal() {
|
|
1640
|
+
return this.local || this.host === "localhost" || this.host === "127.0.0.1";
|
|
1641
|
+
}
|
|
1642
|
+
static getPort({
|
|
1643
|
+
protocol,
|
|
1644
|
+
port
|
|
1645
|
+
}) {
|
|
1646
|
+
if (!port) {
|
|
1647
|
+
if (protocol === "http") {
|
|
1648
|
+
return 80;
|
|
1649
|
+
}
|
|
1650
|
+
return 443;
|
|
1651
|
+
}
|
|
1652
|
+
return port;
|
|
1653
|
+
}
|
|
1654
|
+
isDefaultPort() {
|
|
1655
|
+
return this.port === Host2.getPort({ protocol: this.protocol });
|
|
1656
|
+
}
|
|
1657
|
+
toString(opts = {}) {
|
|
1658
|
+
const url = this.toUrl(opts);
|
|
1659
|
+
return url.toString().replace(/\/$/, "");
|
|
1660
|
+
}
|
|
1661
|
+
toUrl(opts = {}) {
|
|
1662
|
+
const { includeDefaultPort } = opts;
|
|
1663
|
+
const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
|
|
1664
|
+
return new URL(url);
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
var LocalHost = class extends Host2 {
|
|
1668
|
+
constructor({
|
|
1669
|
+
appName,
|
|
1670
|
+
...hostConfig
|
|
1671
|
+
}) {
|
|
1672
|
+
const host = hostConfig.host ?? "localhost";
|
|
1673
|
+
const port = hostConfig.port ?? generatePortFromName({ name: appName });
|
|
1674
|
+
const protocol = hostConfig.protocol ?? "http";
|
|
1675
|
+
super({ protocol, host, port });
|
|
1676
|
+
}
|
|
1677
|
+
};
|
|
1678
|
+
|
|
1679
|
+
// src/config-v2/microfrontends-config/isomorphic/application.ts
|
|
1680
|
+
var Application2 = class {
|
|
1681
|
+
constructor(name, {
|
|
1682
|
+
app,
|
|
1683
|
+
overrides,
|
|
1684
|
+
isDefault
|
|
1685
|
+
}) {
|
|
1686
|
+
var _a, _b;
|
|
1687
|
+
this.name = name;
|
|
1688
|
+
this.development = {
|
|
1689
|
+
local: new LocalHost({
|
|
1690
|
+
appName: name,
|
|
1691
|
+
...(_a = app.development) == null ? void 0 : _a.local
|
|
1692
|
+
}),
|
|
1693
|
+
fallback: ((_b = app.development) == null ? void 0 : _b.fallback) ? new Host2(app.development.fallback) : void 0
|
|
1694
|
+
};
|
|
1695
|
+
this.production = app.production ? new Host2(app.production) : void 0;
|
|
1696
|
+
this.vercel = app.vercel;
|
|
1697
|
+
this.overrides = (overrides == null ? void 0 : overrides.environment) ? {
|
|
1698
|
+
environment: new Host2(overrides.environment)
|
|
1699
|
+
} : void 0;
|
|
1700
|
+
this.default = isDefault ?? false;
|
|
1701
|
+
this.serialized = app;
|
|
1702
|
+
}
|
|
1703
|
+
isDefault() {
|
|
1704
|
+
return this.default;
|
|
1705
|
+
}
|
|
1706
|
+
getAssetPrefix() {
|
|
1707
|
+
return generateAssetPrefixFromName({ name: this.name });
|
|
1708
|
+
}
|
|
1709
|
+
serialize() {
|
|
1710
|
+
return this.serialized;
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
var DefaultApplication = class extends Application2 {
|
|
1714
|
+
constructor(name, {
|
|
1715
|
+
app,
|
|
1716
|
+
overrides
|
|
1717
|
+
}) {
|
|
1718
|
+
super(name, {
|
|
1719
|
+
app,
|
|
1720
|
+
overrides,
|
|
1721
|
+
isDefault: true
|
|
1722
|
+
});
|
|
1723
|
+
this.default = true;
|
|
1724
|
+
this.production = new Host2(app.production);
|
|
1725
|
+
}
|
|
1726
|
+
};
|
|
1727
|
+
var ChildApplication = class extends Application2 {
|
|
1728
|
+
constructor(name, {
|
|
1729
|
+
app,
|
|
1730
|
+
overrides
|
|
1731
|
+
}) {
|
|
1732
|
+
ChildApplication.validate(name, app);
|
|
1733
|
+
super(name, {
|
|
1734
|
+
app,
|
|
1735
|
+
overrides,
|
|
1736
|
+
isDefault: false
|
|
1737
|
+
});
|
|
1738
|
+
this.default = false;
|
|
1739
|
+
this.routing = app.routing;
|
|
1740
|
+
}
|
|
1741
|
+
static validate(name, app) {
|
|
1742
|
+
validateAppPaths(name, app);
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
|
|
1746
|
+
// src/config-v2/microfrontends-config/isomorphic/index.ts
|
|
1747
|
+
var import_jsonc_parser2 = require("jsonc-parser");
|
|
1748
|
+
|
|
1749
|
+
// src/config-v2/microfrontends-config/utils/get-config-from-env.ts
|
|
1750
|
+
function getConfigStringFromEnv() {
|
|
1751
|
+
const config = process.env.MFE_CONFIG;
|
|
1752
|
+
if (!config) {
|
|
1753
|
+
throw new MicrofrontendError2(`Missing "MFE_CONFIG" in environment.`, {
|
|
1754
|
+
type: "config",
|
|
1755
|
+
subtype: "not_found_in_env"
|
|
1756
|
+
});
|
|
1757
|
+
}
|
|
1758
|
+
return config;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
// src/config-v2/schema/utils/is-main-config.ts
|
|
1762
|
+
function isMainConfig(c) {
|
|
1763
|
+
return !("partOf" in c);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
// src/config-v2/microfrontends-config/client/index.ts
|
|
1767
|
+
var import_path_to_regexp3 = require("path-to-regexp");
|
|
1768
|
+
var MicrofrontendConfigClient = class {
|
|
1769
|
+
constructor(config, opts) {
|
|
1770
|
+
this.pathCache = {};
|
|
1771
|
+
this.serialized = config;
|
|
1772
|
+
if (opts == null ? void 0 : opts.removeFlaggedPaths) {
|
|
1773
|
+
for (const app of Object.values(config.applications)) {
|
|
1774
|
+
if (app.routing) {
|
|
1775
|
+
app.routing = app.routing.filter((match) => !match.flag);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1779
|
+
this.applications = config.applications;
|
|
1780
|
+
}
|
|
1781
|
+
/**
|
|
1782
|
+
* Create a new `MicrofrontendConfigClient` from a JSON string.
|
|
1783
|
+
* Config must be passed in to remain framework agnostic
|
|
1784
|
+
*/
|
|
1785
|
+
static fromEnv(config, opts) {
|
|
1786
|
+
if (!config) {
|
|
1787
|
+
throw new Error("No microfrontends configuration found");
|
|
1788
|
+
}
|
|
1789
|
+
return new MicrofrontendConfigClient(
|
|
1790
|
+
JSON.parse(config),
|
|
1791
|
+
opts
|
|
1792
|
+
);
|
|
1793
|
+
}
|
|
1794
|
+
isEqual(other) {
|
|
1795
|
+
return JSON.stringify(this.applications) === JSON.stringify(other.applications);
|
|
1796
|
+
}
|
|
1797
|
+
getApplicationNameForPath(path3) {
|
|
1798
|
+
if (!path3.startsWith("/")) {
|
|
1799
|
+
throw new Error(`Path must start with a /`);
|
|
1800
|
+
}
|
|
1801
|
+
if (this.pathCache[path3]) {
|
|
1802
|
+
return this.pathCache[path3];
|
|
1803
|
+
}
|
|
1804
|
+
const pathname = new URL(path3, "https://example.com").pathname;
|
|
1805
|
+
for (const [name, application] of Object.entries(this.applications)) {
|
|
1806
|
+
if (application.routing) {
|
|
1807
|
+
for (const group of application.routing) {
|
|
1808
|
+
for (const childPath of group.paths) {
|
|
1809
|
+
const regexp = (0, import_path_to_regexp3.pathToRegexp)(childPath);
|
|
1810
|
+
if (regexp.test(pathname)) {
|
|
1811
|
+
this.pathCache[path3] = name;
|
|
1812
|
+
return name;
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
const defaultApplication = Object.entries(this.applications).find(
|
|
1819
|
+
([, application]) => application.default
|
|
1820
|
+
);
|
|
1821
|
+
if (!defaultApplication) {
|
|
1822
|
+
return null;
|
|
1823
|
+
}
|
|
1824
|
+
this.pathCache[path3] = defaultApplication[0];
|
|
1825
|
+
return defaultApplication[0];
|
|
1826
|
+
}
|
|
1827
|
+
serialize() {
|
|
1828
|
+
return this.serialized;
|
|
1829
|
+
}
|
|
1830
|
+
};
|
|
1831
|
+
|
|
1832
|
+
// src/config-v2/overrides/constants.ts
|
|
1833
|
+
var OVERRIDES_COOKIE_PREFIX2 = "vercel-microfrontends-override";
|
|
1834
|
+
var OVERRIDES_ENV_COOKIE_PREFIX = `${OVERRIDES_COOKIE_PREFIX2}:env:`;
|
|
1835
|
+
|
|
1836
|
+
// src/config-v2/overrides/is-override-cookie.ts
|
|
1837
|
+
function isOverrideCookie(cookie) {
|
|
1838
|
+
var _a;
|
|
1839
|
+
return Boolean((_a = cookie.name) == null ? void 0 : _a.startsWith(OVERRIDES_COOKIE_PREFIX2));
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
// src/config-v2/overrides/get-override-from-cookie.ts
|
|
1843
|
+
function getOverrideFromCookie(cookie) {
|
|
1844
|
+
if (!isOverrideCookie(cookie) || !cookie.value)
|
|
1845
|
+
return;
|
|
1846
|
+
return {
|
|
1847
|
+
application: cookie.name.replace(OVERRIDES_ENV_COOKIE_PREFIX, ""),
|
|
1848
|
+
host: cookie.value
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
// src/config-v2/overrides/parse-overrides.ts
|
|
1853
|
+
function parseOverrides(cookies) {
|
|
1854
|
+
const overridesConfig = { applications: {} };
|
|
1855
|
+
cookies.forEach((cookie) => {
|
|
1856
|
+
const override = getOverrideFromCookie(cookie);
|
|
1857
|
+
if (!override)
|
|
1858
|
+
return;
|
|
1859
|
+
overridesConfig.applications[override.application] = {
|
|
1860
|
+
environment: { host: override.host }
|
|
1861
|
+
};
|
|
1862
|
+
});
|
|
1863
|
+
return overridesConfig;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// src/config-v2/microfrontends-config/isomorphic/constants.ts
|
|
1867
|
+
var DEFAULT_LOCAL_PROXY_PORT2 = 3024;
|
|
1868
|
+
|
|
1869
|
+
// src/config-v2/microfrontends-config/isomorphic/index.ts
|
|
1870
|
+
var MicrofrontendConfigIsomorphic = class {
|
|
1871
|
+
constructor({
|
|
1872
|
+
config,
|
|
1873
|
+
overrides,
|
|
1874
|
+
meta
|
|
1875
|
+
}) {
|
|
1876
|
+
this.childApplications = {};
|
|
1877
|
+
var _a, _b, _c, _d;
|
|
1878
|
+
MicrofrontendConfigIsomorphic.validate(config);
|
|
1879
|
+
const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
|
|
1880
|
+
this.overrides = overrides && !disableOverrides ? overrides : void 0;
|
|
1881
|
+
this.isMainConfig = isMainConfig(config);
|
|
1882
|
+
if (isMainConfig(config)) {
|
|
1883
|
+
for (const [appId, appConfig] of Object.entries(config.applications)) {
|
|
1884
|
+
const appOverrides = !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.applications[appId] : void 0;
|
|
1885
|
+
if (isDefaultApp(appConfig)) {
|
|
1886
|
+
this.defaultApplication = new DefaultApplication(appId, {
|
|
1887
|
+
app: appConfig,
|
|
1888
|
+
overrides: appOverrides
|
|
1889
|
+
});
|
|
1890
|
+
} else {
|
|
1891
|
+
this.childApplications[appId] = new ChildApplication(appId, {
|
|
1892
|
+
app: appConfig,
|
|
1893
|
+
overrides: appOverrides
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
} else {
|
|
1898
|
+
this.partOf = config.partOf;
|
|
1899
|
+
const appOverrides = !disableOverrides ? (_d = this.overrides) == null ? void 0 : _d.applications[meta.fromApp] : void 0;
|
|
1900
|
+
this.childApplications[meta.fromApp] = new ChildApplication(
|
|
1901
|
+
meta.fromApp,
|
|
1902
|
+
{
|
|
1903
|
+
// we don't know routing because we're not in the main config
|
|
1904
|
+
app: { routing: [] },
|
|
1905
|
+
overrides: appOverrides
|
|
1906
|
+
}
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
if (isMainConfig(config) && !this.defaultApplication) {
|
|
1910
|
+
throw new MicrofrontendError2(
|
|
1911
|
+
`Could not find default application in microfrontends configuration`,
|
|
1912
|
+
{
|
|
1913
|
+
type: "application",
|
|
1914
|
+
subtype: "not_found"
|
|
1915
|
+
}
|
|
1916
|
+
);
|
|
1917
|
+
}
|
|
1918
|
+
this.config = config;
|
|
1919
|
+
this.options = config.options;
|
|
1920
|
+
this.serialized = {
|
|
1921
|
+
config,
|
|
1922
|
+
overrides,
|
|
1923
|
+
meta
|
|
1924
|
+
};
|
|
1925
|
+
}
|
|
1926
|
+
static validate(config) {
|
|
1927
|
+
const c = typeof config === "string" ? (0, import_jsonc_parser2.parse)(config) : config;
|
|
1928
|
+
if (isMainConfig(c)) {
|
|
1929
|
+
validateConfigVersion(c.version);
|
|
1930
|
+
validateConfigPaths(c.applications);
|
|
1931
|
+
validateConfigDefaultApplication(c.applications);
|
|
1932
|
+
}
|
|
1933
|
+
validateConfigOptions(c.options);
|
|
1934
|
+
return c;
|
|
1935
|
+
}
|
|
1936
|
+
static fromEnv({
|
|
1937
|
+
meta,
|
|
1938
|
+
cookies
|
|
1939
|
+
}) {
|
|
1940
|
+
return new MicrofrontendConfigIsomorphic({
|
|
1941
|
+
config: (0, import_jsonc_parser2.parse)(getConfigStringFromEnv()),
|
|
1942
|
+
overrides: parseOverrides(cookies ?? []),
|
|
1943
|
+
meta
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
isOverridesDisabled() {
|
|
1947
|
+
var _a, _b;
|
|
1948
|
+
return ((_b = (_a = this.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
|
|
1949
|
+
}
|
|
1950
|
+
getConfig() {
|
|
1951
|
+
return this.config;
|
|
1952
|
+
}
|
|
1953
|
+
getApplicationsByType() {
|
|
1954
|
+
return {
|
|
1955
|
+
defaultApplication: this.defaultApplication,
|
|
1956
|
+
applications: Object.values(this.childApplications)
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
getChildApplications() {
|
|
1960
|
+
return Object.values(this.childApplications);
|
|
1961
|
+
}
|
|
1962
|
+
getAllApplications() {
|
|
1963
|
+
return [
|
|
1964
|
+
this.defaultApplication,
|
|
1965
|
+
...Object.values(this.childApplications)
|
|
1966
|
+
].filter(Boolean);
|
|
1967
|
+
}
|
|
1968
|
+
getApplication(name) {
|
|
1969
|
+
var _a;
|
|
1970
|
+
if (((_a = this.defaultApplication) == null ? void 0 : _a.name) === name) {
|
|
1971
|
+
return this.defaultApplication;
|
|
1972
|
+
}
|
|
1973
|
+
const app = this.childApplications[name];
|
|
1974
|
+
if (!app) {
|
|
1975
|
+
throw new MicrofrontendError2(
|
|
1976
|
+
`Could not find microfrontends configuration for application "${name}"`,
|
|
1977
|
+
{
|
|
1978
|
+
type: "application",
|
|
1979
|
+
subtype: "not_found"
|
|
1980
|
+
}
|
|
1981
|
+
);
|
|
1982
|
+
}
|
|
1983
|
+
return app;
|
|
1984
|
+
}
|
|
1985
|
+
getApplicationByProjectId(projectId) {
|
|
1986
|
+
var _a, _b;
|
|
1987
|
+
if (((_b = (_a = this.defaultApplication) == null ? void 0 : _a.vercel) == null ? void 0 : _b.projectId) === projectId) {
|
|
1988
|
+
return this.defaultApplication;
|
|
1989
|
+
}
|
|
1990
|
+
return Object.values(this.childApplications).find(
|
|
1991
|
+
(app) => {
|
|
1992
|
+
var _a2;
|
|
1993
|
+
return ((_a2 = app.vercel) == null ? void 0 : _a2.projectId) === projectId;
|
|
1994
|
+
}
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
/**
|
|
1998
|
+
* Returns the default application. This can throw if the default application
|
|
1999
|
+
* is undefined ( )
|
|
2000
|
+
*/
|
|
2001
|
+
getDefaultApplication() {
|
|
2002
|
+
if (!this.defaultApplication) {
|
|
2003
|
+
throw new MicrofrontendError2(
|
|
2004
|
+
`Could not find default application in microfrontends configuration`,
|
|
2005
|
+
{
|
|
2006
|
+
type: "application",
|
|
2007
|
+
subtype: "not_found"
|
|
2008
|
+
}
|
|
2009
|
+
);
|
|
2010
|
+
}
|
|
2011
|
+
return this.defaultApplication;
|
|
2012
|
+
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Returns the configured port for the local proxy
|
|
2015
|
+
*/
|
|
2016
|
+
getLocalProxyPort() {
|
|
2017
|
+
var _a, _b;
|
|
2018
|
+
return ((_b = (_a = this.config.options) == null ? void 0 : _a.localProxy) == null ? void 0 : _b.port) ?? DEFAULT_LOCAL_PROXY_PORT2;
|
|
2019
|
+
}
|
|
2020
|
+
/**
|
|
2021
|
+
* Serializes the class back to the Schema type.
|
|
2022
|
+
*
|
|
2023
|
+
* NOTE: This is used when writing the config to disk and must always match the input Schema
|
|
2024
|
+
*/
|
|
2025
|
+
toSchemaJson() {
|
|
2026
|
+
return this.serialized.config;
|
|
2027
|
+
}
|
|
2028
|
+
toClientConfig() {
|
|
2029
|
+
const applications = Object.fromEntries(
|
|
2030
|
+
Object.entries(this.childApplications).map(([name, application]) => [
|
|
2031
|
+
name,
|
|
2032
|
+
{
|
|
2033
|
+
default: false,
|
|
2034
|
+
routing: application.routing
|
|
2035
|
+
}
|
|
2036
|
+
])
|
|
2037
|
+
);
|
|
2038
|
+
if (this.defaultApplication) {
|
|
2039
|
+
applications[this.defaultApplication.name] = {
|
|
2040
|
+
default: true
|
|
2041
|
+
};
|
|
2042
|
+
}
|
|
2043
|
+
return new MicrofrontendConfigClient({
|
|
2044
|
+
applications
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2047
|
+
serialize() {
|
|
2048
|
+
return this.serialized;
|
|
2049
|
+
}
|
|
2050
|
+
};
|
|
2051
|
+
|
|
2052
|
+
// src/config-v2/microfrontends-config/isomorphic/main.ts
|
|
2053
|
+
var MicrofrontendMainConfig = class extends MicrofrontendConfigIsomorphic {
|
|
2054
|
+
constructor({
|
|
2055
|
+
config,
|
|
2056
|
+
overrides,
|
|
2057
|
+
meta
|
|
2058
|
+
}) {
|
|
2059
|
+
var _a, _b, _c;
|
|
2060
|
+
super({ config, overrides, meta });
|
|
2061
|
+
this.isMainConfig = true;
|
|
2062
|
+
this.provider = config.provider;
|
|
2063
|
+
const disableOverrides = ((_b = (_a = config.options) == null ? void 0 : _a.vercel) == null ? void 0 : _b.disableOverrides) ?? false;
|
|
2064
|
+
let defaultApplication;
|
|
2065
|
+
for (const [appId, appConfig] of Object.entries(config.applications)) {
|
|
2066
|
+
const appOverrides = !disableOverrides ? (_c = this.overrides) == null ? void 0 : _c.applications[appId] : void 0;
|
|
2067
|
+
if (isDefaultApp(appConfig)) {
|
|
2068
|
+
defaultApplication = new DefaultApplication(appId, {
|
|
2069
|
+
app: appConfig,
|
|
2070
|
+
overrides: appOverrides
|
|
2071
|
+
});
|
|
2072
|
+
} else {
|
|
2073
|
+
this.childApplications[appId] = new ChildApplication(appId, {
|
|
2074
|
+
app: appConfig,
|
|
2075
|
+
overrides: appOverrides
|
|
2076
|
+
});
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
if (!defaultApplication) {
|
|
2080
|
+
throw new MicrofrontendError2(
|
|
2081
|
+
`Could not find default application in microfrontends configuration`,
|
|
2082
|
+
{
|
|
2083
|
+
type: "application",
|
|
2084
|
+
subtype: "not_found"
|
|
2085
|
+
}
|
|
2086
|
+
);
|
|
2087
|
+
}
|
|
2088
|
+
this.defaultApplication = defaultApplication;
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
2091
|
+
|
|
2092
|
+
// src/config-v2/microfrontends/util/is-main-config.ts
|
|
2093
|
+
function isMainConfig2(c) {
|
|
2094
|
+
return !("partOf" in c);
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
// src/config-v2/microfrontends/server/index.ts
|
|
2098
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
2099
|
+
var import_node_path4 = require("path");
|
|
2100
|
+
|
|
2101
|
+
// src/config-v2/microfrontends-config/isomorphic/child.ts
|
|
2102
|
+
var MicrofrontendChildConfig = class extends MicrofrontendConfigIsomorphic {
|
|
2103
|
+
constructor({
|
|
2104
|
+
config,
|
|
2105
|
+
overrides,
|
|
2106
|
+
meta
|
|
2107
|
+
}) {
|
|
2108
|
+
super({ config, overrides, meta });
|
|
2109
|
+
this.isMainConfig = false;
|
|
2110
|
+
this.partOf = config.partOf;
|
|
2111
|
+
}
|
|
2112
|
+
};
|
|
2113
|
+
|
|
2114
|
+
// src/config-v2/microfrontends/isomorphic/index.ts
|
|
2115
|
+
var Microfrontends = class {
|
|
2116
|
+
constructor({
|
|
2117
|
+
config,
|
|
2118
|
+
overrides,
|
|
2119
|
+
meta
|
|
2120
|
+
}) {
|
|
2121
|
+
if (isMainConfig(config)) {
|
|
2122
|
+
this.config = new MicrofrontendMainConfig({ config, overrides, meta });
|
|
2123
|
+
} else {
|
|
2124
|
+
this.config = new MicrofrontendChildConfig({ config, overrides, meta });
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
isChildConfig() {
|
|
2128
|
+
return this.config instanceof MicrofrontendChildConfig;
|
|
2129
|
+
}
|
|
2130
|
+
static fromEnv({
|
|
2131
|
+
cookies,
|
|
2132
|
+
meta
|
|
2133
|
+
}) {
|
|
2134
|
+
const config = MicrofrontendConfigIsomorphic.fromEnv({
|
|
2135
|
+
cookies,
|
|
2136
|
+
meta
|
|
2137
|
+
});
|
|
2138
|
+
return new Microfrontends(config.serialize());
|
|
2139
|
+
}
|
|
2140
|
+
};
|
|
2141
|
+
|
|
2142
|
+
// src/config-v2/microfrontends/server/validation.ts
|
|
2143
|
+
var import_jsonc_parser3 = require("jsonc-parser");
|
|
2144
|
+
var import_ajv2 = require("ajv");
|
|
2145
|
+
|
|
2146
|
+
// schema/schema-v2.json
|
|
2147
|
+
var schema_v2_default = {
|
|
2148
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
2149
|
+
$ref: "#/definitions/Config",
|
|
2150
|
+
definitions: {
|
|
2151
|
+
Config: {
|
|
2152
|
+
anyOf: [
|
|
2153
|
+
{
|
|
2154
|
+
$ref: "#/definitions/MainConfig"
|
|
2155
|
+
},
|
|
2156
|
+
{
|
|
2157
|
+
$ref: "#/definitions/ChildConfig"
|
|
2158
|
+
}
|
|
2159
|
+
]
|
|
2160
|
+
},
|
|
2161
|
+
MainConfig: {
|
|
2162
|
+
type: "object",
|
|
2163
|
+
properties: {
|
|
2164
|
+
$schema: {
|
|
2165
|
+
type: "string"
|
|
2166
|
+
},
|
|
2167
|
+
version: {
|
|
2168
|
+
type: "string",
|
|
2169
|
+
const: "2"
|
|
2170
|
+
},
|
|
2171
|
+
options: {
|
|
2172
|
+
$ref: "#/definitions/Options"
|
|
2173
|
+
},
|
|
2174
|
+
remotes: {
|
|
2175
|
+
type: "object",
|
|
2176
|
+
additionalProperties: {
|
|
2177
|
+
$ref: "#/definitions/Application"
|
|
2178
|
+
},
|
|
2179
|
+
description: "Applications that only serve a subset of the microfrontend routes only need to reference the name of the primary application that owns the full microfrontends configuration."
|
|
2180
|
+
},
|
|
2181
|
+
provider: {
|
|
2182
|
+
$ref: "#/definitions/Provider"
|
|
2183
|
+
},
|
|
2184
|
+
applications: {
|
|
2185
|
+
$ref: "#/definitions/ApplicationRouting",
|
|
2186
|
+
description: "Mapping of application names to the routes that they host. Only needs to be defined in the application that owns the primary microfrontend domain"
|
|
2187
|
+
}
|
|
2188
|
+
},
|
|
2189
|
+
required: ["applications", "provider", "version"]
|
|
2190
|
+
},
|
|
2191
|
+
Options: {
|
|
2192
|
+
type: "object",
|
|
2193
|
+
properties: {
|
|
2194
|
+
vercel: {
|
|
2195
|
+
$ref: "#/definitions/VercelOptions",
|
|
2196
|
+
description: "Micro-Frontends wide options for Vercel."
|
|
2197
|
+
},
|
|
2198
|
+
localProxy: {
|
|
2199
|
+
$ref: "#/definitions/LocalProxyOptions",
|
|
2200
|
+
description: "Options for local proxy."
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
},
|
|
2204
|
+
VercelOptions: {
|
|
2205
|
+
type: "object",
|
|
2206
|
+
properties: {
|
|
2207
|
+
previewDeploymentSuffix: {
|
|
2208
|
+
type: "string",
|
|
2209
|
+
description: "If your team uses a custom Preview Deployment Suffix, please specify it here. See https://vercel.com/docs/deployments/preview-deployment-suffix. The default is `vercel.app`."
|
|
2210
|
+
},
|
|
2211
|
+
teamSlug: {
|
|
2212
|
+
type: "string",
|
|
2213
|
+
description: "Team slug for the Vercel team"
|
|
2214
|
+
},
|
|
2215
|
+
disableOverrides: {
|
|
2216
|
+
type: "boolean",
|
|
2217
|
+
description: "If you want to disable the overrides for the site. For example, if you are managing rewrites between applications externally, you may wish to disable the overrides on the toolbar as they will have no effect."
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
},
|
|
2221
|
+
LocalProxyOptions: {
|
|
2222
|
+
type: "object",
|
|
2223
|
+
properties: {
|
|
2224
|
+
port: {
|
|
2225
|
+
type: "number",
|
|
2226
|
+
description: "The port number used by the local proxy server.\n\nThe default is `3024`."
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
},
|
|
2230
|
+
Application: {
|
|
2231
|
+
anyOf: [
|
|
2232
|
+
{
|
|
2233
|
+
$ref: "#/definitions/DefaultApplication"
|
|
2234
|
+
},
|
|
2235
|
+
{
|
|
2236
|
+
$ref: "#/definitions/ChildApplication"
|
|
2237
|
+
}
|
|
2238
|
+
]
|
|
2239
|
+
},
|
|
2240
|
+
DefaultApplication: {
|
|
2241
|
+
type: "object",
|
|
2242
|
+
properties: {
|
|
2243
|
+
vercel: {
|
|
2244
|
+
$ref: "#/definitions/Vercel"
|
|
2245
|
+
},
|
|
2246
|
+
development: {
|
|
2247
|
+
$ref: "#/definitions/Development"
|
|
2248
|
+
},
|
|
2249
|
+
production: {
|
|
2250
|
+
$ref: "#/definitions/HostConfig"
|
|
2251
|
+
}
|
|
2252
|
+
},
|
|
2253
|
+
required: ["production"]
|
|
2254
|
+
},
|
|
2255
|
+
Vercel: {
|
|
2256
|
+
type: "object",
|
|
2257
|
+
properties: {
|
|
2258
|
+
projectId: {
|
|
2259
|
+
type: "string",
|
|
2260
|
+
description: "Vercel project ID"
|
|
2261
|
+
},
|
|
2262
|
+
routeSpeedInsightsToDefaultZone: {
|
|
2263
|
+
type: "boolean"
|
|
2264
|
+
}
|
|
2265
|
+
},
|
|
2266
|
+
required: ["projectId"]
|
|
2267
|
+
},
|
|
2268
|
+
Development: {
|
|
2269
|
+
type: "object",
|
|
2270
|
+
properties: {
|
|
2271
|
+
local: {
|
|
2272
|
+
$ref: "#/definitions/LocalHostConfig"
|
|
2273
|
+
},
|
|
2274
|
+
fallback: {
|
|
2275
|
+
$ref: "#/definitions/HostConfig",
|
|
2276
|
+
description: "Fallback for local development, could be a host config that points to any environment. If this is not provided, or the application is not running - requests to the application in local development will error."
|
|
2277
|
+
},
|
|
2278
|
+
task: {
|
|
2279
|
+
type: "string",
|
|
2280
|
+
description: "Optional task to run when starting the development server. Should reference a script in the package.json of the application."
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
},
|
|
2284
|
+
LocalHostConfig: {
|
|
2285
|
+
type: "object",
|
|
2286
|
+
properties: {
|
|
2287
|
+
host: {
|
|
2288
|
+
type: "string",
|
|
2289
|
+
description: "The hostname or IP address of the server. This can be a domain name (e.g., `example.com`) or an IP address (e.g., `192.168.1.1`)."
|
|
2290
|
+
},
|
|
2291
|
+
protocol: {
|
|
2292
|
+
type: "string",
|
|
2293
|
+
enum: ["http", "https"],
|
|
2294
|
+
description: 'The protocol to be used for the connection.\n- `http`: Hypertext Transfer Protocol (HTTP).\n- `https`: Secure Hypertext Transfer Protocol (HTTPS).\n\n* @defaultValue "http" for local development, "https" for otherwise'
|
|
2295
|
+
},
|
|
2296
|
+
port: {
|
|
2297
|
+
type: "number",
|
|
2298
|
+
description: "The port number to be used for the connection. Common values include `80` for HTTP and `443` for HTTPS."
|
|
2299
|
+
}
|
|
2300
|
+
}
|
|
2301
|
+
},
|
|
2302
|
+
HostConfig: {
|
|
2303
|
+
type: "object",
|
|
2304
|
+
properties: {
|
|
2305
|
+
protocol: {
|
|
2306
|
+
type: "string",
|
|
2307
|
+
enum: ["http", "https"],
|
|
2308
|
+
description: 'The protocol to be used for the connection.\n- `http`: Hypertext Transfer Protocol (HTTP).\n- `https`: Secure Hypertext Transfer Protocol (HTTPS).\n\n* @defaultValue "http" for local development, "https" for otherwise'
|
|
2309
|
+
},
|
|
2310
|
+
host: {
|
|
2311
|
+
type: "string",
|
|
2312
|
+
description: "The hostname or IP address of the server. This can be a domain name (e.g., `example.com`) or an IP address (e.g., `192.168.1.1`)."
|
|
2313
|
+
},
|
|
2314
|
+
port: {
|
|
2315
|
+
type: "number",
|
|
2316
|
+
description: "The port number to be used for the connection. Common values include `80` for HTTP and `443` for HTTPS."
|
|
2317
|
+
}
|
|
2318
|
+
},
|
|
2319
|
+
required: ["host"]
|
|
2320
|
+
},
|
|
2321
|
+
ChildApplication: {
|
|
2322
|
+
type: "object",
|
|
2323
|
+
properties: {
|
|
2324
|
+
vercel: {
|
|
2325
|
+
$ref: "#/definitions/Vercel"
|
|
2326
|
+
},
|
|
2327
|
+
development: {
|
|
2328
|
+
$ref: "#/definitions/Development"
|
|
2329
|
+
},
|
|
2330
|
+
routing: {
|
|
2331
|
+
$ref: "#/definitions/Routing",
|
|
2332
|
+
description: "Groups of path expressions that are routed to this application."
|
|
2333
|
+
},
|
|
2334
|
+
production: {
|
|
2335
|
+
$ref: "#/definitions/HostConfig"
|
|
2336
|
+
}
|
|
2337
|
+
},
|
|
2338
|
+
required: ["routing"]
|
|
2339
|
+
},
|
|
2340
|
+
Routing: {
|
|
2341
|
+
type: "array",
|
|
2342
|
+
items: {
|
|
2343
|
+
$ref: "#/definitions/PathGroup"
|
|
2344
|
+
}
|
|
2345
|
+
},
|
|
2346
|
+
PathGroup: {
|
|
2347
|
+
type: "object",
|
|
2348
|
+
properties: {
|
|
2349
|
+
group: {
|
|
2350
|
+
type: "string",
|
|
2351
|
+
description: "Optional group name for the paths"
|
|
2352
|
+
},
|
|
2353
|
+
flag: {
|
|
2354
|
+
type: "string",
|
|
2355
|
+
description: "flag name that can be used to enable/disable all paths in the group"
|
|
2356
|
+
},
|
|
2357
|
+
routeToDefaultApplication: {
|
|
2358
|
+
type: "boolean",
|
|
2359
|
+
description: "True to route the request to the default application for this micro-frontends set-up. This must be `true` when using `flag` or when you want to use custom logic to make the routing decision for this group of paths."
|
|
2360
|
+
},
|
|
2361
|
+
paths: {
|
|
2362
|
+
type: "array",
|
|
2363
|
+
items: {
|
|
2364
|
+
type: "string"
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
},
|
|
2368
|
+
required: ["paths"]
|
|
2369
|
+
},
|
|
2370
|
+
Provider: {
|
|
2371
|
+
type: "string",
|
|
2372
|
+
enum: ["vercel", "other"]
|
|
2373
|
+
},
|
|
2374
|
+
ApplicationRouting: {
|
|
2375
|
+
type: "object",
|
|
2376
|
+
additionalProperties: {
|
|
2377
|
+
$ref: "#/definitions/Application"
|
|
2378
|
+
}
|
|
2379
|
+
},
|
|
2380
|
+
ChildConfig: {
|
|
2381
|
+
type: "object",
|
|
2382
|
+
properties: {
|
|
2383
|
+
$schema: {
|
|
2384
|
+
type: "string"
|
|
2385
|
+
},
|
|
2386
|
+
version: {
|
|
2387
|
+
type: "string",
|
|
2388
|
+
const: "2"
|
|
2389
|
+
},
|
|
2390
|
+
options: {
|
|
2391
|
+
$ref: "#/definitions/Options"
|
|
2392
|
+
},
|
|
2393
|
+
remotes: {
|
|
2394
|
+
type: "object",
|
|
2395
|
+
additionalProperties: {
|
|
2396
|
+
$ref: "#/definitions/Application"
|
|
2397
|
+
},
|
|
2398
|
+
description: "Applications that only serve a subset of the microfrontend routes only need to reference the name of the primary application that owns the full microfrontends configuration."
|
|
2399
|
+
},
|
|
2400
|
+
partOf: {
|
|
2401
|
+
type: "string",
|
|
2402
|
+
description: "Applications that only serve a subset of the microfrontend routes only need to reference the name of the primary application that owns the full microfrontends configuration."
|
|
2403
|
+
}
|
|
2404
|
+
},
|
|
2405
|
+
required: ["partOf", "version"]
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
};
|
|
2409
|
+
|
|
2410
|
+
// src/config-v2/schema/utils/load.ts
|
|
2411
|
+
var SCHEMA2 = schema_v2_default;
|
|
2412
|
+
|
|
2413
|
+
// src/config-v2/microfrontends/server/validation.ts
|
|
2414
|
+
function validateSchema2(configString) {
|
|
2415
|
+
const parsedConfig = (0, import_jsonc_parser3.parse)(configString);
|
|
2416
|
+
const ajv = new import_ajv2.Ajv();
|
|
2417
|
+
const validate = ajv.compile(SCHEMA2);
|
|
2418
|
+
const isValid = validate(parsedConfig);
|
|
2419
|
+
if (!isValid) {
|
|
2420
|
+
throw new MicrofrontendError(
|
|
2421
|
+
`Invalid config: ${ajv.errorsText(validate.errors)}`,
|
|
2422
|
+
{ type: "config", subtype: "does_not_match_schema" }
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
return parsedConfig;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
// src/config-v2/microfrontends/server/index.ts
|
|
2429
|
+
var MicrofrontendsServer = class extends Microfrontends {
|
|
2430
|
+
/**
|
|
2431
|
+
* Writes the configuration to a file.
|
|
2432
|
+
*/
|
|
2433
|
+
writeConfig(opts = {
|
|
2434
|
+
pretty: true
|
|
2435
|
+
}) {
|
|
2436
|
+
const outputPath = getOutputFilePath();
|
|
2437
|
+
import_node_fs3.default.mkdirSync((0, import_node_path4.dirname)(outputPath), { recursive: true });
|
|
2438
|
+
import_node_fs3.default.writeFileSync(
|
|
2439
|
+
outputPath,
|
|
2440
|
+
JSON.stringify(
|
|
2441
|
+
this.config.toSchemaJson(),
|
|
2442
|
+
null,
|
|
2443
|
+
opts.pretty ?? true ? 2 : void 0
|
|
2444
|
+
)
|
|
2445
|
+
);
|
|
2446
|
+
}
|
|
2447
|
+
// --------- Static Methods ---------
|
|
2448
|
+
/**
|
|
2449
|
+
* Generates a MicrofrontendsServer instance from an unknown object.
|
|
2450
|
+
*/
|
|
2451
|
+
static fromUnknown({
|
|
2452
|
+
config,
|
|
2453
|
+
cookies,
|
|
2454
|
+
meta
|
|
2455
|
+
}) {
|
|
2456
|
+
const overrides = cookies ? parseOverrides(cookies) : void 0;
|
|
2457
|
+
if (typeof config === "string") {
|
|
2458
|
+
return new MicrofrontendsServer({
|
|
2459
|
+
config: MicrofrontendsServer.validate(config),
|
|
2460
|
+
overrides,
|
|
2461
|
+
meta
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
if (typeof config === "object") {
|
|
2465
|
+
return new MicrofrontendsServer({
|
|
2466
|
+
config,
|
|
2467
|
+
overrides,
|
|
2468
|
+
meta
|
|
2469
|
+
});
|
|
2470
|
+
}
|
|
2471
|
+
throw new MicrofrontendError2(
|
|
2472
|
+
"Invalid config: must be a string or an object",
|
|
2473
|
+
{ type: "config", subtype: "does_not_match_schema" }
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2476
|
+
/**
|
|
2477
|
+
* Generates a MicrofrontendsServer instance from the environment.
|
|
2478
|
+
* Uses additional validation that is only available when in a node runtime
|
|
2479
|
+
*/
|
|
2480
|
+
static fromEnv({
|
|
2481
|
+
cookies,
|
|
2482
|
+
meta
|
|
2483
|
+
}) {
|
|
2484
|
+
return new MicrofrontendsServer({
|
|
2485
|
+
config: MicrofrontendsServer.validate(getConfigStringFromEnv()),
|
|
2486
|
+
overrides: parseOverrides(cookies),
|
|
2487
|
+
meta
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2490
|
+
/**
|
|
2491
|
+
* Validates the configuration against the JSON schema
|
|
2492
|
+
*/
|
|
2493
|
+
static validate(config) {
|
|
2494
|
+
if (typeof config === "string") {
|
|
2495
|
+
const c = validateSchema2(config);
|
|
2496
|
+
return c;
|
|
2497
|
+
}
|
|
2498
|
+
return config;
|
|
2499
|
+
}
|
|
2500
|
+
/*
|
|
2501
|
+
* Generates a MicrofrontendsServer instance from a file.
|
|
2502
|
+
*/
|
|
2503
|
+
static fromFile({
|
|
2504
|
+
filePath,
|
|
2505
|
+
cookies,
|
|
2506
|
+
meta
|
|
2507
|
+
}) {
|
|
2508
|
+
try {
|
|
2509
|
+
const config = import_node_fs3.default.readFileSync(filePath, "utf-8");
|
|
2510
|
+
return new MicrofrontendsServer({
|
|
2511
|
+
config: MicrofrontendsServer.validate(config),
|
|
2512
|
+
overrides: cookies ? parseOverrides(cookies) : void 0,
|
|
2513
|
+
meta
|
|
2514
|
+
});
|
|
2515
|
+
} catch (e) {
|
|
2516
|
+
throw MicrofrontendError2.handle(e, {
|
|
2517
|
+
fileName: filePath
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
/*
|
|
2522
|
+
* Generates a MicrofrontendMainConfig instance from a file.
|
|
2523
|
+
*/
|
|
2524
|
+
static fromMainConfigFile({
|
|
2525
|
+
filePath,
|
|
2526
|
+
overrides
|
|
2527
|
+
}) {
|
|
2528
|
+
try {
|
|
2529
|
+
const config = import_node_fs3.default.readFileSync(filePath, "utf-8");
|
|
2530
|
+
const validatedConfig = MicrofrontendsServer.validate(config);
|
|
2531
|
+
if (!isMainConfig(validatedConfig)) {
|
|
2532
|
+
throw new MicrofrontendError2(
|
|
2533
|
+
`${filePath} is not a main microfrontend config`,
|
|
2534
|
+
{
|
|
2535
|
+
type: "config",
|
|
2536
|
+
subtype: "invalid_main_path"
|
|
2537
|
+
}
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
const [defaultApplication] = Object.entries(validatedConfig.applications).filter(([, app]) => isDefaultApp(app)).map(([name]) => name);
|
|
2541
|
+
if (!defaultApplication) {
|
|
2542
|
+
throw new MicrofrontendError2(
|
|
2543
|
+
`No default application found. At least one application needs to be the default by omitting routing.`,
|
|
2544
|
+
{ type: "config", subtype: "no_default_application" }
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2547
|
+
return new MicrofrontendsServer({
|
|
2548
|
+
config: validatedConfig,
|
|
2549
|
+
overrides,
|
|
2550
|
+
meta: { fromApp: defaultApplication }
|
|
2551
|
+
});
|
|
2552
|
+
} catch (e) {
|
|
2553
|
+
throw MicrofrontendError2.handle(e, {
|
|
2554
|
+
fileName: filePath
|
|
2555
|
+
});
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
};
|
|
2559
|
+
|
|
2560
|
+
// src/bin/local-proxy.ts
|
|
2561
|
+
var MFE_DEBUG = process.env.MFE_DEBUG;
|
|
2562
|
+
var mfeDebug = (message) => {
|
|
2563
|
+
if (MFE_DEBUG === "true" || MFE_DEBUG === "1") {
|
|
2564
|
+
console.log(message);
|
|
2565
|
+
}
|
|
2566
|
+
};
|
|
2567
|
+
function isV2Config(c) {
|
|
2568
|
+
return "isMainConfig" in c;
|
|
2569
|
+
}
|
|
2570
|
+
var LocalProxy = class {
|
|
2571
|
+
constructor(config, {
|
|
2572
|
+
localApps,
|
|
2573
|
+
proxyPort
|
|
2574
|
+
}) {
|
|
2575
|
+
this.config = config;
|
|
2576
|
+
this.localApps = localApps;
|
|
2577
|
+
this.proxyPort = proxyPort ?? this.config.getLocalProxyPort();
|
|
2578
|
+
this.proxy = import_http_proxy.default.createProxyServer({ secure: true });
|
|
2579
|
+
this.proxy.on("error", (err, req, res) => {
|
|
2580
|
+
if (res instanceof http.ServerResponse) {
|
|
2581
|
+
res.writeHead(500, {
|
|
2582
|
+
"Content-Type": "text/plain"
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
const target = this.getTarget(req);
|
|
2586
|
+
res.end(
|
|
2587
|
+
`Error proxying request to ${target.application}. Is the server running locally on port ${target.port}?`
|
|
2588
|
+
);
|
|
2589
|
+
console.error(`Error proxying request for ${target.application}: `, err);
|
|
2590
|
+
});
|
|
2591
|
+
}
|
|
2592
|
+
static fromFile(filePath, {
|
|
2593
|
+
localApps,
|
|
2594
|
+
proxyPort
|
|
2595
|
+
}) {
|
|
2596
|
+
let configV2;
|
|
2597
|
+
try {
|
|
2598
|
+
configV2 = MicrofrontendsServer.fromMainConfigFile({
|
|
2599
|
+
filePath
|
|
2600
|
+
});
|
|
2601
|
+
} catch (error) {
|
|
2602
|
+
if ((0, import_types2.isNativeError)(error)) {
|
|
2603
|
+
mfeDebug(`unable to parse v2 version: ${error.toString()}`);
|
|
2604
|
+
}
|
|
2605
|
+
}
|
|
2606
|
+
if (configV2) {
|
|
2607
|
+
if (isMainConfig2(configV2.config)) {
|
|
2608
|
+
return new LocalProxy(configV2.config, { localApps, proxyPort });
|
|
2609
|
+
}
|
|
2610
|
+
throw new Error("Got child config after parsing main config");
|
|
2611
|
+
}
|
|
2612
|
+
const configV1 = MicrofrontendConfig.fromFile({ filePath });
|
|
2613
|
+
return new LocalProxy(configV1, { localApps, proxyPort });
|
|
2614
|
+
}
|
|
2615
|
+
getDefaultHost(config) {
|
|
2616
|
+
let defaultApp;
|
|
2617
|
+
if (isV2Config(config)) {
|
|
2618
|
+
defaultApp = config.getDefaultApplication();
|
|
2619
|
+
} else {
|
|
2620
|
+
defaultApp = config.getDefaultZone();
|
|
2621
|
+
}
|
|
2622
|
+
return this.getApplicationTarget(defaultApp);
|
|
2623
|
+
}
|
|
2624
|
+
getApplicationTarget(application) {
|
|
2625
|
+
var _a, _b;
|
|
2626
|
+
const useDev = this.localApps.includes(application.name);
|
|
2627
|
+
let host = useDev ? application.development.local : application.production;
|
|
2628
|
+
if (!host) {
|
|
2629
|
+
throw new Error(
|
|
2630
|
+
`${application.name} does not have a production host configured`
|
|
2631
|
+
);
|
|
2632
|
+
}
|
|
2633
|
+
if ((_b = (_a = application.overrides) == null ? void 0 : _a.environment) == null ? void 0 : _b.host) {
|
|
2634
|
+
host = application.overrides.environment;
|
|
2635
|
+
}
|
|
2636
|
+
const protocol = host.protocol;
|
|
2637
|
+
const hostname = host.host;
|
|
2638
|
+
const port = host.port;
|
|
2639
|
+
return {
|
|
2640
|
+
url: host.toUrl(),
|
|
2641
|
+
protocol,
|
|
2642
|
+
hostname,
|
|
2643
|
+
port,
|
|
2644
|
+
application: application.name
|
|
2645
|
+
};
|
|
2646
|
+
}
|
|
2647
|
+
/**
|
|
2648
|
+
* To enable preview deployments in localhost, we need to intercept some auth requests
|
|
2649
|
+
* and make sure they proxy to the correct domain. The toolbar will initiate the `vercel-auth-redirect`
|
|
2650
|
+
* with a `_host_override` param so that we can properly trigger the redirect flow for the
|
|
2651
|
+
* protected host.
|
|
2652
|
+
*/
|
|
2653
|
+
getAuthTarget(request2, config) {
|
|
2654
|
+
var _a, _b;
|
|
2655
|
+
const url = new URL(request2.url ?? "", `http://${request2.headers.host}`);
|
|
2656
|
+
const isAuthRedirect = (_a = request2.url) == null ? void 0 : _a.startsWith(
|
|
2657
|
+
"/.well-known/vercel-auth-redirect"
|
|
2658
|
+
);
|
|
2659
|
+
const isSsoRedirect = (_b = request2.url) == null ? void 0 : _b.startsWith("/sso-api");
|
|
2660
|
+
const isJWTRedirect = url.searchParams.has("_vercel_jwt");
|
|
2661
|
+
const defaultHost = this.getDefaultHost(config);
|
|
2662
|
+
let hostname = null;
|
|
2663
|
+
let path3 = request2.url;
|
|
2664
|
+
if (isAuthRedirect) {
|
|
2665
|
+
hostname = url.searchParams.get("_host_override");
|
|
2666
|
+
}
|
|
2667
|
+
if (isSsoRedirect) {
|
|
2668
|
+
hostname = "vercel.com";
|
|
2669
|
+
}
|
|
2670
|
+
if (isJWTRedirect) {
|
|
2671
|
+
hostname = url.searchParams.get("_host_override");
|
|
2672
|
+
url.searchParams.delete("_host_override");
|
|
2673
|
+
path3 = `${url.pathname}${url.search}`;
|
|
2674
|
+
}
|
|
2675
|
+
if (!hostname) {
|
|
2676
|
+
return void 0;
|
|
2677
|
+
}
|
|
2678
|
+
return { ...defaultHost, path: path3, hostname, protocol: "https", port: 443 };
|
|
2679
|
+
}
|
|
2680
|
+
getConfigWithOverrides(cookies) {
|
|
2681
|
+
if (isV2Config(this.config)) {
|
|
2682
|
+
const cookieOverrides2 = parseOverrides(
|
|
2683
|
+
Object.entries(cookies).map(([name, value]) => ({ name, value }))
|
|
2684
|
+
);
|
|
2685
|
+
const hasOverrides2 = Object.keys(cookieOverrides2.applications).length > 0;
|
|
2686
|
+
const fromApp = this.config.getDefaultApplication().name;
|
|
2687
|
+
const serialized = this.config.serialize().config;
|
|
2688
|
+
if (!isMainConfig(serialized)) {
|
|
2689
|
+
throw new Error("unreachable");
|
|
2690
|
+
}
|
|
2691
|
+
return hasOverrides2 ? new MicrofrontendMainConfig({
|
|
2692
|
+
config: serialized,
|
|
2693
|
+
meta: { fromApp },
|
|
2694
|
+
overrides: cookieOverrides2
|
|
2695
|
+
}) : this.config;
|
|
2696
|
+
}
|
|
2697
|
+
const cookieOverrides = Overrides.parseOverrides(
|
|
2698
|
+
Object.entries(cookies).map(([name, value]) => ({ name, value }))
|
|
2699
|
+
);
|
|
2700
|
+
const hasOverrides = Object.keys(cookieOverrides.applications).length > 0;
|
|
2701
|
+
return hasOverrides ? new MicrofrontendConfig({
|
|
2702
|
+
config: this.config.serialize().config,
|
|
2703
|
+
overrides: cookieOverrides
|
|
2704
|
+
}) : this.config;
|
|
2705
|
+
}
|
|
2706
|
+
getTarget(request2) {
|
|
2707
|
+
const cookies = (0, import_cookie.parse)(request2.headers.cookie || "");
|
|
2708
|
+
const config = this.getConfigWithOverrides(cookies);
|
|
2709
|
+
const path3 = request2.url;
|
|
2710
|
+
if (!path3) {
|
|
2711
|
+
return this.getDefaultHost(config);
|
|
2712
|
+
}
|
|
2713
|
+
const authTarget = this.getAuthTarget(request2, config);
|
|
2714
|
+
if (authTarget) {
|
|
2715
|
+
return authTarget;
|
|
2716
|
+
}
|
|
2717
|
+
const url = new URL(`http://example.com${path3}`);
|
|
2718
|
+
const pathname = url.pathname;
|
|
2719
|
+
if (isV2Config(config)) {
|
|
2720
|
+
const target = this.findMatchingApplicationV2(
|
|
2721
|
+
path3,
|
|
2722
|
+
pathname,
|
|
2723
|
+
config.getChildApplications()
|
|
2724
|
+
);
|
|
2725
|
+
if (target)
|
|
2726
|
+
return target;
|
|
2727
|
+
} else {
|
|
2728
|
+
const target = this.findMatchingApplicationV1(
|
|
2729
|
+
path3,
|
|
2730
|
+
pathname,
|
|
2731
|
+
config.getAllApplications()
|
|
2732
|
+
);
|
|
2733
|
+
if (target)
|
|
2734
|
+
return target;
|
|
2735
|
+
}
|
|
2736
|
+
const defaultHost = this.getDefaultHost(config);
|
|
2737
|
+
mfeDebug(
|
|
2738
|
+
`no matching routes, routing ${path3} to default application: ${JSON.stringify(defaultHost)}`
|
|
2739
|
+
);
|
|
2740
|
+
return { path: path3, ...defaultHost };
|
|
2741
|
+
}
|
|
2742
|
+
findMatchingApplicationV1(path3, pathname, applications) {
|
|
2743
|
+
for (const application of Object.values(applications)) {
|
|
2744
|
+
if (application.routing) {
|
|
2745
|
+
for (const group of application.routing.matches) {
|
|
2746
|
+
for (const childPath of group.paths) {
|
|
2747
|
+
const regexp = (0, import_path_to_regexp4.pathToRegexp)(childPath);
|
|
2748
|
+
if (regexp.test(pathname)) {
|
|
2749
|
+
const target = this.getApplicationTarget(application);
|
|
2750
|
+
mfeDebug(
|
|
2751
|
+
`routing ${path3} to '${target.application}' at ${target.hostname}`
|
|
2752
|
+
);
|
|
2753
|
+
return { path: path3, ...target };
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
if (application.routing.assetPrefix) {
|
|
2757
|
+
const pattern = (0, import_path_to_regexp4.pathToRegexp)(
|
|
2758
|
+
`/${application.routing.assetPrefix}/:path+`
|
|
2759
|
+
);
|
|
2760
|
+
if (pattern.test(pathname)) {
|
|
2761
|
+
const target = this.getApplicationTarget(application);
|
|
2762
|
+
mfeDebug(
|
|
2763
|
+
`routing ${path3} to '${target.application}' at ${target.hostname}`
|
|
2764
|
+
);
|
|
2765
|
+
return { path: path3, ...target };
|
|
2766
|
+
}
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
return null;
|
|
2772
|
+
}
|
|
2773
|
+
findMatchingApplicationV2(path3, pathname, applications) {
|
|
2774
|
+
for (const application of Object.values(applications)) {
|
|
2775
|
+
for (const group of application.routing) {
|
|
2776
|
+
for (const childPath of group.paths) {
|
|
2777
|
+
const regexp = (0, import_path_to_regexp4.pathToRegexp)(childPath);
|
|
2778
|
+
if (regexp.test(pathname)) {
|
|
2779
|
+
const target = this.getApplicationTarget(application);
|
|
2780
|
+
mfeDebug(
|
|
2781
|
+
`routing ${path3} to '${target.application}' at ${target.hostname}`
|
|
2782
|
+
);
|
|
2783
|
+
return { path: path3, ...target };
|
|
2784
|
+
}
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
return null;
|
|
1492
2789
|
}
|
|
1493
2790
|
// Handles requests that return data from the local proxy itself.
|
|
1494
2791
|
// Returns true if the request was handled, false otherwise.
|