@typetron/framework 0.5.3 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,184 @@
1
+ export {}
2
+
3
+ type ArrayItemCallback<T, R = T> = (item: T, index: number, array: T[]) => R;
4
+
5
+ declare global {
6
+ interface Array<T> {
7
+ empty(): boolean;
8
+
9
+ random(): T;
10
+
11
+ randomIndex(): number;
12
+
13
+ pluck<K extends keyof T>(this: T[], property: K): T[K][];
14
+
15
+ mapAsync<U>(callback: ArrayItemCallback<T, Promise<U>>, thisArg?: this): Promise<U[]>;
16
+
17
+ forEachAsync(callback: ArrayItemCallback<T, void>, thisArg?: this): Promise<void>;
18
+
19
+ // tslint:disable-next-line:no-any
20
+ findWhere<K extends keyof T>(this: T[], property: K, value: T[K] | ArrayItemCallback<T, unknown>): T | undefined;
21
+
22
+ first(this: T[], defaultValue?: T): T | undefined;
23
+
24
+ last(this: T[], defaultValue?: T): T | undefined;
25
+
26
+ remove<T>(this: T[], ...items: T[]): void;
27
+
28
+ where<K extends keyof T>(this: T[], property: K, value?: T[K]): T[];
29
+
30
+ whereNot<K extends keyof T>(this: T[], property: K, value?: T[K]): T[];
31
+
32
+ whereIn<K extends keyof T>(this: T[], property: K, value: T[K][]): T[];
33
+
34
+ whereNotIn<K extends keyof T>(this: T[], property: K, value: T[K][]): T[];
35
+
36
+ groupBy<K extends keyof T>(this: T[], property: K): Record<K, T[]>;
37
+
38
+ sortBy<K extends keyof T>(this: T[], key: K, order?: 'ASC' | 'DESC' | 1 | -1): T[]
39
+
40
+ groupBy<U extends string | number | symbol>(this: T[], callback: ArrayItemCallback<T, U>): Record<U, T[]>;
41
+
42
+ unique<K extends keyof T>(this: T[], key?: K | ((item: T, index: number) => unknown)): T[];
43
+
44
+ sum<T extends number>(this: T[]): number;
45
+
46
+ sum<K extends keyof T, U extends number>(key: K | ArrayItemCallback<T, U>): U;
47
+
48
+ whenNotEmpty(this: T[], callback: (values: T[]) => void): this;
49
+
50
+ whenEmpty(callback: () => void): this;
51
+ }
52
+ }
53
+
54
+ Array.prototype.empty = function() {
55
+ return !this.length
56
+ }
57
+
58
+ Array.prototype.random = function() {
59
+ return this[Math.randomInt(0, this.length - 1)]
60
+ }
61
+
62
+ Array.prototype.randomIndex = function() {
63
+ return Math.randomInt(0, this.length - 1)
64
+ }
65
+
66
+ // tslint:disable-next-line:no-any
67
+ Array.prototype.findWhere = function <T, K extends keyof T>(property: K, value: T[K] | ArrayItemCallback<T, unknown>) {
68
+ return this.find(item => item[property] === value)
69
+ }
70
+
71
+ Array.prototype.pluck = function <T, K extends keyof T, U>(property: K) {
72
+ return this.map((item: T) => item[property] as unknown as U)
73
+ }
74
+
75
+ Array.prototype.remove = function <T>(...items: T[]) {
76
+ return items.map(item => {
77
+ const index = this.indexOf(item)
78
+ if (index === -1) {
79
+ return
80
+ }
81
+ this.splice(this.indexOf(item), 1)
82
+ })
83
+ }
84
+
85
+ Array.prototype.where = function <T, K extends keyof T>(property: K, value?: T[K]) {
86
+ return this.filter(item => value !== undefined ? item[property] === value : item[property])
87
+ }
88
+
89
+ Array.prototype.whereNot = function <T, K extends keyof T>(property: K, value?: T[K]) {
90
+ return this.filter(item => value !== undefined ? item[property] !== value : !item[property])
91
+ }
92
+
93
+ Array.prototype.whereIn = function <T, K extends keyof T>(property: K, values: (T[K])[]) {
94
+ return this.filter(item => values.includes(item[property]))
95
+ }
96
+
97
+ Array.prototype.whereNotIn = function <T, K extends keyof T>(property: K, values: (T[K])[]) {
98
+ return this.filter(item => !values.includes(item[property]))
99
+ }
100
+
101
+ Array.prototype.first = function(defaultValue = undefined) {
102
+ const [first] = this
103
+ return first || defaultValue
104
+ }
105
+
106
+ Array.prototype.last = function(defaultValue = undefined) {
107
+ return this[this.length - 1] || defaultValue
108
+ }
109
+
110
+ Array.prototype.groupBy = function <T, K extends keyof T, U>(property: K | ArrayItemCallback<T, U>) {
111
+ const callback = property instanceof Function ? property : (item: T) => item[property]
112
+ return this.reduce((accumulator, item, index, array) => {
113
+ const key = callback(item, index, array);
114
+ (accumulator[key] = accumulator[key] || []).push(item)
115
+ return accumulator
116
+ }, {})
117
+ }
118
+
119
+ Array.prototype.sortBy = function <T, K extends keyof T>(this: T[], key: K, order?: 'ASC' | 'DESC' | 1 | -1): T[] {
120
+ const o = (order === 'ASC' ? 1 : order === 'DESC' ? -1 : order) || 1
121
+ return this.sort((a, b) => a[key] > b[key] ? 1 * o : b[key] > a[key] ? -1 * o : 0)
122
+ }
123
+
124
+ Array.prototype.forEachAsync = async function <T>(callback: ArrayItemCallback<T, void>, thisArg: T[]) {
125
+ for (let index = 0; index < this.length; index++) {
126
+ await callback.call(thisArg, this[index], index, this)
127
+ }
128
+ }
129
+
130
+ Array.prototype.mapAsync = async function <T, U>(callback: ArrayItemCallback<T, Promise<U>>, thisArg: T[]) {
131
+ if (!this.length) {
132
+ return []
133
+ }
134
+ const promises = []
135
+ for (let index = 0; index < this.length; index++) {
136
+ promises.push(callback.call(thisArg, this[index], index, this))
137
+ }
138
+ return Promise.all(promises)
139
+ }
140
+
141
+ Array.prototype.unique = function <T, K extends keyof T>(property?: K | ((item: T, index: number) => unknown)): T[] {
142
+ if (!property) {
143
+ return [...new Set(this)] as T[]
144
+ }
145
+
146
+ let callback: (item: T, index: number, list: T[]) => unknown
147
+ if (typeof property === 'function') {
148
+ callback = property
149
+ } else {
150
+ callback = (item) => item[property]
151
+ }
152
+
153
+ const exists = new Set
154
+ return this.filter((item, index) => {
155
+ let id: unknown
156
+ if (!exists.has(id = callback(item, index, this))) {
157
+ exists.add(id)
158
+ return true
159
+ }
160
+ })
161
+ }
162
+
163
+ Array.prototype.sum = function <T, K extends keyof T, U extends number>(property?: K | ArrayItemCallback<T, U>) {
164
+ const callback = property instanceof Function ? property : (item: T) => property ? item[property] : item
165
+ return this.reduce((accumulator, item, index, array) => {
166
+ return accumulator + callback(item, index, array)
167
+ }, 0)
168
+ }
169
+
170
+ Array.prototype.whenNotEmpty = function <T>(callback: (values: T[]) => void) {
171
+ if (!this.empty()) {
172
+ callback(this)
173
+ }
174
+
175
+ return this
176
+ }
177
+
178
+ Array.prototype.whenEmpty = function(callback: () => void) {
179
+ if (this.empty()) {
180
+ callback()
181
+ }
182
+
183
+ return this
184
+ }
@@ -0,0 +1,12 @@
1
+ export {}
2
+ declare global {
3
+ interface Math {
4
+ randomInt(min?: number, max?: number): number;
5
+ }
6
+ }
7
+
8
+ Math.randomInt = function (min: number = Number.MIN_SAFE_INTEGER, max: number = Number.MAX_SAFE_INTEGER) {
9
+ min = Math.ceil(min)
10
+ max = Math.floor(max)
11
+ return Math.floor(Math.random() * (max - min + 1)) + min
12
+ }
@@ -0,0 +1,31 @@
1
+ import { Constructor } from './index'
2
+
3
+ export function MetadataKey(key: string) {
4
+ return class Metadata {
5
+ // tslint:disable-next-line:no-any
6
+ static get<T extends Metadata>(this: typeof Metadata & Constructor<T>, target: any, defaultValue?: T): T {
7
+ // This handles the copy of dependencies from the parent to the child.
8
+ // It's hacky because `newInjectable.dependencies` is a property of InjectableMetadata
9
+ if (target.__proto__._metadata_?.['injectable'] && target.__proto__._metadata_ === target._metadata_) {
10
+ const newInjectable = new this
11
+ Object.assign(newInjectable, target.__proto__._metadata_['injectable'])
12
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
13
+ // @ts-ignore
14
+ newInjectable.dependencies = {...target.__proto__._metadata_['injectable'].dependencies}
15
+ target['_metadata_'] = {injectable: newInjectable}
16
+ return newInjectable
17
+ }
18
+ return (target['_metadata_'] || (target['_metadata_'] = {[key]: new this}))[key]
19
+ || (target['_metadata_'][key] = defaultValue ?? new this)
20
+ }
21
+
22
+ static set<T extends Metadata>(this: typeof Metadata & Constructor<T>, metadata: T, target: any) {
23
+ const metadataBag = target['_metadata_'] || (target['_metadata_'] = {})
24
+ return metadataBag[key] = metadata
25
+ }
26
+ }
27
+ }
28
+
29
+ export class ParametersTypesMetadata<T> extends MetadataKey('design:paramtypes') {
30
+ parameters: T
31
+ }
@@ -0,0 +1,38 @@
1
+ export {}
2
+ declare global {
3
+ interface StringConstructor {
4
+ random(length?: number, stringDomain?: string): string;
5
+
6
+ randomAlphaNum(length?: number): string;
7
+ }
8
+
9
+ interface String {
10
+ capitalize(): this;
11
+
12
+ limit(length: number, end?: string): this;
13
+ }
14
+ }
15
+
16
+ const string = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~'
17
+ String.random = function(length = Math.randomInt(1, 15), stringDomain = string) {
18
+ const characters = stringDomain.split('')
19
+ let word = ''
20
+ for (let i = 0; i < length; i++) {
21
+ word += characters.random()
22
+ }
23
+ return word
24
+ }
25
+ String.randomAlphaNum = function(length: number) {
26
+ return this.random(length, 'abcdefghijklmnopqrstuvwxyz1234567890')
27
+ }
28
+
29
+ String.prototype.capitalize = function () {
30
+ return this.charAt(0).toUpperCase() + this.slice(1)
31
+ }
32
+
33
+ String.prototype.limit = function (limit: number, end = '...') {
34
+ if (limit < this.length) {
35
+ return this.substring(0, limit - end.length) + end
36
+ }
37
+ return this
38
+ }
@@ -0,0 +1,19 @@
1
+ import './Array'
2
+ import './String'
3
+ import './Math'
4
+ import './utils'
5
+
6
+ export type KeysOfType<T, Condition> = { [P in keyof T]: T[P] extends Condition ? P : never }[keyof T]
7
+ export type ChildKeys<Child, Parent> = Exclude<keyof Child, keyof Parent>
8
+ export type ChildObject<Child, Parent> = Omit<Child, keyof Parent>
9
+
10
+ // tslint:disable-next-line:no-any
11
+ export type Type<T> = new(...args: any[]) => T
12
+ export type AnyFunction<T = any> = (...args: any[]) => T
13
+
14
+ export interface Abstract<T> {
15
+ prototype: T;
16
+ }
17
+
18
+ // tslint:disable-next-line:no-any
19
+ export type Constructor<T = object> = new(...args: any[]) => T
@@ -0,0 +1,7 @@
1
+ export function JSONSafeParse<T>(text?: string) {
2
+ try {
3
+ return text ? JSON.parse(text) as T : undefined
4
+ } catch (error) {
5
+ return undefined
6
+ }
7
+ }
@@ -0,0 +1,193 @@
1
+ import '../Support/Math'
2
+ import { ErrorHandlerInterface, Handler as HTTPHandler, Http, HttpError, Request as HttpRequest, Response } from '../Router/Http'
3
+ import { IncomingHttpHeaders } from 'http'
4
+ import { Application, DatabaseProvider } from '../Framework'
5
+ import { Router } from '../Router'
6
+ import { Auth, User } from '../Framework/Auth'
7
+ import { ActionRequest } from '../Router/Websockets/types'
8
+ import { Handler as WebsocketsHandler, WebSocket } from '../Router/Websockets'
9
+ import { Container } from '../Container'
10
+ import { ID } from '@Typetron/Database'
11
+ import { anything, instance, mock, when } from 'ts-mockito'
12
+ import { Crypt } from '@Typetron/Encryption'
13
+ import { Request } from '@Typetron/Router/Request'
14
+ import { HttpRoute } from '@Typetron/Router/Http/HttpRoute'
15
+
16
+ export abstract class TestCase {
17
+ static app: Application
18
+ app: Container
19
+
20
+ // tslint:disable-next-line:no-any
21
+ actionListeners = new Map<string, (response: any) => void>()
22
+
23
+ providersNeedingReboot = [
24
+ DatabaseProvider
25
+ ]
26
+
27
+ abstract bootstrapApp(): Promise<Application>
28
+
29
+
30
+ async before() {
31
+ if (!TestCase.app) {
32
+ TestCase.app = await this.bootstrapApp()
33
+ }
34
+ await TestCase.app.registerProviders(this.providersNeedingReboot)
35
+ this.app = TestCase.app.createChildContainer()
36
+
37
+ const crypt = mock(Crypt)
38
+ when(crypt.hash(anything(), anything())).thenCall(value => `testing-${value}`)
39
+ when(crypt.compare(anything(), anything())).thenCall((first, second) => Promise.resolve(`testing-${first}` === second))
40
+
41
+ this.app.set(Crypt, instance(crypt))
42
+ }
43
+
44
+ async after() {}
45
+
46
+ async loginById(id: ID) {
47
+ await this.app.get(Auth).loginById(id)
48
+ }
49
+
50
+ /**
51
+ * @deprecated
52
+ */
53
+ async login(user: User) {
54
+ await this.loginById(user.id)
55
+ }
56
+
57
+ async actingAs(user: User) {
58
+ await this.app.get(Auth).loginUser(user)
59
+ }
60
+
61
+ get<T extends string | object | undefined>(routeName: string, content = {}, headers: IncomingHttpHeaders = {}): Promise<Response<T>> {
62
+ return this.request(Http.Method.GET, routeName, content, headers) as Promise<Response<T>>
63
+ }
64
+
65
+ post<T extends string | object | undefined>(
66
+ route: string | [string, Record<string, string | number>],
67
+ content = {},
68
+ headers: IncomingHttpHeaders = {}
69
+ ): Promise<Response<T>> {
70
+ return this.request(Http.Method.POST, route, content, headers) as Promise<Response<T>>
71
+ }
72
+
73
+ patch<T extends string | object | undefined>(
74
+ route: string | [string, Record<string, string | number>],
75
+ content = {},
76
+ headers: IncomingHttpHeaders = {}
77
+ ): Promise<Response<T>> {
78
+ return this.request(Http.Method.PATCH, route, content, headers) as Promise<Response<T>>
79
+ }
80
+
81
+ put<T extends string | object | undefined>(
82
+ route: string | [string, Record<string, string | number>],
83
+ content = {},
84
+ headers: IncomingHttpHeaders = {}
85
+ ): Promise<Response<T>> {
86
+ return this.request(Http.Method.PUT, route, content, headers) as Promise<Response<T>>
87
+ }
88
+
89
+ delete<T extends string | object | undefined>(
90
+ route: string | [string, Record<string, string | number>],
91
+ content = {},
92
+ headers: IncomingHttpHeaders = {}
93
+ ): Promise<Response<T>> {
94
+ return this.request(Http.Method.DELETE, route, content, headers) as Promise<Response<T>>
95
+ }
96
+
97
+ async action<T extends string | object | undefined>(action: string, content?: ActionRequest['message']): Promise<Response<T>> {
98
+ const socketMock = mock(WebSocket)
99
+
100
+ when(socketMock.publishAndSend(anything(), anything(), anything())).thenCall((topic, calledActon, message) => {
101
+ const callback = this.actionListeners.get(calledActon)
102
+ callback?.(message)
103
+ })
104
+
105
+ when(socketMock.send(anything(), anything())).thenCall((calledActon, message) => {
106
+ const callback = this.actionListeners.get(calledActon)
107
+ callback?.(message)
108
+ })
109
+
110
+ const container = this.app.createChildContainer()
111
+ container.set(WebSocket, instance(socketMock))
112
+
113
+ const request = new Request(action, content?.body)
114
+
115
+ // TODO find a way to save parameters so they can be retrieved easily in the Resolvers (check EntityResolver for example)
116
+ content?.parameters?.forEach(parameter => {
117
+ request.parameters[String.random(15)] = parameter
118
+ })
119
+
120
+ try {
121
+ return await this.app.get(WebsocketsHandler).handle(container, request)
122
+ } catch (error) {
123
+ if (error instanceof HttpError) {
124
+ const errorResponse = await this.app.get(ErrorHandlerInterface).handle(error)
125
+ console.error(errorResponse.body)
126
+ return errorResponse as Response<T>
127
+ }
128
+ throw error
129
+ }
130
+ }
131
+
132
+ on<T>(action: string, callback: (response: T) => void) {
133
+ this.actionListeners.set(action, callback)
134
+ }
135
+
136
+ private async request(
137
+ method: Http.Method,
138
+ routeName: string | [string, Record<string, string | number>],
139
+ content = {},
140
+ headers: IncomingHttpHeaders = {}
141
+ ) {
142
+ let routeParameters = {}
143
+ if (typeof routeName !== 'string') {
144
+ [routeName, routeParameters] = routeName
145
+ }
146
+
147
+ const router = this.app.get(Router)
148
+ const route = router.routes.findWhere('name', routeName) as HttpRoute
149
+ if (!route) {
150
+ throw new Error(`Route '${routeName}' not found`)
151
+ }
152
+ const parameters = this.convertRouteParametersToString(routeParameters) || {}
153
+
154
+ const auth = this.app.get(Auth)
155
+ if (auth.id) {
156
+ const token = await auth.sign(auth.id)
157
+ headers.authorization = `Bearer ${token}`
158
+ }
159
+
160
+ const request = new HttpRequest(route.getUrl(parameters), method, {}, {}, content)
161
+
162
+ request.setHeadersLoader(() => {
163
+ return headers
164
+ })
165
+ request.getHeader = <T extends string | string[] | undefined>(name: keyof IncomingHttpHeaders | string): T => {
166
+ return headers[String(name).toLowerCase()] as T
167
+ }
168
+
169
+ let response: Response
170
+ try {
171
+ response = await this.app.get(HTTPHandler).handle(this.app as Application, request)
172
+
173
+ if (response.body instanceof Buffer) {
174
+ response.body = response.body.toString()
175
+ }
176
+
177
+ return response
178
+
179
+ } catch (error) {
180
+ if (error instanceof HttpError) {
181
+ return this.app.get(ErrorHandlerInterface).handle(error)
182
+ }
183
+ throw error
184
+ }
185
+ }
186
+
187
+ private convertRouteParametersToString(routeParameters: Record<string, string | number>) {
188
+ Object.keys(routeParameters).forEach(key => {
189
+ routeParameters[key] = String(routeParameters[key])
190
+ })
191
+ return routeParameters as Record<string, string>
192
+ }
193
+ }
@@ -0,0 +1,9 @@
1
+ import { RuleInterface } from './RuleInterface'
2
+
3
+ export abstract class Rule implements RuleInterface {
4
+ abstract identifier: string
5
+
6
+ abstract message(attribute: string, value: string | number | object | boolean | undefined): string;
7
+
8
+ abstract passes(attribute: string, value: string | number | object | boolean | undefined): boolean ;
9
+ }
@@ -0,0 +1,10 @@
1
+ import { RuleValue } from '.'
2
+
3
+ export interface RuleInterface {
4
+ identifier: string;
5
+
6
+ passes(attribute: string, value?: RuleValue): boolean;
7
+
8
+ message(attribute: string, value: RuleValue): string;
9
+ }
10
+
@@ -0,0 +1,20 @@
1
+ import { RuleInterface, RuleValue } from '..'
2
+ import { Rule } from '../Rule'
3
+ import { Type } from '@Typetron/Support'
4
+
5
+ export function Email(message?: string): Type<RuleInterface> {
6
+ return class extends Rule {
7
+ identifier = 'email'
8
+
9
+ private regex = /^(?:[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)])$/
10
+
11
+ passes(attribute: string, value: RuleValue): boolean {
12
+ return this.regex.test(String(value).trim())
13
+ }
14
+
15
+ message(attribute: string): string {
16
+ return message ?? `The ${attribute} must be a valid email address`
17
+ }
18
+ }
19
+
20
+ }
@@ -0,0 +1,21 @@
1
+ import { Rule, RuleInterface } from '@Typetron/Validation'
2
+ import { Type } from '@Typetron/Support'
3
+ import { File } from '@Typetron/Storage'
4
+
5
+ export function FileExtension(extensions: string[], message?: string): Type<RuleInterface> {
6
+ return class extends Rule {
7
+ identifier = 'fileExtension'
8
+
9
+ passes(attribute: string, value?: File): boolean {
10
+ return value?.extension ? extensions.includes(value.extension) : false
11
+ }
12
+
13
+ message(attribute: string, value?: File): string {
14
+ const messagePart = extensions.length === 1
15
+ ? `the '.${extensions.first()}' extension`
16
+ : `these extensions: ${extensions.map(extension => `.${extension}`).join(', ')}`
17
+
18
+ return message ?? `The ${attribute} must have ${messagePart}`
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,17 @@
1
+ import { RuleInterface, RuleValue } from '..'
2
+ import { Rule } from '../Rule'
3
+ import { Type } from '../../Support'
4
+
5
+ export function MaxLength(max: number, message?: string): Type<RuleInterface> {
6
+ return class extends Rule {
7
+ identifier = 'maxLength'
8
+
9
+ passes(attribute: string, value: RuleValue): boolean {
10
+ return Boolean(value && String(value).length <= max)
11
+ }
12
+
13
+ message(attribute: string): string {
14
+ return message ?? `The ${attribute} must have at most ${max} characters`
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,18 @@
1
+ import { RuleInterface } from '../RuleInterface'
2
+ import { RuleValue } from '..'
3
+ import { Type } from '../../Support'
4
+ import { Rule } from '../Rule'
5
+
6
+ export function MinLength(min: number, message?: string): Type<RuleInterface> {
7
+ return class extends Rule {
8
+ identifier = 'minLength'
9
+
10
+ passes(attribute: string, value: RuleValue): boolean {
11
+ return Boolean(value && String(value).length >= min)
12
+ }
13
+
14
+ message(attribute: string): string {
15
+ return message ?? `The ${attribute} must have at least ${min} characters`
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ import { RuleInterface, RuleValue } from '..'
2
+ import { Rule } from '../Rule'
3
+ import { Type } from '@Typetron/Support'
4
+
5
+ export function Optional(): Type<RuleInterface> {
6
+ return class extends Rule {
7
+ identifier = 'optional'
8
+
9
+ passes(attribute: string, value: RuleValue): boolean {
10
+ return true // TODO
11
+ }
12
+
13
+ message(attribute: string): string {
14
+ return ''
15
+ }
16
+ }
17
+ }
18
+
@@ -0,0 +1,29 @@
1
+ import { RuleInterface, RuleValue } from '..'
2
+ import { Type } from '@Typetron/Support'
3
+
4
+ export function Required(message?: string): Type<RuleInterface> {
5
+ return class {
6
+ identifier = 'required'
7
+
8
+ passes(attribute: string, value: RuleValue): boolean {
9
+
10
+ if (value === undefined || value === null) {
11
+ return false
12
+ }
13
+
14
+ if (typeof value === 'string' && value.trim() === '') {
15
+ return false
16
+ }
17
+
18
+ if (value.constructor === Array && value.length === 0) {
19
+ return false
20
+ }
21
+
22
+ return true
23
+ }
24
+
25
+ message(attribute: string): string {
26
+ return message ?? `The ${attribute} is required`
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,7 @@
1
+ import { RuleInterface } from './RuleInterface'
2
+ import { Type } from '../Support'
3
+
4
+ export abstract class Validator<T> {
5
+ protected constructor(public data: T, public rules: { [key in keyof T]?: (RuleInterface | Type<RuleInterface>)[] }) {
6
+ }
7
+ }
@@ -0,0 +1,12 @@
1
+ // tslint:disable-next-line:no-any
2
+ export type RuleValue = any
3
+ export * from './RuleInterface'
4
+ export * from './Validator'
5
+ export * from './Rule'
6
+ export * from './Rules/MaxLength'
7
+ export * from './Rules/MinLength'
8
+ export * from './Rules/FileExtension'
9
+ export * from './Rules/Required'
10
+ export * from './Rules/Email'
11
+ export * from './Rules/Optional'
12
+ // export * from './Rules/FileExtension'