@aligent/aws-wrappers 0.0.1 → 0.1.1

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.
Files changed (39) hide show
  1. package/cjs/index.cjs +1911 -0
  2. package/cjs/index.d.ts +1 -0
  3. package/cjs/package.json +51 -0
  4. package/{src → cjs/src}/dynamodb/dynamodb.d.ts +2 -2
  5. package/cjs/src/index.d.ts +7 -0
  6. package/{src → cjs/src}/s3/s3.d.ts +2 -2
  7. package/{src → cjs/src}/secrets-manager/secrets-manager.d.ts +2 -2
  8. package/{src → cjs/src}/sfn/sfn.d.ts +2 -2
  9. package/{src → cjs/src}/sns/sns.d.ts +2 -2
  10. package/{src → cjs/src}/sqs/sqs.d.ts +2 -2
  11. package/{src → cjs/src}/ssm/ssm.d.ts +2 -2
  12. package/{src → cjs/src}/util/redact.d.ts +2 -2
  13. package/esm/index.d.ts +1 -0
  14. package/esm/index.mjs +1903 -0
  15. package/esm/package.json +51 -0
  16. package/esm/src/dynamodb/dynamodb.d.ts +127 -0
  17. package/esm/src/index.d.ts +7 -0
  18. package/esm/src/s3/s3.d.ts +131 -0
  19. package/esm/src/secrets-manager/secrets-manager.d.ts +78 -0
  20. package/esm/src/sfn/sfn.d.ts +38 -0
  21. package/esm/src/sns/sns.d.ts +48 -0
  22. package/esm/src/sqs/sqs.d.ts +60 -0
  23. package/esm/src/ssm/ssm.d.ts +84 -0
  24. package/{src/util/redact.js → esm/src/util/redact.d.ts} +2 -13
  25. package/esm/src/util/truncate.d.ts +15 -0
  26. package/package.json +25 -7
  27. package/CLAUDE.md +0 -172
  28. package/src/dynamodb/dynamodb.js +0 -308
  29. package/src/index.d.ts +0 -7
  30. package/src/index.js +0 -17
  31. package/src/s3/s3.js +0 -244
  32. package/src/secrets-manager/secrets-manager.js +0 -152
  33. package/src/sfn/sfn.js +0 -74
  34. package/src/sns/sns.js +0 -110
  35. package/src/sqs/sqs.js +0 -134
  36. package/src/ssm/ssm.js +0 -144
  37. package/src/util/truncate.js +0 -36
  38. package/tsconfig.lib.tsbuildinfo +0 -1
  39. /package/{src → cjs/src}/util/truncate.d.ts +0 -0
package/esm/index.mjs ADDED
@@ -0,0 +1,1903 @@
1
+ import { Logger } from '@aws-lambda-powertools/logger';
2
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
3
+ import { DynamoDBDocumentClient, paginateQuery, paginateScan, GetCommand, PutCommand, UpdateCommand, DeleteCommand, QueryCommand, ScanCommand, BatchGetCommand, BatchWriteCommand } from '@aws-sdk/lib-dynamodb';
4
+ import xray from 'aws-xray-sdk-core';
5
+ import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand, CopyObjectCommand, paginateListObjectsV2, DeleteObjectCommand, DeleteObjectsCommand } from '@aws-sdk/client-s3';
6
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
7
+ import { SecretsManagerClient, CreateSecretCommand, UpdateSecretCommand, PutSecretValueCommand, DeleteSecretCommand, GetSecretValueCommand } from '@aws-sdk/client-secrets-manager';
8
+ import { SFNClient, paginateListExecutions, StartExecutionCommand, DescribeExecutionCommand, StopExecutionCommand } from '@aws-sdk/client-sfn';
9
+ import { SNSClient, PublishCommand, PublishBatchCommand } from '@aws-sdk/client-sns';
10
+ import { SQSClient, SendMessageCommand, ReceiveMessageCommand, DeleteMessageCommand, SendMessageBatchCommand, DeleteMessageBatchCommand } from '@aws-sdk/client-sqs';
11
+ import { SSMClient, GetParameterCommand, GetParametersCommand, PutParameterCommand, DeleteParameterCommand, paginateGetParametersByPath } from '@aws-sdk/client-ssm';
12
+
13
+ /******************************************************************************
14
+ Copyright (c) Microsoft Corporation.
15
+
16
+ Permission to use, copy, modify, and/or distribute this software for any
17
+ purpose with or without fee is hereby granted.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
20
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
21
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
22
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
24
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
25
+ PERFORMANCE OF THIS SOFTWARE.
26
+ ***************************************************************************** */
27
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
28
+
29
+
30
+ var __assign = function() {
31
+ __assign = Object.assign || function __assign(t) {
32
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
33
+ s = arguments[i];
34
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
35
+ }
36
+ return t;
37
+ };
38
+ return __assign.apply(this, arguments);
39
+ };
40
+
41
+ function __awaiter(thisArg, _arguments, P, generator) {
42
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
43
+ return new (P || (P = Promise))(function (resolve, reject) {
44
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
45
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
46
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
47
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
48
+ });
49
+ }
50
+
51
+ function __generator(thisArg, body) {
52
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
53
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
54
+ function verb(n) { return function (v) { return step([n, v]); }; }
55
+ function step(op) {
56
+ if (f) throw new TypeError("Generator is already executing.");
57
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
58
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
59
+ if (y = 0, t) op = [op[0] & 2, t.value];
60
+ switch (op[0]) {
61
+ case 0: case 1: t = op; break;
62
+ case 4: _.label++; return { value: op[1], done: false };
63
+ case 5: _.label++; y = op[1]; op = [0]; continue;
64
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
65
+ default:
66
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
67
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
68
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
69
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
70
+ if (t[2]) _.ops.pop();
71
+ _.trys.pop(); continue;
72
+ }
73
+ op = body.call(thisArg, _);
74
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
75
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
76
+ }
77
+ }
78
+
79
+ function __values(o) {
80
+ var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
81
+ if (m) return m.call(o);
82
+ if (o && typeof o.length === "number") return {
83
+ next: function () {
84
+ if (o && i >= o.length) o = void 0;
85
+ return { value: o && o[i++], done: !o };
86
+ }
87
+ };
88
+ throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
89
+ }
90
+
91
+ function __await(v) {
92
+ return this instanceof __await ? (this.v = v, this) : new __await(v);
93
+ }
94
+
95
+ function __asyncGenerator(thisArg, _arguments, generator) {
96
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
97
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
98
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
99
+ function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
100
+ function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
101
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
102
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
103
+ function fulfill(value) { resume("next", value); }
104
+ function reject(value) { resume("throw", value); }
105
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
106
+ }
107
+
108
+ function __asyncValues(o) {
109
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
110
+ var m = o[Symbol.asyncIterator], i;
111
+ 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);
112
+ 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); }); }; }
113
+ function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
114
+ }
115
+
116
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
117
+ var e = new Error(message);
118
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
119
+ };
120
+
121
+ /**
122
+ * Return a log-safe projection of `input` based on the logger's configured level.
123
+ *
124
+ * At `DEBUG`, the full input is returned unchanged — operators who set
125
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` (or call `logger.setLogLevel('DEBUG')`) have
126
+ * explicitly opted into seeing everything, including payloads, secret material
127
+ * and PII.
128
+ *
129
+ * At any other level, only the fields listed in `safeFields` are included.
130
+ * Missing fields are silently skipped — the result type narrows to
131
+ * `Pick<T, K>` accordingly.
132
+ *
133
+ * Used across the package so that the "what's safe to log at INFO" decision
134
+ * lives in one place. See `packages/aws-wrappers/CLAUDE.md` ("Logging") for
135
+ * the design rationale and conventions on building the safe-field lists.
136
+ */
137
+ function filterFieldsForLogLevel(logger, input, safeFields) {
138
+ if (logger.getLevelName() === 'DEBUG')
139
+ return input;
140
+ var out = {};
141
+ for (var _i = 0, safeFields_1 = safeFields; _i < safeFields_1.length; _i++) {
142
+ var key = safeFields_1[_i];
143
+ if (key in input)
144
+ out[key] = input[key];
145
+ }
146
+ return out;
147
+ }
148
+
149
+ var BATCH_WRITE_MAX_ATTEMPTS = 5;
150
+ var BATCH_WRITE_BASE_DELAY_MS = 200;
151
+ /**
152
+ * Fields safe to log at INFO. Omits `Key` (may carry customer IDs / tenant IDs).
153
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
154
+ */
155
+ var GET_ITEM_SAFE_FIELDS = [
156
+ 'TableName',
157
+ 'ConsistentRead',
158
+ 'ProjectionExpression',
159
+ 'ReturnConsumedCapacity',
160
+ 'ExpressionAttributeNames',
161
+ ];
162
+ /**
163
+ * Fields safe to log at INFO. Omits `Item` (the payload itself) and
164
+ * `ExpressionAttributeValues` (values bound to ConditionExpression, often PII).
165
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
166
+ */
167
+ var PUT_ITEM_SAFE_FIELDS = [
168
+ 'TableName',
169
+ 'ConditionExpression',
170
+ 'ExpressionAttributeNames',
171
+ 'ReturnValues',
172
+ 'ReturnConsumedCapacity',
173
+ 'ReturnItemCollectionMetrics',
174
+ 'ReturnValuesOnConditionCheckFailure',
175
+ ];
176
+ /**
177
+ * Fields safe to log at INFO. Omits `Key` and `ExpressionAttributeValues`.
178
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
179
+ */
180
+ var UPDATE_ITEM_SAFE_FIELDS = [
181
+ 'TableName',
182
+ 'UpdateExpression',
183
+ 'ConditionExpression',
184
+ 'ExpressionAttributeNames',
185
+ 'ReturnValues',
186
+ 'ReturnConsumedCapacity',
187
+ 'ReturnItemCollectionMetrics',
188
+ 'ReturnValuesOnConditionCheckFailure',
189
+ ];
190
+ /**
191
+ * Fields safe to log at INFO. Omits `Key` and `ExpressionAttributeValues`
192
+ * (the latter binds to ConditionExpression and may carry PII).
193
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
194
+ */
195
+ var DELETE_ITEM_SAFE_FIELDS = [
196
+ 'TableName',
197
+ 'ConditionExpression',
198
+ 'ExpressionAttributeNames',
199
+ 'ReturnValues',
200
+ 'ReturnConsumedCapacity',
201
+ 'ReturnItemCollectionMetrics',
202
+ 'ReturnValuesOnConditionCheckFailure',
203
+ ];
204
+ /**
205
+ * Fields safe to log at INFO for `query` and `paginateItems`. Omits
206
+ * `ExpressionAttributeValues` (values often carry PII) and `ExclusiveStartKey`
207
+ * (pagination cursor includes Key shape). `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks
208
+ * the full input.
209
+ */
210
+ var QUERY_SAFE_FIELDS = [
211
+ 'TableName',
212
+ 'IndexName',
213
+ 'KeyConditionExpression',
214
+ 'FilterExpression',
215
+ 'ProjectionExpression',
216
+ 'ExpressionAttributeNames',
217
+ 'ConsistentRead',
218
+ 'ScanIndexForward',
219
+ 'Select',
220
+ 'Limit',
221
+ 'ReturnConsumedCapacity',
222
+ ];
223
+ /**
224
+ * Fields safe to log at INFO for `scan` and `paginateScan`. Omits
225
+ * `ExpressionAttributeValues` and `ExclusiveStartKey`.
226
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
227
+ */
228
+ var SCAN_SAFE_FIELDS = [
229
+ 'TableName',
230
+ 'IndexName',
231
+ 'FilterExpression',
232
+ 'ProjectionExpression',
233
+ 'ExpressionAttributeNames',
234
+ 'ConsistentRead',
235
+ 'Select',
236
+ 'Limit',
237
+ 'Segment',
238
+ 'TotalSegments',
239
+ 'ReturnConsumedCapacity',
240
+ ];
241
+ var sleep = function (ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); };
242
+ var backoffDelay = function (attempt) {
243
+ var exp = BATCH_WRITE_BASE_DELAY_MS * Math.pow(2, attempt);
244
+ return exp + Math.random() * exp;
245
+ };
246
+ /**
247
+ * Wrapper around the AWS DynamoDB Document client providing structured
248
+ * Powertools logging and X-Ray tracing by default.
249
+ *
250
+ * Items are automatically marshalled / unmarshalled via the document client —
251
+ * callers work with plain TypeScript objects in both directions.
252
+ */
253
+ var DynamoDBService = /** @class */ (function () {
254
+ /**
255
+ * @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
256
+ * which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
257
+ * @param opts.client - Optional pre-configured `DynamoDBDocumentClient`.
258
+ * When supplied, the wrapper does not apply X-Ray instrumentation. When
259
+ * omitted, a default `DynamoDBClient` is wrapped with `captureAWSv3Client`
260
+ * *before* being passed to `DynamoDBDocumentClient.from`, so X-Ray
261
+ * tracing captures every DynamoDB call.
262
+ */
263
+ function DynamoDBService(opts) {
264
+ var _a, _b;
265
+ this.client =
266
+ (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : DynamoDBDocumentClient.from(xray.captureAWSv3Client(new DynamoDBClient({})), {
267
+ marshallOptions: { removeUndefinedValues: true },
268
+ });
269
+ this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
270
+ }
271
+ /**
272
+ * Get an item from DynamoDB.
273
+ * @template K - Shape of the partition / sort key.
274
+ * @template R - Expected unmarshalled item shape.
275
+ * @returns The item, or `undefined` if not found.
276
+ */
277
+ DynamoDBService.prototype.getItem = function (input) {
278
+ return __awaiter(this, void 0, void 0, function () {
279
+ var response;
280
+ return __generator(this, function (_a) {
281
+ switch (_a.label) {
282
+ case 0:
283
+ this.logger.info('Getting DynamoDB item', {
284
+ input: filterFieldsForLogLevel(this.logger, input, GET_ITEM_SAFE_FIELDS),
285
+ });
286
+ return [4 /*yield*/, this.client.send(new GetCommand(input))];
287
+ case 1:
288
+ response = _a.sent();
289
+ return [2 /*return*/, response.Item];
290
+ }
291
+ });
292
+ });
293
+ };
294
+ /**
295
+ * Put an item into DynamoDB. The caller's `Item` is typed as `T`, which
296
+ * the document client marshalls automatically.
297
+ * @template T - Type of the item being stored.
298
+ */
299
+ DynamoDBService.prototype.putItem = function (input) {
300
+ return __awaiter(this, void 0, void 0, function () {
301
+ return __generator(this, function (_a) {
302
+ this.logger.info('Putting DynamoDB item', {
303
+ input: filterFieldsForLogLevel(this.logger, input, PUT_ITEM_SAFE_FIELDS),
304
+ });
305
+ return [2 /*return*/, this.client.send(new PutCommand(input))];
306
+ });
307
+ });
308
+ };
309
+ /**
310
+ * Update an item in DynamoDB. The `Attributes` field on the response is
311
+ * typed as `R` — the caller should choose `R` to match their
312
+ * `ReturnValues` setting:
313
+ * - `NONE` (default): no `Attributes` returned.
314
+ * - `ALL_OLD` / `ALL_NEW`: full item.
315
+ * - `UPDATED_OLD` / `UPDATED_NEW`: only updated attributes (partial).
316
+ * @template K - Shape of the partition / sort key.
317
+ * @template R - Expected shape of the returned `Attributes`.
318
+ */
319
+ DynamoDBService.prototype.updateItem = function (input) {
320
+ return __awaiter(this, void 0, void 0, function () {
321
+ var response;
322
+ return __generator(this, function (_a) {
323
+ switch (_a.label) {
324
+ case 0:
325
+ this.logger.info('Updating DynamoDB item', {
326
+ input: filterFieldsForLogLevel(this.logger, input, UPDATE_ITEM_SAFE_FIELDS),
327
+ });
328
+ return [4 /*yield*/, this.client.send(new UpdateCommand(input))];
329
+ case 1:
330
+ response = _a.sent();
331
+ return [2 /*return*/, response];
332
+ }
333
+ });
334
+ });
335
+ };
336
+ /**
337
+ * Delete an item from DynamoDB. The `Attributes` field on the response is
338
+ * typed as `R` — relevant when `ReturnValues: 'ALL_OLD'` is set.
339
+ * @template K - Shape of the partition / sort key.
340
+ * @template R - Expected shape of the returned `Attributes`.
341
+ */
342
+ DynamoDBService.prototype.deleteItem = function (input) {
343
+ return __awaiter(this, void 0, void 0, function () {
344
+ var response;
345
+ return __generator(this, function (_a) {
346
+ switch (_a.label) {
347
+ case 0:
348
+ this.logger.info('Deleting DynamoDB item', {
349
+ input: filterFieldsForLogLevel(this.logger, input, DELETE_ITEM_SAFE_FIELDS),
350
+ });
351
+ return [4 /*yield*/, this.client.send(new DeleteCommand(input))];
352
+ case 1:
353
+ response = _a.sent();
354
+ return [2 /*return*/, response];
355
+ }
356
+ });
357
+ });
358
+ };
359
+ /**
360
+ * Execute a DynamoDB Query. The full `QueryCommandOutput` is returned with
361
+ * `Items` typed as `T[]` so callers retain pagination metadata
362
+ * (`LastEvaluatedKey`, `Count`, etc.).
363
+ * @template T - Expected shape of each unmarshalled item.
364
+ */
365
+ DynamoDBService.prototype.query = function (input) {
366
+ return __awaiter(this, void 0, void 0, function () {
367
+ var response;
368
+ return __generator(this, function (_a) {
369
+ switch (_a.label) {
370
+ case 0:
371
+ this.logger.info('Querying DynamoDB', {
372
+ input: filterFieldsForLogLevel(this.logger, input, QUERY_SAFE_FIELDS),
373
+ });
374
+ return [4 /*yield*/, this.client.send(new QueryCommand(input))];
375
+ case 1:
376
+ response = _a.sent();
377
+ return [2 /*return*/, response];
378
+ }
379
+ });
380
+ });
381
+ };
382
+ /**
383
+ * Scan a DynamoDB table. The full `ScanCommandOutput` is returned with
384
+ * `Items` typed as `T[]` so callers retain pagination metadata.
385
+ *
386
+ * Scan reads every item in the table, so cost and latency grow linearly
387
+ * with table size; it is rarely the right tool in a runtime service.
388
+ * Prefer, in order:
389
+ *
390
+ * 1. `query` with the table's partition key.
391
+ * 2. `query` against a GSI or LSI whose key matches your access pattern.
392
+ * 3. A sparse GSI populated only for the items you need to enumerate.
393
+ * 4. A denormalised lookup item or table maintained on write.
394
+ *
395
+ * Legitimate scan use cases are mostly one-off admin work (export,
396
+ * migration, audit). For those, prefer the AWS CLI or Console rather than
397
+ * embedding a scan in a Lambda.
398
+ *
399
+ * @template T - Expected shape of each unmarshalled item.
400
+ */
401
+ DynamoDBService.prototype.scan = function (input) {
402
+ return __awaiter(this, void 0, void 0, function () {
403
+ var response;
404
+ return __generator(this, function (_a) {
405
+ switch (_a.label) {
406
+ case 0:
407
+ this.logger.info('Scanning DynamoDB', {
408
+ input: filterFieldsForLogLevel(this.logger, input, SCAN_SAFE_FIELDS),
409
+ });
410
+ return [4 /*yield*/, this.client.send(new ScanCommand(input))];
411
+ case 1:
412
+ response = _a.sent();
413
+ return [2 /*return*/, response];
414
+ }
415
+ });
416
+ });
417
+ };
418
+ /**
419
+ * Batch-get items from one or more DynamoDB tables.
420
+ *
421
+ * Note: this method is intentionally **not** generic. `BatchGet`'s
422
+ * `Responses` field is a multi-table `Record<string, item[]>` whose item
423
+ * shapes can differ per table — no single `T` can soundly describe it.
424
+ * Callers should narrow the result type at the call site.
425
+ */
426
+ DynamoDBService.prototype.batchGet = function (input) {
427
+ return __awaiter(this, void 0, void 0, function () {
428
+ var isDebug;
429
+ var _a;
430
+ return __generator(this, function (_b) {
431
+ isDebug = this.logger.getLevelName() === 'DEBUG';
432
+ this.logger.info('Batch getting DynamoDB items', {
433
+ input: isDebug ? input : { tables: Object.keys((_a = input.RequestItems) !== null && _a !== void 0 ? _a : {}) },
434
+ });
435
+ return [2 /*return*/, this.client.send(new BatchGetCommand(input))];
436
+ });
437
+ });
438
+ };
439
+ /**
440
+ * Batch-write items to DynamoDB, retrying `UnprocessedItems` with jittered
441
+ * exponential backoff. Up to 5 attempts (200ms base delay). Throws when
442
+ * items remain unprocessed after the final attempt.
443
+ */
444
+ DynamoDBService.prototype.batchWrite = function (input) {
445
+ return __awaiter(this, void 0, void 0, function () {
446
+ var isDebug, current, attempt, response, unprocessed;
447
+ var _a;
448
+ return __generator(this, function (_b) {
449
+ switch (_b.label) {
450
+ case 0:
451
+ isDebug = this.logger.getLevelName() === 'DEBUG';
452
+ this.logger.info('Batch writing DynamoDB items', {
453
+ input: isDebug ? input : { tables: Object.keys((_a = input.RequestItems) !== null && _a !== void 0 ? _a : {}) },
454
+ });
455
+ current = input;
456
+ attempt = 0;
457
+ _b.label = 1;
458
+ case 1:
459
+ if (!(attempt < BATCH_WRITE_MAX_ATTEMPTS)) return [3 /*break*/, 6];
460
+ return [4 /*yield*/, this.client.send(new BatchWriteCommand(current))];
461
+ case 2:
462
+ response = _b.sent();
463
+ unprocessed = response.UnprocessedItems;
464
+ if (!unprocessed || Object.keys(unprocessed).length === 0)
465
+ return [2 /*return*/, response];
466
+ this.logger.warn('Retrying unprocessed DynamoDB items', {
467
+ attempt: attempt + 1,
468
+ tables: Object.keys(unprocessed),
469
+ });
470
+ if (!(attempt < BATCH_WRITE_MAX_ATTEMPTS - 1)) return [3 /*break*/, 4];
471
+ return [4 /*yield*/, sleep(backoffDelay(attempt))];
472
+ case 3:
473
+ _b.sent();
474
+ _b.label = 4;
475
+ case 4:
476
+ current = __assign(__assign({}, input), { RequestItems: unprocessed });
477
+ _b.label = 5;
478
+ case 5:
479
+ attempt++;
480
+ return [3 /*break*/, 1];
481
+ case 6: throw new Error("batchWrite failed after ".concat(BATCH_WRITE_MAX_ATTEMPTS, " attempts"));
482
+ }
483
+ });
484
+ });
485
+ };
486
+ /**
487
+ * Paginate over Query results, yielding one unmarshalled item at a time.
488
+ * @template T - Expected shape of each yielded item.
489
+ */
490
+ DynamoDBService.prototype.paginateItems = function (input) {
491
+ return __asyncGenerator(this, arguments, function paginateItems_1() {
492
+ var paginator, _a, paginator_1, paginator_1_1, page, _i, _b, item, e_1_1;
493
+ var _c, e_1, _d, _e;
494
+ return __generator(this, function (_f) {
495
+ switch (_f.label) {
496
+ case 0:
497
+ this.logger.info('Paginating DynamoDB query', {
498
+ input: filterFieldsForLogLevel(this.logger, input, QUERY_SAFE_FIELDS),
499
+ });
500
+ paginator = paginateQuery({ client: this.client }, input);
501
+ _f.label = 1;
502
+ case 1:
503
+ _f.trys.push([1, 10, 11, 16]);
504
+ _a = true, paginator_1 = __asyncValues(paginator);
505
+ _f.label = 2;
506
+ case 2: return [4 /*yield*/, __await(paginator_1.next())];
507
+ case 3:
508
+ if (!(paginator_1_1 = _f.sent(), _c = paginator_1_1.done, !_c)) return [3 /*break*/, 9];
509
+ _e = paginator_1_1.value;
510
+ _a = false;
511
+ page = _e;
512
+ if (!page.Items)
513
+ return [3 /*break*/, 8];
514
+ _i = 0, _b = page.Items;
515
+ _f.label = 4;
516
+ case 4:
517
+ if (!(_i < _b.length)) return [3 /*break*/, 8];
518
+ item = _b[_i];
519
+ return [4 /*yield*/, __await(item)];
520
+ case 5: return [4 /*yield*/, _f.sent()];
521
+ case 6:
522
+ _f.sent();
523
+ _f.label = 7;
524
+ case 7:
525
+ _i++;
526
+ return [3 /*break*/, 4];
527
+ case 8:
528
+ _a = true;
529
+ return [3 /*break*/, 2];
530
+ case 9: return [3 /*break*/, 16];
531
+ case 10:
532
+ e_1_1 = _f.sent();
533
+ e_1 = { error: e_1_1 };
534
+ return [3 /*break*/, 16];
535
+ case 11:
536
+ _f.trys.push([11, , 14, 15]);
537
+ if (!(!_a && !_c && (_d = paginator_1.return))) return [3 /*break*/, 13];
538
+ return [4 /*yield*/, __await(_d.call(paginator_1))];
539
+ case 12:
540
+ _f.sent();
541
+ _f.label = 13;
542
+ case 13: return [3 /*break*/, 15];
543
+ case 14:
544
+ if (e_1) throw e_1.error;
545
+ return [7 /*endfinally*/];
546
+ case 15: return [7 /*endfinally*/];
547
+ case 16: return [2 /*return*/];
548
+ }
549
+ });
550
+ });
551
+ };
552
+ /**
553
+ * Paginate over Scan results, yielding one unmarshalled item at a time.
554
+ * @template T - Expected shape of each yielded item.
555
+ */
556
+ DynamoDBService.prototype.paginateScan = function (input) {
557
+ return __asyncGenerator(this, arguments, function paginateScan_1() {
558
+ var paginator, _a, paginator_2, paginator_2_1, page, _i, _b, item, e_2_1;
559
+ var _c, e_2, _d, _e;
560
+ return __generator(this, function (_f) {
561
+ switch (_f.label) {
562
+ case 0:
563
+ this.logger.info('Paginating DynamoDB scan', {
564
+ input: filterFieldsForLogLevel(this.logger, input, SCAN_SAFE_FIELDS),
565
+ });
566
+ paginator = paginateScan({ client: this.client }, input);
567
+ _f.label = 1;
568
+ case 1:
569
+ _f.trys.push([1, 10, 11, 16]);
570
+ _a = true, paginator_2 = __asyncValues(paginator);
571
+ _f.label = 2;
572
+ case 2: return [4 /*yield*/, __await(paginator_2.next())];
573
+ case 3:
574
+ if (!(paginator_2_1 = _f.sent(), _c = paginator_2_1.done, !_c)) return [3 /*break*/, 9];
575
+ _e = paginator_2_1.value;
576
+ _a = false;
577
+ page = _e;
578
+ if (!page.Items)
579
+ return [3 /*break*/, 8];
580
+ _i = 0, _b = page.Items;
581
+ _f.label = 4;
582
+ case 4:
583
+ if (!(_i < _b.length)) return [3 /*break*/, 8];
584
+ item = _b[_i];
585
+ return [4 /*yield*/, __await(item)];
586
+ case 5: return [4 /*yield*/, _f.sent()];
587
+ case 6:
588
+ _f.sent();
589
+ _f.label = 7;
590
+ case 7:
591
+ _i++;
592
+ return [3 /*break*/, 4];
593
+ case 8:
594
+ _a = true;
595
+ return [3 /*break*/, 2];
596
+ case 9: return [3 /*break*/, 16];
597
+ case 10:
598
+ e_2_1 = _f.sent();
599
+ e_2 = { error: e_2_1 };
600
+ return [3 /*break*/, 16];
601
+ case 11:
602
+ _f.trys.push([11, , 14, 15]);
603
+ if (!(!_a && !_c && (_d = paginator_2.return))) return [3 /*break*/, 13];
604
+ return [4 /*yield*/, __await(_d.call(paginator_2))];
605
+ case 12:
606
+ _f.sent();
607
+ _f.label = 13;
608
+ case 13: return [3 /*break*/, 15];
609
+ case 14:
610
+ if (e_2) throw e_2.error;
611
+ return [7 /*endfinally*/];
612
+ case 15: return [7 /*endfinally*/];
613
+ case 16: return [2 /*return*/];
614
+ }
615
+ });
616
+ });
617
+ };
618
+ return DynamoDBService;
619
+ }());
620
+
621
+ var DEFAULT_PRESIGNED_URL_EXPIRES_IN_SECONDS = 3600;
622
+ var DELETE_OBJECTS_BATCH_LIMIT = 1000;
623
+ /**
624
+ * Fields safe to log at INFO for `putObject`. Omits `Body` (object payload).
625
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
626
+ */
627
+ var PUT_OBJECT_SAFE_FIELDS = ['Bucket', 'Key'];
628
+ /**
629
+ * Fields safe to log at INFO for `putJsonObject`. Omits `Body` (the
630
+ * unserialised JSON payload). `Metadata` is included (operational labels).
631
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
632
+ */
633
+ var PUT_JSON_OBJECT_SAFE_FIELDS = [
634
+ 'Bucket',
635
+ 'Key',
636
+ 'Metadata',
637
+ ];
638
+ /**
639
+ * Wrapper around the AWS S3 client providing structured Powertools logging
640
+ * and X-Ray tracing by default.
641
+ *
642
+ * Input shapes are intentionally tight (Bucket/Key/Body only). Callers
643
+ * needing SDK-level options not exposed here (server-side encryption,
644
+ * tagging, version IDs) should use `S3Client` directly.
645
+ */
646
+ var S3Service = /** @class */ (function () {
647
+ /**
648
+ * @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
649
+ * which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
650
+ * @param opts.client - Optional pre-configured `S3Client`. When supplied,
651
+ * the wrapper does not apply X-Ray instrumentation.
652
+ */
653
+ function S3Service(opts) {
654
+ var _a, _b;
655
+ this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new S3Client());
656
+ this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
657
+ }
658
+ /**
659
+ * Put an object into S3.
660
+ *
661
+ * Note: the structured log line only includes `Bucket` and `Key` —
662
+ * `Body` is omitted to avoid spilling large payloads or sensitive
663
+ * content into CloudWatch.
664
+ *
665
+ * @param input - Bucket, Key, and Body of the object to store.
666
+ */
667
+ S3Service.prototype.putObject = function (input) {
668
+ return __awaiter(this, void 0, void 0, function () {
669
+ return __generator(this, function (_a) {
670
+ this.logger.info('Putting S3 object', {
671
+ input: filterFieldsForLogLevel(this.logger, input, PUT_OBJECT_SAFE_FIELDS),
672
+ });
673
+ return [2 /*return*/, this.client.send(new PutObjectCommand(input))];
674
+ });
675
+ });
676
+ };
677
+ /**
678
+ * Serialise a value to JSON and store it as an S3 object.
679
+ *
680
+ * Note: the structured log line only includes `Bucket` and `Key` —
681
+ * the JSON-encoded body is omitted to avoid spilling potentially
682
+ * large or sensitive content into CloudWatch.
683
+ *
684
+ * @template T - Type of the value being stored.
685
+ */
686
+ S3Service.prototype.putJsonObject = function (input) {
687
+ return __awaiter(this, void 0, void 0, function () {
688
+ return __generator(this, function (_a) {
689
+ this.logger.info('Putting S3 JSON object', {
690
+ input: filterFieldsForLogLevel(this.logger, input, PUT_JSON_OBJECT_SAFE_FIELDS),
691
+ });
692
+ return [2 /*return*/, this.client.send(new PutObjectCommand({
693
+ Bucket: input.Bucket,
694
+ Key: input.Key,
695
+ Body: JSON.stringify(input.Body),
696
+ Metadata: input.Metadata,
697
+ }))];
698
+ });
699
+ });
700
+ };
701
+ /**
702
+ * Get an object from S3.
703
+ */
704
+ S3Service.prototype.getObject = function (input) {
705
+ return __awaiter(this, void 0, void 0, function () {
706
+ return __generator(this, function (_a) {
707
+ this.logger.info('Getting S3 object', { input: input });
708
+ return [2 /*return*/, this.client.send(new GetObjectCommand(input))];
709
+ });
710
+ });
711
+ };
712
+ /**
713
+ * Get an object from S3 and return its body as a string.
714
+ * @returns The object body as a string, or `undefined` if the response
715
+ * has no body.
716
+ */
717
+ S3Service.prototype.getObjectBody = function (input) {
718
+ return __awaiter(this, void 0, void 0, function () {
719
+ var response;
720
+ var _a;
721
+ return __generator(this, function (_b) {
722
+ switch (_b.label) {
723
+ case 0:
724
+ this.logger.info('Getting S3 object body', { input: input });
725
+ return [4 /*yield*/, this.client.send(new GetObjectCommand(input))];
726
+ case 1:
727
+ response = _b.sent();
728
+ return [2 /*return*/, (_a = response.Body) === null || _a === void 0 ? void 0 : _a.transformToString()];
729
+ }
730
+ });
731
+ });
732
+ };
733
+ /**
734
+ * Get an object from S3 and parse it as JSON.
735
+ * @template T - Expected type of the parsed value.
736
+ * @returns The parsed value, or `undefined` if the response has no body.
737
+ * @throws If the body is non-empty and not valid JSON.
738
+ */
739
+ S3Service.prototype.getJsonObject = function (input) {
740
+ return __awaiter(this, void 0, void 0, function () {
741
+ var response, body;
742
+ var _a;
743
+ return __generator(this, function (_b) {
744
+ switch (_b.label) {
745
+ case 0:
746
+ this.logger.info('Getting S3 JSON object', { input: input });
747
+ return [4 /*yield*/, this.client.send(new GetObjectCommand(input))];
748
+ case 1:
749
+ response = _b.sent();
750
+ return [4 /*yield*/, ((_a = response.Body) === null || _a === void 0 ? void 0 : _a.transformToString())];
751
+ case 2:
752
+ body = _b.sent();
753
+ return [2 /*return*/, body ? JSON.parse(body) : undefined];
754
+ }
755
+ });
756
+ });
757
+ };
758
+ /**
759
+ * Fetch the metadata for an S3 object without downloading its body.
760
+ */
761
+ S3Service.prototype.headObject = function (input) {
762
+ return __awaiter(this, void 0, void 0, function () {
763
+ return __generator(this, function (_a) {
764
+ this.logger.info('Fetching S3 object metadata', { input: input });
765
+ return [2 /*return*/, this.client.send(new HeadObjectCommand(input))];
766
+ });
767
+ });
768
+ };
769
+ /**
770
+ * Copy an object within S3.
771
+ */
772
+ S3Service.prototype.copyObject = function (input) {
773
+ return __awaiter(this, void 0, void 0, function () {
774
+ return __generator(this, function (_a) {
775
+ this.logger.info('Copying S3 object', { input: input });
776
+ return [2 /*return*/, this.client.send(new CopyObjectCommand(input))];
777
+ });
778
+ });
779
+ };
780
+ /**
781
+ * List object keys under a bucket and optional prefix, auto-paginating
782
+ * across all pages.
783
+ */
784
+ S3Service.prototype.listObjects = function (bucket, prefix) {
785
+ return __awaiter(this, void 0, void 0, function () {
786
+ var paginator, keys, _a, paginator_1, paginator_1_1, page, _i, _b, object, e_1_1;
787
+ var _c, e_1, _d, _e;
788
+ var _f;
789
+ return __generator(this, function (_g) {
790
+ switch (_g.label) {
791
+ case 0:
792
+ this.logger.info('Listing S3 objects', { input: { bucket: bucket, prefix: prefix } });
793
+ paginator = paginateListObjectsV2({ client: this.client }, { Bucket: bucket, Prefix: prefix });
794
+ keys = [];
795
+ _g.label = 1;
796
+ case 1:
797
+ _g.trys.push([1, 6, 7, 12]);
798
+ _a = true, paginator_1 = __asyncValues(paginator);
799
+ _g.label = 2;
800
+ case 2: return [4 /*yield*/, paginator_1.next()];
801
+ case 3:
802
+ if (!(paginator_1_1 = _g.sent(), _c = paginator_1_1.done, !_c)) return [3 /*break*/, 5];
803
+ _e = paginator_1_1.value;
804
+ _a = false;
805
+ page = _e;
806
+ for (_i = 0, _b = (_f = page.Contents) !== null && _f !== void 0 ? _f : []; _i < _b.length; _i++) {
807
+ object = _b[_i];
808
+ if (object.Key)
809
+ keys.push(object.Key);
810
+ }
811
+ _g.label = 4;
812
+ case 4:
813
+ _a = true;
814
+ return [3 /*break*/, 2];
815
+ case 5: return [3 /*break*/, 12];
816
+ case 6:
817
+ e_1_1 = _g.sent();
818
+ e_1 = { error: e_1_1 };
819
+ return [3 /*break*/, 12];
820
+ case 7:
821
+ _g.trys.push([7, , 10, 11]);
822
+ if (!(!_a && !_c && (_d = paginator_1.return))) return [3 /*break*/, 9];
823
+ return [4 /*yield*/, _d.call(paginator_1)];
824
+ case 8:
825
+ _g.sent();
826
+ _g.label = 9;
827
+ case 9: return [3 /*break*/, 11];
828
+ case 10:
829
+ if (e_1) throw e_1.error;
830
+ return [7 /*endfinally*/];
831
+ case 11: return [7 /*endfinally*/];
832
+ case 12: return [2 /*return*/, keys];
833
+ }
834
+ });
835
+ });
836
+ };
837
+ /**
838
+ * List and JSON-parse every object under a bucket and optional prefix.
839
+ * Auto-paginated. Objects without a body are skipped.
840
+ * @template T - Expected type of each parsed object.
841
+ */
842
+ S3Service.prototype.getAllObjects = function (bucket, prefix) {
843
+ return __awaiter(this, void 0, void 0, function () {
844
+ var paginator, bodies, _a, paginator_2, paginator_2_1, page, _i, _b, object, response, body, e_2_1;
845
+ var _c, e_2, _d, _e;
846
+ var _f, _g;
847
+ return __generator(this, function (_h) {
848
+ switch (_h.label) {
849
+ case 0:
850
+ this.logger.info('Getting all S3 objects', { input: { bucket: bucket, prefix: prefix } });
851
+ paginator = paginateListObjectsV2({ client: this.client }, { Bucket: bucket, Prefix: prefix });
852
+ bodies = [];
853
+ _h.label = 1;
854
+ case 1:
855
+ _h.trys.push([1, 10, 11, 16]);
856
+ _a = true, paginator_2 = __asyncValues(paginator);
857
+ _h.label = 2;
858
+ case 2: return [4 /*yield*/, paginator_2.next()];
859
+ case 3:
860
+ if (!(paginator_2_1 = _h.sent(), _c = paginator_2_1.done, !_c)) return [3 /*break*/, 9];
861
+ _e = paginator_2_1.value;
862
+ _a = false;
863
+ page = _e;
864
+ _i = 0, _b = (_f = page.Contents) !== null && _f !== void 0 ? _f : [];
865
+ _h.label = 4;
866
+ case 4:
867
+ if (!(_i < _b.length)) return [3 /*break*/, 8];
868
+ object = _b[_i];
869
+ if (!object.Key)
870
+ return [3 /*break*/, 7];
871
+ return [4 /*yield*/, this.client.send(new GetObjectCommand({ Bucket: bucket, Key: object.Key }))];
872
+ case 5:
873
+ response = _h.sent();
874
+ return [4 /*yield*/, ((_g = response.Body) === null || _g === void 0 ? void 0 : _g.transformToString())];
875
+ case 6:
876
+ body = _h.sent();
877
+ if (body)
878
+ bodies.push(JSON.parse(body));
879
+ _h.label = 7;
880
+ case 7:
881
+ _i++;
882
+ return [3 /*break*/, 4];
883
+ case 8:
884
+ _a = true;
885
+ return [3 /*break*/, 2];
886
+ case 9: return [3 /*break*/, 16];
887
+ case 10:
888
+ e_2_1 = _h.sent();
889
+ e_2 = { error: e_2_1 };
890
+ return [3 /*break*/, 16];
891
+ case 11:
892
+ _h.trys.push([11, , 14, 15]);
893
+ if (!(!_a && !_c && (_d = paginator_2.return))) return [3 /*break*/, 13];
894
+ return [4 /*yield*/, _d.call(paginator_2)];
895
+ case 12:
896
+ _h.sent();
897
+ _h.label = 13;
898
+ case 13: return [3 /*break*/, 15];
899
+ case 14:
900
+ if (e_2) throw e_2.error;
901
+ return [7 /*endfinally*/];
902
+ case 15: return [7 /*endfinally*/];
903
+ case 16: return [2 /*return*/, bodies];
904
+ }
905
+ });
906
+ });
907
+ };
908
+ /**
909
+ * Generate a presigned URL that callers can use to GET or PUT an S3 object
910
+ * directly, without going through the wrapper. The signing happens against
911
+ * the wrapper's `S3Client`, so callers do not need their own client.
912
+ *
913
+ * GET URLs are signed with `ResponseContentDisposition: 'attachment'` so
914
+ * browsers download the object rather than rendering it in-place.
915
+ *
916
+ * The input shape is an inline object (rather than the `Required<Pick<...>>`
917
+ * pattern used by other S3 methods) because this method wraps two SDK
918
+ * commands rather than one, and `action` / `expiresIn` are wrapper-level
919
+ * concerns with no SDK-input equivalent.
920
+ *
921
+ * @param input.Bucket - The S3 bucket name.
922
+ * @param input.Key - The S3 object key.
923
+ * @param input.action - `'get'` to download, `'put'` to upload.
924
+ * @param input.expiresIn - URL lifetime in seconds. Defaults to 3600 (1 hour).
925
+ */
926
+ S3Service.prototype.getPresignedUrl = function (input) {
927
+ return __awaiter(this, void 0, void 0, function () {
928
+ var expiresIn, command;
929
+ var _a;
930
+ return __generator(this, function (_b) {
931
+ expiresIn = (_a = input.expiresIn) !== null && _a !== void 0 ? _a : DEFAULT_PRESIGNED_URL_EXPIRES_IN_SECONDS;
932
+ this.logger.info('Generating S3 presigned URL', {
933
+ input: { Bucket: input.Bucket, Key: input.Key, action: input.action, expiresIn: expiresIn },
934
+ });
935
+ command = input.action === 'get'
936
+ ? new GetObjectCommand({
937
+ Bucket: input.Bucket,
938
+ Key: input.Key,
939
+ ResponseContentDisposition: 'attachment',
940
+ })
941
+ : new PutObjectCommand({ Bucket: input.Bucket, Key: input.Key });
942
+ return [2 /*return*/, getSignedUrl(this.client, command, { expiresIn: expiresIn })];
943
+ });
944
+ });
945
+ };
946
+ /**
947
+ * Delete a single object from S3.
948
+ */
949
+ S3Service.prototype.deleteObject = function (input) {
950
+ return __awaiter(this, void 0, void 0, function () {
951
+ return __generator(this, function (_a) {
952
+ this.logger.info('Deleting S3 object', { input: input });
953
+ return [2 /*return*/, this.client.send(new DeleteObjectCommand(input))];
954
+ });
955
+ });
956
+ };
957
+ /**
958
+ * Delete multiple objects from S3, auto-chunking the request into batches
959
+ * of 1000 keys (the S3-enforced DeleteObjects limit). Returns one output
960
+ * per chunk.
961
+ */
962
+ S3Service.prototype.deleteObjects = function (bucket, keys) {
963
+ return __awaiter(this, void 0, void 0, function () {
964
+ var isDebug, results, i, batch, _a, _b;
965
+ return __generator(this, function (_c) {
966
+ switch (_c.label) {
967
+ case 0:
968
+ isDebug = this.logger.getLevelName() === 'DEBUG';
969
+ this.logger.info('Deleting S3 objects', {
970
+ input: isDebug ? { bucket: bucket, keys: keys } : { bucket: bucket, keyCount: keys.length },
971
+ });
972
+ results = [];
973
+ i = 0;
974
+ _c.label = 1;
975
+ case 1:
976
+ if (!(i < keys.length)) return [3 /*break*/, 4];
977
+ batch = keys.slice(i, i + DELETE_OBJECTS_BATCH_LIMIT);
978
+ _b = (_a = results).push;
979
+ return [4 /*yield*/, this.client.send(new DeleteObjectsCommand({
980
+ Bucket: bucket,
981
+ Delete: { Objects: batch.map(function (Key) { return ({ Key: Key }); }) },
982
+ }))];
983
+ case 2:
984
+ _b.apply(_a, [_c.sent()]);
985
+ _c.label = 3;
986
+ case 3:
987
+ i += DELETE_OBJECTS_BATCH_LIMIT;
988
+ return [3 /*break*/, 1];
989
+ case 4: return [2 /*return*/, results];
990
+ }
991
+ });
992
+ });
993
+ };
994
+ /**
995
+ * Delete every object in a bucket. Streams the listing page-by-page and
996
+ * delegates each page's deletion to `deleteObjects`, so peak memory stays
997
+ * bounded by one page (~1000 keys) regardless of bucket size.
998
+ * @returns The keys of every deleted object.
999
+ */
1000
+ S3Service.prototype.emptyBucket = function (bucket) {
1001
+ return __awaiter(this, void 0, void 0, function () {
1002
+ var paginator, deletedKeys, _a, paginator_3, paginator_3_1, page, keys, e_3_1;
1003
+ var _b, e_3, _c, _d;
1004
+ var _e;
1005
+ return __generator(this, function (_f) {
1006
+ switch (_f.label) {
1007
+ case 0:
1008
+ this.logger.info('Emptying S3 bucket', { input: { bucket: bucket } });
1009
+ paginator = paginateListObjectsV2({ client: this.client }, { Bucket: bucket });
1010
+ deletedKeys = [];
1011
+ _f.label = 1;
1012
+ case 1:
1013
+ _f.trys.push([1, 7, 8, 13]);
1014
+ _a = true, paginator_3 = __asyncValues(paginator);
1015
+ _f.label = 2;
1016
+ case 2: return [4 /*yield*/, paginator_3.next()];
1017
+ case 3:
1018
+ if (!(paginator_3_1 = _f.sent(), _b = paginator_3_1.done, !_b)) return [3 /*break*/, 6];
1019
+ _d = paginator_3_1.value;
1020
+ _a = false;
1021
+ page = _d;
1022
+ keys = ((_e = page.Contents) !== null && _e !== void 0 ? _e : []).flatMap(function (o) { return (o.Key ? [o.Key] : []); });
1023
+ if (keys.length === 0)
1024
+ return [3 /*break*/, 5];
1025
+ return [4 /*yield*/, this.deleteObjects(bucket, keys)];
1026
+ case 4:
1027
+ _f.sent();
1028
+ deletedKeys.push.apply(deletedKeys, keys);
1029
+ _f.label = 5;
1030
+ case 5:
1031
+ _a = true;
1032
+ return [3 /*break*/, 2];
1033
+ case 6: return [3 /*break*/, 13];
1034
+ case 7:
1035
+ e_3_1 = _f.sent();
1036
+ e_3 = { error: e_3_1 };
1037
+ return [3 /*break*/, 13];
1038
+ case 8:
1039
+ _f.trys.push([8, , 11, 12]);
1040
+ if (!(!_a && !_b && (_c = paginator_3.return))) return [3 /*break*/, 10];
1041
+ return [4 /*yield*/, _c.call(paginator_3)];
1042
+ case 9:
1043
+ _f.sent();
1044
+ _f.label = 10;
1045
+ case 10: return [3 /*break*/, 12];
1046
+ case 11:
1047
+ if (e_3) throw e_3.error;
1048
+ return [7 /*endfinally*/];
1049
+ case 12: return [7 /*endfinally*/];
1050
+ case 13: return [2 /*return*/, deletedKeys];
1051
+ }
1052
+ });
1053
+ });
1054
+ };
1055
+ return S3Service;
1056
+ }());
1057
+
1058
+ /**
1059
+ * Fields safe to log at INFO level. Omits `SecretString` and `SecretBinary` —
1060
+ * the secret material itself. `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full
1061
+ * input.
1062
+ */
1063
+ var CREATE_SECRET_SAFE_FIELDS = [
1064
+ 'Name',
1065
+ 'Description',
1066
+ 'KmsKeyId',
1067
+ 'Tags',
1068
+ 'ClientRequestToken',
1069
+ 'AddReplicaRegions',
1070
+ 'ForceOverwriteReplicaSecret',
1071
+ ];
1072
+ var UPDATE_SECRET_SAFE_FIELDS = [
1073
+ 'SecretId',
1074
+ 'Description',
1075
+ 'KmsKeyId',
1076
+ 'ClientRequestToken',
1077
+ ];
1078
+ var PUT_SECRET_VALUE_SAFE_FIELDS = [
1079
+ 'SecretId',
1080
+ 'VersionStages',
1081
+ 'ClientRequestToken',
1082
+ ];
1083
+ /**
1084
+ * `DeleteSecretCommandInput` carries no secret material today, but the
1085
+ * explicit allowlist keeps the log shape consistent with the other write
1086
+ * methods and protects against future field additions.
1087
+ */
1088
+ var DELETE_SECRET_SAFE_FIELDS = [
1089
+ 'SecretId',
1090
+ 'RecoveryWindowInDays',
1091
+ 'ForceDeleteWithoutRecovery',
1092
+ ];
1093
+ /**
1094
+ * Wrapper around the AWS Secrets Manager client providing structured
1095
+ * Powertools logging and X-Ray tracing by default.
1096
+ *
1097
+ * Write operations (`createSecret`, `updateSecret`, `putSecretValue`,
1098
+ * `deleteSecret`) are exposed for convenience but should be used with care:
1099
+ * secret lifecycle is usually managed by IaC (CDK / Terraform). Prefer IaC
1100
+ * for anything that exists at deploy time; reserve runtime writes for
1101
+ * dynamically-issued credentials, rotation flows, or other genuinely
1102
+ * mutable values.
1103
+ */
1104
+ var SecretsManagerService = /** @class */ (function () {
1105
+ /**
1106
+ * @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
1107
+ * which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
1108
+ * @param opts.client - Optional pre-configured `SecretsManagerClient`. When
1109
+ * supplied, the wrapper does not apply X-Ray instrumentation — the caller
1110
+ * owns that decision.
1111
+ */
1112
+ function SecretsManagerService(opts) {
1113
+ var _a, _b;
1114
+ this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SecretsManagerClient());
1115
+ this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
1116
+ }
1117
+ /**
1118
+ * Fetch a secret's string value from Secrets Manager.
1119
+ * @param secretId - The ARN or friendly name of the secret.
1120
+ * @returns The secret's `SecretString` value.
1121
+ * @throws If the response does not contain a `SecretString` (e.g. the secret
1122
+ * stores binary data).
1123
+ */
1124
+ SecretsManagerService.prototype.getSecret = function (secretId) {
1125
+ return __awaiter(this, void 0, void 0, function () {
1126
+ return __generator(this, function (_a) {
1127
+ this.logger.info('Fetching secret', { input: { secretId: secretId } });
1128
+ return [2 /*return*/, this.fetchSecretString(secretId)];
1129
+ });
1130
+ });
1131
+ };
1132
+ /**
1133
+ * Fetch a secret and parse it as JSON.
1134
+ * @param secretId - The ARN or friendly name of the secret.
1135
+ * @template T - Expected shape of the parsed secret.
1136
+ * @returns The parsed secret value.
1137
+ * @throws If the secret has no `SecretString` or the value is not valid JSON.
1138
+ */
1139
+ SecretsManagerService.prototype.getJsonSecret = function (secretId) {
1140
+ return __awaiter(this, void 0, void 0, function () {
1141
+ var secretString;
1142
+ return __generator(this, function (_a) {
1143
+ switch (_a.label) {
1144
+ case 0:
1145
+ this.logger.info('Fetching JSON secret', { input: { secretId: secretId } });
1146
+ return [4 /*yield*/, this.fetchSecretString(secretId)];
1147
+ case 1:
1148
+ secretString = _a.sent();
1149
+ return [2 /*return*/, JSON.parse(secretString)];
1150
+ }
1151
+ });
1152
+ });
1153
+ };
1154
+ /**
1155
+ * Create a new secret. At INFO level the log line includes only identity
1156
+ * and non-secret metadata; `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full
1157
+ * input (including `SecretString` / `SecretBinary`).
1158
+ *
1159
+ * Prefer IaC (CDK / Terraform) for secret lifecycle — use this for
1160
+ * dynamically-issued credentials only.
1161
+ */
1162
+ SecretsManagerService.prototype.createSecret = function (input) {
1163
+ return __awaiter(this, void 0, void 0, function () {
1164
+ return __generator(this, function (_a) {
1165
+ this.logger.info('Creating secret', {
1166
+ input: filterFieldsForLogLevel(this.logger, input, CREATE_SECRET_SAFE_FIELDS),
1167
+ });
1168
+ return [2 /*return*/, this.client.send(new CreateSecretCommand(input))];
1169
+ });
1170
+ });
1171
+ };
1172
+ /**
1173
+ * Update an existing secret's metadata or value. At INFO level the log
1174
+ * line omits `SecretString` / `SecretBinary`; `POWERTOOLS_LOG_LEVEL=DEBUG`
1175
+ * unlocks the full input.
1176
+ *
1177
+ * Prefer IaC (CDK / Terraform) for secret lifecycle — use this for
1178
+ * runtime metadata updates only.
1179
+ */
1180
+ SecretsManagerService.prototype.updateSecret = function (input) {
1181
+ return __awaiter(this, void 0, void 0, function () {
1182
+ return __generator(this, function (_a) {
1183
+ this.logger.info('Updating secret', {
1184
+ input: filterFieldsForLogLevel(this.logger, input, UPDATE_SECRET_SAFE_FIELDS),
1185
+ });
1186
+ return [2 /*return*/, this.client.send(new UpdateSecretCommand(input))];
1187
+ });
1188
+ });
1189
+ };
1190
+ /**
1191
+ * Store a new version of a secret's value. At INFO level the log line
1192
+ * omits `SecretString` / `SecretBinary`; `POWERTOOLS_LOG_LEVEL=DEBUG`
1193
+ * unlocks the full input.
1194
+ *
1195
+ * Typically used by rotation flows.
1196
+ */
1197
+ SecretsManagerService.prototype.putSecretValue = function (input) {
1198
+ return __awaiter(this, void 0, void 0, function () {
1199
+ return __generator(this, function (_a) {
1200
+ this.logger.info('Putting secret value', {
1201
+ input: filterFieldsForLogLevel(this.logger, input, PUT_SECRET_VALUE_SAFE_FIELDS),
1202
+ });
1203
+ return [2 /*return*/, this.client.send(new PutSecretValueCommand(input))];
1204
+ });
1205
+ });
1206
+ };
1207
+ /**
1208
+ * Delete a secret. Pass `ForceDeleteWithoutRecovery: true` to bypass the
1209
+ * default 7-30 day recovery window (irreversible).
1210
+ *
1211
+ * Prefer IaC (CDK / Terraform) for secret lifecycle.
1212
+ */
1213
+ SecretsManagerService.prototype.deleteSecret = function (input) {
1214
+ return __awaiter(this, void 0, void 0, function () {
1215
+ return __generator(this, function (_a) {
1216
+ this.logger.info('Deleting secret', {
1217
+ input: filterFieldsForLogLevel(this.logger, input, DELETE_SECRET_SAFE_FIELDS),
1218
+ });
1219
+ return [2 /*return*/, this.client.send(new DeleteSecretCommand(input))];
1220
+ });
1221
+ });
1222
+ };
1223
+ SecretsManagerService.prototype.fetchSecretString = function (secretId) {
1224
+ return __awaiter(this, void 0, void 0, function () {
1225
+ var response;
1226
+ return __generator(this, function (_a) {
1227
+ switch (_a.label) {
1228
+ case 0: return [4 /*yield*/, this.client.send(new GetSecretValueCommand({ SecretId: secretId }))];
1229
+ case 1:
1230
+ response = _a.sent();
1231
+ if (response.SecretString === undefined) {
1232
+ throw new Error("Secret '".concat(secretId, "' does not contain a string value"));
1233
+ }
1234
+ return [2 /*return*/, response.SecretString];
1235
+ }
1236
+ });
1237
+ });
1238
+ };
1239
+ return SecretsManagerService;
1240
+ }());
1241
+
1242
+ /**
1243
+ * Fields safe to log at INFO for `startExecution`. Omits `input` — the SFN
1244
+ * execution payload, which routinely carries PII (customer IDs, addresses,
1245
+ * order contents, etc.). `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
1246
+ */
1247
+ var START_EXECUTION_SAFE_FIELDS = [
1248
+ 'stateMachineArn',
1249
+ 'name',
1250
+ 'traceHeader',
1251
+ ];
1252
+ /**
1253
+ * Wrapper around the AWS Step Functions client providing structured
1254
+ * Powertools logging and X-Ray tracing by default.
1255
+ */
1256
+ var StepFunctionsService = /** @class */ (function () {
1257
+ /**
1258
+ * @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
1259
+ * which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
1260
+ * @param opts.client - Optional pre-configured `SFNClient`. When supplied,
1261
+ * the wrapper does not apply X-Ray instrumentation.
1262
+ */
1263
+ function StepFunctionsService(opts) {
1264
+ var _a, _b;
1265
+ this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SFNClient());
1266
+ this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
1267
+ }
1268
+ /**
1269
+ * List all executions for a state machine, auto-paginating across all
1270
+ * pages. Typically bounded by `statusFilter` and state-machine retention,
1271
+ * so the flat-array shape is safe in practice.
1272
+ */
1273
+ StepFunctionsService.prototype.listExecutions = function (input) {
1274
+ return __awaiter(this, void 0, void 0, function () {
1275
+ var paginator, executions, _a, paginator_1, paginator_1_1, page, e_1_1;
1276
+ var _b, e_1, _c, _d;
1277
+ return __generator(this, function (_e) {
1278
+ switch (_e.label) {
1279
+ case 0:
1280
+ this.logger.info('Listing Step Functions executions', { input: input });
1281
+ paginator = paginateListExecutions({ client: this.client }, input);
1282
+ executions = [];
1283
+ _e.label = 1;
1284
+ case 1:
1285
+ _e.trys.push([1, 6, 7, 12]);
1286
+ _a = true, paginator_1 = __asyncValues(paginator);
1287
+ _e.label = 2;
1288
+ case 2: return [4 /*yield*/, paginator_1.next()];
1289
+ case 3:
1290
+ if (!(paginator_1_1 = _e.sent(), _b = paginator_1_1.done, !_b)) return [3 /*break*/, 5];
1291
+ _d = paginator_1_1.value;
1292
+ _a = false;
1293
+ page = _d;
1294
+ if (page.executions)
1295
+ executions.push.apply(executions, page.executions);
1296
+ _e.label = 4;
1297
+ case 4:
1298
+ _a = true;
1299
+ return [3 /*break*/, 2];
1300
+ case 5: return [3 /*break*/, 12];
1301
+ case 6:
1302
+ e_1_1 = _e.sent();
1303
+ e_1 = { error: e_1_1 };
1304
+ return [3 /*break*/, 12];
1305
+ case 7:
1306
+ _e.trys.push([7, , 10, 11]);
1307
+ if (!(!_a && !_b && (_c = paginator_1.return))) return [3 /*break*/, 9];
1308
+ return [4 /*yield*/, _c.call(paginator_1)];
1309
+ case 8:
1310
+ _e.sent();
1311
+ _e.label = 9;
1312
+ case 9: return [3 /*break*/, 11];
1313
+ case 10:
1314
+ if (e_1) throw e_1.error;
1315
+ return [7 /*endfinally*/];
1316
+ case 11: return [7 /*endfinally*/];
1317
+ case 12: return [2 /*return*/, executions];
1318
+ }
1319
+ });
1320
+ });
1321
+ };
1322
+ /**
1323
+ * Start a new Step Functions execution.
1324
+ */
1325
+ StepFunctionsService.prototype.startExecution = function (input) {
1326
+ return __awaiter(this, void 0, void 0, function () {
1327
+ return __generator(this, function (_a) {
1328
+ this.logger.info('Starting Step Functions execution', {
1329
+ input: filterFieldsForLogLevel(this.logger, input, START_EXECUTION_SAFE_FIELDS),
1330
+ });
1331
+ return [2 /*return*/, this.client.send(new StartExecutionCommand(input))];
1332
+ });
1333
+ });
1334
+ };
1335
+ /**
1336
+ * Describe an existing Step Functions execution.
1337
+ */
1338
+ StepFunctionsService.prototype.describeExecution = function (input) {
1339
+ return __awaiter(this, void 0, void 0, function () {
1340
+ return __generator(this, function (_a) {
1341
+ this.logger.info('Describing Step Functions execution', { input: input });
1342
+ return [2 /*return*/, this.client.send(new DescribeExecutionCommand(input))];
1343
+ });
1344
+ });
1345
+ };
1346
+ /**
1347
+ * Stop an in-progress Step Functions execution.
1348
+ */
1349
+ StepFunctionsService.prototype.stopExecution = function (input) {
1350
+ return __awaiter(this, void 0, void 0, function () {
1351
+ return __generator(this, function (_a) {
1352
+ this.logger.info('Stopping Step Functions execution', { input: input });
1353
+ return [2 /*return*/, this.client.send(new StopExecutionCommand(input))];
1354
+ });
1355
+ });
1356
+ };
1357
+ return StepFunctionsService;
1358
+ }());
1359
+
1360
+ /**
1361
+ * Truncate a UTF-8 string to fit within `maxBytes` when encoded as UTF-8.
1362
+ *
1363
+ * Walks back from the cut point to the start of the previous codepoint, so the
1364
+ * result is never a malformed UTF-8 sequence. Returns the input unchanged when
1365
+ * it already fits.
1366
+ */
1367
+ function truncateUtf8(str, maxBytes) {
1368
+ var buf = Buffer.from(str, 'utf8');
1369
+ if (buf.length <= maxBytes)
1370
+ return str;
1371
+ var end = maxBytes;
1372
+ while (end > 0) {
1373
+ var byte = buf[end];
1374
+ if (byte === undefined || (byte & 0xc0) !== 0x80)
1375
+ break;
1376
+ end--;
1377
+ }
1378
+ return buf.toString('utf8', 0, end);
1379
+ }
1380
+ /**
1381
+ * Truncate a string to at most `maxChars` Unicode codepoints. Splits the
1382
+ * string via `Array.from` so surrogate pairs (emoji, supplementary-plane
1383
+ * characters) are not split in the middle. Returns the input unchanged when
1384
+ * it already fits.
1385
+ */
1386
+ function truncateCodepoints(str, maxChars) {
1387
+ var codepoints = Array.from(str);
1388
+ if (codepoints.length <= maxChars)
1389
+ return str;
1390
+ return codepoints.slice(0, maxChars).join('');
1391
+ }
1392
+
1393
+ var PUBLISH_BATCH_LIMIT = 10;
1394
+ var SNS_MESSAGE_MAX_BYTES = 256 * 1024;
1395
+ var SNS_SUBJECT_MAX_CHARS = 100;
1396
+ /**
1397
+ * Fields safe to log at INFO level for `publish`. Omits `Message`, `Subject`,
1398
+ * `MessageAttributes`, and `PhoneNumber` — payloads, user-visible content
1399
+ * (subjects often carry order numbers / customer names), and PII recipient
1400
+ * identifiers. `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
1401
+ */
1402
+ var PUBLISH_SAFE_FIELDS = [
1403
+ 'TopicArn',
1404
+ 'TargetArn',
1405
+ 'MessageStructure',
1406
+ 'MessageGroupId',
1407
+ 'MessageDeduplicationId',
1408
+ ];
1409
+ /**
1410
+ * Wrapper around the AWS SNS client providing structured Powertools logging
1411
+ * and X-Ray tracing by default.
1412
+ */
1413
+ var SNSService = /** @class */ (function () {
1414
+ /**
1415
+ * @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
1416
+ * which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
1417
+ * @param opts.client - Optional pre-configured `SNSClient`. When supplied,
1418
+ * the wrapper does not apply X-Ray instrumentation.
1419
+ * @param opts.truncate - When `true`, oversized `Message` / `Subject` are
1420
+ * truncated (byte-safe / codepoint-safe) before sending instead of failing
1421
+ * at the SDK. Defaults to `false` — the SDK throws on oversize, which is
1422
+ * usually the right failure mode. Each `publish` call can override via
1423
+ * its own `truncate` option.
1424
+ */
1425
+ function SNSService(opts) {
1426
+ var _a, _b, _c;
1427
+ this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SNSClient());
1428
+ this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
1429
+ this.truncate = (_c = opts === null || opts === void 0 ? void 0 : opts.truncate) !== null && _c !== void 0 ? _c : false;
1430
+ }
1431
+ /**
1432
+ * Publish a single message to an SNS topic.
1433
+ *
1434
+ * At INFO level the log line includes only routing / dedup metadata; see
1435
+ * `PUBLISH_SAFE_FIELDS` for the list. Setting `POWERTOOLS_LOG_LEVEL=DEBUG`
1436
+ * unlocks the full input.
1437
+ *
1438
+ * @param input - PublishCommandInput including TopicArn and Message.
1439
+ */
1440
+ SNSService.prototype.publish = function (input, opts) {
1441
+ return __awaiter(this, void 0, void 0, function () {
1442
+ var shouldTruncate, effective;
1443
+ var _a;
1444
+ return __generator(this, function (_b) {
1445
+ shouldTruncate = (_a = opts === null || opts === void 0 ? void 0 : opts.truncate) !== null && _a !== void 0 ? _a : this.truncate;
1446
+ effective = shouldTruncate ? this.applyTruncation(input) : input;
1447
+ this.logger.info('Publishing SNS message', {
1448
+ input: filterFieldsForLogLevel(this.logger, effective, PUBLISH_SAFE_FIELDS),
1449
+ });
1450
+ return [2 /*return*/, this.client.send(new PublishCommand(effective))];
1451
+ });
1452
+ });
1453
+ };
1454
+ SNSService.prototype.applyTruncation = function (input) {
1455
+ var truncated = [];
1456
+ var Message = input.Message;
1457
+ if (Message !== undefined && Buffer.byteLength(Message, 'utf8') > SNS_MESSAGE_MAX_BYTES) {
1458
+ Message = truncateUtf8(Message, SNS_MESSAGE_MAX_BYTES);
1459
+ truncated.push('Message');
1460
+ }
1461
+ var Subject = input.Subject;
1462
+ if (Subject !== undefined && Array.from(Subject).length > SNS_SUBJECT_MAX_CHARS) {
1463
+ Subject = truncateCodepoints(Subject, SNS_SUBJECT_MAX_CHARS);
1464
+ truncated.push('Subject');
1465
+ }
1466
+ if (truncated.length > 0) {
1467
+ this.logger.warn('Truncated SNS publish input', { fields: truncated });
1468
+ }
1469
+ return __assign(__assign({}, input), { Message: Message, Subject: Subject });
1470
+ };
1471
+ /**
1472
+ * Publish a batch of messages to an SNS topic. The SNS API caps
1473
+ * PublishBatch at 10 entries per request, so this method auto-chunks
1474
+ * the caller's `PublishBatchRequestEntries` and sends one request per
1475
+ * chunk, returning the array of outputs.
1476
+ * @param input - PublishBatchCommandInput including TopicArn and entries.
1477
+ */
1478
+ SNSService.prototype.publishBatch = function (input) {
1479
+ return __awaiter(this, void 0, void 0, function () {
1480
+ var entries, isDebug, results, i, chunk, _a, _b;
1481
+ var _c;
1482
+ return __generator(this, function (_d) {
1483
+ switch (_d.label) {
1484
+ case 0:
1485
+ entries = (_c = input.PublishBatchRequestEntries) !== null && _c !== void 0 ? _c : [];
1486
+ isDebug = this.logger.getLevelName() === 'DEBUG';
1487
+ this.logger.info('Publishing SNS message batch', {
1488
+ input: isDebug ? input : { TopicArn: input.TopicArn, entryCount: entries.length },
1489
+ });
1490
+ results = [];
1491
+ i = 0;
1492
+ _d.label = 1;
1493
+ case 1:
1494
+ if (!(i < entries.length)) return [3 /*break*/, 4];
1495
+ chunk = entries.slice(i, i + PUBLISH_BATCH_LIMIT);
1496
+ _b = (_a = results).push;
1497
+ return [4 /*yield*/, this.client.send(new PublishBatchCommand(__assign(__assign({}, input), { PublishBatchRequestEntries: chunk })))];
1498
+ case 2:
1499
+ _b.apply(_a, [_d.sent()]);
1500
+ _d.label = 3;
1501
+ case 3:
1502
+ i += PUBLISH_BATCH_LIMIT;
1503
+ return [3 /*break*/, 1];
1504
+ case 4: return [2 /*return*/, results];
1505
+ }
1506
+ });
1507
+ });
1508
+ };
1509
+ return SNSService;
1510
+ }());
1511
+
1512
+ var SQS_BATCH_LIMIT = 10;
1513
+ var SQS_MESSAGE_BODY_MAX_BYTES = 256 * 1024;
1514
+ /**
1515
+ * Fields safe to log at INFO level for `sendMessage`. Omits `MessageBody` and
1516
+ * `MessageAttributes` — both carry payload content.
1517
+ * `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full input.
1518
+ */
1519
+ var SEND_MESSAGE_SAFE_FIELDS = [
1520
+ 'QueueUrl',
1521
+ 'DelaySeconds',
1522
+ 'MessageGroupId',
1523
+ 'MessageDeduplicationId',
1524
+ ];
1525
+ /**
1526
+ * Wrapper around the AWS SQS client providing structured Powertools logging
1527
+ * and X-Ray tracing by default.
1528
+ */
1529
+ var SQSService = /** @class */ (function () {
1530
+ /**
1531
+ * @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
1532
+ * which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
1533
+ * @param opts.client - Optional pre-configured `SQSClient`. When supplied,
1534
+ * the wrapper does not apply X-Ray instrumentation.
1535
+ * @param opts.truncate - When `true`, oversized `MessageBody` is truncated
1536
+ * (byte-safe) before sending instead of failing at the SDK. Defaults to
1537
+ * `false`. Each `sendMessage` call can override via its own `truncate`
1538
+ * option.
1539
+ */
1540
+ function SQSService(opts) {
1541
+ var _a, _b, _c;
1542
+ this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SQSClient());
1543
+ this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
1544
+ this.truncate = (_c = opts === null || opts === void 0 ? void 0 : opts.truncate) !== null && _c !== void 0 ? _c : false;
1545
+ }
1546
+ /**
1547
+ * Send a single message to an SQS queue.
1548
+ *
1549
+ * At INFO level the log line includes only queue routing / FIFO metadata;
1550
+ * see `SEND_MESSAGE_SAFE_FIELDS`. `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the
1551
+ * full input.
1552
+ */
1553
+ SQSService.prototype.sendMessage = function (input, opts) {
1554
+ return __awaiter(this, void 0, void 0, function () {
1555
+ var shouldTruncate, effective;
1556
+ var _a;
1557
+ return __generator(this, function (_b) {
1558
+ shouldTruncate = (_a = opts === null || opts === void 0 ? void 0 : opts.truncate) !== null && _a !== void 0 ? _a : this.truncate;
1559
+ effective = shouldTruncate ? this.applyTruncation(input) : input;
1560
+ this.logger.info('Sending SQS message', {
1561
+ input: filterFieldsForLogLevel(this.logger, effective, SEND_MESSAGE_SAFE_FIELDS),
1562
+ });
1563
+ return [2 /*return*/, this.client.send(new SendMessageCommand(effective))];
1564
+ });
1565
+ });
1566
+ };
1567
+ SQSService.prototype.applyTruncation = function (input) {
1568
+ var truncated = [];
1569
+ var MessageBody = input.MessageBody;
1570
+ if (MessageBody !== undefined &&
1571
+ Buffer.byteLength(MessageBody, 'utf8') > SQS_MESSAGE_BODY_MAX_BYTES) {
1572
+ MessageBody = truncateUtf8(MessageBody, SQS_MESSAGE_BODY_MAX_BYTES);
1573
+ truncated.push('MessageBody');
1574
+ }
1575
+ if (truncated.length > 0) {
1576
+ this.logger.warn('Truncated SQS sendMessage input', { fields: truncated });
1577
+ }
1578
+ return __assign(__assign({}, input), { MessageBody: MessageBody });
1579
+ };
1580
+ /**
1581
+ * Receive messages from an SQS queue. Returns an empty array when no
1582
+ * messages are available. No automatic deletion is performed — visibility
1583
+ * timeout semantics are the caller's responsibility.
1584
+ * @returns The `Messages` array from the response, or `[]` if absent.
1585
+ */
1586
+ SQSService.prototype.receiveMessages = function (input) {
1587
+ return __awaiter(this, void 0, void 0, function () {
1588
+ var response;
1589
+ var _a;
1590
+ return __generator(this, function (_b) {
1591
+ switch (_b.label) {
1592
+ case 0:
1593
+ this.logger.info('Receiving SQS messages', { input: input });
1594
+ return [4 /*yield*/, this.client.send(new ReceiveMessageCommand(input))];
1595
+ case 1:
1596
+ response = _b.sent();
1597
+ return [2 /*return*/, (_a = response.Messages) !== null && _a !== void 0 ? _a : []];
1598
+ }
1599
+ });
1600
+ });
1601
+ };
1602
+ /**
1603
+ * Delete a single message from an SQS queue.
1604
+ */
1605
+ SQSService.prototype.deleteMessage = function (input) {
1606
+ return __awaiter(this, void 0, void 0, function () {
1607
+ return __generator(this, function (_a) {
1608
+ this.logger.info('Deleting SQS message', { input: input });
1609
+ return [2 /*return*/, this.client.send(new DeleteMessageCommand(input))];
1610
+ });
1611
+ });
1612
+ };
1613
+ /**
1614
+ * Send a batch of messages to an SQS queue. The SQS API caps
1615
+ * SendMessageBatch at 10 entries per request, so this method auto-chunks
1616
+ * the caller's entries and sends one request per chunk.
1617
+ */
1618
+ SQSService.prototype.sendMessageBatch = function (input) {
1619
+ return __awaiter(this, void 0, void 0, function () {
1620
+ var entries, isDebug, results, i, chunk, _a, _b;
1621
+ var _c;
1622
+ return __generator(this, function (_d) {
1623
+ switch (_d.label) {
1624
+ case 0:
1625
+ entries = (_c = input.Entries) !== null && _c !== void 0 ? _c : [];
1626
+ isDebug = this.logger.getLevelName() === 'DEBUG';
1627
+ this.logger.info('Sending SQS message batch', {
1628
+ input: isDebug ? input : { QueueUrl: input.QueueUrl, entryCount: entries.length },
1629
+ });
1630
+ results = [];
1631
+ i = 0;
1632
+ _d.label = 1;
1633
+ case 1:
1634
+ if (!(i < entries.length)) return [3 /*break*/, 4];
1635
+ chunk = entries.slice(i, i + SQS_BATCH_LIMIT);
1636
+ _b = (_a = results).push;
1637
+ return [4 /*yield*/, this.client.send(new SendMessageBatchCommand(__assign(__assign({}, input), { Entries: chunk })))];
1638
+ case 2:
1639
+ _b.apply(_a, [_d.sent()]);
1640
+ _d.label = 3;
1641
+ case 3:
1642
+ i += SQS_BATCH_LIMIT;
1643
+ return [3 /*break*/, 1];
1644
+ case 4: return [2 /*return*/, results];
1645
+ }
1646
+ });
1647
+ });
1648
+ };
1649
+ /**
1650
+ * Delete a batch of messages from an SQS queue. The SQS API caps
1651
+ * DeleteMessageBatch at 10 entries per request, so this method auto-chunks
1652
+ * the caller's entries and sends one request per chunk.
1653
+ */
1654
+ SQSService.prototype.deleteMessageBatch = function (input) {
1655
+ return __awaiter(this, void 0, void 0, function () {
1656
+ var entries, isDebug, results, i, chunk, _a, _b;
1657
+ var _c;
1658
+ return __generator(this, function (_d) {
1659
+ switch (_d.label) {
1660
+ case 0:
1661
+ entries = (_c = input.Entries) !== null && _c !== void 0 ? _c : [];
1662
+ isDebug = this.logger.getLevelName() === 'DEBUG';
1663
+ this.logger.info('Deleting SQS message batch', {
1664
+ input: isDebug ? input : { QueueUrl: input.QueueUrl, entryCount: entries.length },
1665
+ });
1666
+ results = [];
1667
+ i = 0;
1668
+ _d.label = 1;
1669
+ case 1:
1670
+ if (!(i < entries.length)) return [3 /*break*/, 4];
1671
+ chunk = entries.slice(i, i + SQS_BATCH_LIMIT);
1672
+ _b = (_a = results).push;
1673
+ return [4 /*yield*/, this.client.send(new DeleteMessageBatchCommand(__assign(__assign({}, input), { Entries: chunk })))];
1674
+ case 2:
1675
+ _b.apply(_a, [_d.sent()]);
1676
+ _d.label = 3;
1677
+ case 3:
1678
+ i += SQS_BATCH_LIMIT;
1679
+ return [3 /*break*/, 1];
1680
+ case 4: return [2 /*return*/, results];
1681
+ }
1682
+ });
1683
+ });
1684
+ };
1685
+ return SQSService;
1686
+ }());
1687
+
1688
+ /**
1689
+ * Fields safe to log at INFO level for `putParameter`. Omits `Value` (the
1690
+ * parameter content itself). `POWERTOOLS_LOG_LEVEL=DEBUG` unlocks the full
1691
+ * input.
1692
+ */
1693
+ var PUT_PARAMETER_SAFE_FIELDS = [
1694
+ 'Name',
1695
+ 'Type',
1696
+ 'Description',
1697
+ 'KeyId',
1698
+ 'Overwrite',
1699
+ 'AllowedPattern',
1700
+ 'Tags',
1701
+ 'Tier',
1702
+ 'Policies',
1703
+ 'DataType',
1704
+ ];
1705
+ /**
1706
+ * Wrapper around the AWS SSM Parameter Store client providing structured
1707
+ * Powertools logging and X-Ray tracing by default. All read operations enable
1708
+ * `WithDecryption` — callers needing plaintext should use `SSMClient`
1709
+ * directly.
1710
+ *
1711
+ * Write operations (`putParameter`, `deleteParameter`) are exposed for
1712
+ * convenience but should be used with care: parameter lifecycle is usually
1713
+ * managed by IaC (CDK / Terraform). Prefer IaC for anything that exists at
1714
+ * deploy time; reserve runtime writes for values that genuinely need to
1715
+ * mutate within the application.
1716
+ */
1717
+ var SSMService = /** @class */ (function () {
1718
+ /**
1719
+ * @param opts.logger - Optional Powertools logger. Defaults to `new Logger()`,
1720
+ * which picks up `POWERTOOLS_SERVICE_NAME` from the environment.
1721
+ * @param opts.client - Optional pre-configured `SSMClient`. When supplied,
1722
+ * the wrapper does not apply X-Ray instrumentation.
1723
+ */
1724
+ function SSMService(opts) {
1725
+ var _a, _b;
1726
+ this.client = (_a = opts === null || opts === void 0 ? void 0 : opts.client) !== null && _a !== void 0 ? _a : xray.captureAWSv3Client(new SSMClient());
1727
+ this.logger = (_b = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _b !== void 0 ? _b : new Logger();
1728
+ }
1729
+ /**
1730
+ * Fetch a single SSM parameter's value.
1731
+ * @param name - The parameter name (or ARN).
1732
+ * @returns The parameter value, or `undefined` if the parameter has no
1733
+ * value set.
1734
+ */
1735
+ SSMService.prototype.getParameter = function (name) {
1736
+ return __awaiter(this, void 0, void 0, function () {
1737
+ var response;
1738
+ var _a;
1739
+ return __generator(this, function (_b) {
1740
+ switch (_b.label) {
1741
+ case 0:
1742
+ this.logger.info('Fetching SSM parameter', { input: { name: name } });
1743
+ return [4 /*yield*/, this.client.send(new GetParameterCommand({ Name: name, WithDecryption: true }))];
1744
+ case 1:
1745
+ response = _b.sent();
1746
+ return [2 /*return*/, (_a = response.Parameter) === null || _a === void 0 ? void 0 : _a.Value];
1747
+ }
1748
+ });
1749
+ });
1750
+ };
1751
+ /**
1752
+ * Fetch multiple SSM parameters in a single request. Callers supply an
1753
+ * alias-to-path record, and the returned record is keyed by the same
1754
+ * aliases — so the SSM path is only mentioned at the call site and the
1755
+ * destructured names are whatever the caller wants to use locally:
1756
+ *
1757
+ * ```ts
1758
+ * const { username, password } = await ssm.getParameters({
1759
+ * username: '/myapp/db/username',
1760
+ * password: '/myapp/db/password',
1761
+ * });
1762
+ * ```
1763
+ *
1764
+ * @param aliases - Record mapping each desired local alias to its SSM
1765
+ * parameter name (or ARN).
1766
+ * @returns A record keyed by the same aliases, mapping each to the
1767
+ * parameter's value, or `undefined` when the parameter is missing or has
1768
+ * no value.
1769
+ */
1770
+ SSMService.prototype.getParameters = function (aliases) {
1771
+ return __awaiter(this, void 0, void 0, function () {
1772
+ var response, byPath, _i, _a, parameter, result, _b, _c, alias;
1773
+ var _d;
1774
+ return __generator(this, function (_e) {
1775
+ switch (_e.label) {
1776
+ case 0:
1777
+ this.logger.info('Fetching SSM parameters', { input: { aliases: aliases } });
1778
+ return [4 /*yield*/, this.client.send(new GetParametersCommand({
1779
+ Names: Object.values(aliases),
1780
+ WithDecryption: true,
1781
+ }))];
1782
+ case 1:
1783
+ response = _e.sent();
1784
+ byPath = new Map();
1785
+ for (_i = 0, _a = (_d = response.Parameters) !== null && _d !== void 0 ? _d : []; _i < _a.length; _i++) {
1786
+ parameter = _a[_i];
1787
+ if (parameter.Name !== undefined)
1788
+ byPath.set(parameter.Name, parameter.Value);
1789
+ }
1790
+ result = {};
1791
+ for (_b = 0, _c = Object.keys(aliases); _b < _c.length; _b++) {
1792
+ alias = _c[_b];
1793
+ result[alias] = byPath.get(aliases[alias]);
1794
+ }
1795
+ return [2 /*return*/, result];
1796
+ }
1797
+ });
1798
+ });
1799
+ };
1800
+ /**
1801
+ * Create or update an SSM parameter. The log line omits `Value` to avoid
1802
+ * leaking secret material.
1803
+ *
1804
+ * Prefer IaC (CDK / Terraform) for parameter lifecycle — use this for
1805
+ * runtime values only.
1806
+ */
1807
+ SSMService.prototype.putParameter = function (input) {
1808
+ return __awaiter(this, void 0, void 0, function () {
1809
+ return __generator(this, function (_a) {
1810
+ this.logger.info('Putting SSM parameter', {
1811
+ input: filterFieldsForLogLevel(this.logger, input, PUT_PARAMETER_SAFE_FIELDS),
1812
+ });
1813
+ return [2 /*return*/, this.client.send(new PutParameterCommand(input))];
1814
+ });
1815
+ });
1816
+ };
1817
+ /**
1818
+ * Delete an SSM parameter by name.
1819
+ *
1820
+ * Prefer IaC (CDK / Terraform) for parameter lifecycle — use this for
1821
+ * runtime cleanup only.
1822
+ */
1823
+ SSMService.prototype.deleteParameter = function (name) {
1824
+ return __awaiter(this, void 0, void 0, function () {
1825
+ return __generator(this, function (_a) {
1826
+ switch (_a.label) {
1827
+ case 0:
1828
+ this.logger.info('Deleting SSM parameter', { input: { name: name } });
1829
+ return [4 /*yield*/, this.client.send(new DeleteParameterCommand({ Name: name }))];
1830
+ case 1:
1831
+ _a.sent();
1832
+ return [2 /*return*/];
1833
+ }
1834
+ });
1835
+ });
1836
+ };
1837
+ /**
1838
+ * Fetch all parameters under an SSM hierarchy path, auto-paginating across
1839
+ * all pages. `Recursive` defaults to `true` (overriding the AWS SDK
1840
+ * default of `false`) to match the typical "load all config under
1841
+ * `/myapp/`" use case.
1842
+ * @param path - The parameter path prefix (e.g. `/myapp/`).
1843
+ * @param opts.recursive - Whether to recurse into nested paths. Defaults
1844
+ * to `true`.
1845
+ * @returns The full `Parameter` objects (including `Version`,
1846
+ * `LastModifiedDate`, etc.).
1847
+ */
1848
+ SSMService.prototype.getParametersByPath = function (path, opts) {
1849
+ return __awaiter(this, void 0, void 0, function () {
1850
+ var recursive, paginator, parameters, _a, paginator_1, paginator_1_1, page, e_1_1;
1851
+ var _b, e_1, _c, _d;
1852
+ var _e, _f;
1853
+ return __generator(this, function (_g) {
1854
+ switch (_g.label) {
1855
+ case 0:
1856
+ recursive = (_e = opts === null || opts === void 0 ? void 0 : opts.recursive) !== null && _e !== void 0 ? _e : true;
1857
+ this.logger.info('Fetching SSM parameters by path', {
1858
+ input: { path: path, recursive: recursive },
1859
+ });
1860
+ paginator = paginateGetParametersByPath({ client: this.client }, { Path: path, Recursive: recursive, WithDecryption: true });
1861
+ parameters = [];
1862
+ _g.label = 1;
1863
+ case 1:
1864
+ _g.trys.push([1, 6, 7, 12]);
1865
+ _a = true, paginator_1 = __asyncValues(paginator);
1866
+ _g.label = 2;
1867
+ case 2: return [4 /*yield*/, paginator_1.next()];
1868
+ case 3:
1869
+ if (!(paginator_1_1 = _g.sent(), _b = paginator_1_1.done, !_b)) return [3 /*break*/, 5];
1870
+ _d = paginator_1_1.value;
1871
+ _a = false;
1872
+ page = _d;
1873
+ parameters.push.apply(parameters, ((_f = page.Parameters) !== null && _f !== void 0 ? _f : []));
1874
+ _g.label = 4;
1875
+ case 4:
1876
+ _a = true;
1877
+ return [3 /*break*/, 2];
1878
+ case 5: return [3 /*break*/, 12];
1879
+ case 6:
1880
+ e_1_1 = _g.sent();
1881
+ e_1 = { error: e_1_1 };
1882
+ return [3 /*break*/, 12];
1883
+ case 7:
1884
+ _g.trys.push([7, , 10, 11]);
1885
+ if (!(!_a && !_b && (_c = paginator_1.return))) return [3 /*break*/, 9];
1886
+ return [4 /*yield*/, _c.call(paginator_1)];
1887
+ case 8:
1888
+ _g.sent();
1889
+ _g.label = 9;
1890
+ case 9: return [3 /*break*/, 11];
1891
+ case 10:
1892
+ if (e_1) throw e_1.error;
1893
+ return [7 /*endfinally*/];
1894
+ case 11: return [7 /*endfinally*/];
1895
+ case 12: return [2 /*return*/, parameters];
1896
+ }
1897
+ });
1898
+ });
1899
+ };
1900
+ return SSMService;
1901
+ }());
1902
+
1903
+ export { DynamoDBService, S3Service, SNSService, SQSService, SSMService, SecretsManagerService, StepFunctionsService };