mevn-orm 3.2.1 → 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.
@@ -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
+ }
@@ -0,0 +1,4 @@
1
+ declare module 'pluralize' {
2
+ function pluralize(word: string): string
3
+ export default pluralize
4
+ }
package/index.js DELETED
@@ -1,3 +0,0 @@
1
- require('dotenv').config()
2
- const {Model, DB} = require('./lib/model')
3
- module.exports = { Model, DB }
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 }