@fjall/components-infrastructure 0.96.0 → 0.99.3

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 (209) hide show
  1. package/dist/lib/app.d.ts +68 -1
  2. package/dist/lib/app.js +113 -4
  3. package/dist/lib/config/aws/__t17fixture.d.ts +1 -0
  4. package/dist/lib/config/aws/__t17fixture.js +3 -0
  5. package/dist/lib/config/aws/__t17fixtureType.d.ts +2 -0
  6. package/dist/lib/config/aws/__t17fixtureType.js +1 -0
  7. package/dist/lib/config/aws/alarmTopic.js +8 -4
  8. package/dist/lib/config/aws/cloudTrail.js +1 -1
  9. package/dist/lib/config/aws/disasterRecovery.js +11 -16
  10. package/dist/lib/config/aws/ecrDefaultImage.d.ts +0 -1
  11. package/dist/lib/config/aws/ecrDefaultImage.js +13 -23
  12. package/dist/lib/config/aws/identityCenter.d.ts +10 -3
  13. package/dist/lib/config/aws/identityCenter.js +101 -37
  14. package/dist/lib/config/aws/identityCenterGroupMembership.js +8 -2
  15. package/dist/lib/config/aws/identityCenterMembership.d.ts +11 -0
  16. package/dist/lib/config/aws/identityCenterMembership.js +61 -0
  17. package/dist/lib/config/aws/index.d.ts +1 -1
  18. package/dist/lib/config/aws/index.js +1 -1
  19. package/dist/lib/config/aws/ipam.js +6 -11
  20. package/dist/lib/config/aws/oidcConnector.js +5 -1
  21. package/dist/lib/config/aws/scpPreset.js +4 -1
  22. package/dist/lib/patterns/aws/_eslint_test_tmp/leak.d.ts +1 -0
  23. package/dist/lib/patterns/aws/_eslint_test_tmp/leak.js +4 -0
  24. package/dist/lib/patterns/aws/account.js +2 -4
  25. package/dist/lib/patterns/aws/apexDomainPattern.js +10 -10
  26. package/dist/lib/patterns/aws/bastionFactory.d.ts +10 -0
  27. package/dist/lib/patterns/aws/bastionFactory.js +29 -0
  28. package/dist/lib/patterns/aws/buildkite.d.ts +2 -2
  29. package/dist/lib/patterns/aws/buildkite.js +51 -97
  30. package/dist/lib/patterns/aws/cdn.js +1 -1
  31. package/dist/lib/patterns/aws/clickhouseDatabase.d.ts +173 -0
  32. package/dist/lib/patterns/aws/clickhouseDatabase.js +601 -0
  33. package/dist/lib/patterns/aws/compute.d.ts +4 -6
  34. package/dist/lib/patterns/aws/compute.js +7 -13
  35. package/dist/lib/patterns/aws/computeEcs.d.ts +93 -5
  36. package/dist/lib/patterns/aws/computeEcs.js +867 -37
  37. package/dist/lib/patterns/aws/computeEcsTypes.d.ts +528 -25
  38. package/dist/lib/patterns/aws/computeEcsTypes.js +10 -0
  39. package/dist/lib/patterns/aws/computeLambda.d.ts +0 -5
  40. package/dist/lib/patterns/aws/computeLambda.js +1 -2
  41. package/dist/lib/patterns/aws/database.d.ts +50 -8
  42. package/dist/lib/patterns/aws/database.js +183 -27
  43. package/dist/lib/patterns/aws/domain.js +6 -4
  44. package/dist/lib/patterns/aws/index.d.ts +1 -0
  45. package/dist/lib/patterns/aws/index.js +1 -0
  46. package/dist/lib/patterns/aws/interfaces/compute.d.ts +7 -1
  47. package/dist/lib/patterns/aws/interfaces/database.d.ts +187 -8
  48. package/dist/lib/patterns/aws/interfaces/database.js +17 -3
  49. package/dist/lib/patterns/aws/interfaces/index.d.ts +2 -1
  50. package/dist/lib/patterns/aws/interfaces/index.js +3 -1
  51. package/dist/lib/patterns/aws/interfaces/messaging.d.ts +7 -0
  52. package/dist/lib/patterns/aws/interfaces/migrationContributor.d.ts +47 -0
  53. package/dist/lib/patterns/aws/interfaces/migrationContributor.js +9 -0
  54. package/dist/lib/patterns/aws/messaging.d.ts +66 -10
  55. package/dist/lib/patterns/aws/messaging.js +115 -20
  56. package/dist/lib/patterns/aws/network.js +16 -7
  57. package/dist/lib/patterns/aws/organisation.d.ts +4 -0
  58. package/dist/lib/patterns/aws/organisation.js +22 -4
  59. package/dist/lib/patterns/aws/storage.d.ts +1 -2
  60. package/dist/lib/patterns/aws/storage.js +3 -2
  61. package/dist/lib/patterns/aws/vpcPeer.js +3 -1
  62. package/dist/lib/resources/aws/analytics/clickhouse.js +18 -9
  63. package/dist/lib/resources/aws/analytics/clickhouseAlarms.d.ts +24 -9
  64. package/dist/lib/resources/aws/analytics/clickhouseAlarms.js +61 -10
  65. package/dist/lib/resources/aws/analytics/clickhouseConstants.d.ts +3 -3
  66. package/dist/lib/resources/aws/analytics/clickhouseConstants.js +3 -3
  67. package/dist/lib/resources/aws/analytics/clickhouseTypes.d.ts +7 -1
  68. package/dist/lib/resources/aws/analytics/clickhouseUserData.d.ts +1 -1
  69. package/dist/lib/resources/aws/analytics/clickhouseUserData.js +53 -3
  70. package/dist/lib/resources/aws/base/awsStack.js +4 -2
  71. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.d.ts +2 -0
  72. package/dist/lib/resources/aws/compute/__tmp__/regression-shape.js +11 -0
  73. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.d.ts +52 -0
  74. package/dist/lib/resources/aws/compute/asgInlineLifecycleHook.js +60 -0
  75. package/dist/lib/resources/aws/compute/blockDeviceVolume.d.ts +8 -0
  76. package/dist/lib/resources/aws/compute/blockDeviceVolume.js +10 -0
  77. package/dist/lib/resources/aws/compute/ec2.d.ts +132 -12
  78. package/dist/lib/resources/aws/compute/ec2.js +163 -23
  79. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.d.ts +41 -0
  80. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.js +194 -0
  81. package/dist/lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs +458 -0
  82. package/dist/lib/resources/aws/compute/ecs.d.ts +27 -1
  83. package/dist/lib/resources/aws/compute/ecs.js +42 -2
  84. package/dist/lib/resources/aws/compute/ecsConstants.d.ts +9 -0
  85. package/dist/lib/resources/aws/compute/ecsConstants.js +16 -0
  86. package/dist/lib/resources/aws/compute/ecsImages.js +32 -20
  87. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.d.ts +96 -0
  88. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.js +113 -0
  89. package/dist/lib/resources/aws/compute/ecsNetworking.d.ts +2 -1
  90. package/dist/lib/resources/aws/compute/ecsNetworking.js +18 -6
  91. package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +13 -4
  92. package/dist/lib/resources/aws/compute/ecsServiceFactory.js +155 -33
  93. package/dist/lib/resources/aws/compute/ecsTaskDefinition.d.ts +31 -1
  94. package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +102 -6
  95. package/dist/lib/resources/aws/compute/ecsTypes.d.ts +173 -13
  96. package/dist/lib/resources/aws/compute/ecsValidation.d.ts +9 -0
  97. package/dist/lib/resources/aws/compute/ecsValidation.js +63 -0
  98. package/dist/lib/resources/aws/compute/index.d.ts +2 -0
  99. package/dist/lib/resources/aws/compute/index.js +2 -0
  100. package/dist/lib/resources/aws/compute/lambda.d.ts +7 -13
  101. package/dist/lib/resources/aws/compute/lambda.js +30 -38
  102. package/dist/lib/resources/aws/compute/lifecycleHookLambda.source.cjs +192 -0
  103. package/dist/lib/resources/aws/compute/persistentDataVolume.d.ts +104 -0
  104. package/dist/lib/resources/aws/compute/persistentDataVolume.js +245 -0
  105. package/dist/lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs +398 -0
  106. package/dist/lib/resources/aws/compute/samApplication.d.ts +15 -0
  107. package/dist/lib/resources/aws/compute/samApplication.js +27 -0
  108. package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +159 -0
  109. package/dist/lib/resources/aws/database/clickhouseConstants.js +181 -0
  110. package/dist/lib/resources/aws/database/clickhouseSchemas.d.ts +71 -0
  111. package/dist/lib/resources/aws/database/clickhouseSchemas.js +160 -0
  112. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.d.ts +14 -0
  113. package/dist/lib/resources/aws/database/clickhouseSecurityGroup.js +23 -0
  114. package/dist/lib/resources/aws/database/clickhouseUserData.d.ts +69 -0
  115. package/dist/lib/resources/aws/database/clickhouseUserData.js +371 -0
  116. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.d.ts +56 -0
  117. package/dist/lib/resources/aws/database/clickhouseXmlRenderer.js +112 -0
  118. package/dist/lib/resources/aws/database/rdsAurora.d.ts +8 -1
  119. package/dist/lib/resources/aws/database/rdsAurora.js +42 -32
  120. package/dist/lib/resources/aws/database/rdsAuroraGlobal.d.ts +15 -2
  121. package/dist/lib/resources/aws/database/rdsAuroraGlobal.js +39 -43
  122. package/dist/lib/resources/aws/database/rdsDefaults.d.ts +6 -0
  123. package/dist/lib/resources/aws/database/rdsDefaults.js +7 -1
  124. package/dist/lib/resources/aws/database/rdsHelpers.d.ts +3 -3
  125. package/dist/lib/resources/aws/database/rdsHelpers.js +1 -0
  126. package/dist/lib/resources/aws/database/rdsInstance.d.ts +8 -1
  127. package/dist/lib/resources/aws/database/rdsInstance.js +51 -34
  128. package/dist/lib/resources/aws/database/rdsProxyOutput.d.ts +1 -1
  129. package/dist/lib/resources/aws/database/rdsProxyOutput.js +1 -1
  130. package/dist/lib/resources/aws/iam/delegationRole.js +1 -1
  131. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.d.ts +9 -0
  132. package/dist/lib/resources/aws/iam/identityCenter/groupMembership.js +12 -0
  133. package/dist/lib/resources/aws/iam/identityCenter/index.d.ts +1 -0
  134. package/dist/lib/resources/aws/iam/identityCenter/index.js +1 -0
  135. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.d.ts +1 -0
  136. package/dist/lib/resources/aws/iam/identityCenter/permissionSet.js +1 -0
  137. package/dist/lib/resources/aws/logging/logGroup.d.ts +0 -8
  138. package/dist/lib/resources/aws/logging/logGroup.js +0 -11
  139. package/dist/lib/resources/aws/messaging/defaultEventBus.d.ts +7 -0
  140. package/dist/lib/resources/aws/messaging/defaultEventBus.js +21 -0
  141. package/dist/lib/resources/aws/messaging/eventBridgeRule.d.ts +96 -0
  142. package/dist/lib/resources/aws/messaging/eventBridgeRule.js +110 -0
  143. package/dist/lib/resources/aws/messaging/eventTargets.d.ts +84 -0
  144. package/dist/lib/resources/aws/messaging/eventTargets.js +152 -0
  145. package/dist/lib/resources/aws/messaging/eventbridge.d.ts +25 -2
  146. package/dist/lib/resources/aws/messaging/eventbridge.js +22 -10
  147. package/dist/lib/resources/aws/messaging/index.d.ts +5 -0
  148. package/dist/lib/resources/aws/messaging/index.js +2 -0
  149. package/dist/lib/resources/aws/messaging/schedule.d.ts +118 -0
  150. package/dist/lib/resources/aws/messaging/schedule.js +64 -0
  151. package/dist/lib/resources/aws/messaging/sns.d.ts +2 -1
  152. package/dist/lib/resources/aws/messaging/sqs.d.ts +2 -1
  153. package/dist/lib/resources/aws/messaging/subscription.d.ts +112 -0
  154. package/dist/lib/resources/aws/messaging/subscription.js +67 -0
  155. package/dist/lib/resources/aws/messaging/utils.d.ts +6 -0
  156. package/dist/lib/resources/aws/messaging/utils.js +10 -0
  157. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.d.ts +60 -0
  158. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.js +139 -0
  159. package/dist/lib/resources/aws/monitoring/index.d.ts +2 -0
  160. package/dist/lib/resources/aws/monitoring/index.js +2 -0
  161. package/dist/lib/resources/aws/monitoring/scheduleAlarms.d.ts +47 -0
  162. package/dist/lib/resources/aws/monitoring/scheduleAlarms.js +106 -0
  163. package/dist/lib/resources/aws/networking/crossAccountDelegationRecord.js +6 -4
  164. package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.js +17 -13
  165. package/dist/lib/resources/aws/networking/dnsRecord/dnsRecordBase.js +7 -5
  166. package/dist/lib/resources/aws/networking/domainCertificate.d.ts +2 -2
  167. package/dist/lib/resources/aws/networking/domainCertificate.js +6 -4
  168. package/dist/lib/resources/aws/networking/hostedZone.js +6 -5
  169. package/dist/lib/resources/aws/networking/serviceDiscovery.d.ts +96 -0
  170. package/dist/lib/resources/aws/networking/serviceDiscovery.js +96 -0
  171. package/dist/lib/resources/aws/networking/vpc.d.ts +4 -1
  172. package/dist/lib/resources/aws/networking/vpc.js +4 -1
  173. package/dist/lib/resources/aws/networking/vpcPeeringConnection.js +21 -3
  174. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.d.ts +16 -5
  175. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.js +17 -3
  176. package/dist/lib/resources/aws/organisation/index.d.ts +1 -1
  177. package/dist/lib/resources/aws/organisation/organisationPolicy.d.ts +2 -0
  178. package/dist/lib/resources/aws/organisation/organisationPolicy.js +3 -2
  179. package/dist/lib/resources/aws/secrets/secret.d.ts +7 -0
  180. package/dist/lib/resources/aws/secrets/secret.js +4 -3
  181. package/dist/lib/resources/aws/storage/bucketDeployment.d.ts +16 -0
  182. package/dist/lib/resources/aws/storage/bucketDeployment.js +17 -0
  183. package/dist/lib/resources/aws/storage/ecr.js +5 -5
  184. package/dist/lib/resources/aws/storage/index.d.ts +1 -0
  185. package/dist/lib/resources/aws/storage/index.js +1 -0
  186. package/dist/lib/resources/aws/storage/s3.js +10 -3
  187. package/dist/lib/resources/aws/utilities/customResource.js +18 -9
  188. package/dist/lib/synth_dump.d.ts +1 -0
  189. package/dist/lib/synth_dump.js +42 -0
  190. package/dist/lib/utils/cdkContext.d.ts +2 -0
  191. package/dist/lib/utils/cdkContext.js +4 -2
  192. package/dist/lib/utils/connections.js +6 -0
  193. package/dist/lib/utils/connector.d.ts +12 -0
  194. package/dist/lib/utils/costAllocationTags.d.ts +9 -0
  195. package/dist/lib/utils/costAllocationTags.js +11 -1
  196. package/dist/lib/utils/databaseTypes.d.ts +14 -0
  197. package/dist/lib/utils/getConfig.d.ts +2 -0
  198. package/dist/lib/utils/getConfig.js +2 -0
  199. package/dist/lib/utils/index.d.ts +1 -0
  200. package/dist/lib/utils/index.js +1 -0
  201. package/dist/lib/utils/manifestWriter.d.ts +6 -89
  202. package/dist/lib/utils/manifestWriter.js +36 -23
  203. package/dist/lib/utils/migrationVersionResolvers.d.ts +2 -0
  204. package/dist/lib/utils/migrationVersionResolvers.js +2 -0
  205. package/dist/lib/utils/orgConfigParser.js +2 -1
  206. package/dist/lib/utils/resolveAlertsTopic.d.ts +14 -0
  207. package/dist/lib/utils/resolveAlertsTopic.js +30 -0
  208. package/dist/lib/utils/validationLogger.js +6 -3
  209. package/package.json +22 -19
@@ -1,14 +1,16 @@
1
1
  import { type Repository } from "aws-cdk-lib/aws-ecr";
2
- import { type RepositoryImage } from "aws-cdk-lib/aws-ecs";
3
- import { type IVpc } from "aws-cdk-lib/aws-ec2";
4
- import { type PolicyDocument, type IManagedPolicy } from "aws-cdk-lib/aws-iam";
2
+ import { type RepositoryImage, type ContainerImage, type PortMapping, type Secret as EcsSecret, type NetworkMode } from "aws-cdk-lib/aws-ecs";
3
+ import { type IPeer, type ISecurityGroup, type IVpc, type Port, type SubnetSelection } from "aws-cdk-lib/aws-ec2";
4
+ import { type ILogGroup, type RetentionDays } from "aws-cdk-lib/aws-logs";
5
+ import { type PolicyDocument, type IManagedPolicy, type PolicyStatement } from "aws-cdk-lib/aws-iam";
5
6
  import type { ITopic } from "aws-cdk-lib/aws-sns";
6
7
  import { type ConnectionSpec } from "./interfaces/connector.js";
7
8
  import { type RemoteConnectionSpec } from "../../resources/aws/compute/ecsRemoteConnections.js";
8
- import { type EcsRoutingConfig } from "../../resources/aws/compute/ecsTypes.js";
9
+ import { type EcsRoutingConfig, type EcsContainerDependency } from "../../resources/aws/compute/ecsTypes.js";
9
10
  import { ScalingType, type DomainConfig, type EcsCapacityProvider, type Ec2CapacityConfig } from "../../resources/aws/compute/ecs.js";
10
11
  import type { EcsServiceAlarmThresholds } from "../../resources/aws/monitoring/index.js";
11
12
  import { type SecretImport } from "../../resources/aws/secrets/index.js";
13
+ import type { DockerBuild } from "@fjall/util/manifest/schemas";
12
14
  export type { RemoteConnectionSpec };
13
15
  export { ScalingType };
14
16
  export type { EcsCapacityProvider, Ec2CapacityConfig };
@@ -21,6 +23,22 @@ export interface EcsCapacityProviderConfig {
21
23
  /** Whether this runs on EC2 instances (vs serverless Fargate) */
22
24
  usesEc2Instances: boolean;
23
25
  }
26
+ /**
27
+ * A dependency on another container in the same task definition.
28
+ * Maps directly to ECS `ContainerDependency` and the `addContainerDependencies`
29
+ * CDK API. `condition` corresponds to ECS's `ContainerDependencyCondition`:
30
+ *
31
+ * - `START` — dependency must have entered the running state
32
+ * - `COMPLETE` — dependency must have exited (any code); only valid for non-essential containers
33
+ * - `SUCCESS` — dependency must have exited 0; only valid for non-essential containers
34
+ * - `HEALTHY` — dependency must have passed its health check
35
+ *
36
+ * Public-facing alias for the canonical resource-layer `EcsContainerDependency`.
37
+ * Re-exported here so factory consumers can import it from the patterns barrel.
38
+ *
39
+ * @see https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDependency.html
40
+ */
41
+ export type ContainerDependency = EcsContainerDependency;
24
42
  /**
25
43
  * Configuration for a container in an ECS task.
26
44
  *
@@ -38,9 +56,20 @@ export interface EcsCapacityProviderConfig {
38
56
  * { name: "app", port: 3000 }, // Primary - receives ALB traffic
39
57
  * { name: "datadog", image: "datadog/agent" } // Sidecar - monitoring
40
58
  * ]
59
+ *
60
+ * @example
61
+ * // Init container with explicit dependsOn
62
+ * containers: [
63
+ * { name: "migrate", essential: false, command: ["npx", "payload", "migrate"] },
64
+ * { name: "app", port: 3000, dependsOn: [{ container: "migrate", condition: "SUCCESS" }] }
65
+ * ]
41
66
  */
42
67
  export interface EcsContainerConfig {
43
- /** Container name. Optional for single-container services. */
68
+ /**
69
+ * Container name. Optional for single-container services.
70
+ * Required when this container is referenced by another's `dependsOn`,
71
+ * or when the `migrations` sugar would otherwise collide with this name.
72
+ */
44
73
  name?: string;
45
74
  /**
46
75
  * Container image. Options:
@@ -90,19 +119,383 @@ export interface EcsContainerConfig {
90
119
  retries?: number;
91
120
  startPeriod?: number;
92
121
  };
122
+ /**
123
+ * Containers in the same service that must reach a given state before this
124
+ * container starts. Resolved at synth time — referenced container names must
125
+ * match another container's `name` field in the same service.
126
+ *
127
+ * @example
128
+ * dependsOn: [{ container: "migrate", condition: "SUCCESS" }]
129
+ */
130
+ dependsOn?: ContainerDependency[];
131
+ /**
132
+ * Multi-port containers. CDK `PortMapping[]` verbatim. Mutually exclusive
133
+ * with `port` — supplying both throws a synth-time error (AC30).
134
+ */
135
+ portMappings?: PortMapping[];
136
+ /**
137
+ * Host-bind volumes mounted into this container. Each entry produces a
138
+ * matching `taskDefinition.addVolume(...)` + `container.addMountPoints(...)`
139
+ * pair (AC31).
140
+ */
141
+ volumes?: ContainerVolume[];
142
+ /**
143
+ * Time (seconds) ECS waits for the container to exit gracefully after
144
+ * sending SIGTERM before sending SIGKILL. Range: 1–120.
145
+ *
146
+ * Stateful containers (databases, queues mid-flush, anything with on-disk
147
+ * state) generally need longer than the CDK default (30s) to flush buffers,
148
+ * close connections, and persist state. Raise this when SIGTERM-to-SIGKILL
149
+ * inside the default window risks data loss or corruption (e.g. ClickHouse
150
+ * draining merges, Postgres flushing WAL).
151
+ *
152
+ * @default 30 (ECS default; CDK omits the field when unset)
153
+ */
154
+ stopTimeout?: number;
155
+ }
156
+ /**
157
+ * How the migration command is executed during deployment.
158
+ *
159
+ * - `"init-container"` (default) — synthesises a non-essential init container
160
+ * in the service's task definition that runs to completion before any other
161
+ * container starts. Every replica task pays the migration startup cost, but
162
+ * the migration runs inside the same task family so the deployment cannot
163
+ * move forward until migrations succeed. Recommended for ≤10-replica
164
+ * services and any service whose `migrations.command` does work other than
165
+ * migrations (seeding, cache warming, environment validation).
166
+ * - `"lifecycle-hook"` — runs the migration once per deployment via an ECS
167
+ * `PRE_SCALE_UP` deployment lifecycle hook (AWS, September 2025). The hook
168
+ * invokes a Lambda that launches the migration as a one-off task; the new
169
+ * replica tasks scale up only after the hook reports `SUCCEEDED`. Cuts
170
+ * per-replica startup latency at the cost of an extra Lambda + IAM role
171
+ * per service. Suitable for high-replica services where the init-container
172
+ * tax dominates rollout time.
173
+ *
174
+ * @see aiDocs/decisions/2026-04-29-ecs-init-container-over-runtask.md
175
+ * @see aiDocs/plans/2026-04-29-ecs-factory-hardening.md § 4.3.4 — threshold
176
+ * guidance for choosing between modes.
177
+ * @see https://aws.amazon.com/blogs/containers/announcing-amazon-ecs-deployment-lifecycle-hooks/
178
+ */
179
+ export type EcsMigrationsMode = "init-container" | "lifecycle-hook" | "post-deploy";
180
+ /**
181
+ * Shorthand for the canonical "run migrations before the app starts" pattern.
182
+ *
183
+ * Default mode (`"init-container"`) synthesises a non-essential init container
184
+ * that runs to completion before any other container in the service starts.
185
+ * Optional `mode: "lifecycle-hook"` shifts the migration to an ECS deployment
186
+ * lifecycle hook — see {@link EcsMigrationsMode} for the trade-off.
187
+ *
188
+ * Defaults inherit from the primary container (first container with a `port`,
189
+ * or the first container if none have a port):
190
+ *
191
+ * - `image` → primary's `image`
192
+ * - `environment` → primary's `environment`
193
+ * - `secrets` → primary's `secrets` (SSM Parameter Store)
194
+ * - `secretsImport` → primary's `secretsImport` (Secrets Manager)
195
+ *
196
+ * Idempotency is the migration tool's responsibility — Payload, Prisma, Drizzle,
197
+ * Rails, Django, and Flyway are all idempotent by default.
198
+ *
199
+ * @example
200
+ * migrations: { command: ["npx", "payload", "migrate"] }
201
+ *
202
+ * @example
203
+ * migrations: {
204
+ * command: ["prisma", "migrate", "deploy"],
205
+ * timeoutSeconds: 600
206
+ * }
207
+ *
208
+ * @example
209
+ * // High-replica service: shift migration off the per-task startup path
210
+ * migrations: {
211
+ * command: ["npx", "payload", "migrate"],
212
+ * mode: "lifecycle-hook"
213
+ * }
214
+ *
215
+ * @see aiDocs/decisions/2026-04-29-ecs-init-container-over-runtask.md
216
+ */
217
+ /**
218
+ * Discriminated on `mode`. The init-container variant keeps its narrow surface
219
+ * (no risk of authors setting CPU/memory/IAM overrides that would silently
220
+ * apply to the per-replica path). The lifecycle-hook variant carries `entryPoint?`
221
+ * and an opt-in `separateTaskDef?` sub-object for cross-cutting migrations
222
+ * that need permissions the service doesn't (e.g. RDS snapshot grants,
223
+ * cross-service Secrets Manager reads).
224
+ *
225
+ * Setting `separateTaskDef` on the init-container variant — or any
226
+ * lifecycle-hook-only field — is a typecheck error. This protects against
227
+ * the Pitfall-5 trap (silent field drift across union variants).
228
+ *
229
+ * @see aiDocs/designs/2026-05-12-migration-runner-abstraction-choice.md
230
+ * @see .claude/rules/typescript-standards.md § "Pitfall 5"
231
+ */
232
+ export type EcsMigrationsConfig = EcsInitContainerMigrationsConfig | EcsLifecycleHookMigrationsConfig | EcsPostDeployMigrationsConfig;
233
+ /**
234
+ * Union of the two lambda-driven deployment hook variants. Internal helper
235
+ * type — narrows from `EcsMigrationsConfig` once `expandMigrationsSugar` has
236
+ * established the migration runs out-of-band via a lifecycle hook (vs as an
237
+ * init container).
238
+ */
239
+ export type EcsHookMigrationsConfig = EcsLifecycleHookMigrationsConfig | EcsPostDeployMigrationsConfig;
240
+ /**
241
+ * Type-narrowing predicate for the two lambda-driven hook variants. Returns
242
+ * true for `"lifecycle-hook"` (PRE_SCALE_UP) and `"post-deploy"` (POST_SCALE_UP).
243
+ * Narrows the whole `EcsMigrationsConfig` to the hook-variant union, not just
244
+ * the `mode` property, so callers can read `separateTaskDef` / `entryPoint`
245
+ * etc. on the narrowed object.
246
+ */
247
+ export declare function isHookMigrations(config: EcsMigrationsConfig): config is EcsHookMigrationsConfig;
248
+ /**
249
+ * `mode: "init-container"` variant — migration runs as a non-essential init
250
+ * container inside each replica's task, dependsOn `COMPLETE` before main starts.
251
+ * `entryPoint` and `separateTaskDef` are intentionally absent — the per-replica
252
+ * init-container path has no separate IAM role to grant against.
253
+ */
254
+ export interface EcsInitContainerMigrationsConfig {
255
+ /**
256
+ * Where the migration command runs during deployment. Defaults to
257
+ * `"init-container"`. Either omit (defaults) or set explicitly.
258
+ */
259
+ mode?: "init-container";
260
+ /** Migration command, e.g. `["npx", "payload", "migrate"]`. Required. */
261
+ command: string[];
262
+ /** Override timeout in seconds. Defaults to 300 (5 min). */
263
+ timeoutSeconds?: number;
264
+ /** Override image. Defaults to inheriting from the primary container. */
265
+ image?: string | Repository;
266
+ /** Override environment. Defaults to inheriting from the primary container. */
267
+ environment?: Record<string, string>;
268
+ /** Override Secrets-Manager imports. Defaults to inheriting from the primary container. */
269
+ secretsImport?: Record<string, SecretImport>;
270
+ /** Override SSM secret keys. Defaults to inheriting from the primary container. */
271
+ secrets?: string[];
272
+ /**
273
+ * Synthetic container name. Defaults to `"migrate"`.
274
+ * Override when a service container is already named `migrate`.
275
+ */
276
+ name?: string;
277
+ }
278
+ /**
279
+ * `mode: "lifecycle-hook"` variant — migration runs out-of-band via an ECS
280
+ * deployment lifecycle hook (PRE_SCALE_UP). The service rollout BLOCKS until
281
+ * the migration succeeds; one task per deploy.
282
+ *
283
+ * When `separateTaskDef` is present, `wireLifecycleHookMigrations` synthesises
284
+ * a SEPARATE migration task definition (its own IAM role, log group, runtime
285
+ * platform) instead of using `ContainerOverrides` on the service task def.
286
+ * Required when migrations need permissions the service doesn't.
287
+ */
288
+ export interface EcsLifecycleHookMigrationsConfig {
289
+ /** Required for the lifecycle-hook variant. */
290
+ mode: "lifecycle-hook";
291
+ /** Migration command, e.g. `["node", "scripts/migration-runner.mjs"]`. Required. */
292
+ command: string[];
293
+ /** Override timeout in seconds. Defaults to 300 (5 min). Capped at 900s per Lambda invocation; longer migrations use the IN_PROGRESS callback chain. */
294
+ timeoutSeconds?: number;
295
+ /**
296
+ * Override image URI (string only). CDK `Repository` constructs cannot be
297
+ * JSON-serialised into the runner Lambda's `MIGRATE_CONFIG` env var; synth
298
+ * throws if a `Repository` is passed in lifecycle-hook mode.
299
+ */
300
+ image?: string;
301
+ /** Override environment. */
302
+ environment?: Record<string, string>;
303
+ /** Secrets-Manager imports for the migration container. */
304
+ secretsImport?: Record<string, SecretImport>;
305
+ /** SSM Parameter Store secret keys. */
306
+ secrets?: string[];
307
+ /** Synthetic container name. Defaults to `"migrate"`. */
308
+ name?: string;
309
+ /**
310
+ * Container entrypoint. Defaults to inheriting from the image. Set to
311
+ * `["/usr/bin/tini", "--"]` (or similar) to keep PID-1 signalling clean
312
+ * inside long-running migration containers.
313
+ */
314
+ entryPoint?: string[];
315
+ /**
316
+ * When present, synthesise a SEPARATE migration task definition (its own
317
+ * IAM role, log group, runtime platform) instead of using `ContainerOverrides`
318
+ * on the service task def. Required when migrations need permissions the
319
+ * service doesn't (e.g. `rds:CreateDBSnapshot`, cross-service Secrets Manager
320
+ * reads) — preserves least-privilege on the service task role.
321
+ *
322
+ * When absent, the lifecycle-hook runner uses the service's task def via
323
+ * ContainerOverrides — existing behaviour, unchanged.
324
+ */
325
+ separateTaskDef?: EcsLifecycleHookMigrationsSeparateTaskDef;
326
+ }
327
+ /**
328
+ * `mode: "post-deploy"` variant — runs out-of-band via an ECS deployment
329
+ * lifecycle hook on the POST_SCALE_UP stage. Fires AFTER the new task revision
330
+ * has scaled up and is healthy. A `FAILED` return from the hook triggers ECS
331
+ * deployment circuit-breaker rollback to the previous revision.
332
+ *
333
+ * Use cases: post-deploy data backfills that should run against the new code
334
+ * (not gate the rollout), cache warming, smoke tests that exercise the live
335
+ * service, sanity-check migrations that don't need to block traffic shift.
336
+ * Do NOT use for schema migrations that the new code depends on — use the
337
+ * pre-deploy `mode: "lifecycle-hook"` variant for those.
338
+ *
339
+ * Identical input shape to the lifecycle-hook variant; the only difference is
340
+ * the stage at which AWS fires the hook.
341
+ */
342
+ export interface EcsPostDeployMigrationsConfig {
343
+ /** Required for the post-deploy variant. */
344
+ mode: "post-deploy";
345
+ /** Migration/backfill command. Required. */
346
+ command: string[];
347
+ /** Override timeout in seconds. Defaults to 300 (5 min). Capped at 900s per Lambda invocation. */
348
+ timeoutSeconds?: number;
349
+ /** Override image URI (string only). */
350
+ image?: string;
351
+ /** Override environment. */
352
+ environment?: Record<string, string>;
353
+ /** Secrets-Manager imports for the migration container. */
354
+ secretsImport?: Record<string, SecretImport>;
355
+ /** SSM Parameter Store secret keys. */
356
+ secrets?: string[];
357
+ /** Synthetic container name. Defaults to `"migrate"`. */
358
+ name?: string;
359
+ /** Container entrypoint. Defaults to inheriting from the image. */
360
+ entryPoint?: string[];
361
+ /**
362
+ * When present, synthesise a SEPARATE migration task definition. Same
363
+ * semantics as `EcsLifecycleHookMigrationsConfig.separateTaskDef`.
364
+ */
365
+ separateTaskDef?: EcsLifecycleHookMigrationsSeparateTaskDef;
366
+ }
367
+ /**
368
+ * Sub-config for `EcsLifecycleHookMigrationsConfig.separateTaskDef`. The
369
+ * migration container is parameterised exactly like every other container via
370
+ * the same `<ServiceName>ImageTag` CfnParameter — task-def revisions roll over
371
+ * whenever the parameter value changes.
372
+ */
373
+ export interface EcsLifecycleHookMigrationsSeparateTaskDef {
374
+ /** Fargate task CPU (256, 512, 1024, 2048, 4096, 8192, 16384). */
375
+ cpu: number;
376
+ /** Task memory in MiB — must be compatible with `cpu` per the AWS Fargate task-size matrix. */
377
+ memoryLimitMiB: number;
378
+ /** Extra IAM statements appended to the migration task role only. */
379
+ taskRolePolicies?: PolicyStatement[];
380
+ /**
381
+ * Extra SG egress for the migration task. Omit to reuse the service's
382
+ * security group(s). Use this when the migration reaches a peer the
383
+ * service itself doesn't talk to.
384
+ */
385
+ egressTo?: Array<{
386
+ peer: IPeer;
387
+ port: Port;
388
+ description: string;
389
+ }>;
390
+ /**
391
+ * Runtime Secrets Manager reads — typed sugar that grants the migration task
392
+ * role `secretsmanager:GetSecretValue` on the named secrets, scoped to the
393
+ * specific ARNs (never `*`).
394
+ *
395
+ * Different from `secretsImport`: `secretsImport` mounts a secret value as
396
+ * an env var at container start via the ECS agent (execution role permission).
397
+ * `extraSecretReads` is for migrations that fetch secret values dynamically
398
+ * at runtime via the AWS SDK — e.g. when the secret name is computed inside
399
+ * the migration script, or when the migration writes back into Secrets
400
+ * Manager after rotation.
401
+ *
402
+ * Equivalent to passing the same statement via `taskRolePolicies`; this form
403
+ * is preferred for readability.
404
+ */
405
+ extraSecretReads?: Array<{
406
+ /** Secrets Manager secret name or ARN. */
407
+ secretName: string;
408
+ /** Optional human-facing description used in IAM statement Sid. */
409
+ description?: string;
410
+ }>;
411
+ }
412
+ /**
413
+ * Deployment circuit breaker policy for an ECS service.
414
+ *
415
+ * The circuit breaker watches a new deployment and, on detecting persistent
416
+ * task launch failures, marks the deployment as failed. With `rollback: true`
417
+ * the service automatically returns to the previous stable task definition.
418
+ *
419
+ * Defaults to `{ rollback: true }` when omitted — matches every example in
420
+ * AWS's own circuit-breaker documentation. CDK's default is `rollback: false`;
421
+ * we override to the safer shape.
422
+ *
423
+ * Set `circuitBreaker: false` to disable the breaker entirely (not recommended
424
+ * for production services).
425
+ *
426
+ * Set `circuitBreaker: { rollback: false }` to enable detection without
427
+ * automatic rollback — useful for first deploys where there is no prior
428
+ * baseline to roll back to.
429
+ *
430
+ * @see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-circuit-breaker.html
431
+ */
432
+ export interface EcsCircuitBreakerConfig {
433
+ /**
434
+ * Roll back to the previous stable deployment when the circuit breaker
435
+ * trips. Defaults to `true`.
436
+ */
437
+ rollback?: boolean;
93
438
  }
94
439
  /**
95
440
  * ECS scaling configuration.
96
- * - Omit: enabled with defaults
97
- * - `{}`: enabled with defaults
98
- * - `{ minCapacity: 2, maxCapacity: 10 }`: custom scaling
441
+ * - Omit: enabled, `minCapacity` tracks `desiredCount`, `maxCapacity` defaults to `Math.max(desiredCount + 1, 3)`
442
+ * - `{}`: same as omit
443
+ * - `{ minCapacity: 2, maxCapacity: 10 }`: custom scaling (explicit values win)
99
444
  * - `false`: explicitly disabled
445
+ *
446
+ * Setting `minCapacity > 0` while `desiredCount: 0` throws at synth — Application
447
+ * Auto Scaling would immediately scale the service back up, defeating the toggle.
100
448
  */
101
449
  export interface EcsScalingConfig {
102
450
  minCapacity?: number;
103
451
  maxCapacity?: number;
104
452
  scalingType?: ScalingType;
105
453
  }
454
+ /**
455
+ * Host-bind volume mounted into one or more containers in the same task.
456
+ * Maps to ECS `Volume` + `MountPoint` pairs — see AC29 + AC31.
457
+ */
458
+ export interface ContainerVolume {
459
+ /** Volume name. Used as `sourceVolume` on the container's mount point. */
460
+ name: string;
461
+ /**
462
+ * Host path bound into the container. Omit for an empty Docker volume that
463
+ * lives only for the task's lifetime (no host directory mount).
464
+ */
465
+ hostSourcePath?: string;
466
+ /** Path inside the container the volume is mounted at. */
467
+ mountPath: string;
468
+ /** Mount the volume read-only. Default: false. */
469
+ readOnly?: boolean;
470
+ }
471
+ /**
472
+ * Scheduled-task entry on the cluster. Each entry produces one
473
+ * `Ec2TaskDefinition` registered under the synthetic key `name` in
474
+ * `EcsCompute`'s task-definition map (collision-checked against steady-state
475
+ * service names) and an `app.addSchedule(...)` invocation. See AC27 + AC34.
476
+ */
477
+ export interface EcsScheduledTaskConfig {
478
+ /** Synthetic name. Must not collide with any steady-state `serviceName`. */
479
+ name: string;
480
+ /** Cron or rate expression — passed verbatim to `app.addSchedule`. */
481
+ schedule: string;
482
+ /** CDK `ContainerImage` — typically `ContainerImage.fromRegistry(...)`. */
483
+ image: ContainerImage;
484
+ cpu: number;
485
+ memoryLimitMiB: number;
486
+ command?: string[];
487
+ /** Container secrets (Secrets Manager / SSM) — same shape as ECS `secrets`. */
488
+ secrets?: Record<string, EcsSecret>;
489
+ /** Pre-existing CDK log group. Mutually exclusive with `logRetention`. */
490
+ logGroup?: ILogGroup;
491
+ /** When `logGroup` is omitted, the awsLogs driver creates one with this retention. */
492
+ logRetention?: RetentionDays;
493
+ securityGroups?: ISecurityGroup[];
494
+ subnetSelection?: SubnetSelection;
495
+ networkMode?: NetworkMode;
496
+ /** Host-bind volumes mounted into the task's container. */
497
+ volumes?: ContainerVolume[];
498
+ }
106
499
  /**
107
500
  * Cluster-level configuration.
108
501
  * Controls the shared ALB for all services in this cluster.
@@ -133,6 +526,19 @@ export interface EcsClusterConfig {
133
526
  * Allows for multi-region deployments with advanced DNS routing.
134
527
  */
135
528
  domainConfig?: DomainConfig;
529
+ /**
530
+ * Externally-supplied security group for the cluster's EC2 capacity. When
531
+ * provided, the ECS service factory wires this SG onto the underlying
532
+ * `Ec2Instance` instead of creating its own. Used by stateful workloads
533
+ * (e.g. ClickHouseDatabase) that own their security group lifecycle.
534
+ */
535
+ securityGroup?: ISecurityGroup;
536
+ /**
537
+ * Scheduled tasks materialised into `Ec2TaskDefinition`s and registered via
538
+ * `app.addSchedule(...)` against the existing `EcsScheduleTarget` shape
539
+ * ({ ecs, serviceName, taskCount }). See AC34.
540
+ */
541
+ scheduledTasks?: EcsScheduledTaskConfig[];
136
542
  }
137
543
  export type { EcsRoutingConfig };
138
544
  /**
@@ -207,21 +613,14 @@ export interface EcsServiceConfig {
207
613
  */
208
614
  scaling?: EcsScalingConfig | false;
209
615
  /**
210
- * Path to Dockerfile for building this service's image.
211
- * Metadata for CLI build process, not used during CDK synthesis.
212
- */
213
- dockerfilePath?: string;
214
- /**
215
- * Docker build target stage for multi-stage Dockerfiles.
216
- * When specified, the CLI builds with `--target <dockerTarget>`.
217
- * The image tag suffix is also updated: `<service>-<target>-latest`.
616
+ * Dockerfile build configuration for this service. Carries the path
617
+ * (absolute or relative), an optional build `context` for monorepo
618
+ * layouts, and an optional multi-stage `target`.
218
619
  *
219
- * @example
220
- * // Dockerfile: FROM node AS base ... FROM base AS api ... FROM base AS worker
221
- * { name: "api", dockerTarget: "api" } // builds: myapp-api-api-latest
222
- * { name: "worker", dockerTarget: "worker" } // builds: myapp-worker-worker-latest
620
+ * Tagged image suffix when `target` is set: `<service>-<target>-latest`.
621
+ * Mutually exclusive with `image` (pre-built URI).
223
622
  */
224
- dockerTarget?: string;
623
+ docker?: DockerBuild;
225
624
  /**
226
625
  * Additional inline policies for this service's task role.
227
626
  * Added on top of the default ECS Exec permissions.
@@ -259,6 +658,21 @@ export interface EcsServiceConfig {
259
658
  * ]
260
659
  */
261
660
  connections?: ConnectionSpec[];
661
+ /**
662
+ * Schema-version gate opt-out. Defaults `true`.
663
+ *
664
+ * When any database in `connections` carries a `migrations` block, every
665
+ * container in this service receives `EXPECTED_SCHEMA_VERSION` resolved
666
+ * from that database's migration tool at synth time. Set `false` to skip
667
+ * the injection — intended for sidecars that intentionally tolerate
668
+ * schema drift or one-shot maintenance tasks.
669
+ *
670
+ * Named opt-out is auditable: grep-recoverable across the monorepo. Note
671
+ * the spelling collision with `@fjall/generator`'s codemod pipeline gate
672
+ * at `validationGate/gates/schema.ts` — different surface, different
673
+ * concept.
674
+ */
675
+ schemaGate?: boolean;
262
676
  /**
263
677
  * Cross-app resources reachable via VPC peering. Each entry resolves the
264
678
  * peered app's exposed resource at synth time (SSM `valueForStringParameter`
@@ -309,6 +723,96 @@ export interface EcsServiceConfig {
309
723
  * - object: override specific thresholds
310
724
  */
311
725
  alarms?: EcsServiceAlarmThresholds | false;
726
+ /**
727
+ * Run an init container before any other container in this service starts.
728
+ * Synthesises a non-essential container with the given migration command,
729
+ * inherits image / env / secrets from the primary container, and auto-wires
730
+ * every other container to wait on `SUCCESS`.
731
+ *
732
+ * @example
733
+ * migrations: { command: ["npx", "payload", "migrate"] }
734
+ */
735
+ migrations?: EcsMigrationsConfig;
736
+ /**
737
+ * Deployment circuit breaker policy. Omit for the safe default
738
+ * `{ enable: true, rollback: true }` — failed deployments automatically
739
+ * roll back to the previous stable task definition.
740
+ *
741
+ * Set to `false` to disable the breaker entirely. Set to
742
+ * `{ rollback: false }` for detection without automatic rollback (useful
743
+ * for first deploys where there is no prior baseline).
744
+ *
745
+ * @see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/deployment-circuit-breaker.html
746
+ */
747
+ circuitBreaker?: false | EcsCircuitBreakerConfig;
748
+ /**
749
+ * Rolling-deploy capacity bounds (percent of `desiredCount`).
750
+ *
751
+ * - `minHealthyPercent` (0–100): floor of healthy tasks during a deploy.
752
+ * `0` allows full task drain before the new task starts (recreate
753
+ * semantics — required for singletons backed by an EBS volume that only
754
+ * one task can attach to at a time, e.g. ClickHouse).
755
+ * - `maxHealthyPercent` (100–200): ceiling of running tasks during a
756
+ * deploy. `100` forbids overlap (matches the singleton/recreate shape).
757
+ * The default `200` allows a fresh task to start before the old one
758
+ * drains (rolling shape — fits stateless services).
759
+ *
760
+ * Both must satisfy `min <= max`, and they cannot both be `100`
761
+ * (a no-op deploy that can't drain or expand).
762
+ *
763
+ * @default `{ minHealthyPercent: 100, maxHealthyPercent: 200 }` (CDK default)
764
+ */
765
+ deployment?: {
766
+ minHealthyPercent?: number;
767
+ maxHealthyPercent?: number;
768
+ };
769
+ /**
770
+ * Cloud Map service discovery for this service. When provided, the pattern
771
+ * registers the service against the application-level Cloud Map namespace
772
+ * (`app.getNamespace().registerService({ name })`) and threads the resulting
773
+ * `IService` to the resources layer, which calls `associateCloudMapService`
774
+ * after service construction. The namespace is `<appName>.local` and is
775
+ * lazily created on first registration — no cluster-level configuration.
776
+ *
777
+ * `dnsRecordType` defaults to `"A"` (paired with the AWS_VPC networkMode
778
+ * default). Set to `"SRV"` only when explicitly overriding `networkMode` to
779
+ * `HOST` or `BRIDGE` — CDK's `Ec2Service.associateCloudMapService(...)`
780
+ * rejects A records under those modes (the task shares the host's ENI IP,
781
+ * so the published port must travel with the record).
782
+ */
783
+ serviceDiscovery?: {
784
+ name: string;
785
+ dnsRecordType?: "A" | "SRV";
786
+ };
787
+ /**
788
+ * Override the task definition's `NetworkMode`. Defaults:
789
+ * - EC2 services: `AWS_VPC` (per-task ENI → per-task SG, A-record service
790
+ * discovery works, current AWS recommendation for new workloads).
791
+ * - EC2 services with `cluster.directAccess: true`: `HOST` (direct port
792
+ * binding to the instance, required for ECS Exec → host-port flows).
793
+ * - Fargate services: `AWS_VPC` (CDK requirement, not overridable).
794
+ *
795
+ * Cloud Map service discovery works under all three modes:
796
+ * - `AWS_VPC`: A records (default `serviceDiscovery.dnsRecordType`).
797
+ * - `HOST` / `BRIDGE`: SRV records — set
798
+ * `serviceDiscovery.dnsRecordType: "SRV"` so the published port travels
799
+ * with the record.
800
+ *
801
+ * ENI quota: each task under `AWS_VPC` attaches an ENI to the host
802
+ * instance. Instance ENI limits cap tasks-per-instance (e.g. t4g.medium
803
+ * → 2 tasks). For scale beyond the limit, enable ENI trunking at the
804
+ * account+instance level, pick larger instances, or override to `BRIDGE`.
805
+ */
806
+ networkMode?: NetworkMode;
807
+ /**
808
+ * Pre-existing security groups attached to this service's task ENIs (AWS_VPC
809
+ * mode). When omitted, CDK auto-creates a default service SG. Stateful
810
+ * patterns that own a wrapper SG (e.g. `ClickHouseDatabase`) set this so
811
+ * `this.connections.securityGroups[0]` is honest — the SG it points at is
812
+ * the SG the task ENI actually wears, not a CDK-autogenerated sibling that
813
+ * arbitrates no traffic.
814
+ */
815
+ securityGroups?: ISecurityGroup[];
312
816
  }
313
817
  /**
314
818
  * ECS compute configuration.
@@ -371,11 +875,10 @@ export interface EcsComputeProps {
371
875
  */
372
876
  ecrRepository?: Repository | RepositoryImage;
373
877
  /**
374
- * Path to Dockerfile for building custom image.
375
- * Note: This is metadata for the CLI build process,
376
- * not used during CDK synthesis.
878
+ * Cluster-level Dockerfile build configuration. Used when no service-level
879
+ * `docker` is set. Metadata for the CLI build process; not used during synth.
377
880
  */
378
- dockerfilePath?: string;
881
+ docker?: DockerBuild;
379
882
  /**
380
883
  * SNS topic for alarm notifications. Resolved to ITopic and passed to EcsCluster.
381
884
  * Accepts either an ITopic directly or a topic ARN string (resolved internally).
@@ -1,2 +1,12 @@
1
1
  import { ScalingType } from "../../resources/aws/compute/ecs.js";
2
2
  export { ScalingType };
3
+ /**
4
+ * Type-narrowing predicate for the two lambda-driven hook variants. Returns
5
+ * true for `"lifecycle-hook"` (PRE_SCALE_UP) and `"post-deploy"` (POST_SCALE_UP).
6
+ * Narrows the whole `EcsMigrationsConfig` to the hook-variant union, not just
7
+ * the `mode` property, so callers can read `separateTaskDef` / `entryPoint`
8
+ * etc. on the narrowed object.
9
+ */
10
+ export function isHookMigrations(config) {
11
+ return config.mode === "lifecycle-hook" || config.mode === "post-deploy";
12
+ }
@@ -106,11 +106,6 @@ interface BaseLambdaProps {
106
106
  * ]
107
107
  */
108
108
  connections?: ConnectionSpec[];
109
- /**
110
- * EventBridge schedule expression for scheduled Lambda invocations.
111
- * Uses cron or rate syntax: "rate(1 hour)" or "cron(0 12 * * ? *)".
112
- */
113
- scheduleExpression?: string;
114
109
  }
115
110
  /**
116
111
  * Container-based Lambda using ECR image.
@@ -68,7 +68,6 @@ export class LambdaCompute extends Construct {
68
68
  functionUrlAuthType,
69
69
  functionUrlCors,
70
70
  functionUrlInvokeMode,
71
- scheduleExpression: props.scheduleExpression,
72
71
  ephemeralStorageSize: props.ephemeralStorageSize,
73
72
  secrets: props.secrets,
74
73
  ssmSecretsPath: props.ssmSecretsPath,
@@ -96,7 +95,7 @@ export class LambdaCompute extends Construct {
96
95
  );
97
96
  }
98
97
  catch (error) {
99
- throw new Error(`Failed to process connections for Lambda '${id}': ${error instanceof Error ? error.message : String(error)}`);
98
+ throw new Error(`Failed to process connections for Lambda '${id}': ${error instanceof Error ? error.message : String(error)}`, { cause: error });
100
99
  }
101
100
  }
102
101
  }