sofa-api 0.11.2 → 0.12.0-alpha.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 +11 -42
- package/index.d.ts +1 -36
- package/index.js +98 -155
- package/index.mjs +100 -155
- package/open-api/index.d.ts +2 -3
- package/package.json +8 -9
- package/router.d.ts +6 -0
- package/sofa.d.ts +10 -5
- package/types.d.ts +5 -2
- package/express.d.ts +0 -25
package/README.md
CHANGED
|
@@ -20,47 +20,14 @@ Here's complete example with no dependency on frameworks, but also integratable
|
|
|
20
20
|
```js
|
|
21
21
|
import http from 'http';
|
|
22
22
|
import getStream from 'get-stream';
|
|
23
|
-
import {
|
|
24
|
-
|
|
25
|
-
const invokeSofa = createSofaRouter({
|
|
26
|
-
basePath: '/api',
|
|
27
|
-
schema,
|
|
28
|
-
});
|
|
23
|
+
import { useSofa } from 'sofa-api';
|
|
29
24
|
|
|
30
|
-
const server = http.createServer(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
contextValue: {
|
|
37
|
-
req,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
if (response) {
|
|
41
|
-
const headers = {
|
|
42
|
-
'Content-Type': 'application/json',
|
|
43
|
-
};
|
|
44
|
-
if (response.statusMessage) {
|
|
45
|
-
res.writeHead(response.status, response.statusMessage, headers);
|
|
46
|
-
} else {
|
|
47
|
-
res.writeHead(response.status, headers);
|
|
48
|
-
}
|
|
49
|
-
if (response.type === 'result') {
|
|
50
|
-
res.end(JSON.stringify(response.body));
|
|
51
|
-
}
|
|
52
|
-
if (response.type === 'error') {
|
|
53
|
-
res.end(JSON.stringify(response.error));
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
res.writeHead(404);
|
|
57
|
-
res.end();
|
|
58
|
-
}
|
|
59
|
-
} catch (error) {
|
|
60
|
-
res.writeHead(500);
|
|
61
|
-
res.end(JSON.stringify(error));
|
|
62
|
-
}
|
|
63
|
-
});
|
|
25
|
+
const server = http.createServer(
|
|
26
|
+
useSofa({
|
|
27
|
+
basePath: '/api',
|
|
28
|
+
schema,
|
|
29
|
+
})
|
|
30
|
+
);
|
|
64
31
|
```
|
|
65
32
|
|
|
66
33
|
Another example with builtin express-like frameworks support
|
|
@@ -302,6 +269,7 @@ Thanks to GraphQL's Type System Sofa is able to generate OpenAPI (Swagger) defin
|
|
|
302
269
|
|
|
303
270
|
```ts
|
|
304
271
|
import { useSofa, OpenAPI } from 'sofa-api';
|
|
272
|
+
import { writeFileSync } from 'fs';
|
|
305
273
|
|
|
306
274
|
const openApi = OpenAPI({
|
|
307
275
|
schema,
|
|
@@ -325,13 +293,14 @@ app.use(
|
|
|
325
293
|
);
|
|
326
294
|
|
|
327
295
|
// writes every recorder route
|
|
328
|
-
|
|
296
|
+
writeFileSync('./swagger.json', JSON.stringify(openApi.get(), null, 2));
|
|
329
297
|
```
|
|
330
298
|
|
|
331
299
|
OpenAPI (Swagger) with Bearer Authentication
|
|
332
300
|
|
|
333
301
|
```ts
|
|
334
302
|
import { useSofa, OpenAPI } from 'sofa-api';
|
|
303
|
+
import { writeFileSync } from 'fs';
|
|
335
304
|
|
|
336
305
|
const openApi = OpenAPI({
|
|
337
306
|
schema,
|
|
@@ -369,7 +338,7 @@ app.use(
|
|
|
369
338
|
);
|
|
370
339
|
|
|
371
340
|
// writes every recorder route
|
|
372
|
-
|
|
341
|
+
writeFileSync('./swagger.json', JSON.stringify(openApi.get(), null, 2));
|
|
373
342
|
```
|
|
374
343
|
|
|
375
344
|
## License
|
package/index.d.ts
CHANGED
|
@@ -1,38 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import * as http from 'http';
|
|
3
|
-
import type { ContextValue } from './types';
|
|
4
1
|
import type { SofaConfig } from './sofa';
|
|
5
2
|
export { OpenAPI } from './open-api';
|
|
6
|
-
declare
|
|
7
|
-
method: string;
|
|
8
|
-
url: string;
|
|
9
|
-
originalUrl?: string;
|
|
10
|
-
body?: any;
|
|
11
|
-
};
|
|
12
|
-
declare type NextFunction = (err?: any) => void;
|
|
13
|
-
declare type Middleware = (req: Request, res: http.ServerResponse, next: NextFunction) => unknown;
|
|
14
|
-
export declare type ContextFn = (init: {
|
|
15
|
-
req: any;
|
|
16
|
-
res: any;
|
|
17
|
-
}) => ContextValue;
|
|
18
|
-
export declare function isContextFn(context: any): context is ContextFn;
|
|
19
|
-
interface SofaMiddlewareConfig extends SofaConfig {
|
|
20
|
-
context?: ContextValue | ContextFn;
|
|
21
|
-
}
|
|
22
|
-
export declare function useSofa({ context, ...config }: SofaMiddlewareConfig): Middleware;
|
|
23
|
-
export declare function createSofaRouter(config: SofaConfig): (request: {
|
|
24
|
-
method: string;
|
|
25
|
-
url: string;
|
|
26
|
-
body: any;
|
|
27
|
-
contextValue: ContextValue;
|
|
28
|
-
}) => Promise<({
|
|
29
|
-
type: "result";
|
|
30
|
-
status: number;
|
|
31
|
-
statusMessage?: string | undefined;
|
|
32
|
-
body: any;
|
|
33
|
-
} | {
|
|
34
|
-
type: "error";
|
|
35
|
-
status: number;
|
|
36
|
-
statusMessage?: string | undefined;
|
|
37
|
-
error: any;
|
|
38
|
-
}) | null>;
|
|
3
|
+
export declare function useSofa(config: SofaConfig): import("@whatwg-node/server").ServerAdapter<unknown, import("itty-router").Router<import("itty-router").Request & Request, {}>>;
|
package/index.js
CHANGED
|
@@ -6,14 +6,13 @@ function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'defau
|
|
|
6
6
|
|
|
7
7
|
const tslib = require('tslib');
|
|
8
8
|
const graphql = require('graphql');
|
|
9
|
-
const
|
|
9
|
+
const ittyRouter = require('itty-router');
|
|
10
10
|
const utils = require('@graphql-tools/utils');
|
|
11
11
|
const paramCase = require('param-case');
|
|
12
12
|
const uuid = require('uuid');
|
|
13
|
-
const
|
|
13
|
+
const fetch = require('@whatwg-node/fetch');
|
|
14
14
|
const colors = _interopDefault(require('ansi-colors'));
|
|
15
|
-
const
|
|
16
|
-
const fs = require('fs');
|
|
15
|
+
const server = require('@whatwg-node/server');
|
|
17
16
|
const titleCase = require('title-case');
|
|
18
17
|
|
|
19
18
|
function getOperationInfo(doc) {
|
|
@@ -62,7 +61,7 @@ function resolveVariable({ value, type, schema, }) {
|
|
|
62
61
|
return value;
|
|
63
62
|
}
|
|
64
63
|
if (type.kind === graphql.Kind.LIST_TYPE) {
|
|
65
|
-
return (Array.isArray(value) ? value : [value]).map(val => resolveVariable({
|
|
64
|
+
return (Array.isArray(value) ? value : [value]).map((val) => resolveVariable({
|
|
66
65
|
value: val,
|
|
67
66
|
type: type.type,
|
|
68
67
|
schema,
|
|
@@ -181,7 +180,7 @@ class SubscriptionManager {
|
|
|
181
180
|
}
|
|
182
181
|
return Object.assign(Object.assign({}, values), { [name]: value });
|
|
183
182
|
}, {});
|
|
184
|
-
const execution = yield
|
|
183
|
+
const execution = yield this.sofa.subscribe({
|
|
185
184
|
schema: this.sofa.schema,
|
|
186
185
|
document,
|
|
187
186
|
operationName,
|
|
@@ -236,12 +235,12 @@ class SubscriptionManager {
|
|
|
236
235
|
}
|
|
237
236
|
const { url } = this.clients.get(id);
|
|
238
237
|
logger.info(`[Subscription] Trigger ${id}`);
|
|
239
|
-
const response = yield
|
|
238
|
+
const response = yield fetch.fetch(url, {
|
|
240
239
|
method: 'POST',
|
|
241
240
|
body: JSON.stringify(result),
|
|
242
241
|
headers: {
|
|
243
242
|
'Content-Type': 'application/json',
|
|
244
|
-
}
|
|
243
|
+
},
|
|
245
244
|
});
|
|
246
245
|
yield response.text();
|
|
247
246
|
});
|
|
@@ -277,7 +276,9 @@ class SubscriptionManager {
|
|
|
277
276
|
|
|
278
277
|
function createRouter(sofa) {
|
|
279
278
|
logger.debug('[Sofa] Creating router');
|
|
280
|
-
const router =
|
|
279
|
+
const router = ittyRouter.Router({
|
|
280
|
+
base: sofa.basePath,
|
|
281
|
+
});
|
|
281
282
|
const queryType = sofa.schema.getQueryType();
|
|
282
283
|
const mutationType = sofa.schema.getMutationType();
|
|
283
284
|
const subscriptionManager = new SubscriptionManager(sofa);
|
|
@@ -297,92 +298,77 @@ function createRouter(sofa) {
|
|
|
297
298
|
}
|
|
298
299
|
});
|
|
299
300
|
}
|
|
300
|
-
router.post('/webhook', (
|
|
301
|
-
const { subscription, variables, url } =
|
|
301
|
+
router.post('/webhook', (request, serverContext) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
302
|
+
const { subscription, variables, url } = yield request.json();
|
|
302
303
|
try {
|
|
304
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
303
305
|
const result = yield subscriptionManager.start({
|
|
304
306
|
subscription,
|
|
305
307
|
variables,
|
|
306
308
|
url,
|
|
307
309
|
}, contextValue);
|
|
308
|
-
return {
|
|
309
|
-
type: 'result',
|
|
310
|
+
return new fetch.Response(JSON.stringify(result), {
|
|
310
311
|
status: 200,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
312
|
+
statusText: 'OK',
|
|
313
|
+
headers: {
|
|
314
|
+
'Content-Type': 'application/json',
|
|
315
|
+
},
|
|
316
|
+
});
|
|
314
317
|
}
|
|
315
318
|
catch (error) {
|
|
316
|
-
return {
|
|
317
|
-
type: 'error',
|
|
319
|
+
return new fetch.Response(JSON.stringify(error), {
|
|
318
320
|
status: 500,
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
};
|
|
321
|
+
statusText: 'Subscription failed',
|
|
322
|
+
});
|
|
322
323
|
}
|
|
323
324
|
}));
|
|
324
|
-
router.post('/webhook/:id', (
|
|
325
|
-
|
|
325
|
+
router.post('/webhook/:id', (request, serverContext) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
326
|
+
var _a;
|
|
327
|
+
const id = (_a = request.params) === null || _a === void 0 ? void 0 : _a.id;
|
|
328
|
+
const body = yield request.json();
|
|
326
329
|
const variables = body.variables;
|
|
327
330
|
try {
|
|
331
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
328
332
|
const result = yield subscriptionManager.update({
|
|
329
333
|
id,
|
|
330
334
|
variables,
|
|
331
335
|
}, contextValue);
|
|
332
|
-
return {
|
|
333
|
-
type: 'result',
|
|
336
|
+
return new fetch.Response(JSON.stringify(result), {
|
|
334
337
|
status: 200,
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
+
statusText: 'OK',
|
|
339
|
+
headers: {
|
|
340
|
+
'Content-Type': 'application/json',
|
|
341
|
+
},
|
|
342
|
+
});
|
|
338
343
|
}
|
|
339
344
|
catch (error) {
|
|
340
|
-
return {
|
|
341
|
-
type: 'error',
|
|
345
|
+
return new fetch.Response(JSON.stringify(error), {
|
|
342
346
|
status: 500,
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
};
|
|
347
|
+
statusText: 'Subscription failed to update',
|
|
348
|
+
});
|
|
346
349
|
}
|
|
347
350
|
}));
|
|
348
|
-
router.delete('/webhook/:id', (
|
|
349
|
-
|
|
351
|
+
router.delete('/webhook/:id', (request) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
352
|
+
var _b;
|
|
353
|
+
const id = (_b = request.params) === null || _b === void 0 ? void 0 : _b.id;
|
|
350
354
|
try {
|
|
351
355
|
const result = yield subscriptionManager.stop(id);
|
|
352
|
-
return {
|
|
353
|
-
type: 'result',
|
|
356
|
+
return new fetch.Response(JSON.stringify(result), {
|
|
354
357
|
status: 200,
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
+
statusText: 'OK',
|
|
359
|
+
headers: {
|
|
360
|
+
'Content-Type': 'application/json',
|
|
361
|
+
},
|
|
362
|
+
});
|
|
358
363
|
}
|
|
359
364
|
catch (error) {
|
|
360
|
-
return {
|
|
361
|
-
type: 'error',
|
|
365
|
+
return new fetch.Response(JSON.stringify(error), {
|
|
362
366
|
status: 500,
|
|
363
|
-
|
|
364
|
-
error,
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
}));
|
|
368
|
-
return ({ method, url, body, contextValue }) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
369
|
-
if (!url.startsWith(sofa.basePath)) {
|
|
370
|
-
return null;
|
|
371
|
-
}
|
|
372
|
-
// trim base path and search
|
|
373
|
-
const [slicedUrl] = url.slice(sofa.basePath.length).split('?');
|
|
374
|
-
const trouterMethod = method.toUpperCase();
|
|
375
|
-
const obj = router.find(trouterMethod, slicedUrl);
|
|
376
|
-
for (const handler of obj.handlers) {
|
|
377
|
-
return yield handler({
|
|
378
|
-
url,
|
|
379
|
-
body,
|
|
380
|
-
params: obj.params,
|
|
381
|
-
contextValue,
|
|
367
|
+
statusText: 'Subscription failed to stop',
|
|
382
368
|
});
|
|
383
369
|
}
|
|
384
|
-
|
|
385
|
-
|
|
370
|
+
}));
|
|
371
|
+
return router;
|
|
386
372
|
}
|
|
387
373
|
function createQueryRoute({ sofa, router, fieldName, }) {
|
|
388
374
|
var _a, _b, _c, _d;
|
|
@@ -413,7 +399,7 @@ function createQueryRoute({ sofa, router, fieldName, }) {
|
|
|
413
399
|
path: (_c = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.path) !== null && _c !== void 0 ? _c : getPath(fieldName, isSingle && hasIdArgument),
|
|
414
400
|
responseStatus: (_d = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.responseStatus) !== null && _d !== void 0 ? _d : 200,
|
|
415
401
|
};
|
|
416
|
-
router[route.method
|
|
402
|
+
router[route.method](route.path, useHandler({ info, route, fieldName, sofa, operation }));
|
|
417
403
|
logger.debug(`[Router] ${fieldName} query available at ${route.method} ${route.path}`);
|
|
418
404
|
return {
|
|
419
405
|
document: operation,
|
|
@@ -446,7 +432,7 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
446
432
|
responseStatus: (_d = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.responseStatus) !== null && _d !== void 0 ? _d : 200,
|
|
447
433
|
};
|
|
448
434
|
const { method, path } = route;
|
|
449
|
-
router[method
|
|
435
|
+
router[method](path, useHandler({ info, route, fieldName, sofa, operation }));
|
|
450
436
|
logger.debug(`[Router] ${fieldName} mutation available at ${method} ${path}`);
|
|
451
437
|
return {
|
|
452
438
|
document: operation,
|
|
@@ -457,11 +443,24 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
457
443
|
function useHandler(config) {
|
|
458
444
|
const { sofa, operation, fieldName } = config;
|
|
459
445
|
const info = config.info;
|
|
460
|
-
return (
|
|
446
|
+
return (request, serverContext) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
447
|
+
var _a;
|
|
448
|
+
let body = {};
|
|
449
|
+
if (request.body != null) {
|
|
450
|
+
const strBody = yield request.text();
|
|
451
|
+
if (strBody) {
|
|
452
|
+
body = JSON.parse(strBody);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
461
455
|
const variableValues = info.variables.reduce((variables, variable) => {
|
|
462
456
|
const name = variable.variable.name.value;
|
|
463
457
|
const value = parseVariable({
|
|
464
|
-
value: pickParam({
|
|
458
|
+
value: pickParam({
|
|
459
|
+
url: request.url,
|
|
460
|
+
body,
|
|
461
|
+
params: request.params || {},
|
|
462
|
+
name,
|
|
463
|
+
}),
|
|
465
464
|
variable,
|
|
466
465
|
schema: sofa.schema,
|
|
467
466
|
});
|
|
@@ -470,36 +469,36 @@ function useHandler(config) {
|
|
|
470
469
|
}
|
|
471
470
|
return Object.assign(Object.assign({}, variables), { [name]: value });
|
|
472
471
|
}, {});
|
|
472
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
473
473
|
const result = yield sofa.execute({
|
|
474
474
|
schema: sofa.schema,
|
|
475
|
-
|
|
475
|
+
document: operation,
|
|
476
476
|
contextValue,
|
|
477
477
|
variableValues,
|
|
478
478
|
operationName: info.operation.name && info.operation.name.value,
|
|
479
479
|
});
|
|
480
480
|
if (result.errors) {
|
|
481
481
|
const defaultErrorHandler = (errors) => {
|
|
482
|
-
return {
|
|
483
|
-
type: 'error',
|
|
482
|
+
return new fetch.Response(errors[0], {
|
|
484
483
|
status: 500,
|
|
485
|
-
|
|
486
|
-
};
|
|
484
|
+
});
|
|
487
485
|
};
|
|
488
486
|
const errorHandler = sofa.errorHandler || defaultErrorHandler;
|
|
489
487
|
return errorHandler(result.errors);
|
|
490
488
|
}
|
|
491
|
-
return {
|
|
492
|
-
type: 'result',
|
|
489
|
+
return new fetch.Response(JSON.stringify((_a = result.data) === null || _a === void 0 ? void 0 : _a[fieldName]), {
|
|
493
490
|
status: config.route.responseStatus,
|
|
494
|
-
|
|
495
|
-
|
|
491
|
+
headers: {
|
|
492
|
+
'Content-Type': 'application/json',
|
|
493
|
+
},
|
|
494
|
+
});
|
|
496
495
|
});
|
|
497
496
|
}
|
|
498
497
|
function getPath(fieldName, hasId = false) {
|
|
499
498
|
return `/${convertName(fieldName)}${hasId ? '/:id' : ''}`;
|
|
500
499
|
}
|
|
501
500
|
function pickParam({ name, url, params, body, }) {
|
|
502
|
-
if (
|
|
501
|
+
if (name in params) {
|
|
503
502
|
return params[name];
|
|
504
503
|
}
|
|
505
504
|
const searchParams = new URLSearchParams(url.split('?')[1]);
|
|
@@ -519,9 +518,25 @@ function createSofa(config) {
|
|
|
519
518
|
const depthLimit = config.depthLimit || 1;
|
|
520
519
|
logger.debug(`[Sofa] models: ${models.join(', ')}`);
|
|
521
520
|
logger.debug(`[Sofa] ignore: ${ignore.join(', ')}`);
|
|
522
|
-
return Object.assign({ execute: graphql.
|
|
521
|
+
return Object.assign({ execute: graphql.execute,
|
|
522
|
+
subscribe: graphql.subscribe,
|
|
523
|
+
models,
|
|
523
524
|
ignore,
|
|
524
|
-
depthLimit
|
|
525
|
+
depthLimit,
|
|
526
|
+
contextFactory(serverContext) {
|
|
527
|
+
if (config.context != null) {
|
|
528
|
+
if (isContextFn(config.context)) {
|
|
529
|
+
return config.context(serverContext);
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
return config.context;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return serverContext;
|
|
536
|
+
} }, config);
|
|
537
|
+
}
|
|
538
|
+
function isContextFn(context) {
|
|
539
|
+
return typeof context === 'function';
|
|
525
540
|
}
|
|
526
541
|
// Objects and Unions are the only things that are used to define return types
|
|
527
542
|
// and both might contain an ID
|
|
@@ -706,7 +721,7 @@ function resolveRequestBody(variables) {
|
|
|
706
721
|
}
|
|
707
722
|
const properties = {};
|
|
708
723
|
const required = [];
|
|
709
|
-
variables.forEach(variable => {
|
|
724
|
+
variables.forEach((variable) => {
|
|
710
725
|
if (variable.type.kind === graphql.Kind.NON_NULL_TYPE) {
|
|
711
726
|
required.push(variable.variable.name.value);
|
|
712
727
|
}
|
|
@@ -762,7 +777,7 @@ function resolveDescription(schema, operation) {
|
|
|
762
777
|
if (!isObjectTypeDefinitionNode(definitionNode)) {
|
|
763
778
|
return '';
|
|
764
779
|
}
|
|
765
|
-
const fieldNode = definitionNode.fields.find(field => field.name.value === fieldName);
|
|
780
|
+
const fieldNode = definitionNode.fields.find((field) => field.name.value === fieldName);
|
|
766
781
|
const descriptionDefinition = fieldNode && fieldNode.description;
|
|
767
782
|
return descriptionDefinition && descriptionDefinition.value
|
|
768
783
|
? descriptionDefinition.value
|
|
@@ -815,84 +830,12 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
815
830
|
get() {
|
|
816
831
|
return swagger;
|
|
817
832
|
},
|
|
818
|
-
save(filepath) {
|
|
819
|
-
const isJSON = /\.json$/i;
|
|
820
|
-
const isYAML = /.ya?ml$/i;
|
|
821
|
-
if (isJSON.test(filepath)) {
|
|
822
|
-
writeOutput(filepath, JSON.stringify(swagger, null, 2));
|
|
823
|
-
}
|
|
824
|
-
else if (isYAML.test(filepath)) {
|
|
825
|
-
writeOutput(filepath, jsYaml.dump(swagger));
|
|
826
|
-
}
|
|
827
|
-
else {
|
|
828
|
-
throw new Error('We only support JSON and YAML files');
|
|
829
|
-
}
|
|
830
|
-
},
|
|
831
833
|
};
|
|
832
|
-
}
|
|
833
|
-
function writeOutput(filepath, contents) {
|
|
834
|
-
fs.writeFileSync(filepath, contents, {
|
|
835
|
-
encoding: 'utf-8',
|
|
836
|
-
});
|
|
837
834
|
}
|
|
838
835
|
|
|
839
|
-
function
|
|
840
|
-
return
|
|
841
|
-
}
|
|
842
|
-
function useSofa(_a) {
|
|
843
|
-
var { context } = _a, config = tslib.__rest(_a, ["context"]);
|
|
844
|
-
const invokeSofa = createSofaRouter(config);
|
|
845
|
-
return (req, res, next) => tslib.__awaiter(this, void 0, void 0, function* () {
|
|
846
|
-
var _b;
|
|
847
|
-
try {
|
|
848
|
-
let contextValue = { req };
|
|
849
|
-
if (context) {
|
|
850
|
-
if (typeof context === 'function') {
|
|
851
|
-
contextValue = yield context({ req, res });
|
|
852
|
-
}
|
|
853
|
-
else {
|
|
854
|
-
contextValue = context;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
const response = yield invokeSofa({
|
|
858
|
-
method: req.method,
|
|
859
|
-
url: (_b = req.originalUrl) !== null && _b !== void 0 ? _b : req.url,
|
|
860
|
-
body: req.body,
|
|
861
|
-
contextValue,
|
|
862
|
-
});
|
|
863
|
-
if (response == null) {
|
|
864
|
-
next();
|
|
865
|
-
}
|
|
866
|
-
else {
|
|
867
|
-
const headers = {
|
|
868
|
-
'Content-Type': 'application/json',
|
|
869
|
-
};
|
|
870
|
-
if (response.statusMessage) {
|
|
871
|
-
res.writeHead(response.status, response.statusMessage, headers);
|
|
872
|
-
}
|
|
873
|
-
else {
|
|
874
|
-
res.writeHead(response.status, headers);
|
|
875
|
-
}
|
|
876
|
-
if (response.type === 'result') {
|
|
877
|
-
res.end(JSON.stringify(response.body));
|
|
878
|
-
}
|
|
879
|
-
if (response.type === 'error') {
|
|
880
|
-
res.end(JSON.stringify(response.error));
|
|
881
|
-
}
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
catch (error) {
|
|
885
|
-
next(error);
|
|
886
|
-
}
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
|
-
function createSofaRouter(config) {
|
|
890
|
-
const sofa = createSofa(config);
|
|
891
|
-
const router = createRouter(sofa);
|
|
892
|
-
return router;
|
|
836
|
+
function useSofa(config) {
|
|
837
|
+
return server.createServerAdapter(createRouter(createSofa(config)));
|
|
893
838
|
}
|
|
894
839
|
|
|
895
840
|
exports.OpenAPI = OpenAPI;
|
|
896
|
-
exports.createSofaRouter = createSofaRouter;
|
|
897
|
-
exports.isContextFn = isContextFn;
|
|
898
841
|
exports.useSofa = useSofa;
|
package/index.mjs
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { __awaiter, __asyncValues
|
|
2
|
-
import { getOperationAST, Kind, isScalarType, isEqualType, GraphQLBoolean, isInputObjectType,
|
|
3
|
-
import
|
|
1
|
+
import { __awaiter, __asyncValues } from 'tslib';
|
|
2
|
+
import { getOperationAST, Kind, isScalarType, isEqualType, GraphQLBoolean, isInputObjectType, isObjectType, isNonNullType, execute, subscribe, getNamedType, isListType, isEnumType, parse, printType, isIntrospectionType } from 'graphql';
|
|
3
|
+
import { Router } from 'itty-router';
|
|
4
4
|
import { buildOperationNodeForField } from '@graphql-tools/utils';
|
|
5
5
|
import { paramCase } from 'param-case';
|
|
6
6
|
import { v4 } from 'uuid';
|
|
7
|
-
import { fetch } from '
|
|
7
|
+
import { fetch, Response } from '@whatwg-node/fetch';
|
|
8
8
|
import colors from 'ansi-colors';
|
|
9
|
-
import {
|
|
10
|
-
import { writeFileSync } from 'fs';
|
|
9
|
+
import { createServerAdapter } from '@whatwg-node/server';
|
|
11
10
|
import { titleCase } from 'title-case';
|
|
12
11
|
|
|
13
12
|
function getOperationInfo(doc) {
|
|
@@ -56,7 +55,7 @@ function resolveVariable({ value, type, schema, }) {
|
|
|
56
55
|
return value;
|
|
57
56
|
}
|
|
58
57
|
if (type.kind === Kind.LIST_TYPE) {
|
|
59
|
-
return (Array.isArray(value) ? value : [value]).map(val => resolveVariable({
|
|
58
|
+
return (Array.isArray(value) ? value : [value]).map((val) => resolveVariable({
|
|
60
59
|
value: val,
|
|
61
60
|
type: type.type,
|
|
62
61
|
schema,
|
|
@@ -175,7 +174,7 @@ class SubscriptionManager {
|
|
|
175
174
|
}
|
|
176
175
|
return Object.assign(Object.assign({}, values), { [name]: value });
|
|
177
176
|
}, {});
|
|
178
|
-
const execution = yield subscribe({
|
|
177
|
+
const execution = yield this.sofa.subscribe({
|
|
179
178
|
schema: this.sofa.schema,
|
|
180
179
|
document,
|
|
181
180
|
operationName,
|
|
@@ -235,7 +234,7 @@ class SubscriptionManager {
|
|
|
235
234
|
body: JSON.stringify(result),
|
|
236
235
|
headers: {
|
|
237
236
|
'Content-Type': 'application/json',
|
|
238
|
-
}
|
|
237
|
+
},
|
|
239
238
|
});
|
|
240
239
|
yield response.text();
|
|
241
240
|
});
|
|
@@ -271,7 +270,9 @@ class SubscriptionManager {
|
|
|
271
270
|
|
|
272
271
|
function createRouter(sofa) {
|
|
273
272
|
logger.debug('[Sofa] Creating router');
|
|
274
|
-
const router =
|
|
273
|
+
const router = Router({
|
|
274
|
+
base: sofa.basePath,
|
|
275
|
+
});
|
|
275
276
|
const queryType = sofa.schema.getQueryType();
|
|
276
277
|
const mutationType = sofa.schema.getMutationType();
|
|
277
278
|
const subscriptionManager = new SubscriptionManager(sofa);
|
|
@@ -291,92 +292,77 @@ function createRouter(sofa) {
|
|
|
291
292
|
}
|
|
292
293
|
});
|
|
293
294
|
}
|
|
294
|
-
router.post('/webhook', (
|
|
295
|
-
const { subscription, variables, url } =
|
|
295
|
+
router.post('/webhook', (request, serverContext) => __awaiter(this, void 0, void 0, function* () {
|
|
296
|
+
const { subscription, variables, url } = yield request.json();
|
|
296
297
|
try {
|
|
298
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
297
299
|
const result = yield subscriptionManager.start({
|
|
298
300
|
subscription,
|
|
299
301
|
variables,
|
|
300
302
|
url,
|
|
301
303
|
}, contextValue);
|
|
302
|
-
return {
|
|
303
|
-
type: 'result',
|
|
304
|
+
return new Response(JSON.stringify(result), {
|
|
304
305
|
status: 200,
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
306
|
+
statusText: 'OK',
|
|
307
|
+
headers: {
|
|
308
|
+
'Content-Type': 'application/json',
|
|
309
|
+
},
|
|
310
|
+
});
|
|
308
311
|
}
|
|
309
312
|
catch (error) {
|
|
310
|
-
return {
|
|
311
|
-
type: 'error',
|
|
313
|
+
return new Response(JSON.stringify(error), {
|
|
312
314
|
status: 500,
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
};
|
|
315
|
+
statusText: 'Subscription failed',
|
|
316
|
+
});
|
|
316
317
|
}
|
|
317
318
|
}));
|
|
318
|
-
router.post('/webhook/:id', (
|
|
319
|
-
|
|
319
|
+
router.post('/webhook/:id', (request, serverContext) => __awaiter(this, void 0, void 0, function* () {
|
|
320
|
+
var _a;
|
|
321
|
+
const id = (_a = request.params) === null || _a === void 0 ? void 0 : _a.id;
|
|
322
|
+
const body = yield request.json();
|
|
320
323
|
const variables = body.variables;
|
|
321
324
|
try {
|
|
325
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
322
326
|
const result = yield subscriptionManager.update({
|
|
323
327
|
id,
|
|
324
328
|
variables,
|
|
325
329
|
}, contextValue);
|
|
326
|
-
return {
|
|
327
|
-
type: 'result',
|
|
330
|
+
return new Response(JSON.stringify(result), {
|
|
328
331
|
status: 200,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
+
statusText: 'OK',
|
|
333
|
+
headers: {
|
|
334
|
+
'Content-Type': 'application/json',
|
|
335
|
+
},
|
|
336
|
+
});
|
|
332
337
|
}
|
|
333
338
|
catch (error) {
|
|
334
|
-
return {
|
|
335
|
-
type: 'error',
|
|
339
|
+
return new Response(JSON.stringify(error), {
|
|
336
340
|
status: 500,
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
};
|
|
341
|
+
statusText: 'Subscription failed to update',
|
|
342
|
+
});
|
|
340
343
|
}
|
|
341
344
|
}));
|
|
342
|
-
router.delete('/webhook/:id', (
|
|
343
|
-
|
|
345
|
+
router.delete('/webhook/:id', (request) => __awaiter(this, void 0, void 0, function* () {
|
|
346
|
+
var _b;
|
|
347
|
+
const id = (_b = request.params) === null || _b === void 0 ? void 0 : _b.id;
|
|
344
348
|
try {
|
|
345
349
|
const result = yield subscriptionManager.stop(id);
|
|
346
|
-
return {
|
|
347
|
-
type: 'result',
|
|
350
|
+
return new Response(JSON.stringify(result), {
|
|
348
351
|
status: 200,
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
+
statusText: 'OK',
|
|
353
|
+
headers: {
|
|
354
|
+
'Content-Type': 'application/json',
|
|
355
|
+
},
|
|
356
|
+
});
|
|
352
357
|
}
|
|
353
358
|
catch (error) {
|
|
354
|
-
return {
|
|
355
|
-
type: 'error',
|
|
359
|
+
return new Response(JSON.stringify(error), {
|
|
356
360
|
status: 500,
|
|
357
|
-
|
|
358
|
-
error,
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
}));
|
|
362
|
-
return ({ method, url, body, contextValue }) => __awaiter(this, void 0, void 0, function* () {
|
|
363
|
-
if (!url.startsWith(sofa.basePath)) {
|
|
364
|
-
return null;
|
|
365
|
-
}
|
|
366
|
-
// trim base path and search
|
|
367
|
-
const [slicedUrl] = url.slice(sofa.basePath.length).split('?');
|
|
368
|
-
const trouterMethod = method.toUpperCase();
|
|
369
|
-
const obj = router.find(trouterMethod, slicedUrl);
|
|
370
|
-
for (const handler of obj.handlers) {
|
|
371
|
-
return yield handler({
|
|
372
|
-
url,
|
|
373
|
-
body,
|
|
374
|
-
params: obj.params,
|
|
375
|
-
contextValue,
|
|
361
|
+
statusText: 'Subscription failed to stop',
|
|
376
362
|
});
|
|
377
363
|
}
|
|
378
|
-
|
|
379
|
-
|
|
364
|
+
}));
|
|
365
|
+
return router;
|
|
380
366
|
}
|
|
381
367
|
function createQueryRoute({ sofa, router, fieldName, }) {
|
|
382
368
|
var _a, _b, _c, _d;
|
|
@@ -407,7 +393,7 @@ function createQueryRoute({ sofa, router, fieldName, }) {
|
|
|
407
393
|
path: (_c = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.path) !== null && _c !== void 0 ? _c : getPath(fieldName, isSingle && hasIdArgument),
|
|
408
394
|
responseStatus: (_d = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.responseStatus) !== null && _d !== void 0 ? _d : 200,
|
|
409
395
|
};
|
|
410
|
-
router[route.method
|
|
396
|
+
router[route.method](route.path, useHandler({ info, route, fieldName, sofa, operation }));
|
|
411
397
|
logger.debug(`[Router] ${fieldName} query available at ${route.method} ${route.path}`);
|
|
412
398
|
return {
|
|
413
399
|
document: operation,
|
|
@@ -440,7 +426,7 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
440
426
|
responseStatus: (_d = routeConfig === null || routeConfig === void 0 ? void 0 : routeConfig.responseStatus) !== null && _d !== void 0 ? _d : 200,
|
|
441
427
|
};
|
|
442
428
|
const { method, path } = route;
|
|
443
|
-
router[method
|
|
429
|
+
router[method](path, useHandler({ info, route, fieldName, sofa, operation }));
|
|
444
430
|
logger.debug(`[Router] ${fieldName} mutation available at ${method} ${path}`);
|
|
445
431
|
return {
|
|
446
432
|
document: operation,
|
|
@@ -451,11 +437,24 @@ function createMutationRoute({ sofa, router, fieldName, }) {
|
|
|
451
437
|
function useHandler(config) {
|
|
452
438
|
const { sofa, operation, fieldName } = config;
|
|
453
439
|
const info = config.info;
|
|
454
|
-
return (
|
|
440
|
+
return (request, serverContext) => __awaiter(this, void 0, void 0, function* () {
|
|
441
|
+
var _a;
|
|
442
|
+
let body = {};
|
|
443
|
+
if (request.body != null) {
|
|
444
|
+
const strBody = yield request.text();
|
|
445
|
+
if (strBody) {
|
|
446
|
+
body = JSON.parse(strBody);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
455
449
|
const variableValues = info.variables.reduce((variables, variable) => {
|
|
456
450
|
const name = variable.variable.name.value;
|
|
457
451
|
const value = parseVariable({
|
|
458
|
-
value: pickParam({
|
|
452
|
+
value: pickParam({
|
|
453
|
+
url: request.url,
|
|
454
|
+
body,
|
|
455
|
+
params: request.params || {},
|
|
456
|
+
name,
|
|
457
|
+
}),
|
|
459
458
|
variable,
|
|
460
459
|
schema: sofa.schema,
|
|
461
460
|
});
|
|
@@ -464,36 +463,36 @@ function useHandler(config) {
|
|
|
464
463
|
}
|
|
465
464
|
return Object.assign(Object.assign({}, variables), { [name]: value });
|
|
466
465
|
}, {});
|
|
466
|
+
const contextValue = yield sofa.contextFactory(serverContext);
|
|
467
467
|
const result = yield sofa.execute({
|
|
468
468
|
schema: sofa.schema,
|
|
469
|
-
|
|
469
|
+
document: operation,
|
|
470
470
|
contextValue,
|
|
471
471
|
variableValues,
|
|
472
472
|
operationName: info.operation.name && info.operation.name.value,
|
|
473
473
|
});
|
|
474
474
|
if (result.errors) {
|
|
475
475
|
const defaultErrorHandler = (errors) => {
|
|
476
|
-
return {
|
|
477
|
-
type: 'error',
|
|
476
|
+
return new Response(errors[0], {
|
|
478
477
|
status: 500,
|
|
479
|
-
|
|
480
|
-
};
|
|
478
|
+
});
|
|
481
479
|
};
|
|
482
480
|
const errorHandler = sofa.errorHandler || defaultErrorHandler;
|
|
483
481
|
return errorHandler(result.errors);
|
|
484
482
|
}
|
|
485
|
-
return {
|
|
486
|
-
type: 'result',
|
|
483
|
+
return new Response(JSON.stringify((_a = result.data) === null || _a === void 0 ? void 0 : _a[fieldName]), {
|
|
487
484
|
status: config.route.responseStatus,
|
|
488
|
-
|
|
489
|
-
|
|
485
|
+
headers: {
|
|
486
|
+
'Content-Type': 'application/json',
|
|
487
|
+
},
|
|
488
|
+
});
|
|
490
489
|
});
|
|
491
490
|
}
|
|
492
491
|
function getPath(fieldName, hasId = false) {
|
|
493
492
|
return `/${convertName(fieldName)}${hasId ? '/:id' : ''}`;
|
|
494
493
|
}
|
|
495
494
|
function pickParam({ name, url, params, body, }) {
|
|
496
|
-
if (
|
|
495
|
+
if (name in params) {
|
|
497
496
|
return params[name];
|
|
498
497
|
}
|
|
499
498
|
const searchParams = new URLSearchParams(url.split('?')[1]);
|
|
@@ -513,9 +512,25 @@ function createSofa(config) {
|
|
|
513
512
|
const depthLimit = config.depthLimit || 1;
|
|
514
513
|
logger.debug(`[Sofa] models: ${models.join(', ')}`);
|
|
515
514
|
logger.debug(`[Sofa] ignore: ${ignore.join(', ')}`);
|
|
516
|
-
return Object.assign({ execute
|
|
515
|
+
return Object.assign({ execute,
|
|
516
|
+
subscribe,
|
|
517
|
+
models,
|
|
517
518
|
ignore,
|
|
518
|
-
depthLimit
|
|
519
|
+
depthLimit,
|
|
520
|
+
contextFactory(serverContext) {
|
|
521
|
+
if (config.context != null) {
|
|
522
|
+
if (isContextFn(config.context)) {
|
|
523
|
+
return config.context(serverContext);
|
|
524
|
+
}
|
|
525
|
+
else {
|
|
526
|
+
return config.context;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return serverContext;
|
|
530
|
+
} }, config);
|
|
531
|
+
}
|
|
532
|
+
function isContextFn(context) {
|
|
533
|
+
return typeof context === 'function';
|
|
519
534
|
}
|
|
520
535
|
// Objects and Unions are the only things that are used to define return types
|
|
521
536
|
// and both might contain an ID
|
|
@@ -700,7 +715,7 @@ function resolveRequestBody(variables) {
|
|
|
700
715
|
}
|
|
701
716
|
const properties = {};
|
|
702
717
|
const required = [];
|
|
703
|
-
variables.forEach(variable => {
|
|
718
|
+
variables.forEach((variable) => {
|
|
704
719
|
if (variable.type.kind === Kind.NON_NULL_TYPE) {
|
|
705
720
|
required.push(variable.variable.name.value);
|
|
706
721
|
}
|
|
@@ -756,7 +771,7 @@ function resolveDescription(schema, operation) {
|
|
|
756
771
|
if (!isObjectTypeDefinitionNode(definitionNode)) {
|
|
757
772
|
return '';
|
|
758
773
|
}
|
|
759
|
-
const fieldNode = definitionNode.fields.find(field => field.name.value === fieldName);
|
|
774
|
+
const fieldNode = definitionNode.fields.find((field) => field.name.value === fieldName);
|
|
760
775
|
const descriptionDefinition = fieldNode && fieldNode.description;
|
|
761
776
|
return descriptionDefinition && descriptionDefinition.value
|
|
762
777
|
? descriptionDefinition.value
|
|
@@ -809,81 +824,11 @@ function OpenAPI({ schema, info, servers, components, security, tags, }) {
|
|
|
809
824
|
get() {
|
|
810
825
|
return swagger;
|
|
811
826
|
},
|
|
812
|
-
save(filepath) {
|
|
813
|
-
const isJSON = /\.json$/i;
|
|
814
|
-
const isYAML = /.ya?ml$/i;
|
|
815
|
-
if (isJSON.test(filepath)) {
|
|
816
|
-
writeOutput(filepath, JSON.stringify(swagger, null, 2));
|
|
817
|
-
}
|
|
818
|
-
else if (isYAML.test(filepath)) {
|
|
819
|
-
writeOutput(filepath, dump(swagger));
|
|
820
|
-
}
|
|
821
|
-
else {
|
|
822
|
-
throw new Error('We only support JSON and YAML files');
|
|
823
|
-
}
|
|
824
|
-
},
|
|
825
827
|
};
|
|
826
|
-
}
|
|
827
|
-
function writeOutput(filepath, contents) {
|
|
828
|
-
writeFileSync(filepath, contents, {
|
|
829
|
-
encoding: 'utf-8',
|
|
830
|
-
});
|
|
831
828
|
}
|
|
832
829
|
|
|
833
|
-
function
|
|
834
|
-
return
|
|
835
|
-
}
|
|
836
|
-
function useSofa(_a) {
|
|
837
|
-
var { context } = _a, config = __rest(_a, ["context"]);
|
|
838
|
-
const invokeSofa = createSofaRouter(config);
|
|
839
|
-
return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
840
|
-
var _b;
|
|
841
|
-
try {
|
|
842
|
-
let contextValue = { req };
|
|
843
|
-
if (context) {
|
|
844
|
-
if (typeof context === 'function') {
|
|
845
|
-
contextValue = yield context({ req, res });
|
|
846
|
-
}
|
|
847
|
-
else {
|
|
848
|
-
contextValue = context;
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
const response = yield invokeSofa({
|
|
852
|
-
method: req.method,
|
|
853
|
-
url: (_b = req.originalUrl) !== null && _b !== void 0 ? _b : req.url,
|
|
854
|
-
body: req.body,
|
|
855
|
-
contextValue,
|
|
856
|
-
});
|
|
857
|
-
if (response == null) {
|
|
858
|
-
next();
|
|
859
|
-
}
|
|
860
|
-
else {
|
|
861
|
-
const headers = {
|
|
862
|
-
'Content-Type': 'application/json',
|
|
863
|
-
};
|
|
864
|
-
if (response.statusMessage) {
|
|
865
|
-
res.writeHead(response.status, response.statusMessage, headers);
|
|
866
|
-
}
|
|
867
|
-
else {
|
|
868
|
-
res.writeHead(response.status, headers);
|
|
869
|
-
}
|
|
870
|
-
if (response.type === 'result') {
|
|
871
|
-
res.end(JSON.stringify(response.body));
|
|
872
|
-
}
|
|
873
|
-
if (response.type === 'error') {
|
|
874
|
-
res.end(JSON.stringify(response.error));
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
catch (error) {
|
|
879
|
-
next(error);
|
|
880
|
-
}
|
|
881
|
-
});
|
|
882
|
-
}
|
|
883
|
-
function createSofaRouter(config) {
|
|
884
|
-
const sofa = createSofa(config);
|
|
885
|
-
const router = createRouter(sofa);
|
|
886
|
-
return router;
|
|
830
|
+
function useSofa(config) {
|
|
831
|
+
return createServerAdapter(createRouter(createSofa(config)));
|
|
887
832
|
}
|
|
888
833
|
|
|
889
|
-
export { OpenAPI,
|
|
834
|
+
export { OpenAPI, useSofa };
|
package/open-api/index.d.ts
CHANGED
|
@@ -9,8 +9,7 @@ export declare function OpenAPI({ schema, info, servers, components, security, t
|
|
|
9
9
|
tags?: Record<string, any>[];
|
|
10
10
|
}): {
|
|
11
11
|
addRoute(info: RouteInfo, config?: {
|
|
12
|
-
basePath?: string
|
|
13
|
-
}
|
|
12
|
+
basePath?: string;
|
|
13
|
+
}): void;
|
|
14
14
|
get(): any;
|
|
15
|
-
save(filepath: string): void;
|
|
16
15
|
};
|
package/package.json
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sofa-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0-alpha.0",
|
|
4
4
|
"description": "Create REST APIs with GraphQL",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
7
7
|
"graphql": "^0.13.2 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@graphql-tools/utils": "8.
|
|
11
|
-
"@
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
10
|
+
"@graphql-tools/utils": "8.12.0",
|
|
11
|
+
"@whatwg-node/fetch": "^0.4.3",
|
|
12
|
+
"@whatwg-node/server": "^0.4.1",
|
|
13
|
+
"ansi-colors": "4.1.3",
|
|
14
|
+
"itty-router": "^2.6.1",
|
|
15
15
|
"param-case": "3.0.4",
|
|
16
16
|
"title-case": "3.0.3",
|
|
17
|
-
"
|
|
18
|
-
"tslib": "2.3.1",
|
|
17
|
+
"tslib": "2.4.0",
|
|
19
18
|
"uuid": "8.3.2"
|
|
20
19
|
},
|
|
21
20
|
"repository": {
|
|
@@ -51,4 +50,4 @@
|
|
|
51
50
|
},
|
|
52
51
|
"./package.json": "./package.json"
|
|
53
52
|
}
|
|
54
|
-
}
|
|
53
|
+
}
|
package/router.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Request as IttyRequest, Router } from 'itty-router';
|
|
2
|
+
import type { Sofa } from './sofa';
|
|
3
|
+
export declare type ErrorHandler = (errors: ReadonlyArray<any>) => Response;
|
|
4
|
+
declare type SofaRequest = IttyRequest & Request;
|
|
5
|
+
export declare function createRouter(sofa: Sofa): Router<SofaRequest, {}>;
|
|
6
|
+
export {};
|
package/sofa.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { GraphQLSchema } from 'graphql';
|
|
2
|
-
import { Ignore,
|
|
3
|
-
import { ErrorHandler } from './
|
|
1
|
+
import { GraphQLSchema, subscribe, execute } from 'graphql';
|
|
2
|
+
import { Ignore, OnRoute, Method, ContextFn, ContextValue } from './types';
|
|
3
|
+
import { ErrorHandler } from './router';
|
|
4
4
|
interface RouteConfig {
|
|
5
5
|
method?: Method;
|
|
6
6
|
path?: string;
|
|
@@ -14,7 +14,8 @@ export interface Route {
|
|
|
14
14
|
export interface SofaConfig {
|
|
15
15
|
basePath: string;
|
|
16
16
|
schema: GraphQLSchema;
|
|
17
|
-
execute?:
|
|
17
|
+
execute?: typeof execute;
|
|
18
|
+
subscribe?: typeof subscribe;
|
|
18
19
|
/**
|
|
19
20
|
* Treats an Object with an ID as not a model.
|
|
20
21
|
* @example ["User", "Message.author"]
|
|
@@ -27,6 +28,7 @@ export interface SofaConfig {
|
|
|
27
28
|
* Overwrites the default HTTP route.
|
|
28
29
|
*/
|
|
29
30
|
routes?: Record<string, RouteConfig>;
|
|
31
|
+
context?: ContextFn | ContextValue;
|
|
30
32
|
}
|
|
31
33
|
export interface Sofa {
|
|
32
34
|
basePath: string;
|
|
@@ -35,9 +37,12 @@ export interface Sofa {
|
|
|
35
37
|
ignore: Ignore;
|
|
36
38
|
depthLimit: number;
|
|
37
39
|
routes?: Record<string, RouteConfig>;
|
|
38
|
-
execute:
|
|
40
|
+
execute: typeof execute;
|
|
41
|
+
subscribe: typeof subscribe;
|
|
39
42
|
onRoute?: OnRoute;
|
|
40
43
|
errorHandler?: ErrorHandler;
|
|
44
|
+
contextFactory: ContextFn;
|
|
41
45
|
}
|
|
42
46
|
export declare function createSofa(config: SofaConfig): Sofa;
|
|
47
|
+
export declare function isContextFn(context: any): context is ContextFn;
|
|
43
48
|
export {};
|
package/types.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DocumentNode } from 'graphql';
|
|
2
2
|
export declare type ContextValue = Record<string, any>;
|
|
3
3
|
export declare type Ignore = string[];
|
|
4
|
-
export declare type ExecuteFn = (args: GraphQLArgs) => Promise<ExecutionResult<any>>;
|
|
5
4
|
export declare type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
|
|
6
5
|
export interface RouteInfo {
|
|
7
6
|
document: DocumentNode;
|
|
@@ -9,3 +8,7 @@ export interface RouteInfo {
|
|
|
9
8
|
method: Method;
|
|
10
9
|
}
|
|
11
10
|
export declare type OnRoute = (info: RouteInfo) => void;
|
|
11
|
+
export declare type ContextFn = (init: {
|
|
12
|
+
req: any;
|
|
13
|
+
res: any;
|
|
14
|
+
}) => Promise<ContextValue> | ContextValue;
|
package/express.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { Sofa } from './sofa';
|
|
2
|
-
import type { ContextValue } from './types';
|
|
3
|
-
export declare type ErrorHandler = (errors: ReadonlyArray<any>) => RouterError;
|
|
4
|
-
declare type RouterRequest = {
|
|
5
|
-
method: string;
|
|
6
|
-
url: string;
|
|
7
|
-
body: any;
|
|
8
|
-
contextValue: ContextValue;
|
|
9
|
-
};
|
|
10
|
-
declare type RouterResult = {
|
|
11
|
-
type: 'result';
|
|
12
|
-
status: number;
|
|
13
|
-
statusMessage?: string;
|
|
14
|
-
body: any;
|
|
15
|
-
};
|
|
16
|
-
declare type RouterError = {
|
|
17
|
-
type: 'error';
|
|
18
|
-
status: number;
|
|
19
|
-
statusMessage?: string;
|
|
20
|
-
error: any;
|
|
21
|
-
};
|
|
22
|
-
declare type RouterResponse = RouterResult | RouterError;
|
|
23
|
-
declare type Router = (request: RouterRequest) => Promise<null | RouterResponse>;
|
|
24
|
-
export declare function createRouter(sofa: Sofa): Router;
|
|
25
|
-
export {};
|