@typetron/framework 0.5.3 → 0.5.5

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 (195) hide show
  1. package/.eslintrc-old.js +208 -0
  2. package/Cache/Cache.ts +13 -0
  3. package/Cache/Stores/DatabaseStore.ts +120 -0
  4. package/Cache/Stores/FileStore.ts +69 -0
  5. package/Cache/Stores/MemoryStore.ts +71 -0
  6. package/Cache/index.ts +5 -0
  7. package/Cache/types.ts +4 -0
  8. package/Container/ClassResolver.ts +106 -0
  9. package/Container/Container.ts +144 -0
  10. package/Container/Contracts.ts +3 -0
  11. package/Container/Decorators.ts +20 -0
  12. package/Container/Metadata.ts +16 -0
  13. package/Container/Resolver.ts +27 -0
  14. package/Container/index.ts +5 -0
  15. package/Database/Clauses/Where.ts +0 -0
  16. package/Database/Connection.ts +52 -0
  17. package/Database/Decorators.ts +142 -0
  18. package/Database/Drivers/DatabaseDriver.ts +37 -0
  19. package/Database/Drivers/MySQL/Alter/index.ts +55 -0
  20. package/Database/Drivers/MySQL/ColumnDefinition.ts +16 -0
  21. package/Database/Drivers/MySQL/Create.ts +47 -0
  22. package/Database/Drivers/MySQL/MysqlDriver.ts +123 -0
  23. package/Database/Drivers/MySQL/Schema.ts +82 -0
  24. package/Database/Drivers/MySQL/index.ts +13 -0
  25. package/Database/Drivers/SQL/Schema.ts +73 -0
  26. package/Database/Drivers/SQL/Statements/Alter.ts +22 -0
  27. package/Database/Drivers/SQL/Statements/BaseStatement.ts +55 -0
  28. package/Database/Drivers/SQL/Statements/Delete.ts +20 -0
  29. package/Database/Drivers/SQL/Statements/Insert.ts +24 -0
  30. package/Database/Drivers/SQL/Statements/Select.ts +55 -0
  31. package/Database/Drivers/SQL/Statements/Update.ts +33 -0
  32. package/Database/Drivers/SQL/index.ts +8 -0
  33. package/Database/Drivers/SQLite/Create.ts +45 -0
  34. package/Database/Drivers/SQLite/Schema.ts +54 -0
  35. package/Database/Drivers/SQLite/SqliteDriver.ts +144 -0
  36. package/Database/Drivers/SQLite/index.ts +15 -0
  37. package/Database/Drivers/SchemaContract.ts +11 -0
  38. package/Database/Drivers/Statement.ts +16 -0
  39. package/Database/Drivers/index.ts +4 -0
  40. package/Database/Entity.ts +377 -0
  41. package/Database/EntityNotFoundError.ts +5 -0
  42. package/Database/EntityProxyHandler.ts +43 -0
  43. package/Database/EntityQuery.ts +197 -0
  44. package/Database/Expression.ts +3 -0
  45. package/Database/Fields.ts +816 -0
  46. package/Database/Helpers.ts +12 -0
  47. package/Database/List.ts +71 -0
  48. package/Database/Migrations/Migration.ts +10 -0
  49. package/Database/Migrations/MigrationHistory.ts +20 -0
  50. package/Database/Migrations/Migrator.ts +122 -0
  51. package/Database/Migrations/index.ts +3 -0
  52. package/Database/ORM/BaseRelationship.ts +10 -0
  53. package/Database/Query.ts +374 -0
  54. package/Database/Seeders/Seeder.ts +3 -0
  55. package/Database/Seeders/SeederManager.ts +27 -0
  56. package/Database/Seeders/index.ts +2 -0
  57. package/Database/StringExpression.ts +9 -0
  58. package/Database/Types.ts +165 -0
  59. package/Database/index.ts +23 -0
  60. package/Encryption/index.ts +51 -0
  61. package/Forms/Decorators.ts +29 -0
  62. package/Forms/Form.ts +70 -0
  63. package/Forms/FormFields.ts +36 -0
  64. package/Forms/index.ts +3 -0
  65. package/Framework/App.ts +10 -0
  66. package/Framework/Application.ts +118 -0
  67. package/Framework/Auth/Auth.ts +122 -0
  68. package/Framework/Auth/Authenticatable.ts +9 -0
  69. package/Framework/Auth/User.ts +27 -0
  70. package/Framework/Auth/index.ts +29 -0
  71. package/Framework/Config/AppConfig.ts +25 -0
  72. package/Framework/Config/AuthConfig.ts +9 -0
  73. package/Framework/Config/BaseConfig.ts +9 -0
  74. package/Framework/Config/CacheConfig.ts +38 -0
  75. package/Framework/Config/DatabaseConfig.ts +21 -0
  76. package/Framework/Config/MailConfig.ts +30 -0
  77. package/Framework/Config/index.ts +6 -0
  78. package/Framework/Middleware/AuthMiddleware.ts +31 -0
  79. package/Framework/Middleware/CorsMiddleware.ts +32 -0
  80. package/Framework/Middleware/StaticAssetsMiddleware.ts +76 -0
  81. package/Framework/Middleware/index.ts +2 -0
  82. package/Framework/Provider.ts +9 -0
  83. package/Framework/Providers/CacheProvider.ts +28 -0
  84. package/Framework/Providers/DatabaseProvider.ts +36 -0
  85. package/Framework/Providers/MailProvider.ts +31 -0
  86. package/Framework/Providers/WebsocketsProvider.ts +120 -0
  87. package/Framework/Resolvers/AuthResolver.ts +23 -0
  88. package/Framework/Resolvers/EntityResolver.ts +41 -0
  89. package/Framework/Resolvers/FormResolver.ts +37 -0
  90. package/Framework/RootDir.ts +1 -0
  91. package/Framework/index.ts +8 -0
  92. package/Frontend/Angular/index.ts +24 -0
  93. package/Frontend/Websockets/index.ts +86 -0
  94. package/Mail/MailAddress.ts +4 -0
  95. package/Mail/MailTransport.ts +6 -0
  96. package/Mail/Mailable.ts +16 -0
  97. package/Mail/Mailer.ts +27 -0
  98. package/Mail/PendingMail.ts +68 -0
  99. package/Mail/SentMessage.ts +20 -0
  100. package/Mail/Transports/MemoryTransport.ts +13 -0
  101. package/Mail/Transports/SendGridTransport.ts +95 -0
  102. package/Mail/index.ts +3 -0
  103. package/Models/Model.ts +41 -0
  104. package/Models/index.ts +54 -0
  105. package/Router/BaseController.ts +7 -0
  106. package/Router/Decorators.ts +224 -0
  107. package/Router/Guard.ts +10 -0
  108. package/Router/Http/Contracts.ts +4 -0
  109. package/Router/Http/ErrorHandler.ts +45 -0
  110. package/Router/Http/ErrorHandlerInterface.ts +6 -0
  111. package/Router/Http/Errors/HttpError.ts +15 -0
  112. package/Router/Http/Handler.ts +183 -0
  113. package/Router/Http/HttpRoute.ts +110 -0
  114. package/Router/Http/Middleware.ts +7 -0
  115. package/Router/Http/Request.ts +225 -0
  116. package/Router/Http/Response.ts +35 -0
  117. package/Router/Http/index.ts +85 -0
  118. package/Router/Metadata.ts +34 -0
  119. package/Router/Middleware.ts +16 -0
  120. package/Router/Request.ts +65 -0
  121. package/Router/Route.ts +16 -0
  122. package/Router/RouteNotFoundError.ts +5 -0
  123. package/Router/Router.ts +35 -0
  124. package/Router/Servers/index.ts +6 -0
  125. package/Router/Servers/node.ts +49 -0
  126. package/Router/Servers/uNetworking.ts +151 -0
  127. package/Router/Websockets/Handler.ts +82 -0
  128. package/Router/Websockets/Middleware.ts +7 -0
  129. package/Router/Websockets/Request.ts +3 -0
  130. package/Router/Websockets/WebsocketRoute.ts +61 -0
  131. package/Router/Websockets/index.ts +57 -0
  132. package/Router/Websockets/types.ts +23 -0
  133. package/Router/index.ts +6 -0
  134. package/Storage/File.ts +30 -0
  135. package/Storage/Image.ts +19 -0
  136. package/Storage/InvalidImage.ts +3 -0
  137. package/Storage/Storage.ts +98 -0
  138. package/Storage/index.ts +4 -0
  139. package/Storage/utils.ts +18 -0
  140. package/Support/Array.ts +184 -0
  141. package/Support/Math.ts +12 -0
  142. package/Support/Metadata.ts +31 -0
  143. package/Support/String.ts +38 -0
  144. package/Support/index.ts +19 -0
  145. package/Support/utils.ts +7 -0
  146. package/Testing/TestCase.ts +193 -0
  147. package/Validation/Rule.ts +9 -0
  148. package/Validation/RuleInterface.ts +10 -0
  149. package/Validation/Rules/Email.ts +20 -0
  150. package/Validation/Rules/FileExtension.ts +21 -0
  151. package/Validation/Rules/MaxLength.ts +17 -0
  152. package/Validation/Rules/MinLength.ts +18 -0
  153. package/Validation/Rules/Optional.ts +18 -0
  154. package/Validation/Rules/Required.ts +29 -0
  155. package/Validation/Validator.ts +7 -0
  156. package/Validation/index.ts +12 -0
  157. package/benchmark-index.js +20 -0
  158. package/benchmark.ts +47 -0
  159. package/package.json +7 -3
  160. package/tests/Cache/DatabaseStoreTest.ts +110 -0
  161. package/tests/Cache/FileStoreTest.ts +93 -0
  162. package/tests/Cache/MemoryStoreTest.ts +92 -0
  163. package/tests/Container/ContainerTest.ts +336 -0
  164. package/tests/Database/Drivers/MySQL/AlterTest.ts +100 -0
  165. package/tests/Database/Drivers/MySQL/CreateTest.ts +39 -0
  166. package/tests/Database/Drivers/MysqlDriverTest.ts +140 -0
  167. package/tests/Database/Drivers/SqliteDriverTest.ts +126 -0
  168. package/tests/Database/Entities/Article.ts +21 -0
  169. package/tests/Database/Entities/Profile.ts +21 -0
  170. package/tests/Database/Entities/Role.ts +15 -0
  171. package/tests/Database/Entities/User.ts +35 -0
  172. package/tests/Database/EntityRelationshipsTest.ts +475 -0
  173. package/tests/Database/EntityTest.ts +202 -0
  174. package/tests/Database/Migrations/RunnerTest.ts +96 -0
  175. package/tests/Database/Migrations/migrations/1.createUserTable.ts +12 -0
  176. package/tests/Database/Migrations/migrations/2.createArticlesTable.ts +12 -0
  177. package/tests/Database/QueryTest.ts +573 -0
  178. package/tests/Database/SchemaTest.ts +32 -0
  179. package/tests/Database/Seeders/RunnerTest.ts +30 -0
  180. package/tests/Database/Seeders/seeders/RandomSeeder.ts +10 -0
  181. package/tests/Database/utils.ts +16 -0
  182. package/tests/Forms/FormsTest.ts +59 -0
  183. package/tests/Framework/Middleware/StaticAssetsMiddlewareTest.ts +129 -0
  184. package/tests/Framework/Resolvers/EntityResolverTest.ts +48 -0
  185. package/tests/Framework/Router/RouterTest.ts +33 -0
  186. package/tests/Mail/MailerTest.ts +76 -0
  187. package/tests/Model/ModelTest.ts +183 -0
  188. package/tests/Router/HttpHandlerTest.ts +72 -0
  189. package/tests/Router/RequestTest.ts +77 -0
  190. package/tests/Router/WebSocketsHandlerTest.ts +51 -0
  191. package/tests/Storage/StorageTest.ts +97 -0
  192. package/tests/Support/ArrayTest.ts +167 -0
  193. package/tests/Support/StringTest.ts +18 -0
  194. package/tests/Validation/RulesTest.ts +54 -0
  195. package/types.d.ts +8 -0
@@ -0,0 +1,224 @@
1
+ import { App } from '../Framework'
2
+ import { Abstract, Constructor, Type } from '../Support'
3
+ import { GlobalMiddleware } from './Middleware'
4
+ import { ActionMetadata, ControllerMetadata, ControllerOptions, MethodMetadata, RouteMetadata } from './Metadata'
5
+ import { Guard } from './Guard'
6
+ import { Handler as HttpHandler, Http, Request as HttpRequest } from './Http'
7
+ import { Request } from './Request'
8
+ import { Handler as WebsocketsHandler } from './Websockets'
9
+ import { WebsocketRoute } from './Websockets/WebsocketRoute'
10
+ import { Container } from '@Typetron/Container'
11
+
12
+ export function Controller(path = '', options?: ControllerOptions) {
13
+ return (target: object) => {
14
+ const metadata = ControllerMetadata.get(target)
15
+ const prefix = options?.prefix || path ? path + '.' : ''
16
+
17
+ const httpHandler = Container.getInstance().get(HttpHandler)
18
+ const websocketHandler = Container.getInstance().get(WebsocketsHandler)
19
+
20
+ Object.keys(metadata.routes).forEach(action => {
21
+ const routeMetadata = metadata.routes[action]
22
+ const route = httpHandler.addRoute(
23
+ path + (path && routeMetadata.path ? '/' : '') + routeMetadata.path,
24
+ routeMetadata.method,
25
+ target as Constructor,
26
+ action,
27
+ prefix + (routeMetadata.name || action),
28
+ routeMetadata.parametersTypes,
29
+ metadata.middleware.concat(routeMetadata.middleware)
30
+ )
31
+ route.guards = routeMetadata.guards
32
+ })
33
+
34
+ Object.keys(metadata.actions).forEach(actionName => {
35
+ const actionMetadata = metadata.actions[actionName]
36
+ const name = prefix + (actionMetadata.name || actionName)
37
+ const action = websocketHandler.addAction(
38
+ name,
39
+ target as Constructor,
40
+ actionName,
41
+ actionMetadata.parametersTypes,
42
+ metadata.middleware.concat(actionMetadata.middleware)
43
+ )
44
+ action.guards = actionMetadata.guards
45
+ })
46
+ }
47
+ }
48
+
49
+ function addAction(controller: Constructor, methodName: string, name: string) {
50
+ const metadata = ControllerMetadata.get(controller)
51
+
52
+ const methodMetadata = metadata.methods[methodName]
53
+ const action = new ActionMetadata()
54
+ Object.assign(action, methodMetadata)
55
+
56
+ action.parametersTypes = Reflect.getMetadata('design:paramtypes', controller.prototype, methodName)
57
+ action.name = name
58
+
59
+ metadata.actions[methodName] = action
60
+
61
+ ControllerMetadata.set(metadata, controller)
62
+ }
63
+
64
+ function addRoute(controller: Constructor, methodName: string, method: Http.Method, path: string, name: string) {
65
+ const metadata = ControllerMetadata.get(controller)
66
+
67
+ const methodMetadata = metadata.methods[methodName]
68
+ const route = new RouteMetadata()
69
+ Object.assign(route, methodMetadata)
70
+
71
+ route.parametersTypes = Reflect.getMetadata('design:paramtypes', controller.prototype, methodName)
72
+ route.path = path
73
+ route.name = name
74
+ route.method = method
75
+
76
+ metadata.routes[methodName] = route
77
+
78
+ ControllerMetadata.set(metadata, controller)
79
+ }
80
+
81
+ export function Action(name = '') {
82
+ return (target: object, action: string) => {
83
+ addAction(target.constructor as Constructor, action, name)
84
+ }
85
+ }
86
+
87
+ export function OnOpen(target: object, action: string) {
88
+ const websocketHandler = App.get(WebsocketsHandler)
89
+
90
+ websocketHandler.onOpen = new WebsocketRoute(
91
+ '',
92
+ target.constructor as Constructor,
93
+ action,
94
+ Reflect.getMetadata('design:paramtypes', target, action),
95
+ )
96
+ }
97
+
98
+ export function OnClose(target: object, action: string) {
99
+ const websocketHandler = App.get(WebsocketsHandler)
100
+
101
+ websocketHandler.onClose = new WebsocketRoute(
102
+ '',
103
+ target.constructor as Constructor,
104
+ action,
105
+ Reflect.getMetadata('design:paramtypes', target, action),
106
+ )
107
+ }
108
+
109
+ export function Arg(name: string) {
110
+ return function(target: object, action: string, parameterIndex: number) {
111
+ // addAction(target.constructor as Constructor, action, name)
112
+ }
113
+ }
114
+
115
+ export function Get(path = '', name = '') {
116
+ return (target: object, action: string) => {
117
+ addRoute(target.constructor as Constructor, action, Http.Method.GET, path, name)
118
+ }
119
+ }
120
+
121
+ export function Post(path: string = '', name = '') {
122
+ return (target: object, action: string) => {
123
+ addRoute(target.constructor as Constructor, action, Http.Method.POST, path, name)
124
+ }
125
+ }
126
+
127
+ export function Put(path: string = '', name = '') {
128
+ return (target: object, action: string) => {
129
+ addRoute(target.constructor as Constructor, action, Http.Method.PUT, path, name)
130
+ }
131
+ }
132
+
133
+ export function Patch(path: string = '', name = '') {
134
+ return (target: object, action: string) => {
135
+ addRoute(target.constructor as Constructor, action, Http.Method.PATCH, path, name)
136
+ }
137
+ }
138
+
139
+ export function Delete(path: string = '', name = '') {
140
+ return (target: object, action: string) => {
141
+ addRoute(target.constructor as Constructor, action, Http.Method.DELETE, path, name)
142
+ }
143
+ }
144
+
145
+ export function Middleware(...middleware: Abstract<GlobalMiddleware>[]) {
146
+ return (target: object, action?: string) => {
147
+ if (action) {
148
+ target = target.constructor
149
+ }
150
+
151
+ const metadata = ControllerMetadata.get(target)
152
+
153
+ if (action) {
154
+ const route = metadata.methods[action] || new MethodMetadata()
155
+ route.middleware = route.middleware.concat(middleware)
156
+ metadata.methods[action] = route
157
+ } else {
158
+ metadata.middleware = metadata.middleware.concat(middleware)
159
+ }
160
+
161
+ ControllerMetadata.set(metadata, target)
162
+ }
163
+ }
164
+
165
+ export function AllowIf(...guards: Type<Guard>[]) {
166
+ return function(target: object, action: string) {
167
+ if (action) {
168
+ target = target.constructor
169
+ }
170
+
171
+ const metadata = ControllerMetadata.get(target)
172
+
173
+ if (action) {
174
+ const route = metadata.methods[action] || new MethodMetadata()
175
+ route.guards = route.guards.concat(guards)
176
+ metadata.methods[action] = route
177
+ } else {
178
+ metadata.guards = metadata.guards.concat(guards)
179
+ }
180
+
181
+ ControllerMetadata.set(metadata, target)
182
+ }
183
+ }
184
+
185
+ export function Query(name: string) {
186
+ return function(target: object, action: string, parameterIndex: number) {
187
+ const metadata = ControllerMetadata.get(target.constructor)
188
+ const parametersTypes = Reflect.getMetadata('design:paramtypes', target, action)
189
+
190
+ const route = metadata.methods[action] || new MethodMetadata()
191
+ route.parametersOverrides[parameterIndex] = function(container: Container) {
192
+ const request = container.get(HttpRequest)
193
+ const value = request.query[name]
194
+
195
+ if (parametersTypes[parameterIndex].name === 'Number') {
196
+ return value === undefined || value === 'undefined' ? undefined : Number(value)
197
+ }
198
+ return value
199
+ }
200
+
201
+ metadata.methods[action] = route
202
+ ControllerMetadata.set(metadata, target.constructor)
203
+ }
204
+ }
205
+
206
+ export function Body() {
207
+ return function(target: object, action: string, parameterIndex: number) {
208
+ const metadata = ControllerMetadata.get(target.constructor)
209
+ const route = metadata.methods[action] || new MethodMetadata()
210
+ route.parametersOverrides[parameterIndex] = function(container: Container) {
211
+ const request = container.get(Request)
212
+
213
+ // TODO: should the framework throw error or return undefined?
214
+ // if (!request.body) {
215
+ // throw new Error(`There is no data in the 'body' of the request`)
216
+ // }
217
+
218
+ return request.body
219
+ }
220
+
221
+ metadata.methods[action] = route
222
+ ControllerMetadata.set(metadata, target.constructor)
223
+ }
224
+ }
@@ -0,0 +1,10 @@
1
+ import { Http, HttpError } from './Http'
2
+
3
+ export abstract class Guard {
4
+ // tslint:disable-next-line:no-any
5
+ abstract condition(...args: any[]): Promise<boolean>
6
+
7
+ onFail(): void {
8
+ throw new HttpError('Unauthorized', Http.Status.UNAUTHORIZED)
9
+ }
10
+ }
@@ -0,0 +1,4 @@
1
+ export interface Parameters {
2
+ [key: string]: string | undefined;
3
+ }
4
+
@@ -0,0 +1,45 @@
1
+ import { Response } from './Response'
2
+ import { ErrorHandlerInterface } from './ErrorHandlerInterface'
3
+ import { HttpError } from './Errors/HttpError'
4
+ import { Http } from './index'
5
+ import { Request } from './Request'
6
+ import { Inject } from '../../Container'
7
+ import { AppConfig } from '../../Framework/Config'
8
+
9
+ export class ErrorHandler implements ErrorHandlerInterface {
10
+
11
+ @Inject()
12
+ appConfig: AppConfig
13
+
14
+ handle(error: Error, request?: Request) {
15
+ if (this.appConfig.debug) {
16
+ return this.handleDevelopmentError(error, request)
17
+ }
18
+
19
+ if (error instanceof HttpError) {
20
+ return new Response(error.content, error.status)
21
+ }
22
+ return new Response(error.message, Http.Status.BAD_REQUEST)
23
+ }
24
+
25
+ private handleDevelopmentError(error: Error, request?: Request) {
26
+ let code = Http.Status.BAD_REQUEST, message: string | object | number = '', stack: string[]
27
+
28
+ stack = error.stack ? error.stack.replace(/\n|\s{2,}/g, '').split('at ') : []
29
+ if (error instanceof HttpError) {
30
+ code = error.status
31
+ message = error.content
32
+ if (request) {
33
+ stack.unshift(`Route: ${request.method} ${request.uri}`)
34
+ }
35
+ } else {
36
+ message = error.message
37
+ }
38
+
39
+ const errorInfo: {message: string | object | number, stack?: string[]} = {
40
+ message, stack
41
+ }
42
+
43
+ return new Response(errorInfo, code)
44
+ }
45
+ }
@@ -0,0 +1,6 @@
1
+ import { Response } from './Response'
2
+ import { Request } from './Request'
3
+
4
+ export class ErrorHandlerInterface {
5
+ handle: (error: Error, request?: Request) => Promise<Response> | Response
6
+ }
@@ -0,0 +1,15 @@
1
+ import { Http } from '../index'
2
+
3
+ export class HttpError extends Error {
4
+
5
+ constructor(
6
+ public content: string | object,
7
+ public status: Http.Status = Http.Status.BAD_REQUEST
8
+ ) {
9
+ super(typeof content === 'string' ? content : 'HTTP Error')
10
+ }
11
+
12
+ getMessage() {
13
+ return this.content
14
+ }
15
+ }
@@ -0,0 +1,183 @@
1
+ import { Container, Inject } from '../../Container'
2
+ import { RequestHandler, RouteNotFoundError, Router } from '../'
3
+ import { ErrorHandlerInterface, Http, Response } from './'
4
+ import { Request } from './Request'
5
+ import { HttpRoute as Route } from './HttpRoute'
6
+ import { AppConfig } from '../../Framework'
7
+ import { Request as BaseRequest } from '../Request'
8
+ import { Abstract, Constructor, Type } from '@Typetron/Support'
9
+ import { AppServer, nodeServer, uNetworkingServer } from '@Typetron/Router/Servers'
10
+ import { HttpMiddleware } from '@Typetron/Router/Http/Middleware'
11
+
12
+ const httpServers: Record<AppConfig['server'], AppServer> = {
13
+ node: nodeServer,
14
+ uNetworking: uNetworkingServer
15
+ }
16
+
17
+ export class Handler {
18
+ @Inject()
19
+ router: Router
20
+
21
+ @Inject()
22
+ errorHandler: ErrorHandlerInterface
23
+
24
+ cachedRoutes: Record<string, [Route, Record<string, string>]> = {}
25
+
26
+ addRoute(
27
+ uri: string,
28
+ method: Http.Method,
29
+ controller: Constructor,
30
+ action: string,
31
+ name: string,
32
+ parametersTypes: (Type<(...args: any[]) => any> | FunctionConstructor)[] = [],
33
+ middleware: Abstract<HttpMiddleware>[] = []
34
+ ): Route {
35
+ uri = this.prepareUri(uri)
36
+ const route = new Route(uri, method, name, controller, action, parametersTypes, middleware)
37
+ if (this.router.routes.find(item => item.uri === uri && item.method === method)) {
38
+ throw new Error(`There is already a route with the same url: [${method}] '${uri}'`)
39
+ }
40
+ this.router.routes.push(route)
41
+ return route
42
+ }
43
+
44
+ // async handle(app: Application, request: Request) {
45
+ // try {
46
+ // return await this.handle(app, request)
47
+ // } catch (error) {
48
+ // return this.errorHandler.handle(error, request)
49
+ // }
50
+ // }
51
+
52
+ startServer(app: Container): void {
53
+ const config = app.get(AppConfig)
54
+ const startServer = httpServers[config.server]
55
+
56
+ startServer(config.port, async (request) => {
57
+ try {
58
+ return await this.handle(app, request)
59
+ } catch (error) {
60
+ return this.errorHandler.handle(error as Error)
61
+ }
62
+ })
63
+ }
64
+
65
+ async handle(app: Container, request: Request): Promise<Response> {
66
+ const container = app.createChildContainer()
67
+
68
+ container.set(BaseRequest, request)
69
+ container.set(Request, request)
70
+
71
+ let stack: RequestHandler = async () => {
72
+ const [route, parameters] = this.cachedRoutes[`${request.method} ${request.uri}`]
73
+ ?? this.findRouteIndex(request.uri ?? '', request.method)
74
+
75
+ container.set(Route, route)
76
+ request.parameters = parameters
77
+
78
+ let routeStack: RequestHandler = async () => {
79
+
80
+ const content = await route.run(container, request.parameters)
81
+
82
+ if (content instanceof Response) {
83
+ return content
84
+ }
85
+
86
+ return Response.ok(content)
87
+ }
88
+
89
+ route.middleware.forEach(middlewareClass => {
90
+ const middleware = container.get(middlewareClass)
91
+ routeStack = middleware.handle.bind(middleware, request, routeStack)
92
+ })
93
+
94
+ return routeStack(request)
95
+ }
96
+
97
+ this.router.middleware.global?.forEach(middlewareClass => {
98
+ const middleware = container.get(middlewareClass)
99
+ stack = middleware.handle.bind(middleware, request, stack)
100
+ })
101
+
102
+ this.router.middleware.http?.forEach(middlewareClass => {
103
+ const middleware = container.get(middlewareClass)
104
+ stack = middleware.handle.bind(middleware, request, stack)
105
+ })
106
+
107
+ return stack(request)
108
+ }
109
+
110
+ matches(uri: string, route: Route) {
111
+ const uriParts = uri.split('/') // ex: ['users', '1', 'posts'];
112
+ let part
113
+ let parameterTypeIndex = 0
114
+ const parameters: Record<string, string> = {}
115
+ for (part = 0; part < uriParts.length; part++) {
116
+ if (!route.uriParts[part]) {
117
+ return false
118
+ }
119
+ if (route.uriParts[part].type === 'parameter' && route.hasCorrectPrimitiveType(parameterTypeIndex, uriParts[part])) {
120
+ parameterTypeIndex++
121
+ parameters[route.uriParts[part].name] = uriParts[part]
122
+ continue
123
+ }
124
+
125
+ if (route.uriParts[part].name !== uriParts[part]) {
126
+ return false
127
+ }
128
+ }
129
+
130
+ if (part !== route.uriParts.length) {
131
+ return false
132
+ }
133
+ return parameters
134
+ }
135
+
136
+ private findRouteIndex(uri: string, method: string): [Route, Record<string, string> | undefined] {
137
+ const preparedUri = this.prepareUri(uri)
138
+
139
+ for (const route of this.router.routes) {
140
+ if (route.method.toLowerCase() === method.toLowerCase()) {
141
+ const parameters = this.matches(preparedUri, route)
142
+
143
+ if (parameters) {
144
+ this.cachedRoutes[`${method} ${uri}`] = [route, parameters]
145
+ return [route, parameters]
146
+ }
147
+ }
148
+ }
149
+
150
+ throw new RouteNotFoundError(`[${method}] ${uri}`)
151
+ // return this.routes
152
+ // .where('uri', uri)
153
+ // .findWhere('method', method) || this.routeNotFound(uri);
154
+
155
+ // const uriParts = uri.split('/'); // ['users', '1', 'posts'];
156
+ //
157
+ // let currentRoute: string | undefined;
158
+ //
159
+ // let routesGroup = this.routesMap;
160
+ // uriParts.forEach(part => {
161
+ // if (routesGroup[part]) {
162
+ // return routesGroup = routesGroup[part];
163
+ // }
164
+ // if (routesGroup['*']) {
165
+ // return routesGroup = routesGroup['*'];
166
+ // }
167
+ //
168
+ // throw new Error(`Route for '${uri}' not found`);
169
+ // });
170
+
171
+ // this.routes;
172
+ }
173
+
174
+ private prepareUri(uri: string) {
175
+ if (uri[0] === '/') {
176
+ uri = uri.substr(1)
177
+ }
178
+ if (uri[uri.length - 1] === '/') {
179
+ uri = uri.substring(0, uri.length - 1)
180
+ }
181
+ return uri
182
+ }
183
+ }
@@ -0,0 +1,110 @@
1
+ import { Container } from '../../Container'
2
+ import { Abstract, Constructor, Type } from '../../Support'
3
+ import { ControllerMetadata } from './../Metadata'
4
+ import { Route } from '@Typetron/Router'
5
+ import { AnyFunction } from '@Typetron/Support'
6
+ import { HttpMiddleware } from '@Typetron/Router/Http/Middleware'
7
+
8
+ export class HttpRoute extends Route {
9
+
10
+ public readonly uriParts: {name: string, type: 'part' | 'parameter'}[] = []
11
+
12
+ constructor(
13
+ public uri: string,
14
+ public method: string,
15
+ public name = '',
16
+ public controller: Constructor,
17
+ public controllerMethod: string,
18
+ public parametersTypes: (Type<AnyFunction> | FunctionConstructor)[] = [],
19
+ public middleware: Abstract<HttpMiddleware>[] = []
20
+ ) {
21
+ super(name, controller, controllerMethod, parametersTypes, middleware)
22
+ this.splitUriParts()
23
+ }
24
+
25
+ async run(container: Container, requestParameters: Record<string, string | number>): Promise<object | string> {
26
+ const controller = await container.get(this.controller)
27
+
28
+ try {
29
+ const metadata = ControllerMetadata.get(this.controller).routes[this.controllerMethod]
30
+ const parameters = await this.resolveParameters(
31
+ requestParameters,
32
+ metadata.parametersTypes,
33
+ metadata.parametersOverrides,
34
+ container
35
+ )
36
+
37
+ for await (const guardClass of this.guards) {
38
+ const guard = container.get(guardClass)
39
+ if (!await guard.condition(...parameters)) {
40
+ guard.onFail()
41
+ }
42
+ }
43
+
44
+ return (controller[this.controllerMethod as keyof Constructor] as AnyFunction<object | string>).apply(controller, parameters)
45
+ } catch (unknownError) {
46
+ const error = unknownError as Error
47
+ error.stack = `Controller: ${controller.constructor.name}.${this.controllerMethod} \n at ` + error.stack
48
+ throw error
49
+ }
50
+ }
51
+
52
+ splitUriParts() {
53
+ this.uri.split('/').forEach(uriPart => {
54
+ const matches = uriPart.match(/:(\w+)/)
55
+ if (matches) {
56
+ this.uriParts.push({
57
+ name: matches[1],
58
+ type: 'parameter'
59
+ })
60
+ } else {
61
+ this.uriParts.push({
62
+ name: uriPart,
63
+ type: 'part'
64
+ })
65
+ }
66
+ })
67
+ }
68
+
69
+ getUrl(parameters: Record<string, string>): string {
70
+ return this.uriParts.map(part => {
71
+ if (part.type === 'parameter') {
72
+ return parameters[part.name]
73
+ }
74
+ return part.name
75
+ }).filter(Boolean).join('/')
76
+ }
77
+
78
+ hasCorrectPrimitiveType(parameterTypeIndex: number, uriPart: string) {
79
+ const parameterType = this.parametersTypes[parameterTypeIndex]
80
+ return (primitiveTypes[parameterType.name] || (value => value))(uriPart)
81
+ }
82
+
83
+ private async resolveParameters(
84
+ requestParameters: Record<string, string | number>,
85
+ parameters: (Type<(...args: any[]) => any> | FunctionConstructor)[],
86
+ overrides: ((container: Container) => unknown)[],
87
+ container: Container
88
+ ) {
89
+ let parameterIndex = 0
90
+ const routeParameters = Object.values(requestParameters)
91
+ return parameters.mapAsync(async (parameter, index) => {
92
+ const newValueFunction = overrides[index]
93
+ if (newValueFunction) {
94
+ return await newValueFunction.call(undefined, container)
95
+ }
96
+ if (parameter.name === 'String') {
97
+ return routeParameters[parameterIndex++]
98
+ }
99
+ if (parameter.name === 'Number') {
100
+ const value = routeParameters[parameterIndex++]
101
+ return value === undefined || value === 'undefined' ? undefined : Number(value)
102
+ }
103
+ return container.get(parameter)
104
+ })
105
+ }
106
+ }
107
+
108
+ const primitiveTypes: Record<string, (value: string) => boolean> = {
109
+ Number: (value: string) => !isNaN(Number(value)),
110
+ }
@@ -0,0 +1,7 @@
1
+ import { Request } from '@Typetron/Router/Request'
2
+ import { Response } from '.'
3
+ import { GlobalMiddleware, RequestHandler } from '..'
4
+
5
+ export abstract class HttpMiddleware extends GlobalMiddleware {
6
+ abstract handle(request: Request, next: RequestHandler): Promise<Response>
7
+ }