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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "forj",
3
3
  "description": "SQLite ORM and Query Builder whitout dependencies",
4
- "version": "0.0.5",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
7
7
  "files": ["src"],
@@ -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: 'BIGINT', unsigned: true, autoIncrement: true, primary: true, nullable: false })
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
- integer(name: string) {
37
+ int(name: string) {
38
38
  return this.#column({ name, type: 'INTEGER', nullable: false })
39
39
  }
40
-
41
- bigInteger(name: string) {
42
- return this.#column({ name, type: 'BIGINT', nullable: false })
40
+ integer(name: string) {
41
+ return this.int(name)
43
42
  }
44
-
45
- tinyInteger(name: string) {
46
- return this.#column({ name, type: 'TINYINT', nullable: false })
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: 'BOOLEAN', nullable: false })
59
+ return this.#column({ name, type: 'INTEGER', nullable: false })
51
60
  }
52
61
 
53
- decimal(name: string, precision: number = 8, scale: number = 2) {
54
- return this.#column({ name, type: `DECIMAL(${precision},${scale})`, nullable: false })
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
- return this.#column({ name, type: 'FLOAT', nullable: false })
59
- }
66
+ // float(name: string) {
67
+ // return this.#column({ name, type: 'FLOAT', nullable: false })
68
+ // }
60
69
 
61
- double(name: string) {
62
- return this.#column({ name, type: 'DOUBLE', nullable: false })
63
- }
70
+ // double(name: string) {
71
+ // return this.#column({ name, type: 'DOUBLE', nullable: false })
72
+ // }
64
73
 
65
- date(name: string) {
66
- return this.#column({ name, type: 'DATE', nullable: false })
67
- }
74
+ // date(name: string) {
75
+ // return this.#column({ name, type: 'DATE', nullable: false })
76
+ // }
68
77
 
69
- dateTime(name: string) {
70
- return this.#column({ name, type: 'DATETIME', nullable: false })
71
- }
78
+ // dateTime(name: string) {
79
+ // return this.#column({ name, type: 'DATETIME', nullable: false })
80
+ // }
72
81
 
73
- timestamp(name: string) {
74
- return this.#column({ name, type: 'TIMESTAMP', nullable: false })
75
- }
82
+ // timestamp(name: string) {
83
+ // return this.#column({ name, type: 'TIMESTAMP', nullable: false })
84
+ // }
76
85
 
77
- time(name: string) {
78
- return this.#column({ name, type: 'TIME', nullable: false })
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.timestamp('created_at')
91
- this.timestamp('updated_at')
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.timestamp(name).nullable()
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.unsigned)
57
- sql += ' UNSIGNED'
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
- sql += ` COMMENT '${column.comment.replace(/'/g, "''")}'`
86
+ // if (column.comment)
87
+ // sql += ` COMMENT '${column.comment.replace(/'/g, "''")}'`
88
88
 
89
89
  return sql
90
90
  }
@@ -1,5 +1,4 @@
1
1
  export { Migration } from './migration'
2
2
  export { Migrator } from './migrator'
3
-
4
3
  export { Schema } from './schema'
5
4
  export { Blueprint } from './blueprint'
@@ -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 #dir: string = ''
12
- static #folder: string = 'migrations'
13
- static #sqlFolder: string = 'sql'
11
+ static #input: string = join(__root, 'migrations')
12
+ static #output: string = join(__root, 'migrations', 'sql')
14
13
 
15
- static dir(dir: string) {
16
- this.#dir = dir
14
+ static inputDir(dir: string) {
15
+ this.#input = join(__root, dir)
17
16
  return this
18
17
  }
19
-
20
- static folder(dir: string) {
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 toSql(outputDir: string = '') {
26
- const dir = this.#dir || join(__root, this.#folder)
27
- outputDir ||= join(dir, this.#sqlFolder)
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
- const sql = await this.run(info.handler)
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
- const name = fileName.replace(/\.[jt]s$/, '')
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 mod = await IMPORT(join(__root, fileName))
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: name.split('/').at(-1) as string,
84
- fileName,
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 #toClassName(name: string) {
91
- return name
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
- name.replace(/([A-Z])/g, '_$1') // snake_case
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) {
@@ -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 {