@apollo/gateway 0.48.3 → 0.50.1
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/dist/__generated__/graphqlTypes.d.ts +1 -0
- package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
- package/dist/config.d.ts +8 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +4 -3
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -7
- package/dist/index.js.map +1 -1
- package/dist/schema-helper/index.d.ts +0 -1
- package/dist/schema-helper/index.d.ts.map +1 -1
- package/dist/schema-helper/index.js +0 -1
- package/dist/schema-helper/index.js.map +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -1
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts +1 -1
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -1
- package/dist/supergraphManagers/LocalCompose/index.d.ts +1 -1
- package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +4 -2
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.js +20 -4
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts +3 -2
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js +9 -3
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -1
- package/package.json +6 -6
- package/src/__generated__/graphqlTypes.ts +1 -1
- package/src/__tests__/CucumberREADME.md +1 -0
- package/src/__tests__/build-query-plan-fragmentization.feature +10 -0
- package/src/__tests__/build-query-plan.feature +84 -16
- package/src/__tests__/buildQueryPlan.test.ts +272 -1
- package/src/__tests__/execution-utils.ts +4 -4
- package/src/__tests__/gateway/executor.test.ts +1 -1
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +4 -4
- package/src/__tests__/gateway/reporting.test.ts +5 -0
- package/src/__tests__/gateway/supergraphSdl.test.ts +4 -4
- package/src/__tests__/integration/abstract-types.test.ts +3 -3
- package/src/__tests__/integration/configuration.test.ts +1 -12
- package/src/__tests__/integration/logger.test.ts +1 -1
- package/src/__tests__/integration/networkRequests.test.ts +1 -1
- package/src/__tests__/integration/requires.test.ts +1 -1
- package/src/__tests__/integration/value-types.test.ts +1 -1
- package/src/config.ts +13 -10
- package/src/executeQueryPlan.ts +4 -0
- package/src/index.ts +13 -7
- package/src/schema-helper/index.ts +0 -1
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +1 -1
- package/src/supergraphManagers/IntrospectAndCompose/index.ts +1 -1
- package/src/supergraphManagers/LegacyFetcher/index.ts +1 -1
- package/src/supergraphManagers/LocalCompose/index.ts +1 -1
- package/src/supergraphManagers/UplinkFetcher/__tests__/loadSupergraphSdlFromStorage.test.ts +41 -0
- package/src/supergraphManagers/UplinkFetcher/index.ts +38 -19
- package/src/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts +9 -1
- package/dist/schema-helper/error.d.ts +0 -6
- package/dist/schema-helper/error.d.ts.map +0 -1
- package/dist/schema-helper/error.js +0 -14
- package/dist/schema-helper/error.js.map +0 -1
- package/src/schema-helper/error.ts +0 -11
package/src/index.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { deprecate } from 'util';
|
|
|
2
2
|
import { GraphQLService, Unsubscriber } from 'apollo-server-core';
|
|
3
3
|
import {
|
|
4
4
|
GraphQLExecutionResult,
|
|
5
|
-
Logger,
|
|
6
5
|
GraphQLRequestContextExecutionDidStart,
|
|
7
6
|
} from 'apollo-server-types';
|
|
7
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
8
8
|
import { InMemoryLRUCache } from 'apollo-server-caching';
|
|
9
9
|
import {
|
|
10
10
|
isObjectType,
|
|
@@ -202,8 +202,12 @@ export class ApolloGateway implements GraphQLService {
|
|
|
202
202
|
this.experimental_didUpdateSupergraph =
|
|
203
203
|
config?.experimental_didUpdateSupergraph;
|
|
204
204
|
|
|
205
|
-
this.
|
|
206
|
-
|
|
205
|
+
if (isManagedConfig(this.config)) {
|
|
206
|
+
this.pollIntervalInMs =
|
|
207
|
+
this.config.fallbackPollIntervalInMs ?? this.config.pollIntervalInMs;
|
|
208
|
+
} else if (isServiceListConfig(this.config)) {
|
|
209
|
+
this.pollIntervalInMs = this.config?.pollIntervalInMs;
|
|
210
|
+
}
|
|
207
211
|
|
|
208
212
|
this.issueConfigurationWarningsIfApplicable();
|
|
209
213
|
|
|
@@ -254,7 +258,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
254
258
|
'Polling Apollo services at a frequency of less than once per 10 ' +
|
|
255
259
|
'seconds (10000) is disallowed. Instead, the minimum allowed ' +
|
|
256
260
|
'pollInterval of 10000 will be used. Please reconfigure your ' +
|
|
257
|
-
'`
|
|
261
|
+
'`fallbackPollIntervalInMs` accordingly. If this is problematic for ' +
|
|
258
262
|
'your team, please contact support.',
|
|
259
263
|
);
|
|
260
264
|
}
|
|
@@ -288,9 +292,11 @@ export class ApolloGateway implements GraphQLService {
|
|
|
288
292
|
);
|
|
289
293
|
}
|
|
290
294
|
|
|
291
|
-
if ('
|
|
295
|
+
if (isManagedConfig(this.config) && 'pollIntervalInMs' in this.config) {
|
|
292
296
|
this.logger.warn(
|
|
293
|
-
'The `
|
|
297
|
+
'The `pollIntervalInMs` option is deprecated and will be removed in a future version of `@apollo/gateway`. ' +
|
|
298
|
+
'Please migrate to the equivalent `fallbackPollIntervalInMs` configuration option. ' +
|
|
299
|
+
'The poll interval is now defined by Uplink, this option will only be used if it is greater than the value defined by Uplink or as a fallback.',
|
|
294
300
|
);
|
|
295
301
|
}
|
|
296
302
|
}
|
|
@@ -404,7 +410,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
404
410
|
subgraphHealthCheck: this.config.serviceHealthCheck,
|
|
405
411
|
fetcher: this.fetcher,
|
|
406
412
|
logger: this.logger,
|
|
407
|
-
|
|
413
|
+
fallbackPollIntervalInMs: this.pollIntervalInMs ?? 10000,
|
|
408
414
|
}),
|
|
409
415
|
);
|
|
410
416
|
}
|
|
@@ -9,7 +9,7 @@ import { IntrospectAndCompose } from '..';
|
|
|
9
9
|
import { mockAllServicesSdlQuerySuccess } from '../../../__tests__/integration/nockMocks';
|
|
10
10
|
import { getTestingSupergraphSdl, wait } from '../../../__tests__/execution-utils';
|
|
11
11
|
import resolvable from '@josephg/resolvable';
|
|
12
|
-
import { Logger } from 'apollo
|
|
12
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
13
13
|
|
|
14
14
|
describe('IntrospectAndCompose', () => {
|
|
15
15
|
beforeEach(nockBeforeEach);
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
compositionHasErrors,
|
|
4
4
|
ServiceDefinition,
|
|
5
5
|
} from '@apollo/federation';
|
|
6
|
-
import { Logger } from 'apollo
|
|
6
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
7
7
|
import { HeadersInit } from 'node-fetch';
|
|
8
8
|
import resolvable from '@josephg/resolvable';
|
|
9
9
|
import {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* configuration options of the gateway and will be removed in a future release
|
|
5
5
|
* along with those options.
|
|
6
6
|
*/
|
|
7
|
-
import { Logger } from 'apollo
|
|
7
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
8
8
|
import resolvable from '@josephg/resolvable';
|
|
9
9
|
import {
|
|
10
10
|
SupergraphManager,
|
|
@@ -65,6 +65,7 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
65
65
|
compositionId: "originalId-1234",
|
|
66
66
|
maxRetries: 1,
|
|
67
67
|
roundRobinSeed: 0,
|
|
68
|
+
earliestFetchTime: null,
|
|
68
69
|
});
|
|
69
70
|
|
|
70
71
|
expect(result).toMatchObject({
|
|
@@ -88,6 +89,7 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
88
89
|
compositionId: "originalId-1234",
|
|
89
90
|
maxRetries: 1,
|
|
90
91
|
roundRobinSeed: 0,
|
|
92
|
+
earliestFetchTime: null,
|
|
91
93
|
}),
|
|
92
94
|
).rejects.toThrowError(
|
|
93
95
|
new UplinkFetcherError(
|
|
@@ -388,10 +390,49 @@ describe("loadSupergraphSdlFromUplinks", () => {
|
|
|
388
390
|
compositionId: "id-1234",
|
|
389
391
|
maxRetries: 5,
|
|
390
392
|
roundRobinSeed: 0,
|
|
393
|
+
earliestFetchTime: null,
|
|
391
394
|
});
|
|
392
395
|
|
|
393
396
|
expect(result).toBeNull();
|
|
394
397
|
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
395
398
|
});
|
|
399
|
+
|
|
400
|
+
it("Waits the correct time before retrying", async () => {
|
|
401
|
+
const timeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
402
|
+
|
|
403
|
+
mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1).reply(500);
|
|
404
|
+
mockSupergraphSdlRequestIfAfter('originalId-1234', mockCloudConfigUrl2).reply(
|
|
405
|
+
200,
|
|
406
|
+
JSON.stringify({
|
|
407
|
+
data: {
|
|
408
|
+
routerConfig: {
|
|
409
|
+
__typename: 'RouterConfigResult',
|
|
410
|
+
id: 'originalId-1234',
|
|
411
|
+
supergraphSdl: getTestingSupergraphSdl()
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
}),
|
|
415
|
+
);
|
|
416
|
+
const fetcher = getDefaultFetcher();
|
|
417
|
+
|
|
418
|
+
await loadSupergraphSdlFromUplinks({
|
|
419
|
+
graphRef,
|
|
420
|
+
apiKey,
|
|
421
|
+
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
422
|
+
errorReportingEndpoint: undefined,
|
|
423
|
+
fetcher: fetcher,
|
|
424
|
+
compositionId: "originalId-1234",
|
|
425
|
+
maxRetries: 1,
|
|
426
|
+
roundRobinSeed: 0,
|
|
427
|
+
earliestFetchTime: new Date(Date.now() + 1000),
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// test if setTimeout was called with a value in range to deal with time jitter
|
|
431
|
+
const setTimeoutCall = timeoutSpy.mock.calls[1][1];
|
|
432
|
+
expect(setTimeoutCall).toBeLessThanOrEqual(1000);
|
|
433
|
+
expect(setTimeoutCall).toBeGreaterThanOrEqual(900);
|
|
434
|
+
|
|
435
|
+
timeoutSpy.mockRestore();
|
|
436
|
+
});
|
|
396
437
|
});
|
|
397
438
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { fetch } from 'apollo-server-env';
|
|
2
|
-
import { Logger } from 'apollo
|
|
2
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
3
3
|
import resolvable from '@josephg/resolvable';
|
|
4
4
|
import { SupergraphManager, SupergraphSdlHookOptions } from '../../config';
|
|
5
5
|
import { SubgraphHealthCheckFunction, SupergraphSdlUpdateFunction } from '../..';
|
|
6
6
|
import { loadSupergraphSdlFromUplinks } from './loadSupergraphSdlFromStorage';
|
|
7
7
|
|
|
8
8
|
export interface UplinkFetcherOptions {
|
|
9
|
-
|
|
9
|
+
fallbackPollIntervalInMs: number;
|
|
10
10
|
subgraphHealthCheck?: boolean;
|
|
11
11
|
graphRef: string;
|
|
12
12
|
apiKey: string;
|
|
@@ -31,6 +31,8 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
31
31
|
process.env.APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT ?? undefined;
|
|
32
32
|
private compositionId?: string;
|
|
33
33
|
private fetchCount: number = 0;
|
|
34
|
+
private minDelayMs: number | null = null;
|
|
35
|
+
private earliestFetchTime: Date | null = null;
|
|
34
36
|
|
|
35
37
|
constructor(options: UplinkFetcherOptions) {
|
|
36
38
|
this.config = options;
|
|
@@ -46,7 +48,12 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
46
48
|
|
|
47
49
|
let initialSupergraphSdl: string | null = null;
|
|
48
50
|
try {
|
|
49
|
-
|
|
51
|
+
const result = await this.updateSupergraphSdl();
|
|
52
|
+
initialSupergraphSdl = result?.supergraphSdl || null;
|
|
53
|
+
if (result?.minDelaySeconds) {
|
|
54
|
+
this.minDelayMs = 1000 * result?.minDelaySeconds;
|
|
55
|
+
this.earliestFetchTime = new Date(Date.now() + this.minDelayMs);
|
|
56
|
+
}
|
|
50
57
|
} catch (e) {
|
|
51
58
|
this.logUpdateFailure(e);
|
|
52
59
|
throw e;
|
|
@@ -83,6 +90,7 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
83
90
|
compositionId: this.compositionId ?? null,
|
|
84
91
|
maxRetries: this.config.maxRetries,
|
|
85
92
|
roundRobinSeed: this.fetchCount++,
|
|
93
|
+
earliestFetchTime: this.earliestFetchTime,
|
|
86
94
|
});
|
|
87
95
|
|
|
88
96
|
if (!result) {
|
|
@@ -91,7 +99,8 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
91
99
|
this.compositionId = result.id;
|
|
92
100
|
// the healthCheck fn is only assigned if it's enabled in the config
|
|
93
101
|
await this.healthCheck?.(result.supergraphSdl);
|
|
94
|
-
|
|
102
|
+
const { supergraphSdl, minDelaySeconds } = result;
|
|
103
|
+
return { supergraphSdl, minDelaySeconds };
|
|
95
104
|
}
|
|
96
105
|
}
|
|
97
106
|
|
|
@@ -101,24 +110,34 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
101
110
|
}
|
|
102
111
|
|
|
103
112
|
private poll() {
|
|
104
|
-
this.timerRef = setTimeout(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
this.timerRef = setTimeout(
|
|
114
|
+
async () => {
|
|
115
|
+
if (this.state.phase === 'polling') {
|
|
116
|
+
const pollingPromise = resolvable();
|
|
117
|
+
|
|
118
|
+
this.state.pollingPromise = pollingPromise;
|
|
119
|
+
try {
|
|
120
|
+
const result = await this.updateSupergraphSdl();
|
|
121
|
+
const maybeNewSupergraphSdl = result?.supergraphSdl || null;
|
|
122
|
+
if (result?.minDelaySeconds) {
|
|
123
|
+
this.minDelayMs = 1000 * result?.minDelaySeconds;
|
|
124
|
+
this.earliestFetchTime = new Date(Date.now() + this.minDelayMs);
|
|
125
|
+
}
|
|
126
|
+
if (maybeNewSupergraphSdl) {
|
|
127
|
+
this.update?.(maybeNewSupergraphSdl);
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
this.logUpdateFailure(e);
|
|
113
131
|
}
|
|
114
|
-
|
|
115
|
-
this.logUpdateFailure(e);
|
|
132
|
+
pollingPromise.resolve();
|
|
116
133
|
}
|
|
117
|
-
pollingPromise.resolve();
|
|
118
|
-
}
|
|
119
134
|
|
|
120
|
-
|
|
121
|
-
|
|
135
|
+
this.poll();
|
|
136
|
+
},
|
|
137
|
+
this.minDelayMs
|
|
138
|
+
? Math.max(this.minDelayMs, this.config.fallbackPollIntervalInMs)
|
|
139
|
+
: this.config.fallbackPollIntervalInMs,
|
|
140
|
+
);
|
|
122
141
|
}
|
|
123
142
|
|
|
124
143
|
private logUpdateFailure(e: any) {
|
|
@@ -13,6 +13,7 @@ export const SUPERGRAPH_SDL_QUERY = /* GraphQL */`#graphql
|
|
|
13
13
|
... on RouterConfigResult {
|
|
14
14
|
id
|
|
15
15
|
supergraphSdl: supergraphSDL
|
|
16
|
+
minDelaySeconds
|
|
16
17
|
}
|
|
17
18
|
... on FetchError {
|
|
18
19
|
code
|
|
@@ -56,6 +57,7 @@ export async function loadSupergraphSdlFromUplinks({
|
|
|
56
57
|
compositionId,
|
|
57
58
|
maxRetries,
|
|
58
59
|
roundRobinSeed,
|
|
60
|
+
earliestFetchTime,
|
|
59
61
|
}: {
|
|
60
62
|
graphRef: string;
|
|
61
63
|
apiKey: string;
|
|
@@ -65,6 +67,7 @@ export async function loadSupergraphSdlFromUplinks({
|
|
|
65
67
|
compositionId: string | null;
|
|
66
68
|
maxRetries: number,
|
|
67
69
|
roundRobinSeed: number,
|
|
70
|
+
earliestFetchTime: Date | null
|
|
68
71
|
}) : Promise<SupergraphSdlUpdate | null> {
|
|
69
72
|
// This Promise resolves with either an updated supergraph or null if no change.
|
|
70
73
|
// This Promise can reject in the case that none of the retries are successful,
|
|
@@ -81,6 +84,10 @@ export async function loadSupergraphSdlFromUplinks({
|
|
|
81
84
|
}),
|
|
82
85
|
{
|
|
83
86
|
retries: maxRetries,
|
|
87
|
+
onRetry: async () => {
|
|
88
|
+
const delayMS = earliestFetchTime ? earliestFetchTime.getTime() - Date.now(): 0;
|
|
89
|
+
if (delayMS > 0) await new Promise(resolve => setTimeout(resolve, delayMS));
|
|
90
|
+
}
|
|
84
91
|
},
|
|
85
92
|
);
|
|
86
93
|
|
|
@@ -176,9 +183,10 @@ export async function loadSupergraphSdlFromStorage({
|
|
|
176
183
|
const {
|
|
177
184
|
id,
|
|
178
185
|
supergraphSdl,
|
|
186
|
+
minDelaySeconds,
|
|
179
187
|
// messages,
|
|
180
188
|
} = routerConfig;
|
|
181
|
-
return { id, supergraphSdl: supergraphSdl
|
|
189
|
+
return { id, supergraphSdl: supergraphSdl!, minDelaySeconds };
|
|
182
190
|
} else if (routerConfig.__typename === 'FetchError') {
|
|
183
191
|
// FetchError case
|
|
184
192
|
const { code, message } = routerConfig;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/schema-helper/error.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,qBAAa,4BAA6B,SAAQ,KAAK;IAClC,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC;gBAAnC,MAAM,EAAE,aAAa,CAAC,YAAY,CAAC;CAOvD"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GraphQLSchemaValidationError = void 0;
|
|
4
|
-
class GraphQLSchemaValidationError extends Error {
|
|
5
|
-
constructor(errors) {
|
|
6
|
-
super();
|
|
7
|
-
this.errors = errors;
|
|
8
|
-
this.name = this.constructor.name;
|
|
9
|
-
Error.captureStackTrace(this, this.constructor);
|
|
10
|
-
this.message = errors.map(error => error.message).join("\n\n");
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
exports.GraphQLSchemaValidationError = GraphQLSchemaValidationError;
|
|
14
|
-
//# sourceMappingURL=error.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/schema-helper/error.ts"],"names":[],"mappings":";;;AAEA,MAAa,4BAA6B,SAAQ,KAAK;IACrD,YAAmB,MAAmC;QACpD,KAAK,EAAE,CAAC;QADS,WAAM,GAAN,MAAM,CAA6B;QAGpD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAClC,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;QAChD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjE,CAAC;CACF;AARD,oEAQC"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { GraphQLError } from "graphql";
|
|
2
|
-
|
|
3
|
-
export class GraphQLSchemaValidationError extends Error {
|
|
4
|
-
constructor(public errors: ReadonlyArray<GraphQLError>) {
|
|
5
|
-
super();
|
|
6
|
-
|
|
7
|
-
this.name = this.constructor.name;
|
|
8
|
-
Error.captureStackTrace(this, this.constructor);
|
|
9
|
-
this.message = errors.map(error => error.message).join("\n\n");
|
|
10
|
-
}
|
|
11
|
-
}
|