@fjall/components-infrastructure 37.0.0 → 39.0.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 (72) hide show
  1. package/dist/lib/app.d.ts +52 -12
  2. package/dist/lib/app.js +60 -24
  3. package/dist/lib/config/aws/configRecorder.js +3 -0
  4. package/dist/lib/config/aws/disasterRecovery.d.ts +1 -2
  5. package/dist/lib/config/aws/disasterRecovery.js +167 -45
  6. package/dist/lib/patterns/aws/buildkite/buildkite.js +7 -2
  7. package/dist/lib/patterns/aws/cdn.d.ts +19 -11
  8. package/dist/lib/patterns/aws/cdn.js +53 -25
  9. package/dist/lib/patterns/aws/clickhouseDatabase.js +8 -4
  10. package/dist/lib/patterns/aws/computeEcs.js +17 -1
  11. package/dist/lib/patterns/aws/computeEcsTypes.d.ts +30 -4
  12. package/dist/lib/patterns/aws/computePropApplicability.js +1 -0
  13. package/dist/lib/patterns/aws/domain.js +3 -3
  14. package/dist/lib/patterns/aws/externalRecordsPattern.d.ts +9 -6
  15. package/dist/lib/patterns/aws/externalRecordsPattern.js +22 -46
  16. package/dist/lib/patterns/aws/interfaces/domain.d.ts +8 -5
  17. package/dist/lib/patterns/aws/interfaces/pattern.d.ts +9 -10
  18. package/dist/lib/patterns/aws/messaging.d.ts +10 -0
  19. package/dist/lib/patterns/aws/messaging.js +10 -6
  20. package/dist/lib/patterns/aws/payload.d.ts +5 -0
  21. package/dist/lib/patterns/aws/payload.js +23 -11
  22. package/dist/lib/patterns/aws/storage.d.ts +41 -0
  23. package/dist/lib/patterns/aws/storage.js +23 -8
  24. package/dist/lib/patterns/aws/targets/targetResolution.d.ts +4 -4
  25. package/dist/lib/patterns/aws/targets/targetResolution.js +4 -4
  26. package/dist/lib/resources/aws/backup/backupVault.d.ts +27 -2
  27. package/dist/lib/resources/aws/backup/backupVault.js +49 -6
  28. package/dist/lib/resources/aws/cdn/cloudFront.d.ts +42 -18
  29. package/dist/lib/resources/aws/cdn/cloudFront.js +39 -51
  30. package/dist/lib/resources/aws/cdn/cloudFrontLogDelivery.d.ts +90 -0
  31. package/dist/lib/resources/aws/cdn/cloudFrontLogDelivery.js +213 -0
  32. package/dist/lib/resources/aws/cdn/index.d.ts +1 -0
  33. package/dist/lib/resources/aws/cdn/index.js +1 -0
  34. package/dist/lib/resources/aws/compute/ecs.d.ts +7 -4
  35. package/dist/lib/resources/aws/compute/ecs.js +27 -21
  36. package/dist/lib/resources/aws/compute/ecsServiceFactory.js +3 -0
  37. package/dist/lib/resources/aws/compute/ecsTypes.d.ts +11 -0
  38. package/dist/lib/resources/aws/compute/ecsValidation.d.ts +8 -0
  39. package/dist/lib/resources/aws/compute/ecsValidation.js +8 -0
  40. package/dist/lib/resources/aws/compute/persistentDataVolume.d.ts +8 -0
  41. package/dist/lib/resources/aws/compute/persistentDataVolume.js +2 -0
  42. package/dist/lib/resources/aws/database/dynamodb.js +2 -1
  43. package/dist/lib/resources/aws/database/rdsAurora.js +6 -1
  44. package/dist/lib/resources/aws/database/rdsInstance.js +6 -1
  45. package/dist/lib/resources/aws/logging/accessLogBucket.d.ts +15 -8
  46. package/dist/lib/resources/aws/logging/accessLogBucket.js +16 -9
  47. package/dist/lib/resources/aws/logging/cloudTrail.js +3 -0
  48. package/dist/lib/resources/aws/messaging/eventBridgeQueueGrants.d.ts +16 -0
  49. package/dist/lib/resources/aws/messaging/eventBridgeQueueGrants.js +91 -0
  50. package/dist/lib/resources/aws/messaging/eventBridgeRule.js +36 -5
  51. package/dist/lib/resources/aws/messaging/eventTargets.d.ts +4 -3
  52. package/dist/lib/resources/aws/messaging/eventTargets.js +71 -31
  53. package/dist/lib/resources/aws/messaging/queuePolicyQuota.d.ts +25 -0
  54. package/dist/lib/resources/aws/messaging/queuePolicyQuota.js +190 -0
  55. package/dist/lib/resources/aws/messaging/schedule.d.ts +5 -5
  56. package/dist/lib/resources/aws/messaging/sqs.d.ts +36 -1
  57. package/dist/lib/resources/aws/messaging/sqs.js +40 -2
  58. package/dist/lib/resources/aws/messaging/subscription.d.ts +4 -3
  59. package/dist/lib/resources/aws/networking/vpc.js +12 -6
  60. package/dist/lib/resources/aws/secrets/stackEncryptionKey.d.ts +26 -1
  61. package/dist/lib/resources/aws/secrets/stackEncryptionKey.js +56 -1
  62. package/dist/lib/resources/aws/storage/s3.d.ts +21 -1
  63. package/dist/lib/resources/aws/storage/s3.js +89 -3
  64. package/dist/lib/utils/backupTierMapping.d.ts +23 -2
  65. package/dist/lib/utils/backupTierMapping.js +25 -0
  66. package/dist/lib/utils/cdkContext.d.ts +14 -0
  67. package/dist/lib/utils/cdkContext.js +38 -1
  68. package/dist/lib/utils/exportNaming.d.ts +30 -27
  69. package/dist/lib/utils/exportNaming.js +32 -28
  70. package/dist/lib/utils/standardTagsAspect.d.ts +8 -18
  71. package/dist/lib/utils/standardTagsAspect.js +8 -34
  72. package/package.json +4 -4
@@ -1,35 +1,38 @@
1
1
  import type { Construct } from "constructs";
2
2
  /**
3
- * Qualify a CloudFormation export name with the stack that emits it.
3
+ * Construct-side adapter for the stack-qualified export names whose contract
4
+ * is `@fjall/util`'s `stackExportNames` module.
4
5
  *
5
- * Export names are unique per account per region, so a name built only from a
6
- * construct id or a cluster name is shared by every app that happens to use
7
- * the same id — and the ids inside a pattern are literals the app author never
8
- * chooses. Two apps then collide, and the second one fails at CloudFormation
9
- * execution ("Export with name X is already exported by stack Y") after its
10
- * first resources exist, so it rolls back rather than refusing up front.
6
+ * Export names are unique per account per region, so a name built only from
7
+ * a construct id or a cluster name is shared by every app that happens to use
8
+ * the same id — and the ids inside a pattern are literals the app author
9
+ * never chooses. Two apps then collide, and the second one fails at
10
+ * CloudFormation execution ("Export with name X is already exported by stack
11
+ * Y") after its first resources exist, so it rolls back rather than refusing
12
+ * up front. Stack names are already unique per account+region and Fjall
13
+ * derives them from the app name, so qualifying with the stack makes the
14
+ * export unique AND puts the app name in it.
11
15
  *
12
- * Stack names are already unique per account+region (CloudFormation enforces
13
- * it) and Fjall derives them from the app name, so prefixing with the stack
14
- * name makes the export globally unique AND puts the app name in it.
16
+ * `@fjall/util` owns the composition — `stackScopedExportName` and the
17
+ * builders on top of it (`deployableServiceExportName`,
18
+ * `deployableClusterExportName`, `clusterArnExportName`,
19
+ * `vpcCidrExportName`) — so the runtime consumers that read an export back
20
+ * (deploy-core's `EcsServiceResolver`, the fjall.io webapp's VPC CIDR
21
+ * capture) rebuild the exact name through the same function the construct
22
+ * emitted it with. This helper only supplies the stack: a construct passes
23
+ * itself and the suffix is qualified with the stack it lives in. An export a
24
+ * consumer rebuilds by whole name goes through its specific builder with
25
+ * `Stack.of(this).stackName`, never through this generic form.
15
26
  *
16
- * The suffix must stay last. Consumers match these by suffix and by app-name
17
- * token, never by whole string — `EcsServiceResolver` filters on
18
- * `endsWith("deployableservice")` (deploy-core
19
- * services/infrastructure/EcsServiceResolver.ts) and the webapp finds a linked
20
- * app's CIDR by `ListExports` + a `-vpc-cidr` suffix filter
21
- * (webapp app/.server/services/infrastructure/captureVpcCidr.ts). Prefixing is
22
- * transparent to both; changing the tail is not.
23
- *
24
- * NOT for exports a consumer reconstructs by whole name. Those are contracts
25
- * with their own vocabulary module and must keep their exact spelling:
26
- * `getDomainExportNames` (@fjall/util), `EXPORT_NAMES`
27
- * (patterns/aws/targets/targetResolution.ts), `SHARED_ALARM_TOPIC_EXPORT_NAME`,
28
- * the CloudTrail output keys, and `${toPascalCase(appName)}EcrRepositoryName`
29
- * (read by `resolveEcrRepositoryName` in the CLI). Account-level singletons
30
- * (`AccountId`, `Environment`, `OrganisationId`, `FjallAuditRoleArn`, …) also
31
- * keep their bare names: exactly one is meant to exist, so a collision there
32
- * correctly reports a duplicate deployment.
27
+ * NOT for exports a consumer reconstructs from its own vocabulary module.
28
+ * Those keep their exact spelling: `getDomainExportNames` (@fjall/util),
29
+ * `EXPORT_NAMES` (patterns/aws/targets/targetResolution.ts),
30
+ * `SHARED_ALARM_TOPIC_EXPORT_NAME`, the CloudTrail output keys, and
31
+ * `${toPascalCase(appName)}EcrRepositoryName` (read by
32
+ * `resolveEcrRepositoryName` in the CLI). Account-level singletons
33
+ * (`AccountId`, `Environment`, `OrganisationId`, `FjallAuditRoleArn`, …)
34
+ * also keep their bare names: exactly one is meant to exist, so a collision
35
+ * there correctly reports a duplicate deployment.
33
36
  */
34
37
  export declare function stackScopedExportName(scope: Construct, suffix: string): string;
35
38
  /**
@@ -1,38 +1,42 @@
1
1
  import { Stack } from "aws-cdk-lib";
2
+ import { stackScopedExportName as qualifyWithStack } from "@fjall/util";
2
3
  /**
3
- * Qualify a CloudFormation export name with the stack that emits it.
4
+ * Construct-side adapter for the stack-qualified export names whose contract
5
+ * is `@fjall/util`'s `stackExportNames` module.
4
6
  *
5
- * Export names are unique per account per region, so a name built only from a
6
- * construct id or a cluster name is shared by every app that happens to use
7
- * the same id — and the ids inside a pattern are literals the app author never
8
- * chooses. Two apps then collide, and the second one fails at CloudFormation
9
- * execution ("Export with name X is already exported by stack Y") after its
10
- * first resources exist, so it rolls back rather than refusing up front.
7
+ * Export names are unique per account per region, so a name built only from
8
+ * a construct id or a cluster name is shared by every app that happens to use
9
+ * the same id — and the ids inside a pattern are literals the app author
10
+ * never chooses. Two apps then collide, and the second one fails at
11
+ * CloudFormation execution ("Export with name X is already exported by stack
12
+ * Y") after its first resources exist, so it rolls back rather than refusing
13
+ * up front. Stack names are already unique per account+region and Fjall
14
+ * derives them from the app name, so qualifying with the stack makes the
15
+ * export unique AND puts the app name in it.
11
16
  *
12
- * Stack names are already unique per account+region (CloudFormation enforces
13
- * it) and Fjall derives them from the app name, so prefixing with the stack
14
- * name makes the export globally unique AND puts the app name in it.
17
+ * `@fjall/util` owns the composition — `stackScopedExportName` and the
18
+ * builders on top of it (`deployableServiceExportName`,
19
+ * `deployableClusterExportName`, `clusterArnExportName`,
20
+ * `vpcCidrExportName`) — so the runtime consumers that read an export back
21
+ * (deploy-core's `EcsServiceResolver`, the fjall.io webapp's VPC CIDR
22
+ * capture) rebuild the exact name through the same function the construct
23
+ * emitted it with. This helper only supplies the stack: a construct passes
24
+ * itself and the suffix is qualified with the stack it lives in. An export a
25
+ * consumer rebuilds by whole name goes through its specific builder with
26
+ * `Stack.of(this).stackName`, never through this generic form.
15
27
  *
16
- * The suffix must stay last. Consumers match these by suffix and by app-name
17
- * token, never by whole string — `EcsServiceResolver` filters on
18
- * `endsWith("deployableservice")` (deploy-core
19
- * services/infrastructure/EcsServiceResolver.ts) and the webapp finds a linked
20
- * app's CIDR by `ListExports` + a `-vpc-cidr` suffix filter
21
- * (webapp app/.server/services/infrastructure/captureVpcCidr.ts). Prefixing is
22
- * transparent to both; changing the tail is not.
23
- *
24
- * NOT for exports a consumer reconstructs by whole name. Those are contracts
25
- * with their own vocabulary module and must keep their exact spelling:
26
- * `getDomainExportNames` (@fjall/util), `EXPORT_NAMES`
27
- * (patterns/aws/targets/targetResolution.ts), `SHARED_ALARM_TOPIC_EXPORT_NAME`,
28
- * the CloudTrail output keys, and `${toPascalCase(appName)}EcrRepositoryName`
29
- * (read by `resolveEcrRepositoryName` in the CLI). Account-level singletons
30
- * (`AccountId`, `Environment`, `OrganisationId`, `FjallAuditRoleArn`, …) also
31
- * keep their bare names: exactly one is meant to exist, so a collision there
32
- * correctly reports a duplicate deployment.
28
+ * NOT for exports a consumer reconstructs from its own vocabulary module.
29
+ * Those keep their exact spelling: `getDomainExportNames` (@fjall/util),
30
+ * `EXPORT_NAMES` (patterns/aws/targets/targetResolution.ts),
31
+ * `SHARED_ALARM_TOPIC_EXPORT_NAME`, the CloudTrail output keys, and
32
+ * `${toPascalCase(appName)}EcrRepositoryName` (read by
33
+ * `resolveEcrRepositoryName` in the CLI). Account-level singletons
34
+ * (`AccountId`, `Environment`, `OrganisationId`, `FjallAuditRoleArn`, …)
35
+ * also keep their bare names: exactly one is meant to exist, so a collision
36
+ * there correctly reports a duplicate deployment.
33
37
  */
34
38
  export function stackScopedExportName(scope, suffix) {
35
- return `${Stack.of(scope).stackName}${suffix}`;
39
+ return qualifyWithStack(Stack.of(scope).stackName, suffix);
36
40
  }
37
41
  /**
38
42
  * Export name for the account's IPAM private default scope.
@@ -5,12 +5,19 @@ import { type IConstruct } from "constructs";
5
5
  *
6
6
  * This aspect handles tags that depend on resource-specific properties:
7
7
  * - fjall:operations:pool on VPCs with IPAM configuration
8
- * - fjall:disasterRecovery:tier on S3 buckets with backup configuration
9
8
  *
10
9
  * Standard Fjall tags (costAllocation:environment, costAllocation:service, etc.)
11
10
  * are applied via Tags.of(app).add() in the App class, which properly merges
12
11
  * with existing tags from L2 constructs.
13
12
  *
13
+ * The disaster-recovery tier tag is NOT applied here. It is stamped at
14
+ * construction by the five constructs that own a backup-eligible resource
15
+ * (`tagBackupTier`), because an aspect can only reach the resource by walking
16
+ * up the scope chain — which cannot tell an Aurora cluster from its member
17
+ * instances, or a primary from its read replica — and because `Tags.of(...)`
18
+ * called inside `visit()` is an aspect adding an aspect, dropped silently when
19
+ * `aspectStabilization` is off.
20
+ *
14
21
  * This aspect uses Tags.of(node).add() to properly merge with existing tags
15
22
  */
16
23
  export declare class StandardTagsAspect implements IAspect {
@@ -21,21 +28,4 @@ export declare class StandardTagsAspect implements IAspect {
21
28
  * Tag format: fjall:operations:pool = "{accountId}-{region}"
22
29
  */
23
30
  private addIpamPoolTag;
24
- /**
25
- * Add the disaster recovery tier tag to S3 buckets from the bucket's
26
- * effective tier — the per-bucket `backupVaultTier` override, else the
27
- * app-level tier the factory threaded in. CDK child tags override parent
28
- * tags, so an override wins over the app-level tag set via
29
- * `App.getApp({ backup })`; an inherited app tier writes the value that tag
30
- * already carries. The same resolution decides the bucket's versioning
31
- * default (`S3Bucket`).
32
- *
33
- * Tag format: fjall:disasterRecovery:tier = "{tier}"
34
- *
35
- * Tier mapping:
36
- * - standard -> "default" (matches existing backup plans)
37
- * - resilient -> "resilient"
38
- * - enterprise -> "enterprise"
39
- */
40
- private addBackupTierTag;
41
31
  }
@@ -1,17 +1,23 @@
1
1
  import { CfnResource, Stack, Tags } from "aws-cdk-lib";
2
- import { BACKUP_TIER_TAG_KEY, BACKUP_TIER_TAG_MAP } from "./backupTierMapping.js";
3
2
  import { formatIpamPairTagValue, IPAM_OPERATIONS_POOL_TAG_KEY } from "@fjall/util/aws";
4
3
  /**
5
4
  * Aspect to apply special Fjall tags to specific resource types.
6
5
  *
7
6
  * This aspect handles tags that depend on resource-specific properties:
8
7
  * - fjall:operations:pool on VPCs with IPAM configuration
9
- * - fjall:disasterRecovery:tier on S3 buckets with backup configuration
10
8
  *
11
9
  * Standard Fjall tags (costAllocation:environment, costAllocation:service, etc.)
12
10
  * are applied via Tags.of(app).add() in the App class, which properly merges
13
11
  * with existing tags from L2 constructs.
14
12
  *
13
+ * The disaster-recovery tier tag is NOT applied here. It is stamped at
14
+ * construction by the five constructs that own a backup-eligible resource
15
+ * (`tagBackupTier`), because an aspect can only reach the resource by walking
16
+ * up the scope chain — which cannot tell an Aurora cluster from its member
17
+ * instances, or a primary from its read replica — and because `Tags.of(...)`
18
+ * called inside `visit()` is an aspect adding an aspect, dropped silently when
19
+ * `aspectStabilization` is off.
20
+ *
15
21
  * This aspect uses Tags.of(node).add() to properly merge with existing tags
16
22
  */
17
23
  export class StandardTagsAspect {
@@ -31,9 +37,6 @@ export class StandardTagsAspect {
31
37
  if (resource.cfnResourceType === "AWS::EC2::VPC") {
32
38
  this.addIpamPoolTag(resource);
33
39
  }
34
- if (resource.cfnResourceType === "AWS::S3::Bucket") {
35
- this.addBackupTierTag(resource);
36
- }
37
40
  }
38
41
  /**
39
42
  * Add IPAM pool tag to VPCs that use IPAM for IP address allocation.
@@ -53,33 +56,4 @@ export class StandardTagsAspect {
53
56
  Tags.of(vpc).add(IPAM_OPERATIONS_POOL_TAG_KEY, formatIpamPairTagValue(accountId, region));
54
57
  }
55
58
  }
56
- /**
57
- * Add the disaster recovery tier tag to S3 buckets from the bucket's
58
- * effective tier — the per-bucket `backupVaultTier` override, else the
59
- * app-level tier the factory threaded in. CDK child tags override parent
60
- * tags, so an override wins over the app-level tag set via
61
- * `App.getApp({ backup })`; an inherited app tier writes the value that tag
62
- * already carries. The same resolution decides the bucket's versioning
63
- * default (`S3Bucket`).
64
- *
65
- * Tag format: fjall:disasterRecovery:tier = "{tier}"
66
- *
67
- * Tier mapping:
68
- * - standard -> "default" (matches existing backup plans)
69
- * - resilient -> "resilient"
70
- * - enterprise -> "enterprise"
71
- */
72
- addBackupTierTag(resource) {
73
- let construct = resource;
74
- while (construct) {
75
- const backupConstruct = construct;
76
- if (backupConstruct.effectiveBackupTier) {
77
- const tier = backupConstruct.effectiveBackupTier;
78
- const tagValue = BACKUP_TIER_TAG_MAP[tier];
79
- Tags.of(resource).add(BACKUP_TIER_TAG_KEY, tagValue);
80
- break;
81
- }
82
- construct = construct.node.scope;
83
- }
84
- }
85
59
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fjall/components-infrastructure",
3
- "version": "37.0.0",
3
+ "version": "39.0.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/fjall-tech/fjall.git",
@@ -76,12 +76,12 @@
76
76
  "reflect-metadata": "^0.2.2",
77
77
  "tsx": "^4.23.1",
78
78
  "typescript": "^6.0.3",
79
- "vitest": "^4.1.10"
79
+ "vitest": "^4.1.11"
80
80
  },
81
81
  "dependencies": {
82
82
  "@aws-sdk/client-organizations": "^3.1098.0",
83
- "@fjall/generator": "^37.0.0",
84
- "@fjall/util": "^37.0.0",
83
+ "@fjall/generator": "^39.0.0",
84
+ "@fjall/util": "^39.0.0",
85
85
  "constructs": "^10.7.2",
86
86
  "zod": "^4.4.3"
87
87
  },