@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,151 @@
1
+ import { App, HttpRequest, HttpResponse } from 'uWebSockets.js'
2
+ import { Http, Request, Response } from '@Typetron/Router/Http'
3
+ import { Buffer } from 'buffer'
4
+ import { IncomingHttpHeaders } from 'http'
5
+ import * as console from 'console'
6
+ import busboy, { FileInfo } from 'busboy'
7
+ import { File } from '@Typetron/Storage'
8
+ import { Readable } from 'stream'
9
+ import os from 'os'
10
+ import fs from 'fs'
11
+
12
+ async function handleRequest(serverResponse: HttpResponse, serverRequest: HttpRequest, handler: (request: Request) => Promise<Response>) {
13
+ serverResponse.onAborted(() => {
14
+ console.log('uNetworking response aborted ->', serverResponse)
15
+ })
16
+
17
+ try {
18
+ const request = new Request(serverRequest.getUrl(), serverRequest.getMethod() as Http.Method, serverRequest.getQuery())
19
+ const headers: Record<string, string> = {}
20
+
21
+ serverRequest.forEach((name, value) => headers[name] = value)
22
+ request.setHeadersLoader(() => headers)
23
+ request.getHeader = <T extends string | string[] | undefined>(name: keyof IncomingHttpHeaders | string): T => {
24
+ return serverRequest.getHeader(name.toString()) as T
25
+ }
26
+
27
+ if (request.method.toLowerCase() !== Http.Method.GET.toLowerCase()) {
28
+ if (request.isMultipartRequest()) {
29
+ [request.body, request.files] = await readForm(serverResponse, headers)
30
+ } else {
31
+ request.body = await readJson(serverResponse, headers['content-type'])
32
+ }
33
+
34
+ // const overwrittenMethod = (request.body as Record<string, string | undefined>)[Request.methodField] || ''
35
+ // request.method = Http.Method[overwrittenMethod.toUpperCase() as Http.Method] || request.method
36
+ }
37
+
38
+ const response = await handler(request)
39
+ let content = response.body as string | object | number | Array<any> | Buffer
40
+
41
+ serverResponse.writeStatus(response.status.toString())
42
+
43
+ if (!(content instanceof Buffer)) {
44
+ if (content instanceof Object) {
45
+ content = JSON.stringify(content)
46
+ serverResponse.writeHeader('Content-Type', 'application/json')
47
+ }
48
+ content = String(content ?? '')
49
+ }
50
+
51
+ Object.keys(response.headers).forEach(header => {
52
+ const headerValue = response.headers[header]
53
+ if (headerValue) {
54
+ serverResponse.writeHeader(header, String(headerValue))
55
+ }
56
+ })
57
+ serverResponse.end(content)
58
+ } catch (error) {
59
+ console.log('error ->', error)
60
+ }
61
+ }
62
+
63
+ export function uNetworkingServer(port: number, handler: (request: Request) => Promise<Response>) {
64
+ const app = App({ // or maybe SSLApp?
65
+ // key_file_name: 'misc/key.pem',
66
+ // cert_file_name: 'misc/cert.pem',
67
+ // passphrase: '1234'
68
+ })
69
+ .any('*', async (response, request) => {
70
+ await handleRequest(response, request, handler)
71
+ })
72
+ .any('/*', async (response, request) => {
73
+ await handleRequest(response, request, handler)
74
+ })
75
+ .listen(port, (token) => {
76
+ if (!token) {
77
+ console.log('Failed to listen to port ' + port)
78
+ }
79
+ })
80
+ }
81
+
82
+ /* Helper function for reading a posted JSON body */
83
+ function readJson(response: HttpResponse, contentType: string) {
84
+ let buffer: Buffer
85
+ return new Promise<object | string | undefined>((resolve, reject) => {
86
+ response.onData((ab, isLast) => {
87
+ const chunk = Buffer.from(ab)
88
+ if (!isLast) {
89
+ buffer = Buffer.concat(buffer ? [buffer, chunk] : [chunk])
90
+ return
91
+ }
92
+
93
+ const content = (buffer ? Buffer.concat([buffer, chunk]) : chunk)
94
+ if (contentType === 'application/json') {
95
+ try {
96
+ const json = JSON.parse(content.toString())
97
+ resolve(json)
98
+ } catch (e) {
99
+ resolve({})
100
+ }
101
+ } else {
102
+ resolve(content)
103
+ }
104
+
105
+ })
106
+ })
107
+ }
108
+
109
+ function readForm(response: HttpResponse, headers: any): Promise<[string | object, Record<string, File | File[]>]> {
110
+ return new Promise((resolve, reject) => {
111
+ const formDataParser = busboy({headers})
112
+ const files: Record<string, File | File[]> = {}
113
+ const content: Record<string, string> = {}
114
+
115
+ formDataParser.on('file', function(fieldName: string, file: Readable, {filename, encoding, mimeType}: FileInfo) {
116
+ const extension = filename.split('.').pop()
117
+ const fileInstance = new File(`upload_${new Date().getTime().toString()}_${String.randomAlphaNum(9)}.${extension}`)
118
+
119
+ fileInstance.originalName = filename
120
+ fileInstance.directory = os.tmpdir()
121
+ fileInstance.saved = true
122
+
123
+ const stream = fs.createWriteStream(fileInstance.path)
124
+ stream.on('error', reject)
125
+
126
+ file.on('data', function(data) {
127
+ stream.write(data)
128
+ })
129
+ file.on('end', function() {
130
+ stream.end()
131
+
132
+ const field = files[fieldName]
133
+ files[fieldName] = field instanceof Array ? [...field, fileInstance] : field ? [field, fileInstance] : fileInstance
134
+ })
135
+ })
136
+ formDataParser.on('field', function(fieldName, value) {
137
+ content[fieldName] = value
138
+ })
139
+ formDataParser.on('finish', function() {
140
+ resolve([content, files])
141
+ })
142
+
143
+ response.onData((ab, isLast) => {
144
+ formDataParser.write(Buffer.from(ab.slice(0)))
145
+ if (isLast) {
146
+ formDataParser.end()
147
+ }
148
+ })
149
+ })
150
+
151
+ }
@@ -0,0 +1,82 @@
1
+ import { Container, Inject } from '../../Container'
2
+ import { RequestHandler, Router } from '../../Router'
3
+ import { Response } from '../Http'
4
+ import { Request as BaseRequest } from '../Request'
5
+ import { Abstract, Constructor, Type } from '../../Support'
6
+ import { WebsocketRoute } from './WebsocketRoute'
7
+ import { Request } from '@Typetron/Router/Websockets/Request'
8
+ import { WebsocketMiddleware } from '@Typetron/Router/Websockets/Middleware'
9
+
10
+ export class Handler {
11
+ @Inject()
12
+ router: Router
13
+
14
+ onOpen?: WebsocketRoute
15
+ onClose?: WebsocketRoute
16
+
17
+ addAction(
18
+ name: string,
19
+ controller: Constructor,
20
+ actionName: string,
21
+ parametersTypes: (Type<(...args: any[]) => any> | FunctionConstructor)[] = [],
22
+ middleware: Abstract<WebsocketMiddleware>[] = []
23
+ ) {
24
+ const action = new WebsocketRoute(
25
+ name,
26
+ controller,
27
+ actionName,
28
+ parametersTypes,
29
+ middleware
30
+ )
31
+ if (this.router.actions.has(name)) {
32
+ throw new Error(`There is already an action with the same name: '${name}'`)
33
+ }
34
+ this.router.actions.set(name, action)
35
+ return action
36
+ }
37
+
38
+ async handle<T = unknown>(container: Container, request: Request): Promise<Response<T>> {
39
+
40
+ container.set(BaseRequest, request)
41
+ container.set(Request, request)
42
+
43
+ let stack: RequestHandler = async () => {
44
+ const route = this.router.actions.get(request.name)
45
+
46
+ if (!route) {
47
+ throw new Error(`Action '${request.name}' not found`)
48
+ }
49
+
50
+ container.set(WebsocketRoute, route)
51
+
52
+ let routeStack: RequestHandler = async () => {
53
+ const content = await route.run(container, request.parameters)
54
+
55
+ if (content instanceof Response) {
56
+ return content
57
+ }
58
+
59
+ return Response.ok(content)
60
+ }
61
+
62
+ route.middleware.forEach(middlewareClass => {
63
+ const middleware = container.get(middlewareClass)
64
+ routeStack = middleware.handle.bind(middleware, request, routeStack)
65
+ })
66
+
67
+ return await routeStack(request)
68
+ }
69
+
70
+ this.router.middleware.global?.forEach(middlewareClass => {
71
+ const middleware = container.get(middlewareClass)
72
+ stack = middleware.handle.bind(middleware, request, stack)
73
+ })
74
+
75
+ this.router.middleware.websocket?.forEach(middlewareClass => {
76
+ const middleware = container.get(middlewareClass)
77
+ stack = middleware.handle.bind(middleware, request, stack)
78
+ })
79
+
80
+ return await stack(request) as Response<T>
81
+ }
82
+ }
@@ -0,0 +1,7 @@
1
+ import { Request } from '@Typetron/Router/Websockets/Request'
2
+ import { GlobalMiddleware, RequestHandler } from '@Typetron/Router'
3
+ import { Response } from '@Typetron/Router/Http' // TODO remove any Http reference from websockets
4
+
5
+ export abstract class WebsocketMiddleware extends GlobalMiddleware {
6
+ abstract handle(request: Request, next: RequestHandler): Promise<Response>
7
+ }
@@ -0,0 +1,3 @@
1
+ import { Request as BaseRequest } from '@Typetron/Router/Request'
2
+
3
+ export class Request extends BaseRequest {}
@@ -0,0 +1,61 @@
1
+ import { Container } from '../../Container'
2
+ import { AnyFunction, Constructor, Type } from '../../Support'
3
+ import { ControllerMetadata, MethodMetadata } from '../Metadata'
4
+ import { Route } from '@Typetron/Router'
5
+
6
+ export class WebsocketRoute extends Route {
7
+
8
+ async run(container: Container, requestParameters: Record<string, unknown>): Promise<object | string> {
9
+ const controller = await container.get(this.controller)
10
+
11
+ try {
12
+ const metadata: MethodMetadata | undefined = ControllerMetadata.get(this.controller).actions[this.controllerMethod]
13
+ const parameters = await this.resolveParameters(
14
+ requestParameters,
15
+ this.parametersTypes,
16
+ metadata?.parametersOverrides ?? [],
17
+ container
18
+ )
19
+
20
+ for await (const guardClass of this.guards) {
21
+ const guard = container.get(guardClass)
22
+ if (!await guard.condition(...parameters)) {
23
+ guard.onFail()
24
+ }
25
+ }
26
+
27
+ return (controller[this.controllerMethod as keyof Constructor] as AnyFunction).apply(controller, parameters)
28
+ } catch (unknownError) {
29
+ const error = unknownError as Error
30
+ error.stack = `Controller: ${controller.constructor.name}.${this.controllerMethod} \n at ` + error.stack
31
+ throw error
32
+ }
33
+ }
34
+
35
+ private async resolveParameters(
36
+ requestParameters: Record<string, unknown>,
37
+ parametersTypes: (Type<AnyFunction> | FunctionConstructor)[],
38
+ overrides: AnyFunction[],
39
+ container: Container
40
+ ) {
41
+ let parameterIndex = 0
42
+ // tslint:disable-next-line:no-any
43
+ const routeParameters: any[] = Object.values(requestParameters)
44
+ return parametersTypes.mapAsync(async (parameter, index) => {
45
+ const newValueFunction = overrides[index]
46
+ if (newValueFunction) {
47
+ return await newValueFunction.call(undefined, container)
48
+ }
49
+ if (parameter.name === 'String') {
50
+ return routeParameters[parameterIndex++]
51
+ }
52
+ if (parameter.name === 'Number') {
53
+ const value = routeParameters[parameterIndex++]
54
+ return value === undefined || value === 'undefined' ? undefined : Number(value)
55
+ }
56
+ return container.get(parameter, [routeParameters[parameterIndex++]])
57
+ })
58
+ }
59
+
60
+ }
61
+
@@ -0,0 +1,57 @@
1
+ import { WebSocket as uWebSocket } from 'uWebSockets.js'
2
+ import { ActionResponse, WebsocketMessageStatus } from './types'
3
+ import { Container } from '@Typetron/Container'
4
+
5
+ export * from './Handler'
6
+ export * from './types'
7
+
8
+ export class WebSocket {
9
+
10
+ public connection: uWebSocket<any> & {container: Container}
11
+ id?: number | string
12
+
13
+ constructor(connection: uWebSocket<any>, public container: Container) {
14
+ this.connection = Object.assign(connection, {container})
15
+ this.reset(container)
16
+ }
17
+
18
+ subscribe(topic: string) {
19
+ this.connection.subscribe(topic)
20
+ }
21
+
22
+ unsubscribe(topic: string) {
23
+ this.connection.unsubscribe(topic)
24
+ }
25
+
26
+ reset(container?: Container) {
27
+ this.connection.container = container || this.connection.container.parent?.createChildContainer() || new Container()
28
+ this.connection.container.set(WebSocket, this)
29
+ }
30
+
31
+ // tslint:disable-next-line:no-any
32
+ publish(topic: string, action: string, body?: unknown) {
33
+ const sentResponse: ActionResponse<unknown> = {
34
+ action,
35
+ message: body,
36
+ status: WebsocketMessageStatus.OK,
37
+ }
38
+ this.connection.publish(topic, JSON.stringify(sentResponse), false, true)
39
+ }
40
+
41
+ // tslint:disable-next-line:no-any
42
+ publishAndSend(topic: string, action: string, body?: unknown) {
43
+ this.publish(topic, action, body)
44
+ this.send(action, body)
45
+ }
46
+
47
+ // tslint:disable-next-line:no-any
48
+ send(action: string, body?: unknown) {
49
+ const sentResponse: ActionResponse<unknown> = {
50
+ action,
51
+ message: body,
52
+ status: WebsocketMessageStatus.OK,
53
+ }
54
+ this.connection.send(JSON.stringify(sentResponse), false, true)
55
+ }
56
+ }
57
+
@@ -0,0 +1,23 @@
1
+ export enum WebsocketMessageStatus {
2
+ OK = 'OK',
3
+ Error = 'Error',
4
+ }
5
+
6
+ export interface Action<T> {
7
+ action: string
8
+ message?: T
9
+ }
10
+
11
+ export interface ActionRequestMessage {
12
+ parameters?: (string | number)[]
13
+ // tslint:disable-next-line:no-any
14
+ body?: any
15
+ }
16
+
17
+ export type ActionRequest = Action<ActionRequestMessage>
18
+
19
+ export interface ActionResponse<T> extends Action<T> {
20
+ status: WebsocketMessageStatus
21
+ }
22
+
23
+ export type ActionErrorResponse<T extends string = string> = ActionResponse<{message: T, stack: string}>
@@ -0,0 +1,6 @@
1
+ export * from './Route'
2
+ export * from './Router'
3
+ export * from './Middleware'
4
+ export * from './Decorators'
5
+ export * from './RouteNotFoundError'
6
+ export * from './BaseController'
@@ -0,0 +1,30 @@
1
+ import * as path from 'path'
2
+
3
+ export class File {
4
+ // content?: string | Buffer
5
+ originalName?: string
6
+
7
+ saved = false
8
+
9
+ constructor(public name: string, public directory?: string) {
10
+ }
11
+
12
+ get extension() {
13
+ return this.guessExtension()
14
+ }
15
+
16
+ get path() {
17
+ return path.join(this.directory || '', this.name)
18
+ }
19
+
20
+ guessExtension() {
21
+ const fileParts = this.name.split('.')
22
+ if (fileParts.length >= 1) {
23
+ return fileParts.pop()
24
+ }
25
+ }
26
+
27
+ toString() {
28
+ return this.name
29
+ }
30
+ }
@@ -0,0 +1,19 @@
1
+ import { File } from './File'
2
+
3
+ export class Image extends File {
4
+ // static fromBase64(base64: string): Image {
5
+ // const matches = base64.match(/data:image\/([a-zA-Z]*);base64,([^\\"]*)/)
6
+ // if (!matches) {
7
+ // throw new InvalidImage()
8
+ // }
9
+ // if (matches.length !== 3) {
10
+ // throw InvalidImage
11
+ // }
12
+ // const extension = matches[1]
13
+ // const data = matches[2]
14
+ //
15
+ // const image = new this('.' + extension)
16
+ // image.content = Buffer.from(data, 'base64')
17
+ // return image
18
+ // }
19
+ }
@@ -0,0 +1,3 @@
1
+ export class InvalidImage extends Error {
2
+ constructor() {super('The image has an invalid data format') }
3
+ }
@@ -0,0 +1,98 @@
1
+ import * as fileSystemDeprecated from 'fs'
2
+ import { File } from './File'
3
+ import * as path from 'path'
4
+ import * as fileSystem from 'node:fs/promises'
5
+
6
+ export class Storage {
7
+ files(directory: string, recursively = false): File[] {
8
+ return fileSystemDeprecated
9
+ .readdirSync(directory) // TODO make this async
10
+ .reduce((files, name) => {
11
+ const filePath = path.join(directory, name)
12
+ const stats = fileSystemDeprecated.statSync(filePath)
13
+ if (stats.isFile()) {
14
+ const file = new File(name)
15
+ file.directory = directory
16
+ files.push(file)
17
+ }
18
+
19
+ if (recursively && stats.isDirectory()) {
20
+ files = files.concat(this.files(filePath))
21
+ }
22
+ return files
23
+ }, [] as File[])
24
+ }
25
+
26
+ async read(filePath: string): Promise<Buffer> {
27
+ return fileSystem.readFile(filePath)
28
+ }
29
+
30
+ async exists(filePath: string): Promise<boolean> {
31
+ try {
32
+ await fileSystem.access(filePath)
33
+ return true
34
+ } catch (error) {
35
+ return false
36
+ }
37
+ }
38
+
39
+ async save(file: File, directory: string = '', name?: string | number): Promise<File> {
40
+ const newPath = path.join(directory, file.name)
41
+ if (!file.name) {
42
+ file.name = String(name || this.generateRandomFileName() + '.' + file.extension)
43
+ }
44
+ if (directory && !(await this.exists(directory))) {
45
+ await this.makeDirectory(directory)
46
+ }
47
+ return new Promise(async (resolve, reject) => {
48
+ fileSystemDeprecated
49
+ .createReadStream(file.path)
50
+ .on('error', reject)
51
+ .pipe(fileSystemDeprecated.createWriteStream(newPath))
52
+ .on('finish', () => resolve(file))
53
+ .on('error', reject)
54
+ })
55
+ }
56
+
57
+ async put(filePath: string, content: string | Buffer, options?: any): Promise<File> {
58
+ const file = new File(path.basename(filePath))
59
+ file.directory = path.dirname(filePath)
60
+
61
+ if (file.directory && !(await this.exists(file.directory))) {
62
+ await this.makeDirectory(file.directory)
63
+ }
64
+
65
+ await fileSystem.writeFile(filePath, content, options)
66
+ return file
67
+ }
68
+
69
+ async makeDirectory(directory: string): Promise<void> {
70
+ await fileSystem.mkdir(directory, {recursive: true})
71
+ }
72
+
73
+ async deleteDirectory(directory: string): Promise<void> {
74
+ if (await this.exists(directory)) {
75
+ await fileSystem.rm(directory, {recursive: true})
76
+ }
77
+ }
78
+
79
+ async delete(filePath: string): Promise<void> {
80
+ if (!(await this.exists(filePath))) {
81
+ return
82
+ }
83
+
84
+ if ((await fileSystem.stat(filePath)).isDirectory()) {
85
+ throw new Error('Can not delete because this path leads to a directory and not a file.')
86
+ }
87
+
88
+ await fileSystem.unlink(filePath)
89
+ }
90
+
91
+ private generateRandomFileName(): string {
92
+ const stringDomain =
93
+ 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
94
+ return (
95
+ String.random(13, stringDomain) + '-' + new Date().getTime().toString()
96
+ )
97
+ }
98
+ }
@@ -0,0 +1,4 @@
1
+ export * from './File'
2
+ export * from './Storage'
3
+ export * from './Image'
4
+ export * from './utils'
@@ -0,0 +1,18 @@
1
+ import { InvalidImage } from '@Typetron/Storage/InvalidImage'
2
+
3
+ export function fromBase64(base64: string): {extension: string, content: Buffer} {
4
+ const matches = base64.match(/data:image\/([a-zA-Z]*);base64,([^\\"]*)/)
5
+ if (!matches) {
6
+ throw new InvalidImage()
7
+ }
8
+ if (matches.length !== 3) {
9
+ throw InvalidImage
10
+ }
11
+ const extension = matches[1]
12
+ const data = matches[2]
13
+
14
+ return {
15
+ content: Buffer.from(data, 'base64'),
16
+ extension: extension
17
+ }
18
+ }