@cheetah.js/orm 0.1.106 → 0.1.107

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
@@ -123,7 +123,7 @@ export class User {
123
123
  }
124
124
  ```
125
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):
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
127
 
128
128
  ```javascript
129
129
  @Entity()
@@ -134,29 +134,29 @@ export class User {
134
134
  @Property()
135
135
  name: string;
136
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'])
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
160
  ```
161
161
 
162
162
  #### Property options
@@ -170,6 +170,56 @@ export class User {
170
170
  | onUpdate | string | Define the action to be taken for this property when updating the entity in the database |
171
171
  | onInsert | string | Defines the action to be taken for this property when inserting the entity in the database |
172
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
+
173
223
  ### [Hooks](#hooks)
174
224
  Cheetah ORM supports hooks for entities. The available hooks are: BeforeCreate, AfterCreate, BeforeUpdate, AfterUpdate, BeforeDelete, AfterDelete.
175
225
  Hooks is only for modify the entity, not for create, update or delete another entities statements in database.
@@ -317,21 +367,21 @@ const user = await User.findOne({
317
367
  | $ne | Not Equal | Matches all values that are not equal to a specified value. |
318
368
  | $nin | Not In | Matches none of the values specified in an array. |
319
369
  | $and | And | Joins query clauses with a logical AND returns all documents that match the conditions of both clauses. |
320
- | $or | Or | Joins query clauses with a logical OR returns all documents that match the conditions of either clause. |
321
- | $not | Not | Inverts the effect of a query expression and returns documents that do not match the query expression. |
322
-
323
- #### Filter by Date columns
324
- You can filter using JavaScript `Date` instances and the ORM translates them into precise SQL comparisons:
325
-
326
- ```typescript
327
- const reinforcement = await User.findOne({
328
- updatedAt: new Date('2024-06-01T00:00:00.000Z'),
329
- });
330
- ```
331
-
332
- ### [Migrations](#migrations)
333
- Cheetah ORM is capable of creating and running migrations.
334
- To do this, you need to install our cli package:
370
+ | $or | Or | Joins query clauses with a logical OR returns all documents that match the conditions of either clause. |
371
+ | $not | Not | Inverts the effect of a query expression and returns documents that do not match the query expression. |
372
+
373
+ #### Filter by Date columns
374
+ You can filter using JavaScript `Date` instances and the ORM translates them into precise SQL comparisons:
375
+
376
+ ```typescript
377
+ const reinforcement = await User.findOne({
378
+ updatedAt: new Date('2024-06-01T00:00:00.000Z'),
379
+ });
380
+ ```
381
+
382
+ ### [Migrations](#migrations)
383
+ Cheetah ORM is capable of creating and running migrations.
384
+ To do this, you need to install our cli package:
335
385
 
336
386
  ```bash
337
387
  bun install @cheetah.js/cli
@@ -3,3 +3,4 @@ export declare const PROPERTIES = "cheetah:properties";
3
3
  export declare const PROPERTIES_METADATA = "cheetah:properties:metadata";
4
4
  export declare const PROPERTIES_RELATIONS = "cheetah:properties:relations";
5
5
  export declare const EVENTS_METADATA = "cheetah:events:metadata";
6
+ export declare const COMPUTED_PROPERTIES = "cheetah:computed:properties";
package/dist/constants.js CHANGED
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EVENTS_METADATA = exports.PROPERTIES_RELATIONS = exports.PROPERTIES_METADATA = exports.PROPERTIES = exports.ENTITIES = void 0;
3
+ exports.COMPUTED_PROPERTIES = exports.EVENTS_METADATA = exports.PROPERTIES_RELATIONS = exports.PROPERTIES_METADATA = exports.PROPERTIES = exports.ENTITIES = void 0;
4
4
  exports.ENTITIES = 'cheetah:entities';
5
5
  exports.PROPERTIES = 'cheetah:properties';
6
6
  exports.PROPERTIES_METADATA = 'cheetah:properties:metadata';
7
7
  exports.PROPERTIES_RELATIONS = 'cheetah:properties:relations';
8
8
  exports.EVENTS_METADATA = 'cheetah:events:metadata';
9
+ exports.COMPUTED_PROPERTIES = 'cheetah:computed:properties';
@@ -0,0 +1 @@
1
+ export declare function Computed(): PropertyDecorator;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Computed = Computed;
4
+ const core_1 = require("@cheetah.js/core");
5
+ const constants_1 = require("../constants");
6
+ function Computed() {
7
+ return (target, propertyKey) => {
8
+ const computedProperties = core_1.Metadata.get(constants_1.COMPUTED_PROPERTIES, target.constructor) || [];
9
+ computedProperties.push(propertyKey);
10
+ core_1.Metadata.set(constants_1.COMPUTED_PROPERTIES, computedProperties, target.constructor);
11
+ };
12
+ }
@@ -47,4 +47,11 @@ export declare abstract class BaseEntity {
47
47
  */
48
48
  isPersisted(): boolean;
49
49
  toJSON(): Record<string, any>;
50
+ private serializeWithEntity;
51
+ private serializeWithMetadata;
52
+ private shouldSkipProperty;
53
+ private shouldSkipPropertyBasic;
54
+ private isInternalProperty;
55
+ private getHiddenPropertiesFromMetadata;
56
+ private addComputedProperties;
50
57
  }
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BaseEntity = void 0;
4
4
  const SqlBuilder_1 = require("../SqlBuilder");
5
5
  const entities_1 = require("./entities");
6
+ const core_1 = require("@cheetah.js/core");
7
+ const constants_1 = require("../constants");
6
8
  class BaseEntity {
7
9
  constructor() {
8
10
  this._oldValues = {};
@@ -121,24 +123,71 @@ class BaseEntity {
121
123
  return this.$_isPersisted;
122
124
  }
123
125
  toJSON() {
124
- let data = {};
125
- let storage = entities_1.EntityStorage.getInstance();
126
- let entity = storage.get(this.constructor);
127
- let allProperties = new Map(Object.entries(entity.properties).map(([key, value]) => [key, value]));
126
+ const storage = entities_1.EntityStorage.getInstance();
127
+ const entity = storage.get(this.constructor);
128
+ const data = entity
129
+ ? this.serializeWithEntity(entity)
130
+ : this.serializeWithMetadata();
131
+ this.addComputedProperties(data);
132
+ return data;
133
+ }
134
+ serializeWithEntity(entity) {
135
+ const data = {};
136
+ const allProperties = new Set(Object.keys(entity.properties));
137
+ const hidePropertiesSet = new Set(entity.hideProperties);
128
138
  for (const key in this) {
129
- // if starts with $ or _, ignore
130
- if (key.startsWith('$') || key.startsWith('_')) {
131
- continue;
132
- }
133
- if (!allProperties.has(key)) {
139
+ if (this.shouldSkipProperty(key, allProperties, hidePropertiesSet)) {
134
140
  continue;
135
141
  }
136
- if (entity.hideProperties.includes(key)) {
142
+ data[key] = this[key];
143
+ }
144
+ return data;
145
+ }
146
+ serializeWithMetadata() {
147
+ const data = {};
148
+ const hideProperties = this.getHiddenPropertiesFromMetadata();
149
+ const hidePropertiesSet = new Set(hideProperties);
150
+ for (const key in this) {
151
+ if (this.shouldSkipPropertyBasic(key, hidePropertiesSet)) {
137
152
  continue;
138
153
  }
139
154
  data[key] = this[key];
140
155
  }
141
156
  return data;
142
157
  }
158
+ shouldSkipProperty(key, allProperties, hideProperties) {
159
+ if (this.isInternalProperty(key)) {
160
+ return true;
161
+ }
162
+ if (!allProperties.has(key)) {
163
+ return true;
164
+ }
165
+ return hideProperties.has(key);
166
+ }
167
+ shouldSkipPropertyBasic(key, hideProperties) {
168
+ if (this.isInternalProperty(key)) {
169
+ return true;
170
+ }
171
+ return hideProperties.has(key);
172
+ }
173
+ isInternalProperty(key) {
174
+ return key.startsWith('$') || key.startsWith('_');
175
+ }
176
+ getHiddenPropertiesFromMetadata() {
177
+ const properties = core_1.Metadata.get(constants_1.PROPERTIES_METADATA, this.constructor) || {};
178
+ const hideProperties = [];
179
+ for (const [key, prop] of Object.entries(properties)) {
180
+ if (prop.options?.hidden) {
181
+ hideProperties.push(key);
182
+ }
183
+ }
184
+ return hideProperties;
185
+ }
186
+ addComputedProperties(data) {
187
+ const computedProperties = core_1.Metadata.get(constants_1.COMPUTED_PROPERTIES, this.constructor) || [];
188
+ for (const key of computedProperties) {
189
+ data[key] = this[key];
190
+ }
191
+ }
143
192
  }
144
193
  exports.BaseEntity = BaseEntity;
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './decorators/one-many.decorator';
5
5
  export * from './decorators/index.decorator';
6
6
  export * from './decorators/event-hook.decorator';
7
7
  export * from './decorators/enum.decorator';
8
+ export * from './decorators/computed.decorator';
8
9
  export * from './orm';
9
10
  export * from './orm.service';
10
11
  export * from './domain/base-entity';
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ __exportStar(require("./decorators/one-many.decorator"), exports);
22
22
  __exportStar(require("./decorators/index.decorator"), exports);
23
23
  __exportStar(require("./decorators/event-hook.decorator"), exports);
24
24
  __exportStar(require("./decorators/enum.decorator"), exports);
25
+ __exportStar(require("./decorators/computed.decorator"), exports);
25
26
  __exportStar(require("./orm"), exports);
26
27
  __exportStar(require("./orm.service"), exports);
27
28
  __exportStar(require("./domain/base-entity"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cheetah.js/orm",
3
- "version": "0.1.106",
3
+ "version": "0.1.107",
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": "90721637d6b394e2bbf17278296122111358b881"
58
+ "gitHead": "0c79e3c070683f0a432056d7e57d6d6a6a53b190"
59
59
  }