remult 0.23.0-exp.18 → 0.23.0-exp.19

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/esm/graphql.js CHANGED
@@ -794,7 +794,7 @@ export function translateWhereToRestBody(fields, { where }) {
794
794
  return undefined;
795
795
  const result = {};
796
796
  for (const field of fields) {
797
- if (field.includedInApi === false)
797
+ if (field.options.includeInApi === false)
798
798
  continue;
799
799
  const condition = where[field.key];
800
800
  if (condition) {
package/esm/index.d.ts CHANGED
@@ -30,4 +30,16 @@ export { ValueConverters } from './src/valueConverters';
30
30
  export { remult } from './src/remult-proxy';
31
31
  export { SubscriptionServer, LiveQueryStorage, StoredQuery, InMemoryLiveQueryStorage, } from './src/live-query/SubscriptionServer';
32
32
  export { SubscriptionListener, SubscriptionClientConnection, SubscriptionClient, SubscriptionChannel, LiveQueryChange, Unsubscribe, } from './src/live-query/SubscriptionChannel';
33
+ /**
34
+ * A convenient shortcut function to quickly obtain a repository for a specific entity type in Remult.
35
+ *
36
+ * @param entity The entity class type for which you want to get a repository.
37
+ * @returns A repository instance for the specified entity type.
38
+ *
39
+ * Example usage:
40
+ * ```ts
41
+ * await repo(Task).find()
42
+ * await repo(Customer).insert()
43
+ * ```
44
+ */
33
45
  export declare function repo<entityType>(entity: ClassType<entityType>): import("./src/remult3/remult3").Repository<entityType>;
package/esm/index.js CHANGED
@@ -25,6 +25,18 @@ import { remult } from './src/remult-proxy';
25
25
  //export { getId } from './src/remult3/getId';
26
26
  export { InMemoryLiveQueryStorage, } from './src/live-query/SubscriptionServer';
27
27
  export { SubscriptionChannel, } from './src/live-query/SubscriptionChannel';
28
+ /**
29
+ * A convenient shortcut function to quickly obtain a repository for a specific entity type in Remult.
30
+ *
31
+ * @param entity The entity class type for which you want to get a repository.
32
+ * @returns A repository instance for the specified entity type.
33
+ *
34
+ * Example usage:
35
+ * ```ts
36
+ * await repo(Task).find()
37
+ * await repo(Customer).insert()
38
+ * ```
39
+ */
28
40
  export function repo(entity) {
29
41
  return remult.repo(entity);
30
42
  }
@@ -6,7 +6,7 @@ export declare class CompoundIdField implements FieldMetadata<string> {
6
6
  constructor(...columns: FieldMetadata[]);
7
7
  apiUpdateAllowed(item: any): boolean;
8
8
  displayValue(item: any): string;
9
- includedInApi: boolean;
9
+ includedInApi(item: any): boolean;
10
10
  toInput(value: string, inputType?: string): string;
11
11
  fromInput(inputValue: string, inputType?: string): string;
12
12
  getDbName(): Promise<string>;
@@ -9,7 +9,9 @@ export class CompoundIdField {
9
9
  displayValue(item) {
10
10
  throw new Error('Method not implemented.');
11
11
  }
12
- includedInApi;
12
+ includedInApi(item) {
13
+ throw new Error('Method not implemented.');
14
+ }
13
15
  toInput(value, inputType) {
14
16
  throw new Error('Method not implemented.');
15
17
  }
@@ -1,6 +1,6 @@
1
1
  import type { ClassType } from '../classType';
2
- import type { Allowed, AllowedForInstance } from './context';
3
- import type { EntityMetadata, FieldRef, LifeCycleEvent, OmitEB } from './remult3/remult3';
2
+ import type { AllowedForInstance } from './context';
3
+ import type { EntityMetadata, FieldRef, LifecycleEvent, OmitEB } from './remult3/remult3';
4
4
  export interface FieldOptions<entityType = any, valueType = any> {
5
5
  /** A human readable name for the field. Can be used to achieve a consistent caption for a field throughout the app
6
6
  * @example
@@ -11,7 +11,7 @@ export interface FieldOptions<entityType = any, valueType = any> {
11
11
  allowNull?: boolean;
12
12
  /** If this field data is included in the api.
13
13
  * @see [allowed](http://remult.dev/docs/allowed.html)*/
14
- includeInApi?: Allowed;
14
+ includeInApi?: AllowedForInstance<entityType>;
15
15
  /** If this field data can be updated in the api.
16
16
  * @see [allowed](http://remult.dev/docs/allowed.html)*/
17
17
  allowApiUpdate?: AllowedForInstance<entityType>;
@@ -37,7 +37,7 @@ export interface FieldOptions<entityType = any, valueType = any> {
37
37
  */
38
38
  validate?: ((entity: entityType, fieldRef: FieldRef<entityType, valueType>) => any | Promise<any>) | ((entity: entityType, fieldRef: FieldRef<entityType, valueType>) => any | Promise<any>)[];
39
39
  /** Will be fired before this field is saved to the server/database */
40
- saving?: (entity: entityType, e: LifeCycleEvent<entityType>, fieldRef: FieldRef<entityType, valueType>) => any | Promise<any>;
40
+ saving?: (entity: entityType, e: LifecycleEvent<entityType>, fieldRef: FieldRef<entityType, valueType>) => any | Promise<any>;
41
41
  /** An expression that will determine this fields value on the backend and be provided to the front end*/
42
42
  serverExpression?: (entity: entityType) => valueType | Promise<valueType>;
43
43
  /** The name of the column in the database that holds the data for this field. If no name is set, the key will be used instead.
@@ -134,7 +134,7 @@ export interface FieldMetadata<valueType = any, entityType = any> {
134
134
  */
135
135
  displayValue(item: Partial<OmitEB<entityType>>): string;
136
136
  apiUpdateAllowed(item?: Partial<OmitEB<entityType>>): boolean;
137
- readonly includedInApi: boolean;
137
+ includedInApi(item?: Partial<OmitEB<entityType>>): boolean;
138
138
  /** Adapts the value for usage with html input
139
139
  * @example
140
140
  * @Fields.dateOnly()
@@ -286,3 +286,6 @@ class transactionLiveQueryPublisher {
286
286
  }
287
287
  }
288
288
  }
289
+ //p1 - seems that after backend methods relations are loaded for some reason - see trempim - intake doRequest backend method
290
+ //p1 - reconsider the case in tremping where create user id was updated with some user and not the actual user
291
+ //[ ] - when value changes for field with error, clear error - so the user will feel comfortable
@@ -1,6 +1,6 @@
1
1
  import type { Allowed, AllowedForInstance } from './context';
2
2
  import type { FieldMetadata } from './column-interfaces';
3
- import type { EntityFilter, EntityIdFields, EntityMetadata, EntityOrderBy, EntityRef, FieldsMetadata, LifeCycleEvent } from './remult3/remult3';
3
+ import type { EntityFilter, EntityIdFields, EntityMetadata, EntityOrderBy, EntityRef, FieldsMetadata, LifecycleEvent } from './remult3/remult3';
4
4
  export interface EntityOptions<entityType = any> {
5
5
  /**A human readable name for the entity */
6
6
  caption?: string;
@@ -55,20 +55,51 @@ export interface EntityOptions<entityType = any> {
55
55
  * this is the place to run logic that we want to run in any case before an entity is saved.
56
56
  * @example
57
57
  * @Entity<Task>("tasks", {
58
- * saving: async task => {
59
- * task.lastUpdated = new Date()
60
- * }
61
- *})
58
+ * saving: async (task, e) => {
59
+ * if (e.isNew) {
60
+ * task.createdAt = new Date(); // Set the creation date for new tasks.
61
+ * }
62
+ * task.lastUpdated = new Date(); // Update the last updated date.
63
+ * },
64
+ * })
65
+ * @param entity - The instance of the entity being saved.
66
+ * @param event - an @link LifeCycleEvent object
67
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
62
68
  */
63
- saving?: (row: entityType, e: LifeCycleEvent<entityType>) => Promise<any> | any;
64
- /** will be called after the Entity was saved to the data source. */
65
- saved?: (row: entityType, e: LifeCycleEvent<entityType>) => Promise<any> | any;
66
- /** Will be called before an Entity is deleted. */
67
- deleting?: (row: entityType, e: LifeCycleEvent<entityType>) => Promise<any> | any;
68
- /** Will be called after an Entity is deleted */
69
- deleted?: (row: entityType, e: LifeCycleEvent<entityType>) => Promise<any> | any;
70
- /** Will be called when the entity is being validated, usually prior to the `saving` event */
71
- validation?: (row: entityType, ref: EntityRef<entityType>) => Promise<any> | any;
69
+ saving?: (entity: entityType, event: LifecycleEvent<entityType>) => Promise<any> | any;
70
+ /**
71
+ * A hook that runs after an entity has been successfully saved.
72
+ *
73
+ * @param entity The instance of the entity that was saved.
74
+ * @param event - an @link LifeCycleEvent object
75
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
76
+ */
77
+ saved?: (entity: entityType, e: LifecycleEvent<entityType>) => Promise<any> | any;
78
+ /**
79
+ * A hook that runs before an entity is deleted.
80
+ *
81
+ * @param entity The instance of the entity being deleted.
82
+ * @param event - an @link LifeCycleEvent object
83
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
84
+ */
85
+ deleting?: (entity: entityType, e: LifecycleEvent<entityType>) => Promise<any> | any;
86
+ /**
87
+ * A hook that runs after an entity has been successfully deleted.
88
+ *
89
+ * @param entity The instance of the entity that was deleted.
90
+ * @param event - an @link LifeCycleEvent object
91
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
92
+ */
93
+ deleted?: (entity: entityType, e: LifecycleEvent<entityType>) => Promise<any> | any;
94
+ /**
95
+ * A hook that runs to perform validation checks on an entity before saving.
96
+ * This hook is also executed on the frontend.
97
+ *
98
+ * @param entity The instance of the entity being validated.
99
+ * @param event - an @link LifeCycleEvent object
100
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
101
+ */
102
+ validation?: (entity: entityType, ref: LifecycleEvent<entityType>) => Promise<any> | any;
72
103
  /** The name of the table in the database that holds the data for this entity.
73
104
  * If no name is set, the `key` will be used instead.
74
105
  * @example
@@ -84,10 +115,10 @@ export interface EntityOptions<entityType = any> {
84
115
  /** An arrow function that identifies the `id` column to use for this entity
85
116
  * @example
86
117
  * //Single column id
87
- * @Entity<Products>("products", { id:p=>p.productCode })
118
+ * @Entity<Products>("products", { id: {productCode: true} })
88
119
  * @example
89
120
  * //Multiple columns id
90
- * @Entity<OrderDetails>("orderDetails", { id:od=> [od.orderId, od.productCode] })
121
+ * @Entity<OrderDetails>("orderDetails", { id:{ orderId:true, productCode:true} })
91
122
  */
92
123
  id?: EntityIdFields<entityType> | ((entity: FieldsMetadata<entityType>) => FieldMetadata | FieldMetadata[]);
93
124
  entityRefInit?: (ref: EntityRef<entityType>, row: entityType) => void;
@@ -190,7 +190,7 @@ export declare class columnDefsImpl implements FieldMetadata {
190
190
  constructor(settings: FieldOptions, entityDefs: EntityFullInfo<any>, remult: Remult);
191
191
  apiUpdateAllowed(item?: any): boolean;
192
192
  displayValue(item: any): string;
193
- get includedInApi(): boolean;
193
+ includedInApi(item?: any): boolean;
194
194
  toInput(value: any, inputType?: string): string;
195
195
  fromInput(inputValue: string, inputType?: string): any;
196
196
  getDbName(): Promise<string>;
@@ -1039,7 +1039,7 @@ class rowHelperBase {
1039
1039
  for (const col of this.columnsInfo) {
1040
1040
  if (!this.remult ||
1041
1041
  col.includeInApi === undefined ||
1042
- this.remult.isAllowed(col.includeInApi)) {
1042
+ this.remult.isAllowedForInstance(this.instance, col.includeInApi)) {
1043
1043
  let val;
1044
1044
  let lu = this.lookups.get(col.key);
1045
1045
  let disable = false;
@@ -1073,7 +1073,7 @@ class rowHelperBase {
1073
1073
  for (const col of this.columnsInfo) {
1074
1074
  if (keys.includes(col.key))
1075
1075
  if (col.includeInApi === undefined ||
1076
- this.remult.isAllowed(col.includeInApi)) {
1076
+ this.remult.isAllowedForInstance(this.instance, col.includeInApi)) {
1077
1077
  if (!this.remult ||
1078
1078
  ignoreApiAllowed ||
1079
1079
  col.allowApiUpdate === undefined ||
@@ -1363,8 +1363,10 @@ export class rowHelperImplementation extends rowHelperBase {
1363
1363
  await col.__performValidation();
1364
1364
  }
1365
1365
  }
1366
- if (this.info.entityInfo.validation)
1367
- await this.info.entityInfo.validation(this.instance, this);
1366
+ if (this.info.entityInfo.validation) {
1367
+ let e = this.buildLifeCycleEvent(() => { });
1368
+ await this.info.entityInfo.validation(this.instance, e);
1369
+ }
1368
1370
  if (this.repository.listeners)
1369
1371
  for (const listener of this.repository.listeners.filter((x) => x.validating)) {
1370
1372
  await listener.validating(this.instance);
@@ -1635,10 +1637,10 @@ export class columnDefsImpl {
1635
1637
  .getEntityMetadataWithoutBreakingTheEntity(item)
1636
1638
  .fields.find(this.key).displayValue;
1637
1639
  }
1638
- get includedInApi() {
1640
+ includedInApi(item) {
1639
1641
  if (this.options.includeInApi === undefined)
1640
1642
  return true;
1641
- return this.remult.isAllowed(this.options.includeInApi); //TODO 2- consolidate other code paths to go through here
1643
+ return this.remult.isAllowedForInstance(item, this.options.includeInApi);
1642
1644
  }
1643
1645
  toInput(value, inputType) {
1644
1646
  return this.valueConverter.toInput(value, inputType);
@@ -29,14 +29,48 @@ export interface EntityRef<entityType> extends Subscribable {
29
29
  readonly apiInsertAllowed: boolean;
30
30
  readonly isLoading: boolean;
31
31
  }
32
- export interface LifeCycleEvent<entityType> {
32
+ /**
33
+ * Represents a lifecycle event associated with an entity instance. These events
34
+ * are triggered during various stages of the entity's lifecycle, such as validation,
35
+ * saving, saved, deleting, and deleted.
36
+ *
37
+ * @template entityType The type of the entity associated with the event.
38
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
39
+ */
40
+ export interface LifecycleEvent<entityType> {
41
+ /**
42
+ * Indicates whether the entity is new or existing.
43
+ */
33
44
  isNew: boolean;
45
+ /**
46
+ * A reference to the fields of the entity, providing access to its properties.
47
+ */
34
48
  fields: FieldsRef<entityType>;
49
+ /**
50
+ * The ID of the entity.
51
+ */
35
52
  id: idType<entityType>;
53
+ /**
54
+ * The original ID of the entity, useful for tracking changes.
55
+ */
36
56
  originalId: idType<entityType>;
57
+ /**
58
+ * The repository associated with the entity, providing access to repository methods.
59
+ */
37
60
  repository: Repository<entityType>;
61
+ /**
62
+ * Metadata describing the entity's structure and configuration.
63
+ */
38
64
  metadata: EntityMetadata<entityType>;
65
+ /**
66
+ * A function that can be used to prevent the default behavior associated with
67
+ * the lifecycle event.
68
+ */
39
69
  preventDefault: VoidFunction;
70
+ /**
71
+ * A reference to the repository relations associated with the entity, providing
72
+ * access to related entities and their data.
73
+ */
40
74
  relations: RepositoryRelations<entityType>;
41
75
  }
42
76
  export interface ControllerRef<entityType> extends Subscribable {
@@ -461,6 +495,12 @@ export interface RelationInfo {
461
495
  * @template optionsType The type of find options to apply to the relation (default is FindOptionsBase<toEntity>).
462
496
  */
463
497
  export type RelationOptions<fromEntity, toEntity, matchIdEntity, optionsType extends FindOptionsBase<toEntity> = FindOptionsBase<toEntity>> = {
498
+ /**
499
+ * An object specifying custom field names for the relation.
500
+ * Each key represents a field in the related entity, and its value is the corresponding field in the source entity.
501
+ * For example, `{ customerId: 'id' }` maps the 'customerId' field in the related entity to the 'id' field in the source entity.
502
+ * This is useful when you want to define custom field mappings for the relation.
503
+ */
464
504
  fields?: {
465
505
  [K in keyof toEntity]?: keyof fromEntity;
466
506
  };
@@ -1,3 +1,31 @@
1
1
  export {};
2
2
  //p1 - include relations in the api docs
3
3
  //p1 - include Relation options in the api docs
4
+ //p1 - fix { allowApiRead: r => r.user.name === 'superman' } } in allowed for instace docs
5
+ //p1 - fix seeallowed in ref_entity
6
+ //p1 - write doc about controlling updates of rows...
7
+ //p1 - 'eventId' autocomplete in task app did not work
8
+ //p1 - need a way to extract the fields from the relation - for generating relation based sql
9
+ //p1 - remult object was used outside context
10
+ // ```
11
+ // sqlExpression: async (e) => {
12
+ // const v = await dbNamesOf(volunteerInTask)
13
+ // return `select true from ${v} where ${
14
+ // v.task
15
+ // }=${await e.getDbName()}.${await e.fields.id.getDbName()} and ${SqlDatabase.filterToRaw<volunteerInTask>(
16
+ // repo(volunteerInTask),
17
+ // {
18
+ // canceled: false,
19
+ // }
20
+ // )}`
21
+ // },
22
+ // })
23
+ // ```
24
+ //p2 - consider includeInApi as AllowedForInstance
25
+ //p1 - why didn't this._.relations.statusChanges.insert({})work?
26
+ //p1 - active record with include thinks that the row was change!
27
+ //p1 - add docs for apply changes
28
+ //p1 - update related field with null should respect it's allowed null - otherwise we get a null error for that field.
29
+ //p1 - when the include doesn't find the many to one - it deletes the id!!!
30
+ //y1 - discuss a default date formatter
31
+ //p1 - consider using this in saving etc...
package/graphql.js CHANGED
@@ -908,7 +908,7 @@ function translateWhereToRestBody(fields, _a) {
908
908
  var result = {};
909
909
  var _loop_3 = function (field) {
910
910
  var e_8, _c;
911
- if (field.includedInApi === false)
911
+ if (field.options.includeInApi === false)
912
912
  return "continue";
913
913
  var condition = where[field.key];
914
914
  if (condition) {
package/index.d.ts CHANGED
@@ -30,4 +30,16 @@ export { ValueConverters } from './src/valueConverters';
30
30
  export { remult } from './src/remult-proxy';
31
31
  export { SubscriptionServer, LiveQueryStorage, StoredQuery, InMemoryLiveQueryStorage, } from './src/live-query/SubscriptionServer';
32
32
  export { SubscriptionListener, SubscriptionClientConnection, SubscriptionClient, SubscriptionChannel, LiveQueryChange, Unsubscribe, } from './src/live-query/SubscriptionChannel';
33
+ /**
34
+ * A convenient shortcut function to quickly obtain a repository for a specific entity type in Remult.
35
+ *
36
+ * @param entity The entity class type for which you want to get a repository.
37
+ * @returns A repository instance for the specified entity type.
38
+ *
39
+ * Example usage:
40
+ * ```ts
41
+ * await repo(Task).find()
42
+ * await repo(Customer).insert()
43
+ * ```
44
+ */
33
45
  export declare function repo<entityType>(entity: ClassType<entityType>): import("./src/remult3/remult3").Repository<entityType>;
package/index.js CHANGED
@@ -67,6 +67,18 @@ var SubscriptionServer_1 = require("./src/live-query/SubscriptionServer");
67
67
  Object.defineProperty(exports, "InMemoryLiveQueryStorage", { enumerable: true, get: function () { return SubscriptionServer_1.InMemoryLiveQueryStorage; } });
68
68
  var SubscriptionChannel_1 = require("./src/live-query/SubscriptionChannel");
69
69
  Object.defineProperty(exports, "SubscriptionChannel", { enumerable: true, get: function () { return SubscriptionChannel_1.SubscriptionChannel; } });
70
+ /**
71
+ * A convenient shortcut function to quickly obtain a repository for a specific entity type in Remult.
72
+ *
73
+ * @param entity The entity class type for which you want to get a repository.
74
+ * @returns A repository instance for the specified entity type.
75
+ *
76
+ * Example usage:
77
+ * ```ts
78
+ * await repo(Task).find()
79
+ * await repo(Customer).insert()
80
+ * ```
81
+ */
70
82
  function repo(entity) {
71
83
  return remult_proxy_2.remult.repo(entity);
72
84
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remult",
3
- "version": "0.23.0-exp.18",
3
+ "version": "0.23.0-exp.19",
4
4
  "description": "A CRUD framework for full-stack TypeScript",
5
5
  "types": "index.d.ts",
6
6
  "module": "esm/index.js",
@@ -6,7 +6,7 @@ export declare class CompoundIdField implements FieldMetadata<string> {
6
6
  constructor(...columns: FieldMetadata[]);
7
7
  apiUpdateAllowed(item: any): boolean;
8
8
  displayValue(item: any): string;
9
- includedInApi: boolean;
9
+ includedInApi(item: any): boolean;
10
10
  toInput(value: string, inputType?: string): string;
11
11
  fromInput(inputValue: string, inputType?: string): string;
12
12
  getDbName(): Promise<string>;
@@ -15,6 +15,9 @@ var CompoundIdField = /** @class */ (function () {
15
15
  CompoundIdField.prototype.displayValue = function (item) {
16
16
  throw new Error('Method not implemented.');
17
17
  };
18
+ CompoundIdField.prototype.includedInApi = function (item) {
19
+ throw new Error('Method not implemented.');
20
+ };
18
21
  CompoundIdField.prototype.toInput = function (value, inputType) {
19
22
  throw new Error('Method not implemented.');
20
23
  };
@@ -1,6 +1,6 @@
1
1
  import type { ClassType } from '../classType';
2
- import type { Allowed, AllowedForInstance } from './context';
3
- import type { EntityMetadata, FieldRef, LifeCycleEvent, OmitEB } from './remult3/remult3';
2
+ import type { AllowedForInstance } from './context';
3
+ import type { EntityMetadata, FieldRef, LifecycleEvent, OmitEB } from './remult3/remult3';
4
4
  export interface FieldOptions<entityType = any, valueType = any> {
5
5
  /** A human readable name for the field. Can be used to achieve a consistent caption for a field throughout the app
6
6
  * @example
@@ -11,7 +11,7 @@ export interface FieldOptions<entityType = any, valueType = any> {
11
11
  allowNull?: boolean;
12
12
  /** If this field data is included in the api.
13
13
  * @see [allowed](http://remult.dev/docs/allowed.html)*/
14
- includeInApi?: Allowed;
14
+ includeInApi?: AllowedForInstance<entityType>;
15
15
  /** If this field data can be updated in the api.
16
16
  * @see [allowed](http://remult.dev/docs/allowed.html)*/
17
17
  allowApiUpdate?: AllowedForInstance<entityType>;
@@ -37,7 +37,7 @@ export interface FieldOptions<entityType = any, valueType = any> {
37
37
  */
38
38
  validate?: ((entity: entityType, fieldRef: FieldRef<entityType, valueType>) => any | Promise<any>) | ((entity: entityType, fieldRef: FieldRef<entityType, valueType>) => any | Promise<any>)[];
39
39
  /** Will be fired before this field is saved to the server/database */
40
- saving?: (entity: entityType, e: LifeCycleEvent<entityType>, fieldRef: FieldRef<entityType, valueType>) => any | Promise<any>;
40
+ saving?: (entity: entityType, e: LifecycleEvent<entityType>, fieldRef: FieldRef<entityType, valueType>) => any | Promise<any>;
41
41
  /** An expression that will determine this fields value on the backend and be provided to the front end*/
42
42
  serverExpression?: (entity: entityType) => valueType | Promise<valueType>;
43
43
  /** The name of the column in the database that holds the data for this field. If no name is set, the key will be used instead.
@@ -134,7 +134,7 @@ export interface FieldMetadata<valueType = any, entityType = any> {
134
134
  */
135
135
  displayValue(item: Partial<OmitEB<entityType>>): string;
136
136
  apiUpdateAllowed(item?: Partial<OmitEB<entityType>>): boolean;
137
- readonly includedInApi: boolean;
137
+ includedInApi(item?: Partial<OmitEB<entityType>>): boolean;
138
138
  /** Adapts the value for usage with html input
139
139
  * @example
140
140
  * @Fields.dateOnly()
package/src/context.js CHANGED
@@ -450,3 +450,6 @@ var transactionLiveQueryPublisher = /** @class */ (function () {
450
450
  };
451
451
  return transactionLiveQueryPublisher;
452
452
  }());
453
+ //p1 - seems that after backend methods relations are loaded for some reason - see trempim - intake doRequest backend method
454
+ //p1 - reconsider the case in tremping where create user id was updated with some user and not the actual user
455
+ //[ ] - when value changes for field with error, clear error - so the user will feel comfortable
package/src/entity.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { Allowed, AllowedForInstance } from './context';
2
2
  import type { FieldMetadata } from './column-interfaces';
3
- import type { EntityFilter, EntityIdFields, EntityMetadata, EntityOrderBy, EntityRef, FieldsMetadata, LifeCycleEvent } from './remult3/remult3';
3
+ import type { EntityFilter, EntityIdFields, EntityMetadata, EntityOrderBy, EntityRef, FieldsMetadata, LifecycleEvent } from './remult3/remult3';
4
4
  export interface EntityOptions<entityType = any> {
5
5
  /**A human readable name for the entity */
6
6
  caption?: string;
@@ -55,20 +55,51 @@ export interface EntityOptions<entityType = any> {
55
55
  * this is the place to run logic that we want to run in any case before an entity is saved.
56
56
  * @example
57
57
  * @Entity<Task>("tasks", {
58
- * saving: async task => {
59
- * task.lastUpdated = new Date()
60
- * }
61
- *})
58
+ * saving: async (task, e) => {
59
+ * if (e.isNew) {
60
+ * task.createdAt = new Date(); // Set the creation date for new tasks.
61
+ * }
62
+ * task.lastUpdated = new Date(); // Update the last updated date.
63
+ * },
64
+ * })
65
+ * @param entity - The instance of the entity being saved.
66
+ * @param event - an @link LifeCycleEvent object
67
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
62
68
  */
63
- saving?: (row: entityType, e: LifeCycleEvent<entityType>) => Promise<any> | any;
64
- /** will be called after the Entity was saved to the data source. */
65
- saved?: (row: entityType, e: LifeCycleEvent<entityType>) => Promise<any> | any;
66
- /** Will be called before an Entity is deleted. */
67
- deleting?: (row: entityType, e: LifeCycleEvent<entityType>) => Promise<any> | any;
68
- /** Will be called after an Entity is deleted */
69
- deleted?: (row: entityType, e: LifeCycleEvent<entityType>) => Promise<any> | any;
70
- /** Will be called when the entity is being validated, usually prior to the `saving` event */
71
- validation?: (row: entityType, ref: EntityRef<entityType>) => Promise<any> | any;
69
+ saving?: (entity: entityType, event: LifecycleEvent<entityType>) => Promise<any> | any;
70
+ /**
71
+ * A hook that runs after an entity has been successfully saved.
72
+ *
73
+ * @param entity The instance of the entity that was saved.
74
+ * @param event - an @link LifeCycleEvent object
75
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
76
+ */
77
+ saved?: (entity: entityType, e: LifecycleEvent<entityType>) => Promise<any> | any;
78
+ /**
79
+ * A hook that runs before an entity is deleted.
80
+ *
81
+ * @param entity The instance of the entity being deleted.
82
+ * @param event - an @link LifeCycleEvent object
83
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
84
+ */
85
+ deleting?: (entity: entityType, e: LifecycleEvent<entityType>) => Promise<any> | any;
86
+ /**
87
+ * A hook that runs after an entity has been successfully deleted.
88
+ *
89
+ * @param entity The instance of the entity that was deleted.
90
+ * @param event - an @link LifeCycleEvent object
91
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
92
+ */
93
+ deleted?: (entity: entityType, e: LifecycleEvent<entityType>) => Promise<any> | any;
94
+ /**
95
+ * A hook that runs to perform validation checks on an entity before saving.
96
+ * This hook is also executed on the frontend.
97
+ *
98
+ * @param entity The instance of the entity being validated.
99
+ * @param event - an @link LifeCycleEvent object
100
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
101
+ */
102
+ validation?: (entity: entityType, ref: LifecycleEvent<entityType>) => Promise<any> | any;
72
103
  /** The name of the table in the database that holds the data for this entity.
73
104
  * If no name is set, the `key` will be used instead.
74
105
  * @example
@@ -84,10 +115,10 @@ export interface EntityOptions<entityType = any> {
84
115
  /** An arrow function that identifies the `id` column to use for this entity
85
116
  * @example
86
117
  * //Single column id
87
- * @Entity<Products>("products", { id:p=>p.productCode })
118
+ * @Entity<Products>("products", { id: {productCode: true} })
88
119
  * @example
89
120
  * //Multiple columns id
90
- * @Entity<OrderDetails>("orderDetails", { id:od=> [od.orderId, od.productCode] })
121
+ * @Entity<OrderDetails>("orderDetails", { id:{ orderId:true, productCode:true} })
91
122
  */
92
123
  id?: EntityIdFields<entityType> | ((entity: FieldsMetadata<entityType>) => FieldMetadata | FieldMetadata[]);
93
124
  entityRefInit?: (ref: EntityRef<entityType>, row: entityType) => void;
@@ -190,7 +190,7 @@ export declare class columnDefsImpl implements FieldMetadata {
190
190
  constructor(settings: FieldOptions, entityDefs: EntityFullInfo<any>, remult: Remult);
191
191
  apiUpdateAllowed(item?: any): boolean;
192
192
  displayValue(item: any): string;
193
- get includedInApi(): boolean;
193
+ includedInApi(item?: any): boolean;
194
194
  toInput(value: any, inputType?: string): string;
195
195
  fromInput(inputValue: string, inputType?: string): any;
196
196
  getDbName(): Promise<string>;
@@ -1635,7 +1635,7 @@ var rowHelperBase = /** @class */ (function () {
1635
1635
  var col = _c.value;
1636
1636
  if (!this.remult ||
1637
1637
  col.includeInApi === undefined ||
1638
- this.remult.isAllowed(col.includeInApi)) {
1638
+ this.remult.isAllowedForInstance(this.instance, col.includeInApi)) {
1639
1639
  var val = void 0;
1640
1640
  var lu = this.lookups.get(col.key);
1641
1641
  var disable = false;
@@ -1686,7 +1686,7 @@ var rowHelperBase = /** @class */ (function () {
1686
1686
  col = _b.value;
1687
1687
  if (keys.includes(col.key))
1688
1688
  if (col.includeInApi === undefined ||
1689
- this.remult.isAllowed(col.includeInApi)) {
1689
+ this.remult.isAllowedForInstance(this.instance, col.includeInApi)) {
1690
1690
  if (!this.remult ||
1691
1691
  ignoreApiAllowed ||
1692
1692
  col.allowApiUpdate === undefined ||
@@ -2264,7 +2264,7 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2264
2264
  };
2265
2265
  rowHelperImplementation.prototype.__performColumnAndEntityValidations = function () {
2266
2266
  return tslib_1.__awaiter(this, void 0, void 0, function () {
2267
- var _a, _b, c, col, e_37_1, _c, _d, listener, e_38_1;
2267
+ var _a, _b, c, col, e_37_1, e, _c, _d, listener, e_38_1;
2268
2268
  var e_37, _e, e_38, _f;
2269
2269
  return tslib_1.__generator(this, function (_g) {
2270
2270
  switch (_g.label) {
@@ -2297,7 +2297,8 @@ var rowHelperImplementation = /** @class */ (function (_super) {
2297
2297
  return [7 /*endfinally*/];
2298
2298
  case 7:
2299
2299
  if (!this.info.entityInfo.validation) return [3 /*break*/, 9];
2300
- return [4 /*yield*/, this.info.entityInfo.validation(this.instance, this)];
2300
+ e = this.buildLifeCycleEvent(function () { });
2301
+ return [4 /*yield*/, this.info.entityInfo.validation(this.instance, e)];
2301
2302
  case 8:
2302
2303
  _g.sent();
2303
2304
  _g.label = 9;
@@ -2725,15 +2726,11 @@ var columnDefsImpl = /** @class */ (function () {
2725
2726
  .getEntityMetadataWithoutBreakingTheEntity(item)
2726
2727
  .fields.find(this.key).displayValue;
2727
2728
  };
2728
- Object.defineProperty(columnDefsImpl.prototype, "includedInApi", {
2729
- get: function () {
2730
- if (this.options.includeInApi === undefined)
2731
- return true;
2732
- return this.remult.isAllowed(this.options.includeInApi); //TODO 2- consolidate other code paths to go through here
2733
- },
2734
- enumerable: false,
2735
- configurable: true
2736
- });
2729
+ columnDefsImpl.prototype.includedInApi = function (item) {
2730
+ if (this.options.includeInApi === undefined)
2731
+ return true;
2732
+ return this.remult.isAllowedForInstance(item, this.options.includeInApi);
2733
+ };
2737
2734
  columnDefsImpl.prototype.toInput = function (value, inputType) {
2738
2735
  return this.valueConverter.toInput(value, inputType);
2739
2736
  };
@@ -29,14 +29,48 @@ export interface EntityRef<entityType> extends Subscribable {
29
29
  readonly apiInsertAllowed: boolean;
30
30
  readonly isLoading: boolean;
31
31
  }
32
- export interface LifeCycleEvent<entityType> {
32
+ /**
33
+ * Represents a lifecycle event associated with an entity instance. These events
34
+ * are triggered during various stages of the entity's lifecycle, such as validation,
35
+ * saving, saved, deleting, and deleted.
36
+ *
37
+ * @template entityType The type of the entity associated with the event.
38
+ * @see [Entity Lifecycle Hooks](http://remult.dev/docs/lifecycle-hooks)
39
+ */
40
+ export interface LifecycleEvent<entityType> {
41
+ /**
42
+ * Indicates whether the entity is new or existing.
43
+ */
33
44
  isNew: boolean;
45
+ /**
46
+ * A reference to the fields of the entity, providing access to its properties.
47
+ */
34
48
  fields: FieldsRef<entityType>;
49
+ /**
50
+ * The ID of the entity.
51
+ */
35
52
  id: idType<entityType>;
53
+ /**
54
+ * The original ID of the entity, useful for tracking changes.
55
+ */
36
56
  originalId: idType<entityType>;
57
+ /**
58
+ * The repository associated with the entity, providing access to repository methods.
59
+ */
37
60
  repository: Repository<entityType>;
61
+ /**
62
+ * Metadata describing the entity's structure and configuration.
63
+ */
38
64
  metadata: EntityMetadata<entityType>;
65
+ /**
66
+ * A function that can be used to prevent the default behavior associated with
67
+ * the lifecycle event.
68
+ */
39
69
  preventDefault: VoidFunction;
70
+ /**
71
+ * A reference to the repository relations associated with the entity, providing
72
+ * access to related entities and their data.
73
+ */
40
74
  relations: RepositoryRelations<entityType>;
41
75
  }
42
76
  export interface ControllerRef<entityType> extends Subscribable {
@@ -461,6 +495,12 @@ export interface RelationInfo {
461
495
  * @template optionsType The type of find options to apply to the relation (default is FindOptionsBase<toEntity>).
462
496
  */
463
497
  export type RelationOptions<fromEntity, toEntity, matchIdEntity, optionsType extends FindOptionsBase<toEntity> = FindOptionsBase<toEntity>> = {
498
+ /**
499
+ * An object specifying custom field names for the relation.
500
+ * Each key represents a field in the related entity, and its value is the corresponding field in the source entity.
501
+ * For example, `{ customerId: 'id' }` maps the 'customerId' field in the related entity to the 'id' field in the source entity.
502
+ * This is useful when you want to define custom field mappings for the relation.
503
+ */
464
504
  fields?: {
465
505
  [K in keyof toEntity]?: keyof fromEntity;
466
506
  };
@@ -2,3 +2,31 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  //p1 - include relations in the api docs
4
4
  //p1 - include Relation options in the api docs
5
+ //p1 - fix { allowApiRead: r => r.user.name === 'superman' } } in allowed for instace docs
6
+ //p1 - fix seeallowed in ref_entity
7
+ //p1 - write doc about controlling updates of rows...
8
+ //p1 - 'eventId' autocomplete in task app did not work
9
+ //p1 - need a way to extract the fields from the relation - for generating relation based sql
10
+ //p1 - remult object was used outside context
11
+ // ```
12
+ // sqlExpression: async (e) => {
13
+ // const v = await dbNamesOf(volunteerInTask)
14
+ // return `select true from ${v} where ${
15
+ // v.task
16
+ // }=${await e.getDbName()}.${await e.fields.id.getDbName()} and ${SqlDatabase.filterToRaw<volunteerInTask>(
17
+ // repo(volunteerInTask),
18
+ // {
19
+ // canceled: false,
20
+ // }
21
+ // )}`
22
+ // },
23
+ // })
24
+ // ```
25
+ //p2 - consider includeInApi as AllowedForInstance
26
+ //p1 - why didn't this._.relations.statusChanges.insert({})work?
27
+ //p1 - active record with include thinks that the row was change!
28
+ //p1 - add docs for apply changes
29
+ //p1 - update related field with null should respect it's allowed null - otherwise we get a null error for that field.
30
+ //p1 - when the include doesn't find the many to one - it deletes the id!!!
31
+ //y1 - discuss a default date formatter
32
+ //p1 - consider using this in saving etc...