dyna-record 0.3.6 → 0.4.1

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.
Files changed (38) hide show
  1. package/README.md +56 -10
  2. package/dist/src/DynaRecord.d.ts +39 -7
  3. package/dist/src/DynaRecord.d.ts.map +1 -1
  4. package/dist/src/DynaRecord.js +36 -10
  5. package/dist/src/decorators/attributes/ForeignKeyAttribute.d.ts +10 -6
  6. package/dist/src/decorators/attributes/ForeignKeyAttribute.d.ts.map +1 -1
  7. package/dist/src/decorators/attributes/ForeignKeyAttribute.js +11 -5
  8. package/dist/src/decorators/relationships/BelongsTo.d.ts +3 -2
  9. package/dist/src/decorators/relationships/BelongsTo.d.ts.map +1 -1
  10. package/dist/src/decorators/relationships/types.d.ts +10 -5
  11. package/dist/src/decorators/relationships/types.d.ts.map +1 -1
  12. package/dist/src/metadata/AttributeMetadata.d.ts +4 -0
  13. package/dist/src/metadata/AttributeMetadata.d.ts.map +1 -1
  14. package/dist/src/metadata/AttributeMetadata.js +3 -0
  15. package/dist/src/metadata/EntityMetadata.d.ts +11 -1
  16. package/dist/src/metadata/EntityMetadata.d.ts.map +1 -1
  17. package/dist/src/metadata/EntityMetadata.js +17 -0
  18. package/dist/src/metadata/types.d.ts +13 -1
  19. package/dist/src/metadata/types.d.ts.map +1 -1
  20. package/dist/src/metadata/utils.d.ts +7 -1
  21. package/dist/src/metadata/utils.d.ts.map +1 -1
  22. package/dist/src/metadata/utils.js +9 -1
  23. package/dist/src/operations/Create/Create.d.ts +14 -3
  24. package/dist/src/operations/Create/Create.d.ts.map +1 -1
  25. package/dist/src/operations/Create/Create.js +42 -5
  26. package/dist/src/operations/Create/types.d.ts +12 -0
  27. package/dist/src/operations/Create/types.d.ts.map +1 -1
  28. package/dist/src/operations/Update/Update.d.ts +17 -2
  29. package/dist/src/operations/Update/Update.d.ts.map +1 -1
  30. package/dist/src/operations/Update/Update.js +52 -9
  31. package/dist/src/operations/Update/types.d.ts +12 -0
  32. package/dist/src/operations/Update/types.d.ts.map +1 -1
  33. package/dist/src/relationships/JoinTable.d.ts +17 -1
  34. package/dist/src/relationships/JoinTable.d.ts.map +1 -1
  35. package/dist/src/relationships/JoinTable.js +16 -9
  36. package/dist/src/types.d.ts +13 -3
  37. package/dist/src/types.d.ts.map +1 -1
  38. package/package.json +1 -1
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
 
@@ -355,6 +357,25 @@ const grade: Grade = await Grade.create({
355
357
  });
356
358
  ```
357
359
 
360
+ #### Skipping Referential Integrity Checks
361
+
362
+ By default, when creating entities with foreign key references, dyna-record performs condition checks to ensure that referenced entities exist. This prevents creating entities with invalid foreign key references. However, in high-contention or high-throughput systems where the same foreign key may be referenced in parallel operations, these condition checks can fail due to transaction conflicts. In scenarios such as bulk imports or when you've already verified the references, you may want to skip these checks to prevent such failures.
363
+
364
+ To skip referential integrity checks, pass an options object as the second parameter with `referentialIntegrityCheck: false`:
365
+
366
+ ```typescript
367
+ const grade: Grade = await Grade.create(
368
+ {
369
+ gradeValue: "A+",
370
+ assignmentId: "123",
371
+ studentId: "456"
372
+ },
373
+ { referentialIntegrityCheck: false }
374
+ );
375
+ ```
376
+
377
+ **Note:** When `referentialIntegrityCheck` is set to `false`, the condition checks that verify foreign key references exist are skipped. This means you can create entities even if the referenced entities don't exist, which may lead to data integrity issues. Use this option with caution.
378
+
358
379
  #### Error handling
359
380
 
360
381
  The method is designed to throw errors under various conditions, such as transaction cancellation due to failed conditional checks. For instance, if you attempt to create a `Grade` for an `Assignment` that already has one, the method throws a [TransactionWriteFailedError](https://dyna-record.com/classes/TransactionWriteFailedError.html).
@@ -547,6 +568,31 @@ const updatedInstance = await petInstance.update({
547
568
  });
548
569
  ```
549
570
 
571
+ #### Skipping Referential Integrity Checks
572
+
573
+ By default, when updating entities with foreign key references, dyna-record performs condition checks to ensure that referenced entities exist. This prevents updating entities with invalid foreign key references. However, in high-contention or high-throughput systems where the same foreign key may be referenced in parallel operations, these condition checks can fail due to transaction conflicts. In scenarios such as bulk updates or when you've already verified the references, you may want to skip these checks to prevent such failures.
574
+
575
+ To skip referential integrity checks, pass an options object as the third parameter with `referentialIntegrityCheck: false`:
576
+
577
+ ```typescript
578
+ await PaymentMethod.update(
579
+ "123",
580
+ { customerId: "456" },
581
+ { referentialIntegrityCheck: false }
582
+ );
583
+ ```
584
+
585
+ For instance methods:
586
+
587
+ ```typescript
588
+ const updatedInstance = await paymentMethodInstance.update(
589
+ { customerId: "456" },
590
+ { referentialIntegrityCheck: false }
591
+ );
592
+ ```
593
+
594
+ **Note:** When `referentialIntegrityCheck` is set to `false`, the condition checks that verify foreign key references exist are skipped. This means you can update entities even if the referenced entities don't exist, which may lead to data integrity issues. Use this option with caution.
595
+
550
596
  ### Delete
551
597
 
552
598
  [Docs](https://dyna-record.com/classes/default.html#delete)
@@ -181,24 +181,36 @@ declare abstract class DynaRecord implements DynaRecordBase {
181
181
  */
182
182
  static query<T extends DynaRecord>(this: EntityClass<T>, key: IndexKeyConditions<T>, options: OptionsWithIndex): Promise<QueryResults<T>>;
183
183
  /**
184
- * Create an entity. If foreign keys are included in the attributes then links will be demoralized accordingly
184
+ * Create an entity. If foreign keys are included in the attributes then links will be denormalized accordingly
185
185
  * @param attributes - Attributes of the model to create
186
+ * @param options - Optional operation options including referentialIntegrityCheck flag
186
187
  * @returns The new Entity
187
188
  *
189
+ * @example Basic usage
188
190
  * ```typescript
189
191
  * const newUser = await User.create({ name: "Alice", email: "alice@example.com", profileId: "123" });
190
192
  * ```
193
+ *
194
+ * @example With referential integrity check disabled
195
+ * ```typescript
196
+ * const newUser = await User.create(
197
+ * { name: "Alice", email: "alice@example.com", profileId: "123" },
198
+ * { referentialIntegrityCheck: false }
199
+ * );
200
+ * ```
191
201
  */
192
- static create<T extends DynaRecord>(this: EntityClass<T>, attributes: CreateOptions<T>): Promise<ReturnType<Create<T>["run"]>>;
202
+ static create<T extends DynaRecord>(this: EntityClass<T>, attributes: CreateOptions<T>, options?: {
203
+ referentialIntegrityCheck?: boolean;
204
+ }): Promise<ReturnType<Create<T>["run"]>>;
193
205
  /**
194
- * Update an entity. If foreign keys are included in the attribute then:
206
+ * Update an entity. If foreign keys are included in the attributes then:
195
207
  * - Manages associated relationship links as needed
196
208
  * - If the entity already had a foreign key relationship, then denormalized records will be deleted from each partition
197
209
  * - If the foreign key is not nullable then a {@link NullConstraintViolationError} is thrown.
198
210
  * - Validation errors will be thrown if the attribute being removed is not nullable
199
211
  * @param id - The id of the entity to update
200
212
  * @param attributes - Attributes to update
201
- *
213
+ * @param options - Optional operation options including referentialIntegrityCheck flag
202
214
  *
203
215
  * @example Updating an entity.
204
216
  * ```typescript
@@ -209,12 +221,22 @@ declare abstract class DynaRecord implements DynaRecordBase {
209
221
  * ```typescript
210
222
  * await User.update("userId", { email: "newemail@example.com", someKey: null });
211
223
  * ```
224
+ *
225
+ * @example With referential integrity check disabled
226
+ * ```typescript
227
+ * await User.update(
228
+ * "userId",
229
+ * { email: "newemail@example.com", profileId: 789 },
230
+ * { referentialIntegrityCheck: false }
231
+ * );
232
+ * ```
212
233
  */
213
- static update<T extends DynaRecord>(this: EntityClass<T>, id: string, attributes: UpdateOptions<T>): Promise<void>;
234
+ static update<T extends DynaRecord>(this: EntityClass<T>, id: string, attributes: UpdateOptions<T>, options?: {
235
+ referentialIntegrityCheck?: boolean;
236
+ }): Promise<void>;
214
237
  /**
215
238
  * Same as the static `update` method but on an instance. Returns the full updated instance
216
239
  *
217
- *
218
240
  * @example Updating an entity.
219
241
  * ```typescript
220
242
  * const updatedInstance = await instance.update({ email: "newemail@example.com", profileId: 789 });
@@ -224,8 +246,18 @@ declare abstract class DynaRecord implements DynaRecordBase {
224
246
  * ```typescript
225
247
  * const updatedInstance = await instance.update({ email: "newemail@example.com", someKey: null });
226
248
  * ```
249
+ *
250
+ * @example With referential integrity check disabled
251
+ * ```typescript
252
+ * const updatedInstance = await instance.update(
253
+ * { email: "newemail@example.com", profileId: 789 },
254
+ * { referentialIntegrityCheck: false }
255
+ * );
256
+ * ```
227
257
  */
228
- update<T extends this>(attributes: UpdateOptions<T>): Promise<EntityAttributesInstance<T>>;
258
+ update<T extends this>(attributes: UpdateOptions<T>, options?: {
259
+ referentialIntegrityCheck?: boolean;
260
+ }): Promise<EntityAttributesInstance<T>>;
229
261
  /**
230
262
  * Delete an entity by ID
231
263
  * - Delete all denormalized records
@@ -1 +1 @@
1
- {"version":3,"file":"DynaRecord.d.ts","sourceRoot":"","sources":["../../src/DynaRecord.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAExB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,MAAM,EACN,KAAK,aAAa,EAElB,KAAK,aAAa,EAGlB,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGtE,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,uBAAe,UAAW,YAAW,cAAc;IACjD;;OAEG;IACH,SACgB,EAAE,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,SACgB,IAAI,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,SACgB,SAAS,EAAE,IAAI,CAAC;IAEhC;;OAEG;IACH,SACgB,SAAS,EAAE,IAAI,CAAC;IAEhC;;;;;;;;;;;;;;;;;;;;;;OAsBG;WAEiB,QAAQ,CAAC,CAAC,SAAS,UAAU,EAC/C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,SAAS,GAClB,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC;WAG7B,QAAQ,CAC1B,CAAC,SAAS,UAAU,EACpB,GAAG,SAAS,oBAAoB,CAAC,CAAC,CAAC,GAAG,EAAE,EAExC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,eAAe,CAAC,CAAC,EAAE,GAAG,CAAC,GAC/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAgBjD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;WACiB,KAAK,CAAC,CAAC,SAAS,UAAU,EAC5C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqDG;WACiB,KAAK,CAAC,CAAC,SAAS,UAAU,EAC5C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,GAAG,EAAE,mBAAmB,CAAC,CAAC,CAAC,EAC3B,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAE3B;;;;;;;;;;;;;;;OAeG;WACiB,KAAK,CAAC,CAAC,SAAS,UAAU,EAC5C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,GAAG,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC1B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAW3B;;;;;;;;OAQG;WACiB,MAAM,CAAC,CAAC,SAAS,UAAU,EAC7C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IAKxC;;;;;;;;;;;;;;;;;;;OAmBG;WACiB,MAAM,CAAC,CAAC,SAAS,UAAU,EAC7C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,IAAI,CAAC;IAKhB;;;;;;;;;;;;;OAaG;IACU,MAAM,CAAC,CAAC,SAAS,IAAI,EAChC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;IAkBvC;;;;;;;;;;OAUG;WACiB,MAAM,CAAC,CAAC,SAAS,UAAU,EAC7C,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC,EACpB,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,CAAC;IAKhB;;;;;;;;;OASG;WACW,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM;IAKnD;;OAEG;WACW,iBAAiB,CAAC,CAAC,SAAS,UAAU,EAClD,IAAI,EAAE,UAAU,CAAC,EACjB,SAAS,EAAE,eAAe,GACzB,wBAAwB,CAAC,CAAC,CAAC;IAW9B;;;OAGG;IACI,iBAAiB,IAAI,MAAM;CAGnC;AAED,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"DynaRecord.d.ts","sourceRoot":"","sources":["../../src/DynaRecord.ts"],"names":[],"mappings":"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"}
@@ -145,27 +145,37 @@ let DynaRecord = (() => {
145
145
  return await op.run(key, options);
146
146
  }
147
147
  /**
148
- * Create an entity. If foreign keys are included in the attributes then links will be demoralized accordingly
148
+ * Create an entity. If foreign keys are included in the attributes then links will be denormalized accordingly
149
149
  * @param attributes - Attributes of the model to create
150
+ * @param options - Optional operation options including referentialIntegrityCheck flag
150
151
  * @returns The new Entity
151
152
  *
153
+ * @example Basic usage
152
154
  * ```typescript
153
155
  * const newUser = await User.create({ name: "Alice", email: "alice@example.com", profileId: "123" });
154
156
  * ```
157
+ *
158
+ * @example With referential integrity check disabled
159
+ * ```typescript
160
+ * const newUser = await User.create(
161
+ * { name: "Alice", email: "alice@example.com", profileId: "123" },
162
+ * { referentialIntegrityCheck: false }
163
+ * );
164
+ * ```
155
165
  */
156
- static async create(attributes) {
166
+ static async create(attributes, options) {
157
167
  const op = new operations_1.Create(this);
158
- return await op.run(attributes);
168
+ return await op.run(attributes, options);
159
169
  }
160
170
  /**
161
- * Update an entity. If foreign keys are included in the attribute then:
171
+ * Update an entity. If foreign keys are included in the attributes then:
162
172
  * - Manages associated relationship links as needed
163
173
  * - If the entity already had a foreign key relationship, then denormalized records will be deleted from each partition
164
174
  * - If the foreign key is not nullable then a {@link NullConstraintViolationError} is thrown.
165
175
  * - Validation errors will be thrown if the attribute being removed is not nullable
166
176
  * @param id - The id of the entity to update
167
177
  * @param attributes - Attributes to update
168
- *
178
+ * @param options - Optional operation options including referentialIntegrityCheck flag
169
179
  *
170
180
  * @example Updating an entity.
171
181
  * ```typescript
@@ -176,15 +186,23 @@ let DynaRecord = (() => {
176
186
  * ```typescript
177
187
  * await User.update("userId", { email: "newemail@example.com", someKey: null });
178
188
  * ```
189
+ *
190
+ * @example With referential integrity check disabled
191
+ * ```typescript
192
+ * await User.update(
193
+ * "userId",
194
+ * { email: "newemail@example.com", profileId: 789 },
195
+ * { referentialIntegrityCheck: false }
196
+ * );
197
+ * ```
179
198
  */
180
- static async update(id, attributes) {
199
+ static async update(id, attributes, options) {
181
200
  const op = new operations_1.Update(this);
182
- await op.run(id, attributes);
201
+ await op.run(id, attributes, options);
183
202
  }
184
203
  /**
185
204
  * Same as the static `update` method but on an instance. Returns the full updated instance
186
205
  *
187
- *
188
206
  * @example Updating an entity.
189
207
  * ```typescript
190
208
  * const updatedInstance = await instance.update({ email: "newemail@example.com", profileId: 789 });
@@ -194,11 +212,19 @@ let DynaRecord = (() => {
194
212
  * ```typescript
195
213
  * const updatedInstance = await instance.update({ email: "newemail@example.com", someKey: null });
196
214
  * ```
215
+ *
216
+ * @example With referential integrity check disabled
217
+ * ```typescript
218
+ * const updatedInstance = await instance.update(
219
+ * { email: "newemail@example.com", profileId: 789 },
220
+ * { referentialIntegrityCheck: false }
221
+ * );
222
+ * ```
197
223
  */
198
- async update(attributes) {
224
+ async update(attributes, options) {
199
225
  const InstanceClass = this.constructor;
200
226
  const op = new operations_1.Update(InstanceClass);
201
- const updatedAttributes = await op.run(this.id, attributes);
227
+ const updatedAttributes = await op.run(this.id, attributes, options);
202
228
  const clone = structuredClone(this);
203
229
  // Update the current instance with new attributes
204
230
  Object.assign(clone, updatedAttributes);
@@ -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