@fjall/components-infrastructure 4.4.0 → 6.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 (46) hide show
  1. package/dist/lib/config/aws/ebsDefaultEncryption.js +1 -1
  2. package/dist/lib/config/aws/inspectorEnablement.js +1 -1
  3. package/dist/lib/config/aws/oidcConnector.d.ts +6 -6
  4. package/dist/lib/config/aws/oidcConnector.js +1 -1
  5. package/dist/lib/config/aws/s3BlockPublicAccess.js +1 -1
  6. package/dist/lib/config/aws/securityServicesAdmin.js +2 -2
  7. package/dist/lib/lambda-assets/cert-generator/asset/index.js +31 -31
  8. package/dist/lib/patterns/aws/account.d.ts +11 -10
  9. package/dist/lib/patterns/aws/account.js +11 -10
  10. package/dist/lib/patterns/aws/clickhouseDatabase.d.ts +16 -3
  11. package/dist/lib/patterns/aws/clickhouseDatabase.js +23 -2
  12. package/dist/lib/patterns/aws/compute.js +1 -1
  13. package/dist/lib/patterns/aws/computeEcs.js +5 -0
  14. package/dist/lib/patterns/aws/computeEcsTypes.d.ts +12 -2
  15. package/dist/lib/patterns/aws/computeLambda.d.ts +1 -1
  16. package/dist/lib/patterns/aws/database.js +5 -5
  17. package/dist/lib/patterns/aws/devSubstrate.js +1 -1
  18. package/dist/lib/patterns/aws/payload.js +3 -3
  19. package/dist/lib/patterns/aws/staticSite.js +1 -1
  20. package/dist/lib/resources/aws/compute/ec2GracefulTerminationHandler.js +1 -1
  21. package/dist/lib/resources/aws/compute/ecs.js +1 -0
  22. package/dist/lib/resources/aws/compute/ecsCapacityConfig.d.ts +43 -0
  23. package/dist/lib/resources/aws/compute/ecsCapacityConfig.js +161 -0
  24. package/dist/lib/resources/aws/compute/ecsConstants.d.ts +18 -0
  25. package/dist/lib/resources/aws/compute/ecsConstants.js +32 -0
  26. package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.js +1 -1
  27. package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +9 -6
  28. package/dist/lib/resources/aws/compute/ecsServiceFactory.js +17 -41
  29. package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +2 -6
  30. package/dist/lib/resources/aws/compute/ecsValidation.js +15 -2
  31. package/dist/lib/resources/aws/compute/persistentDataVolume.js +1 -1
  32. package/dist/lib/resources/aws/database/clickhouseConstants.d.ts +58 -25
  33. package/dist/lib/resources/aws/database/clickhouseConstants.js +89 -25
  34. package/dist/lib/resources/aws/database/clickhouseUserData.js +77 -4
  35. package/dist/lib/resources/aws/database/rdsAurora.js +1 -1
  36. package/dist/lib/resources/aws/database/rdsInstance.js +2 -2
  37. package/dist/lib/resources/aws/iam/identityCenter/user.js +1 -1
  38. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.d.ts +9 -5
  39. package/dist/lib/resources/aws/monitoring/clickhouseAlarms.js +21 -12
  40. package/dist/lib/resources/aws/networking/crossAccountReturnRoutes.js +1 -1
  41. package/dist/lib/resources/aws/networking/ipamPool.js +1 -1
  42. package/dist/lib/resources/aws/organisation/costAllocationTagActivator.js +1 -1
  43. package/dist/lib/resources/aws/utilities/tlsCertGenerator.js +1 -1
  44. package/dist/lib/utils/engineCompat.d.ts +42 -13
  45. package/dist/lib/utils/engineCompat.js +111 -25
  46. package/package.json +30 -23
@@ -26,10 +26,21 @@
26
26
  * hand-edit. A same-major-or-newer engine proceeds; an older-major engine
27
27
  * hard-refuses.
28
28
  *
29
- * The optional `maximumEngineMajor` / `minimumAwsCdkCli` bounds are deliberately
30
- * NOT emitted in Phase 1 they are ADR open question Q5 (engine-ahead cap) and
31
- * the Phase-2 FM1 aws-cdk floor graft respectively, both unresolved and
32
- * deferred. This emitter stamps only the two required fields.
29
+ * `minimumAwsCdkCli` (Phase 2, FM1) is the floor for the deploy engine's bundled
30
+ * `aws-cdk` CLI: an engine whose CLI is older than the version these constructs
31
+ * were blessed against cannot reliably read the cloud assembly they synthesised.
32
+ * The aws-cdk CLI and aws-cdk-lib version axes DIVERGED in CDK v2 (CLI ~2.1134.x
33
+ * vs lib ~2.262.x), so this is a distinct check from `minimumEngineVersion`, on a
34
+ * different axis. It is derived from the constructs' OWN
35
+ * `peerDependencies["aws-cdk"]` floor — self-honest, and shipped with the package
36
+ * (unlike the blessed-pin JSON, which is not in `files`) — and conditional-spread
37
+ * so a package whose peer floor cannot be resolved simply omits it (fail-safe: the
38
+ * FM1 check degrades to no-op rather than emitting a garbage floor).
39
+ *
40
+ * The optional `maximumEngineMajor` bound is deliberately NOT emitted — it is ADR
41
+ * open question Q5 (engine-ahead cap), and capping a newer engine would break the
42
+ * normal fjall-upgrade-then-redeploy path. The classifier still honours one if a
43
+ * future assembly stamps it by hand; this emitter never does.
33
44
  *
34
45
  * Fail-safe: if the constructs' own version cannot be resolved (a broken
35
46
  * package layout), `buildEngineCompat` returns undefined and the caller stamps
@@ -43,7 +54,51 @@ import { existsSync, readFileSync } from "fs";
43
54
  import { getErrorMessage, maskSensitiveOutput } from "@fjall/util";
44
55
  import { FjallLogger } from "./validationLogger.js";
45
56
  const PACKAGE_NAME = "@fjall/components-infrastructure";
46
- function resolveConstructsVersion() {
57
+ /**
58
+ * Normalise an npm dependency RANGE to its minimum X.Y.Z floor: strip a leading
59
+ * inclusive range operator (`^`, `~`, `>=`, `=`, `v`), then reconstruct
60
+ * `${major}.${minor}.${patch}` (missing minor/patch default to 0).
61
+ *
62
+ * Deliberately conservative — returns undefined (→ the caller omits the field,
63
+ * FM1 degrades to no-op) for every shape whose true floor the first-token strip
64
+ * would MIS-state rather than merely fail to state:
65
+ * - unions (`^3 || ^2` floors at 2.0.0, not the first branch's 3.0.0)
66
+ * - strict lower bounds (`>2.1134.0` excludes its own literal)
67
+ * - prerelease bounds (`^2.0.0-rc.1` floors below the stripped 2.0.0)
68
+ * - non-decimal segments (`1e2`, hex, `2.x`, `*`, git URLs, 4+ segments)
69
+ * A wrong floor is worse than no floor: over-strict refuses a valid engine,
70
+ * under-strict admits an incompatible one — both silently.
71
+ */
72
+ export function rangeFloor(range) {
73
+ if (range === undefined || range.length === 0)
74
+ return undefined;
75
+ const trimmed = range.trim();
76
+ if (trimmed.includes("||"))
77
+ return undefined;
78
+ const firstToken = trimmed.split(/\s+/)[0] ?? "";
79
+ if (/^>(?!=)/.test(firstToken))
80
+ return undefined;
81
+ const core = firstToken.replace(/^[\^~>=v ]+/i, "");
82
+ if (/[-+]/.test(core))
83
+ return undefined;
84
+ const parts = core.split(".");
85
+ if (parts.length === 0 || parts.length > 3)
86
+ return undefined;
87
+ if (!parts.every((segment) => /^\d+$/.test(segment)))
88
+ return undefined;
89
+ const major = Number(parts[0]);
90
+ const minor = parts.length > 1 ? Number(parts[1]) : 0;
91
+ const patch = parts.length > 2 ? Number(parts[2]) : 0;
92
+ return `${major}.${minor}.${patch}`;
93
+ }
94
+ /**
95
+ * Resolve the constructs' own package.json (version + aws-cdk peer floor) by the
96
+ * same upward `name`-matching walk `DEPLOY_CORE_VERSION` uses — robust across the
97
+ * source (`lib/utils/`) and compiled (`dist/lib/utils/`) layouts, and skipping any
98
+ * unrelated `package.json` higher up (a consuming app's). Returns undefined only
99
+ * when the walk finds no matching `package.json` (broken layout).
100
+ */
101
+ function resolveConstructsPackageMeta() {
47
102
  try {
48
103
  let dir = dirname(fileURLToPath(import.meta.url));
49
104
  const { root } = parse(dir);
@@ -54,7 +109,21 @@ function resolveConstructsVersion() {
54
109
  if (pkg.name === PACKAGE_NAME &&
55
110
  typeof pkg.version === "string" &&
56
111
  pkg.version.length > 0) {
57
- return pkg.version;
112
+ const awsCdkPeer = pkg.peerDependencies?.["aws-cdk"];
113
+ const awsCdkCliFloor = typeof awsCdkPeer === "string" ? rangeFloor(awsCdkPeer) : undefined;
114
+ if (awsCdkCliFloor === undefined) {
115
+ // The FM1 omission is fail-safe but must never be silent — an
116
+ // assembly without minimumAwsCdkCli skips the deploy engine's
117
+ // CLI-floor check entirely.
118
+ FjallLogger.warn(typeof awsCdkPeer === "string"
119
+ ? `engineCompat: aws-cdk peer range "${awsCdkPeer}" has no ` +
120
+ `derivable minimum floor — minimumAwsCdkCli will be ` +
121
+ `omitted and the deploy engine's CLI-floor check skipped`
122
+ : "engineCompat: no aws-cdk peerDependency found — " +
123
+ "minimumAwsCdkCli will be omitted and the deploy " +
124
+ "engine's CLI-floor check skipped");
125
+ }
126
+ return { version: pkg.version, awsCdkCliFloor };
58
127
  }
59
128
  }
60
129
  if (dir === root)
@@ -63,36 +132,51 @@ function resolveConstructsVersion() {
63
132
  }
64
133
  }
65
134
  catch (error) {
66
- FjallLogger.warn(`Could not resolve ${PACKAGE_NAME} version for engineCompat: ` +
135
+ FjallLogger.warn(`Could not resolve ${PACKAGE_NAME} metadata for engineCompat: ` +
67
136
  maskSensitiveOutput(getErrorMessage(error)));
68
137
  return undefined;
69
138
  }
70
139
  }
140
+ const CONSTRUCTS_PACKAGE_META = resolveConstructsPackageMeta();
71
141
  /**
72
142
  * The constructs' own package version — the assembly's `synthesisedBy`.
73
143
  * `undefined` only when the package layout is broken enough that the upward
74
144
  * walk finds no matching `package.json`.
75
145
  */
76
- export const CONSTRUCTS_VERSION = resolveConstructsVersion();
146
+ export const CONSTRUCTS_VERSION = CONSTRUCTS_PACKAGE_META?.version;
147
+ /**
148
+ * The minimum `aws-cdk` CLI floor these constructs were blessed against, derived
149
+ * from their own `peerDependencies["aws-cdk"]` — the FM1 `minimumAwsCdkCli` emit
150
+ * value. `undefined` when the peer is absent or non-semver (the field is then
151
+ * omitted from the stamped block).
152
+ */
153
+ export const AWS_CDK_CLI_FLOOR = CONSTRUCTS_PACKAGE_META?.awsCdkCliFloor;
77
154
  /**
78
155
  * Pure core of the emitter: derive the `engineCompat` block from a given
79
- * version string (or undefined). Both the absent/empty guard and the
80
- * `${major}.0.0` non-integer-major derivation live here — the single source of
81
- * the derivation, delegated to by both `MINIMUM_ENGINE_VERSION` and
82
- * `buildEngineCompat` — so the fail-safe and malformed-version paths are
83
- * unit-testable directly, without mocking module-load state. Returns undefined
84
- * when the version is absent, empty, or has no valid integer major (fail-safe:
85
- * the caller stamps no block).
156
+ * version string (or undefined) and an optional aws-cdk CLI floor. Both the
157
+ * absent/empty guard and the `${major}.0.0` non-integer-major derivation live
158
+ * here — the single source of the derivation, delegated to by both
159
+ * `MINIMUM_ENGINE_VERSION` and `buildEngineCompat` — so the fail-safe and
160
+ * malformed-version paths are unit-testable directly, without mocking
161
+ * module-load state. Returns undefined when the version is absent, empty, or has
162
+ * no valid integer major (fail-safe: the caller stamps no block).
163
+ *
164
+ * `awsCdkCliFloor` is conditional-spread: an absent/empty floor omits
165
+ * `minimumAwsCdkCli` entirely (Zod-pitfall-2 safe — never `undefined`), so the
166
+ * FM1 check degrades to no-op rather than shipping a malformed constraint.
86
167
  */
87
- export function buildEngineCompatFrom(version) {
168
+ export function buildEngineCompatFrom(version, awsCdkCliFloor) {
88
169
  if (version === undefined || version.length === 0)
89
170
  return undefined;
90
- const major = Number(version.split(".")[0]);
91
- if (!Number.isInteger(major) || major < 0)
171
+ const majorToken = version.split(".")[0] ?? "";
172
+ if (!/^\d+$/.test(majorToken))
92
173
  return undefined;
174
+ const major = Number(majorToken);
93
175
  return {
94
176
  synthesisedBy: version,
95
- minimumEngineVersion: `${major}.0.0`
177
+ minimumEngineVersion: `${major}.0.0`,
178
+ ...(awsCdkCliFloor !== undefined &&
179
+ awsCdkCliFloor.length > 0 && { minimumAwsCdkCli: awsCdkCliFloor })
96
180
  };
97
181
  }
98
182
  /**
@@ -107,12 +191,14 @@ export const MINIMUM_ENGINE_VERSION = buildEngineCompatFrom(CONSTRUCTS_VERSION)?
107
191
  * when the constructs' own version cannot be resolved (fail-safe: the caller
108
192
  * stamps no block and the deploy degrades to a constraint-free proceed).
109
193
  *
110
- * Phase 1 emits only the two required fields. The verdict classification of the
111
- * emitted floor is proven in `@fjall/deploy-core`'s
112
- * `orchestration/application/__tests__/engineCompat.test.ts` (a `4.0.0` floor
113
- * refuses a 3.x engine, proceeds for 4.x/5.x); this module's tests prove the
114
- * emitted shape matches that floor and reads back through the gate's reader.
194
+ * Emits the two required fields plus `minimumAwsCdkCli` (Phase 2 FM1) whenever the
195
+ * aws-cdk peer floor resolves. The verdict classification of the emitted floors is
196
+ * proven in `@fjall/deploy-core`'s
197
+ * `orchestration/application/__tests__/engineCompat.test.ts` (a `4.0.0` engine
198
+ * floor refuses a 3.x engine; a `minimumAwsCdkCli` floor refuses an older bundled
199
+ * CLI); this module's tests prove the emitted shape matches those floors and reads
200
+ * back through the gate's reader.
115
201
  */
116
202
  export function buildEngineCompat() {
117
- return buildEngineCompatFrom(CONSTRUCTS_VERSION);
203
+ return buildEngineCompatFrom(CONSTRUCTS_VERSION, AWS_CDK_CLI_FLOOR);
118
204
  }
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@fjall/components-infrastructure",
3
- "version": "4.4.0",
3
+ "version": "6.0.0",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/fjall-tech/fjall.git",
7
+ "directory": "components/infrastructure"
8
+ },
4
9
  "license": "SEE LICENSE IN LICENSE",
5
10
  "type": "module",
6
11
  "bin": {
@@ -34,53 +39,55 @@
34
39
  "clean": "rm -rf ./dist",
35
40
  "clean:node": "rm -rf ./node_modules",
36
41
  "build:cert-gen-lambda": "node lib/lambda-assets/cert-generator/src/build.mjs",
37
- "build": "npm run build:cert-gen-lambda && tsc && cp -r lib/layers dist/lib/layers && mkdir -p dist/lib/lambda-assets/cert-generator/asset && cp lib/lambda-assets/cert-generator/asset/index.js dist/lib/lambda-assets/cert-generator/asset/index.js && cp lib/lambda-assets/cert-generator/asset/package.json dist/lib/lambda-assets/cert-generator/asset/package.json && mkdir -p dist/lib/lambda-assets/identity-store-user/asset && cp lib/lambda-assets/identity-store-user/asset/index.js dist/lib/lambda-assets/identity-store-user/asset/index.js && cp lib/lambda-assets/identity-store-user/asset/package.json dist/lib/lambda-assets/identity-store-user/asset/package.json && mkdir -p dist/lib/lambda-assets/static-site-forms/asset && cp lib/lambda-assets/static-site-forms/asset/index.js dist/lib/lambda-assets/static-site-forms/asset/index.js && cp lib/lambda-assets/static-site-forms/asset/package.json dist/lib/lambda-assets/static-site-forms/asset/package.json && cp lib/resources/aws/compute/lifecycleHookLambda.source.cjs dist/lib/resources/aws/compute/lifecycleHookLambda.source.cjs && cp lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs dist/lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs && cp lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs dist/lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs && cp lib/patterns/aws/devSubstrate.waker.source.cjs dist/lib/patterns/aws/devSubstrate.waker.source.cjs",
42
+ "build": "npm run build:cert-gen-lambda && tsc && cp -r lib/layers dist/lib/layers && mkdir -p dist/lib/lambda-assets/cert-generator/asset && cp lib/lambda-assets/cert-generator/asset/index.js dist/lib/lambda-assets/cert-generator/asset/index.js && cp lib/lambda-assets/cert-generator/asset/package.json dist/lib/lambda-assets/cert-generator/asset/package.json && mkdir -p dist/lib/lambda-assets/identity-store-user/asset && cp lib/lambda-assets/identity-store-user/asset/index.js dist/lib/lambda-assets/identity-store-user/asset/index.js && cp lib/lambda-assets/identity-store-user/asset/package.json dist/lib/lambda-assets/identity-store-user/asset/package.json && mkdir -p dist/lib/lambda-assets/static-site-forms/asset && cp lib/lambda-assets/static-site-forms/asset/index.js dist/lib/lambda-assets/static-site-forms/asset/index.js && cp lib/lambda-assets/static-site-forms/asset/package.json dist/lib/lambda-assets/static-site-forms/asset/package.json && cp lib/resources/aws/compute/lifecycleHookLambda.source.cjs dist/lib/resources/aws/compute/lifecycleHookLambda.source.cjs && cp lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs dist/lib/resources/aws/compute/ec2GracefulTerminationLambda.source.cjs && cp lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs dist/lib/resources/aws/compute/persistentDataVolumeLambda.source.cjs && cp lib/patterns/aws/devSubstrate.waker.source.cjs dist/lib/patterns/aws/devSubstrate.waker.source.cjs && node ../../scripts/stamp-build.mjs",
38
43
  "prepack": "node ../../scripts/check-dist-freshness.mjs",
39
44
  "watch": "tsc -w",
40
45
  "watch:only": "tsc -w",
41
46
  "test": "vitest run",
42
47
  "test:watch": "vitest",
48
+ "contract:manifest": "FJALL_WRITE_CONTRACT_MANIFEST=1 vitest run lib/__tests__/_contract/contract.test.ts",
43
49
  "cdk": "cdk",
44
50
  "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
45
51
  "typecheck:tests": "tsc --noEmit -p tsconfig.test.json",
46
52
  "check:scripts": "tsc --project tsconfig.scripts.json",
53
+ "deploy-test": "tsx deploy-tests/run.mts",
47
54
  "lint": "eslint lib --no-warn-ignored",
48
55
  "lint:fix": "eslint lib --fix --no-warn-ignored",
49
- "format": "prettier --write \"lib/**/*.{ts,tsx,js,jsx,json}\" \"scripts/**/*.mts\"",
50
- "format:check": "prettier --check \"lib/**/*.{ts,tsx,js,jsx,json}\" \"scripts/**/*.mts\""
56
+ "format": "prettier --write \"lib/**/*.{ts,tsx,js,jsx,json}\" \"scripts/**/*.mts\" \"deploy-tests/**/*.mts\"",
57
+ "format:check": "prettier --check \"lib/**/*.{ts,tsx,js,jsx,json}\" \"scripts/**/*.mts\" \"deploy-tests/**/*.mts\""
51
58
  },
52
59
  "devDependencies": {
53
- "@aws-sdk/client-elastic-load-balancing-v2": "^3.1045.0",
54
- "@aws-sdk/client-iam": "^3.1038.0",
55
- "@aws-sdk/client-identitystore": "^3.1038.0",
56
- "@aws-sdk/client-sesv2": "^3.1087.0",
60
+ "@aws-sdk/client-elastic-load-balancing-v2": "^3.1098.0",
61
+ "@aws-sdk/client-iam": "^3.1098.0",
62
+ "@aws-sdk/client-identitystore": "^3.1098.0",
63
+ "@aws-sdk/client-sesv2": "^3.1098.0",
57
64
  "@peculiar/x509": "2.0.0",
58
- "@types/aws-lambda": "^8.10.161",
59
- "@types/node": "^26.0.0",
60
- "@typescript-eslint/eslint-plugin": "^8.59.1",
61
- "@typescript-eslint/parser": "^8.59.1",
62
- "eslint": "^10.2.1",
63
- "prettier": "^3.8.3",
65
+ "@types/aws-lambda": "^8.10.162",
66
+ "@types/node": "^26.1.2",
67
+ "@typescript-eslint/eslint-plugin": "^8.65.0",
68
+ "@typescript-eslint/parser": "^8.65.0",
69
+ "eslint": "^10.8.0",
70
+ "prettier": "^3.9.6",
64
71
  "reflect-metadata": "^0.2.2",
65
72
  "typescript": "^6.0.3",
66
- "vitest": "^4.1.7"
73
+ "vitest": "^4.1.10"
67
74
  },
68
75
  "dependencies": {
69
- "@aws-sdk/client-organizations": "^3.1038.0",
70
- "@fjall/generator": "^4.4.0",
71
- "@fjall/util": "^4.4.0",
72
- "constructs": "^10.6.0"
76
+ "@aws-sdk/client-organizations": "^3.1098.0",
77
+ "@fjall/generator": "^6.0.0",
78
+ "@fjall/util": "^6.0.0",
79
+ "constructs": "^10.7.2"
73
80
  },
74
81
  "overrides": {
75
82
  "@smithy/core": "2.5.5"
76
83
  },
77
84
  "peerDependencies": {
78
- "aws-cdk": "^2.1128.1",
79
- "aws-cdk-lib": "^2.260.0",
80
- "constructs": "^10.6.0"
85
+ "aws-cdk": "^2.1134.0",
86
+ "aws-cdk-lib": "^2.263.0",
87
+ "constructs": "^10.7.2"
81
88
  },
82
89
  "engines": {
83
90
  "node": ">=18.0.0"
84
91
  },
85
- "gitHead": "c700128600ab453fa63ce28b3effe8965373be43"
92
+ "gitHead": "7620fc7c93bbef1046580d6568546afe3cd450a7"
86
93
  }