ilana-orm 1.0.5 → 1.0.8
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 +21 -4
- package/cli/ilana.js +214 -466
- package/database/connection.mjs +7 -0
- package/example.config.mjs +44 -0
- package/index.mjs +33 -0
- package/orm/Collection.mjs +7 -0
- package/orm/Model.js +21 -7
- package/orm/Model.mjs +7 -0
- package/orm/ModelRegistry.mjs +7 -0
- package/orm/QueryBuilder.mjs +7 -0
- package/orm/Relation.mjs +7 -0
- package/package.json +31 -1
package/README.md
CHANGED
|
@@ -150,10 +150,13 @@ touch ilana.config.js
|
|
|
150
150
|
|
|
151
151
|
### 2. Configure Database
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
**For CommonJS projects**, create `ilana.config.js` in your project root:
|
|
154
|
+
|
|
155
|
+
**For ES Module projects** (with `"type": "module"` in package.json), create `ilana.config.mjs`:
|
|
154
156
|
|
|
155
157
|
```javascript
|
|
156
|
-
|
|
158
|
+
// ilana.config.mjs
|
|
159
|
+
export default {
|
|
157
160
|
default: "sqlite",
|
|
158
161
|
|
|
159
162
|
connections: {
|
|
@@ -212,11 +215,18 @@ This creates:
|
|
|
212
215
|
|
|
213
216
|
### 4. Define the Model
|
|
214
217
|
|
|
215
|
-
**JavaScript:**
|
|
218
|
+
**JavaScript (CommonJS):**
|
|
216
219
|
|
|
217
220
|
```javascript
|
|
218
221
|
// models/User.js
|
|
219
222
|
const Model = require("ilana-orm/orm/Model");
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**JavaScript (ES Modules):**
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
// models/User.js
|
|
229
|
+
import Model from "ilana-orm/orm/Model";
|
|
220
230
|
|
|
221
231
|
class User extends Model {
|
|
222
232
|
static table = "users";
|
|
@@ -246,7 +256,8 @@ class User extends Model {
|
|
|
246
256
|
}
|
|
247
257
|
}
|
|
248
258
|
|
|
249
|
-
|
|
259
|
+
export default User; // For ES modules
|
|
260
|
+
// module.exports = User; // For CommonJS
|
|
250
261
|
```
|
|
251
262
|
|
|
252
263
|
**TypeScript (auto-generated when `tsconfig.json` detected):**
|
|
@@ -294,8 +305,14 @@ npx ilana migrate
|
|
|
294
305
|
|
|
295
306
|
### 6. Start Using the Model
|
|
296
307
|
|
|
308
|
+
**CommonJS:**
|
|
297
309
|
```javascript
|
|
298
310
|
const User = require("./models/User");
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
**ES Modules:**
|
|
314
|
+
```javascript
|
|
315
|
+
import User from "./models/User.js";
|
|
299
316
|
|
|
300
317
|
// Create user
|
|
301
318
|
const user = await User.create({
|
package/cli/ilana.js
CHANGED
|
@@ -31,26 +31,39 @@ const defaultConfig = {
|
|
|
31
31
|
|
|
32
32
|
// Load configuration and auto-initialize
|
|
33
33
|
async function loadConfig() {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const configPathJs = path.join(process.cwd(), 'ilana.config.js');
|
|
35
|
+
const configPathMjs = path.join(process.cwd(), 'ilana.config.mjs');
|
|
36
|
+
|
|
37
|
+
// Try .mjs first (ES modules)
|
|
38
|
+
if (fs.existsSync(configPathMjs)) {
|
|
39
|
+
try {
|
|
40
|
+
const configModule = await import(configPathMjs);
|
|
41
|
+
const config = configModule.default || configModule;
|
|
42
|
+
return config;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.error('Error loading ilana.config.mjs:', error.message);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Try .js (CommonJS)
|
|
50
|
+
if (fs.existsSync(configPathJs)) {
|
|
51
|
+
delete require.cache[configPathJs];
|
|
37
52
|
try {
|
|
38
|
-
const config = require(
|
|
39
|
-
// Config file already initializes database
|
|
53
|
+
const config = require(configPathJs);
|
|
40
54
|
return config;
|
|
41
55
|
} catch (error) {
|
|
42
56
|
if (error.code === 'ERR_REQUIRE_ESM') {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const config = configModule.default || configModule;
|
|
46
|
-
return config;
|
|
57
|
+
console.error('Found ilana.config.js but project uses ES modules. Rename to ilana.config.mjs');
|
|
58
|
+
process.exit(1);
|
|
47
59
|
} else {
|
|
48
60
|
throw error;
|
|
49
61
|
}
|
|
50
62
|
}
|
|
51
63
|
}
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
|
|
65
|
+
// No config file found
|
|
66
|
+
console.error('No ilana.config.js or ilana.config.mjs found. Run "npx ilana setup" first.');
|
|
54
67
|
process.exit(1);
|
|
55
68
|
}
|
|
56
69
|
|
|
@@ -95,6 +108,122 @@ function pluralize(str) {
|
|
|
95
108
|
return str + 's';
|
|
96
109
|
}
|
|
97
110
|
|
|
111
|
+
function getESModuleConfigTemplate() {
|
|
112
|
+
return `import 'dotenv/config';
|
|
113
|
+
import Database from 'ilana-orm/database/connection';
|
|
114
|
+
|
|
115
|
+
const config = {
|
|
116
|
+
default: process.env.DB_CONNECTION || 'mysql',
|
|
117
|
+
timezone: process.env.DB_TIMEZONE || 'UTC',
|
|
118
|
+
|
|
119
|
+
connections: {
|
|
120
|
+
sqlite: {
|
|
121
|
+
client: 'sqlite3',
|
|
122
|
+
connection: {
|
|
123
|
+
filename: process.env.DB_FILENAME || './database.sqlite'
|
|
124
|
+
},
|
|
125
|
+
useNullAsDefault: true
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
mysql: {
|
|
129
|
+
client: 'mysql2',
|
|
130
|
+
connection: {
|
|
131
|
+
host: process.env.DB_HOST || 'localhost',
|
|
132
|
+
port: process.env.DB_PORT || 3306,
|
|
133
|
+
user: process.env.DB_USERNAME || 'root',
|
|
134
|
+
password: process.env.DB_PASSWORD || '',
|
|
135
|
+
database: process.env.DB_DATABASE || 'your_database',
|
|
136
|
+
timezone: process.env.DB_TIMEZONE || 'UTC'
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
postgres: {
|
|
141
|
+
client: 'pg',
|
|
142
|
+
connection: {
|
|
143
|
+
host: process.env.DB_HOST || 'localhost',
|
|
144
|
+
port: process.env.DB_PORT || 5432,
|
|
145
|
+
user: process.env.DB_USERNAME || 'postgres',
|
|
146
|
+
password: process.env.DB_PASSWORD || '',
|
|
147
|
+
database: process.env.DB_DATABASE || 'your_database'
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
migrations: {
|
|
153
|
+
directory: './database/migrations',
|
|
154
|
+
tableName: 'migrations'
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
seeds: {
|
|
158
|
+
directory: './database/seeds'
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Auto-initialize database connections
|
|
163
|
+
Database.configure(config);
|
|
164
|
+
|
|
165
|
+
export default config;
|
|
166
|
+
`;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function getCommonJSConfigTemplate() {
|
|
170
|
+
return `require('dotenv').config();
|
|
171
|
+
const Database = require('ilana-orm/database/connection');
|
|
172
|
+
|
|
173
|
+
const config = {
|
|
174
|
+
default: process.env.DB_CONNECTION || 'mysql',
|
|
175
|
+
timezone: process.env.DB_TIMEZONE || 'UTC',
|
|
176
|
+
|
|
177
|
+
connections: {
|
|
178
|
+
sqlite: {
|
|
179
|
+
client: 'sqlite3',
|
|
180
|
+
connection: {
|
|
181
|
+
filename: process.env.DB_FILENAME || './database.sqlite'
|
|
182
|
+
},
|
|
183
|
+
useNullAsDefault: true
|
|
184
|
+
},
|
|
185
|
+
|
|
186
|
+
mysql: {
|
|
187
|
+
client: 'mysql2',
|
|
188
|
+
connection: {
|
|
189
|
+
host: process.env.DB_HOST || 'localhost',
|
|
190
|
+
port: process.env.DB_PORT || 3306,
|
|
191
|
+
user: process.env.DB_USERNAME || 'root',
|
|
192
|
+
password: process.env.DB_PASSWORD || '',
|
|
193
|
+
database: process.env.DB_DATABASE || 'your_database',
|
|
194
|
+
timezone: process.env.DB_TIMEZONE || 'UTC'
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
postgres: {
|
|
199
|
+
client: 'pg',
|
|
200
|
+
connection: {
|
|
201
|
+
host: process.env.DB_HOST || 'localhost',
|
|
202
|
+
port: process.env.DB_PORT || 5432,
|
|
203
|
+
user: process.env.DB_USERNAME || 'postgres',
|
|
204
|
+
password: process.env.DB_PASSWORD || '',
|
|
205
|
+
database: process.env.DB_DATABASE || 'your_database'
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
migrations: {
|
|
211
|
+
directory: './database/migrations',
|
|
212
|
+
tableName: 'migrations'
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
seeds: {
|
|
216
|
+
directory: './database/seeds'
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// Auto-initialize database connections
|
|
221
|
+
Database.configure(config);
|
|
222
|
+
|
|
223
|
+
module.exports = config;
|
|
224
|
+
`;
|
|
225
|
+
}
|
|
226
|
+
|
|
98
227
|
function generateModel(name, options = {}) {
|
|
99
228
|
const className = toPascalCase(name);
|
|
100
229
|
const tableName = pluralize(toSnakeCase(name));
|
|
@@ -130,9 +259,11 @@ function generateModel(name, options = {}) {
|
|
|
130
259
|
}
|
|
131
260
|
|
|
132
261
|
function getModelTemplate(className, tableName) {
|
|
262
|
+
const isESModule = isESModuleProject();
|
|
263
|
+
|
|
133
264
|
if (isTypeScriptProject()) {
|
|
134
|
-
return `import Model from 'ilana-orm/orm/Model
|
|
135
|
-
// import { MoneyCast, EncryptedCast } from 'ilana-orm/orm/CustomCasts
|
|
265
|
+
return `import Model from 'ilana-orm/orm/Model';
|
|
266
|
+
// import { MoneyCast, EncryptedCast } from 'ilana-orm/orm/CustomCasts';
|
|
136
267
|
|
|
137
268
|
export default class ${className} extends Model {
|
|
138
269
|
protected static table = '${tableName}';
|
|
@@ -175,6 +306,53 @@ export default class ${className} extends Model {
|
|
|
175
306
|
`;
|
|
176
307
|
}
|
|
177
308
|
|
|
309
|
+
if (isESModule) {
|
|
310
|
+
return `import Model from 'ilana-orm/orm/Model';
|
|
311
|
+
// import { MoneyCast, EncryptedCast } from 'ilana-orm/orm/CustomCasts';
|
|
312
|
+
|
|
313
|
+
class ${className} extends Model {
|
|
314
|
+
static table = '${tableName}';
|
|
315
|
+
static timestamps = true;
|
|
316
|
+
static softDeletes = false;
|
|
317
|
+
|
|
318
|
+
// For UUID primary keys, uncomment:
|
|
319
|
+
// static keyType = 'string';
|
|
320
|
+
// static incrementing = false;
|
|
321
|
+
|
|
322
|
+
fillable = [];
|
|
323
|
+
hidden = [];
|
|
324
|
+
appends = [];
|
|
325
|
+
casts = {
|
|
326
|
+
// Basic casts
|
|
327
|
+
// is_active: 'boolean',
|
|
328
|
+
// metadata: 'json',
|
|
329
|
+
// tags: 'array',
|
|
330
|
+
|
|
331
|
+
// Custom casts
|
|
332
|
+
// price: new MoneyCast(),
|
|
333
|
+
// secret: new EncryptedCast('your-key'),
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
// Define relationships here
|
|
337
|
+
// example() {
|
|
338
|
+
// return this.hasMany(RelatedModel, 'foreign_key');
|
|
339
|
+
// }
|
|
340
|
+
|
|
341
|
+
// Define scopes here
|
|
342
|
+
// static scopeActive(query) {
|
|
343
|
+
// query.where('is_active', true);
|
|
344
|
+
// }
|
|
345
|
+
|
|
346
|
+
// Register for polymorphic relationships
|
|
347
|
+
// static {
|
|
348
|
+
// this.register();
|
|
349
|
+
// }
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export default ${className};
|
|
353
|
+
`;
|
|
354
|
+
}
|
|
355
|
+
|
|
178
356
|
return `const Model = require('ilana-orm/orm/Model');
|
|
179
357
|
// const { MoneyCast, EncryptedCast } = require('ilana-orm/orm/CustomCasts');
|
|
180
358
|
|
|
@@ -222,8 +400,10 @@ module.exports = ${className};
|
|
|
222
400
|
}
|
|
223
401
|
|
|
224
402
|
function getPivotModelTemplate(className, tableName) {
|
|
403
|
+
const isESModule = isESModuleProject();
|
|
404
|
+
|
|
225
405
|
if (isTypeScriptProject()) {
|
|
226
|
-
return `import Model from 'ilana-orm/orm/Model
|
|
406
|
+
return `import Model from 'ilana-orm/orm/Model';
|
|
227
407
|
|
|
228
408
|
export default class ${className} extends Model {
|
|
229
409
|
protected static table = '${tableName}';
|
|
@@ -236,6 +416,22 @@ export default class ${className} extends Model {
|
|
|
236
416
|
`;
|
|
237
417
|
}
|
|
238
418
|
|
|
419
|
+
if (isESModule) {
|
|
420
|
+
return `import Model from 'ilana-orm/orm/Model';
|
|
421
|
+
|
|
422
|
+
class ${className} extends Model {
|
|
423
|
+
static table = '${tableName}';
|
|
424
|
+
static timestamps = true;
|
|
425
|
+
|
|
426
|
+
fillable = [];
|
|
427
|
+
|
|
428
|
+
// Define pivot relationships here
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export default ${className};
|
|
432
|
+
`;
|
|
433
|
+
}
|
|
434
|
+
|
|
239
435
|
return `const Model = require('ilana-orm/orm/Model');
|
|
240
436
|
|
|
241
437
|
class ${className} extends Model {
|
|
@@ -350,126 +546,11 @@ const commands = {
|
|
|
350
546
|
}
|
|
351
547
|
|
|
352
548
|
// Create config file if it doesn't exist
|
|
353
|
-
const
|
|
549
|
+
const isESModule = isESModuleProject();
|
|
550
|
+
const configPath = isESModule ? 'ilana.config.mjs' : 'ilana.config.js';
|
|
551
|
+
|
|
354
552
|
if (!fs.existsSync(configPath)) {
|
|
355
|
-
const isESModule = isESModuleProject();
|
|
356
553
|
const configTemplate = isESModule ? getESModuleConfigTemplate() : getCommonJSConfigTemplate();
|
|
357
|
-
|
|
358
|
-
function getESModuleConfigTemplate() {
|
|
359
|
-
return `import 'dotenv/config';
|
|
360
|
-
import Database from 'ilana-orm/database/connection.js';
|
|
361
|
-
|
|
362
|
-
const config = {
|
|
363
|
-
default: process.env.DB_CONNECTION || 'mysql',
|
|
364
|
-
timezone: process.env.DB_TIMEZONE || 'UTC',
|
|
365
|
-
|
|
366
|
-
connections: {
|
|
367
|
-
sqlite: {
|
|
368
|
-
client: 'sqlite3',
|
|
369
|
-
connection: {
|
|
370
|
-
filename: process.env.DB_FILENAME || './database.sqlite'
|
|
371
|
-
},
|
|
372
|
-
useNullAsDefault: true
|
|
373
|
-
},
|
|
374
|
-
|
|
375
|
-
mysql: {
|
|
376
|
-
client: 'mysql2',
|
|
377
|
-
connection: {
|
|
378
|
-
host: process.env.DB_HOST || 'localhost',
|
|
379
|
-
port: process.env.DB_PORT || 3306,
|
|
380
|
-
user: process.env.DB_USERNAME || 'root',
|
|
381
|
-
password: process.env.DB_PASSWORD || '',
|
|
382
|
-
database: process.env.DB_DATABASE || 'your_database',
|
|
383
|
-
timezone: process.env.DB_TIMEZONE || 'UTC'
|
|
384
|
-
}
|
|
385
|
-
},
|
|
386
|
-
|
|
387
|
-
postgres: {
|
|
388
|
-
client: 'pg',
|
|
389
|
-
connection: {
|
|
390
|
-
host: process.env.DB_HOST || 'localhost',
|
|
391
|
-
port: process.env.DB_PORT || 5432,
|
|
392
|
-
user: process.env.DB_USERNAME || 'postgres',
|
|
393
|
-
password: process.env.DB_PASSWORD || '',
|
|
394
|
-
database: process.env.DB_DATABASE || 'your_database'
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
},
|
|
398
|
-
|
|
399
|
-
migrations: {
|
|
400
|
-
directory: './database/migrations',
|
|
401
|
-
tableName: 'migrations'
|
|
402
|
-
},
|
|
403
|
-
|
|
404
|
-
seeds: {
|
|
405
|
-
directory: './database/seeds'
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
// Auto-initialize database connections
|
|
410
|
-
Database.configure(config);
|
|
411
|
-
|
|
412
|
-
export default config;
|
|
413
|
-
`;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
function getCommonJSConfigTemplate() {
|
|
417
|
-
return `require('dotenv').config();
|
|
418
|
-
const Database = require('ilana-orm/database/connection');
|
|
419
|
-
|
|
420
|
-
const config = {
|
|
421
|
-
default: process.env.DB_CONNECTION || 'mysql',
|
|
422
|
-
timezone: process.env.DB_TIMEZONE || 'UTC',
|
|
423
|
-
|
|
424
|
-
connections: {
|
|
425
|
-
sqlite: {
|
|
426
|
-
client: 'sqlite3',
|
|
427
|
-
connection: {
|
|
428
|
-
filename: process.env.DB_FILENAME || './database.sqlite'
|
|
429
|
-
},
|
|
430
|
-
useNullAsDefault: true
|
|
431
|
-
},
|
|
432
|
-
|
|
433
|
-
mysql: {
|
|
434
|
-
client: 'mysql2',
|
|
435
|
-
connection: {
|
|
436
|
-
host: process.env.DB_HOST || 'localhost',
|
|
437
|
-
port: process.env.DB_PORT || 3306,
|
|
438
|
-
user: process.env.DB_USERNAME || 'root',
|
|
439
|
-
password: process.env.DB_PASSWORD || '',
|
|
440
|
-
database: process.env.DB_DATABASE || 'your_database',
|
|
441
|
-
timezone: process.env.DB_TIMEZONE || 'UTC'
|
|
442
|
-
}
|
|
443
|
-
},
|
|
444
|
-
|
|
445
|
-
postgres: {
|
|
446
|
-
client: 'pg',
|
|
447
|
-
connection: {
|
|
448
|
-
host: process.env.DB_HOST || 'localhost',
|
|
449
|
-
port: process.env.DB_PORT || 5432,
|
|
450
|
-
user: process.env.DB_USERNAME || 'postgres',
|
|
451
|
-
password: process.env.DB_PASSWORD || '',
|
|
452
|
-
database: process.env.DB_DATABASE || 'your_database'
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
},
|
|
456
|
-
|
|
457
|
-
migrations: {
|
|
458
|
-
directory: './database/migrations',
|
|
459
|
-
tableName: 'migrations'
|
|
460
|
-
},
|
|
461
|
-
|
|
462
|
-
seeds: {
|
|
463
|
-
directory: './database/seeds'
|
|
464
|
-
}
|
|
465
|
-
};
|
|
466
|
-
|
|
467
|
-
// Auto-initialize database connections
|
|
468
|
-
Database.configure(config);
|
|
469
|
-
|
|
470
|
-
module.exports = config;
|
|
471
|
-
`;
|
|
472
|
-
}
|
|
473
554
|
fs.writeFileSync(configPath, configTemplate);
|
|
474
555
|
console.log(`Created config file: ${configPath}`);
|
|
475
556
|
}
|
|
@@ -517,6 +598,7 @@ DB_TIMEZONE=UTC
|
|
|
517
598
|
console.log('2. Run: ilana make:model User -m');
|
|
518
599
|
console.log('3. Run: ilana migrate');
|
|
519
600
|
},
|
|
601
|
+
|
|
520
602
|
async 'make:migration'(name, ...flags) {
|
|
521
603
|
if (!name) {
|
|
522
604
|
console.error('Migration name is required');
|
|
@@ -617,329 +699,6 @@ DB_TIMEZONE=UTC
|
|
|
617
699
|
generateModel(name, options);
|
|
618
700
|
},
|
|
619
701
|
|
|
620
|
-
async migrate(...args) {
|
|
621
|
-
await initializeDatabase();
|
|
622
|
-
const runner = new MigrationRunner();
|
|
623
|
-
|
|
624
|
-
let connection;
|
|
625
|
-
let onlyFile;
|
|
626
|
-
let toFile;
|
|
627
|
-
|
|
628
|
-
for (let i = 0; i < args.length; i++) {
|
|
629
|
-
const arg = args[i];
|
|
630
|
-
if (arg === '--connection' && args[i + 1]) {
|
|
631
|
-
connection = args[i + 1];
|
|
632
|
-
i++;
|
|
633
|
-
} else if (arg.startsWith('--connection=')) {
|
|
634
|
-
connection = arg.split('=')[1];
|
|
635
|
-
} else if (arg === '--only' && args[i + 1]) {
|
|
636
|
-
onlyFile = args[i + 1];
|
|
637
|
-
i++;
|
|
638
|
-
} else if (arg.startsWith('--only=')) {
|
|
639
|
-
onlyFile = arg.split('=')[1];
|
|
640
|
-
} else if (arg === '--to' && args[i + 1]) {
|
|
641
|
-
toFile = args[i + 1];
|
|
642
|
-
i++;
|
|
643
|
-
} else if (arg.startsWith('--to=')) {
|
|
644
|
-
toFile = arg.split('=')[1];
|
|
645
|
-
} else if (!arg.startsWith('--')) {
|
|
646
|
-
connection = arg;
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
await runner.migrate(connection, onlyFile, toFile);
|
|
651
|
-
process.exit(0);
|
|
652
|
-
},
|
|
653
|
-
|
|
654
|
-
async 'migrate:fresh'(...args) {
|
|
655
|
-
await initializeDatabase();
|
|
656
|
-
const runner = new MigrationRunner();
|
|
657
|
-
|
|
658
|
-
let connection;
|
|
659
|
-
let withSeed = false;
|
|
660
|
-
|
|
661
|
-
for (const arg of args) {
|
|
662
|
-
if (arg === '--seed') {
|
|
663
|
-
withSeed = true;
|
|
664
|
-
} else if (arg.startsWith('--connection=')) {
|
|
665
|
-
connection = arg.split('=')[1];
|
|
666
|
-
} else if (!arg.startsWith('--')) {
|
|
667
|
-
connection = arg;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
await runner.fresh(connection);
|
|
672
|
-
|
|
673
|
-
if (withSeed) {
|
|
674
|
-
await commands.seed();
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
process.exit(0);
|
|
678
|
-
},
|
|
679
|
-
|
|
680
|
-
async 'migrate:list'(connection) {
|
|
681
|
-
await initializeDatabase();
|
|
682
|
-
const runner = new MigrationRunner();
|
|
683
|
-
await runner.list(connection);
|
|
684
|
-
process.exit(0);
|
|
685
|
-
},
|
|
686
|
-
|
|
687
|
-
async 'migrate:unlock'(connection) {
|
|
688
|
-
await initializeDatabase();
|
|
689
|
-
const runner = new MigrationRunner();
|
|
690
|
-
await runner.unlock(connection);
|
|
691
|
-
process.exit(0);
|
|
692
|
-
},
|
|
693
|
-
|
|
694
|
-
async 'migrate:rollback'(...args) {
|
|
695
|
-
await initializeDatabase();
|
|
696
|
-
const runner = new MigrationRunner();
|
|
697
|
-
|
|
698
|
-
let steps = 1;
|
|
699
|
-
let connection;
|
|
700
|
-
let toFile;
|
|
701
|
-
|
|
702
|
-
for (let i = 0; i < args.length; i++) {
|
|
703
|
-
const arg = args[i];
|
|
704
|
-
if (arg === '--step' && args[i + 1]) {
|
|
705
|
-
steps = parseInt(args[i + 1]);
|
|
706
|
-
i++;
|
|
707
|
-
} else if (arg.startsWith('--step=')) {
|
|
708
|
-
steps = parseInt(arg.split('=')[1]);
|
|
709
|
-
} else if (arg === '--connection' && args[i + 1]) {
|
|
710
|
-
connection = args[i + 1];
|
|
711
|
-
i++;
|
|
712
|
-
} else if (arg.startsWith('--connection=')) {
|
|
713
|
-
connection = arg.split('=')[1];
|
|
714
|
-
} else if (arg === '--to' && args[i + 1]) {
|
|
715
|
-
toFile = args[i + 1];
|
|
716
|
-
i++;
|
|
717
|
-
} else if (arg.startsWith('--to=')) {
|
|
718
|
-
toFile = arg.split('=')[1];
|
|
719
|
-
} else if (!isNaN(parseInt(arg))) {
|
|
720
|
-
steps = parseInt(arg);
|
|
721
|
-
} else if (!arg.startsWith('--')) {
|
|
722
|
-
connection = arg;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
await runner.rollback(steps, connection, toFile);
|
|
727
|
-
process.exit(0);
|
|
728
|
-
},
|
|
729
|
-
|
|
730
|
-
async 'migrate:reset'(connection) {
|
|
731
|
-
await initializeDatabase();
|
|
732
|
-
const runner = new MigrationRunner();
|
|
733
|
-
await runner.reset(connection);
|
|
734
|
-
process.exit(0);
|
|
735
|
-
},
|
|
736
|
-
|
|
737
|
-
async 'migrate:refresh'(connection) {
|
|
738
|
-
await initializeDatabase();
|
|
739
|
-
const runner = new MigrationRunner();
|
|
740
|
-
await runner.refresh(connection);
|
|
741
|
-
process.exit(0);
|
|
742
|
-
},
|
|
743
|
-
|
|
744
|
-
async 'migrate:status'(connection) {
|
|
745
|
-
await initializeDatabase();
|
|
746
|
-
const runner = new MigrationRunner();
|
|
747
|
-
await runner.status(connection);
|
|
748
|
-
process.exit(0);
|
|
749
|
-
},
|
|
750
|
-
|
|
751
|
-
async seed(...args) {
|
|
752
|
-
await initializeDatabase();
|
|
753
|
-
|
|
754
|
-
let seederName;
|
|
755
|
-
let connection;
|
|
756
|
-
|
|
757
|
-
for (let i = 0; i < args.length; i++) {
|
|
758
|
-
const arg = args[i];
|
|
759
|
-
if (arg === '--class' && args[i + 1]) {
|
|
760
|
-
seederName = args[i + 1];
|
|
761
|
-
i++;
|
|
762
|
-
} else if (arg.startsWith('--class=')) {
|
|
763
|
-
seederName = arg.split('=')[1];
|
|
764
|
-
} else if (arg === '--connection' && args[i + 1]) {
|
|
765
|
-
connection = args[i + 1];
|
|
766
|
-
i++;
|
|
767
|
-
} else if (arg.startsWith('--connection=')) {
|
|
768
|
-
connection = arg.split('=')[1];
|
|
769
|
-
} else if (!arg.startsWith('--')) {
|
|
770
|
-
if (!seederName) seederName = arg;
|
|
771
|
-
else connection = arg;
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
const seedsPath = path.join(process.cwd(), 'database/seeds');
|
|
776
|
-
if (!fs.existsSync(seedsPath)) {
|
|
777
|
-
console.log('No seeds directory found');
|
|
778
|
-
process.exit(0);
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
const seedFiles = fs.readdirSync(seedsPath)
|
|
782
|
-
.filter(file => file.endsWith('.ts') || file.endsWith('.js'))
|
|
783
|
-
.sort();
|
|
784
|
-
|
|
785
|
-
if (seedFiles.length === 0) {
|
|
786
|
-
console.log('No seed files found');
|
|
787
|
-
process.exit(0);
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
const filesToRun = seederName
|
|
791
|
-
? seedFiles.filter(file => file.includes(seederName))
|
|
792
|
-
: seedFiles;
|
|
793
|
-
|
|
794
|
-
console.log(`Running ${filesToRun.length} seeders...`);
|
|
795
|
-
|
|
796
|
-
for (const file of filesToRun) {
|
|
797
|
-
console.log(`Seeding: ${file}`);
|
|
798
|
-
const filepath = path.join(seedsPath, file);
|
|
799
|
-
delete require.cache[filepath];
|
|
800
|
-
|
|
801
|
-
let seederModule;
|
|
802
|
-
try {
|
|
803
|
-
seederModule = require(filepath);
|
|
804
|
-
} catch (error) {
|
|
805
|
-
if (error.code === 'ERR_REQUIRE_ESM') {
|
|
806
|
-
// Handle ES modules
|
|
807
|
-
seederModule = await import(filepath);
|
|
808
|
-
} else {
|
|
809
|
-
throw error;
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
const SeederClass = seederModule.default || seederModule;
|
|
814
|
-
const seeder = new SeederClass();
|
|
815
|
-
|
|
816
|
-
if (connection) {
|
|
817
|
-
seeder.connection = connection;
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
if (typeof seeder.run === 'function') {
|
|
821
|
-
await seeder.run();
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
console.log(`Seeded: ${file}`);
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
console.log('Seeding completed');
|
|
828
|
-
process.exit(0);
|
|
829
|
-
},
|
|
830
|
-
|
|
831
|
-
async 'db:seed'(seederName) {
|
|
832
|
-
return commands.seed(seederName);
|
|
833
|
-
},
|
|
834
|
-
|
|
835
|
-
async 'db:wipe'(connection) {
|
|
836
|
-
await initializeDatabase();
|
|
837
|
-
const runner = new MigrationRunner();
|
|
838
|
-
await runner.wipe(connection);
|
|
839
|
-
process.exit(0);
|
|
840
|
-
},
|
|
841
|
-
|
|
842
|
-
async 'make:observer'(name, ...flags) {
|
|
843
|
-
if (!name) {
|
|
844
|
-
console.error('Observer name is required');
|
|
845
|
-
console.log('Usage: ilana make:observer <ObserverName> [--model=ModelName]');
|
|
846
|
-
process.exit(1);
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
let modelName = '';
|
|
850
|
-
|
|
851
|
-
for (const flag of flags) {
|
|
852
|
-
if (flag.startsWith('--model=')) {
|
|
853
|
-
modelName = flag.split('=')[1];
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
const className = toPascalCase(name.replace('Observer', ''));
|
|
858
|
-
const fileName = `${className}Observer${getFileExtension()}`;
|
|
859
|
-
const filePath = path.join(process.cwd(), 'observers', fileName);
|
|
860
|
-
|
|
861
|
-
if (!fs.existsSync(path.dirname(filePath))) {
|
|
862
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
const template = isTypeScriptProject() ?
|
|
866
|
-
`${modelName ? `import ${modelName} from '../models/${modelName}.js';\n\n` : ''}export default class ${className}Observer {
|
|
867
|
-
async creating(model: ${modelName || 'any'}): Promise<void> {}
|
|
868
|
-
async created(model: ${modelName || 'any'}): Promise<void> {}
|
|
869
|
-
async updating(model: ${modelName || 'any'}): Promise<void> {}
|
|
870
|
-
async updated(model: ${modelName || 'any'}): Promise<void> {}
|
|
871
|
-
async saving(model: ${modelName || 'any'}): Promise<void> {}
|
|
872
|
-
async saved(model: ${modelName || 'any'}): Promise<void> {}
|
|
873
|
-
async deleting(model: ${modelName || 'any'}): Promise<void> {}
|
|
874
|
-
async deleted(model: ${modelName || 'any'}): Promise<void> {}
|
|
875
|
-
}
|
|
876
|
-
` :
|
|
877
|
-
`${modelName ? `const ${modelName} = require('../models/${modelName}');\n\n` : ''}class ${className}Observer {
|
|
878
|
-
async creating(model) {}
|
|
879
|
-
async created(model) {}
|
|
880
|
-
async updating(model) {}
|
|
881
|
-
async updated(model) {}
|
|
882
|
-
async saving(model) {}
|
|
883
|
-
async saved(model) {}
|
|
884
|
-
async deleting(model) {}
|
|
885
|
-
async deleted(model) {}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
module.exports = ${className}Observer;
|
|
889
|
-
`;
|
|
890
|
-
|
|
891
|
-
fs.writeFileSync(filePath, template);
|
|
892
|
-
console.log(`Created observer: observers/${fileName}`);
|
|
893
|
-
|
|
894
|
-
if (modelName) {
|
|
895
|
-
console.log(`Observer configured for model: ${modelName}`);
|
|
896
|
-
}
|
|
897
|
-
},
|
|
898
|
-
|
|
899
|
-
async 'make:cast'(name) {
|
|
900
|
-
if (!name) {
|
|
901
|
-
console.error('Cast name is required');
|
|
902
|
-
process.exit(1);
|
|
903
|
-
}
|
|
904
|
-
|
|
905
|
-
const className = toPascalCase(name.replace('Cast', ''));
|
|
906
|
-
const fileName = `${className}Cast${getFileExtension()}`;
|
|
907
|
-
const filePath = path.join(process.cwd(), 'casts', fileName);
|
|
908
|
-
|
|
909
|
-
if (!fs.existsSync(path.dirname(filePath))) {
|
|
910
|
-
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
const template = isTypeScriptProject() ?
|
|
914
|
-
`import { CustomCast } from 'ilana-orm/orm/Model.js';
|
|
915
|
-
|
|
916
|
-
export default class ${className}Cast implements CustomCast {
|
|
917
|
-
get(value: any): any {
|
|
918
|
-
return value;
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
set(value: any): any {
|
|
922
|
-
return value;
|
|
923
|
-
}
|
|
924
|
-
}
|
|
925
|
-
` :
|
|
926
|
-
`class ${className}Cast {
|
|
927
|
-
get(value) {
|
|
928
|
-
return value;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
set(value) {
|
|
932
|
-
return value;
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
module.exports = ${className}Cast;
|
|
937
|
-
`;
|
|
938
|
-
|
|
939
|
-
fs.writeFileSync(filePath, template);
|
|
940
|
-
console.log(`Created cast: casts/${fileName}`);
|
|
941
|
-
},
|
|
942
|
-
|
|
943
702
|
help() {
|
|
944
703
|
console.log(`
|
|
945
704
|
Ilana ORM CLI
|
|
@@ -956,13 +715,6 @@ Available commands:
|
|
|
956
715
|
-mfs Generate model + migration + factory + seeder
|
|
957
716
|
|
|
958
717
|
make:migration <name> Create a new migration file
|
|
959
|
-
migrate [connection] Run all pending migrations
|
|
960
|
-
migrate:rollback [steps] [connection] Rollback the last batch of migrations
|
|
961
|
-
migrate:reset [connection] Rollback all migrations
|
|
962
|
-
migrate:refresh [connection] Reset and re-run all migrations
|
|
963
|
-
migrate:status [connection] Show migration status
|
|
964
|
-
seed [name] Run database seeders
|
|
965
|
-
db:seed [name] Alias for seed command
|
|
966
718
|
help Show this help message
|
|
967
719
|
|
|
968
720
|
Examples:
|
|
@@ -973,10 +725,6 @@ Examples:
|
|
|
973
725
|
ilana make:model Permission --all
|
|
974
726
|
ilana make:model UserPost --pivot
|
|
975
727
|
ilana make:migration create_users_table
|
|
976
|
-
ilana migrate
|
|
977
|
-
ilana migrate mysql
|
|
978
|
-
ilana migrate:rollback 2 postgres
|
|
979
|
-
ilana seed UserSeeder
|
|
980
728
|
`);
|
|
981
729
|
}
|
|
982
730
|
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// ilana.config.mjs - ES Module config example
|
|
2
|
+
export default {
|
|
3
|
+
default: "sqlite",
|
|
4
|
+
|
|
5
|
+
connections: {
|
|
6
|
+
sqlite: {
|
|
7
|
+
client: "sqlite3",
|
|
8
|
+
connection: {
|
|
9
|
+
filename: "./database.sqlite",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
mysql: {
|
|
14
|
+
client: "mysql2",
|
|
15
|
+
connection: {
|
|
16
|
+
host: "localhost",
|
|
17
|
+
port: 3306,
|
|
18
|
+
user: "your_username",
|
|
19
|
+
password: "your_password",
|
|
20
|
+
database: "your_database",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
postgres: {
|
|
25
|
+
client: "pg",
|
|
26
|
+
connection: {
|
|
27
|
+
host: "localhost",
|
|
28
|
+
port: 5432,
|
|
29
|
+
user: "your_username",
|
|
30
|
+
password: "your_password",
|
|
31
|
+
database: "your_database",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
migrations: {
|
|
37
|
+
directory: "./migrations",
|
|
38
|
+
tableName: "migrations",
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
seeds: {
|
|
42
|
+
directory: "./seeds",
|
|
43
|
+
},
|
|
44
|
+
};
|
package/index.mjs
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// index.mjs - ES Module wrapper
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const exports = require('./index.js');
|
|
6
|
+
|
|
7
|
+
export const {
|
|
8
|
+
Model,
|
|
9
|
+
QueryBuilder,
|
|
10
|
+
Collection,
|
|
11
|
+
Database,
|
|
12
|
+
DB,
|
|
13
|
+
SchemaBuilder,
|
|
14
|
+
MigrationRunner,
|
|
15
|
+
Seeder,
|
|
16
|
+
Factory,
|
|
17
|
+
defineFactory,
|
|
18
|
+
Relation,
|
|
19
|
+
HasOne,
|
|
20
|
+
HasMany,
|
|
21
|
+
BelongsTo,
|
|
22
|
+
BelongsToMany,
|
|
23
|
+
HasManyThrough,
|
|
24
|
+
MorphTo,
|
|
25
|
+
MorphMany,
|
|
26
|
+
MoneyCast,
|
|
27
|
+
EncryptedCast,
|
|
28
|
+
JsonCast,
|
|
29
|
+
ArrayCast,
|
|
30
|
+
DateCast
|
|
31
|
+
} = exports;
|
|
32
|
+
|
|
33
|
+
export default exports.Model;
|
package/orm/Model.js
CHANGED
|
@@ -8,10 +8,14 @@ const Database = require('../database/connection');
|
|
|
8
8
|
(function autoLoadConfig() {
|
|
9
9
|
const fs = require('fs');
|
|
10
10
|
const path = require('path');
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const configPathJs = path.join(process.cwd(), 'ilana.config.js');
|
|
12
|
+
const configPathMjs = path.join(process.cwd(), 'ilana.config.mjs');
|
|
13
|
+
|
|
14
|
+
if (fs.existsSync(configPathJs)) {
|
|
15
|
+
delete require.cache[configPathJs];
|
|
16
|
+
require(configPathJs);
|
|
17
|
+
} else if (fs.existsSync(configPathMjs)) {
|
|
18
|
+
// For ES modules, we'll handle this in the _getConfig method
|
|
15
19
|
}
|
|
16
20
|
})();
|
|
17
21
|
|
|
@@ -371,9 +375,19 @@ class Model {
|
|
|
371
375
|
_getConfig() {
|
|
372
376
|
try {
|
|
373
377
|
const path = require('path');
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
378
|
+
const fs = require('fs');
|
|
379
|
+
const configPathJs = path.join(process.cwd(), 'ilana.config.js');
|
|
380
|
+
const configPathMjs = path.join(process.cwd(), 'ilana.config.mjs');
|
|
381
|
+
|
|
382
|
+
if (fs.existsSync(configPathJs)) {
|
|
383
|
+
delete require.cache[configPathJs];
|
|
384
|
+
return require(configPathJs);
|
|
385
|
+
} else if (fs.existsSync(configPathMjs)) {
|
|
386
|
+
// For ES modules, return a promise or handle async import
|
|
387
|
+
// For now, return null and let the caller handle it
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
return null;
|
|
377
391
|
} catch (e) {
|
|
378
392
|
return null;
|
|
379
393
|
}
|
package/orm/Model.mjs
ADDED
package/orm/Relation.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Relation.mjs - ES Module wrapper
|
|
2
|
+
import { createRequire } from 'module';
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
|
|
5
|
+
const Relations = require('./Relation.js');
|
|
6
|
+
|
|
7
|
+
export const { Relation, HasOne, HasMany, BelongsTo, BelongsToMany, HasManyThrough, MorphTo, MorphMany } = Relations;
|
package/package.json
CHANGED
|
@@ -1,9 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ilana-orm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "A fully-featured, Eloquent-style ORM for Node.js with TypeScript support",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./index.mjs",
|
|
10
|
+
"require": "./index.js",
|
|
11
|
+
"types": "./index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./orm/Model": {
|
|
14
|
+
"import": "./orm/Model.mjs",
|
|
15
|
+
"require": "./orm/Model.js",
|
|
16
|
+
"types": "./orm/Model.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"./orm/QueryBuilder": {
|
|
19
|
+
"import": "./orm/QueryBuilder.mjs",
|
|
20
|
+
"require": "./orm/QueryBuilder.js",
|
|
21
|
+
"types": "./orm/QueryBuilder.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"./orm/Collection": {
|
|
24
|
+
"import": "./orm/Collection.mjs",
|
|
25
|
+
"require": "./orm/Collection.js",
|
|
26
|
+
"types": "./orm/Collection.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./database/connection": {
|
|
29
|
+
"import": "./database/connection.mjs",
|
|
30
|
+
"require": "./database/connection.js",
|
|
31
|
+
"types": "./database/connection.d.ts"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
7
34
|
"bin": {
|
|
8
35
|
"ilana": "cli/ilana.js"
|
|
9
36
|
},
|
|
@@ -39,11 +66,14 @@
|
|
|
39
66
|
},
|
|
40
67
|
"files": [
|
|
41
68
|
"*.js",
|
|
69
|
+
"*.mjs",
|
|
42
70
|
"*.d.ts",
|
|
43
71
|
"cli/**/*.js",
|
|
44
72
|
"database/**/*.js",
|
|
73
|
+
"database/**/*.mjs",
|
|
45
74
|
"database/**/*.d.ts",
|
|
46
75
|
"orm/**/*.js",
|
|
76
|
+
"orm/**/*.mjs",
|
|
47
77
|
"orm/**/*.d.ts",
|
|
48
78
|
"README.md",
|
|
49
79
|
"LICENSE",
|