simple-dynamo-ts 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/README.md ADDED
@@ -0,0 +1,454 @@
1
+ # simple-dynamo-ts
2
+
3
+ A simple, type-safe TypeScript library for working with AWS DynamoDB using decorators and a repository pattern. This library provides a clean and intuitive API for DynamoDB operations while maintaining type safety and leveraging TypeScript decorators for entity configuration.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Type-Safe**: Full TypeScript support with type inference
8
+ - 🏗️ **Decorator-Based**: Use decorators to define DynamoDB entities and keys
9
+ - 📦 **Repository Pattern**: Extend `DynamoDBRepository` for clean, reusable data access
10
+ - 🔍 **Query Support**: Built-in support for queries, indexes, and sort key comparisons
11
+ - 🛡️ **Error Handling**: Custom error classes for better error handling
12
+ - ⚡ **Lightweight**: Minimal dependencies, only requires `@aws-sdk/lib-dynamodb` and `reflect-metadata`
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install simple-dynamo-ts
18
+ # or
19
+ pnpm add simple-dynamo-ts
20
+ # or
21
+ yarn add simple-dynamo-ts
22
+ ```
23
+
24
+ ### Peer Dependencies
25
+
26
+ This library requires `@aws-sdk/lib-dynamodb` as a peer dependency:
27
+
28
+ ```bash
29
+ npm install @aws-sdk/lib-dynamodb
30
+ ```
31
+
32
+ ## Prerequisites
33
+
34
+ - TypeScript 5.7+
35
+ - Node.js 18+
36
+ - Enable `experimentalDecorators` and `emitDecoratorMetadata` in your `tsconfig.json`
37
+
38
+ ```json
39
+ {
40
+ "compilerOptions": {
41
+ "experimentalDecorators": true,
42
+ "emitDecoratorMetadata": true
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ### 1. Define Your Entity
50
+
51
+ Create a class representing your DynamoDB table item and decorate it:
52
+
53
+ ```typescript
54
+ import { DynamoTable, PartitionKey, SortKey, IndexSortKey } from "simple-dynamo-ts";
55
+
56
+ @DynamoTable("User")
57
+ export class UserEntity {
58
+ @PartitionKey()
59
+ orgId!: string;
60
+
61
+ @SortKey()
62
+ id: string = "generate-id";
63
+
64
+ @IndexSortKey("EmailIndex")
65
+ email!: string;
66
+
67
+ password!: string;
68
+ role: string = "USER";
69
+ createdAt!: string;
70
+ updatedAt!: string;
71
+ deletedAt?: string;
72
+ }
73
+ ```
74
+
75
+ ### 2. Create a Repository
76
+
77
+ Extend `DynamoDBRepository` to create your repository:
78
+
79
+ ```typescript
80
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
81
+ import { DynamoDBRepository, QueryOptions } from "simple-dynamo-ts";
82
+ import { UserEntity } from "./user.entity";
83
+
84
+ export class UsersRepository extends DynamoDBRepository<UserEntity> {
85
+ constructor(protected readonly client: DynamoDBDocumentClient) {
86
+ super(client, UserEntity);
87
+ }
88
+
89
+ async findAll(): Promise<UserEntity[]> {
90
+ const response = await this.query({ pk: "USER" });
91
+ return response.items;
92
+ }
93
+
94
+ async getById(id: string): Promise<UserEntity> {
95
+ return await this.getItem("USER", id);
96
+ }
97
+
98
+ async findByEmail(email: string): Promise<UserEntity> {
99
+ const queryOptions: QueryOptions = {
100
+ pk: "USER",
101
+ sk: email,
102
+ indexName: "EmailIndex",
103
+ };
104
+ const response = await this.query(queryOptions);
105
+
106
+ if (response.count) return response.items[0];
107
+ throw new Error("User with this email not found!");
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### 3. Use Your Repository
113
+
114
+ ```typescript
115
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
116
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
117
+ import { UsersRepository } from "./users.repository";
118
+
119
+ // Initialize DynamoDB client
120
+ const client = new DynamoDBClient({ region: "us-east-1" });
121
+ const docClient = DynamoDBDocumentClient.from(client);
122
+
123
+ // Create repository instance
124
+ const usersRepository = new UsersRepository(docClient);
125
+
126
+ // Use repository methods
127
+ const user = await usersRepository.getById("user-123");
128
+ const allUsers = await usersRepository.findAll();
129
+ const userByEmail = await usersRepository.findByEmail("user@example.com");
130
+ ```
131
+
132
+ ## Decorators
133
+
134
+ ### `@DynamoTable(tableName?)`
135
+
136
+ Marks a class as a DynamoDB table entity. The table name is optional - if not provided, the class name will be used.
137
+
138
+ ```typescript
139
+ @DynamoTable("User") // Explicit table name
140
+ export class UserEntity { }
141
+
142
+ @DynamoTable() // Uses class name "UserEntity" as table name
143
+ export class UserEntity { }
144
+ ```
145
+
146
+ ### `@PartitionKey(fieldName?)`
147
+
148
+ Marks a property as the partition key (HASH key). The field name is optional - if not provided, the property name will be used.
149
+
150
+ ```typescript
151
+ @PartitionKey() // Uses property name "orgId"
152
+ orgId!: string;
153
+
154
+ @PartitionKey("organizationId") // Uses "organizationId" as DynamoDB field name
155
+ orgId!: string;
156
+ ```
157
+
158
+ ### `@SortKey(fieldName?)`
159
+
160
+ Marks a property as the sort key (RANGE key). The field name is optional.
161
+
162
+ ```typescript
163
+ @SortKey()
164
+ id!: string;
165
+ ```
166
+
167
+ ### `@IndexPartitionKey(indexName, fieldName?)`
168
+
169
+ Marks a property as a partition key for a DynamoDB Global Secondary Index (GSI).
170
+
171
+ ```typescript
172
+ @IndexPartitionKey("EmailIndex", "orgId")
173
+ orgId!: string;
174
+ ```
175
+
176
+ ### `@IndexSortKey(indexName, fieldName?)`
177
+
178
+ Marks a property as a sort key for a DynamoDB Global Secondary Index (GSI).
179
+
180
+ ```typescript
181
+ @IndexSortKey("EmailIndex", "email")
182
+ email!: string;
183
+ ```
184
+
185
+ ## Repository API
186
+
187
+ The `DynamoDBRepository<T>` class provides the following methods:
188
+
189
+ ### `create(item: T): Promise<T>`
190
+
191
+ Creates a new item in DynamoDB. Throws an error if an item with the same key already exists.
192
+
193
+ ```typescript
194
+ const newUser: UserEntity = {
195
+ orgId: "ORG-123",
196
+ id: "user-456",
197
+ email: "user@example.com",
198
+ password: "hashed-password",
199
+ role: "USER",
200
+ createdAt: new Date().toISOString(),
201
+ updatedAt: new Date().toISOString(),
202
+ };
203
+
204
+ const created = await usersRepository.create(newUser);
205
+ ```
206
+
207
+ ### `getItem(pk: DynamoKey, sk?: DynamoKey): Promise<T>`
208
+
209
+ Retrieves a single item by its partition key and optional sort key. Throws `ItemNotFoundError` if the item doesn't exist.
210
+
211
+ ```typescript
212
+ // With partition key only
213
+ const item = await repository.getItem("PK-VALUE");
214
+
215
+ // With partition and sort key
216
+ const item = await repository.getItem("PK-VALUE", "SK-VALUE");
217
+ ```
218
+
219
+ ### `put(item: T): Promise<T>`
220
+
221
+ Puts an item into DynamoDB (creates or updates). Unlike `create`, this will overwrite existing items.
222
+
223
+ ```typescript
224
+ const updated = await usersRepository.put(userEntity);
225
+ ```
226
+
227
+ ### `query(options: QueryOptions): Promise<{ items: T[]; lastEvaluatedKey: T; count: number }>`
228
+
229
+ Queries DynamoDB items. Supports partition key queries, sort key comparisons, and index queries.
230
+
231
+ ```typescript
232
+ // Simple partition key query
233
+ const result = await repository.query({ pk: "USER" });
234
+
235
+ // Query with sort key
236
+ const result = await repository.query({
237
+ pk: "USER",
238
+ sk: "user-123",
239
+ });
240
+
241
+ // Query with sort key comparison
242
+ const result = await repository.query({
243
+ pk: "USER",
244
+ sk: "user-",
245
+ skComparator: "begins_with",
246
+ });
247
+
248
+ // Query using an index
249
+ const result = await repository.query({
250
+ pk: "ORG-123",
251
+ sk: "user@example.com",
252
+ indexName: "EmailIndex",
253
+ });
254
+
255
+ // Query with limit and sort order
256
+ const result = await repository.query({
257
+ pk: "USER",
258
+ limit: 10,
259
+ scanIndexForward: false, // Sort descending
260
+ });
261
+ ```
262
+
263
+ #### QueryOptions
264
+
265
+ ```typescript
266
+ type QueryOptions = {
267
+ pk: DynamoKey; // Partition key value (required)
268
+ sk?: DynamoKey; // Sort key value (optional)
269
+ skComparator?: "=" | ">" | "<" | ">=" | "<=" | "BETWEEN" | "begins_with";
270
+ indexName?: string; // Index name for GSI or LSI queries
271
+ scanIndexForward?: boolean; // Sort order (default: true)
272
+ limit?: number; // Maximum number of items to return
273
+ };
274
+ ```
275
+
276
+ ### `softDelete(pk: DynamoKey, sk?: DynamoKey): Promise<void>`
277
+
278
+ Performs a soft delete by setting the `deletedAt` field to the current timestamp.
279
+
280
+ ```typescript
281
+ await usersRepository.softDelete("USER", "user-123");
282
+ ```
283
+
284
+ ### `remove(pk: DynamoKey, sk?: DynamoKey): Promise<void>`
285
+
286
+ Permanently deletes an item from DynamoDB.
287
+
288
+ ```typescript
289
+ await usersRepository.remove("USER", "user-123");
290
+ ```
291
+
292
+ ## Error Handling
293
+
294
+ The library provides custom error classes for better error handling:
295
+
296
+ ### `ItemNotFoundError`
297
+
298
+ Thrown when attempting to get an item that doesn't exist.
299
+
300
+ ```typescript
301
+ import { ItemNotFoundError } from "simple-dynamo-ts";
302
+
303
+ try {
304
+ const user = await repository.getItem("PK", "SK");
305
+ } catch (error) {
306
+ if (error instanceof ItemNotFoundError) {
307
+ console.log("Item not found");
308
+ }
309
+ }
310
+ ```
311
+
312
+ ### `InvalidParametersError`
313
+
314
+ Thrown when invalid parameters are passed to repository methods.
315
+
316
+ ### `DecoratorMissingError`
317
+
318
+ Thrown when required decorators are missing from entity classes.
319
+
320
+ ### `DuplicateDecoratorError`
321
+
322
+ Thrown when duplicate decorators are applied (e.g., multiple `@PartitionKey` decorators).
323
+
324
+ ## Complete Example
325
+
326
+ Here's a complete example demonstrating entity definition and repository usage:
327
+
328
+ ```typescript
329
+ // user.entity.ts
330
+ import { DynamoTable, PartitionKey, SortKey, IndexSortKey } from "simple-dynamo-ts";
331
+
332
+ @DynamoTable("User")
333
+ export class UserEntity {
334
+ @PartitionKey()
335
+ orgId!: string;
336
+
337
+ @SortKey()
338
+ id: string = "generate-id";
339
+
340
+ @IndexSortKey("EmailIndex")
341
+ email!: string;
342
+
343
+ password!: string;
344
+ role: string = "USER";
345
+ createdAt!: string;
346
+ updatedAt!: string;
347
+ deletedAt?: string;
348
+ }
349
+
350
+ // users.repository.ts
351
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
352
+ import { DynamoDBRepository, QueryOptions } from "simple-dynamo-ts";
353
+ import { UserEntity } from "./user.entity";
354
+
355
+ export class UsersRepository extends DynamoDBRepository<UserEntity> {
356
+ constructor(protected readonly client: DynamoDBDocumentClient) {
357
+ super(client, UserEntity);
358
+ }
359
+
360
+ async findAll(): Promise<UserEntity[]> {
361
+ const response = await this.query({ pk: "USER" });
362
+ return response.items;
363
+ }
364
+
365
+ async getById(id: string): Promise<UserEntity> {
366
+ return await this.getItem("USER", id);
367
+ }
368
+
369
+ async delete(id: string): Promise<void> {
370
+ return await this.softDelete("USER", id);
371
+ }
372
+
373
+ async findByEmail(email: string): Promise<UserEntity> {
374
+ const queryOptions: QueryOptions = {
375
+ pk: "USER",
376
+ sk: email,
377
+ indexName: "EmailIndex",
378
+ };
379
+ const response = await this.query(queryOptions);
380
+
381
+ if (response.count) return response.items[0];
382
+ throw new Error("User with this email not found!");
383
+ }
384
+ }
385
+
386
+ // app.ts
387
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
388
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
389
+ import { UsersRepository } from "./users.repository";
390
+
391
+ const client = new DynamoDBClient({ region: "us-east-1" });
392
+ const docClient = DynamoDBDocumentClient.from(client);
393
+ const usersRepository = new UsersRepository(docClient);
394
+
395
+ // Create a user
396
+ const newUser: UserEntity = {
397
+ orgId: "ORG-123",
398
+ id: "user-456",
399
+ email: "user@example.com",
400
+ password: "hashed-password",
401
+ role: "USER",
402
+ createdAt: new Date().toISOString(),
403
+ updatedAt: new Date().toISOString(),
404
+ };
405
+ await usersRepository.create(newUser);
406
+
407
+ // Get user by ID
408
+ const user = await usersRepository.getById("user-456");
409
+
410
+ // Find user by email
411
+ const userByEmail = await usersRepository.findByEmail("user@example.com");
412
+
413
+ // Get all users
414
+ const allUsers = await usersRepository.findAll();
415
+
416
+ // Soft delete
417
+ await usersRepository.delete("user-456");
418
+ ```
419
+
420
+ ## Type Definitions
421
+
422
+ ### `DynamoKey`
423
+
424
+ ```typescript
425
+ type DynamoKey = string | number;
426
+ ```
427
+
428
+ ### `DynamoKeyMap`
429
+
430
+ ```typescript
431
+ type DynamoKeyMap = Record<string, DynamoKey>;
432
+ ```
433
+
434
+ ### `QueryOptions`
435
+
436
+ See the [Query API](#queryoptions) section above.
437
+
438
+ ## Helper Functions
439
+
440
+ The library also exports helper functions for retrieving metadata from decorated classes:
441
+
442
+ - `getDynamoTableName(target)`: Get the table name from a decorated class
443
+ - `getPartitionKeyName(target)`: Get the partition key field name
444
+ - `getSortKeyName(target)`: Get the sort key field name
445
+ - `getIndexPartitionKeyName(target, indexName)`: Get the partition key for an index
446
+ - `getIndexSortKeyName(target, indexName)`: Get the sort key for an index
447
+
448
+ ## Contributing
449
+
450
+ Contributions are welcome! Please feel free to submit a Pull Request.
451
+
452
+ ## License
453
+
454
+ MIT © Blimit
@@ -0,0 +1,18 @@
1
+ import "reflect-metadata";
2
+ import { DynamoEntityTarget } from "./types";
3
+ export declare const DYNAMO_TABLE_NAME_KEY = "dynamo:table:name";
4
+ export declare const DYNAMO_PARTITION_KEY_KEY = "dynamo:partition:key";
5
+ export declare const DYNAMO_SORT_KEY_KEY = "dynamo:sort:key";
6
+ export declare const DYNAMO_INDEX_PARTITION_KEYS_KEY = "dynamo:index:partition:keys";
7
+ export declare const DYNAMO_INDEX_SORT_KEYS_KEY = "dynamo:index:sort:keys";
8
+ export declare function DynamoTable(tableName?: string): ClassDecorator;
9
+ export declare function PartitionKey(fieldName?: string): PropertyDecorator;
10
+ export declare function SortKey(fieldName?: string): PropertyDecorator;
11
+ export declare function IndexPartitionKey(indexName: string, fieldName?: string): PropertyDecorator;
12
+ export declare function IndexSortKey(indexName: string, fieldName?: string): PropertyDecorator;
13
+ export declare function getDynamoTableName(target: DynamoEntityTarget): string | undefined;
14
+ export declare function getPartitionKeyName(target: DynamoEntityTarget): string | undefined;
15
+ export declare function getSortKeyName(target: DynamoEntityTarget): string | undefined;
16
+ export declare function getIndexPartitionKeyName(target: DynamoEntityTarget, indexName: string): string | undefined;
17
+ export declare function getIndexSortKeyName(target: DynamoEntityTarget, indexName: string): string | undefined;
18
+ //# sourceMappingURL=decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAE7C,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AACzD,eAAO,MAAM,wBAAwB,yBAAyB,CAAC;AAC/D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AACrD,eAAO,MAAM,+BAA+B,gCAAgC,CAAC;AAC7E,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAiEnE,wBAAgB,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,cAAc,CAc9D;AAqBD,wBAAgB,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAelE;AAuBD,wBAAgB,OAAO,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,iBAAiB,CAe7D;AA2BD,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,iBAAiB,CAyBnB;AA2BD,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,GACjB,iBAAiB,CAqBnB;AAeD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,GACzB,MAAM,GAAG,SAAS,CAKpB;AAeD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,GACzB,MAAM,GAAG,SAAS,CAKpB;AAeD,wBAAgB,cAAc,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,SAAS,CAK7E;AAgBD,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,kBAAkB,EAC1B,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,SAAS,CAUpB;AAgBD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,EAC1B,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,SAAS,CAUpB"}
@@ -0,0 +1,116 @@
1
+ import "reflect-metadata";
2
+ import { DuplicateDecoratorError } from "./exceptions";
3
+ export const DYNAMO_TABLE_NAME_KEY = "dynamo:table:name";
4
+ export const DYNAMO_PARTITION_KEY_KEY = "dynamo:partition:key";
5
+ export const DYNAMO_SORT_KEY_KEY = "dynamo:sort:key";
6
+ export const DYNAMO_INDEX_PARTITION_KEYS_KEY = "dynamo:index:partition:keys";
7
+ export const DYNAMO_INDEX_SORT_KEYS_KEY = "dynamo:index:sort:keys";
8
+ function getConstructor(target) {
9
+ return typeof target === "function" ? target : target.constructor;
10
+ }
11
+ function getPrototype(target) {
12
+ return getConstructor(target).prototype;
13
+ }
14
+ function validateDuplicateDecorator(target, key, decoratorName, conflict) {
15
+ const existingKey = Reflect.getMetadata(key, target);
16
+ if (existingKey !== undefined) {
17
+ throw new DuplicateDecoratorError(`Multiple ${decoratorName} decorators found in class "${target.constructor?.name || "Unknown"}". ` +
18
+ `Existing decorator: "${existingKey}", conflicting property: "${String(conflict)}"`);
19
+ }
20
+ }
21
+ function validateNonEmptyString(value, paramName) {
22
+ if (value.trim() === "") {
23
+ throw new Error(`Invalid ${paramName}: cannot be an empty string.`);
24
+ }
25
+ }
26
+ export function DynamoTable(tableName) {
27
+ return function (target) {
28
+ if (tableName) {
29
+ validateNonEmptyString(tableName, "tableName");
30
+ }
31
+ const dynamoTableName = tableName ?? target.name;
32
+ validateDuplicateDecorator(target, DYNAMO_TABLE_NAME_KEY, "@DynamoTable", dynamoTableName);
33
+ Reflect.defineMetadata(DYNAMO_TABLE_NAME_KEY, dynamoTableName, target);
34
+ };
35
+ }
36
+ export function PartitionKey(fieldName) {
37
+ return function (target, propertyKey) {
38
+ if (fieldName) {
39
+ validateNonEmptyString(fieldName, "fieldName");
40
+ }
41
+ const dynamoFieldName = fieldName ?? String(propertyKey);
42
+ validateDuplicateDecorator(target, DYNAMO_PARTITION_KEY_KEY, "@PartitionKey", dynamoFieldName);
43
+ Reflect.defineMetadata(DYNAMO_PARTITION_KEY_KEY, dynamoFieldName, target);
44
+ };
45
+ }
46
+ export function SortKey(fieldName) {
47
+ return function (target, propertyKey) {
48
+ if (fieldName) {
49
+ validateNonEmptyString(fieldName, "fieldName");
50
+ }
51
+ const dynamoFieldName = fieldName ?? String(propertyKey);
52
+ validateDuplicateDecorator(target, DYNAMO_SORT_KEY_KEY, "@SortKey", propertyKey);
53
+ Reflect.defineMetadata(DYNAMO_SORT_KEY_KEY, dynamoFieldName, target);
54
+ };
55
+ }
56
+ export function IndexPartitionKey(indexName, fieldName) {
57
+ return function (target, propertyKey) {
58
+ validateNonEmptyString(indexName, "indexName");
59
+ if (fieldName) {
60
+ validateNonEmptyString(fieldName, "fieldName");
61
+ }
62
+ const dynamoFieldName = fieldName ?? String(propertyKey);
63
+ const existingKeys = Reflect.getMetadata(DYNAMO_INDEX_PARTITION_KEYS_KEY, target) ?? {};
64
+ if (existingKeys[indexName] !== undefined) {
65
+ throw new DuplicateDecoratorError(`Multiple @IndexPartitionKey decorators found for index "${indexName}" in class "${target.constructor?.name || "Unknown"}". ` +
66
+ `Existing partition key: "${existingKeys[indexName]}", conflicting property: "${String(propertyKey)}"`);
67
+ }
68
+ existingKeys[indexName] = dynamoFieldName;
69
+ Reflect.defineMetadata(DYNAMO_INDEX_PARTITION_KEYS_KEY, existingKeys, target);
70
+ };
71
+ }
72
+ export function IndexSortKey(indexName, fieldName) {
73
+ return function (target, propertyKey) {
74
+ validateNonEmptyString(indexName, "indexName");
75
+ if (fieldName) {
76
+ validateNonEmptyString(fieldName, "fieldName");
77
+ }
78
+ const dynamoFieldName = fieldName ?? String(propertyKey);
79
+ const existingKeys = Reflect.getMetadata(DYNAMO_INDEX_SORT_KEYS_KEY, target) ?? {};
80
+ if (existingKeys[indexName] !== undefined) {
81
+ throw new DuplicateDecoratorError(`Multiple @IndexSortKey decorators found for index "${indexName}" in class "${target.constructor?.name || "Unknown"}". ` +
82
+ `Existing sort key: "${existingKeys[indexName]}", conflicting property: "${String(propertyKey)}"`);
83
+ }
84
+ existingKeys[indexName] = dynamoFieldName;
85
+ Reflect.defineMetadata(DYNAMO_INDEX_SORT_KEYS_KEY, existingKeys, target);
86
+ };
87
+ }
88
+ export function getDynamoTableName(target) {
89
+ const constructor = getConstructor(target);
90
+ return Reflect.getMetadata(DYNAMO_TABLE_NAME_KEY, constructor);
91
+ }
92
+ export function getPartitionKeyName(target) {
93
+ const prototype = getPrototype(target);
94
+ return Reflect.getMetadata(DYNAMO_PARTITION_KEY_KEY, prototype);
95
+ }
96
+ export function getSortKeyName(target) {
97
+ const prototype = getPrototype(target);
98
+ return Reflect.getMetadata(DYNAMO_SORT_KEY_KEY, prototype);
99
+ }
100
+ export function getIndexPartitionKeyName(target, indexName) {
101
+ if (!indexName || indexName.trim() === "") {
102
+ throw new Error("indexName cannot be empty");
103
+ }
104
+ const prototype = getPrototype(target);
105
+ const indexKeys = Reflect.getMetadata(DYNAMO_INDEX_PARTITION_KEYS_KEY, prototype);
106
+ return indexKeys?.[indexName];
107
+ }
108
+ export function getIndexSortKeyName(target, indexName) {
109
+ if (!indexName || indexName.trim() === "") {
110
+ throw new Error("indexName cannot be empty");
111
+ }
112
+ const prototype = getPrototype(target);
113
+ const indexKeys = Reflect.getMetadata(DYNAMO_INDEX_SORT_KEYS_KEY, prototype);
114
+ return indexKeys?.[indexName];
115
+ }
116
+ //# sourceMappingURL=decorators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export declare class ItemNotFoundError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class InvalidParametersError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ export declare class DecoratorMissingError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ export declare class DuplicateDecoratorError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ //# sourceMappingURL=exceptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exceptions.d.ts","sourceRoot":"","sources":["../src/exceptions.ts"],"names":[],"mappings":"AAAA,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAO5B;AAED,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAO5B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAO5B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,EAAE,MAAM;CAO5B"}
@@ -0,0 +1,29 @@
1
+ export class ItemNotFoundError extends Error {
2
+ constructor(message) {
3
+ super(message);
4
+ this.name = "ItemNotFoundError";
5
+ Object.setPrototypeOf(this, ItemNotFoundError.prototype);
6
+ }
7
+ }
8
+ export class InvalidParametersError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = "InvalidParametersError";
12
+ Object.setPrototypeOf(this, InvalidParametersError.prototype);
13
+ }
14
+ }
15
+ export class DecoratorMissingError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "DecoratorMissingError";
19
+ Object.setPrototypeOf(this, DecoratorMissingError.prototype);
20
+ }
21
+ }
22
+ export class DuplicateDecoratorError extends Error {
23
+ constructor(message) {
24
+ super(message);
25
+ this.name = "DuplicateDecoratorError";
26
+ Object.setPrototypeOf(this, DuplicateDecoratorError.prototype);
27
+ }
28
+ }
29
+ //# sourceMappingURL=exceptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exceptions.js","sourceRoot":"","sources":["../src/exceptions.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;QAGhC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC3D,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;QAGrC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;CACF;AAED,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QAGpC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,qBAAqB,CAAC,SAAS,CAAC,CAAC;IAC/D,CAAC;CACF;AAED,MAAM,OAAO,uBAAwB,SAAQ,KAAK;IAChD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;QAGtC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,uBAAuB,CAAC,SAAS,CAAC,CAAC;IACjE,CAAC;CACF"}
@@ -0,0 +1,5 @@
1
+ export { DynamoDBRepository } from "./simple-dynamodb-repository";
2
+ export { DynamoTable, PartitionKey, SortKey, IndexPartitionKey, IndexSortKey, getDynamoTableName, getPartitionKeyName, getSortKeyName, getIndexPartitionKeyName, getIndexSortKeyName, } from "./decorators";
3
+ export type { QueryOptions, DynamoKey, DynamoKeyMap } from "./types";
4
+ export { ItemNotFoundError, InvalidParametersError, DecoratorMissingError, DuplicateDecoratorError, } from "./exceptions";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGlE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGrE,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { DynamoDBRepository } from "./simple-dynamodb-repository";
2
+ export { DynamoTable, PartitionKey, SortKey, IndexPartitionKey, IndexSortKey, getDynamoTableName, getPartitionKeyName, getSortKeyName, getIndexPartitionKeyName, getIndexSortKeyName, } from "./decorators";
3
+ export { ItemNotFoundError, InvalidParametersError, DecoratorMissingError, DuplicateDecoratorError, } from "./exceptions";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGlE,OAAO,EACL,WAAW,EACX,YAAY,EACZ,OAAO,EACP,iBAAiB,EACjB,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,cAAc,CAAC;AAMtB,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,cAAc,CAAC"}