@aws/nx-plugin 0.102.0 → 0.104.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/LICENSE-THIRD-PARTY +207 -0
- package/package.json +1 -1
- package/src/open-api/ts-client/__snapshots__/generator.additional-properties.spec.ts.snap +202 -0
- package/src/open-api/ts-client/__snapshots__/generator.content-type.spec.ts.snap +475 -0
- package/src/open-api/ts-client/__snapshots__/generator.fast-api.spec.ts.snap +353 -0
- package/src/open-api/ts-client/files/client.gen.ts.template +22 -1
- package/src/open-api/ts-client/files/types.gen.ts.template +5 -1
- package/src/open-api/utils/codegen-data.js +7 -2
- package/src/open-api/utils/codegen-data.js.map +1 -1
- package/src/open-api/utils/normalise.js +14 -0
- package/src/open-api/utils/normalise.js.map +1 -1
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +518 -9
- package/src/py/lambda-function/__snapshots__/generator.spec.ts.snap +3 -3
- package/src/py/mcp-server/files/deploy/Dockerfile.template +1 -1
- package/src/py/project/generator.js +4 -4
- package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/py/strands-agent/files/deploy/Dockerfile.template +1 -1
- package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +437 -0
- package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +522 -1
- package/src/utils/agent-connection/files/py-core-auth/auth/sigv4.py.template +1 -1
- package/src/utils/api-constructs/files/cdk/app/apis/http/__apiNameKebabCase__.ts.template +1 -1
- package/src/utils/api-constructs/files/cdk/app/apis/rest/__apiNameKebabCase__.ts.template +7 -1
- package/src/utils/api-constructs/files/cdk/core/api/rest/rest-api.ts.template +99 -1
- package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +1 -1
- package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +21 -1
- package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template +115 -0
- package/src/utils/function-constructs/function-constructs.js +2 -2
|
@@ -25,7 +25,7 @@ class SigV4HTTPXAuth(httpx.Auth):
|
|
|
25
25
|
|
|
26
26
|
def auth_flow(
|
|
27
27
|
self, request: httpx.Request
|
|
28
|
-
) -> Generator[httpx.Request, httpx.Response
|
|
28
|
+
) -> Generator[httpx.Request, httpx.Response]:
|
|
29
29
|
headers = dict(request.headers)
|
|
30
30
|
|
|
31
31
|
headers.pop("connection", None)
|
|
@@ -76,6 +76,12 @@ export interface <%= apiNameClassName %>Props<
|
|
|
76
76
|
userPool: IUserPool;
|
|
77
77
|
};
|
|
78
78
|
<%_ } _%>
|
|
79
|
+
/**
|
|
80
|
+
* Whether to enable AWS WAFv2 with the default managed ruleset on the API's default stage.
|
|
81
|
+
*
|
|
82
|
+
* @default true
|
|
83
|
+
*/
|
|
84
|
+
enableWaf?: boolean;
|
|
79
85
|
}
|
|
80
86
|
|
|
81
87
|
/**
|
|
@@ -122,7 +128,7 @@ export class <%= apiNameClassName %><
|
|
|
122
128
|
),
|
|
123
129
|
),
|
|
124
130
|
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
125
|
-
runtime: Runtime.
|
|
131
|
+
runtime: Runtime.PYTHON_3_14,
|
|
126
132
|
handler: 'run.sh',
|
|
127
133
|
code: Code.fromAsset(
|
|
128
134
|
url.fileURLToPath(
|
|
@@ -7,7 +7,13 @@ import {
|
|
|
7
7
|
IResource,
|
|
8
8
|
Stage,
|
|
9
9
|
} from 'aws-cdk-lib/aws-apigateway';
|
|
10
|
-
import { IAspect } from 'aws-cdk-lib';
|
|
10
|
+
import { IAspect, RemovalPolicy } from 'aws-cdk-lib';
|
|
11
|
+
import {
|
|
12
|
+
CfnLoggingConfiguration,
|
|
13
|
+
CfnWebACL,
|
|
14
|
+
CfnWebACLAssociation,
|
|
15
|
+
} from 'aws-cdk-lib/aws-wafv2';
|
|
16
|
+
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
11
17
|
import { RuntimeConfig } from '../runtime-config.js';
|
|
12
18
|
import {
|
|
13
19
|
ApiIntegrations,
|
|
@@ -38,6 +44,14 @@ export interface RestApiProps<
|
|
|
38
44
|
* Map of integration keys to their API Gateway integrations
|
|
39
45
|
*/
|
|
40
46
|
readonly integrations: TIntegrations;
|
|
47
|
+
/**
|
|
48
|
+
* Whether to enable AWS WAFv2 with the default managed ruleset
|
|
49
|
+
* (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet)
|
|
50
|
+
* and associate it with the API's default stage.
|
|
51
|
+
*
|
|
52
|
+
* @default true
|
|
53
|
+
*/
|
|
54
|
+
readonly enableWaf?: boolean;
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
/**
|
|
@@ -61,6 +75,9 @@ export class RestApi<
|
|
|
61
75
|
/** Map of integration keys to their API Gateway integrations */
|
|
62
76
|
public readonly integrations: TIntegrations;
|
|
63
77
|
|
|
78
|
+
/** The WAFv2 Web ACL associated with the API's default stage, if WAF is enabled */
|
|
79
|
+
public readonly webAcl?: CfnWebACL;
|
|
80
|
+
|
|
64
81
|
constructor(
|
|
65
82
|
scope: Construct,
|
|
66
83
|
id: string,
|
|
@@ -68,6 +85,7 @@ export class RestApi<
|
|
|
68
85
|
apiName,
|
|
69
86
|
operations,
|
|
70
87
|
integrations,
|
|
88
|
+
enableWaf = true,
|
|
71
89
|
...props
|
|
72
90
|
}: RestApiProps<TIntegrations, TOperation>,
|
|
73
91
|
) {
|
|
@@ -77,6 +95,86 @@ export class RestApi<
|
|
|
77
95
|
// Create the API Gateway REST API
|
|
78
96
|
this.api = new _RestApi(this, 'Api', props);
|
|
79
97
|
|
|
98
|
+
if (enableWaf) {
|
|
99
|
+
this.webAcl = new CfnWebACL(this, 'WebAcl', {
|
|
100
|
+
defaultAction: { allow: {} },
|
|
101
|
+
scope: 'REGIONAL',
|
|
102
|
+
visibilityConfig: {
|
|
103
|
+
cloudWatchMetricsEnabled: true,
|
|
104
|
+
metricName: `${apiName}WebAcl`,
|
|
105
|
+
sampledRequestsEnabled: true,
|
|
106
|
+
},
|
|
107
|
+
rules: [
|
|
108
|
+
{
|
|
109
|
+
name: 'CRSRule',
|
|
110
|
+
priority: 0,
|
|
111
|
+
statement: {
|
|
112
|
+
managedRuleGroupStatement: {
|
|
113
|
+
name: 'AWSManagedRulesCommonRuleSet',
|
|
114
|
+
vendorName: 'AWS',
|
|
115
|
+
// Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
116
|
+
ruleActionOverrides: [
|
|
117
|
+
{
|
|
118
|
+
name: 'SizeRestrictions_BODY',
|
|
119
|
+
actionToUse: { count: {} },
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
visibilityConfig: {
|
|
125
|
+
cloudWatchMetricsEnabled: true,
|
|
126
|
+
metricName: `${apiName}WebAcl-CRS`,
|
|
127
|
+
sampledRequestsEnabled: true,
|
|
128
|
+
},
|
|
129
|
+
overrideAction: {
|
|
130
|
+
none: {},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'KnownBadInputsRule',
|
|
135
|
+
priority: 1,
|
|
136
|
+
statement: {
|
|
137
|
+
managedRuleGroupStatement: {
|
|
138
|
+
name: 'AWSManagedRulesKnownBadInputsRuleSet',
|
|
139
|
+
vendorName: 'AWS',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
visibilityConfig: {
|
|
143
|
+
cloudWatchMetricsEnabled: true,
|
|
144
|
+
metricName: `${apiName}WebAcl-KnownBadInputs`,
|
|
145
|
+
sampledRequestsEnabled: true,
|
|
146
|
+
},
|
|
147
|
+
overrideAction: {
|
|
148
|
+
none: {},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
new CfnWebACLAssociation(this, 'WebAclAssociation', {
|
|
155
|
+
resourceArn: this.api.deploymentStage.stageArn,
|
|
156
|
+
webAclArn: this.webAcl.attrArn,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Send WAF request logs to CloudWatch. The log group name must start with
|
|
160
|
+
// `aws-waf-logs-` to satisfy the WAFv2 logging destination requirement.
|
|
161
|
+
const wafLogGroup = new LogGroup(this, 'WebAclLogs', {
|
|
162
|
+
logGroupName: `aws-waf-logs-${apiName}-${this.node.addr.slice(-8)}`,
|
|
163
|
+
retention: RetentionDays.ONE_MONTH,
|
|
164
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
165
|
+
});
|
|
166
|
+
suppressRules(
|
|
167
|
+
wafLogGroup,
|
|
168
|
+
['CKV_AWS_158'],
|
|
169
|
+
'Using default CloudWatch log encryption for WAF logs',
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
new CfnLoggingConfiguration(this, 'WebAclLoggingConfig', {
|
|
173
|
+
resourceArn: this.webAcl.attrArn,
|
|
174
|
+
logDestinationConfigs: [wafLogGroup.logGroupArn],
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
80
178
|
suppressRules(
|
|
81
179
|
this.api,
|
|
82
180
|
['CKV_AWS_120'],
|
|
@@ -133,7 +133,7 @@ resource "aws_lambda_function" "api_lambda" {
|
|
|
133
133
|
runtime = "nodejs22.x"
|
|
134
134
|
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
135
135
|
handler = "run.sh"
|
|
136
|
-
runtime = "python3.
|
|
136
|
+
runtime = "python3.14"
|
|
137
137
|
layers = ["arn:aws:lambda:${data.aws_region.current.name}:753240598075:layer:LambdaAdapterLayerX86:24"]
|
|
138
138
|
<%_ } _%>
|
|
139
139
|
timeout = 30
|
|
@@ -58,6 +58,13 @@ variable "cors_allow_origins" {
|
|
|
58
58
|
default = ["*"]
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
# WAF Configuration
|
|
62
|
+
variable "enable_waf" {
|
|
63
|
+
description = "Whether to enable AWS WAFv2 with the default managed ruleset on the API stage"
|
|
64
|
+
type = bool
|
|
65
|
+
default = true
|
|
66
|
+
}
|
|
67
|
+
|
|
61
68
|
# Tags
|
|
62
69
|
variable "tags" {
|
|
63
70
|
description = "Tags to apply to all resources"
|
|
@@ -87,6 +94,9 @@ module "rest_api" {
|
|
|
87
94
|
stage_name = "prod"
|
|
88
95
|
stage_auto_deploy = true
|
|
89
96
|
|
|
97
|
+
# WAF Configuration
|
|
98
|
+
enable_waf = var.enable_waf
|
|
99
|
+
|
|
90
100
|
# CORS Configuration
|
|
91
101
|
cors_allow_headers = var.cors_allow_headers
|
|
92
102
|
cors_allow_methods = var.cors_allow_methods
|
|
@@ -96,6 +106,15 @@ module "rest_api" {
|
|
|
96
106
|
tags = var.tags
|
|
97
107
|
}
|
|
98
108
|
|
|
109
|
+
resource "aws_wafv2_web_acl_association" "api_waf_association" {
|
|
110
|
+
count = var.enable_waf ? 1 : 0
|
|
111
|
+
|
|
112
|
+
resource_arn = aws_api_gateway_stage.api_stage.arn
|
|
113
|
+
web_acl_arn = module.rest_api.waf_web_acl_arn
|
|
114
|
+
|
|
115
|
+
depends_on = [aws_api_gateway_stage.api_stage]
|
|
116
|
+
}
|
|
117
|
+
|
|
99
118
|
# Lambda function
|
|
100
119
|
resource "aws_lambda_function" "api_lambda" {
|
|
101
120
|
#checkov:skip=CKV_AWS_117:Lambda function does not need to be in VPC for this use case
|
|
@@ -111,7 +130,7 @@ resource "aws_lambda_function" "api_lambda" {
|
|
|
111
130
|
runtime = "nodejs22.x"
|
|
112
131
|
<%_ } else if (backend.type === 'fastapi') { _%>
|
|
113
132
|
handler = "run.sh"
|
|
114
|
-
runtime = "python3.
|
|
133
|
+
runtime = "python3.14"
|
|
115
134
|
layers = ["arn:aws:lambda:${data.aws_region.current.name}:753240598075:layer:LambdaAdapterLayerX86:24"]
|
|
116
135
|
<%_ } _%>
|
|
117
136
|
timeout = 30
|
|
@@ -364,6 +383,7 @@ resource "aws_api_gateway_stage" "api_stage" {
|
|
|
364
383
|
#checkov:skip=CKV_AWS_76:API Gateway access logging disabled due to account-level CloudWatch Logs role ARN requirement
|
|
365
384
|
#checkov:skip=CKV2_AWS_4:API Gateway logging level not applicable as access logging is disabled
|
|
366
385
|
#checkov:skip=CKV2_AWS_51:Client certificate authentication not required for this use case
|
|
386
|
+
#checkov:skip=CKV2_AWS_77:WAFv2 Web ACL is attached via aws_wafv2_web_acl_association when var.enable_waf is true (default) and includes AWSManagedRulesKnownBadInputsRuleSet for Log4j protection; Checkov does not track the association across modules
|
|
367
387
|
deployment_id = aws_api_gateway_deployment.api_deployment.id
|
|
368
388
|
rest_api_id = module.rest_api.api_id
|
|
369
389
|
stage_name = "prod"
|
package/src/utils/api-constructs/files/terraform/core/api/rest/rest-api/rest-api.tf.template
CHANGED
|
@@ -9,6 +9,10 @@ terraform {
|
|
|
9
9
|
source = "hashicorp/aws"
|
|
10
10
|
version = "~> 6.33"
|
|
11
11
|
}
|
|
12
|
+
random = {
|
|
13
|
+
source = "hashicorp/random"
|
|
14
|
+
version = "~> 3.1"
|
|
15
|
+
}
|
|
12
16
|
}
|
|
13
17
|
}
|
|
14
18
|
|
|
@@ -37,6 +41,12 @@ variable "stage_auto_deploy" {
|
|
|
37
41
|
default = true
|
|
38
42
|
}
|
|
39
43
|
|
|
44
|
+
variable "enable_waf" {
|
|
45
|
+
description = "Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet). The Web ACL is created here and associated with the stage in the consuming module."
|
|
46
|
+
type = bool
|
|
47
|
+
default = true
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
# CORS Configuration
|
|
41
51
|
|
|
42
52
|
variable "cors_allow_headers" {
|
|
@@ -118,6 +128,106 @@ resource "aws_api_gateway_gateway_response" "cors_5xx" {
|
|
|
118
128
|
}
|
|
119
129
|
}
|
|
120
130
|
|
|
131
|
+
resource "aws_wafv2_web_acl" "api_waf" {
|
|
132
|
+
#checkov:skip=CKV2_AWS_31:Logging configuration is defined below in aws_wafv2_web_acl_logging_configuration.api_waf_logging; Checkov does not resolve the separate resource
|
|
133
|
+
count = var.enable_waf ? 1 : 0
|
|
134
|
+
|
|
135
|
+
name = "${var.api_name}-waf"
|
|
136
|
+
scope = "REGIONAL"
|
|
137
|
+
|
|
138
|
+
default_action {
|
|
139
|
+
allow {}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
rule {
|
|
143
|
+
name = "CRSRule"
|
|
144
|
+
priority = 0
|
|
145
|
+
|
|
146
|
+
override_action {
|
|
147
|
+
none {}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
statement {
|
|
151
|
+
managed_rule_group_statement {
|
|
152
|
+
name = "AWSManagedRulesCommonRuleSet"
|
|
153
|
+
vendor_name = "AWS"
|
|
154
|
+
|
|
155
|
+
# Count instead of Block: the CRS 8 KB body limit is too restrictive for most APIs.
|
|
156
|
+
rule_action_override {
|
|
157
|
+
name = "SizeRestrictions_BODY"
|
|
158
|
+
action_to_use {
|
|
159
|
+
count {}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
visibility_config {
|
|
166
|
+
cloudwatch_metrics_enabled = true
|
|
167
|
+
metric_name = "${var.api_name}WebAcl-CRS"
|
|
168
|
+
sampled_requests_enabled = true
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
rule {
|
|
173
|
+
name = "KnownBadInputsRule"
|
|
174
|
+
priority = 1
|
|
175
|
+
|
|
176
|
+
override_action {
|
|
177
|
+
none {}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
statement {
|
|
181
|
+
managed_rule_group_statement {
|
|
182
|
+
name = "AWSManagedRulesKnownBadInputsRuleSet"
|
|
183
|
+
vendor_name = "AWS"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
visibility_config {
|
|
188
|
+
cloudwatch_metrics_enabled = true
|
|
189
|
+
metric_name = "${var.api_name}WebAcl-KnownBadInputs"
|
|
190
|
+
sampled_requests_enabled = true
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
visibility_config {
|
|
195
|
+
cloudwatch_metrics_enabled = true
|
|
196
|
+
metric_name = "${var.api_name}WebAcl"
|
|
197
|
+
sampled_requests_enabled = true
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
tags = var.tags
|
|
201
|
+
|
|
202
|
+
lifecycle {
|
|
203
|
+
create_before_destroy = true
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# CloudWatch Log Group for WAF request logs. Name must start with `aws-waf-logs-`.
|
|
208
|
+
resource "aws_cloudwatch_log_group" "api_waf_logs" {
|
|
209
|
+
#checkov:skip=CKV_AWS_158:Using default CloudWatch log encryption
|
|
210
|
+
#checkov:skip=CKV_AWS_338:Log retention set to one month which is sufficient for WAF logs
|
|
211
|
+
count = var.enable_waf ? 1 : 0
|
|
212
|
+
|
|
213
|
+
name = "aws-waf-logs-${var.api_name}-${random_id.waf_log_suffix[0].hex}"
|
|
214
|
+
retention_in_days = 30
|
|
215
|
+
tags = var.tags
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
resource "random_id" "waf_log_suffix" {
|
|
219
|
+
count = var.enable_waf ? 1 : 0
|
|
220
|
+
byte_length = 4
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
resource "aws_wafv2_web_acl_logging_configuration" "api_waf_logging" {
|
|
224
|
+
count = var.enable_waf ? 1 : 0
|
|
225
|
+
|
|
226
|
+
log_destination_configs = [aws_cloudwatch_log_group.api_waf_logs[0].arn]
|
|
227
|
+
resource_arn = aws_wafv2_web_acl.api_waf[0].arn
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
|
|
121
231
|
# Outputs
|
|
122
232
|
|
|
123
233
|
output "api_id" {
|
|
@@ -145,6 +255,11 @@ output "api_root_resource_id" {
|
|
|
145
255
|
value = aws_api_gateway_rest_api.rest_api.root_resource_id
|
|
146
256
|
}
|
|
147
257
|
|
|
258
|
+
output "waf_web_acl_arn" {
|
|
259
|
+
description = "ARN of the WAFv2 Web ACL associated with the API, or null if WAF is disabled"
|
|
260
|
+
value = var.enable_waf ? aws_wafv2_web_acl.api_waf[0].arn : null
|
|
261
|
+
}
|
|
262
|
+
|
|
148
263
|
# Note: Stage and deployment outputs are provided by the consuming module
|
|
149
264
|
|
|
150
265
|
# Note: CloudWatch log group outputs removed due to account-level CloudWatch Logs role ARN requirement
|
|
@@ -42,7 +42,7 @@ const addLambdaFunctionCdkConstructs = async (tree, options) => {
|
|
|
42
42
|
// Generate app specific CDK construct
|
|
43
43
|
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'cdk', 'app', 'lambda-functions'), (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'app', 'lambda-functions'), {
|
|
44
44
|
...options,
|
|
45
|
-
runtime: `Runtime.${options.runtime === 'python' ? '
|
|
45
|
+
runtime: `Runtime.${options.runtime === 'python' ? 'PYTHON_3_14' : 'NODEJS_LATEST'}`,
|
|
46
46
|
}, {
|
|
47
47
|
overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting,
|
|
48
48
|
});
|
|
@@ -54,7 +54,7 @@ const addLambdaFunctionTerraformModules = (tree, options) => {
|
|
|
54
54
|
// Generate app specific terraform module
|
|
55
55
|
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'terraform', 'app', 'lambda-functions'), (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_TERRAFORM_DIR, 'src', 'app', 'lambda-functions'), {
|
|
56
56
|
...options,
|
|
57
|
-
runtime: options.runtime === 'python' ? 'python3.
|
|
57
|
+
runtime: options.runtime === 'python' ? 'python3.14' : 'nodejs22.x',
|
|
58
58
|
}, {
|
|
59
59
|
overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting,
|
|
60
60
|
});
|