@composurecdk/lambda 0.8.4 → 0.8.6

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 (66) hide show
  1. package/README.md +67 -16
  2. package/dist/commonjs/alarm-config.d.ts +22 -0
  3. package/dist/commonjs/alarm-config.d.ts.map +1 -1
  4. package/dist/commonjs/alarm-defaults.d.ts +1 -0
  5. package/dist/commonjs/alarm-defaults.d.ts.map +1 -1
  6. package/dist/commonjs/alarm-defaults.js +15 -0
  7. package/dist/commonjs/alarm-defaults.js.map +1 -1
  8. package/dist/commonjs/event-sources/composure-event-source.d.ts +4 -4
  9. package/dist/commonjs/event-sources/composure-event-source.d.ts.map +1 -1
  10. package/dist/commonjs/event-sources/composure-event-source.js +7 -3
  11. package/dist/commonjs/event-sources/composure-event-source.js.map +1 -1
  12. package/dist/commonjs/event-sources/dynamodb-event-source.d.ts +55 -0
  13. package/dist/commonjs/event-sources/dynamodb-event-source.d.ts.map +1 -0
  14. package/dist/commonjs/event-sources/dynamodb-event-source.js +88 -0
  15. package/dist/commonjs/event-sources/dynamodb-event-source.js.map +1 -0
  16. package/dist/commonjs/event-sources/event-source-relationship-guards.d.ts +28 -0
  17. package/dist/commonjs/event-sources/event-source-relationship-guards.d.ts.map +1 -0
  18. package/dist/commonjs/event-sources/event-source-relationship-guards.js +105 -0
  19. package/dist/commonjs/event-sources/event-source-relationship-guards.js.map +1 -0
  20. package/dist/commonjs/event-sources/sqs-event-source.d.ts +7 -4
  21. package/dist/commonjs/event-sources/sqs-event-source.d.ts.map +1 -1
  22. package/dist/commonjs/event-sources/sqs-event-source.js +7 -4
  23. package/dist/commonjs/event-sources/sqs-event-source.js.map +1 -1
  24. package/dist/commonjs/function-alarms.d.ts.map +1 -1
  25. package/dist/commonjs/function-alarms.js +111 -47
  26. package/dist/commonjs/function-alarms.js.map +1 -1
  27. package/dist/commonjs/function-builder.d.ts.map +1 -1
  28. package/dist/commonjs/function-builder.js +7 -0
  29. package/dist/commonjs/function-builder.js.map +1 -1
  30. package/dist/commonjs/index.d.ts +2 -0
  31. package/dist/commonjs/index.d.ts.map +1 -1
  32. package/dist/commonjs/index.js +6 -1
  33. package/dist/commonjs/index.js.map +1 -1
  34. package/dist/esm/alarm-config.d.ts +22 -0
  35. package/dist/esm/alarm-config.d.ts.map +1 -1
  36. package/dist/esm/alarm-defaults.d.ts +1 -0
  37. package/dist/esm/alarm-defaults.d.ts.map +1 -1
  38. package/dist/esm/alarm-defaults.js +15 -0
  39. package/dist/esm/alarm-defaults.js.map +1 -1
  40. package/dist/esm/event-sources/composure-event-source.d.ts +4 -4
  41. package/dist/esm/event-sources/composure-event-source.d.ts.map +1 -1
  42. package/dist/esm/event-sources/composure-event-source.js +7 -3
  43. package/dist/esm/event-sources/composure-event-source.js.map +1 -1
  44. package/dist/esm/event-sources/dynamodb-event-source.d.ts +55 -0
  45. package/dist/esm/event-sources/dynamodb-event-source.d.ts.map +1 -0
  46. package/dist/esm/event-sources/dynamodb-event-source.js +84 -0
  47. package/dist/esm/event-sources/dynamodb-event-source.js.map +1 -0
  48. package/dist/esm/event-sources/event-source-relationship-guards.d.ts +28 -0
  49. package/dist/esm/event-sources/event-source-relationship-guards.d.ts.map +1 -0
  50. package/dist/esm/event-sources/event-source-relationship-guards.js +102 -0
  51. package/dist/esm/event-sources/event-source-relationship-guards.js.map +1 -0
  52. package/dist/esm/event-sources/sqs-event-source.d.ts +7 -4
  53. package/dist/esm/event-sources/sqs-event-source.d.ts.map +1 -1
  54. package/dist/esm/event-sources/sqs-event-source.js +7 -4
  55. package/dist/esm/event-sources/sqs-event-source.js.map +1 -1
  56. package/dist/esm/function-alarms.d.ts.map +1 -1
  57. package/dist/esm/function-alarms.js +112 -48
  58. package/dist/esm/function-alarms.js.map +1 -1
  59. package/dist/esm/function-builder.d.ts.map +1 -1
  60. package/dist/esm/function-builder.js +7 -0
  61. package/dist/esm/function-builder.js.map +1 -1
  62. package/dist/esm/index.d.ts +2 -0
  63. package/dist/esm/index.d.ts.map +1 -1
  64. package/dist/esm/index.js +2 -0
  65. package/dist/esm/index.js.map +1 -1
  66. package/package.json +15 -5
package/README.md CHANGED
@@ -125,16 +125,21 @@ Opt back into CDK's auto-created role attached to `AWSLambdaBasicExecutionRole`.
125
125
 
126
126
  The builder creates [AWS-recommended CloudWatch alarms](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#Lambda) by default. No alarm actions are configured — access alarms from the build result to add SNS topics or other actions.
127
127
 
128
- | Alarm | Metric | Default threshold | Created when |
129
- | ------------------------ | --------------------------------- | --------------------------- | ------------------------------------- |
130
- | `errors` | Errors (Sum, 1 min) | > 0 | Always |
131
- | `throttles` | Throttles (Sum, 1 min) | > 0 | Always |
132
- | `duration` | Duration (p99, 1 min) | > 90% of configured timeout | `timeout` is set |
133
- | `concurrentExecutions` | ConcurrentExecutions (Max, 1 min) | >= 80% of reserved limit | `reservedConcurrentExecutions` is set |
134
- | `<key>FailedInvocations` | FailedInvokeEventCount (Sum) | > 0 | An SQS event source is attached |
135
- | `<key>DroppedEvents` | DroppedEventCount (Sum) | > 0 | An SQS event source is attached |
128
+ | Alarm | Metric | Default threshold | Created when |
129
+ | ------------------------ | --------------------------------- | --------------------------- | -------------------------------------- |
130
+ | `errors` | Errors (Sum, 1 min) | > 0 | Always |
131
+ | `throttles` | Throttles (Sum, 1 min) | > 0 | Always |
132
+ | `duration` | Duration (p99, 1 min) | > 90% of configured timeout | `timeout` is set |
133
+ | `concurrentExecutions` | ConcurrentExecutions (Max, 1 min) | >= 80% of reserved limit | `reservedConcurrentExecutions` is set |
134
+ | `<key>FailedInvocations` | FailedInvokeEventCount (Sum) | > 0 | An SQS or DynamoDB source is attached |
135
+ | `<key>DroppedEvents` | DroppedEventCount (Sum) | > 0 | An SQS or DynamoDB source is attached |
136
+ | `iteratorAge` | IteratorAge (Max, 1 min) | > 60000 ms for 3 min¹ | A stream source (DynamoDB) is attached |
136
137
 
137
- The event-source alarms are contextual: one pair is created per event source attached via `addEventSource` (see [Event sources](#event-sources)) whose kind emits per-mapping ESM metrics. Each alarm's key is the event source's key suffixed with `FailedInvocations` / `DroppedEvents` e.g. an event source added as `"orders"` produces `ordersFailedInvocations` and `ordersDroppedEvents`. The `eventSourceFailedInvocations` / `eventSourceDroppedEvents` fields on `recommendedAlarms` tune every such alarm.
138
+ ¹ AWS recommends alarming on `IteratorAge` for stream consumers but prescribes no fixed threshold it is workload dependent. The 60s/3-minute default is deliberately conservative; tune it per workload via `eventSourceIteratorAge`.
139
+
140
+ The per-mapping event-source alarms are contextual: one pair is created per event source attached via `addEventSource` (see [Event sources](#event-sources)) whose kind emits per-mapping ESM metrics. Each alarm's key is the event source's key suffixed with `FailedInvocations` / `DroppedEvents` — e.g. an event source added as `"orders"` produces `ordersFailedInvocations` and `ordersDroppedEvents`. The `eventSourceFailedInvocations` / `eventSourceDroppedEvents` fields on `recommendedAlarms` tune every such alarm.
141
+
142
+ `iteratorAge` is different: `IteratorAge` is a function-level metric, so a single alarm (keyed `iteratorAge`) is created whenever at least one stream source (currently DynamoDB streams) is attached, regardless of how many. It warns when the consumer falls behind its stream. Tune or disable it via the `eventSourceIteratorAge` field on `recommendedAlarms`.
138
143
 
139
144
  The defaults are exported as `FUNCTION_ALARM_DEFAULTS` for visibility and testing:
140
145
 
@@ -226,9 +231,10 @@ for (const alarm of Object.values(result.alarms)) {
226
231
  function can have many event sources of mixed types, so the hook is repeatable
227
232
  and keyed — the resolved sources are exposed on `result.eventSources`.
228
233
 
229
- Pass a `ComposureEventSource` from a factory (`sqsEventSource`), which carries
230
- its own `Resolvable` so the source queue can be a `ref()` to a sibling
231
- component, or a bare CDK `IEventSource` as an escape hatch.
234
+ Pass a `ComposureEventSource` from a factory (`sqsEventSource`,
235
+ `dynamoEventSource`), which carries its own `Resolvable` so the source queue or
236
+ table can be a `ref()` to a sibling component, or a bare CDK `IEventSource` as
237
+ an escape hatch.
232
238
 
233
239
  ```ts
234
240
  import { compose, ref } from "@composurecdk/core";
@@ -250,7 +256,44 @@ const system = compose(
250
256
 
251
257
  The source is attached _after_ the function and its least-privilege execution
252
258
  role exist, so the `source.bind(fn)` that `addEventSource` performs grants the
253
- queue-consume permission onto the builder's role rather than CDK's auto-role.
259
+ consume permission (SQS `ReceiveMessage`, or DynamoDB `grantStreamRead`) onto
260
+ the builder's role rather than CDK's auto-role.
261
+
262
+ `dynamoEventSource(table, props?)` mirrors the SQS factory for DynamoDB streams.
263
+ The table must have a stream enabled (via the [DynamoDB builder](../dynamodb)'s
264
+ `.dynamoStream(...)` / `.stream(...)`, or `TableProps.stream`); otherwise CDK
265
+ throws `DynamoDB Streams must be enabled` at build time. `startingPosition`
266
+ defaults to `LATEST` and is overridable via `props`:
267
+
268
+ ```ts
269
+ import { StartingPosition } from "aws-cdk-lib/aws-lambda";
270
+ import { StreamViewType } from "aws-cdk-lib/aws-dynamodb";
271
+ import { compose, ref } from "@composurecdk/core";
272
+ import { createFunctionBuilder, dynamoEventSource } from "@composurecdk/lambda";
273
+ import { createTableV2Builder } from "@composurecdk/dynamodb";
274
+
275
+ compose(
276
+ {
277
+ orders: createTableV2Builder()
278
+ .partitionKey({ name: "pk", type: AttributeType.STRING })
279
+ .dynamoStream(StreamViewType.NEW_AND_OLD_IMAGES),
280
+ processor: createFunctionBuilder()
281
+ .runtime(Runtime.NODEJS_22_X)
282
+ .handler("index.handler")
283
+ .code(Code.fromAsset("lambda"))
284
+ .addEventSource(
285
+ "orders",
286
+ dynamoEventSource(
287
+ ref("orders", (r) => r.table),
288
+ {
289
+ startingPosition: StartingPosition.TRIM_HORIZON,
290
+ },
291
+ ),
292
+ ),
293
+ },
294
+ { orders: [], processor: ["orders"] },
295
+ );
296
+ ```
254
297
 
255
298
  ### Secure defaults
256
299
 
@@ -262,6 +305,15 @@ second `props` argument and exported as `DEFAULT_SQS_EVENT_SOURCE_PROPS`:
262
305
  | `reportBatchItemFailures` | `true` | A single poison message fails only its own record, not the whole batch. CDK defaults this `false`. |
263
306
  | `metricsConfig` | `{ metrics: [EventCount] }` | Enables the per-mapping ESM metrics that back the event-source contextual alarms. |
264
307
 
308
+ `dynamoEventSource` applies the same defaults plus `startingPosition`, exported
309
+ as `DEFAULT_DYNAMO_EVENT_SOURCE_PROPS`:
310
+
311
+ | Property | Default | Rationale |
312
+ | ------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------- |
313
+ | `startingPosition` | `LATEST` | A newly-attached consumer reads from the stream tip, not the table's existing change history. |
314
+ | `reportBatchItemFailures` | `true` | A single poison record fails only its own record, not the whole batch. CDK defaults this `false`. |
315
+ | `metricsConfig` | `{ metrics: [EventCount] }` | Enables the per-mapping ESM metrics that back the event-source contextual alarms. |
316
+
265
317
  ### Cross-component invariants
266
318
 
267
319
  AWS Well-Architected guidance spans the queue and the function — the source
@@ -271,9 +323,8 @@ today (the queue often arrives as an unresolved `ref()`); they are tracked in
271
323
  [#123](https://github.com/laazyj/composureCDK/issues/123) and
272
324
  [#124](https://github.com/laazyj/composureCDK/issues/124).
273
325
 
274
- `kinesisEventSource` / `dynamoEventSource` and the stream-only `IteratorAge`
275
- alarm are deferred — see [#120](https://github.com/laazyj/composureCDK/issues/120)
276
- and [#121](https://github.com/laazyj/composureCDK/issues/121).
326
+ `kinesisEventSource` is still deferred see
327
+ [#120](https://github.com/laazyj/composureCDK/issues/120).
277
328
 
278
329
  ## Examples
279
330
 
@@ -110,5 +110,27 @@ export interface FunctionAlarmConfig {
110
110
  * @see https://aws.amazon.com/blogs/compute/introducing-new-event-source-mapping-esm-metrics-for-aws-lambda/
111
111
  */
112
112
  eventSourceDroppedEvents?: AlarmConfig | false;
113
+ /**
114
+ * Alarm when the function is falling behind a stream event source.
115
+ *
116
+ * Contextual: a single alarm is created when at least one stream event
117
+ * source (currently DynamoDB streams) is attached via
118
+ * {@link IFunctionBuilder.addEventSource}. Unlike the per-mapping
119
+ * {@link FunctionAlarmConfig.eventSourceFailedInvocations} /
120
+ * {@link FunctionAlarmConfig.eventSourceDroppedEvents} alarms, `IteratorAge`
121
+ * is a function-level metric, so there is one alarm per function (keyed
122
+ * `iteratorAge`) regardless of how many stream sources are attached.
123
+ *
124
+ * The threshold is an absolute age in milliseconds (not a percentage).
125
+ *
126
+ * Metric: `AWS/Lambda IteratorAge`, statistic Maximum, period 1 minute,
127
+ * dimensioned on `FunctionName`. AWS recommends alarming on this metric for
128
+ * stream consumers but does not prescribe a threshold (it is workload
129
+ * dependent); the default is a deliberately conservative > 60000 ms (60s) for
130
+ * 3 consecutive minutes.
131
+ *
132
+ * @see https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html
133
+ */
134
+ eventSourceIteratorAge?: AlarmConfig | false;
113
135
  }
114
136
  //# sourceMappingURL=alarm-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"alarm-config.d.ts","sourceRoot":"","sources":["../../src/alarm-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG;IACnE;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC,IAAI,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC,CAAC;AAE/F;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAE7B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAEzC;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAErD;;;;;;;;;;;;;OAaG;IACH,4BAA4B,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAEnD;;;;;;;;;;;;;OAaG;IACH,wBAAwB,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;CAChD"}
1
+ {"version":3,"file":"alarm-config.d.ts","sourceRoot":"","sources":["../../src/alarm-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,qBAAqB,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG;IACnE;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GAAG,QAAQ,CAAC,IAAI,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC,CAAC;AAE/F;;;;;;;GAOG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAE7B;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAEhC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAEzC;;;;;;;;;;OAUG;IACH,oBAAoB,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAErD;;;;;;;;;;;;;OAaG;IACH,4BAA4B,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAEnD;;;;;;;;;;;;;OAaG;IACH,wBAAwB,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAE/C;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,sBAAsB,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;CAC9C"}
@@ -8,6 +8,7 @@ interface FunctionAlarmDefaults {
8
8
  concurrentExecutions: PercentageAlarmConfigDefaults;
9
9
  eventSourceFailedInvocations: AlarmConfigDefaults;
10
10
  eventSourceDroppedEvents: AlarmConfigDefaults;
11
+ eventSourceIteratorAge: AlarmConfigDefaults;
11
12
  }
12
13
  /**
13
14
  * AWS-recommended default alarm configuration for Lambda functions.
@@ -1 +1 @@
1
- {"version":3,"file":"alarm-defaults.d.ts","sourceRoot":"","sources":["../../src/alarm-defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEvE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,mBAAmB,CAAC;IAC/B,QAAQ,EAAE,6BAA6B,CAAC;IACxC,oBAAoB,EAAE,6BAA6B,CAAC;IACpD,4BAA4B,EAAE,mBAAmB,CAAC;IAClD,wBAAwB,EAAE,mBAAmB,CAAC;CAC/C;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,qBAwDrC,CAAC"}
1
+ {"version":3,"file":"alarm-defaults.d.ts","sourceRoot":"","sources":["../../src/alarm-defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AACpE,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAC;AAEvE,UAAU,qBAAqB;IAC7B,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,mBAAmB,CAAC;IAC/B,QAAQ,EAAE,6BAA6B,CAAC;IACxC,oBAAoB,EAAE,6BAA6B,CAAC;IACpD,4BAA4B,EAAE,mBAAmB,CAAC;IAClD,wBAAwB,EAAE,mBAAmB,CAAC;IAC9C,sBAAsB,EAAE,mBAAmB,CAAC;CAC7C;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,qBAwErC,CAAC"}
@@ -57,5 +57,20 @@ exports.FUNCTION_ALARM_DEFAULTS = {
57
57
  datapointsToAlarm: 1,
58
58
  treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
59
59
  },
60
+ /**
61
+ * The consumer falling behind the stream risks data loss once records age
62
+ * past the stream's 24h retention. AWS recommends alarming on `IteratorAge`
63
+ * for stream consumers but publishes no fixed threshold — the right value is
64
+ * workload dependent. 60s of sustained lag for 3 consecutive minutes is a
65
+ * deliberately conservative default; tune via `eventSourceIteratorAge`. Idle
66
+ * functions emit no datapoints and stay OK (`NOT_BREACHING`).
67
+ * @see https://docs.aws.amazon.com/lambda/latest/dg/monitoring-metrics.html
68
+ */
69
+ eventSourceIteratorAge: {
70
+ threshold: 60_000,
71
+ evaluationPeriods: 3,
72
+ datapointsToAlarm: 3,
73
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
74
+ },
60
75
  };
61
76
  //# sourceMappingURL=alarm-defaults.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"alarm-defaults.js","sourceRoot":"","sources":["../../src/alarm-defaults.ts"],"names":[],"mappings":";;;AAAA,+DAA8D;AAc9D;;;;GAIG;AACU,QAAA,uBAAuB,GAA0B;IAC5D,OAAO,EAAE,IAAI;IAEb,qDAAqD;IACrD,MAAM,EAAE;QACN,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED,6DAA6D;IAC7D,SAAS,EAAE;QACT,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED;;;OAGG;IACH,QAAQ,EAAE;QACR,gBAAgB,EAAE,GAAG;QACrB,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED;;;OAGG;IACH,oBAAoB,EAAE;QACpB,gBAAgB,EAAE,GAAG;QACrB,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED,sFAAsF;IACtF,4BAA4B,EAAE;QAC5B,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED,mDAAmD;IACnD,wBAAwB,EAAE;QACxB,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;CACF,CAAC"}
1
+ {"version":3,"file":"alarm-defaults.js","sourceRoot":"","sources":["../../src/alarm-defaults.ts"],"names":[],"mappings":";;;AAAA,+DAA8D;AAe9D;;;;GAIG;AACU,QAAA,uBAAuB,GAA0B;IAC5D,OAAO,EAAE,IAAI;IAEb,qDAAqD;IACrD,MAAM,EAAE;QACN,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED,6DAA6D;IAC7D,SAAS,EAAE;QACT,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED;;;OAGG;IACH,QAAQ,EAAE;QACR,gBAAgB,EAAE,GAAG;QACrB,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED;;;OAGG;IACH,oBAAoB,EAAE;QACpB,gBAAgB,EAAE,GAAG;QACrB,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED,sFAAsF;IACtF,4BAA4B,EAAE;QAC5B,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED,mDAAmD;IACnD,wBAAwB,EAAE;QACxB,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;IAED;;;;;;;;OAQG;IACH,sBAAsB,EAAE;QACtB,SAAS,EAAE,MAAM;QACjB,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,iCAAgB,CAAC,aAAa;KACjD;CACF,CAAC"}
@@ -9,7 +9,7 @@ import type { Resolvable } from "@composurecdk/core";
9
9
  * straight to {@link IFunctionBuilder.addEventSource} as an escape hatch —
10
10
  * the builder still attaches it, but cannot reason about it.
11
11
  */
12
- export type EventSourceKind = "sqs" | "unknown";
12
+ export type EventSourceKind = "sqs" | "dynamodb" | "unknown";
13
13
  /** @internal — brands {@link ComposureEventSource} so the guard is unambiguous. */
14
14
  declare const COMPOSURE_EVENT_SOURCE: unique symbol;
15
15
  /**
@@ -40,9 +40,9 @@ export declare function composureEventSource(kind: EventSourceKind, source: Reso
40
40
  /**
41
41
  * Reads the event source mapping UUID off a bound CDK source, keyed by
42
42
  * {@link EventSourceKind}. Defined only for kinds whose per-mapping ESM
43
- * metrics back contextual alarms (currently SQS); `FunctionBuilder` invokes
44
- * the reader after `addEventSource` so the binding exists. Keying off `kind`
45
- * — like {@link EVENT_SOURCE_ALARM_SPECS} — keeps the builder from
43
+ * metrics back contextual alarms (SQS and DynamoDB streams); `FunctionBuilder`
44
+ * invokes the reader after `addEventSource` so the binding exists. Keying off
45
+ * `kind` — like {@link EVENT_SOURCE_ALARM_SPECS} — keeps the builder from
46
46
  * `instanceof`-ing CDK source classes.
47
47
  *
48
48
  * @internal
@@ -1 +1 @@
1
- {"version":3,"file":"composure-event-source.d.ts","sourceRoot":"","sources":["../../../src/event-sources/composure-event-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,SAAS,CAAC;AAEhD,mFAAmF;AACnF,QAAA,MAAM,sBAAsB,eAAgD,CAAC;AAE7E;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,oBAAoB;IACnC,gBAAgB;IAChB,QAAQ,CAAC,CAAC,sBAAsB,CAAC,EAAE,IAAI,CAAC;IAExC,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;CAC3C;AAED,oEAAoE;AACpE,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC,GAC/B,oBAAoB,CAEtB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAClD,eAAe,EACf,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC,GAAG,SAAS,CAO9C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GACzC,KAAK,IAAI,oBAAoB,CAE/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IAEZ,8DAA8D;IAC9D,IAAI,EAAE,eAAe,CAAC;IAEtB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B"}
1
+ {"version":3,"file":"composure-event-source.d.ts","sourceRoot":"","sources":["../../../src/event-sources/composure-event-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAErD;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,UAAU,GAAG,SAAS,CAAC;AAE7D,mFAAmF;AACnF,QAAA,MAAM,sBAAsB,eAAgD,CAAC;AAE7E;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,oBAAoB;IACnC,gBAAgB;IAChB,QAAQ,CAAC,CAAC,sBAAsB,CAAC,EAAE,IAAI,CAAC;IAExC,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC;CAC3C;AAED,oEAAoE;AACpE,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,eAAe,EACrB,MAAM,EAAE,UAAU,CAAC,YAAY,CAAC,GAC/B,oBAAoB,CAEtB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,EAAE,MAAM,CAClD,eAAe,EACf,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC,GAAG,SAAS,CAW9C,CAAC;AAEF;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GACzC,KAAK,IAAI,oBAAoB,CAE/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,GAAG,EAAE,MAAM,CAAC;IAEZ,8DAA8D;IAC9D,IAAI,EAAE,eAAe,CAAC;IAEtB;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B"}
@@ -12,9 +12,9 @@ function composureEventSource(kind, source) {
12
12
  /**
13
13
  * Reads the event source mapping UUID off a bound CDK source, keyed by
14
14
  * {@link EventSourceKind}. Defined only for kinds whose per-mapping ESM
15
- * metrics back contextual alarms (currently SQS); `FunctionBuilder` invokes
16
- * the reader after `addEventSource` so the binding exists. Keying off `kind`
17
- * — like {@link EVENT_SOURCE_ALARM_SPECS} — keeps the builder from
15
+ * metrics back contextual alarms (SQS and DynamoDB streams); `FunctionBuilder`
16
+ * invokes the reader after `addEventSource` so the binding exists. Keying off
17
+ * `kind` — like {@link EVENT_SOURCE_ALARM_SPECS} — keeps the builder from
18
18
  * `instanceof`-ing CDK source classes.
19
19
  *
20
20
  * @internal
@@ -24,6 +24,10 @@ exports.EVENT_SOURCE_MAPPING_ID_READERS = {
24
24
  // constructs the `SqsEventSource` in the same call — kind and concrete class
25
25
  // move in lockstep.
26
26
  sqs: (bound) => bound.eventSourceMappingId,
27
+ // Safe: the `"dynamodb"` kind is only ever assigned by `dynamoEventSource()`,
28
+ // which constructs the `DynamoEventSource` in the same call — kind and
29
+ // concrete class move in lockstep.
30
+ dynamodb: (bound) => bound.eventSourceMappingId,
27
31
  unknown: undefined,
28
32
  };
29
33
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"composure-event-source.js","sourceRoot":"","sources":["../../../src/event-sources/composure-event-source.ts"],"names":[],"mappings":";;;AA6CA,oDAKC;AA2BD,wDAIC;AAlED,mFAAmF;AACnF,MAAM,sBAAsB,GAAG,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AA4B7E,oEAAoE;AACpE,SAAgB,oBAAoB,CAClC,IAAqB,EACrB,MAAgC;IAEhC,OAAO,EAAE,CAAC,sBAAsB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1D,CAAC;AAED;;;;;;;;;GASG;AACU,QAAA,+BAA+B,GAGxC;IACF,4EAA4E;IAC5E,6EAA6E;IAC7E,oBAAoB;IACpB,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAE,KAAwB,CAAC,oBAAoB;IAC9D,OAAO,EAAE,SAAS;CACnB,CAAC;AAEF;;;GAGG;AACH,SAAgB,sBAAsB,CACpC,KAA0C;IAE1C,OAAO,sBAAsB,IAAI,KAAK,CAAC;AACzC,CAAC"}
1
+ {"version":3,"file":"composure-event-source.js","sourceRoot":"","sources":["../../../src/event-sources/composure-event-source.ts"],"names":[],"mappings":";;;AA6CA,oDAKC;AA+BD,wDAIC;AAtED,mFAAmF;AACnF,MAAM,sBAAsB,GAAG,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;AA4B7E,oEAAoE;AACpE,SAAgB,oBAAoB,CAClC,IAAqB,EACrB,MAAgC;IAEhC,OAAO,EAAE,CAAC,sBAAsB,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1D,CAAC;AAED;;;;;;;;;GASG;AACU,QAAA,+BAA+B,GAGxC;IACF,4EAA4E;IAC5E,6EAA6E;IAC7E,oBAAoB;IACpB,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,CAAE,KAAwB,CAAC,oBAAoB;IAC9D,8EAA8E;IAC9E,uEAAuE;IACvE,mCAAmC;IACnC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAE,KAA2B,CAAC,oBAAoB;IACtE,OAAO,EAAE,SAAS;CACnB,CAAC;AAEF;;;GAGG;AACH,SAAgB,sBAAsB,CACpC,KAA0C;IAE1C,OAAO,sBAAsB,IAAI,KAAK,CAAC;AACzC,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { ITable } from "aws-cdk-lib/aws-dynamodb";
2
+ import { type DynamoEventSourceProps } from "aws-cdk-lib/aws-lambda-event-sources";
3
+ import { type Resolvable } from "@composurecdk/core";
4
+ import { type ComposureEventSource } from "./composure-event-source.js";
5
+ /**
6
+ * Secure, AWS-recommended defaults applied to every DynamoDB stream event
7
+ * source built with {@link dynamoEventSource}. Each property can be overridden
8
+ * via the factory's `props` argument.
9
+ */
10
+ export declare const DEFAULT_DYNAMO_EVENT_SOURCE_PROPS: Pick<DynamoEventSourceProps, "startingPosition" | "reportBatchItemFailures" | "metricsConfig">;
11
+ /**
12
+ * Wraps a DynamoDB table's change stream as a Lambda {@link IEventSource},
13
+ * deferring resolution when the table is a `ref()` to a sibling component's
14
+ * output.
15
+ *
16
+ * Follows the `events/targets` factory shape: register the result with
17
+ * {@link IFunctionBuilder.addEventSource} and the builder resolves the
18
+ * `ref()`, attaches the source, and (because `addEventSource` calls
19
+ * `source.bind(fn)`) grants the function's least-privilege execution role
20
+ * permission to read the stream via `grantStreamRead`.
21
+ *
22
+ * Applies {@link DEFAULT_DYNAMO_EVENT_SOURCE_PROPS}; pass `props` to override.
23
+ *
24
+ * ## Cross-component invariant (enforced by CDK at bind time)
25
+ *
26
+ * The table must have a stream enabled (via the table builder's
27
+ * `.dynamoStream(...)` / `.stream(...)`, or `TableProps.stream`). If it does
28
+ * not, CDK's `DynamoEventSource.bind()` throws `DynamoDB Streams must be
29
+ * enabled on the table` when the function is built — the table often arrives
30
+ * as a `ref()` that is not resolvable at configuration time, so this is not
31
+ * validated earlier.
32
+ *
33
+ * @param table - The source table, concrete or a `ref()` to a sibling.
34
+ * @param props - Overrides for {@link DEFAULT_DYNAMO_EVENT_SOURCE_PROPS} and
35
+ * any other {@link DynamoEventSourceProps}.
36
+ *
37
+ * @example
38
+ * ```ts
39
+ * compose(
40
+ * {
41
+ * orders: createTableV2Builder()
42
+ * .partitionKey({ name: "pk", type: AttributeType.STRING })
43
+ * .dynamoStream(StreamViewType.NEW_AND_OLD_IMAGES),
44
+ * processor: createFunctionBuilder()
45
+ * .runtime(Runtime.NODEJS_22_X)
46
+ * .handler("index.handler")
47
+ * .code(Code.fromAsset("lambda"))
48
+ * .addEventSource("orders", dynamoEventSource(ref("orders", (r) => r.table))),
49
+ * },
50
+ * { orders: [], processor: ["orders"] },
51
+ * );
52
+ * ```
53
+ */
54
+ export declare function dynamoEventSource(table: Resolvable<ITable>, props?: DynamoEventSourceProps): ComposureEventSource;
55
+ //# sourceMappingURL=dynamodb-event-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamodb-event-source.d.ts","sourceRoot":"","sources":["../../../src/event-sources/dynamodb-event-source.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAEL,KAAK,sBAAsB,EAC5B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,KAAK,oBAAoB,EAAwB,MAAM,6BAA6B,CAAC;AAE9F;;;;GAIG;AACH,eAAO,MAAM,iCAAiC,EAAE,IAAI,CAClD,sBAAsB,EACtB,kBAAkB,GAAG,yBAAyB,GAAG,eAAe,CA0BjE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,EACzB,KAAK,CAAC,EAAE,sBAAsB,GAC7B,oBAAoB,CAMtB"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_DYNAMO_EVENT_SOURCE_PROPS = void 0;
4
+ exports.dynamoEventSource = dynamoEventSource;
5
+ const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
6
+ const aws_lambda_event_sources_1 = require("aws-cdk-lib/aws-lambda-event-sources");
7
+ const core_1 = require("@composurecdk/core");
8
+ const composure_event_source_js_1 = require("./composure-event-source.js");
9
+ /**
10
+ * Secure, AWS-recommended defaults applied to every DynamoDB stream event
11
+ * source built with {@link dynamoEventSource}. Each property can be overridden
12
+ * via the factory's `props` argument.
13
+ */
14
+ exports.DEFAULT_DYNAMO_EVENT_SOURCE_PROPS = {
15
+ /**
16
+ * Start reading from the tip of the stream so a newly-attached consumer does
17
+ * not replay the table's existing change history on first deploy. Override
18
+ * with {@link StartingPosition.TRIM_HORIZON} to reprocess from the oldest
19
+ * record in the stream.
20
+ * @see https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html
21
+ */
22
+ startingPosition: aws_lambda_1.StartingPosition.LATEST,
23
+ /**
24
+ * Report partial batch failures so a single poison record does not fail the
25
+ * whole batch and force redelivery of already-processed records. CDK
26
+ * defaults this to `false`.
27
+ * @see https://docs.aws.amazon.com/lambda/latest/dg/with-ddb.html#services-ddb-batchfailurereporting
28
+ */
29
+ reportBatchItemFailures: true,
30
+ /**
31
+ * Enable the per-mapping `EventCount` ESM metrics (`FailedInvokeEventCount`,
32
+ * `DroppedEventCount`, …). They emit only when opted in, and the
33
+ * event-source contextual alarms on {@link IFunctionBuilder} depend on them.
34
+ * @see https://aws.amazon.com/blogs/compute/introducing-new-event-source-mapping-esm-metrics-for-aws-lambda/
35
+ */
36
+ metricsConfig: { metrics: [aws_lambda_1.MetricType.EVENT_COUNT] },
37
+ };
38
+ /**
39
+ * Wraps a DynamoDB table's change stream as a Lambda {@link IEventSource},
40
+ * deferring resolution when the table is a `ref()` to a sibling component's
41
+ * output.
42
+ *
43
+ * Follows the `events/targets` factory shape: register the result with
44
+ * {@link IFunctionBuilder.addEventSource} and the builder resolves the
45
+ * `ref()`, attaches the source, and (because `addEventSource` calls
46
+ * `source.bind(fn)`) grants the function's least-privilege execution role
47
+ * permission to read the stream via `grantStreamRead`.
48
+ *
49
+ * Applies {@link DEFAULT_DYNAMO_EVENT_SOURCE_PROPS}; pass `props` to override.
50
+ *
51
+ * ## Cross-component invariant (enforced by CDK at bind time)
52
+ *
53
+ * The table must have a stream enabled (via the table builder's
54
+ * `.dynamoStream(...)` / `.stream(...)`, or `TableProps.stream`). If it does
55
+ * not, CDK's `DynamoEventSource.bind()` throws `DynamoDB Streams must be
56
+ * enabled on the table` when the function is built — the table often arrives
57
+ * as a `ref()` that is not resolvable at configuration time, so this is not
58
+ * validated earlier.
59
+ *
60
+ * @param table - The source table, concrete or a `ref()` to a sibling.
61
+ * @param props - Overrides for {@link DEFAULT_DYNAMO_EVENT_SOURCE_PROPS} and
62
+ * any other {@link DynamoEventSourceProps}.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * compose(
67
+ * {
68
+ * orders: createTableV2Builder()
69
+ * .partitionKey({ name: "pk", type: AttributeType.STRING })
70
+ * .dynamoStream(StreamViewType.NEW_AND_OLD_IMAGES),
71
+ * processor: createFunctionBuilder()
72
+ * .runtime(Runtime.NODEJS_22_X)
73
+ * .handler("index.handler")
74
+ * .code(Code.fromAsset("lambda"))
75
+ * .addEventSource("orders", dynamoEventSource(ref("orders", (r) => r.table))),
76
+ * },
77
+ * { orders: [], processor: ["orders"] },
78
+ * );
79
+ * ```
80
+ */
81
+ function dynamoEventSource(table, props) {
82
+ const merged = { ...exports.DEFAULT_DYNAMO_EVENT_SOURCE_PROPS, ...props };
83
+ const source = (0, core_1.isRef)(table)
84
+ ? table.map((resolved) => new aws_lambda_event_sources_1.DynamoEventSource(resolved, merged))
85
+ : new aws_lambda_event_sources_1.DynamoEventSource(table, merged);
86
+ return (0, composure_event_source_js_1.composureEventSource)("dynamodb", source);
87
+ }
88
+ //# sourceMappingURL=dynamodb-event-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamodb-event-source.js","sourceRoot":"","sources":["../../../src/event-sources/dynamodb-event-source.ts"],"names":[],"mappings":";;;AAwFA,8CASC;AA/FD,uDAAsE;AACtE,mFAG8C;AAC9C,6CAA4D;AAC5D,2EAA8F;AAE9F;;;;GAIG;AACU,QAAA,iCAAiC,GAG1C;IACF;;;;;;OAMG;IACH,gBAAgB,EAAE,6BAAgB,CAAC,MAAM;IAEzC;;;;;OAKG;IACH,uBAAuB,EAAE,IAAI;IAE7B;;;;;OAKG;IACH,aAAa,EAAE,EAAE,OAAO,EAAE,CAAC,uBAAU,CAAC,WAAW,CAAC,EAAE;CACrD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,SAAgB,iBAAiB,CAC/B,KAAyB,EACzB,KAA8B;IAE9B,MAAM,MAAM,GAA2B,EAAE,GAAG,yCAAiC,EAAE,GAAG,KAAK,EAAE,CAAC;IAC1F,MAAM,MAAM,GAA6B,IAAA,YAAK,EAAC,KAAK,CAAC;QACnD,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,4CAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC,CAAC,IAAI,4CAAiB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACzC,OAAO,IAAA,gDAAoB,EAAC,UAAU,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { type Duration } from "aws-cdk-lib";
2
+ import type { Function as LambdaFunction, IEventSource } from "aws-cdk-lib/aws-lambda";
3
+ import type { EventSourceKind } from "./composure-event-source.js";
4
+ /**
5
+ * Suppression id for the SQS visibility-timeout relationship guard. Stable and
6
+ * part of the public surface — silence the warning with
7
+ * `Annotations.of(scope).acknowledgeWarning(SQS_VISIBILITY_TIMEOUT_WARNING_ID)`,
8
+ * so it must not be renamed casually.
9
+ */
10
+ export declare const SQS_VISIBILITY_TIMEOUT_WARNING_ID = "@composurecdk/lambda:sqs-visibility-timeout";
11
+ /**
12
+ * Guards a cross-component relationship that spans an attached event source and
13
+ * its consumer function. `FunctionBuilder` dispatches on {@link EventSourceKind}
14
+ * so it never has to `instanceof` CDK internals — see ADR-0011.
15
+ *
16
+ * @internal
17
+ */
18
+ export type EventSourceRelationshipGuard = (fn: LambdaFunction, id: string, key: string, bound: IEventSource, timeout: Duration | undefined) => void;
19
+ /**
20
+ * Per-kind relationship guards, keyed by {@link EventSourceKind}. A kind maps to
21
+ * the (possibly empty) list of relationships to guard for a source of that kind
22
+ * — SQS gains a second entry for the `maxReceiveCount` floor (#124), and a bare
23
+ * escape-hatch source attached as `"unknown"` has none.
24
+ *
25
+ * @internal
26
+ */
27
+ export declare const EVENT_SOURCE_RELATIONSHIP_GUARDS: Record<EventSourceKind, EventSourceRelationshipGuard[]>;
28
+ //# sourceMappingURL=event-source-relationship-guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-source-relationship-guards.d.ts","sourceRoot":"","sources":["../../../src/event-sources/event-source-relationship-guards.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqC,KAAK,QAAQ,EAAS,MAAM,aAAa,CAAC;AACtF,OAAO,KAAK,EAAE,QAAQ,IAAI,cAAc,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAIvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAiBnE;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,gDAAgD,CAAC;AAE/F;;;;;;GAMG;AACH,MAAM,MAAM,4BAA4B,GAAG,CACzC,EAAE,EAAE,cAAc,EAClB,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,QAAQ,GAAG,SAAS,KAC1B,IAAI,CAAC;AAEV;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,EAAE,MAAM,CACnD,eAAe,EACf,4BAA4B,EAAE,CAK/B,CAAC"}
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EVENT_SOURCE_RELATIONSHIP_GUARDS = exports.SQS_VISIBILITY_TIMEOUT_WARNING_ID = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
5
+ const aws_sqs_1 = require("aws-cdk-lib/aws-sqs");
6
+ /**
7
+ * AWS guidance: an SQS source queue's `visibilityTimeout` should be at least
8
+ * this multiple of the consumer function's `timeout`, so Lambda has room to
9
+ * retry a throttled batch before a message becomes visible again.
10
+ *
11
+ * @see https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html
12
+ */
13
+ const SQS_VISIBILITY_TIMEOUT_MULTIPLIER = 6;
14
+ /** Lambda's own default `timeout` when none is set (CDK leaves it unset). */
15
+ const LAMBDA_DEFAULT_TIMEOUT_SECONDS = 3;
16
+ /** SQS's own default `visibilityTimeout` when none is set (applied by CloudFormation). */
17
+ const SQS_DEFAULT_VISIBILITY_TIMEOUT_SECONDS = 30;
18
+ /**
19
+ * Suppression id for the SQS visibility-timeout relationship guard. Stable and
20
+ * part of the public surface — silence the warning with
21
+ * `Annotations.of(scope).acknowledgeWarning(SQS_VISIBILITY_TIMEOUT_WARNING_ID)`,
22
+ * so it must not be renamed casually.
23
+ */
24
+ exports.SQS_VISIBILITY_TIMEOUT_WARNING_ID = "@composurecdk/lambda:sqs-visibility-timeout";
25
+ /**
26
+ * Per-kind relationship guards, keyed by {@link EventSourceKind}. A kind maps to
27
+ * the (possibly empty) list of relationships to guard for a source of that kind
28
+ * — SQS gains a second entry for the `maxReceiveCount` floor (#124), and a bare
29
+ * escape-hatch source attached as `"unknown"` has none.
30
+ *
31
+ * @internal
32
+ */
33
+ exports.EVENT_SOURCE_RELATIONSHIP_GUARDS = {
34
+ sqs: [guardSqsVisibilityTimeout],
35
+ dynamodb: [],
36
+ unknown: [],
37
+ };
38
+ /**
39
+ * The source queue behind a bound SQS event source. Safe cast: only reached for
40
+ * the `"sqs"` kind, whose source `sqsEventSource()` constructs as an
41
+ * {@link SqsEventSource} in the same call. `SqsEventSource.queue` is a public
42
+ * getter.
43
+ */
44
+ function sqsQueue(bound) {
45
+ return bound.queue;
46
+ }
47
+ /**
48
+ * Warns when the source queue's `visibilityTimeout` is below 6× the consumer
49
+ * function's `timeout`. Registers a synth-time Aspect so it reads the queue's
50
+ * *final* value off its L1 `CfnQueue` — the value the L2 `Queue` does not
51
+ * re-expose — regardless of build order or later mutation (ADR-0011).
52
+ */
53
+ function guardSqsVisibilityTimeout(fn, id, key, bound, timeout) {
54
+ const queue = sqsQueue(bound);
55
+ aws_cdk_lib_1.Aspects.of(fn).add({
56
+ visit(node) {
57
+ // The Aspect visits fn and its subtree; act once, against fn itself.
58
+ if (node !== fn)
59
+ return;
60
+ const target = targetVisibilityTimeoutSeconds(timeout);
61
+ if (target === undefined)
62
+ return; // token timeout — no concrete target to compare
63
+ const actual = readVisibilityTimeoutSeconds(queue);
64
+ if (actual === undefined || actual >= target)
65
+ return; // unknowable or compliant — stay quiet
66
+ aws_cdk_lib_1.Annotations.of(fn).addWarningV2(exports.SQS_VISIBILITY_TIMEOUT_WARNING_ID, `FunctionBuilder "${id}": SQS event source "${key}" — source queue visibilityTimeout is ` +
67
+ `${String(actual)}s but should be >= ${String(target)}s ` +
68
+ `(${String(SQS_VISIBILITY_TIMEOUT_MULTIPLIER)}x the function timeout) so Lambda can retry ` +
69
+ `a throttled batch. See https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html`);
70
+ },
71
+ });
72
+ }
73
+ /**
74
+ * The 6× target in seconds, derived from the function timeout (or Lambda's 3s
75
+ * default). `undefined` when the timeout is a token, which has no concrete
76
+ * target at synth time. Guards `isUnresolved` *before* converting, so a token
77
+ * Duration never reaches `toSeconds()`.
78
+ */
79
+ function targetVisibilityTimeoutSeconds(timeout) {
80
+ if (timeout === undefined) {
81
+ return SQS_VISIBILITY_TIMEOUT_MULTIPLIER * LAMBDA_DEFAULT_TIMEOUT_SECONDS;
82
+ }
83
+ if (timeout.isUnresolved())
84
+ return undefined;
85
+ return SQS_VISIBILITY_TIMEOUT_MULTIPLIER * timeout.toSeconds();
86
+ }
87
+ /**
88
+ * The queue's resolved `visibilityTimeout` in seconds, read off its L1
89
+ * `CfnQueue` (the L2 `Queue` does not re-expose it). `undefined` when the queue
90
+ * is imported (no L1 child) or the value is an unresolved token; `30` (the SQS
91
+ * default) when the L1 carries no explicit value.
92
+ */
93
+ function readVisibilityTimeoutSeconds(queue) {
94
+ const child = queue.node.defaultChild;
95
+ if (child === undefined ||
96
+ !aws_cdk_lib_1.CfnResource.isCfnResource(child) ||
97
+ child.cfnResourceType !== aws_sqs_1.CfnQueue.CFN_RESOURCE_TYPE_NAME) {
98
+ return undefined;
99
+ }
100
+ const value = child.visibilityTimeout;
101
+ if (value === undefined)
102
+ return SQS_DEFAULT_VISIBILITY_TIMEOUT_SECONDS;
103
+ return aws_cdk_lib_1.Token.isUnresolved(value) ? undefined : value;
104
+ }
105
+ //# sourceMappingURL=event-source-relationship-guards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-source-relationship-guards.js","sourceRoot":"","sources":["../../../src/event-sources/event-source-relationship-guards.ts"],"names":[],"mappings":";;;AAAA,6CAAsF;AAGtF,iDAA4D;AAI5D;;;;;;GAMG;AACH,MAAM,iCAAiC,GAAG,CAAC,CAAC;AAE5C,6EAA6E;AAC7E,MAAM,8BAA8B,GAAG,CAAC,CAAC;AAEzC,0FAA0F;AAC1F,MAAM,sCAAsC,GAAG,EAAE,CAAC;AAElD;;;;;GAKG;AACU,QAAA,iCAAiC,GAAG,6CAA6C,CAAC;AAiB/F;;;;;;;GAOG;AACU,QAAA,gCAAgC,GAGzC;IACF,GAAG,EAAE,CAAC,yBAAyB,CAAC;IAChC,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,EAAE;CACZ,CAAC;AAEF;;;;;GAKG;AACH,SAAS,QAAQ,CAAC,KAAmB;IACnC,OAAQ,KAAwB,CAAC,KAAK,CAAC;AACzC,CAAC;AAED;;;;;GAKG;AACH,SAAS,yBAAyB,CAChC,EAAkB,EAClB,EAAU,EACV,GAAW,EACX,KAAmB,EACnB,OAA6B;IAE7B,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9B,qBAAO,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;QACjB,KAAK,CAAC,IAAgB;YACpB,qEAAqE;YACrE,IAAI,IAAI,KAAK,EAAE;gBAAE,OAAO;YAExB,MAAM,MAAM,GAAG,8BAA8B,CAAC,OAAO,CAAC,CAAC;YACvD,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,CAAC,gDAAgD;YAElF,MAAM,MAAM,GAAG,4BAA4B,CAAC,KAAK,CAAC,CAAC;YACnD,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,IAAI,MAAM;gBAAE,OAAO,CAAC,uCAAuC;YAE7F,yBAAW,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,YAAY,CAC7B,yCAAiC,EACjC,oBAAoB,EAAE,wBAAwB,GAAG,wCAAwC;gBACvF,GAAG,MAAM,CAAC,MAAM,CAAC,sBAAsB,MAAM,CAAC,MAAM,CAAC,IAAI;gBACzD,IAAI,MAAM,CAAC,iCAAiC,CAAC,8CAA8C;gBAC3F,mFAAmF,CACtF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAS,8BAA8B,CAAC,OAA6B;IACnE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,OAAO,iCAAiC,GAAG,8BAA8B,CAAC;IAC5E,CAAC;IACD,IAAI,OAAO,CAAC,YAAY,EAAE;QAAE,OAAO,SAAS,CAAC;IAC7C,OAAO,iCAAiC,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;AACjE,CAAC;AAED;;;;;GAKG;AACH,SAAS,4BAA4B,CAAC,KAAa;IACjD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC;IACtC,IACE,KAAK,KAAK,SAAS;QACnB,CAAC,yBAAW,CAAC,aAAa,CAAC,KAAK,CAAC;QACjC,KAAK,CAAC,eAAe,KAAK,kBAAQ,CAAC,sBAAsB,EACzD,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,KAAK,GAAI,KAAkB,CAAC,iBAAiB,CAAC;IACpD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,sCAAsC,CAAC;IACvE,OAAO,mBAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC;AACvD,CAAC"}
@@ -20,16 +20,19 @@ export declare const DEFAULT_SQS_EVENT_SOURCE_PROPS: Pick<SqsEventSourceProps, "
20
20
  *
21
21
  * Applies {@link DEFAULT_SQS_EVENT_SOURCE_PROPS}; pass `props` to override.
22
22
  *
23
- * ## Cross-component invariants (not enforced)
23
+ * ## Cross-component invariants
24
24
  *
25
25
  * AWS Well-Architected guidance spans the queue and the function:
26
26
  * - the source queue's visibility timeout should be ≥ 6× the function
27
27
  * timeout, leaving room for Lambda to retry a throttled batch;
28
28
  * - the source queue's redrive `maxReceiveCount` should be ≥ 5 before the DLQ.
29
29
  *
30
- * These are not validated here — the queue often arrives as a `ref()` that is
31
- * not resolvable at configuration time. Tracked in laazyj/composureCDK#123
32
- * and #124.
30
+ * The visibility-timeout rule is enforced as a synth-time **relationship
31
+ * guard**: when this source is attached to a {@link IFunctionBuilder}, the
32
+ * builder reads the queue's resolved `visibilityTimeout` off its L1 `CfnQueue`
33
+ * (the `Queue` construct does not re-expose it) and warns, suppressibly, on an
34
+ * actual violation — see ADR-0011. The `maxReceiveCount` floor is tracked in
35
+ * laazyj/composureCDK#124.
33
36
  *
34
37
  * @param queue - The source queue, concrete or a `ref()` to a sibling.
35
38
  * @param props - Overrides for {@link DEFAULT_SQS_EVENT_SOURCE_PROPS} and any
@@ -1 +1 @@
1
- {"version":3,"file":"sqs-event-source.d.ts","sourceRoot":"","sources":["../../../src/event-sources/sqs-event-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAChG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,KAAK,oBAAoB,EAAwB,MAAM,6BAA6B,CAAC;AAE9F;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,IAAI,CAC/C,mBAAmB,EACnB,yBAAyB,GAAG,eAAe,CAkB5C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,EACzB,KAAK,CAAC,EAAE,mBAAmB,GAC1B,oBAAoB,CAMtB"}
1
+ {"version":3,"file":"sqs-event-source.d.ts","sourceRoot":"","sources":["../../../src/event-sources/sqs-event-source.ts"],"names":[],"mappings":"AAEA,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAChG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAS,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,KAAK,oBAAoB,EAAwB,MAAM,6BAA6B,CAAC;AAE9F;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,IAAI,CAC/C,mBAAmB,EACnB,yBAAyB,GAAG,eAAe,CAkB5C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,EACzB,KAAK,CAAC,EAAE,mBAAmB,GAC1B,oBAAoB,CAMtB"}
@@ -40,16 +40,19 @@ exports.DEFAULT_SQS_EVENT_SOURCE_PROPS = {
40
40
  *
41
41
  * Applies {@link DEFAULT_SQS_EVENT_SOURCE_PROPS}; pass `props` to override.
42
42
  *
43
- * ## Cross-component invariants (not enforced)
43
+ * ## Cross-component invariants
44
44
  *
45
45
  * AWS Well-Architected guidance spans the queue and the function:
46
46
  * - the source queue's visibility timeout should be ≥ 6× the function
47
47
  * timeout, leaving room for Lambda to retry a throttled batch;
48
48
  * - the source queue's redrive `maxReceiveCount` should be ≥ 5 before the DLQ.
49
49
  *
50
- * These are not validated here — the queue often arrives as a `ref()` that is
51
- * not resolvable at configuration time. Tracked in laazyj/composureCDK#123
52
- * and #124.
50
+ * The visibility-timeout rule is enforced as a synth-time **relationship
51
+ * guard**: when this source is attached to a {@link IFunctionBuilder}, the
52
+ * builder reads the queue's resolved `visibilityTimeout` off its L1 `CfnQueue`
53
+ * (the `Queue` construct does not re-expose it) and warns, suppressibly, on an
54
+ * actual violation — see ADR-0011. The `maxReceiveCount` floor is tracked in
55
+ * laazyj/composureCDK#124.
53
56
  *
54
57
  * @param queue - The source queue, concrete or a `ref()` to a sibling.
55
58
  * @param props - Overrides for {@link DEFAULT_SQS_EVENT_SOURCE_PROPS} and any
@@ -1 +1 @@
1
- {"version":3,"file":"sqs-event-source.js","sourceRoot":"","sources":["../../../src/event-sources/sqs-event-source.ts"],"names":[],"mappings":";;;AA4EA,wCASC;AApFD,uDAAoD;AACpD,mFAAgG;AAEhG,6CAA4D;AAC5D,2EAA8F;AAE9F;;;;GAIG;AACU,QAAA,8BAA8B,GAGvC;IACF;;;;;;OAMG;IACH,uBAAuB,EAAE,IAAI;IAE7B;;;;;OAKG;IACH,aAAa,EAAE,EAAE,OAAO,EAAE,CAAC,uBAAU,CAAC,WAAW,CAAC,EAAE;CACrD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,SAAgB,cAAc,CAC5B,KAAyB,EACzB,KAA2B;IAE3B,MAAM,MAAM,GAAwB,EAAE,GAAG,sCAA8B,EAAE,GAAG,KAAK,EAAE,CAAC;IACpF,MAAM,MAAM,GAA6B,IAAA,YAAK,EAAC,KAAK,CAAC;QACnD,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,yCAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC,CAAC,IAAI,yCAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACtC,OAAO,IAAA,gDAAoB,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"sqs-event-source.js","sourceRoot":"","sources":["../../../src/event-sources/sqs-event-source.ts"],"names":[],"mappings":";;;AA+EA,wCASC;AAvFD,uDAAoD;AACpD,mFAAgG;AAEhG,6CAA4D;AAC5D,2EAA8F;AAE9F;;;;GAIG;AACU,QAAA,8BAA8B,GAGvC;IACF;;;;;;OAMG;IACH,uBAAuB,EAAE,IAAI;IAE7B;;;;;OAKG;IACH,aAAa,EAAE,EAAE,OAAO,EAAE,CAAC,uBAAU,CAAC,WAAW,CAAC,EAAE;CACrD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,SAAgB,cAAc,CAC5B,KAAyB,EACzB,KAA2B;IAE3B,MAAM,MAAM,GAAwB,EAAE,GAAG,sCAA8B,EAAE,GAAG,KAAK,EAAE,CAAC;IACpF,MAAM,MAAM,GAA6B,IAAA,YAAK,EAAC,KAAK,CAAC;QACnD,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,IAAI,yCAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/D,CAAC,CAAC,IAAI,yCAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACtC,OAAO,IAAA,gDAAoB,EAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAC7C,CAAC"}