dyna-record 0.3.6 → 0.4.0

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
@@ -172,6 +172,8 @@ Define foreign keys in order to support [@BelongsTo](https://dyna-record.com/fun
172
172
  - The [alias](https://dyna-record.com/interfaces/AttributeOptions.html#alias) option allows you to specify the attribute name as it appears in the DynamoDB table, different from your class property name.
173
173
  - Set nullable foreign key attributes as optional for optimal type safety
174
174
  - Attempting to remove an entity from a non-nullable foreign key will result in a [NullConstrainViolationError](https://dyna-record.com/classes/NullConstraintViolationError.html)
175
+ - Always provide the referenced entity class to `@ForeignKeyAttribute` (for example `@ForeignKeyAttribute(() => Customer)`); this allows DynaRecord to enforce referential integrity even when no relationship decorator is defined.
176
+ - `Create` and `Update` automatically add DynamoDB condition checks for standalone foreign keys (those without a relationship decorator) to ensure the referenced entity exists, enabling referential integrity even when no denormalised access pattern is required.
175
177
 
176
178
  ```typescript
177
179
  import {
@@ -184,8 +186,8 @@ import {
184
186
 
185
187
  @Entity
186
188
  class Assignment extends MyTable {
187
- @ForeignKeyAttribute()
188
- public readonly courseId: ForeignKey;
189
+ @ForeignKeyAttribute(() => Course)
190
+ public readonly courseId: ForeignKey<Course>;
189
191
 
190
192
  @BelongsTo(() => Course, { foreignKey: "courseId" })
191
193
  public readonly course: Course;
@@ -193,8 +195,8 @@ class Assignment extends MyTable {
193
195
 
194
196
  @Entity
195
197
  class Course extends MyTable {
196
- @ForeignKeyAttribute({ nullable: true })
197
- public readonly teacherId?: NullableForeignKey; // Set as optional
198
+ @ForeignKeyAttribute(() => Teacher, { nullable: true })
199
+ public readonly teacherId?: NullableForeignKey<Teacher>; // Set as optional
198
200
 
199
201
  @BelongsTo(() => Teacher, { foreignKey: "teacherId" })
200
202
  public readonly teacher?: Teacher; // Set as optional because its linked through NullableForeignKey
@@ -232,8 +234,8 @@ class Assignment extends MyTable {
232
234
 
233
235
  @Entity
234
236
  class Grade extends MyTable {
235
- @ForeignKeyAttribute()
236
- public readonly assignmentId: ForeignKey;
237
+ @ForeignKeyAttribute(() => Assignment)
238
+ public readonly assignmentId: ForeignKey<Assignment>;
237
239
 
238
240
  // 'assignmentId' Must be defined on self as ForeignKey or NullableForeignKey
239
241
  @BelongsTo(() => Assignment, { foreignKey: "assignmentId" })
@@ -257,8 +259,8 @@ class Teacher extends MyTable {
257
259
 
258
260
  @Entity
259
261
  class Course extends MyTable {
260
- @ForeignKeyAttribute({ nullable: true })
261
- public readonly teacherId?: NullableForeignKey; // Mark as optional
262
+ @ForeignKeyAttribute(() => Teacher, { nullable: true })
263
+ public readonly teacherId?: NullableForeignKey<Teacher>; // Mark as optional
262
264
 
263
265
  // 'teacherId' Must be defined on self as ForeignKey or NullableForeignKey
264
266
  @BelongsTo(() => Teacher, { foreignKey: "teacherId" })
@@ -282,8 +284,8 @@ class Teacher extends MyTable {
282
284
 
283
285
  @Entity
284
286
  class Course extends MyTable {
285
- @ForeignKeyAttribute({ nullable: true })
286
- public readonly teacherId?: NullableForeignKey; // Mark as optional
287
+ @ForeignKeyAttribute(() => Teacher, { nullable: true })
288
+ public readonly teacherId?: NullableForeignKey<Teacher>; // Mark as optional
287
289
  }
288
290
  ```
289
291
 
@@ -1,5 +1,5 @@
1
1
  import type DynaRecord from "../../DynaRecord";
2
- import type { ForeignKey, NullableForeignKey } from "../../types";
2
+ import type { EntityClass, ForeignKey, NullableForeignKey } from "../../types";
3
3
  import type { AttributeDecoratorContext, AttributeOptions } from "../types";
4
4
  /**
5
5
  * A decorator for annotating class fields as foreign keys within the context of a single-table design entity, aimed at establishing and managing relationships between different entities in a relational manner. This decorator enables the clear and explicit declaration of foreign key relationships, contributing to the ORM's ability to navigate and resolve these associations efficiently.
@@ -8,21 +8,25 @@ import type { AttributeDecoratorContext, AttributeOptions } from "../types";
8
8
  *
9
9
  * Does not allow property to be optional.
10
10
  *
11
+ * Supplying the target entity enables DynaRecord to enforce referential integrity for the foreign key even when no relationship decorators are defined (for example when a foreign key is used purely for validation without denormalising related records).
12
+ *
11
13
  * @template T The entity the decorator is applied to.
14
+ * @template K The entity that the foreign key references.
15
+ * @param getTarget A function returning the constructor for the entity referenced by the foreign key. This allows deferred resolution to avoid circular dependency issues.
12
16
  * @param props An optional object of {@link AttributeOptions}, including configuration options such as metadata attributes. These options allow for additional customization of the foreign key attribute, including aliasing and metadata tagging.
13
17
  * @returns A class field decorator function that targets and initializes the class's prototype to register the foreign key with the ORM's metadata system. This registration is crucial for enabling the ORM to correctly interpret and manage the relationships between entities.
14
18
  *
15
19
  * Usage example:
16
20
  * ```typescript
17
21
  * class Order extends TableClass {
18
- * @ForeignKeyAttribute({ alias: 'UserID' })
19
- * public userId: ForeignKey; // Foreign key to the User entity. Cannot be optional.
22
+ * @ForeignKeyAttribute(() => User, { alias: 'UserID' })
23
+ * public userId: ForeignKey<User>; // Foreign key to the User entity. Cannot be optional.
20
24
  *
21
25
  * @BelongsTo(() => User, { foreignKey: "userId" })
22
26
  * public readonly user: User; // Cannot be optional
23
27
  *
24
- * @ForeignKeyAttribute({ alias: 'ProfileId', nullable: true })
25
- * public profileId?: NullableForeignKey; // Set to optional. Nullable foreign key to another entity (e.g., UserProfile)
28
+ * @ForeignKeyAttribute(() => Profile, { alias: 'ProfileId', nullable: true })
29
+ * public profileId?: NullableForeignKey<Profile>; // Set to optional. Nullable foreign key to another entity (e.g., UserProfile)
26
30
  *
27
31
  * @BelongsTo(() => Profile, { foreignKey: "profileId" })
28
32
  * public readonly profile?: Profile; // Set to optional because its linked via a NullableForeignKey
@@ -31,6 +35,6 @@ import type { AttributeDecoratorContext, AttributeOptions } from "../types";
31
35
  *
32
36
  * Here, `@ForeignKeyAttribute` decorates `userId` of `Order`, designating it as a foreign key that references the `User` entity. This decoration not only clarifies the nature of the relationship but also empowers the ORM to enforce relational integrity and facilitate entity association operations.
33
37
  */
34
- declare function ForeignKeyAttribute<T extends DynaRecord, P extends AttributeOptions>(props?: P): (_value: undefined, context: AttributeDecoratorContext<T, P["nullable"] extends true ? NullableForeignKey : ForeignKey, P>) => void;
38
+ declare function ForeignKeyAttribute<TargetEntity extends DynaRecord, T extends DynaRecord, P extends AttributeOptions>(getTarget: () => EntityClass<TargetEntity>, props?: P): (_value: undefined, context: AttributeDecoratorContext<T, P["nullable"] extends true ? NullableForeignKey<TargetEntity> : ForeignKey<TargetEntity>, P>) => void;
35
39
  export default ForeignKeyAttribute;
36
40
  //# sourceMappingURL=ForeignKeyAttribute.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ForeignKeyAttribute.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/ForeignKeyAttribute.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,iBAAS,mBAAmB,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,gBAAgB,EAC3E,KAAK,CAAC,EAAE,CAAC,YAGC,SAAS,WACR,yBAAyB,CAChC,CAAC,EACD,CAAC,CAAC,UAAU,CAAC,SAAS,IAAI,GAAG,kBAAkB,GAAG,UAAU,EAC5D,CAAC,CACF,UAaJ;AAED,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"ForeignKeyAttribute.d.ts","sourceRoot":"","sources":["../../../../src/decorators/attributes/ForeignKeyAttribute.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,iBAAS,mBAAmB,CAC1B,YAAY,SAAS,UAAU,EAC/B,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,gBAAgB,EAC1B,SAAS,EAAE,MAAM,WAAW,CAAC,YAAY,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,YAE3C,SAAS,WACR,yBAAyB,CAChC,CAAC,EACD,CAAC,CAAC,UAAU,CAAC,SAAS,IAAI,GACtB,kBAAkB,CAAC,YAAY,CAAC,GAChC,UAAU,CAAC,YAAY,CAAC,EAC5B,CAAC,CACF,UAgBJ;AAED,eAAe,mBAAmB,CAAC"}
@@ -12,21 +12,25 @@ const metadata_1 = __importDefault(require("../../metadata"));
12
12
  *
13
13
  * Does not allow property to be optional.
14
14
  *
15
+ * Supplying the target entity enables DynaRecord to enforce referential integrity for the foreign key even when no relationship decorators are defined (for example when a foreign key is used purely for validation without denormalising related records).
16
+ *
15
17
  * @template T The entity the decorator is applied to.
18
+ * @template K The entity that the foreign key references.
19
+ * @param getTarget A function returning the constructor for the entity referenced by the foreign key. This allows deferred resolution to avoid circular dependency issues.
16
20
  * @param props An optional object of {@link AttributeOptions}, including configuration options such as metadata attributes. These options allow for additional customization of the foreign key attribute, including aliasing and metadata tagging.
17
21
  * @returns A class field decorator function that targets and initializes the class's prototype to register the foreign key with the ORM's metadata system. This registration is crucial for enabling the ORM to correctly interpret and manage the relationships between entities.
18
22
  *
19
23
  * Usage example:
20
24
  * ```typescript
21
25
  * class Order extends TableClass {
22
- * @ForeignKeyAttribute({ alias: 'UserID' })
23
- * public userId: ForeignKey; // Foreign key to the User entity. Cannot be optional.
26
+ * @ForeignKeyAttribute(() => User, { alias: 'UserID' })
27
+ * public userId: ForeignKey<User>; // Foreign key to the User entity. Cannot be optional.
24
28
  *
25
29
  * @BelongsTo(() => User, { foreignKey: "userId" })
26
30
  * public readonly user: User; // Cannot be optional
27
31
  *
28
- * @ForeignKeyAttribute({ alias: 'ProfileId', nullable: true })
29
- * public profileId?: NullableForeignKey; // Set to optional. Nullable foreign key to another entity (e.g., UserProfile)
32
+ * @ForeignKeyAttribute(() => Profile, { alias: 'ProfileId', nullable: true })
33
+ * public profileId?: NullableForeignKey<Profile>; // Set to optional. Nullable foreign key to another entity (e.g., UserProfile)
30
34
  *
31
35
  * @BelongsTo(() => Profile, { foreignKey: "profileId" })
32
36
  * public readonly profile?: Profile; // Set to optional because its linked via a NullableForeignKey
@@ -35,14 +39,16 @@ const metadata_1 = __importDefault(require("../../metadata"));
35
39
  *
36
40
  * Here, `@ForeignKeyAttribute` decorates `userId` of `Order`, designating it as a foreign key that references the `User` entity. This decoration not only clarifies the nature of the relationship but also empowers the ORM to enforce relational integrity and facilitate entity association operations.
37
41
  */
38
- function ForeignKeyAttribute(props) {
42
+ function ForeignKeyAttribute(getTarget, props) {
39
43
  return function (_value, context) {
40
44
  if (context.kind === "field") {
41
45
  context.addInitializer(function () {
46
+ const targetEntity = getTarget();
42
47
  metadata_1.default.addEntityAttribute(this.constructor.name, {
43
48
  attributeName: context.name.toString(),
44
49
  nullable: props?.nullable,
45
50
  type: zod_1.z.string(),
51
+ foreignKeyTarget: targetEntity,
46
52
  ...props
47
53
  });
48
54
  });
@@ -1,6 +1,7 @@
1
1
  import type DynaRecord from "../../DynaRecord";
2
2
  import type { EntityClass } from "../../types";
3
- import type { BelongsToField, BelongsToProps } from "./types";
3
+ import type { BelongsToField, BelongsToProps, BelongsToTarget } from "./types";
4
+ import type { ForeignEntityAttribute } from "../types";
4
5
  /**
5
6
  * A decorator for defining a "BelongsTo" relationship between entities in a single-table design using DynaRecord. This relationship indicates that the decorated field is a reference to another entity, effectively establishing a parent-child linkage. The decorator dynamically enforces the presence or optionality of this reference based on the nature of the foreign key, enhancing type safety and relationship integrity within the ORM model.
6
7
  *
@@ -29,6 +30,6 @@ import type { BelongsToField, BelongsToProps } from "./types";
29
30
  * ```
30
31
  * In this example, `@BelongsTo` decorates the `user` field of the `Order` entity, establishing a "BelongsTo" relationship with the `User` entity via the `userId` foreign key. This decoration signifies that each `Order` instance is related to a specific `User` instance.
31
32
  */
32
- declare function BelongsTo<T extends DynaRecord, K extends DynaRecord>(getTarget: () => EntityClass<K>, props: BelongsToProps<T>): (_value: undefined, context: ClassFieldDecoratorContext<T, BelongsToField<T, K, typeof props.foreignKey>>) => void;
33
+ declare function BelongsTo<Source extends DynaRecord, FK extends ForeignEntityAttribute<Source>>(getTarget: () => EntityClass<BelongsToTarget<Source, FK>>, props: BelongsToProps<Source, FK>): (_value: undefined, context: ClassFieldDecoratorContext<Source, BelongsToField<Source, FK>>) => void;
33
34
  export default BelongsTo;
34
35
  //# sourceMappingURL=BelongsTo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BelongsTo.d.ts","sourceRoot":"","sources":["../../../../src/decorators/relationships/BelongsTo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,iBAAS,SAAS,CAAC,CAAC,SAAS,UAAU,EAAE,CAAC,SAAS,UAAU,EAC3D,SAAS,EAAE,MAAM,WAAW,CAAC,CAAC,CAAC,EAC/B,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,YAGd,SAAS,WACR,0BAA0B,CACjC,CAAC,EACD,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,KAAK,CAAC,UAAU,CAAC,CAC9C,UAaJ;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"BelongsTo.d.ts","sourceRoot":"","sources":["../../../../src/decorators/relationships/BelongsTo.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,iBAAS,SAAS,CAChB,MAAM,SAAS,UAAU,EACzB,EAAE,SAAS,sBAAsB,CAAC,MAAM,CAAC,EAEzC,SAAS,EAAE,MAAM,WAAW,CAAC,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EACzD,KAAK,EAAE,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC,YAGvB,SAAS,WACR,0BAA0B,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,UAa1E;AAED,eAAe,SAAS,CAAC"}
@@ -1,11 +1,16 @@
1
1
  import type DynaRecord from "../../DynaRecord";
2
- import type { NullableForeignKey, Optional } from "../../types";
2
+ import type { ForeignKey, NullableForeignKey, Optional } from "../../types";
3
3
  import type { ForeignEntityAttribute } from "../types";
4
- export interface BelongsToProps<T extends DynaRecord> {
5
- foreignKey: ForeignEntityAttribute<T>;
4
+ type NullableForeignKeyBrand<T extends DynaRecord> = NonNullable<NullableForeignKey<T>>;
5
+ type NormalizedForeignKey<Value> = NonNullable<Value>;
6
+ type ExtractForeignKeyTarget<Value> = NormalizedForeignKey<Value> extends ForeignKey<infer Target> ? Target : NormalizedForeignKey<Value> extends NullableForeignKeyBrand<infer Target> ? Target : never;
7
+ export interface BelongsToProps<T extends DynaRecord, FK extends ForeignEntityAttribute<T>> {
8
+ foreignKey: FK;
6
9
  }
10
+ export type BelongsToTarget<T extends DynaRecord, FK extends ForeignEntityAttribute<T>> = FK extends keyof T ? ExtractForeignKeyTarget<T[FK]> : never;
7
11
  /**
8
- * If the relationship is linked by a NullableForeignKey then it allows the field to be optional, otherwise it ensures that is is not optional
12
+ * If the relationship is linked by a NullableForeignKey then it allows the field to be optional, otherwise it ensures that it is not optional
9
13
  */
10
- export type BelongsToField<T extends DynaRecord, K extends DynaRecord, FK extends ForeignEntityAttribute<T>> = FK extends keyof T ? T[FK] extends NullableForeignKey ? Optional<K> : K : never;
14
+ export type BelongsToField<T extends DynaRecord, FK extends ForeignEntityAttribute<T>> = FK extends keyof T ? BelongsToTarget<T, FK> extends never ? never : undefined extends T[FK] ? Optional<BelongsToTarget<T, FK>> : BelongsToTarget<T, FK> : never;
15
+ export {};
11
16
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/decorators/relationships/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvD,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,UAAU;IAClD,UAAU,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC;CACvC;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,CACxB,CAAC,SAAS,UAAU,EACpB,CAAC,SAAS,UAAU,EACpB,EAAE,SAAS,sBAAsB,CAAC,CAAC,CAAC,IAClC,EAAE,SAAS,MAAM,CAAC,GAClB,CAAC,CAAC,EAAE,CAAC,SAAS,kBAAkB,GAC9B,QAAQ,CAAC,CAAC,CAAC,GACX,CAAC,GACH,KAAK,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/decorators/relationships/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAEvD,KAAK,uBAAuB,CAAC,CAAC,SAAS,UAAU,IAAI,WAAW,CAC9D,kBAAkB,CAAC,CAAC,CAAC,CACtB,CAAC;AAEF,KAAK,oBAAoB,CAAC,KAAK,IAAI,WAAW,CAAC,KAAK,CAAC,CAAC;AAEtD,KAAK,uBAAuB,CAAC,KAAK,IAChC,oBAAoB,CAAC,KAAK,CAAC,SAAS,UAAU,CAAC,MAAM,MAAM,CAAC,GACxD,MAAM,GACN,oBAAoB,CAAC,KAAK,CAAC,SAAS,uBAAuB,CAAC,MAAM,MAAM,CAAC,GACvE,MAAM,GACN,KAAK,CAAC;AAEd,MAAM,WAAW,cAAc,CAC7B,CAAC,SAAS,UAAU,EACpB,EAAE,SAAS,sBAAsB,CAAC,CAAC,CAAC;IAEpC,UAAU,EAAE,EAAE,CAAC;CAChB;AAED,MAAM,MAAM,eAAe,CACzB,CAAC,SAAS,UAAU,EACpB,EAAE,SAAS,sBAAsB,CAAC,CAAC,CAAC,IAClC,EAAE,SAAS,MAAM,CAAC,GAAG,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;AAEhE;;GAEG;AACH,MAAM,MAAM,cAAc,CACxB,CAAC,SAAS,UAAU,EACpB,EAAE,SAAS,sBAAsB,CAAC,CAAC,CAAC,IAClC,EAAE,SAAS,MAAM,CAAC,GAClB,eAAe,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,KAAK,GAClC,KAAK,GACL,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC,GACrB,QAAQ,CAAC,eAAe,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAChC,eAAe,CAAC,CAAC,EAAE,EAAE,CAAC,GAC1B,KAAK,CAAC"}
@@ -1,5 +1,7 @@
1
1
  import { type ZodType } from "zod";
2
+ import type DynaRecord from "../DynaRecord";
2
3
  import type { AttributeMetadataOptions, Serializers } from "./types";
4
+ import type { EntityClass } from "../types";
3
5
  /**
4
6
  * Represents the metadata for an attribute of an entity, including its name, alias (if any), nullability, and serialization strategies.
5
7
  *
@@ -10,6 +12,7 @@ import type { AttributeMetadataOptions, Serializers } from "./types";
10
12
  * @property {boolean} nullable - Indicates whether the attribute can be `null`, defining the attribute's nullability constraint within the database.
11
13
  * @property {Serializers | undefined} serializers - Optional serialization strategies for converting the attribute between its database representation and its entity representation. This is particularly useful for custom data types not natively supported by DynamoDB.
12
14
  * @property {ZodType} type - Zod validator to run on the attribute
15
+ * @property {EntityClass | undefined} foreignKeyTarget - When present, identifies the entity referenced by a foreign key attribute, enabling referential integrity enforcement.
13
16
  *
14
17
  * @param {AttributeMetadataOptions} options - Configuration options for the attribute metadata, including the attribute's name, optional alias, nullability, and serialization strategies.
15
18
  */
@@ -19,6 +22,7 @@ declare class AttributeMetadata {
19
22
  readonly nullable: boolean;
20
23
  readonly serializers?: Serializers;
21
24
  readonly type: ZodType;
25
+ readonly foreignKeyTarget?: EntityClass<DynaRecord>;
22
26
  constructor(options: AttributeMetadataOptions);
23
27
  }
24
28
  export default AttributeMetadata;
@@ -1 +1 @@
1
- {"version":3,"file":"AttributeMetadata.d.ts","sourceRoot":"","sources":["../../../src/metadata/AttributeMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,KAAK,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAErE;;;;;;;;;;;;GAYG;AACH,cAAM,iBAAiB;IACrB,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,QAAQ,EAAE,OAAO,CAAC;IAClC,SAAgB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1C,SAAgB,IAAI,EAAE,OAAO,CAAC;gBAElB,OAAO,EAAE,wBAAwB;CAY9C;AAED,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"AttributeMetadata.d.ts","sourceRoot":"","sources":["../../../src/metadata/AttributeMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,KAAK,CAAC;AACnC,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACrE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,cAAM,iBAAiB;IACrB,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,KAAK,EAAE,MAAM,CAAC;IAC9B,SAAgB,QAAQ,EAAE,OAAO,CAAC;IAClC,SAAgB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1C,SAAgB,IAAI,EAAE,OAAO,CAAC;IAC9B,SAAgB,gBAAgB,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;gBAE/C,OAAO,EAAE,wBAAwB;CAa9C;AAED,eAAe,iBAAiB,CAAC"}
@@ -10,6 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
  * @property {boolean} nullable - Indicates whether the attribute can be `null`, defining the attribute's nullability constraint within the database.
11
11
  * @property {Serializers | undefined} serializers - Optional serialization strategies for converting the attribute between its database representation and its entity representation. This is particularly useful for custom data types not natively supported by DynamoDB.
12
12
  * @property {ZodType} type - Zod validator to run on the attribute
13
+ * @property {EntityClass | undefined} foreignKeyTarget - When present, identifies the entity referenced by a foreign key attribute, enabling referential integrity enforcement.
13
14
  *
14
15
  * @param {AttributeMetadataOptions} options - Configuration options for the attribute metadata, including the attribute's name, optional alias, nullability, and serialization strategies.
15
16
  */
@@ -19,11 +20,13 @@ class AttributeMetadata {
19
20
  nullable;
20
21
  serializers;
21
22
  type;
23
+ foreignKeyTarget;
22
24
  constructor(options) {
23
25
  this.name = options.attributeName;
24
26
  this.alias = options.alias ?? options.attributeName;
25
27
  this.nullable = options.nullable ?? false;
26
28
  this.serializers = options.serializers;
29
+ this.foreignKeyTarget = options.foreignKeyTarget;
27
30
  if (options.nullable === true) {
28
31
  this.type = options.type.optional().nullable();
29
32
  }
@@ -1,4 +1,4 @@
1
- import type { BelongsToRelationship, HasRelationships, AttributeMetadata, AttributeMetadataStorage, RelationshipMetadataStorage, RelationshipMetadata, OwnedByRelationship, BelongsToOrOwnedByRelationship } from ".";
1
+ import type { BelongsToRelationship, HasRelationships, AttributeMetadata, AttributeMetadataStorage, RelationshipMetadataStorage, RelationshipMetadata, OwnedByRelationship, BelongsToOrOwnedByRelationship, ForeignKeyAttributeMetadata } from ".";
2
2
  import type DynaRecord from "../DynaRecord";
3
3
  import { type EntityDefinedAttributes } from "../operations";
4
4
  type EntityClass = new (...args: any) => DynaRecord;
@@ -83,6 +83,16 @@ declare class EntityMetadata {
83
83
  * Returns the "Has" relationship metadata for the entity (EX: "HasMany")
84
84
  */
85
85
  get hasRelationships(): HasRelationships;
86
+ /**
87
+ * Returns attribute metadata for attributes that reference a foreign entity via {@link ForeignKeyAttribute}.
88
+ * The returned metadata guarantees a non-null {@link ForeignKeyAttributeMetadata.foreignKeyTarget}.
89
+ */
90
+ get foreignKeyAttributes(): ForeignKeyAttributeMetadata[];
91
+ /**
92
+ * Returns foreign key attributes that are not linked through a relationship decorator.
93
+ * These attributes rely on standalone referential integrity checks during create and update operations.
94
+ */
95
+ get standaloneForeignKeyAttributes(): ForeignKeyAttributeMetadata[];
86
96
  }
87
97
  export default EntityMetadata;
88
98
  //# sourceMappingURL=EntityMetadata.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EntityMetadata.d.ts","sourceRoot":"","sources":["../../../src/metadata/EntityMetadata.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,2BAA2B,EAC3B,oBAAoB,EACpB,mBAAmB,EACnB,8BAA8B,EAC/B,MAAM,GAAG,CAAC;AACX,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAU7D,KAAK,WAAW,GAAG,KAAK,GAAG,IAAI,EAAE,GAAG,KAAK,UAAU,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,cAAM,cAAc;;IAClB;;OAEG;IACH,SAAgB,cAAc,EAAE,MAAM,CAAC;IACvC;;OAEG;IACH,SAAgB,UAAU,EAAE,wBAAwB,CAAC;IACrD;;OAEG;IACH,SAAgB,eAAe,EAAE,wBAAwB,CAAC;IAE1D;;OAEG;IACH,SAAgB,aAAa,EAAE,2BAA2B,CAAC;IAE3D,SAAgB,WAAW,EAAE,WAAW,CAAC;IAEzC;;OAEG;IACI,OAAO,EAAE,MAAM,CAAC;gBAiBX,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM;IAS5D;;;OAGG;IACI,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAOtD;;;;;OAKG;IACI,+BAA+B,CACpC,UAAU,EAAE,uBAAuB,CAAC,UAAU,CAAC,GAC9C,uBAAuB,CAAC,UAAU,CAAC;IAatC;;;;;;OAMG;IACI,sCAAsC,CAC3C,UAAU,EAAE,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,GACvD,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC;IA0B/C;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACH,IAAW,gBAAgB,IAAI,oBAAoB,EAAE,CAEpD;IAED;;OAEG;IACH,IAAW,sBAAsB,IAAI,qBAAqB,EAAE,CAI3D;IAED;;OAEG;IACH,IAAW,oBAAoB,IAAI,mBAAmB,EAAE,CAIvD;IAED;;OAEG;IACH,IAAW,+BAA+B,IAAI,8BAA8B,EAAE,CAE7E;IAED;;OAEG;IACH,IAAW,gBAAgB,IAAI,gBAAgB,CAO9C;CACF;AAED,eAAe,cAAc,CAAC"}
1
+ {"version":3,"file":"EntityMetadata.d.ts","sourceRoot":"","sources":["../../../src/metadata/EntityMetadata.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,EACxB,2BAA2B,EAC3B,oBAAoB,EACpB,mBAAmB,EACnB,8BAA8B,EAC9B,2BAA2B,EAC5B,MAAM,GAAG,CAAC;AACX,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAE5C,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAY7D,KAAK,WAAW,GAAG,KAAK,GAAG,IAAI,EAAE,GAAG,KAAK,UAAU,CAAC;AAEpD;;;;;;;;;;;GAWG;AACH,cAAM,cAAc;;IAClB;;OAEG;IACH,SAAgB,cAAc,EAAE,MAAM,CAAC;IACvC;;OAEG;IACH,SAAgB,UAAU,EAAE,wBAAwB,CAAC;IACrD;;OAEG;IACH,SAAgB,eAAe,EAAE,wBAAwB,CAAC;IAE1D;;OAEG;IACH,SAAgB,aAAa,EAAE,2BAA2B,CAAC;IAE3D,SAAgB,WAAW,EAAE,WAAW,CAAC;IAEzC;;OAEG;IACI,OAAO,EAAE,MAAM,CAAC;gBAiBX,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM;IAS5D;;;OAGG;IACI,YAAY,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAOtD;;;;;OAKG;IACI,+BAA+B,CACpC,UAAU,EAAE,uBAAuB,CAAC,UAAU,CAAC,GAC9C,uBAAuB,CAAC,UAAU,CAAC;IAatC;;;;;;OAMG;IACI,sCAAsC,CAC3C,UAAU,EAAE,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC,GACvD,OAAO,CAAC,uBAAuB,CAAC,UAAU,CAAC,CAAC;IA0B/C;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAM5B;;OAEG;IACH,IAAW,gBAAgB,IAAI,oBAAoB,EAAE,CAEpD;IAED;;OAEG;IACH,IAAW,sBAAsB,IAAI,qBAAqB,EAAE,CAI3D;IAED;;OAEG;IACH,IAAW,oBAAoB,IAAI,mBAAmB,EAAE,CAIvD;IAED;;OAEG;IACH,IAAW,+BAA+B,IAAI,8BAA8B,EAAE,CAE7E;IAED;;OAEG;IACH,IAAW,gBAAgB,IAAI,gBAAgB,CAO9C;IAED;;;OAGG;IACH,IAAW,oBAAoB,IAAI,2BAA2B,EAAE,CAE/D;IAED;;;OAGG;IACH,IAAW,8BAA8B,IAAI,2BAA2B,EAAE,CAUzE;CACF;AAED,eAAe,cAAc,CAAC"}
@@ -159,5 +159,22 @@ class EntityMetadata {
159
159
  (0, utils_1.isHasManyRelationship)(relMeta) ||
160
160
  (0, utils_1.isHasAndBelongsToManyRelationship)(relMeta));
161
161
  }
162
+ /**
163
+ * Returns attribute metadata for attributes that reference a foreign entity via {@link ForeignKeyAttribute}.
164
+ * The returned metadata guarantees a non-null {@link ForeignKeyAttributeMetadata.foreignKeyTarget}.
165
+ */
166
+ get foreignKeyAttributes() {
167
+ return Object.values(this.attributes).filter(utils_1.isForeignKeyAttributeMetadata);
168
+ }
169
+ /**
170
+ * Returns foreign key attributes that are not linked through a relationship decorator.
171
+ * These attributes rely on standalone referential integrity checks during create and update operations.
172
+ */
173
+ get standaloneForeignKeyAttributes() {
174
+ const associatedForeignKeys = new Set(Object.values(this.relationships)
175
+ .filter(utils_1.isRelationshipMetadataWithForeignKey)
176
+ .map(rel => rel.foreignKey.toString()));
177
+ return this.foreignKeyAttributes.filter(attr => !associatedForeignKeys.has(attr.name));
178
+ }
162
179
  }
163
180
  exports.default = EntityMetadata;
@@ -1,7 +1,7 @@
1
1
  import type { NativeScalarAttributeValue } from "@aws-sdk/util-dynamodb";
2
2
  import type { AttributeMetadata, BelongsToRelationship, EntityMetadata, JoinTableMetadata, OwnedByRelationship, RelationshipMetadata, TableMetadata } from ".";
3
3
  import type DynaRecord from "../DynaRecord";
4
- import type { MakeOptional } from "../types";
4
+ import type { EntityClass, MakeOptional } from "../types";
5
5
  import type { ZodType } from "zod";
6
6
  /**
7
7
  * Represents relationship metadata that includes a foreign key reference to another entity.
@@ -94,9 +94,21 @@ export interface AttributeMetadataOptions {
94
94
  alias?: string;
95
95
  nullable?: boolean;
96
96
  serializers?: Serializers;
97
+ /**
98
+ * When the attribute represents a foreign key, this references the target entity class.
99
+ * Used to enforce referential integrity even when a relationship decorator is not present.
100
+ */
101
+ foreignKeyTarget?: EntityClass<DynaRecord>;
97
102
  }
98
103
  /**
99
104
  * A relationship that is either BelongsTo (bi-directional to parent) or OwnedBy (uni directional to parent)
100
105
  */
101
106
  export type BelongsToOrOwnedByRelationship = BelongsToRelationship | OwnedByRelationship;
107
+ /**
108
+ * Attribute metadata for foreign key attributes where {@link AttributeMetadata.foreignKeyTarget} is guaranteed.
109
+ * Provides the target entity class so operations can enforce referential integrity even when no relationship metadata exists.
110
+ */
111
+ export interface ForeignKeyAttributeMetadata extends AttributeMetadata {
112
+ foreignKeyTarget: NonNullable<AttributeMetadata["foreignKeyTarget"]>;
113
+ }
102
114
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/metadata/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACd,MAAM,GAAG,CAAC;AACX,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,kCAAkC,GAAG,OAAO,CACtD,oBAAoB,EACpB;IAAE,UAAU,EAAE,MAAM,UAAU,CAAA;CAAE,CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;KACzB,CAAC,IAAI,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAClE,KAAK,GACL,CAAC;CACN,CAAC,MAAM,UAAU,CAAC,CAAC;AAEpB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CACrC,aAAa,EACb,IAAI,CAAC,iBAAiB,EAAE,OAAO,CAAC,CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GAC5D,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,GAAG;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAC7C,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,YAAY,CACrD,IAAI,CAAC,wBAAwB,EAAE,UAAU,CAAC,EAC1C,OAAO,CACR,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,0BAA0B,KAAK,GAAG,CAAC;AAE1E;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,0BAA0B,CAAC;AAEzE;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,iBAAiB,EAAE,gBAAgB,CAAC;IACpC;;OAEG;IACH,gBAAgB,EAAE,eAAe,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACtC,qBAAqB,GACrB,mBAAmB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/metadata/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EACV,iBAAiB,EACjB,qBAAqB,EACrB,cAAc,EACd,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACd,MAAM,GAAG,CAAC;AACX,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,kCAAkC,GAAG,OAAO,CACtD,oBAAoB,EACpB;IAAE,UAAU,EAAE,MAAM,UAAU,CAAA;CAAE,CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAEnE;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAC;AAE3E;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,WAAW,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;KACzB,CAAC,IAAI,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,GAClE,KAAK,GACL,CAAC;CACN,CAAC,MAAM,UAAU,CAAC,CAAC;AAEpB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CACrC,aAAa,EACb,IAAI,CAAC,iBAAiB,EAAE,OAAO,CAAC,CACjC,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,oBAAoB,GAAG,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GAC5D,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC,GAAG;IAC1C,aAAa,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAC7C,CAAC;AAEJ;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,YAAY,CACrD,IAAI,CAAC,wBAAwB,EAAE,UAAU,CAAC,EAC1C,OAAO,CACR,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,0BAA0B,KAAK,GAAG,CAAC;AAE1E;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,GAAG,KAAK,0BAA0B,CAAC;AAEzE;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B;;OAEG;IACH,iBAAiB,EAAE,gBAAgB,CAAC;IACpC;;OAEG;IACH,gBAAgB,EAAE,eAAe,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,MAAM,8BAA8B,GACtC,qBAAqB,GACrB,mBAAmB,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,2BAA4B,SAAQ,iBAAiB;IACpE,gBAAgB,EAAE,WAAW,CAAC,iBAAiB,CAAC,kBAAkB,CAAC,CAAC,CAAC;CACtE"}
@@ -1,7 +1,8 @@
1
- import type { RelationshipMetadata, HasManyRelationship, BelongsToRelationship, HasOneRelationship, HasAndBelongsToManyRelationship, OwnedByRelationship } from ".";
1
+ import type { RelationshipMetadata, HasManyRelationship, BelongsToRelationship, HasOneRelationship, HasAndBelongsToManyRelationship, OwnedByRelationship, ForeignKeyAttributeMetadata } from ".";
2
2
  import type DynaRecord from "../DynaRecord";
3
3
  import type { BelongsToOrOwnedByRelationship, RelationshipMetadataWithForeignKey } from "./types";
4
4
  import type { EntityClass } from "../types";
5
+ import type AttributeMetadata from "./AttributeMetadata";
5
6
  /**
6
7
  * Type guard to check if the relationship is a HasMany
7
8
  */
@@ -28,6 +29,11 @@ export declare const isOwnedByRelationship: (rel: RelationshipMetadata) => rel i
28
29
  * @returns
29
30
  */
30
31
  export declare const isRelationshipMetadataWithForeignKey: (rel: RelationshipMetadata) => rel is RelationshipMetadataWithForeignKey;
32
+ /**
33
+ * Type guard that checks whether attribute metadata represents a foreign key.
34
+ * Returns {@link ForeignKeyAttributeMetadata} with a guaranteed target entity.
35
+ */
36
+ export declare const isForeignKeyAttributeMetadata: (attr: AttributeMetadata) => attr is ForeignKeyAttributeMetadata;
31
37
  /**
32
38
  * Returns true if an "Entity" BelongsTo the provided relationship as a HasOne
33
39
  * @param Entity
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/metadata/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,+BAA+B,EAC/B,mBAAmB,EACpB,MAAM,GAAG,CAAC;AACX,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EACV,8BAA8B,EAC9B,kCAAkC,EACnC,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAC3B,oBAAoB,KACxB,GAAG,IAAI,mBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,QAC7B,oBAAoB,KACxB,GAAG,IAAI,qBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAC1B,oBAAoB,KACxB,GAAG,IAAI,kBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iCAAiC,QACvC,oBAAoB,KACxB,GAAG,IAAI,+BAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAC3B,oBAAoB,KACxB,GAAG,IAAI,mBAET,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,QAC1C,oBAAoB,KACxB,GAAG,IAAI,kCAET,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,GAAI,CAAC,SAAS,UAAU,UACxD,WAAW,CAAC,CAAC,CAAC,OACjB,8BAA8B,KAClC,OAMF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GAAI,CAAC,SAAS,UAAU,UACzD,WAAW,CAAC,CAAC,CAAC,OACjB,8BAA8B,KAClC,OAMF,CAAC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/metadata/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAClB,+BAA+B,EAC/B,mBAAmB,EACnB,2BAA2B,EAC5B,MAAM,GAAG,CAAC;AACX,OAAO,KAAK,UAAU,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EACV,8BAA8B,EAC9B,kCAAkC,EACnC,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,KAAK,iBAAiB,MAAM,qBAAqB,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAC3B,oBAAoB,KACxB,GAAG,IAAI,mBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,QAC7B,oBAAoB,KACxB,GAAG,IAAI,qBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAC1B,oBAAoB,KACxB,GAAG,IAAI,kBAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iCAAiC,QACvC,oBAAoB,KACxB,GAAG,IAAI,+BAET,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAC3B,oBAAoB,KACxB,GAAG,IAAI,mBAET,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oCAAoC,QAC1C,oBAAoB,KACxB,GAAG,IAAI,kCAET,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,6BAA6B,SAClC,iBAAiB,KACtB,IAAI,IAAI,2BAEV,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,GAAI,CAAC,SAAS,UAAU,UACxD,WAAW,CAAC,CAAC,CAAC,OACjB,8BAA8B,KAClC,OAMF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,8BAA8B,GAAI,CAAC,SAAS,UAAU,UACzD,WAAW,CAAC,CAAC,CAAC,OACjB,8BAA8B,KAClC,OAMF,CAAC"}
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.doesEntityBelongToRelAsHasMany = exports.doesEntityBelongToRelAsHasOne = exports.isRelationshipMetadataWithForeignKey = exports.isOwnedByRelationship = exports.isHasAndBelongsToManyRelationship = exports.isHasOneRelationship = exports.isBelongsToRelationship = exports.isHasManyRelationship = void 0;
6
+ exports.doesEntityBelongToRelAsHasMany = exports.doesEntityBelongToRelAsHasOne = exports.isForeignKeyAttributeMetadata = exports.isRelationshipMetadataWithForeignKey = exports.isOwnedByRelationship = exports.isHasAndBelongsToManyRelationship = exports.isHasOneRelationship = exports.isBelongsToRelationship = exports.isHasManyRelationship = void 0;
7
7
  const _1 = __importDefault(require("./"));
8
8
  /**
9
9
  * Type guard to check if the relationship is a HasMany
@@ -49,6 +49,14 @@ const isRelationshipMetadataWithForeignKey = (rel) => {
49
49
  return "foreignKey" in rel;
50
50
  };
51
51
  exports.isRelationshipMetadataWithForeignKey = isRelationshipMetadataWithForeignKey;
52
+ /**
53
+ * Type guard that checks whether attribute metadata represents a foreign key.
54
+ * Returns {@link ForeignKeyAttributeMetadata} with a guaranteed target entity.
55
+ */
56
+ const isForeignKeyAttributeMetadata = (attr) => {
57
+ return attr.foreignKeyTarget !== undefined;
58
+ };
59
+ exports.isForeignKeyAttributeMetadata = isForeignKeyAttributeMetadata;
52
60
  /**
53
61
  * Returns true if an "Entity" BelongsTo the provided relationship as a HasOne
54
62
  * @param Entity
@@ -78,6 +78,13 @@ declare class Create<T extends DynaRecord> extends OperationBase<T> {
78
78
  * @private
79
79
  */
80
80
  private buildBelongsToTransactions;
81
+ /**
82
+ * Adds DynamoDB condition checks for foreign keys that are not associated with a relationship decorator.
83
+ * Ensures referenced entities exist even when denormalised access patterns are not defined.
84
+ *
85
+ * @param entityData - Attributes being persisted for the new entity.
86
+ */
87
+ private buildStandaloneForeignKeyConditionChecks;
81
88
  /**
82
89
  * Retrieves the DynamoDB items for all entities that the new entity references via "BelongsTo" relationships.
83
90
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Create.d.ts","sourceRoot":"","sources":["../../../../src/operations/Create/Create.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,aAAa,CAAC;AAOhE,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,UAAU,CAAC;AAIlB;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAM,MAAM,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;;gBAG7C,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAKlC;;;;;;;;;;;;;;;;OAgBG;IACU,GAAG,CACd,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;IA4BnC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,uBAAuB;IA8B/B;;;;;;;;;OASG;IACH,OAAO,CAAC,uBAAuB;IAiB/B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAkClC;;;;;;;;;;;OAWG;YACW,sBAAsB;IAmCpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,2CAA2C;IAmBnD;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,uCAAuC;CA0BhD;AAED,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Create.d.ts","sourceRoot":"","sources":["../../../../src/operations/Create/Create.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,aAAa,CAAC;AAOhE,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7C,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,UAAU,CAAC;AAIlB;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAM,MAAM,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;;gBAG7C,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC;IAKlC;;;;;;;;;;;;;;;;OAgBG;IACU,GAAG,CACd,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;IA6BnC;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,uBAAuB;IA8B/B;;;;;;;;;OASG;IACH,OAAO,CAAC,uBAAuB;IAiB/B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAkClC;;;;;OAKG;IACH,OAAO,CAAC,wCAAwC;IA8BhD;;;;;;;;;;;OAWG;YACW,sBAAsB;IAmCpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,2CAA2C;IAmBnD;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,uCAAuC;CA0BhD;AAED,eAAe,MAAM,CAAC"}
@@ -57,6 +57,7 @@ class Create extends OperationBase_1.default {
57
57
  const tableItem = (0, utils_1.entityToTableItem)(this.EntityClass, entityData);
58
58
  this.buildPutItemTransaction(tableItem, entityData.id);
59
59
  this.buildBelongsToTransactions(entityData, tableItem);
60
+ this.buildStandaloneForeignKeyConditionChecks(entityData);
60
61
  // Attempt to fetch all belongs-to entities to properly create reverse denormalization links
61
62
  const belongsToTableItems = await this.getBelongsToTableItems(entityData);
62
63
  // If there are any belongs-to relationships, add the inverse link records into the new entity's partition
@@ -145,6 +146,32 @@ class Create extends OperationBase_1.default {
145
146
  }
146
147
  }
147
148
  }
149
+ /**
150
+ * Adds DynamoDB condition checks for foreign keys that are not associated with a relationship decorator.
151
+ * Ensures referenced entities exist even when denormalised access patterns are not defined.
152
+ *
153
+ * @param entityData - Attributes being persisted for the new entity.
154
+ */
155
+ buildStandaloneForeignKeyConditionChecks(entityData) {
156
+ const standaloneForeignKeys = this.entityMetadata.standaloneForeignKeyAttributes;
157
+ for (const attrMeta of standaloneForeignKeys) {
158
+ const target = attrMeta.foreignKeyTarget;
159
+ const foreignKeyValue = entityData[attrMeta.name];
160
+ if (!(0, utils_1.isString)(foreignKeyValue))
161
+ continue;
162
+ const foreignKey = foreignKeyValue;
163
+ const errMsg = `${target.name} with ID '${foreignKey}' does not exist`;
164
+ const conditionCheck = {
165
+ TableName: this.tableMetadata.name,
166
+ Key: {
167
+ [this.partitionKeyAlias]: target.partitionKeyValue(foreignKey),
168
+ [this.sortKeyAlias]: target.name
169
+ },
170
+ ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`
171
+ };
172
+ this.#transactionBuilder.addConditionCheck(conditionCheck, errMsg);
173
+ }
174
+ }
148
175
  /**
149
176
  * Retrieves the DynamoDB items for all entities that the new entity references via "BelongsTo" relationships.
150
177
  *
@@ -46,6 +46,12 @@ declare class Update<T extends DynaRecord> extends OperationBase<T> {
46
46
  * @returns
47
47
  */
48
48
  private getBelongsToRelMetaAndKeyForUpdatedKeys;
49
+ /**
50
+ * Adds condition checks for standalone foreign keys present in the update payload to ensure the referenced records exist.
51
+ *
52
+ * @param attributes - Partial entity attributes supplied to the update call.
53
+ */
54
+ private addStandaloneForeignKeyConditionChecks;
49
55
  /**
50
56
  * Pre-fetches the target entity and any related entities from the database. This is done using a strong read operation to ensure consistency.
51
57
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Update.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/Update.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAGL,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAkB5B,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,KAAK,EAAmB,WAAW,EAAgB,MAAM,aAAa,CAAC;AA4D9E;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAM,MAAM,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;IACzD,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;gBAG1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EACtB,kBAAkB,CAAC,EAAE,oBAAoB;IAO3C;;;;;;;;;;;;;OAaG;IACU,GAAG,CACd,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;cA4ChB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlD;;;;OAIG;IACH,OAAO,CAAC,uCAAuC;IAY/C;;;;;;;;;;;;;;;OAeG;YACW,QAAQ;IAsDtB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IA4B7B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAyBlC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,0BAA0B;IAuDlC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,iCAAiC;IAuBzC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,8BAA8B;IA+BtC;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAkBlC;;;;;OAKG;IACH,OAAO,CAAC,mCAAmC;IAsB3C;;;;;;;;OAQG;IACH,OAAO,CAAC,sCAAsC;IAqC9C;;;;;;;;;;OAUG;IACH,OAAO,CAAC,6BAA6B;IAuCrC;;;;;;;OAOG;IACH,OAAO,CAAC,4BAA4B;IA6BpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,6BAA6B;IAwBrC;;;;;;OAMG;IACH,OAAO,CAAC,gCAAgC;IAgBxC;;;OAGG;IACH,OAAO,CAAC,iCAAiC;CAU1C;AAED,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Update.d.ts","sourceRoot":"","sources":["../../../../src/operations/Update/Update.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAGL,oBAAoB,EACrB,MAAM,oBAAoB,CAAC;AAmB5B,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,KAAK,EAAmB,WAAW,EAAgB,MAAM,aAAa,CAAC;AA4D9E;;;;;;;;;;;;;;;;;GAiBG;AACH,cAAM,MAAM,CAAC,CAAC,SAAS,UAAU,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;IACzD,SAAS,CAAC,QAAQ,CAAC,kBAAkB,EAAE,oBAAoB,CAAC;gBAG1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EACtB,kBAAkB,CAAC,EAAE,oBAAoB;IAO3C;;;;;;;;;;;;;OAaG;IACU,GAAG,CACd,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC,GACpC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;cA6ChB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIlD;;;;OAIG;IACH,OAAO,CAAC,uCAAuC;IAY/C;;;;OAIG;IACH,OAAO,CAAC,sCAAsC;IAgC9C;;;;;;;;;;;;;;;OAeG;YACW,QAAQ;IAsDtB;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IA4B7B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,mBAAmB;IAY3B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,0BAA0B;IAyBlC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,0BAA0B;IAuDlC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,iCAAiC;IAuBzC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,8BAA8B;IA+BtC;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAkBlC;;;;;OAKG;IACH,OAAO,CAAC,mCAAmC;IAsB3C;;;;;;;;OAQG;IACH,OAAO,CAAC,sCAAsC;IAqC9C;;;;;;;;;;OAUG;IACH,OAAO,CAAC,6BAA6B;IAuCrC;;;;;;;OAOG;IACH,OAAO,CAAC,4BAA4B;IA6BpC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,6BAA6B;IAwBrC;;;;;;OAMG;IACH,OAAO,CAAC,gCAAgC;IAgBxC;;;OAGG;IACH,OAAO,CAAC,iCAAiC;CAU1C;AAED,eAAe,MAAM,CAAC"}
@@ -54,6 +54,7 @@ class Update extends OperationBase_1.default {
54
54
  const entityAttrs = entityMeta.parseRawEntityDefinedAttributesPartial(attributes);
55
55
  const { updatedAttrs, expression } = this.buildUpdateMetadata(entityAttrs);
56
56
  this.buildUpdateItemTransaction(id, expression);
57
+ this.addStandaloneForeignKeyConditionChecks(entityAttrs);
57
58
  // Only need to prefetch if the entity has relationships
58
59
  if (entityMeta.allRelationships.length > 0) {
59
60
  const belongsToRelMetaBeingUpdated = this.getBelongsToRelMetaAndKeyForUpdatedKeys(entityAttrs);
@@ -86,6 +87,33 @@ class Update extends OperationBase_1.default {
86
87
  return acc;
87
88
  }, []);
88
89
  }
90
+ /**
91
+ * Adds condition checks for standalone foreign keys present in the update payload to ensure the referenced records exist.
92
+ *
93
+ * @param attributes - Partial entity attributes supplied to the update call.
94
+ */
95
+ addStandaloneForeignKeyConditionChecks(attributes) {
96
+ const standaloneForeignKeys = this.entityMetadata.standaloneForeignKeyAttributes;
97
+ for (const attrMeta of standaloneForeignKeys) {
98
+ const target = attrMeta.foreignKeyTarget;
99
+ if (!(0, utils_1.isKeyOfObject)(attributes, attrMeta.name))
100
+ continue;
101
+ const foreignKeyValue = attributes[attrMeta.name];
102
+ if (!(0, utils_1.isString)(foreignKeyValue))
103
+ continue;
104
+ const foreignKey = foreignKeyValue;
105
+ const errMsg = `${target.name} with ID '${foreignKey}' does not exist`;
106
+ const conditionCheck = {
107
+ TableName: this.tableMetadata.name,
108
+ Key: {
109
+ [this.partitionKeyAlias]: target.partitionKeyValue(foreignKey),
110
+ [this.sortKeyAlias]: target.name
111
+ },
112
+ ConditionExpression: `attribute_exists(${this.partitionKeyAlias})`
113
+ };
114
+ this.transactionBuilder.addConditionCheck(conditionCheck, errMsg);
115
+ }
116
+ }
89
117
  /**
90
118
  * Pre-fetches the target entity and any related entities from the database. This is done using a strong read operation to ensure consistency.
91
119
  *
@@ -16,13 +16,23 @@ export type SortKey = Brand<string, "SortKey">;
16
16
  */
17
17
  export type PartitionKey = Brand<string, "PartitionKey">;
18
18
  /**
19
- * A branded string type to represent foreign keys in DynamoDB tables
19
+ * A branded string type to represent foreign keys in DynamoDB tables.
20
+ *
21
+ * @typeParam T - The entity that the foreign key references. Defaults to {@link DynaRecord}.
20
22
  */
21
- export type ForeignKey = Brand<string, "ForeignKey">;
23
+ export type ForeignKey<T extends DynaRecord = DynaRecord> = Brand<string, {
24
+ kind: "ForeignKey";
25
+ entity: T;
26
+ }>;
22
27
  /**
23
28
  * A branded string type to represent nullable foreign keys in DynamoDB tables, which can also be undefined.
29
+ *
30
+ * @typeParam T - The entity that the foreign key references. Defaults to {@link DynaRecord}.
24
31
  */
25
- export type NullableForeignKey = Optional<Brand<string, "NullableForeignKey">>;
32
+ export type NullableForeignKey<T extends DynaRecord = DynaRecord> = Optional<Brand<string, {
33
+ kind: "NullableForeignKey";
34
+ entity: T;
35
+ }>>;
26
36
  /**
27
37
  * Represents a foreign key property on an entity within a DynaRecord model
28
38
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,UAAU,GAAG,UAAU,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,kBAAkB,CAAC;IACpC,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GACzD,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEtB;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,OAAO,UAAU,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;KAC3D,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACjB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC9E,OAAO,KAAK,UAAU,MAAM,cAAc,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AAEzD;;;;GAIG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IAAI,KAAK,CAC/D,MAAM,EACN;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAClC,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU,IAAI,QAAQ,CAC1E,KAAK,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,CAAC,CACzD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,UAAU,GAAG,UAAU,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;AAEzE;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,kBAAkB,CAAC;IACpC,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GACzD,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAEtB;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,GAAG,OAAO,UAAU,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;KAC3D,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACjB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dyna-record",
3
- "version": "0.3.6",
3
+ "version": "0.4.0",
4
4
  "description": "Typescript Data Modeler and ORM for Dynamo",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",