@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.
- package/.eslintrc-old.js +208 -0
- package/Cache/Cache.ts +13 -0
- package/Cache/Stores/DatabaseStore.ts +120 -0
- package/Cache/Stores/FileStore.ts +69 -0
- package/Cache/Stores/MemoryStore.ts +71 -0
- package/Cache/index.ts +5 -0
- package/Cache/types.ts +4 -0
- package/Container/ClassResolver.ts +106 -0
- package/Container/Container.ts +144 -0
- package/Container/Contracts.ts +3 -0
- package/Container/Decorators.ts +20 -0
- package/Container/Metadata.ts +16 -0
- package/Container/Resolver.ts +27 -0
- package/Container/index.ts +5 -0
- package/Database/Clauses/Where.ts +0 -0
- package/Database/Connection.ts +52 -0
- package/Database/Decorators.ts +142 -0
- package/Database/Drivers/DatabaseDriver.ts +37 -0
- package/Database/Drivers/MySQL/Alter/index.ts +55 -0
- package/Database/Drivers/MySQL/ColumnDefinition.ts +16 -0
- package/Database/Drivers/MySQL/Create.ts +47 -0
- package/Database/Drivers/MySQL/MysqlDriver.ts +123 -0
- package/Database/Drivers/MySQL/Schema.ts +82 -0
- package/Database/Drivers/MySQL/index.ts +13 -0
- package/Database/Drivers/SQL/Schema.ts +73 -0
- package/Database/Drivers/SQL/Statements/Alter.ts +22 -0
- package/Database/Drivers/SQL/Statements/BaseStatement.ts +55 -0
- package/Database/Drivers/SQL/Statements/Delete.ts +20 -0
- package/Database/Drivers/SQL/Statements/Insert.ts +24 -0
- package/Database/Drivers/SQL/Statements/Select.ts +55 -0
- package/Database/Drivers/SQL/Statements/Update.ts +33 -0
- package/Database/Drivers/SQL/index.ts +8 -0
- package/Database/Drivers/SQLite/Create.ts +45 -0
- package/Database/Drivers/SQLite/Schema.ts +54 -0
- package/Database/Drivers/SQLite/SqliteDriver.ts +144 -0
- package/Database/Drivers/SQLite/index.ts +15 -0
- package/Database/Drivers/SchemaContract.ts +11 -0
- package/Database/Drivers/Statement.ts +16 -0
- package/Database/Drivers/index.ts +4 -0
- package/Database/Entity.ts +377 -0
- package/Database/EntityNotFoundError.ts +5 -0
- package/Database/EntityProxyHandler.ts +43 -0
- package/Database/EntityQuery.ts +197 -0
- package/Database/Expression.ts +3 -0
- package/Database/Fields.ts +816 -0
- package/Database/Helpers.ts +12 -0
- package/Database/List.ts +71 -0
- package/Database/Migrations/Migration.ts +10 -0
- package/Database/Migrations/MigrationHistory.ts +20 -0
- package/Database/Migrations/Migrator.ts +122 -0
- package/Database/Migrations/index.ts +3 -0
- package/Database/ORM/BaseRelationship.ts +10 -0
- package/Database/Query.ts +374 -0
- package/Database/Seeders/Seeder.ts +3 -0
- package/Database/Seeders/SeederManager.ts +27 -0
- package/Database/Seeders/index.ts +2 -0
- package/Database/StringExpression.ts +9 -0
- package/Database/Types.ts +165 -0
- package/Database/index.ts +23 -0
- package/Encryption/index.ts +51 -0
- package/Forms/Decorators.ts +29 -0
- package/Forms/Form.ts +70 -0
- package/Forms/FormFields.ts +36 -0
- package/Forms/index.ts +3 -0
- package/Framework/App.ts +10 -0
- package/Framework/Application.ts +118 -0
- package/Framework/Auth/Auth.ts +122 -0
- package/Framework/Auth/Authenticatable.ts +9 -0
- package/Framework/Auth/User.ts +27 -0
- package/Framework/Auth/index.ts +29 -0
- package/Framework/Config/AppConfig.ts +25 -0
- package/Framework/Config/AuthConfig.ts +9 -0
- package/Framework/Config/BaseConfig.ts +9 -0
- package/Framework/Config/CacheConfig.ts +38 -0
- package/Framework/Config/DatabaseConfig.ts +21 -0
- package/Framework/Config/MailConfig.ts +30 -0
- package/Framework/Config/index.ts +6 -0
- package/Framework/Middleware/AuthMiddleware.ts +31 -0
- package/Framework/Middleware/CorsMiddleware.ts +32 -0
- package/Framework/Middleware/StaticAssetsMiddleware.ts +76 -0
- package/Framework/Middleware/index.ts +2 -0
- package/Framework/Provider.ts +9 -0
- package/Framework/Providers/CacheProvider.ts +28 -0
- package/Framework/Providers/DatabaseProvider.ts +36 -0
- package/Framework/Providers/MailProvider.ts +31 -0
- package/Framework/Providers/WebsocketsProvider.ts +120 -0
- package/Framework/Resolvers/AuthResolver.ts +23 -0
- package/Framework/Resolvers/EntityResolver.ts +41 -0
- package/Framework/Resolvers/FormResolver.ts +37 -0
- package/Framework/RootDir.ts +1 -0
- package/Framework/index.ts +8 -0
- package/Frontend/Angular/index.ts +24 -0
- package/Frontend/Websockets/index.ts +86 -0
- package/Mail/MailAddress.ts +4 -0
- package/Mail/MailTransport.ts +6 -0
- package/Mail/Mailable.ts +16 -0
- package/Mail/Mailer.ts +27 -0
- package/Mail/PendingMail.ts +68 -0
- package/Mail/SentMessage.ts +20 -0
- package/Mail/Transports/MemoryTransport.ts +13 -0
- package/Mail/Transports/SendGridTransport.ts +95 -0
- package/Mail/index.ts +3 -0
- package/Models/Model.ts +41 -0
- package/Models/index.ts +54 -0
- package/Router/BaseController.ts +7 -0
- package/Router/Decorators.ts +224 -0
- package/Router/Guard.ts +10 -0
- package/Router/Http/Contracts.ts +4 -0
- package/Router/Http/ErrorHandler.ts +45 -0
- package/Router/Http/ErrorHandlerInterface.ts +6 -0
- package/Router/Http/Errors/HttpError.ts +15 -0
- package/Router/Http/Handler.ts +183 -0
- package/Router/Http/HttpRoute.ts +110 -0
- package/Router/Http/Middleware.ts +7 -0
- package/Router/Http/Request.ts +225 -0
- package/Router/Http/Response.ts +35 -0
- package/Router/Http/index.ts +85 -0
- package/Router/Metadata.ts +34 -0
- package/Router/Middleware.ts +16 -0
- package/Router/Request.ts +65 -0
- package/Router/Route.ts +16 -0
- package/Router/RouteNotFoundError.ts +5 -0
- package/Router/Router.ts +35 -0
- package/Router/Servers/index.ts +6 -0
- package/Router/Servers/node.ts +49 -0
- package/Router/Servers/uNetworking.ts +151 -0
- package/Router/Websockets/Handler.ts +82 -0
- package/Router/Websockets/Middleware.ts +7 -0
- package/Router/Websockets/Request.ts +3 -0
- package/Router/Websockets/WebsocketRoute.ts +61 -0
- package/Router/Websockets/index.ts +57 -0
- package/Router/Websockets/types.ts +23 -0
- package/Router/index.ts +6 -0
- package/Storage/File.ts +30 -0
- package/Storage/Image.ts +19 -0
- package/Storage/InvalidImage.ts +3 -0
- package/Storage/Storage.ts +98 -0
- package/Storage/index.ts +4 -0
- package/Storage/utils.ts +18 -0
- package/Support/Array.ts +184 -0
- package/Support/Math.ts +12 -0
- package/Support/Metadata.ts +31 -0
- package/Support/String.ts +38 -0
- package/Support/index.ts +19 -0
- package/Support/utils.ts +7 -0
- package/Testing/TestCase.ts +193 -0
- package/Validation/Rule.ts +9 -0
- package/Validation/RuleInterface.ts +10 -0
- package/Validation/Rules/Email.ts +20 -0
- package/Validation/Rules/FileExtension.ts +21 -0
- package/Validation/Rules/MaxLength.ts +17 -0
- package/Validation/Rules/MinLength.ts +18 -0
- package/Validation/Rules/Optional.ts +18 -0
- package/Validation/Rules/Required.ts +29 -0
- package/Validation/Validator.ts +7 -0
- package/Validation/index.ts +12 -0
- package/benchmark-index.js +20 -0
- package/benchmark.ts +47 -0
- package/package.json +7 -3
- package/tests/Cache/DatabaseStoreTest.ts +110 -0
- package/tests/Cache/FileStoreTest.ts +93 -0
- package/tests/Cache/MemoryStoreTest.ts +92 -0
- package/tests/Container/ContainerTest.ts +336 -0
- package/tests/Database/Drivers/MySQL/AlterTest.ts +100 -0
- package/tests/Database/Drivers/MySQL/CreateTest.ts +39 -0
- package/tests/Database/Drivers/MysqlDriverTest.ts +140 -0
- package/tests/Database/Drivers/SqliteDriverTest.ts +126 -0
- package/tests/Database/Entities/Article.ts +21 -0
- package/tests/Database/Entities/Profile.ts +21 -0
- package/tests/Database/Entities/Role.ts +15 -0
- package/tests/Database/Entities/User.ts +35 -0
- package/tests/Database/EntityRelationshipsTest.ts +475 -0
- package/tests/Database/EntityTest.ts +202 -0
- package/tests/Database/Migrations/RunnerTest.ts +96 -0
- package/tests/Database/Migrations/migrations/1.createUserTable.ts +12 -0
- package/tests/Database/Migrations/migrations/2.createArticlesTable.ts +12 -0
- package/tests/Database/QueryTest.ts +573 -0
- package/tests/Database/SchemaTest.ts +32 -0
- package/tests/Database/Seeders/RunnerTest.ts +30 -0
- package/tests/Database/Seeders/seeders/RandomSeeder.ts +10 -0
- package/tests/Database/utils.ts +16 -0
- package/tests/Forms/FormsTest.ts +59 -0
- package/tests/Framework/Middleware/StaticAssetsMiddlewareTest.ts +129 -0
- package/tests/Framework/Resolvers/EntityResolverTest.ts +48 -0
- package/tests/Framework/Router/RouterTest.ts +33 -0
- package/tests/Mail/MailerTest.ts +76 -0
- package/tests/Model/ModelTest.ts +183 -0
- package/tests/Router/HttpHandlerTest.ts +72 -0
- package/tests/Router/RequestTest.ts +77 -0
- package/tests/Router/WebSocketsHandlerTest.ts +51 -0
- package/tests/Storage/StorageTest.ts +97 -0
- package/tests/Support/ArrayTest.ts +167 -0
- package/tests/Support/StringTest.ts +18 -0
- package/tests/Validation/RulesTest.ts +54 -0
- package/types.d.ts +8 -0
package/Support/Array.ts
ADDED
|
@@ -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
|
+
}
|
package/Support/Math.ts
ADDED
|
@@ -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
|
+
}
|
package/Support/index.ts
ADDED
|
@@ -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
|
package/Support/utils.ts
ADDED
|
@@ -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,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,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'
|