tspace-spear 1.2.2 → 1.2.4
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 +304 -23
- package/dist/lib/core/const/index.d.ts +153 -0
- package/dist/lib/core/const/index.js +105 -0
- package/dist/lib/core/const/index.js.map +1 -0
- package/dist/lib/core/decorators/context.d.ts +16 -9
- package/dist/lib/core/decorators/context.js +85 -59
- package/dist/lib/core/decorators/context.js.map +1 -1
- package/dist/lib/core/decorators/headers.d.ts +2 -2
- package/dist/lib/core/decorators/headers.js +1 -1
- package/dist/lib/core/decorators/headers.js.map +1 -1
- package/dist/lib/core/decorators/methods.d.ts +7 -7
- package/dist/lib/core/decorators/methods.js.map +1 -1
- package/dist/lib/core/decorators/middleware.d.ts +3 -3
- package/dist/lib/core/decorators/middleware.js +2 -2
- package/dist/lib/core/decorators/middleware.js.map +1 -1
- package/dist/lib/core/decorators/statusCode.d.ts +1 -1
- package/dist/lib/core/decorators/statusCode.js.map +1 -1
- package/dist/lib/core/decorators/swagger.d.ts +1 -1
- package/dist/lib/core/decorators/swagger.js.map +1 -1
- package/dist/lib/core/server/express.d.ts +295 -0
- package/dist/lib/core/server/express.js +1356 -0
- package/dist/lib/core/server/express.js.map +1 -0
- package/dist/lib/core/server/fast-router.d.ts +133 -0
- package/dist/lib/core/server/fast-router.js +277 -0
- package/dist/lib/core/server/fast-router.js.map +1 -0
- package/dist/lib/core/server/index.d.ts +43 -36
- package/dist/lib/core/server/index.js +300 -426
- package/dist/lib/core/server/index.js.map +1 -1
- package/dist/lib/core/server/net/index.d.ts +20 -0
- package/dist/lib/core/server/net/index.js +393 -0
- package/dist/lib/core/server/net/index.js.map +1 -0
- package/dist/lib/core/server/parser-factory.d.ts +20 -12
- package/dist/lib/core/server/parser-factory.js +283 -196
- package/dist/lib/core/server/parser-factory.js.map +1 -1
- package/dist/lib/core/server/request.d.ts +2 -0
- package/dist/lib/core/server/request.js +7 -0
- package/dist/lib/core/server/request.js.map +1 -0
- package/dist/lib/core/server/response.d.ts +6 -0
- package/dist/lib/core/server/response.js +168 -0
- package/dist/lib/core/server/response.js.map +1 -0
- package/dist/lib/core/server/router.d.ts +2 -2
- package/dist/lib/core/server/router.js +2 -1
- package/dist/lib/core/server/router.js.map +1 -1
- package/dist/lib/core/server/uWS/index.d.ts +30 -0
- package/dist/lib/core/server/uWS/index.js +357 -0
- package/dist/lib/core/server/uWS/index.js.map +1 -0
- package/dist/lib/core/types/index.d.ts +159 -38
- package/dist/lib/core/utils/index.d.ts +12 -0
- package/dist/lib/core/utils/index.js +137 -0
- package/dist/lib/core/utils/index.js.map +1 -0
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.js +3 -2
- package/dist/lib/index.js.map +1 -1
- package/package.json +27 -11
- package/dist/tests/benchmark.test.d.ts +0 -1
- package/dist/tests/benchmark.test.js +0 -145
- package/dist/tests/benchmark.test.js.map +0 -1
|
@@ -4,16 +4,42 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.ParserFactory = void 0;
|
|
7
|
+
const busboy_1 = __importDefault(require("busboy"));
|
|
7
8
|
const fs_1 = __importDefault(require("fs"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
9
10
|
const mime_types_1 = __importDefault(require("mime-types"));
|
|
10
11
|
const crypto_1 = __importDefault(require("crypto"));
|
|
11
12
|
const swagger_ui_dist_1 = __importDefault(require("swagger-ui-dist"));
|
|
12
|
-
const querystring_1 = __importDefault(require("querystring"));
|
|
13
13
|
const string_decoder_1 = require("string_decoder");
|
|
14
|
-
const
|
|
14
|
+
const fast_querystring_1 = __importDefault(require("fast-querystring"));
|
|
15
|
+
const utils_1 = require("../utils");
|
|
16
|
+
const uWS_1 = require("./uWS");
|
|
17
|
+
const net_1 = require("./net");
|
|
15
18
|
class ParserFactory {
|
|
16
|
-
|
|
19
|
+
uWebStockets = false;
|
|
20
|
+
net = false;
|
|
21
|
+
useAdater(adapter) {
|
|
22
|
+
if (adapter.kind === 'uWS') {
|
|
23
|
+
this.uWebStockets = true;
|
|
24
|
+
}
|
|
25
|
+
if (adapter.kind === 'net') {
|
|
26
|
+
this.net = true;
|
|
27
|
+
}
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
queryString(url) {
|
|
31
|
+
const i = url.indexOf("?");
|
|
32
|
+
if (i === -1)
|
|
33
|
+
return {};
|
|
34
|
+
return fast_querystring_1.default.parse(url.slice(i + 1));
|
|
35
|
+
}
|
|
36
|
+
async files({ req, res, options, }) {
|
|
37
|
+
if (this.uWebStockets) {
|
|
38
|
+
return (0, uWS_1.uWSfiles)({ req, res, options });
|
|
39
|
+
}
|
|
40
|
+
if (this.net) {
|
|
41
|
+
return (0, net_1.netFiles)(req, options);
|
|
42
|
+
}
|
|
17
43
|
const temp = options.tempFileDir;
|
|
18
44
|
if (!fs_1.default.existsSync(temp)) {
|
|
19
45
|
try {
|
|
@@ -25,7 +51,7 @@ class ParserFactory {
|
|
|
25
51
|
const body = {};
|
|
26
52
|
const files = {};
|
|
27
53
|
const fileWritePromises = [];
|
|
28
|
-
const bb = (0, busboy_1.default)({ headers: req.headers, defParamCharset:
|
|
54
|
+
const bb = (0, busboy_1.default)({ headers: req.headers, defParamCharset: "utf8" });
|
|
29
55
|
const removeTemp = (fileTemp, ms) => {
|
|
30
56
|
const remove = () => {
|
|
31
57
|
try {
|
|
@@ -35,13 +61,16 @@ class ParserFactory {
|
|
|
35
61
|
};
|
|
36
62
|
setTimeout(remove, ms);
|
|
37
63
|
};
|
|
38
|
-
bb.on(
|
|
64
|
+
bb.on("file", (fieldName, fileData, info) => {
|
|
39
65
|
const { filename, mimeType } = info;
|
|
40
|
-
const
|
|
66
|
+
const extension = mime_types_1.default.extension(mimeType) ||
|
|
67
|
+
path_1.default.extname(filename).replace(".", "") ||
|
|
68
|
+
"bin";
|
|
69
|
+
const tempFilename = crypto_1.default.randomBytes(16).toString("hex");
|
|
41
70
|
const filePath = path_1.default.join(path_1.default.resolve(), `${temp}/${tempFilename}`);
|
|
42
71
|
const writeStream = fs_1.default.createWriteStream(filePath);
|
|
43
72
|
let fileSize = 0;
|
|
44
|
-
fileData.on(
|
|
73
|
+
fileData.on("data", (data) => {
|
|
45
74
|
fileSize += data.length;
|
|
46
75
|
if (fileSize > options.limit) {
|
|
47
76
|
fileData.unpipe(writeStream);
|
|
@@ -51,38 +80,39 @@ class ParserFactory {
|
|
|
51
80
|
});
|
|
52
81
|
const fileWritePromise = new Promise((resolve, reject) => {
|
|
53
82
|
fileData.pipe(writeStream);
|
|
54
|
-
writeStream.on(
|
|
83
|
+
writeStream.on("finish", () => {
|
|
55
84
|
const file = {
|
|
56
85
|
name: filename,
|
|
57
86
|
tempFilePath: filePath,
|
|
58
87
|
tempFileName: tempFilename,
|
|
59
88
|
mimetype: mimeType,
|
|
60
|
-
extension:
|
|
89
|
+
extension: extension,
|
|
61
90
|
size: fileSize,
|
|
62
91
|
sizes: {
|
|
63
92
|
bytes: fileSize,
|
|
64
93
|
kb: fileSize / 1024,
|
|
65
94
|
mb: fileSize / 1024 / 1024,
|
|
66
|
-
gb: fileSize / 1024 / 1024 / 1024
|
|
95
|
+
gb: fileSize / 1024 / 1024 / 1024,
|
|
67
96
|
},
|
|
68
97
|
write: (to) => {
|
|
69
98
|
return new Promise((resolve, reject) => {
|
|
70
|
-
fs_1.default
|
|
99
|
+
fs_1.default
|
|
100
|
+
.createReadStream(filePath)
|
|
71
101
|
.pipe(fs_1.default.createWriteStream(to))
|
|
72
|
-
.on(
|
|
102
|
+
.on("finish", () => {
|
|
73
103
|
return resolve(null);
|
|
74
104
|
})
|
|
75
|
-
.on(
|
|
105
|
+
.on("error", (err) => {
|
|
76
106
|
return reject(err);
|
|
77
107
|
});
|
|
78
108
|
});
|
|
79
109
|
},
|
|
80
110
|
remove: () => {
|
|
81
|
-
return new Promise(resolve => setTimeout(() => {
|
|
111
|
+
return new Promise((resolve) => setTimeout(() => {
|
|
82
112
|
fs_1.default.unlinkSync(filePath);
|
|
83
113
|
return resolve(null);
|
|
84
114
|
}, 100));
|
|
85
|
-
}
|
|
115
|
+
},
|
|
86
116
|
};
|
|
87
117
|
if (files[fieldName] == null) {
|
|
88
118
|
files[fieldName] = [];
|
|
@@ -93,52 +123,56 @@ class ParserFactory {
|
|
|
93
123
|
}
|
|
94
124
|
return resolve(null);
|
|
95
125
|
});
|
|
96
|
-
writeStream.on(
|
|
126
|
+
writeStream.on("error", reject);
|
|
97
127
|
});
|
|
98
128
|
fileWritePromises.push(fileWritePromise);
|
|
99
129
|
});
|
|
100
|
-
bb.on(
|
|
130
|
+
bb.on("field", (name, value) => {
|
|
101
131
|
body[name] = value;
|
|
102
132
|
});
|
|
103
|
-
bb.on(
|
|
133
|
+
bb.on("finish", () => {
|
|
104
134
|
Promise.all(fileWritePromises)
|
|
105
135
|
.then(() => {
|
|
106
136
|
return resolve({
|
|
107
137
|
files,
|
|
108
|
-
body
|
|
138
|
+
body,
|
|
109
139
|
});
|
|
110
140
|
})
|
|
111
141
|
.catch((err) => {
|
|
112
142
|
return reject(err);
|
|
113
143
|
});
|
|
114
144
|
});
|
|
115
|
-
bb.on(
|
|
145
|
+
bb.on("error", (err) => {
|
|
116
146
|
return reject(err);
|
|
117
147
|
});
|
|
118
148
|
req.pipe(bb);
|
|
119
149
|
});
|
|
120
150
|
}
|
|
121
|
-
body(req) {
|
|
151
|
+
async body(req, res) {
|
|
152
|
+
if (this.uWebStockets) {
|
|
153
|
+
return (await (0, uWS_1.uWSBody)(req, res));
|
|
154
|
+
}
|
|
155
|
+
if (this.net) {
|
|
156
|
+
return (await (0, net_1.netBody)(req));
|
|
157
|
+
}
|
|
122
158
|
return new Promise((resolve, reject) => {
|
|
123
|
-
const decoder = new string_decoder_1.StringDecoder(
|
|
124
|
-
let payload =
|
|
125
|
-
req.on(
|
|
159
|
+
const decoder = new string_decoder_1.StringDecoder("utf-8");
|
|
160
|
+
let payload = "";
|
|
161
|
+
req.on("data", (data) => {
|
|
126
162
|
payload += decoder.write(data);
|
|
127
163
|
});
|
|
128
|
-
req.on(
|
|
164
|
+
req.on("end", async () => {
|
|
165
|
+
payload += decoder.end();
|
|
166
|
+
const contentType = req.headers["content-type"]?.toLowerCase() || null;
|
|
129
167
|
try {
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
return resolve(querystring_1.default.parse(payload));
|
|
133
|
-
}
|
|
134
|
-
payload += decoder.end();
|
|
135
|
-
return resolve(JSON.parse(payload));
|
|
168
|
+
const body = await (0, utils_1.normalizeRequestBody)({ contentType, payload });
|
|
169
|
+
return resolve(body);
|
|
136
170
|
}
|
|
137
|
-
catch (
|
|
138
|
-
return
|
|
171
|
+
catch (err) {
|
|
172
|
+
return reject(err);
|
|
139
173
|
}
|
|
140
174
|
});
|
|
141
|
-
req.on(
|
|
175
|
+
req.on("error", (err) => {
|
|
142
176
|
return reject(err);
|
|
143
177
|
});
|
|
144
178
|
});
|
|
@@ -148,15 +182,15 @@ class ParserFactory {
|
|
|
148
182
|
const cookieString = req.headers?.cookie;
|
|
149
183
|
if (cookieString == null)
|
|
150
184
|
return null;
|
|
151
|
-
for (const cookie of cookieString.split(
|
|
152
|
-
const [name, value] = cookie.split(
|
|
185
|
+
for (const cookie of cookieString.split(";")) {
|
|
186
|
+
const [name, value] = cookie.split("=").map((v) => v.trim());
|
|
153
187
|
cookies[name] = decodeURIComponent(value);
|
|
154
188
|
}
|
|
155
189
|
for (const name of Object.keys(cookies)) {
|
|
156
190
|
const cookie = cookies[name];
|
|
157
|
-
if (!cookie.startsWith(
|
|
191
|
+
if (!cookie.startsWith("Expires="))
|
|
158
192
|
continue;
|
|
159
|
-
const expiresString = cookie.replace(
|
|
193
|
+
const expiresString = cookie.replace("Expires=", "");
|
|
160
194
|
const expiresDate = new Date(expiresString);
|
|
161
195
|
if (isNaN(expiresDate.getTime()) || expiresDate < new Date()) {
|
|
162
196
|
delete cookies[name];
|
|
@@ -168,9 +202,9 @@ class ParserFactory {
|
|
|
168
202
|
const spec = {
|
|
169
203
|
openapi: "3.1.0",
|
|
170
204
|
info: doc.info ?? {
|
|
171
|
-
title:
|
|
205
|
+
title: "API Documentation",
|
|
172
206
|
description: "Documentation",
|
|
173
|
-
version:
|
|
207
|
+
version: "1.0.0",
|
|
174
208
|
},
|
|
175
209
|
components: {
|
|
176
210
|
securitySchemes: {
|
|
@@ -178,14 +212,14 @@ class ParserFactory {
|
|
|
178
212
|
type: "http",
|
|
179
213
|
scheme: "bearer",
|
|
180
214
|
name: "Authorization",
|
|
181
|
-
description: "Enter your token in the format : 'Bearer {TOKEN}'"
|
|
215
|
+
description: "Enter your token in the format : 'Bearer {TOKEN}'",
|
|
182
216
|
},
|
|
183
217
|
cookies: {
|
|
184
218
|
type: "apiKey",
|
|
185
219
|
in: "header",
|
|
186
220
|
name: "Cookie",
|
|
187
|
-
description: "Enter your cookies in the headers"
|
|
188
|
-
}
|
|
221
|
+
description: "Enter your cookies in the headers",
|
|
222
|
+
},
|
|
189
223
|
},
|
|
190
224
|
},
|
|
191
225
|
servers: doc.servers,
|
|
@@ -194,14 +228,30 @@ class ParserFactory {
|
|
|
194
228
|
};
|
|
195
229
|
const specPaths = (routes) => {
|
|
196
230
|
let paths = {};
|
|
231
|
+
const defaultSpecResponse = {
|
|
232
|
+
"200": {
|
|
233
|
+
description: "OK",
|
|
234
|
+
content: {
|
|
235
|
+
"application/json": {
|
|
236
|
+
schema: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
message: {
|
|
240
|
+
example: "success",
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
};
|
|
197
248
|
for (const r of routes) {
|
|
198
|
-
if (r.path ===
|
|
249
|
+
if (r.path === "*")
|
|
199
250
|
continue;
|
|
200
251
|
const path = r.path.replace(/:(\w+)/g, "{$1}");
|
|
201
|
-
const method = r.method.
|
|
202
|
-
const swagger = (doc.specs ?? []).find(s => {
|
|
203
|
-
return s.path === r.path &&
|
|
204
|
-
s.method.toLocaleLowerCase() === method;
|
|
252
|
+
const method = r.method.toLowerCase();
|
|
253
|
+
const swagger = (doc.specs ?? []).find((s) => {
|
|
254
|
+
return s.path === r.path && s.method.toLowerCase() === method;
|
|
205
255
|
});
|
|
206
256
|
const decoratedOnly = doc.options?.decoratedOnly ?? false;
|
|
207
257
|
if ((swagger == null && decoratedOnly) || swagger?.disabled) {
|
|
@@ -209,15 +259,15 @@ class ParserFactory {
|
|
|
209
259
|
}
|
|
210
260
|
if (paths[path] == null) {
|
|
211
261
|
paths[path] = {
|
|
212
|
-
[method]: {}
|
|
262
|
+
[method]: {},
|
|
213
263
|
};
|
|
214
264
|
}
|
|
215
265
|
const spec = {};
|
|
216
266
|
const tags = /\/api\/v\d+/.test(r.path)
|
|
217
|
-
? r.path.split(
|
|
267
|
+
? r.path.split("/")[3]
|
|
218
268
|
: /\/api/.test(r.path)
|
|
219
|
-
? r.path.split(
|
|
220
|
-
: r.path.split(
|
|
269
|
+
? r.path.split("/")[2]
|
|
270
|
+
: r.path.split("/")[1];
|
|
221
271
|
spec.parameters = [];
|
|
222
272
|
spec.responses = {};
|
|
223
273
|
spec.tags = [];
|
|
@@ -231,152 +281,192 @@ class ParserFactory {
|
|
|
231
281
|
content: {
|
|
232
282
|
"application/json": {
|
|
233
283
|
schema: {
|
|
234
|
-
type:
|
|
284
|
+
type: "object",
|
|
235
285
|
properties: response.example == null
|
|
236
286
|
? {}
|
|
237
|
-
: Object.keys(response.example)
|
|
238
|
-
|
|
239
|
-
|
|
287
|
+
: Object.keys(response.example).reduce((prev, key) => {
|
|
288
|
+
prev[key] = {
|
|
289
|
+
example: (response?.example ?? {})[key] ?? {},
|
|
290
|
+
};
|
|
240
291
|
return prev;
|
|
241
|
-
}, {})
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
292
|
+
}, {}),
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
245
296
|
};
|
|
246
297
|
}
|
|
247
298
|
spec.responses = {
|
|
248
|
-
...responses
|
|
299
|
+
...responses,
|
|
249
300
|
};
|
|
250
301
|
}
|
|
251
|
-
if (swagger
|
|
302
|
+
if (swagger == null) {
|
|
252
303
|
spec.tags = [
|
|
253
|
-
|
|
254
|
-
?
|
|
255
|
-
:
|
|
304
|
+
tags == null || tags === "" || /^:[^:]*$/.test(tags)
|
|
305
|
+
? "default"
|
|
306
|
+
: tags,
|
|
256
307
|
];
|
|
257
|
-
if (swagger.bearerToken) {
|
|
258
|
-
spec.security = [{ "BearerToken": [] }];
|
|
259
|
-
}
|
|
260
|
-
if (swagger.summary != null) {
|
|
261
|
-
spec.summary = swagger.summary;
|
|
262
|
-
}
|
|
263
|
-
if (swagger.description != null) {
|
|
264
|
-
spec.description = swagger.description;
|
|
265
|
-
}
|
|
266
308
|
if (Array.isArray(r.params) && Array.from(r.params).length) {
|
|
267
|
-
spec.parameters = Array.from(r
|
|
309
|
+
spec.parameters = Array.from(r.params).map((p) => {
|
|
268
310
|
return {
|
|
269
311
|
name: p,
|
|
270
312
|
in: "path",
|
|
271
313
|
required: true,
|
|
272
314
|
schema: {
|
|
273
|
-
type: "string"
|
|
274
|
-
}
|
|
315
|
+
type: "string",
|
|
316
|
+
},
|
|
275
317
|
};
|
|
276
318
|
});
|
|
277
319
|
}
|
|
278
|
-
if (
|
|
279
|
-
spec.
|
|
280
|
-
...spec.parameters,
|
|
281
|
-
...Object.entries(swagger.query).map(([k, v]) => {
|
|
282
|
-
return {
|
|
283
|
-
name: k,
|
|
284
|
-
in: "query",
|
|
285
|
-
required: v.required == null ? false : true,
|
|
286
|
-
schema: {
|
|
287
|
-
type: v.type
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
})
|
|
291
|
-
];
|
|
292
|
-
}
|
|
293
|
-
if (swagger.cookies != null) {
|
|
294
|
-
spec.parameters = [
|
|
295
|
-
...spec.parameters,
|
|
296
|
-
...[{
|
|
297
|
-
name: "Cookie",
|
|
298
|
-
in: "header",
|
|
299
|
-
required: swagger.cookies.required == null ? false : true,
|
|
300
|
-
schema: {
|
|
301
|
-
type: "string"
|
|
302
|
-
},
|
|
303
|
-
example: swagger.cookies.names.map((v, i) => `${v}={value${i + 1}}`).join(' ; '),
|
|
304
|
-
description: swagger.cookies?.description
|
|
305
|
-
}]
|
|
306
|
-
];
|
|
307
|
-
}
|
|
308
|
-
if (swagger.body != null) {
|
|
309
|
-
spec.requestBody = {
|
|
310
|
-
description: swagger.body?.description == null ? "description" : swagger.body.description,
|
|
311
|
-
required: swagger.body?.required == null ? false : true,
|
|
312
|
-
content: {
|
|
313
|
-
"application/json": {
|
|
314
|
-
schema: {
|
|
315
|
-
type: "object",
|
|
316
|
-
properties: swagger.body.properties
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
if (swagger.files != null) {
|
|
323
|
-
spec.requestBody = {
|
|
324
|
-
description: swagger.files?.description == null ? "description" : swagger.files.description,
|
|
325
|
-
required: swagger.files?.required == null ? false : true,
|
|
326
|
-
content: {
|
|
327
|
-
"multipart/form-data": {
|
|
328
|
-
schema: {
|
|
329
|
-
type: "object",
|
|
330
|
-
properties: swagger.files.properties
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
if (swagger.responses != null) {
|
|
337
|
-
const responses = {};
|
|
338
|
-
for (const response of swagger.responses) {
|
|
339
|
-
if (response == null || !Object.keys(response).length)
|
|
340
|
-
continue;
|
|
341
|
-
responses[`${response.status}`] = {
|
|
342
|
-
description: response.description,
|
|
343
|
-
content: {
|
|
344
|
-
"application/json": {
|
|
345
|
-
schema: {
|
|
346
|
-
type: 'object',
|
|
347
|
-
properties: response.example == null
|
|
348
|
-
? {}
|
|
349
|
-
: Object.keys(response.example)
|
|
350
|
-
.reduce((prev, key) => {
|
|
351
|
-
prev[key] = { example: (response?.example ?? {})[key] ?? {} };
|
|
352
|
-
return prev;
|
|
353
|
-
}, {})
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
spec.responses = {
|
|
360
|
-
...responses
|
|
361
|
-
};
|
|
320
|
+
if (!Object.keys(spec.responses).length) {
|
|
321
|
+
spec.responses = defaultSpecResponse;
|
|
362
322
|
}
|
|
363
323
|
paths[path][method] = spec;
|
|
364
324
|
continue;
|
|
365
325
|
}
|
|
366
326
|
spec.tags = [
|
|
367
|
-
tags == null
|
|
327
|
+
swagger.tags == null
|
|
328
|
+
? tags == null || tags === "" || /^:[^:]*$/.test(tags)
|
|
329
|
+
? "default"
|
|
330
|
+
: tags
|
|
331
|
+
: swagger.tags,
|
|
368
332
|
];
|
|
369
|
-
if (
|
|
370
|
-
spec.
|
|
333
|
+
if (swagger.bearerToken) {
|
|
334
|
+
spec.security = [{ BearerToken: [] }];
|
|
335
|
+
}
|
|
336
|
+
if (swagger.summary != null) {
|
|
337
|
+
spec.summary = swagger.summary;
|
|
338
|
+
}
|
|
339
|
+
if (swagger.description != null) {
|
|
340
|
+
spec.description = swagger.description;
|
|
341
|
+
}
|
|
342
|
+
if (swagger.params != null) {
|
|
343
|
+
const params = Object.entries(swagger.params).map(([k, v]) => {
|
|
344
|
+
return {
|
|
345
|
+
name: k,
|
|
346
|
+
in: "path",
|
|
347
|
+
required: v?.required === true,
|
|
348
|
+
description: v?.description,
|
|
349
|
+
example: v?.example || v?.enum,
|
|
350
|
+
schema: {
|
|
351
|
+
type: v?.type ?? "string",
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
});
|
|
355
|
+
spec.parameters = [
|
|
356
|
+
...(spec.parameters ?? []),
|
|
357
|
+
...params,
|
|
358
|
+
];
|
|
359
|
+
}
|
|
360
|
+
else if (Array.isArray(r.params) && Array.from(r.params).length) {
|
|
361
|
+
spec.parameters = Array.from(r?.params).map((p) => {
|
|
371
362
|
return {
|
|
372
363
|
name: p,
|
|
373
364
|
in: "path",
|
|
374
365
|
required: true,
|
|
375
366
|
schema: {
|
|
376
|
-
type: "string"
|
|
367
|
+
type: "string",
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
if (swagger.query != null) {
|
|
373
|
+
const queryParams = Object.entries(swagger.query).map(([k, v]) => {
|
|
374
|
+
return {
|
|
375
|
+
name: k,
|
|
376
|
+
in: "query",
|
|
377
|
+
required: v?.required === true,
|
|
378
|
+
description: v?.description,
|
|
379
|
+
example: v?.example || v?.enum,
|
|
380
|
+
schema: {
|
|
381
|
+
type: v?.type ?? "string",
|
|
377
382
|
}
|
|
378
383
|
};
|
|
379
384
|
});
|
|
385
|
+
spec.parameters = [
|
|
386
|
+
...(spec.parameters ?? []),
|
|
387
|
+
...queryParams,
|
|
388
|
+
];
|
|
389
|
+
}
|
|
390
|
+
if (swagger.cookies != null) {
|
|
391
|
+
spec.parameters = [
|
|
392
|
+
...spec.parameters,
|
|
393
|
+
...[
|
|
394
|
+
{
|
|
395
|
+
name: "Cookie",
|
|
396
|
+
in: "header",
|
|
397
|
+
required: swagger.cookies.required === true,
|
|
398
|
+
schema: {
|
|
399
|
+
type: "string",
|
|
400
|
+
},
|
|
401
|
+
example: swagger.cookies.names
|
|
402
|
+
.map((v, i) => `${v}={value${i + 1}}`)
|
|
403
|
+
.join(" ; "),
|
|
404
|
+
description: swagger.cookies?.description,
|
|
405
|
+
},
|
|
406
|
+
],
|
|
407
|
+
];
|
|
408
|
+
}
|
|
409
|
+
if (swagger.body != null) {
|
|
410
|
+
spec.requestBody = {
|
|
411
|
+
description: swagger.body.description,
|
|
412
|
+
required: swagger.body?.required === true,
|
|
413
|
+
content: {
|
|
414
|
+
"application/json": {
|
|
415
|
+
schema: {
|
|
416
|
+
type: "object",
|
|
417
|
+
properties: swagger.body.properties,
|
|
418
|
+
required: Object.entries(swagger.body.properties)
|
|
419
|
+
.filter(([_, v]) => v.required)
|
|
420
|
+
.map(([key]) => key)
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (swagger.files != null) {
|
|
427
|
+
spec.requestBody = {
|
|
428
|
+
description: swagger.files.description,
|
|
429
|
+
required: swagger.files?.required === true,
|
|
430
|
+
content: {
|
|
431
|
+
"multipart/form-data": {
|
|
432
|
+
schema: {
|
|
433
|
+
type: "object",
|
|
434
|
+
properties: swagger.files.properties,
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
if (swagger.responses != null) {
|
|
441
|
+
const responses = {};
|
|
442
|
+
for (const response of swagger.responses) {
|
|
443
|
+
if (response == null || !Object.keys(response).length)
|
|
444
|
+
continue;
|
|
445
|
+
responses[`${response.status}`] = {
|
|
446
|
+
description: response.description,
|
|
447
|
+
content: {
|
|
448
|
+
"application/json": {
|
|
449
|
+
schema: {
|
|
450
|
+
type: "object",
|
|
451
|
+
properties: response.example == null
|
|
452
|
+
? {}
|
|
453
|
+
: Object.keys(response.example).reduce((prev, key) => {
|
|
454
|
+
prev[key] = {
|
|
455
|
+
example: (response?.example ?? {})[key] ?? {},
|
|
456
|
+
};
|
|
457
|
+
return prev;
|
|
458
|
+
}, {}),
|
|
459
|
+
},
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
spec.responses = {
|
|
465
|
+
...responses,
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
if (!Object.keys(spec.responses).length) {
|
|
469
|
+
spec.responses = defaultSpecResponse;
|
|
380
470
|
}
|
|
381
471
|
paths[path][method] = spec;
|
|
382
472
|
}
|
|
@@ -384,20 +474,17 @@ class ParserFactory {
|
|
|
384
474
|
};
|
|
385
475
|
spec.paths = specPaths(doc.routes ?? []);
|
|
386
476
|
const normalizePath = (...paths) => {
|
|
387
|
-
const path = paths
|
|
388
|
-
|
|
389
|
-
.replace(/\/+/g, '/')
|
|
390
|
-
.replace(/\/+$/, '');
|
|
391
|
-
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
477
|
+
const path = paths.join("/").replace(/\/+/g, "/").replace(/\/+$/, "");
|
|
478
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
392
479
|
return /\/api\/api/.test(normalizedPath)
|
|
393
480
|
? normalizedPath.replace(/\/api\/api\//, "/api/")
|
|
394
481
|
: normalizedPath;
|
|
395
482
|
};
|
|
396
|
-
const STATIC_URL =
|
|
397
|
-
const iconURL = normalizePath(doc.staticUrl ??
|
|
398
|
-
const cssURL = normalizePath(doc.staticUrl ??
|
|
399
|
-
const scriptBundle = normalizePath(doc.staticUrl ??
|
|
400
|
-
const scriptStandalonePreset = normalizePath(doc.staticUrl ??
|
|
483
|
+
const STATIC_URL = "/swagger-ui";
|
|
484
|
+
const iconURL = normalizePath(doc.staticUrl ?? "", `${STATIC_URL}/favicon-32x32.png`).replace(/^\/(http[s]?:\/{0,2})/, "$1");
|
|
485
|
+
const cssURL = normalizePath(doc.staticUrl ?? "", `${STATIC_URL}/swagger-ui.css`).replace(/^\/(http[s]?:\/{0,2})/, "$1");
|
|
486
|
+
const scriptBundle = normalizePath(doc.staticUrl ?? "", `${STATIC_URL}/swagger-ui-bundle.js`).replace(/^\/(http[s]?:\/{0,2})/, "$1");
|
|
487
|
+
const scriptStandalonePreset = normalizePath(doc.staticUrl ?? "", `${STATIC_URL}/swagger-ui-standalone-preset.js`).replace(/^\/(http[s]?:\/{0,2})/, "$1");
|
|
401
488
|
const html = `
|
|
402
489
|
<!DOCTYPE html>
|
|
403
490
|
<html lang="en">
|
|
@@ -425,7 +512,7 @@ class ParserFactory {
|
|
|
425
512
|
presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
|
|
426
513
|
spec : ${JSON.stringify(spec)},
|
|
427
514
|
withCredentials: ${doc.options?.withCredentials ?? "true"},
|
|
428
|
-
layout: "${doc.options?.layout ??
|
|
515
|
+
layout: "${doc.options?.layout ?? "StandaloneLayout"}",
|
|
429
516
|
filter: "${doc.options?.filter ?? "false"}",
|
|
430
517
|
docExpansion: "${doc.options?.docExpansion ?? "list"}",
|
|
431
518
|
deepLinking: "${doc.options?.deepLinking ?? "true"}",
|
|
@@ -437,36 +524,36 @@ class ParserFactory {
|
|
|
437
524
|
</html>
|
|
438
525
|
`;
|
|
439
526
|
const staticSwaggerHandler = (req, res, params) => {
|
|
440
|
-
const swaggerUiPath = swagger_ui_dist_1.default.getAbsoluteFSPath();
|
|
441
|
-
const mimeTypes = {
|
|
442
|
-
'.html': 'text/html',
|
|
443
|
-
'.css': 'text/css',
|
|
444
|
-
'.js': 'application/javascript',
|
|
445
|
-
'.png': 'image/png',
|
|
446
|
-
'.jpg': 'image/jpeg',
|
|
447
|
-
'.gif': 'image/gif',
|
|
448
|
-
'.svg': 'image/svg+xml',
|
|
449
|
-
'.json': 'application/json'
|
|
450
|
-
};
|
|
451
|
-
const requestedFilePath = params['*'];
|
|
452
|
-
const filePath = path_1.default.join(swaggerUiPath, requestedFilePath);
|
|
453
|
-
const extname = path_1.default.extname(filePath);
|
|
454
|
-
const contentType = mimeTypes[extname] || 'text/html';
|
|
455
527
|
try {
|
|
528
|
+
const swaggerUiPath = swagger_ui_dist_1.default.getAbsoluteFSPath();
|
|
529
|
+
const mimeTypes = {
|
|
530
|
+
".html": "text/html",
|
|
531
|
+
".css": "text/css",
|
|
532
|
+
".js": "application/javascript",
|
|
533
|
+
".png": "image/png",
|
|
534
|
+
".jpg": "image/jpeg",
|
|
535
|
+
".gif": "image/gif",
|
|
536
|
+
".svg": "image/svg+xml",
|
|
537
|
+
".json": "application/json",
|
|
538
|
+
};
|
|
539
|
+
const requestedFilePath = params["*"];
|
|
540
|
+
const filePath = path_1.default.join(swaggerUiPath, requestedFilePath);
|
|
541
|
+
const extname = path_1.default.extname(filePath);
|
|
542
|
+
const contentType = mimeTypes[extname] || "text/html";
|
|
456
543
|
const content = fs_1.default.readFileSync(filePath);
|
|
457
|
-
res.writeHead(200, {
|
|
458
|
-
return res.end(content,
|
|
544
|
+
res.writeHead(200, { "Content-Type": contentType });
|
|
545
|
+
return res.end(content, "utf-8");
|
|
459
546
|
}
|
|
460
547
|
catch (err) {
|
|
461
|
-
res.writeHead(404, {
|
|
462
|
-
return res.end(
|
|
548
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
549
|
+
return res.end("Not found");
|
|
463
550
|
}
|
|
464
551
|
};
|
|
465
552
|
return {
|
|
466
553
|
path: doc.path,
|
|
467
554
|
staticUrl: `${STATIC_URL}/*`,
|
|
468
555
|
staticSwaggerHandler,
|
|
469
|
-
html
|
|
556
|
+
html,
|
|
470
557
|
};
|
|
471
558
|
}
|
|
472
559
|
}
|