@typetron/framework 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/.eslintrc-old.js +208 -0
  2. package/Cache/Cache.ts +13 -0
  3. package/Cache/Stores/DatabaseStore.ts +120 -0
  4. package/Cache/Stores/FileStore.ts +69 -0
  5. package/Cache/Stores/MemoryStore.ts +71 -0
  6. package/Cache/index.ts +5 -0
  7. package/Cache/types.ts +4 -0
  8. package/Container/ClassResolver.ts +106 -0
  9. package/Container/Container.ts +144 -0
  10. package/Container/Contracts.ts +3 -0
  11. package/Container/Decorators.ts +20 -0
  12. package/Container/Metadata.ts +16 -0
  13. package/Container/Resolver.ts +27 -0
  14. package/Container/index.ts +5 -0
  15. package/Database/Clauses/Where.ts +0 -0
  16. package/Database/Connection.ts +52 -0
  17. package/Database/Decorators.ts +142 -0
  18. package/Database/Drivers/DatabaseDriver.ts +37 -0
  19. package/Database/Drivers/MySQL/Alter/index.ts +55 -0
  20. package/Database/Drivers/MySQL/ColumnDefinition.ts +16 -0
  21. package/Database/Drivers/MySQL/Create.ts +47 -0
  22. package/Database/Drivers/MySQL/MysqlDriver.ts +123 -0
  23. package/Database/Drivers/MySQL/Schema.ts +82 -0
  24. package/Database/Drivers/MySQL/index.ts +13 -0
  25. package/Database/Drivers/SQL/Schema.ts +73 -0
  26. package/Database/Drivers/SQL/Statements/Alter.ts +22 -0
  27. package/Database/Drivers/SQL/Statements/BaseStatement.ts +55 -0
  28. package/Database/Drivers/SQL/Statements/Delete.ts +20 -0
  29. package/Database/Drivers/SQL/Statements/Insert.ts +24 -0
  30. package/Database/Drivers/SQL/Statements/Select.ts +55 -0
  31. package/Database/Drivers/SQL/Statements/Update.ts +33 -0
  32. package/Database/Drivers/SQL/index.ts +8 -0
  33. package/Database/Drivers/SQLite/Create.ts +45 -0
  34. package/Database/Drivers/SQLite/Schema.ts +54 -0
  35. package/Database/Drivers/SQLite/SqliteDriver.ts +144 -0
  36. package/Database/Drivers/SQLite/index.ts +15 -0
  37. package/Database/Drivers/SchemaContract.ts +11 -0
  38. package/Database/Drivers/Statement.ts +16 -0
  39. package/Database/Drivers/index.ts +4 -0
  40. package/Database/Entity.ts +377 -0
  41. package/Database/EntityNotFoundError.ts +5 -0
  42. package/Database/EntityProxyHandler.ts +43 -0
  43. package/Database/EntityQuery.ts +197 -0
  44. package/Database/Expression.ts +3 -0
  45. package/Database/Fields.ts +816 -0
  46. package/Database/Helpers.ts +12 -0
  47. package/Database/List.ts +71 -0
  48. package/Database/Migrations/Migration.ts +10 -0
  49. package/Database/Migrations/MigrationHistory.ts +20 -0
  50. package/Database/Migrations/Migrator.ts +122 -0
  51. package/Database/Migrations/index.ts +3 -0
  52. package/Database/ORM/BaseRelationship.ts +10 -0
  53. package/Database/Query.ts +374 -0
  54. package/Database/Seeders/Seeder.ts +3 -0
  55. package/Database/Seeders/SeederManager.ts +27 -0
  56. package/Database/Seeders/index.ts +2 -0
  57. package/Database/StringExpression.ts +9 -0
  58. package/Database/Types.ts +165 -0
  59. package/Database/index.ts +23 -0
  60. package/Encryption/index.ts +51 -0
  61. package/Forms/Decorators.ts +29 -0
  62. package/Forms/Form.ts +70 -0
  63. package/Forms/FormFields.ts +36 -0
  64. package/Forms/index.ts +3 -0
  65. package/Framework/App.ts +10 -0
  66. package/Framework/Application.ts +118 -0
  67. package/Framework/Auth/Auth.ts +122 -0
  68. package/Framework/Auth/Authenticatable.ts +9 -0
  69. package/Framework/Auth/User.ts +27 -0
  70. package/Framework/Auth/index.ts +29 -0
  71. package/Framework/Config/AppConfig.ts +25 -0
  72. package/Framework/Config/AuthConfig.ts +9 -0
  73. package/Framework/Config/BaseConfig.ts +9 -0
  74. package/Framework/Config/CacheConfig.ts +38 -0
  75. package/Framework/Config/DatabaseConfig.ts +21 -0
  76. package/Framework/Config/MailConfig.ts +30 -0
  77. package/Framework/Config/index.ts +6 -0
  78. package/Framework/Middleware/AuthMiddleware.ts +31 -0
  79. package/Framework/Middleware/CorsMiddleware.ts +32 -0
  80. package/Framework/Middleware/StaticAssetsMiddleware.ts +76 -0
  81. package/Framework/Middleware/index.ts +2 -0
  82. package/Framework/Provider.ts +9 -0
  83. package/Framework/Providers/CacheProvider.ts +28 -0
  84. package/Framework/Providers/DatabaseProvider.ts +36 -0
  85. package/Framework/Providers/MailProvider.ts +31 -0
  86. package/Framework/Providers/WebsocketsProvider.ts +120 -0
  87. package/Framework/Resolvers/AuthResolver.ts +23 -0
  88. package/Framework/Resolvers/EntityResolver.ts +41 -0
  89. package/Framework/Resolvers/FormResolver.ts +37 -0
  90. package/Framework/RootDir.ts +1 -0
  91. package/Framework/index.ts +8 -0
  92. package/Frontend/Angular/index.ts +24 -0
  93. package/Frontend/Websockets/index.ts +86 -0
  94. package/Mail/MailAddress.ts +4 -0
  95. package/Mail/MailTransport.ts +6 -0
  96. package/Mail/Mailable.ts +16 -0
  97. package/Mail/Mailer.ts +27 -0
  98. package/Mail/PendingMail.ts +68 -0
  99. package/Mail/SentMessage.ts +20 -0
  100. package/Mail/Transports/MemoryTransport.ts +13 -0
  101. package/Mail/Transports/SendGridTransport.ts +95 -0
  102. package/Mail/index.ts +3 -0
  103. package/Models/Model.ts +41 -0
  104. package/Models/index.ts +54 -0
  105. package/Router/BaseController.ts +7 -0
  106. package/Router/Decorators.ts +224 -0
  107. package/Router/Guard.ts +10 -0
  108. package/Router/Http/Contracts.ts +4 -0
  109. package/Router/Http/ErrorHandler.ts +45 -0
  110. package/Router/Http/ErrorHandlerInterface.ts +6 -0
  111. package/Router/Http/Errors/HttpError.ts +15 -0
  112. package/Router/Http/Handler.ts +183 -0
  113. package/Router/Http/HttpRoute.ts +110 -0
  114. package/Router/Http/Middleware.ts +7 -0
  115. package/Router/Http/Request.ts +225 -0
  116. package/Router/Http/Response.ts +35 -0
  117. package/Router/Http/index.ts +85 -0
  118. package/Router/Metadata.ts +34 -0
  119. package/Router/Middleware.ts +16 -0
  120. package/Router/Request.ts +65 -0
  121. package/Router/Route.ts +16 -0
  122. package/Router/RouteNotFoundError.ts +5 -0
  123. package/Router/Router.ts +35 -0
  124. package/Router/Servers/index.ts +6 -0
  125. package/Router/Servers/node.ts +49 -0
  126. package/Router/Servers/uNetworking.ts +151 -0
  127. package/Router/Websockets/Handler.ts +82 -0
  128. package/Router/Websockets/Middleware.ts +7 -0
  129. package/Router/Websockets/Request.ts +3 -0
  130. package/Router/Websockets/WebsocketRoute.ts +61 -0
  131. package/Router/Websockets/index.ts +57 -0
  132. package/Router/Websockets/types.ts +23 -0
  133. package/Router/index.ts +6 -0
  134. package/Storage/File.ts +30 -0
  135. package/Storage/Image.ts +19 -0
  136. package/Storage/InvalidImage.ts +3 -0
  137. package/Storage/Storage.ts +98 -0
  138. package/Storage/index.ts +4 -0
  139. package/Storage/utils.ts +18 -0
  140. package/Support/Array.ts +184 -0
  141. package/Support/Math.ts +12 -0
  142. package/Support/Metadata.ts +31 -0
  143. package/Support/String.ts +38 -0
  144. package/Support/index.ts +19 -0
  145. package/Support/utils.ts +7 -0
  146. package/Testing/TestCase.ts +193 -0
  147. package/Validation/Rule.ts +9 -0
  148. package/Validation/RuleInterface.ts +10 -0
  149. package/Validation/Rules/Email.ts +20 -0
  150. package/Validation/Rules/FileExtension.ts +21 -0
  151. package/Validation/Rules/MaxLength.ts +17 -0
  152. package/Validation/Rules/MinLength.ts +18 -0
  153. package/Validation/Rules/Optional.ts +18 -0
  154. package/Validation/Rules/Required.ts +29 -0
  155. package/Validation/Validator.ts +7 -0
  156. package/Validation/index.ts +12 -0
  157. package/benchmark-index.js +20 -0
  158. package/benchmark.ts +47 -0
  159. package/package.json +7 -3
  160. package/tests/Cache/DatabaseStoreTest.ts +110 -0
  161. package/tests/Cache/FileStoreTest.ts +93 -0
  162. package/tests/Cache/MemoryStoreTest.ts +92 -0
  163. package/tests/Container/ContainerTest.ts +336 -0
  164. package/tests/Database/Drivers/MySQL/AlterTest.ts +100 -0
  165. package/tests/Database/Drivers/MySQL/CreateTest.ts +39 -0
  166. package/tests/Database/Drivers/MysqlDriverTest.ts +140 -0
  167. package/tests/Database/Drivers/SqliteDriverTest.ts +126 -0
  168. package/tests/Database/Entities/Article.ts +21 -0
  169. package/tests/Database/Entities/Profile.ts +21 -0
  170. package/tests/Database/Entities/Role.ts +15 -0
  171. package/tests/Database/Entities/User.ts +35 -0
  172. package/tests/Database/EntityRelationshipsTest.ts +475 -0
  173. package/tests/Database/EntityTest.ts +202 -0
  174. package/tests/Database/Migrations/RunnerTest.ts +96 -0
  175. package/tests/Database/Migrations/migrations/1.createUserTable.ts +12 -0
  176. package/tests/Database/Migrations/migrations/2.createArticlesTable.ts +12 -0
  177. package/tests/Database/QueryTest.ts +573 -0
  178. package/tests/Database/SchemaTest.ts +32 -0
  179. package/tests/Database/Seeders/RunnerTest.ts +30 -0
  180. package/tests/Database/Seeders/seeders/RandomSeeder.ts +10 -0
  181. package/tests/Database/utils.ts +16 -0
  182. package/tests/Forms/FormsTest.ts +59 -0
  183. package/tests/Framework/Middleware/StaticAssetsMiddlewareTest.ts +129 -0
  184. package/tests/Framework/Resolvers/EntityResolverTest.ts +48 -0
  185. package/tests/Framework/Router/RouterTest.ts +33 -0
  186. package/tests/Mail/MailerTest.ts +76 -0
  187. package/tests/Model/ModelTest.ts +183 -0
  188. package/tests/Router/HttpHandlerTest.ts +72 -0
  189. package/tests/Router/RequestTest.ts +77 -0
  190. package/tests/Router/WebSocketsHandlerTest.ts +51 -0
  191. package/tests/Storage/StorageTest.ts +97 -0
  192. package/tests/Support/ArrayTest.ts +167 -0
  193. package/tests/Support/StringTest.ts +18 -0
  194. package/tests/Validation/RulesTest.ts +54 -0
  195. package/types.d.ts +8 -0
@@ -0,0 +1,123 @@
1
+ import { DatabaseDriver } from '../DatabaseDriver'
2
+ import { Connection, ConnectionConfig, createConnection, OkPacket } from 'mysql'
3
+ import { SqlValue } from '../../Types'
4
+ import { Create } from './Create'
5
+ import { Select } from '../SQL/Statements/Select'
6
+ import { Insert } from '../SQL/Statements/Insert'
7
+ import { Delete } from '../SQL/Statements/Delete'
8
+ import { Update } from '../SQL/Statements/Update'
9
+ import { Alter } from '../SQL/Statements/Alter'
10
+ import { SchemaContract } from '../SchemaContract'
11
+ import { Schema } from './Schema'
12
+
13
+ interface ColumnInfo {
14
+ name: string
15
+ type: string
16
+ nullable: 0 | 1
17
+ default?: string | number
18
+ autoIncrement: 0 | 1
19
+ primaryKey: 0 | 1
20
+ }
21
+
22
+ export class MysqlDriver extends DatabaseDriver {
23
+ connection: Connection
24
+
25
+ schema: SchemaContract = new Schema(this)
26
+
27
+ statements = {
28
+ create: Create,
29
+ select: Select,
30
+ insert: Insert,
31
+ delete: Delete,
32
+ update: Update,
33
+ alter: Alter,
34
+ }
35
+
36
+ constructor(connectionConfig: ConnectionConfig) {
37
+ super()
38
+ this.connection = createConnection({
39
+ connectTimeout: 30000,
40
+ ...connectionConfig,
41
+ })
42
+
43
+ this.connection.connect()
44
+ }
45
+
46
+ async truncate(table: string) {
47
+ await this.run(`TRUNCATE TABLE ${table}`)
48
+ }
49
+
50
+ runRaw<T>(query: string, params: SqlValue[] = []): Promise<T | OkPacket> {
51
+ return new Promise((resolve, reject) => {
52
+ this.connection.query(query, params, (error, results, fields) => {
53
+ if (error) {
54
+ error.message += `in SQL: "${error.sql}"`
55
+ return reject(error)
56
+ }
57
+ resolve(results)
58
+ })
59
+ })
60
+ }
61
+
62
+ async run(query: string, params: SqlValue[] = []) {
63
+ await this.runRaw(query, params)
64
+ }
65
+
66
+ async get<T>(query: string, params: SqlValue[] = []): Promise<T[]> {
67
+ return (await this.runRaw(query, params) as T[])
68
+ }
69
+
70
+ async first<T>(query: string, params: SqlValue[] = []): Promise<T> {
71
+ return (await this.runRaw(query, params) as T[])?.[0]
72
+ }
73
+
74
+ async insertOne(query: string, params: SqlValue[] = []): Promise<number> {
75
+ return (await this.runRaw(query, params) as OkPacket).insertId
76
+ }
77
+
78
+ tables() {
79
+ return this.get<{name: string}>(
80
+ `select TABLE_NAME as name
81
+ from information_schema.TABLES
82
+ WHERE table_schema = ?
83
+ `,
84
+ [this.connection.config.database]
85
+ )
86
+ }
87
+
88
+ tableExists(table: string): Promise<boolean> {
89
+ return this.first(
90
+ `select *
91
+ from information_schema.tables
92
+ where table_name = ?
93
+ and table_schema = ?
94
+ `,
95
+ [table, this.connection.config.database]
96
+ )
97
+ }
98
+
99
+ async tableColumns(table: string) {
100
+ const columnsInfo = await this.get<ColumnInfo>(
101
+ `select cast(IS_NULLABLE = 'YES' as unsigned) as nullable,
102
+ cast(EXTRA = 'auto_increment' as unsigned) as autoIncrement,
103
+ cast(COLUMN_KEY = 'PRI' as unsigned) as primaryKey,
104
+ COLUMN_NAME as name,
105
+ DATA_TYPE as type,
106
+ COLUMN_DEFAULT as 'default'
107
+ from information_schema.COLUMNS
108
+ where table_name = ?
109
+ and table_schema = ?
110
+ `,
111
+ [table, this.connection.config.database]
112
+ )
113
+
114
+ return columnsInfo.map(info => ({
115
+ name: info.name,
116
+ type: info.type,
117
+ nullable: Boolean(info.nullable),
118
+ default: info.default,
119
+ autoIncrement: info.autoIncrement,
120
+ primaryKey: Boolean(info.primaryKey)
121
+ }))
122
+ }
123
+ }
@@ -0,0 +1,82 @@
1
+ import { ColumnField, Entity, Expression, PrimaryField } from '../..'
2
+ import { Schema as BaseSchema } from '../SQL/Schema'
3
+ import { Create } from './Create'
4
+ import { typesMatches } from '.'
5
+ import { ColumnDefinitionOptions } from '../SQL'
6
+ import { AddColumn, AddConstraint, Alter, Constraints, DropColumn, ModifyColumn } from './Alter'
7
+
8
+ export class Schema extends BaseSchema {
9
+
10
+ columnMetadataToColumnDefinition(metadata: ColumnField<Entity>) {
11
+ return {
12
+ name: metadata.column,
13
+ primaryKey: metadata.constructor === PrimaryField,
14
+ autoIncrement: Number(metadata.constructor === PrimaryField),
15
+ type: this.getColumnTypeBasedOnTypescriptType(metadata),
16
+ default: undefined,
17
+ nullable: true
18
+ }
19
+ }
20
+
21
+ async create(table: string, columns: ColumnField<Entity>[]) {
22
+ const columnsDefinitions: ColumnDefinitionOptions[] = columns.map(column => this.columnMetadataToColumnDefinition(column))
23
+ const createStatement = new Create(table, columnsDefinitions)
24
+
25
+ await this.driver.run(createStatement.toSQL())
26
+ }
27
+
28
+ protected async syncTableColumns(table: string, columnsMetadata: ColumnField<Entity>[]) {
29
+ const columns = columnsMetadata.map(metadata => this.columnMetadataToColumnDefinition(metadata))
30
+
31
+ const tableColumns = await this.driver.tableColumns(table)
32
+
33
+ let alters: Expression[] = []
34
+
35
+ alters = alters.concat(
36
+ columns
37
+ .whereNotIn('name', tableColumns.pluck('name'))
38
+ .map(column => new AddColumn({...column, autoIncrement: undefined}))
39
+ )
40
+
41
+ columns.whereIn('name', tableColumns.pluck('name'))
42
+ .forEach(column => {
43
+ const typeInDB = tableColumns.findWhere('name', column.name)?.type
44
+ if (typeInDB !== column.type) {
45
+ alters.push(new ModifyColumn(column))
46
+ }
47
+ })
48
+
49
+ alters = alters.concat(
50
+ tableColumns
51
+ .whereNotIn('name', columns.pluck('name'))
52
+ .map(tableColumn => new DropColumn(tableColumn.name))
53
+ )
54
+
55
+ await this.driver.run(new Alter(table, alters).toSQL())
56
+
57
+ const additionalAlters: Expression[] = []
58
+ columns.forEach(column => {
59
+ const dbInfo = tableColumns.findWhere('name', column.name)
60
+ if (column.primaryKey && !dbInfo?.primaryKey) {
61
+ additionalAlters.push(new AddConstraint(column, Constraints.PrimaryKey))
62
+ }
63
+ if (column.autoIncrement && !dbInfo?.autoIncrement) {
64
+ additionalAlters.push(new ModifyColumn(column))
65
+ }
66
+ })
67
+
68
+ if (!additionalAlters.empty()) {
69
+ await this.driver.run(new Alter(table, additionalAlters).toSQL())
70
+ }
71
+ }
72
+
73
+ private getColumnTypeBasedOnTypescriptType(column: ColumnField<Entity>) {
74
+ const typePrototype = Array.from(typesMatches.keys())
75
+ .find(key => column.type().prototype instanceof key) || String
76
+
77
+ return typesMatches.get(column.type())
78
+ ?? typesMatches.get(column.constructor)
79
+ ?? typesMatches.get(typePrototype)
80
+ ?? typesMatches.get(String) as string
81
+ }
82
+ }
@@ -0,0 +1,13 @@
1
+ import { Entity } from '../../Entity'
2
+ import { JSONField, PrimaryField } from '../../Fields'
3
+
4
+ // tslint:disable-next-line:no-any
5
+ export const typesMatches = new Map<any, string>([
6
+ [Entity, 'integer'],
7
+ [PrimaryField, `integer`],
8
+ [Number, 'integer'],
9
+ [String, 'varchar(255)'],
10
+ [Date, 'datetime'],
11
+ [JSONField, 'text'],
12
+ [Boolean, 'integer'],
13
+ ])
@@ -0,0 +1,73 @@
1
+ import { SchemaContract } from '../SchemaContract'
2
+ import { BelongsToManyField, ColumnField, Entity, EntityConstructor, EntityMetadata } from '../..'
3
+
4
+ export abstract class Schema extends SchemaContract {
5
+
6
+ // async create(table: string, columns: ColumnField<Entity>[]) {
7
+ // const columnsSQLs = columns
8
+ // .filter(column => column.column)
9
+ // .map(columnMetadata => this.getColumnSql(columnMetadata))
10
+ //
11
+ // // @ts-ignore
12
+ // const createStatement = new Create({
13
+ // table,
14
+ // columns: columnsSQLs
15
+ // })
16
+ //
17
+ // await this.driver.run(createStatement.toSql())
18
+ // }
19
+
20
+ // async addColumn(table: string, column: ColumnField<Entity>) {
21
+ // await this.driver.run(`ALTER TABLE ${table}
22
+ // ADD ${this.getColumnSql(column)}(6)`)
23
+ // }
24
+
25
+ async synchronize(entitiesMetadata: EntityMetadata<Entity, Entity>[]) {
26
+ const pivotTables = new Map<string, ColumnField<Entity>[]>()
27
+ for await(const metadata of entitiesMetadata) {
28
+ const table = metadata.table as string
29
+ const belongsToManyFields = Object.values(metadata.inverseRelationships)
30
+ .filter(field => field instanceof BelongsToManyField) as BelongsToManyField<Entity, Entity>[]
31
+
32
+ belongsToManyFields.forEach(field => {
33
+ const entity = Entity as EntityConstructor<Entity>
34
+ const pivotTableColumns: ColumnField<Entity>[] = [
35
+ // new ColumnField(entity, 'id', () => PrimaryField, 'id'),
36
+ ...[field.getParentForeignKey(), field.getRelatedForeignKey()].sort().map(columnName => {
37
+ return new ColumnField(entity, columnName, () => Number, columnName)
38
+ })
39
+ ]
40
+ pivotTables.set(field.getPivotTable(), pivotTableColumns)
41
+ })
42
+ const tableInfo = await this.driver.tableExists(table)
43
+ if (!tableInfo) {
44
+ await this.create(table, Object.values({...metadata.columns, ...metadata.relationships}))
45
+ continue
46
+ }
47
+ await this.syncTableColumns(table, Object.values({...metadata.columns, ...metadata.relationships}))
48
+ }
49
+
50
+ for await (const [name, columns] of pivotTables) {
51
+ const tableInfo = await this.driver.tableExists(name)
52
+ if (!tableInfo) {
53
+ await this.create(name, columns)
54
+ continue
55
+ }
56
+ await this.syncTableColumns(name, columns)
57
+ }
58
+ }
59
+
60
+ protected abstract syncTableColumns(table: string, columns: ColumnField<Entity>[]): Promise<void>;
61
+
62
+ // protected getColumnSql(columnMetadata: ColumnField<Entity>): string {
63
+ // const columnType = columnMetadata.type()
64
+ // const type = Array.from(this.typesMatches.keys())
65
+ // .find(key =>
66
+ // key === columnMetadata.type() || columnType.prototype instanceof key || columnMetadata instanceof key
67
+ // ) || String
68
+ //
69
+ // const columnInfo = this.typesMatches.get(type)
70
+ //
71
+ // return `${columnMetadata.column} ${columnInfo}`
72
+ // }
73
+ }
@@ -0,0 +1,22 @@
1
+ import { BaseStatement } from './BaseStatement'
2
+ import { ColumnField, Entity } from '../../..'
3
+
4
+ export class Alter extends BaseStatement {
5
+
6
+ columns: ColumnField<Entity>[] = []
7
+
8
+ getColumns() {
9
+ return ''
10
+ }
11
+
12
+ toSQL() {
13
+ return `
14
+ ALTER
15
+ TABLE
16
+ ${this.table}
17
+ (
18
+ ${this.getColumns()}
19
+ )
20
+ `
21
+ }
22
+ }
@@ -0,0 +1,55 @@
1
+ import { wrap } from '../../../Helpers'
2
+ import { Statement } from '../../Statement'
3
+
4
+ export abstract class BaseStatement extends Statement {
5
+
6
+ get table() {
7
+ return wrap(this.components.table || '')
8
+ }
9
+
10
+ get wheres() {
11
+ const wheres = this.components.wheres || []
12
+
13
+ return wheres.map(where => {
14
+ this.bindings = this.bindings.concat(where.getValues())
15
+ return where.toSql()
16
+ }).join(' ').replace(/^(and |or )/i, '')
17
+ }
18
+
19
+ get limit() {
20
+ const limit = this.components.limit
21
+ return limit ? `LIMIT ${limit.from}` + (limit.count ? `, ${limit.count}` : '') : ''
22
+ }
23
+
24
+ get joins() {
25
+ return this.components.joins.map(join => {
26
+ return `${join.type} JOIN ${wrap(join.table)} ON ${join.first} ${join.operator} ${join.second}`
27
+ }).join(' ')
28
+ }
29
+
30
+ get groups() {
31
+ if (!this.components.groups.length) {
32
+ return ''
33
+ }
34
+
35
+ return `GROUP BY ` + this.components.groups.map(column => wrap(column)).join(', ')
36
+ }
37
+
38
+ get having() {
39
+ return ''
40
+ }
41
+
42
+ get orders() {
43
+ if (!this.components.orders?.length) {
44
+ return ''
45
+ }
46
+
47
+ return 'ORDER BY ' + this.components.orders.map(order => `${wrap(order[0])} ${order[1]}`).join(', ')
48
+ }
49
+
50
+ abstract toSQL(): string;
51
+
52
+ toString() {
53
+ return this.toSQL()
54
+ }
55
+ }
@@ -0,0 +1,20 @@
1
+ import { BaseStatement } from './BaseStatement'
2
+
3
+ export class Delete extends BaseStatement {
4
+
5
+ get wheres() {
6
+ if (!this.components.wheres.length) {
7
+ return ''
8
+ }
9
+
10
+ return `WHERE ` + super.wheres
11
+ }
12
+
13
+ toSQL() {
14
+ return `
15
+ DELETE
16
+ FROM ${this.table} ${this.wheres} ${this.orders} ${this.limit}
17
+ `
18
+ }
19
+
20
+ }
@@ -0,0 +1,24 @@
1
+ import { BaseStatement } from './BaseStatement'
2
+ import { wrap } from '../../../Helpers'
3
+
4
+ export class Insert extends BaseStatement {
5
+
6
+ get columns() {
7
+ const values = this.components.insert || []
8
+
9
+ return wrap(Object.keys(values.first() ?? {}))
10
+ }
11
+
12
+ get values() {
13
+ return (this.components.insert || []).map(valuesMap => {
14
+ const values = Object.values(valuesMap)
15
+ this.bindings = this.bindings.concat(values)
16
+ return '(' + [...Array(values.length)].map(() => '?').join(', ') + ')'
17
+ }).join(', ')
18
+ }
19
+
20
+ toSQL() {
21
+ return `INSERT INTO ${this.table} (${this.columns})
22
+ VALUES ${this.values} `
23
+ }
24
+ }
@@ -0,0 +1,55 @@
1
+ import { BaseStatement } from './BaseStatement'
2
+ import { StringExpression } from '../../..'
3
+ import { wrap } from '../../../Helpers'
4
+
5
+ export class Select extends BaseStatement {
6
+ get distinct() {
7
+ return this.components.distinct ? 'DISTINCT ' : ''
8
+ }
9
+
10
+ get columns() {
11
+ let columns = this.components.columns || []
12
+ columns = columns.map(column => {
13
+ return column instanceof StringExpression ? column.value : wrap(column)
14
+ })
15
+
16
+ const aggregate = this.components.aggregate
17
+ if (aggregate) {
18
+ columns.push(`${aggregate.function}(${wrap(aggregate.columns) || '*'}) as aggregate`)
19
+ }
20
+
21
+ if (!columns.length) {
22
+ return '*'
23
+ }
24
+
25
+ return columns.join(', ')
26
+ }
27
+
28
+ get wheres() {
29
+ if (!this.components.wheres.length) {
30
+ return ''
31
+ }
32
+
33
+ return `WHERE ${super.wheres}`
34
+ }
35
+
36
+ toSQL() {
37
+ return `
38
+ SELECT ${this.distinct}${this.columns}
39
+ FROM ${this.table} ${this.joins} ${this.wheres} ${this.groups}
40
+ ${this.having}
41
+ ${this.orders}
42
+ ${this.limit}
43
+ `
44
+ }
45
+ }
46
+
47
+ /*
48
+ const query = `SELECT *
49
+ FROM users
50
+ where name = 'john' -- where basic
51
+ or (age in (1, 2, 3) and age > 2) -- where nested
52
+ or age in (1, 2, 3) -- where in
53
+ or age in (SELECT age from users) -- where subSelect
54
+ `;
55
+ */
@@ -0,0 +1,33 @@
1
+ import { BaseStatement } from './BaseStatement'
2
+
3
+ export class Update extends BaseStatement {
4
+
5
+ get wheres() {
6
+ if (!this.components.wheres.length) {
7
+ return ''
8
+ }
9
+
10
+ return 'WHERE ' + super.wheres
11
+ }
12
+
13
+ get columns() {
14
+ const columns = []
15
+ const values = this.components.update || {}
16
+ for (const column in values) {
17
+ if (values.hasOwnProperty(column)) {
18
+ this.bindings.push(values[column])
19
+ columns.push(`${column} = ?`)
20
+ }
21
+ }
22
+ return columns.join(', ')
23
+ }
24
+
25
+ toSQL() {
26
+ return `
27
+ UPDATE
28
+ ${this.table}
29
+ SET ${this.columns}
30
+ ${this.wheres} ${this.orders} ${this.limit}
31
+ `
32
+ }
33
+ }
@@ -0,0 +1,8 @@
1
+ export interface ColumnDefinitionOptions {
2
+ name: string
3
+ type: string
4
+ nullable?: boolean
5
+ default?: string | number
6
+ autoIncrement?: number
7
+ primaryKey?: boolean
8
+ }
@@ -0,0 +1,45 @@
1
+ import { BaseStatement } from '../SQL/Statements/BaseStatement'
2
+ import { ColumnDefinitionOptions } from '../SQL'
3
+
4
+ export class Create extends BaseStatement {
5
+
6
+ constructor(table: string, public columns: ColumnDefinitionOptions[] = []) {
7
+ super({
8
+ table,
9
+ columns: [],
10
+ joins: [],
11
+ wheres: [],
12
+ groups: [],
13
+ orders: [],
14
+ having: []
15
+ })
16
+ }
17
+
18
+ toSQL() {
19
+ const definitions: string[] = this.getColumnsSQL(this.columns)
20
+
21
+ return `
22
+ CREATE TABLE ${this.table}
23
+ (
24
+ ${definitions.join(', \n')}
25
+ )
26
+ `
27
+ }
28
+
29
+ private getColumnsSQL(columns: ColumnDefinitionOptions[]) {
30
+ return columns.map(this.getColumnSQL)
31
+ }
32
+
33
+ private getColumnSQL(column: ColumnDefinitionOptions) {
34
+ const sqlParts = [column.name, column.type]
35
+
36
+ if (column.primaryKey) {
37
+ sqlParts.push('PRIMARY KEY')
38
+ }
39
+ if (column.autoIncrement) {
40
+ sqlParts.push('AUTOINCREMENT')
41
+ }
42
+
43
+ return sqlParts.join(' ')
44
+ }
45
+ }
@@ -0,0 +1,54 @@
1
+ import { ColumnField, Entity, PrimaryField } from '../..'
2
+ import { Schema as BaseSchema } from '../SQL/Schema'
3
+ import { Create } from './Create'
4
+ import { typesMatches } from '.'
5
+ import { ColumnDefinitionOptions } from '../SQL'
6
+ import { wrap } from '../../Helpers'
7
+
8
+ export class Schema extends BaseSchema {
9
+
10
+ columnMetadataToColumnDefinition(metadata: ColumnField<Entity>) {
11
+ return {
12
+ name: metadata.column,
13
+ primaryKey: metadata.constructor === PrimaryField,
14
+ autoIncrement: Number(metadata.constructor === PrimaryField),
15
+ type: this.getColumnTypeBasedOnTypescriptType(metadata),
16
+ default: undefined,
17
+ nullable: true
18
+ }
19
+ }
20
+
21
+ async create(table: string, columns: ColumnField<Entity>[]) {
22
+ const columnsDefinitions: ColumnDefinitionOptions[] = columns.map(column => this.columnMetadataToColumnDefinition(column))
23
+ const createStatement = new Create(table, columnsDefinitions)
24
+
25
+ await this.driver.run(createStatement.toSQL())
26
+ }
27
+
28
+ protected async syncTableColumns(table: string, columnsMetadata: ColumnField<Entity>[]) {
29
+ const columns = columnsMetadata.map(metadata => this.columnMetadataToColumnDefinition(metadata))
30
+ const tableColumns = await this.driver.tableColumns(table)
31
+
32
+ if (tableColumns.length) {
33
+ const temporaryTableName = table + '_alter_tmp'
34
+ await this.create(temporaryTableName, columnsMetadata)
35
+ const columnList = wrap(columns.whereIn('name', tableColumns.pluck('name')).pluck('name'))
36
+ await this.driver.run(`INSERT INTO ${temporaryTableName}(${columnList})
37
+ SELECT ${columnList}
38
+ FROM ${table}`)
39
+ await this.driver.run(`DROP TABLE ${table}`)
40
+ await this.driver.run(`ALTER TABLE ${temporaryTableName} RENAME TO ${table}`)
41
+ }
42
+ }
43
+
44
+ private getColumnTypeBasedOnTypescriptType(column: ColumnField<Entity>) {
45
+ const typePrototype = Array.from(typesMatches.keys())
46
+ .find(key => column.type().prototype instanceof key) || String
47
+
48
+ return typesMatches.get(column.type())
49
+ ?? typesMatches.get(column.constructor)
50
+ ?? typesMatches.get(typePrototype)
51
+ ?? typesMatches.get(String) as string
52
+ }
53
+
54
+ }