tspace-spear 1.0.0-rc

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.
@@ -0,0 +1,868 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
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);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.Application = exports.Spear = void 0;
39
+ const fs_1 = __importDefault(require("fs"));
40
+ const path_1 = __importDefault(require("path"));
41
+ const find_my_way_1 = __importDefault(require("find-my-way"));
42
+ const formidable_1 = __importDefault(require("formidable"));
43
+ const url_1 = require("url");
44
+ const on_finished_1 = __importDefault(require("on-finished"));
45
+ const string_decoder_1 = require("string_decoder");
46
+ const http_1 = __importStar(require("http"));
47
+ /**
48
+ *
49
+ * The 'Spear' class is used to create a server and handle HTTP requests.
50
+ *
51
+ * @returns {Spear} application
52
+ * @example
53
+ * new Application()
54
+ * .get('/' , () => 'Hello world!')
55
+ * .get('/json' , () => {
56
+ * return {
57
+ * message : 'Hello world!'
58
+ * }
59
+ * })
60
+ * .listen(3000 , () => console.log('server listening on port : 3000'))
61
+ *
62
+ */
63
+ class Spear {
64
+ constructor({ controllers, middlewares, globalPrefix, logger } = {}) {
65
+ this._router = (0, find_my_way_1.default)();
66
+ this._errorHandler = null;
67
+ this._globalMiddlewares = [];
68
+ this._formatResponse = null;
69
+ this._onListeners = [];
70
+ this._fileUploadOptions = {
71
+ limits: Infinity,
72
+ useTempFiles: false,
73
+ tempFileDir: 'tmp',
74
+ removeTempFile: {
75
+ remove: true,
76
+ ms: 1000 * 60 * 10
77
+ }
78
+ };
79
+ this._filesParser = (req) => __awaiter(this, void 0, void 0, function* () {
80
+ try {
81
+ const temp = this._fileUploadOptions.tempFileDir;
82
+ if (!fs_1.default.existsSync(temp)) {
83
+ try {
84
+ fs_1.default.mkdirSync(temp, { recursive: true });
85
+ }
86
+ catch (err) { }
87
+ }
88
+ const form = (0, formidable_1.default)({ uploadDir: temp });
89
+ const [dataBody, dataFiles] = yield form.parse(req);
90
+ const files = {};
91
+ const body = {};
92
+ const removeTemp = (fileTemp) => {
93
+ if (!this._fileUploadOptions.removeTempFile.remove)
94
+ return;
95
+ const remove = () => fs_1.default.unlinkSync(fileTemp);
96
+ setTimeout(remove, this._fileUploadOptions.removeTempFile.ms);
97
+ };
98
+ for (const key in dataFiles) {
99
+ const v = dataFiles[key];
100
+ if (v == null)
101
+ continue;
102
+ const file = v[0];
103
+ if (file.size > this._fileUploadOptions.limits) {
104
+ fs_1.default.unlinkSync(file.filepath);
105
+ continue;
106
+ }
107
+ files[key] = {
108
+ size: file.size,
109
+ tempFilePath: file.filepath,
110
+ tempFileName: file.newFilename,
111
+ mimetype: file.mimetype,
112
+ name: file.originalFilename
113
+ };
114
+ removeTemp(file.filepath);
115
+ }
116
+ for (const key in dataBody) {
117
+ const v = dataBody[key];
118
+ if (v == null)
119
+ continue;
120
+ body[key] = v[0];
121
+ }
122
+ return {
123
+ body,
124
+ files
125
+ };
126
+ }
127
+ catch (e) {
128
+ return {
129
+ body: {},
130
+ files: {}
131
+ };
132
+ }
133
+ });
134
+ this._bodyParser = (req) => {
135
+ if ((req === null || req === void 0 ? void 0 : req._writeParserEnd) === true)
136
+ return req._bodyParser;
137
+ return new Promise((resolve, reject) => {
138
+ const decoder = new string_decoder_1.StringDecoder('utf-8');
139
+ let payload = '';
140
+ req.on('data', (data) => {
141
+ payload += decoder.write(data);
142
+ });
143
+ req.on('end', () => {
144
+ try {
145
+ payload += decoder.end();
146
+ req._writeParserEnd = true;
147
+ req._bodyParser = JSON.parse(payload);
148
+ return resolve(JSON.parse(payload));
149
+ }
150
+ catch (e) {
151
+ req._writeParserEnd = true;
152
+ req._bodyParser = {};
153
+ return resolve({});
154
+ }
155
+ });
156
+ req.on('error', (err) => {
157
+ req._writeParserEnd = true;
158
+ req._bodyParser = {};
159
+ return reject(err);
160
+ });
161
+ });
162
+ };
163
+ this._wrapHandlers = (...handlers) => {
164
+ return (req, res, params) => __awaiter(this, void 0, void 0, function* () {
165
+ const runHandler = (index = 0) => __awaiter(this, void 0, void 0, function* () {
166
+ const response = this._customizeResponse(req, res);
167
+ const request = req;
168
+ const body = request._bodyParser;
169
+ const files = request._filesParser;
170
+ const cookies = request._cookiesParser;
171
+ const query = Object.assign({}, (0, url_1.parse)(String(req.url), true).query);
172
+ const objectOrNull = (data) => {
173
+ if (data == null)
174
+ return null;
175
+ return Object.keys(data).length ? data : null;
176
+ };
177
+ const ctx = {
178
+ req: request,
179
+ res: response,
180
+ params: objectOrNull(params),
181
+ query: objectOrNull(query),
182
+ body: objectOrNull(body),
183
+ files: objectOrNull(files),
184
+ cookies: objectOrNull(cookies),
185
+ };
186
+ if (index === handlers.length - 1) {
187
+ return this._wrapResponse(handlers[index].bind(handlers[index]))(ctx, this._nextFunction(ctx));
188
+ }
189
+ return handlers[index](ctx, () => {
190
+ return runHandler(index + 1);
191
+ });
192
+ });
193
+ yield runHandler()
194
+ .catch(err => {
195
+ const ctx = {
196
+ req,
197
+ res: this._customizeResponse(req, res),
198
+ params: Object.keys(params).length ? params : null,
199
+ query: null,
200
+ body: null,
201
+ files: null,
202
+ cookies: null
203
+ };
204
+ return this._nextFunction(ctx)(err);
205
+ });
206
+ });
207
+ };
208
+ if (logger)
209
+ this._onListeners.push(() => this.use(this._logger));
210
+ this._controllers = controllers;
211
+ this._middlewares = middlewares;
212
+ this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
213
+ }
214
+ /**
215
+ * The 'enableCors' is used to enable the cors origins on the server.
216
+ *
217
+ * @params {Object}
218
+ * @property {(string | RegExp)[]} origins
219
+ * @property {boolean} credentials
220
+ * @returns
221
+ */
222
+ enableCors({ origins, credentials } = {}) {
223
+ this._globalMiddlewares.push(({ req, res }, next) => {
224
+ var _a;
225
+ const origin = (_a = req.headers) === null || _a === void 0 ? void 0 : _a.origin;
226
+ if (origin == null)
227
+ return next();
228
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
229
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
230
+ if (origins == null) {
231
+ res.setHeader('Access-Control-Allow-Origin', '*');
232
+ }
233
+ if (Array.isArray(origins) && origins.length) {
234
+ if (origins.includes(origin)) {
235
+ res.setHeader('Access-Control-Allow-Origin', origin);
236
+ }
237
+ }
238
+ if (credentials) {
239
+ res.setHeader('Access-Control-Allow-Credentials', 'true');
240
+ }
241
+ return next();
242
+ });
243
+ return this;
244
+ }
245
+ /**
246
+ * The 'use' method is used to add the middleware into the request pipeline.
247
+ *
248
+ * @callback {Function} middleware
249
+ * @property {Object} ctx - context { req , res , query , params , cookies , files , body}
250
+ * @property {Function} next - go to next function
251
+ * @returns {this}
252
+ */
253
+ use(middleware) {
254
+ this._globalMiddlewares.push(middleware);
255
+ return this;
256
+ }
257
+ /**
258
+ * The 'useRouter' method is used to add the router in the request context.
259
+ *
260
+ * @parms {Function} router
261
+ * @property {Function} router - get() , post() , put() , patch() , delete()
262
+ * @returns {this}
263
+ */
264
+ useRouter(router) {
265
+ const routes = router.routes;
266
+ for (const { path, method, handlers } of routes) {
267
+ this[method](this._normalizePath(this._globalPrefix, path), ...handlers);
268
+ }
269
+ return this;
270
+ }
271
+ /**
272
+ * The 'useBodyParser' method is a middleware used to parse the request body of incoming HTTP requests.
273
+ *
274
+ * @returns {this}
275
+ */
276
+ useBodyParser() {
277
+ this._globalMiddlewares.push(({ req, res }, next) => __awaiter(this, void 0, void 0, function* () {
278
+ const contentType = req === null || req === void 0 ? void 0 : req.headers['content-type'];
279
+ const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
280
+ const isListMethods = ['POST', 'PATCH', 'PUT'].includes(String(req.method));
281
+ if (!isListMethods)
282
+ return next();
283
+ if (isFileUpload)
284
+ return next();
285
+ if ((req === null || req === void 0 ? void 0 : req._bodyParser) != null)
286
+ return next();
287
+ req._bodyParser = yield this._bodyParser(req);
288
+ return next();
289
+ }));
290
+ return this;
291
+ }
292
+ /**
293
+ * The 'useCookiesParser' method is a middleware used to parses cookies attached to the client request object.
294
+ *
295
+ * @returns {this}
296
+ */
297
+ useCookiesParser() {
298
+ this._globalMiddlewares.push(({ req }, next) => {
299
+ if ((req === null || req === void 0 ? void 0 : req._cookiesParser) != null)
300
+ return next();
301
+ req._cookiesParser = this._cookiesParser(req);
302
+ return next();
303
+ });
304
+ return this;
305
+ }
306
+ /**
307
+ * The 'useFileUpload' method is a middleware used to handler file uploads. It adds a file upload of incoming HTTP requests.
308
+ *
309
+ * @param {?Object}
310
+ * @property {?number} limits
311
+ * @property {?boolean} useTempFiles
312
+ * @property {?string} tempFileDir
313
+ * @property {?Object} removeTempFile
314
+ * @property {boolean} removeTempFile.remove
315
+ * @property {number} removeTempFile.ms
316
+ * @returns
317
+ */
318
+ useFileUpload({ limits, useTempFiles, tempFileDir, removeTempFile } = {}) {
319
+ if (limits != null) {
320
+ this._fileUploadOptions.limits = limits;
321
+ }
322
+ if (tempFileDir != null) {
323
+ this._fileUploadOptions.tempFileDir = tempFileDir;
324
+ }
325
+ if (useTempFiles != null) {
326
+ this._fileUploadOptions.useTempFiles = useTempFiles;
327
+ }
328
+ if (removeTempFile != null) {
329
+ this._fileUploadOptions.removeTempFile = removeTempFile;
330
+ }
331
+ this._globalMiddlewares.push(({ req }, next) => __awaiter(this, void 0, void 0, function* () {
332
+ const contentType = req === null || req === void 0 ? void 0 : req.headers['content-type'];
333
+ const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
334
+ const isListMethods = ['POST', 'PATCH', 'PUT'].includes(String(req.method));
335
+ if (!isListMethods)
336
+ return next();
337
+ if (!isFileUpload)
338
+ return next();
339
+ if ((req === null || req === void 0 ? void 0 : req._filesParser) != null)
340
+ return next();
341
+ const { body, files } = yield this._filesParser(req);
342
+ req._filesParser = files;
343
+ req._bodyParser = body;
344
+ return next();
345
+ }));
346
+ return this;
347
+ }
348
+ /**
349
+ * The 'formatResponse' method is used to format the response
350
+ *
351
+ * @param {function} format
352
+ * @returns
353
+ */
354
+ formatResponse(format) {
355
+ this._formatResponse = format;
356
+ return this;
357
+ }
358
+ /**
359
+ * The 'errorHandler' method is middleware that is specifically designed to handle errors that occur during the processing of requests
360
+ *
361
+ * @param {function} error
362
+ * @returns
363
+ */
364
+ errorHandler(error) {
365
+ this._errorHandler = error;
366
+ return this;
367
+ }
368
+ /**
369
+ * The 'notFoundHandler' method is middleware that is specifically designed to handle errors notfound that occur during the processing of requests
370
+ *
371
+ * @param {function} notfound
372
+ * @returns
373
+ */
374
+ notFoundHandler(notfound) {
375
+ const handler = ({ req, res }) => {
376
+ return notfound({
377
+ req,
378
+ res: this._customizeResponse(req, res),
379
+ query: null,
380
+ files: null,
381
+ body: null,
382
+ params: null,
383
+ cookies: null
384
+ });
385
+ };
386
+ this._onListeners.push(() => {
387
+ return this._router.all('*', this._wrapHandlers(...this._globalMiddlewares, handler));
388
+ });
389
+ return this;
390
+ }
391
+ /**
392
+ * The 'get' method is used to add the request handler to the router for the 'GET' method.
393
+ *
394
+ * @param {string} path
395
+ * @callback {...Function[]} handlers of the middlewares
396
+ * @property {Object} ctx - context { req , res , query , params , cookies , files , body}
397
+ * @property {Function} next - go to next function
398
+ * @returns {this}
399
+ */
400
+ get(path, ...handlers) {
401
+ this._onListeners.push(() => {
402
+ return this._router.get(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
403
+ });
404
+ return this;
405
+ }
406
+ /**
407
+ * The 'post' method is used to add the request handler to the router for the 'POST' method.
408
+ *
409
+ * @param {string} path
410
+ * @callback {...Function[]} handlers of the middlewares
411
+ * @property {Object} ctx - context { req , res , query , params , cookies , files , body}
412
+ * @property {Function} next - go to next function
413
+ * @returns {this}
414
+ */
415
+ post(path, ...handlers) {
416
+ this._onListeners.push(() => {
417
+ return this._router.post(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
418
+ });
419
+ return this;
420
+ }
421
+ /**
422
+ * The 'put' method is used to add the request handler to the router for the 'PUT' method.
423
+ *
424
+ * @param {string} path
425
+ * @callback {...Function[]} handlers of the middlewares
426
+ * @property {Object} ctx - context { req , res , query , params , cookies , files , body}
427
+ * @property {Function} next - go to next function
428
+ * @returns {this}
429
+ */
430
+ put(path, ...handlers) {
431
+ this._onListeners.push(() => {
432
+ return this._router.put(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
433
+ });
434
+ return this;
435
+ }
436
+ /**
437
+ * The 'patch' method is used to add the request handler to the router for the 'PATCH' method.
438
+ *
439
+ * @param {string} path
440
+ * @callback {...Function[]} handlers of the middlewares
441
+ * @property {Object} ctx - context { req , res , query , params , cookies , files , body}
442
+ * @property {Function} next - go to next function
443
+ * @returns {this}
444
+ */
445
+ patch(path, ...handlers) {
446
+ this._onListeners.push(() => {
447
+ return this._router.patch(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
448
+ });
449
+ return this;
450
+ }
451
+ /**
452
+ * The 'delete' method is used to add the request handler to the router for the 'DELETE' method.
453
+ *
454
+ * @param {string} path
455
+ * @callback {...Function[]} handlers of the middlewares
456
+ * @property {Object} ctx - context { req , res , query , params , cookies , files , body}
457
+ * @property {Function} next - go to next function
458
+ * @returns {this}
459
+ */
460
+ delete(path, ...handlers) {
461
+ this._onListeners.push(() => {
462
+ return this._router.delete(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
463
+ });
464
+ return this;
465
+ }
466
+ /**
467
+ * The 'all' method is used to add the request handler to the router for the 'all' method.
468
+ *
469
+ * @param {string} path
470
+ * @callback {...Function[]} handlers of the middlewares
471
+ * @property {object} ctx - context { req , res , query , params , cookies , files , body}
472
+ * @property {function} next - go to next function
473
+ * @returns {this}
474
+ */
475
+ all(path, ...handlers) {
476
+ this._onListeners.push(() => {
477
+ return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
478
+ });
479
+ return this;
480
+ }
481
+ /**
482
+ * The 'listen' method is used to bind and start a server to a particular port and optionally a hostname.
483
+ *
484
+ * @param {number} port
485
+ * @param {function} cb
486
+ * @returns
487
+ */
488
+ listen(port = 3000, cb) {
489
+ return __awaiter(this, arguments, void 0, function* () {
490
+ if (arguments.length === 1 && typeof port === 'function') {
491
+ cb = port;
492
+ port = 3000;
493
+ }
494
+ const server = yield this._createServer();
495
+ server.listen(port == null ? 3000 : port, () => {
496
+ if (cb)
497
+ cb({ server, port });
498
+ });
499
+ server.on('listening', () => {
500
+ this._onListeners.forEach(listener => listener());
501
+ });
502
+ server.on('error', (_) => {
503
+ port = Math.floor(Math.random() * 8999) + 1000;
504
+ server.listen(port);
505
+ });
506
+ return;
507
+ });
508
+ }
509
+ _logger({ req, res }, next) {
510
+ const diffTime = (hrtime) => {
511
+ const MS = 1000;
512
+ if (hrtime == null)
513
+ return 0;
514
+ const [start, end] = process.hrtime(hrtime);
515
+ const time = +(((start * MS) + (end / 1e6)).toFixed(2));
516
+ return `${time > MS ? `${time / MS} s` : `${time} ms`}`;
517
+ };
518
+ const statusCode = (res) => {
519
+ const statusCode = res.statusCode == null ? 500 : Number(res.statusCode);
520
+ return statusCode < 400
521
+ ? `\x1b[32m${statusCode}\x1b[0m`
522
+ : `\x1b[31m${statusCode}\x1b[0m`;
523
+ };
524
+ const startTime = process.hrtime();
525
+ (0, on_finished_1.default)(res, () => {
526
+ console.log([
527
+ `[\x1b[1m\x1b[34mINFO\x1b[0m]`,
528
+ `\x1b[34m${new Date().toJSON()}\x1b[0m`,
529
+ `\x1b[33m${req.method}\x1b[0m`,
530
+ `${decodeURIComponent(String(req.url))}`,
531
+ `${statusCode(res)}`,
532
+ `${diffTime(startTime)}`,
533
+ ].join(" "));
534
+ });
535
+ return next();
536
+ }
537
+ _import(dir, pattern) {
538
+ return __awaiter(this, void 0, void 0, function* () {
539
+ const directories = fs_1.default.readdirSync(dir, { withFileTypes: true });
540
+ const files = (yield Promise.all(directories.map((directory) => {
541
+ const newDir = path_1.default.resolve(String(dir), directory.name);
542
+ if (pattern == null) {
543
+ return directory.isDirectory() ? this._import(newDir) : newDir;
544
+ }
545
+ return directory.isDirectory()
546
+ ? this._import(newDir)
547
+ : pattern.test(directory.name)
548
+ ? newDir
549
+ : null;
550
+ }))).filter(d => d != null);
551
+ return [].concat(...files);
552
+ });
553
+ }
554
+ _registerControllers() {
555
+ return __awaiter(this, void 0, void 0, function* () {
556
+ if (this._controllers == null)
557
+ return;
558
+ if (!Array.isArray(this._controllers)) {
559
+ const controllers = yield this._import(this._controllers.folder, this._controllers.name);
560
+ for (const file of controllers) {
561
+ const response = yield Promise.resolve(`${file}`).then(s => __importStar(require(s)));
562
+ const controller = response === null || response === void 0 ? void 0 : response.default;
563
+ const controllerInstance = new controller();
564
+ const prefixPath = Reflect.getMetadata("controllers", controller);
565
+ const routers = Reflect.getMetadata("routers", controller);
566
+ if (prefixPath == null)
567
+ continue;
568
+ for (const { method, path, handler } of routers) {
569
+ this[method](this._normalizePath(this._globalPrefix, prefixPath, path), this._wrapResponse(controllerInstance[String(handler)].bind(controllerInstance)));
570
+ }
571
+ }
572
+ return;
573
+ }
574
+ for (const controller of this._controllers) {
575
+ const controllerInstance = new controller();
576
+ const prefixPath = Reflect.getMetadata("controllers", controller);
577
+ const routers = Reflect.getMetadata("routers", controller);
578
+ if (prefixPath == null)
579
+ continue;
580
+ for (const { method, path, handler } of routers) {
581
+ this[method](this._normalizePath(this._globalPrefix, prefixPath, path), this._wrapResponse(controllerInstance[String(handler)].bind(controllerInstance)));
582
+ }
583
+ }
584
+ });
585
+ }
586
+ _registerMiddlewares() {
587
+ return __awaiter(this, void 0, void 0, function* () {
588
+ if (this._middlewares == null)
589
+ return;
590
+ if (!Array.isArray(this._middlewares)) {
591
+ const middlewares = yield this._import(this._middlewares.folder, this._middlewares.name);
592
+ for (const file of middlewares) {
593
+ const response = yield Promise.resolve(`${file}`).then(s => __importStar(require(s)));
594
+ const middleware = response === null || response === void 0 ? void 0 : response.default;
595
+ this.use(middleware);
596
+ }
597
+ return;
598
+ }
599
+ const middlewares = this._middlewares;
600
+ for (const middleware of middlewares) {
601
+ this.use(middleware);
602
+ }
603
+ return;
604
+ });
605
+ }
606
+ _cookiesParser(req) {
607
+ var _a;
608
+ const cookies = {};
609
+ const cookieString = (_a = req.headers) === null || _a === void 0 ? void 0 : _a.cookie;
610
+ if (cookieString == null)
611
+ return null;
612
+ for (const cookie of cookieString.split(';')) {
613
+ const [name, value] = cookie.split('=').map(v => v.trim());
614
+ cookies[name] = decodeURIComponent(value);
615
+ }
616
+ for (const name of Object.keys(cookies)) {
617
+ const cookie = cookies[name];
618
+ if (!cookie.startsWith('Expires='))
619
+ continue;
620
+ const expiresString = cookie.replace('Expires=', '');
621
+ const expiresDate = new Date(expiresString);
622
+ if (isNaN(expiresDate.getTime()) || expiresDate < new Date()) {
623
+ delete cookies[name];
624
+ }
625
+ }
626
+ return cookies;
627
+ }
628
+ _customizeResponse(req, res) {
629
+ const response = res;
630
+ response.json = (results) => {
631
+ if (typeof results === 'string') {
632
+ if (!res.headersSent) {
633
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
634
+ }
635
+ return res.end(results);
636
+ }
637
+ if (!res.headersSent) {
638
+ res.writeHead(200, { 'Content-Type': 'application/json' });
639
+ }
640
+ if (results == null) {
641
+ if (this._formatResponse != null) {
642
+ return res.end(JSON.stringify(this._formatResponse(null, res.statusCode), null, 2));
643
+ }
644
+ return res.end();
645
+ }
646
+ if (this._formatResponse != null) {
647
+ return res.end(JSON.stringify(this._formatResponse(Object.assign({}, results), res.statusCode), null, 2));
648
+ }
649
+ return res.end(JSON.stringify(Object.assign({}, results), null, 2));
650
+ };
651
+ response.send = (results) => {
652
+ res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' });
653
+ return res.end(results);
654
+ };
655
+ response.error = (err) => {
656
+ var _a, _b, _c, _d, _e, _f, _g, _h;
657
+ let code = +((_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.code) ||
658
+ +err.code ||
659
+ +err.status ||
660
+ +err.statusCode ||
661
+ +((_d = (_c = err.response) === null || _c === void 0 ? void 0 : _c.data) === null || _d === void 0 ? void 0 : _d.statusCode) ||
662
+ 500;
663
+ code = (code == null || typeof code !== 'number') ? 500 : Number.isNaN(code) ? 500 : code < 400 ? 500 : code;
664
+ const message = ((_f = (_e = err.response) === null || _e === void 0 ? void 0 : _e.data) === null || _f === void 0 ? void 0 : _f.errorMessage) ||
665
+ ((_h = (_g = err.response) === null || _g === void 0 ? void 0 : _g.data) === null || _h === void 0 ? void 0 : _h.message) ||
666
+ err.message ||
667
+ `The url '${req.url}' resulted in a server error. Please investigate.`;
668
+ response.status(code);
669
+ if (this._formatResponse != null) {
670
+ return res.end(JSON.stringify(this._formatResponse({ message }, code), null, 2));
671
+ }
672
+ return res.end(JSON.stringify({
673
+ message: message
674
+ }, null, 2));
675
+ };
676
+ response.ok = (results) => {
677
+ return response.json(results == null ? {} : results);
678
+ };
679
+ response.created = (results) => {
680
+ response.status(201);
681
+ return response.json(results == null ? {} : results);
682
+ };
683
+ response.accepted = (results) => {
684
+ response.status(202);
685
+ return response.json(results == null ? {} : results);
686
+ };
687
+ response.noContent = () => {
688
+ response.status(202);
689
+ return res.end();
690
+ };
691
+ response.badRequest = (message) => {
692
+ response.status(400);
693
+ message = message !== null && message !== void 0 ? message : `The url '${req.url}' resulted in a bad request. Please review the data and try again.`;
694
+ if (this._formatResponse != null) {
695
+ return res.end(JSON.stringify(this._formatResponse({ message }, 400), null, 2));
696
+ }
697
+ return res.end(JSON.stringify({
698
+ message: message
699
+ }, null, 2));
700
+ };
701
+ response.unauthorized = (message) => {
702
+ response.status(401);
703
+ message = message !== null && message !== void 0 ? message : `The url '${req.url}' is unauthorized. Please verify.`;
704
+ if (this._formatResponse != null) {
705
+ return res.end(JSON.stringify(this._formatResponse({ message }, 401), null, 2));
706
+ }
707
+ return res.end(JSON.stringify({
708
+ message
709
+ }, null, 2));
710
+ };
711
+ response.paymentRequired = (message) => {
712
+ response.status(402);
713
+ message = message !== null && message !== void 0 ? message : `The url '${req.url}' requires payment. Please proceed with payment.`;
714
+ if (this._formatResponse != null) {
715
+ return res.end(JSON.stringify(this._formatResponse({ message }, 402), null, 2));
716
+ }
717
+ return res.end(JSON.stringify({
718
+ message
719
+ }, null, 2));
720
+ };
721
+ response.forbidden = (message) => {
722
+ response.status(403);
723
+ message = message !== null && message !== void 0 ? message : `The url '${req.url}' is forbidden. Please check the permissions or access rights.`;
724
+ if (this._formatResponse != null) {
725
+ return res.end(JSON.stringify(this._formatResponse({ message }, 403), null, 2));
726
+ }
727
+ return res.end(JSON.stringify({
728
+ message
729
+ }, null, 2));
730
+ };
731
+ response.notFound = (message) => {
732
+ response.status(404);
733
+ message = message !== null && message !== void 0 ? message : `The url '${req.url}' was not found. Please re-check the your url again`;
734
+ if (this._formatResponse != null) {
735
+ return res.end(JSON.stringify(this._formatResponse({ message }, 404), null, 2));
736
+ }
737
+ return res.end(JSON.stringify({
738
+ message
739
+ }, null, 2));
740
+ };
741
+ response.serverError = (message) => {
742
+ response.status(500);
743
+ message = message !== null && message !== void 0 ? message : `The url '${req.url}' resulted in a server error. Please investigate.`;
744
+ if (this._formatResponse != null) {
745
+ return res.end(JSON.stringify(this._formatResponse({ message }, 500), null, 2));
746
+ }
747
+ return res.end(JSON.stringify({
748
+ message
749
+ }, null, 2));
750
+ };
751
+ response.status = (code) => {
752
+ res.writeHead(code, { 'Content-Type': 'application/json' });
753
+ return res;
754
+ };
755
+ response.setCookies = (cookies) => {
756
+ for (const [key, v] of Object.entries(cookies)) {
757
+ if (typeof v === 'string') {
758
+ res.setHeader('Set-Cookie', `${key}=${v}`);
759
+ continue;
760
+ }
761
+ if (v.value === '' || v.value == null)
762
+ continue;
763
+ let str = `${key}=${v.value}`;
764
+ if (v.sameSite != null) {
765
+ str += ` ;SameSite=${v.sameSite}`;
766
+ }
767
+ if (v.domain != null) {
768
+ str += ` ;Domain=${v.domain}`;
769
+ }
770
+ if (v.httpOnly != null) {
771
+ str += ` ;HttpOnly`;
772
+ }
773
+ if (v.secure != null) {
774
+ str += ` ;Secure`;
775
+ }
776
+ if (v.expires != null) {
777
+ str += ` ;Expires=${v.expires.toUTCString()}`;
778
+ }
779
+ res.setHeader('Set-Cookie', str);
780
+ }
781
+ };
782
+ return response;
783
+ }
784
+ _nextFunction(ctx) {
785
+ return (err) => __awaiter(this, void 0, void 0, function* () {
786
+ if (err != null) {
787
+ if (this._errorHandler != null) {
788
+ return this._errorHandler(err, ctx);
789
+ }
790
+ ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
791
+ if (this._formatResponse != null) {
792
+ return ctx.res.end(JSON.stringify(this._formatResponse({
793
+ message: err === null || err === void 0 ? void 0 : err.message,
794
+ }, ctx.res.statusCode), null, 2));
795
+ }
796
+ return ctx.res.end(JSON.stringify({
797
+ message: err === null || err === void 0 ? void 0 : err.message,
798
+ }, null, 2));
799
+ }
800
+ if (this._errorHandler != null) {
801
+ return this._errorHandler(new Error(`The 'next' function does not have any subsequent function.`), ctx);
802
+ }
803
+ ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
804
+ if (this._formatResponse != null) {
805
+ return ctx.res.end(JSON.stringify(this._formatResponse({
806
+ message: `The 'next' function does not have any subsequent function.`
807
+ }, ctx.res.statusCode), null, 2));
808
+ }
809
+ return ctx.res.end(JSON.stringify({
810
+ message: `The 'next' function does not have any subsequent function.`
811
+ }, null, 2));
812
+ });
813
+ }
814
+ _wrapResponse(handler) {
815
+ return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
816
+ const result = yield handler(ctx, next);
817
+ if (result == null)
818
+ return;
819
+ if (result instanceof http_1.ServerResponse)
820
+ return;
821
+ if (typeof result === 'string') {
822
+ if (!ctx.res.headersSent) {
823
+ ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
824
+ }
825
+ return ctx.res.end(result);
826
+ }
827
+ if (this._formatResponse != null) {
828
+ const formatResponse = this._formatResponse(Object.assign({}, result), ctx.res.statusCode);
829
+ if (typeof formatResponse === 'string') {
830
+ if (!ctx.res.headersSent) {
831
+ ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
832
+ }
833
+ return ctx.res.end(formatResponse);
834
+ }
835
+ if (!ctx.res.headersSent) {
836
+ ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
837
+ }
838
+ return ctx.res.end(JSON.stringify(this._formatResponse(Object.assign({}, result), ctx.res.statusCode), null, 2));
839
+ }
840
+ if (!ctx.res.headersSent) {
841
+ ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
842
+ }
843
+ return ctx.res.end(JSON.stringify(Object.assign({}, result), null, 2));
844
+ });
845
+ }
846
+ _createServer() {
847
+ return __awaiter(this, void 0, void 0, function* () {
848
+ yield this._registerMiddlewares();
849
+ yield this._registerControllers();
850
+ const server = http_1.default.createServer((req, res) => {
851
+ return this._router.lookup(req, res);
852
+ });
853
+ return server;
854
+ });
855
+ }
856
+ _normalizePath(...paths) {
857
+ let path = paths
858
+ .join('/')
859
+ .replace(/\/+/g, '/')
860
+ .replace(/\/+$/, '');
861
+ return path.startsWith('/') ? path : `/${path}`;
862
+ }
863
+ }
864
+ exports.Spear = Spear;
865
+ class Application extends Spear {
866
+ }
867
+ exports.Application = Application;
868
+ exports.default = Spear;