@intentius/chant-lexicon-aws 0.44.13 → 0.45.0

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 (52) hide show
  1. package/dist/agentcore/trace-fetch.d.ts +4 -1
  2. package/dist/agentcore/trace-fetch.d.ts.map +1 -1
  3. package/dist/api/read-client.d.ts +31 -1
  4. package/dist/api/read-client.d.ts.map +1 -1
  5. package/dist/codegen/docs.d.ts.map +1 -1
  6. package/dist/components/capability-plugin.d.ts.map +1 -1
  7. package/dist/components/cloud-executor.d.ts +9 -0
  8. package/dist/components/cloud-executor.d.ts.map +1 -1
  9. package/dist/composites/agentcore-agent.d.ts +33 -19
  10. package/dist/composites/agentcore-agent.d.ts.map +1 -1
  11. package/dist/composites/index.d.ts +1 -1
  12. package/dist/composites/index.d.ts.map +1 -1
  13. package/dist/deep-observe.d.ts +21 -0
  14. package/dist/deep-observe.d.ts.map +1 -1
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/integrity.json +2 -2
  18. package/dist/manifest.json +1 -1
  19. package/dist/op/activities/aws-apply.d.ts +14 -4
  20. package/dist/op/activities/aws-apply.d.ts.map +1 -1
  21. package/dist/plugin.d.ts.map +1 -1
  22. package/dist/properties.d.ts +4 -3
  23. package/dist/properties.d.ts.map +1 -1
  24. package/dist/spec/fetch.d.ts +12 -1
  25. package/dist/spec/fetch.d.ts.map +1 -1
  26. package/package.json +2 -2
  27. package/src/agentcore/trace-fetch.test.ts +17 -0
  28. package/src/agentcore/trace-fetch.ts +7 -2
  29. package/src/api/read-client.test.ts +87 -0
  30. package/src/api/read-client.ts +57 -1
  31. package/src/codegen/docs-links.test.ts +44 -30
  32. package/src/codegen/docs.ts +2 -1034
  33. package/src/components/capability-plugin.ts +5 -2
  34. package/src/components/cloud-executor.test.ts +29 -1
  35. package/src/components/cloud-executor.ts +23 -5
  36. package/src/composites/agentcore-agent.test.ts +32 -12
  37. package/src/composites/agentcore-agent.ts +43 -23
  38. package/src/composites/index.ts +1 -1
  39. package/src/composites/microvm-app.test.ts +2 -2
  40. package/src/deep-observe.test.ts +94 -0
  41. package/src/deep-observe.ts +58 -2
  42. package/src/import/roundtrip-fixtures.test.ts +1 -1
  43. package/src/index.ts +3 -1
  44. package/src/lifecycle-integration.test.ts +89 -0
  45. package/src/op/activities/aws-apply.test.ts +40 -4
  46. package/src/op/activities/aws-apply.ts +23 -7
  47. package/src/plugin.ts +34 -35
  48. package/src/properties.test.ts +5 -5
  49. package/src/properties.ts +6 -5
  50. package/src/serializer.test.ts +73 -33
  51. package/src/spec/fetch.test.ts +40 -0
  52. package/src/spec/fetch.ts +24 -3
@@ -108,9 +108,9 @@ export async function generateDocs(options?: { verbose?: boolean }): Promise<voi
108
108
  // mechanism azure/helm already use), so its "Folds?" column can never
109
109
  // drift from the registration the way #1062's foldability matrix
110
110
  // depends on. The hand-written usage guide with full worked examples
111
- // moves to a separate "intrinsics-guide" page below — content
111
+ // moves to a separate "intrinsics-guide" page in docs/pages/ — content
112
112
  // unchanged, just no longer sharing a slug with generated data.
113
- // `rules` is NOT suppressed: the hand-written `lint-rules` page below
113
+ // `rules` is NOT suppressed: the hand-written `lint-rules` page in docs/pages/
114
114
  // explains 26 of the lexicon's 50 rules in depth, and the whole WAW032+
115
115
  // hardening pass had no entry there at all (#1312). The generated table is
116
116
  // the complete, always-current list, so both ship — the overview/reference
@@ -118,1038 +118,6 @@ export async function generateDocs(options?: { verbose?: boolean }): Promise<voi
118
118
  // would just create a third copy to drift.
119
119
  suppressPages: ["pseudo-parameters"],
120
120
  examplesDir: join(pkgDir, "examples"),
121
- extraPages: [
122
- {
123
- slug: "cloudformation",
124
- title: "CloudFormation Concepts",
125
- description: "Templates, resources, parameters, outputs, dependencies, and tagging in the AWS CloudFormation lexicon",
126
- content: `Every exported resource declaration becomes a logical resource in a CloudFormation template. The serializer handles the translation automatically:
127
-
128
- - Wraps output in \`AWSTemplateFormatVersion: "2010-09-09"\`
129
- - Converts camelCase property names to PascalCase (CloudFormation convention)
130
- - Resolves \`AttrRef\` references to \`Fn::GetAtt\`
131
- - Resolves resource references to \`Ref\` intrinsics
132
-
133
- {{file:lambda-s3/src/main.ts}}
134
-
135
- The \`LambdaS3\` composite expands to 3 CloudFormation resources: an S3 Bucket, an IAM Role (with S3 read policy), and a Lambda Function. Property names like \`BucketName\` use the CloudFormation spec-native PascalCase directly, and the export name \`app\` becomes the resource name prefix (e.g. \`appBucket\`, \`appRole\`, \`appFunc\`).
136
-
137
- ## Resource types and naming
138
-
139
- CloudFormation resource types like \`AWS::S3::Bucket\` are mapped to short TypeScript class names. The lexicon uses a naming strategy that prioritizes readability:
140
-
141
- | CloudFormation Type | Chant Class | Rule |
142
- |---|---|---|
143
- | \`AWS::S3::Bucket\` | \`Bucket\` | Priority name (common resource) |
144
- | \`AWS::Lambda::Function\` | \`Function\` | Priority name |
145
- | \`AWS::IAM::Role\` | \`Role\` | Priority name |
146
- | \`AWS::EC2::Instance\` | \`Instance\` | Short name (last segment) |
147
- | \`AWS::EC2::SecurityGroup\` | \`SecurityGroup\` | Short name |
148
- | \`AWS::ECS::Service\` | \`EcsService\` | Service-prefixed (avoids collision with \`AWS::AppRunner::Service\`) |
149
-
150
- Common resources get fixed short names for stability. When two services define the same resource name (e.g. both ECS and AppRunner have \`Service\`), the less common one gets a service prefix.
151
-
152
- **Discovering available resources:** Your editor's autocomplete is the best tool — every resource is a named export from the lexicon. You can also run \`chant list\` to see all resource types, or browse the generated TypeScript types.
153
-
154
- ## Imports and cross-file references
155
-
156
- Chant projects use standard TypeScript imports. Lexicon types come from the lexicon package, and cross-file references are standard imports:
157
-
158
- {{file:lambda-api/src/health-api.ts}}
159
-
160
- When you reference a resource or attribute from another file (e.g. \`dataBucket.Arn\`), the serializer resolves it to \`Fn::GetAtt\` or \`Ref\` as appropriate. This is how cross-file references work — standard imports, no indirection.
161
-
162
- ## Parameters
163
-
164
- CloudFormation parameters let you customize a stack at deploy time. Export a \`Parameter\` to add it to the template's \`Parameters\` section:
165
-
166
- {{file:docs-snippets/src/parameter-declaration.ts}}
167
-
168
- Produces:
169
-
170
- \`\`\`json
171
- "Parameters": {
172
- "Environment": {
173
- "Type": "String",
174
- "Default": "dev",
175
- "Description": "Deployment environment"
176
- }
177
- }
178
- \`\`\`
179
-
180
- Reference parameters with \`Ref\`:
181
-
182
- {{file:docs-snippets/src/parameter-cross-file-ref.ts}}
183
-
184
- ## Outputs
185
-
186
- Use \`output()\` to create explicit stack outputs. Accepts an \`AttrRef\` (resource attribute)
187
- or any intrinsic (e.g. \`Sub\`, \`Join\`) for computed values. Cross-resource \`AttrRef\` usage
188
- is also auto-detected and promoted to outputs when needed.
189
-
190
- {{file:docs-snippets/src/output-explicit.ts}}
191
-
192
- Produces:
193
-
194
- \`\`\`json
195
- "Outputs": {
196
- "DataBucketArn": {
197
- "Value": { "Fn::GetAtt": ["DataBucket", "Arn"] }
198
- }
199
- }
200
- \`\`\`
201
-
202
- Use \`Sub\` to export a computed value like a constructed URL:
203
-
204
- \`\`\`typescript
205
- export const solrUrl = output(Sub\`http://\${Ref(albDnsName)}/solr\`, "solrUrl");
206
- \`\`\`
207
-
208
- ## Pseudo-parameters
209
-
210
- Runtime context values available in every template, accessed via the \`AWS\` namespace:
211
-
212
- {{file:docs-snippets/src/pseudo-params.ts}}
213
-
214
- | Pseudo-parameter | Description |
215
- |---|---|
216
- | \`AWS.StackName\` | Name of the stack |
217
- | \`AWS.Region\` | AWS region |
218
- | \`AWS.AccountId\` | AWS account ID |
219
- | \`AWS.StackId\` | Stack ID |
220
- | \`AWS.URLSuffix\` | Domain suffix (usually \`amazonaws.com\`) |
221
- | \`AWS.Partition\` | Partition (\`aws\`, \`aws-cn\`, \`aws-us-gov\`) |
222
- | \`AWS.NotificationARNs\` | Notification ARNs |
223
- | \`AWS.NoValue\` | Removes property when used with \`Fn::If\` |
224
-
225
- ## Intrinsic functions
226
-
227
- The lexicon provides 9 intrinsic functions (\`Sub\`, \`Ref\`, \`GetAtt\`, \`If\`, \`Join\`, \`Select\`, \`Split\`, \`Base64\`, \`GetAZs\`) that map directly to CloudFormation \`Fn::\` calls. See [Intrinsic Functions](../intrinsics/) for the reference table or the [Intrinsics Guide](../intrinsics-guide/) for full usage examples.
228
-
229
- ## Dependencies
230
-
231
- CloudFormation automatically creates dependencies between resources when you use \`Ref\` or \`Fn::GetAtt\`. Chant leverages this — when you reference \`$.myBucket.arn\`, the serializer emits \`Fn::GetAtt\` and CloudFormation infers the dependency.
232
-
233
- For cases where you need an explicit dependency without a property reference, pass \`DependsOn\` as a resource-level attribute (second constructor argument):
234
-
235
- {{file:docs-snippets/src/depends-on.ts}}
236
-
237
- \`DependsOn\` values can be string logical names or references to other resource objects — Declarable references are resolved to their logical names automatically at build time.
238
-
239
- The \`WAW010\` post-synth check warns if a \`DependsOn\` target is already referenced via \`Ref\` or \`Fn::GetAtt\` in properties — in that case the explicit dependency is redundant.
240
-
241
- ## Resource attributes
242
-
243
- Every resource constructor accepts an optional second argument for CloudFormation resource-level attributes. These control lifecycle behavior, conditional creation, and metadata — they are distinct from resource *properties* (the first argument).
244
-
245
- {{file:docs-snippets/src/resource-attributes.ts}}
246
-
247
- | Attribute | Type | Description |
248
- |-----------|------|-------------|
249
- | \`DependsOn\` | \`Declarable \\| Declarable[] \\| string \\| string[]\` | Explicit ordering dependency. Accepts resource references or logical name strings. |
250
- | \`Condition\` | \`string\` | Only create this resource when the named Condition evaluates to true. |
251
- | \`DeletionPolicy\` | \`"Delete" \\| "Retain" \\| "RetainExceptOnCreate" \\| "Snapshot"\` | What happens when the resource is removed from the template or the stack is deleted. |
252
- | \`UpdateReplacePolicy\` | \`"Delete" \\| "Retain" \\| "Snapshot"\` | What happens to the old resource when CloudFormation replaces it during an update. |
253
- | \`UpdatePolicy\` | \`object\` | Controls how Auto Scaling Groups perform rolling updates (\`AutoScalingRollingUpdate\`, \`AutoScalingReplacingUpdate\`). |
254
- | \`CreationPolicy\` | \`object\` | Wait for resource signals before marking creation complete (\`ResourceSignal\` with \`Count\` and \`Timeout\`). |
255
- | \`Metadata\` | \`Record<string, unknown>\` | Arbitrary metadata. Commonly used for \`AWS::CloudFormation::Init\` (cfn-init bootstrapping). Intrinsic functions in metadata values are resolved at build time. |
256
-
257
- All attributes are optional. When omitted, CloudFormation uses its defaults (e.g. \`DeletionPolicy: "Delete"\`).
258
-
259
- ## Policy documents
260
-
261
- IAM policy documents appear on many AWS resources — \`Role.assumeRolePolicyDocument\`, \`ManagedPolicy.policyDocument\`, \`BucketPolicy.policyDocument\`, and others. These properties are typed as \`PolicyDocument\`, giving you autocomplete for the IAM JSON Policy Language.
262
-
263
- The \`PolicyDocument\` interface and its supporting types:
264
-
265
- | Type | Fields |
266
- |------|--------|
267
- | \`PolicyDocument\` | \`Version?\` (\`"2012-10-17"\` \\| \`"2008-10-17"\`), \`Id?\`, \`Statement\` |
268
- | \`IamPolicyStatement\` | \`Effect\` (\`"Allow"\` \\| \`"Deny"\`), \`Action?\`, \`Resource?\`, \`Principal?\`, \`Condition?\`, and their \`Not\` variants |
269
- | \`IamPolicyPrincipal\` | \`"*"\` or \`{ AWS?, Service?, Federated? }\` |
270
-
271
- Policy documents use **PascalCase keys** (\`Effect\`, \`Action\`, \`Resource\`) because they follow the IAM JSON Policy Language spec — CloudFormation passes them through to IAM as-is, unlike resource properties which are automatically converted from camelCase.
272
-
273
- The recommended pattern is to extract policies into your \`defaults.ts\` and import them directly:
274
-
275
- {{file:docs-snippets/src/policy-trust.ts}}
276
-
277
- Then reference them from resource files:
278
-
279
- {{file:docs-snippets/src/policy-role.ts}}
280
-
281
- For scoped resource ARNs, use \`Sub\` in the policy constant:
282
-
283
- {{file:docs-snippets/src/policy-scoped.ts}}
284
-
285
- The \`IamPolicyPrincipal\` type supports all principal forms — wildcard (\`"*"\`), AWS accounts, services, and federated providers:
286
-
287
- \`\`\`typescript
288
- // Wildcard principal
289
- Principal: "*",
290
-
291
- // Service principal
292
- Principal: { Service: "lambda.amazonaws.com" },
293
-
294
- // Cross-account
295
- Principal: { AWS: "arn:aws:iam::123456789012:root" },
296
-
297
- // Multiple services
298
- Principal: { Service: ["lambda.amazonaws.com", "edgelambda.amazonaws.com"] },
299
- \`\`\`
300
-
301
- ## Conditions
302
-
303
- Use the \`If\` intrinsic for conditional values within resource properties:
304
-
305
- {{file:docs-snippets/src/conditions.ts}}
306
-
307
- CloudFormation \`Conditions\` blocks are recognized by the serializer when importing existing templates. For new stacks, use TypeScript logic for build-time decisions and \`If\` for deploy-time decisions.
308
-
309
- ## Mappings
310
-
311
- CloudFormation Mappings are a static lookup mechanism. In chant, use TypeScript objects instead — they're evaluated at build time and produce the same result:
312
-
313
- {{file:docs-snippets/src/mappings.ts}}
314
-
315
- For deploy-time region lookups, combine \`AWS.Region\` with \`If\` or use \`Fn::Sub\` with SSM parameter store references.
316
-
317
- ## Nested stacks
318
-
319
- :::caution
320
- Nested stacks add deployment complexity and are not recommended for most projects. Prefer [flat composites](../composites/) instead.
321
- :::
322
-
323
- CloudFormation nested stacks (\`AWS::CloudFormation::Stack\`) split resources into child templates. The lexicon supports them via \`nestedStack()\` for cases where you exceed the 500-resource limit or need to package reusable infrastructure as a black box. See the [Nested Stacks](../nested-stacks/) page for details.
324
-
325
- ## Tagging
326
-
327
- Use \`defaultTags()\` to declare project-wide tags. The serializer automatically injects them into every taggable resource at synthesis time:
328
-
329
- {{file:docs-snippets/src/tagging.ts}}
330
-
331
- No other changes needed — all taggable resources in the project get these tags automatically. Resources with explicit \`Tags\` keep them (explicit key wins over default). Non-taggable resources like \`AWS::Lambda::Permission\` are never tagged.
332
-
333
- Tag values support strings, \`Parameter\` references, and intrinsic functions (\`Sub\`, \`Ref\`, etc.).`,
334
- },
335
- {
336
- slug: "intrinsics-guide",
337
- title: "Intrinsics Guide",
338
- description: "Worked examples for every CloudFormation intrinsic function and their chant syntax",
339
- content: `See [Intrinsic Functions](../intrinsics/) for the generated reference table (name, description, output key, whether it's a tagged template, whether folding can reduce it). This page is the worked-example companion — one \`Fn::\` intrinsic function per section, with real usage.
340
-
341
- CloudFormation intrinsic functions are available as imports from the lexicon. They produce the corresponding \`Fn::\` calls in the serialized template.
342
-
343
- Only \`Sub\` is a tagged template — the others below are plain function calls. Both forms fold on the [default build path](/chant/concepts/typescript-as-data/#folded-vs-run) ([#1044](https://github.com/INTENTIUS/chant/issues/1044)): every AWS intrinsic on this page is registered with its call form opted in, so using \`Ref\`, \`GetAtt\`, \`If\`, \`Join\`, \`Select\`, \`Split\`, \`Base64\`, or \`GetAZs\` in a resource's props no longer forces that file back to the run path. What folds is the call, not calls in general — the name has to be one this lexicon registered and opted in, imported from this lexicon by the file using it. A same-file resource passed to one (\`Ref(bucket)\` where \`bucket\` is declared in the same file) still falls back, since the intrinsic needs the real constructed resource.
344
-
345
- Here is a complete example using all intrinsic functions:
346
-
347
- {{file:docs-snippets/src/intrinsics.ts}}
348
-
349
- ## \`Sub\` — string substitution
350
-
351
- Tagged template literal that produces \`Fn::Sub\`. The most common intrinsic — use it for dynamic naming with pseudo-parameters and attribute references:
352
-
353
- {{file:docs-snippets/src/intrinsics-detail.ts:3-5}}
354
-
355
- \`Sub\` is a tagged template — use it with backticks, not as a function call.
356
-
357
- ## \`Ref\` — resource and parameter references
358
-
359
- References a resource's physical ID or a parameter's value:
360
-
361
- {{file:docs-snippets/src/intrinsics-detail.ts:7-9}}
362
-
363
- In most cases you don't need \`Ref\` directly — the serializer automatically generates \`Ref\` when you reference an imported resource (e.g. \`dataBucket\` imported from another file).
364
-
365
- ## \`GetAtt\` — resource attributes
366
-
367
- **Preferred:** Use AttrRef directly via the resource's typed properties. When you write \`dataBucket.arn\` (imported from the file that defines it), the serializer automatically emits \`Fn::GetAtt\`. Explicit \`GetAtt\` is only needed for dynamic or imported resource names.
368
-
369
- ## \`If\` — conditional values
370
-
371
- Returns one of two values based on a condition:
372
-
373
- {{file:docs-snippets/src/intrinsics-detail.ts:11-12}}
374
-
375
- Use with \`AWS.NoValue\` to conditionally omit a property — see [Conditions](#conditions) on the CloudFormation Concepts page.
376
-
377
- ## \`Join\` — join values
378
-
379
- Joins values with a delimiter:
380
-
381
- {{file:docs-snippets/src/intrinsics-detail.ts:14-15}}
382
-
383
- ## \`Select\` — select by index
384
-
385
- Selects a value from a list by index:
386
-
387
- {{file:docs-snippets/src/intrinsics-detail.ts:17-18}}
388
-
389
- ## \`Split\` — split string
390
-
391
- Splits a string by a delimiter:
392
-
393
- {{file:docs-snippets/src/intrinsics-detail.ts:20-21}}
394
-
395
- ## \`Base64\` — encode to Base64
396
-
397
- Encodes a string to Base64, commonly used for EC2 user data:
398
-
399
- {{file:docs-snippets/src/intrinsics-detail.ts:23-27}}
400
-
401
- ## \`GetAZs\` — availability zones
402
-
403
- Returns the list of Availability Zones for a region:
404
-
405
- {{file:docs-snippets/src/intrinsics-detail.ts:29-31}}`,
406
- },
407
- {
408
- slug: "composites",
409
- title: "Composites",
410
- description: "Composite resources, built-in composites, action constants, and withDefaults presets in the AWS CloudFormation lexicon",
411
- content: `Composites group related resources into reusable factories. See also the core [Composite Resources](/chant/guide/composite-resources/) guide.
412
-
413
- {{file:lambda-api/src/lambda-api.ts}}
414
-
415
- Instantiate and export:
416
-
417
- {{file:lambda-api/src/health-api.ts}}
418
-
419
- During build, composites expand to flat CloudFormation resources: \`healthApiRole\`, \`healthApiFunc\`, \`healthApiPermission\`.
420
-
421
- A top-level composite call assigned directly to an export — like \`healthApi\` above — is one of the patterns [folding](/chant/concepts/typescript-as-data/#folded-vs-run) can reduce with no module execution (chant #1023). Defining a composite (the \`Composite(...)\` call inside \`lambda-api.ts\` itself) doesn't fold — its factory callback is a function, which is outside the fold subset — and neither does a composite call embedded as a nested value inside another resource's own properties; only a composite call that is itself a file's top-level export (or destructured/re-exported from one) is eligible.
422
-
423
- ## Built-in composites
424
-
425
- The AWS lexicon ships ready-to-use composites for common patterns. Import them from \`@intentius/chant-lexicon-aws\`:
426
-
427
- {{file:docs-snippets/src/builtin-composites.ts}}
428
-
429
- | Composite | Members | Description |
430
- |-----------|---------|-------------|
431
- | \`LambdaFunction\` | \`role\`, \`func\` | IAM Role + Lambda Function. Auto-attaches \`AWSLambdaBasicExecutionRole\`; adds \`AWSLambdaVPCAccessExecutionRole\` when \`VpcConfig\` is provided. |
432
- | \`LambdaNode\` | \`role\`, \`func\` | \`LambdaFunction\` preset with \`Runtime: "nodejs20.x"\` and \`Handler: "index.handler"\` |
433
- | \`LambdaPython\` | \`role\`, \`func\` | \`LambdaFunction\` preset with \`Runtime: "python3.12"\` and \`Handler: "handler.handler"\` |
434
- | \`LambdaApi\` | \`role\`, \`func\`, \`permission\` | \`LambdaFunction\` + Lambda Permission for API Gateway invocation |
435
- | \`LambdaScheduled\` | \`role\`, \`func\`, \`rule\`, \`permission\` | \`LambdaFunction\` + EventBridge Rule + Lambda Permission |
436
- | \`LambdaSqs\` | \`queue\`, \`role\`, \`func\` | SQS Queue + Lambda + EventSourceMapping. Auto-attaches SQS receive policy. |
437
- | \`LambdaEventBridge\` | \`rule\`, \`role\`, \`func\`, \`permission\` | EventBridge Rule + Lambda. Supports \`schedule\` and/or \`eventPattern\`. |
438
- | \`LambdaDynamoDB\` | \`table\`, \`role\`, \`func\` | DynamoDB Table + Lambda. Auto-attaches DynamoDB policy and injects \`TABLE_NAME\` env var. |
439
- | \`LambdaS3\` | \`bucket\`, \`role\`, \`func\` | S3 Bucket (encrypted, public access blocked) + Lambda. Auto-attaches S3 policy and injects \`BUCKET_NAME\` env var. |
440
- | \`LambdaSns\` | \`topic\`, \`role\`, \`func\`, \`subscription\`, \`permission\` | SNS Topic + Lambda via Subscription. Auto-attaches invoke permission for SNS. |
441
- | \`VpcDefault\` | \`vpc\`, \`igw\`, \`igwAttachment\`, \`publicSubnet1\`, \`publicSubnet2\`, \`privateSubnet1\`, \`privateSubnet2\`, \`publicRouteTable\`, \`publicRoute\`, \`publicRta1\`, \`publicRta2\`, \`privateRouteTable\`, \`privateRta1\`, \`privateRta2\`, \`natEip\`, \`natGateway\`, \`privateRoute\` | Production-ready VPC: 2 public + 2 private subnets across 2 AZs, internet gateway, single NAT gateway. |
442
- | \`FargateAlb\` | \`cluster\`, \`executionRole\`, \`taskRole\`, \`logGroup\`, \`taskDef\`, \`albSg\`, \`taskSg\`, \`alb\`, \`targetGroup\`, \`listener\`, \`service\` | Fargate service behind an ALB. Accepts VPC outputs as props. |
443
- | \`AlbShared\` | \`cluster\`, \`executionRole\`, \`albSg\`, \`alb\`, \`listener\` | Shared ALB infrastructure (ECS cluster, execution role, ALB, listener with 404 default). Created once, consumed by multiple \`FargateService\` instances. |
444
- | \`FargateService\` | \`taskRole\`, \`logGroup\`, \`taskDef\`, \`taskSg\`, \`targetGroup\`, \`rule\`, \`service\` | Per-service Fargate resources with listener rule routing. Wire to an \`AlbShared\` instance for multi-service ALB patterns. |
445
- | \`RdsInstance\` | \`subnetGroup\`, \`sg\`, \`db\` (+ \`parameterGroup\` if configured) | RDS instance (postgres, mysql, mariadb) in private subnets. Creates DB subnet group, security group, and optionally a parameter group. Engine-specific defaults for port, username, and version. Encrypted by default. |
446
-
447
- All built-in composites accept \`ManagedPolicyArns\` and \`Policies\` for adding IAM permissions to the auto-created role.
448
-
449
- ## Action constants
450
-
451
- Typed IAM action constants for common AWS services. Use them in policy documents instead of hand-typing action strings:
452
-
453
- {{file:docs-snippets/src/action-constants.ts}}
454
-
455
- Available constants:
456
-
457
- | Constant | Key groups |
458
- |----------|------------|
459
- | \`S3Actions\` | \`ReadOnly\`, \`WriteOnly\`, \`ReadWrite\`, \`Full\`, \`GetObject\`, \`PutObject\`, \`DeleteObject\`, \`ListObjects\` |
460
- | \`LambdaActions\` | \`Invoke\`, \`ReadOnly\`, \`Full\` |
461
- | \`DynamoDBActions\` | \`ReadOnly\`, \`WriteOnly\`, \`ReadWrite\`, \`Full\`, \`GetItem\`, \`PutItem\`, \`Query\`, \`Scan\` |
462
- | \`SQSActions\` | \`SendMessage\`, \`ReceiveMessage\`, \`Full\` |
463
- | \`SNSActions\` | \`Publish\`, \`Subscribe\`, \`Full\` |
464
- | \`IAMActions\` | \`PassRole\` |
465
- | \`ECRActions\` | \`Pull\`, \`Full\` |
466
- | \`LogsActions\` | \`Write\`, \`Full\` |
467
- | \`ECSActions\` | \`RunTask\`, \`Service\`, \`Full\` |
468
-
469
- Broad groups like \`ReadWrite\` are always supersets of their narrow counterparts (\`ReadOnly\` + \`WriteOnly\`). All values are \`as const\` arrays for full type safety.
470
-
471
- ## \`withDefaults\` — composite presets
472
-
473
- Wrap a composite with pre-applied defaults. Defaulted props become optional:
474
-
475
- {{file:docs-snippets/src/with-defaults.ts}}
476
-
477
- \`withDefaults\` preserves the original composite's identity — same \`_id\` and \`compositeName\`, no new registry entry.
478
-
479
- ### Computed defaults
480
-
481
- \`withDefaults\` also accepts a function that receives the caller's props and returns defaults. This enables conditional logic without generating extra resources:
482
-
483
- {{file:docs-snippets/src/computed-defaults.ts}}
484
-
485
- Merge order: computed defaults are applied first, then user-provided props override them.
486
-
487
- ## \`propagate\` — shared properties
488
-
489
- Attach properties that merge into every member during expansion:
490
-
491
- {{file:docs-snippets/src/propagate.ts}}
492
-
493
- Merge semantics:
494
- - **Scalars** — member-specific value wins over shared
495
- - **Arrays** (e.g. tags) — shared values prepended, member values appended
496
- - **\`undefined\`** — stripped from shared props, never overwrites
497
-
498
- ## Nested stacks
499
-
500
- :::caution
501
- Nested stacks add deployment complexity and are not recommended for most projects. Prefer flat composites instead.
502
- :::
503
-
504
- When you need to split resources into a separate CloudFormation template, the lexicon supports nested stacks via \`nestedStack()\`. See the [Nested Stacks](../nested-stacks/) page for details.`,
505
- },
506
- {
507
- slug: "nested-stacks",
508
- title: "Nested Stacks",
509
- sidebar: false,
510
- description: "Splitting resources into child CloudFormation templates with automatic cross-stack reference wiring",
511
- content: `import Diagram from '../../components/Diagram.astro';
512
-
513
- CloudFormation nested stacks (\`AWS::CloudFormation::Stack\`) let you decompose large templates into smaller, reusable child templates. The AWS lexicon's \`nestedStack()\` function references a **child project directory** — a subdirectory that builds independently to a valid CloudFormation template.
514
-
515
- :::caution[Consider alternatives first]
516
- Nested stacks add deployment complexity: child templates must be uploaded to S3, rollbacks are all-or-nothing at the parent level, drift detection doesn't recurse into children, and debugging failures requires drilling into child stack events. For splitting a large project into separately-deployed pieces, prefer [Multi-Stack Projects](/chant/guide/multi-stack/) — each \`src/\` subdirectory becomes an independent stack with no parent orchestration and no S3 upload step. Nested stacks are supported for specific cases — exceeding CloudFormation's 500-resource limit inside a single deployable unit, or packaging reusable infrastructure for other teams to deploy as a black box — but are not the recommended default.
517
- :::
518
-
519
- <Diagram name="nested-stacks" alt="Parent and child project source directories producing separate CloudFormation templates, both uploaded to S3" caption="Nested stack source layout and output" />
520
-
521
- ## Project structure
522
-
523
- A nested stack is a child project — a subdirectory with its own resource files and explicit \`stackOutput()\` declarations:
524
-
525
- \`\`\`
526
- src/
527
- app.ts # parent resources
528
- network/ # ← child project (nested stack)
529
- vpc.ts # VPC, subnet, internet gateway, routing
530
- security.ts # security group for Lambda
531
- outputs.ts # declares cross-stack outputs
532
- \`\`\`
533
-
534
- ## Declaring outputs in the child
535
-
536
- Use \`stackOutput()\` to mark values that the parent can reference. Each \`stackOutput()\` becomes an entry in the child template's \`Outputs\` section:
537
-
538
- {{file:../src/testdata/nested-stacks/network/outputs.ts}}
539
-
540
- The child can be built independently:
541
-
542
- \`\`\`bash
543
- chant build src/network/ -o network.json
544
- # Produces a standalone, valid CloudFormation template with Outputs
545
- \`\`\`
546
-
547
- ## Referencing from the parent
548
-
549
- Use \`nestedStack()\` in the parent to reference a child project directory. It returns an object with an \`outputs\` proxy for cross-stack references:
550
-
551
- {{file:../src/testdata/nested-stacks/app.ts}}
552
-
553
- \`network.outputs.subnetId\` produces a \`NestedStackOutputRef\` that serializes to \`{ "Fn::GetAtt": ["Network", "Outputs.SubnetId"] }\`.
554
-
555
- ## Build output
556
-
557
- \`chant build\` produces multiple template files:
558
-
559
- \`\`\`bash
560
- chant build -o template.json
561
- # Produces:
562
- # template.json — parent template
563
- # network.template.json — child template
564
- \`\`\`
565
-
566
- The parent template includes an \`AWS::CloudFormation::Stack\` resource pointing to the child:
567
-
568
- \`\`\`json
569
- "Network": {
570
- "Type": "AWS::CloudFormation::Stack",
571
- "Properties": {
572
- "TemplateURL": { "Fn::Sub": "\${TemplateBasePath}/network.template.json" }
573
- }
574
- }
575
- \`\`\`
576
-
577
- ## \`TemplateBasePath\` parameter
578
-
579
- Every parent template gets a \`TemplateBasePath\` parameter (default \`"."\`) that controls where CloudFormation looks for child templates:
580
-
581
- \`\`\`bash
582
- # Local dev — default "." works with rain and similar tools
583
- chant build -o template.json
584
-
585
- # Production — override with S3 URL
586
- aws cloudformation deploy \\
587
- --template-file template.json \\
588
- --stack-name my-stack \\
589
- --parameter-overrides TemplateBasePath=https://my-bucket.s3.amazonaws.com/templates
590
- \`\`\`
591
-
592
- Child templates also receive the \`TemplateBasePath\` parameter so it propagates through all nesting levels.
593
-
594
- All child template files must be uploaded alongside the parent template (or to the S3 path specified by \`TemplateBasePath\`).
595
-
596
- ## Explicit parameters
597
-
598
- Pass CloudFormation Parameters to child stacks with the \`parameters\` option:
599
-
600
- \`\`\`typescript
601
- import { nestedStack } from "@intentius/chant-lexicon-aws";
602
-
603
- const network = nestedStack("network", import.meta.dirname + "/network", {
604
- parameters: { Environment: "prod", CidrBlock: "10.0.0.0/16" },
605
- });
606
- \`\`\`
607
-
608
- ## Recursive nesting
609
-
610
- Child projects can themselves reference grandchild projects. Each level produces its own template file:
611
-
612
- \`\`\`
613
- src/
614
- app.ts
615
- infra/
616
- network/
617
- vpc.ts
618
- outputs.ts
619
- database/
620
- cluster.ts
621
- outputs.ts
622
- \`\`\`
623
-
624
- The build pipeline detects circular references and reports an error if child A references child B which references child A.
625
-
626
- ## Lint rules
627
-
628
- Three lint rules help catch common nested stack issues:
629
-
630
- | Rule | Severity | Description |
631
- |------|----------|-------------|
632
- | **WAW013** | error | Child project has no \`stackOutput()\` exports — parent can't reference anything |
633
- | **WAW014** | warning | \`nestedStack()\` outputs never referenced from parent — could be a separate build |
634
- | **WAW015** | error | Circular project references |
635
-
636
- ## When to use nested stacks
637
-
638
- Most splitting use cases are better served by other mechanisms:
639
-
640
- - **Splitting a large project into separately-deployed pieces** → [Multi-Stack Projects](/chant/guide/multi-stack/). Organize \`src/\` into subdirectories — each becomes an independent stack, deployed on its own, with cross-stack references resolved via standard CloudFormation \`Export\`/\`ImportValue\`. No parent template, no S3 upload of children, no \`TemplateBasePath\` parameter.
641
- - **Reusing resource patterns within a single template** → [Composites](../composites/). Composites expand into the same template and deploy atomically — they're about DRY, not splitting.
642
-
643
- **Use nested stacks only when:**
644
- - A single deployable unit exceeds CloudFormation's 500-resource limit
645
- - You're packaging reusable infrastructure for other teams to consume as a black box (the parent-child relationship is load-bearing)
646
- - You need an orchestrator parent that coordinates child lifecycles via CloudFormation Parameters and Outputs specifically`,
647
- },
648
- {
649
- slug: "lint-rules",
650
- title: "Lint Rules",
651
- description: "Built-in lint rules and post-synth checks for AWS CloudFormation",
652
- content: `The AWS lexicon ships lint rules that run during \`chant lint\` and post-synth checks that validate the serialized CloudFormation output after \`chant build\`.
653
-
654
- This page explains the most commonly hit rules in depth. For the complete list —
655
- every rule the lexicon registers, generated from the registration itself so it
656
- cannot fall behind — see [All Rules](../rules/). The security-hardening rules
657
- (WAW032 onward) also carry remediation guidance and upstream references in the
658
- [audit rules reference](/chant/lint-rules/audit-rules/).
659
-
660
- ## Lint rules
661
-
662
- Lint rules analyze your TypeScript source code before build.
663
-
664
- ### WAW001 — Hardcoded Region
665
-
666
- **Severity:** warning | **Category:** security
667
-
668
- Flags hardcoded AWS region strings like \`us-east-1\`. Use \`AWS.Region\` instead so templates are portable across regions.
669
-
670
- **Bad** — triggers WAW001:
671
-
672
- {{file:docs-snippets/src/lint-waw001-bad.ts}}
673
-
674
- **Good** — uses \`AWS.Region\`:
675
-
676
- {{file:docs-snippets/src/lint-waw001-good.ts}}
677
-
678
- ### WAW006 — S3 Bucket Encryption
679
-
680
- **Severity:** warning | **Category:** security
681
-
682
- Flags S3 buckets that don't configure server-side encryption. AWS recommends enabling encryption on all buckets.
683
-
684
- **Bad** — triggers WAW006:
685
-
686
- {{file:docs-snippets/src/lint-waw006-bad.ts}}
687
-
688
- **Good** — encryption configured:
689
-
690
- {{file:docs-snippets/src/lint-waw006-good.ts}}
691
-
692
- ### WAW009 — IAM Wildcard Resource
693
-
694
- **Severity:** warning | **Category:** security
695
-
696
- Flags IAM policy statements that use \`"Resource": "*"\`. Prefer scoped resource ARNs following the principle of least privilege.
697
-
698
- **Bad** — triggers WAW009:
699
-
700
- {{file:docs-snippets/src/lint-waw009-bad.ts}}
701
-
702
- **Good** — scoped ARN:
703
-
704
- {{file:docs-snippets/src/lint-waw009-good.ts}}
705
-
706
- IAM policy documents use PascalCase keys (\`Effect\`, \`Action\`, \`Resource\`) matching the IAM JSON Policy Language spec. The \`PolicyDocument\` and \`IamPolicyStatement\` types provide full autocomplete for these fields.
707
-
708
- ## Post-synth checks
709
-
710
- Post-synth checks run against the serialized CloudFormation JSON after build. They catch issues that are only visible in the final template.
711
-
712
- ### COR020 — Circular Resource Dependency
713
-
714
- Detects cycles in the resource dependency graph built from \`Ref\`, \`Fn::GetAtt\`, and \`DependsOn\` entries. Circular dependencies cause CloudFormation deployments to fail.
715
-
716
- ### EXT001 — Extension Constraint Violation
717
-
718
- Validates cross-property constraints from CloudFormation's cfn-lint extension schemas. For example, an EC2 instance might require \`SubnetId\` when \`NetworkInterfaces\` is not set.
719
-
720
- ### WAW010 — Redundant DependsOn
721
-
722
- Flags \`DependsOn\` entries where the target resource is already referenced via \`Ref\` or \`Fn::GetAtt\` in the resource's properties. CloudFormation automatically creates dependencies for these references, making the explicit \`DependsOn\` unnecessary.
723
-
724
- ### WAW011 — Deprecated Lambda Runtime
725
-
726
- Flags Lambda functions using deprecated or approaching-end-of-life runtimes (e.g. \`nodejs16.x\`, \`python3.8\`). Using deprecated runtimes prevents function updates and may cause deployment failures.
727
-
728
- ### WAW013 — No Stack Outputs
729
-
730
- **Severity:** error | **Category:** correctness
731
-
732
- Flags child projects (nested stacks) that have no \`stackOutput()\` exports. Without outputs, the parent stack can't reference any values from the child — either add \`stackOutput()\` declarations or remove the \`nestedStack()\` reference.
733
-
734
- ### WAW014 — Unreferenced Stack Outputs
735
-
736
- **Severity:** warning | **Category:** style
737
-
738
- Flags \`nestedStack()\` references whose outputs are never used from the parent. If no cross-stack references exist, the child project could just be built and deployed independently.
739
-
740
- ### WAW015 — Circular Project References
741
-
742
- **Severity:** error | **Category:** correctness
743
-
744
- Detects circular references between child projects (e.g. project A references project B which references project A). Circular project dependencies cause infinite build recursion.
745
-
746
- ### WAW016 — Deprecated Property Usage
747
-
748
- **Severity:** warning | **Category:** correctness
749
-
750
- Flags properties marked as deprecated in the CloudFormation Registry. Data comes from two sources: the explicit \`deprecatedProperties\` array in the Registry schema, and description text mining (keywords like "deprecated", "legacy", "no longer recommended").
751
-
752
- For example, \`AccessControl\` on \`AWS::S3::Bucket\` is a legacy property — use a bucket policy to grant access instead.
753
-
754
- \`\`\`
755
- WAW016: Resource "MyBucket" (AWS::S3::Bucket) uses deprecated property "AccessControl" — consider alternatives
756
- \`\`\`
757
-
758
- ### WAW017 — Missing Tags on Taggable Resource
759
-
760
- **Severity:** info | **Category:** best practice
761
-
762
- Flags resources that support tagging but have no \`Tags\` property set. Tags are important for cost allocation, compliance, and operational visibility. The check uses the \`tagging\` metadata from the CloudFormation Registry to determine which resources are taggable.
763
-
764
- \`\`\`
765
- WAW017: Resource "MyBucket" (AWS::S3::Bucket) supports tagging but has no Tags — consider adding tags for cost allocation and compliance
766
- \`\`\`
767
-
768
- ### WAW029 — Invalid DependsOn Target
769
-
770
- **Severity:** error | **Category:** correctness
771
-
772
- Flags \`DependsOn\` entries that reference a non-existent resource (typo or deleted resource) or that create a self-reference. Both cases cause CloudFormation deployments to fail immediately.
773
-
774
- \`\`\`
775
- WAW029: Resource "MyService" has DependsOn "MyBukcet" which does not exist in the template
776
- WAW029: Resource "MyBucket" has a DependsOn on itself — self-references are invalid
777
- \`\`\`
778
-
779
- ### WAW030 — Missing DependsOn for Known Patterns
780
-
781
- **Severity:** warning | **Category:** best practice
782
-
783
- Flags resources that are likely missing a required explicit \`DependsOn\` based on well-known CloudFormation ordering requirements:
784
-
785
- - **ECS Service + Listener**: An ECS Service with \`LoadBalancers\` should depend on the ALB Listener so the target group is fully configured before the service starts registering tasks.
786
- - **EC2 Route + VPCGatewayAttachment**: A Route using a \`GatewayId\` should depend on the VPCGatewayAttachment so the gateway is attached to the VPC before the route is created.
787
- - **API Gateway Deployment + Method**: A Deployment only references \`RestApiId\` — it needs an explicit \`DependsOn\` on its Methods or CloudFormation may create the deployment before any methods exist.
788
- - **API Gateway V2 Deployment + Route**: Same as above for HTTP APIs — a V2 Deployment needs \`DependsOn\` on its Routes.
789
- - **DynamoDB Table + ScalableTarget**: A ScalableTarget with \`ServiceNamespace: "dynamodb"\` references the table by string \`ResourceId\`, not \`Ref\` — it needs \`DependsOn\` so the table exists before scaling is registered.
790
- - **ECS Service + ScalableTarget**: A ScalableTarget with \`ServiceNamespace: "ecs"\` references the service by string — it needs \`DependsOn\` so the ECS Service exists first.
791
-
792
- \`\`\`
793
- WAW030: ECS Service "MyService" has LoadBalancers but no DependsOn on a Listener
794
- WAW030: Route "PublicRoute" uses a Gateway but has no dependency on VPCGatewayAttachment
795
- WAW030: API Gateway Deployment "MyDeployment" has no DependsOn on any Method
796
- WAW030: ScalableTarget "MyTarget" targets DynamoDB but has no DependsOn on any Table
797
- \`\`\`
798
-
799
- ### WAW018 — S3 Bucket Missing Public Access Block
800
-
801
- **Severity:** error | **Category:** security
802
-
803
- Flags S3 buckets without a \`PublicAccessBlockConfiguration\`. Without an explicit public access block, the bucket may be publicly accessible. Always set \`BlockPublicAcls\`, \`BlockPublicPolicy\`, \`IgnorePublicAcls\`, and \`RestrictPublicBuckets\` to \`true\`.
804
-
805
- ### WAW019 — Security Group Unrestricted Ingress on Sensitive Ports
806
-
807
- **Severity:** error | **Category:** security
808
-
809
- Flags security group ingress rules that allow unrestricted access (\`0.0.0.0/0\` or \`::/0\`) on sensitive ports (22, 3389, 3306, 5432, 1433, 6379, 27017). Restrict ingress to known CIDR ranges or security groups.
810
-
811
- ### WAW020 — IAM Policy Uses Wildcard Action
812
-
813
- **Severity:** warning | **Category:** security
814
-
815
- Flags IAM policy statements that use wildcard actions (\`"Action": "*"\` or \`"Action": "s3:*"\`). Use specific action names following the principle of least privilege.
816
-
817
- ### WAW021 — RDS Storage Not Encrypted
818
-
819
- **Severity:** error | **Category:** security
820
-
821
- Flags RDS instances without \`StorageEncrypted: true\`. All RDS instances should encrypt data at rest to meet compliance and security requirements.
822
-
823
- ### WAW022 — Lambda Not in VPC
824
-
825
- **Severity:** warning | **Category:** security
826
-
827
- Flags Lambda functions without a \`VpcConfig\`. Functions that access internal resources (databases, caches, internal APIs) should run inside a VPC. Functions that only call public APIs can safely skip VPC configuration.
828
-
829
- ### WAW023 — CloudFront Without WAF
830
-
831
- **Severity:** warning | **Category:** security
832
-
833
- Flags CloudFront distributions without a \`WebACLId\`. Attaching a WAF web ACL protects your distribution from common web exploits and bots.
834
-
835
- ### WAW024 — ALB Without Access Logging
836
-
837
- **Severity:** warning | **Category:** best practice
838
-
839
- Flags Application Load Balancers without access logging enabled. Enable \`access_logs.s3.enabled\` to capture request logs for debugging and compliance.
840
-
841
- ### WAW025 — SNS Topic Not Encrypted
842
-
843
- **Severity:** warning | **Category:** security
844
-
845
- Flags SNS topics without \`KmsMasterKeyId\`. Encrypting topics at rest protects sensitive notification payloads.
846
-
847
- ### WAW026 — SQS Queue Not Encrypted
848
-
849
- **Severity:** warning | **Category:** security
850
-
851
- Flags SQS queues without \`KmsMasterKeyId\` or \`SqsManagedSseEnabled\`. Encrypting queues at rest protects sensitive message payloads.
852
-
853
- ### WAW027 — DynamoDB Missing Point-in-Time Recovery
854
-
855
- **Severity:** info | **Category:** best practice
856
-
857
- Flags DynamoDB tables without \`PointInTimeRecoverySpecification.PointInTimeRecoveryEnabled\` set to \`true\`. Point-in-time recovery provides continuous backups and protects against accidental writes or deletes.
858
-
859
- ### WAW028 — EBS Volume Not Encrypted
860
-
861
- **Severity:** warning | **Category:** security
862
-
863
- Flags EBS volumes without \`Encrypted: true\`. All EBS volumes should encrypt data at rest for compliance and security.
864
-
865
- ### WAW031 — EKS Addon Missing ServiceAccountRoleArn
866
-
867
- **Severity:** warning | **Category:** correctness
868
-
869
- Flags EKS addons that require an IRSA role but don't have \`ServiceAccountRoleArn\` set. Without an IRSA role, the addon pods can't authenticate to AWS APIs and the addon hangs in CREATING status. Known addons that require IRSA: \`aws-ebs-csi-driver\`, \`aws-efs-csi-driver\`, \`adot\`, \`amazon-cloudwatch-observability\`.
870
-
871
- \`\`\`
872
- WAW031: EKS Addon "EbsCsiAddon" (aws-ebs-csi-driver) has no ServiceAccountRoleArn — it needs an IRSA role for EBS API access
873
- \`\`\`
874
-
875
- ## Running lint
876
-
877
- \`\`\`bash
878
- # Lint your chant project
879
- chant lint
880
-
881
- # Lint with auto-fix where supported
882
- chant lint --fix
883
- \`\`\`
884
-
885
- To suppress a rule on a specific line:
886
-
887
- \`\`\`typescript
888
- // chant-disable-next-line WAW001
889
- const endpoint = "s3.us-east-1.amazonaws.com";
890
- \`\`\`
891
-
892
- To suppress globally in \`chant.config.ts\`:
893
-
894
- \`\`\`typescript
895
- export default {
896
- lint: {
897
- rules: {
898
- WAW001: "off",
899
- },
900
- },
901
- };
902
- \`\`\`
903
-
904
- See also [Custom Lint Rules](../custom-rules/) for writing project-specific rules.`,
905
- },
906
- {
907
- slug: "custom-rules",
908
- title: "Custom Lint Rules",
909
- description: "Writing and registering project-specific lint rules for AWS CloudFormation",
910
- content: `Chant's lint engine runs TypeScript AST visitors. Write project-specific rules that enforce domain conventions.
911
-
912
- ## Anatomy of a lint rule
913
-
914
- The lambda-api example includes a full custom rule implementation:
915
-
916
- {{file:lambda-api/src/lint/api-timeout.ts}}
917
-
918
- The \`check\` function receives a \`LintContext\` containing the TypeScript \`sourceFile\` and returns an array of diagnostics with file, line, column, and message.
919
-
920
- ## Registering custom rules
921
-
922
- Add a \`chant.config.ts\` to your project:
923
-
924
- {{file:lambda-api/src/chant.config.ts}}
925
-
926
- The \`plugins\` array accepts relative paths. Each plugin module should export a \`LintRule\` object.`,
927
- },
928
- {
929
- slug: "examples",
930
- title: "Examples",
931
- description: "Walkthrough of the AWS CloudFormation lexicon examples",
932
- content: `Runnable examples live in the lexicon's \`examples/\` directory — one per built-in composite. Clone the repo and try them:
933
-
934
- \`\`\`bash
935
- cd examples/lambda-function
936
- npm install
937
- chant build # produces CloudFormation JSON
938
- chant lint # runs lint rules
939
- npx vitest run # run the tests
940
- \`\`\`
941
-
942
- ## Lambda Function
943
-
944
- \`examples/lambda-function/\` — the simplest possible example. Uses \`LambdaNode\` to create a basic Lambda.
945
-
946
- {{file:lambda-function/src/main.ts}}
947
-
948
- Produces 2 CloudFormation resources: IAM Role + Lambda Function.
949
-
950
- ## Lambda S3
951
-
952
- \`examples/lambda-s3/\` — Lambda that lists S3 objects using the \`LambdaS3\` composite.
953
-
954
- {{file:lambda-s3/src/main.ts}}
955
-
956
- Produces 3 resources: S3 Bucket (encrypted, public access blocked) + IAM Role (with S3 read policy) + Lambda Function. The \`BUCKET_NAME\` environment variable is auto-injected.
957
-
958
- ## Lambda DynamoDB
959
-
960
- \`examples/lambda-dynamodb/\` — Lambda that reads/writes DynamoDB items using the \`LambdaDynamoDB\` composite.
961
-
962
- {{file:lambda-dynamodb/src/main.ts}}
963
-
964
- Produces 3 resources: DynamoDB Table + IAM Role (with DynamoDB read/write policy) + Lambda Function. The \`TABLE_NAME\` environment variable is auto-injected.
965
-
966
- ## Lambda SQS
967
-
968
- \`examples/lambda-sqs/\` — Lambda processing messages from an SQS queue using the \`LambdaSqs\` composite.
969
-
970
- {{file:lambda-sqs/src/main.ts}}
971
-
972
- Produces 4 resources: SQS Queue + IAM Role (with SQS receive policy) + Lambda Function + EventSourceMapping.
973
-
974
- ## Lambda SNS
975
-
976
- \`examples/lambda-sns/\` — Lambda triggered by SNS notifications using the \`LambdaSns\` composite.
977
-
978
- {{file:lambda-sns/src/main.ts}}
979
-
980
- Produces 5 resources: SNS Topic + IAM Role + Lambda Function + SNS Subscription + Lambda Permission.
981
-
982
- ## Lambda Scheduled
983
-
984
- \`examples/lambda-scheduled/\` — Lambda on a cron schedule using the \`LambdaScheduled\` composite.
985
-
986
- {{file:lambda-scheduled/src/main.ts}}
987
-
988
- Produces 4 resources: IAM Role + Lambda Function + EventBridge Rule + Lambda Permission.
989
-
990
- ## Lambda EventBridge
991
-
992
- \`examples/lambda-eventbridge/\` — Lambda triggered by EventBridge events using the \`LambdaEventBridge\` composite.
993
-
994
- {{file:lambda-eventbridge/src/main.ts}}
995
-
996
- Produces 4 resources: EventBridge Rule + IAM Role + Lambda Function + Lambda Permission.
997
-
998
- ## VPC
999
-
1000
- \`examples/vpc/\` — production-ready VPC using the \`VpcDefault\` composite.
1001
-
1002
- {{file:vpc/src/main.ts}}
1003
-
1004
- Produces 17 CloudFormation resources: VPC, Internet Gateway, 2 public + 2 private subnets, NAT Gateway with EIP, route tables, routes, and associations.
1005
-
1006
- ## Fargate ALB
1007
-
1008
- \`examples/fargate-alb/\` — Fargate service behind an ALB, consuming a VPC. Demonstrates composite composability.
1009
-
1010
- {{file:fargate-alb/src/network.ts}}
1011
-
1012
- {{file:fargate-alb/src/service.ts}}
1013
-
1014
- Produces 28 CloudFormation resources: 17 from VpcDefault + 11 from FargateAlb (ECS Cluster, execution/task roles, log group, task definition, security groups, ALB, target group, listener, and ECS service).
1015
-
1016
- ## Multi-Service ALB
1017
-
1018
- \`examples/multi-service-alb/\` — multiple Fargate services behind a single shared ALB using \`AlbShared\` + \`FargateService\`.
1019
-
1020
- {{file:multi-service-alb/src/shared.ts}}
1021
-
1022
- {{file:multi-service-alb/src/services.ts}}
1023
-
1024
- Produces 36 CloudFormation resources: 17 from VpcDefault + 5 from AlbShared + 7×2 from FargateService (task role, log group, task definition, task security group, target group, listener rule, and ECS service per service).
1025
-
1026
- ## Shared ALB (Separate Projects)
1027
-
1028
- \`examples/shared-alb/\`, \`examples/shared-alb-api/\`, \`examples/shared-alb-ui/\` — the same multi-service ALB pattern as above, but split across separate CloudFormation stacks for independent deployment.
1029
-
1030
- ### Infra stack
1031
-
1032
- The shared-alb stack contains VPC, ALB, ECS cluster, and ECR repositories. It exports outputs that service stacks consume as parameters:
1033
-
1034
- {{file:shared-alb/src/alb.ts}}
1035
-
1036
- {{file:shared-alb/src/ecr.ts}}
1037
-
1038
- {{file:shared-alb/src/outputs.ts}}
1039
-
1040
- ### Service stacks
1041
-
1042
- Each service stack receives shared infrastructure as parameters and deploys a single Fargate service:
1043
-
1044
- {{file:shared-alb-api/src/params.ts}}
1045
-
1046
- {{file:shared-alb-api/src/service.ts}}
1047
-
1048
- **Deployment pattern:**
1049
-
1050
- 1. Deploy the infra stack first — creates VPC, ALB, ECS cluster, and ECR repos
1051
- 2. Deploy each service stack independently with \`--parameter-overrides\` mapping infra outputs to service parameters
1052
- 3. Each service gets its own \`image\` parameter for CI/CD pipelines to inject the container image URI
1053
-
1054
- The separate-project pattern enables independent team ownership and deployment cadences. See the [GitLab CI/CD lexicon examples](/chant/lexicons/gitlab/examples/) for pipeline definitions that automate this workflow.
1055
-
1056
- ## Lambda API (Custom Composite)
1057
-
1058
- \`examples/lambda-api/\` — demonstrates building your own composite factory with presets and a custom lint rule. This is the only example that teaches custom composite authoring.
1059
-
1060
- \`\`\`
1061
- src/
1062
- ├── chant.config.ts # Lint config: strict preset + custom plugin
1063
- ├── defaults.ts # Encryption, versioning, access block, Lambda trust policy
1064
- ├── data-bucket.ts # S3 bucket
1065
- ├── lambda-api.ts # Composite factory + SecureApi/HighMemoryApi presets
1066
- ├── health-api.ts # SecureApi — minimal health check
1067
- ├── upload-api.ts # SecureApi + S3 PutObject policy
1068
- ├── process-api.ts # HighMemoryApi + S3 read/write policy
1069
- └── lint/
1070
- └── api-timeout.ts # Custom WAW012 rule
1071
- \`\`\`
1072
-
1073
- **Patterns demonstrated:**
1074
-
1075
- - **Custom composites** — \`LambdaApi\` groups Role + Function + Permission into a reusable unit (see [Composites](../composites/))
1076
- - **Composite presets** — \`SecureApi\` (low memory, short timeout) and \`HighMemoryApi\` (high memory, longer timeout)
1077
- - **Custom lint rule** — \`api-timeout.ts\` enforces API Gateway's 29-second timeout limit (see [Custom Lint Rules](../custom-rules/))
1078
-
1079
- The example produces 10 CloudFormation resources: 1 S3 bucket + 3 composites × 3 members each.
1080
-
1081
- ## RDS Instance
1082
-
1083
- \`examples/rds-postgres/\` — production RDS PostgreSQL instance using the \`RdsInstance\` composite with VPC networking and SSM parameter references.
1084
-
1085
- {{file:rds-postgres/src/params.ts}}
1086
-
1087
- {{file:rds-postgres/src/network.ts}}
1088
-
1089
- {{file:rds-postgres/src/database.ts}}
1090
-
1091
- Produces a complete RDS stack: VPC infrastructure (from \`VpcDefault\`), DB subnet group, security group, and RDS instance with encrypted storage.`,
1092
- },
1093
- {
1094
- slug: "skills",
1095
- title: "AI Skills",
1096
- description: "AI agent skills bundled with the AWS CloudFormation lexicon",
1097
- content: `The AWS lexicon ships an AI skill called **chant-aws** that teaches AI coding agents (like Claude Code) how to build, validate, and deploy CloudFormation templates from a chant project.
1098
-
1099
- ## What are skills?
1100
-
1101
- Skills are structured markdown documents bundled with a lexicon. When an AI agent works in a chant project, it discovers and loads relevant skills automatically — giving it operational knowledge about the deployment workflow without requiring the user to explain each step.
1102
-
1103
- ## Installation
1104
-
1105
- When you scaffold a new project with \`chant init --lexicon aws\`, the skill is installed to \`skills/chant-aws/SKILL.md\` for automatic discovery by Claude Code.
1106
-
1107
- For existing projects, create the file manually:
1108
-
1109
- \`\`\`
1110
- .claude/
1111
- skills/
1112
- chant-aws/
1113
- SKILL.md # skill content (see below)
1114
- \`\`\`
1115
-
1116
- ## Skill: chant-aws
1117
-
1118
- The \`chant-aws\` skill covers the full deployment lifecycle:
1119
-
1120
- - **Build** — \`chant build src/ --output stack.json\`
1121
- - **Validate** — \`chant lint src/\` + \`aws cloudformation validate-template\`
1122
- - **Deploy** — \`aws cloudformation deploy\` with capabilities
1123
- - **Update** — change sets for preview, or direct deploy
1124
- - **Delete** — \`aws cloudformation delete-stack\`
1125
- - **Status** — \`describe-stacks\` and \`describe-stack-events\`
1126
- - **Troubleshooting** — event inspection, rollback recovery, drift detection
1127
-
1128
- The skill is invocable as a slash command: \`/chant-aws\`
1129
-
1130
- ## MCP integration
1131
-
1132
- The lexicon also provides MCP (Model Context Protocol) tools and resources that AI agents can use programmatically:
1133
-
1134
- | MCP tool | Description |
1135
- |----------|-------------|
1136
- | \`build\` | Build the chant project |
1137
- | \`lint\` | Run lint rules |
1138
- | \`explain\` | Summarize project resources |
1139
- | \`scaffold\` | Generate starter files |
1140
- | \`search\` | Search available resource types |
1141
- | \`aws:diff\` | Compare current build output against previous |
1142
-
1143
- | MCP resource | Description |
1144
- |--------------|-------------|
1145
- | \`resource-catalog\` | JSON list of all supported CloudFormation resource types |
1146
- | \`examples/basic-stack\` | Example stack with S3 bucket and IAM role |`,
1147
- },
1148
- ],
1149
- sidebarExtra: [
1150
- { label: "Deploying to EKS", slug: "eks-kubernetes" },
1151
- { label: "Nested Stacks", slug: "nested-stacks" },
1152
- ],
1153
121
  };
1154
122
 
1155
123
  log("Generating AWS documentation...");