@cheetah.js/orm 0.1.113 → 0.1.118

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
@@ -1,435 +1,435 @@
1
- # Cheetah.js ORM
2
- Cheetah.js ORM is a simple and powerful ORM for Cheetah.js and Bun.
3
- <br>We don't use any query builder like knex, we have our own query builder making us faster.
4
- **In development.**
5
-
6
- ### Menu
7
- - [Installation](#install)
8
- - [Entities](#entities)
9
- - [Value Objects](#value-objects)
10
- - [Hooks](#hooks)
11
- - [Usage](#usage)
12
- - [Migrations](#migrations)
13
-
14
- ### [Installation](#install)
15
- For install Cheetah.js ORM, run the command below:
16
-
17
- ```bash
18
- bun install @cheetah.js/orm
19
- ```
20
- Create a configuration file for the ORM in the root of the project called "cheetah.config.ts" and configure the database connection, providers and entities:
21
-
22
- ```javascript
23
- import { PgDriver } from '@cheetah.js/orm';
24
- import { ConnectionSettings } from '@cheetah.js/orm/driver/driver.interface';
25
-
26
- const config: ConnectionSettings<any> = {
27
- host: 'localhost',
28
- port: 5432,
29
- database: 'postgres',
30
- username: 'postgres',
31
- password: 'postgres',
32
- driver: PgDriver,
33
- migrationPath: 'path_migrations',
34
- entities: 'entity/*.ts' // or [User, Post, ...]
35
- };
36
-
37
- export default config;
38
- ```
39
- Actually, the ORM only supports PostgreSQL, but in the future it will support other databases.
40
- - Entities: Path to entities. Accepts glob patterns or an array of Entity classes.
41
- - MigrationPath: Path to migrations. Accepts glob patterns. Is optional.
42
- <br/>
43
- <br/>
44
- After that, you need to import the ORM into the project and add it to the Cheetah.js instance:
45
-
46
- ```javascript
47
- import { Cheetah } from '@cheetah.js/core';
48
- import { CheetahOrm } from '@cheetah.js/orm';
49
-
50
- new Cheetah().use(CheetahOrm).listen();
51
- ```
52
-
53
- ### [Entities](#entities)
54
- Entities are classes that map to database tables. Each entity must have a primary key.
55
-
56
- #### Example:
57
- ```javascript
58
- import { Entity, PrimaryKey, Property } from '@cheetah.js/orm';
59
-
60
- @Entity()
61
- export class User {
62
- @PrimaryKey()
63
- id: number;
64
-
65
- @Property()
66
- name: string;
67
- }
68
- ```
69
-
70
- #### PrimaryKey
71
- The @PrimaryKey decorator is used to define the primary key of the entity.
72
-
73
- #### Nullable property
74
- For define a nullable property, add a parameter to the @Property decorator:
75
-
76
- ```javascript
77
- @Entity()
78
- export class User {
79
- @PrimaryKey()
80
- id: number;
81
-
82
- @Property({ nullable: true })
83
- name: string;
84
- }
85
- ```
86
- Cheetah ORM can also distinguish nullable properties automatically by adding the question mark to the end of the property name:
87
-
88
- ```javascript
89
- export class User {
90
- @PrimaryKey()
91
- id: number;
92
-
93
- @Property()
94
- name?:string;
95
- }
96
- ```
97
-
98
- #### Unique property
99
- For define a unique property, add a parameter to the @Property decorator:
100
-
101
- ```javascript
102
- @Entity()
103
- export class User {
104
- @PrimaryKey()
105
- id: number;
106
-
107
- @Property({ unique: true })
108
- name: string;
109
- }
110
- ```
111
-
112
- #### Index property
113
- For define a index for a unique property, add a parameter to the @Property decorator:
114
-
115
- ```javascript
116
- @Entity()
117
- export class User {
118
- @PrimaryKey()
119
- id: number;
120
-
121
- @Property({ index: true })
122
- name: string;
123
- }
124
- ```
125
-
126
- For define a index for a multiple properties, add the @Index decorator. You can use it on a property or on the class. It accepts either an array of property names (legacy) or an object with a properties field (recommended):
127
-
128
- ```javascript
129
- @Entity()
130
- export class User {
131
- @PrimaryKey()
132
- id: number;
133
-
134
- @Property()
135
- name: string;
136
-
137
- // Property-level (compound index)
138
- @Index({ properties: ['name', 'email'] })
139
- @Property()
140
- email: string;
141
- }
142
-
143
- // Or, at the class level
144
-
145
- @Entity()
146
- @Index<{ User }>({ properties: ['name', 'email'] })
147
- export class User {
148
- @PrimaryKey()
149
- id: number;
150
-
151
- @Property()
152
- name: string;
153
-
154
- @Property()
155
- email: string;
156
- }
157
-
158
- // Backward compatible usage (array):
159
- // @Index(['name', 'email'])
160
- ```
161
-
162
- #### Property options
163
- | Option | Type | Description |
164
- | ------ | ---- |--------------------------------------------------------------------------------------------|
165
- | nullable | boolean | Defines if the property is nullable. |
166
- | unique | boolean | Defines if the property is unique. |
167
- | index | boolean | Defines if the property is index. |
168
- | default | any | Defines the default value of the property. |
169
- | length | number | Defines the length of the property. |
170
- | onUpdate | string | Define the action to be taken for this property when updating the entity in the database |
171
- | onInsert | string | Defines the action to be taken for this property when inserting the entity in the database |
172
-
173
- #### Computed Properties
174
- The `@Computed` decorator allows you to define properties that are included in serialization but NOT persisted to the database. This is useful for derived values, formatted data, or any computation based on existing properties.
175
-
176
- Computed properties are evaluated when the entity is serialized (e.g., `JSON.stringify()`) and are perfect for transforming or combining existing data.
177
-
178
- ##### Example:
179
- ```javascript
180
- import { Entity, PrimaryKey, Property, Computed } from '@cheetah.js/orm';
181
-
182
- @Entity()
183
- export class Article {
184
- @PrimaryKey()
185
- id: number;
186
-
187
- @Property()
188
- title: string;
189
-
190
- @Property()
191
- body: string;
192
-
193
- @Computed()
194
- get excerpt() {
195
- return this.body?.substring(0, 50) || '';
196
- }
197
-
198
- @Computed()
199
- get titleUppercase() {
200
- return this.title?.toUpperCase() || '';
201
- }
202
- }
203
- ```
204
-
205
- When you serialize an article:
206
- ```javascript
207
- const article = await Article.create({
208
- title: 'My Article',
209
- body: 'This is the full body text of the article...'
210
- });
211
-
212
- console.log(JSON.stringify(article));
213
- // Output: {"id":1,"title":"My Article","body":"This is the full body text of the article...","excerpt":"This is the full body text of the article...","titleUppercase":"MY ARTICLE"}
214
- ```
215
-
216
- **Important Notes:**
217
- - Computed properties are **NOT** stored in the database
218
- - They are evaluated during serialization (toJSON)
219
- - Best used with getter functions
220
- - Can access other properties and even other computed properties
221
- - Can return any JSON-serializable type (string, number, object, array, etc.)
222
-
223
- ### [Hooks](#hooks)
224
- Cheetah ORM supports hooks for entities. The available hooks are: BeforeCreate, AfterCreate, BeforeUpdate, AfterUpdate, BeforeDelete, AfterDelete.
225
- Hooks is only for modify the entity, not for create, update or delete another entities statements in database.
226
-
227
- ### Example:
228
- ```javascript
229
- import { Entity, PrimaryKey, Property, BeforeCreate } from '@cheetah.js/orm';
230
-
231
- @Entity()
232
- export class User {
233
- @PrimaryKey()
234
- id: number;
235
-
236
- @Property()
237
- name: string;
238
-
239
- @BeforeCreate()
240
- static beforeCreate() {
241
- this.name = 'John Doe';
242
- }
243
- }
244
- ```
245
-
246
- #### Value Objects
247
- A Value Object is an immutable type that is distinguishable only by the state of its properties. That is, unlike an Entity, which has a unique identifier and remains distinct even if its properties are otherwise identical, two Value Objects with the exact same properties can be considered equal.
248
- Cheetah ORM Entities support Value Objects. To define a Value Object, extends the ValueObject class:
249
-
250
- ```javascript
251
- import { ValueObject } from '@cheetah.js/orm';
252
-
253
- export class Name extends ValueObject<string, Name> { // First type is a value scalar type,
254
- // and second is a ValueObject
255
-
256
- validate(value): boolean {
257
- return value.length > 0; // Any validation
258
- }
259
- }
260
-
261
- const name = new Name('John Doe');
262
- const name2 = Name.from('John Doe'); // Same as above
263
-
264
- console.log(name.equals(name2)); // true
265
-
266
- ```
267
-
268
- ### Caching
269
- You can cache SELECT queries by providing the `cache` option in find methods or QueryBuilder:
270
-
271
- - `cache: true` keeps the result cached using the driver default policy
272
- - `cache: number` sets a TTL in milliseconds
273
- - `cache: Date` sets an absolute expiration date
274
-
275
- Examples:
276
-
277
- ```ts
278
- // Cache with TTL (5 seconds)
279
- await repo.find({ where: { name: 'John' }, cache: 5000 });
280
-
281
- // Cache until a specific date
282
- await repo.find({ where: { name: 'John' }, cache: new Date(Date.now() + 60_000) });
283
-
284
- // Infinite/driver-default cache
285
- await repo.find({ where: { name: 'John' }, cache: true });
286
- ```
287
- Is Required to implement the validate method, that returns a boolean value.
288
- To use the Value Object in the Entity, just add the ValueObject type to the property:
289
-
290
- ```javascript
291
- import { Entity, PrimaryKey, Property } from '@cheetah.js/orm';
292
-
293
- @Entity()
294
- export class User {
295
- @PrimaryKey()
296
- id: number;
297
-
298
- @Property()
299
- name: Name;
300
- }
301
- ```
302
- Cheetah ORM will automatically convert the Value Object to the database type and vice versa.<br>
303
- Important: If you value object is different from string type, you need to define the database type in the @Property decorator, because the Cheetah ORM would not know the correct type of your value object:
304
-
305
- ```javascript
306
- import { Entity, PrimaryKey, Property } from '@cheetah.js/orm';
307
-
308
- @Entity()
309
- export class User {
310
- @PrimaryKey()
311
- id: number;
312
-
313
- @Property({ type: 'json' })
314
- name: Name;
315
- }
316
- ```
317
-
318
- #### Relations
319
- Cheetah ORM supports relations between entities. The available relations are: OneToMany, ManyToOne.
320
-
321
- ##### OneToMany
322
- The OneToMany relation is used to define a one-to-many relationship between two entities. For example, a user can have multiple posts, but a post can only have one user.
323
-
324
- ```javascript
325
- @Entity()
326
- export class User {
327
- @PrimaryKey()
328
- id: number;
329
-
330
- @Property()
331
- name: string;
332
-
333
- @OneToMany(() => Post, (post) => post.user)
334
- posts: Post[];
335
- }
336
- ```
337
-
338
- #### ManyToOne
339
- The owner side of the relation is the side that has the @ManyToOne decorator. The inverse side is the side that has the @OneToMany decorator. The owner side is always the side that has the foreign key.
340
-
341
- ```javascript
342
- @Entity()
343
- export class Post {
344
- @PrimaryKey()
345
- id: number;
346
-
347
- @Property()
348
- title: string;
349
-
350
- @ManyToOne(() => User)
351
- user: User;
352
- }
353
- ```
354
-
355
- ### [Usage](#usage)
356
- #### Create a new entity
357
- ```javascript
358
- import { User } from './entity/user';
359
-
360
- const user = User.create({ name: 'John Doe' });
361
-
362
- // OR
363
- const user = new User();
364
- user.name = 'John Doe';
365
- await user.save();
366
- ```
367
-
368
- #### Find a entity
369
- ```javascript
370
- import { User } from './entity/user';
371
-
372
- const user = await User.findOne({
373
- name: 'John Doe',
374
- old: { $gte: 16, $lte: 30 }
375
- });
376
- ```
377
-
378
- #### Transactions
379
- ```typescript
380
- import { Orm } from '@cheetah.js/orm';
381
-
382
- const orm = Orm.getInstance();
383
-
384
- await orm.transaction(async (tx) => {
385
- await tx`INSERT INTO users (name) VALUES (${ 'Jane Doe' })`;
386
- await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = ${ 1 }`;
387
- });
388
- ```
389
- The `transaction` method leverages the active driver implementation, ensuring consistent transactional semantics across supported databases.
390
-
391
- #### List of supported operators
392
- | Operator |Name | Description |
393
- | ------ | ---- |--------------------------------------------------------------------------------------------|
394
- | $eq | Equal | Matches values that are equal to a specified value. |
395
- | $gt | Greater Than | Matches values that are greater than a specified value. |
396
- | $gte | Greater Than or Equal | Matches values that are greater than or equal to a specified value. |
397
- | $in | In | Matches any of the values specified in an array. |
398
- | $lt | Less Than | Matches values that are less than a specified value. |
399
- | $lte | Less Than or Equal | Matches values that are less than or equal to a specified value. |
400
- | $ne | Not Equal | Matches all values that are not equal to a specified value. |
401
- | $nin | Not In | Matches none of the values specified in an array. |
402
- | $and | And | Joins query clauses with a logical AND returns all documents that match the conditions of both clauses. |
403
- | $or | Or | Joins query clauses with a logical OR returns all documents that match the conditions of either clause. |
404
- | $not | Not | Inverts the effect of a query expression and returns documents that do not match the query expression. |
405
-
406
- #### Filter by Date columns
407
- You can filter using JavaScript `Date` instances and the ORM translates them into precise SQL comparisons:
408
-
409
- ```typescript
410
- const reinforcement = await User.findOne({
411
- updatedAt: new Date('2024-06-01T00:00:00.000Z'),
412
- });
413
- ```
414
-
415
- ### [Migrations](#migrations)
416
- Cheetah ORM is capable of creating and running migrations.
417
- To do this, you need to install our cli package:
418
-
419
- ```bash
420
- bun install @cheetah.js/cli
421
- ```
422
-
423
- You must have the connection configuration file in the project root "cheetah.config.ts".
424
- To create a migration, run the command below:
425
-
426
- ```bash
427
- bunx cli migration:generate
428
- ```
429
- This command will create a migration file in the path defined in the configuration file, differentiating your entities created with the database.
430
-
431
- #### Example:
432
- ```bash
433
- bunx cli cheetah-orm migration:run
434
- ```
435
- This command will run all migrations that have not yet been run.
1
+ # Cheetah.js ORM
2
+ Cheetah.js ORM is a simple and powerful ORM for Cheetah.js and Bun.
3
+ <br>We don't use any query builder like knex, we have our own query builder making us faster.
4
+ **In development.**
5
+
6
+ ### Menu
7
+ - [Installation](#install)
8
+ - [Entities](#entities)
9
+ - [Value Objects](#value-objects)
10
+ - [Hooks](#hooks)
11
+ - [Usage](#usage)
12
+ - [Migrations](#migrations)
13
+
14
+ ### [Installation](#install)
15
+ For install Cheetah.js ORM, run the command below:
16
+
17
+ ```bash
18
+ bun install @cheetah.js/orm
19
+ ```
20
+ Create a configuration file for the ORM in the root of the project called "cheetah.config.ts" and configure the database connection, providers and entities:
21
+
22
+ ```javascript
23
+ import { PgDriver } from '@cheetah.js/orm';
24
+ import { ConnectionSettings } from '@cheetah.js/orm/driver/driver.interface';
25
+
26
+ const config: ConnectionSettings<any> = {
27
+ host: 'localhost',
28
+ port: 5432,
29
+ database: 'postgres',
30
+ username: 'postgres',
31
+ password: 'postgres',
32
+ driver: PgDriver,
33
+ migrationPath: 'path_migrations',
34
+ entities: 'entity/*.ts' // or [User, Post, ...]
35
+ };
36
+
37
+ export default config;
38
+ ```
39
+ Actually, the ORM only supports PostgreSQL, but in the future it will support other databases.
40
+ - Entities: Path to entities. Accepts glob patterns or an array of Entity classes.
41
+ - MigrationPath: Path to migrations. Accepts glob patterns. Is optional.
42
+ <br/>
43
+ <br/>
44
+ After that, you need to import the ORM into the project and add it to the Cheetah.js instance:
45
+
46
+ ```javascript
47
+ import { Cheetah } from '@cheetah.js/core';
48
+ import { CheetahOrm } from '@cheetah.js/orm';
49
+
50
+ new Cheetah().use(CheetahOrm).listen();
51
+ ```
52
+
53
+ ### [Entities](#entities)
54
+ Entities are classes that map to database tables. Each entity must have a primary key.
55
+
56
+ #### Example:
57
+ ```javascript
58
+ import { Entity, PrimaryKey, Property } from '@cheetah.js/orm';
59
+
60
+ @Entity()
61
+ export class User {
62
+ @PrimaryKey()
63
+ id: number;
64
+
65
+ @Property()
66
+ name: string;
67
+ }
68
+ ```
69
+
70
+ #### PrimaryKey
71
+ The @PrimaryKey decorator is used to define the primary key of the entity.
72
+
73
+ #### Nullable property
74
+ For define a nullable property, add a parameter to the @Property decorator:
75
+
76
+ ```javascript
77
+ @Entity()
78
+ export class User {
79
+ @PrimaryKey()
80
+ id: number;
81
+
82
+ @Property({ nullable: true })
83
+ name: string;
84
+ }
85
+ ```
86
+ Cheetah ORM can also distinguish nullable properties automatically by adding the question mark to the end of the property name:
87
+
88
+ ```javascript
89
+ export class User {
90
+ @PrimaryKey()
91
+ id: number;
92
+
93
+ @Property()
94
+ name?:string;
95
+ }
96
+ ```
97
+
98
+ #### Unique property
99
+ For define a unique property, add a parameter to the @Property decorator:
100
+
101
+ ```javascript
102
+ @Entity()
103
+ export class User {
104
+ @PrimaryKey()
105
+ id: number;
106
+
107
+ @Property({ unique: true })
108
+ name: string;
109
+ }
110
+ ```
111
+
112
+ #### Index property
113
+ For define a index for a unique property, add a parameter to the @Property decorator:
114
+
115
+ ```javascript
116
+ @Entity()
117
+ export class User {
118
+ @PrimaryKey()
119
+ id: number;
120
+
121
+ @Property({ index: true })
122
+ name: string;
123
+ }
124
+ ```
125
+
126
+ For define a index for a multiple properties, add the @Index decorator. You can use it on a property or on the class. It accepts either an array of property names (legacy) or an object with a properties field (recommended):
127
+
128
+ ```javascript
129
+ @Entity()
130
+ export class User {
131
+ @PrimaryKey()
132
+ id: number;
133
+
134
+ @Property()
135
+ name: string;
136
+
137
+ // Property-level (compound index)
138
+ @Index({ properties: ['name', 'email'] })
139
+ @Property()
140
+ email: string;
141
+ }
142
+
143
+ // Or, at the class level
144
+
145
+ @Entity()
146
+ @Index<{ User }>({ properties: ['name', 'email'] })
147
+ export class User {
148
+ @PrimaryKey()
149
+ id: number;
150
+
151
+ @Property()
152
+ name: string;
153
+
154
+ @Property()
155
+ email: string;
156
+ }
157
+
158
+ // Backward compatible usage (array):
159
+ // @Index(['name', 'email'])
160
+ ```
161
+
162
+ #### Property options
163
+ | Option | Type | Description |
164
+ | ------ | ---- |--------------------------------------------------------------------------------------------|
165
+ | nullable | boolean | Defines if the property is nullable. |
166
+ | unique | boolean | Defines if the property is unique. |
167
+ | index | boolean | Defines if the property is index. |
168
+ | default | any | Defines the default value of the property. |
169
+ | length | number | Defines the length of the property. |
170
+ | onUpdate | string | Define the action to be taken for this property when updating the entity in the database |
171
+ | onInsert | string | Defines the action to be taken for this property when inserting the entity in the database |
172
+
173
+ #### Computed Properties
174
+ The `@Computed` decorator allows you to define properties that are included in serialization but NOT persisted to the database. This is useful for derived values, formatted data, or any computation based on existing properties.
175
+
176
+ Computed properties are evaluated when the entity is serialized (e.g., `JSON.stringify()`) and are perfect for transforming or combining existing data.
177
+
178
+ ##### Example:
179
+ ```javascript
180
+ import { Entity, PrimaryKey, Property, Computed } from '@cheetah.js/orm';
181
+
182
+ @Entity()
183
+ export class Article {
184
+ @PrimaryKey()
185
+ id: number;
186
+
187
+ @Property()
188
+ title: string;
189
+
190
+ @Property()
191
+ body: string;
192
+
193
+ @Computed()
194
+ get excerpt() {
195
+ return this.body?.substring(0, 50) || '';
196
+ }
197
+
198
+ @Computed()
199
+ get titleUppercase() {
200
+ return this.title?.toUpperCase() || '';
201
+ }
202
+ }
203
+ ```
204
+
205
+ When you serialize an article:
206
+ ```javascript
207
+ const article = await Article.create({
208
+ title: 'My Article',
209
+ body: 'This is the full body text of the article...'
210
+ });
211
+
212
+ console.log(JSON.stringify(article));
213
+ // Output: {"id":1,"title":"My Article","body":"This is the full body text of the article...","excerpt":"This is the full body text of the article...","titleUppercase":"MY ARTICLE"}
214
+ ```
215
+
216
+ **Important Notes:**
217
+ - Computed properties are **NOT** stored in the database
218
+ - They are evaluated during serialization (toJSON)
219
+ - Best used with getter functions
220
+ - Can access other properties and even other computed properties
221
+ - Can return any JSON-serializable type (string, number, object, array, etc.)
222
+
223
+ ### [Hooks](#hooks)
224
+ Cheetah ORM supports hooks for entities. The available hooks are: BeforeCreate, AfterCreate, BeforeUpdate, AfterUpdate, BeforeDelete, AfterDelete.
225
+ Hooks is only for modify the entity, not for create, update or delete another entities statements in database.
226
+
227
+ ### Example:
228
+ ```javascript
229
+ import { Entity, PrimaryKey, Property, BeforeCreate } from '@cheetah.js/orm';
230
+
231
+ @Entity()
232
+ export class User {
233
+ @PrimaryKey()
234
+ id: number;
235
+
236
+ @Property()
237
+ name: string;
238
+
239
+ @BeforeCreate()
240
+ static beforeCreate() {
241
+ this.name = 'John Doe';
242
+ }
243
+ }
244
+ ```
245
+
246
+ #### Value Objects
247
+ A Value Object is an immutable type that is distinguishable only by the state of its properties. That is, unlike an Entity, which has a unique identifier and remains distinct even if its properties are otherwise identical, two Value Objects with the exact same properties can be considered equal.
248
+ Cheetah ORM Entities support Value Objects. To define a Value Object, extends the ValueObject class:
249
+
250
+ ```javascript
251
+ import { ValueObject } from '@cheetah.js/orm';
252
+
253
+ export class Name extends ValueObject<string, Name> { // First type is a value scalar type,
254
+ // and second is a ValueObject
255
+
256
+ validate(value): boolean {
257
+ return value.length > 0; // Any validation
258
+ }
259
+ }
260
+
261
+ const name = new Name('John Doe');
262
+ const name2 = Name.from('John Doe'); // Same as above
263
+
264
+ console.log(name.equals(name2)); // true
265
+
266
+ ```
267
+
268
+ ### Caching
269
+ You can cache SELECT queries by providing the `cache` option in find methods or QueryBuilder:
270
+
271
+ - `cache: true` keeps the result cached using the driver default policy
272
+ - `cache: number` sets a TTL in milliseconds
273
+ - `cache: Date` sets an absolute expiration date
274
+
275
+ Examples:
276
+
277
+ ```ts
278
+ // Cache with TTL (5 seconds)
279
+ await repo.find({ where: { name: 'John' }, cache: 5000 });
280
+
281
+ // Cache until a specific date
282
+ await repo.find({ where: { name: 'John' }, cache: new Date(Date.now() + 60_000) });
283
+
284
+ // Infinite/driver-default cache
285
+ await repo.find({ where: { name: 'John' }, cache: true });
286
+ ```
287
+ Is Required to implement the validate method, that returns a boolean value.
288
+ To use the Value Object in the Entity, just add the ValueObject type to the property:
289
+
290
+ ```javascript
291
+ import { Entity, PrimaryKey, Property } from '@cheetah.js/orm';
292
+
293
+ @Entity()
294
+ export class User {
295
+ @PrimaryKey()
296
+ id: number;
297
+
298
+ @Property()
299
+ name: Name;
300
+ }
301
+ ```
302
+ Cheetah ORM will automatically convert the Value Object to the database type and vice versa.<br>
303
+ Important: If you value object is different from string type, you need to define the database type in the @Property decorator, because the Cheetah ORM would not know the correct type of your value object:
304
+
305
+ ```javascript
306
+ import { Entity, PrimaryKey, Property } from '@cheetah.js/orm';
307
+
308
+ @Entity()
309
+ export class User {
310
+ @PrimaryKey()
311
+ id: number;
312
+
313
+ @Property({ type: 'json' })
314
+ name: Name;
315
+ }
316
+ ```
317
+
318
+ #### Relations
319
+ Cheetah ORM supports relations between entities. The available relations are: OneToMany, ManyToOne.
320
+
321
+ ##### OneToMany
322
+ The OneToMany relation is used to define a one-to-many relationship between two entities. For example, a user can have multiple posts, but a post can only have one user.
323
+
324
+ ```javascript
325
+ @Entity()
326
+ export class User {
327
+ @PrimaryKey()
328
+ id: number;
329
+
330
+ @Property()
331
+ name: string;
332
+
333
+ @OneToMany(() => Post, (post) => post.user)
334
+ posts: Post[];
335
+ }
336
+ ```
337
+
338
+ #### ManyToOne
339
+ The owner side of the relation is the side that has the @ManyToOne decorator. The inverse side is the side that has the @OneToMany decorator. The owner side is always the side that has the foreign key.
340
+
341
+ ```javascript
342
+ @Entity()
343
+ export class Post {
344
+ @PrimaryKey()
345
+ id: number;
346
+
347
+ @Property()
348
+ title: string;
349
+
350
+ @ManyToOne(() => User)
351
+ user: User;
352
+ }
353
+ ```
354
+
355
+ ### [Usage](#usage)
356
+ #### Create a new entity
357
+ ```javascript
358
+ import { User } from './entity/user';
359
+
360
+ const user = User.create({ name: 'John Doe' });
361
+
362
+ // OR
363
+ const user = new User();
364
+ user.name = 'John Doe';
365
+ await user.save();
366
+ ```
367
+
368
+ #### Find a entity
369
+ ```javascript
370
+ import { User } from './entity/user';
371
+
372
+ const user = await User.findOne({
373
+ name: 'John Doe',
374
+ old: { $gte: 16, $lte: 30 }
375
+ });
376
+ ```
377
+
378
+ #### Transactions
379
+ ```typescript
380
+ import { Orm } from '@cheetah.js/orm';
381
+
382
+ const orm = Orm.getInstance();
383
+
384
+ await orm.transaction(async (tx) => {
385
+ await tx`INSERT INTO users (name) VALUES (${ 'Jane Doe' })`;
386
+ await tx`UPDATE accounts SET balance = balance - 100 WHERE user_id = ${ 1 }`;
387
+ });
388
+ ```
389
+ The `transaction` method leverages the active driver implementation, ensuring consistent transactional semantics across supported databases.
390
+
391
+ #### List of supported operators
392
+ | Operator |Name | Description |
393
+ | ------ | ---- |--------------------------------------------------------------------------------------------|
394
+ | $eq | Equal | Matches values that are equal to a specified value. |
395
+ | $gt | Greater Than | Matches values that are greater than a specified value. |
396
+ | $gte | Greater Than or Equal | Matches values that are greater than or equal to a specified value. |
397
+ | $in | In | Matches any of the values specified in an array. |
398
+ | $lt | Less Than | Matches values that are less than a specified value. |
399
+ | $lte | Less Than or Equal | Matches values that are less than or equal to a specified value. |
400
+ | $ne | Not Equal | Matches all values that are not equal to a specified value. |
401
+ | $nin | Not In | Matches none of the values specified in an array. |
402
+ | $and | And | Joins query clauses with a logical AND returns all documents that match the conditions of both clauses. |
403
+ | $or | Or | Joins query clauses with a logical OR returns all documents that match the conditions of either clause. |
404
+ | $not | Not | Inverts the effect of a query expression and returns documents that do not match the query expression. |
405
+
406
+ #### Filter by Date columns
407
+ You can filter using JavaScript `Date` instances and the ORM translates them into precise SQL comparisons:
408
+
409
+ ```typescript
410
+ const reinforcement = await User.findOne({
411
+ updatedAt: new Date('2024-06-01T00:00:00.000Z'),
412
+ });
413
+ ```
414
+
415
+ ### [Migrations](#migrations)
416
+ Cheetah ORM is capable of creating and running migrations.
417
+ To do this, you need to install our cli package:
418
+
419
+ ```bash
420
+ bun install @cheetah.js/cli
421
+ ```
422
+
423
+ You must have the connection configuration file in the project root "cheetah.config.ts".
424
+ To create a migration, run the command below:
425
+
426
+ ```bash
427
+ bunx cli migration:generate
428
+ ```
429
+ This command will create a migration file in the path defined in the configuration file, differentiating your entities created with the database.
430
+
431
+ #### Example:
432
+ ```bash
433
+ bunx cli cheetah-orm migration:run
434
+ ```
435
+ This command will run all migrations that have not yet been run.
@@ -12,6 +12,7 @@ export declare abstract class BunDriverBase implements Partial<DriverInterface>
12
12
  protected validateConnection(): Promise<void>;
13
13
  disconnect(): Promise<void>;
14
14
  executeSql(sqlString: string): Promise<any>;
15
+ private getExecutionContext;
15
16
  transaction<T>(callback: (tx: SQL) => Promise<T>): Promise<T>;
16
17
  protected toDatabaseValue(value: unknown): string | number | boolean;
17
18
  protected escapeString(value: string): string;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BunDriverBase = void 0;
4
4
  const bun_1 = require("bun");
5
+ const transaction_context_1 = require("../transaction/transaction-context");
5
6
  class BunDriverBase {
6
7
  constructor(options) {
7
8
  this.connectionString = this.buildConnectionString(options);
@@ -31,10 +32,18 @@ class BunDriverBase {
31
32
  await this.sql.close();
32
33
  }
33
34
  async executeSql(sqlString) {
35
+ const context = this.getExecutionContext();
36
+ return await context.unsafe(sqlString);
37
+ }
38
+ getExecutionContext() {
39
+ const txContext = transaction_context_1.transactionContext.getContext();
40
+ if (txContext) {
41
+ return txContext;
42
+ }
34
43
  if (!this.sql) {
35
44
  throw new Error('Database not connected');
36
45
  }
37
- return await this.sql.unsafe(sqlString);
46
+ return this.sql;
38
47
  }
39
48
  async transaction(callback) {
40
49
  if (!this.sql) {
@@ -119,13 +128,14 @@ class BunDriverBase {
119
128
  }
120
129
  async executeStatement(statement) {
121
130
  const startTime = Date.now();
131
+ const context = this.getExecutionContext();
122
132
  if (statement.statement === 'insert') {
123
133
  const sql = this.buildInsertSqlWithReturn(statement);
124
- const result = await this.sql.unsafe(sql);
134
+ const result = await context.unsafe(sql);
125
135
  return this.handleInsertReturn(statement, result, sql, startTime);
126
136
  }
127
137
  const sql = this.buildStatementSql(statement);
128
- const result = await this.sql.unsafe(sql);
138
+ const result = await context.unsafe(sql);
129
139
  return {
130
140
  query: { rows: Array.isArray(result) ? result : [] },
131
141
  startTime,
package/dist/index.d.ts CHANGED
@@ -22,3 +22,4 @@ export * from './common/value-object';
22
22
  export * from './common/email.vo';
23
23
  export * from './common/uuid';
24
24
  export * from './repository/Repository';
25
+ export { transactionContext } from './transaction/transaction-context';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.EntityStorage = void 0;
17
+ exports.transactionContext = exports.EntityStorage = void 0;
18
18
  __exportStar(require("./decorators/entity.decorator"), exports);
19
19
  __exportStar(require("./decorators/property.decorator"), exports);
20
20
  __exportStar(require("./decorators/primary-key.decorator"), exports);
@@ -39,3 +39,5 @@ __exportStar(require("./common/value-object"), exports);
39
39
  __exportStar(require("./common/email.vo"), exports);
40
40
  __exportStar(require("./common/uuid"), exports);
41
41
  __exportStar(require("./repository/Repository"), exports);
42
+ var transaction_context_1 = require("./transaction/transaction-context");
43
+ Object.defineProperty(exports, "transactionContext", { enumerable: true, get: function () { return transaction_context_1.transactionContext; } });
package/dist/orm.js CHANGED
@@ -14,6 +14,7 @@ exports.Orm = void 0;
14
14
  const core_1 = require("@cheetah.js/core");
15
15
  const SqlBuilder_1 = require("./SqlBuilder");
16
16
  const query_cache_manager_1 = require("./cache/query-cache-manager");
17
+ const transaction_context_1 = require("./transaction/transaction-context");
17
18
  let Orm = Orm_1 = class Orm {
18
19
  constructor(logger, cacheService) {
19
20
  this.logger = logger;
@@ -47,7 +48,9 @@ let Orm = Orm_1 = class Orm {
47
48
  if (!this.driverInstance) {
48
49
  throw new Error('Driver instance not initialized');
49
50
  }
50
- return this.driverInstance.transaction(operation);
51
+ return this.driverInstance.transaction(async (tx) => {
52
+ return transaction_context_1.transactionContext.run(tx, () => operation(tx));
53
+ });
51
54
  }
52
55
  };
53
56
  exports.Orm = Orm;
@@ -285,7 +285,7 @@ let OrmService = class OrmService {
285
285
  for (const entity of entities) {
286
286
  const nullableDefaultEntity = this.allEntities.get(entity.target.name);
287
287
  const propertiesFromMetadata = core_1.Metadata.get(constants_1.PROPERTIES_METADATA, entity.target);
288
- const relationship = core_1.Metadata.get(constants_1.PROPERTIES_RELATIONS, entity.target);
288
+ const relationshipsFromMetadata = core_1.Metadata.get(constants_1.PROPERTIES_RELATIONS, entity.target) || [];
289
289
  const hooks = core_1.Metadata.get(constants_1.EVENTS_METADATA, entity.target);
290
290
  // Cria uma cópia profunda das propriedades para evitar mutação compartilhada
291
291
  const properties = {};
@@ -305,7 +305,14 @@ let OrmService = class OrmService {
305
305
  properties[property].options.default = nullableDefaultEntity?.defaults[property];
306
306
  }
307
307
  }
308
- this.storage.add(entity, properties, relationship, hooks);
308
+ const relationships = relationshipsFromMetadata.map((relation) => ({ ...relation }));
309
+ for (const relation of relationships) {
310
+ const relationKey = String(relation.propertyKey);
311
+ if (nullableDefaultEntity?.nullables.includes(relationKey)) {
312
+ relation.nullable = true;
313
+ }
314
+ }
315
+ this.storage.add(entity, properties, relationships, hooks);
309
316
  }
310
317
  }
311
318
  getSourceFilePaths() {
@@ -0,0 +1,10 @@
1
+ import { SQL } from 'bun';
2
+ declare class TransactionContextManager {
3
+ private storage;
4
+ constructor();
5
+ run<T>(tx: SQL, callback: () => Promise<T>): Promise<T>;
6
+ getContext(): SQL | undefined;
7
+ hasContext(): boolean;
8
+ }
9
+ export declare const transactionContext: TransactionContextManager;
10
+ export {};
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transactionContext = void 0;
4
+ const async_hooks_1 = require("async_hooks");
5
+ class TransactionContextManager {
6
+ constructor() {
7
+ this.storage = new async_hooks_1.AsyncLocalStorage();
8
+ }
9
+ run(tx, callback) {
10
+ return this.storage.run({ tx }, callback);
11
+ }
12
+ getContext() {
13
+ return this.storage.getStore()?.tx;
14
+ }
15
+ hasContext() {
16
+ return this.storage.getStore() !== undefined;
17
+ }
18
+ }
19
+ exports.transactionContext = new TransactionContextManager();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cheetah.js/orm",
3
- "version": "0.1.113",
3
+ "version": "0.1.118",
4
4
  "description": "A simple ORM for Cheetah.js",
5
5
  "type": "commonjs",
6
6
  "main": "dist/index.js",
@@ -55,5 +55,5 @@
55
55
  "bun",
56
56
  "value-object"
57
57
  ],
58
- "gitHead": "439b0bb37b4f7b81d2aebf4171ba27792062d762"
58
+ "gitHead": "d5b23c4d1d3d720a93e3e157e658b8e57c828251"
59
59
  }
package/build.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
@@ -1,3 +0,0 @@
1
- import { ConnectionSettings } from '@cheetah.js/orm/driver/driver.interface';
2
- declare const config: ConnectionSettings<any>;
3
- export default config;