@flipboxlabs/aws-audit-sdk 1.2.0 → 1.2.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/audits.js +1 -1
- package/dist/repository.d.ts +15 -1
- package/dist/repository.d.ts.map +1 -1
- package/dist/repository.js +48 -8
- package/dist/repository.utils.d.ts +10 -11
- package/dist/repository.utils.d.ts.map +1 -1
- package/dist/repository.utils.js +13 -38
- package/package.json +1 -1
package/dist/audits.js
CHANGED
|
@@ -120,7 +120,7 @@ export class Audits {
|
|
|
120
120
|
* @param item - The audit entry to buffer
|
|
121
121
|
*/
|
|
122
122
|
addAudit(item) {
|
|
123
|
-
if (
|
|
123
|
+
if (this.storedAudits.length >= MAX_AUDITS_SIZE) {
|
|
124
124
|
this.publishStoredAudits();
|
|
125
125
|
}
|
|
126
126
|
this.config.schemas.resourceReference.parse(item.target);
|
package/dist/repository.d.ts
CHANGED
|
@@ -176,7 +176,7 @@ export declare class AuditRepository<C extends AuditConfig> {
|
|
|
176
176
|
* - Timestamps (createdAt, updatedAt)
|
|
177
177
|
*
|
|
178
178
|
* @param items - Array of audit records to upsert
|
|
179
|
-
* @returns True when the operation completes (
|
|
179
|
+
* @returns True when the operation completes (retries unprocessed items with exponential backoff; throws if max retries exceeded)
|
|
180
180
|
*
|
|
181
181
|
* @example
|
|
182
182
|
* ```typescript
|
|
@@ -197,6 +197,20 @@ export declare class AuditRepository<C extends AuditConfig> {
|
|
|
197
197
|
* ```
|
|
198
198
|
*/
|
|
199
199
|
upsertBatch(items: Array<UpsertAuditInput>): Promise<boolean>;
|
|
200
|
+
/**
|
|
201
|
+
* Writes a batch of items to DynamoDB with exponential backoff retry for unprocessed items.
|
|
202
|
+
*
|
|
203
|
+
* DynamoDB's BatchWriteItem can return unprocessed items when throttled or under transient
|
|
204
|
+
* errors without throwing an exception. This method retries those items automatically.
|
|
205
|
+
*
|
|
206
|
+
* @param items - Array of WriteRequests to send
|
|
207
|
+
* @param tableName - Target DynamoDB table name
|
|
208
|
+
* @param maxRetries - Maximum number of retry attempts (default: 8)
|
|
209
|
+
* @param baseDelayMs - Initial delay in milliseconds before first retry (default: 100)
|
|
210
|
+
* @throws Error if unprocessed items remain after maxRetries attempts
|
|
211
|
+
* @internal
|
|
212
|
+
*/
|
|
213
|
+
private writeBatchWithRetry;
|
|
200
214
|
/**
|
|
201
215
|
* Upserts a single audit record with atomic attempt tracking.
|
|
202
216
|
*
|
package/dist/repository.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"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"}
|
package/dist/repository.js
CHANGED
|
@@ -126,7 +126,7 @@ export class AuditRepository {
|
|
|
126
126
|
* - Timestamps (createdAt, updatedAt)
|
|
127
127
|
*
|
|
128
128
|
* @param items - Array of audit records to upsert
|
|
129
|
-
* @returns True when the operation completes (
|
|
129
|
+
* @returns True when the operation completes (retries unprocessed items with exponential backoff; throws if max retries exceeded)
|
|
130
130
|
*
|
|
131
131
|
* @example
|
|
132
132
|
* ```typescript
|
|
@@ -180,13 +180,53 @@ export class AuditRepository {
|
|
|
180
180
|
for (let i = 0, j = Items.length; i < j; i += BATCH_SIZE) {
|
|
181
181
|
ItemBatches.push(Items.slice(i, i + BATCH_SIZE));
|
|
182
182
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}))));
|
|
183
|
+
// Process batches sequentially to reduce risk of triggering additional throttling
|
|
184
|
+
for (const ItemBatch of ItemBatches) {
|
|
185
|
+
await this.writeBatchWithRetry(ItemBatch, DynamoDB.Table.Name());
|
|
186
|
+
}
|
|
188
187
|
return true;
|
|
189
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Writes a batch of items to DynamoDB with exponential backoff retry for unprocessed items.
|
|
191
|
+
*
|
|
192
|
+
* DynamoDB's BatchWriteItem can return unprocessed items when throttled or under transient
|
|
193
|
+
* errors without throwing an exception. This method retries those items automatically.
|
|
194
|
+
*
|
|
195
|
+
* @param items - Array of WriteRequests to send
|
|
196
|
+
* @param tableName - Target DynamoDB table name
|
|
197
|
+
* @param maxRetries - Maximum number of retry attempts (default: 8)
|
|
198
|
+
* @param baseDelayMs - Initial delay in milliseconds before first retry (default: 100)
|
|
199
|
+
* @throws Error if unprocessed items remain after maxRetries attempts
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
async writeBatchWithRetry(items, tableName, maxRetries = 8, baseDelayMs = 100) {
|
|
203
|
+
let requestItems = { [tableName]: items };
|
|
204
|
+
let attempt = 0;
|
|
205
|
+
let delayMs = baseDelayMs;
|
|
206
|
+
while (attempt <= maxRetries) {
|
|
207
|
+
const response = await this.client.send(new BatchWriteItemCommand({ RequestItems: requestItems }));
|
|
208
|
+
const unprocessed = response.UnprocessedItems;
|
|
209
|
+
if (!unprocessed || Object.keys(unprocessed).length === 0) {
|
|
210
|
+
return; // all items written successfully
|
|
211
|
+
}
|
|
212
|
+
attempt++;
|
|
213
|
+
if (attempt > maxRetries) {
|
|
214
|
+
this.logger.error("BatchWriteItem: unprocessed items remain after max retries", {
|
|
215
|
+
unprocessed,
|
|
216
|
+
attempt,
|
|
217
|
+
});
|
|
218
|
+
throw new Error(`BatchWriteItem failed: unprocessed items after ${maxRetries} retries`);
|
|
219
|
+
}
|
|
220
|
+
this.logger.warn("BatchWriteItem: retrying unprocessed items", {
|
|
221
|
+
count: Object.values(unprocessed).flat().length,
|
|
222
|
+
attempt,
|
|
223
|
+
delayMs,
|
|
224
|
+
});
|
|
225
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
226
|
+
delayMs = Math.min(delayMs * 2, 30_000); // cap at 30 seconds
|
|
227
|
+
requestItems = unprocessed;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
190
230
|
/**
|
|
191
231
|
* Upserts a single audit record with atomic attempt tracking.
|
|
192
232
|
*
|
|
@@ -524,11 +564,11 @@ export class AuditRepository {
|
|
|
524
564
|
* @internal
|
|
525
565
|
*/
|
|
526
566
|
transformItem(item) {
|
|
527
|
-
return
|
|
567
|
+
return AuditSchema.parse({
|
|
528
568
|
trace: `${item.GSI1_SN_PK}:${item.GSI1_SN_SK}`,
|
|
529
569
|
...item,
|
|
530
570
|
id: item.id.split("#").pop(),
|
|
531
|
-
})
|
|
571
|
+
});
|
|
532
572
|
}
|
|
533
573
|
/**
|
|
534
574
|
* Transforms a trace listing item into an audit list item payload.
|
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Encodes a pagination object into
|
|
2
|
+
* Encodes a pagination object into a base64url token string.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* The token format is: `{iv}:{encryptedData}` where both parts are hex-encoded.
|
|
4
|
+
* Pagination tokens are opaque cursors representing DynamoDB LastEvaluatedKey
|
|
5
|
+
* objects. They do not require confidentiality — base64url encoding provides
|
|
6
|
+
* a compact, URL-safe representation.
|
|
8
7
|
*
|
|
9
8
|
* @param nextPageObject - DynamoDB LastEvaluatedKey object to encode
|
|
10
|
-
* @returns
|
|
9
|
+
* @returns Base64url-encoded pagination token string, or undefined if input is null/undefined
|
|
11
10
|
*
|
|
12
11
|
* @example
|
|
13
12
|
* ```typescript
|
|
14
13
|
* const lastKey = { PK: 'App1.USER', SK: 'audit-123' };
|
|
15
14
|
* const token = encodeNextPageToken(lastKey);
|
|
16
|
-
* // Returns
|
|
15
|
+
* // Returns a base64url string like: "eyJQSyI6IkFwcDEuVVNFUiIsIlNLIjoiYXVkaXQtMTIzIn0"
|
|
17
16
|
* ```
|
|
18
17
|
*/
|
|
19
18
|
export declare function encodeNextPageToken(nextPageObject?: Record<string, string>): string | undefined;
|
|
20
19
|
/**
|
|
21
|
-
* Decodes
|
|
20
|
+
* Decodes a base64url pagination token back into a DynamoDB key object.
|
|
22
21
|
*
|
|
23
|
-
* Reverses the
|
|
22
|
+
* Reverses the encoding performed by {@link encodeNextPageToken} to
|
|
24
23
|
* restore the original LastEvaluatedKey for DynamoDB queries.
|
|
25
24
|
*
|
|
26
|
-
* @param nextPageString -
|
|
25
|
+
* @param nextPageString - Base64url-encoded pagination token from a previous response
|
|
27
26
|
* @returns Decoded DynamoDB key object, or undefined if input is null/undefined/invalid
|
|
28
27
|
*
|
|
29
28
|
* @example
|
|
30
29
|
* ```typescript
|
|
31
|
-
* const token = "
|
|
30
|
+
* const token = "eyJQSyI6IkFwcDEuVVNFUiIsIlNLIjoiYXVkaXQtMTIzIn0";
|
|
32
31
|
* const lastKey = decodeNextPageToken(token);
|
|
33
32
|
* // Returns: { PK: 'App1.USER', SK: 'audit-123' }
|
|
34
33
|
* ```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.utils.d.ts","sourceRoot":"","sources":["../src/repository.utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"repository.utils.d.ts","sourceRoot":"","sources":["../src/repository.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,SAAS,CAM/F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACzC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAMpC"}
|
package/dist/repository.utils.js
CHANGED
|
@@ -1,70 +1,45 @@
|
|
|
1
|
-
import * as crypto from "node:crypto";
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
* AES-256-CTR provides symmetric encryption with counter mode.
|
|
5
|
-
*/
|
|
6
|
-
const ENCRYPTION_ALGORITHM = "aes-256-ctr";
|
|
7
|
-
/**
|
|
8
|
-
* Key size in bytes for AES-256 encryption.
|
|
9
|
-
*/
|
|
10
|
-
const KEY_SIZE = 32;
|
|
11
|
-
/**
|
|
12
|
-
* Encodes a pagination object into an encrypted token string.
|
|
2
|
+
* Encodes a pagination object into a base64url token string.
|
|
13
3
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* The token format is: `{iv}:{encryptedData}` where both parts are hex-encoded.
|
|
4
|
+
* Pagination tokens are opaque cursors representing DynamoDB LastEvaluatedKey
|
|
5
|
+
* objects. They do not require confidentiality — base64url encoding provides
|
|
6
|
+
* a compact, URL-safe representation.
|
|
18
7
|
*
|
|
19
8
|
* @param nextPageObject - DynamoDB LastEvaluatedKey object to encode
|
|
20
|
-
* @returns
|
|
9
|
+
* @returns Base64url-encoded pagination token string, or undefined if input is null/undefined
|
|
21
10
|
*
|
|
22
11
|
* @example
|
|
23
12
|
* ```typescript
|
|
24
13
|
* const lastKey = { PK: 'App1.USER', SK: 'audit-123' };
|
|
25
14
|
* const token = encodeNextPageToken(lastKey);
|
|
26
|
-
* // Returns
|
|
15
|
+
* // Returns a base64url string like: "eyJQSyI6IkFwcDEuVVNFUiIsIlNLIjoiYXVkaXQtMTIzIn0"
|
|
27
16
|
* ```
|
|
28
17
|
*/
|
|
29
18
|
export function encodeNextPageToken(nextPageObject) {
|
|
30
19
|
if (nextPageObject == null) {
|
|
31
20
|
return undefined;
|
|
32
21
|
}
|
|
33
|
-
|
|
34
|
-
const iv = crypto.randomBytes(16);
|
|
35
|
-
const cipher = crypto.createCipheriv(ENCRYPTION_ALGORITHM, Buffer.concat([Buffer.alloc(KEY_SIZE)], KEY_SIZE), iv);
|
|
36
|
-
const encrypted = cipher.update(nextPageString);
|
|
37
|
-
return [iv.toString("hex"), encrypted.toString("hex")].join(":");
|
|
22
|
+
return Buffer.from(JSON.stringify(nextPageObject)).toString("base64url");
|
|
38
23
|
}
|
|
39
24
|
/**
|
|
40
|
-
* Decodes
|
|
25
|
+
* Decodes a base64url pagination token back into a DynamoDB key object.
|
|
41
26
|
*
|
|
42
|
-
* Reverses the
|
|
27
|
+
* Reverses the encoding performed by {@link encodeNextPageToken} to
|
|
43
28
|
* restore the original LastEvaluatedKey for DynamoDB queries.
|
|
44
29
|
*
|
|
45
|
-
* @param nextPageString -
|
|
30
|
+
* @param nextPageString - Base64url-encoded pagination token from a previous response
|
|
46
31
|
* @returns Decoded DynamoDB key object, or undefined if input is null/undefined/invalid
|
|
47
32
|
*
|
|
48
33
|
* @example
|
|
49
34
|
* ```typescript
|
|
50
|
-
* const token = "
|
|
35
|
+
* const token = "eyJQSyI6IkFwcDEuVVNFUiIsIlNLIjoiYXVkaXQtMTIzIn0";
|
|
51
36
|
* const lastKey = decodeNextPageToken(token);
|
|
52
37
|
* // Returns: { PK: 'App1.USER', SK: 'audit-123' }
|
|
53
38
|
* ```
|
|
54
39
|
*/
|
|
55
40
|
export function decodeNextPageToken(nextPageString) {
|
|
56
|
-
if (nextPageString == null) {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
const textParts = nextPageString.split(":");
|
|
60
|
-
const iv = textParts.shift();
|
|
61
|
-
if (!iv) {
|
|
41
|
+
if (nextPageString == null || nextPageString === "") {
|
|
62
42
|
return undefined;
|
|
63
43
|
}
|
|
64
|
-
|
|
65
|
-
const decrypted = Buffer.concat([
|
|
66
|
-
decipher.update(Buffer.from(textParts.join(":"), "hex")),
|
|
67
|
-
decipher.final(),
|
|
68
|
-
]);
|
|
69
|
-
return JSON.parse(decrypted.toString());
|
|
44
|
+
return JSON.parse(Buffer.from(nextPageString, "base64url").toString());
|
|
70
45
|
}
|