mevn-orm 3.2.2 → 4.0.0
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/README.md +361 -39
- package/index.ts +31 -0
- package/initDb.ts +112 -0
- package/knexfile.ts +46 -0
- package/package.json +18 -15
- package/pnpm-workspace.yaml +1 -0
- package/scripts/migrate.ts +97 -0
- package/src/config.ts +270 -0
- package/src/model.ts +301 -0
- package/src/relationships.ts +93 -0
- package/tsconfig.json +27 -0
- package/types/pluralize.d.ts +4 -0
- package/index.js +0 -3
- package/knexfile.js +0 -46
- package/lib/model.js +0 -252
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Knex } from 'knex'
|
|
2
|
+
|
|
3
|
+
type Row = Record<string, unknown>
|
|
4
|
+
|
|
5
|
+
interface RelationshipModel {
|
|
6
|
+
[key: string]: unknown
|
|
7
|
+
table: string
|
|
8
|
+
modelName: string
|
|
9
|
+
id?: number | string
|
|
10
|
+
stripColumns<T extends RelationshipModel>(model: T, keepInternalState?: boolean): T
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type RelatedModelCtor = new (properties?: Row) => RelationshipModel
|
|
14
|
+
|
|
15
|
+
interface RelationshipMethods {
|
|
16
|
+
hasOne(
|
|
17
|
+
this: RelationshipModel,
|
|
18
|
+
Related: RelatedModelCtor,
|
|
19
|
+
localKey?: number | string,
|
|
20
|
+
foreignKey?: string,
|
|
21
|
+
): Promise<RelationshipModel | null>
|
|
22
|
+
hasMany(
|
|
23
|
+
this: RelationshipModel,
|
|
24
|
+
Related: RelatedModelCtor,
|
|
25
|
+
localKey?: number | string,
|
|
26
|
+
foreignKey?: string,
|
|
27
|
+
): Promise<RelationshipModel[]>
|
|
28
|
+
belongsTo(
|
|
29
|
+
this: RelationshipModel,
|
|
30
|
+
Related: RelatedModelCtor,
|
|
31
|
+
foreignKey?: string,
|
|
32
|
+
ownerKey?: string,
|
|
33
|
+
): Promise<RelationshipModel | null>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Builds relationship methods that run against the active Knex instance. */
|
|
37
|
+
const createRelationshipMethods = (getDB: () => Knex): RelationshipMethods => ({
|
|
38
|
+
async hasOne(this: RelationshipModel, Related: RelatedModelCtor, localKey?: number | string, foreignKey?: string) {
|
|
39
|
+
const table = new Related().table
|
|
40
|
+
const relation: Row = {}
|
|
41
|
+
const keyValue = localKey ?? this.id
|
|
42
|
+
const relationKey = foreignKey ?? `${this.modelName}_id`
|
|
43
|
+
|
|
44
|
+
if (keyValue !== undefined) {
|
|
45
|
+
relation[relationKey] = keyValue
|
|
46
|
+
const result = await getDB()(table).where(relation).first<Row>()
|
|
47
|
+
if (result) {
|
|
48
|
+
const related = new Related(result)
|
|
49
|
+
return related.stripColumns(related)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return null
|
|
54
|
+
},
|
|
55
|
+
async hasMany(this: RelationshipModel, Related: RelatedModelCtor, localKey?: number | string, foreignKey?: string) {
|
|
56
|
+
const table = new Related().table
|
|
57
|
+
const relation: Row = {}
|
|
58
|
+
const keyValue = localKey ?? this.id
|
|
59
|
+
const relationKey = foreignKey ?? `${this.modelName}_id`
|
|
60
|
+
|
|
61
|
+
if (keyValue === undefined) {
|
|
62
|
+
return []
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
relation[relationKey] = keyValue
|
|
66
|
+
const rows = await getDB()(table).where(relation).select<Row[]>('*')
|
|
67
|
+
return rows.map((row) => {
|
|
68
|
+
const related = new Related(row)
|
|
69
|
+
return related.stripColumns(related)
|
|
70
|
+
})
|
|
71
|
+
},
|
|
72
|
+
async belongsTo(this: RelationshipModel, Related: RelatedModelCtor, foreignKey?: string, ownerKey = 'id') {
|
|
73
|
+
const table = new Related().table
|
|
74
|
+
const relation: Row = {}
|
|
75
|
+
const relationKey = foreignKey ?? `${new Related().modelName}_id`
|
|
76
|
+
const relationValue = this[relationKey]
|
|
77
|
+
|
|
78
|
+
if (relationValue === undefined || relationValue === null) {
|
|
79
|
+
return null
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
relation[ownerKey] = relationValue
|
|
83
|
+
const row = await getDB()(table).where(relation).first<Row>()
|
|
84
|
+
if (!row) {
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const related = new Related(row)
|
|
89
|
+
return related.stripColumns(related)
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
export { createRelationshipMethods }
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"noImplicitOverride": true,
|
|
8
|
+
"noUncheckedIndexedAccess": true,
|
|
9
|
+
"exactOptionalPropertyTypes": true,
|
|
10
|
+
"useUnknownInCatchVariables": true,
|
|
11
|
+
"noEmit": true,
|
|
12
|
+
"skipLibCheck": false,
|
|
13
|
+
"esModuleInterop": false,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"types": [
|
|
16
|
+
"node",
|
|
17
|
+
"vitest/globals"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"include": [
|
|
21
|
+
"**/*.ts",
|
|
22
|
+
"**/*.d.ts"
|
|
23
|
+
],
|
|
24
|
+
"exclude": [
|
|
25
|
+
"node_modules"
|
|
26
|
+
]
|
|
27
|
+
}
|
package/index.js
DELETED
package/knexfile.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
module.exports = {
|
|
2
|
-
|
|
3
|
-
development: {
|
|
4
|
-
client: 'sqlite3',
|
|
5
|
-
connection: {
|
|
6
|
-
filename: './dev.sqlite'
|
|
7
|
-
},
|
|
8
|
-
useNullAsDefault: true,
|
|
9
|
-
migrations: {
|
|
10
|
-
tableName: 'migrations',
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
staging: {
|
|
15
|
-
client: process.env.DB_CLIENT || 'mysql2',
|
|
16
|
-
connection: {
|
|
17
|
-
database: process.env.DB_DATABASE || 'my_db',
|
|
18
|
-
user: process.env.DB_USER || 'username',
|
|
19
|
-
password: process.env.DB_PASSWORD || 'password'
|
|
20
|
-
},
|
|
21
|
-
pool: {
|
|
22
|
-
min: 2,
|
|
23
|
-
max: 10
|
|
24
|
-
},
|
|
25
|
-
migrations: {
|
|
26
|
-
tableName: 'migrations'
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
production: {
|
|
31
|
-
client: process.env.DB_CLIENT || 'mysql2',
|
|
32
|
-
connection: {
|
|
33
|
-
database: process.env.DB_DATABASE || 'my_db',
|
|
34
|
-
user: process.env.DB_USER || 'username',
|
|
35
|
-
password: process.env.DB_PASSWORD || 'password'
|
|
36
|
-
},
|
|
37
|
-
pool: {
|
|
38
|
-
min: 2,
|
|
39
|
-
max: 10
|
|
40
|
-
},
|
|
41
|
-
migrations: {
|
|
42
|
-
tableName: 'migrations'
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
}
|
package/lib/model.js
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const knex = require('knex').knex
|
|
3
|
-
|
|
4
|
-
let fileName
|
|
5
|
-
|
|
6
|
-
if (fs.existsSync(process.cwd() + '/knexfile.js')) {
|
|
7
|
-
fileName = process.cwd() + '/knexfile.js'
|
|
8
|
-
} else {
|
|
9
|
-
fileName = process.cwd() + '/knexfile.cjs'
|
|
10
|
-
}
|
|
11
|
-
const { development, staging, production } = require(fileName)
|
|
12
|
-
|
|
13
|
-
const pluralize = require('pluralize')
|
|
14
|
-
let config
|
|
15
|
-
switch (process.env.NODE_ENV) {
|
|
16
|
-
case 'testing':
|
|
17
|
-
config = development
|
|
18
|
-
break
|
|
19
|
-
case 'development':
|
|
20
|
-
config = development
|
|
21
|
-
break
|
|
22
|
-
case 'staging':
|
|
23
|
-
config = staging
|
|
24
|
-
break
|
|
25
|
-
default:
|
|
26
|
-
config = production
|
|
27
|
-
break
|
|
28
|
-
}
|
|
29
|
-
const DB = knex(config)
|
|
30
|
-
class Model {
|
|
31
|
-
#private
|
|
32
|
-
static currentTable = pluralize(this.name.toLowerCase())
|
|
33
|
-
static currentQuery
|
|
34
|
-
constructor(properties) {
|
|
35
|
-
for (const key in properties) {
|
|
36
|
-
this[key] = properties[key]
|
|
37
|
-
}
|
|
38
|
-
this.fillable = []
|
|
39
|
-
this.hidden = []
|
|
40
|
-
this.#private = ['fillable', 'hidden']
|
|
41
|
-
this.modelName = this.constructor.name.toLowerCase()
|
|
42
|
-
this.table = pluralize(this.constructor.name.toLowerCase())
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Save a model to the database
|
|
47
|
-
*/
|
|
48
|
-
async save() {
|
|
49
|
-
try {
|
|
50
|
-
let rows = {}
|
|
51
|
-
this.fillable.forEach((f) => {
|
|
52
|
-
rows[f] = this[f]
|
|
53
|
-
})
|
|
54
|
-
const [id] = await DB(this.table)
|
|
55
|
-
.insert(rows)
|
|
56
|
-
const fields = await DB(this.table).where({ id }).first()
|
|
57
|
-
for (const f in fields) {
|
|
58
|
-
this[f] = fields[f]
|
|
59
|
-
}
|
|
60
|
-
this['id'] = id
|
|
61
|
-
return this.stripColumns(this)
|
|
62
|
-
} catch (error) {
|
|
63
|
-
throw new Error(error)
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Update a model
|
|
69
|
-
* @param {*} properties
|
|
70
|
-
* @returns this
|
|
71
|
-
*/
|
|
72
|
-
async update(properties) {
|
|
73
|
-
try {
|
|
74
|
-
const id = await DB(this.table)
|
|
75
|
-
.where({ id: this.id })
|
|
76
|
-
.update(properties)
|
|
77
|
-
const fields = await DB(this.table).where({ id }).first()
|
|
78
|
-
// console.log(fields)
|
|
79
|
-
return this.stripColumns(new this.constructor(fields))
|
|
80
|
-
} catch (error) {
|
|
81
|
-
throw new Error(error)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Delete a model
|
|
87
|
-
* @returns this
|
|
88
|
-
*/
|
|
89
|
-
async delete() {
|
|
90
|
-
try {
|
|
91
|
-
await DB(this.table)
|
|
92
|
-
.where({ id: this.id })
|
|
93
|
-
.del()
|
|
94
|
-
return
|
|
95
|
-
} catch (error) {
|
|
96
|
-
throw new Error(error)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Update a model
|
|
103
|
-
* @param {*} properties
|
|
104
|
-
* @returns
|
|
105
|
-
*/
|
|
106
|
-
static async update(properties) {
|
|
107
|
-
try {
|
|
108
|
-
if (!this.currentQuery) {
|
|
109
|
-
const rows = await DB(this.table).update(properties)
|
|
110
|
-
return rows
|
|
111
|
-
}
|
|
112
|
-
} catch (error) {
|
|
113
|
-
throw new Error(error)
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Delete a model
|
|
119
|
-
* @returns
|
|
120
|
-
*/
|
|
121
|
-
static async destroy() {
|
|
122
|
-
try {
|
|
123
|
-
if (!this.currentQuery) {
|
|
124
|
-
const rows = await DB(this.table).delete()
|
|
125
|
-
return rows
|
|
126
|
-
}
|
|
127
|
-
} catch (error) {
|
|
128
|
-
throw new Error(error)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Find a model
|
|
134
|
-
* @param {*} id
|
|
135
|
-
* @param {*} columns
|
|
136
|
-
* @returns this
|
|
137
|
-
*/
|
|
138
|
-
static async find(id, columns = '*') {
|
|
139
|
-
const table = pluralize(this.name.toLowerCase())
|
|
140
|
-
try {
|
|
141
|
-
const fields = await DB(table)
|
|
142
|
-
.where({ id })
|
|
143
|
-
.first(columns)
|
|
144
|
-
if (fields) {
|
|
145
|
-
return new this(fields)
|
|
146
|
-
}
|
|
147
|
-
return null
|
|
148
|
-
} catch (error) {
|
|
149
|
-
throw new Error(error)
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Create a new model
|
|
155
|
-
* @param {*} properties
|
|
156
|
-
* @returns this
|
|
157
|
-
*/
|
|
158
|
-
static async create(properties) {
|
|
159
|
-
const table = pluralize(this.name.toLowerCase())
|
|
160
|
-
try {
|
|
161
|
-
const fields = await DB(table)
|
|
162
|
-
.insert(properties)
|
|
163
|
-
const record = await DB(table).where({ id: fields[0] }).first()
|
|
164
|
-
const model = new this(record)
|
|
165
|
-
return model.stripColumns(model)
|
|
166
|
-
} catch (error) {
|
|
167
|
-
throw new Error(error)
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* --------------|
|
|
173
|
-
* Relationships |
|
|
174
|
-
* --------------|
|
|
175
|
-
*/
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* One to one relationship
|
|
179
|
-
* @param {*} related
|
|
180
|
-
*/
|
|
181
|
-
async hasOne(Related, localKey, foreignKey) {
|
|
182
|
-
const table = new Related().table
|
|
183
|
-
const relation = {}
|
|
184
|
-
if (!localKey) {
|
|
185
|
-
localKey = this.id
|
|
186
|
-
}
|
|
187
|
-
if (!foreignKey) {
|
|
188
|
-
foreignKey = `${this.modelName}_id`
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (localKey) {
|
|
192
|
-
relation[foreignKey] = localKey
|
|
193
|
-
const res = await DB(table).where(relation).first()
|
|
194
|
-
if (res) {
|
|
195
|
-
return this.stripColumns(new Related(res))
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
return null
|
|
199
|
-
}
|
|
200
|
-
// hasMany(related) {}
|
|
201
|
-
// hasManyThrough(related) {}
|
|
202
|
-
// belongsTo(related) {}
|
|
203
|
-
// belongsToMany(related) {}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Where condition
|
|
207
|
-
* @param {*} conditions
|
|
208
|
-
* @returns
|
|
209
|
-
*/
|
|
210
|
-
static where(conditions = {}) {
|
|
211
|
-
const table = pluralize(this.name.toLowerCase())
|
|
212
|
-
this.currentQuery = DB(table).where(conditions)
|
|
213
|
-
return this
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/*ß
|
|
217
|
-
* Return the first model
|
|
218
|
-
* @param {*} columns
|
|
219
|
-
*/
|
|
220
|
-
static async first(columns = '*') {
|
|
221
|
-
try {
|
|
222
|
-
if (!this.currentQuery) {
|
|
223
|
-
const rows = await DB(this.table).first(columns)
|
|
224
|
-
if (rows) {
|
|
225
|
-
return new this(rows)
|
|
226
|
-
}
|
|
227
|
-
return null
|
|
228
|
-
}
|
|
229
|
-
const rows = await this.currentQuery.first(columns)
|
|
230
|
-
if (rows) {
|
|
231
|
-
return new this(rows)
|
|
232
|
-
}
|
|
233
|
-
return null
|
|
234
|
-
} catch (error) {
|
|
235
|
-
throw new Error(error)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Delete columns that are not needed
|
|
242
|
-
*
|
|
243
|
-
*/
|
|
244
|
-
stripColumns(model) {
|
|
245
|
-
this.#private.concat(this.hidden).forEach((h) => {
|
|
246
|
-
delete model[h]
|
|
247
|
-
})
|
|
248
|
-
return model
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
module.exports = { Model, DB }
|