dyna-record 0.7.1 → 0.7.3

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
@@ -14,6 +14,7 @@ Note: ACID compliant according to DynamoDB [limitations](https://docs.aws.amazon
14
14
  - [Installation](#installation)
15
15
  - [Configuration](#configuration)
16
16
  - [Defining Entities](#defining-entities)
17
+ - [Entity inheritance and shared base classes](#entity-inheritance-and-shared-base-classes)
17
18
  - [Attributes](#attributes)
18
19
  - [Relationships](#relationships)
19
20
  - [CRUD Operations](#crud-operations)
@@ -157,6 +158,54 @@ class Course extends MyTable {
157
158
 
158
159
  > **Note:** `declare readonly type` is a pure TypeScript type annotation with zero runtime impact. The ORM sets `type` to the class name automatically. The declaration simply tells TypeScript the exact literal type, enabling typed query filters and return type narrowing.
159
160
 
161
+ #### Entity inheritance and shared base classes
162
+
163
+ Entities do not have to extend the table class directly. The `@Entity` decorator resolves an entity's table by walking the class hierarchy until it finds a class decorated with `@Table`, so shared attributes and relationships can live in an abstract base class between the table and the concrete entities:
164
+
165
+ ```typescript
166
+ import {
167
+ Entity,
168
+ StringAttribute,
169
+ NumberAttribute,
170
+ BooleanAttribute
171
+ } from "dyna-record";
172
+
173
+ // Not decorated with @Entity — this class is never registered as an entity,
174
+ // never appears in table metadata, and no records of its own type exist
175
+ abstract class Vehicle extends MyTable {
176
+ @StringAttribute({ alias: "Make" })
177
+ public readonly make: string;
178
+
179
+ @NumberAttribute({ alias: "Year" })
180
+ public readonly year: number;
181
+ }
182
+
183
+ @Entity
184
+ class Car extends Vehicle {
185
+ declare readonly type: "Car";
186
+
187
+ @NumberAttribute({ alias: "Doors" })
188
+ public readonly doors: number;
189
+ }
190
+
191
+ @Entity
192
+ class Motorcycle extends Vehicle {
193
+ declare readonly type: "Motorcycle";
194
+
195
+ @BooleanAttribute({ alias: "HasSidecar" })
196
+ public readonly hasSidecar: boolean;
197
+ }
198
+ ```
199
+
200
+ `Car` and `Motorcycle` are full entities of `MyTable`: each inherits `make` and `year` (including runtime schema validation for them), and all CRUD operations work as if the attributes were declared on the entity itself. The abstract base class is only a container for shared code — it cannot be queried or persisted.
201
+
202
+ A few things to keep in mind:
203
+
204
+ - If an entity class does not extend a class decorated with `@Table` anywhere in its hierarchy, the `@Entity` decorator throws at class definition time.
205
+ - Extending a **concrete** entity (e.g. `class Pickup extends Truck`) is supported at runtime, but TypeScript will not allow the subclass to narrow the inherited `type` literal (`"Pickup"` is not assignable to `"Truck"`). Prefer abstract base classes for shared attributes.
206
+ - There is no polymorphic querying: entities are stored and queried by their exact class name. Querying `Car` will never return `Motorcycle` records, even though they share a base class.
207
+ - A base class couples its subclasses to one table, since it must extend a specific table class. To share a shape across entities in different tables, use a TypeScript interface instead (each entity must still declare its own decorated attributes).
208
+
160
209
  ### Attributes
161
210
 
162
211
  Use the attribute decorators below to define attributes on a model. The decorator maps class properties to DynamoDB table attributes.
@@ -10,7 +10,7 @@ interface DynaRecordBase {
10
10
  /**
11
11
  * Serves as an abstract base class for entities in the ORM system. It defines standard fields such as `id`, `type`, `createdAt`, and `updatedAt`, and provides static methods for CRUD operations and queries. This class encapsulates common behaviors and properties that all entities share, leveraging decorators for attribute metadata and supporting operations like finding, creating, updating, and deleting entities.
12
12
  *
13
- * Table classes should extend this class, and each entity should extend the table class
13
+ * Table classes should extend this class, and each entity should extend the table class — either directly or through intermediate abstract base classes that hold shared attributes
14
14
  *
15
15
  * Entities extending `DynaRecord` can utilize these operations to interact with their corresponding records in the database, including handling relationships between different entities.
16
16
  * @example
@@ -40,7 +40,7 @@ import { createInstance, tableItemToEntity } from "./utils.js";
40
40
  /**
41
41
  * Serves as an abstract base class for entities in the ORM system. It defines standard fields such as `id`, `type`, `createdAt`, and `updatedAt`, and provides static methods for CRUD operations and queries. This class encapsulates common behaviors and properties that all entities share, leveraging decorators for attribute metadata and supporting operations like finding, creating, updating, and deleting entities.
42
42
  *
43
- * Table classes should extend this class, and each entity should extend the table class
43
+ * Table classes should extend this class, and each entity should extend the table class — either directly or through intermediate abstract base classes that hold shared attributes
44
44
  *
45
45
  * Entities extending `DynaRecord` can utilize these operations to interact with their corresponding records in the database, including handling relationships between different entities.
46
46
  * @example
@@ -2,7 +2,7 @@ import type DynaRecord from "../DynaRecord.js";
2
2
  /**
3
3
  * A class decorator for marking a class as an entity within the context of the ORM system. This decorator is essential for registering the class as a distinct entity in the ORM's metadata system, enabling the ORM to recognize and manage instances of this class as part of its data model. By designating classes as entities, it facilitates their integration into the ORM framework, allowing for operations such as querying, persisting, and managing relationships between entities.
4
4
  *
5
- * IMPORTANT - All entity classes should extend a table class decorated by {@link Table}
5
+ * IMPORTANT - All entity classes must extend a table class decorated by {@link Table}, either directly or through intermediate classes. The entity's table is resolved by walking the class hierarchy until a class decorated with {@link Table} is found, so entities can share attributes and relationships through abstract base classes. A base class that is not decorated with `Entity` is never registered as an entity itself — only the decorated subclasses are part of the table's metadata.
6
6
  *
7
7
  * Entities MUST declare their `type` property as a string literal matching the class name.
8
8
  * This enables compile-time type safety for query filters and return types.
@@ -12,6 +12,7 @@ import type DynaRecord from "../DynaRecord.js";
12
12
  * @param target The constructor function of the class being decorated.
13
13
  * @param context The context in which the decorator is applied, provided by the TypeScript runtime.
14
14
  * @returns {void} The decorator does not return a value.
15
+ * @throws Error if the decorated class does not extend a class decorated with {@link Table} anywhere in its class hierarchy.
15
16
  *
16
17
  * Usage example:
17
18
  * ```typescript
@@ -21,6 +22,23 @@ import type DynaRecord from "../DynaRecord.js";
21
22
  * // User entity implementation
22
23
  * }
23
24
  * ```
25
+ *
26
+ * Sharing attributes through an abstract base class:
27
+ * ```typescript
28
+ * // Not decorated with Entity — never registered and never persisted itself
29
+ * abstract class Vehicle extends MyTable {
30
+ * @StringAttribute({ alias: "Make" })
31
+ * public readonly make: string;
32
+ * }
33
+ *
34
+ * @Entity
35
+ * class Car extends Vehicle {
36
+ * declare readonly type: "Car";
37
+ * // Inherits the `make` attribute from Vehicle
38
+ * }
39
+ * ```
40
+ *
41
+ * Note: extending a concrete entity (rather than an abstract base class) is supported at runtime, but TypeScript will not allow the subclass to narrow the inherited `type` literal of its parent. Prefer abstract base classes for shared attributes.
24
42
  */
25
43
  declare function Entity<C extends abstract new (...args: never[]) => DynaRecord>(target: C & (string extends InstanceType<C>["type"] ? {
26
44
  __entityTypeError: 'Entity must declare: declare readonly type: "ClassName"';
@@ -1 +1 @@
1
- {"version":3,"file":"Entity.d.ts","sourceRoot":"","sources":["../../../src/decorators/Entity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,iBAAS,MAAM,CAAC,CAAC,SAAS,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,UAAU,EACrE,MAAM,EAAE,CAAC,GACP,CAAC,MAAM,SAAS,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GACnC;IACE,iBAAiB,EAAE,yDAAyD,CAAC;CAC9E,GACD,OAAO,CAAC,EACd,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACjC,IAAI,CAKN;AAED,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"Entity.d.ts","sourceRoot":"","sources":["../../../src/decorators/Entity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,iBAAS,MAAM,CAAC,CAAC,SAAS,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,UAAU,EACrE,MAAM,EAAE,CAAC,GACP,CAAC,MAAM,SAAS,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GACnC;IACE,iBAAiB,EAAE,yDAAyD,CAAC;CAC9E,GACD,OAAO,CAAC,EACd,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACjC,IAAI,CAEN;AAED,eAAe,MAAM,CAAC"}
@@ -2,7 +2,7 @@ import Metadata from "../metadata/index.js";
2
2
  /**
3
3
  * A class decorator for marking a class as an entity within the context of the ORM system. This decorator is essential for registering the class as a distinct entity in the ORM's metadata system, enabling the ORM to recognize and manage instances of this class as part of its data model. By designating classes as entities, it facilitates their integration into the ORM framework, allowing for operations such as querying, persisting, and managing relationships between entities.
4
4
  *
5
- * IMPORTANT - All entity classes should extend a table class decorated by {@link Table}
5
+ * IMPORTANT - All entity classes must extend a table class decorated by {@link Table}, either directly or through intermediate classes. The entity's table is resolved by walking the class hierarchy until a class decorated with {@link Table} is found, so entities can share attributes and relationships through abstract base classes. A base class that is not decorated with `Entity` is never registered as an entity itself — only the decorated subclasses are part of the table's metadata.
6
6
  *
7
7
  * Entities MUST declare their `type` property as a string literal matching the class name.
8
8
  * This enables compile-time type safety for query filters and return types.
@@ -12,6 +12,7 @@ import Metadata from "../metadata/index.js";
12
12
  * @param target The constructor function of the class being decorated.
13
13
  * @param context The context in which the decorator is applied, provided by the TypeScript runtime.
14
14
  * @returns {void} The decorator does not return a value.
15
+ * @throws Error if the decorated class does not extend a class decorated with {@link Table} anywhere in its class hierarchy.
15
16
  *
16
17
  * Usage example:
17
18
  * ```typescript
@@ -21,9 +22,25 @@ import Metadata from "../metadata/index.js";
21
22
  * // User entity implementation
22
23
  * }
23
24
  * ```
25
+ *
26
+ * Sharing attributes through an abstract base class:
27
+ * ```typescript
28
+ * // Not decorated with Entity — never registered and never persisted itself
29
+ * abstract class Vehicle extends MyTable {
30
+ * @StringAttribute({ alias: "Make" })
31
+ * public readonly make: string;
32
+ * }
33
+ *
34
+ * @Entity
35
+ * class Car extends Vehicle {
36
+ * declare readonly type: "Car";
37
+ * // Inherits the `make` attribute from Vehicle
38
+ * }
39
+ * ```
40
+ *
41
+ * Note: extending a concrete entity (rather than an abstract base class) is supported at runtime, but TypeScript will not allow the subclass to narrow the inherited `type` literal of its parent. Prefer abstract base classes for shared attributes.
24
42
  */
25
43
  function Entity(target, _context) {
26
- const tableClassName = Object.getPrototypeOf(target).name;
27
- Metadata.addEntity(target, tableClassName);
44
+ Metadata.addEntity(target);
28
45
  }
29
46
  export default Entity;
@@ -3,7 +3,7 @@ import type DynaRecord from "../DynaRecord.js";
3
3
  /**
4
4
  * A class decorator for defining and customizing the table metadata associated with an entity class within the ORM system. This decorator enriches the entity with additional metadata, specifying how the entity relates to the underlying database table. By providing custom table options, such as table names or schema definitions, this decorator plays a crucial role in bridging the gap between the ORM's abstract entities and their concrete database representations.
5
5
  *
6
- * IMPORTANT - All entity classes should extend a table
6
+ * IMPORTANT - All entity classes should extend a table, either directly or through intermediate base classes (see {@link Entity})
7
7
  *
8
8
  * @param props The {@link TableMetadataOptions} object containing metadata configuration for the table. This can include options like the table's name, delimiter and default field customizations.
9
9
  * @returns A class decorator factory function that takes a target class extending `DynaRecord` and a context object provided by the TypeScript runtime. The decorator function registers the provided metadata options with the ORM's metadata system, ensuring the entity is properly configured and recognized by the ORM.
@@ -2,7 +2,7 @@ import Metadata from "../metadata/index.js";
2
2
  /**
3
3
  * A class decorator for defining and customizing the table metadata associated with an entity class within the ORM system. This decorator enriches the entity with additional metadata, specifying how the entity relates to the underlying database table. By providing custom table options, such as table names or schema definitions, this decorator plays a crucial role in bridging the gap between the ORM's abstract entities and their concrete database representations.
4
4
  *
5
- * IMPORTANT - All entity classes should extend a table
5
+ * IMPORTANT - All entity classes should extend a table, either directly or through intermediate base classes (see {@link Entity})
6
6
  *
7
7
  * @param props The {@link TableMetadataOptions} object containing metadata configuration for the table. This can include options like the table's name, delimiter and default field customizations.
8
8
  * @returns A class decorator factory function that takes a target class extending `DynaRecord` and a context object provided by the TypeScript runtime. The decorator function registers the provided metadata options with the ORM's metadata system, ensuring the entity is properly configured and recognized by the ORM.
@@ -3,7 +3,7 @@ export * from "./errors.js";
3
3
  export * from "./relationships/index.js";
4
4
  export * from "./dynamo-utils/errors.js";
5
5
  export type { EntityAttributesOnly, EntityAttributesInstance as EntityInstance, FindByIdIncludesRes, TypedFilterParams, TypedSortKeyCondition, SKScopedFilterParams, InferQueryResults, PartitionEntityNames, ShouldNarrow, NarrowByNames, FallbackToFilterKeys, IntersectTypeWithOr } from "./operations/index.js";
6
- export type { PartitionKey, SortKey, ForeignKey, NullableForeignKey, Optional } from "./types.js";
6
+ export type { Brand, PartitionKey, SortKey, ForeignKey, NullableForeignKey, Optional } from "./types.js";
7
7
  export type { AttributeKind } from "./metadata/types.js";
8
8
  export type { SerializedTableMetadata } from "./metadata/schemas.js";
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AAEzC,YAAY,EACV,oBAAoB,EACpB,wBAAwB,IAAI,cAAc,EAC1C,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,YAAY,EACZ,OAAO,EACP,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACT,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,aAAa,CAAC;AAC5B,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AAEzC,YAAY,EACV,oBAAoB,EACpB,wBAAwB,IAAI,cAAc,EAC1C,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,oBAAoB,EACpB,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,KAAK,EACL,YAAY,EACZ,OAAO,EACP,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACT,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,YAAY,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -60,11 +60,13 @@ declare class MetadataStorage {
60
60
  */
61
61
  addTable(tableClassName: string, options: TableMetadataOptions): void;
62
62
  /**
63
- * Add an entity to metadata storage
64
- * @param entityName
65
- * @param tableName
63
+ * Add an entity to metadata storage. The table the entity belongs to is
64
+ * resolved by walking the entity's class hierarchy until a class decorated
65
+ * with the Table decorator is found, supporting entities that extend other
66
+ * entities or intermediate abstract classes.
67
+ * @param entityClass
66
68
  */
67
- addEntity(entityClass: EntityMetadata["EntityClass"], tableClassName: string): void;
69
+ addEntity(entityClass: EntityMetadata["EntityClass"]): void;
68
70
  /**
69
71
  * Adds a relationship to an Entity's metadata storage
70
72
  * @param entityName
@@ -105,6 +107,14 @@ declare class MetadataStorage {
105
107
  * Initialize metadata object
106
108
  */
107
109
  private init;
110
+ /**
111
+ * Walks an entity class's hierarchy and returns the name of the first
112
+ * ancestor registered as a table via the Table decorator. Throws if the
113
+ * entity does not extend a table class anywhere in its hierarchy.
114
+ * @param entityClass
115
+ * @returns Name of the table class the entity belongs to
116
+ */
117
+ private resolveTableClassName;
108
118
  /**
109
119
  * Recursively search prototype chain and return TableMetadata for an entity class if it exists
110
120
  * @param classPrototype
@@ -1 +1 @@
1
- {"version":3,"file":"MetadataStorage.d.ts","sourceRoot":"","sources":["../../../src/metadata/MetadataStorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,cAAc,MAAM,qBAAqB,CAAC;AAEjD,OAAO,iBAAiB,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,KAAK,EACV,wBAAwB,EAExB,qBAAqB,EAErB,oBAAoB,EAEpB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,cAAM,eAAe;;IAOnB;;;;OAIG;IACI,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc;IAKpD;;;;OAIG;IACI,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa;IAKjD;;;;OAIG;IACI,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa;IAMxD;;;;OAIG;IACI,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAK/D;;;;OAIG;IACI,mBAAmB,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB;IAWzE;;;OAGG;IACI,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,wBAAwB;IAYxE;;;;OAIG;IACI,wBAAwB,CAC7B,UAAU,EAAE,MAAM,GACjB,wBAAwB;IAY3B;;;;OAIG;IACI,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAI5E;;;;OAIG;IACI,SAAS,CACd,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,EAC1C,cAAc,EAAE,MAAM,GACrB,IAAI;IAOP;;;;OAIG;IACI,qBAAqB,CAC1B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,oBAAoB,GAC5B,IAAI;IAQP;;;;OAIG;IACI,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAW5E;;;;OAIG;IACI,kBAAkB,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,wBAAwB,EAAE,OAAO,CAAC,GACvD,IAAI;IAYP;;;;OAIG;IACI,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAKpE;;;;OAIG;IACI,wBAAwB,CAC7B,WAAW,EAAE,UAAU,EACvB,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC,GACrE,IAAI;IAQP;;;;OAIG;IACI,mBAAmB,CACxB,WAAW,EAAE,UAAU,EACvB,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC,GACrE,IAAI;IAQP;;OAEG;IACH,OAAO,CAAC,IAAI;IAUZ;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;CAe/B;AAED,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"MetadataStorage.d.ts","sourceRoot":"","sources":["../../../src/metadata/MetadataStorage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,aAAa,MAAM,oBAAoB,CAAC;AAC/C,OAAO,cAAc,MAAM,qBAAqB,CAAC;AAEjD,OAAO,iBAAiB,MAAM,wBAAwB,CAAC;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,KAAK,EACV,wBAAwB,EAExB,qBAAqB,EAErB,oBAAoB,EAEpB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,cAAM,eAAe;;IAOnB;;;;OAIG;IACI,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc;IAKpD;;;;OAIG;IACI,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa;IAKjD;;;;OAIG;IACI,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa;IAMxD;;;;OAIG;IACI,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAK/D;;;;OAIG;IACI,mBAAmB,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB;IAWzE;;;OAGG;IACI,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,wBAAwB;IAYxE;;;;OAIG;IACI,wBAAwB,CAC7B,UAAU,EAAE,MAAM,GACjB,wBAAwB;IAY3B;;;;OAIG;IACI,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAI5E;;;;;;OAMG;IACI,SAAS,CAAC,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,GAAG,IAAI;IAQlE;;;;OAIG;IACI,qBAAqB,CAC1B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,oBAAoB,GAC5B,IAAI;IAQP;;;;OAIG;IACI,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAW5E;;;;OAIG;IACI,kBAAkB,CACvB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,CAAC,wBAAwB,EAAE,OAAO,CAAC,GACvD,IAAI;IAYP;;;;OAIG;IACI,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAKpE;;;;OAIG;IACI,wBAAwB,CAC7B,WAAW,EAAE,UAAU,EACvB,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC,GACrE,IAAI;IAQP;;;;OAIG;IACI,mBAAmB,CACxB,WAAW,EAAE,UAAU,EACvB,OAAO,EAAE,UAAU,CAAC,aAAa,CAAC,0BAA0B,CAAC,CAAC,CAAC,MAAM,CAAC,GACrE,IAAI;IAQP;;OAEG;IACH,OAAO,CAAC,IAAI;IAUZ;;;;;;OAMG;IACH,OAAO,CAAC,qBAAqB;IAgB7B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;CAe/B;AAED,eAAe,eAAe,CAAC"}
@@ -103,11 +103,14 @@ class MetadataStorage {
103
103
  this.#tables[tableClassName] = new TableMetadata(options);
104
104
  }
105
105
  /**
106
- * Add an entity to metadata storage
107
- * @param entityName
108
- * @param tableName
106
+ * Add an entity to metadata storage. The table the entity belongs to is
107
+ * resolved by walking the entity's class hierarchy until a class decorated
108
+ * with the Table decorator is found, supporting entities that extend other
109
+ * entities or intermediate abstract classes.
110
+ * @param entityClass
109
111
  */
110
- addEntity(entityClass, tableClassName) {
112
+ addEntity(entityClass) {
113
+ const tableClassName = this.resolveTableClassName(entityClass);
111
114
  this.#entities[entityClass.name] = new EntityMetadata(entityClass, tableClassName);
112
115
  }
113
116
  /**
@@ -193,6 +196,23 @@ class MetadataStorage {
193
196
  this.#initialized = true;
194
197
  }
195
198
  }
199
+ /**
200
+ * Walks an entity class's hierarchy and returns the name of the first
201
+ * ancestor registered as a table via the Table decorator. Throws if the
202
+ * entity does not extend a table class anywhere in its hierarchy.
203
+ * @param entityClass
204
+ * @returns Name of the table class the entity belongs to
205
+ */
206
+ resolveTableClassName(entityClass) {
207
+ let current = Object.getPrototypeOf(entityClass);
208
+ // The constructor chain ends at Function.prototype, whose name is ""
209
+ while (typeof current === "function" && current.name !== "") {
210
+ if (current.name in this.#tables)
211
+ return current.name;
212
+ current = Object.getPrototypeOf(current);
213
+ }
214
+ throw new Error(`Entity ${entityClass.name} must extend a class decorated with @Table, either directly or through its class hierarchy`);
215
+ }
196
216
  /**
197
217
  * Recursively search prototype chain and return TableMetadata for an entity class if it exists
198
218
  * @param classPrototype
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dyna-record",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Typescript Data Modeler and ORM for Dynamo",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",