@opble/repository-dynamodb 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/repository.ts","../src/types.ts"],"names":["debug","createDebug","SORT_ASCENDING","BATCH_SIZE","now","AbstractDynamoDBRepository","table","factory","DynamoDBDocumentClient","DynamoDBClient","id","hashKey","query","pagination","sorting","lastEvaluatedKey","currentPage","resultItems","filterReturnedItemsExcludeSortKey","items","item","command","QueryCommand","sortKey","result","strict","keys","chunks","i","chunk","BatchGetCommand","sortOrder","a","b","filter","ScanCommand","batches","batch","deleteRequests","key","BatchWriteCommand","response","unprocessedItems","unprocessedKeys","error","GetCommand","isTimestamps","isTimestamp","PutCommand","DeleteCommand","TableKeychema","z","TableSpecSchema","QueryWithPaginationSchema","val","QueryWithSortingSchema","QueryAllSchema","ScanFilterSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport {\n BatchGetCommand,\n BatchWriteCommand,\n DeleteCommand,\n DynamoDBDocumentClient,\n GetCommand,\n PutCommand,\n QueryCommand,\n ScanCommand,\n} from '@aws-sdk/lib-dynamodb';\nimport { createDebug } from '@opble/debug';\nimport { isTimestamp, isTimestamps } from '@opble/entity';\nimport { Factory, HashMap } from '@opble/types';\n\nimport {\n DynamoDBRepository,\n QueryAll,\n QueryWithPagination,\n QueryWithSorting,\n ScanFilter,\n TableKey,\n TableSpec,\n} from './types';\n\nconst debug = createDebug('opble:repository-dynamodb');\nconst SORT_ASCENDING = 'asc';\nconst BATCH_SIZE = 50;\n\nfunction now(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nexport class AbstractDynamoDBRepository<\n T extends HashMap,\n ID extends TableKey = TableKey,\n> implements DynamoDBRepository<T, ID> {\n protected client: DynamoDBDocumentClient;\n\n constructor(\n protected table: TableSpec,\n protected factory: Factory<T>\n ) {\n this.client = DynamoDBDocumentClient.from(new DynamoDBClient({}));\n }\n\n protected getKey(id: ID): HashMap {\n return {\n [this.table.keys.hashKey]: id.hashKey,\n ...(this.table.keys.sortKey\n ? { [this.table.keys.sortKey]: id.sortKey }\n : {}),\n };\n }\n\n async query(\n hashKey: unknown,\n query?: QueryAll,\n pagination?: QueryWithPagination,\n sorting?: Pick<QueryWithSorting, 'sortOrder'>\n ): Promise<T[]> {\n /**\n * Internal state for pagination\n * - lastEvaluatedKey tells DynamoDB where to resume from\n * - currentPage helps us skip earlier pages\n * - resultItems will store the final page’s items\n */\n let lastEvaluatedKey: Record<string, unknown> | undefined;\n let currentPage = 1;\n let resultItems: HashMap[] = [];\n const filterReturnedItemsExcludeSortKey = (items: HashMap[]) => {\n /**\n * Business rule: Exclude a specific sortKey (if requested)\n * This is used to skip “parent” or “header” records within the same partition.\n */\n return query?.excludeSortKey && this.table.keys.sortKey\n ? items.filter(\n (item) => item[this.table.keys.sortKey!] !== query.excludeSortKey\n )\n : items;\n };\n\n do {\n /**\n * Prepare the QueryCommand\n * - Basic condition: match all items with the given hash key\n * - Add pagination via Limit + ExclusiveStartKey\n */\n const command = new QueryCommand({\n TableName: this.table.name,\n KeyConditionExpression: `#${this.table.keys.hashKey} = :hashKeyValue`,\n ExpressionAttributeNames: {\n [`#${this.table.keys.hashKey}`]: this.table.keys.hashKey,\n },\n ExpressionAttributeValues: {\n ':hashKeyValue': hashKey,\n },\n ExclusiveStartKey: lastEvaluatedKey,\n });\n\n if (pagination) {\n command.input.Limit = pagination.limit;\n }\n if (sorting) {\n command.input.ScanIndexForward = sorting.sortOrder === 'asc';\n }\n\n /**\n * Apply optional filters if provided\n */\n if (query) {\n // If querying a secondary index\n if (query.index) {\n command.input.IndexName = query.index.name;\n command.input.KeyConditionExpression = `#${query.index.keys.hashKey} = :hashKeyValue`;\n command.input.ExpressionAttributeNames = {\n [`#${query.index.keys.hashKey}`]: query.index.keys.hashKey,\n };\n }\n\n // If we want to match only sort keys starting with a specific prefix\n const sortKey = query.index?.keys.sortKey ?? this.table.keys.sortKey;\n if (query.sortKeyBeginsWith && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND begins_with(#${sortKey}, :sortKeyBeginsWith)`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyBeginsWith'] =\n query.sortKeyBeginsWith;\n } else if (query.sortKey && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND #${sortKey} = :sortKeyValue`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyValue'] =\n query.sortKey;\n } else if (query.sortKeyBetween && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND #${sortKey} BETWEEN :sortKeyStart AND :sortKeyEnd`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyStart'] =\n query.sortKeyBetween.start;\n command.input.ExpressionAttributeValues[':sortKeyEnd'] =\n query.sortKeyBetween.end;\n }\n }\n debug('command#input', command.input);\n\n // Execute the query\n const result = await this.client.send(command);\n\n // Keep the pagination pointer (if DynamoDB says there’s more data)\n lastEvaluatedKey = result.LastEvaluatedKey;\n\n if (!pagination) {\n // pagination is disabled, fetch until no results found\n resultItems.push(\n ...filterReturnedItemsExcludeSortKey(result.Items ?? [])\n );\n } else if (currentPage === pagination.page) {\n /**\n * Once we reach the desired page, capture those items.\n * DynamoDB doesn’t support direct page jumps — we simulate it by looping\n * until we reach the correct page number.\n */\n\n resultItems = filterReturnedItemsExcludeSortKey(result.Items ?? []);\n break; // Stop — we’ve collected the requested page\n }\n\n currentPage++; // Move to next page and continue querying\n } while (lastEvaluatedKey);\n\n /**\n * Map each DynamoDB item into an instance of your model class (T)\n * When querrying with index, turn strict to false\n */\n const strict = !query || !query.index;\n return resultItems.map((item) => this.factory.create(item, { strict }));\n }\n\n async queryInBulk(keys: HashMap[], query?: QueryWithSorting): Promise<T[]> {\n // Split keys into chunks of BATCH_SIZE (DynamoDB BatchGet limit)\n const chunks: HashMap[][] = [];\n for (let i = 0; i < keys.length; i += BATCH_SIZE) {\n chunks.push(keys.slice(i, i + BATCH_SIZE));\n }\n\n // Execute all batch requests concurrently\n const results = await Promise.all(\n chunks.map(async (chunk) => {\n const command = new BatchGetCommand({\n RequestItems: {\n [this.table.name]: { Keys: chunk },\n },\n });\n const response = await this.client.send(command);\n return response.Responses?.[this.table.name] ?? [];\n })\n );\n\n // Merge all items from all responses\n const mergedItems = results.flat();\n\n // Convert each item to T using factory\n const items = mergedItems.map((item) => this.factory.create(item));\n\n // Sort if query.sortBy is specified\n if (query && query.sortBy) {\n const sortOrder = query.sortOrder ?? SORT_ASCENDING;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n items.sort((a: any, b: any) => {\n if (a[query.sortBy!] < b[query.sortBy!])\n return sortOrder === SORT_ASCENDING ? -1 : 1;\n if (a[query.sortBy!] > b[query.sortBy!])\n return sortOrder === SORT_ASCENDING ? 1 : -1;\n return 0;\n });\n }\n\n return items;\n }\n\n async scan(\n pagination?: QueryWithPagination,\n filter?: ScanFilter\n ): Promise<T[]> {\n /**\n * Internal state for pagination\n * - lastEvaluatedKey tells DynamoDB where to resume from\n * - currentPage helps us skip earlier pages\n * - resultItems will store the final page’s items\n */\n let lastEvaluatedKey: Record<string, unknown> | undefined;\n let currentPage = 1;\n let resultItems: HashMap[] = [];\n\n do {\n /**\n * Prepare the ScanCommand\n * - Add pagination via Limit + ExclusiveStartKey\n */\n const command = new ScanCommand({\n TableName: this.table.name,\n ExclusiveStartKey: lastEvaluatedKey,\n });\n\n if (pagination) {\n command.input.Limit = pagination.limit;\n }\n\n if (filter) {\n command.input.FilterExpression = filter.filterExpression;\n command.input.ExpressionAttributeNames =\n filter.expressionAttributeNames;\n command.input.ExpressionAttributeValues =\n filter.expressionAttributeValues;\n }\n\n /**\n * Execute the scan\n */\n const result = await this.client.send(command);\n\n // Keep the pagination pointer (if DynamoDB says there’s more data)\n lastEvaluatedKey = result.LastEvaluatedKey;\n\n if (!pagination) {\n // pagination is disabled, fetch until no results found\n resultItems.push(...(result.Items ?? []));\n } else if (currentPage === pagination.page) {\n /**\n * Once we reach the desired page, capture those items.\n * DynamoDB doesn’t support direct page jumps — we simulate it by looping\n * until we reach the correct page number.\n */\n\n resultItems = result.Items ?? [];\n break; // Stop — we’ve collected the requested page\n }\n\n currentPage++; // Move to next page and continue querying\n } while (lastEvaluatedKey);\n\n /**\n * Map each DynamoDB item into an instance of your model class (T)\n */\n return resultItems.map((item) => this.factory.create(item));\n }\n\n async deleteAll(hashKey: unknown): Promise<void> {\n const items = await this.query(hashKey);\n const keys = items.map((item) => {\n if (this.table.keys.sortKey) {\n return {\n [this.table.keys.hashKey]: hashKey,\n [this.table.keys.sortKey]:\n item[this.table.keys.sortKey as keyof typeof item],\n };\n } else {\n return {\n [this.table.keys.hashKey]: hashKey,\n };\n }\n });\n\n await this.deleteInBulk(keys);\n }\n\n async deleteInBulk(keys: HashMap[]): Promise<void> {\n const batches = [];\n while (keys.length > 0) {\n batches.push(keys.splice(0, BATCH_SIZE));\n }\n\n for (const batch of batches) {\n // Create delete requests for each item in the batch\n const deleteRequests = batch.map((key) => ({\n DeleteRequest: { Key: key },\n }));\n\n // Execute the BatchWriteCommand\n const command = new BatchWriteCommand({\n RequestItems: {\n [this.table.name]: deleteRequests,\n },\n });\n\n try {\n const response = await this.client.send(command);\n debug('[INFO] Batch delete response:', response);\n\n // Check if there are unprocessed items and retry them if necessary\n if (\n response.UnprocessedItems &&\n response.UnprocessedItems[this.table.name]\n ) {\n debug('[WARN] Unprocessed items found. Retrying...');\n const unprocessedItems = response.UnprocessedItems[this.table.name];\n if (unprocessedItems === undefined) {\n continue;\n }\n const unprocessedKeys = unprocessedItems\n .map((item) => item.DeleteRequest?.Key)\n .filter((key): key is Record<string, unknown> => key != null); // Filter out undefined keys\n\n await this.deleteInBulk(unprocessedKeys);\n }\n } catch (error) {\n debug('[ERROR] Error deleting items in batch:', error);\n }\n }\n }\n\n async get(id: ID): Promise<T | null> {\n const command = new GetCommand({\n TableName: this.table.name,\n Key: this.getKey(id),\n });\n\n const result = await this.client.send(command);\n return result.Item ? this.factory.create(result.Item) : null;\n }\n\n /**\n * Allow to manipulate the data before saving\n * @param item\n * @returns\n */\n protected beforeSave(item: T): T {\n if (isTimestamps(item)) {\n item.updatedAt = now();\n } else if (isTimestamp(item)) {\n item.timestamp = now();\n }\n\n return item;\n }\n\n async save(item: T): Promise<T> {\n item = this.beforeSave(item);\n const command = new PutCommand({\n TableName: this.table.name,\n Item: { ...item },\n });\n await this.client.send(command);\n\n return item;\n }\n\n async delete(id: ID): Promise<void | T> {\n await this.client.send(\n new DeleteCommand({\n TableName: this.table.name,\n Key: this.getKey(id),\n })\n );\n }\n}\n","import {\n DeletableRepository,\n HashMap,\n ReadableRepository,\n SavableRepository,\n} from '@opble/types';\nimport { z } from 'zod';\n\nexport const TableKeychema = z.object({\n hashKey: z.string(),\n sortKey: z.union([z.string(), z.number()]).optional(),\n});\n\nexport const TableSpecSchema = z.object({\n name: z.string(),\n keys: z.object({\n hashKey: z.string(),\n sortKey: z.string().optional(),\n }),\n});\n\nexport const QueryWithPaginationSchema = z.object({\n page: z\n .string()\n .optional()\n .transform((val) => (val ? parseInt(val, 10) : 1)) // default 1\n .refine((val) => val >= 1, { message: 'Page must be at least 1' }),\n\n limit: z\n .string()\n .optional()\n .transform((val) => (val ? parseInt(val, 10) : 25)) // default 25\n .refine((val) => val >= 5 && val <= 100, {\n message: 'Limit must be between 5 and 100',\n }),\n});\n\nexport const QueryWithSortingSchema = z.object({\n sortBy: z.string().optional(),\n sortOrder: z\n .enum(['asc', 'desc'])\n .optional()\n .transform((val) => val ?? 'asc'),\n});\n\nexport const QueryAllSchema = z.object({\n excludeSortKey: z.string().optional(),\n sortKeyBetween: z\n .object({\n start: z.string(),\n end: z.string(),\n })\n .optional(),\n sortKeyBeginsWith: z.string().optional(),\n sortKey: z.any().optional(),\n index: TableSpecSchema.optional(),\n});\n\nexport const ScanFilterSchema = z.object({\n filterExpression: z.string(),\n expressionAttributeNames: z.record(z.string(), z.string()),\n expressionAttributeValues: z.record(z.string(), z.any()),\n});\n\nexport type TableKey = z.infer<typeof TableKeychema>;\nexport type TableSpec = z.infer<typeof TableSpecSchema>;\n\nexport type QueryWithPagination = z.infer<typeof QueryWithPaginationSchema>;\nexport type QueryWithSorting = z.infer<typeof QueryWithSortingSchema>;\nexport type QueryAll = z.infer<typeof QueryAllSchema>;\nexport type ScanFilter = z.infer<typeof ScanFilterSchema>;\n\n/**\n * A generic repository interface specifically designed for DynamoDB operations.\n * It extends the basic CRUD repositories (Readable, Savable, Deletable) and adds\n * DynamoDB-specific methods for efficient querying, scanning, bulk operations,\n * and deletion patterns commonly used with DynamoDB's key structure.\n *\n * @template T - The type of the entity/document stored in DynamoDB (must be a HashMap / Record<string, unknown>)\n * @template ID - The type used to identify items. Defaults to `TableKey` (hash + optional sort key).\n */\nexport interface DynamoDBRepository<T extends HashMap, ID = TableKey>\n extends\n ReadableRepository<T, ID>,\n SavableRepository<T>,\n DeletableRepository<T, ID> {\n /**\n * Queries items using a partition key (hashKey) and optional sort key conditions.\n * This is the most common and efficient way to read data from a DynamoDB table\n * when you know the partition key.\n *\n * @param hashKey - The value of the partition key (hash key)\n * @param query - Optional advanced query conditions for sort key (begins_with, between, specific value, etc.)\n * @param pagination - Optional pagination parameters (page & limit)\n * @param sorting - Optional sort direction (only 'sortOrder' is used)\n * @returns Promise of array of matching items (T[])\n *\n * @example\n * repo.query(\"user#123\", { sortKeyBeginsWith: \"order#\" }, { limit: 20 }, { sortOrder: \"desc\" })\n */\n query(\n hashKey: unknown,\n query?: QueryAll,\n pagination?: QueryWithPagination,\n sorting?: Pick<QueryWithSorting, 'sortOrder'>\n ): Promise<T[]>;\n\n /**\n * Batch retrieves multiple items using their full composite keys (hash + sort).\n * This method uses `BatchGetItem` under the hood — much more efficient than individual gets\n * when fetching many items by primary key.\n *\n * @param keys - Array of objects containing at least `{ hashKey, sortKey? }`\n * @param query - Optional sorting preferences (rarely used in batch get)\n * @returns Promise of array of found items (in the order requested, missing items are omitted)\n *\n * @example\n * repo.queryInBulk([\n * { hashKey: \"user#abc\", sortKey: \"profile\" },\n * { hashKey: \"user#abc\", sortKey: \"settings\" }\n * ])\n */\n queryInBulk(keys: HashMap[], query?: QueryWithSorting): Promise<T[]>;\n\n /**\n * Performs a full table or index scan with optional filtering and pagination.\n * Scans are less efficient than queries — use only when you cannot use a partition key.\n *\n * @param pagination - Optional page & limit controls\n * @param filter - Optional raw filter expression (FilterExpression + attribute names/values)\n * @returns Promise of array of matching items\n *\n * @warning Scans can be expensive and slow on large tables — prefer query() when possible\n */\n scan(pagination?: QueryWithPagination, filter?: ScanFilter): Promise<T[]>;\n\n /**\n * Deletes **all items** that share the same partition key (hashKey).\n * Useful for cleaning up all records related to a specific entity (e.g. all orders of a user).\n *\n * @param hashKey - The partition key value whose items should be deleted\n * @returns Promise that resolves when deletion is complete\n *\n * @warning This operation may consume many write capacity units if the partition is large.\n * Consider using Query + BatchWriteItem in production for very large partitions.\n */\n deleteAll(hashKey: unknown): Promise<void>;\n\n /**\n * Deletes multiple items in a single BatchWriteItem call using their full keys.\n * Most efficient way to delete many known items at once.\n *\n * @param keys - Array of objects with hashKey and sortKey\n * @returns Promise that resolves when all deletions are complete\n *\n * @example\n * repo.deleteInBulk([\n * { hashKey: \"user#123\", sortKey: \"session#abc123\" },\n * { hashKey: \"user#123\", sortKey: \"session#def456\" }\n * ])\n */\n deleteInBulk(keys: HashMap[]): Promise<void>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/repository.ts","../src/types.ts"],"names":["debug","createDebug","SORT_ASCENDING","BATCH_SIZE","now","AbstractDynamoDBRepository","table","factory","DynamoDBDocumentClient","DynamoDBClient","id","hashKey","query","pagination","sorting","lastEvaluatedKey","currentPage","resultItems","filterReturnedItemsExcludeSortKey","items","item","command","QueryCommand","sortKey","result","strict","keys","chunks","i","chunk","BatchGetCommand","sortOrder","a","b","filter","ScanCommand","batches","batch","deleteRequests","key","BatchWriteCommand","response","unprocessedItems","unprocessedKeys","error","GetCommand","isTimestamps","isTimestamp","PutCommand","DeleteCommand","TableKeychema","z","TableSpecSchema","QueryWithPaginationSchema","val","QueryWithSortingSchema","QueryAllSchema","ScanFilterSchema"],"mappings":"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","file":"index.cjs","sourcesContent":["import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport {\n BatchGetCommand,\n BatchWriteCommand,\n DeleteCommand,\n DynamoDBDocumentClient,\n GetCommand,\n PutCommand,\n QueryCommand,\n ScanCommand,\n} from '@aws-sdk/lib-dynamodb';\nimport { createDebug } from '@opble/debug';\nimport { isTimestamp, isTimestamps } from '@opble/entity';\nimport { Factory, HashMap } from '@opble/types';\n\nimport {\n DynamoDBRepository,\n QueryAll,\n QueryWithPagination,\n QueryWithSorting,\n ScanFilter,\n TableKey,\n TableSpec,\n} from './types';\n\nconst debug = createDebug('opble:repository-dynamodb');\nconst SORT_ASCENDING = 'asc';\nconst BATCH_SIZE = 50;\n\nfunction now(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nexport abstract class AbstractDynamoDBRepository<\n T extends HashMap,\n ID extends TableKey = TableKey,\n> implements DynamoDBRepository<T, ID> {\n protected client: DynamoDBDocumentClient;\n\n constructor(\n protected table: TableSpec,\n protected factory: Factory<T>\n ) {\n this.client = DynamoDBDocumentClient.from(new DynamoDBClient({}));\n }\n\n protected getKey(id: ID): HashMap {\n return {\n [this.table.keys.hashKey]: id.hashKey,\n ...(this.table.keys.sortKey\n ? { [this.table.keys.sortKey]: id.sortKey }\n : {}),\n };\n }\n\n async query(\n hashKey: unknown,\n query?: QueryAll,\n pagination?: QueryWithPagination,\n sorting?: Pick<QueryWithSorting, 'sortOrder'>\n ): Promise<T[]> {\n /**\n * Internal state for pagination\n * - lastEvaluatedKey tells DynamoDB where to resume from\n * - currentPage helps us skip earlier pages\n * - resultItems will store the final page’s items\n */\n let lastEvaluatedKey: Record<string, unknown> | undefined;\n let currentPage = 1;\n let resultItems: HashMap[] = [];\n const filterReturnedItemsExcludeSortKey = (items: HashMap[]) => {\n /**\n * Business rule: Exclude a specific sortKey (if requested)\n * This is used to skip “parent” or “header” records within the same partition.\n */\n return query?.excludeSortKey && this.table.keys.sortKey\n ? items.filter(\n (item) => item[this.table.keys.sortKey!] !== query.excludeSortKey\n )\n : items;\n };\n\n do {\n /**\n * Prepare the QueryCommand\n * - Basic condition: match all items with the given hash key\n * - Add pagination via Limit + ExclusiveStartKey\n */\n const command = new QueryCommand({\n TableName: this.table.name,\n KeyConditionExpression: `#${this.table.keys.hashKey} = :hashKeyValue`,\n ExpressionAttributeNames: {\n [`#${this.table.keys.hashKey}`]: this.table.keys.hashKey,\n },\n ExpressionAttributeValues: {\n ':hashKeyValue': hashKey,\n },\n ExclusiveStartKey: lastEvaluatedKey,\n });\n\n if (pagination) {\n command.input.Limit = pagination.limit;\n }\n if (sorting) {\n command.input.ScanIndexForward = sorting.sortOrder === 'asc';\n }\n\n /**\n * Apply optional filters if provided\n */\n if (query) {\n // If querying a secondary index\n if (query.index) {\n command.input.IndexName = query.index.name;\n command.input.KeyConditionExpression = `#${query.index.keys.hashKey} = :hashKeyValue`;\n command.input.ExpressionAttributeNames = {\n [`#${query.index.keys.hashKey}`]: query.index.keys.hashKey,\n };\n }\n\n // If we want to match only sort keys starting with a specific prefix\n const sortKey = query.index?.keys.sortKey ?? this.table.keys.sortKey;\n if (query.sortKeyBeginsWith && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND begins_with(#${sortKey}, :sortKeyBeginsWith)`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyBeginsWith'] =\n query.sortKeyBeginsWith;\n } else if (query.sortKey && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND #${sortKey} = :sortKeyValue`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyValue'] =\n query.sortKey;\n } else if (query.sortKeyBetween && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND #${sortKey} BETWEEN :sortKeyStart AND :sortKeyEnd`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyStart'] =\n query.sortKeyBetween.start;\n command.input.ExpressionAttributeValues[':sortKeyEnd'] =\n query.sortKeyBetween.end;\n }\n }\n debug('command#input', command.input);\n\n // Execute the query\n const result = await this.client.send(command);\n\n // Keep the pagination pointer (if DynamoDB says there’s more data)\n lastEvaluatedKey = result.LastEvaluatedKey;\n\n if (!pagination) {\n // pagination is disabled, fetch until no results found\n resultItems.push(\n ...filterReturnedItemsExcludeSortKey(result.Items ?? [])\n );\n } else if (currentPage === pagination.page) {\n /**\n * Once we reach the desired page, capture those items.\n * DynamoDB doesn’t support direct page jumps — we simulate it by looping\n * until we reach the correct page number.\n */\n\n resultItems = filterReturnedItemsExcludeSortKey(result.Items ?? []);\n break; // Stop — we’ve collected the requested page\n }\n\n currentPage++; // Move to next page and continue querying\n } while (lastEvaluatedKey);\n\n /**\n * Map each DynamoDB item into an instance of your model class (T)\n * When querrying with index, turn strict to false\n */\n const strict = !query || !query.index;\n return resultItems.map((item) => this.factory.create(item, { strict }));\n }\n\n async queryInBulk(keys: HashMap[], query?: QueryWithSorting): Promise<T[]> {\n // Split keys into chunks of BATCH_SIZE (DynamoDB BatchGet limit)\n const chunks: HashMap[][] = [];\n for (let i = 0; i < keys.length; i += BATCH_SIZE) {\n chunks.push(keys.slice(i, i + BATCH_SIZE));\n }\n\n // Execute all batch requests concurrently\n const results = await Promise.all(\n chunks.map(async (chunk) => {\n const command = new BatchGetCommand({\n RequestItems: {\n [this.table.name]: { Keys: chunk },\n },\n });\n const response = await this.client.send(command);\n return response.Responses?.[this.table.name] ?? [];\n })\n );\n\n // Merge all items from all responses\n const mergedItems = results.flat();\n\n // Convert each item to T using factory\n const items = mergedItems.map((item) => this.factory.create(item));\n\n // Sort if query.sortBy is specified\n if (query && query.sortBy) {\n const sortOrder = query.sortOrder ?? SORT_ASCENDING;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n items.sort((a: any, b: any) => {\n if (a[query.sortBy!] < b[query.sortBy!])\n return sortOrder === SORT_ASCENDING ? -1 : 1;\n if (a[query.sortBy!] > b[query.sortBy!])\n return sortOrder === SORT_ASCENDING ? 1 : -1;\n return 0;\n });\n }\n\n return items;\n }\n\n async scan(\n pagination?: QueryWithPagination,\n filter?: ScanFilter\n ): Promise<T[]> {\n /**\n * Internal state for pagination\n * - lastEvaluatedKey tells DynamoDB where to resume from\n * - currentPage helps us skip earlier pages\n * - resultItems will store the final page’s items\n */\n let lastEvaluatedKey: Record<string, unknown> | undefined;\n let currentPage = 1;\n let resultItems: HashMap[] = [];\n\n do {\n /**\n * Prepare the ScanCommand\n * - Add pagination via Limit + ExclusiveStartKey\n */\n const command = new ScanCommand({\n TableName: this.table.name,\n ExclusiveStartKey: lastEvaluatedKey,\n });\n\n if (pagination) {\n command.input.Limit = pagination.limit;\n }\n\n if (filter) {\n command.input.FilterExpression = filter.filterExpression;\n command.input.ExpressionAttributeNames =\n filter.expressionAttributeNames;\n command.input.ExpressionAttributeValues =\n filter.expressionAttributeValues;\n }\n\n /**\n * Execute the scan\n */\n const result = await this.client.send(command);\n\n // Keep the pagination pointer (if DynamoDB says there’s more data)\n lastEvaluatedKey = result.LastEvaluatedKey;\n\n if (!pagination) {\n // pagination is disabled, fetch until no results found\n resultItems.push(...(result.Items ?? []));\n } else if (currentPage === pagination.page) {\n /**\n * Once we reach the desired page, capture those items.\n * DynamoDB doesn’t support direct page jumps — we simulate it by looping\n * until we reach the correct page number.\n */\n\n resultItems = result.Items ?? [];\n break; // Stop — we’ve collected the requested page\n }\n\n currentPage++; // Move to next page and continue querying\n } while (lastEvaluatedKey);\n\n /**\n * Map each DynamoDB item into an instance of your model class (T)\n */\n return resultItems.map((item) => this.factory.create(item));\n }\n\n async deleteAll(hashKey: unknown): Promise<void> {\n const items = await this.query(hashKey);\n const keys = items.map((item) => {\n if (this.table.keys.sortKey) {\n return {\n [this.table.keys.hashKey]: hashKey,\n [this.table.keys.sortKey]:\n item[this.table.keys.sortKey as keyof typeof item],\n };\n } else {\n return {\n [this.table.keys.hashKey]: hashKey,\n };\n }\n });\n\n await this.deleteInBulk(keys);\n }\n\n async deleteInBulk(keys: HashMap[]): Promise<void> {\n const batches = [];\n while (keys.length > 0) {\n batches.push(keys.splice(0, BATCH_SIZE));\n }\n\n for (const batch of batches) {\n // Create delete requests for each item in the batch\n const deleteRequests = batch.map((key) => ({\n DeleteRequest: { Key: key },\n }));\n\n // Execute the BatchWriteCommand\n const command = new BatchWriteCommand({\n RequestItems: {\n [this.table.name]: deleteRequests,\n },\n });\n\n try {\n const response = await this.client.send(command);\n debug('[INFO] Batch delete response:', response);\n\n // Check if there are unprocessed items and retry them if necessary\n if (\n response.UnprocessedItems &&\n response.UnprocessedItems[this.table.name]\n ) {\n debug('[WARN] Unprocessed items found. Retrying...');\n const unprocessedItems = response.UnprocessedItems[this.table.name];\n if (unprocessedItems === undefined) {\n continue;\n }\n const unprocessedKeys = unprocessedItems\n .map((item) => item.DeleteRequest?.Key)\n .filter((key): key is Record<string, unknown> => key != null); // Filter out undefined keys\n\n await this.deleteInBulk(unprocessedKeys);\n }\n } catch (error) {\n debug('[ERROR] Error deleting items in batch:', error);\n }\n }\n }\n\n async get(id: ID): Promise<T | null> {\n const command = new GetCommand({\n TableName: this.table.name,\n Key: this.getKey(id),\n });\n\n const result = await this.client.send(command);\n return result.Item ? this.factory.create(result.Item) : null;\n }\n\n /**\n * Allow to manipulate the data before saving\n * @param item\n * @returns\n */\n protected beforeSave(item: T): T {\n if (isTimestamps(item)) {\n item.updatedAt = now();\n } else if (isTimestamp(item)) {\n item.timestamp = now();\n }\n\n return item;\n }\n\n async save(item: T): Promise<T> {\n item = this.beforeSave(item);\n const command = new PutCommand({\n TableName: this.table.name,\n Item: { ...item },\n });\n await this.client.send(command);\n\n return item;\n }\n\n async delete(id: ID): Promise<void | T> {\n await this.client.send(\n new DeleteCommand({\n TableName: this.table.name,\n Key: this.getKey(id),\n })\n );\n }\n}\n","import {\n DeletableRepository,\n HashMap,\n ReadableRepository,\n SavableRepository,\n} from '@opble/types';\nimport { z } from 'zod';\n\nexport const TableKeychema = z.object({\n hashKey: z.string(),\n sortKey: z.union([z.string(), z.number()]).optional(),\n});\n\nexport const TableSpecSchema = z.object({\n name: z.string(),\n keys: z.object({\n hashKey: z.string(),\n sortKey: z.string().optional(),\n }),\n});\n\nexport const QueryWithPaginationSchema = z.object({\n page: z\n .string()\n .optional()\n .transform((val) => (val ? parseInt(val, 10) : 1)) // default 1\n .refine((val) => val >= 1, { message: 'Page must be at least 1' }),\n\n limit: z\n .string()\n .optional()\n .transform((val) => (val ? parseInt(val, 10) : 25)) // default 25\n .refine((val) => val >= 5 && val <= 100, {\n message: 'Limit must be between 5 and 100',\n }),\n});\n\nexport const QueryWithSortingSchema = z.object({\n sortBy: z.string().optional(),\n sortOrder: z\n .enum(['asc', 'desc'])\n .optional()\n .transform((val) => val ?? 'asc'),\n});\n\nexport const QueryAllSchema = z.object({\n excludeSortKey: z.string().optional(),\n sortKeyBetween: z\n .object({\n start: z.string(),\n end: z.string(),\n })\n .optional(),\n sortKeyBeginsWith: z.string().optional(),\n sortKey: z.any().optional(),\n index: TableSpecSchema.optional(),\n});\n\nexport const ScanFilterSchema = z.object({\n filterExpression: z.string(),\n expressionAttributeNames: z.record(z.string(), z.string()),\n expressionAttributeValues: z.record(z.string(), z.any()),\n});\n\nexport type TableKey = z.infer<typeof TableKeychema>;\nexport type TableSpec = z.infer<typeof TableSpecSchema>;\n\nexport type QueryWithPagination = z.infer<typeof QueryWithPaginationSchema>;\nexport type QueryWithSorting = z.infer<typeof QueryWithSortingSchema>;\nexport type QueryAll = z.infer<typeof QueryAllSchema>;\nexport type ScanFilter = z.infer<typeof ScanFilterSchema>;\n\n/**\n * A generic repository interface specifically designed for DynamoDB operations.\n * It extends the basic CRUD repositories (Readable, Savable, Deletable) and adds\n * DynamoDB-specific methods for efficient querying, scanning, bulk operations,\n * and deletion patterns commonly used with DynamoDB's key structure.\n *\n * @template T - The type of the entity/document stored in DynamoDB (must be a HashMap / Record<string, unknown>)\n * @template ID - The type used to identify items. Defaults to `TableKey` (hash + optional sort key).\n */\nexport interface DynamoDBRepository<T extends HashMap, ID = TableKey>\n extends\n ReadableRepository<T, ID>,\n SavableRepository<T>,\n DeletableRepository<T, ID> {\n /**\n * Queries items using a partition key (hashKey) and optional sort key conditions.\n * This is the most common and efficient way to read data from a DynamoDB table\n * when you know the partition key.\n *\n * @param hashKey - The value of the partition key (hash key)\n * @param query - Optional advanced query conditions for sort key (begins_with, between, specific value, etc.)\n * @param pagination - Optional pagination parameters (page & limit)\n * @param sorting - Optional sort direction (only 'sortOrder' is used)\n * @returns Promise of array of matching items (T[])\n *\n * @example\n * repo.query(\"user#123\", { sortKeyBeginsWith: \"order#\" }, { limit: 20 }, { sortOrder: \"desc\" })\n */\n query(\n hashKey: unknown,\n query?: QueryAll,\n pagination?: QueryWithPagination,\n sorting?: Pick<QueryWithSorting, 'sortOrder'>\n ): Promise<T[]>;\n\n /**\n * Batch retrieves multiple items using their full composite keys (hash + sort).\n * This method uses `BatchGetItem` under the hood — much more efficient than individual gets\n * when fetching many items by primary key.\n *\n * @param keys - Array of objects containing at least `{ hashKey, sortKey? }`\n * @param query - Optional sorting preferences (rarely used in batch get)\n * @returns Promise of array of found items (in the order requested, missing items are omitted)\n *\n * @example\n * repo.queryInBulk([\n * { hashKey: \"user#abc\", sortKey: \"profile\" },\n * { hashKey: \"user#abc\", sortKey: \"settings\" }\n * ])\n */\n queryInBulk(keys: HashMap[], query?: QueryWithSorting): Promise<T[]>;\n\n /**\n * Performs a full table or index scan with optional filtering and pagination.\n * Scans are less efficient than queries — use only when you cannot use a partition key.\n *\n * @param pagination - Optional page & limit controls\n * @param filter - Optional raw filter expression (FilterExpression + attribute names/values)\n * @returns Promise of array of matching items\n *\n * @warning Scans can be expensive and slow on large tables — prefer query() when possible\n */\n scan(pagination?: QueryWithPagination, filter?: ScanFilter): Promise<T[]>;\n\n /**\n * Deletes **all items** that share the same partition key (hashKey).\n * Useful for cleaning up all records related to a specific entity (e.g. all orders of a user).\n *\n * @param hashKey - The partition key value whose items should be deleted\n * @returns Promise that resolves when deletion is complete\n *\n * @warning This operation may consume many write capacity units if the partition is large.\n * Consider using Query + BatchWriteItem in production for very large partitions.\n */\n deleteAll(hashKey: unknown): Promise<void>;\n\n /**\n * Deletes multiple items in a single BatchWriteItem call using their full keys.\n * Most efficient way to delete many known items at once.\n *\n * @param keys - Array of objects with hashKey and sortKey\n * @returns Promise that resolves when all deletions are complete\n *\n * @example\n * repo.deleteInBulk([\n * { hashKey: \"user#123\", sortKey: \"session#abc123\" },\n * { hashKey: \"user#123\", sortKey: \"session#def456\" }\n * ])\n */\n deleteInBulk(keys: HashMap[]): Promise<void>;\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -130,7 +130,7 @@ interface DynamoDBRepository<T extends HashMap, ID = TableKey> extends ReadableR
|
|
|
130
130
|
deleteInBulk(keys: HashMap[]): Promise<void>;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
declare class AbstractDynamoDBRepository<T extends HashMap, ID extends TableKey = TableKey> implements DynamoDBRepository<T, ID> {
|
|
133
|
+
declare abstract class AbstractDynamoDBRepository<T extends HashMap, ID extends TableKey = TableKey> implements DynamoDBRepository<T, ID> {
|
|
134
134
|
protected table: TableSpec;
|
|
135
135
|
protected factory: Factory<T>;
|
|
136
136
|
protected client: DynamoDBDocumentClient;
|
package/dist/index.d.ts
CHANGED
|
@@ -130,7 +130,7 @@ interface DynamoDBRepository<T extends HashMap, ID = TableKey> extends ReadableR
|
|
|
130
130
|
deleteInBulk(keys: HashMap[]): Promise<void>;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
declare class AbstractDynamoDBRepository<T extends HashMap, ID extends TableKey = TableKey> implements DynamoDBRepository<T, ID> {
|
|
133
|
+
declare abstract class AbstractDynamoDBRepository<T extends HashMap, ID extends TableKey = TableKey> implements DynamoDBRepository<T, ID> {
|
|
134
134
|
protected table: TableSpec;
|
|
135
135
|
protected factory: Factory<T>;
|
|
136
136
|
protected client: DynamoDBDocumentClient;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/repository.ts","../src/types.ts"],"names":["debug","createDebug","SORT_ASCENDING","BATCH_SIZE","now","AbstractDynamoDBRepository","table","factory","DynamoDBDocumentClient","DynamoDBClient","id","hashKey","query","pagination","sorting","lastEvaluatedKey","currentPage","resultItems","filterReturnedItemsExcludeSortKey","items","item","command","QueryCommand","sortKey","result","strict","keys","chunks","i","chunk","BatchGetCommand","sortOrder","a","b","filter","ScanCommand","batches","batch","deleteRequests","key","BatchWriteCommand","response","unprocessedItems","unprocessedKeys","error","GetCommand","isTimestamps","isTimestamp","PutCommand","DeleteCommand","TableKeychema","z","TableSpecSchema","QueryWithPaginationSchema","val","QueryWithSortingSchema","QueryAllSchema","ScanFilterSchema"],"mappings":"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","file":"index.js","sourcesContent":["import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport {\n BatchGetCommand,\n BatchWriteCommand,\n DeleteCommand,\n DynamoDBDocumentClient,\n GetCommand,\n PutCommand,\n QueryCommand,\n ScanCommand,\n} from '@aws-sdk/lib-dynamodb';\nimport { createDebug } from '@opble/debug';\nimport { isTimestamp, isTimestamps } from '@opble/entity';\nimport { Factory, HashMap } from '@opble/types';\n\nimport {\n DynamoDBRepository,\n QueryAll,\n QueryWithPagination,\n QueryWithSorting,\n ScanFilter,\n TableKey,\n TableSpec,\n} from './types';\n\nconst debug = createDebug('opble:repository-dynamodb');\nconst SORT_ASCENDING = 'asc';\nconst BATCH_SIZE = 50;\n\nfunction now(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nexport class AbstractDynamoDBRepository<\n T extends HashMap,\n ID extends TableKey = TableKey,\n> implements DynamoDBRepository<T, ID> {\n protected client: DynamoDBDocumentClient;\n\n constructor(\n protected table: TableSpec,\n protected factory: Factory<T>\n ) {\n this.client = DynamoDBDocumentClient.from(new DynamoDBClient({}));\n }\n\n protected getKey(id: ID): HashMap {\n return {\n [this.table.keys.hashKey]: id.hashKey,\n ...(this.table.keys.sortKey\n ? { [this.table.keys.sortKey]: id.sortKey }\n : {}),\n };\n }\n\n async query(\n hashKey: unknown,\n query?: QueryAll,\n pagination?: QueryWithPagination,\n sorting?: Pick<QueryWithSorting, 'sortOrder'>\n ): Promise<T[]> {\n /**\n * Internal state for pagination\n * - lastEvaluatedKey tells DynamoDB where to resume from\n * - currentPage helps us skip earlier pages\n * - resultItems will store the final page’s items\n */\n let lastEvaluatedKey: Record<string, unknown> | undefined;\n let currentPage = 1;\n let resultItems: HashMap[] = [];\n const filterReturnedItemsExcludeSortKey = (items: HashMap[]) => {\n /**\n * Business rule: Exclude a specific sortKey (if requested)\n * This is used to skip “parent” or “header” records within the same partition.\n */\n return query?.excludeSortKey && this.table.keys.sortKey\n ? items.filter(\n (item) => item[this.table.keys.sortKey!] !== query.excludeSortKey\n )\n : items;\n };\n\n do {\n /**\n * Prepare the QueryCommand\n * - Basic condition: match all items with the given hash key\n * - Add pagination via Limit + ExclusiveStartKey\n */\n const command = new QueryCommand({\n TableName: this.table.name,\n KeyConditionExpression: `#${this.table.keys.hashKey} = :hashKeyValue`,\n ExpressionAttributeNames: {\n [`#${this.table.keys.hashKey}`]: this.table.keys.hashKey,\n },\n ExpressionAttributeValues: {\n ':hashKeyValue': hashKey,\n },\n ExclusiveStartKey: lastEvaluatedKey,\n });\n\n if (pagination) {\n command.input.Limit = pagination.limit;\n }\n if (sorting) {\n command.input.ScanIndexForward = sorting.sortOrder === 'asc';\n }\n\n /**\n * Apply optional filters if provided\n */\n if (query) {\n // If querying a secondary index\n if (query.index) {\n command.input.IndexName = query.index.name;\n command.input.KeyConditionExpression = `#${query.index.keys.hashKey} = :hashKeyValue`;\n command.input.ExpressionAttributeNames = {\n [`#${query.index.keys.hashKey}`]: query.index.keys.hashKey,\n };\n }\n\n // If we want to match only sort keys starting with a specific prefix\n const sortKey = query.index?.keys.sortKey ?? this.table.keys.sortKey;\n if (query.sortKeyBeginsWith && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND begins_with(#${sortKey}, :sortKeyBeginsWith)`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyBeginsWith'] =\n query.sortKeyBeginsWith;\n } else if (query.sortKey && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND #${sortKey} = :sortKeyValue`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyValue'] =\n query.sortKey;\n } else if (query.sortKeyBetween && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND #${sortKey} BETWEEN :sortKeyStart AND :sortKeyEnd`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyStart'] =\n query.sortKeyBetween.start;\n command.input.ExpressionAttributeValues[':sortKeyEnd'] =\n query.sortKeyBetween.end;\n }\n }\n debug('command#input', command.input);\n\n // Execute the query\n const result = await this.client.send(command);\n\n // Keep the pagination pointer (if DynamoDB says there’s more data)\n lastEvaluatedKey = result.LastEvaluatedKey;\n\n if (!pagination) {\n // pagination is disabled, fetch until no results found\n resultItems.push(\n ...filterReturnedItemsExcludeSortKey(result.Items ?? [])\n );\n } else if (currentPage === pagination.page) {\n /**\n * Once we reach the desired page, capture those items.\n * DynamoDB doesn’t support direct page jumps — we simulate it by looping\n * until we reach the correct page number.\n */\n\n resultItems = filterReturnedItemsExcludeSortKey(result.Items ?? []);\n break; // Stop — we’ve collected the requested page\n }\n\n currentPage++; // Move to next page and continue querying\n } while (lastEvaluatedKey);\n\n /**\n * Map each DynamoDB item into an instance of your model class (T)\n * When querrying with index, turn strict to false\n */\n const strict = !query || !query.index;\n return resultItems.map((item) => this.factory.create(item, { strict }));\n }\n\n async queryInBulk(keys: HashMap[], query?: QueryWithSorting): Promise<T[]> {\n // Split keys into chunks of BATCH_SIZE (DynamoDB BatchGet limit)\n const chunks: HashMap[][] = [];\n for (let i = 0; i < keys.length; i += BATCH_SIZE) {\n chunks.push(keys.slice(i, i + BATCH_SIZE));\n }\n\n // Execute all batch requests concurrently\n const results = await Promise.all(\n chunks.map(async (chunk) => {\n const command = new BatchGetCommand({\n RequestItems: {\n [this.table.name]: { Keys: chunk },\n },\n });\n const response = await this.client.send(command);\n return response.Responses?.[this.table.name] ?? [];\n })\n );\n\n // Merge all items from all responses\n const mergedItems = results.flat();\n\n // Convert each item to T using factory\n const items = mergedItems.map((item) => this.factory.create(item));\n\n // Sort if query.sortBy is specified\n if (query && query.sortBy) {\n const sortOrder = query.sortOrder ?? SORT_ASCENDING;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n items.sort((a: any, b: any) => {\n if (a[query.sortBy!] < b[query.sortBy!])\n return sortOrder === SORT_ASCENDING ? -1 : 1;\n if (a[query.sortBy!] > b[query.sortBy!])\n return sortOrder === SORT_ASCENDING ? 1 : -1;\n return 0;\n });\n }\n\n return items;\n }\n\n async scan(\n pagination?: QueryWithPagination,\n filter?: ScanFilter\n ): Promise<T[]> {\n /**\n * Internal state for pagination\n * - lastEvaluatedKey tells DynamoDB where to resume from\n * - currentPage helps us skip earlier pages\n * - resultItems will store the final page’s items\n */\n let lastEvaluatedKey: Record<string, unknown> | undefined;\n let currentPage = 1;\n let resultItems: HashMap[] = [];\n\n do {\n /**\n * Prepare the ScanCommand\n * - Add pagination via Limit + ExclusiveStartKey\n */\n const command = new ScanCommand({\n TableName: this.table.name,\n ExclusiveStartKey: lastEvaluatedKey,\n });\n\n if (pagination) {\n command.input.Limit = pagination.limit;\n }\n\n if (filter) {\n command.input.FilterExpression = filter.filterExpression;\n command.input.ExpressionAttributeNames =\n filter.expressionAttributeNames;\n command.input.ExpressionAttributeValues =\n filter.expressionAttributeValues;\n }\n\n /**\n * Execute the scan\n */\n const result = await this.client.send(command);\n\n // Keep the pagination pointer (if DynamoDB says there’s more data)\n lastEvaluatedKey = result.LastEvaluatedKey;\n\n if (!pagination) {\n // pagination is disabled, fetch until no results found\n resultItems.push(...(result.Items ?? []));\n } else if (currentPage === pagination.page) {\n /**\n * Once we reach the desired page, capture those items.\n * DynamoDB doesn’t support direct page jumps — we simulate it by looping\n * until we reach the correct page number.\n */\n\n resultItems = result.Items ?? [];\n break; // Stop — we’ve collected the requested page\n }\n\n currentPage++; // Move to next page and continue querying\n } while (lastEvaluatedKey);\n\n /**\n * Map each DynamoDB item into an instance of your model class (T)\n */\n return resultItems.map((item) => this.factory.create(item));\n }\n\n async deleteAll(hashKey: unknown): Promise<void> {\n const items = await this.query(hashKey);\n const keys = items.map((item) => {\n if (this.table.keys.sortKey) {\n return {\n [this.table.keys.hashKey]: hashKey,\n [this.table.keys.sortKey]:\n item[this.table.keys.sortKey as keyof typeof item],\n };\n } else {\n return {\n [this.table.keys.hashKey]: hashKey,\n };\n }\n });\n\n await this.deleteInBulk(keys);\n }\n\n async deleteInBulk(keys: HashMap[]): Promise<void> {\n const batches = [];\n while (keys.length > 0) {\n batches.push(keys.splice(0, BATCH_SIZE));\n }\n\n for (const batch of batches) {\n // Create delete requests for each item in the batch\n const deleteRequests = batch.map((key) => ({\n DeleteRequest: { Key: key },\n }));\n\n // Execute the BatchWriteCommand\n const command = new BatchWriteCommand({\n RequestItems: {\n [this.table.name]: deleteRequests,\n },\n });\n\n try {\n const response = await this.client.send(command);\n debug('[INFO] Batch delete response:', response);\n\n // Check if there are unprocessed items and retry them if necessary\n if (\n response.UnprocessedItems &&\n response.UnprocessedItems[this.table.name]\n ) {\n debug('[WARN] Unprocessed items found. Retrying...');\n const unprocessedItems = response.UnprocessedItems[this.table.name];\n if (unprocessedItems === undefined) {\n continue;\n }\n const unprocessedKeys = unprocessedItems\n .map((item) => item.DeleteRequest?.Key)\n .filter((key): key is Record<string, unknown> => key != null); // Filter out undefined keys\n\n await this.deleteInBulk(unprocessedKeys);\n }\n } catch (error) {\n debug('[ERROR] Error deleting items in batch:', error);\n }\n }\n }\n\n async get(id: ID): Promise<T | null> {\n const command = new GetCommand({\n TableName: this.table.name,\n Key: this.getKey(id),\n });\n\n const result = await this.client.send(command);\n return result.Item ? this.factory.create(result.Item) : null;\n }\n\n /**\n * Allow to manipulate the data before saving\n * @param item\n * @returns\n */\n protected beforeSave(item: T): T {\n if (isTimestamps(item)) {\n item.updatedAt = now();\n } else if (isTimestamp(item)) {\n item.timestamp = now();\n }\n\n return item;\n }\n\n async save(item: T): Promise<T> {\n item = this.beforeSave(item);\n const command = new PutCommand({\n TableName: this.table.name,\n Item: { ...item },\n });\n await this.client.send(command);\n\n return item;\n }\n\n async delete(id: ID): Promise<void | T> {\n await this.client.send(\n new DeleteCommand({\n TableName: this.table.name,\n Key: this.getKey(id),\n })\n );\n }\n}\n","import {\n DeletableRepository,\n HashMap,\n ReadableRepository,\n SavableRepository,\n} from '@opble/types';\nimport { z } from 'zod';\n\nexport const TableKeychema = z.object({\n hashKey: z.string(),\n sortKey: z.union([z.string(), z.number()]).optional(),\n});\n\nexport const TableSpecSchema = z.object({\n name: z.string(),\n keys: z.object({\n hashKey: z.string(),\n sortKey: z.string().optional(),\n }),\n});\n\nexport const QueryWithPaginationSchema = z.object({\n page: z\n .string()\n .optional()\n .transform((val) => (val ? parseInt(val, 10) : 1)) // default 1\n .refine((val) => val >= 1, { message: 'Page must be at least 1' }),\n\n limit: z\n .string()\n .optional()\n .transform((val) => (val ? parseInt(val, 10) : 25)) // default 25\n .refine((val) => val >= 5 && val <= 100, {\n message: 'Limit must be between 5 and 100',\n }),\n});\n\nexport const QueryWithSortingSchema = z.object({\n sortBy: z.string().optional(),\n sortOrder: z\n .enum(['asc', 'desc'])\n .optional()\n .transform((val) => val ?? 'asc'),\n});\n\nexport const QueryAllSchema = z.object({\n excludeSortKey: z.string().optional(),\n sortKeyBetween: z\n .object({\n start: z.string(),\n end: z.string(),\n })\n .optional(),\n sortKeyBeginsWith: z.string().optional(),\n sortKey: z.any().optional(),\n index: TableSpecSchema.optional(),\n});\n\nexport const ScanFilterSchema = z.object({\n filterExpression: z.string(),\n expressionAttributeNames: z.record(z.string(), z.string()),\n expressionAttributeValues: z.record(z.string(), z.any()),\n});\n\nexport type TableKey = z.infer<typeof TableKeychema>;\nexport type TableSpec = z.infer<typeof TableSpecSchema>;\n\nexport type QueryWithPagination = z.infer<typeof QueryWithPaginationSchema>;\nexport type QueryWithSorting = z.infer<typeof QueryWithSortingSchema>;\nexport type QueryAll = z.infer<typeof QueryAllSchema>;\nexport type ScanFilter = z.infer<typeof ScanFilterSchema>;\n\n/**\n * A generic repository interface specifically designed for DynamoDB operations.\n * It extends the basic CRUD repositories (Readable, Savable, Deletable) and adds\n * DynamoDB-specific methods for efficient querying, scanning, bulk operations,\n * and deletion patterns commonly used with DynamoDB's key structure.\n *\n * @template T - The type of the entity/document stored in DynamoDB (must be a HashMap / Record<string, unknown>)\n * @template ID - The type used to identify items. Defaults to `TableKey` (hash + optional sort key).\n */\nexport interface DynamoDBRepository<T extends HashMap, ID = TableKey>\n extends\n ReadableRepository<T, ID>,\n SavableRepository<T>,\n DeletableRepository<T, ID> {\n /**\n * Queries items using a partition key (hashKey) and optional sort key conditions.\n * This is the most common and efficient way to read data from a DynamoDB table\n * when you know the partition key.\n *\n * @param hashKey - The value of the partition key (hash key)\n * @param query - Optional advanced query conditions for sort key (begins_with, between, specific value, etc.)\n * @param pagination - Optional pagination parameters (page & limit)\n * @param sorting - Optional sort direction (only 'sortOrder' is used)\n * @returns Promise of array of matching items (T[])\n *\n * @example\n * repo.query(\"user#123\", { sortKeyBeginsWith: \"order#\" }, { limit: 20 }, { sortOrder: \"desc\" })\n */\n query(\n hashKey: unknown,\n query?: QueryAll,\n pagination?: QueryWithPagination,\n sorting?: Pick<QueryWithSorting, 'sortOrder'>\n ): Promise<T[]>;\n\n /**\n * Batch retrieves multiple items using their full composite keys (hash + sort).\n * This method uses `BatchGetItem` under the hood — much more efficient than individual gets\n * when fetching many items by primary key.\n *\n * @param keys - Array of objects containing at least `{ hashKey, sortKey? }`\n * @param query - Optional sorting preferences (rarely used in batch get)\n * @returns Promise of array of found items (in the order requested, missing items are omitted)\n *\n * @example\n * repo.queryInBulk([\n * { hashKey: \"user#abc\", sortKey: \"profile\" },\n * { hashKey: \"user#abc\", sortKey: \"settings\" }\n * ])\n */\n queryInBulk(keys: HashMap[], query?: QueryWithSorting): Promise<T[]>;\n\n /**\n * Performs a full table or index scan with optional filtering and pagination.\n * Scans are less efficient than queries — use only when you cannot use a partition key.\n *\n * @param pagination - Optional page & limit controls\n * @param filter - Optional raw filter expression (FilterExpression + attribute names/values)\n * @returns Promise of array of matching items\n *\n * @warning Scans can be expensive and slow on large tables — prefer query() when possible\n */\n scan(pagination?: QueryWithPagination, filter?: ScanFilter): Promise<T[]>;\n\n /**\n * Deletes **all items** that share the same partition key (hashKey).\n * Useful for cleaning up all records related to a specific entity (e.g. all orders of a user).\n *\n * @param hashKey - The partition key value whose items should be deleted\n * @returns Promise that resolves when deletion is complete\n *\n * @warning This operation may consume many write capacity units if the partition is large.\n * Consider using Query + BatchWriteItem in production for very large partitions.\n */\n deleteAll(hashKey: unknown): Promise<void>;\n\n /**\n * Deletes multiple items in a single BatchWriteItem call using their full keys.\n * Most efficient way to delete many known items at once.\n *\n * @param keys - Array of objects with hashKey and sortKey\n * @returns Promise that resolves when all deletions are complete\n *\n * @example\n * repo.deleteInBulk([\n * { hashKey: \"user#123\", sortKey: \"session#abc123\" },\n * { hashKey: \"user#123\", sortKey: \"session#def456\" }\n * ])\n */\n deleteInBulk(keys: HashMap[]): Promise<void>;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/repository.ts","../src/types.ts"],"names":["debug","createDebug","SORT_ASCENDING","BATCH_SIZE","now","AbstractDynamoDBRepository","table","factory","DynamoDBDocumentClient","DynamoDBClient","id","hashKey","query","pagination","sorting","lastEvaluatedKey","currentPage","resultItems","filterReturnedItemsExcludeSortKey","items","item","command","QueryCommand","sortKey","result","strict","keys","chunks","i","chunk","BatchGetCommand","sortOrder","a","b","filter","ScanCommand","batches","batch","deleteRequests","key","BatchWriteCommand","response","unprocessedItems","unprocessedKeys","error","GetCommand","isTimestamps","isTimestamp","PutCommand","DeleteCommand","TableKeychema","z","TableSpecSchema","QueryWithPaginationSchema","val","QueryWithSortingSchema","QueryAllSchema","ScanFilterSchema"],"mappings":"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","file":"index.js","sourcesContent":["import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport {\n BatchGetCommand,\n BatchWriteCommand,\n DeleteCommand,\n DynamoDBDocumentClient,\n GetCommand,\n PutCommand,\n QueryCommand,\n ScanCommand,\n} from '@aws-sdk/lib-dynamodb';\nimport { createDebug } from '@opble/debug';\nimport { isTimestamp, isTimestamps } from '@opble/entity';\nimport { Factory, HashMap } from '@opble/types';\n\nimport {\n DynamoDBRepository,\n QueryAll,\n QueryWithPagination,\n QueryWithSorting,\n ScanFilter,\n TableKey,\n TableSpec,\n} from './types';\n\nconst debug = createDebug('opble:repository-dynamodb');\nconst SORT_ASCENDING = 'asc';\nconst BATCH_SIZE = 50;\n\nfunction now(): number {\n return Math.floor(Date.now() / 1000);\n}\n\nexport abstract class AbstractDynamoDBRepository<\n T extends HashMap,\n ID extends TableKey = TableKey,\n> implements DynamoDBRepository<T, ID> {\n protected client: DynamoDBDocumentClient;\n\n constructor(\n protected table: TableSpec,\n protected factory: Factory<T>\n ) {\n this.client = DynamoDBDocumentClient.from(new DynamoDBClient({}));\n }\n\n protected getKey(id: ID): HashMap {\n return {\n [this.table.keys.hashKey]: id.hashKey,\n ...(this.table.keys.sortKey\n ? { [this.table.keys.sortKey]: id.sortKey }\n : {}),\n };\n }\n\n async query(\n hashKey: unknown,\n query?: QueryAll,\n pagination?: QueryWithPagination,\n sorting?: Pick<QueryWithSorting, 'sortOrder'>\n ): Promise<T[]> {\n /**\n * Internal state for pagination\n * - lastEvaluatedKey tells DynamoDB where to resume from\n * - currentPage helps us skip earlier pages\n * - resultItems will store the final page’s items\n */\n let lastEvaluatedKey: Record<string, unknown> | undefined;\n let currentPage = 1;\n let resultItems: HashMap[] = [];\n const filterReturnedItemsExcludeSortKey = (items: HashMap[]) => {\n /**\n * Business rule: Exclude a specific sortKey (if requested)\n * This is used to skip “parent” or “header” records within the same partition.\n */\n return query?.excludeSortKey && this.table.keys.sortKey\n ? items.filter(\n (item) => item[this.table.keys.sortKey!] !== query.excludeSortKey\n )\n : items;\n };\n\n do {\n /**\n * Prepare the QueryCommand\n * - Basic condition: match all items with the given hash key\n * - Add pagination via Limit + ExclusiveStartKey\n */\n const command = new QueryCommand({\n TableName: this.table.name,\n KeyConditionExpression: `#${this.table.keys.hashKey} = :hashKeyValue`,\n ExpressionAttributeNames: {\n [`#${this.table.keys.hashKey}`]: this.table.keys.hashKey,\n },\n ExpressionAttributeValues: {\n ':hashKeyValue': hashKey,\n },\n ExclusiveStartKey: lastEvaluatedKey,\n });\n\n if (pagination) {\n command.input.Limit = pagination.limit;\n }\n if (sorting) {\n command.input.ScanIndexForward = sorting.sortOrder === 'asc';\n }\n\n /**\n * Apply optional filters if provided\n */\n if (query) {\n // If querying a secondary index\n if (query.index) {\n command.input.IndexName = query.index.name;\n command.input.KeyConditionExpression = `#${query.index.keys.hashKey} = :hashKeyValue`;\n command.input.ExpressionAttributeNames = {\n [`#${query.index.keys.hashKey}`]: query.index.keys.hashKey,\n };\n }\n\n // If we want to match only sort keys starting with a specific prefix\n const sortKey = query.index?.keys.sortKey ?? this.table.keys.sortKey;\n if (query.sortKeyBeginsWith && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND begins_with(#${sortKey}, :sortKeyBeginsWith)`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyBeginsWith'] =\n query.sortKeyBeginsWith;\n } else if (query.sortKey && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND #${sortKey} = :sortKeyValue`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyValue'] =\n query.sortKey;\n } else if (query.sortKeyBetween && sortKey) {\n command.input.KeyConditionExpression = `${command.input.KeyConditionExpression} AND #${sortKey} BETWEEN :sortKeyStart AND :sortKeyEnd`;\n command.input.ExpressionAttributeNames ??= {};\n command.input.ExpressionAttributeNames[`#${sortKey}`] = sortKey;\n command.input.ExpressionAttributeValues ??= {};\n command.input.ExpressionAttributeValues[':sortKeyStart'] =\n query.sortKeyBetween.start;\n command.input.ExpressionAttributeValues[':sortKeyEnd'] =\n query.sortKeyBetween.end;\n }\n }\n debug('command#input', command.input);\n\n // Execute the query\n const result = await this.client.send(command);\n\n // Keep the pagination pointer (if DynamoDB says there’s more data)\n lastEvaluatedKey = result.LastEvaluatedKey;\n\n if (!pagination) {\n // pagination is disabled, fetch until no results found\n resultItems.push(\n ...filterReturnedItemsExcludeSortKey(result.Items ?? [])\n );\n } else if (currentPage === pagination.page) {\n /**\n * Once we reach the desired page, capture those items.\n * DynamoDB doesn’t support direct page jumps — we simulate it by looping\n * until we reach the correct page number.\n */\n\n resultItems = filterReturnedItemsExcludeSortKey(result.Items ?? []);\n break; // Stop — we’ve collected the requested page\n }\n\n currentPage++; // Move to next page and continue querying\n } while (lastEvaluatedKey);\n\n /**\n * Map each DynamoDB item into an instance of your model class (T)\n * When querrying with index, turn strict to false\n */\n const strict = !query || !query.index;\n return resultItems.map((item) => this.factory.create(item, { strict }));\n }\n\n async queryInBulk(keys: HashMap[], query?: QueryWithSorting): Promise<T[]> {\n // Split keys into chunks of BATCH_SIZE (DynamoDB BatchGet limit)\n const chunks: HashMap[][] = [];\n for (let i = 0; i < keys.length; i += BATCH_SIZE) {\n chunks.push(keys.slice(i, i + BATCH_SIZE));\n }\n\n // Execute all batch requests concurrently\n const results = await Promise.all(\n chunks.map(async (chunk) => {\n const command = new BatchGetCommand({\n RequestItems: {\n [this.table.name]: { Keys: chunk },\n },\n });\n const response = await this.client.send(command);\n return response.Responses?.[this.table.name] ?? [];\n })\n );\n\n // Merge all items from all responses\n const mergedItems = results.flat();\n\n // Convert each item to T using factory\n const items = mergedItems.map((item) => this.factory.create(item));\n\n // Sort if query.sortBy is specified\n if (query && query.sortBy) {\n const sortOrder = query.sortOrder ?? SORT_ASCENDING;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n items.sort((a: any, b: any) => {\n if (a[query.sortBy!] < b[query.sortBy!])\n return sortOrder === SORT_ASCENDING ? -1 : 1;\n if (a[query.sortBy!] > b[query.sortBy!])\n return sortOrder === SORT_ASCENDING ? 1 : -1;\n return 0;\n });\n }\n\n return items;\n }\n\n async scan(\n pagination?: QueryWithPagination,\n filter?: ScanFilter\n ): Promise<T[]> {\n /**\n * Internal state for pagination\n * - lastEvaluatedKey tells DynamoDB where to resume from\n * - currentPage helps us skip earlier pages\n * - resultItems will store the final page’s items\n */\n let lastEvaluatedKey: Record<string, unknown> | undefined;\n let currentPage = 1;\n let resultItems: HashMap[] = [];\n\n do {\n /**\n * Prepare the ScanCommand\n * - Add pagination via Limit + ExclusiveStartKey\n */\n const command = new ScanCommand({\n TableName: this.table.name,\n ExclusiveStartKey: lastEvaluatedKey,\n });\n\n if (pagination) {\n command.input.Limit = pagination.limit;\n }\n\n if (filter) {\n command.input.FilterExpression = filter.filterExpression;\n command.input.ExpressionAttributeNames =\n filter.expressionAttributeNames;\n command.input.ExpressionAttributeValues =\n filter.expressionAttributeValues;\n }\n\n /**\n * Execute the scan\n */\n const result = await this.client.send(command);\n\n // Keep the pagination pointer (if DynamoDB says there’s more data)\n lastEvaluatedKey = result.LastEvaluatedKey;\n\n if (!pagination) {\n // pagination is disabled, fetch until no results found\n resultItems.push(...(result.Items ?? []));\n } else if (currentPage === pagination.page) {\n /**\n * Once we reach the desired page, capture those items.\n * DynamoDB doesn’t support direct page jumps — we simulate it by looping\n * until we reach the correct page number.\n */\n\n resultItems = result.Items ?? [];\n break; // Stop — we’ve collected the requested page\n }\n\n currentPage++; // Move to next page and continue querying\n } while (lastEvaluatedKey);\n\n /**\n * Map each DynamoDB item into an instance of your model class (T)\n */\n return resultItems.map((item) => this.factory.create(item));\n }\n\n async deleteAll(hashKey: unknown): Promise<void> {\n const items = await this.query(hashKey);\n const keys = items.map((item) => {\n if (this.table.keys.sortKey) {\n return {\n [this.table.keys.hashKey]: hashKey,\n [this.table.keys.sortKey]:\n item[this.table.keys.sortKey as keyof typeof item],\n };\n } else {\n return {\n [this.table.keys.hashKey]: hashKey,\n };\n }\n });\n\n await this.deleteInBulk(keys);\n }\n\n async deleteInBulk(keys: HashMap[]): Promise<void> {\n const batches = [];\n while (keys.length > 0) {\n batches.push(keys.splice(0, BATCH_SIZE));\n }\n\n for (const batch of batches) {\n // Create delete requests for each item in the batch\n const deleteRequests = batch.map((key) => ({\n DeleteRequest: { Key: key },\n }));\n\n // Execute the BatchWriteCommand\n const command = new BatchWriteCommand({\n RequestItems: {\n [this.table.name]: deleteRequests,\n },\n });\n\n try {\n const response = await this.client.send(command);\n debug('[INFO] Batch delete response:', response);\n\n // Check if there are unprocessed items and retry them if necessary\n if (\n response.UnprocessedItems &&\n response.UnprocessedItems[this.table.name]\n ) {\n debug('[WARN] Unprocessed items found. Retrying...');\n const unprocessedItems = response.UnprocessedItems[this.table.name];\n if (unprocessedItems === undefined) {\n continue;\n }\n const unprocessedKeys = unprocessedItems\n .map((item) => item.DeleteRequest?.Key)\n .filter((key): key is Record<string, unknown> => key != null); // Filter out undefined keys\n\n await this.deleteInBulk(unprocessedKeys);\n }\n } catch (error) {\n debug('[ERROR] Error deleting items in batch:', error);\n }\n }\n }\n\n async get(id: ID): Promise<T | null> {\n const command = new GetCommand({\n TableName: this.table.name,\n Key: this.getKey(id),\n });\n\n const result = await this.client.send(command);\n return result.Item ? this.factory.create(result.Item) : null;\n }\n\n /**\n * Allow to manipulate the data before saving\n * @param item\n * @returns\n */\n protected beforeSave(item: T): T {\n if (isTimestamps(item)) {\n item.updatedAt = now();\n } else if (isTimestamp(item)) {\n item.timestamp = now();\n }\n\n return item;\n }\n\n async save(item: T): Promise<T> {\n item = this.beforeSave(item);\n const command = new PutCommand({\n TableName: this.table.name,\n Item: { ...item },\n });\n await this.client.send(command);\n\n return item;\n }\n\n async delete(id: ID): Promise<void | T> {\n await this.client.send(\n new DeleteCommand({\n TableName: this.table.name,\n Key: this.getKey(id),\n })\n );\n }\n}\n","import {\n DeletableRepository,\n HashMap,\n ReadableRepository,\n SavableRepository,\n} from '@opble/types';\nimport { z } from 'zod';\n\nexport const TableKeychema = z.object({\n hashKey: z.string(),\n sortKey: z.union([z.string(), z.number()]).optional(),\n});\n\nexport const TableSpecSchema = z.object({\n name: z.string(),\n keys: z.object({\n hashKey: z.string(),\n sortKey: z.string().optional(),\n }),\n});\n\nexport const QueryWithPaginationSchema = z.object({\n page: z\n .string()\n .optional()\n .transform((val) => (val ? parseInt(val, 10) : 1)) // default 1\n .refine((val) => val >= 1, { message: 'Page must be at least 1' }),\n\n limit: z\n .string()\n .optional()\n .transform((val) => (val ? parseInt(val, 10) : 25)) // default 25\n .refine((val) => val >= 5 && val <= 100, {\n message: 'Limit must be between 5 and 100',\n }),\n});\n\nexport const QueryWithSortingSchema = z.object({\n sortBy: z.string().optional(),\n sortOrder: z\n .enum(['asc', 'desc'])\n .optional()\n .transform((val) => val ?? 'asc'),\n});\n\nexport const QueryAllSchema = z.object({\n excludeSortKey: z.string().optional(),\n sortKeyBetween: z\n .object({\n start: z.string(),\n end: z.string(),\n })\n .optional(),\n sortKeyBeginsWith: z.string().optional(),\n sortKey: z.any().optional(),\n index: TableSpecSchema.optional(),\n});\n\nexport const ScanFilterSchema = z.object({\n filterExpression: z.string(),\n expressionAttributeNames: z.record(z.string(), z.string()),\n expressionAttributeValues: z.record(z.string(), z.any()),\n});\n\nexport type TableKey = z.infer<typeof TableKeychema>;\nexport type TableSpec = z.infer<typeof TableSpecSchema>;\n\nexport type QueryWithPagination = z.infer<typeof QueryWithPaginationSchema>;\nexport type QueryWithSorting = z.infer<typeof QueryWithSortingSchema>;\nexport type QueryAll = z.infer<typeof QueryAllSchema>;\nexport type ScanFilter = z.infer<typeof ScanFilterSchema>;\n\n/**\n * A generic repository interface specifically designed for DynamoDB operations.\n * It extends the basic CRUD repositories (Readable, Savable, Deletable) and adds\n * DynamoDB-specific methods for efficient querying, scanning, bulk operations,\n * and deletion patterns commonly used with DynamoDB's key structure.\n *\n * @template T - The type of the entity/document stored in DynamoDB (must be a HashMap / Record<string, unknown>)\n * @template ID - The type used to identify items. Defaults to `TableKey` (hash + optional sort key).\n */\nexport interface DynamoDBRepository<T extends HashMap, ID = TableKey>\n extends\n ReadableRepository<T, ID>,\n SavableRepository<T>,\n DeletableRepository<T, ID> {\n /**\n * Queries items using a partition key (hashKey) and optional sort key conditions.\n * This is the most common and efficient way to read data from a DynamoDB table\n * when you know the partition key.\n *\n * @param hashKey - The value of the partition key (hash key)\n * @param query - Optional advanced query conditions for sort key (begins_with, between, specific value, etc.)\n * @param pagination - Optional pagination parameters (page & limit)\n * @param sorting - Optional sort direction (only 'sortOrder' is used)\n * @returns Promise of array of matching items (T[])\n *\n * @example\n * repo.query(\"user#123\", { sortKeyBeginsWith: \"order#\" }, { limit: 20 }, { sortOrder: \"desc\" })\n */\n query(\n hashKey: unknown,\n query?: QueryAll,\n pagination?: QueryWithPagination,\n sorting?: Pick<QueryWithSorting, 'sortOrder'>\n ): Promise<T[]>;\n\n /**\n * Batch retrieves multiple items using their full composite keys (hash + sort).\n * This method uses `BatchGetItem` under the hood — much more efficient than individual gets\n * when fetching many items by primary key.\n *\n * @param keys - Array of objects containing at least `{ hashKey, sortKey? }`\n * @param query - Optional sorting preferences (rarely used in batch get)\n * @returns Promise of array of found items (in the order requested, missing items are omitted)\n *\n * @example\n * repo.queryInBulk([\n * { hashKey: \"user#abc\", sortKey: \"profile\" },\n * { hashKey: \"user#abc\", sortKey: \"settings\" }\n * ])\n */\n queryInBulk(keys: HashMap[], query?: QueryWithSorting): Promise<T[]>;\n\n /**\n * Performs a full table or index scan with optional filtering and pagination.\n * Scans are less efficient than queries — use only when you cannot use a partition key.\n *\n * @param pagination - Optional page & limit controls\n * @param filter - Optional raw filter expression (FilterExpression + attribute names/values)\n * @returns Promise of array of matching items\n *\n * @warning Scans can be expensive and slow on large tables — prefer query() when possible\n */\n scan(pagination?: QueryWithPagination, filter?: ScanFilter): Promise<T[]>;\n\n /**\n * Deletes **all items** that share the same partition key (hashKey).\n * Useful for cleaning up all records related to a specific entity (e.g. all orders of a user).\n *\n * @param hashKey - The partition key value whose items should be deleted\n * @returns Promise that resolves when deletion is complete\n *\n * @warning This operation may consume many write capacity units if the partition is large.\n * Consider using Query + BatchWriteItem in production for very large partitions.\n */\n deleteAll(hashKey: unknown): Promise<void>;\n\n /**\n * Deletes multiple items in a single BatchWriteItem call using their full keys.\n * Most efficient way to delete many known items at once.\n *\n * @param keys - Array of objects with hashKey and sortKey\n * @returns Promise that resolves when all deletions are complete\n *\n * @example\n * repo.deleteInBulk([\n * { hashKey: \"user#123\", sortKey: \"session#abc123\" },\n * { hashKey: \"user#123\", sortKey: \"session#def456\" }\n * ])\n */\n deleteInBulk(keys: HashMap[]): Promise<void>;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opble/repository-dynamodb",
|
|
3
3
|
"description": "Provide DynamoDB repository implementation.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.cjs",
|
|
@@ -35,8 +35,9 @@
|
|
|
35
35
|
"provenance": false
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"
|
|
39
|
-
"@opble/typescript-config": "0.0.0"
|
|
38
|
+
"aws-sdk-client-mock": "^4.1.0",
|
|
39
|
+
"@opble/typescript-config": "0.0.0",
|
|
40
|
+
"@opble/eslint-config": "0.0.0"
|
|
40
41
|
},
|
|
41
42
|
"dependencies": {
|
|
42
43
|
"@aws-sdk/client-dynamodb": "^3.971.0",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"scripts": {
|
|
50
51
|
"build": "tsup",
|
|
51
52
|
"clean": "rm -rf dist",
|
|
52
|
-
"lint": "eslint \"src/**/*.ts*\" --max-warnings 0",
|
|
53
|
-
"test": "
|
|
53
|
+
"lint": "eslint \"{src,test}/**/*.ts*\" --max-warnings 0",
|
|
54
|
+
"test": "vitest run"
|
|
54
55
|
}
|
|
55
56
|
}
|