From f370d39143d295bd114ef664e95b5ddef8b96093 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 6 Sep 2025 16:38:35 +0200 Subject: [PATCH 1/6] Add comprehensive documentation for EntityManager and Entity classes, including method descriptions, examples, and transaction operations. --- lib/entity/entityManager.ts | 555 ++++++++++++++++++++++++++++++++++++ lib/entity/index.ts | 58 ++++ 2 files changed, 613 insertions(+) diff --git a/lib/entity/entityManager.ts b/lib/entity/entityManager.ts index 58bdc9e..859a6c9 100644 --- a/lib/entity/entityManager.ts +++ b/lib/entity/entityManager.ts @@ -57,34 +57,156 @@ import { } from '@lib/transactionWrite/types'; import { AttributeValues, ExpressionBuilder, fromDynamo, NotFoundError } from '@lib/utils'; +/** + * Creates an EntityManager instance for a specific entity and table. + * + * The EntityManager provides a comprehensive set of methods for performing + * CRUD operations, batch operations, and transactions on DynamoDB entities. + * It handles the conversion between entity instances and DynamoDB attribute values, + * manages conditions, and provides type-safe operations. + * + * @param entity - The entity class constructor + * @param tableName - The name of the DynamoDB table + * @returns An object containing all entity management methods + * + * @example + * ```typescript + * class User extends Entity { + * @attribute.partitionKey.string() + * id: string; + * + * @attribute.string() + * name: string; + * } + * + * const UserManager = EntityManager(User, 'users-table'); + * + * // Create a new user + * const user = await UserManager.put(new User({ id: '1', name: 'John' })); + * + * // Get a user + * const retrievedUser = await UserManager.get({ id: '1' }); + * + * // Update a user + * const updatedUser = await UserManager.update({ id: '1' }, { set: { name: 'Jane' } }); + * ``` + * + * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/managers/entityManager} for more information + */ export default function EntityManager, E extends typeof Entity>(entity: E, tableName: string) { + /** + * Creates a new condition builder for this entity. + * + * @returns A new Condition instance for building conditional expressions + * + * @example + * ```typescript + * const condition = UserManager.condition() + * .attribute('name').eq('John') + * .and() + * .attribute('age').gt(18); + * + * await UserManager.update({ id: '1' }, { set: { status: 'active' } }, { condition }); + * ``` + */ function condition(): Condition { return new Condition(entity); } + /** + * Creates a new query builder for this entity. + * + * @returns A new Query instance for building query operations + * + * @example + * ```typescript + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .sortKey('createdAt').gt(new Date('2023-01-01')) + * .run(); + * ``` + */ function query(): Query { return new Query(entity); } + /** + * Creates a new scan builder for this entity. + * + * @returns A new Scan instance for building scan operations + * + * ⚠️ Keep in mind that scan operations are expensive, slow, and against [best practices](https://dynobase.dev/dynamodb-best-practices/). + * + * @example + * ```typescript + * const allUsers = await UserManager.scan() + * .attribute('status').eq('active') + * .run(); + * ``` + */ function scan(): Scan { return new Scan(entity); } + /** + * Retrieves a single item from the table by its primary key. + * + * @param primaryKey - The primary key of the item to retrieve + * @param options - Optional configuration for the get operation + * @returns A promise that resolves to the entity instance + * @throws {NotFoundError} When the item is not found + * + * @example + * ```typescript + * // Get a user by ID + * const user = await UserManager.get({ id: 'user-123' }); + * + * // Get with consistent read + * const user = await UserManager.get({ id: 'user-123' }, { consistent: true }); + * + * // Get only specific attributes + * const user = await UserManager.get({ id: 'user-123' }, { + * attributes: ['id', 'name'] + * }); + * ``` + */ function get( primaryKey: TablePrimaryKey, options?: EntityGetOptions & { return?: 'default' }, ): Promise>; + /** + * Retrieves a single item from the table by its primary key, returning the raw AWS response. + * + * @param primaryKey - The primary key of the item to retrieve + * @param options - Configuration for the get operation with return type 'output' + * @returns A promise that resolves to the raw GetItemCommandOutput + */ function get( primaryKey: TablePrimaryKey, options: EntityGetOptions & { return: 'output' }, ): Promise; + /** + * Builds the GetItem command input without executing it. + * + * @param primaryKey - The primary key of the item to retrieve + * @param options - Configuration for the get operation with return type 'input' + * @returns The GetItemCommandInput object + */ function get( primaryKey: TablePrimaryKey, options: EntityGetOptions & { return: 'input' }, ): GetItemCommandInput; + /** + * Retrieves a single item from the table by its primary key. + * + * @param primaryKey - The primary key of the item to retrieve + * @param options - Optional configuration for the get operation + * @returns A promise that resolves to the entity instance, raw AWS response, or command input + * @throws {NotFoundError} When the item is not found and return type is 'default' + */ function get( primaryKey: TablePrimaryKey, options?: EntityGetOptions, @@ -119,24 +241,79 @@ export default function EntityManager, E extends typeof En })(); } + /** + * Updates an item in the table by its primary key. + * + * @param primaryKey - The primary key of the item to update + * @param props - The update operations to perform + * @param options - Optional configuration for the update operation + * @returns A promise that resolves to the updated entity instance + * + * @example + * ```typescript + * // Update with SET operation + * const updatedUser = await UserManager.update( + * { id: 'user-123' }, + * { set: { name: 'Jane', age: 25 } } + * ); + * + * // Update with ADD operation (for numbers and sets) + * const updatedUser = await UserManager.update( + * { id: 'user-123' }, + * { add: { score: 10 } } + * ); + * + * // Update with conditional expression + * const condition = UserManager.condition().attribute('version').eq(1); + * const updatedUser = await UserManager.update( + * { id: 'user-123' }, + * { set: { name: 'Jane' } }, + * { condition } + * ); + * ``` + */ function update( primaryKey: TablePrimaryKey, props: UpdateProps, options?: EntityUpdateOptions & { return?: 'default' }, ): Promise>; + /** + * Updates an item in the table, returning the raw AWS response. + * + * @param primaryKey - The primary key of the item to update + * @param props - The update operations to perform + * @param options - Configuration for the update operation with return type 'output' + * @returns A promise that resolves to the raw UpdateItemCommandOutput + */ function update( primaryKey: TablePrimaryKey, props: UpdateProps, options: EntityUpdateOptions & { return: 'output' }, ): Promise; + /** + * Builds the UpdateItem command input without executing it. + * + * @param primaryKey - The primary key of the item to update + * @param props - The update operations to perform + * @param options - Configuration for the update operation with return type 'input' + * @returns The UpdateItemCommandInput object + */ function update( primaryKey: TablePrimaryKey, props: UpdateProps, options: EntityUpdateOptions & { return: 'input' }, ): UpdateItemCommandInput; + /** + * Updates an item in the table by its primary key. + * + * @param primaryKey - The primary key of the item to update + * @param props - The update operations to perform (set, add, remove, etc.) + * @param options - Optional configuration for the update operation + * @returns A promise that resolves to the updated entity instance, raw AWS response, or command input + */ function update( primaryKey: TablePrimaryKey, props: UpdateProps, @@ -174,14 +351,56 @@ export default function EntityManager, E extends typeof En })(); } + /** + * Puts (creates or overwrites) an item in the table. + * + * @param item - The entity instance to put + * @param options - Optional configuration for the put operation + * @returns A promise that resolves to the entity instance + * + * @example + * ```typescript + * const user = new User({ id: 'user-123', name: 'John', age: 30 }); + * const savedUser = await UserManager.put(user); + * + * // Put with conditional expression + * const condition = UserManager.condition().attribute('id').not().exists(); + * const savedUser = await UserManager.put(user, { condition }); + * + * // Put without overwriting existing items + * const savedUser = await UserManager.put(user, { overwrite: false }); + * ``` + */ function put(item: InstanceType, options?: EntityPutOptions & { return?: 'default' }): Promise>; + /** + * Puts an item in the table, returning the raw AWS response. + * + * @param item - The entity instance to put + * @param options - Configuration for the put operation with return type 'output' + * @returns A promise that resolves to the raw PutItemCommandOutput + */ function put( item: InstanceType, options: EntityPutOptions & { return: 'output' }, ): Promise; + + /** + * Builds the PutItem command input without executing it. + * + * @param item - The entity instance to put + * @param options - Configuration for the put operation with return type 'input' + * @returns The PutItemCommandInput object + */ function put(item: InstanceType, options: EntityPutOptions & { return: 'input' }): PutItemCommandInput; + /** + * Puts (creates or overwrites) an item in the table. + * + * @param item - The entity instance to put + * @param options - Optional configuration for the put operation + * @returns A promise that resolves to the entity instance, raw AWS response, or command input + */ function put( item: InstanceType, options?: EntityPutOptions, @@ -220,17 +439,59 @@ export default function EntityManager, E extends typeof En })(); } + /** + * Creates a new item in the table (fails if item already exists). + * + * This is equivalent to calling `put()` with `overwrite: false`. + * + * @param item - The entity instance to create + * @param options - Optional configuration for the create operation + * @returns A promise that resolves to the entity instance + * @throws {ConditionalCheckFailedException} When the item already exists + * + * @example + * ```typescript + * const user = new User({ id: 'user-123', name: 'John', age: 30 }); + * const createdUser = await UserManager.create(user); + * + * // Create with conditional expression + * const condition = UserManager.condition().attribute('status').eq('pending'); + * const createdUser = await UserManager.create(user, { condition }); + * ``` + */ function create( item: InstanceType, options?: EntityPutOptions & { return?: 'default' }, ): Promise>; + /** + * Creates a new item in the table, returning the raw AWS response. + * + * @param item - The entity instance to create + * @param options - Configuration for the create operation with return type 'output' + * @returns A promise that resolves to the raw PutItemCommandOutput + */ function create( item: InstanceType, options: EntityPutOptions & { return: 'output' }, ): Promise; + + /** + * Builds the PutItem command input for creating an item without executing it. + * + * @param item - The entity instance to create + * @param options - Configuration for the create operation with return type 'input' + * @returns The PutItemCommandInput object + */ function create(item: InstanceType, options: EntityPutOptions & { return: 'input' }): PutItemCommandInput; + /** + * Creates a new item in the table (fails if item already exists). + * + * @param item - The entity instance to create + * @param options - Optional configuration for the create operation + * @returns A promise that resolves to the entity instance, raw AWS response, or command input + */ function create( item: InstanceType, options?: EntityPutOptions, @@ -239,21 +500,65 @@ export default function EntityManager, E extends typeof En return put(item, { ...options, overwrite } as any); } + /** + * Deletes an item from the table by its primary key. + * + * @param primaryKey - The primary key of the item to delete + * @param options - Optional configuration for the delete operation + * @returns A promise that resolves to the deleted entity instance or null if not found + * + * @example + * ```typescript + * // Delete a user + * const deletedUser = await UserManager.delete({ id: 'user-123' }); + * + * // Delete with conditional expression + * const condition = UserManager.condition().attribute('status').eq('inactive'); + * const deletedUser = await UserManager.delete({ id: 'user-123' }, { condition }); + * + * // Delete and throw error if item doesn't exist + * const deletedUser = await UserManager.delete( + * { id: 'user-123' }, + * { throwErrorIfNotExists: true } + * ); + * ``` + */ function _delete( primaryKey: TablePrimaryKey, options?: EntityDeleteOptions & { return?: 'default' }, ): Promise | null>; + /** + * Deletes an item from the table, returning the raw AWS response. + * + * @param primaryKey - The primary key of the item to delete + * @param options - Configuration for the delete operation with return type 'output' + * @returns A promise that resolves to the raw DeleteItemCommandOutput + */ function _delete( primaryKey: TablePrimaryKey, options: EntityDeleteOptions & { return: 'output' }, ): Promise; + /** + * Builds the DeleteItem command input without executing it. + * + * @param primaryKey - The primary key of the item to delete + * @param options - Configuration for the delete operation with return type 'input' + * @returns The DeleteItemCommandInput object + */ function _delete( primaryKey: TablePrimaryKey, options: EntityDeleteOptions & { return: 'input' }, ): DeleteItemCommandInput; + /** + * Deletes an item from the table by its primary key. + * + * @param primaryKey - The primary key of the item to delete + * @param options - Optional configuration for the delete operation + * @returns A promise that resolves to the deleted entity instance, null, raw AWS response, or command input + */ function _delete( primaryKey: TablePrimaryKey, options?: EntityDeleteOptions, @@ -291,21 +596,70 @@ export default function EntityManager, E extends typeof En })(); } + /** + * Retrieves multiple items from the table by their primary keys. + * + * @param primaryKeys - Array of primary keys to retrieve + * @param options - Optional configuration for the batch get operation + * @returns A promise that resolves to an object containing retrieved items and unprocessed keys + * + * @example + * ```typescript + * // Get multiple users + * const result = await UserManager.batchGet([ + * { id: 'user-1' }, + * { id: 'user-2' }, + * { id: 'user-3' } + * ]); + * + * console.log(result.items); // Array of User instances + * console.log(result.unprocessedKeys); // Keys that couldn't be processed + * + * // Get with consistent read + * const result = await UserManager.batchGet(primaryKeys, { consistent: true }); + * + * // Get only specific attributes + * const result = await UserManager.batchGet(primaryKeys, { + * attributes: ['id', 'name'] + * }); + * ``` + */ function batchGet( primaryKeys: Array>, options?: EntityBatchGetOptions & { return?: 'default' }, ): Promise>; + /** + * Retrieves multiple items, returning the raw AWS response. + * + * @param primaryKeys - Array of primary keys to retrieve + * @param options - Configuration for the batch get operation with return type 'output' + * @returns A promise that resolves to the raw BatchGetItemCommandOutput + */ function batchGet( primaryKeys: Array>, options: EntityBatchGetOptions & { return: 'output' }, ): Promise; + /** + * Builds the BatchGetItem command input without executing it. + * + * @param primaryKeys - Array of primary keys to retrieve + * @param options - Configuration for the batch get operation with return type 'input' + * @returns The BatchGetItemCommandInput object + */ function batchGet( primaryKeys: Array>, options: EntityBatchGetOptions & { return: 'input' }, ): BatchGetItemCommandInput; + /** + * Retrieves multiple items from the table by their primary keys. + * + * @param primaryKeys - Array of primary keys to retrieve + * @param options - Optional configuration for the batch get operation + * @returns A promise that resolves to the batch get result, raw AWS response, or command input + */ function batchGet( primaryKeys: Array>, options?: EntityBatchGetOptions, @@ -353,21 +707,62 @@ export default function EntityManager, E extends typeof En })(); } + /** + * Puts multiple items in the table in a single batch operation. + * + * @param items - Array of entity instances to put + * @param options - Optional configuration for the batch put operation + * @returns A promise that resolves to an object containing processed items and unprocessed items + * + * @example + * ```typescript + * const users = [ + * new User({ id: 'user-1', name: 'John' }), + * new User({ id: 'user-2', name: 'Jane' }), + * new User({ id: 'user-3', name: 'Bob' }) + * ]; + * + * const result = await UserManager.batchPut(users); + * console.log(result.items); // Array of processed User instances + * console.log(result.unprocessedItems); // Items that couldn't be processed + * ``` + */ function batchPut( items: Array>, options?: EntityBatchPutOptions & { return?: 'default' }, ): Promise>; + /** + * Puts multiple items, returning the raw AWS response. + * + * @param items - Array of entity instances to put + * @param options - Configuration for the batch put operation with return type 'output' + * @returns A promise that resolves to the raw BatchWriteItemCommandOutput + */ function batchPut( items: Array>, options: EntityBatchPutOptions & { return: 'output' }, ): Promise; + /** + * Builds the BatchWriteItem command input for putting items without executing it. + * + * @param items - Array of entity instances to put + * @param options - Configuration for the batch put operation with return type 'input' + * @returns The BatchWriteItemCommandInput object + */ function batchPut( items: Array>, options: EntityBatchPutOptions & { return: 'input' }, ): BatchWriteItemCommandInput; + /** + * Puts multiple items in the table in a single batch operation. + * + * @param items - Array of entity instances to put + * @param options - Optional configuration for the batch put operation + * @returns A promise that resolves to the batch put result, raw AWS response, or command input + */ function batchPut( items: Array>, options?: EntityBatchPutOptions, @@ -415,21 +810,61 @@ export default function EntityManager, E extends typeof En })(); } + /** + * Deletes multiple items from the table by their primary keys in a single batch operation. + * + * @param primaryKeys - Array of primary keys to delete + * @param options - Optional configuration for the batch delete operation + * @returns A promise that resolves to an object containing unprocessed items + * + * @example + * ```typescript + * const primaryKeys = [ + * { id: 'user-1' }, + * { id: 'user-2' }, + * { id: 'user-3' } + * ]; + * + * const result = await UserManager.batchDelete(primaryKeys); + * console.log(result.unprocessedItems); // Keys that couldn't be processed + * ``` + */ function batchDelete( primaryKeys: Array>, options?: EntityBatchDeleteOptions & { return?: 'default' }, ): Promise>>; + /** + * Deletes multiple items, returning the raw AWS response. + * + * @param primaryKeys - Array of primary keys to delete + * @param options - Configuration for the batch delete operation with return type 'output' + * @returns A promise that resolves to the raw BatchWriteItemCommandOutput + */ function batchDelete( primaryKeys: Array>, options: EntityBatchDeleteOptions & { return: 'output' }, ): Promise; + /** + * Builds the BatchWriteItem command input for deleting items without executing it. + * + * @param primaryKeys - Array of primary keys to delete + * @param options - Configuration for the batch delete operation with return type 'input' + * @returns The BatchWriteItemCommandInput object + */ function batchDelete( primaryKeys: Array>, options: EntityBatchDeleteOptions & { return: 'input' }, ): BatchWriteItemCommandInput; + /** + * Deletes multiple items from the table by their primary keys in a single batch operation. + * + * @param primaryKeys - Array of primary keys to delete + * @param options - Optional configuration for the batch delete operation + * @returns A promise that resolves to the batch delete result, raw AWS response, or command input + */ function batchDelete( primaryKeys: Array>, options?: EntityBatchDeleteOptions, @@ -474,6 +909,31 @@ export default function EntityManager, E extends typeof En })(); } + /** + * Creates a transaction get operation for retrieving an item. + * + * This method creates a transaction get operation that can be used with + * Dynamode's transaction system. The operation is not executed immediately + * but must be passed to a transaction manager. + * + * @param primaryKey - The primary key of the item to retrieve + * @param options - Optional configuration for the transaction get operation + * @returns A TransactionGet object that can be used in a transaction + * + * @example + * ```typescript + * const getOperation = UserManager.transactionGet({ id: 'user-123' }); + * + * // Use in a transaction + * const result = await Dynamode.transaction.write([ + * getOperation, + * UserManager.transaction.put(newUser), + * UserManager.transaction.delete({ id: 'old-user' }) + * ]); + * ``` + * + * You can read more about transactions [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions). + */ function transactionGet( primaryKey: TablePrimaryKey, options?: EntityTransactionGetOptions>, @@ -494,6 +954,24 @@ export default function EntityManager, E extends typeof En return commandInput; } + /** + * Creates a transaction update operation for updating an item. + * + * @param primaryKey - The primary key of the item to update + * @param props - The update operations to perform + * @param options - Optional configuration for the transaction update operation + * @returns A TransactionUpdate object that can be used in a transaction + * + * @example + * ```typescript + * const updateOperation = UserManager.transactionUpdate( + * { id: 'user-123' }, + * { set: { name: 'Jane', status: 'active' } } + * ); + * ``` + * + * You can read more about transactions [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions). + */ function transactionUpdate( primaryKey: TablePrimaryKey, props: UpdateProps, @@ -522,6 +1000,20 @@ export default function EntityManager, E extends typeof En return commandInput; } + /** + * Creates a transaction put operation for putting an item. + * + * @param item - The entity instance to put + * @param options - Optional configuration for the transaction put operation + * @returns A TransactionPut object that can be used in a transaction + * + * @example + * ```typescript + * const putOperation = UserManager.transactionPut(new User({ id: 'user-123', name: 'John' })); + * ``` + * + * You can read more about transactions [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions). + */ function transactionPut(item: InstanceType, options?: EntityTransactionPutOptions): TransactionPut { const overwrite = options?.overwrite ?? true; const partitionKey = Dynamode.storage.getEntityMetadata(entity.name).partitionKey as EntityKey; @@ -547,11 +1039,41 @@ export default function EntityManager, E extends typeof En return commandInput; } + /** + * Creates a transaction create operation for creating an item (fails if item already exists). + * + * This is equivalent to calling `transactionPut()` with `overwrite: false`. + * + * @param item - The entity instance to create + * @param options - Optional configuration for the transaction create operation + * @returns A TransactionPut object that can be used in a transaction + * + * @example + * ```typescript + * const createOperation = UserManager.transactionCreate(new User({ id: 'user-123', name: 'John' })); + * ``` + * + * You can read more about transactions [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions). + */ function transactionCreate(item: InstanceType, options?: EntityTransactionPutOptions): TransactionPut { const overwrite = options?.overwrite ?? false; return transactionPut(item, { ...options, overwrite }); } + /** + * Creates a transaction delete operation for deleting an item. + * + * @param primaryKey - The primary key of the item to delete + * @param options - Optional configuration for the transaction delete operation + * @returns A TransactionDelete object that can be used in a transaction + * + * @example + * ```typescript + * const deleteOperation = UserManager.transactionDelete({ id: 'user-123' }); + * ``` + * + * You can read more about transactions [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions). + */ function transactionDelete( primaryKey: TablePrimaryKey, options?: EntityTransactionDeleteOptions, @@ -573,6 +1095,21 @@ export default function EntityManager, E extends typeof En return commandInput; } + /** + * Creates a transaction condition check operation. + * + * @param primaryKey - The primary key of the item to check + * @param conditionInstance - The condition to check + * @returns A TransactionCondition object that can be used in a transaction + * + * @example + * ```typescript + * const condition = UserManager.condition().attribute('status').eq('active'); + * const conditionCheck = UserManager.transactionCondition({ id: 'user-123' }, condition); + * ``` + * + * You can read more about transactions [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions). + */ function transactionCondition( primaryKey: TablePrimaryKey, conditionInstance: Condition, @@ -595,26 +1132,44 @@ export default function EntityManager, E extends typeof En } return { + /** Query builder for this entity */ query, + /** Scan builder for this entity */ scan, + /** Condition builder for this entity */ condition, + /** Get a single item by primary key */ get, + /** Update an item by primary key */ update, + /** Put (create or overwrite) an item */ put, + /** Create a new item (fails if exists) */ create, + /** Delete an item by primary key */ delete: _delete, + /** Get multiple items by primary keys */ batchGet, + /** Put multiple items */ batchPut, + /** Delete multiple items by primary keys */ batchDelete, + /** Transaction operations */ transaction: { + /** Create a transaction get operation */ get: transactionGet, + /** Create a transaction update operation */ update: transactionUpdate, + /** Create a transaction put operation */ put: transactionPut, + /** Create a transaction create operation */ create: transactionCreate, + /** Create a transaction delete operation */ delete: transactionDelete, + /** Create a transaction condition check operation */ condition: transactionCondition, }, }; diff --git a/lib/entity/index.ts b/lib/entity/index.ts index 01bdefb..cecc309 100644 --- a/lib/entity/index.ts +++ b/lib/entity/index.ts @@ -1,9 +1,67 @@ import Dynamode from '@lib/dynamode/index'; import { DYNAMODE_ENTITY } from '@lib/utils'; +/** + * Base class for all Dynamode entities. + * + * This class serves as the foundation for all entity classes in Dynamode. + * It automatically registers the entity with the Dynamode storage system + * and provides a unique identifier for each entity instance. + * + * @example + * ```typescript + * class User extends Entity { + * @attribute.partitionKey.string() + * id: string; + * + * @attribute.string() + * name: string; + * + * constructor(props: { id: string; name: string }) { + * super(); + * this.id = props.id; + * this.name = props.name; + * } + * } + * ``` + * + * @see {@link https://blazejkustra.github.io/dynamode/docs/getting_started/introduction} for more information + */ export default class Entity { + /** + * Unique identifier for the entity type. + * This is automatically set to the constructor's name. + * + * @readonly + * @example + * ```typescript + * const user = new User({ id: '1', name: 'John' }); + * console.log(user.dynamodeEntity); // "User" + * + * You can also use the `@entity.customName` decorator to change the name of the entity. + * @entity.customName('CustomName') + * ``` + */ public readonly dynamodeEntity!: string; + /** + * Creates a new Entity instance. + * + * @param args - Variable arguments (currently unused but reserved for future use) + * + * @example + * ```typescript + * class Product extends Entity { + * @attribute.partitionKey.string() + * id: string; + * + * constructor(props: { id: string }) { + * super(); // Always call super() first + * this.id = props.id; + * } + * } + * ``` + */ // eslint-disable-next-line @typescript-eslint/no-unused-vars, unused-imports/no-unused-vars constructor(...args: unknown[]) { this.dynamodeEntity = this.constructor.name; From d20310e52d1ebfb4fcffd03eb235b36a496bd71b Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 6 Sep 2025 16:59:09 +0200 Subject: [PATCH 2/6] Enhance documentation across the Dynamode library, adding detailed descriptions, examples, and usage notes for core components including the main entry point, module exports, condition builder, decorators, query and scan builders, and stream processing. This update aims to improve usability and understanding for developers integrating with DynamoDB. --- lib/condition/index.ts | 177 ++++++++++++++ lib/condition/types.ts | 16 ++ lib/decorators/index.ts | 71 ++++++ lib/decorators/types.ts | 9 + lib/dynamode/index.ts | 56 +++++ lib/dynamode/storage/types.ts | 87 ++++++- lib/entity/types.ts | 70 +++++- lib/index.ts | 43 ++++ lib/module.ts | 45 +++- lib/query/index.ts | 158 +++++++++++++ lib/query/types.ts | 25 ++ lib/scan/index.ts | 108 +++++++++ lib/scan/types.ts | 21 ++ lib/stream/index.ts | 96 +++++++- lib/stream/types.ts | 28 ++- lib/table/index.ts | 114 +++++++++- lib/table/types.ts | 38 +++- lib/transactionGet/types.ts | 26 +++ lib/transactionWrite/types.ts | 60 ++++- lib/utils/constants.ts | 418 ++++++++++++++++++++++++++++------ lib/utils/types.ts | 93 +++++++- 21 files changed, 1664 insertions(+), 95 deletions(-) diff --git a/lib/condition/index.ts b/lib/condition/index.ts index ccabc77..dc38503 100644 --- a/lib/condition/index.ts +++ b/lib/condition/index.ts @@ -4,16 +4,84 @@ import { transformValue } from '@lib/entity/helpers/transformValues'; import { EntityKey, EntityValue } from '@lib/entity/types'; import { BASE_OPERATOR, OPERATORS, Operators, ValidationError } from '@lib/utils'; +/** + * Condition builder for DynamoDB condition expressions. + * + * This class provides a fluent interface for building complex condition expressions + * that can be used in DynamoDB operations like PutItem, UpdateItem, and DeleteItem. + * It supports all DynamoDB condition operators and functions with type safety. + * + * @template E - The entity class type + * + * @example + * ```typescript + * // Basic conditions + * const condition = new Condition(User) + * .attribute('status').eq('active') + * .attribute('age').gt(18); + * + * // Complex conditions with grouping + * const condition = new Condition(User) + * .attribute('status').eq('active') + * .parenthesis( + * new Condition(User) + * .attribute('age').gt(18) + * .or + * .attribute('role').eq('admin') + * ); + * + * // Using with entity manager + * await UserManager.update( + * { id: 'user-123' }, + * { status: 'inactive' }, + * { condition } + * ); + * ``` + */ export default class Condition { + /** The entity class for type-safe attribute access */ protected entity: E; + /** The current logical operator (AND or OR) for chaining conditions */ protected logicalOperator: typeof BASE_OPERATOR.and | typeof BASE_OPERATOR.or = BASE_OPERATOR.and; + /** Array of operators that make up the condition expression */ protected operators: Operators; + /** + * Creates a new Condition instance for the specified entity. + * + * @param entity - The entity class to build conditions for + * + * @example + * ```typescript + * const condition = new Condition(User); + * ``` + */ constructor(entity: E) { this.entity = entity; this.operators = []; } + /** + * Starts building a condition for a specific attribute. + * + * This method returns an object with all available comparison operators + * and functions for the specified attribute, providing a fluent interface + * for building conditions. + * + * @template C - The condition instance type + * @template K - The attribute key type + * @param key - The attribute name to build a condition for + * @returns An object with comparison operators and functions + * + * @example + * ```typescript + * const condition = new Condition(User) + * .attribute('status').eq('active') + * .attribute('age').gt(18) + * .attribute('email').beginsWith('admin@') + * .attribute('tags').contains('premium'); + * ``` + */ public attribute>(this: C, key: K) { this.maybePushLogicalOperator(); @@ -137,6 +205,25 @@ export default class Condition { }; } + /** + * Wraps a condition in parentheses for grouping. + * + * @param condition - Optional condition to wrap in parentheses + * @returns The condition instance for chaining + * + * @example + * ```typescript + * const condition = new Condition(User) + * .attribute('status').eq('active') + * .and + * .parenthesis( + * new Condition(User) + * .attribute('age').gt(18) + * .or + * .attribute('role').eq('admin') + * ); + * ``` + */ public parenthesis(condition?: Condition): this { if (condition) { this.maybePushLogicalOperator(); @@ -145,10 +232,34 @@ export default class Condition { return this; } + /** + * Alias for parenthesis() method for grouping conditions. + * + * @param condition - Optional condition to wrap in parentheses + * @returns The condition instance for chaining + */ public group(condition?: Condition): this { return this.parenthesis(condition); } + /** + * Adds another condition to this condition. + * + * @param condition - The condition to add + * @returns The condition instance for chaining + * + * @example + * ```typescript + * const baseCondition = new Condition(User) + * .attribute('status').eq('active'); + * + * const finalCondition = baseCondition + * .condition( + * new Condition(User) + * .attribute('age').gt(18) + * ); + * ``` + */ public condition(condition?: Condition): this { if (condition) { this.maybePushLogicalOperator(); @@ -157,51 +268,108 @@ export default class Condition { return this; } + /** + * Sets the logical operator to AND for the next condition. + * + * @returns The condition instance for chaining + * + * @example + * ```typescript + * const condition = new Condition(User) + * .attribute('status').eq('active') + * .attribute('age').gt(18); + * ``` + */ public get and(): this { this.logicalOperator = BASE_OPERATOR.and; return this; } + /** + * Sets the logical operator to OR for the next condition. + * + * @returns The condition instance for chaining + * + * @example + * ```typescript + * const condition = new Condition(User) + * .attribute('status').eq('active') + * .or + * .attribute('role').eq('admin'); + * ``` + */ public get or(): this { this.logicalOperator = BASE_OPERATOR.or; return this; } + /** + * Adds an equality comparison operator. + * @protected + */ protected eq>(operators: Operators, key: K, value: EntityValue): this { operators.push(...OPERATORS.eq(key, transformValue(this.entity, key, value))); return this; } + /** + * Adds a not equal comparison operator. + * @protected + */ protected ne>(operators: Operators, key: K, value: EntityValue): this { operators.push(...OPERATORS.ne(key, transformValue(this.entity, key, value))); return this; } + /** + * Adds a less than comparison operator. + * @protected + */ protected lt>(operators: Operators, key: K, value: EntityValue): this { operators.push(...OPERATORS.lt(key, transformValue(this.entity, key, value))); return this; } + /** + * Adds a less than or equal comparison operator. + * @protected + */ protected le>(operators: Operators, key: K, value: EntityValue): this { operators.push(...OPERATORS.le(key, transformValue(this.entity, key, value))); return this; } + /** + * Adds a greater than comparison operator. + * @protected + */ protected gt>(operators: Operators, key: K, value: EntityValue): this { operators.push(...OPERATORS.gt(key, transformValue(this.entity, key, value))); return this; } + /** + * Adds a greater than or equal comparison operator. + * @protected + */ protected ge>(operators: Operators, key: K, value: EntityValue): this { operators.push(...OPERATORS.ge(key, transformValue(this.entity, key, value))); return this; } + /** + * Adds a begins_with function operator. + * @protected + */ protected beginsWith>(operators: Operators, key: K, value: EntityValue): this { operators.push(...OPERATORS.beginsWith(key, transformValue(this.entity, key, value))); return this; } + /** + * Adds a BETWEEN operator. + * @protected + */ protected between>( operators: Operators, key: K, @@ -214,6 +382,10 @@ export default class Condition { return this; } + /** + * Conditionally adds a logical operator between conditions. + * @protected + */ protected maybePushLogicalOperator(): void { if (this.operators.length > 0) { this.operators.push(BASE_OPERATOR.space, this.logicalOperator, BASE_OPERATOR.space); @@ -222,4 +394,9 @@ export default class Condition { } } +/** + * DynamoDB attribute types enumeration. + * + * @see {@link AttributeType} for the full enumeration + */ export { AttributeType }; diff --git a/lib/condition/types.ts b/lib/condition/types.ts index 36f6a0b..a36d7e7 100644 --- a/lib/condition/types.ts +++ b/lib/condition/types.ts @@ -1,12 +1,28 @@ +/** + * DynamoDB attribute types enumeration. + * + * These correspond to the attribute types used in DynamoDB operations + * and condition expressions. + */ export enum AttributeType { + /** String attribute type */ String = 'S', + /** String set attribute type */ StringSet = 'SS', + /** Number attribute type */ Number = 'N', + /** Number set attribute type */ NumberSet = 'NS', + /** Binary attribute type */ Binary = 'B', + /** Binary set attribute type */ BinarySet = 'BS', + /** Boolean attribute type */ Boolean = 'BOOL', + /** Null attribute type */ Null = 'NULL', + /** List attribute type */ List = 'L', + /** Map attribute type */ Map = 'M', } diff --git a/lib/decorators/index.ts b/lib/decorators/index.ts index f39bc2b..d0e926c 100644 --- a/lib/decorators/index.ts +++ b/lib/decorators/index.ts @@ -16,50 +16,121 @@ import { stringSortKey, } from '@lib/decorators/helpers/primaryKey'; +/** + * Attribute decorators for defining entity properties. + * + * These decorators are used to define the structure and behavior of entity properties + * in Dynamode. They specify the data type, key roles, and index configurations. + * + * @example + * ```typescript + * class User extends Entity { + * @attribute.partitionKey.string() + * id: string; + * + * @attribute.sortKey.number() + * createdAt: number; + * + * @attribute.string() + * name: string; + * + * @attribute.gsi.partitionKey.string({ indexName: 'NameIndex' }) + * nameIndex: string; + * + * @attribute.lsi.sortKey.string({ indexName: 'StatusIndex' }) + * status: string; + * } + * ``` + * + * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/entity/decorators} for more information + */ const attribute = { + /** String attribute decorators */ string, + /** Number attribute decorators */ number, + /** Boolean attribute decorators */ boolean, + /** Object attribute decorators */ object, + /** Array attribute decorators */ array, + /** Set attribute decorators */ set, + /** Map attribute decorators */ map, + /** Binary attribute decorators */ binary, + /** Date attribute decorators */ date: { + /** String-based date decorators */ string: stringDate, + /** Number-based date decorators */ number: numberDate, }, + /** Partition key decorators */ partitionKey: { + /** String partition key decorators */ string: stringPartitionKey, + /** Number partition key decorators */ number: numberPartitionKey, }, + /** Sort key decorators */ sortKey: { + /** String sort key decorators */ string: stringSortKey, + /** Number sort key decorators */ number: numberSortKey, }, + /** Global Secondary Index (GSI) decorators */ gsi: { + /** GSI partition key decorators */ partitionKey: { + /** String GSI partition key decorators */ string: stringGsiPartitionKey, + /** Number GSI partition key decorators */ number: numberGsiPartitionKey, }, + /** GSI sort key decorators */ sortKey: { + /** String GSI sort key decorators */ string: stringGsiSortKey, + /** Number GSI sort key decorators */ number: numberGsiSortKey, }, }, + /** Local Secondary Index (LSI) decorators */ lsi: { + /** LSI sort key decorators */ sortKey: { + /** String LSI sort key decorators */ string: stringLsiSortKey, + /** Number LSI sort key decorators */ number: numberLsiSortKey, }, }, + /** Prefix decorator for adding prefixes to attribute values */ prefix, + /** Suffix decorator for adding suffixes to attribute values */ suffix, }; +/** + * Entity decorators for defining entity-level configurations. + * + * @example + * ```typescript + * @entity.customName('CustomUser') + * class User extends Entity { + * @attribute.partitionKey.string() + * id: string; + * } + * ``` + */ const entity = { + /** Custom name decorator for entities */ customName, }; diff --git a/lib/decorators/types.ts b/lib/decorators/types.ts index 7733f39..67af543 100644 --- a/lib/decorators/types.ts +++ b/lib/decorators/types.ts @@ -1,8 +1,17 @@ +/** + * Options for prefix and suffix decorators. + */ export type PrefixSuffixOptions = { + /** Prefix to add to attribute values */ prefix?: string; + /** Suffix to add to attribute values */ suffix?: string; }; +/** + * Options for index decorators. + */ export type IndexDecoratorOptions = { + /** Name of the index */ indexName: string; }; diff --git a/lib/dynamode/index.ts b/lib/dynamode/index.ts index 6cbb024..713042b 100644 --- a/lib/dynamode/index.ts +++ b/lib/dynamode/index.ts @@ -3,14 +3,64 @@ import DDB, { DDBType } from '@lib/dynamode/ddb'; import separator, { SeparatorType } from '@lib/dynamode/separator'; import DynamodeStorage from '@lib/dynamode/storage'; +/** + * Main Dynamode class that provides access to all core functionality. + * + * This class serves as the central hub for Dynamode operations, providing + * access to DynamoDB client, storage management, data conversion, and + * separator utilities. It follows a singleton pattern with a default instance. + * + * @example + * ```typescript + * import Dynamode from 'dynamode'; + * + * // Configure Dynamode + * Dynamode.configure({ + * region: 'us-east-1', + * endpoint: 'http://localhost:8000' // for local development + * }); + * + * // Access DynamoDB client + * const ddb = Dynamode.ddb.get(); + * + * // Access storage for entity management + * const entityClass = Dynamode.storage.getEntityClass('User'); + * + * // Use converter for data transformation + * const converted = Dynamode.converter.toDynamo({ id: '123', name: 'John' }); + * + * // Use separator for key operations + * const key = Dynamode.separator.join(['user', '123']); + * ``` + */ class Dynamode { + /** Default singleton instance of Dynamode */ static default: Dynamode = new Dynamode(); + /** DynamoDB client wrapper for AWS SDK operations */ public ddb: DDBType; + /** Storage manager for entity and table metadata */ public storage: DynamodeStorage; + /** Data converter for DynamoDB attribute value transformations */ public converter: typeof converter; + /** Separator utility for key operations and data separation */ public separator: SeparatorType; + /** + * Creates a new Dynamode instance. + * + * Initializes all core components including the DynamoDB client, + * storage manager, converter, and separator utilities. + * + * @example + * ```typescript + * // Create a new instance (usually not needed) + * const dynamode = new Dynamode(); + * + * // Use the default singleton instance (recommended) + * const dynamode = Dynamode.default; + * ``` + */ constructor() { this.ddb = DDB(); this.storage = new DynamodeStorage(); @@ -19,4 +69,10 @@ class Dynamode { } } +/** + * Default Dynamode instance. + * + * This is the singleton instance that should be used throughout + * the application for all Dynamode operations. + */ export default Dynamode.default; diff --git a/lib/dynamode/storage/types.ts b/lib/dynamode/storage/types.ts index 40e35b5..e458e74 100644 --- a/lib/dynamode/storage/types.ts +++ b/lib/dynamode/storage/types.ts @@ -1,9 +1,23 @@ import Entity from '@lib/entity'; import { Metadata } from '@lib/table/types'; +/** + * Storage and metadata types for Dynamode. + */ + +/** + * Attribute types that can be used for indexes. + */ export type IndexAttributeType = StringConstructor | NumberConstructor; + +/** + * Attribute types that can be used for timestamps. + */ export type TimestampAttributeType = StringConstructor | NumberConstructor; +/** + * All supported attribute types in Dynamode. + */ export type AttributeType = | StringConstructor | NumberConstructor @@ -14,64 +28,133 @@ export type AttributeType = | MapConstructor | Uint8ArrayConstructor; +/** + * Roles that attributes can have in Dynamode. + */ export type AttributeRole = 'partitionKey' | 'sortKey' | 'index' | 'date' | 'attribute' | 'dynamodeEntity'; + +/** + * Roles that attributes can have in secondary indexes. + */ export type AttributeIndexRole = 'gsiPartitionKey' | 'gsiSortKey' | 'lsiSortKey'; +/** + * Base attribute metadata structure. + */ type BaseAttributeMetadata = { + /** Name of the property */ propertyName: string; + /** Type of the attribute */ type: AttributeType; + /** Optional prefix for the attribute value */ prefix?: string; + /** Optional suffix for the attribute value */ suffix?: string; }; -export type IndexMetadata = { name: string; role: AttributeIndexRole }; +/** + * Metadata for secondary index attributes. + */ +export type IndexMetadata = { + /** Name of the index */ + name: string; + /** Role of the attribute in the index */ + role: AttributeIndexRole; +}; +/** + * Complete attribute metadata including role and indexes. + */ export type AttributeMetadata = BaseAttributeMetadata & { + /** Role of the attribute */ role: AttributeRole; + /** Associated indexes */ indexes?: IndexMetadata[]; }; +/** + * Metadata for attributes that are part of indexes. + */ export type AttributeIndexMetadata = BaseAttributeMetadata & { + /** Role is always 'index' for this type */ role: 'index'; + /** Required indexes for this attribute */ indexes: IndexMetadata[]; }; +/** + * Collection of attribute metadata for an entity. + */ export type AttributesMetadata = { [attributeName: string]: AttributeMetadata; }; +/** + * Complete entity metadata including table and attributes. + */ export type EntityMetadata = { + /** Name of the table */ tableName: string; + /** Entity class */ entity: typeof Entity; + /** Attributes metadata */ attributes: AttributesMetadata; }; +/** + * Collection of entity metadata. + */ export type EntitiesMetadata = { [entityName: string]: EntityMetadata; }; +/** + * Table metadata including entity and configuration. + */ export type TableMetadata = { + /** Table entity class */ tableEntity: typeof Entity; + /** Table configuration metadata */ metadata: Metadata; }; +/** + * Collection of table metadata. + */ export type TablesMetadata = { [tableName: string]: TableMetadata; }; -// helpers +/** + * Helper types for validation operations. + */ +/** + * Type for validating metadata attributes. + */ export type ValidateMetadataAttribute = { + /** Name of the entity */ entityName: string; + /** Name of the attribute */ name: string; + /** Attributes metadata */ attributes: AttributesMetadata; + /** Valid roles for the attribute */ validRoles: AttributeRole[]; + /** Optional index name */ indexName?: string; }; +/** + * Type for validating decorated attributes. + */ export type ValidateDecoratedAttribute = { + /** Name of the entity */ entityName: string; + /** Name of the attribute */ name: string; + /** Attribute metadata */ attribute: AttributeMetadata; + /** Table metadata */ metadata: Metadata; }; diff --git a/lib/entity/types.ts b/lib/entity/types.ts index e29bc3c..aeaa88a 100644 --- a/lib/entity/types.ts +++ b/lib/entity/types.ts @@ -18,36 +18,83 @@ import Entity from '@lib/entity'; import { Metadata, TablePrimaryKey } from '@lib/table/types'; import { AttributeNames, AttributeValues, FlattenObject } from '@lib/utils'; -// Return types - +/** + * Return type options for DynamoDB operations. + */ export type ReturnOption = 'default' | 'input' | 'output'; + +/** + * Return values options for DynamoDB operations. + */ export type ReturnValues = 'none' | 'allOld' | 'allNew' | 'updatedOld' | 'updatedNew'; + +/** + * Limited return values options for DynamoDB operations. + */ export type ReturnValuesLimited = 'none' | 'allOld'; -// Entity Props +/** + * Entity property types and utilities. + */ +/** + * Properties of an entity instance. + * + * @template E - The entity class type + */ export type EntityProperties = Partial>>; + +/** + * Key names of an entity. + * + * @template E - The entity class type + */ export type EntityKey = keyof EntityProperties extends string ? keyof EntityProperties : never; -export type EntityValue> = FlattenObject>[K]; -// entityManager.get +/** + * Value type for a specific entity key. + * + * @template E - The entity class type + * @template K - The key type + */ +export type EntityValue> = FlattenObject>[K]; +/** + * Options for entity get operations. + * + * @template E - The entity class type + */ export type EntityGetOptions = { + /** Additional DynamoDB input parameters */ extraInput?: Partial; + /** Return type option */ return?: ReturnOption; + /** Specific attributes to retrieve */ attributes?: Array>; + /** Whether to use consistent read */ consistent?: boolean; }; +/** + * Built projection expression for get operations. + */ export type BuildGetProjectionExpression = { + /** Attribute names mapping */ attributeNames?: AttributeNames; + /** Projection expression string */ projectionExpression?: string; }; // entityManager.update +/** + * Utility type to pick properties of a specific type. + * + * @template T - The object type + * @template Value - The value type to filter by + */ export type PickByType = Omit< { [P in keyof T as T[P] extends Value | undefined ? P : never]: T[P]; @@ -59,14 +106,27 @@ export type PickByType = Omit< : U | Record : never; +/** + * Update operations for entity update operations. + * + * @template E - The entity class type + */ export type UpdateProps = RequireAtLeastOne<{ + /** ADD operation for numbers and sets */ add?: PickByType, number | Set>; + /** SET operation for setting attribute values */ set?: EntityProperties; + /** SET operation only if attribute doesn't exist */ setIfNotExists?: EntityProperties; + /** LIST_APPEND operation for arrays */ listAppend?: PickByType, Array>; + /** Increment operation for numbers */ increment?: PickByType, number>; + /** Decrement operation for numbers */ decrement?: PickByType, number>; + /** DELETE operation for sets */ delete?: PickByType, Set>; + /** REMOVE operation for removing attributes */ remove?: Array>; }>; diff --git a/lib/index.ts b/lib/index.ts index 80f26cc..351f20f 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,3 +1,46 @@ +/** + * @fileoverview Main entry point for the Dynamode library. + * + * Dynamode is a modeling tool for Amazon's DynamoDB that provides a straightforward, + * object-oriented class-based solution to model your data. It includes strongly typed + * classes and methods, query and scan builders, and much more. + * + * @example + * ```typescript + * import Dynamode, { Entity, TableManager, attribute } from 'dynamode'; + * + * // Configure Dynamode + * Dynamode.configure({ + * region: 'us-east-1', + * endpoint: 'http://localhost:8000' // for local development + * }); + * + * // Define an entity + * class User extends Entity { + * @attribute.partitionKey.string() + * id: string; + * + * @attribute.string() + * name: string; + * } + * + * // Create table manager + * const UserTableManager = new TableManager(User, { + * tableName: 'users-table', + * partitionKey: 'id' + * }); + * + * // Create table and get entity manager + * await UserTableManager.createTable(); + * const UserManager = UserTableManager.entityManager(); + * + * // Use the entity manager + * const user = await UserManager.put(new User({ id: '1', name: 'John' })); + * ``` + * + * @see {@link https://blazejkustra.github.io/dynamode/docs/getting_started/introduction} for more information + */ + import * as dynamode from '@lib/module'; export default dynamode; diff --git a/lib/module.ts b/lib/module.ts index abab036..85f52f6 100644 --- a/lib/module.ts +++ b/lib/module.ts @@ -19,34 +19,61 @@ declare global { interface File {} } +/** + * Main Dynamode module exports. + * + * This module provides all the core functionality of Dynamode, including + * entity management, table operations, querying, scanning, and transactions. + * + * @example + * ```typescript + * import { Entity, TableManager, attribute } from 'dynamode'; + * + * class User extends Entity { + * @attribute.partitionKey.string() + * id: string; + * + * @attribute.string() + * name: string; + * } + * + * const UserTableManager = new TableManager(User, { + * tableName: 'users-table', + * partitionKey: 'id' + * }); + * ``` + */ export { - //Condition + /** Condition builder for building conditional expressions */ Condition, + /** Attribute type definitions */ AttributeType, - //decorators + /** Attribute decorators for defining entity properties */ attribute, + /** Entity decorators for entity-level configurations */ entity, - //Entity + /** Base Entity class for all Dynamode entities */ Entity, - //table manager + /** Table manager for managing DynamoDB tables */ TableManager, - //Query + /** Query builder for DynamoDB query operations */ Query, - //Scan + /** Scan builder for DynamoDB scan operations */ Scan, - //Dynamode + /** Main Dynamode instance for configuration and global operations */ Dynamode, - //transactions + /** Transaction get operations */ transactionGet, + /** Transaction write operations */ transactionWrite, - //Stream + /** Stream operations for DynamoDB streams */ Stream, }; diff --git a/lib/query/index.ts b/lib/query/index.ts index 9631d07..14dbc82 100644 --- a/lib/query/index.ts +++ b/lib/query/index.ts @@ -17,19 +17,110 @@ import { ValidationError, } from '@lib/utils'; +/** + * Query builder for DynamoDB query operations. + * + * The Query class provides a fluent interface for building and executing + * DynamoDB query operations. It supports querying by partition key and + * sort key with various comparison operators, and can work with both + * primary keys and secondary indexes. + * + * @example + * ```typescript + * // Query by partition key only + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .run(); + * + * // Query by partition key and sort key + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .sortKey('createdAt').gt(new Date('2023-01-01')) + * .run(); + * + * // Query with filter expression + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .attribute('status').eq('active') + * .run(); + * + * // Query with index + * const users = await UserManager.query() + * .indexName('StatusIndex') + * .partitionKey('status').eq('active') + * .sortKey('createdAt').between(startDate, endDate) + * .run(); + * ``` + * + * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/query} for more information + */ export default class Query, E extends typeof Entity> extends RetrieverBase { + /** The DynamoDB QueryInput object */ protected declare input: QueryInput; + /** Key condition operators for building key condition expressions */ protected keyOperators: Operators = []; + /** Metadata for the partition key attribute */ protected partitionKeyMetadata?: AttributeMetadata; + /** Metadata for the sort key attribute */ protected sortKeyMetadata?: AttributeMetadata; + /** + * Creates a new Query instance. + * + * @param entity - The entity class to query + */ constructor(entity: E) { super(entity); } + /** + * Executes the query operation. + * + * @param options - Optional configuration for the query execution + * @returns A promise that resolves to the query results + * + * @example + * ```typescript + * // Execute query and get all results + * const result = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .run(); + * + * // Execute query with pagination + * const result = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .run({ all: false, max: 10 }); + * + * // Execute query with delay between pages + * const result = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .run({ all: true, delay: 100 }); + * ``` + */ public run(options?: QueryRunOptions & { return?: 'default' }): Promise>; + + /** + * Executes the query operation, returning the raw AWS response. + * + * @param options - Configuration for the query execution with return type 'output' + * @returns A promise that resolves to the raw QueryCommandOutput + */ public run(options: QueryRunOptions & { return: 'output' }): Promise; + + /** + * Builds the Query command input without executing it. + * + * @param options - Configuration for the query execution with return type 'input' + * @returns The QueryInput object + */ public run(options: QueryRunOptions & { return: 'input' }): QueryInput; + + /** + * Executes the query operation. + * + * @param options - Optional configuration for the query execution + * @returns A promise that resolves to the query results, raw AWS response, or command input + */ public run(options?: QueryRunOptions): Promise | QueryCommandOutput> | QueryInput { this.setAssociatedIndexName(); this.buildQueryInput(options?.extraInput); @@ -76,34 +167,101 @@ export default class Query, E extends typeof Entity> exten })(); } + /** + * Specifies the partition key for the query. + * + * @template Q - The query instance type + * @template K - The partition key type + * @param key - The partition key attribute name + * @returns An object with comparison operators for the partition key + * + * @example + * ```typescript + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .run(); + * ``` + */ public partitionKey, K extends EntityKey & TablePartitionKeys>(this: Q, key: K) { this.maybePushKeyLogicalOperator(); const attributes = Dynamode.storage.getEntityAttributes(this.entity.name); this.partitionKeyMetadata = attributes[key as string]; return { + /** Equal comparison operator */ eq: (value: EntityValue): Q => this.eq(this.keyOperators, key, value), }; } + /** + * Specifies the sort key for the query. + * + * @template Q - The query instance type + * @template K - The sort key type + * @param key - The sort key attribute name + * @returns An object with comparison operators for the sort key + * + * @example + * ```typescript + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .sortKey('createdAt').gt(new Date('2023-01-01')) + * .run(); + * + * // Using between operator + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .sortKey('createdAt').between(startDate, endDate) + * .run(); + * ``` + */ public sortKey, K extends EntityKey & TableSortKeys>(this: Q, key: K) { this.maybePushKeyLogicalOperator(); const attributes = Dynamode.storage.getEntityAttributes(this.entity.name); this.sortKeyMetadata = attributes[key as string]; return { + /** Equal comparison operator */ eq: (value: EntityValue): Q => this.eq(this.keyOperators, key, value), + /** Not equal comparison operator */ ne: (value: EntityValue): Q => this.ne(this.keyOperators, key, value), + /** Less than comparison operator */ lt: (value: EntityValue): Q => this.lt(this.keyOperators, key, value), + /** Less than or equal comparison operator */ le: (value: EntityValue): Q => this.le(this.keyOperators, key, value), + /** Greater than comparison operator */ gt: (value: EntityValue): Q => this.gt(this.keyOperators, key, value), + /** Greater than or equal comparison operator */ ge: (value: EntityValue): Q => this.ge(this.keyOperators, key, value), + /** Begins with comparison operator (for string values) */ beginsWith: (value: EntityValue): Q => this.beginsWith(this.keyOperators, key, value), + /** Between comparison operator */ between: (value1: EntityValue, value2: EntityValue): Q => this.between(this.keyOperators, key, value1, value2), }; } + /** + * Sets the sort order for the query results. + * + * @param order - The sort order ('ascending' or 'descending') + * @returns The query instance for method chaining + * + * @example + * ```typescript + * // Sort in ascending order (default) + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .sort('ascending') + * .run(); + * + * // Sort in descending order + * const users = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .sort('descending') + * .run(); + * ``` + */ public sort(order: 'ascending' | 'descending'): this { this.input.ScanIndexForward = order === 'ascending'; return this; diff --git a/lib/query/types.ts b/lib/query/types.ts index 539ef27..0ab22f3 100644 --- a/lib/query/types.ts +++ b/lib/query/types.ts @@ -4,24 +4,49 @@ import type { ReturnOption } from '@lib/entity/types'; import { Metadata, TableRetrieverLastKey } from '@lib/table/types'; import { AttributeNames, AttributeValues } from '@lib/utils'; +/** + * Options for query operation execution. + */ export type QueryRunOptions = { + /** Additional DynamoDB query input parameters */ extraInput?: Partial; + /** Return type option */ return?: ReturnOption; + /** Whether to fetch all results (pagination) */ all?: boolean; + /** Delay between paginated requests in milliseconds */ delay?: number; + /** Maximum number of items to return */ max?: number; }; +/** + * Output from query operation execution. + * + * @template M - The metadata type for the table + * @template E - The entity class type + */ export type QueryRunOutput, E extends typeof Entity> = { + /** Array of entity instances returned from the query */ items: Array>; + /** Number of items returned */ count: number; + /** Number of items scanned */ scannedCount: number; + /** Last evaluated key for pagination */ lastKey?: TableRetrieverLastKey; }; +/** + * Built query condition expression components. + */ export type BuildQueryConditionExpression = { + /** Attribute names mapping */ attributeNames: AttributeNames; + /** Attribute values mapping */ attributeValues: AttributeValues; + /** Filter expression string */ conditionExpression: string; + /** Key condition expression string */ keyConditionExpression: string; }; diff --git a/lib/scan/index.ts b/lib/scan/index.ts index f532713..7b2c429 100644 --- a/lib/scan/index.ts +++ b/lib/scan/index.ts @@ -7,16 +7,94 @@ import type { ScanRunOptions, ScanRunOutput } from '@lib/scan/types'; import { Metadata } from '@lib/table/types'; import { ExpressionBuilder, isNotEmptyString } from '@lib/utils'; +/** + * Scan builder for DynamoDB scan operations. + * + * The Scan class provides a fluent interface for building and executing + * DynamoDB scan operations. Scans examine every item in the table or index + * and can be filtered using filter expressions. + * + * ⚠️ **Warning**: Scan operations are expensive and slow. They examine every + * item in the table or index, which can consume significant read capacity. + * Consider using Query operations instead when possible. + * + * @example + * ```typescript + * // Scan all items with a filter + * const users = await UserManager.scan() + * .attribute('status').eq('active') + * .run(); + * + * // Scan with pagination + * const users = await UserManager.scan() + * .attribute('age').gt(18) + * .run({ all: false, max: 100 }); + * + * // Scan with parallel segments + * const users = await UserManager.scan() + * .segment(0) + * .totalSegments(4) + * .run(); + * ``` + * + * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/scan} for more information + */ export default class Scan, E extends typeof Entity> extends RetrieverBase { + /** The DynamoDB ScanInput object */ protected declare input: ScanInput; + /** + * Creates a new Scan instance. + * + * @param entity - The entity class to scan + */ constructor(entity: E) { super(entity); } + /** + * Executes the scan operation. + * + * @param options - Optional configuration for the scan execution + * @returns A promise that resolves to the scan results + * + * @example + * ```typescript + * // Execute scan and get all results + * const result = await UserManager.scan() + * .attribute('status').eq('active') + * .run(); + * + * // Execute scan with pagination + * const result = await UserManager.scan() + * .attribute('age').gt(18) + * .run({ all: false, max: 100 }); + * ``` + */ public run(options?: ScanRunOptions & { return?: 'default' }): Promise>; + + /** + * Executes the scan operation, returning the raw AWS response. + * + * @param options - Configuration for the scan execution with return type 'output' + * @returns A promise that resolves to the raw ScanCommandOutput + */ public run(options: ScanRunOptions & { return: 'output' }): Promise; + + /** + * Builds the Scan command input without executing it. + * + * @param options - Configuration for the scan execution with return type 'input' + * @returns The ScanInput object + */ public run(options: ScanRunOptions & { return: 'input' }): ScanInput; + + /** + * Executes the scan operation. + * + * @param options - Optional configuration for the scan execution + * @returns A promise that resolves to the scan results, raw AWS response, or command input + */ public run(options?: ScanRunOptions): Promise | ScanCommandOutput> | ScanInput { this.buildScanInput(options?.extraInput); @@ -44,11 +122,41 @@ export default class Scan, E extends typeof Entity> extend })(); } + /** + * Sets the segment number for parallel scan operations. + * + * @param value - The segment number (0-based) + * @returns The scan instance for method chaining + * + * @example + * ```typescript + * // Use segment 0 of 4 total segments + * const users = await UserManager.scan() + * .segment(0) + * .totalSegments(4) + * .run(); + * ``` + */ public segment(value: number) { this.input.Segment = value; return this; } + /** + * Sets the total number of segments for parallel scan operations. + * + * @param value - The total number of segments + * @returns The scan instance for method chaining + * + * @example + * ```typescript + * // Use 4 parallel segments + * const users = await UserManager.scan() + * .segment(0) + * .totalSegments(4) + * .run(); + * ``` + */ public totalSegments(value: number) { this.input.TotalSegments = value; return this; diff --git a/lib/scan/types.ts b/lib/scan/types.ts index cfe4168..acd375a 100644 --- a/lib/scan/types.ts +++ b/lib/scan/types.ts @@ -4,20 +4,41 @@ import type { ReturnOption } from '@lib/entity/types'; import { Metadata, TableRetrieverLastKey } from '@lib/table/types'; import { AttributeNames, AttributeValues } from '@lib/utils'; +/** + * Options for scan operation execution. + */ export type ScanRunOptions = { + /** Additional DynamoDB scan input parameters */ extraInput?: Partial; + /** Return type option */ return?: ReturnOption; }; +/** + * Output from scan operation execution. + * + * @template M - The metadata type for the table + * @template E - The entity class type + */ export type ScanRunOutput, E extends typeof Entity> = { + /** Array of entity instances returned from the scan */ items: Array>; + /** Number of items returned */ count: number; + /** Number of items scanned */ scannedCount: number; + /** Last evaluated key for pagination */ lastKey?: TableRetrieverLastKey; }; +/** + * Built scan condition expression components. + */ export type BuildScanConditionExpression = { + /** Attribute names mapping */ attributeNames: AttributeNames; + /** Attribute values mapping */ attributeValues: AttributeValues; + /** Filter expression string */ conditionExpression: string; }; diff --git a/lib/stream/index.ts b/lib/stream/index.ts index 1d45393..6ebe38b 100644 --- a/lib/stream/index.ts +++ b/lib/stream/index.ts @@ -5,15 +5,82 @@ import { AttributeValues, DynamodeStreamError, fromDynamo } from '@lib/utils'; import { DynamoDBRecord } from './types'; +/** + * Stream class for processing DynamoDB stream records. + * + * This class provides a convenient way to process DynamoDB stream records + * and convert them into Dynamode entity instances. It handles different + * stream view types and operations (INSERT, MODIFY, REMOVE). + * + * @template E - The entity class type + * + * @example + * ```typescript + * // In an AWS Lambda function + * export const handler = async (event: any) => { + * for (const record of event.Records) { + * const stream = new Stream(record); + * + * if (stream.operation === 'insert') { + * console.log('New item:', stream.newImage); + * } else if (stream.operation === 'modify') { + * console.log('Old item:', stream.oldImage); + * console.log('New item:', stream.newImage); + * } else if (stream.operation === 'remove') { + * console.log('Removed item:', stream.oldImage); + * } + * + * // Type-safe entity checking + * if (stream.isEntity(User)) { + * // stream is now typed as Stream + * console.log('User entity:', stream.newImage?.name); + * } + * } + * }; + * ``` + * + * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/stream} for more information + */ export default class Stream { + /** The type of stream data available */ streamType: 'newImage' | 'oldImage' | 'both'; + /** The operation that triggered the stream record */ operation: 'insert' | 'modify' | 'remove'; + /** The old image of the item (available for MODIFY and REMOVE operations) */ oldImage?: InstanceType; + /** The new image of the item (available for INSERT and MODIFY operations) */ newImage?: InstanceType; - // Dynamode entity class + /** The Dynamode entity class for this stream record */ entity: E; + /** + * Creates a new Stream instance from a DynamoDB stream record. + * + * @param record - The DynamoDB stream record containing event information + * @throws {DynamodeStreamError} When the operation is invalid + * @throws {DynamodeStreamError} When the record is invalid + * @throws {DynamodeStreamError} When the stream type is KEYS_ONLY (not supported) + * @throws {DynamodeStreamError} When the stream type is invalid + * @throws {DynamodeStreamError} When the processed item is not a Dynamode entity + * + * @example + * ```typescript + * // In AWS Lambda handler + * export const handler = async (event: any) => { + * for (const record of event.Records) { + * try { + * const stream = new Stream(record); + * // Process the stream... + * } catch (error) { + * if (error instanceof DynamodeStreamError) { + * console.error('Stream processing error:', error.message); + * } + * } + * } + * }; + * ``` + */ constructor({ dynamodb: record, eventName }: DynamoDBRecord) { switch (eventName) { case 'INSERT': @@ -66,6 +133,33 @@ export default class Stream { } } + /** + * Type guard to check if the stream record is for a specific entity type. + * + * This method provides type-safe entity checking and narrows the type + * of the stream instance to the specified entity type. + * + * @template TargetEntity - The target entity class type + * @param entity - The entity class to check against + * @returns True if the stream record is for the specified entity type + * + * @example + * ```typescript + * const stream = new Stream(record); + * + * if (stream.isEntity(User)) { + * // stream is now typed as Stream + * console.log('User name:', stream.newImage?.name); + * console.log('User email:', stream.newImage?.email); + * } + * + * if (stream.isEntity(Product)) { + * // stream is now typed as Stream + * console.log('Product title:', stream.newImage?.title); + * console.log('Product price:', stream.newImage?.price); + * } + * ``` + */ isEntity(entity: TargetEntity): this is Stream { return this.entity === entity; } diff --git a/lib/stream/types.ts b/lib/stream/types.ts index 265b048..5d3b7d9 100644 --- a/lib/stream/types.ts +++ b/lib/stream/types.ts @@ -1,26 +1,52 @@ import type { AttributeValue } from '@aws-sdk/client-dynamodb'; -// For compatibility with aws lambda: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/aws-lambda/trigger/dynamodb-stream.d.ts +/** + * Attribute value type for AWS Lambda compatibility. + * + * This type is used for compatibility with AWS Lambda DynamoDB stream triggers. + * @see {@link https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/aws-lambda/trigger/dynamodb-stream.d.ts} + */ type AttributeValueAWSLambda = { + /** Binary attribute */ B?: string | undefined; + /** Binary set attribute */ BS?: string[] | undefined; + /** Boolean attribute */ BOOL?: boolean | undefined; + /** List attribute */ L?: AttributeValueAWSLambda[] | undefined; + /** Map attribute */ M?: { [id: string]: AttributeValueAWSLambda } | undefined; + /** Number attribute */ N?: string | undefined; + /** Number set attribute */ NS?: string[] | undefined; + /** Null attribute */ NULL?: boolean | undefined; + /** String attribute */ S?: string | undefined; + /** String set attribute */ SS?: string[] | undefined; }; +/** + * DynamoDB stream payload containing item images. + */ export type StreamPayload = { + /** New image of the item after the change */ NewImage?: Record | undefined; + /** Old image of the item before the change */ OldImage?: Record | undefined; + /** Type of stream view */ StreamViewType?: 'KEYS_ONLY' | 'NEW_IMAGE' | 'OLD_IMAGE' | 'NEW_AND_OLD_IMAGES' | undefined; }; +/** + * DynamoDB stream record containing event information. + */ export type DynamoDBRecord = { + /** Name of the event that triggered the stream record */ eventName?: 'INSERT' | 'MODIFY' | 'REMOVE' | undefined; + /** DynamoDB stream payload */ dynamodb?: StreamPayload | undefined; }; diff --git a/lib/table/index.ts b/lib/table/index.ts index 38ba9c9..2107a3e 100644 --- a/lib/table/index.ts +++ b/lib/table/index.ts @@ -30,10 +30,78 @@ import { isNotEmptyArray, Narrow, ValidationError } from '@lib/utils'; import { getTableGlobalSecondaryIndexes, getTableLocalSecondaryIndexes } from './helpers/indexes'; +/** + * Manages DynamoDB table operations and provides entity managers. + * + * The TableManager is responsible for creating, deleting, and managing DynamoDB tables, + * as well as providing entity managers for performing CRUD operations on entities + * within those tables. + * + * @example + * ```typescript + * class User extends Entity { + * @attribute.partitionKey.string() + * id: string; + * + * @attribute.string() + * name: string; + * } + * + * const UserTableManager = new TableManager(User, { + * tableName: 'users-table', + * partitionKey: 'id', + * indexes: { + * NameIndex: { + * partitionKey: 'name' + * } + * } + * }); + * + * // Create the table + * await UserTableManager.createTable(); + * + * // Get an entity manager + * const UserManager = UserTableManager.entityManager(); + * ``` + * + * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/managers/tableManager} for more information + */ export default class TableManager, TE extends typeof Entity> { + /** + * The table metadata configuration. + * + * @readonly + */ public tableMetadata: M; + + /** + * The base entity class for this table. + * + * @readonly + */ public tableEntity: TE; + /** + * Creates a new TableManager instance. + * + * @param tableEntity - The base entity class for the table + * @param tableMetadata - The table configuration metadata + * + * @example + * ```typescript + * const tableManager = new TableManager(User, { + * tableName: 'users-table', + * partitionKey: 'id', + * sortKey: 'createdAt', + * indexes: { + * StatusIndex: { + * partitionKey: 'status', + * sortKey: 'createdAt' + * } + * } + * }); + * ``` + */ constructor(tableEntity: TE, tableMetadata: Narrow) { const metadata: M = tableMetadata as M; @@ -45,8 +113,18 @@ export default class TableManager, TE extends typeof Enti this.tableEntity = tableEntity; } + /** + * Creates an entity manager for the base table entity. + * + * @returns An EntityManager instance for the base table entity + * + * @example + * ```typescript + * const UserManager = UserTableManager.entityManager(); + * const user = await UserManager.get({ id: 'user-123' }); + * ``` + */ public entityManager(): ReturnType>; - public entityManager(entity: E): ReturnType>; public entityManager( entity?: E, ): ReturnType> | ReturnType> { @@ -242,9 +320,43 @@ export default class TableManager, TE extends typeof Enti })(); } + /** + * Validates that the existing DynamoDB table matches the table metadata configuration. + * + * @param options - Optional configuration for table validation + * @returns A promise that resolves to table data + * @throws {ValidationError} When the table structure doesn't match the metadata + * + * @example + * ```typescript + * // Validate the table structure + * const tableData = await UserTableManager.validateTable(); + * ``` + */ public validateTable(options?: TableValidateOptions & { return?: 'default' }): Promise; + + /** + * Validates the table, returning the raw AWS response. + * + * @param options - Configuration for table validation with return type 'output' + * @returns A promise that resolves to the raw DescribeTableCommandOutput + */ public validateTable(options: TableValidateOptions & { return: 'output' }): Promise; + + /** + * Builds the DescribeTable command input without executing it. + * + * @param options - Configuration for table validation with return type 'input' + * @returns The DescribeTableCommandInput object + */ public validateTable(options: TableValidateOptions & { return: 'input' }): DescribeTableCommandInput; + + /** + * Validates that the existing DynamoDB table matches the table metadata configuration. + * + * @param options - Optional configuration for table validation + * @returns A promise that resolves to table data, raw AWS response, or command input + */ public validateTable( options?: TableValidateOptions, ): Promise | DescribeTableCommandInput { diff --git a/lib/table/types.ts b/lib/table/types.ts index af9d124..38c3f6e 100644 --- a/lib/table/types.ts +++ b/lib/table/types.ts @@ -9,32 +9,66 @@ import { import Entity from '@lib/entity'; import { ReturnOption } from '@lib/entity/types'; -// Table metadata types - +/** + * Table metadata types and configurations. + */ + +/** + * Entity key type for table metadata. + * + * @template E - The entity class type + */ type EntityKey = keyof InstanceType extends string ? keyof InstanceType : never; +/** + * Table indexes metadata configuration. + * + * @template E - The entity class type + */ type TableIndexesMetadata = { [indexName: string]: { + /** Partition key for the index */ partitionKey?: EntityKey; + /** Sort key for the index */ sortKey?: EntityKey; }; }; +/** + * Table metadata configuration. + * + * @template E - The entity class type + */ export type Metadata = { + /** Name of the DynamoDB table */ tableName: string; + /** Partition key attribute name */ partitionKey: EntityKey; + /** Sort key attribute name (optional) */ sortKey?: EntityKey; + /** Secondary indexes configuration */ indexes?: TableIndexesMetadata; + /** Created at timestamp attribute (optional) */ createdAt?: EntityKey; + /** Updated at timestamp attribute (optional) */ updatedAt?: EntityKey; }; +/** Sort key type from metadata */ type SK, E extends typeof Entity> = M['sortKey']; +/** Partition key type from metadata */ type PK, E extends typeof Entity> = M['partitionKey']; +/** Indexes type from metadata */ type Idx, E extends typeof Entity> = M['indexes']; +/** + * Primary key type for table operations. + * + * @template M - The metadata type + * @template E - The entity class type + */ export type TablePrimaryKey, E extends typeof Entity> = Pick< InstanceType, Extract, SK extends string ? PK | SK : PK> diff --git a/lib/transactionGet/types.ts b/lib/transactionGet/types.ts index 1f9c4e3..e3a81cd 100644 --- a/lib/transactionGet/types.ts +++ b/lib/transactionGet/types.ts @@ -2,22 +2,48 @@ import { Get, TransactGetItemsCommandInput } from '@aws-sdk/client-dynamodb'; import Entity from '@lib/entity'; import type { ReturnOption } from '@lib/entity/types'; +/** + * Transaction get operation for a specific entity. + * + * @template E - The entity class type + */ export type TransactionGet = { + /** DynamoDB get operation */ get: Get; + /** Entity class for the operation */ entity: E; }; +/** + * Input type for transaction get operations. + * + * @template E - Array of entity class types + */ export type TransactionGetInput> = { readonly [K in keyof E]: TransactionGet; }; +/** + * Options for transaction get operations. + */ export type TransactionGetOptions = { + /** Return type option */ return?: ReturnOption; + /** Additional DynamoDB transaction get input parameters */ extraInput?: Partial; + /** Whether to throw an error if items are not found */ throwOnNotFound?: boolean; }; +/** + * Output from transaction get operations. + * + * @template E - Array of entity class types + * @template Extra - Additional type for items that couldn't be retrieved + */ export type TransactionGetOutput, Extra = never> = { + /** Array of retrieved entity instances */ items: { [K in keyof E]: InstanceType | Extra }; + /** Number of items retrieved */ count: number; }; diff --git a/lib/transactionWrite/types.ts b/lib/transactionWrite/types.ts index 5590a09..20ec01e 100644 --- a/lib/transactionWrite/types.ts +++ b/lib/transactionWrite/types.ts @@ -2,38 +2,96 @@ import { ConditionCheck, Delete, Put, TransactWriteItemsCommandInput, Update } f import Entity from '@lib/entity'; import type { ReturnOption } from '@lib/entity/types'; +/** + * Transaction update operation for a specific entity. + * + * @template E - The entity class type + */ export type TransactionUpdate = { + /** Entity class for the operation */ entity: E; + /** DynamoDB update operation */ update: Update; }; -export type TransactionPut = { entity: E; put: Put }; + +/** + * Transaction put operation for a specific entity. + * + * @template E - The entity class type + */ +export type TransactionPut = { + /** Entity class for the operation */ + entity: E; + /** DynamoDB put operation */ + put: Put; +}; + +/** + * Transaction delete operation for a specific entity. + * + * @template E - The entity class type + */ export type TransactionDelete = { + /** Entity class for the operation */ entity: E; + /** DynamoDB delete operation */ delete: Delete; }; + +/** + * Transaction condition check operation for a specific entity. + * + * @template E - The entity class type + */ export type TransactionCondition = { + /** Entity class for the operation */ entity: E; + /** DynamoDB condition check operation */ condition: ConditionCheck; }; + +/** + * Union type for all transaction write operations. + * + * @template E - The entity class type + */ export type TransactionWrite = | TransactionUpdate | TransactionPut | TransactionDelete | TransactionCondition; +/** + * Input type for transaction write operations. + * + * @template TW - Array of transaction write operations + */ export type TransactionWriteInput>> = { readonly [K in keyof TW]: TW[K]; }; +/** + * Options for transaction write operations. + */ export type TransactionWriteOptions = { + /** Return type option */ return?: ReturnOption; + /** Additional DynamoDB transaction write input parameters */ extraInput?: Partial; + /** Idempotency key for the transaction */ idempotencyKey?: string; }; +/** + * Output from transaction write operations. + * + * @template TW - Array of transaction write operations + */ export type TransactionWriteOutput>> = { + /** Array of items from put operations */ items: { [K in keyof TW]: TW[K] extends TransactionPut ? InstanceType : undefined; }; + /** Number of operations in the transaction */ count: number; }; diff --git a/lib/utils/constants.ts b/lib/utils/constants.ts index cae6fdc..27f6bd8 100644 --- a/lib/utils/constants.ts +++ b/lib/utils/constants.ts @@ -1,15 +1,40 @@ import { AttributeType } from '@lib/dynamode/storage/types'; import { insertBetween } from '@lib/utils'; +/** + * Core constants for Dynamode operations. + */ + +/** + * The property name used to identify Dynamode entities. + * This is automatically added to all entity instances. + */ export const DYNAMODE_ENTITY = 'dynamodeEntity'; +/** + * Mapping of JavaScript types to DynamoDB attribute types for keys. + * Only String and Number types are allowed for partition and sort keys. + */ export const DYNAMODE_DYNAMO_KEY_TYPE_MAP = new Map([ [String, 'S'], [Number, 'N'], ]); +/** + * Array of attribute types that are allowed for DynamoDB keys. + * DynamoDB only supports String and Number types for partition and sort keys. + */ export const DYNAMODE_ALLOWED_KEY_TYPES: AttributeType[] = [String, Number]; +/** + * Set of reserved words that cannot be used as attribute names in DynamoDB. + * + * These are SQL and DynamoDB reserved words that would cause conflicts + * if used as attribute names in expressions. Dynamode automatically + * handles these by using expression attribute names when needed. + * + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html} + */ export const RESERVED_WORDS = new Set([ 'ABORT', 'ABSOLUTE', @@ -586,87 +611,132 @@ export const RESERVED_WORDS = new Set([ 'ZONE', ]); +/** + * Expression operator types for building DynamoDB expressions. + */ + +/** + * Represents a literal expression string in an operator chain. + */ export type OperatorExpression = { expression: string }; + +/** + * Represents an attribute name reference in an operator chain. + */ export type OperatorKey = { key: string }; + +/** + * Represents an attribute value with its corresponding key in an operator chain. + */ export type OperatorValue = { value: unknown; key: string }; + +/** + * Union type for all possible operator elements in an expression. + */ export type Operators = Array; +/** + * Base operators for building DynamoDB expressions. + * + * These are the fundamental building blocks for constructing + * condition expressions, update expressions, and other DynamoDB + * expression types. + */ export const BASE_OPERATOR = { - /** space */ + /** Space character */ space: { expression: ' ' }, - /** , */ + /** Comma separator */ comma: { expression: ',' }, - /** ( */ + /** Left parenthesis */ leftParenthesis: { expression: '(' }, - /** ) */ + /** Right parenthesis */ rightParenthesis: { expression: ')' }, - /** + */ + /** Plus operator */ plus: { expression: '+' }, - /** - */ + /** Minus operator */ minus: { expression: '-' }, - /** NOT */ + /** NOT logical operator */ not: { expression: 'NOT' }, - /** AND */ + /** AND logical operator */ and: { expression: 'AND' }, - /** OR */ + /** OR logical operator */ or: { expression: 'OR' }, - /** = */ + /** Equal comparison operator */ eq: { expression: '=' }, - /** <> */ + /** Not equal comparison operator */ ne: { expression: '<>' }, - /** < */ + /** Less than comparison operator */ lt: { expression: '<' }, - /** <= */ + /** Less than or equal comparison operator */ le: { expression: '<=' }, - /** > */ + /** Greater than comparison operator */ gt: { expression: '>' }, - /** >= */ + /** Greater than or equal comparison operator */ ge: { expression: '>=' }, - /** attribute_exists */ + /** attribute_exists function */ attributeExists: { expression: 'attribute_exists' }, - /** contains */ + /** contains function */ contains: { expression: 'contains' }, - /** IN */ + /** IN operator */ in: { expression: 'IN' }, - /** BETWEEN */ + /** BETWEEN operator */ between: { expression: 'BETWEEN' }, - /** attribute_type */ + /** attribute_type function */ attributeType: { expression: 'attribute_type' }, - /** begins_with */ + /** begins_with function */ beginsWith: { expression: 'begins_with' }, - /** attribute_not_exists */ + /** attribute_not_exists function */ attributeNotExists: { expression: 'attribute_not_exists' }, - /** size */ + /** size function */ size: { expression: 'size' }, - /** if_not_exists */ + /** if_not_exists function */ ifNotExists: { expression: 'if_not_exists' }, - /** list_append */ + /** list_append function */ listAppend: { expression: 'list_append' }, - /** SET */ + /** SET update action */ set: { expression: 'SET' }, - /** ADD */ + /** ADD update action */ add: { expression: 'ADD' }, - /** REMOVE */ + /** REMOVE update action */ remove: { expression: 'REMOVE' }, - /** DELETE */ + /** DELETE update action */ delete: { expression: 'DELETE' }, } as const; +/** + * Condition operators for building DynamoDB condition expressions. + * + * These operators construct complex condition expressions by combining + * attribute names, values, and comparison operators. Each operator + * returns an array of operator elements that can be processed to + * generate the final expression string. + */ export const OPERATORS = { - /** parenthesis */ + /** + * Wraps an operator structure in parentheses. + * + * @param operatorStructure - The operators to wrap in parentheses + * @returns Operators wrapped in parentheses + */ parenthesis: (operatorStructure: Operators): Operators => [ BASE_OPERATOR.leftParenthesis, ...operatorStructure, BASE_OPERATOR.rightParenthesis, ], - /** $K = $V */ + /** + * Creates an equality comparison: $K = $V + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for equality comparison + */ eq: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -674,7 +744,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** $K <> $V */ + /** + * Creates a not equal comparison: $K <> $V + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for not equal comparison + */ ne: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -682,7 +758,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** $K < $V */ + /** + * Creates a less than comparison: $K < $V + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for less than comparison + */ lt: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -690,7 +772,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** $K <= $V */ + /** + * Creates a less than or equal comparison: $K <= $V + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for less than or equal comparison + */ le: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -698,7 +786,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** $K > $V */ + /** + * Creates a greater than comparison: $K > $V + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for greater than comparison + */ gt: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -706,7 +800,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** $K >= $V */ + /** + * Creates a greater than or equal comparison: $K >= $V + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for greater than or equal comparison + */ ge: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -715,14 +815,31 @@ export const OPERATORS = { { value, key }, ], - /** attribute_exists($K) */ + /** + * Creates an attribute_exists function: attribute_exists($K) + * + * @param key - The attribute name to check for existence + * @returns Operators for attribute_exists function + */ attributeExists: (key: string): Operators => [BASE_OPERATOR.attributeExists, ...OPERATORS.parenthesis([{ key }])], - /** contains($K, $V) */ + /** + * Creates a contains function: contains($K, $V) + * + * @param key - The attribute name + * @param value - The value to check if the attribute contains + * @returns Operators for contains function + */ contains: (key: string, value: unknown): Operators => [ BASE_OPERATOR.contains, ...OPERATORS.parenthesis([{ key }, BASE_OPERATOR.comma, BASE_OPERATOR.space, { value, key }]), ], - /** $K IN ($V, $V, $V) */ + /** + * Creates an IN operator: $K IN ($V, $V, $V) + * + * @param key - The attribute name + * @param values - Array of values to check against + * @returns Operators for IN comparison + */ in: (key: string, values: unknown[]): Operators => [ { key }, BASE_OPERATOR.space, @@ -735,7 +852,14 @@ export const OPERATORS = { ), BASE_OPERATOR.rightParenthesis, ], - /** $K BETWEEN $V AND $V */ + /** + * Creates a BETWEEN operator: $K BETWEEN $V AND $V + * + * @param key - The attribute name + * @param value1 - The lower bound value + * @param value2 - The upper bound value + * @returns Operators for BETWEEN comparison + */ between: (key: string, value1: unknown, value2: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -747,48 +871,119 @@ export const OPERATORS = { BASE_OPERATOR.space, { value: value2, key }, ], - /** attribute_type($K, $V) */ + /** + * Creates an attribute_type function: attribute_type($K, $V) + * + * @param key - The attribute name + * @param value - The expected attribute type + * @returns Operators for attribute_type function + */ attributeType: (key: string, value: unknown): Operators => [ BASE_OPERATOR.attributeType, ...OPERATORS.parenthesis([{ key }, BASE_OPERATOR.comma, BASE_OPERATOR.space, { value, key }]), ], - /** begins_with($K, $V) */ + /** + * Creates a begins_with function: begins_with($K, $V) + * + * @param key - The attribute name + * @param value - The prefix to check for + * @returns Operators for begins_with function + */ beginsWith: (key: string, value: unknown): Operators => [ BASE_OPERATOR.beginsWith, ...OPERATORS.parenthesis([{ key }, BASE_OPERATOR.comma, BASE_OPERATOR.space, { value, key }]), ], - /** attribute_not_exists($K) */ + /** + * Creates an attribute_not_exists function: attribute_not_exists($K) + * + * @param key - The attribute name to check for non-existence + * @returns Operators for attribute_not_exists function + */ attributeNotExists: (key: string): Operators => [ BASE_OPERATOR.attributeNotExists, ...OPERATORS.parenthesis([{ key }]), ], - /** NOT contains($K, $V) */ + /** + * Creates a NOT contains function: NOT contains($K, $V) + * + * @param key - The attribute name + * @param value - The value to check if the attribute does not contain + * @returns Operators for NOT contains function + */ notContains: (key: string, value: unknown): Operators => [ BASE_OPERATOR.not, BASE_OPERATOR.space, ...OPERATORS.contains(key, value), ], - /** NOT ($K IN $V, $V, $V) */ + /** + * Creates a NOT IN operator: NOT ($K IN $V, $V, $V) + * + * @param key - The attribute name + * @param values - Array of values to check against + * @returns Operators for NOT IN comparison + */ notIn: (key: string, values: unknown[]): Operators => [ BASE_OPERATOR.not, BASE_OPERATOR.space, ...OPERATORS.parenthesis(OPERATORS.in(key, values)), ], - /** NOT $K = $V */ + /** + * Creates a NOT equal comparison: NOT $K = $V (equivalent to $K <> $V) + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for NOT equal comparison + */ notEq: (key: string, value: unknown): Operators => OPERATORS.ne(key, value), - /** NOT $K <> $V */ + /** + * Creates a NOT not equal comparison: NOT $K <> $V (equivalent to $K = $V) + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for NOT not equal comparison + */ notNe: (key: string, value: unknown): Operators => OPERATORS.eq(key, value), - /** NOT $K < $V */ + /** + * Creates a NOT less than comparison: NOT $K < $V (equivalent to $K >= $V) + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for NOT less than comparison + */ notLt: (key: string, value: unknown): Operators => OPERATORS.ge(key, value), - /** NOT $K <= $V */ + /** + * Creates a NOT less than or equal comparison: NOT $K <= $V (equivalent to $K > $V) + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for NOT less than or equal comparison + */ notLe: (key: string, value: unknown): Operators => OPERATORS.gt(key, value), - /** NOT $K > $V */ + /** + * Creates a NOT greater than comparison: NOT $K > $V (equivalent to $K <= $V) + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for NOT greater than comparison + */ notGt: (key: string, value: unknown): Operators => OPERATORS.le(key, value), - /** NOT $K >= $V */ + /** + * Creates a NOT greater than or equal comparison: NOT $K >= $V (equivalent to $K < $V) + * + * @param key - The attribute name + * @param value - The value to compare against + * @returns Operators for NOT greater than or equal comparison + */ notGe: (key: string, value: unknown): Operators => OPERATORS.lt(key, value), - /** size($K) = $V */ + /** + * Creates a size function with equality: size($K) = $V + * + * @param key - The attribute name + * @param value - The size value to compare against + * @returns Operators for size equality comparison + */ sizeEq: (key: string, value: unknown): Operators => [ BASE_OPERATOR.size, ...OPERATORS.parenthesis([{ key }]), @@ -797,7 +992,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** size($K) <> $V */ + /** + * Creates a size function with not equal: size($K) <> $V + * + * @param key - The attribute name + * @param value - The size value to compare against + * @returns Operators for size not equal comparison + */ sizeNe: (key: string, value: unknown): Operators => [ BASE_OPERATOR.size, ...OPERATORS.parenthesis([{ key }]), @@ -806,7 +1007,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** size($K) < $V */ + /** + * Creates a size function with less than: size($K) < $V + * + * @param key - The attribute name + * @param value - The size value to compare against + * @returns Operators for size less than comparison + */ sizeLt: (key: string, value: unknown): Operators => [ BASE_OPERATOR.size, ...OPERATORS.parenthesis([{ key }]), @@ -815,7 +1022,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** size($K) <= $V */ + /** + * Creates a size function with less than or equal: size($K) <= $V + * + * @param key - The attribute name + * @param value - The size value to compare against + * @returns Operators for size less than or equal comparison + */ sizeLe: (key: string, value: unknown): Operators => [ BASE_OPERATOR.size, ...OPERATORS.parenthesis([{ key }]), @@ -824,7 +1037,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** size($K) > $V */ + /** + * Creates a size function with greater than: size($K) > $V + * + * @param key - The attribute name + * @param value - The size value to compare against + * @returns Operators for size greater than comparison + */ sizeGt: (key: string, value: unknown): Operators => [ BASE_OPERATOR.size, ...OPERATORS.parenthesis([{ key }]), @@ -833,7 +1052,13 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** size($K) >= $V */ + /** + * Creates a size function with greater than or equal: size($K) >= $V + * + * @param key - The attribute name + * @param value - The size value to compare against + * @returns Operators for size greater than or equal comparison + */ sizeGe: (key: string, value: unknown): Operators => [ BASE_OPERATOR.size, ...OPERATORS.parenthesis([{ key }]), @@ -842,7 +1067,15 @@ export const OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** attribute_exists(Id) AND attribute_not_exists(Id) */ + /** + * Creates an impossible condition: attribute_exists($K) AND attribute_not_exists($K) + * + * This creates a condition that can never be true, useful for testing + * or creating conditions that should always fail. + * + * @param key - The attribute name + * @returns Operators for impossible condition + */ impossibleCondition: (key: string): Operators => [ ...OPERATORS.attributeExists(key), BASE_OPERATOR.space, @@ -852,8 +1085,20 @@ export const OPERATORS = { ], }; +/** + * Update operators for building DynamoDB update expressions. + * + * These operators construct update expressions for DynamoDB UpdateItem + * operations, including SET, ADD, REMOVE, and DELETE actions. + */ export const UPDATE_OPERATORS = { - /** $K = $V */ + /** + * Creates a SET operation: $K = $V + * + * @param key - The attribute name to set + * @param value - The value to set + * @returns Operators for SET operation + */ set: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -861,7 +1106,13 @@ export const UPDATE_OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** $K = if_not_exists($K, $V) */ + /** + * Creates a SET operation with if_not_exists: $K = if_not_exists($K, $V) + * + * @param key - The attribute name to set + * @param value - The default value if attribute doesn't exist + * @returns Operators for SET if_not_exists operation + */ setIfNotExists: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -870,7 +1121,13 @@ export const UPDATE_OPERATORS = { BASE_OPERATOR.ifNotExists, ...OPERATORS.parenthesis([{ key }, BASE_OPERATOR.comma, BASE_OPERATOR.space, { value, key }]), ], - /** $K = list_append($K, $V) */ + /** + * Creates a SET operation with list_append: $K = list_append($K, $V) + * + * @param key - The list attribute name + * @param value - The list to append + * @returns Operators for SET list_append operation + */ listAppend: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -879,7 +1136,13 @@ export const UPDATE_OPERATORS = { BASE_OPERATOR.listAppend, ...OPERATORS.parenthesis([{ key }, BASE_OPERATOR.comma, BASE_OPERATOR.space, { value, key }]), ], - /** $K = $K + $V */ + /** + * Creates a SET operation with increment: $K = $K + $V + * + * @param key - The numeric attribute name + * @param value - The value to add + * @returns Operators for SET increment operation + */ increment: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -891,7 +1154,13 @@ export const UPDATE_OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** $K = $K - $V */ + /** + * Creates a SET operation with decrement: $K = $K - $V + * + * @param key - The numeric attribute name + * @param value - The value to subtract + * @returns Operators for SET decrement operation + */ decrement: (key: string, value: unknown): Operators => [ { key }, BASE_OPERATOR.space, @@ -903,10 +1172,27 @@ export const UPDATE_OPERATORS = { BASE_OPERATOR.space, { value, key }, ], - /** $K $V */ + /** + * Creates an ADD operation: $K $V + * + * @param key - The attribute name + * @param value - The value to add (for numbers or sets) + * @returns Operators for ADD operation + */ add: (key: string, value: unknown): Operators => [{ key }, BASE_OPERATOR.space, { value, key }], - /** $K $V */ + /** + * Creates a DELETE operation: $K $V + * + * @param key - The set attribute name + * @param value - The set elements to delete + * @returns Operators for DELETE operation + */ delete: (key: string, value: unknown): Operators => [{ key }, BASE_OPERATOR.space, { value, key }], - /** $K */ + /** + * Creates a REMOVE operation: $K + * + * @param key - The attribute name to remove + * @returns Operators for REMOVE operation + */ remove: (key: string): Operators => [{ key }], }; diff --git a/lib/utils/types.ts b/lib/utils/types.ts index 418d23c..9352854 100644 --- a/lib/utils/types.ts +++ b/lib/utils/types.ts @@ -1,44 +1,112 @@ import type { AttributeValue } from '@aws-sdk/client-dynamodb'; -// Common +/** + * Common utility types for Dynamode operations. + */ +/** + * DynamoDB attribute values mapping. + */ export type AttributeValues = Record; + +/** + * DynamoDB attribute names mapping. + */ export type AttributeNames = Record; + +/** + * Generic object type for flexible data structures. + */ export type GenericObject = Record; -// Flatten entity +/** + * Entity flattening utility types. + */ + +/** + * Flattens a nested object structure into a single level with dot notation keys. + * + * @template TValue - The type to flatten + * @example + * ```typescript + * type Nested = { + * user: { + * name: string; + * address: { + * city: string; + * }; + * }; + * }; + * + * type Flattened = FlattenObject; + * // Result: { + * // 'user.name': string; + * // 'user.address.city': string; + * // } + * ``` + */ export type FlattenObject = CollapseEntries>; +/** Internal entry type for flattening operations */ type Entry = { key: string; value: unknown }; +/** Empty entry type for terminal values */ type EmptyEntry = { key: ''; value: TValue }; +/** Types that are excluded from flattening */ type ExcludedTypes = Date | Set | Map | Uint8Array; +/** Array index encoder type */ type ArrayEncoder = `[${bigint}]`; +/** Escapes array keys in dot notation */ type EscapeArrayKey = TKey extends `${infer TKeyBefore}.${ArrayEncoder}${infer TKeyAfter}` ? EscapeArrayKey<`${TKeyBefore}${ArrayEncoder}${TKeyAfter}`> : TKey; -// Transforms entries to one flattened type +/** + * Transforms entries to one flattened type. + * + * @template TEntry - The entry type to collapse + */ type CollapseEntries = { [E in TEntry as EscapeArrayKey]: E['value']; }; -// Transforms array type to object +/** + * Transforms array type to object. + * + * @template TValue - The value type + * @template TValueInitial - The initial value type for recursion control + */ type CreateArrayEntry = OmitItself< TValue extends unknown[] ? { [k: ArrayEncoder]: TValue[number] } : TValue, TValueInitial >; -// Omit the type that references itself +/** + * Omit the type that references itself to prevent infinite recursion. + * + * @template TValue - The value type + * @template TValueInitial - The initial value type for recursion control + */ type OmitItself = TValue extends TValueInitial ? EmptyEntry : OmitExcludedTypes; -// Omit the type that is listed in ExcludedTypes union +/** + * Omit the type that is listed in ExcludedTypes union. + * + * @template TValue - The value type + * @template TValueInitial - The initial value type for recursion control + */ type OmitExcludedTypes = TValue extends ExcludedTypes ? EmptyEntry : CreateObjectEntries; +/** + * Creates object entries for flattening operations. + * + * @template TValue - The value type + * @template TValueInitial - The initial value type for recursion control + */ type CreateObjectEntries = TValue extends object ? { // Checks that Key is of type string @@ -66,9 +134,20 @@ type CreateObjectEntries = TValue extends object }[keyof TValue] // Builds entry for each key : EmptyEntry; -// Narrow utility +/** + * Narrow utility types. + */ +/** Types that can be narrowed */ type Narrowable = string | number | bigint | boolean; + +/** + * Narrow utility type for const assertions. + * + * This utility type helps with const assertions and type narrowing. + * + * @template T - The type to narrow + */ export type Narrow = | (T extends Narrowable ? T : never) | { From f2ac00e8b6eb4b2eeac9bb1a5ca5d3fa7b3aeea3 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 6 Sep 2025 17:04:34 +0200 Subject: [PATCH 3/6] Add detailed documentation for RetrieverBase, transactionGet, and transactionWrite functions, including usage examples and parameter descriptions to enhance developer understanding and usability. --- lib/retriever/index.ts | 115 ++++++++++++++++++++++++++++++++++ lib/transactionGet/index.ts | 25 ++++++++ lib/transactionWrite/index.ts | 25 ++++++++ 3 files changed, 165 insertions(+) diff --git a/lib/retriever/index.ts b/lib/retriever/index.ts index 2b5a0e1..63af09a 100644 --- a/lib/retriever/index.ts +++ b/lib/retriever/index.ts @@ -8,11 +8,29 @@ import { EntityKey } from '@lib/entity/types'; import { Metadata, TableIndexNames, TableRetrieverLastKey } from '@lib/table/types'; import { AttributeNames, AttributeValues } from '@lib/utils'; +/** + * Base class for DynamoDB retrieval operations (Query and Scan). + * + * This abstract class provides common functionality for both Query and Scan operations, + * including index selection, pagination, filtering, and projection. It extends the + * Condition class to provide filtering capabilities. + * + * @template M - The metadata type for the table + * @template E - The entity class type + */ export default class RetrieverBase, E extends typeof Entity> extends Condition { + /** The DynamoDB input object (QueryInput or ScanInput) */ protected input: QueryInput | ScanInput; + /** Attribute names mapping for expression attribute names */ protected attributeNames: AttributeNames = {}; + /** Attribute values mapping for expression attribute values */ protected attributeValues: AttributeValues = {}; + /** + * Creates a new RetrieverBase instance. + * + * @param entity - The entity class to retrieve data for + */ constructor(entity: E) { super(entity); this.input = { @@ -20,16 +38,64 @@ export default class RetrieverBase, E extends typeof Entit }; } + /** + * Specifies the index to query or scan. + * + * @param name - The name of the index to use + * @returns The retriever instance for chaining + * + * @example + * ```typescript + * const users = await UserManager.query() + * .indexName('StatusIndex') + * .partitionKey('status').eq('active') + * .run(); + * ``` + */ public indexName(name: TableIndexNames) { this.input.IndexName = String(name); return this; } + /** + * Limits the number of items returned. + * + * @param count - Maximum number of items to return + * @returns The retriever instance for chaining + * + * @example + * ```typescript + * const users = await UserManager.query() + * .partitionKey('status').eq('active') + * .limit(10) + * .run(); + * ``` + */ public limit(count: number) { this.input.Limit = count; return this; } + /** + * Sets the starting point for pagination. + * + * @param key - The last evaluated key from a previous operation + * @returns The retriever instance for chaining + * + * @example + * ```typescript + * const firstPage = await UserManager.query() + * .partitionKey('status').eq('active') + * .limit(10) + * .run(); + * + * const secondPage = await UserManager.query() + * .partitionKey('status').eq('active') + * .limit(10) + * .startAt(firstPage.lastKey) + * .run(); + * ``` + */ public startAt(key?: TableRetrieverLastKey) { if (key) { this.input.ExclusiveStartKey = convertRetrieverLastKeyToAttributeValues(this.entity, key); @@ -38,16 +104,65 @@ export default class RetrieverBase, E extends typeof Entit return this; } + /** + * Enables consistent read for the operation. + * + * Consistent reads return data that reflects all successful write operations + * that completed before the read operation. This may increase latency and + * consume more read capacity. + * + * @returns The retriever instance for chaining + * + * @example + * ```typescript + * const user = await UserManager.query() + * .partitionKey('id').eq('user-123') + * .consistent() + * .run(); + * ``` + */ public consistent() { this.input.ConsistentRead = true; return this; } + /** + * Returns only the count of items instead of the actual items. + * + * This is more efficient when you only need to know how many items + * match your criteria without retrieving the actual data. + * + * @returns The retriever instance for chaining + * + * @example + * ```typescript + * const result = await UserManager.query() + * .partitionKey('status').eq('active') + * .count() + * .run(); + * + * console.log('Active users count:', result.count); + * ``` + */ public count() { this.input.Select = 'COUNT'; return this; } + /** + * Specifies which attributes to return. + * + * @param attributes - Array of attribute names to project + * @returns The retriever instance for chaining + * + * @example + * ```typescript + * const users = await UserManager.query() + * .partitionKey('status').eq('active') + * .attributes(['id', 'name', 'email']) + * .run(); + * ``` + */ public attributes(attributes: Array>) { this.input.ProjectionExpression = buildGetProjectionExpression( attributes, diff --git a/lib/transactionGet/index.ts b/lib/transactionGet/index.ts index ba63a1f..1ebe78e 100644 --- a/lib/transactionGet/index.ts +++ b/lib/transactionGet/index.ts @@ -5,6 +5,31 @@ import { convertAttributeValuesToEntity } from '@lib/entity/helpers/converters'; import type { TransactionGetInput, TransactionGetOptions, TransactionGetOutput } from '@lib/transactionGet/types'; import { NotFoundError } from '@lib/utils'; +/** + * Executes a DynamoDB TransactGetItems operation. + * + * This function allows you to retrieve multiple items from one or more tables + * in a single atomic operation. All items are retrieved or none are retrieved. + * + * @param transactions - Array of transaction get operations + * @returns A promise that resolves to the transaction results + * @throws {NotFoundError} When items are not found and throwOnNotFound is true + * + * @example + * ```typescript + * // Get multiple items in a transaction + * const result = await transactionGet([ + * UserManager.transactionGet({ id: 'user-1' }), + * UserManager.transactionGet({ id: 'user-2' }), + * UserManager.transactionGet({ id: 'product-1' }), + * ]); + * + * console.log('Users:', result.items[0], result.items[1]); + * console.log('Product:', result.items[2]); + * ``` + * + * see more examples [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions#transactiongettransactions-options). + */ export default function transactionGet>( transactions: TransactionGetInput<[...E]>, ): Promise>; diff --git a/lib/transactionWrite/index.ts b/lib/transactionWrite/index.ts index 630c36b..c796c9d 100644 --- a/lib/transactionWrite/index.ts +++ b/lib/transactionWrite/index.ts @@ -9,6 +9,31 @@ import type { TransactionWriteOutput, } from '@lib/transactionWrite/types'; +/** + * Executes a DynamoDB TransactWriteItems operation. + * + * This function allows you to perform multiple write operations (Put, Update, Delete, ConditionCheck) + * across one or more tables in a single atomic transaction. All operations succeed or all fail. + * + * @param transactions - Array of transaction write operations + * @param options - Optional configuration for the transaction + * @returns A promise that resolves to the transaction results + * + * @example + * ```typescript + * // Perform multiple write operations in a transaction + * const result = await transactionWrite([ + * UserManager.transaction.put(new User({ id: 'user-1', name: 'John' })), + * UserManager.transaction.update({ id: 'user-2' }, { set: { status: 'active' } }), + * UserManager.transaction.delete({ id: 'product-1' }), + * UserManager.transaction.condition({ id: 'user-3' }, UserManager.condition().attribute('status').eq('active')), + * ]); + * + * console.log('Transaction completed:', result.count, 'operations'); + * ``` + * + * see more examples [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions#transactionwritetransactions-options). + */ export default function transactionWrite>>( transactions: TransactionWriteInput<[...TW]>, options?: TransactionWriteOptions & { return?: 'default' }, From 30391feb58f727f9b81bbd63a821d3b046e8c84a Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 6 Sep 2025 17:07:58 +0200 Subject: [PATCH 4/6] Enhance documentation across various modules by adding links to AWS DynamoDB resources for condition expressions, query operations, scan operations, streams, and transaction APIs. This update aims to provide developers with direct access to official documentation for better understanding and implementation. --- lib/condition/index.ts | 3 +++ lib/dynamode/index.ts | 3 +++ lib/query/index.ts | 1 + lib/retriever/index.ts | 3 --- lib/scan/index.ts | 1 + lib/stream/index.ts | 2 ++ lib/transactionGet/index.ts | 3 ++- lib/transactionWrite/index.ts | 3 ++- lib/utils/constants.ts | 3 ++- 9 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/condition/index.ts b/lib/condition/index.ts index dc38503..054f887 100644 --- a/lib/condition/index.ts +++ b/lib/condition/index.ts @@ -37,6 +37,9 @@ import { BASE_OPERATOR, OPERATORS, Operators, ValidationError } from '@lib/utils * { condition } * ); * ``` + * + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ConditionExpressions.html} for DynamoDB condition expressions + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html} for DynamoDB operators and functions */ export default class Condition { /** The entity class for type-safe attribute access */ diff --git a/lib/dynamode/index.ts b/lib/dynamode/index.ts index 713042b..732d75e 100644 --- a/lib/dynamode/index.ts +++ b/lib/dynamode/index.ts @@ -32,6 +32,9 @@ import DynamodeStorage from '@lib/dynamode/storage'; * // Use separator for key operations * const key = Dynamode.separator.join(['user', '123']); * ``` + * + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html} for DynamoDB overview + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.html} for DynamoDB concepts */ class Dynamode { /** Default singleton instance of Dynamode */ diff --git a/lib/query/index.ts b/lib/query/index.ts index 14dbc82..214affe 100644 --- a/lib/query/index.ts +++ b/lib/query/index.ts @@ -53,6 +53,7 @@ import { * ``` * * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/query} for more information + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html} for DynamoDB Query operations */ export default class Query, E extends typeof Entity> extends RetrieverBase { /** The DynamoDB QueryInput object */ diff --git a/lib/retriever/index.ts b/lib/retriever/index.ts index 63af09a..86c3a9b 100644 --- a/lib/retriever/index.ts +++ b/lib/retriever/index.ts @@ -14,9 +14,6 @@ import { AttributeNames, AttributeValues } from '@lib/utils'; * This abstract class provides common functionality for both Query and Scan operations, * including index selection, pagination, filtering, and projection. It extends the * Condition class to provide filtering capabilities. - * - * @template M - The metadata type for the table - * @template E - The entity class type */ export default class RetrieverBase, E extends typeof Entity> extends Condition { /** The DynamoDB input object (QueryInput or ScanInput) */ diff --git a/lib/scan/index.ts b/lib/scan/index.ts index 7b2c429..d905b46 100644 --- a/lib/scan/index.ts +++ b/lib/scan/index.ts @@ -38,6 +38,7 @@ import { ExpressionBuilder, isNotEmptyString } from '@lib/utils'; * ``` * * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/scan} for more information + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html} for DynamoDB Scan operations */ export default class Scan, E extends typeof Entity> extends RetrieverBase { /** The DynamoDB ScanInput object */ diff --git a/lib/stream/index.ts b/lib/stream/index.ts index 6ebe38b..134952e 100644 --- a/lib/stream/index.ts +++ b/lib/stream/index.ts @@ -40,6 +40,8 @@ import { DynamoDBRecord } from './types'; * ``` * * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/stream} for more information + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Streams.html} for DynamoDB Streams documentation + * @see {@link https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html} for AWS Lambda with DynamoDB streams */ export default class Stream { /** The type of stream data available */ diff --git a/lib/transactionGet/index.ts b/lib/transactionGet/index.ts index 1ebe78e..4d28404 100644 --- a/lib/transactionGet/index.ts +++ b/lib/transactionGet/index.ts @@ -28,7 +28,8 @@ import { NotFoundError } from '@lib/utils'; * console.log('Product:', result.items[2]); * ``` * - * see more examples [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions#transactiongettransactions-options). + * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/transactions#transactiongettransactions-options} for more examples + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-get} for AWS TransactGetItems documentation */ export default function transactionGet>( transactions: TransactionGetInput<[...E]>, diff --git a/lib/transactionWrite/index.ts b/lib/transactionWrite/index.ts index c796c9d..d97eed9 100644 --- a/lib/transactionWrite/index.ts +++ b/lib/transactionWrite/index.ts @@ -32,7 +32,8 @@ import type { * console.log('Transaction completed:', result.count, 'operations'); * ``` * - * see more examples [here](https://blazejkustra.github.io/dynamode/docs/guide/transactions#transactionwritetransactions-options). + * @see {@link https://blazejkustra.github.io/dynamode/docs/guide/transactions#transactionwritetransactions-options} for more examples + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-write} for AWS TransactWriteItems documentation */ export default function transactionWrite>>( transactions: TransactionWriteInput<[...TW]>, diff --git a/lib/utils/constants.ts b/lib/utils/constants.ts index 27f6bd8..7de3efa 100644 --- a/lib/utils/constants.ts +++ b/lib/utils/constants.ts @@ -33,7 +33,8 @@ export const DYNAMODE_ALLOWED_KEY_TYPES: AttributeType[] = [String, Number]; * if used as attribute names in expressions. Dynamode automatically * handles these by using expression attribute names when needed. * - * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html} + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/ReservedWords.html} for complete list of reserved words + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ExpressionAttributeNames.html} for expression attribute names */ export const RESERVED_WORDS = new Set([ 'ABORT', From 9c6765b54b99ea83101665c459d90cb144024292 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 6 Sep 2025 17:09:30 +0200 Subject: [PATCH 5/6] Update documentation in Entity class to include additional links for DynamoDB data modeling best practices and core components, enhancing resources available for developers. --- lib/entity/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/entity/index.ts b/lib/entity/index.ts index cecc309..535a683 100644 --- a/lib/entity/index.ts +++ b/lib/entity/index.ts @@ -26,6 +26,9 @@ import { DYNAMODE_ENTITY } from '@lib/utils'; * ``` * * @see {@link https://blazejkustra.github.io/dynamode/docs/getting_started/introduction} for more information + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-general-nosql-design.html} for DynamoDB data modeling best practices + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-modeling-nosql.html} for NoSQL data modeling concepts + * @see {@link https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.CoreComponents.html} for DynamoDB core components */ export default class Entity { /** From f37ae13f55863827d99e2d7ad4b45599f1228ab2 Mon Sep 17 00:00:00 2001 From: Blazej Kustra Date: Sat, 6 Sep 2025 17:39:09 +0200 Subject: [PATCH 6/6] Refactor attribute decorators in Entity class to use the correct character for partition and sort key annotations. Update documentation examples to reflect these changes, ensuring consistency across the library. --- lib/decorators/index.ts | 12 +- lib/entity/entityManager.ts | 4 +- lib/entity/index.ts | 6 +- lib/index.ts | 4 +- lib/module.ts | 4 +- lib/table/index.ts | 273 +++++++++++++++++++++++++++++++++++- 6 files changed, 281 insertions(+), 22 deletions(-) diff --git a/lib/decorators/index.ts b/lib/decorators/index.ts index d0e926c..ce4b24b 100644 --- a/lib/decorators/index.ts +++ b/lib/decorators/index.ts @@ -25,19 +25,19 @@ import { * @example * ```typescript * class User extends Entity { - * @attribute.partitionKey.string() + * @attribute.partitionKey.string() * id: string; * - * @attribute.sortKey.number() + * @attribute.sortKey.number() * createdAt: number; * - * @attribute.string() + * @attribute.string() * name: string; * - * @attribute.gsi.partitionKey.string({ indexName: 'NameIndex' }) + * @attribute.gsi.partitionKey.string({ indexName: 'NameIndex' }) * nameIndex: string; * - * @attribute.lsi.sortKey.string({ indexName: 'StatusIndex' }) + * @attribute.lsi.sortKey.string({ indexName: 'StatusIndex' }) * status: string; * } * ``` @@ -124,7 +124,7 @@ const attribute = { * ```typescript * @entity.customName('CustomUser') * class User extends Entity { - * @attribute.partitionKey.string() + * @attribute.partitionKey.string() * id: string; * } * ``` diff --git a/lib/entity/entityManager.ts b/lib/entity/entityManager.ts index 859a6c9..fdd71de 100644 --- a/lib/entity/entityManager.ts +++ b/lib/entity/entityManager.ts @@ -72,10 +72,10 @@ import { AttributeValues, ExpressionBuilder, fromDynamo, NotFoundError } from '@ * @example * ```typescript * class User extends Entity { - * @attribute.partitionKey.string() + * @attribute.partitionKey.string() * id: string; * - * @attribute.string() + * @attribute.string() * name: string; * } * diff --git a/lib/entity/index.ts b/lib/entity/index.ts index 535a683..6160ead 100644 --- a/lib/entity/index.ts +++ b/lib/entity/index.ts @@ -11,10 +11,10 @@ import { DYNAMODE_ENTITY } from '@lib/utils'; * @example * ```typescript * class User extends Entity { - * @attribute.partitionKey.string() + * @attribute.partitionKey.string() * id: string; * - * @attribute.string() + * @attribute.string() * name: string; * * constructor(props: { id: string; name: string }) { @@ -55,7 +55,7 @@ export default class Entity { * @example * ```typescript * class Product extends Entity { - * @attribute.partitionKey.string() + * @attribute.partitionKey.string() * id: string; * * constructor(props: { id: string }) { diff --git a/lib/index.ts b/lib/index.ts index 351f20f..31e7f97 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -17,10 +17,10 @@ * * // Define an entity * class User extends Entity { - * @attribute.partitionKey.string() + * @attribute.partitionKey.string() * id: string; * - * @attribute.string() + * @attribute.string() * name: string; * } * diff --git a/lib/module.ts b/lib/module.ts index 85f52f6..b896cad 100644 --- a/lib/module.ts +++ b/lib/module.ts @@ -30,10 +30,10 @@ declare global { * import { Entity, TableManager, attribute } from 'dynamode'; * * class User extends Entity { - * @attribute.partitionKey.string() + * @attribute.partitionKey.string() * id: string; * - * @attribute.string() + * @attribute.string() * name: string; * } * diff --git a/lib/table/index.ts b/lib/table/index.ts index 2107a3e..5f5e778 100644 --- a/lib/table/index.ts +++ b/lib/table/index.ts @@ -40,10 +40,10 @@ import { getTableGlobalSecondaryIndexes, getTableLocalSecondaryIndexes } from '. * @example * ```typescript * class User extends Entity { - * @attribute.partitionKey.string() + * @attribute.partitionKey.string() * id: string; * - * @attribute.string() + * @attribute.string() * name: string; * } * @@ -114,17 +114,50 @@ export default class TableManager, TE extends typeof Enti } /** - * Creates an entity manager for the base table entity. + * Creates an entity manager for the table. * * @returns An EntityManager instance for the base table entity * * @example * ```typescript + * // Get manager for base table entity * const UserManager = UserTableManager.entityManager(); * const user = await UserManager.get({ id: 'user-123' }); * ``` */ public entityManager(): ReturnType>; + /** + * Creates an entity manager for a specific entity. + * + * @param entity - The entity class constructor + * @returns An EntityManager instance for the specific entity + * + * @example + * ```typescript + * // Get manager for specific entity + * const AdminManager = UserTableManager.entityManager(Admin); + * const admin = await AdminManager.get({ id: 'admin-123' }); + * ``` + */ + public entityManager(entity: E): ReturnType>; + + /** + * Creates an entity manager for the table. + * + * @param entity - Optional entity class constructor. If not provided, uses the base table entity. + * @returns An EntityManager instance for the specified entity or base table entity + * + * @example + * ```typescript + * // Get manager for base table entity + * const UserManager = UserTableManager.entityManager(); + * const user = await UserManager.get({ id: 'user-123' }); + * + * // Get manager for specific entity + * const AdminManager = UserTableManager.entityManager(Admin); + * const admin = await AdminManager.get({ id: 'admin-123' }); + * ``` + */ public entityManager( entity?: E, ): ReturnType> | ReturnType> { @@ -136,9 +169,63 @@ export default class TableManager, TE extends typeof Enti return EntityManager(this.tableEntity, this.tableMetadata.tableName); } + /** + * Creates a DynamoDB table with default return type (converted table data). + * + * @param options - Optional configuration for table creation + * @returns A promise that resolves to converted table data + * + * @example + * ```typescript + * const tableData = await UserTableManager.createTable(); + * ``` + */ public createTable(options?: TableCreateOptions & { return?: 'default' }): Promise; + + /** + * Creates a DynamoDB table, returning the raw AWS response. + * + * @param options - Configuration for table creation with return type 'output' + * @returns A promise that resolves to the raw CreateTableCommandOutput + * + * @example + * ```typescript + * const response = await UserTableManager.createTable({ return: 'output' }); + * console.log(response.TableDescription); + * ``` + */ public createTable(options: TableCreateOptions & { return: 'output' }): Promise; + + /** + * Builds the CreateTable command input without executing it. + * + * @param options - Configuration for table creation with return type 'input' + * @returns The CreateTableCommandInput object + * + * @example + * ```typescript + * const input = UserTableManager.createTable({ return: 'input' }); + * // Use input with AWS SDK directly + * ``` + */ public createTable(options: TableCreateOptions & { return: 'input' }): CreateTableCommandInput; + + /** + * Creates a DynamoDB table with the specified configuration. + * + * @param options - Optional configuration for table creation + * @returns A promise that resolves to table data, raw AWS response, or command input + * + * @example + * ```typescript + * // Create table with custom options + * const tableData = await UserTableManager.createTable({ + * throughput: { read: 5, write: 5 }, + * tags: { Environment: 'production' }, + * deletionProtection: true + * }); + * ``` + */ public createTable( options?: TableCreateOptions, ): Promise | CreateTableCommandInput { @@ -178,15 +265,69 @@ export default class TableManager, TE extends typeof Enti })(); } + /** + * Deletes a DynamoDB table with default return type (converted table data). + * + * @param tableName - The name of the table to delete + * @param options - Optional configuration for table deletion + * @returns A promise that resolves to converted table data + * + * @example + * ```typescript + * const tableData = await UserTableManager.deleteTable('UserTable'); + * ``` + */ public deleteTable(tableName: string, options?: TableDeleteOptions & { return?: 'default' }): Promise; + /** + * Deletes a DynamoDB table, returning the raw AWS response. + * + * @param tableName - The name of the table to delete + * @param options - Configuration for table deletion with return type 'output' + * @returns A promise that resolves to the raw DeleteTableCommandOutput + * + * @example + * ```typescript + * const response = await UserTableManager.deleteTable('UserTable', { return: 'output' }); + * console.log(response.TableDescription); + * ``` + */ public deleteTable( tableName: string, options: TableDeleteOptions & { return: 'output' }, ): Promise; + /** + * Builds the DeleteTable command input without executing it. + * + * @param tableName - The name of the table to delete + * @param options - Configuration for table deletion with return type 'input' + * @returns The DeleteTableCommandInput object + * + * @example + * ```typescript + * const input = UserTableManager.deleteTable('UserTable', { return: 'input' }); + * // Use input with AWS SDK directly + * ``` + */ public deleteTable(tableName: string, options: TableDeleteOptions & { return: 'input' }): DeleteTableCommandInput; + /** + * Deletes a DynamoDB table with the specified configuration. + * + * @param tableName - The name of the table to delete + * @param options - Optional configuration for table deletion + * @returns A promise that resolves to table data, raw AWS response, or command input + * @throws {ValidationError} When the table name doesn't match the table metadata + * + * @example + * ```typescript + * // Delete table with custom options + * const tableData = await UserTableManager.deleteTable('UserTable', { + * extraInput: { BillingMode: 'PAY_PER_REQUEST' } + * }); + * ``` + */ public deleteTable( tableName: string, options?: TableDeleteOptions, @@ -212,21 +353,75 @@ export default class TableManager, TE extends typeof Enti })(); } + /** + * Creates a table index with default return type (converted table data). + * + * @param indexName - The name of the index to create + * @param options - Optional configuration for index creation + * @returns A promise that resolves to converted table data + * + * @example + * ```typescript + * const tableData = await UserTableManager.createTableIndex('GSI_1_NAME'); + * ``` + */ public createTableIndex( indexName: TableIndexNames, options?: TableCreateIndexOptions & { return?: 'default' }, ): Promise; + /** + * Creates a table index, returning the raw AWS response. + * + * @param indexName - The name of the index to create + * @param options - Configuration for index creation with return type 'output' + * @returns A promise that resolves to the raw UpdateTableCommandOutput + * + * @example + * ```typescript + * const response = await UserTableManager.createTableIndex('GSI_1_NAME', { return: 'output' }); + * console.log(response.TableDescription); + * ``` + */ public createTableIndex( indexName: TableIndexNames, options: TableCreateIndexOptions & { return: 'output' }, ): Promise; + /** + * Builds the UpdateTable command input for creating an index without executing it. + * + * @param indexName - The name of the index to create + * @param options - Configuration for index creation with return type 'input' + * @returns The UpdateTableCommandInput object + * + * @example + * ```typescript + * const input = UserTableManager.createTableIndex('GSI_1_NAME', { return: 'input' }); + * // Use input with AWS SDK directly + * ``` + */ public createTableIndex( indexName: TableIndexNames, options: TableCreateIndexOptions & { return: 'input' }, ): UpdateTableCommandInput; + /** + * Creates a table index with the specified configuration. + * + * @param indexName - The name of the index to create + * @param options - Optional configuration for index creation + * @returns A promise that resolves to table data, raw AWS response, or command input + * @throws {ValidationError} When the index is not decorated in the entity or missing partition key + * + * @example + * ```typescript + * // Create index with custom throughput + * const tableData = await UserTableManager.createTableIndex('GSI_1_NAME', { + * throughput: { read: 5, write: 5 } + * }); + * ``` + */ public createTableIndex( indexName: TableIndexNames, options?: TableCreateIndexOptions, @@ -273,21 +468,75 @@ export default class TableManager, TE extends typeof Enti })(); } + /** + * Deletes a table index with default return type (converted table data). + * + * @param indexName - The name of the index to delete + * @param options - Optional configuration for index deletion + * @returns A promise that resolves to converted table data + * + * @example + * ```typescript + * const tableData = await UserTableManager.deleteTableIndex('OldIndex'); + * ``` + */ public deleteTableIndex( indexName: string, options?: TableDeleteIndexOptions & { return?: 'default' }, ): Promise; + /** + * Deletes a table index, returning the raw AWS response. + * + * @param indexName - The name of the index to delete + * @param options - Configuration for index deletion with return type 'output' + * @returns A promise that resolves to the raw UpdateTableCommandOutput + * + * @example + * ```typescript + * const response = await UserTableManager.deleteTableIndex('OldIndex', { return: 'output' }); + * console.log(response.TableDescription); + * ``` + */ public deleteTableIndex( indexName: string, options: TableDeleteIndexOptions & { return: 'output' }, ): Promise; + /** + * Builds the UpdateTable command input for deleting an index without executing it. + * + * @param indexName - The name of the index to delete + * @param options - Configuration for index deletion with return type 'input' + * @returns The UpdateTableCommandInput object + * + * @example + * ```typescript + * const input = UserTableManager.deleteTableIndex('OldIndex', { return: 'input' }); + * // Use input with AWS SDK directly + * ``` + */ public deleteTableIndex( indexName: string, options: TableDeleteIndexOptions & { return: 'input' }, ): UpdateTableCommandInput; + /** + * Deletes a table index with the specified configuration. + * + * @param indexName - The name of the index to delete + * @param options - Optional configuration for index deletion + * @returns A promise that resolves to table data, raw AWS response, or command input + * @throws {ValidationError} When the index is still decorated in the entity + * + * @example + * ```typescript + * // Delete index with custom options + * const tableData = await UserTableManager.deleteTableIndex('OldIndex', { + * extraInput: { BillingMode: 'PAY_PER_REQUEST' } + * }); + * ``` + */ public deleteTableIndex( indexName: string, options?: TableDeleteIndexOptions, @@ -321,15 +570,13 @@ export default class TableManager, TE extends typeof Enti } /** - * Validates that the existing DynamoDB table matches the table metadata configuration. + * Validates a table with default return type (converted table data). * * @param options - Optional configuration for table validation - * @returns A promise that resolves to table data - * @throws {ValidationError} When the table structure doesn't match the metadata + * @returns A promise that resolves to converted table data * * @example * ```typescript - * // Validate the table structure * const tableData = await UserTableManager.validateTable(); * ``` */ @@ -340,6 +587,12 @@ export default class TableManager, TE extends typeof Enti * * @param options - Configuration for table validation with return type 'output' * @returns A promise that resolves to the raw DescribeTableCommandOutput + * + * @example + * ```typescript + * const response = await UserTableManager.validateTable({ return: 'output' }); + * console.log(response.Table); + * ``` */ public validateTable(options: TableValidateOptions & { return: 'output' }): Promise; @@ -348,6 +601,12 @@ export default class TableManager, TE extends typeof Enti * * @param options - Configuration for table validation with return type 'input' * @returns The DescribeTableCommandInput object + * + * @example + * ```typescript + * const input = UserTableManager.validateTable({ return: 'input' }); + * // Use input with AWS SDK directly + * ``` */ public validateTable(options: TableValidateOptions & { return: 'input' }): DescribeTableCommandInput;