@newmo/graphql-fake-server 0.3.1 → 0.4.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newmo/graphql-fake-server",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "GraphQL fake server for testing",
6
6
  "keywords": [
@@ -40,10 +40,9 @@
40
40
  ],
41
41
  "scripts": {
42
42
  "prepare": "npm run build",
43
- "build": "run-s -c build:*",
43
+ "build": "tsc -p tsconfig.build.json",
44
44
  "build:esm": "tsc -p tsconfig.build.json",
45
- "lint": "run-s -c lint:*",
46
- "lint:tsc": "tsc",
45
+ "lint": "tsc --noEmit",
47
46
  "test": "vitest run",
48
47
  "updateSnapshot": "vitest run -u"
49
48
  },
@@ -51,12 +50,12 @@
51
50
  "@apollo/server": "^4.10.4",
52
51
  "@graphql-tools/mock": "^9.0.2",
53
52
  "@graphql-tools/schema": "^10.0.3",
54
- "@newmo/graphql-fake-core": "0.3.0",
55
- "graphql-depth-limit": "^1.1.0"
53
+ "@hono/node-server": "^1.11.1",
54
+ "@newmo/graphql-fake-core": "0.4.0",
55
+ "graphql-depth-limit": "^1.1.0",
56
+ "hono": "^4.3.2"
56
57
  },
57
58
  "devDependencies": {
58
- "@graphql-codegen/cli": "^5.0.0",
59
- "@graphql-codegen/typescript": "^4.0.1",
60
59
  "@tsconfig/node18": "^18.2.0",
61
60
  "@tsconfig/strictest": "^2.0.1",
62
61
  "@types/eslint": "^8.44.2",
@@ -64,7 +63,6 @@
64
63
  "@vitest/browser": "^1.3.1",
65
64
  "eslint": "^8.47.0",
66
65
  "graphql": "^16.8.1",
67
- "npm-run-all": "^4.1.5",
68
66
  "typescript": "^5.4.2",
69
67
  "vitest": "^1.3.1",
70
68
  "vitest-github-actions-reporter": "^0.11.1"
@@ -79,5 +77,5 @@
79
77
  "access": "public",
80
78
  "registry": "https://registry.npmjs.org/"
81
79
  },
82
- "gitHead": "c0bb7357d7212071dc305685b4bdf42dfc445737"
80
+ "gitHead": "bab6c38ebdeffb527923f72106da19456e753ecb"
83
81
  }
package/src/bin.ts CHANGED
@@ -2,12 +2,12 @@
2
2
  import { run } from "./cli.js";
3
3
 
4
4
  const ret = await run();
5
- if (typeof ret !== "function") {
6
- if (ret.stdout) {
7
- console.log(ret.stdout);
8
- }
9
- if (ret.stderr) {
10
- console.error(ret.stderr);
11
- }
5
+ if (ret.stdout) {
6
+ console.log(ret.stdout);
7
+ }
8
+ if (ret.stderr) {
9
+ console.error(ret.stderr);
10
+ }
11
+ if (!ret.doNotExit) {
12
12
  process.exit(ret.exitCode);
13
13
  }
package/src/cli.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import * as fs from "node:fs/promises";
3
2
  import { parseArgs } from "node:util";
4
- import { buildSchema } from "graphql/utilities/index.js";
5
- import { createMock, startFakeServer } from "./index.js";
3
+ import { createFakeServer } from "./index.js";
6
4
  import { type LogLevel, createLogger } from "./logger.js";
7
5
 
8
6
  const HELP = `
@@ -25,11 +23,16 @@ export const cli = parseArgs({
25
23
  description: "Path to the schema file. e.g. schema.graphql",
26
24
  },
27
25
  // --port
28
- port: {
26
+ mainPort: {
29
27
  type: "string",
30
28
  description: "Port to run the server on",
31
29
  default: "4000",
32
30
  },
31
+ apolloPort: {
32
+ type: "string",
33
+ description: "Port to run the server on",
34
+ default: "4002",
35
+ },
33
36
  logLevel: {
34
37
  type: "string",
35
38
  description: "log level: debug, info, warn, error",
@@ -39,14 +42,12 @@ export const cli = parseArgs({
39
42
  });
40
43
  export const run = async ({
41
44
  values,
42
- }: typeof cli = cli): Promise<
43
- | {
44
- stdout: string;
45
- stderr: string | Error;
46
- exitCode: number;
47
- }
48
- | (() => void)
49
- > => {
45
+ }: typeof cli = cli): Promise<{
46
+ stdout: string;
47
+ stderr: string | Error;
48
+ exitCode: number;
49
+ doNotExit?: boolean;
50
+ }> => {
50
51
  const logLevel = values.logLevel as LogLevel | undefined;
51
52
  if (!logLevel || !["debug", "info", "warn", "error"].includes(logLevel)) {
52
53
  return {
@@ -65,28 +66,32 @@ export const run = async ({
65
66
  exitCode: 1,
66
67
  };
67
68
  }
68
- const port = values.port ? Number.parseInt(values.port, 10) : Number.NaN;
69
- if (Number.isNaN(port)) {
69
+ const mainPort = values.mainPort ? Number.parseInt(values.mainPort, 10) : Number.NaN;
70
+ const apolloPort = values.apolloPort ? Number.parseInt(values.apolloPort, 10) : Number.NaN;
71
+ if (Number.isNaN(mainPort) || Number.isNaN(apolloPort)) {
70
72
  logger.info(HELP);
71
73
  return {
72
74
  stdout: "",
73
- stderr: "--port must be a number",
75
+ stderr: "port must be a number",
74
76
  exitCode: 1,
75
77
  };
76
78
  }
77
79
  try {
78
- const schema = buildSchema(await fs.readFile(schemaPath, "utf-8"));
79
- const mockObject = await createMock({
80
- schema,
81
- logLevel: logLevel,
82
- });
83
- const closeServer = await startFakeServer({
84
- mockObject,
85
- port,
86
- schema,
80
+ const server = await createFakeServer({
81
+ schemaFilePath: schemaPath,
82
+ ports: {
83
+ fakeServer: mainPort,
84
+ apolloServer: apolloPort,
85
+ },
87
86
  });
88
- // TODO: more readable output?
89
- return closeServer;
87
+ const { urls } = await server.start();
88
+ logger.info(`🚀 GraphQL Fake Server listening at: ${urls.fakeServer}`);
89
+ return {
90
+ stdout: "",
91
+ stderr: "",
92
+ exitCode: 0,
93
+ doNotExit: true,
94
+ };
90
95
  } catch (error) {
91
96
  logger.error(error);
92
97
  return {
@@ -0,0 +1,47 @@
1
+ import vm from "node:vm";
2
+ import { generateCode, getTypeInfos, normalizeConfig } from "@newmo/graphql-fake-core";
3
+ import type { GraphQLSchema } from "graphql/index.js";
4
+ import { type LogLevel, createLogger } from "./logger.js";
5
+ export type MockObject = Record<string, unknown>;
6
+ export type GenerateMockOptions = {
7
+ schema: GraphQLSchema;
8
+ maxFieldRecursionDepth?: number | undefined;
9
+ logLevel?: LogLevel | undefined;
10
+ };
11
+ const cloneAsJSON = (obj: unknown) => {
12
+ return JSON.parse(JSON.stringify(obj));
13
+ };
14
+ /**
15
+ * Create mock object from schema
16
+ * It supports @example directive
17
+ * @param options
18
+ */
19
+ export const createMock = async (options: GenerateMockOptions): Promise<MockObject> => {
20
+ const logger = createLogger(options.logLevel);
21
+ try {
22
+ const normalizedConfig = normalizeConfig({
23
+ maxFieldRecursionDepth: options.maxFieldRecursionDepth ?? 3,
24
+ });
25
+ const typeInfos = getTypeInfos(normalizedConfig, options.schema);
26
+ const code = generateCode(
27
+ {
28
+ ...normalizedConfig,
29
+ outputType: "commonjs",
30
+ },
31
+ typeInfos,
32
+ );
33
+ logger.debug("Generated code:");
34
+ logger.debug(code);
35
+ // execute code in vm and get all exports
36
+ const exports = {};
37
+ vm.runInNewContext(code, { exports });
38
+ // Apollo Server does not support Function type in mock object
39
+ const plainObject = cloneAsJSON(exports);
40
+ logger.debug("Exports:");
41
+ logger.debug(JSON.stringify(plainObject, null, 2));
42
+ return plainObject;
43
+ } catch (error) {
44
+ logger.error(error);
45
+ process.exit(1);
46
+ }
47
+ };
package/src/index.ts CHANGED
@@ -1,87 +1,2 @@
1
- import vm from "node:vm";
2
- import { ApolloServer } from "@apollo/server";
3
- import { startStandaloneServer } from "@apollo/server/standalone";
4
- import { addMocksToSchema } from "@graphql-tools/mock";
5
- import { makeExecutableSchema } from "@graphql-tools/schema";
6
- import { generateCode, getTypeInfos, normalizeConfig } from "@newmo/graphql-fake-core";
7
- import type { GraphQLSchema } from "graphql";
8
- //@ts-expect-error
9
- import depthLimit from "graphql-depth-limit";
10
- import { type LogLevel, createLogger } from "./logger.js";
11
-
12
- export type MockObject = Record<string, unknown>;
13
- export type StartFakeServerOptions = {
14
- schema: GraphQLSchema;
15
- mockObject: MockObject;
16
- port?: number;
17
- logLevel?: LogLevel;
18
- };
19
- export const startFakeServer = async ({
20
- schema,
21
- mockObject,
22
- port,
23
- logLevel,
24
- }: StartFakeServerOptions) => {
25
- const logger = createLogger(logLevel);
26
- const mocks = Object.fromEntries(
27
- Object.entries(mockObject).map(([key, value]) => {
28
- return [key, () => value];
29
- }),
30
- );
31
- const server = new ApolloServer({
32
- schema: addMocksToSchema({
33
- schema: makeExecutableSchema({
34
- typeDefs: schema,
35
- }),
36
- mocks,
37
- }),
38
- validationRules: [depthLimit(3)],
39
- });
40
- const { url } = await startStandaloneServer(server, { listen: { port: port } });
41
- logger.info(`🚀 Server listening at: ${url}`);
42
- return () => {
43
- // close
44
- server.stop();
45
- };
46
- };
47
- export type GenerateMockOptions = {
48
- schema: GraphQLSchema;
49
- logLevel?: LogLevel;
50
- };
51
- const cloneAsJSON = (obj: unknown) => {
52
- return JSON.parse(JSON.stringify(obj));
53
- };
54
- /**
55
- * Create mock object from schema
56
- * It supports @example directive
57
- * @param options
58
- */
59
- export const createMock = async (options: GenerateMockOptions): Promise<MockObject> => {
60
- const logger = createLogger(options.logLevel);
61
- try {
62
- const normalizedConfig = normalizeConfig({
63
- maxFieldRecursionDepth: 4,
64
- });
65
- const typeInfos = getTypeInfos(normalizedConfig, options.schema);
66
- const code = generateCode(
67
- {
68
- ...normalizedConfig,
69
- outputType: "commonjs",
70
- },
71
- typeInfos,
72
- );
73
- logger.debug("Generated code:");
74
- logger.debug(code);
75
- // execute code in vm and get all exports
76
- const exports = {};
77
- vm.runInNewContext(code, { exports });
78
- // Apollo Server does not support Function type in mock object
79
- const plainObject = cloneAsJSON(exports);
80
- logger.debug("Exports:");
81
- logger.debug(JSON.stringify(plainObject, null, 2));
82
- return plainObject;
83
- } catch (error) {
84
- logger.error(error);
85
- process.exit(1);
86
- }
87
- };
1
+ export { createFakeServer } from "./server.js";
2
+ export type { CreateFakeServerOptions } from "./server.js";
package/src/server.ts ADDED
@@ -0,0 +1,348 @@
1
+ import fs from "node:fs/promises";
2
+ import { ApolloServer } from "@apollo/server";
3
+ import { startStandaloneServer } from "@apollo/server/standalone";
4
+ import { addMocksToSchema } from "@graphql-tools/mock";
5
+ import { makeExecutableSchema } from "@graphql-tools/schema";
6
+ import { serve } from "@hono/node-server";
7
+ //@ts-expect-error
8
+ import depthLimit from "graphql-depth-limit";
9
+ import type { GraphQLSchema } from "graphql/index.js";
10
+ import { buildSchema } from "graphql/utilities/index.js";
11
+ import { type Context, Hono } from "hono";
12
+ import { type MockObject, createMock } from "./createMock.js";
13
+ import { type LogLevel, createLogger } from "./logger.js";
14
+
15
+ export type CreateFakeServerOptions = {
16
+ schemaFilePath: string;
17
+ ports?: {
18
+ fakeServer: number;
19
+ apolloServer: number;
20
+ };
21
+ /**
22
+ * maxDepth for depthLimit
23
+ * Default is 3
24
+ */
25
+ maxDepth?: number;
26
+ /**
27
+ * maxFieldRecursionDepth for Mocking
28
+ * Default is maxDepth + 1
29
+ */
30
+ maxFieldRecursionDepth?: number;
31
+ /**
32
+ * max number of registered sequences
33
+ * Default is 100
34
+ * If the number of registered sequences exceeds this number, the oldest sequence is deleted.
35
+ */
36
+ maxRegisteredSequences?: number;
37
+ logLevel?: LogLevel;
38
+ };
39
+
40
+ type FakeServerInternal = {
41
+ schema: GraphQLSchema;
42
+ mockObject: MockObject;
43
+ ports: {
44
+ fakeServer: number;
45
+ apolloServer: number;
46
+ };
47
+ maxDepth: number;
48
+ maxFieldRecursionDepth: number;
49
+ maxRegisteredSequences: number;
50
+ logLevel: LogLevel;
51
+ };
52
+ const creteApolloServer = async (options: FakeServerInternal) => {
53
+ const mocks = Object.fromEntries(
54
+ Object.entries(options.mockObject).map(([key, value]) => {
55
+ return [key, () => value];
56
+ }),
57
+ );
58
+ return new ApolloServer({
59
+ schema: addMocksToSchema({
60
+ schema: makeExecutableSchema({
61
+ typeDefs: options.schema,
62
+ }),
63
+ mocks,
64
+ }),
65
+ validationRules: [depthLimit(options.maxDepth)],
66
+ });
67
+ };
68
+ export type RegisterSequenceNetworkError = {
69
+ type: "network-error";
70
+ operationName: string;
71
+ responseStatusCode: number;
72
+ errors: Record<string, unknown>[];
73
+ };
74
+ export type RegisterSequenceOperation = {
75
+ type: "operation";
76
+ operationName: string;
77
+ data: Record<string, unknown>;
78
+ };
79
+ export type RegisterSequenceOptions = RegisterSequenceNetworkError | RegisterSequenceOperation;
80
+ export type RegisterOperationResponse =
81
+ | {
82
+ ok: true;
83
+ }
84
+ | {
85
+ ok: false;
86
+ errors: string[];
87
+ };
88
+ const validateSequenceRegistration = (data: unknown): data is RegisterSequenceOptions => {
89
+ if (typeof data !== "object" || data === null) return false;
90
+ if ("type" in data && typeof data.type === "string") {
91
+ if (data.type === "network-error") {
92
+ return (
93
+ "errors" in data &&
94
+ Array.isArray(data.errors) &&
95
+ "responseStatusCode" in data &&
96
+ typeof data.responseStatusCode === "number" &&
97
+ "operationName" in data &&
98
+ typeof data.operationName === "string"
99
+ );
100
+ }
101
+ if (data.type === "operation") {
102
+ return (
103
+ "data" in data &&
104
+ typeof data.data === "object" &&
105
+ "operationName" in data &&
106
+ typeof data.operationName === "string"
107
+ );
108
+ }
109
+ }
110
+ return false;
111
+ };
112
+
113
+ class LRUMap<K, V> {
114
+ private map = new Map<K, V>();
115
+ private keys: K[] = [];
116
+ private maxSize: number;
117
+
118
+ constructor({ maxSize }: { maxSize: number }) {
119
+ this.maxSize = maxSize;
120
+ }
121
+
122
+ set(key: K, value: V) {
123
+ this.map.set(key, value);
124
+ this.keys.push(key);
125
+ if (this.keys.length > this.maxSize) {
126
+ const oldestKey = this.keys.shift();
127
+ if (oldestKey) {
128
+ this.map.delete(oldestKey);
129
+ }
130
+ }
131
+ }
132
+
133
+ get(key: K): V | undefined {
134
+ return this.map.get(key);
135
+ }
136
+ }
137
+
138
+ const createRoutingServer = async ({
139
+ logLevel,
140
+ ports,
141
+ maxRegisteredSequences,
142
+ }: {
143
+ logLevel: LogLevel;
144
+ maxRegisteredSequences: number;
145
+ ports: {
146
+ fakeServer: number;
147
+ apolloServer: number;
148
+ };
149
+ }) => {
150
+ const logger = createLogger(logLevel);
151
+ // pass through to apollo server
152
+ const passToApollo = async (c: Context) => {
153
+ // remove prefix
154
+ // prefix = /app1/*, path = /app1/a/b
155
+ // => suffix_path = /a/b
156
+ // let path = new URL(c.req.raw.url).pathname
157
+ let path = c.req.path;
158
+ path = path.replace(new RegExp(`^${c.req.routePath.replace("*", "")}`), "/");
159
+ let url = `http://127.0.0.1:${ports.apolloServer}${path}`;
160
+ // add params to URL
161
+ if (c.req.query()) url = `${url}?${new URLSearchParams(c.req.query())}`;
162
+ // request
163
+ const rep = await fetch(url, {
164
+ method: c.req.method,
165
+ headers: c.req.raw.headers,
166
+ body: c.req.raw.body,
167
+ duplex: "half",
168
+ });
169
+ // log response with pipe
170
+ if (rep.status === 101) return rep;
171
+ return new Response(rep.body, rep);
172
+ };
173
+ const sequenceLruMap = new LRUMap<string, RegisterSequenceOptions>({
174
+ maxSize: maxRegisteredSequences,
175
+ });
176
+ const app = new Hono();
177
+ app.post("/fake", async (c) => {
178
+ logger.debug("/fake");
179
+ const sequenceId = c.req.header("sequence-id");
180
+ if (!sequenceId) {
181
+ return Response.json(
182
+ JSON.stringify({ ok: false, errors: ["sequence-id is required"] }),
183
+ {
184
+ status: 400,
185
+ },
186
+ );
187
+ }
188
+ const body = await c.req.json();
189
+ logger.debug("/fake: got fake body", {
190
+ sequenceId,
191
+ body,
192
+ });
193
+ if (!validateSequenceRegistration(body)) {
194
+ return Response.json(JSON.stringify({ ok: false, errors: ["invalid fake body"] }), {
195
+ status: 400,
196
+ });
197
+ }
198
+ logger.debug("/fake got body type", {
199
+ sequenceId,
200
+ type: body.type,
201
+ });
202
+ sequenceLruMap.set(sequenceId, body);
203
+ return Response.json(JSON.stringify({ ok: true }), {
204
+ status: 200,
205
+ });
206
+ });
207
+ const fakeGraphQLQuery = async (c: Context) => {
208
+ /**
209
+ * Steps:
210
+ * 1. Receive a request for a GraphQL query
211
+ * 2. Does it contain a sequence id?
212
+ * - if Yes: type is network error → return an error
213
+ * - if No: Pass through to Apollo Server -> exit
214
+ * 3. Send a request to Apollo Server
215
+ * 4. Merge the registration data with the response from 3
216
+ * 5. Return the merged data
217
+ */
218
+ const sequenceId = c.req.header("sequence-id");
219
+ // 2. Does it contain a sequence id?
220
+ if (!sequenceId) return passToApollo(c);
221
+ const sequence = sequenceLruMap.get(sequenceId);
222
+ logger.debug(`/query: sequence-id: ${sequenceId}, sequence exists: ${Boolean(sequence)}`, {
223
+ sequence,
224
+ sequenceId,
225
+ });
226
+ if (!sequence) return passToApollo(c);
227
+
228
+ const requestBody = await c.req.raw.clone().json();
229
+ const requestOperationName =
230
+ typeof requestBody === "object" &&
231
+ requestBody !== null &&
232
+ "operationName" in requestBody &&
233
+ requestBody.operationName;
234
+ logger.debug(`operationName: ${requestOperationName} sequenceId: ${sequenceId}`, {
235
+ sequenceId,
236
+ });
237
+ if (requestOperationName !== sequence.operationName) {
238
+ return Response.json(
239
+ JSON.stringify({
240
+ errors: [
241
+ `operationName does not match. operationName: ${requestOperationName} sequenceId: ${sequenceId}`,
242
+ ],
243
+ }),
244
+ {
245
+ status: 400,
246
+ },
247
+ );
248
+ }
249
+ if (sequence.type === "network-error") {
250
+ return new Response(JSON.stringify(sequence.errors), {
251
+ status: sequence.responseStatusCode,
252
+ });
253
+ }
254
+ // 3. Send a request to Apollo Server
255
+ logger.debug("request to apollo-server", {
256
+ sequenceId,
257
+ });
258
+ const rep = await fetch(`http://127.0.0.1:${ports.apolloServer}/graphql`, {
259
+ method: c.req.method,
260
+ headers: c.req.raw.headers,
261
+ body: c.req.raw.body,
262
+ duplex: "half",
263
+ });
264
+ logger.debug("/query: response from apollo-server", {
265
+ sequenceId,
266
+ rep,
267
+ });
268
+ if (rep.status === 101) return rep;
269
+ // 4. Does the request contain a sequence id?
270
+ const responseBody = await rep.json();
271
+ // 5. Merge the registration data with the response from 2
272
+ const data = sequence.data;
273
+ logger.debug(`/query: merge sequence-id: ${sequenceId}`, {
274
+ data,
275
+ responseBody,
276
+ });
277
+ const merged = {
278
+ //@ts-expect-error
279
+ ...responseBody.data,
280
+ ...data,
281
+ };
282
+ return Response.json(
283
+ {
284
+ data: merged,
285
+ },
286
+ rep,
287
+ );
288
+ };
289
+ app.use("/graphql", fakeGraphQLQuery);
290
+ app.use("/query", fakeGraphQLQuery);
291
+ app.all("*", passToApollo);
292
+ return app;
293
+ };
294
+ export const createFakeServer = async (options: CreateFakeServerOptions) => {
295
+ const schema = buildSchema(await fs.readFile(options.schemaFilePath, "utf-8"));
296
+ const mockObject = await createMock({
297
+ schema,
298
+ logLevel: options.logLevel,
299
+ maxFieldRecursionDepth: options.maxFieldRecursionDepth,
300
+ });
301
+ const ports = {
302
+ fakeServer: options.ports?.fakeServer ?? 4000,
303
+ apolloServer: options.ports?.apolloServer ?? 4001,
304
+ };
305
+ const maxDepth = options.maxDepth ?? 3;
306
+ const maxFieldRecursionDepth = options.maxFieldRecursionDepth ?? maxDepth + 1;
307
+ const maxRegisteredSequences = options.maxRegisteredSequences ?? 100;
308
+ return createFakeServerInternal({
309
+ ports,
310
+ schema,
311
+ mockObject,
312
+ maxDepth,
313
+ maxFieldRecursionDepth,
314
+ maxRegisteredSequences,
315
+ logLevel: options.logLevel ?? "info",
316
+ });
317
+ };
318
+
319
+ export const createFakeServerInternal = async (options: FakeServerInternal) => {
320
+ const apolloServer = await creteApolloServer(options);
321
+ const routingServer = await createRoutingServer({
322
+ logLevel: options.logLevel,
323
+ ports: options.ports,
324
+ maxRegisteredSequences: options.maxRegisteredSequences,
325
+ });
326
+ let routerServer: ReturnType<typeof serve> | null = null;
327
+ return {
328
+ start: async () => {
329
+ const { url } = await startStandaloneServer(apolloServer, {
330
+ listen: { port: options.ports.apolloServer },
331
+ });
332
+ routerServer = serve({
333
+ fetch: routingServer.fetch,
334
+ port: options.ports.fakeServer,
335
+ });
336
+ return {
337
+ urls: {
338
+ fakeServer: `http://127.0.0.1:${options.ports.fakeServer}`,
339
+ apolloServer: `http://127.0.0.1:${options.ports.apolloServer}`,
340
+ },
341
+ };
342
+ },
343
+ stop: () => {
344
+ apolloServer.stop();
345
+ routerServer?.close();
346
+ },
347
+ };
348
+ };