ilana-orm 1.0.3 → 1.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/README.md CHANGED
@@ -150,10 +150,13 @@ touch ilana.config.js
150
150
 
151
151
  ### 2. Configure Database
152
152
 
153
- Create `ilana.config.js` in your project root:
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
- module.exports = {
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
- module.exports = User;
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 configPath = path.join(process.cwd(), 'ilana.config.js');
35
- if (fs.existsSync(configPath)) {
36
- delete require.cache[configPath];
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(configPath);
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
- // Handle ES modules
44
- const configModule = await import(configPath);
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
- // No config file found - don't initialize with hardcoded default
53
- console.error('No ilana.config.js found. Run "npx ilana setup" first.');
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
 
@@ -130,9 +143,11 @@ function generateModel(name, options = {}) {
130
143
  }
131
144
 
132
145
  function getModelTemplate(className, tableName) {
146
+ const isESModule = isESModuleProject();
147
+
133
148
  if (isTypeScriptProject()) {
134
- return `import Model from 'ilana-orm/orm/Model.js';
135
- // import { MoneyCast, EncryptedCast } from 'ilana-orm/orm/CustomCasts.js';
149
+ return `import Model from 'ilana-orm/orm/Model';
150
+ // import { MoneyCast, EncryptedCast } from 'ilana-orm/orm/CustomCasts';
136
151
 
137
152
  export default class ${className} extends Model {
138
153
  protected static table = '${tableName}';
@@ -175,6 +190,53 @@ export default class ${className} extends Model {
175
190
  `;
176
191
  }
177
192
 
193
+ if (isESModule) {
194
+ return `import Model from 'ilana-orm/orm/Model';
195
+ // import { MoneyCast, EncryptedCast } from 'ilana-orm/orm/CustomCasts';
196
+
197
+ class ${className} extends Model {
198
+ static table = '${tableName}';
199
+ static timestamps = true;
200
+ static softDeletes = false;
201
+
202
+ // For UUID primary keys, uncomment:
203
+ // static keyType = 'string';
204
+ // static incrementing = false;
205
+
206
+ fillable = [];
207
+ hidden = [];
208
+ appends = [];
209
+ casts = {
210
+ // Basic casts
211
+ // is_active: 'boolean',
212
+ // metadata: 'json',
213
+ // tags: 'array',
214
+
215
+ // Custom casts
216
+ // price: new MoneyCast(),
217
+ // secret: new EncryptedCast('your-key'),
218
+ };
219
+
220
+ // Define relationships here
221
+ // example() {
222
+ // return this.hasMany(RelatedModel, 'foreign_key');
223
+ // }
224
+
225
+ // Define scopes here
226
+ // static scopeActive(query) {
227
+ // query.where('is_active', true);
228
+ // }
229
+
230
+ // Register for polymorphic relationships
231
+ // static {
232
+ // this.register();
233
+ // }
234
+ }
235
+
236
+ export default ${className};
237
+ `;
238
+ }
239
+
178
240
  return `const Model = require('ilana-orm/orm/Model');
179
241
  // const { MoneyCast, EncryptedCast } = require('ilana-orm/orm/CustomCasts');
180
242
 
@@ -222,8 +284,10 @@ module.exports = ${className};
222
284
  }
223
285
 
224
286
  function getPivotModelTemplate(className, tableName) {
287
+ const isESModule = isESModuleProject();
288
+
225
289
  if (isTypeScriptProject()) {
226
- return `import Model from 'ilana-orm/orm/Model.js';
290
+ return `import Model from 'ilana-orm/orm/Model';
227
291
 
228
292
  export default class ${className} extends Model {
229
293
  protected static table = '${tableName}';
@@ -236,6 +300,22 @@ export default class ${className} extends Model {
236
300
  `;
237
301
  }
238
302
 
303
+ if (isESModule) {
304
+ return `import Model from 'ilana-orm/orm/Model';
305
+
306
+ class ${className} extends Model {
307
+ static table = '${tableName}';
308
+ static timestamps = true;
309
+
310
+ fillable = [];
311
+
312
+ // Define pivot relationships here
313
+ }
314
+
315
+ export default ${className};
316
+ `;
317
+ }
318
+
239
319
  return `const Model = require('ilana-orm/orm/Model');
240
320
 
241
321
  class ${className} extends Model {
@@ -350,10 +430,16 @@ const commands = {
350
430
  }
351
431
 
352
432
  // Create config file if it doesn't exist
353
- const configPath = 'ilana.config.js';
433
+ const isESModule = isESModuleProject();
434
+ const configPath = isESModule ? 'ilana.config.mjs' : 'ilana.config.js';
435
+
354
436
  if (!fs.existsSync(configPath)) {
355
- const isESModule = isESModuleProject();
356
437
  const configTemplate = isESModule ? getESModuleConfigTemplate() : getCommonJSConfigTemplate();
438
+ fs.writeFileSync(configPath, configTemplate);
439
+ console.log(`Created config file: ${configPath}`);
440
+ }
441
+ },
442
+ };
357
443
 
358
444
  function getESModuleConfigTemplate() {
359
445
  return `import 'dotenv/config';
@@ -470,9 +556,6 @@ Database.configure(config);
470
556
  module.exports = config;
471
557
  `;
472
558
  }
473
- fs.writeFileSync(configPath, configTemplate);
474
- console.log(`Created config file: ${configPath}`);
475
- }
476
559
 
477
560
  // Create .env file if it doesn't exist
478
561
  const envPath = '.env';
@@ -0,0 +1,45 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export interface DatabaseConfig {
4
+ default: string;
5
+ connections: {
6
+ [name: string]: Knex.Config;
7
+ };
8
+ migrations?: {
9
+ directory?: string;
10
+ tableName?: string;
11
+ };
12
+ seeds?: {
13
+ directory?: string;
14
+ };
15
+ }
16
+
17
+ export interface Transaction extends Knex.Transaction {
18
+ commit(): Promise<void>;
19
+ rollback(): Promise<void>;
20
+ }
21
+
22
+ export default class Database {
23
+ static connections: Map<string, Knex>;
24
+ static config: DatabaseConfig;
25
+ static defaultConnection: string;
26
+ static instance: Knex;
27
+ private static _currentTransaction: Knex.Transaction | null;
28
+
29
+ static configure(config: DatabaseConfig): void;
30
+ static connection(name?: string): Knex;
31
+ static getDefaultConnection(): string;
32
+ static hasConnection(name: string): boolean;
33
+ static getInstance(): Knex;
34
+ static transaction<T>(
35
+ callback: (trx: Knex.Transaction) => Promise<T>,
36
+ attempts?: number,
37
+ connection?: string
38
+ ): Promise<T>;
39
+ static beginTransaction(connection?: string): Promise<Transaction>;
40
+ static commit(trx: Knex.Transaction): Promise<void>;
41
+ static rollback(trx: Knex.Transaction): Promise<void>;
42
+ static getCurrentTransaction(): Knex.Transaction | null;
43
+ static table(tableName: string, connectionName?: string): Knex.QueryBuilder;
44
+ static raw(sql: string, bindings?: any[]): Knex.Raw;
45
+ }
@@ -0,0 +1,7 @@
1
+ // connection.mjs - ES Module wrapper
2
+ import { createRequire } from 'module';
3
+ const require = createRequire(import.meta.url);
4
+
5
+ const Database = require('./connection.js');
6
+
7
+ export default Database;
@@ -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.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ export { default as Model } from './orm/Model';
2
+ export { default as QueryBuilder } from './orm/QueryBuilder';
3
+ export { default as Collection } from './orm/Collection';
4
+ export { default as Database } from './database/connection';
5
+ export * from './orm/Relation';
6
+ export * from './orm/CustomCasts';
7
+
8
+ // Default export
9
+ export { default } from './orm/Model';
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;
@@ -0,0 +1,53 @@
1
+ export default class Collection<T = any> extends Array<T> {
2
+ constructor(items?: T[]);
3
+
4
+ // Static factory methods
5
+ static make<T>(items?: T[]): Collection<T>;
6
+ static times<T>(count: number, callback: (index: number) => T): Collection<T>;
7
+ static range(start: number, end: number): Collection<number>;
8
+
9
+ // Collection methods
10
+ filter(callback: (item: T, index: number) => boolean): Collection<T>;
11
+ map<U>(callback: (item: T, index: number) => U): Collection<U>;
12
+ first(): T | undefined;
13
+ last(): T | undefined;
14
+ pluck<K extends keyof T>(key: K): Collection<T[K]>;
15
+ unique(): Collection<T>;
16
+ unique<K extends keyof T>(key: K): Collection<T>;
17
+ groupBy<K extends keyof T>(key: K): { [key: string]: Collection<T> };
18
+ sortBy<K extends keyof T>(key: K): Collection<T>;
19
+ sortByDesc<K extends keyof T>(key: K): Collection<T>;
20
+ where<K extends keyof T>(key: K, value: T[K]): Collection<T>;
21
+ firstWhere<K extends keyof T>(key: K, value: T[K]): T | undefined;
22
+ sum(): number;
23
+ sum<K extends keyof T>(key: K): number;
24
+ avg(): number;
25
+ avg<K extends keyof T>(key: K): number;
26
+ min(): T | undefined;
27
+ min<K extends keyof T>(key: K): T[K] | undefined;
28
+ max(): T | undefined;
29
+ max<K extends keyof T>(key: K): T[K] | undefined;
30
+ chunk(size: number): Collection<Collection<T>>;
31
+ toJSON(): any[];
32
+
33
+ // Advanced collection methods
34
+ reject(callback: (item: T, index: number) => boolean): Collection<T>;
35
+ partition(callback: (item: T, index: number) => boolean): [Collection<T>, Collection<T>];
36
+ keyBy<K extends keyof T>(key: K): { [key: string]: T };
37
+ countBy<K extends keyof T>(key: K): { [key: string]: number };
38
+ flatten(): Collection<any>;
39
+ take(count: number): Collection<T>;
40
+ skip(count: number): Collection<T>;
41
+ random(): T | undefined;
42
+ random(count: number): Collection<T>;
43
+ shuffle(): Collection<T>;
44
+ tap(callback: (collection: this) => void): this;
45
+ pipe<U>(callback: (collection: this) => U): U;
46
+ whenEmpty(callback: (collection: this) => void): this;
47
+ whenNotEmpty(callback: (collection: this) => void): this;
48
+ unless(condition: boolean, callback: (collection: this) => void): this;
49
+ when(condition: boolean, callback: (collection: this) => void): this;
50
+ toArray(): T[];
51
+ isEmpty(): boolean;
52
+ isNotEmpty(): boolean;
53
+ }
@@ -0,0 +1,7 @@
1
+ // Collection.mjs - ES Module wrapper
2
+ import { createRequire } from 'module';
3
+ const require = createRequire(import.meta.url);
4
+
5
+ const Collection = require('./Collection.js');
6
+
7
+ export default Collection;
@@ -0,0 +1,30 @@
1
+ export interface Cast {
2
+ get(value: any): any;
3
+ set(value: any): any;
4
+ }
5
+
6
+ export class MoneyCast implements Cast {
7
+ get(value: any): number | null;
8
+ set(value: any): number | null;
9
+ }
10
+
11
+ export class EncryptedCast implements Cast {
12
+ constructor(key?: string);
13
+ get(value: any): string | null;
14
+ set(value: any): string | null;
15
+ }
16
+
17
+ export class JsonCast implements Cast {
18
+ get(value: any): any;
19
+ set(value: any): string | null;
20
+ }
21
+
22
+ export class ArrayCast implements Cast {
23
+ get(value: any): any[] | null;
24
+ set(value: any): string | null;
25
+ }
26
+
27
+ export class DateCast implements Cast {
28
+ get(value: any): Date | null;
29
+ set(value: any): string | null;
30
+ }
package/orm/Model.d.ts ADDED
@@ -0,0 +1,160 @@
1
+ import QueryBuilder from './QueryBuilder';
2
+ import { HasOne, HasMany, BelongsTo, BelongsToMany, HasManyThrough, MorphTo, MorphMany } from './Relation';
3
+
4
+ export interface ModelAttributes {
5
+ [key: string]: any;
6
+ }
7
+
8
+ export interface ModelCasts {
9
+ [key: string]: 'string' | 'number' | 'boolean' | 'date' | 'json' | 'array' | 'object' | 'float';
10
+ }
11
+
12
+ export interface ModelEvents {
13
+ [event: string]: Array<(model: any) => Promise<boolean | void> | boolean | void>;
14
+ }
15
+
16
+ export interface Observer {
17
+ creating?(model: any): Promise<void> | void;
18
+ created?(model: any): Promise<void> | void;
19
+ updating?(model: any): Promise<void> | void;
20
+ updated?(model: any): Promise<void> | void;
21
+ saving?(model: any): Promise<void> | void;
22
+ saved?(model: any): Promise<void> | void;
23
+ deleting?(model: any): Promise<void> | void;
24
+ deleted?(model: any): Promise<void> | void;
25
+ restoring?(model: any): Promise<void> | void;
26
+ restored?(model: any): Promise<void> | void;
27
+ }
28
+
29
+ export default class Model {
30
+ // Static properties
31
+ protected static table: string;
32
+ protected static connection?: string;
33
+ protected static primaryKey: string;
34
+ protected static keyType: 'number' | 'string';
35
+ protected static incrementing: boolean;
36
+ protected static timestamps: boolean;
37
+ protected static softDeletes: boolean;
38
+ protected static fillable: string[];
39
+ protected static guarded: string[];
40
+ protected static casts: ModelCasts;
41
+ protected static events: ModelEvents;
42
+ protected static globalScopes: Map<string, (query: QueryBuilder) => void>;
43
+ protected static appends: string[];
44
+ protected static timezone: string;
45
+
46
+ // Instance properties
47
+ attributes: ModelAttributes;
48
+ original: ModelAttributes;
49
+ relations: { [key: string]: any };
50
+ exists: boolean;
51
+ wasRecentlyCreated: boolean;
52
+ protected _dirty: Set<string>;
53
+ protected fillable: string[];
54
+ protected guarded: string[];
55
+ protected casts: ModelCasts;
56
+ protected hidden?: string[];
57
+ protected appends?: string[];
58
+ protected _deferred?: ModelAttributes;
59
+
60
+ constructor(attributes?: ModelAttributes);
61
+
62
+ // Static methods
63
+ static register(): void;
64
+ static resolveRelatedModel(related: string | typeof Model): typeof Model;
65
+ static query(): QueryBuilder;
66
+ static with(...relations: string[]): QueryBuilder;
67
+ static on(connectionOrTrx: string | any): QueryBuilder;
68
+ static all(): Promise<Model[]>;
69
+ static find(id: any): Promise<Model | null>;
70
+ static findBy(column: string, value: any): Promise<Model | null>;
71
+ static first(): Promise<Model | null>;
72
+ static firstOrFail(): Promise<Model>;
73
+ static latest(column?: string): QueryBuilder;
74
+ static oldest(column?: string): QueryBuilder;
75
+ static make(attributes?: ModelAttributes): Model;
76
+ static create(attributes?: ModelAttributes): Promise<Model>;
77
+ static generateUuid(): string;
78
+ static insert(data: ModelAttributes | ModelAttributes[]): Promise<any>;
79
+ static destroy(ids: any | any[]): Promise<number>;
80
+ static firstOrCreate(attributes: ModelAttributes, values?: ModelAttributes): Promise<Model>;
81
+ static firstOrNew(attributes: ModelAttributes, values?: ModelAttributes): Promise<Model>;
82
+ static updateOrCreate(attributes: ModelAttributes, values?: ModelAttributes): Promise<Model>;
83
+
84
+ // Scopes
85
+ static addGlobalScope(name: string, scope: (query: QueryBuilder) => void): void;
86
+ static removeGlobalScope(name: string): void;
87
+ static withoutGlobalScope(name: string): QueryBuilder;
88
+ static applyGlobalScopes(query: QueryBuilder): void;
89
+
90
+ // Events
91
+ static creating(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
92
+ static created(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
93
+ static updating(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
94
+ static updated(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
95
+ static saving(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
96
+ static saved(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
97
+ static deleting(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
98
+ static deleted(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
99
+ static restoring(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
100
+ static restored(callback: (model: Model) => Promise<boolean | void> | boolean | void): void;
101
+ static observe(observer: Observer | (new () => Observer)): void;
102
+ static fireEvent(event: string, model: Model): Promise<boolean>;
103
+
104
+ // Table info
105
+ protected static getTableName(): string;
106
+ protected static getPrimaryKey(): string;
107
+ protected static getKeyType(): string;
108
+ protected static getIncrementing(): boolean;
109
+ protected static getConnectionName(): string | undefined;
110
+
111
+ // Instance methods
112
+ getKey(): any;
113
+ fill(attributes: ModelAttributes): this;
114
+ isFillable(key: string): boolean;
115
+ getAttribute(key: string): any;
116
+ setAttribute(key: string, value: any): this;
117
+ syncOriginal(): void;
118
+ save(): Promise<boolean>;
119
+ update(attributes?: ModelAttributes): Promise<boolean>;
120
+ isDirty(key?: string): boolean;
121
+ getDirty(): ModelAttributes;
122
+ delete(): Promise<boolean>;
123
+ restore(): Promise<boolean>;
124
+ trashed(): boolean;
125
+ only(keys: string[]): ModelAttributes;
126
+ except(keys: string[]): ModelAttributes;
127
+ forceDelete(): Promise<boolean>;
128
+ toJSON(): any;
129
+
130
+ // Relationships
131
+ hasOne(related: string | typeof Model, foreignKey?: string, localKey?: string): HasOne;
132
+ hasMany(related: string | typeof Model, foreignKey?: string, localKey?: string): HasMany;
133
+ belongsTo(related: string | typeof Model, foreignKey?: string, ownerKey?: string): BelongsTo;
134
+ belongsToMany(
135
+ related: string | typeof Model,
136
+ pivotTable?: string,
137
+ foreignPivotKey?: string,
138
+ relatedPivotKey?: string,
139
+ parentKey?: string,
140
+ relatedKey?: string
141
+ ): BelongsToMany;
142
+ hasManyThrough(
143
+ related: string | typeof Model,
144
+ through: string | typeof Model,
145
+ firstKey?: string,
146
+ secondKey?: string,
147
+ localKey?: string,
148
+ secondLocalKey?: string
149
+ ): HasManyThrough;
150
+ morphTo(typeColumn?: string, idColumn?: string): MorphTo;
151
+ morphMany(related: string | typeof Model, typeColumn?: string, idColumn?: string): MorphMany;
152
+
153
+ // Protected methods
154
+ protected _initialize(): void;
155
+ protected _createAttributeGetters(): void;
156
+ protected _getCurrentTimestamp(): Date;
157
+ protected _getTimezoneOffset(timezone: string, date: Date): number;
158
+ protected _getConfig(): any;
159
+ protected _resolveRelatedName(related: string | typeof Model): string;
160
+ }
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 configPath = path.join(process.cwd(), 'ilana.config.js');
12
- if (fs.existsSync(configPath)) {
13
- delete require.cache[configPath];
14
- require(configPath);
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 configPath = path.join(process.cwd(), 'ilana.config.js');
375
- delete require.cache[configPath];
376
- return require(configPath);
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
@@ -0,0 +1,7 @@
1
+ // Model.mjs - ES Module wrapper
2
+ import { createRequire } from 'module';
3
+ const require = createRequire(import.meta.url);
4
+
5
+ const Model = require('./Model.js');
6
+
7
+ export default Model;
@@ -0,0 +1,11 @@
1
+ import Model from './Model';
2
+
3
+ export default class ModelRegistry {
4
+ private static models: Map<string, typeof Model>;
5
+
6
+ static register(name: string, model: typeof Model): void;
7
+ static get(name: string): typeof Model | undefined;
8
+ static has(name: string): boolean;
9
+ static all(): Map<string, typeof Model>;
10
+ static clear(): void;
11
+ }
@@ -0,0 +1,7 @@
1
+ // ModelRegistry.mjs - ES Module wrapper
2
+ import { createRequire } from 'module';
3
+ const require = createRequire(import.meta.url);
4
+
5
+ const ModelRegistry = require('./ModelRegistry.js');
6
+
7
+ export default ModelRegistry;
@@ -0,0 +1,155 @@
1
+ import Collection from './Collection';
2
+ import Model from './Model';
3
+
4
+ export interface PaginationResult<T> {
5
+ data: Collection<T>;
6
+ total: number;
7
+ perPage: number;
8
+ currentPage: number;
9
+ lastPage: number;
10
+ from: number | null;
11
+ to: number | null;
12
+ nextPage: number | null;
13
+ }
14
+
15
+ export interface SimplePaginationResult<T> {
16
+ data: Collection<T>;
17
+ hasMore: boolean;
18
+ }
19
+
20
+ export interface CursorPaginationResult<T> {
21
+ data: Collection<T>;
22
+ nextCursor?: string;
23
+ prevCursor?: string;
24
+ hasNextPage: boolean;
25
+ hasPrevPage: boolean;
26
+ path: string;
27
+ perPage: number;
28
+ }
29
+
30
+ export default class QueryBuilder {
31
+ protected query: any;
32
+ protected modelClass: typeof Model;
33
+ protected connectionName?: string;
34
+ protected eagerLoad: string[];
35
+ protected eagerLoadConstraints: { [key: string]: (query: QueryBuilder) => void };
36
+ protected _transaction?: any;
37
+ protected _includeTrashed?: boolean;
38
+ protected _onlyTrashed?: boolean;
39
+ protected _withoutTrashed?: boolean;
40
+
41
+ constructor(tableName: string, modelClass: typeof Model, connectionName?: string);
42
+
43
+ // Where clauses
44
+ where(column: string, value: any): this;
45
+ where(column: string, operator: string, value: any): this;
46
+ orWhere(column: string, value: any): this;
47
+ orWhere(column: string, operator: string, value: any): this;
48
+ whereIn(column: string, values: any[]): this;
49
+ whereNotIn(column: string, values: any[]): this;
50
+ whereNull(column: string): this;
51
+ whereNotNull(column: string): this;
52
+ whereBetween(column: string, range: [any, any]): this;
53
+ whereJsonContains(column: string, value: any): this;
54
+ whereJsonLength(column: string, operator: string, value: number): this;
55
+ whereDate(column: string, value: string): this;
56
+ whereDate(column: string, operator: string, value: string): this;
57
+ whereMonth(column: string, month: number): this;
58
+ whereYear(column: string, year: number): this;
59
+ whereExists(callback: (query: QueryBuilder) => void): this;
60
+ when<T>(condition: T, callback: (query: this, condition: T) => void, otherwise?: (query: this) => void): this;
61
+
62
+ // Joins
63
+ join(table: string, first: string, operator: string, second: string): this;
64
+ leftJoin(table: string, first: string, operator: string, second: string): this;
65
+ rightJoin(table: string, first: string, operator: string, second: string): this;
66
+
67
+ // Ordering and limits
68
+ orderBy(column: string, direction?: 'asc' | 'desc'): this;
69
+ latest(column?: string): this;
70
+ oldest(column?: string): this;
71
+ limit(count: number): this;
72
+ offset(count: number): this;
73
+ take(count: number): this;
74
+ skip(count: number): this;
75
+
76
+ // Grouping
77
+ groupBy(...columns: string[]): this;
78
+ having(column: string, operator: string, value: any): this;
79
+ having(rawSql: string): this;
80
+
81
+ // Locking
82
+ lockForUpdate(): this;
83
+ sharedLock(): this;
84
+ skipLocked(): this;
85
+ noWait(): this;
86
+
87
+ // Selection
88
+ select(...columns: string[]): this;
89
+ distinct(): this;
90
+
91
+ // Raw queries
92
+ whereRaw(sql: string, bindings?: any[]): this;
93
+ selectRaw(sql: string, bindings?: any[]): this;
94
+
95
+ // Aggregates
96
+ count(column?: string): Promise<number>;
97
+ sum(column: string): Promise<number>;
98
+ avg(column: string): Promise<number>;
99
+ min(column: string): Promise<any>;
100
+ max(column: string): Promise<any>;
101
+
102
+ // Eager loading
103
+ with(...relations: string[]): this;
104
+ withConstraints(relation: string, callback: (query: QueryBuilder) => void): this;
105
+ withConstraints(relations: { [key: string]: (query: QueryBuilder) => void }): this;
106
+ withCount(...relations: string[]): this;
107
+ whereHas(relation: string, callback?: (query: QueryBuilder) => void): this;
108
+
109
+ // Execution methods
110
+ get(): Promise<Collection<Model>>;
111
+ first(): Promise<Model | null>;
112
+ find(id: any): Promise<Model | null>;
113
+ findOrFail(id: any): Promise<Model>;
114
+ pluck(column: string): Promise<any[]>;
115
+ exists(): Promise<boolean>;
116
+
117
+ // Pagination
118
+ paginate(page?: number, perPage?: number): Promise<PaginationResult<Model>>;
119
+ simplePaginate(page?: number, perPage?: number): Promise<SimplePaginationResult<Model>>;
120
+ cursorPaginate(perPage?: number, cursor?: string, column?: string, direction?: 'asc' | 'desc'): Promise<CursorPaginationResult<Model>>;
121
+
122
+ // Chunking
123
+ chunk(size: number, callback: (models: Collection<Model>) => Promise<void>): Promise<void>;
124
+ cursor(chunkSize?: number): AsyncGenerator<Model, void, unknown>;
125
+ lazy(chunkSize?: number): AsyncGenerator<Model, void, unknown>;
126
+
127
+ // Insert/Update/Delete
128
+ insert(data: any | any[]): Promise<any>;
129
+ insertGetId(data: any): Promise<any>;
130
+ update(data: any): Promise<number>;
131
+ delete(): Promise<number>;
132
+ upsert(data: any[], uniqueBy: string[], update?: string[]): Promise<any>;
133
+
134
+ // Connection switching
135
+ connection(name: string): QueryBuilder;
136
+ on(connectionOrTrx: string | any): QueryBuilder;
137
+
138
+ // Clone query
139
+ clone(): QueryBuilder;
140
+
141
+ // Get underlying Knex query
142
+ toKnex(): any;
143
+
144
+ // Soft delete methods
145
+ withTrashed(): this;
146
+ onlyTrashed(): this;
147
+ withoutTrashed(): this;
148
+
149
+ // Debug
150
+ toSql(): string;
151
+
152
+ // Protected methods
153
+ protected loadRelations(models: Model[]): Promise<void>;
154
+ protected loadRelation(models: Model[], relationName: string): Promise<void>;
155
+ }
@@ -0,0 +1,7 @@
1
+ // QueryBuilder.mjs - ES Module wrapper
2
+ import { createRequire } from 'module';
3
+ const require = createRequire(import.meta.url);
4
+
5
+ const QueryBuilder = require('./QueryBuilder.js');
6
+
7
+ export default QueryBuilder;
@@ -0,0 +1,99 @@
1
+ import QueryBuilder from './QueryBuilder';
2
+ import Model from './Model';
3
+ import Collection from './Collection';
4
+
5
+ export class Relation {
6
+ protected parent: Model;
7
+ protected related: string | typeof Model;
8
+ protected foreignKey: string;
9
+ protected localKey: string;
10
+
11
+ constructor(parent: Model, related: string | typeof Model, foreignKey: string, localKey?: string);
12
+
13
+ getRelatedClass(): typeof Model;
14
+ newQuery(): QueryBuilder;
15
+ }
16
+
17
+ export class HasOne extends Relation {
18
+ addConstraints(): void;
19
+ getResults(): Promise<Model | null>;
20
+ }
21
+
22
+ export class HasMany extends Relation {
23
+ addConstraints(): void;
24
+ getResults(): Promise<Collection<Model>>;
25
+ }
26
+
27
+ export class BelongsTo extends Relation {
28
+ addConstraints(): void;
29
+ getResults(): Promise<Model | null>;
30
+ }
31
+
32
+ export class BelongsToMany extends Relation {
33
+ protected pivotTable: string;
34
+ protected parentPivotKey: string;
35
+ protected relatedPivotKey: string;
36
+ protected parentKey: string;
37
+ protected relatedKey: string;
38
+ protected pivotColumns: string[];
39
+ protected pivotTimestamps: boolean;
40
+
41
+ constructor(
42
+ parent: Model,
43
+ related: string | typeof Model,
44
+ pivotTable?: string,
45
+ foreignPivotKey?: string,
46
+ relatedPivotKey?: string,
47
+ parentKey?: string,
48
+ relatedKey?: string
49
+ );
50
+
51
+ withPivot(...columns: string[]): this;
52
+ withTimestamps(): this;
53
+ addConstraints(): void;
54
+ getResults(): Promise<Model[]>;
55
+ attach(id: any, attributes?: any): Promise<void>;
56
+ detach(id?: any): Promise<number>;
57
+ sync(ids: any[]): Promise<void>;
58
+ }
59
+
60
+ export class HasManyThrough extends Relation {
61
+ protected through: string | typeof Model;
62
+ protected firstKey: string;
63
+ protected secondKey: string;
64
+ protected secondLocalKey: string;
65
+
66
+ constructor(
67
+ parent: Model,
68
+ related: string | typeof Model,
69
+ through: string | typeof Model,
70
+ firstKey?: string,
71
+ secondKey?: string,
72
+ localKey?: string,
73
+ secondLocalKey?: string
74
+ );
75
+
76
+ addConstraints(): void;
77
+ getResults(): Promise<Collection<Model>>;
78
+ }
79
+
80
+ export class MorphTo extends Relation {
81
+ protected morphType: string;
82
+ protected morphId: string;
83
+
84
+ constructor(parent: Model, morphType: string, morphId: string);
85
+
86
+ addConstraints(): void;
87
+ getResults(): Promise<Model | null>;
88
+ }
89
+
90
+ export class MorphMany extends Relation {
91
+ protected morphType: string;
92
+ protected morphId: string;
93
+ protected morphClass: string;
94
+
95
+ constructor(parent: Model, related: string | typeof Model, morphType: string, morphId: string, morphClass: string);
96
+
97
+ addConstraints(): void;
98
+ getResults(): Promise<Collection<Model>>;
99
+ }
@@ -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,8 +1,36 @@
1
1
  {
2
2
  "name": "ilana-orm",
3
- "version": "1.0.3",
3
+ "version": "1.0.7",
4
4
  "description": "A fully-featured, Eloquent-style ORM for Node.js with TypeScript support",
5
5
  "main": "index.js",
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
+ },
6
34
  "bin": {
7
35
  "ilana": "cli/ilana.js"
8
36
  },
@@ -38,9 +66,15 @@
38
66
  },
39
67
  "files": [
40
68
  "*.js",
69
+ "*.mjs",
70
+ "*.d.ts",
41
71
  "cli/**/*.js",
42
72
  "database/**/*.js",
73
+ "database/**/*.mjs",
74
+ "database/**/*.d.ts",
43
75
  "orm/**/*.js",
76
+ "orm/**/*.mjs",
77
+ "orm/**/*.d.ts",
44
78
  "README.md",
45
79
  "LICENSE",
46
80
  "ilana.png"
@@ -65,6 +99,9 @@
65
99
  "@types/jest": "^29.0.0",
66
100
  "ts-jest": "^29.0.0"
67
101
  },
102
+ "peerDependencies": {
103
+ "@types/node": ">=16.0.0"
104
+ },
68
105
  "engines": {
69
106
  "node": ">=16.0.0"
70
107
  }