sofa-api 0.12.0 → 0.14.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/index.js +31 -22
- package/index.mjs +31 -22
- package/package.json +2 -2
- package/types.d.ts +5 -4
package/README.md
CHANGED
|
@@ -150,7 +150,7 @@ Sofa allows you to cutomize the http method, path and response status. For examp
|
|
|
150
150
|
```typescript
|
|
151
151
|
api.use(
|
|
152
152
|
'/api',
|
|
153
|
-
|
|
153
|
+
useSofa({
|
|
154
154
|
schema,
|
|
155
155
|
routes: {
|
|
156
156
|
'Query.feed': { method: 'POST' },
|
|
@@ -341,7 +341,7 @@ app.use(
|
|
|
341
341
|
writeFileSync('./swagger.json', JSON.stringify(openApi.get(), null, 2));
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
-
OpenAPI (Swagger) with custom tags, summary and description
|
|
344
|
+
OpenAPI (Swagger) with custom tags, summary and description
|
|
345
345
|
|
|
346
346
|
```ts
|
|
347
347
|
const openApi = OpenAPI({
|
package/index.js
CHANGED
|
@@ -297,12 +297,11 @@ function createRouter(sofa) {
|
|
|
297
297
|
router.post('/webhook', (request, serverContext) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
298
298
|
const { subscription, variables, url } = yield request.json();
|
|
299
299
|
try {
|
|
300
|
-
const contextValue = yield sofa.contextFactory(serverContext);
|
|
301
300
|
const result = yield subscriptionManager.start({
|
|
302
301
|
subscription,
|
|
303
302
|
variables,
|
|
304
303
|
url,
|
|
305
|
-
},
|
|
304
|
+
}, Object.assign(Object.assign({}, serverContext), { request }));
|
|
306
305
|
return new fetch.Response(JSON.stringify(result), {
|
|
307
306
|
status: 200,
|
|
308
307
|
statusText: 'OK',
|
|
@@ -324,7 +323,8 @@ function createRouter(sofa) {
|
|
|
324
323
|
const body = yield request.json();
|
|
325
324
|
const variables = body.variables;
|
|
326
325
|
try {
|
|
327
|
-
const
|
|
326
|
+
const sofaServerContext = Object.assign(Object.assign({}, serverContext), { request });
|
|
327
|
+
const contextValue = yield sofa.contextFactory(sofaServerContext);
|
|
328
328
|
const result = yield subscriptionManager.update({
|
|
329
329
|
id,
|
|
330
330
|
variables,
|
|
@@ -367,7 +367,7 @@ function createRouter(sofa) {
|
|
|
367
367
|
return router;
|
|
368
368
|
}
|
|
369
369
|
function createQueryRoute({ sofa, router, fieldName, }) {
|
|
370
|
-
var _a, _b, _c, _d, _e, _f;
|
|
370
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
371
371
|
logger.debug(`[Router] Creating ${fieldName} query`);
|
|
372
372
|
const queryType = sofa.schema.getQueryType();
|
|
373
373
|
const operationNode = utils.buildOperationNodeForField({
|
|
@@ -402,13 +402,14 @@ function createQueryRoute({ sofa, router, fieldName, }) {
|
|
|
402
402
|
path: route.path,
|
|
403
403
|
method: route.method.toUpperCase(),
|
|
404
404
|
tags: (_e = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.tags) !== null && _e !== void 0 ? _e : [],
|
|
405
|
-
description: (_f = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) !== null && _f !== void 0 ? _f : '',
|
|
405
|
+
description: (_g = (_f = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) !== null && _f !== void 0 ? _f : field.description) !== null && _g !== void 0 ? _g : '',
|
|
406
406
|
};
|
|
407
407
|
}
|
|
408
408
|
function createMutationRoute({ sofa, router, fieldName, }) {
|
|
409
|
-
var _a, _b, _c, _d;
|
|
409
|
+
var _a, _b, _c, _d, _e, _f;
|
|
410
410
|
logger.debug(`[Router] Creating ${fieldName} mutation`);
|
|
411
411
|
const mutationType = sofa.schema.getMutationType();
|
|
412
|
+
const field = mutationType.getFields()[fieldName];
|
|
412
413
|
const operationNode = utils.buildOperationNodeForField({
|
|
413
414
|
kind: 'mutation',
|
|
414
415
|
schema: sofa.schema,
|
|
@@ -437,7 +438,7 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
437
438
|
path,
|
|
438
439
|
method,
|
|
439
440
|
tags: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.tags) || [],
|
|
440
|
-
description: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description)
|
|
441
|
+
description: (_f = (_e = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) !== null && _e !== void 0 ? _e : field.description) !== null && _f !== void 0 ? _f : '',
|
|
441
442
|
};
|
|
442
443
|
}
|
|
443
444
|
function useHandler(config) {
|
|
@@ -469,7 +470,8 @@ function useHandler(config) {
|
|
|
469
470
|
}
|
|
470
471
|
return Object.assign(Object.assign({}, variables), { [name]: value });
|
|
471
472
|
}, {});
|
|
472
|
-
const
|
|
473
|
+
const sofaServerContext = Object.assign(Object.assign({}, serverContext), { request });
|
|
474
|
+
const contextValue = yield sofa.contextFactory(sofaServerContext);
|
|
473
475
|
const result = yield sofa.execute({
|
|
474
476
|
schema: sofa.schema,
|
|
475
477
|
document: operation,
|
|
@@ -479,8 +481,9 @@ function useHandler(config) {
|
|
|
479
481
|
});
|
|
480
482
|
if (result.errors) {
|
|
481
483
|
const defaultErrorHandler = (errors) => {
|
|
482
|
-
return new fetch.Response(errors[0], {
|
|
484
|
+
return new fetch.Response(JSON.stringify(errors[0]), {
|
|
483
485
|
status: 500,
|
|
486
|
+
headers: { 'Content-Type': 'application/json' },
|
|
484
487
|
});
|
|
485
488
|
};
|
|
486
489
|
const errorHandler = sofa.errorHandler || defaultErrorHandler;
|
|
@@ -642,6 +645,7 @@ function resolveField(field, opts) {
|
|
|
642
645
|
// type -> $ref
|
|
643
646
|
// scalar -> swagger primitive
|
|
644
647
|
function resolveFieldType(type, opts) {
|
|
648
|
+
var _a;
|
|
645
649
|
if (graphql.isNonNullType(type)) {
|
|
646
650
|
return resolveFieldType(type.ofType, opts);
|
|
647
651
|
}
|
|
@@ -658,9 +662,11 @@ function resolveFieldType(type, opts) {
|
|
|
658
662
|
}
|
|
659
663
|
if (graphql.isScalarType(type)) {
|
|
660
664
|
return (mapToPrimitive(type.name) ||
|
|
661
|
-
opts.customScalars[type.name] ||
|
|
662
|
-
type:
|
|
663
|
-
|
|
665
|
+
opts.customScalars[type.name] ||
|
|
666
|
+
((_a = type.extensions) === null || _a === void 0 ? void 0 : _a.jsonSchema)
|
|
667
|
+
|| {
|
|
668
|
+
type: 'object',
|
|
669
|
+
});
|
|
664
670
|
}
|
|
665
671
|
if (graphql.isEnumType(type)) {
|
|
666
672
|
return {
|
|
@@ -683,13 +689,15 @@ function buildPathFromOperation({ url, schema, operation, useRequestBody, tags,
|
|
|
683
689
|
requestBody: {
|
|
684
690
|
content: {
|
|
685
691
|
'application/json': {
|
|
686
|
-
schema: resolveRequestBody(info.operation.variableDefinitions
|
|
692
|
+
schema: resolveRequestBody(info.operation.variableDefinitions, {
|
|
693
|
+
customScalars,
|
|
694
|
+
}),
|
|
687
695
|
},
|
|
688
696
|
},
|
|
689
697
|
},
|
|
690
698
|
}
|
|
691
699
|
: {
|
|
692
|
-
parameters: resolveParameters(url, info.operation.variableDefinitions),
|
|
700
|
+
parameters: resolveParameters(url, info.operation.variableDefinitions, { customScalars }),
|
|
693
701
|
})), { responses: {
|
|
694
702
|
200: {
|
|
695
703
|
description: summary,
|
|
@@ -705,7 +713,7 @@ function buildPathFromOperation({ url, schema, operation, useRequestBody, tags,
|
|
|
705
713
|
},
|
|
706
714
|
} });
|
|
707
715
|
}
|
|
708
|
-
function resolveParameters(url, variables) {
|
|
716
|
+
function resolveParameters(url, variables, opts) {
|
|
709
717
|
if (!variables) {
|
|
710
718
|
return [];
|
|
711
719
|
}
|
|
@@ -714,11 +722,11 @@ function resolveParameters(url, variables) {
|
|
|
714
722
|
in: isInPath(url, variable.variable.name.value) ? 'path' : 'query',
|
|
715
723
|
name: variable.variable.name.value,
|
|
716
724
|
required: variable.type.kind === graphql.Kind.NON_NULL_TYPE,
|
|
717
|
-
schema: resolveParamSchema(variable.type),
|
|
725
|
+
schema: resolveParamSchema(variable.type, opts),
|
|
718
726
|
};
|
|
719
727
|
});
|
|
720
728
|
}
|
|
721
|
-
function resolveRequestBody(variables) {
|
|
729
|
+
function resolveRequestBody(variables, opts) {
|
|
722
730
|
if (!variables) {
|
|
723
731
|
return {};
|
|
724
732
|
}
|
|
@@ -728,25 +736,26 @@ function resolveRequestBody(variables) {
|
|
|
728
736
|
if (variable.type.kind === graphql.Kind.NON_NULL_TYPE) {
|
|
729
737
|
required.push(variable.variable.name.value);
|
|
730
738
|
}
|
|
731
|
-
properties[variable.variable.name.value] = resolveParamSchema(variable.type);
|
|
739
|
+
properties[variable.variable.name.value] = resolveParamSchema(variable.type, opts);
|
|
732
740
|
});
|
|
733
741
|
return Object.assign({ type: 'object', properties }, (required.length ? { required } : {}));
|
|
734
742
|
}
|
|
735
743
|
// array -> [type]
|
|
736
744
|
// type -> $ref
|
|
737
745
|
// scalar -> swagger primitive
|
|
738
|
-
function resolveParamSchema(type) {
|
|
746
|
+
function resolveParamSchema(type, opts) {
|
|
739
747
|
if (type.kind === graphql.Kind.NON_NULL_TYPE) {
|
|
740
|
-
return resolveParamSchema(type.type);
|
|
748
|
+
return resolveParamSchema(type.type, opts);
|
|
741
749
|
}
|
|
742
750
|
if (type.kind === graphql.Kind.LIST_TYPE) {
|
|
743
751
|
return {
|
|
744
752
|
type: 'array',
|
|
745
|
-
items: resolveParamSchema(type.type),
|
|
753
|
+
items: resolveParamSchema(type.type, opts),
|
|
746
754
|
};
|
|
747
755
|
}
|
|
748
756
|
const primitive = mapToPrimitive(type.name.value);
|
|
749
|
-
return (primitive ||
|
|
757
|
+
return (primitive ||
|
|
758
|
+
opts.customScalars[type.name.value] || {
|
|
750
759
|
$ref: mapToRef(type.name.value),
|
|
751
760
|
});
|
|
752
761
|
}
|
package/index.mjs
CHANGED
|
@@ -291,12 +291,11 @@ function createRouter(sofa) {
|
|
|
291
291
|
router.post('/webhook', (request, serverContext) => __awaiter(this, void 0, void 0, function* () {
|
|
292
292
|
const { subscription, variables, url } = yield request.json();
|
|
293
293
|
try {
|
|
294
|
-
const contextValue = yield sofa.contextFactory(serverContext);
|
|
295
294
|
const result = yield subscriptionManager.start({
|
|
296
295
|
subscription,
|
|
297
296
|
variables,
|
|
298
297
|
url,
|
|
299
|
-
},
|
|
298
|
+
}, Object.assign(Object.assign({}, serverContext), { request }));
|
|
300
299
|
return new Response(JSON.stringify(result), {
|
|
301
300
|
status: 200,
|
|
302
301
|
statusText: 'OK',
|
|
@@ -318,7 +317,8 @@ function createRouter(sofa) {
|
|
|
318
317
|
const body = yield request.json();
|
|
319
318
|
const variables = body.variables;
|
|
320
319
|
try {
|
|
321
|
-
const
|
|
320
|
+
const sofaServerContext = Object.assign(Object.assign({}, serverContext), { request });
|
|
321
|
+
const contextValue = yield sofa.contextFactory(sofaServerContext);
|
|
322
322
|
const result = yield subscriptionManager.update({
|
|
323
323
|
id,
|
|
324
324
|
variables,
|
|
@@ -361,7 +361,7 @@ function createRouter(sofa) {
|
|
|
361
361
|
return router;
|
|
362
362
|
}
|
|
363
363
|
function createQueryRoute({ sofa, router, fieldName, }) {
|
|
364
|
-
var _a, _b, _c, _d, _e, _f;
|
|
364
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
365
365
|
logger.debug(`[Router] Creating ${fieldName} query`);
|
|
366
366
|
const queryType = sofa.schema.getQueryType();
|
|
367
367
|
const operationNode = buildOperationNodeForField({
|
|
@@ -396,13 +396,14 @@ function createQueryRoute({ sofa, router, fieldName, }) {
|
|
|
396
396
|
path: route.path,
|
|
397
397
|
method: route.method.toUpperCase(),
|
|
398
398
|
tags: (_e = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.tags) !== null && _e !== void 0 ? _e : [],
|
|
399
|
-
description: (_f = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) !== null && _f !== void 0 ? _f : '',
|
|
399
|
+
description: (_g = (_f = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) !== null && _f !== void 0 ? _f : field.description) !== null && _g !== void 0 ? _g : '',
|
|
400
400
|
};
|
|
401
401
|
}
|
|
402
402
|
function createMutationRoute({ sofa, router, fieldName, }) {
|
|
403
|
-
var _a, _b, _c, _d;
|
|
403
|
+
var _a, _b, _c, _d, _e, _f;
|
|
404
404
|
logger.debug(`[Router] Creating ${fieldName} mutation`);
|
|
405
405
|
const mutationType = sofa.schema.getMutationType();
|
|
406
|
+
const field = mutationType.getFields()[fieldName];
|
|
406
407
|
const operationNode = buildOperationNodeForField({
|
|
407
408
|
kind: 'mutation',
|
|
408
409
|
schema: sofa.schema,
|
|
@@ -431,7 +432,7 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
431
432
|
path,
|
|
432
433
|
method,
|
|
433
434
|
tags: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.tags) || [],
|
|
434
|
-
description: (routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description)
|
|
435
|
+
description: (_f = (_e = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.description) !== null && _e !== void 0 ? _e : field.description) !== null && _f !== void 0 ? _f : '',
|
|
435
436
|
};
|
|
436
437
|
}
|
|
437
438
|
function useHandler(config) {
|
|
@@ -463,7 +464,8 @@ function useHandler(config) {
|
|
|
463
464
|
}
|
|
464
465
|
return Object.assign(Object.assign({}, variables), { [name]: value });
|
|
465
466
|
}, {});
|
|
466
|
-
const
|
|
467
|
+
const sofaServerContext = Object.assign(Object.assign({}, serverContext), { request });
|
|
468
|
+
const contextValue = yield sofa.contextFactory(sofaServerContext);
|
|
467
469
|
const result = yield sofa.execute({
|
|
468
470
|
schema: sofa.schema,
|
|
469
471
|
document: operation,
|
|
@@ -473,8 +475,9 @@ function useHandler(config) {
|
|
|
473
475
|
});
|
|
474
476
|
if (result.errors) {
|
|
475
477
|
const defaultErrorHandler = (errors) => {
|
|
476
|
-
return new Response(errors[0], {
|
|
478
|
+
return new Response(JSON.stringify(errors[0]), {
|
|
477
479
|
status: 500,
|
|
480
|
+
headers: { 'Content-Type': 'application/json' },
|
|
478
481
|
});
|
|
479
482
|
};
|
|
480
483
|
const errorHandler = sofa.errorHandler || defaultErrorHandler;
|
|
@@ -636,6 +639,7 @@ function resolveField(field, opts) {
|
|
|
636
639
|
// type -> $ref
|
|
637
640
|
// scalar -> swagger primitive
|
|
638
641
|
function resolveFieldType(type, opts) {
|
|
642
|
+
var _a;
|
|
639
643
|
if (isNonNullType(type)) {
|
|
640
644
|
return resolveFieldType(type.ofType, opts);
|
|
641
645
|
}
|
|
@@ -652,9 +656,11 @@ function resolveFieldType(type, opts) {
|
|
|
652
656
|
}
|
|
653
657
|
if (isScalarType(type)) {
|
|
654
658
|
return (mapToPrimitive(type.name) ||
|
|
655
|
-
opts.customScalars[type.name] ||
|
|
656
|
-
type:
|
|
657
|
-
|
|
659
|
+
opts.customScalars[type.name] ||
|
|
660
|
+
((_a = type.extensions) === null || _a === void 0 ? void 0 : _a.jsonSchema)
|
|
661
|
+
|| {
|
|
662
|
+
type: 'object',
|
|
663
|
+
});
|
|
658
664
|
}
|
|
659
665
|
if (isEnumType(type)) {
|
|
660
666
|
return {
|
|
@@ -677,13 +683,15 @@ function buildPathFromOperation({ url, schema, operation, useRequestBody, tags,
|
|
|
677
683
|
requestBody: {
|
|
678
684
|
content: {
|
|
679
685
|
'application/json': {
|
|
680
|
-
schema: resolveRequestBody(info.operation.variableDefinitions
|
|
686
|
+
schema: resolveRequestBody(info.operation.variableDefinitions, {
|
|
687
|
+
customScalars,
|
|
688
|
+
}),
|
|
681
689
|
},
|
|
682
690
|
},
|
|
683
691
|
},
|
|
684
692
|
}
|
|
685
693
|
: {
|
|
686
|
-
parameters: resolveParameters(url, info.operation.variableDefinitions),
|
|
694
|
+
parameters: resolveParameters(url, info.operation.variableDefinitions, { customScalars }),
|
|
687
695
|
})), { responses: {
|
|
688
696
|
200: {
|
|
689
697
|
description: summary,
|
|
@@ -699,7 +707,7 @@ function buildPathFromOperation({ url, schema, operation, useRequestBody, tags,
|
|
|
699
707
|
},
|
|
700
708
|
} });
|
|
701
709
|
}
|
|
702
|
-
function resolveParameters(url, variables) {
|
|
710
|
+
function resolveParameters(url, variables, opts) {
|
|
703
711
|
if (!variables) {
|
|
704
712
|
return [];
|
|
705
713
|
}
|
|
@@ -708,11 +716,11 @@ function resolveParameters(url, variables) {
|
|
|
708
716
|
in: isInPath(url, variable.variable.name.value) ? 'path' : 'query',
|
|
709
717
|
name: variable.variable.name.value,
|
|
710
718
|
required: variable.type.kind === Kind.NON_NULL_TYPE,
|
|
711
|
-
schema: resolveParamSchema(variable.type),
|
|
719
|
+
schema: resolveParamSchema(variable.type, opts),
|
|
712
720
|
};
|
|
713
721
|
});
|
|
714
722
|
}
|
|
715
|
-
function resolveRequestBody(variables) {
|
|
723
|
+
function resolveRequestBody(variables, opts) {
|
|
716
724
|
if (!variables) {
|
|
717
725
|
return {};
|
|
718
726
|
}
|
|
@@ -722,25 +730,26 @@ function resolveRequestBody(variables) {
|
|
|
722
730
|
if (variable.type.kind === Kind.NON_NULL_TYPE) {
|
|
723
731
|
required.push(variable.variable.name.value);
|
|
724
732
|
}
|
|
725
|
-
properties[variable.variable.name.value] = resolveParamSchema(variable.type);
|
|
733
|
+
properties[variable.variable.name.value] = resolveParamSchema(variable.type, opts);
|
|
726
734
|
});
|
|
727
735
|
return Object.assign({ type: 'object', properties }, (required.length ? { required } : {}));
|
|
728
736
|
}
|
|
729
737
|
// array -> [type]
|
|
730
738
|
// type -> $ref
|
|
731
739
|
// scalar -> swagger primitive
|
|
732
|
-
function resolveParamSchema(type) {
|
|
740
|
+
function resolveParamSchema(type, opts) {
|
|
733
741
|
if (type.kind === Kind.NON_NULL_TYPE) {
|
|
734
|
-
return resolveParamSchema(type.type);
|
|
742
|
+
return resolveParamSchema(type.type, opts);
|
|
735
743
|
}
|
|
736
744
|
if (type.kind === Kind.LIST_TYPE) {
|
|
737
745
|
return {
|
|
738
746
|
type: 'array',
|
|
739
|
-
items: resolveParamSchema(type.type),
|
|
747
|
+
items: resolveParamSchema(type.type, opts),
|
|
740
748
|
};
|
|
741
749
|
}
|
|
742
750
|
const primitive = mapToPrimitive(type.name.value);
|
|
743
|
-
return (primitive ||
|
|
751
|
+
return (primitive ||
|
|
752
|
+
opts.customScalars[type.name.value] || {
|
|
744
753
|
$ref: mapToRef(type.name.value),
|
|
745
754
|
});
|
|
746
755
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sofa-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Create REST APIs with GraphQL",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@graphql-tools/utils": "8.12.0",
|
|
11
|
-
"ansi-colors": "4.1.3",
|
|
12
11
|
"@whatwg-node/fetch": "^0.4.3",
|
|
13
12
|
"@whatwg-node/server": "^0.4.1",
|
|
13
|
+
"ansi-colors": "4.1.3",
|
|
14
14
|
"itty-router": "^2.6.1",
|
|
15
15
|
"openapi-types": "12.0.2",
|
|
16
16
|
"param-case": "3.0.4",
|
package/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { DefaultServerAdapterContext } from '@whatwg-node/server';
|
|
1
2
|
import { DocumentNode } from 'graphql';
|
|
2
3
|
export declare type ContextValue = Record<string, any>;
|
|
3
4
|
export declare type Ignore = string[];
|
|
@@ -10,7 +11,7 @@ export interface RouteInfo {
|
|
|
10
11
|
description?: string;
|
|
11
12
|
}
|
|
12
13
|
export declare type OnRoute = (info: RouteInfo) => void;
|
|
13
|
-
export declare type ContextFn = (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
14
|
+
export declare type ContextFn = (serverContext: DefaultSofaServerContext) => Promise<ContextValue> | ContextValue;
|
|
15
|
+
export declare type DefaultSofaServerContext = DefaultServerAdapterContext & {
|
|
16
|
+
request: Request;
|
|
17
|
+
};
|