@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.
- 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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { Constructor, Type } from '../Support'
|
|
2
|
+
import { ClassResolver } from './ClassResolver'
|
|
3
|
+
import { ServiceIdentifier } from './Contracts'
|
|
4
|
+
import { InjectableMetadata, Scope } from './Metadata'
|
|
5
|
+
import { Resolver } from './Resolver'
|
|
6
|
+
|
|
7
|
+
export class Container {
|
|
8
|
+
protected static instance: Container
|
|
9
|
+
|
|
10
|
+
// TODO performance: get rid of this
|
|
11
|
+
public resolvers: Resolver[] = [
|
|
12
|
+
new ClassResolver(this)
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
// tslint:disable-next-line:no-any
|
|
16
|
+
protected instances = new Map<ServiceIdentifier<any>, any>()
|
|
17
|
+
|
|
18
|
+
public parent?: Container
|
|
19
|
+
|
|
20
|
+
static getInstance() {
|
|
21
|
+
if (!Container.instance) {
|
|
22
|
+
Container.instance = new Container()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return Container.instance
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static setInstance(container: Container) {
|
|
29
|
+
return Container.instance = container
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getInstances() {
|
|
33
|
+
return this.instances
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
set<T>(abstract: ServiceIdentifier<T>, concrete: T | Type<T> | (() => void)) {
|
|
37
|
+
this.instances.set(abstract, concrete)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @deprecated
|
|
42
|
+
*/
|
|
43
|
+
forceSet<T>(abstract: ServiceIdentifier<T>, concrete: T | Type<T> | (() => any)) {
|
|
44
|
+
this.set(abstract, concrete)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// tslint:disable-next-line:no-any
|
|
48
|
+
get<T>(abstract: ServiceIdentifier<T>, parameters: any[] = []): T {
|
|
49
|
+
const metadata = this.getMetadata(abstract)
|
|
50
|
+
|
|
51
|
+
if (metadata.scope === Scope.TRANSIENT) {
|
|
52
|
+
return this.getResolver(abstract, metadata).resolve(abstract, parameters) as T
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let concrete = this.getInstance(abstract)
|
|
56
|
+
if (concrete) {
|
|
57
|
+
return concrete
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// if (this.parent && metadata.scope !== Scope.REQUEST
|
|
61
|
+
// && (concrete = this.parent.getInstance(abstract) ?? this.parent.get(abstract))
|
|
62
|
+
// ) {
|
|
63
|
+
// return concrete
|
|
64
|
+
// }
|
|
65
|
+
|
|
66
|
+
if (this.parent && metadata.scope !== Scope.REQUEST && (concrete = this.parent.getInstance(abstract))) {
|
|
67
|
+
return concrete
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (typeof abstract === 'string') {
|
|
71
|
+
const instance = this.getInstance<T>(abstract) as T
|
|
72
|
+
if (!instance) {
|
|
73
|
+
throw new Error(`No instance found for '${this.getAbstractName(abstract)}'`)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const resolver = this.getResolver(abstract, metadata)
|
|
78
|
+
concrete = resolver.resolve(abstract, parameters) as T
|
|
79
|
+
this.set(abstract, concrete)
|
|
80
|
+
return concrete
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
createChildContainer(): Container {
|
|
84
|
+
const childContainer = new Container()
|
|
85
|
+
childContainer.parent = this
|
|
86
|
+
childContainer.set(Container, childContainer)
|
|
87
|
+
// TODO performance: get rid of this
|
|
88
|
+
childContainer.resolvers = this.resolvers.map(resolver => {
|
|
89
|
+
return new (resolver.constructor as Constructor<Resolver>)(childContainer)
|
|
90
|
+
})
|
|
91
|
+
return childContainer
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private getMetadata<T>(abstract: ServiceIdentifier<T>) {
|
|
95
|
+
if (abstract instanceof Object) {
|
|
96
|
+
return InjectableMetadata.get(abstract)
|
|
97
|
+
}
|
|
98
|
+
return new InjectableMetadata
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private getAbstractName<T>(abstract: ServiceIdentifier<T>): string {
|
|
102
|
+
if (typeof abstract === 'string') {
|
|
103
|
+
return abstract
|
|
104
|
+
}
|
|
105
|
+
if (abstract === undefined) {
|
|
106
|
+
throw new Error(`
|
|
107
|
+
Cannot resolve a parameter having the value undefined.
|
|
108
|
+
This might be due to circular dependencies in your app
|
|
109
|
+
`)
|
|
110
|
+
}
|
|
111
|
+
if (typeof abstract === 'symbol') {
|
|
112
|
+
return abstract.toString()
|
|
113
|
+
}
|
|
114
|
+
if ('name' in abstract) {
|
|
115
|
+
return abstract.name
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Cannot get abstract name for ${abstract}`)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private getResolver<T>(abstract: ServiceIdentifier<T>, metadata: InjectableMetadata) {
|
|
121
|
+
let resolver = metadata.resolver
|
|
122
|
+
if (!resolver) {
|
|
123
|
+
resolver = this.resolvers.find(item => item.canResolve(abstract))
|
|
124
|
+
}
|
|
125
|
+
if (!resolver) {
|
|
126
|
+
throw new Error(`Resolver not found for '${this.getAbstractName(abstract)}'`)
|
|
127
|
+
}
|
|
128
|
+
this.setResolverForAbstract(abstract, resolver, metadata)
|
|
129
|
+
resolver.container = this
|
|
130
|
+
return resolver
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private setResolverForAbstract<T>(abstract: ServiceIdentifier<T>, resolver: Resolver, metadata: InjectableMetadata) {
|
|
134
|
+
if (typeof abstract === 'symbol') {
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
metadata.resolver = resolver
|
|
138
|
+
InjectableMetadata.set(metadata, abstract)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private getInstance<T>(abstract: ServiceIdentifier<T>): T | undefined {
|
|
142
|
+
return this.instances.get(abstract) || this.parent?.getInstance(abstract)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ServiceIdentifier } from './Contracts'
|
|
2
|
+
import { InjectableMetadata, Scope } from './Metadata'
|
|
3
|
+
|
|
4
|
+
export function Injectable(scope: Scope = Scope.SINGLETON) {
|
|
5
|
+
return function <T extends Function>(target: T) {
|
|
6
|
+
const metadata = InjectableMetadata.get(target)
|
|
7
|
+
metadata.scope = scope
|
|
8
|
+
InjectableMetadata.set(metadata, target)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Inject<T extends Object>(abstract?: ServiceIdentifier<T>) {
|
|
13
|
+
return function (target: T, targetKey: string, index?: number) {
|
|
14
|
+
const fieldType = Reflect.getMetadata('design:type', target, targetKey) as ServiceIdentifier<T>
|
|
15
|
+
const metadata = InjectableMetadata.get(target.constructor)
|
|
16
|
+
metadata.dependencies[targetKey] = abstract ?? fieldType
|
|
17
|
+
InjectableMetadata.set(metadata, target.constructor)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Resolver } from './Resolver'
|
|
2
|
+
import { ServiceIdentifier } from './Contracts'
|
|
3
|
+
import { MetadataKey } from '../Support/Metadata'
|
|
4
|
+
|
|
5
|
+
export enum Scope {
|
|
6
|
+
TRANSIENT = 'TRANSIENT',
|
|
7
|
+
SINGLETON = 'SINGLETON',
|
|
8
|
+
REQUEST = 'REQUEST',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class InjectableMetadata extends MetadataKey('injectable') {
|
|
12
|
+
resolver?: Resolver
|
|
13
|
+
|
|
14
|
+
scope: Scope = Scope.SINGLETON
|
|
15
|
+
dependencies: {[key: string]: ServiceIdentifier<{}>} = {}
|
|
16
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Container } from './Container'
|
|
2
|
+
import { ServiceIdentifier } from './Contracts'
|
|
3
|
+
|
|
4
|
+
export interface Resolver {
|
|
5
|
+
container: Container;
|
|
6
|
+
|
|
7
|
+
canResolve<T>(abstract: ServiceIdentifier<T>): boolean;
|
|
8
|
+
|
|
9
|
+
resolve<T>(abstract: ServiceIdentifier<T>, parameters: object[]): T | Promise<T>;
|
|
10
|
+
|
|
11
|
+
reload<T>(abstract: ServiceIdentifier<T>, concrete: T, container?: Container): T;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export abstract class BaseResolver implements Resolver {
|
|
15
|
+
constructor(public container: Container) {}
|
|
16
|
+
|
|
17
|
+
abstract canResolve<T>(abstract: ServiceIdentifier<T>): boolean;
|
|
18
|
+
|
|
19
|
+
abstract resolve<T>(abstract: ServiceIdentifier<T>, parameters: object[]): T | Promise<T>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Used to update the dependencies that have the REQUEST scope
|
|
23
|
+
*/
|
|
24
|
+
reload<T>(abstract: ServiceIdentifier<T>, concrete: T, container?: Container): T {
|
|
25
|
+
return concrete
|
|
26
|
+
}
|
|
27
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Query } from './Query'
|
|
2
|
+
import { DatabaseDriver } from './Drivers/DatabaseDriver'
|
|
3
|
+
import { SqlValue } from './Types'
|
|
4
|
+
|
|
5
|
+
export class Connection {
|
|
6
|
+
|
|
7
|
+
constructor(public driver: DatabaseDriver) {}
|
|
8
|
+
|
|
9
|
+
async run(query: Query): Promise<void> {
|
|
10
|
+
return this.driver.run(query.toSQL(), query.getBindings())
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async insertOne(query: Query): Promise<number> {
|
|
14
|
+
return this.driver.insertOne(query.toSQL(), query.getBindings())
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
truncate(table: string) {
|
|
18
|
+
return this.driver.truncate(table)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async get<T>(query: Query<T>): Promise<T[]> {
|
|
22
|
+
return await this.driver.get<T>(query.toSQL(), query.getBindings()) as T[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async first<T>(query: Query<T>): Promise<T | undefined> {
|
|
26
|
+
return await this.driver.first(query.toSQL(), query.getBindings()) as T
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async getRaw<T>(rawQuery: string, params: SqlValue[] = []): Promise<T[]> {
|
|
30
|
+
return await this.driver.get(rawQuery, params) as T[]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async firstRaw<T>(rawQuery: string, params: SqlValue[] = []): Promise<T> {
|
|
34
|
+
return await this.driver.first(rawQuery, params) as T
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async runRaw(rawQuery: string, params: SqlValue[] = []): Promise<void> {
|
|
38
|
+
return this.driver.run(rawQuery, params)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async tables() {
|
|
42
|
+
return this.driver.tables()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async tableExists(table: string) {
|
|
46
|
+
return this.driver.tableExists(table)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async tableColumns(table: string) {
|
|
50
|
+
return this.driver.tableColumns(table)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { ChildObject, Constructor, KeysOfType } from '@Typetron/Support'
|
|
2
|
+
import { Entity } from './Entity'
|
|
3
|
+
import {
|
|
4
|
+
BelongsTo,
|
|
5
|
+
BelongsToMany,
|
|
6
|
+
BelongsToManyField,
|
|
7
|
+
BelongsToManyFieldOptions,
|
|
8
|
+
ColumnField,
|
|
9
|
+
HasMany,
|
|
10
|
+
HasManyField,
|
|
11
|
+
HasOne,
|
|
12
|
+
HasOneField,
|
|
13
|
+
JSONField,
|
|
14
|
+
PrimaryField,
|
|
15
|
+
RelationshipField
|
|
16
|
+
} from './Fields'
|
|
17
|
+
import { EntityConstructor } from './index'
|
|
18
|
+
import { List } from './List'
|
|
19
|
+
import { BaseRelationship } from './ORM/BaseRelationship'
|
|
20
|
+
|
|
21
|
+
export const EntityMetadataKey = 'framework:entity'
|
|
22
|
+
|
|
23
|
+
export type ID = number | string;
|
|
24
|
+
|
|
25
|
+
export class EntityOptions<T extends Entity> {
|
|
26
|
+
table?: string
|
|
27
|
+
touch?: KeysOfType<T, Entity | List<T, Entity>>[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class EntityMetadata<T extends Entity, R extends Entity> extends EntityOptions<T> {
|
|
31
|
+
columns: Record<string, ColumnField<T>> = {}
|
|
32
|
+
relationships: Record<string, RelationshipField<T, R>> = {}
|
|
33
|
+
inverseRelationships: Record<string, BelongsToManyField<T, R> | HasManyField<T, R> | HasOneField<T, R>> = {}
|
|
34
|
+
createdAtColumn?: string
|
|
35
|
+
updatedAtColumn?: string
|
|
36
|
+
|
|
37
|
+
get allRelationships() {
|
|
38
|
+
return {...this.relationships, ...this.inverseRelationships}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static get<T extends Entity, R extends Entity>(entity: T) {
|
|
42
|
+
const metadata: EntityMetadata<T, R> = Reflect.getMetadata(EntityMetadataKey, entity) || new this()
|
|
43
|
+
metadata.table = metadata?.table || entity.constructor.name.toLowerCase()
|
|
44
|
+
return metadata
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function Options<T extends Entity>(options: EntityOptions<T> = {}) {
|
|
49
|
+
return (entity: object) => {
|
|
50
|
+
const entityClass = entity as unknown as EntityConstructor<T>
|
|
51
|
+
const metadata = EntityMetadata.get(entityClass.prototype)
|
|
52
|
+
options.table = options.table || entityClass.name.toLowerCase()
|
|
53
|
+
Object.assign(metadata, options)
|
|
54
|
+
Reflect.defineMetadata(EntityMetadataKey, metadata, entityClass.prototype)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function setField<T extends Entity>(entity: T, field: ColumnField<T>) {
|
|
59
|
+
const entityMetadata = EntityMetadata.get(entity)
|
|
60
|
+
entityMetadata.columns[field.property] = field
|
|
61
|
+
Reflect.defineMetadata(EntityMetadataKey, entityMetadata, entity)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function Column<T extends Entity>(column?: string | {type: any}, options?: {type: any}): PropertyDecorator {
|
|
65
|
+
return function(entity: Object, name: string | symbol) {
|
|
66
|
+
const actualColumn: string | undefined = typeof column === 'string' ? column : undefined
|
|
67
|
+
const type = options?.type ?? Reflect.getMetadata('design:type', entity, name)
|
|
68
|
+
const field = new ColumnField(entity.constructor as EntityConstructor<T>, String(name), () => type, actualColumn || String(name))
|
|
69
|
+
setField(entity as T, field)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function JSONColumn<T extends Entity>(column?: string) {
|
|
74
|
+
return function(entity: object, name: string) {
|
|
75
|
+
const field = new JSONField(entity.constructor as EntityConstructor<T>, name, () => String, column || name)
|
|
76
|
+
setField(entity as T, field)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function Enum<T extends Entity>(...values: string[]) {
|
|
81
|
+
return function(entity: object, name: string) {
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function PrimaryColumn<T extends Entity>(column?: string) {
|
|
86
|
+
return function(entity: object, name: string) {
|
|
87
|
+
const type = Reflect.getMetadata('design:type', entity, name)
|
|
88
|
+
const field = new PrimaryField(entity.constructor as EntityConstructor<T>, name, () => type, column || name)
|
|
89
|
+
setField(entity as T, field)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function CreatedAt<T extends Entity>(column?: string) {
|
|
94
|
+
return function(entity: object, name: string) {
|
|
95
|
+
Column(column)(entity as T, name)
|
|
96
|
+
const entityMetadata = EntityMetadata.get(entity as T)
|
|
97
|
+
entityMetadata.createdAtColumn = column || name
|
|
98
|
+
Reflect.defineMetadata(EntityMetadataKey, entityMetadata, entity)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function UpdatedAt<T extends Entity>(column?: string) {
|
|
103
|
+
return function(entity: object, name: string) {
|
|
104
|
+
Column(column)(entity as T, name)
|
|
105
|
+
const entityMetadata = EntityMetadata.get(entity as T)
|
|
106
|
+
entityMetadata.updatedAtColumn = column || name
|
|
107
|
+
Reflect.defineMetadata(EntityMetadataKey, entityMetadata, entity)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type RelationshipsList = typeof BelongsTo | typeof HasOne | typeof HasMany | typeof BelongsToMany;
|
|
112
|
+
const entityOptionsCache = new Map<Entity, Record<string, object>>()
|
|
113
|
+
|
|
114
|
+
export function Relation<T extends Entity, R extends Entity>(
|
|
115
|
+
type: () => Constructor<R>,
|
|
116
|
+
// tslint:disable-next-line:no-any
|
|
117
|
+
// inverseBy: KeysOfType<Omit<R, keyof Entity>, Entity | HasOne<any> | BelongsTo<any> | HasMany<any> | BelongsToMany<any>>,
|
|
118
|
+
// tslint:disable-next-line:no-any
|
|
119
|
+
// inverseBy: KeysOfType<R, Entity | HasOne<any> | BelongsTo<any> | HasMany<any> | BelongsToMany<any>>,
|
|
120
|
+
// tslint:disable-next-line:no-any
|
|
121
|
+
inverseBy: KeysOfType<ChildObject<R, Entity>, BaseRelationship<T>>,
|
|
122
|
+
// options: Partial<RelationshipOptions> = {}
|
|
123
|
+
) {
|
|
124
|
+
return function(entity: object, property: string) {
|
|
125
|
+
let entityMetadata = EntityMetadata.get(entity as T)
|
|
126
|
+
const fieldClass = Reflect.getMetadata('design:type', entity, property) as RelationshipsList
|
|
127
|
+
const entityClass = entity.constructor as EntityConstructor<T>
|
|
128
|
+
|
|
129
|
+
const options = entityOptionsCache.get(entity as T) || {}
|
|
130
|
+
// @ts-ignore
|
|
131
|
+
entityMetadata = fieldClass.relationship(entityMetadata, entityClass, property, type, inverseBy as string, options[property])
|
|
132
|
+
Reflect.defineMetadata(EntityMetadataKey, entityMetadata, entity)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function BelongsToManyOptions<T extends Entity>(options: BelongsToManyFieldOptions) {
|
|
137
|
+
return function(entity: object, property: string) {
|
|
138
|
+
const entityOptions = entityOptionsCache.get(entity as T) || {}
|
|
139
|
+
entityOptions[property] = options
|
|
140
|
+
entityOptionsCache.set(entity as T, entityOptions)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { SqlValue } from '../Types'
|
|
2
|
+
import { Statement } from './Statement'
|
|
3
|
+
import { Constructor } from '@Typetron/Support'
|
|
4
|
+
import { SchemaContract } from './SchemaContract'
|
|
5
|
+
import { ColumnDefinitionOptions } from '@Typetron/Database/Drivers/SQL'
|
|
6
|
+
|
|
7
|
+
interface StatementsList {
|
|
8
|
+
create: Constructor<Statement>,
|
|
9
|
+
select: Constructor<Statement>,
|
|
10
|
+
insert: Constructor<Statement>,
|
|
11
|
+
delete: Constructor<Statement>,
|
|
12
|
+
update: Constructor<Statement>,
|
|
13
|
+
alter: Constructor<Statement>,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export abstract class DatabaseDriver {
|
|
17
|
+
|
|
18
|
+
abstract statements: StatementsList
|
|
19
|
+
|
|
20
|
+
abstract schema: SchemaContract
|
|
21
|
+
|
|
22
|
+
abstract run(query: string, params?: SqlValue[]): Promise<void>
|
|
23
|
+
|
|
24
|
+
abstract truncate(table: string): Promise<void>
|
|
25
|
+
|
|
26
|
+
abstract insertOne(query: string, params: SqlValue[]): Promise<number>
|
|
27
|
+
|
|
28
|
+
abstract get<T>(query: string, params: SqlValue[]): Promise<T | T[]>
|
|
29
|
+
|
|
30
|
+
abstract first<T>(query: string, params: SqlValue[]): Promise<T | undefined>
|
|
31
|
+
|
|
32
|
+
abstract tables(): Promise<{name: string}[]>
|
|
33
|
+
|
|
34
|
+
abstract tableExists(table: string): Promise<boolean>
|
|
35
|
+
|
|
36
|
+
abstract tableColumns(table: string): Promise<ColumnDefinitionOptions[]>
|
|
37
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ColumnDefinitionOptions } from '../../../Drivers/SQL'
|
|
2
|
+
import { wrap } from '../../../Helpers'
|
|
3
|
+
import { ColumnDefinition } from '@Typetron/Database/Drivers/MySQL/ColumnDefinition'
|
|
4
|
+
import { Expression } from '@Typetron/Database'
|
|
5
|
+
|
|
6
|
+
export enum Constraints {
|
|
7
|
+
PrimaryKey = 'PRIMARY KEY',
|
|
8
|
+
ForeignKey = 'FOREIGN KEY',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export abstract class AlterOption extends Expression {
|
|
12
|
+
constructor(public column: ColumnDefinitionOptions) {super()}
|
|
13
|
+
|
|
14
|
+
abstract toSQL(): string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class AddColumn extends AlterOption {
|
|
18
|
+
toSQL(): string {
|
|
19
|
+
return `ADD ${new ColumnDefinition(this.column).toSQL()}`
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class AddConstraint extends AlterOption {
|
|
24
|
+
|
|
25
|
+
constructor(column: ColumnDefinitionOptions, public type: Constraints) {super(column)}
|
|
26
|
+
|
|
27
|
+
toSQL(): string {
|
|
28
|
+
return `ADD CONSTRAINT ${this.type} (${this.column.name})`
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class ModifyColumn extends AlterOption {
|
|
33
|
+
toSQL(): string {
|
|
34
|
+
return `MODIFY ${new ColumnDefinition(this.column).toSQL()}`
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class DropColumn extends Expression {
|
|
39
|
+
constructor(public column: string) {super()}
|
|
40
|
+
|
|
41
|
+
toSQL(): string {
|
|
42
|
+
return `DROP COLUMN ${wrap(this.column)}`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class Alter extends Expression {
|
|
47
|
+
constructor(public table: string, public alterOptions: Expression[]) {super()}
|
|
48
|
+
|
|
49
|
+
toSQL() {
|
|
50
|
+
return `
|
|
51
|
+
ALTER TABLE ${wrap(this.table)}
|
|
52
|
+
${this.alterOptions.map(option => option.toSQL()).join(', \n')}
|
|
53
|
+
`
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ColumnDefinitionOptions } from '../../Drivers/SQL'
|
|
2
|
+
import { wrap } from '../../Helpers'
|
|
3
|
+
|
|
4
|
+
export class ColumnDefinition {
|
|
5
|
+
constructor(public options: ColumnDefinitionOptions) {}
|
|
6
|
+
|
|
7
|
+
toSQL() {
|
|
8
|
+
const sqlParts = [wrap(this.options.name), this.options.type]
|
|
9
|
+
|
|
10
|
+
if (this.options.autoIncrement) {
|
|
11
|
+
sqlParts.push('AUTO_INCREMENT')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return sqlParts.join(' ')
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { BaseStatement } from '../SQL/Statements/BaseStatement'
|
|
2
|
+
import { ColumnDefinitionOptions } from '../SQL'
|
|
3
|
+
import { wrap } from '../../Helpers'
|
|
4
|
+
|
|
5
|
+
export class Create extends BaseStatement {
|
|
6
|
+
|
|
7
|
+
constructor(table: string, public columns: ColumnDefinitionOptions[] = []) {
|
|
8
|
+
super({
|
|
9
|
+
table,
|
|
10
|
+
columns: [],
|
|
11
|
+
joins: [],
|
|
12
|
+
wheres: [],
|
|
13
|
+
groups: [],
|
|
14
|
+
orders: [],
|
|
15
|
+
having: []
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
toSQL() {
|
|
20
|
+
const definitions: string[] = this.getColumnsSQL(this.columns)
|
|
21
|
+
|
|
22
|
+
this.columns.where('primaryKey').whenNotEmpty(primaryColumns => {
|
|
23
|
+
definitions.push(`PRIMARY KEY (${primaryColumns.pluck('name').join(', ')})`)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return `
|
|
27
|
+
CREATE TABLE ${this.table}
|
|
28
|
+
(
|
|
29
|
+
${definitions.join(', \n')}
|
|
30
|
+
)
|
|
31
|
+
`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private getColumnsSQL(columns: ColumnDefinitionOptions[]) {
|
|
35
|
+
return columns.map(this.getColumnSQL)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private getColumnSQL(column: ColumnDefinitionOptions) {
|
|
39
|
+
const sqlParts = [wrap(column.name), column.type]
|
|
40
|
+
|
|
41
|
+
if (column.autoIncrement) {
|
|
42
|
+
sqlParts.push('AUTO_INCREMENT')
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return sqlParts.join(' ')
|
|
46
|
+
}
|
|
47
|
+
}
|