alado 1.0.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.
Files changed (156) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +515 -0
  3. package/ROADMAP.md +0 -0
  4. package/accets/swagger-initializer.js +20 -0
  5. package/dist/alado-server/alado-server.d.ts +18 -0
  6. package/dist/alado-server/alado-server.js +66 -0
  7. package/dist/alado-server/alado-server.js.map +1 -0
  8. package/dist/alado-server/index.d.ts +1 -0
  9. package/dist/alado-server/index.js +6 -0
  10. package/dist/alado-server/index.js.map +1 -0
  11. package/dist/constants/content-type.d.ts +6 -0
  12. package/dist/constants/content-type.js +11 -0
  13. package/dist/constants/content-type.js.map +1 -0
  14. package/dist/constants/default-doc-object.d.ts +1 -0
  15. package/dist/constants/default-doc-object.js +122 -0
  16. package/dist/constants/default-doc-object.js.map +1 -0
  17. package/dist/constants/http-method.d.ts +10 -0
  18. package/dist/constants/http-method.js +15 -0
  19. package/dist/constants/http-method.js.map +1 -0
  20. package/dist/constants/http-status.d.ts +1 -0
  21. package/dist/constants/http-status.js +79 -0
  22. package/dist/constants/http-status.js.map +1 -0
  23. package/dist/constants/index.d.ts +5 -0
  24. package/dist/constants/index.js +22 -0
  25. package/dist/constants/index.js.map +1 -0
  26. package/dist/constants/reg-ex.d.ts +5 -0
  27. package/dist/constants/reg-ex.js +9 -0
  28. package/dist/constants/reg-ex.js.map +1 -0
  29. package/dist/dto/alado-server-error.d.ts +4 -0
  30. package/dist/dto/alado-server-error.js +3 -0
  31. package/dist/dto/alado-server-error.js.map +1 -0
  32. package/dist/dto/alado-server-logger.d.ts +3 -0
  33. package/dist/dto/alado-server-logger.js +3 -0
  34. package/dist/dto/alado-server-logger.js.map +1 -0
  35. package/dist/dto/context.d.ts +11 -0
  36. package/dist/dto/context.js +3 -0
  37. package/dist/dto/context.js.map +1 -0
  38. package/dist/dto/index.d.ts +8 -0
  39. package/dist/dto/index.js +25 -0
  40. package/dist/dto/index.js.map +1 -0
  41. package/dist/dto/property-definition.d.ts +30 -0
  42. package/dist/dto/property-definition.js +3 -0
  43. package/dist/dto/property-definition.js.map +1 -0
  44. package/dist/dto/request-authentication.d.ts +9 -0
  45. package/dist/dto/request-authentication.js +3 -0
  46. package/dist/dto/request-authentication.js.map +1 -0
  47. package/dist/dto/request.d.ts +28 -0
  48. package/dist/dto/request.js +3 -0
  49. package/dist/dto/request.js.map +1 -0
  50. package/dist/dto/response.d.ts +7 -0
  51. package/dist/dto/response.js +3 -0
  52. package/dist/dto/response.js.map +1 -0
  53. package/dist/dto/route.d.ts +9 -0
  54. package/dist/dto/route.js +3 -0
  55. package/dist/dto/route.js.map +1 -0
  56. package/dist/factory/http-server-factory.d.ts +8 -0
  57. package/dist/factory/http-server-factory.js +16 -0
  58. package/dist/factory/http-server-factory.js.map +1 -0
  59. package/dist/factory/index.d.ts +2 -0
  60. package/dist/factory/index.js +19 -0
  61. package/dist/factory/index.js.map +1 -0
  62. package/dist/factory/open-api-doc-factory.d.ts +2 -0
  63. package/dist/factory/open-api-doc-factory.js +151 -0
  64. package/dist/factory/open-api-doc-factory.js.map +1 -0
  65. package/dist/helpers/accessor/get-property.d.ts +1 -0
  66. package/dist/helpers/accessor/get-property.js +38 -0
  67. package/dist/helpers/accessor/get-property.js.map +1 -0
  68. package/dist/helpers/accessor/index.d.ts +2 -0
  69. package/dist/helpers/accessor/index.js +19 -0
  70. package/dist/helpers/accessor/index.js.map +1 -0
  71. package/dist/helpers/accessor/set-property.d.ts +1 -0
  72. package/dist/helpers/accessor/set-property.js +45 -0
  73. package/dist/helpers/accessor/set-property.js.map +1 -0
  74. package/dist/helpers/index.d.ts +4 -0
  75. package/dist/helpers/index.js +21 -0
  76. package/dist/helpers/index.js.map +1 -0
  77. package/dist/helpers/parser/authenticate.d.ts +2 -0
  78. package/dist/helpers/parser/authenticate.js +27 -0
  79. package/dist/helpers/parser/authenticate.js.map +1 -0
  80. package/dist/helpers/parser/body-parser.d.ts +3 -0
  81. package/dist/helpers/parser/body-parser.js +68 -0
  82. package/dist/helpers/parser/body-parser.js.map +1 -0
  83. package/dist/helpers/parser/index.d.ts +3 -0
  84. package/dist/helpers/parser/index.js +20 -0
  85. package/dist/helpers/parser/index.js.map +1 -0
  86. package/dist/helpers/parser/query-parser.d.ts +1 -0
  87. package/dist/helpers/parser/query-parser.js +24 -0
  88. package/dist/helpers/parser/query-parser.js.map +1 -0
  89. package/dist/helpers/service/clear-route.d.ts +1 -0
  90. package/dist/helpers/service/clear-route.js +16 -0
  91. package/dist/helpers/service/clear-route.js.map +1 -0
  92. package/dist/helpers/service/index.d.ts +2 -0
  93. package/dist/helpers/service/index.js +19 -0
  94. package/dist/helpers/service/index.js.map +1 -0
  95. package/dist/helpers/service/is-readable-stream.d.ts +3 -0
  96. package/dist/helpers/service/is-readable-stream.js +15 -0
  97. package/dist/helpers/service/is-readable-stream.js.map +1 -0
  98. package/dist/helpers/validate/alado-options/index.d.ts +1 -0
  99. package/dist/helpers/validate/alado-options/index.js +18 -0
  100. package/dist/helpers/validate/alado-options/index.js.map +1 -0
  101. package/dist/helpers/validate/alado-options/validate-alado-options.d.ts +2 -0
  102. package/dist/helpers/validate/alado-options/validate-alado-options.js +79 -0
  103. package/dist/helpers/validate/alado-options/validate-alado-options.js.map +1 -0
  104. package/dist/helpers/validate/context/index.d.ts +3 -0
  105. package/dist/helpers/validate/context/index.js +20 -0
  106. package/dist/helpers/validate/context/index.js.map +1 -0
  107. package/dist/helpers/validate/context/validate-context.d.ts +3 -0
  108. package/dist/helpers/validate/context/validate-context.js +165 -0
  109. package/dist/helpers/validate/context/validate-context.js.map +1 -0
  110. package/dist/helpers/validate/context/validate-request-files.d.ts +2 -0
  111. package/dist/helpers/validate/context/validate-request-files.js +33 -0
  112. package/dist/helpers/validate/context/validate-request-files.js.map +1 -0
  113. package/dist/helpers/validate/context/validate-request-part.d.ts +5 -0
  114. package/dist/helpers/validate/context/validate-request-part.js +46 -0
  115. package/dist/helpers/validate/context/validate-request-part.js.map +1 -0
  116. package/dist/helpers/validate/index.d.ts +2 -0
  117. package/dist/helpers/validate/index.js +19 -0
  118. package/dist/helpers/validate/index.js.map +1 -0
  119. package/dist/index.d.ts +4 -0
  120. package/dist/index.js +9 -0
  121. package/dist/index.js.map +1 -0
  122. package/dist/options/alado-server-options.d.ts +12 -0
  123. package/dist/options/alado-server-options.js +3 -0
  124. package/dist/options/alado-server-options.js.map +1 -0
  125. package/dist/options/context-options.d.ts +8 -0
  126. package/dist/options/context-options.js +3 -0
  127. package/dist/options/context-options.js.map +1 -0
  128. package/dist/options/cors-options.d.ts +7 -0
  129. package/dist/options/cors-options.js +3 -0
  130. package/dist/options/cors-options.js.map +1 -0
  131. package/dist/options/index.d.ts +6 -0
  132. package/dist/options/index.js +23 -0
  133. package/dist/options/index.js.map +1 -0
  134. package/dist/options/open-api-doc.d.ts +10 -0
  135. package/dist/options/open-api-doc.js +3 -0
  136. package/dist/options/open-api-doc.js.map +1 -0
  137. package/dist/options/request-processor-options.d.ts +9 -0
  138. package/dist/options/request-processor-options.js +3 -0
  139. package/dist/options/request-processor-options.js.map +1 -0
  140. package/dist/options/router-options.d.ts +8 -0
  141. package/dist/options/router-options.js +3 -0
  142. package/dist/options/router-options.js.map +1 -0
  143. package/dist/request-processor/index.d.ts +1 -0
  144. package/dist/request-processor/index.js +18 -0
  145. package/dist/request-processor/index.js.map +1 -0
  146. package/dist/request-processor/request-processor.d.ts +11 -0
  147. package/dist/request-processor/request-processor.js +163 -0
  148. package/dist/request-processor/request-processor.js.map +1 -0
  149. package/dist/router/index.d.ts +1 -0
  150. package/dist/router/index.js +18 -0
  151. package/dist/router/index.js.map +1 -0
  152. package/dist/router/router.d.ts +14 -0
  153. package/dist/router/router.js +110 -0
  154. package/dist/router/router.js.map +1 -0
  155. package/package.json +47 -0
  156. package/postinstall.sh +3 -0
package/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ (The MIT License)
2
+ Copyright (c) 2024 Pavlo Lazunko <pavlo@databikers.com>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5
+ and associated documentation files (the "Software"), to deal in the Software without restriction,
6
+ including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice
11
+ shall be included in all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,515 @@
1
+ Alado is an extremely fast, simple, robust, and lightweight framework
2
+ for creating server applications written in Node.js and running over the HTTP protocol.
3
+
4
+ Its main difference from most similar well-known frameworks is that with a minimum of external dependencies
5
+ it provides out-of-the-box such fully-functioning features as routing, CORS, automatic API documentation (Open API 3.0),
6
+ parsing query and path parameters, and the request body, file uploading, request authentication, etc.
7
+
8
+ The basis of use is a combination of interface simplicity and separation of the “routine” part of writing API
9
+ from business logic implementation - due to a declarative approach to describing all request parameters
10
+ in the form of static reusable structures.
11
+
12
+ 1. Installation
13
+
14
+ ```shell
15
+ npm i -s alado
16
+ ```
17
+
18
+ 2. Instancing
19
+
20
+ ```typescript
21
+
22
+ import { AladoServer } from 'alado';
23
+
24
+ const app: AladoServer = new AladoServer({
25
+ port: 3000,
26
+ cors: {
27
+ enable: true,
28
+ allowedOrigin: '*',
29
+ allowedHeaders: [ 'Authorization' ],
30
+ exposeHeaders: [ 'x-total-count' ],
31
+ },
32
+ openApiDoc: {
33
+ enable: true,
34
+ route: '/open-api',
35
+ info: {
36
+ title: 'My API title',
37
+ description: 'My API description',
38
+ version: '1.0.0'
39
+ }
40
+ }
41
+ })
42
+
43
+ ```
44
+
45
+ In case you want to run an HTTPs server (even though normally a Node.js app runs behind nginx/ingress etc.)
46
+ You can do it using the ssl parameter (look at SecureContextOptions in the "tls" module -
47
+ [Node.js TLS module](https://nodejs.org/api/tls.html#tlscreateserveroptions-secureconnectionlistener):
48
+
49
+
50
+ ```typescript
51
+
52
+ import { readFileSync } from 'fs';
53
+ import { AladoServer } from 'alado';
54
+
55
+ const app = new AladoServer({
56
+ port: 3000,
57
+ ssl: {
58
+ cert: readFileSync('/path/to/cert'),
59
+ key: readFileSync('/path/to/key'),
60
+ },
61
+ cors: {
62
+ enable: true,
63
+ allowedOrigin: '*',
64
+ allowedHeaders: [ 'Authorization' ],
65
+ exposeHeaders: [ 'x-total-count' ],
66
+ },
67
+ openApiDoc: {
68
+ enable: true,
69
+ route: '/',
70
+ info: {
71
+ title: 'My API title',
72
+ description: 'My API description',
73
+ version: '1.0.0'
74
+ }
75
+ }
76
+ })
77
+
78
+ ```
79
+ 3. Defining routes
80
+
81
+ Here is how you define routes in Alado:
82
+
83
+ ```typescript
84
+
85
+ app.get('/user/:id', context, (request: Request): Response<UserDto> => {
86
+ const user = {
87
+ name: 'John Doe',
88
+ id: request.path.id
89
+ }
90
+ return {
91
+ statusCode: 200,
92
+ headers: {
93
+ 'Content-Type': 'application/json',
94
+ },
95
+ body: user
96
+ }
97
+ });
98
+
99
+
100
+ app.start(() => console.log(`[API]: application has been launched successfully`))
101
+
102
+ ```
103
+ The application provides "get", "post", "delete", "patch", "put" and "head" methods for defining routes.
104
+ These methods accept three required arguments:
105
+
106
+ * route or path: A string that may contain variables to specify the route.
107
+ * context: An aggregating object that fully describes the request.
108
+ * handler: A function that handles the request.
109
+
110
+ 3.1 Route or path.
111
+
112
+ Path variables defining works in the same way as it works in other frameworks: You just define a part of your route (means part separated by slashes)
113
+ as ":id" and receive the passed value as request.path.id).
114
+
115
+ 3.2 Context
116
+
117
+ Here's the contract of the request context:
118
+
119
+ ```typescript
120
+
121
+ interface Context <T> {
122
+ title: string;
123
+ auth?: RequestAuthentication;
124
+ options: ContextOptions;
125
+ request: ContextRequest;
126
+ response: Response<T>;
127
+ }
128
+
129
+ ```
130
+ Unlike context.title, which is simply a unique string, the other properties may not be immediately clear.
131
+ However, once explained, they become quite obvious and extremely logical.
132
+
133
+ **Note that all parts of the context and its nested properties can be reused**
134
+
135
+ 3.2.1 Context.auth
136
+
137
+ ```typescript
138
+
139
+ import { RequestAuthentication } from 'alado';
140
+
141
+ const bearerAuth: RequestAuthentication = {
142
+ inputProperty: 'headers.authorization',
143
+ outputProperty: 'auth.user',
144
+ handler: async (bearer: string) => {
145
+ // validate header, return user
146
+ return {
147
+ name: 'John Doe',
148
+ id: 1
149
+ }
150
+ },
151
+ required: true, // false You want "soft" checking
152
+ error: {
153
+ statusCode: 401,
154
+ message: 'Unauthorized'
155
+ }
156
+ }
157
+
158
+ ```
159
+ This means that the application takes an "Authorization" header
160
+ (or "api_token" query parameter if you provide "query.api_token" as an inputProperty,
161
+ "body.someThing" and "path.token" work also)
162
+ use the provided handler to get the user and set the received user to request.auth.user,
163
+ (and the mentioned "request" here is the request argument in your route handler)
164
+ Or just returns 401 Unauthorized.
165
+ This means that the application can authenticate using an "Authorization" header, or alternatively,
166
+ using an "api_token" query parameter (if specified as "query.api_token" in the inputProperty).
167
+ It can also authenticate using values like "body.someThing" or "path.token".
168
+ In the example the provided handler retrieves the user and assigns it to request.auth.user
169
+ (where request is the argument in your route handler).
170
+ If authentication fails, it returns a 401 Unauthorized response.
171
+
172
+ This also works:
173
+
174
+ ```typescript
175
+ import { RequestAuthentication } from 'alado';
176
+
177
+ const bearerAuth: RequestAuthentication = {
178
+ inputProperty: 'header.x-api-token',
179
+ outputProperty: 'auth',
180
+ handler: async (xApiToken: string) => {
181
+ // You will receive user and company as nested properties in request auth
182
+ return {
183
+ user: {
184
+ name: 'John Doe',
185
+ id: 1
186
+ },
187
+ company: {
188
+ name: 'Databikers Limited',
189
+ id: 2
190
+ }
191
+ }
192
+ },
193
+ //optional property if You want to specify execution context for the auth handler
194
+ handlerContext: {},
195
+ required: true, // false You want "soft" checking
196
+ error: {
197
+ statusCode: 401,
198
+ message: 'Unauthorized'
199
+ }
200
+ }
201
+ ```
202
+
203
+ 3.2.2 Context.options
204
+
205
+ ```typescript
206
+ interface ContextOptions {
207
+ allowUnknownFields?: boolean;
208
+ openApiDoc?: {
209
+ description?: string;
210
+ operationId: string;
211
+ tags?: string[];
212
+ }
213
+ }
214
+ ```
215
+ Setting "allowUnknownFields" to false protects your application from receiving unexpected properties in the request body, query, and files. It does not affect request.path, as path variables are defined at the routing level, nor does it affect request headers. Conversely, you can receive { name: 'John Doe', role: 'Admin' } when only expecting name.
216
+
217
+ The "openApiDoc" property is part of the Open API route definition and is optional.
218
+
219
+ 3.2.3 Context.request
220
+
221
+ Context.request is aggregating object that describes all HTTP request properties You expect
222
+
223
+ ```typescript
224
+
225
+ interface ContextRequest {
226
+ headers?: Record<string, PropertyDefinition>;
227
+ query?: Record<string, PropertyDefinition>;
228
+ path?: Record<string, PropertyDefinition>;
229
+ body?: Record<string, PropertyDefinition>;
230
+ files?: Record<string, FilePropertyDefinition>;
231
+ auth?: Record<string, any>;
232
+ }
233
+ ```
234
+ There's an example for request with path property only:
235
+
236
+ ```typescript
237
+ const request: ContextRequest = {
238
+ path: {
239
+ id: {
240
+ openApiDoc: {
241
+ schema: {
242
+ type: 'string',
243
+ },
244
+ example: '123456',
245
+ description: 'User id'
246
+ },
247
+ validation: {
248
+ required: true,
249
+ handler (value) {
250
+ return /^\d+$/.test(value);
251
+ },
252
+ transform (value) {
253
+ return parseInt(value, 10);
254
+ },
255
+ error: {
256
+ statusCode: 400,
257
+ message: '400.1.0'
258
+ }
259
+ }
260
+ }
261
+ }
262
+ };
263
+ ```
264
+ Validation.handler returns boolean (any => boolean | any => Promise<boolean>) if received value fits to provided requirements,
265
+ and in falsy case it throws an error and app returns 400 Bad Request with a json body contains specified message
266
+ Handler can be async and can use complex logic under the hood; it allows to use
267
+ Avoid using an arrow functions as handlers - in this case You can lose execution context,
268
+ The execution context of validation.handler (and validation transform) is the request
269
+ (aggregating object - the argument used at the route handler)
270
+
271
+ ```typescript
272
+
273
+ const request: ContextRequest = {
274
+ body: {
275
+ role: {
276
+ openApiDoc: {
277
+ schema: {
278
+ type: 'string',
279
+ },
280
+ example: 'admin',
281
+ description: 'User role'
282
+ },
283
+ validation: {
284
+ required: true,
285
+ async handler (value) {
286
+ if (this.auth.user.role === 'admin') {
287
+ return true;
288
+ }
289
+ await userService.makeSomeChecking(this.query.scope);
290
+ return value === 'customer'
291
+ },
292
+ error: {
293
+ statusCode: 400,
294
+ message: '400.1.0'
295
+ }
296
+ }
297
+ }
298
+ }
299
+ };
300
+
301
+ ```
302
+ In the same way You can describe the request headers, query, body. Remember, if context.options.allowUnknownFields
303
+ is set to false, all non-described request properties (or non-described nested properties in the described request properties) never be accessible.
304
+
305
+ ```typescript
306
+
307
+ const request: ContextRequest = {
308
+ query: {
309
+ sortBy: {
310
+ openApiDoc: {
311
+ schema: {
312
+ type: 'string',
313
+ },
314
+ example: 'createdAt',
315
+ description: 'Sort by field'
316
+ },
317
+ validation: {
318
+ required: true,
319
+ handler (value) {
320
+ return ['createdAt','updatedAt'].includes(value);
321
+ },
322
+ error: {
323
+ statusCode: 400,
324
+ message: 'sortBy should be createdAt or updatedAt'
325
+ }
326
+ }
327
+ },
328
+ sortOrder: {
329
+ openApiDoc: {
330
+ schema: {
331
+ type: 'string',
332
+ },
333
+ example: 'asc',
334
+ description: 'Sort order'
335
+ },
336
+ validation: {
337
+ required: true,
338
+ handler (value) {
339
+ return ['asc','desc'].includes(value);
340
+ },
341
+ error: {
342
+ statusCode: 400,
343
+ message: 'sortOrder should be asc or desc'
344
+ }
345
+ }
346
+ }
347
+ }
348
+ };
349
+
350
+ ```
351
+ Now, if you send GET /user?sortBy=updateAt&sortOrder=desc&limit=10, the limit parameter will be ignored since it wasn't described:
352
+ ```typescript
353
+
354
+ app.post('/user', context, (request: Request) => {
355
+
356
+ console.log(request.query);
357
+
358
+ // {
359
+ // sortBy: 'updatedAt',
360
+ // sortOrder: 'desc'
361
+ // }
362
+
363
+ return {
364
+ statusCode: 200,
365
+ headers: {
366
+ 'Content-Type': 'application/json'
367
+ },
368
+ body: []
369
+ }
370
+ })
371
+
372
+
373
+ ```
374
+
375
+ Regarding the request body even it described properly it will be available only for POST, PUT and PATCH HTTP requests.
376
+ If You want dealing with uploaded files You just should to describe it in the request:
377
+
378
+ ```typescript
379
+ const request: ContextRequest = {
380
+ files: {
381
+ avatar: {
382
+ mimetypes: [
383
+ 'image/png'
384
+ ],
385
+ maxSize: 1048576,
386
+ required: true,
387
+ maxSizeError: {
388
+ statusCode: 413,
389
+ message: 'The avatar should not be larger than 1MB'
390
+ },
391
+ mimetypeError: {
392
+ statusCode: 415,
393
+ message: 'The avatar should be a PNG image'
394
+ },
395
+ requiredError: {
396
+ statusCode: 400,
397
+ message: 'The avatar file is required'
398
+ }
399
+ }
400
+ }
401
+ };
402
+
403
+ ```
404
+ and uploaded file will be acceptable in the request:
405
+
406
+ ```typescript
407
+ import { createWriteStream } from 'fs';
408
+
409
+ app.post('/user/:id/avatar', context, (request: Request) => {
410
+
411
+ console.log(request.files);
412
+ // {
413
+ // avatar: {
414
+ // stream: PassThrough,
415
+ // size: 65025,
416
+ // mimetype: 'image/png'
417
+ // }
418
+ // }
419
+
420
+ // You can play with request.files.avatar.stream as You want e.g. save it locally
421
+
422
+ const writeStream = createWriteStream(`/path/to/user/avatars/${req.path.id}.png`);
423
+ request.files.avatar.stream.pipe(writeStream);
424
+ return {
425
+ statusCode: 200,
426
+ headers: {
427
+ 'Content-Type': 'application/json'
428
+ },
429
+ body: user
430
+ }
431
+ })
432
+
433
+
434
+ ```
435
+
436
+ 3.2.4 Context response
437
+
438
+ Context.response is always the same as the return type of the route handler
439
+
440
+ ```typescript
441
+
442
+ interface Response<T> {
443
+ title?: string;
444
+ description?: string;
445
+ statusCode: number;
446
+ headers?: Record<string, string>;
447
+ body?: T
448
+ }
449
+
450
+ ```
451
+
452
+ 3.3 Route handler
453
+
454
+ The route handler is the function that implements the following contract:
455
+
456
+ ```typescript
457
+
458
+ (request: Request) => Response<T> | Promise<Response<T>>;
459
+
460
+ ```
461
+
462
+ Despite having a strict contract for the return type,
463
+ the response body can be an object (for application/json content-type), text, or even a stream
464
+
465
+ ```typescript
466
+ interface UserDto {
467
+ id: number;
468
+ name: string;
469
+ }
470
+
471
+ const handlerWithJSONBody = (request: Request): Response<UserDto> => {
472
+ const body: UserDto = {
473
+ id: 1,
474
+ name: 'John Doe'
475
+ }
476
+ return {
477
+ statusCode: 200,
478
+ headers: {'Content-Type': 'application/json' },
479
+ body
480
+ }
481
+ }
482
+
483
+
484
+ const handlerWithStringBody = (request: Request): Response<string> => {
485
+ const body: string = 'Not Found'
486
+ return {
487
+ statusCode: 404,
488
+ headers: {'Content-Type': 'text/plain' },
489
+ body
490
+ }
491
+ }
492
+
493
+ const handlerWithStreamBody = (request: Request): Response<ReadStream> => {
494
+ const body = createReadStream('/path/to/image.png')
495
+ return {
496
+ statusCode: 200,
497
+ headers: {'Content-Type': 'image/png' },
498
+ body
499
+ }
500
+ }
501
+
502
+ ```
503
+
504
+ 4. Running
505
+
506
+
507
+ ```typescript
508
+
509
+ app.start(() => console.log('Your awesome application has been successfully launched'));
510
+
511
+ // And stop the application when needed
512
+ app.stop(() => console.log('Your awesome application has been successfully launched'));
513
+
514
+ ```
515
+ Don't forget to check swagger at the defined above options.openApiDoc.route
package/ROADMAP.md ADDED
File without changes
@@ -0,0 +1,20 @@
1
+ window.onload = function() {
2
+ //<editor-fold desc="Changeable Configuration Block">
3
+
4
+ // the following lines will be replaced by docker/configurator, when it runs in a docker-container
5
+ window.ui = SwaggerUIBundle({
6
+ url: "/swagger.json",
7
+ dom_id: '#swagger-ui',
8
+ deepLinking: true,
9
+ presets: [
10
+ SwaggerUIBundle.presets.apis,
11
+ SwaggerUIStandalonePreset
12
+ ],
13
+ plugins: [
14
+ SwaggerUIBundle.plugins.DownloadUrl
15
+ ],
16
+ layout: "StandaloneLayout"
17
+ });
18
+
19
+ //</editor-fold>
20
+ };
@@ -0,0 +1,18 @@
1
+ import { AladoServerOptions } from '../options';
2
+ import { Context, Response } from '../dto';
3
+ export declare class AladoServer {
4
+ private readonly router;
5
+ private readonly server;
6
+ private readonly openApiDocObject;
7
+ private port;
8
+ private readonly logger?;
9
+ constructor(options: AladoServerOptions);
10
+ get(path: string, context: Context<any>, handler: (request: any) => Promise<Response<any>>): void;
11
+ post(path: string, context: Context<any>, handler: (request: any) => Promise<Response<any>>): void;
12
+ put(path: string, context: Context<any>, handler: (request: any) => Promise<Response<any>>): void;
13
+ patch(path: string, context: Context<any>, handler: (request: any) => Promise<Response<any>>): void;
14
+ delete(path: string, context: Context<any>, handler: (request: any) => Promise<Response<any>>): void;
15
+ head(path: string, context: Context<any>, handler: (request: any) => Promise<Response<any>>): void;
16
+ start(cb: () => void): void;
17
+ stop(cb: (error?: Error) => void): void;
18
+ }
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AladoServer = void 0;
4
+ const _const_1 = require("../constants");
5
+ const _router_1 = require("../router");
6
+ const _factory_1 = require("../factory");
7
+ const _helper_1 = require("../helpers");
8
+ class AladoServer {
9
+ router;
10
+ server;
11
+ openApiDocObject;
12
+ port;
13
+ logger;
14
+ constructor(options) {
15
+ (0, _helper_1.validateAladoOptions)(options);
16
+ if (options.openApiDoc?.route) {
17
+ options.openApiDoc.route = `${(0, _helper_1.clearRoute)(options.openApiDoc.route)}`;
18
+ }
19
+ this.logger = options.logger || console;
20
+ this.openApiDocObject = { ..._const_1.defaultOpenApiDocObject, info: options.openApiDoc?.info || {} };
21
+ this.router = new _router_1.Router({
22
+ cors: {
23
+ enable: options.cors?.enable,
24
+ allowedOrigin: options?.cors?.allowedOrigin || '*',
25
+ allowedHeaders: options?.cors?.allowedHeaders || [],
26
+ allowedMethods: new Map,
27
+ exposeHeaders: options?.cors?.exposeHeaders || [],
28
+ },
29
+ openApiDoc: options.openApiDoc,
30
+ logger: this.logger
31
+ }, this.openApiDocObject);
32
+ this.port = options.port;
33
+ this.server = (0, _factory_1.httpServerFactory)({
34
+ router: this.router,
35
+ openApiDoc: options.openApiDoc,
36
+ enableCors: options.cors?.enable,
37
+ logger: this.logger
38
+ }, options.ssl);
39
+ }
40
+ get(path, context, handler) {
41
+ this.router.use(_const_1.HttpMethod.GET, path, context, handler);
42
+ }
43
+ post(path, context, handler) {
44
+ this.router.use(_const_1.HttpMethod.POST, path, context, handler);
45
+ }
46
+ put(path, context, handler) {
47
+ this.router.use(_const_1.HttpMethod.PUT, path, context, handler);
48
+ }
49
+ patch(path, context, handler) {
50
+ this.router.use(_const_1.HttpMethod.PATCH, path, context, handler);
51
+ }
52
+ delete(path, context, handler) {
53
+ this.router.use(_const_1.HttpMethod.DELETE, path, context, handler);
54
+ }
55
+ head(path, context, handler) {
56
+ this.router.use(_const_1.HttpMethod.HEAD, path, context, handler);
57
+ }
58
+ start(cb) {
59
+ this.server.listen(this.port, cb);
60
+ }
61
+ stop(cb) {
62
+ this.server.close(cb);
63
+ }
64
+ }
65
+ exports.AladoServer = AladoServer;
66
+ //# sourceMappingURL=alado-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alado-server.js","sourceRoot":"","sources":["../../src/alado-server/alado-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export { AladoServer } from './alado-server';
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AladoServer = void 0;
4
+ var alado_server_1 = require("./alado-server");
5
+ Object.defineProperty(exports, "AladoServer", { enumerable: true, get: function () { return alado_server_1.AladoServer; } });
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/alado-server/index.ts"],"names":[],"mappings":";;;AAAA,+CAA6C;AAApC,2GAAA,WAAW,OAAA"}
@@ -0,0 +1,6 @@
1
+ export declare enum ContentType {
2
+ TEXT_PLAIN = "text/plain",
3
+ JSON = "application/json",
4
+ FORM_DATA = "multipart/form-data",
5
+ X_WWW_FORM_URLENCODED = "application/x-www-form-urlencoded"
6
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContentType = void 0;
4
+ var ContentType;
5
+ (function (ContentType) {
6
+ ContentType["TEXT_PLAIN"] = "text/plain";
7
+ ContentType["JSON"] = "application/json";
8
+ ContentType["FORM_DATA"] = "multipart/form-data";
9
+ ContentType["X_WWW_FORM_URLENCODED"] = "application/x-www-form-urlencoded";
10
+ })(ContentType || (exports.ContentType = ContentType = {}));
11
+ //# sourceMappingURL=content-type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-type.js","sourceRoot":"","sources":["../../src/constants/content-type.ts"],"names":[],"mappings":";;;AAAA,IAAY,WAKX;AALD,WAAY,WAAW;IACrB,wCAAyB,CAAA;IACzB,wCAAyB,CAAA;IACzB,gDAAiC,CAAA;IACjC,0EAA2D,CAAA;AAC7D,CAAC,EALW,WAAW,2BAAX,WAAW,QAKtB"}
@@ -0,0 +1 @@
1
+ export declare const defaultOpenApiDocObject: any;