@vercel/microfrontends 0.17.4 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -159
- package/dist/bin/cli.cjs +202 -139
- package/dist/config.cjs +103 -57
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.ts +4 -4
- package/dist/config.js +103 -57
- package/dist/config.js.map +1 -1
- package/dist/{index-f094deb1.d.ts → index-09b1ddf9.d.ts} +16 -19
- package/dist/{index-5fcf0863.d.ts → index-2f78c0ca.d.ts} +44 -17
- package/dist/microfrontends/server.cjs +180 -133
- package/dist/microfrontends/server.cjs.map +1 -1
- package/dist/microfrontends/server.d.ts +4 -4
- package/dist/microfrontends/server.js +180 -133
- package/dist/microfrontends/server.js.map +1 -1
- package/dist/microfrontends.cjs +104 -58
- package/dist/microfrontends.cjs.map +1 -1
- package/dist/microfrontends.d.ts +4 -4
- package/dist/microfrontends.js +104 -58
- package/dist/microfrontends.js.map +1 -1
- package/dist/next/client.cjs +1 -1
- package/dist/next/client.cjs.map +1 -1
- package/dist/next/client.d.ts +1 -1
- package/dist/next/client.js +1 -1
- package/dist/next/client.js.map +1 -1
- package/dist/next/config.cjs +197 -145
- package/dist/next/config.cjs.map +1 -1
- package/dist/next/config.js +197 -145
- package/dist/next/config.js.map +1 -1
- package/dist/next/endpoints.d.ts +2 -2
- package/dist/next/middleware.cjs +121 -80
- package/dist/next/middleware.cjs.map +1 -1
- package/dist/next/middleware.js +121 -80
- package/dist/next/middleware.js.map +1 -1
- package/dist/next/testing.cjs +110 -63
- package/dist/next/testing.cjs.map +1 -1
- package/dist/next/testing.d.ts +4 -4
- package/dist/next/testing.js +110 -63
- package/dist/next/testing.js.map +1 -1
- package/dist/overrides.d.ts +3 -3
- package/dist/schema.d.ts +1 -1
- package/dist/{types-5900be7c.d.ts → types-4ef2bddb.d.ts} +1 -1
- package/dist/{types-ecd7b91b.d.ts → types-b6d38aea.d.ts} +1 -1
- package/dist/utils/mfe-port.cjs +181 -134
- package/dist/utils/mfe-port.cjs.map +1 -1
- package/dist/utils/mfe-port.js +181 -134
- package/dist/utils/mfe-port.js.map +1 -1
- package/dist/validation.cjs +74 -73
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js +74 -73
- package/dist/validation.js.map +1 -1
- package/package.json +19 -3
- package/schema/schema.json +80 -73
package/dist/next/endpoints.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextResponse } from 'next/server';
|
|
2
|
-
import { C as ClientConfig } from '../types-
|
|
3
|
-
import '../index-
|
|
2
|
+
import { C as ClientConfig } from '../types-4ef2bddb.js';
|
|
3
|
+
import '../index-2f78c0ca.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Data that is returned from the `.well-known/vercel/microfrontends/client-config`
|
package/dist/next/middleware.cjs
CHANGED
|
@@ -35,7 +35,7 @@ var import_jsonc_parser = require("jsonc-parser");
|
|
|
35
35
|
// src/config/errors.ts
|
|
36
36
|
var MicrofrontendError = class extends Error {
|
|
37
37
|
constructor(message, opts) {
|
|
38
|
-
super(message);
|
|
38
|
+
super(message, { cause: opts?.cause });
|
|
39
39
|
this.name = "MicrofrontendsError";
|
|
40
40
|
this.source = opts?.source ?? "@vercel/microfrontends";
|
|
41
41
|
this.type = opts?.type ?? "unknown";
|
|
@@ -257,7 +257,8 @@ var validateConfigPaths = (applicationConfigsById) => {
|
|
|
257
257
|
if (isDefaultApp(app)) {
|
|
258
258
|
continue;
|
|
259
259
|
}
|
|
260
|
-
|
|
260
|
+
const childApp = app;
|
|
261
|
+
for (const pathMatch of childApp.routing) {
|
|
261
262
|
for (const path of pathMatch.paths) {
|
|
262
263
|
const maybeError = validatePathExpression(path);
|
|
263
264
|
if (maybeError) {
|
|
@@ -277,33 +278,31 @@ var validateConfigPaths = (applicationConfigsById) => {
|
|
|
277
278
|
}
|
|
278
279
|
}
|
|
279
280
|
const entries = Array.from(pathsByApplicationId.entries());
|
|
280
|
-
|
|
281
|
+
for (const [path, { applications: ids, matcher, applicationId }] of entries) {
|
|
281
282
|
if (ids.length > 1) {
|
|
282
283
|
errors.push(
|
|
283
284
|
`Duplicate path "${path}" for applications "${ids.join(", ")}"`
|
|
284
285
|
);
|
|
285
286
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return;
|
|
293
|
-
}
|
|
294
|
-
if (applicationId === matchApplicationId) {
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
if (matcher.test(matchPath)) {
|
|
298
|
-
const source = `"${path}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
|
|
299
|
-
const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
|
|
300
|
-
errors.push(
|
|
301
|
-
`Overlapping path detected between ${source} and ${destination}`
|
|
302
|
-
);
|
|
303
|
-
}
|
|
287
|
+
for (const [
|
|
288
|
+
matchPath,
|
|
289
|
+
{ applications: matchIds, applicationId: matchApplicationId }
|
|
290
|
+
] of entries) {
|
|
291
|
+
if (path === matchPath) {
|
|
292
|
+
continue;
|
|
304
293
|
}
|
|
305
|
-
|
|
306
|
-
|
|
294
|
+
if (applicationId === matchApplicationId) {
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
if (matcher.test(matchPath)) {
|
|
298
|
+
const source = `"${path}" of application${ids.length > 0 ? "s" : ""} ${ids.join(", ")}`;
|
|
299
|
+
const destination = `"${matchPath}" of application${matchIds.length > 0 ? "s" : ""} ${matchIds.join(", ")}`;
|
|
300
|
+
errors.push(
|
|
301
|
+
`Overlapping path detected between ${source} and ${destination}`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
307
306
|
if (errors.length) {
|
|
308
307
|
throw new MicrofrontendError(`Invalid paths: ${errors.join(", ")}`, {
|
|
309
308
|
type: "config",
|
|
@@ -372,7 +371,7 @@ var validateConfigDefaultApplication = (applicationConfigsById) => {
|
|
|
372
371
|
const numApplicationsWithoutRouting = numApplications - numApplicationsWithRouting;
|
|
373
372
|
if (numApplicationsWithoutRouting === 0) {
|
|
374
373
|
throw new MicrofrontendError(
|
|
375
|
-
|
|
374
|
+
"No default application found. At least one application needs to be the default by omitting routing.",
|
|
376
375
|
{ type: "config", subtype: "no_default_application" }
|
|
377
376
|
);
|
|
378
377
|
}
|
|
@@ -418,47 +417,80 @@ function generatePortFromName({
|
|
|
418
417
|
// src/config/microfrontends-config/isomorphic/host.ts
|
|
419
418
|
var Host = class {
|
|
420
419
|
constructor(hostConfig, options) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
420
|
+
if (typeof hostConfig === "string") {
|
|
421
|
+
({
|
|
422
|
+
protocol: this.protocol,
|
|
423
|
+
host: this.host,
|
|
424
|
+
port: this.port
|
|
425
|
+
} = Host.parseUrl(hostConfig));
|
|
426
|
+
} else {
|
|
427
|
+
const { protocol = "https", host, port } = hostConfig;
|
|
428
|
+
this.protocol = protocol;
|
|
429
|
+
this.host = host;
|
|
430
|
+
this.port = port;
|
|
431
|
+
}
|
|
425
432
|
this.local = options?.isLocal;
|
|
426
433
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
protocol,
|
|
432
|
-
port
|
|
433
|
-
}) {
|
|
434
|
-
if (!port) {
|
|
435
|
-
if (protocol === "http") {
|
|
436
|
-
return 80;
|
|
437
|
-
}
|
|
438
|
-
return 443;
|
|
434
|
+
static parseUrl(url) {
|
|
435
|
+
let hostToParse = url;
|
|
436
|
+
if (!/^https?:\/\//.exec(hostToParse)) {
|
|
437
|
+
hostToParse = `https://${hostToParse}`;
|
|
439
438
|
}
|
|
440
|
-
|
|
439
|
+
const parsed = new URL(hostToParse);
|
|
440
|
+
if (!parsed.hostname) {
|
|
441
|
+
throw new Error(Host.getMicrofrontendsError(url, "requires a host"));
|
|
442
|
+
}
|
|
443
|
+
if (parsed.hash) {
|
|
444
|
+
throw new Error(
|
|
445
|
+
Host.getMicrofrontendsError(url, "cannot have a fragment")
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
if (parsed.username || parsed.password) {
|
|
449
|
+
throw new Error(
|
|
450
|
+
Host.getMicrofrontendsError(
|
|
451
|
+
url,
|
|
452
|
+
"cannot have authentication credentials (username and/or password)"
|
|
453
|
+
)
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
if (parsed.pathname !== "/") {
|
|
457
|
+
throw new Error(Host.getMicrofrontendsError(url, "cannot have a path"));
|
|
458
|
+
}
|
|
459
|
+
if (parsed.search) {
|
|
460
|
+
throw new Error(
|
|
461
|
+
Host.getMicrofrontendsError(url, "cannot have query parameters")
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
const protocol = parsed.protocol.slice(0, -1);
|
|
465
|
+
return {
|
|
466
|
+
protocol,
|
|
467
|
+
host: parsed.hostname,
|
|
468
|
+
port: parsed.port ? Number.parseInt(parsed.port) : void 0
|
|
469
|
+
};
|
|
441
470
|
}
|
|
442
|
-
|
|
443
|
-
return
|
|
471
|
+
static getMicrofrontendsError(url, message) {
|
|
472
|
+
return `Microfrontends configuration error: the URL ${url} in your microfrontends.json ${message}.`;
|
|
444
473
|
}
|
|
445
|
-
|
|
446
|
-
|
|
474
|
+
isLocal() {
|
|
475
|
+
return this.local || this.host === "localhost" || this.host === "127.0.0.1";
|
|
476
|
+
}
|
|
477
|
+
toString() {
|
|
478
|
+
const url = this.toUrl();
|
|
447
479
|
return url.toString().replace(/\/$/, "");
|
|
448
480
|
}
|
|
449
|
-
toUrl(
|
|
450
|
-
const {
|
|
451
|
-
const url = `${this.protocol}://${this.host}${this.isDefaultPort() && !includeDefaultPort ? "" : `:${this.port}`}`;
|
|
481
|
+
toUrl() {
|
|
482
|
+
const url = `${this.protocol}://${this.host}${this.port ? `:${this.port}` : ""}`;
|
|
452
483
|
return new URL(url);
|
|
453
484
|
}
|
|
454
485
|
};
|
|
455
486
|
var LocalHost = class extends Host {
|
|
456
487
|
constructor({
|
|
457
488
|
appName,
|
|
489
|
+
localPort,
|
|
458
490
|
...hostConfig
|
|
459
491
|
}) {
|
|
460
492
|
const host = hostConfig.host ?? "localhost";
|
|
461
|
-
const port = hostConfig.port ?? generatePortFromName({ name: appName });
|
|
493
|
+
const port = localPort ?? hostConfig.port ?? generatePortFromName({ name: appName });
|
|
462
494
|
const protocol = hostConfig.protocol ?? "http";
|
|
463
495
|
super({ protocol, host, port });
|
|
464
496
|
}
|
|
@@ -475,12 +507,17 @@ var Application = class {
|
|
|
475
507
|
this.development = {
|
|
476
508
|
local: new LocalHost({
|
|
477
509
|
appName: name,
|
|
510
|
+
localPort: app.development?.localPort,
|
|
478
511
|
...app.development?.local
|
|
479
512
|
}),
|
|
480
513
|
fallback: app.development?.fallback ? new Host(app.development.fallback) : void 0
|
|
481
514
|
};
|
|
482
|
-
|
|
483
|
-
|
|
515
|
+
if (app.development?.fallback) {
|
|
516
|
+
this.fallback = new Host(app.development.fallback);
|
|
517
|
+
} else if (app.production) {
|
|
518
|
+
this.fallback = new Host(app.production);
|
|
519
|
+
}
|
|
520
|
+
this.projectId = app.projectId ?? app.vercel?.projectId;
|
|
484
521
|
this.overrides = overrides?.environment ? {
|
|
485
522
|
environment: new Host(overrides.environment)
|
|
486
523
|
} : void 0;
|
|
@@ -508,7 +545,16 @@ var DefaultApplication = class extends Application {
|
|
|
508
545
|
isDefault: true
|
|
509
546
|
});
|
|
510
547
|
this.default = true;
|
|
511
|
-
|
|
548
|
+
const fallbackHost = app.development?.fallback ?? app.production;
|
|
549
|
+
if (fallbackHost === void 0) {
|
|
550
|
+
throw new Error(
|
|
551
|
+
"`app.production` or `app.development.fallback` must be set in the default application in microfrontends.json."
|
|
552
|
+
);
|
|
553
|
+
}
|
|
554
|
+
this.fallback = new Host(fallbackHost);
|
|
555
|
+
if (app.production) {
|
|
556
|
+
this.production = new Host(app.production);
|
|
557
|
+
}
|
|
512
558
|
}
|
|
513
559
|
getAssetPrefix() {
|
|
514
560
|
return "";
|
|
@@ -578,7 +624,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
578
624
|
}
|
|
579
625
|
if (isMainConfig(config) && !this.defaultApplication) {
|
|
580
626
|
throw new MicrofrontendError(
|
|
581
|
-
|
|
627
|
+
"Could not find default application in microfrontends configuration",
|
|
582
628
|
{
|
|
583
629
|
type: "application",
|
|
584
630
|
subtype: "not_found"
|
|
@@ -654,11 +700,11 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
654
700
|
return app;
|
|
655
701
|
}
|
|
656
702
|
getApplicationByProjectId(projectId) {
|
|
657
|
-
if (this.defaultApplication?.
|
|
703
|
+
if (this.defaultApplication?.projectId === projectId) {
|
|
658
704
|
return this.defaultApplication;
|
|
659
705
|
}
|
|
660
706
|
return Object.values(this.childApplications).find(
|
|
661
|
-
(app) => app.
|
|
707
|
+
(app) => app.projectId === projectId
|
|
662
708
|
);
|
|
663
709
|
}
|
|
664
710
|
/**
|
|
@@ -668,7 +714,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
668
714
|
getDefaultApplication() {
|
|
669
715
|
if (!this.defaultApplication) {
|
|
670
716
|
throw new MicrofrontendError(
|
|
671
|
-
|
|
717
|
+
"Could not find default application in microfrontends configuration",
|
|
672
718
|
{
|
|
673
719
|
type: "application",
|
|
674
720
|
subtype: "not_found"
|
|
@@ -681,7 +727,7 @@ var MicrofrontendConfigIsomorphic = class {
|
|
|
681
727
|
* Returns the configured port for the local proxy
|
|
682
728
|
*/
|
|
683
729
|
getLocalProxyPort() {
|
|
684
|
-
return this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
|
|
730
|
+
return this.config.options?.localProxyPort ?? this.config.options?.localProxy?.port ?? DEFAULT_LOCAL_PROXY_PORT;
|
|
685
731
|
}
|
|
686
732
|
/**
|
|
687
733
|
* Serializes the class back to the Schema type.
|
|
@@ -755,7 +801,7 @@ var MicrofrontendMainConfig = class extends MicrofrontendConfigIsomorphic {
|
|
|
755
801
|
}
|
|
756
802
|
if (!defaultApplication) {
|
|
757
803
|
throw new MicrofrontendError(
|
|
758
|
-
|
|
804
|
+
"Could not find default application in microfrontends configuration",
|
|
759
805
|
{
|
|
760
806
|
type: "application",
|
|
761
807
|
subtype: "not_found"
|
|
@@ -805,12 +851,10 @@ function getDomainFromEnvironment({
|
|
|
805
851
|
if (mfeProjects.length === 0) {
|
|
806
852
|
throw new Error("Missing related microfrontends project information");
|
|
807
853
|
}
|
|
808
|
-
if (!app.
|
|
854
|
+
if (!app.projectId) {
|
|
809
855
|
throw new Error(`Missing applications[${app.name}].vercel.projectId`);
|
|
810
856
|
}
|
|
811
|
-
const vercelProject = mfeProjects.find(
|
|
812
|
-
(p) => p.project.id === app.vercel?.projectId
|
|
813
|
-
);
|
|
857
|
+
const vercelProject = mfeProjects.find((p) => p.project.id === app.projectId);
|
|
814
858
|
if (!vercelProject) {
|
|
815
859
|
throw new Error(
|
|
816
860
|
`Missing related microfrontends project information for application "${app.name}"`
|
|
@@ -844,9 +888,11 @@ function getCurrentEnvironment() {
|
|
|
844
888
|
const isProduction = process.env.VERCEL_ENV === "production";
|
|
845
889
|
if (isDevelopment) {
|
|
846
890
|
return { group: "development" };
|
|
847
|
-
}
|
|
891
|
+
}
|
|
892
|
+
if (isProduction) {
|
|
848
893
|
return { group: "production" };
|
|
849
|
-
}
|
|
894
|
+
}
|
|
895
|
+
if (isPreview) {
|
|
850
896
|
return { group: "preview" };
|
|
851
897
|
}
|
|
852
898
|
return { group: "custom", name: process.env.VERCEL_ENV };
|
|
@@ -857,10 +903,10 @@ function getDomainForCurrentEnvironment(config, appName, opts = {}) {
|
|
|
857
903
|
return app.overrides.environment.toString();
|
|
858
904
|
}
|
|
859
905
|
const { group } = getCurrentEnvironment();
|
|
860
|
-
const
|
|
906
|
+
const fallbackHost = config.getDefaultApplication().fallback.toString();
|
|
861
907
|
switch (group) {
|
|
862
908
|
case "development": {
|
|
863
|
-
const domain = ["test", "development"].includes(process.env.NODE_ENV) ? app.development.local.toString() :
|
|
909
|
+
const domain = ["test", "development"].includes(process.env.NODE_ENV) ? app.development.local.toString() : fallbackHost;
|
|
864
910
|
debugDomains(appName, "development", domain);
|
|
865
911
|
return domain;
|
|
866
912
|
}
|
|
@@ -871,10 +917,9 @@ function getDomainForCurrentEnvironment(config, appName, opts = {}) {
|
|
|
871
917
|
return getDomainFromEnvironment({ app, target: "production" });
|
|
872
918
|
}
|
|
873
919
|
case "custom":
|
|
874
|
-
|
|
875
|
-
|
|
920
|
+
throw new Error(
|
|
921
|
+
"Custom environments are not supported in getDomainForCurrentEnvironment"
|
|
876
922
|
);
|
|
877
|
-
return productionHost;
|
|
878
923
|
}
|
|
879
924
|
}
|
|
880
925
|
|
|
@@ -902,7 +947,7 @@ function getHandler({
|
|
|
902
947
|
application,
|
|
903
948
|
flagFn,
|
|
904
949
|
pattern,
|
|
905
|
-
|
|
950
|
+
fallback
|
|
906
951
|
}) {
|
|
907
952
|
return async (req) => {
|
|
908
953
|
try {
|
|
@@ -922,7 +967,7 @@ function getHandler({
|
|
|
922
967
|
const zoneFallbackCookieName = `__zone_${application.name}_production_fallback`;
|
|
923
968
|
const assetPrefix = application.getAssetPrefix();
|
|
924
969
|
if (assetPrefix && pathname.startsWith(`/${assetPrefix}`) && req.cookies.get(zoneFallbackCookieName)?.value === "1") {
|
|
925
|
-
rewriteDomain =
|
|
970
|
+
rewriteDomain = fallback.toString();
|
|
926
971
|
} else {
|
|
927
972
|
try {
|
|
928
973
|
let deploymentFound;
|
|
@@ -932,7 +977,7 @@ function getHandler({
|
|
|
932
977
|
deploymentFound = await verifyPreviewDomain(req, rewriteDomain);
|
|
933
978
|
}
|
|
934
979
|
if (!deploymentFound) {
|
|
935
|
-
rewriteDomain =
|
|
980
|
+
rewriteDomain = fallback.toString();
|
|
936
981
|
responseCallbacks.push((response) => {
|
|
937
982
|
response.cookies.set(zoneFallbackCookieName, "1", {
|
|
938
983
|
httpOnly: true,
|
|
@@ -947,7 +992,7 @@ function getHandler({
|
|
|
947
992
|
if (!existingCookie?.includes("__vercel_toolbar")) {
|
|
948
993
|
patchedHeaders.set(
|
|
949
994
|
"cookie",
|
|
950
|
-
[
|
|
995
|
+
["__vercel_toolbar=1", existingCookie].join("; ")
|
|
951
996
|
);
|
|
952
997
|
responseCallbacks.push((response) => {
|
|
953
998
|
response.cookies.set("__vercel_toolbar", "1", {
|
|
@@ -983,10 +1028,6 @@ function getHandler({
|
|
|
983
1028
|
import_server.NextResponse.next({
|
|
984
1029
|
request: {
|
|
985
1030
|
headers
|
|
986
|
-
},
|
|
987
|
-
headers: {
|
|
988
|
-
// temporary, can delete when proxyRouting flag is removed
|
|
989
|
-
"x-vercel-mfe-middleware-sent-proxy": application.name
|
|
990
1031
|
}
|
|
991
1032
|
})
|
|
992
1033
|
);
|
|
@@ -1021,7 +1062,7 @@ function getMicrofrontendsMiddleware({
|
|
|
1021
1062
|
return middlewares;
|
|
1022
1063
|
}
|
|
1023
1064
|
const config = microfrontends.config;
|
|
1024
|
-
const
|
|
1065
|
+
const fallback = config.defaultApplication.fallback;
|
|
1025
1066
|
for (const application of config.getChildApplications()) {
|
|
1026
1067
|
if (application.name === process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION) {
|
|
1027
1068
|
continue;
|
|
@@ -1034,7 +1075,7 @@ function getMicrofrontendsMiddleware({
|
|
|
1034
1075
|
config,
|
|
1035
1076
|
application,
|
|
1036
1077
|
pattern,
|
|
1037
|
-
|
|
1078
|
+
fallback
|
|
1038
1079
|
})
|
|
1039
1080
|
});
|
|
1040
1081
|
}
|
|
@@ -1059,7 +1100,7 @@ function getMicrofrontendsMiddleware({
|
|
|
1059
1100
|
application,
|
|
1060
1101
|
flagFn,
|
|
1061
1102
|
pattern,
|
|
1062
|
-
|
|
1103
|
+
fallback
|
|
1063
1104
|
})
|
|
1064
1105
|
});
|
|
1065
1106
|
}
|