ilana-orm 1.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 +4763 -0
- package/cli/ilana.js +928 -0
- package/database/DB.js +85 -0
- package/database/connection.js +114 -0
- package/database/schema-builder.js +219 -0
- package/ilana.config.js +61 -0
- package/ilana.png +0 -0
- package/index.js +34 -0
- package/orm/Collection.js +283 -0
- package/orm/CustomCasts.js +75 -0
- package/orm/Factory.js +372 -0
- package/orm/MigrationRunner.js +458 -0
- package/orm/Model.js +607 -0
- package/orm/ModelRegistry.js +26 -0
- package/orm/QueryBuilder.js +680 -0
- package/orm/Relation.js +299 -0
- package/orm/Seeder.js +153 -0
- package/package.json +71 -0
- package/test-role.js +26 -0
package/orm/Relation.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
const QueryBuilder = require('./QueryBuilder');
|
|
2
|
+
const Model = require('./Model');
|
|
3
|
+
const ModelRegistry = require('./ModelRegistry');
|
|
4
|
+
|
|
5
|
+
class Relation {
|
|
6
|
+
constructor(parent, related, foreignKey, localKey = 'id') {
|
|
7
|
+
this.parent = parent;
|
|
8
|
+
this.related = related;
|
|
9
|
+
this.foreignKey = foreignKey;
|
|
10
|
+
this.localKey = localKey;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getRelatedClass() {
|
|
14
|
+
// If it's a string, resolve from registry
|
|
15
|
+
if (typeof this.related === 'string') {
|
|
16
|
+
return this.parent.constructor.resolveRelatedModel(this.related);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// If it's a function (class), check if it's properly initialized
|
|
20
|
+
if (typeof this.related === 'function') {
|
|
21
|
+
// For classes that might not be fully loaded, try to get from registry first
|
|
22
|
+
if (this.related.name && ModelRegistry.has(this.related.name)) {
|
|
23
|
+
return ModelRegistry.get(this.related.name);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// If not in registry but has required methods, use it directly
|
|
27
|
+
if (this.related.getTableName && typeof this.related.getTableName === 'function') {
|
|
28
|
+
return this.related;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// If it's a lazy loading function
|
|
32
|
+
if (this.related.length === 0) {
|
|
33
|
+
const resolved = this.related();
|
|
34
|
+
return resolved;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return this.related;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
newQuery() {
|
|
42
|
+
const relatedClass = this.getRelatedClass();
|
|
43
|
+
return new QueryBuilder(relatedClass.getTableName(), relatedClass, relatedClass.getConnectionName());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class HasOne extends Relation {
|
|
48
|
+
addConstraints() {
|
|
49
|
+
// Constraints added when executing query
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getRelatedClass() {
|
|
53
|
+
return super.getRelatedClass();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getResults() {
|
|
57
|
+
return this.newQuery()
|
|
58
|
+
.where(this.foreignKey, this.parent.getAttribute(this.localKey))
|
|
59
|
+
.first();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class HasMany extends Relation {
|
|
64
|
+
addConstraints() {
|
|
65
|
+
// Constraints added when executing query
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getRelatedClass() {
|
|
69
|
+
return super.getRelatedClass();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async getResults() {
|
|
73
|
+
return this.newQuery()
|
|
74
|
+
.where(this.foreignKey, this.parent.getAttribute(this.localKey))
|
|
75
|
+
.get();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
class BelongsTo extends Relation {
|
|
80
|
+
addConstraints() {
|
|
81
|
+
// Constraints added when executing query
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getRelatedClass() {
|
|
85
|
+
return super.getRelatedClass();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async getResults() {
|
|
89
|
+
return this.newQuery()
|
|
90
|
+
.where(this.localKey, this.parent.getAttribute(this.foreignKey))
|
|
91
|
+
.first();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
class BelongsToMany extends Relation {
|
|
96
|
+
constructor(
|
|
97
|
+
parent,
|
|
98
|
+
related,
|
|
99
|
+
pivotTable,
|
|
100
|
+
foreignPivotKey,
|
|
101
|
+
relatedPivotKey,
|
|
102
|
+
parentKey = 'id',
|
|
103
|
+
relatedKey = 'id'
|
|
104
|
+
) {
|
|
105
|
+
super(parent, related, relatedKey, parentKey);
|
|
106
|
+
|
|
107
|
+
const relatedClass = this.getRelatedClass();
|
|
108
|
+
|
|
109
|
+
this.pivotTable = pivotTable || [
|
|
110
|
+
parent.constructor.getTableName(),
|
|
111
|
+
relatedClass.getTableName()
|
|
112
|
+
].sort().join('_');
|
|
113
|
+
|
|
114
|
+
this.parentPivotKey = foreignPivotKey || `${parent.constructor.getTableName().slice(0, -1)}_id`;
|
|
115
|
+
this.relatedPivotKey = relatedPivotKey || `${relatedClass.getTableName().slice(0, -1)}_id`;
|
|
116
|
+
this.parentKey = parentKey || parent.constructor.getPrimaryKey();
|
|
117
|
+
this.relatedKey = relatedKey || relatedClass.getPrimaryKey();
|
|
118
|
+
this.pivotColumns = [];
|
|
119
|
+
this.pivotTimestamps = false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
withPivot(...columns) {
|
|
123
|
+
this.pivotColumns.push(...columns);
|
|
124
|
+
return this;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
withTimestamps() {
|
|
128
|
+
this.pivotTimestamps = true;
|
|
129
|
+
this.pivotColumns.push('created_at', 'updated_at');
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
addConstraints() {
|
|
134
|
+
// Constraints added when executing query
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async getResults() {
|
|
138
|
+
const relatedClass = this.getRelatedClass();
|
|
139
|
+
const selectColumns = [`${relatedClass.getTableName()}.*`];
|
|
140
|
+
|
|
141
|
+
// Add pivot columns
|
|
142
|
+
if (this.pivotColumns.length > 0) {
|
|
143
|
+
for (const column of this.pivotColumns) {
|
|
144
|
+
selectColumns.push(`${this.pivotTable}.${column} as pivot_${column}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const query = this.newQuery()
|
|
149
|
+
.select(...selectColumns)
|
|
150
|
+
.join(this.pivotTable, `${relatedClass.getTableName()}.${this.relatedKey}`, `${this.pivotTable}.${this.relatedPivotKey}`)
|
|
151
|
+
.where(`${this.pivotTable}.${this.parentPivotKey}`, this.parent.getAttribute(this.parentKey));
|
|
152
|
+
|
|
153
|
+
const results = await query.get();
|
|
154
|
+
|
|
155
|
+
// Convert Collection to array if needed
|
|
156
|
+
const resultsArray = Array.isArray(results) ? results : Array.from(results);
|
|
157
|
+
|
|
158
|
+
// Add pivot data to models
|
|
159
|
+
return resultsArray.map((model) => {
|
|
160
|
+
const pivotData = {};
|
|
161
|
+
for (const column of this.pivotColumns) {
|
|
162
|
+
if (model.attributes[`pivot_${column}`] !== undefined) {
|
|
163
|
+
pivotData[column] = model.attributes[`pivot_${column}`];
|
|
164
|
+
delete model.attributes[`pivot_${column}`];
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
model.pivot = pivotData;
|
|
168
|
+
return model;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async attach(id, attributes = {}) {
|
|
173
|
+
const pivotData = {
|
|
174
|
+
[this.parentPivotKey]: this.parent.getAttribute(this.parentKey),
|
|
175
|
+
[this.relatedPivotKey]: id,
|
|
176
|
+
...attributes
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (this.pivotTimestamps) {
|
|
180
|
+
const now = new Date();
|
|
181
|
+
pivotData.created_at = now;
|
|
182
|
+
pivotData.updated_at = now;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
await new QueryBuilder(this.pivotTable).insert(pivotData);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async detach(id) {
|
|
189
|
+
const query = new QueryBuilder(this.pivotTable)
|
|
190
|
+
.where(this.parentPivotKey, this.parent.getAttribute(this.parentKey));
|
|
191
|
+
|
|
192
|
+
if (id !== undefined) {
|
|
193
|
+
query.where(this.relatedPivotKey, id);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return query.delete();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async sync(ids) {
|
|
200
|
+
await this.detach();
|
|
201
|
+
for (const id of ids) {
|
|
202
|
+
await this.attach(id);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
class HasManyThrough extends Relation {
|
|
208
|
+
constructor(
|
|
209
|
+
parent,
|
|
210
|
+
related,
|
|
211
|
+
through,
|
|
212
|
+
firstKey,
|
|
213
|
+
secondKey,
|
|
214
|
+
localKey = 'id',
|
|
215
|
+
secondLocalKey = 'id'
|
|
216
|
+
) {
|
|
217
|
+
super(parent, related, secondKey, localKey);
|
|
218
|
+
this.through = through;
|
|
219
|
+
this.firstKey = firstKey;
|
|
220
|
+
this.secondKey = secondKey;
|
|
221
|
+
this.localKey = localKey;
|
|
222
|
+
this.secondLocalKey = secondLocalKey;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
addConstraints() {
|
|
226
|
+
// Constraints added when executing query
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
async getResults() {
|
|
230
|
+
return this.newQuery()
|
|
231
|
+
.select(`${this.related.getTableName()}.*`)
|
|
232
|
+
.join(this.through.getTableName(), `${this.related.getTableName()}.${this.secondLocalKey}`, `${this.through.getTableName()}.${this.secondKey}`)
|
|
233
|
+
.where(`${this.through.getTableName()}.${this.firstKey}`, this.parent.getAttribute(this.localKey))
|
|
234
|
+
.get();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Polymorphic Relations
|
|
239
|
+
class MorphTo extends Relation {
|
|
240
|
+
constructor(parent, morphType, morphId) {
|
|
241
|
+
super(parent, Model, morphId, 'id');
|
|
242
|
+
this.morphType = morphType;
|
|
243
|
+
this.morphId = morphId;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
addConstraints() {
|
|
247
|
+
// Constraints added when executing query
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async getResults() {
|
|
251
|
+
const morphType = this.parent.getAttribute(this.morphType);
|
|
252
|
+
const morphId = this.parent.getAttribute(this.morphId);
|
|
253
|
+
|
|
254
|
+
if (!morphType || !morphId) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Resolve model class from registry
|
|
259
|
+
const ModelClass = ModelRegistry.get(morphType);
|
|
260
|
+
if (!ModelClass) {
|
|
261
|
+
throw new Error(`Model '${morphType}' not found in registry. Register it using ModelRegistry.register()`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return new QueryBuilder(ModelClass.getTableName(), ModelClass)
|
|
265
|
+
.where(ModelClass.getPrimaryKey(), morphId)
|
|
266
|
+
.first();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
class MorphMany extends Relation {
|
|
271
|
+
constructor(parent, related, morphType, morphId, morphClass) {
|
|
272
|
+
super(parent, related, morphId, 'id');
|
|
273
|
+
this.morphType = morphType;
|
|
274
|
+
this.morphId = morphId;
|
|
275
|
+
this.morphClass = morphClass;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
addConstraints() {
|
|
279
|
+
// Constraints added when executing query
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async getResults() {
|
|
283
|
+
return this.newQuery()
|
|
284
|
+
.where(this.morphType, this.morphClass)
|
|
285
|
+
.where(this.morphId, this.parent.getAttribute(this.localKey))
|
|
286
|
+
.get();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
module.exports = {
|
|
291
|
+
Relation,
|
|
292
|
+
HasOne,
|
|
293
|
+
HasMany,
|
|
294
|
+
BelongsTo,
|
|
295
|
+
BelongsToMany,
|
|
296
|
+
HasManyThrough,
|
|
297
|
+
MorphTo,
|
|
298
|
+
MorphMany
|
|
299
|
+
};
|
package/orm/Seeder.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const Database = require('../database/connection');
|
|
2
|
+
|
|
3
|
+
class Seeder {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.db = Database;
|
|
6
|
+
this.batchSize = 1000;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Batch processing utilities
|
|
10
|
+
async createInBatches(factory, count, attributes = {}) {
|
|
11
|
+
const results = [];
|
|
12
|
+
const batches = Math.ceil(count / this.batchSize);
|
|
13
|
+
|
|
14
|
+
for (let i = 0; i < batches; i++) {
|
|
15
|
+
const currentBatchSize = Math.min(this.batchSize, count - (i * this.batchSize));
|
|
16
|
+
const batch = await factory.times(currentBatchSize).create(attributes);
|
|
17
|
+
results.push(...(Array.isArray(batch) ? batch : [batch]));
|
|
18
|
+
|
|
19
|
+
if (batches > 1) {
|
|
20
|
+
console.log(`Batch ${i + 1}/${batches} completed (${results.length}/${count})`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return results;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async disableForeignKeyChecks() {
|
|
28
|
+
const client = this.db.connection(this.connection).client.config.client;
|
|
29
|
+
|
|
30
|
+
if (client === 'mysql2') {
|
|
31
|
+
await this.db.raw('SET FOREIGN_KEY_CHECKS = 0');
|
|
32
|
+
} else if (client === 'pg') {
|
|
33
|
+
await this.db.raw('SET session_replication_role = replica');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async enableForeignKeyChecks() {
|
|
38
|
+
const client = this.db.connection(this.connection).client.config.client;
|
|
39
|
+
|
|
40
|
+
if (client === 'mysql2') {
|
|
41
|
+
await this.db.raw('SET FOREIGN_KEY_CHECKS = 1');
|
|
42
|
+
} else if (client === 'pg') {
|
|
43
|
+
await this.db.raw('SET session_replication_role = DEFAULT');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async call(seeders) {
|
|
48
|
+
// Handle both single seeder and array of seeders
|
|
49
|
+
const seederArray = Array.isArray(seeders) ? seeders : [seeders];
|
|
50
|
+
|
|
51
|
+
for (const SeederClass of seederArray) {
|
|
52
|
+
const seeder = new SeederClass();
|
|
53
|
+
if (this.connection) {
|
|
54
|
+
seeder.connection = this.connection;
|
|
55
|
+
}
|
|
56
|
+
await seeder.run();
|
|
57
|
+
console.log(`Seeded: ${SeederClass.name}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async callWith(seeders, connection) {
|
|
62
|
+
for (const [name, SeederClass] of Object.entries(seeders)) {
|
|
63
|
+
const seeder = new SeederClass();
|
|
64
|
+
if (connection || this.connection) {
|
|
65
|
+
seeder.connection = connection || this.connection;
|
|
66
|
+
}
|
|
67
|
+
await seeder.run();
|
|
68
|
+
console.log(`Seeded: ${name}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async callOnce(SeederClass, identifier) {
|
|
73
|
+
const tableName = 'seeder_log';
|
|
74
|
+
|
|
75
|
+
// Ensure seeder log table exists
|
|
76
|
+
const knex = this.db.getInstance();
|
|
77
|
+
const hasTable = await knex.schema.hasTable(tableName);
|
|
78
|
+
if (!hasTable) {
|
|
79
|
+
await knex.schema.createTable(tableName, (table) => {
|
|
80
|
+
table.increments('id');
|
|
81
|
+
table.string('seeder');
|
|
82
|
+
table.timestamp('executed_at').defaultTo(knex.fn.now());
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check if already executed
|
|
87
|
+
const exists = await this.db.table(tableName).where('seeder', identifier).first();
|
|
88
|
+
if (exists) {
|
|
89
|
+
console.log(`Skipped: ${identifier} (already executed)`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Execute seeder
|
|
94
|
+
const seeder = new SeederClass();
|
|
95
|
+
if (this.connection) {
|
|
96
|
+
seeder.connection = this.connection;
|
|
97
|
+
}
|
|
98
|
+
await seeder.run();
|
|
99
|
+
|
|
100
|
+
// Log execution
|
|
101
|
+
await this.db.table(tableName).insert({
|
|
102
|
+
seeder: identifier,
|
|
103
|
+
executed_at: new Date()
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
console.log(`Seeded: ${identifier}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async progress(total, callback) {
|
|
110
|
+
let completed = 0;
|
|
111
|
+
const updateProgress = (count) => {
|
|
112
|
+
completed += count;
|
|
113
|
+
const percentage = Math.round((completed / total) * 100);
|
|
114
|
+
console.log(`Progress: ${percentage}% (${completed}/${total})`);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
await callback(updateProgress);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async truncate(table) {
|
|
121
|
+
await this.db.table(table, this.connection).truncate();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
async truncateInOrder(tables) {
|
|
125
|
+
await this.disableForeignKeyChecks();
|
|
126
|
+
|
|
127
|
+
for (const table of tables) {
|
|
128
|
+
await this.truncate(table);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await this.enableForeignKeyChecks();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async wipeDatabase() {
|
|
135
|
+
const knex = this.db.connection(this.connection);
|
|
136
|
+
let tables = [];
|
|
137
|
+
|
|
138
|
+
if (knex.client.config.client === 'sqlite3') {
|
|
139
|
+
const result = await knex.raw("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'");
|
|
140
|
+
tables = result.map((row) => row.name);
|
|
141
|
+
} else if (knex.client.config.client === 'mysql2') {
|
|
142
|
+
const result = await knex.raw('SHOW TABLES');
|
|
143
|
+
tables = result[0].map((row) => Object.values(row)[0]);
|
|
144
|
+
} else if (knex.client.config.client === 'pg') {
|
|
145
|
+
const result = await knex.raw("SELECT tablename FROM pg_tables WHERE schemaname = 'public'");
|
|
146
|
+
tables = result.rows.map((row) => row.tablename);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
await this.truncateInOrder(tables);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
module.exports = Seeder;
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ilana-orm",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A fully-featured, Eloquent-style ORM for Node.js with TypeScript support",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ilana": "cli/ilana.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "echo 'No build needed - using JavaScript files directly'",
|
|
11
|
+
"test": "jest",
|
|
12
|
+
"prepublishOnly": "echo 'Ready to publish'"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"orm",
|
|
16
|
+
"nodejs",
|
|
17
|
+
"javascript",
|
|
18
|
+
"typescript-orm",
|
|
19
|
+
"typescript",
|
|
20
|
+
"database",
|
|
21
|
+
"eloquent",
|
|
22
|
+
"mysql",
|
|
23
|
+
"postgresql",
|
|
24
|
+
"sqlite",
|
|
25
|
+
"query-builder",
|
|
26
|
+
"migrations",
|
|
27
|
+
"relationships"
|
|
28
|
+
],
|
|
29
|
+
"author": "Raphael Abayomi <raphyabak@gmail.com>",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/raphyabak/ilana-orm.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/raphyabak/ilana-orm#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/raphyabak/ilana-orm/issues"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"*.js",
|
|
41
|
+
"cli/**/*.js",
|
|
42
|
+
"database/**/*.js",
|
|
43
|
+
"orm/**/*.js",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"ilana.png"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"knex": "^3.0.0",
|
|
50
|
+
"uuid": "^9.0.0",
|
|
51
|
+
"@faker-js/faker": "^8.0.0",
|
|
52
|
+
"pg": "^8.0.0",
|
|
53
|
+
"mysql2": "^3.0.0",
|
|
54
|
+
"sqlite3": "^5.0.0",
|
|
55
|
+
"dotenv": "^16.3.1",
|
|
56
|
+
"moment-timezone": "^0.6.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "^20.0.0",
|
|
60
|
+
"@types/uuid": "^9.0.0",
|
|
61
|
+
"typescript": "^5.0.0",
|
|
62
|
+
"ts-node": "^10.0.0",
|
|
63
|
+
"ts-node-dev": "^2.0.0",
|
|
64
|
+
"jest": "^29.0.0",
|
|
65
|
+
"@types/jest": "^29.0.0",
|
|
66
|
+
"ts-jest": "^29.0.0"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=16.0.0"
|
|
70
|
+
}
|
|
71
|
+
}
|
package/test-role.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const Model = require('./orm/Model');
|
|
2
|
+
|
|
3
|
+
class Role extends Model {
|
|
4
|
+
static table = 'roles';
|
|
5
|
+
static timestamps = true;
|
|
6
|
+
static softDeletes = false;
|
|
7
|
+
|
|
8
|
+
fillable = ['name', 'description', 'permissions'];
|
|
9
|
+
hidden = [];
|
|
10
|
+
casts = {
|
|
11
|
+
permissions: 'json',
|
|
12
|
+
created_at: 'date',
|
|
13
|
+
updated_at: 'date'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Use string reference instead of importing User
|
|
17
|
+
users() {
|
|
18
|
+
return this.belongsToMany('User', 'user_roles', 'role_id', 'user_id');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static {
|
|
22
|
+
this.register();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = Role;
|