forj 0.0.5 → 0.0.7
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/package.json
CHANGED
|
@@ -23,7 +23,7 @@ export class Blueprint {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
id(name: string = 'id') { // Auto-increment ID (bigint unsigned)
|
|
26
|
-
return this.#column({ name, type: '
|
|
26
|
+
return this.#column({ name, type: 'INTEGER', autoIncrement: true, primary: true, nullable: false })
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
string(name: string, length: number = 255) {
|
|
@@ -34,49 +34,58 @@ export class Blueprint {
|
|
|
34
34
|
return this.#column({ name, type: 'TEXT', nullable: false })
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
int(name: string) {
|
|
38
38
|
return this.#column({ name, type: 'INTEGER', nullable: false })
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return this.#column({ name, type: 'BIGINT', nullable: false })
|
|
40
|
+
integer(name: string) {
|
|
41
|
+
return this.int(name)
|
|
43
42
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
real(name: string) {
|
|
44
|
+
return this.#column({ name, type: 'REAL', nullable: false })
|
|
45
|
+
}
|
|
46
|
+
numeric(name: string) {
|
|
47
|
+
return this.#column({ name, type: 'NUMERIC', nullable: false })
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
// bigInteger(name: string) {
|
|
51
|
+
// return this.#column({ name, type: 'BIGINT', nullable: false })
|
|
52
|
+
// }
|
|
53
|
+
|
|
54
|
+
// tinyInteger(name: string) {
|
|
55
|
+
// return this.#column({ name, type: 'TINYINT', nullable: false })
|
|
56
|
+
// }
|
|
57
|
+
|
|
49
58
|
boolean(name: string) {
|
|
50
|
-
return this.#column({ name, type: '
|
|
59
|
+
return this.#column({ name, type: 'INTEGER', nullable: false })
|
|
51
60
|
}
|
|
52
61
|
|
|
53
|
-
decimal(name: string, precision: number = 8, scale: number = 2) {
|
|
54
|
-
|
|
55
|
-
}
|
|
62
|
+
// decimal(name: string, precision: number = 8, scale: number = 2) {
|
|
63
|
+
// return this.#column({ name, type: `DECIMAL(${precision},${scale})`, nullable: false })
|
|
64
|
+
// }
|
|
56
65
|
|
|
57
|
-
float(name: string) {
|
|
58
|
-
|
|
59
|
-
}
|
|
66
|
+
// float(name: string) {
|
|
67
|
+
// return this.#column({ name, type: 'FLOAT', nullable: false })
|
|
68
|
+
// }
|
|
60
69
|
|
|
61
|
-
double(name: string) {
|
|
62
|
-
|
|
63
|
-
}
|
|
70
|
+
// double(name: string) {
|
|
71
|
+
// return this.#column({ name, type: 'DOUBLE', nullable: false })
|
|
72
|
+
// }
|
|
64
73
|
|
|
65
|
-
date(name: string) {
|
|
66
|
-
|
|
67
|
-
}
|
|
74
|
+
// date(name: string) {
|
|
75
|
+
// return this.#column({ name, type: 'DATE', nullable: false })
|
|
76
|
+
// }
|
|
68
77
|
|
|
69
|
-
dateTime(name: string) {
|
|
70
|
-
|
|
71
|
-
}
|
|
78
|
+
// dateTime(name: string) {
|
|
79
|
+
// return this.#column({ name, type: 'DATETIME', nullable: false })
|
|
80
|
+
// }
|
|
72
81
|
|
|
73
|
-
timestamp(name: string) {
|
|
74
|
-
|
|
75
|
-
}
|
|
82
|
+
// timestamp(name: string) {
|
|
83
|
+
// return this.#column({ name, type: 'TIMESTAMP', nullable: false })
|
|
84
|
+
// }
|
|
76
85
|
|
|
77
|
-
time(name: string) {
|
|
78
|
-
|
|
79
|
-
}
|
|
86
|
+
// time(name: string) {
|
|
87
|
+
// return this.#column({ name, type: 'TIME', nullable: false })
|
|
88
|
+
// }
|
|
80
89
|
|
|
81
90
|
json(name: string) {
|
|
82
91
|
return this.#column({ name, type: 'JSON', nullable: false })
|
|
@@ -86,14 +95,18 @@ export class Blueprint {
|
|
|
86
95
|
return this.#column({ name, type: `ENUM(${values.map(v => `'${v}'`).join(', ')})`, nullable: false })
|
|
87
96
|
}
|
|
88
97
|
|
|
98
|
+
blob(name: string) {
|
|
99
|
+
return this.#column({ name, type: 'BLOB', nullable: false })
|
|
100
|
+
}
|
|
101
|
+
|
|
89
102
|
timestamps() {
|
|
90
|
-
this
|
|
91
|
-
this
|
|
103
|
+
this.#column({ name: 'created_at', type: 'TEXT', nullable: false })
|
|
104
|
+
this.#column({ name: 'updated_at', type: 'TEXT', nullable: false })
|
|
92
105
|
return this
|
|
93
106
|
}
|
|
94
107
|
|
|
95
108
|
softDelete(name: string = 'deleted_at') {
|
|
96
|
-
this
|
|
109
|
+
this.#column({ name, type: 'TEXT', nullable: true })
|
|
97
110
|
return this
|
|
98
111
|
}
|
|
99
112
|
|
|
@@ -53,8 +53,11 @@ export default class SchemaBuilder {
|
|
|
53
53
|
if (column.length && !column.type.includes('('))
|
|
54
54
|
sql += `(${column.length})`
|
|
55
55
|
|
|
56
|
-
if (column.
|
|
57
|
-
sql += '
|
|
56
|
+
if (column.autoIncrement)
|
|
57
|
+
sql += ' AUTOINCREMENT'
|
|
58
|
+
|
|
59
|
+
// if (column.unsigned)
|
|
60
|
+
// sql += ' UNSIGNED'
|
|
58
61
|
|
|
59
62
|
if (column.nullable) {
|
|
60
63
|
sql += ' NULL'
|
|
@@ -62,9 +65,6 @@ export default class SchemaBuilder {
|
|
|
62
65
|
sql += ' NOT NULL'
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
if (column.autoIncrement)
|
|
66
|
-
sql += ' AUTO_INCREMENT'
|
|
67
|
-
|
|
68
68
|
if (column.default !== undefined) {
|
|
69
69
|
if (column.default === null) {
|
|
70
70
|
sql += ' DEFAULT NULL'
|
|
@@ -83,8 +83,8 @@ export default class SchemaBuilder {
|
|
|
83
83
|
if (column.primary)
|
|
84
84
|
sql += ' PRIMARY KEY'
|
|
85
85
|
|
|
86
|
-
if (column.comment)
|
|
87
|
-
|
|
86
|
+
// if (column.comment)
|
|
87
|
+
// sql += ` COMMENT '${column.comment.replace(/'/g, "''")}'`
|
|
88
88
|
|
|
89
89
|
return sql
|
|
90
90
|
}
|
package/src/migrations/index.ts
CHANGED
|
@@ -3,44 +3,45 @@ import { mkdirSync, existsSync, writeFileSync } from 'node:fs'
|
|
|
3
3
|
import { dirname, join, resolve } from 'node:path'
|
|
4
4
|
import { Datte, IMPORT } from 't0n'
|
|
5
5
|
import { Schema } from './schema'
|
|
6
|
-
import { MigrationInfo, MigrationClass } from './types'
|
|
6
|
+
import { MigrationInfo, MigrationClass, Queue } from './types'
|
|
7
7
|
|
|
8
8
|
const __root = resolve(dirname(new URL(import.meta.url).pathname), '../../../..')
|
|
9
9
|
|
|
10
10
|
export class Migrator {
|
|
11
|
-
static #
|
|
12
|
-
static #
|
|
13
|
-
static #sqlFolder: string = 'sql'
|
|
11
|
+
static #input: string = join(__root, 'migrations')
|
|
12
|
+
static #output: string = join(__root, 'migrations', 'sql')
|
|
14
13
|
|
|
15
|
-
static
|
|
16
|
-
this.#
|
|
14
|
+
static inputDir(dir: string) {
|
|
15
|
+
this.#input = join(__root, dir)
|
|
17
16
|
return this
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.#dir = dir
|
|
18
|
+
static outputDir(dir: string) {
|
|
19
|
+
this.#output = join(__root, dir)
|
|
22
20
|
return this
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
static async
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.#ensureDir(dir)
|
|
30
|
-
this.#ensureDir(outputDir)
|
|
31
|
-
const files = (await glob(join(dir, '/*.{ts,js}'))).filter(file => !file.includes('.d.')) // TODO: sort
|
|
23
|
+
static async queue() {
|
|
24
|
+
this.#ensureDir(this.#input)
|
|
25
|
+
const files = await glob(join(this.#input, '/*.{ts,js}'))
|
|
26
|
+
const list: Queue = {'pending': [], 'migrated': []}
|
|
32
27
|
|
|
33
28
|
for (const file of files) {
|
|
29
|
+
if (file.includes('.d.')) continue
|
|
34
30
|
const info = await this.#info(file)
|
|
35
|
-
if (!info)
|
|
36
|
-
continue // TODO: trigger a warn
|
|
31
|
+
if (!info) continue // TODO: trigger a warn
|
|
37
32
|
|
|
38
|
-
|
|
39
|
-
const path = join(outputDir, info.name +'.sql')
|
|
40
|
-
if (!existsSync(path))
|
|
41
|
-
writeFileSync(path, `-- Migration: ${info.name}\n\n${sql}\n`)
|
|
33
|
+
list[info.migrated ? 'migrated' : 'pending'].push(info)
|
|
42
34
|
}
|
|
43
35
|
|
|
36
|
+
return list
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
static async compile(migrations: MigrationInfo[]) {
|
|
40
|
+
for (const migration of migrations) {
|
|
41
|
+
const sql = await this.run(migration.handler)
|
|
42
|
+
if (!existsSync(migration.output))
|
|
43
|
+
writeFileSync(migration.output, `-- Migration: ${migration.name}\n\n${sql}\n`)
|
|
44
|
+
}
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
static async run(handler: MigrationClass) {
|
|
@@ -62,13 +63,16 @@ export class Migrator {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
static async #info(fileName: string): Promise<MigrationInfo | null> {
|
|
65
|
-
|
|
66
|
+
let name = fileName.replace(/\.[jt]s$/, '')
|
|
66
67
|
const match = name.match(/\/(\d{4})_(\d{2})_(\d{2})_(\d{2})(\d{2})(\d{2})_(.+)$/)
|
|
68
|
+
name = name.split('/').at(-1) as string
|
|
67
69
|
|
|
68
70
|
if (!match) return null
|
|
69
71
|
const [, year, month, day, hour, minute, second, slugName] = match
|
|
70
72
|
|
|
71
|
-
const
|
|
73
|
+
const input = join(__root, fileName)
|
|
74
|
+
const output = join(this.#output, name +'.sql')
|
|
75
|
+
const mod = await IMPORT(input)
|
|
72
76
|
const handler = mod.default as MigrationClass
|
|
73
77
|
|
|
74
78
|
return {
|
|
@@ -80,28 +84,36 @@ export class Migrator {
|
|
|
80
84
|
parseInt(minute),
|
|
81
85
|
parseInt(second)
|
|
82
86
|
).getTime(),
|
|
83
|
-
name
|
|
84
|
-
|
|
85
|
-
className: this.#toClassName(slugName),
|
|
87
|
+
name,
|
|
88
|
+
className: this.className(slugName),
|
|
86
89
|
handler,
|
|
90
|
+
input,
|
|
91
|
+
output,
|
|
92
|
+
migrated: existsSync(output)
|
|
87
93
|
}
|
|
88
94
|
}
|
|
89
95
|
|
|
90
|
-
static
|
|
91
|
-
|
|
96
|
+
static className(name: string) {
|
|
97
|
+
const lastSlashIndex = name.lastIndexOf('/')
|
|
98
|
+
const fileName = lastSlashIndex >= 0 ? name.substring(lastSlashIndex + 1) : name
|
|
99
|
+
|
|
100
|
+
const dotIndex = fileName.lastIndexOf('.')
|
|
101
|
+
const baseName = dotIndex > 0 ? fileName.substring(0, dotIndex) : fileName
|
|
102
|
+
|
|
103
|
+
return baseName
|
|
92
104
|
.split(/[-_.]/)
|
|
93
105
|
.map(s => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())
|
|
94
106
|
.join('')
|
|
107
|
+
+ Datte.dateTime().replace(/[:.\-\s_]/g, '')
|
|
95
108
|
}
|
|
96
109
|
|
|
97
110
|
static fileName(name: string) {
|
|
98
111
|
return (
|
|
99
|
-
|
|
112
|
+
Datte.dateTime().replace(/[.-\s]/g, '_').replace(/\:/g, '')
|
|
113
|
+
+'_'+ name.replace(/([A-Z])/g, '_$1') // snake_case
|
|
100
114
|
.replace(/\s+/g, '_')
|
|
101
115
|
.toLowerCase()
|
|
102
|
-
|
|
103
|
-
+ Datte.dateTime().replace(/[:.-]/g, '_').replace(/_+/g, '_')
|
|
104
|
-
).replace(/^_+|_+$/g, '')
|
|
116
|
+
).replace(/_+/g, '_')
|
|
105
117
|
}
|
|
106
118
|
|
|
107
119
|
static #ensureDir(dir: string) {
|
package/src/migrations/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Blueprint } from './blueprint'
|
|
2
|
-
import { Migration } from './migration'
|
|
2
|
+
// import { Migration } from './migration'
|
|
3
3
|
|
|
4
4
|
export type BlueprintFn = (table: Blueprint) => void
|
|
5
5
|
|
|
@@ -34,12 +34,17 @@ export interface ForeignKeyDefinition {
|
|
|
34
34
|
onUpdate?: 'cascade' | 'set null' | 'restrict' | 'no action',
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
export type QueueType = 'pending' | 'migrated'
|
|
38
|
+
export type Queue = Record<QueueType, MigrationInfo[]>
|
|
39
|
+
|
|
37
40
|
export interface MigrationInfo {
|
|
38
41
|
timestamp: number,
|
|
39
42
|
name: string,
|
|
40
|
-
fileName: string,
|
|
41
43
|
className: string,
|
|
42
44
|
handler: MigrationClass,
|
|
45
|
+
input: string,
|
|
46
|
+
output: string,
|
|
47
|
+
migrated: boolean,
|
|
43
48
|
}
|
|
44
49
|
|
|
45
50
|
export interface MigrationRecord {
|