barehttp 0.4.2 → 0.6.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 +8 -4
- package/lib/request.js +41 -32
- 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 +44 -15
- 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
|
@@ -14,15 +14,17 @@ export declare type CacheOpts = {
|
|
|
14
14
|
expirationSeconds?: number;
|
|
15
15
|
revalidation?: Revalidation;
|
|
16
16
|
};
|
|
17
|
-
export declare class BareRequest {
|
|
17
|
+
export declare class BareRequest<H extends {
|
|
18
|
+
[key: string]: string | undefined;
|
|
19
|
+
} = {
|
|
20
|
+
[key: string]: string | undefined;
|
|
21
|
+
}> {
|
|
18
22
|
_originalRequest: IncomingMessage;
|
|
19
23
|
_originalResponse: ServerResponse;
|
|
20
24
|
ID: {
|
|
21
25
|
code: string;
|
|
22
26
|
};
|
|
23
|
-
params:
|
|
24
|
-
[k: string]: string | undefined;
|
|
25
|
-
};
|
|
27
|
+
params: H;
|
|
26
28
|
query: {
|
|
27
29
|
[k: string]: string | undefined;
|
|
28
30
|
};
|
|
@@ -38,6 +40,7 @@ export declare class BareRequest {
|
|
|
38
40
|
private startTime?;
|
|
39
41
|
private startDate;
|
|
40
42
|
private remoteClient;
|
|
43
|
+
private logging;
|
|
41
44
|
private requestTimeFormat?;
|
|
42
45
|
private headers;
|
|
43
46
|
private cookies;
|
|
@@ -76,6 +79,7 @@ export declare class BareRequest {
|
|
|
76
79
|
stream<T extends NodeJS.WritableStream>(stream: T): void;
|
|
77
80
|
json(data: any): void;
|
|
78
81
|
_send(chunk?: string | ArrayBuffer | NodeJS.ArrayBufferView | SharedArrayBuffer): void;
|
|
82
|
+
sendStringifiedJson(data: string): void;
|
|
79
83
|
send(anything?: any): void;
|
|
80
84
|
}
|
|
81
85
|
export {};
|
package/lib/request.js
CHANGED
|
@@ -21,7 +21,7 @@ class BareRequest {
|
|
|
21
21
|
_originalRequest;
|
|
22
22
|
_originalResponse;
|
|
23
23
|
ID;
|
|
24
|
-
params
|
|
24
|
+
params;
|
|
25
25
|
query = {};
|
|
26
26
|
remoteIp;
|
|
27
27
|
requestBody;
|
|
@@ -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 = {};
|
|
@@ -41,10 +42,12 @@ class BareRequest {
|
|
|
41
42
|
constructor(_originalRequest, _originalResponse, options) {
|
|
42
43
|
this._originalRequest = _originalRequest;
|
|
43
44
|
this._originalResponse = _originalResponse;
|
|
45
|
+
this.params = {};
|
|
44
46
|
this.ID = { code: _originalRequest.headers['x-request-id'] || generateId() };
|
|
45
47
|
this.remoteIp = _originalRequest.socket.remoteAddress;
|
|
46
48
|
this.contentType = this._originalRequest.headers['content-type'];
|
|
47
49
|
this.requestHeaders = this._originalRequest.headers;
|
|
50
|
+
this.logging = options?.logging ?? false;
|
|
48
51
|
// this is a placeholder URL base that we need to make class working
|
|
49
52
|
new url_1.default.URL(`http://localhost/${this._originalRequest.url}`).searchParams.forEach((value, name) => (this.query[name] = value));
|
|
50
53
|
// parsed;
|
|
@@ -57,26 +60,28 @@ class BareRequest {
|
|
|
57
60
|
this.startTime = process.hrtime();
|
|
58
61
|
this.requestTimeFormat = options.requestTimeFormat;
|
|
59
62
|
}
|
|
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
63
|
}
|
|
65
64
|
readBody() {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
65
|
+
switch (this._originalRequest.method) {
|
|
66
|
+
case 'POST':
|
|
67
|
+
case 'PATCH':
|
|
68
|
+
case 'PUT':
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const temp = [];
|
|
71
|
+
this._originalRequest
|
|
72
|
+
.on('data', (chunk) => temp.push(chunk))
|
|
73
|
+
.on('end', () => {
|
|
74
|
+
const parsed = this.classifyRequestBody(temp);
|
|
75
|
+
if (util_1.types.isNativeError(parsed))
|
|
76
|
+
return reject(parsed);
|
|
77
|
+
this.requestBody = parsed;
|
|
78
|
+
resolve(parsed);
|
|
79
|
+
})
|
|
80
|
+
.on('error', reject);
|
|
81
|
+
});
|
|
82
|
+
default:
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
80
85
|
}
|
|
81
86
|
attachCookieManager(opts) {
|
|
82
87
|
this.cm = new cookie_manager_1.CookiesManager(opts, this);
|
|
@@ -108,8 +113,6 @@ class BareRequest {
|
|
|
108
113
|
this.remoteClient = remoteClient;
|
|
109
114
|
}
|
|
110
115
|
setRequestTime() {
|
|
111
|
-
if (!this.requestTimeFormat)
|
|
112
|
-
return;
|
|
113
116
|
const diff = process.hrtime(this.startTime);
|
|
114
117
|
const time = diff[0] * (this.requestTimeFormat === 's' ? 1 : 1e3) +
|
|
115
118
|
diff[1] * (this.requestTimeFormat === 's' ? 1e-9 : 1e-6);
|
|
@@ -207,14 +210,6 @@ class BareRequest {
|
|
|
207
210
|
logger_1.logMe.error('Trying to send with the headers already sent');
|
|
208
211
|
return;
|
|
209
212
|
}
|
|
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
213
|
// work basic headers
|
|
219
214
|
if (typeof chunk !== 'undefined' && chunk !== null)
|
|
220
215
|
this.setHeader('Content-Length', Buffer.byteLength(chunk, 'utf-8'));
|
|
@@ -222,10 +217,20 @@ class BareRequest {
|
|
|
222
217
|
this.setHeaders({ 'Cache-Control': 'no-store', Expire: 0, Pragma: 'no-cache' });
|
|
223
218
|
if (this.statusToSend >= 400 && this.statusToSend !== 404 && this.statusToSend !== 410)
|
|
224
219
|
this.cleanHeader('Cache-Control');
|
|
225
|
-
this.
|
|
220
|
+
if (this.requestTimeFormat)
|
|
221
|
+
this.setRequestTime();
|
|
226
222
|
// perform sending
|
|
227
|
-
this._originalResponse.writeHead(this.statusToSend,
|
|
228
|
-
this._originalResponse.end(
|
|
223
|
+
this._originalResponse.writeHead(this.statusToSend, '', this.headers);
|
|
224
|
+
this._originalResponse.end(chunk || statusTuples[this.statusToSend]);
|
|
225
|
+
this.sent = true;
|
|
226
|
+
// call logging section
|
|
227
|
+
if (this.logging === true) {
|
|
228
|
+
(0, logger_1.logHttp)(this.headers, this.startDate, this.remoteClient, this._originalRequest, this._originalResponse);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
sendStringifiedJson(data) {
|
|
232
|
+
this.setHeader('Content-Type', 'application/json');
|
|
233
|
+
this._send(data);
|
|
229
234
|
}
|
|
230
235
|
send(anything) {
|
|
231
236
|
if (this.sent)
|
|
@@ -236,6 +241,8 @@ class BareRequest {
|
|
|
236
241
|
case Uint8Array:
|
|
237
242
|
case Uint16Array:
|
|
238
243
|
case Uint32Array:
|
|
244
|
+
this._send(Buffer.from(anything.buffer));
|
|
245
|
+
break;
|
|
239
246
|
case Buffer:
|
|
240
247
|
case String:
|
|
241
248
|
this._send(anything);
|
|
@@ -243,10 +250,12 @@ class BareRequest {
|
|
|
243
250
|
case Boolean:
|
|
244
251
|
case Number:
|
|
245
252
|
this._send('' + anything);
|
|
253
|
+
break;
|
|
246
254
|
case stream_1.Writable:
|
|
247
255
|
this.stream(anything);
|
|
248
256
|
break;
|
|
249
257
|
case Object:
|
|
258
|
+
case Array:
|
|
250
259
|
this.json(anything);
|
|
251
260
|
break;
|
|
252
261
|
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
|
+
}[];
|