@fjall/components-infrastructure 2.9.1 → 2.12.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.
- package/dist/lib/config/aws/disasterRecovery.js +23 -4
- package/dist/lib/config/aws/ecrDefaultImage.js +2 -2
- package/dist/lib/config/aws/s3BlockPublicAccess.d.ts +22 -4
- package/dist/lib/config/aws/s3BlockPublicAccess.js +33 -13
- package/dist/lib/config/aws/scpPreset.js +6 -1
- package/dist/lib/patterns/aws/account.d.ts +15 -0
- package/dist/lib/patterns/aws/account.js +32 -6
- package/dist/lib/patterns/aws/buildkite.js +1 -1
- package/dist/lib/patterns/aws/clickhouseDatabase.js +5 -2
- package/dist/lib/patterns/aws/compute.d.ts +22 -0
- package/dist/lib/patterns/aws/compute.js +42 -0
- package/dist/lib/patterns/aws/computeEcs.d.ts +2 -1
- package/dist/lib/patterns/aws/computeEcs.js +67 -24
- package/dist/lib/patterns/aws/computeEcsTypes.d.ts +8 -2
- package/dist/lib/patterns/aws/organisation.d.ts +19 -8
- package/dist/lib/patterns/aws/organisation.js +23 -10
- package/dist/lib/patterns/aws/organisationFactory.js +2 -1
- package/dist/lib/patterns/aws/platform.d.ts +1 -0
- package/dist/lib/patterns/aws/platform.js +3 -0
- package/dist/lib/resources/aws/backup/backupVault.js +5 -3
- package/dist/lib/resources/aws/compute/ecsConstants.d.ts +1 -1
- package/dist/lib/resources/aws/compute/ecsConstants.js +4 -1
- package/dist/lib/resources/aws/compute/ecsLifecycleHookMigration.js +2 -2
- package/dist/lib/resources/aws/compute/ecsRoles.js +4 -13
- package/dist/lib/resources/aws/compute/ecsServiceFactory.d.ts +5 -3
- package/dist/lib/resources/aws/compute/ecsServiceFactory.js +76 -3
- package/dist/lib/resources/aws/compute/ecsTaskDefinition.d.ts +0 -5
- package/dist/lib/resources/aws/compute/ecsTaskDefinition.js +2 -20
- package/dist/lib/resources/aws/compute/ecsTypes.d.ts +50 -8
- package/dist/lib/resources/aws/compute/ecsTypes.js +11 -0
- package/dist/lib/resources/aws/compute/ecsValidation.js +37 -0
- package/dist/lib/resources/aws/compute/lambda.d.ts +11 -0
- package/dist/lib/resources/aws/compute/lambda.js +23 -3
- package/dist/lib/resources/aws/secrets/parameter.js +3 -3
- package/dist/lib/resources/aws/secrets/secret.d.ts +32 -11
- package/dist/lib/resources/aws/secrets/secret.js +53 -0
- package/dist/lib/resources/aws/utilities/customResource.d.ts +4 -0
- package/dist/lib/resources/aws/utilities/customResource.js +4 -0
- package/dist/lib/utils/env.js +2 -2
- package/dist/lib/utils/getConfig.js +2 -2
- package/dist/lib/utils/orgConfigParser.js +16 -2
- package/package.json +4 -4
|
@@ -15,6 +15,17 @@ export interface LambdaFunctionProps {
|
|
|
15
15
|
handler: string;
|
|
16
16
|
lambdaDescription?: string;
|
|
17
17
|
roleDescription?: string;
|
|
18
|
+
/**
|
|
19
|
+
* Fixed IAM path for the auto-generated execution role (e.g. "/fjall/").
|
|
20
|
+
* Used with roleName to produce an SCP-exemptable ARN.
|
|
21
|
+
*/
|
|
22
|
+
rolePath?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Fixed name for the auto-generated execution role. Account-global — only set
|
|
25
|
+
* on a Lambda confined to one (account, region) stack, else regional
|
|
26
|
+
* duplicates collide on the role name.
|
|
27
|
+
*/
|
|
28
|
+
roleName?: string;
|
|
18
29
|
runtime: Runtime;
|
|
19
30
|
/** Lambda CPU architecture. Default: x86_64 */
|
|
20
31
|
architecture?: Architecture;
|
|
@@ -8,9 +8,9 @@ import { EventType } from "aws-cdk-lib/aws-s3";
|
|
|
8
8
|
import { PolicyStatement, Effect } from "aws-cdk-lib/aws-iam";
|
|
9
9
|
import { RetentionDays } from "aws-cdk-lib/aws-logs";
|
|
10
10
|
import { LogGroup } from "../logging/logGroup.js";
|
|
11
|
-
import { Secret } from "aws-cdk-lib/aws-secretsmanager";
|
|
12
11
|
import { v4 as uuid } from "uuid";
|
|
13
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
import { resolveImportedSecret } from "../secrets/index.js";
|
|
14
14
|
import { toPascalCase } from "../../../utils/capitaliseString.js";
|
|
15
15
|
import { resolvePrivateSubnetType } from "../../../utils/vpcUtils.js";
|
|
16
16
|
import { createLambdaAlarms } from "../monitoring/index.js";
|
|
@@ -31,6 +31,24 @@ function applyRoleDescription(fn, description) {
|
|
|
31
31
|
if (cfnRole !== undefined)
|
|
32
32
|
cfnRole.description = description;
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Pin a fixed IAM path and/or name onto CDK's auto-generated Lambda execution
|
|
36
|
+
* role. Reaches the L1 CfnRole (FunctionProps exposes neither) so a stable,
|
|
37
|
+
* predictable ARN can be matched by an SCP exemption pattern. A fixed name is
|
|
38
|
+
* account-global, so only set one on a Lambda that lives in a single
|
|
39
|
+
* (account, region) stack — never one duplicated across regions.
|
|
40
|
+
*/
|
|
41
|
+
function applyRolePathAndName(fn, rolePath, roleName) {
|
|
42
|
+
if (rolePath === undefined && roleName === undefined)
|
|
43
|
+
return;
|
|
44
|
+
const cfnRole = fn.role?.node.defaultChild;
|
|
45
|
+
if (cfnRole === undefined)
|
|
46
|
+
return;
|
|
47
|
+
if (rolePath !== undefined)
|
|
48
|
+
cfnRole.path = rolePath;
|
|
49
|
+
if (roleName !== undefined)
|
|
50
|
+
cfnRole.roleName = roleName;
|
|
51
|
+
}
|
|
34
52
|
/**
|
|
35
53
|
* AWS Parameters and Secrets Lambda Extension configuration.
|
|
36
54
|
* @see https://docs.aws.amazon.com/systems-manager/latest/userguide/ps-integration-lambda-extensions.html
|
|
@@ -61,6 +79,7 @@ export class SingletonFunction extends singletonFunction {
|
|
|
61
79
|
});
|
|
62
80
|
addPoliciesToRole(this, props.inlinePolicy);
|
|
63
81
|
applyRoleDescription(this, props.roleDescription);
|
|
82
|
+
applyRolePathAndName(this, props.rolePath, props.roleName);
|
|
64
83
|
}
|
|
65
84
|
/**
|
|
66
85
|
* The Lambda's execution role (auto-generated by CDK)
|
|
@@ -91,6 +110,7 @@ export class LambdaFunction extends Function {
|
|
|
91
110
|
});
|
|
92
111
|
addPoliciesToRole(this, props.inlinePolicy);
|
|
93
112
|
applyRoleDescription(this, props.roleDescription);
|
|
113
|
+
applyRolePathAndName(this, props.rolePath, props.roleName);
|
|
94
114
|
this.addSecretsSupport(props.secrets, props.ssmSecretsPath, props.secretsImport, props.appName, props.functionName, props.architecture);
|
|
95
115
|
// Sanitise id for CloudFormation output keys (must be alphanumeric)
|
|
96
116
|
const outputName = toPascalCase(id);
|
|
@@ -250,7 +270,7 @@ export class LambdaFunction extends Function {
|
|
|
250
270
|
*/
|
|
251
271
|
addSecretsManagerSupport(secretsImport) {
|
|
252
272
|
for (const [key, secretImport] of Object.entries(secretsImport)) {
|
|
253
|
-
const secret =
|
|
273
|
+
const secret = resolveImportedSecret(this, `${this.node.id}ImportedSecret${key}`, secretImport);
|
|
254
274
|
this.addEnvironment(`${key}_SECRET_ARN`, secret.secretArn);
|
|
255
275
|
if (secretImport.field) {
|
|
256
276
|
this.addEnvironment(`${key}_SECRET_FIELD`, secretImport.field);
|
|
@@ -281,7 +301,7 @@ export class LambdaFunction extends Function {
|
|
|
281
301
|
effect: Effect.ALLOW,
|
|
282
302
|
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
283
303
|
resources: [
|
|
284
|
-
`arn:
|
|
304
|
+
`arn:${Stack.of(this).partition}:ssm:${Stack.of(this).region}:${Stack.of(this).account}:parameter${scopedPath}`
|
|
285
305
|
]
|
|
286
306
|
}));
|
|
287
307
|
this.addToRolePolicy(new PolicyStatement({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { aws_ssm as ssm } from "aws-cdk-lib";
|
|
1
|
+
import { aws_ssm as ssm, Stack } from "aws-cdk-lib";
|
|
2
2
|
import { PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
3
3
|
import { AwsCustomResourcePolicy, PhysicalResourceId } from "aws-cdk-lib/custom-resources";
|
|
4
4
|
import { Construct } from "constructs";
|
|
@@ -73,7 +73,7 @@ export class SecureStringParameter extends Construct {
|
|
|
73
73
|
new PolicyStatement({
|
|
74
74
|
actions: ["kms:Encrypt"],
|
|
75
75
|
resources: [
|
|
76
|
-
`arn:
|
|
76
|
+
`arn:${Stack.of(this).partition}:kms:${props.region}:${props.accountId}:key/${this.cmk.key.keyId}`
|
|
77
77
|
]
|
|
78
78
|
}),
|
|
79
79
|
new PolicyStatement({
|
|
@@ -85,7 +85,7 @@ export class SecureStringParameter extends Construct {
|
|
|
85
85
|
"logs:PutRetentionPolicy"
|
|
86
86
|
],
|
|
87
87
|
resources: [
|
|
88
|
-
`arn:
|
|
88
|
+
`arn:${Stack.of(this).partition}:ssm:${props.region}:${props.accountId}:parameter${props.name}`
|
|
89
89
|
]
|
|
90
90
|
})
|
|
91
91
|
])
|
|
@@ -24,20 +24,41 @@ interface SecretProps {
|
|
|
24
24
|
/** Set to true to import an existing secret instead of creating a new one (for replicated secrets) */
|
|
25
25
|
importExisting?: boolean;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Reference to an existing (externally-managed) Secrets Manager secret.
|
|
29
|
+
*
|
|
30
|
+
* Provide EXACTLY ONE of `name` (the common case — resolved to its complete ARN at
|
|
31
|
+
* deploy time, see {@link resolveImportedSecret}) or `arn` (a complete-ARN escape
|
|
32
|
+
* hatch for cross-account/region secrets the deploy identity cannot DescribeSecret).
|
|
33
|
+
*/
|
|
27
34
|
export type SecretImport = {
|
|
28
|
-
/**
|
|
29
|
-
* Secret ID
|
|
30
|
-
*/
|
|
35
|
+
/** Construct ID for the imported secret. */
|
|
31
36
|
id: string;
|
|
32
|
-
/**
|
|
33
|
-
* Secret name
|
|
34
|
-
*/
|
|
35
|
-
name: string;
|
|
36
|
-
/**
|
|
37
|
-
* Optional - may be used to import a specific field from the secret
|
|
38
|
-
*/
|
|
37
|
+
/** Optional — import a single JSON field from the secret. */
|
|
39
38
|
field?: string;
|
|
40
|
-
}
|
|
39
|
+
} & ({
|
|
40
|
+
name: string;
|
|
41
|
+
arn?: undefined;
|
|
42
|
+
} | {
|
|
43
|
+
arn: string;
|
|
44
|
+
name?: undefined;
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* Resolves an imported (externally-managed) Secrets Manager secret to an `ISecret`
|
|
48
|
+
* whose ARN is COMPLETE (with the AWS 6-char suffix) wherever possible.
|
|
49
|
+
*
|
|
50
|
+
* `Secret.fromSecretNameV2` renders a SUFFIXLESS partial ARN into the ECS container
|
|
51
|
+
* `valueFrom`, which real Secrets Manager rejects with AccessDenied at task launch (the
|
|
52
|
+
* 2026-06-04 outage). `fromSecretCompleteArn` renders the full ARN AND an exact-ARN
|
|
53
|
+
* `grantRead`, which authorises. The full ARN is obtained three ways, in order:
|
|
54
|
+
* 1. an explicit `arn` on the import (cross-account/region escape hatch);
|
|
55
|
+
* 2. the `fjallResolvedSecretArns` context map injected by @fjall/deploy-core, which
|
|
56
|
+
* DescribeSecret-resolves every name -> full ARN at deploy time;
|
|
57
|
+
* 3. fallback to `fromSecretNameV2` ONLY when neither is present — i.e. offline
|
|
58
|
+
* `cdk synth` for template generation. A real deploy ALWAYS runs deploy-core's
|
|
59
|
+
* resolution, so the suffixless shape never reaches a deployed task-def.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveImportedSecret(scope: Construct, id: string, secretImport: SecretImport): ISecret;
|
|
41
62
|
export declare class Secret extends Construct {
|
|
42
63
|
id: string;
|
|
43
64
|
readonly secret: ISecret;
|
|
@@ -2,6 +2,59 @@ import { SecretValue } from "aws-cdk-lib";
|
|
|
2
2
|
import { Secret as CdkSecret } from "aws-cdk-lib/aws-secretsmanager";
|
|
3
3
|
import { Construct } from "constructs";
|
|
4
4
|
import { CustomerManagedKey } from "./kms.js";
|
|
5
|
+
/**
|
|
6
|
+
* Context key carrying the deploy-time `secretName -> completeArn` map. Written by
|
|
7
|
+
* @fjall/deploy-core (`CdkArgumentBuilder.buildContextArgs` emits
|
|
8
|
+
* `-c fjallResolvedSecretArns=<json>`); read here. Keep the literal in sync with that
|
|
9
|
+
* canonical writer — matches the `ipamPoolId` / `orgConfig` literal-key house style.
|
|
10
|
+
*/
|
|
11
|
+
const RESOLVED_SECRET_ARNS_CONTEXT_KEY = "fjallResolvedSecretArns";
|
|
12
|
+
function isStringRecord(value) {
|
|
13
|
+
return (typeof value === "object" &&
|
|
14
|
+
value !== null &&
|
|
15
|
+
!Array.isArray(value) &&
|
|
16
|
+
Object.values(value).every((entry) => typeof entry === "string"));
|
|
17
|
+
}
|
|
18
|
+
function readResolvedSecretArn(scope, secretName) {
|
|
19
|
+
const raw = scope.node.tryGetContext(RESOLVED_SECRET_ARNS_CONTEXT_KEY);
|
|
20
|
+
if (typeof raw !== "string" || raw === "")
|
|
21
|
+
return undefined;
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
if (!isStringRecord(parsed))
|
|
30
|
+
return undefined;
|
|
31
|
+
return parsed[secretName];
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolves an imported (externally-managed) Secrets Manager secret to an `ISecret`
|
|
35
|
+
* whose ARN is COMPLETE (with the AWS 6-char suffix) wherever possible.
|
|
36
|
+
*
|
|
37
|
+
* `Secret.fromSecretNameV2` renders a SUFFIXLESS partial ARN into the ECS container
|
|
38
|
+
* `valueFrom`, which real Secrets Manager rejects with AccessDenied at task launch (the
|
|
39
|
+
* 2026-06-04 outage). `fromSecretCompleteArn` renders the full ARN AND an exact-ARN
|
|
40
|
+
* `grantRead`, which authorises. The full ARN is obtained three ways, in order:
|
|
41
|
+
* 1. an explicit `arn` on the import (cross-account/region escape hatch);
|
|
42
|
+
* 2. the `fjallResolvedSecretArns` context map injected by @fjall/deploy-core, which
|
|
43
|
+
* DescribeSecret-resolves every name -> full ARN at deploy time;
|
|
44
|
+
* 3. fallback to `fromSecretNameV2` ONLY when neither is present — i.e. offline
|
|
45
|
+
* `cdk synth` for template generation. A real deploy ALWAYS runs deploy-core's
|
|
46
|
+
* resolution, so the suffixless shape never reaches a deployed task-def.
|
|
47
|
+
*/
|
|
48
|
+
export function resolveImportedSecret(scope, id, secretImport) {
|
|
49
|
+
if (secretImport.arn !== undefined) {
|
|
50
|
+
return CdkSecret.fromSecretCompleteArn(scope, id, secretImport.arn);
|
|
51
|
+
}
|
|
52
|
+
const resolvedArn = readResolvedSecretArn(scope, secretImport.name);
|
|
53
|
+
if (resolvedArn !== undefined) {
|
|
54
|
+
return CdkSecret.fromSecretCompleteArn(scope, id, resolvedArn);
|
|
55
|
+
}
|
|
56
|
+
return CdkSecret.fromSecretNameV2(scope, id, secretImport.name);
|
|
57
|
+
}
|
|
5
58
|
export class Secret extends Construct {
|
|
6
59
|
id;
|
|
7
60
|
secret;
|
|
@@ -7,6 +7,10 @@ interface CustomResourceProps {
|
|
|
7
7
|
runtime: Runtime;
|
|
8
8
|
roleDescription?: string;
|
|
9
9
|
lambdaDescription?: string;
|
|
10
|
+
/** Fixed IAM path for the handler's execution role (e.g. "/fjall/"). */
|
|
11
|
+
rolePath?: string;
|
|
12
|
+
/** Fixed name for the handler's execution role (account-global — see LambdaFunctionProps). */
|
|
13
|
+
roleName?: string;
|
|
10
14
|
inlinePolicy: PolicyStatement[];
|
|
11
15
|
properties?: {
|
|
12
16
|
[key: string]: string;
|
|
@@ -39,6 +39,8 @@ export class CustomResource extends Construct {
|
|
|
39
39
|
timeout,
|
|
40
40
|
lambdaDescription: props.lambdaDescription ?? `${id} lambda`,
|
|
41
41
|
roleDescription: props.roleDescription ?? `${id} custom resource lambda`,
|
|
42
|
+
rolePath: props.rolePath,
|
|
43
|
+
roleName: props.roleName,
|
|
42
44
|
inlinePolicy: props.inlinePolicy
|
|
43
45
|
})
|
|
44
46
|
: new SingletonFunction(this, `${id}Lambda`, {
|
|
@@ -47,6 +49,8 @@ export class CustomResource extends Construct {
|
|
|
47
49
|
runtime: props.runtime,
|
|
48
50
|
lambdaDescription: props.lambdaDescription ?? `${id} lambda`,
|
|
49
51
|
roleDescription: props.roleDescription ?? `${id} custom resource lambda`,
|
|
52
|
+
rolePath: props.rolePath,
|
|
53
|
+
roleName: props.roleName,
|
|
50
54
|
inlinePolicy: props.inlinePolicy
|
|
51
55
|
});
|
|
52
56
|
const provider = new Provider(this, `${id}Provider`, {
|
package/dist/lib/utils/env.js
CHANGED
|
@@ -99,12 +99,12 @@ function getProviderAccountsFromContext() {
|
|
|
99
99
|
*/
|
|
100
100
|
function resolveEnvironmentFromAccountId(accountId) {
|
|
101
101
|
const accounts = getProviderAccountsFromContext();
|
|
102
|
-
return accounts.find((pa) => pa.id === accountId)?.environment;
|
|
102
|
+
return accounts.find((pa) => pa.id === accountId)?.environment ?? undefined;
|
|
103
103
|
}
|
|
104
104
|
/**
|
|
105
105
|
* Look up environment from account name via orgConfig CDK context.
|
|
106
106
|
*/
|
|
107
107
|
function resolveEnvironmentFromAccountName(accountName) {
|
|
108
108
|
const accounts = getProviderAccountsFromContext();
|
|
109
|
-
return accounts.find((pa) => pa.name === accountName)?.environment;
|
|
109
|
+
return (accounts.find((pa) => pa.name === accountName)?.environment ?? undefined);
|
|
110
110
|
}
|
|
@@ -63,7 +63,7 @@ export function getConfig(accountName) {
|
|
|
63
63
|
if (providerAccount) {
|
|
64
64
|
config.accountId = providerAccount.id;
|
|
65
65
|
config.accountName = providerAccount.name;
|
|
66
|
-
config.environment = providerAccount.environment;
|
|
66
|
+
config.environment = providerAccount.environment ?? "unknown";
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
// If we still don't have an account name - try to retrieve accountId from context
|
|
@@ -75,7 +75,7 @@ export function getConfig(accountName) {
|
|
|
75
75
|
const providerAccount = providerAccounts.find((pa) => pa.id === accountId);
|
|
76
76
|
if (providerAccount) {
|
|
77
77
|
config.accountName = providerAccount.name;
|
|
78
|
-
config.environment = providerAccount.environment;
|
|
78
|
+
config.environment = providerAccount.environment ?? "unknown";
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VAULT_LOCK_MODES, S3_BPA_MODES } from "@fjall/util/config";
|
|
1
2
|
import { maskSensitiveOutput } from "@fjall/util";
|
|
2
3
|
import { FjallLogger } from "./validationLogger.js";
|
|
3
4
|
/**
|
|
@@ -23,11 +24,24 @@ export function parseOrgConfig(raw) {
|
|
|
23
24
|
item !== null &&
|
|
24
25
|
typeof item.id === "string" &&
|
|
25
26
|
typeof item.name === "string" &&
|
|
26
|
-
|
|
27
|
+
// null environment (structural accounts) must survive — rejecting it drops the account at synth.
|
|
28
|
+
(typeof item.environment ===
|
|
29
|
+
"string" ||
|
|
30
|
+
item.environment === null) &&
|
|
27
31
|
(item.managed === undefined ||
|
|
28
32
|
typeof item.managed === "boolean") &&
|
|
29
33
|
(item.oidcRoleArn === undefined ||
|
|
30
|
-
typeof item.oidcRoleArn ===
|
|
34
|
+
typeof item.oidcRoleArn ===
|
|
35
|
+
"string") &&
|
|
36
|
+
(item.vaultLock === undefined ||
|
|
37
|
+
VAULT_LOCK_MODES.includes(item.vaultLock)) &&
|
|
38
|
+
(item.s3BlockPublicAccess ===
|
|
39
|
+
undefined ||
|
|
40
|
+
S3_BPA_MODES.includes(item.s3BlockPublicAccess)) &&
|
|
41
|
+
(item.acknowledgeImmutableVaultLock ===
|
|
42
|
+
undefined ||
|
|
43
|
+
typeof item
|
|
44
|
+
.acknowledgeImmutableVaultLock === "boolean"))
|
|
31
45
|
: [];
|
|
32
46
|
const primaryRegion = typeof obj.primaryRegion === "string" ? obj.primaryRegion : undefined;
|
|
33
47
|
const secondaryRegions = Array.isArray(obj.secondaryRegions)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjall/components-infrastructure",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -63,8 +63,8 @@
|
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
65
|
"@aws-sdk/client-organizations": "^3.1038.0",
|
|
66
|
-
"@fjall/generator": "^2.
|
|
67
|
-
"@fjall/util": "^2.
|
|
66
|
+
"@fjall/generator": "^2.12.0",
|
|
67
|
+
"@fjall/util": "^2.12.0",
|
|
68
68
|
"constructs": "^10.0.0",
|
|
69
69
|
"uuid": "^14.0.0"
|
|
70
70
|
},
|
|
@@ -79,5 +79,5 @@
|
|
|
79
79
|
"engines": {
|
|
80
80
|
"node": ">=18.0.0"
|
|
81
81
|
},
|
|
82
|
-
"gitHead": "
|
|
82
|
+
"gitHead": "dca39a47da956d3d94c689dd782fe285d711d25e"
|
|
83
83
|
}
|