@eresearchqut/ddb-repository 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.
@@ -0,0 +1,61 @@
1
+ name: Build
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, develop ]
6
+ pull_request:
7
+ branches: [ main, develop ]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Checkout code
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Setup Node.js
17
+ uses: actions/setup-node@v4
18
+ with:
19
+ node-version: 'lts/*'
20
+ registry-url: 'https://registry.npmjs.org'
21
+
22
+ - name: Install dependencies
23
+ run: npm ci
24
+
25
+ - name: Run linter
26
+ run: npm run lint
27
+
28
+ - name: Run tests with coverage
29
+ run: npm run test:coverage
30
+
31
+ - name: Upload coverage reports to Codecov
32
+ uses: codecov/codecov-action@v4
33
+ with:
34
+ file: ./coverage/lcov.info
35
+ flags: unittests
36
+ name: codecov-umbrella
37
+ fail_ci_if_error: false
38
+
39
+ - name: Upload coverage to Coveralls
40
+ uses: coverallsapp/github-action@v2
41
+ with:
42
+ github-token: ${{ secrets.GITHUB_TOKEN }}
43
+ path-to-lcov: ./coverage/lcov.info
44
+ continue-on-error: true
45
+
46
+ - name: Build package
47
+ run: npm run build
48
+
49
+ - name: Upload build artifacts
50
+ uses: actions/upload-artifact@v4
51
+ with:
52
+ name: dist
53
+ path: dist/
54
+ retention-days: 7
55
+
56
+ - name: Upload coverage artifacts
57
+ uses: actions/upload-artifact@v4
58
+ with:
59
+ name: coverage-report
60
+ path: coverage/
61
+ retention-days: 7
@@ -0,0 +1,42 @@
1
+ name: Publish
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ issues: write
14
+ pull-requests: write
15
+ id-token: write
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+ persist-credentials: false
22
+
23
+ - name: Setup Node.js
24
+ uses: actions/setup-node@v4
25
+ with:
26
+ node-version: 'lts/*'
27
+
28
+ - name: Install dependencies
29
+ run: npm ci
30
+
31
+ - name: Run tests
32
+ run: npm test
33
+
34
+ - name: Build
35
+ run: npm run build
36
+
37
+ - name: Release
38
+ env:
39
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
40
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
41
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
42
+ run: npx semantic-release
@@ -0,0 +1,17 @@
1
+ {
2
+ "branches": ["main"],
3
+ "plugins": [
4
+ "@semantic-release/commit-analyzer",
5
+ "@semantic-release/release-notes-generator",
6
+ "@semantic-release/changelog",
7
+ "@semantic-release/npm",
8
+ [
9
+ "@semantic-release/git",
10
+ {
11
+ "assets": ["package.json", "package-lock.json", "CHANGELOG.md"],
12
+ "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
13
+ }
14
+ ],
15
+ "@semantic-release/github"
16
+ ]
17
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ ## [1.0.2](https://github.com/eresearchqut/ddb-repository/compare/v1.0.1...v1.0.2) (2025-11-19)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * setting publish config access to public ([eed3a3b](https://github.com/eresearchqut/ddb-repository/commit/eed3a3b382153c457e3508d844e111b7f5b3123d))
7
+
8
+ ## [1.0.1](https://github.com/eresearchqut/ddb-repository/compare/v1.0.0...v1.0.1) (2025-11-19)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * remove npm registry url from the node setup as per comments on https://github.com/semantic-release/semantic-release/issues/2313 ([291d61b](https://github.com/eresearchqut/ddb-repository/commit/291d61b55c3c5c665623c264306c40e1c21977a2))
14
+ * Repository and package are now public ([b07f151](https://github.com/eresearchqut/ddb-repository/commit/b07f151732f8a158a2b6f0a584e64ea9eb7f5825))
15
+ * updated secret name for GITHUB_TOKEN ([7e00208](https://github.com/eresearchqut/ddb-repository/commit/7e00208f487f5987e72646c15a50fe78808f6ba4))
16
+ * updated secret name for GITHUB_TOKEN ([6d3eff2](https://github.com/eresearchqut/ddb-repository/commit/6d3eff2fff43f0d0848cfc03b7e035d830a3ceb5))
17
+
18
+ # 1.0.0 (2025-11-19)
19
+
20
+
21
+ ### Features
22
+
23
+ * coverage badge ([0f3c9d1](https://github.com/eresearchqut/ddb-repository/commit/0f3c9d128b25466758062ea9e41d1c7d755c2191))
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # DynamoDB Repository
2
+
3
+ [![Coverage Status](https://coveralls.io/repos/github/eresearchqut/ddb-repository/badge.svg?branch=main)](https://coveralls.io/github/eresearchqut/ddb-repository?branch=main)
4
+
5
+ A TypeScript library providing a generic repository pattern implementation for AWS DynamoDB, simplifying CRUD operations and common database interactions.
6
+
7
+ ## Features
8
+
9
+ - 🚀 Generic repository pattern for type-safe DynamoDB operations
10
+ - 📦 Simple and intuitive API
11
+ - 🔍 Support for common CRUD operations (Create, Read, Update, Delete)
12
+ - 🎯 Batch operations support
13
+ - 🧪 Fully tested with Jest and Testcontainers
14
+ - 💪 Written in TypeScript with full type safety
15
+ - ⚡ Built on top of AWS SDK v3
16
+
17
+ ## Installation
18
+ ```sh
19
+ npm install ddb-repository
20
+ ```
21
+ ## Prerequisites
22
+
23
+ - Node.js
24
+ - AWS credentials configured (for production use)
25
+ - DynamoDB table with appropriate schema
26
+
27
+ ## Usage
28
+
29
+ ### Basic Example
30
+ ```typescript
31
+ import { DynamoDbRepository } from 'ddb-repository';
32
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
33
+
34
+ // Define your entity type
35
+ interface User {
36
+ id: string;
37
+ name: string;
38
+ email: string;
39
+ createdAt: string;
40
+ }
41
+
42
+ // Initialize DynamoDB client
43
+ const client = new DynamoDBClient({ region: 'us-east-1' });
44
+
45
+ // Create repository instance
46
+ const userRepository = new DynamoDbRepository<User>(
47
+ client,
48
+ 'users-table',
49
+ 'id' // partition key
50
+ );
51
+
52
+ // Create a new user
53
+ await userRepository.create({
54
+ id: '123',
55
+ name: 'John Doe',
56
+ email: 'john@example.com',
57
+ createdAt: new Date().toISOString()
58
+ });
59
+
60
+ // Find a user by ID
61
+ const user = await userRepository.findById('123');
62
+
63
+ // Update a user
64
+ await userRepository.update('123', {
65
+ email: 'newemail@example.com'
66
+ });
67
+
68
+ // Delete a user
69
+ await userRepository.delete('123');
70
+ ```
71
+
72
+ ## API Reference
73
+
74
+ ### Constructor
75
+ ```
76
+ typescript
77
+ new DynamoDbRepository<T>(client: DynamoDBClient, tableName: string, partitionKey: string, sortKey?: string)
78
+ ```
79
+ ### Methods
80
+
81
+ - `create(item: T): Promise<T>` - Create a new item
82
+ - `findById(id: string): Promise<T | null>` - Find item by partition key
83
+ - `update(id: string, updates: Partial<T>): Promise<T>` - Update an existing item
84
+ - `delete(id: string): Promise<void>` - Delete an item
85
+ - `findAll(): Promise<T[]>` - Retrieve all items (use with caution on large tables)
86
+ - `batchCreate(items: T[]): Promise<void>` - Create multiple items in batch
87
+ - `query(options: QueryOptions): Promise<T[]>` - Query items with custom conditions
88
+
89
+ ## Development
90
+
91
+ ### Setup
92
+ ```sh
93
+ # Install dependencies
94
+ npm install
95
+
96
+ # Run linter
97
+ npm run lint
98
+
99
+ # Fix linting issues automatically
100
+ npm run lint:fix
101
+
102
+ # Run tests
103
+ npm test
104
+
105
+ # Run tests in watch mode
106
+ npm run test:watch
107
+
108
+ # Run tests with coverage report
109
+ npm run test:coverage
110
+
111
+ # Build the project
112
+ npm run build
113
+ ```
114
+ ### Testing
115
+
116
+ The project uses Jest with Testcontainers for integration testing against a real DynamoDB instance:
117
+ ```sh
118
+ npm test
119
+ ```
120
+
121
+ ### Run tests with coverage
122
+
123
+ Generate coverage report
124
+
125
+ ```sh
126
+ npm run test:coverage
127
+ ```
128
+
129
+ Coverage reports are generated in the coverage/ directory:
130
+ * coverage/lcov-report/index.html - Interactive HTML report
131
+ * coverage/lcov.info - LCOV format for CI/CD integration
132
+
133
+ View HTML coverage report, open coverage/lcov-report/index.html
134
+
135
+ ## Configuration
136
+
137
+ ### AWS Credentials
138
+
139
+ Ensure your AWS credentials are configured via:
140
+ - Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)
141
+ - AWS credentials file (`~/.aws/credentials`)
142
+ - IAM role (when running on AWS infrastructure)
143
+
144
+ ### Required IAM Permissions
145
+
146
+ ```json
147
+ {
148
+ "Version": "2012-10-17",
149
+ "Statement": [
150
+ {
151
+ "Effect": "Allow",
152
+ "Action": [
153
+ "dynamodb:PutItem",
154
+ "dynamodb:GetItem",
155
+ "dynamodb:UpdateItem",
156
+ "dynamodb:DeleteItem",
157
+ "dynamodb:Query",
158
+ "dynamodb:Scan",
159
+ "dynamodb:BatchWriteItem"
160
+ ],
161
+ "Resource": "arn:aws:dynamodb:*:*:table/your-table-name"
162
+ }
163
+ ]
164
+ }
165
+ ```
166
+
167
+ ## Contributing
168
+
169
+ Contributions are welcome! Please feel free to submit a Pull Request.
170
+
171
+
172
+ ### Commit message convention
173
+ Semantic release uses conventional commits. Your commit messages should follow this format:
174
+
175
+ * feat: new feature → triggers minor version bump (1.x.0)
176
+ * fix: bug fix → triggers patch version bump (1.0.x)
177
+ * perf: performance improvement → triggers patch version bump
178
+ * docs: documentation change → no release
179
+ * chore: maintenance task → no release
180
+ * BREAKING CHANGE: in footer → triggers major version bump (x.0.0)
181
+
182
+ Example:
183
+
184
+ > feat: add batch write support
185
+ >
186
+ > Added support for batch write operations to improve performance
187
+
188
+ Or with breaking change:
189
+
190
+ > feat: change repository API
191
+ >
192
+ > BREAKING CHANGE: The query method now returns a Promise instead of an Observable
193
+
194
+
195
+ ## License
196
+
197
+ MIT
198
+
199
+ ## Support
200
+
201
+ For issues and questions, please open an issue on the GitHub repository.
202
+
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ var __asyncValues = (this && this.__asyncValues) || function (o) {
23
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
24
+ var m = o[Symbol.asyncIterator], i;
25
+ return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
26
+ function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
27
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.DynamoDbRepository = exports.mapFilterExpressions = exports.mapFilterExpression = exports.FilterOperator = void 0;
31
+ const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
32
+ const util_dynamodb_1 = require("@aws-sdk/util-dynamodb");
33
+ const lodash_1 = require("lodash");
34
+ const expressionAttributeKey = (key) => (0, lodash_1.replace)(key, /-/g, "_");
35
+ var FilterOperator;
36
+ (function (FilterOperator) {
37
+ FilterOperator["EQUALS"] = "=";
38
+ FilterOperator["NOT_EQUALS"] = "<>";
39
+ FilterOperator["GREATER_THAN_OR_EQUALS"] = ">=";
40
+ FilterOperator["GREATER_THAN"] = ">";
41
+ FilterOperator["LESS_THAN"] = "<";
42
+ FilterOperator["LESS_THAN_OR_EQUALS"] = "<=";
43
+ FilterOperator["IN"] = "IN";
44
+ FilterOperator["BETWEEN"] = "BETWEEN";
45
+ })(FilterOperator || (exports.FilterOperator = FilterOperator = {}));
46
+ const mapInKeys = (filterExpression) => Array.isArray(filterExpression.value)
47
+ ? filterExpression.value.map((_, index) => `:${filterExpression.attribute}${index}`)
48
+ : `:${filterExpression.attribute}`;
49
+ const mapFilterExpression = (filterExpression) => {
50
+ switch (filterExpression.operator) {
51
+ case FilterOperator.IN:
52
+ return (`#${expressionAttributeKey(filterExpression.attribute)} ${filterExpression.operator} ` +
53
+ `(${mapInKeys(filterExpression)})`);
54
+ case FilterOperator.BETWEEN:
55
+ return (`#${expressionAttributeKey(filterExpression.attribute)} ${filterExpression.operator} ` +
56
+ `:${expressionAttributeKey(filterExpression.attribute)}0 AND :${expressionAttributeKey(filterExpression.attribute)}1`);
57
+ default:
58
+ return (`#${expressionAttributeKey(filterExpression.attribute)} ${filterExpression.operator} ` +
59
+ `:${expressionAttributeKey(filterExpression.attribute)}`);
60
+ }
61
+ };
62
+ exports.mapFilterExpression = mapFilterExpression;
63
+ const mapFilterExpressions = (filterExpressions) => filterExpressions
64
+ .map((filterExpression) => filterExpression.negate
65
+ ? `NOT ${(0, exports.mapFilterExpression)(filterExpression)}`
66
+ : (0, exports.mapFilterExpression)(filterExpression))
67
+ .join(" AND ");
68
+ exports.mapFilterExpressions = mapFilterExpressions;
69
+ const mapFilterExpressionValues = (filterExpression) => Array.isArray(filterExpression.value)
70
+ ? filterExpression.value.reduce((reduction, value, index) => (Object.assign(Object.assign({}, reduction), { [`:${expressionAttributeKey(filterExpression.attribute)}${index}`]: value })), Object.assign({}))
71
+ : {
72
+ [`:${expressionAttributeKey(filterExpression.attribute)}`]: filterExpression.value,
73
+ };
74
+ const paginate = (array, pageSize) => {
75
+ return array.reduce((acc, val, i) => {
76
+ const idx = Math.floor(i / pageSize);
77
+ const page = acc[idx] || (acc[idx] = []);
78
+ page.push(val);
79
+ return acc;
80
+ }, []);
81
+ };
82
+ class DynamoDbRepository {
83
+ constructor(dynamoDBClient, tableName, hashKey, rangKey) {
84
+ this.dynamoDBClient = dynamoDBClient;
85
+ this.tableName = tableName;
86
+ this.hashKey = hashKey;
87
+ this.rangKey = rangKey;
88
+ this.getItem = (key) => __awaiter(this, void 0, void 0, function* () {
89
+ return this.dynamoDBClient
90
+ .send(new client_dynamodb_1.GetItemCommand({
91
+ TableName: this.tableName,
92
+ Key: (0, util_dynamodb_1.marshall)(key, { removeUndefinedValues: true }),
93
+ }))
94
+ .then((result) => result.Item ? (0, util_dynamodb_1.unmarshall)(result.Item) : undefined);
95
+ });
96
+ this.putItem = (key, record) => __awaiter(this, void 0, void 0, function* () {
97
+ const Item = (0, util_dynamodb_1.marshall)(Object.assign(Object.assign({}, record), key), { removeUndefinedValues: true });
98
+ return this.dynamoDBClient
99
+ .send(new client_dynamodb_1.PutItemCommand({
100
+ TableName: this.tableName,
101
+ Item,
102
+ }))
103
+ .then(() => this.getItem(key));
104
+ });
105
+ this.deleteItem = (key) => __awaiter(this, void 0, void 0, function* () {
106
+ return this.dynamoDBClient.send(new client_dynamodb_1.DeleteItemCommand({
107
+ TableName: this.tableName,
108
+ Key: (0, util_dynamodb_1.marshall)(key),
109
+ })).then((result) => result.Attributes ?
110
+ (0, util_dynamodb_1.unmarshall)(result.Attributes) : undefined);
111
+ });
112
+ this.updateItem = (key, updates, remove) => __awaiter(this, void 0, void 0, function* () {
113
+ const hasUpdates = Object.keys(updates).length > 0;
114
+ const setAttributesExpression = hasUpdates ? `SET ${Object.entries(updates)
115
+ .filter(([, value]) => value !== undefined)
116
+ .map(([key]) => `#${expressionAttributeKey(key)} = :${expressionAttributeKey(key)}`)
117
+ .join(", ")}` : '';
118
+ const removeAttributesExpression = remove
119
+ ? ` REMOVE ${remove.map((key) => `#${expressionAttributeKey(key)}`).join(", ")}`
120
+ : "";
121
+ const removeAttributeNames = remove
122
+ ? remove.map(expressionAttributeKey).reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [`#${expressionAttributeKey(key)}`]: key })), {})
123
+ : {};
124
+ const updateItemCommandInput = {
125
+ TableName: this.tableName,
126
+ Key: (0, util_dynamodb_1.marshall)(key),
127
+ UpdateExpression: `${setAttributesExpression}${removeAttributesExpression}`,
128
+ ExpressionAttributeNames: Object.entries(updates)
129
+ .filter(([, value]) => value !== undefined)
130
+ .reduce((acc, [key]) => (Object.assign(Object.assign({}, acc), { [`#${expressionAttributeKey(key)}`]: key })), Object.assign(removeAttributeNames)),
131
+ ExpressionAttributeValues: hasUpdates ? (0, util_dynamodb_1.marshall)(Object.entries(updates).reduce((acc, [key, value]) => (Object.assign(Object.assign({}, acc), { [`:${expressionAttributeKey(key)}`]: value })), Object.assign({})), { removeUndefinedValues: true }) : undefined,
132
+ };
133
+ return this.dynamoDBClient
134
+ .send(new client_dynamodb_1.UpdateItemCommand(updateItemCommandInput))
135
+ .then(() => this.getItem(key));
136
+ });
137
+ this.getItems = (query) => __awaiter(this, void 0, void 0, function* () {
138
+ var _a, e_1, _b, _c, _d, e_2, _e, _f;
139
+ var _g;
140
+ const { index, filterExpressions, projectedAttributes } = query, keys = __rest(query, ["index", "filterExpressions", "projectedAttributes"]);
141
+ const KeyConditionExpression = Object.keys(keys)
142
+ .map((key) => `#${expressionAttributeKey(key)} = :${expressionAttributeKey(key)}`).join(' AND ');
143
+ const keyExpressionAttributeNames = Object.keys(keys)
144
+ .reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [`#${expressionAttributeKey(key)}`]: key })), Object.assign({}));
145
+ const keyExpressionAttributeValues = Object.entries(keys)
146
+ .reduce((acc, [key, value]) => (Object.assign(Object.assign({}, acc), { [`:${expressionAttributeKey(key)}`]: value })), Object.assign({}));
147
+ const ProjectionExpression = !index && projectedAttributes
148
+ ? projectedAttributes.map((attribute) => `#${expressionAttributeKey(attribute)}`).join(',')
149
+ : undefined;
150
+ const projectionAttributeNames = !index && projectedAttributes ? projectedAttributes.reduce((reduction, attribute) => (Object.assign(Object.assign({}, reduction), { [`#${expressionAttributeKey(attribute)}`]: attribute })), Object.assign({})) : {};
151
+ const hasFilterExpressions = Array.isArray(filterExpressions) && filterExpressions.length > 0;
152
+ const FilterExpression = hasFilterExpressions
153
+ ? (0, exports.mapFilterExpressions)(filterExpressions)
154
+ : undefined;
155
+ const filterAttributeNames = hasFilterExpressions
156
+ ? filterExpressions.reduce((reduction, filterExpression) => (Object.assign(Object.assign({}, reduction), { [`#${expressionAttributeKey(filterExpression.attribute)}`]: filterExpression.attribute })), Object.assign({}))
157
+ : {};
158
+ const filterAttributeValues = filterExpressions
159
+ ? filterExpressions.reduce((reduction, filterExpression) => (Object.assign(Object.assign({}, reduction), mapFilterExpressionValues(filterExpression))), Object.assign({}))
160
+ : {};
161
+ const queryCommandInput = {
162
+ TableName: this.tableName,
163
+ IndexName: index,
164
+ KeyConditionExpression,
165
+ FilterExpression,
166
+ ProjectionExpression,
167
+ ExpressionAttributeNames: Object.assign(Object.assign(Object.assign({}, keyExpressionAttributeNames), filterAttributeNames), projectionAttributeNames),
168
+ ExpressionAttributeValues: (0, util_dynamodb_1.marshall)(Object.assign(Object.assign({}, keyExpressionAttributeValues), filterAttributeValues), { removeUndefinedValues: true }),
169
+ };
170
+ const paginator = (0, client_dynamodb_1.paginateQuery)({ client: this.dynamoDBClient, pageSize: 100 }, queryCommandInput);
171
+ if (index) {
172
+ const keys = [];
173
+ try {
174
+ for (var _h = true, paginator_1 = __asyncValues(paginator), paginator_1_1; paginator_1_1 = yield paginator_1.next(), _a = paginator_1_1.done, !_a; _h = true) {
175
+ _c = paginator_1_1.value;
176
+ _h = false;
177
+ const page = _c;
178
+ if (page.Items) {
179
+ keys.push(...(page.Items.map((item) => (0, util_dynamodb_1.unmarshall)(item))
180
+ .map((item) => (0, lodash_1.pickBy)(item, (_, key) => (key === this.hashKey || key === this.rangKey)))));
181
+ }
182
+ }
183
+ }
184
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
185
+ finally {
186
+ try {
187
+ if (!_h && !_a && (_b = paginator_1.return)) yield _b.call(paginator_1);
188
+ }
189
+ finally { if (e_1) throw e_1.error; }
190
+ }
191
+ const items = yield this.batchGetItems(keys, query);
192
+ return items;
193
+ }
194
+ const items = [];
195
+ try {
196
+ for (var _j = true, paginator_2 = __asyncValues(paginator), paginator_2_1; paginator_2_1 = yield paginator_2.next(), _d = paginator_2_1.done, !_d; _j = true) {
197
+ _f = paginator_2_1.value;
198
+ _j = false;
199
+ const page = _f;
200
+ if (page.Items) {
201
+ items.push(...(((_g = page.Items) === null || _g === void 0 ? void 0 : _g.map((item) => (0, util_dynamodb_1.unmarshall)(item))) || []));
202
+ }
203
+ }
204
+ }
205
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
206
+ finally {
207
+ try {
208
+ if (!_j && !_d && (_e = paginator_2.return)) yield _e.call(paginator_2);
209
+ }
210
+ finally { if (e_2) throw e_2.error; }
211
+ }
212
+ return items;
213
+ });
214
+ this.batchGetItems = (keys, projectedQuery) => __awaiter(this, void 0, void 0, function* () {
215
+ const uniqueKeys = (0, lodash_1.uniqWith)(keys, lodash_1.isEqual);
216
+ const keyPages = paginate(uniqueKeys, 100);
217
+ const { projectedAttributes } = projectedQuery || {};
218
+ const ProjectionExpression = projectedAttributes
219
+ ? projectedAttributes.map((attribute) => `#${expressionAttributeKey(attribute)}`).join(',')
220
+ : undefined;
221
+ const ExpressionAttributeNames = projectedAttributes ?
222
+ projectedAttributes.reduce((reduction, attribute) => (Object.assign(Object.assign({}, reduction), { [`#${expressionAttributeKey(attribute)}`]: attribute })), Object.assign({})) : undefined;
223
+ return Promise.all((keyPages.map((keyPage) => __awaiter(this, void 0, void 0, function* () {
224
+ const batchRequest = {
225
+ RequestItems: {
226
+ [this.tableName]: {
227
+ Keys: keyPage.map((key) => ((0, util_dynamodb_1.marshall)(key))),
228
+ ProjectionExpression,
229
+ ExpressionAttributeNames
230
+ }
231
+ }
232
+ };
233
+ return this.dynamoDBClient.send(new client_dynamodb_1.BatchGetItemCommand(batchRequest)).then(result => { var _a; return (_a = result.Responses) === null || _a === void 0 ? void 0 : _a[this.tableName].map((item) => (0, util_dynamodb_1.unmarshall)(item)); });
234
+ }))))
235
+ .then((itemSets) => itemSets.flat());
236
+ });
237
+ }
238
+ }
239
+ exports.DynamoDbRepository = DynamoDbRepository;
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./DynamoDbRepository"), exports);
@@ -0,0 +1,13 @@
1
+ import eslint from '@eslint/js';
2
+ import { defineConfig, globalIgnores } from 'eslint/config';
3
+
4
+ import tseslint from 'typescript-eslint';
5
+
6
+ export default defineConfig([
7
+ globalIgnores([
8
+ "node_modules/*", // ignore its content
9
+ "dist/*",
10
+ ])],
11
+ eslint.configs.recommended,
12
+ tseslint.configs.recommended,
13
+ );
package/jest.config.ts ADDED
@@ -0,0 +1,28 @@
1
+ import type { Config } from '@jest/types';
2
+
3
+ const config: Config.InitialOptions = {
4
+ preset: 'ts-jest',
5
+ testEnvironment: 'node',
6
+ roots: ['<rootDir>/src', '<rootDir>/test'],
7
+ testMatch: ['**/*.test.ts', '**/*.spec.ts'],
8
+ collectCoverage: false, // Set to true by default if you want coverage on every run
9
+ collectCoverageFrom: [
10
+ 'src/**/*.ts',
11
+ '!src/**/*.d.ts',
12
+ '!src/**/*.interface.ts',
13
+ '!src/**/index.ts', // Typically just exports, can be excluded
14
+ ],
15
+ coverageDirectory: 'coverage',
16
+ coverageReporters: [
17
+ 'text', // Console output
18
+ 'text-summary', // Summary in console
19
+ 'html', // HTML report in coverage/
20
+ 'lcov', // For CI/CD tools
21
+ 'json', // JSON format
22
+ ],
23
+ moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
24
+ verbose: true,
25
+ testTimeout: 60000
26
+ };
27
+
28
+ export default config;