barehttp 0.4.2 → 0.5.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 +35 -10
- package/lib/logger/index.js +6 -2
- package/lib/request.d.ts +2 -0
- package/lib/request.js +39 -31
- package/lib/schemas/custom-schema.d.ts +32 -0
- package/lib/schemas/custom-schema.js +69 -0
- package/lib/schemas/dirty-tsm.d.ts +1 -0
- package/lib/schemas/dirty-tsm.js +201 -0
- package/lib/schemas/generator.d.ts +7 -0
- package/lib/schemas/generator.js +186 -0
- package/lib/schemas/helpers.d.ts +27 -0
- package/lib/schemas/helpers.js +50 -0
- package/lib/schemas/json-schema.d.ts +2 -0
- package/lib/schemas/json-schema.js +52 -0
- package/lib/schemas/openami-schema.d.ts +2 -0
- package/lib/schemas/openami-schema.js +63 -0
- package/lib/schemas/project.d.ts +0 -0
- package/lib/schemas/project.js +1 -0
- package/lib/server.d.ts +32 -12
- package/lib/server.js +103 -62
- package/lib/websocket.js +3 -1
- package/package.json +14 -10
- package/lib/report.d.ts +0 -2
- package/lib/report.js +0 -20
package/README.md
CHANGED
|
@@ -51,23 +51,23 @@ import { BareHttp, logMe } from 'barehttp';
|
|
|
51
51
|
|
|
52
52
|
const app = new BareHttp();
|
|
53
53
|
|
|
54
|
-
app.get({
|
|
54
|
+
app.route.get({
|
|
55
55
|
route: '/route',
|
|
56
56
|
handler: function routeGet(flow) {
|
|
57
|
-
flow.json({
|
|
57
|
+
flow.json({ everything: 'OK' });
|
|
58
58
|
})
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
// you can chain the routes
|
|
63
|
-
app
|
|
63
|
+
app.route
|
|
64
64
|
.post({
|
|
65
65
|
route: '/route',
|
|
66
66
|
handler: async function routePost(flow) {
|
|
67
67
|
return 'RESPONSE POST';
|
|
68
68
|
},
|
|
69
69
|
})
|
|
70
|
-
.patch({
|
|
70
|
+
.route.patch({
|
|
71
71
|
route: '/route',
|
|
72
72
|
handler: async function routePatch(flow) {
|
|
73
73
|
return 'RESPONSE PATCH';
|
|
@@ -90,7 +90,7 @@ import { BareHttp, logMe } from 'barehttp';
|
|
|
90
90
|
|
|
91
91
|
const app = new BareHttp({ logging: true });
|
|
92
92
|
|
|
93
|
-
app.get({
|
|
93
|
+
app.route.get({
|
|
94
94
|
route:'/route',
|
|
95
95
|
handler: async function routeV1() {
|
|
96
96
|
return { promised: 'data' };
|
|
@@ -195,7 +195,7 @@ The order of the middlewares is followed by code declarations order.
|
|
|
195
195
|
|
|
196
196
|
---
|
|
197
197
|
|
|
198
|
-
## `BareServer.get | post | patch | put | delete | options | head | declare` (Function)
|
|
198
|
+
## `BareServer.route.get | post | patch | put | delete | options | head | declare` (Function)
|
|
199
199
|
|
|
200
200
|
To set a route for `get | post | patch | put | delete | options | head` with following parameters:
|
|
201
201
|
|
|
@@ -206,7 +206,7 @@ To set a route for `get | post | patch | put | delete | options | head` with fol
|
|
|
206
206
|
Example
|
|
207
207
|
|
|
208
208
|
```ts
|
|
209
|
-
app.get({
|
|
209
|
+
app.route.get({
|
|
210
210
|
route: '/route',
|
|
211
211
|
options: { timeout: 2000 },
|
|
212
212
|
handler: async (flow) => {
|
|
@@ -214,7 +214,7 @@ app.get({
|
|
|
214
214
|
},
|
|
215
215
|
});
|
|
216
216
|
|
|
217
|
-
app.declare({
|
|
217
|
+
app.route.declare({
|
|
218
218
|
route: '/declared_route',
|
|
219
219
|
handler: () => {
|
|
220
220
|
return 'My declared route response';
|
|
@@ -399,17 +399,42 @@ Some of the features are in progress.
|
|
|
399
399
|
- [x] Request execution cancellation by timeout
|
|
400
400
|
- [x] Bulk/chaining routes declaration
|
|
401
401
|
- [x] Runtime routes hot swapping
|
|
402
|
+
- [x] runtime validation schema generation per route response types (on project compile/on launch)
|
|
402
403
|
- [ ] middlewares per route
|
|
403
404
|
- [ ] swagger OpenAPI 3.0 on `/docs` endpoint
|
|
404
405
|
- [ ] swagger OpenAPI 3.0 scheme on `/docs_raw` endpoint
|
|
405
406
|
- [ ] optional export of generated schema to a location (yaml, json)
|
|
406
407
|
- [ ] streaming/receiving of chunked multipart
|
|
407
|
-
- [ ] runtime validation schema generation per route response types (on project compile/on launch)
|
|
408
408
|
- [ ] runtime route params or query validation upon declared types (on project compile/on launch)
|
|
409
409
|
|
|
410
|
+
## Runtime schema validation on response (EXPERIMENTAL)
|
|
411
|
+
|
|
412
|
+
This feature enables a runtime check for the returned value for a route,
|
|
413
|
+
for now it only works for `return` statements of the routes declared in handlers.
|
|
414
|
+
|
|
415
|
+
Please write your `return` statements with plain response objects within the `handler` or `controller` function.
|
|
416
|
+
|
|
417
|
+
To enable this feature you need to set up the following:
|
|
418
|
+
|
|
419
|
+
- On `BareHttp` settings set: `enableSchemaValidation: true`
|
|
420
|
+
- On `BareHttp` settings set: `declaredRoutesPaths: [...array of paths to routes]`,
|
|
421
|
+
- On `route.<method>` declaration set: `options: { builtInRuntime: { output: true } }`
|
|
422
|
+
|
|
410
423
|
## Benchmarks
|
|
411
424
|
|
|
412
|
-
|
|
425
|
+
Done on MacBook Pro with M1 Pro processor. No logs enabled. `NODE_ENV=production` is set. All settings set to default.
|
|
426
|
+
|
|
427
|
+
### BareHttp
|
|
428
|
+
|
|
429
|
+

|
|
430
|
+
|
|
431
|
+
### Express
|
|
432
|
+
|
|
433
|
+

|
|
434
|
+
|
|
435
|
+
### Fastify
|
|
436
|
+
|
|
437
|
+

|
|
413
438
|
|
|
414
439
|
## Support
|
|
415
440
|
|
package/lib/logger/index.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
3
|
if (k2 === undefined) k2 = k;
|
|
4
|
-
Object.
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
5
9
|
}) : (function(o, m, k, k2) {
|
|
6
10
|
if (k2 === undefined) k2 = k;
|
|
7
11
|
o[k2] = m[k];
|
|
@@ -30,7 +34,7 @@ const pinoCommonOptions = {
|
|
|
30
34
|
level: (label) => ({ level: label }),
|
|
31
35
|
},
|
|
32
36
|
messageKey: 'message',
|
|
33
|
-
|
|
37
|
+
transport: env_1.envs.isProd ? undefined : { target: 'pino-pretty', options: { colorize: true } },
|
|
34
38
|
};
|
|
35
39
|
const logger = (0, pino_1.default)(pinoCommonOptions, asyncDest[0]);
|
|
36
40
|
const logHttp = (...params) => {
|
package/lib/request.d.ts
CHANGED
|
@@ -38,6 +38,7 @@ export declare class BareRequest {
|
|
|
38
38
|
private startTime?;
|
|
39
39
|
private startDate;
|
|
40
40
|
private remoteClient;
|
|
41
|
+
private logging;
|
|
41
42
|
private requestTimeFormat?;
|
|
42
43
|
private headers;
|
|
43
44
|
private cookies;
|
|
@@ -76,6 +77,7 @@ export declare class BareRequest {
|
|
|
76
77
|
stream<T extends NodeJS.WritableStream>(stream: T): void;
|
|
77
78
|
json(data: any): void;
|
|
78
79
|
_send(chunk?: string | ArrayBuffer | NodeJS.ArrayBufferView | SharedArrayBuffer): void;
|
|
80
|
+
sendStringifiedJson(data: string): void;
|
|
79
81
|
send(anything?: any): void;
|
|
80
82
|
}
|
|
81
83
|
export {};
|
package/lib/request.js
CHANGED
|
@@ -33,6 +33,7 @@ class BareRequest {
|
|
|
33
33
|
startTime;
|
|
34
34
|
startDate = new Date();
|
|
35
35
|
remoteClient = '';
|
|
36
|
+
logging = false;
|
|
36
37
|
requestTimeFormat;
|
|
37
38
|
headers = {};
|
|
38
39
|
cookies = {};
|
|
@@ -45,6 +46,7 @@ class BareRequest {
|
|
|
45
46
|
this.remoteIp = _originalRequest.socket.remoteAddress;
|
|
46
47
|
this.contentType = this._originalRequest.headers['content-type'];
|
|
47
48
|
this.requestHeaders = this._originalRequest.headers;
|
|
49
|
+
this.logging = options?.logging ?? false;
|
|
48
50
|
// this is a placeholder URL base that we need to make class working
|
|
49
51
|
new url_1.default.URL(`http://localhost/${this._originalRequest.url}`).searchParams.forEach((value, name) => (this.query[name] = value));
|
|
50
52
|
// parsed;
|
|
@@ -57,26 +59,28 @@ class BareRequest {
|
|
|
57
59
|
this.startTime = process.hrtime();
|
|
58
60
|
this.requestTimeFormat = options.requestTimeFormat;
|
|
59
61
|
}
|
|
60
|
-
// call logging section
|
|
61
|
-
if (options?.logging === true) {
|
|
62
|
-
_originalResponse.on('close', () => (0, logger_1.logHttp)(this.headers, this.startDate, this.remoteClient, _originalRequest, _originalResponse));
|
|
63
|
-
}
|
|
64
62
|
}
|
|
65
63
|
readBody() {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
switch (this._originalRequest.method) {
|
|
65
|
+
case 'POST':
|
|
66
|
+
case 'PATCH':
|
|
67
|
+
case 'PUT':
|
|
68
|
+
return new Promise((resolve, reject) => {
|
|
69
|
+
const temp = [];
|
|
70
|
+
this._originalRequest
|
|
71
|
+
.on('data', (chunk) => temp.push(chunk))
|
|
72
|
+
.on('end', () => {
|
|
73
|
+
const parsed = this.classifyRequestBody(temp);
|
|
74
|
+
if (util_1.types.isNativeError(parsed))
|
|
75
|
+
return reject(parsed);
|
|
76
|
+
this.requestBody = parsed;
|
|
77
|
+
resolve(parsed);
|
|
78
|
+
})
|
|
79
|
+
.on('error', reject);
|
|
80
|
+
});
|
|
81
|
+
default:
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
80
84
|
}
|
|
81
85
|
attachCookieManager(opts) {
|
|
82
86
|
this.cm = new cookie_manager_1.CookiesManager(opts, this);
|
|
@@ -108,8 +112,6 @@ class BareRequest {
|
|
|
108
112
|
this.remoteClient = remoteClient;
|
|
109
113
|
}
|
|
110
114
|
setRequestTime() {
|
|
111
|
-
if (!this.requestTimeFormat)
|
|
112
|
-
return;
|
|
113
115
|
const diff = process.hrtime(this.startTime);
|
|
114
116
|
const time = diff[0] * (this.requestTimeFormat === 's' ? 1 : 1e3) +
|
|
115
117
|
diff[1] * (this.requestTimeFormat === 's' ? 1e-9 : 1e-6);
|
|
@@ -207,14 +209,6 @@ class BareRequest {
|
|
|
207
209
|
logger_1.logMe.error('Trying to send with the headers already sent');
|
|
208
210
|
return;
|
|
209
211
|
}
|
|
210
|
-
this.sent = true;
|
|
211
|
-
let toSend = chunk;
|
|
212
|
-
switch (chunk?.constructor) {
|
|
213
|
-
case Uint16Array:
|
|
214
|
-
case Uint8Array:
|
|
215
|
-
case Uint32Array:
|
|
216
|
-
toSend = Buffer.from(chunk.buffer);
|
|
217
|
-
}
|
|
218
212
|
// work basic headers
|
|
219
213
|
if (typeof chunk !== 'undefined' && chunk !== null)
|
|
220
214
|
this.setHeader('Content-Length', Buffer.byteLength(chunk, 'utf-8'));
|
|
@@ -222,10 +216,20 @@ class BareRequest {
|
|
|
222
216
|
this.setHeaders({ 'Cache-Control': 'no-store', Expire: 0, Pragma: 'no-cache' });
|
|
223
217
|
if (this.statusToSend >= 400 && this.statusToSend !== 404 && this.statusToSend !== 410)
|
|
224
218
|
this.cleanHeader('Cache-Control');
|
|
225
|
-
this.
|
|
219
|
+
if (this.requestTimeFormat)
|
|
220
|
+
this.setRequestTime();
|
|
226
221
|
// perform sending
|
|
227
|
-
this._originalResponse.writeHead(this.statusToSend,
|
|
228
|
-
this._originalResponse.end(
|
|
222
|
+
this._originalResponse.writeHead(this.statusToSend, '', this.headers);
|
|
223
|
+
this._originalResponse.end(chunk || statusTuples[this.statusToSend]);
|
|
224
|
+
this.sent = true;
|
|
225
|
+
// call logging section
|
|
226
|
+
if (this.logging === true) {
|
|
227
|
+
(0, logger_1.logHttp)(this.headers, this.startDate, this.remoteClient, this._originalRequest, this._originalResponse);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
sendStringifiedJson(data) {
|
|
231
|
+
this.setHeader('Content-Type', 'application/json');
|
|
232
|
+
this._send(data);
|
|
229
233
|
}
|
|
230
234
|
send(anything) {
|
|
231
235
|
if (this.sent)
|
|
@@ -236,6 +240,8 @@ class BareRequest {
|
|
|
236
240
|
case Uint8Array:
|
|
237
241
|
case Uint16Array:
|
|
238
242
|
case Uint32Array:
|
|
243
|
+
this._send(Buffer.from(anything.buffer));
|
|
244
|
+
break;
|
|
239
245
|
case Buffer:
|
|
240
246
|
case String:
|
|
241
247
|
this._send(anything);
|
|
@@ -243,10 +249,12 @@ class BareRequest {
|
|
|
243
249
|
case Boolean:
|
|
244
250
|
case Number:
|
|
245
251
|
this._send('' + anything);
|
|
252
|
+
break;
|
|
246
253
|
case stream_1.Writable:
|
|
247
254
|
this.stream(anything);
|
|
248
255
|
break;
|
|
249
256
|
case Object:
|
|
257
|
+
case Array:
|
|
250
258
|
this.json(anything);
|
|
251
259
|
break;
|
|
252
260
|
default:
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ts, Type } from 'ts-morph';
|
|
2
|
+
export declare type StringSchemaType = {
|
|
3
|
+
type: 'string';
|
|
4
|
+
nullable: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare type NumberSchemaType = {
|
|
7
|
+
type: 'number';
|
|
8
|
+
nullable: boolean;
|
|
9
|
+
};
|
|
10
|
+
export declare type BooleanSchemaType = {
|
|
11
|
+
type: 'boolean';
|
|
12
|
+
nullable: boolean;
|
|
13
|
+
};
|
|
14
|
+
export declare type ArraySchemaType = {
|
|
15
|
+
type: 'array';
|
|
16
|
+
items: StringSchemaType | NumberSchemaType | BooleanSchemaType | ArraySchemaType | ObjectSchemaType;
|
|
17
|
+
nullable: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare type ObjectSchemaType = {
|
|
20
|
+
type: 'object';
|
|
21
|
+
properties: {
|
|
22
|
+
[key: string]: StringSchemaType | NumberSchemaType | BooleanSchemaType | ArraySchemaType | ObjectSchemaType;
|
|
23
|
+
};
|
|
24
|
+
nullable: boolean;
|
|
25
|
+
};
|
|
26
|
+
export declare type UnionSchemaType = {
|
|
27
|
+
type: 'union';
|
|
28
|
+
anyOf: CustomSchema[];
|
|
29
|
+
nullable: boolean;
|
|
30
|
+
};
|
|
31
|
+
export declare type CustomSchema = StringSchemaType | NumberSchemaType | BooleanSchemaType | ArraySchemaType | ObjectSchemaType | UnionSchemaType;
|
|
32
|
+
export declare const generateCustomSchema: (t: Type<ts.Type>) => CustomSchema;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateCustomSchema = void 0;
|
|
7
|
+
const find_1 = __importDefault(require("lodash/find"));
|
|
8
|
+
const helpers_1 = require("./helpers");
|
|
9
|
+
const generateCustomSchema = (t) => {
|
|
10
|
+
if ((0, helpers_1.isFinalType)(t)) {
|
|
11
|
+
return { type: (0, helpers_1.getTypeGenericText)(t), nullable: false };
|
|
12
|
+
}
|
|
13
|
+
if (t.isUnion()) {
|
|
14
|
+
const nulled = t.getUnionTypes().some((nt) => (0, helpers_1.isNullType)(nt));
|
|
15
|
+
const cleanTypes = helpers_1.helpers.cleanNullableTypes(t.getUnionTypes());
|
|
16
|
+
let returning = {
|
|
17
|
+
nullable: false,
|
|
18
|
+
type: 'union',
|
|
19
|
+
};
|
|
20
|
+
const transformed = cleanTypes.reduce((acc, ut) => {
|
|
21
|
+
const regenerated = (0, exports.generateCustomSchema)(ut);
|
|
22
|
+
if ((0, find_1.default)(acc, regenerated))
|
|
23
|
+
return acc;
|
|
24
|
+
return acc.concat(regenerated);
|
|
25
|
+
}, []);
|
|
26
|
+
if (transformed.length > 1) {
|
|
27
|
+
returning.anyOf = transformed;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
returning = transformed[0];
|
|
31
|
+
}
|
|
32
|
+
if (nulled) {
|
|
33
|
+
returning.nullable = true;
|
|
34
|
+
}
|
|
35
|
+
return returning;
|
|
36
|
+
}
|
|
37
|
+
if (t.isIntersection()) {
|
|
38
|
+
return t.getIntersectionTypes().reduce((acc, it) => {
|
|
39
|
+
const generatedSchema = (0, exports.generateCustomSchema)(it);
|
|
40
|
+
if (Object.keys(acc).length === 0) {
|
|
41
|
+
acc = generatedSchema;
|
|
42
|
+
return acc;
|
|
43
|
+
}
|
|
44
|
+
if (generatedSchema.type === acc.type && acc.type === 'object') {
|
|
45
|
+
acc.properties = { ...acc.properties, ...generatedSchema.properties };
|
|
46
|
+
}
|
|
47
|
+
return acc;
|
|
48
|
+
}, {});
|
|
49
|
+
}
|
|
50
|
+
if (t.isArray()) {
|
|
51
|
+
return {
|
|
52
|
+
type: 'array',
|
|
53
|
+
items: (0, exports.generateCustomSchema)(t.getArrayElementType()),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (t.isInterface() || t.isObject()) {
|
|
57
|
+
const result = t.getProperties().reduce((acc, ci) => {
|
|
58
|
+
const val = ci.getValueDeclaration();
|
|
59
|
+
acc.properties = { ...acc.properties, [ci.getName()]: (0, exports.generateCustomSchema)(val.getType()) };
|
|
60
|
+
return acc;
|
|
61
|
+
}, { type: 'object', properties: {} });
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
type: (0, helpers_1.getApparentTypeName)(t),
|
|
66
|
+
nullable: false,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
exports.generateCustomSchema = generateCustomSchema;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ts_morph_1 = require("ts-morph");
|
|
4
|
+
const custom_schema_1 = require("./custom-schema");
|
|
5
|
+
const helpers_1 = require("./helpers");
|
|
6
|
+
const project = new ts_morph_1.Project({ tsConfigFilePath: 'tsconfig.json' });
|
|
7
|
+
project.enableLogging();
|
|
8
|
+
const sourceFile = project.getSourceFile('server.ts');
|
|
9
|
+
const tp = sourceFile?.getClass('BareServer')?.getMember('route');
|
|
10
|
+
const isHandler = (c) => c.getSymbol()?.getName() === 'handler';
|
|
11
|
+
const isRoute = (c) => c.getSymbol()?.getName() === 'route';
|
|
12
|
+
function returnFinder(route, base) {
|
|
13
|
+
if (!base) {
|
|
14
|
+
throw new Error('No project been allocated, theres some issue');
|
|
15
|
+
}
|
|
16
|
+
const refsAcrossProject = base
|
|
17
|
+
.getChildrenOfKind(ts_morph_1.ts.SyntaxKind.Identifier)[0]
|
|
18
|
+
.findReferences()[0]
|
|
19
|
+
.getReferences()
|
|
20
|
+
?.filter((re) => re.compilerObject.fileName.includes(route));
|
|
21
|
+
if (!refsAcrossProject?.length) {
|
|
22
|
+
console.log('There are no routes declarations across the project');
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
const extractedReturns = refsAcrossProject.map((ref) => {
|
|
26
|
+
return ref
|
|
27
|
+
.getNode()
|
|
28
|
+
.getAncestors()
|
|
29
|
+
.find((n) => n.getKind() === ts_morph_1.ts.SyntaxKind.CallExpression)
|
|
30
|
+
?.getChildren()
|
|
31
|
+
.find((n) => n.getKind() === ts_morph_1.ts.SyntaxKind.SyntaxList)
|
|
32
|
+
?.getFirstChild()
|
|
33
|
+
?.getChildSyntaxList()
|
|
34
|
+
?.getChildren()
|
|
35
|
+
.filter((c) => {
|
|
36
|
+
return c.getKind() === ts_morph_1.ts.SyntaxKind.PropertyAssignment && (isHandler(c) || isRoute(c));
|
|
37
|
+
})
|
|
38
|
+
.map((c) => {
|
|
39
|
+
if (isHandler(c)) {
|
|
40
|
+
return {
|
|
41
|
+
type: 'handler',
|
|
42
|
+
syntaxList: c
|
|
43
|
+
.getChildren()
|
|
44
|
+
.find((n) => n.getKind() === ts_morph_1.ts.SyntaxKind.ArrowFunction ||
|
|
45
|
+
n.getKind() === ts_morph_1.ts.SyntaxKind.FunctionExpression)
|
|
46
|
+
?.getLastChild()
|
|
47
|
+
?.getChildSyntaxList(),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
type: 'route',
|
|
52
|
+
value: c
|
|
53
|
+
.getNodeProperty('initializer')
|
|
54
|
+
.getText()
|
|
55
|
+
?.replaceAll("'", ''),
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
const perRoute = extractedReturns
|
|
60
|
+
.map((routeCombination) => {
|
|
61
|
+
return routeCombination.reduce((acc, curr) => {
|
|
62
|
+
if (curr.type === 'handler') {
|
|
63
|
+
return {
|
|
64
|
+
...acc,
|
|
65
|
+
handler: curr.syntaxList,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
return {
|
|
70
|
+
...acc,
|
|
71
|
+
route: curr.value,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}, {});
|
|
75
|
+
})
|
|
76
|
+
.map((routeCombination) => ({
|
|
77
|
+
...routeCombination,
|
|
78
|
+
handler: getReturnStatements(routeCombination.handler),
|
|
79
|
+
}));
|
|
80
|
+
const schemas = perRoute.map(({ handler, route }) => {
|
|
81
|
+
const schemas = handler.map((t) => (0, custom_schema_1.generateCustomSchema)(t));
|
|
82
|
+
let finalSchema = schemas[0];
|
|
83
|
+
if (schemas.length > 1) {
|
|
84
|
+
finalSchema = {
|
|
85
|
+
type: 'union',
|
|
86
|
+
nullable: false,
|
|
87
|
+
anyOf: schemas,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
route,
|
|
92
|
+
schemas,
|
|
93
|
+
finalSchema,
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return schemas;
|
|
97
|
+
}
|
|
98
|
+
// const res = tp
|
|
99
|
+
// ?.getChildrenOfKind(ts.SyntaxKind.Identifier)[0]
|
|
100
|
+
// .findReferences()[0]
|
|
101
|
+
// .getReferences()
|
|
102
|
+
// .filter((ref) => !ref.compilerObject.fileName.includes('server.ts'))[0]
|
|
103
|
+
// .getNode()
|
|
104
|
+
// .getAncestors()
|
|
105
|
+
// .filter((n) => n.getKind() === ts.SyntaxKind.CallExpression)[0]
|
|
106
|
+
// .getAncestors()[0]
|
|
107
|
+
// .getChildrenOfKind(ts.SyntaxKind.CallExpression)[0]
|
|
108
|
+
// .getChildrenOfKind(ts.SyntaxKind.SyntaxList)[0]
|
|
109
|
+
// .getChildren()[0]
|
|
110
|
+
// .getChildrenOfKind(ts.SyntaxKind.SyntaxList)[0]
|
|
111
|
+
// .getChildrenOfKind(ts.SyntaxKind.PropertyAssignment)
|
|
112
|
+
// .find((node) =>
|
|
113
|
+
// node
|
|
114
|
+
// .getChildren()
|
|
115
|
+
// .find(
|
|
116
|
+
// (n) =>
|
|
117
|
+
// n.getKind() === ts.SyntaxKind.ArrowFunction ||
|
|
118
|
+
// n.getKind() === ts.SyntaxKind.FunctionExpression,
|
|
119
|
+
// ),
|
|
120
|
+
// )
|
|
121
|
+
// ?.getChildren()
|
|
122
|
+
// ?.find((c) => c.getKind() === ts.SyntaxKind.FunctionExpression)
|
|
123
|
+
// ?.getLastChild()
|
|
124
|
+
// ?.getChildSyntaxList()
|
|
125
|
+
// ?.getChildren()
|
|
126
|
+
// .filter((c) => c.getKind() === ts.SyntaxKind.IfStatement)[0]
|
|
127
|
+
// .getChildren()
|
|
128
|
+
// .find((c) => c.getKind() === ts.SyntaxKind.Block)
|
|
129
|
+
// ?.getChildSyntaxList();
|
|
130
|
+
const extractReturnStatements = (accumulator, n) => {
|
|
131
|
+
if (!n)
|
|
132
|
+
return;
|
|
133
|
+
if (ts_morph_1.ts.SyntaxKind.IfStatement === n.getKind()) {
|
|
134
|
+
const thenProp = n.getNodeProperty('thenStatement');
|
|
135
|
+
const elseProp = n.getNodeProperty('elseStatement');
|
|
136
|
+
const thenSyntax = thenProp?.getChildSyntaxList();
|
|
137
|
+
const elseSyntax = elseProp?.getChildSyntaxList();
|
|
138
|
+
extractReturnStatements(accumulator, thenSyntax);
|
|
139
|
+
extractReturnStatements(accumulator, elseSyntax);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (n.getChildren().length) {
|
|
143
|
+
const cleanChildren = n.getChildren().filter((c) => typeof c.getKind === 'function');
|
|
144
|
+
const findReturn = cleanChildren.find((c) => c.getKind() === ts_morph_1.ts.SyntaxKind.ReturnStatement);
|
|
145
|
+
const thereIf = cleanChildren.find((c) => c.getKind() === ts_morph_1.ts.SyntaxKind.IfStatement);
|
|
146
|
+
const thereWhile = cleanChildren.find((c) => c.getKind() === ts_morph_1.ts.SyntaxKind.WhileKeyword);
|
|
147
|
+
const thereFor = cleanChildren.find((c) => c.getKind() === ts_morph_1.ts.SyntaxKind.ForStatement);
|
|
148
|
+
const syntaxList = n.getChildSyntaxList();
|
|
149
|
+
if (findReturn) {
|
|
150
|
+
accumulator.push(findReturn);
|
|
151
|
+
}
|
|
152
|
+
extractReturnStatements(accumulator, thereIf);
|
|
153
|
+
extractReturnStatements(accumulator, thereWhile);
|
|
154
|
+
extractReturnStatements(accumulator, thereFor);
|
|
155
|
+
extractReturnStatements(accumulator, syntaxList);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const getReturnStatements = (n) => {
|
|
159
|
+
if (!n)
|
|
160
|
+
return [];
|
|
161
|
+
const accumulator = [];
|
|
162
|
+
extractReturnStatements(accumulator, n);
|
|
163
|
+
return accumulator
|
|
164
|
+
.map((r) => r.getChildren().find((c) => {
|
|
165
|
+
const type = c.getType();
|
|
166
|
+
return type.isObject() || (0, helpers_1.isFinalType)(type);
|
|
167
|
+
}))
|
|
168
|
+
.filter((n) => n)
|
|
169
|
+
.map((acc) => acc.getType());
|
|
170
|
+
// console.log({ accumulator });
|
|
171
|
+
// let baseChildren = n?.getChildren()?.filter((c) => typeof c.getKind === 'function') ?? [];
|
|
172
|
+
// baseChildren = baseChildren.flat(5).filter((c) => typeof c.getKind === 'function');
|
|
173
|
+
// if (thereIf || thereBlock || thereWhile || thereFor) {
|
|
174
|
+
// baseChildren?.push(
|
|
175
|
+
// getReturnStatements(thereIf) as any,
|
|
176
|
+
// getReturnStatements(thereWhile) as any,
|
|
177
|
+
// getReturnStatements(thereBlock) as any,
|
|
178
|
+
// getReturnStatements(thereFor) as any,
|
|
179
|
+
// );
|
|
180
|
+
// }
|
|
181
|
+
// baseChildren = baseChildren.flat(5).filter((c) => typeof c.getKind === 'function');
|
|
182
|
+
// return baseChildren
|
|
183
|
+
// .filter((c) => typeof c.getKind === 'function')
|
|
184
|
+
// .filter((c) => c.getKind() === ts.SyntaxKind.ReturnStatement)
|
|
185
|
+
// .map((r) =>
|
|
186
|
+
// r.getChildren().find((c) => {
|
|
187
|
+
// const type = c.getType();
|
|
188
|
+
// return type.isObject() || isFinalType(type);
|
|
189
|
+
// }),
|
|
190
|
+
// )
|
|
191
|
+
// .filter((v) => v)
|
|
192
|
+
// .map((v) => v!.getType());
|
|
193
|
+
};
|
|
194
|
+
// returnFinder('examples', tp);
|
|
195
|
+
(0, helpers_1.logInternals)(returnFinder('examples', tp));
|
|
196
|
+
// logInternals(returnFinder('examples', tp).map((s) => convertToJsonSchema(s)));
|
|
197
|
+
// console.log(tp);
|
|
198
|
+
// logInternals(getReturnStatements(res!)?.map((t) => regenerateTypeSchema(t!)));
|
|
199
|
+
// regenerateTypeSchema(res![0].getType());
|
|
200
|
+
// logInternals(regenerateTypeSchema(res![0].getType()));
|
|
201
|
+
// console.log(regenerateTypeSchema(res![0].getType()));
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const generateRouteSchema: (fileRouteToDeclarations: string) => {
|
|
2
|
+
route: string;
|
|
3
|
+
methodName: "get" | "post" | "put" | "delete" | "patch" | "options" | "head";
|
|
4
|
+
schemas: import("./custom-schema").CustomSchema[];
|
|
5
|
+
finalSchema: import("./custom-schema").CustomSchema;
|
|
6
|
+
jsonSchema: any;
|
|
7
|
+
}[];
|