@awsless/awsless 0.0.15 → 0.0.16
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/README.MD +6 -0
- package/dist/bin.cjs +1488 -295
- package/dist/bin.js +1474 -281
- package/dist/index.d.ts +493 -13
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -57,7 +57,7 @@ var flushDebug = () => {
|
|
|
57
57
|
// src/util/param.ts
|
|
58
58
|
import { DeleteParameterCommand, GetParameterCommand, GetParametersByPathCommand, ParameterType, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
|
|
59
59
|
var configParameterPrefix = (config) => {
|
|
60
|
-
return
|
|
60
|
+
return `/.awsless/${config.name}`;
|
|
61
61
|
};
|
|
62
62
|
var Params = class {
|
|
63
63
|
constructor(config) {
|
|
@@ -138,8 +138,18 @@ var Params = class {
|
|
|
138
138
|
}
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
+
// src/formation/asset.ts
|
|
142
|
+
import { paramCase } from "change-case";
|
|
143
|
+
var Asset = class {
|
|
144
|
+
constructor(type, id) {
|
|
145
|
+
this.type = type;
|
|
146
|
+
this.id = paramCase(id);
|
|
147
|
+
}
|
|
148
|
+
id;
|
|
149
|
+
};
|
|
150
|
+
|
|
141
151
|
// src/formation/util.ts
|
|
142
|
-
import { paramCase, pascalCase } from "change-case";
|
|
152
|
+
import { paramCase as paramCase2, pascalCase } from "change-case";
|
|
143
153
|
var ref = (logicalId) => {
|
|
144
154
|
return { Ref: logicalId };
|
|
145
155
|
};
|
|
@@ -156,10 +166,10 @@ var importValue = (name) => {
|
|
|
156
166
|
return { "Fn::ImportValue": name };
|
|
157
167
|
};
|
|
158
168
|
var formatLogicalId = (id) => {
|
|
159
|
-
return pascalCase(id);
|
|
169
|
+
return pascalCase(id).replaceAll("_", "");
|
|
160
170
|
};
|
|
161
171
|
var formatName = (name) => {
|
|
162
|
-
return
|
|
172
|
+
return paramCase2(name);
|
|
163
173
|
};
|
|
164
174
|
|
|
165
175
|
// src/formation/resource.ts
|
|
@@ -295,11 +305,11 @@ var Function = class extends Resource {
|
|
|
295
305
|
});
|
|
296
306
|
role.addInlinePolicy(policy);
|
|
297
307
|
role.addManagedPolicy(ManagedPolicy.fromAwsManagedPolicyName("AWSLambdaBasicExecutionRole"));
|
|
298
|
-
super("AWS::Lambda::Function", logicalId, [
|
|
299
|
-
role,
|
|
300
|
-
props.code
|
|
301
|
-
]);
|
|
308
|
+
super("AWS::Lambda::Function", logicalId, [role]);
|
|
302
309
|
this.props = props;
|
|
310
|
+
if (props.code instanceof Asset) {
|
|
311
|
+
this.children.push(props.code);
|
|
312
|
+
}
|
|
303
313
|
this.dependsOn(role);
|
|
304
314
|
this.role = role;
|
|
305
315
|
this.policy = policy;
|
|
@@ -352,16 +362,6 @@ var Function = class extends Resource {
|
|
|
352
362
|
}
|
|
353
363
|
};
|
|
354
364
|
|
|
355
|
-
// src/formation/asset.ts
|
|
356
|
-
import { paramCase as paramCase2 } from "change-case";
|
|
357
|
-
var Asset = class {
|
|
358
|
-
constructor(type, id) {
|
|
359
|
-
this.type = type;
|
|
360
|
-
this.id = paramCase2(id);
|
|
361
|
-
}
|
|
362
|
-
id;
|
|
363
|
-
};
|
|
364
|
-
|
|
365
365
|
// src/formation/stack.ts
|
|
366
366
|
var Stack = class {
|
|
367
367
|
constructor(name, region) {
|
|
@@ -386,10 +386,16 @@ var Stack = class {
|
|
|
386
386
|
return this;
|
|
387
387
|
}
|
|
388
388
|
export(name, value) {
|
|
389
|
-
|
|
390
|
-
this.exports.set(name, value);
|
|
389
|
+
this.exports.set(formatName(name), value);
|
|
391
390
|
return this;
|
|
392
391
|
}
|
|
392
|
+
get(name) {
|
|
393
|
+
name = formatName(name);
|
|
394
|
+
if (!this.exports.has(name)) {
|
|
395
|
+
throw new Error(`Undefined export value: ${name}`);
|
|
396
|
+
}
|
|
397
|
+
return this.exports.get(name);
|
|
398
|
+
}
|
|
393
399
|
import(name) {
|
|
394
400
|
name = formatName(name);
|
|
395
401
|
if (!this.exports.has(name)) {
|
|
@@ -797,55 +803,24 @@ var zipFiles = (files) => {
|
|
|
797
803
|
};
|
|
798
804
|
|
|
799
805
|
// src/formation/resource/lambda/code.ts
|
|
800
|
-
import { createHash as createHash2 } from "crypto";
|
|
801
806
|
var Code = class {
|
|
802
807
|
static fromFile(id, file, bundler) {
|
|
803
808
|
return new FileCode(id, file, bundler);
|
|
804
809
|
}
|
|
805
|
-
static fromInline(
|
|
806
|
-
return new InlineCode(
|
|
810
|
+
static fromInline(code, handler) {
|
|
811
|
+
return new InlineCode(code, handler);
|
|
807
812
|
}
|
|
808
813
|
};
|
|
809
|
-
var InlineCode = class
|
|
810
|
-
constructor(
|
|
811
|
-
super("function", id);
|
|
814
|
+
var InlineCode = class {
|
|
815
|
+
constructor(code, handler = "index.default") {
|
|
812
816
|
this.code = code;
|
|
813
817
|
this.handler = handler;
|
|
814
818
|
}
|
|
815
|
-
hash;
|
|
816
|
-
bundle;
|
|
817
|
-
s3;
|
|
818
|
-
async build({ write }) {
|
|
819
|
-
const hash = createHash2("sha1").update(this.code).digest("hex");
|
|
820
|
-
const bundle = await zipFiles([{
|
|
821
|
-
name: "index.js",
|
|
822
|
-
code: this.code
|
|
823
|
-
}]);
|
|
824
|
-
await Promise.all([
|
|
825
|
-
write("HASH", hash),
|
|
826
|
-
write("bundle.zip", bundle),
|
|
827
|
-
write("files/inline.js", this.code)
|
|
828
|
-
]);
|
|
829
|
-
this.bundle = bundle;
|
|
830
|
-
this.hash = hash;
|
|
831
|
-
return {
|
|
832
|
-
size: formatByteSize(bundle.byteLength)
|
|
833
|
-
};
|
|
834
|
-
}
|
|
835
|
-
async publish({ publish }) {
|
|
836
|
-
this.s3 = await publish(
|
|
837
|
-
`${this.id}.zip`,
|
|
838
|
-
this.bundle,
|
|
839
|
-
this.hash
|
|
840
|
-
);
|
|
841
|
-
}
|
|
842
819
|
toCodeJson() {
|
|
843
820
|
return {
|
|
844
821
|
Handler: this.handler,
|
|
845
822
|
Code: {
|
|
846
|
-
|
|
847
|
-
S3Key: this.s3.key,
|
|
848
|
-
S3ObjectVersion: this.s3.version
|
|
823
|
+
ZipFile: this.code
|
|
849
824
|
}
|
|
850
825
|
};
|
|
851
826
|
}
|
|
@@ -896,6 +871,44 @@ var FileCode = class extends Asset {
|
|
|
896
871
|
}
|
|
897
872
|
};
|
|
898
873
|
|
|
874
|
+
// src/formation/resource/lambda/event-invoke-config.ts
|
|
875
|
+
var EventInvokeConfig = class extends Resource {
|
|
876
|
+
constructor(logicalId, props) {
|
|
877
|
+
super("AWS::Lambda::EventInvokeConfig", logicalId);
|
|
878
|
+
this.props = props;
|
|
879
|
+
}
|
|
880
|
+
setOnFailure(arn) {
|
|
881
|
+
this.props.onFailure = arn;
|
|
882
|
+
return this;
|
|
883
|
+
}
|
|
884
|
+
setOnSuccess(arn) {
|
|
885
|
+
this.props.onSuccess = arn;
|
|
886
|
+
return this;
|
|
887
|
+
}
|
|
888
|
+
properties() {
|
|
889
|
+
return {
|
|
890
|
+
FunctionName: this.props.functionName,
|
|
891
|
+
Qualifier: this.props.qualifier || "$LATEST",
|
|
892
|
+
...this.attr("MaximumEventAgeInSeconds", this.props.maxEventAge?.toSeconds()),
|
|
893
|
+
...this.attr("MaximumRetryAttempts", this.props.retryAttempts),
|
|
894
|
+
...this.props.onFailure || this.props.onSuccess ? {
|
|
895
|
+
DestinationConfig: {
|
|
896
|
+
...this.props.onFailure ? {
|
|
897
|
+
OnFailure: {
|
|
898
|
+
Destination: this.props.onFailure
|
|
899
|
+
}
|
|
900
|
+
} : {},
|
|
901
|
+
...this.props.onSuccess ? {
|
|
902
|
+
OnSuccess: {
|
|
903
|
+
Destination: this.props.onSuccess
|
|
904
|
+
}
|
|
905
|
+
} : {}
|
|
906
|
+
}
|
|
907
|
+
} : {}
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
|
|
899
912
|
// src/plugins/function.ts
|
|
900
913
|
var MemorySizeSchema = SizeSchema.refine(sizeMin(Size.megaBytes(128)), "Minimum memory size is 128 MB").refine(sizeMax(Size.gigaBytes(10)), "Minimum memory size is 10 GB");
|
|
901
914
|
var TimeoutSchema = DurationSchema.refine(durationMin(Duration.seconds(10)), "Minimum timeout duration is 10 seconds").refine(durationMax(Duration.minutes(15)), "Maximum timeout duration is 15 minutes");
|
|
@@ -910,29 +923,104 @@ var RuntimeSchema = z6.enum([
|
|
|
910
923
|
var FunctionSchema = z6.union([
|
|
911
924
|
LocalFileSchema,
|
|
912
925
|
z6.object({
|
|
926
|
+
/** The file path ofthe function code. */
|
|
913
927
|
file: LocalFileSchema,
|
|
928
|
+
/** The amount of time that Lambda allows a function to run before stopping it.
|
|
929
|
+
* You can specify a size value from 1 second to 15 minutes.
|
|
930
|
+
* @default '10 seconds'
|
|
931
|
+
*/
|
|
914
932
|
timeout: TimeoutSchema.optional(),
|
|
933
|
+
/** The identifier of the function's runtime.
|
|
934
|
+
* @default 'nodejs18.x'
|
|
935
|
+
*/
|
|
915
936
|
runtime: RuntimeSchema.optional(),
|
|
937
|
+
/** The amount of memory available to the function at runtime.
|
|
938
|
+
* Increasing the function memory also increases its CPU allocation.
|
|
939
|
+
* The value can be any multiple of 1 MB.
|
|
940
|
+
* You can specify a size value from 128 MB to 10 GB.
|
|
941
|
+
* @default '128 MB'
|
|
942
|
+
*/
|
|
916
943
|
memorySize: MemorySizeSchema.optional(),
|
|
944
|
+
/** The instruction set architecture that the function supports.
|
|
945
|
+
* @default 'arm64'
|
|
946
|
+
*/
|
|
917
947
|
architecture: ArchitectureSchema.optional(),
|
|
948
|
+
/** The size of the function's /tmp directory.
|
|
949
|
+
* You can specify a size value from 512 MB to 10 GB.
|
|
950
|
+
* @default 512 MB
|
|
951
|
+
*/
|
|
918
952
|
ephemeralStorageSize: EphemeralStorageSizeSchema.optional(),
|
|
953
|
+
/** The maximum number of times to retry when the function returns an error.
|
|
954
|
+
* You can specify a number from 0 to 2.
|
|
955
|
+
* @default 2
|
|
956
|
+
*/
|
|
919
957
|
retryAttempts: RetryAttemptsSchema.optional(),
|
|
958
|
+
/** Environment variable key-value pairs.
|
|
959
|
+
* @example
|
|
960
|
+
* {
|
|
961
|
+
* environment: {
|
|
962
|
+
* name: 'value'
|
|
963
|
+
* }
|
|
964
|
+
* }
|
|
965
|
+
*/
|
|
920
966
|
environment: EnvironmentSchema.optional()
|
|
967
|
+
// onFailure: ResourceIdSchema.optional(),
|
|
921
968
|
})
|
|
922
969
|
]);
|
|
923
970
|
var schema = z6.object({
|
|
924
971
|
defaults: z6.object({
|
|
925
972
|
function: z6.object({
|
|
973
|
+
/** The amount of time that Lambda allows a function to run before stopping it.
|
|
974
|
+
* You can specify a size value from 1 second to 15 minutes.
|
|
975
|
+
* @default '10 seconds'
|
|
976
|
+
*/
|
|
926
977
|
timeout: TimeoutSchema.default("10 seconds"),
|
|
978
|
+
/** The identifier of the function's runtime.
|
|
979
|
+
* @default 'nodejs18.x'
|
|
980
|
+
*/
|
|
927
981
|
runtime: RuntimeSchema.default("nodejs18.x"),
|
|
982
|
+
/** The amount of memory available to the function at runtime.
|
|
983
|
+
* Increasing the function memory also increases its CPU allocation.
|
|
984
|
+
* The value can be any multiple of 1 MB.
|
|
985
|
+
* You can specify a size value from 128 MB to 10 GB.
|
|
986
|
+
* @default '128 MB'
|
|
987
|
+
*/
|
|
928
988
|
memorySize: MemorySizeSchema.default("128 MB"),
|
|
989
|
+
/** The instruction set architecture that the function supports.
|
|
990
|
+
* @default 'arm64'
|
|
991
|
+
*/
|
|
929
992
|
architecture: ArchitectureSchema.default("arm64"),
|
|
993
|
+
/** The size of the function's /tmp directory.
|
|
994
|
+
* You can specify a size value from 512 MB to 10 GB.
|
|
995
|
+
* @default 512 MB
|
|
996
|
+
*/
|
|
930
997
|
ephemeralStorageSize: EphemeralStorageSizeSchema.default("512 MB"),
|
|
998
|
+
/** The maximum number of times to retry when the function returns an error.
|
|
999
|
+
* You can specify a number from 0 to 2.
|
|
1000
|
+
* @default 2
|
|
1001
|
+
*/
|
|
931
1002
|
retryAttempts: RetryAttemptsSchema.default(2),
|
|
1003
|
+
/** Environment variable key-value pairs.
|
|
1004
|
+
* @example
|
|
1005
|
+
* {
|
|
1006
|
+
* environment: {
|
|
1007
|
+
* name: 'value'
|
|
1008
|
+
* }
|
|
1009
|
+
* }
|
|
1010
|
+
*/
|
|
932
1011
|
environment: EnvironmentSchema.optional()
|
|
1012
|
+
// onFailure: ResourceIdSchema.optional(),
|
|
933
1013
|
}).default({})
|
|
934
1014
|
}).default({}),
|
|
935
1015
|
stacks: z6.object({
|
|
1016
|
+
/** Define the functions in your stack.
|
|
1017
|
+
* @example
|
|
1018
|
+
* {
|
|
1019
|
+
* functions: {
|
|
1020
|
+
* FUNCTION_NAME: 'function.ts'
|
|
1021
|
+
* }
|
|
1022
|
+
* }
|
|
1023
|
+
*/
|
|
936
1024
|
functions: z6.record(
|
|
937
1025
|
ResourceIdSchema,
|
|
938
1026
|
FunctionSchema
|
|
@@ -958,12 +1046,15 @@ var toLambdaFunction = (ctx, id, fileOrProps) => {
|
|
|
958
1046
|
code: Code.fromFile(id, props.file),
|
|
959
1047
|
...props
|
|
960
1048
|
});
|
|
961
|
-
lambda.addEnvironment("APP", config.name);
|
|
962
|
-
lambda.addEnvironment("STAGE", config.stage);
|
|
963
|
-
lambda.addEnvironment("STACK", stack.name);
|
|
1049
|
+
lambda.addEnvironment("APP", config.name).addEnvironment("STAGE", config.stage).addEnvironment("STACK", stack.name);
|
|
964
1050
|
if (props.runtime.startsWith("nodejs")) {
|
|
965
1051
|
lambda.addEnvironment("AWS_NODEJS_CONNECTION_REUSE_ENABLED", "1");
|
|
966
1052
|
}
|
|
1053
|
+
const invoke = new EventInvokeConfig(id, {
|
|
1054
|
+
functionName: lambda.name,
|
|
1055
|
+
retryAttempts: props.retryAttempts
|
|
1056
|
+
}).dependsOn(lambda);
|
|
1057
|
+
ctx.stack.add(invoke);
|
|
967
1058
|
return lambda;
|
|
968
1059
|
};
|
|
969
1060
|
|
|
@@ -1041,9 +1132,26 @@ var cronPlugin = definePlugin({
|
|
|
1041
1132
|
name: "cron",
|
|
1042
1133
|
schema: z7.object({
|
|
1043
1134
|
stacks: z7.object({
|
|
1135
|
+
/** Define the crons in your stack
|
|
1136
|
+
* @example
|
|
1137
|
+
* {
|
|
1138
|
+
* crons: {
|
|
1139
|
+
* CRON_NAME: {
|
|
1140
|
+
* consumer: 'function.ts',
|
|
1141
|
+
* schedule: 'rate(5 minutes)',
|
|
1142
|
+
* }
|
|
1143
|
+
* }
|
|
1144
|
+
* }
|
|
1145
|
+
* */
|
|
1044
1146
|
crons: z7.record(ResourceIdSchema, z7.object({
|
|
1147
|
+
/** The consuming lambda function properties */
|
|
1045
1148
|
consumer: FunctionSchema,
|
|
1149
|
+
/** The scheduling expression.
|
|
1150
|
+
* @example 'cron(0 20 * * ? *)'
|
|
1151
|
+
* @example 'rate(5 minutes)'
|
|
1152
|
+
*/
|
|
1046
1153
|
schedule: ScheduleExpressionSchema,
|
|
1154
|
+
// Valid JSON passed to the consumer.
|
|
1047
1155
|
payload: z7.unknown().optional()
|
|
1048
1156
|
})).optional()
|
|
1049
1157
|
}).array()
|
|
@@ -1072,6 +1180,10 @@ var Queue = class extends Resource {
|
|
|
1072
1180
|
this.name = formatName(this.props.name || logicalId);
|
|
1073
1181
|
}
|
|
1074
1182
|
name;
|
|
1183
|
+
setDeadLetter(arn) {
|
|
1184
|
+
this.props.deadLetterArn = arn;
|
|
1185
|
+
return this;
|
|
1186
|
+
}
|
|
1075
1187
|
get arn() {
|
|
1076
1188
|
return getAtt(this.logicalId, "Arn");
|
|
1077
1189
|
}
|
|
@@ -1096,7 +1208,12 @@ var Queue = class extends Resource {
|
|
|
1096
1208
|
MaximumMessageSize: this.props.maxMessageSize?.toBytes() ?? Size.kiloBytes(256).toBytes(),
|
|
1097
1209
|
MessageRetentionPeriod: this.props.retentionPeriod?.toSeconds() ?? Duration.days(4).toSeconds(),
|
|
1098
1210
|
ReceiveMessageWaitTimeSeconds: this.props.receiveMessageWaitTime?.toSeconds() ?? 0,
|
|
1099
|
-
VisibilityTimeout: this.props.visibilityTimeout?.toSeconds() ?? 30
|
|
1211
|
+
VisibilityTimeout: this.props.visibilityTimeout?.toSeconds() ?? 30,
|
|
1212
|
+
...this.props.deadLetterArn ? {
|
|
1213
|
+
RedrivePolicy: {
|
|
1214
|
+
deadLetterTargetArn: this.props.deadLetterArn
|
|
1215
|
+
}
|
|
1216
|
+
} : {}
|
|
1100
1217
|
};
|
|
1101
1218
|
}
|
|
1102
1219
|
};
|
|
@@ -1108,6 +1225,10 @@ var EventSourceMapping = class extends Resource {
|
|
|
1108
1225
|
super("AWS::Lambda::EventSourceMapping", logicalId);
|
|
1109
1226
|
this.props = props;
|
|
1110
1227
|
}
|
|
1228
|
+
setOnFailure(arn) {
|
|
1229
|
+
this.props.onFailure = arn;
|
|
1230
|
+
return this;
|
|
1231
|
+
}
|
|
1111
1232
|
properties() {
|
|
1112
1233
|
return {
|
|
1113
1234
|
Enabled: true,
|
|
@@ -1166,7 +1287,7 @@ var queuePlugin = definePlugin({
|
|
|
1166
1287
|
name: "queue",
|
|
1167
1288
|
schema: z8.object({
|
|
1168
1289
|
defaults: z8.object({
|
|
1169
|
-
/** Define the defaults properties for all queue's in your app */
|
|
1290
|
+
/** Define the defaults properties for all queue's in your app. */
|
|
1170
1291
|
queue: z8.object({
|
|
1171
1292
|
/** The number of seconds that Amazon SQS retains a message.
|
|
1172
1293
|
* You can specify a duration value from 1 minute to 14 days.
|
|
@@ -1195,20 +1316,20 @@ var queuePlugin = definePlugin({
|
|
|
1195
1316
|
}).default({})
|
|
1196
1317
|
}).default({}),
|
|
1197
1318
|
stacks: z8.object({
|
|
1198
|
-
/** Define the queues in your stack
|
|
1319
|
+
/** Define the queues in your stack.
|
|
1199
1320
|
* @example
|
|
1200
1321
|
* {
|
|
1201
1322
|
* queues: {
|
|
1202
1323
|
* QUEUE_NAME: 'function.ts'
|
|
1203
1324
|
* }
|
|
1204
1325
|
* }
|
|
1205
|
-
|
|
1326
|
+
*/
|
|
1206
1327
|
queues: z8.record(
|
|
1207
1328
|
ResourceIdSchema,
|
|
1208
1329
|
z8.union([
|
|
1209
1330
|
LocalFileSchema,
|
|
1210
1331
|
z8.object({
|
|
1211
|
-
/** The consuming lambda function properties */
|
|
1332
|
+
/** The consuming lambda function properties. */
|
|
1212
1333
|
consumer: FunctionSchema,
|
|
1213
1334
|
/** The number of seconds that Amazon SQS retains a message.
|
|
1214
1335
|
* You can specify a duration value from 1 minute to 14 days.
|
|
@@ -1226,7 +1347,6 @@ var queuePlugin = definePlugin({
|
|
|
1226
1347
|
/** Specifies the duration, in seconds,
|
|
1227
1348
|
* that the ReceiveMessage action call waits until a message is in the queue in order to include it in the response,
|
|
1228
1349
|
* rather than returning an empty response if a message isn't yet available.
|
|
1229
|
-
* You can specify an integer from 1 to 20.
|
|
1230
1350
|
* You can specify a duration value from 1 to 20 seconds.
|
|
1231
1351
|
* @default '0 seconds' */
|
|
1232
1352
|
receiveMessageWaitTime: DurationSchema.optional(),
|
|
@@ -1247,7 +1367,7 @@ var queuePlugin = definePlugin({
|
|
|
1247
1367
|
name: `${config.name}-${stack.name}-${id}`,
|
|
1248
1368
|
...props
|
|
1249
1369
|
});
|
|
1250
|
-
const lambda = toLambdaFunction(ctx, id
|
|
1370
|
+
const lambda = toLambdaFunction(ctx, `queue-${id}`, props.consumer);
|
|
1251
1371
|
const source = new SqsEventSource(id, lambda, {
|
|
1252
1372
|
queueArn: queue2.arn
|
|
1253
1373
|
});
|
|
@@ -1273,12 +1393,21 @@ var Table = class extends Resource {
|
|
|
1273
1393
|
}
|
|
1274
1394
|
name;
|
|
1275
1395
|
indexes;
|
|
1396
|
+
enableStream(viewType) {
|
|
1397
|
+
this.props.stream = viewType;
|
|
1398
|
+
}
|
|
1276
1399
|
addIndex(name, props) {
|
|
1277
1400
|
this.indexes[name] = props;
|
|
1278
1401
|
}
|
|
1279
|
-
get
|
|
1402
|
+
get id() {
|
|
1280
1403
|
return ref(this.logicalId);
|
|
1281
1404
|
}
|
|
1405
|
+
get arn() {
|
|
1406
|
+
return getAtt(this.logicalId, "Arn");
|
|
1407
|
+
}
|
|
1408
|
+
get streamArn() {
|
|
1409
|
+
return getAtt(this.logicalId, "StreamArn");
|
|
1410
|
+
}
|
|
1282
1411
|
get permissions() {
|
|
1283
1412
|
return {
|
|
1284
1413
|
actions: [
|
|
@@ -1312,6 +1441,11 @@ var Table = class extends Resource {
|
|
|
1312
1441
|
AttributeName: name,
|
|
1313
1442
|
AttributeType: type[0].toUpperCase()
|
|
1314
1443
|
})),
|
|
1444
|
+
...this.props.stream ? {
|
|
1445
|
+
StreamSpecification: {
|
|
1446
|
+
StreamViewType: constantCase2(this.props.stream)
|
|
1447
|
+
}
|
|
1448
|
+
} : {},
|
|
1315
1449
|
...this.props.timeToLiveAttribute ? {
|
|
1316
1450
|
TimeToLiveSpecification: {
|
|
1317
1451
|
AttributeName: this.props.timeToLiveAttribute,
|
|
@@ -1334,13 +1468,42 @@ var Table = class extends Resource {
|
|
|
1334
1468
|
}
|
|
1335
1469
|
};
|
|
1336
1470
|
|
|
1471
|
+
// src/formation/resource/lambda/event-source/dynamodb.ts
|
|
1472
|
+
var DynamoDBEventSource = class extends Group {
|
|
1473
|
+
constructor(id, lambda, props) {
|
|
1474
|
+
const source = new EventSourceMapping(id, {
|
|
1475
|
+
functionArn: lambda.arn,
|
|
1476
|
+
sourceArn: props.tableArn,
|
|
1477
|
+
batchSize: props.batchSize ?? 100,
|
|
1478
|
+
bisectBatchOnError: props.bisectBatchOnError ?? true,
|
|
1479
|
+
maxBatchingWindow: props.maxBatchingWindow,
|
|
1480
|
+
maxRecordAge: props.maxRecordAge,
|
|
1481
|
+
retryAttempts: props.retryAttempts ?? -1,
|
|
1482
|
+
parallelizationFactor: props.parallelizationFactor ?? 1,
|
|
1483
|
+
startingPosition: props.startingPosition,
|
|
1484
|
+
startingPositionTimestamp: props.startingPositionTimestamp,
|
|
1485
|
+
tumblingWindow: props.tumblingWindow
|
|
1486
|
+
});
|
|
1487
|
+
lambda.addPermissions({
|
|
1488
|
+
actions: [
|
|
1489
|
+
"dynamodb:ListStreams",
|
|
1490
|
+
"dynamodb:DescribeStream",
|
|
1491
|
+
"dynamodb:GetRecords",
|
|
1492
|
+
"dynamodb:GetShardIterator"
|
|
1493
|
+
],
|
|
1494
|
+
resources: [props.tableArn]
|
|
1495
|
+
});
|
|
1496
|
+
super([source]);
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1337
1500
|
// src/plugins/table.ts
|
|
1338
1501
|
var KeySchema = z9.string().min(1).max(255);
|
|
1339
1502
|
var tablePlugin = definePlugin({
|
|
1340
1503
|
name: "table",
|
|
1341
1504
|
schema: z9.object({
|
|
1342
1505
|
stacks: z9.object({
|
|
1343
|
-
/** Define the tables in your stack
|
|
1506
|
+
/** Define the tables in your stack.
|
|
1344
1507
|
* @example
|
|
1345
1508
|
* {
|
|
1346
1509
|
* tables: {
|
|
@@ -1367,20 +1530,34 @@ var tablePlugin = definePlugin({
|
|
|
1367
1530
|
* id: 'string'
|
|
1368
1531
|
* }
|
|
1369
1532
|
* }
|
|
1370
|
-
|
|
1533
|
+
*/
|
|
1371
1534
|
fields: z9.record(z9.string(), z9.enum(["string", "number", "binary"])),
|
|
1372
1535
|
/** The table class of the table.
|
|
1373
1536
|
* @default 'standard'
|
|
1374
|
-
|
|
1537
|
+
*/
|
|
1375
1538
|
class: z9.enum(["standard", "standard-infrequent-access"]).default("standard"),
|
|
1376
1539
|
/** Indicates whether point in time recovery is enabled on the table.
|
|
1377
1540
|
* @default false
|
|
1378
|
-
|
|
1541
|
+
*/
|
|
1379
1542
|
pointInTimeRecovery: z9.boolean().default(false),
|
|
1380
1543
|
/** The name of the TTL attribute used to store the expiration time for items in the table.
|
|
1381
1544
|
* - To update this property, you must first disable TTL and then enable TTL with the new attribute name.
|
|
1382
|
-
|
|
1545
|
+
*/
|
|
1383
1546
|
timeToLiveAttribute: KeySchema.optional(),
|
|
1547
|
+
/** The settings for the DynamoDB table stream, which capture changes to items stored in the table. */
|
|
1548
|
+
stream: z9.object({
|
|
1549
|
+
/** When an item in the table is modified,
|
|
1550
|
+
* stream.type determines what information is written to the stream for this table.
|
|
1551
|
+
* Valid values are:
|
|
1552
|
+
* - keys-only - Only the key attributes of the modified item are written to the stream.
|
|
1553
|
+
* - new-image - The entire item, as it appears after it was modified, is written to the stream.
|
|
1554
|
+
* - old-image - The entire item, as it appeared before it was modified, is written to the stream.
|
|
1555
|
+
* - new-and-old-images - Both the new and the old item images of the item are written to the stream.
|
|
1556
|
+
*/
|
|
1557
|
+
type: z9.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]),
|
|
1558
|
+
/** The consuming lambda function for the stream */
|
|
1559
|
+
consumer: FunctionSchema
|
|
1560
|
+
}).optional(),
|
|
1384
1561
|
/** Specifies the global secondary indexes to be created on the table.
|
|
1385
1562
|
* @example
|
|
1386
1563
|
* {
|
|
@@ -1390,7 +1567,7 @@ var tablePlugin = definePlugin({
|
|
|
1390
1567
|
* }
|
|
1391
1568
|
* }
|
|
1392
1569
|
* }
|
|
1393
|
-
|
|
1570
|
+
*/
|
|
1394
1571
|
indexes: z9.record(z9.string(), z9.object({
|
|
1395
1572
|
/** Specifies the name of the partition / hash key that makes up the primary key for the global secondary index. */
|
|
1396
1573
|
hash: KeySchema,
|
|
@@ -1418,13 +1595,23 @@ var tablePlugin = definePlugin({
|
|
|
1418
1595
|
).optional()
|
|
1419
1596
|
}).array()
|
|
1420
1597
|
}),
|
|
1421
|
-
onStack(
|
|
1598
|
+
onStack(ctx) {
|
|
1599
|
+
const { config, stack, stackConfig, bind } = ctx;
|
|
1422
1600
|
for (const [id, props] of Object.entries(stackConfig.tables || {})) {
|
|
1423
1601
|
const table = new Table(id, {
|
|
1602
|
+
...props,
|
|
1424
1603
|
name: `${config.name}-${stack.name}-${id}`,
|
|
1425
|
-
|
|
1604
|
+
stream: props.stream?.type
|
|
1426
1605
|
});
|
|
1427
1606
|
stack.add(table);
|
|
1607
|
+
if (props.stream) {
|
|
1608
|
+
const lambda = toLambdaFunction(ctx, `stream-${id}`, props.stream.consumer);
|
|
1609
|
+
const source = new DynamoDBEventSource(id, lambda, {
|
|
1610
|
+
tableArn: table.arn,
|
|
1611
|
+
...props.stream
|
|
1612
|
+
});
|
|
1613
|
+
stack.add(lambda, source);
|
|
1614
|
+
}
|
|
1428
1615
|
bind((lambda) => {
|
|
1429
1616
|
lambda.addPermissions(table.permissions);
|
|
1430
1617
|
});
|
|
@@ -1479,12 +1666,12 @@ var storePlugin = definePlugin({
|
|
|
1479
1666
|
name: "store",
|
|
1480
1667
|
schema: z10.object({
|
|
1481
1668
|
stacks: z10.object({
|
|
1482
|
-
/** Define the stores in your stack
|
|
1669
|
+
/** Define the stores in your stack.
|
|
1483
1670
|
* @example
|
|
1484
1671
|
* {
|
|
1485
1672
|
* stores: [ 'STORE_NAME' ]
|
|
1486
1673
|
* }
|
|
1487
|
-
|
|
1674
|
+
*/
|
|
1488
1675
|
stores: z10.array(ResourceIdSchema).optional()
|
|
1489
1676
|
}).array()
|
|
1490
1677
|
}),
|
|
@@ -1568,14 +1755,14 @@ var topicPlugin = definePlugin({
|
|
|
1568
1755
|
name: "topic",
|
|
1569
1756
|
schema: z11.object({
|
|
1570
1757
|
stacks: z11.object({
|
|
1571
|
-
/** Define the topics to listen too in your stack
|
|
1758
|
+
/** Define the topics to listen too in your stack.
|
|
1572
1759
|
* @example
|
|
1573
1760
|
* {
|
|
1574
1761
|
* topics: {
|
|
1575
1762
|
* TOPIC_NAME: 'function.ts'
|
|
1576
1763
|
* }
|
|
1577
1764
|
* }
|
|
1578
|
-
|
|
1765
|
+
*/
|
|
1579
1766
|
topics: z11.record(ResourceIdSchema, FunctionSchema).optional()
|
|
1580
1767
|
}).array()
|
|
1581
1768
|
}),
|
|
@@ -1605,7 +1792,7 @@ var topicPlugin = definePlugin({
|
|
|
1605
1792
|
onStack(ctx) {
|
|
1606
1793
|
const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
|
|
1607
1794
|
for (const [id, props] of Object.entries(stackConfig.topics || {})) {
|
|
1608
|
-
const lambda = toLambdaFunction(ctx, id
|
|
1795
|
+
const lambda = toLambdaFunction(ctx, `topic-${id}`, props);
|
|
1609
1796
|
const source = new SnsEventSource(id, lambda, {
|
|
1610
1797
|
topicArn: bootstrap2.import(`topic-${id}-arn`)
|
|
1611
1798
|
});
|
|
@@ -1619,10 +1806,10 @@ import { z as z12 } from "zod";
|
|
|
1619
1806
|
var extendPlugin = definePlugin({
|
|
1620
1807
|
name: "extend",
|
|
1621
1808
|
schema: z12.object({
|
|
1622
|
-
/** Extend your app with custom resources */
|
|
1809
|
+
/** Extend your app with custom resources. */
|
|
1623
1810
|
extend: z12.custom().optional(),
|
|
1624
1811
|
stacks: z12.object({
|
|
1625
|
-
/** Extend your stack with custom resources */
|
|
1812
|
+
/** Extend your stack with custom resources. */
|
|
1626
1813
|
extend: z12.custom().optional()
|
|
1627
1814
|
}).array()
|
|
1628
1815
|
}),
|
|
@@ -1688,7 +1875,7 @@ var pubsubPlugin = definePlugin({
|
|
|
1688
1875
|
name: "pubsub",
|
|
1689
1876
|
schema: z13.object({
|
|
1690
1877
|
stacks: z13.object({
|
|
1691
|
-
/** Define the pubsub subscriber in your stack
|
|
1878
|
+
/** Define the pubsub subscriber in your stack.
|
|
1692
1879
|
* @example
|
|
1693
1880
|
* {
|
|
1694
1881
|
* pubsub: {
|
|
@@ -1700,11 +1887,11 @@ var pubsubPlugin = definePlugin({
|
|
|
1700
1887
|
* }
|
|
1701
1888
|
*/
|
|
1702
1889
|
pubsub: z13.record(ResourceIdSchema, z13.object({
|
|
1703
|
-
/** The SQL statement used to query the iot topic */
|
|
1890
|
+
/** The SQL statement used to query the iot topic. */
|
|
1704
1891
|
sql: z13.string(),
|
|
1705
|
-
/** The version of the SQL rules engine to use when evaluating the rule */
|
|
1892
|
+
/** The version of the SQL rules engine to use when evaluating the rule. */
|
|
1706
1893
|
sqlVersion: z13.enum(["2015-10-08", "2016-03-23", "beta"]).default("2016-03-23"),
|
|
1707
|
-
/** The consuming lambda function properties */
|
|
1894
|
+
/** The consuming lambda function properties. */
|
|
1708
1895
|
consumer: FunctionSchema
|
|
1709
1896
|
})).optional()
|
|
1710
1897
|
}).array()
|
|
@@ -1720,7 +1907,7 @@ var pubsubPlugin = definePlugin({
|
|
|
1720
1907
|
onStack(ctx) {
|
|
1721
1908
|
const { config, stack, stackConfig } = ctx;
|
|
1722
1909
|
for (const [id, props] of Object.entries(stackConfig.pubsub || {})) {
|
|
1723
|
-
const lambda = toLambdaFunction(ctx, id
|
|
1910
|
+
const lambda = toLambdaFunction(ctx, `pubsub-${id}`, props.consumer);
|
|
1724
1911
|
const source = new IotEventSource(id, lambda, {
|
|
1725
1912
|
name: `${config.name}-${stack.name}-${id}`,
|
|
1726
1913
|
sql: props.sql,
|
|
@@ -1747,34 +1934,6 @@ import { paramCase as paramCase3 } from "change-case";
|
|
|
1747
1934
|
|
|
1748
1935
|
// src/formation/resource/appsync/graphql-api.ts
|
|
1749
1936
|
import { constantCase as constantCase3 } from "change-case";
|
|
1750
|
-
var GraphQL = class extends Group {
|
|
1751
|
-
constructor(logicalId, props) {
|
|
1752
|
-
const api = new GraphQLApi(logicalId, props);
|
|
1753
|
-
const schema2 = new GraphQLSchema(logicalId, {
|
|
1754
|
-
apiId: api.id,
|
|
1755
|
-
definition: props.schema
|
|
1756
|
-
}).dependsOn(api);
|
|
1757
|
-
super([api, schema2]);
|
|
1758
|
-
this.logicalId = logicalId;
|
|
1759
|
-
this.api = api;
|
|
1760
|
-
this.schema = schema2;
|
|
1761
|
-
}
|
|
1762
|
-
api;
|
|
1763
|
-
schema;
|
|
1764
|
-
attachDomainName(domainName, certificateArn) {
|
|
1765
|
-
const id = this.logicalId + domainName;
|
|
1766
|
-
const domain = new DomainName(id, {
|
|
1767
|
-
domainName,
|
|
1768
|
-
certificateArn
|
|
1769
|
-
});
|
|
1770
|
-
const association = new DomainNameApiAssociation(id, {
|
|
1771
|
-
apiId: this.api.id,
|
|
1772
|
-
domainName
|
|
1773
|
-
}).dependsOn(this.api, domain);
|
|
1774
|
-
this.children.push(domain, association);
|
|
1775
|
-
return this;
|
|
1776
|
-
}
|
|
1777
|
-
};
|
|
1778
1937
|
var GraphQLApi = class extends Resource {
|
|
1779
1938
|
constructor(logicalId, props) {
|
|
1780
1939
|
super("AWS::AppSync::GraphQLApi", logicalId);
|
|
@@ -1816,44 +1975,6 @@ var GraphQLApi = class extends Resource {
|
|
|
1816
1975
|
};
|
|
1817
1976
|
}
|
|
1818
1977
|
};
|
|
1819
|
-
var GraphQLSchema = class extends Resource {
|
|
1820
|
-
constructor(logicalId, props) {
|
|
1821
|
-
super("AWS::AppSync::GraphQLSchema", logicalId, [
|
|
1822
|
-
props.definition
|
|
1823
|
-
]);
|
|
1824
|
-
this.props = props;
|
|
1825
|
-
}
|
|
1826
|
-
properties() {
|
|
1827
|
-
return {
|
|
1828
|
-
ApiId: this.props.apiId,
|
|
1829
|
-
Definition: this.props.definition.toDefinition()
|
|
1830
|
-
};
|
|
1831
|
-
}
|
|
1832
|
-
};
|
|
1833
|
-
var DomainName = class extends Resource {
|
|
1834
|
-
constructor(logicalId, props) {
|
|
1835
|
-
super("AWS::AppSync::DomainName", logicalId);
|
|
1836
|
-
this.props = props;
|
|
1837
|
-
}
|
|
1838
|
-
properties() {
|
|
1839
|
-
return {
|
|
1840
|
-
DomainName: this.props.domainName,
|
|
1841
|
-
CertificateArn: this.props.certificateArn
|
|
1842
|
-
};
|
|
1843
|
-
}
|
|
1844
|
-
};
|
|
1845
|
-
var DomainNameApiAssociation = class extends Resource {
|
|
1846
|
-
constructor(logicalId, props) {
|
|
1847
|
-
super("AWS::AppSync::DomainNameApiAssociation", logicalId);
|
|
1848
|
-
this.props = props;
|
|
1849
|
-
}
|
|
1850
|
-
properties() {
|
|
1851
|
-
return {
|
|
1852
|
-
ApiId: this.props.apiId,
|
|
1853
|
-
DomainName: this.props.domainName
|
|
1854
|
-
};
|
|
1855
|
-
}
|
|
1856
|
-
};
|
|
1857
1978
|
|
|
1858
1979
|
// src/formation/resource/route53/record-set.ts
|
|
1859
1980
|
var RecordSet = class extends Resource {
|
|
@@ -1874,19 +1995,33 @@ var RecordSet = class extends Resource {
|
|
|
1874
1995
|
} : {},
|
|
1875
1996
|
...this.props.alias ? {
|
|
1876
1997
|
AliasTarget: {
|
|
1877
|
-
DNSName: this.props.alias,
|
|
1878
|
-
HostedZoneId: this.props.hostedZoneId
|
|
1998
|
+
DNSName: this.props.alias.dnsName,
|
|
1999
|
+
HostedZoneId: this.props.alias.hostedZoneId
|
|
1879
2000
|
}
|
|
1880
2001
|
} : {}
|
|
1881
2002
|
};
|
|
1882
2003
|
}
|
|
1883
2004
|
};
|
|
1884
2005
|
|
|
1885
|
-
// src/formation/resource/appsync/schema.ts
|
|
2006
|
+
// src/formation/resource/appsync/graphql-schema.ts
|
|
1886
2007
|
import { print } from "graphql";
|
|
1887
2008
|
import { readFile } from "fs/promises";
|
|
1888
2009
|
import { mergeTypeDefs } from "@graphql-tools/merge";
|
|
1889
|
-
var
|
|
2010
|
+
var GraphQLSchema = class extends Resource {
|
|
2011
|
+
constructor(logicalId, props) {
|
|
2012
|
+
super("AWS::AppSync::GraphQLSchema", logicalId, [
|
|
2013
|
+
props.definition
|
|
2014
|
+
]);
|
|
2015
|
+
this.props = props;
|
|
2016
|
+
}
|
|
2017
|
+
properties() {
|
|
2018
|
+
return {
|
|
2019
|
+
ApiId: this.props.apiId,
|
|
2020
|
+
Definition: this.props.definition.toString()
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
var Definition = class extends Asset {
|
|
1890
2025
|
constructor(id, files) {
|
|
1891
2026
|
super("graphql", id);
|
|
1892
2027
|
this.files = files;
|
|
@@ -1899,10 +2034,14 @@ var Schema = class extends Asset {
|
|
|
1899
2034
|
}));
|
|
1900
2035
|
const defs = mergeTypeDefs(schemas);
|
|
1901
2036
|
const schema2 = print(defs);
|
|
2037
|
+
const size = Buffer.from(schema2, "utf8").byteLength;
|
|
1902
2038
|
await write("schema.gql", schema2);
|
|
1903
2039
|
this.schema = schema2;
|
|
2040
|
+
return {
|
|
2041
|
+
size: formatByteSize(size)
|
|
2042
|
+
};
|
|
1904
2043
|
}
|
|
1905
|
-
|
|
2044
|
+
toString() {
|
|
1906
2045
|
return this.schema;
|
|
1907
2046
|
}
|
|
1908
2047
|
};
|
|
@@ -2082,6 +2221,41 @@ var AppsyncEventSource = class extends Group {
|
|
|
2082
2221
|
}
|
|
2083
2222
|
};
|
|
2084
2223
|
|
|
2224
|
+
// src/formation/resource/appsync/domain-name.ts
|
|
2225
|
+
var DomainName = class extends Resource {
|
|
2226
|
+
constructor(logicalId, props) {
|
|
2227
|
+
super("AWS::AppSync::DomainName", logicalId);
|
|
2228
|
+
this.props = props;
|
|
2229
|
+
}
|
|
2230
|
+
get appSyncDomainName() {
|
|
2231
|
+
return getAtt(this.logicalId, "AppSyncDomainName");
|
|
2232
|
+
}
|
|
2233
|
+
get domainName() {
|
|
2234
|
+
return getAtt(this.logicalId, "DomainName");
|
|
2235
|
+
}
|
|
2236
|
+
get hostedZoneId() {
|
|
2237
|
+
return getAtt(this.logicalId, "HostedZoneId");
|
|
2238
|
+
}
|
|
2239
|
+
properties() {
|
|
2240
|
+
return {
|
|
2241
|
+
DomainName: this.props.domainName,
|
|
2242
|
+
CertificateArn: this.props.certificateArn
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
};
|
|
2246
|
+
var DomainNameApiAssociation = class extends Resource {
|
|
2247
|
+
constructor(logicalId, props) {
|
|
2248
|
+
super("AWS::AppSync::DomainNameApiAssociation", logicalId);
|
|
2249
|
+
this.props = props;
|
|
2250
|
+
}
|
|
2251
|
+
properties() {
|
|
2252
|
+
return {
|
|
2253
|
+
ApiId: this.props.apiId,
|
|
2254
|
+
DomainName: this.props.domainName
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
};
|
|
2258
|
+
|
|
2085
2259
|
// src/plugins/graphql.ts
|
|
2086
2260
|
var defaultResolver = `
|
|
2087
2261
|
export function request(ctx) {
|
|
@@ -2131,38 +2305,51 @@ var graphqlPlugin = definePlugin({
|
|
|
2131
2305
|
}
|
|
2132
2306
|
}
|
|
2133
2307
|
for (const id of apis) {
|
|
2134
|
-
const
|
|
2308
|
+
const schemaFiles = [];
|
|
2135
2309
|
for (const stack of config.stacks) {
|
|
2136
2310
|
const files = toArray(stack.graphql?.[id]?.schema || []);
|
|
2137
|
-
|
|
2311
|
+
schemaFiles.push(...files);
|
|
2138
2312
|
}
|
|
2139
|
-
const
|
|
2313
|
+
const api = new GraphQLApi(id, {
|
|
2140
2314
|
name: `${config.name}-${id}`,
|
|
2141
|
-
authenticationType: "api-key"
|
|
2142
|
-
schema: new Schema(id, schema2)
|
|
2315
|
+
authenticationType: "api-key"
|
|
2143
2316
|
});
|
|
2144
|
-
|
|
2317
|
+
const schema2 = new GraphQLSchema(id, {
|
|
2318
|
+
apiId: api.id,
|
|
2319
|
+
definition: new Definition(id, schemaFiles)
|
|
2320
|
+
}).dependsOn(api);
|
|
2321
|
+
bootstrap2.add(api).add(schema2).export(`graphql-${id}`, api.id);
|
|
2145
2322
|
const props = config.defaults.graphql?.[id];
|
|
2146
2323
|
if (!props) {
|
|
2147
2324
|
continue;
|
|
2148
2325
|
}
|
|
2149
2326
|
if (props.authorization) {
|
|
2150
2327
|
const lambda = toLambdaFunction(ctx, `${id}-authorizer`, props.authorization.authorizer);
|
|
2151
|
-
|
|
2328
|
+
api.addLambdaAuthProvider(lambda.arn, props.authorization.ttl);
|
|
2152
2329
|
bootstrap2.add(lambda);
|
|
2153
2330
|
}
|
|
2154
2331
|
if (props.domain) {
|
|
2155
2332
|
const domainName = props.subDomain ? `${props.subDomain}.${props.domain}` : props.domain;
|
|
2156
|
-
const hostedZoneId =
|
|
2333
|
+
const hostedZoneId = usEastBootstrap.import(`hosted-zone-${props.domain}-id`);
|
|
2157
2334
|
const certificateArn = usEastBootstrap.import(`certificate-${props.domain}-arn`);
|
|
2158
|
-
|
|
2159
|
-
|
|
2335
|
+
const domain = new DomainName(id, {
|
|
2336
|
+
domainName,
|
|
2337
|
+
certificateArn
|
|
2338
|
+
});
|
|
2339
|
+
const association = new DomainNameApiAssociation(id, {
|
|
2340
|
+
apiId: api.id,
|
|
2341
|
+
domainName: domain.domainName
|
|
2342
|
+
}).dependsOn(api, domain);
|
|
2343
|
+
const record = new RecordSet(`${id}-graphql`, {
|
|
2160
2344
|
hostedZoneId,
|
|
2161
2345
|
type: "A",
|
|
2162
2346
|
name: domainName,
|
|
2163
|
-
alias:
|
|
2164
|
-
|
|
2165
|
-
|
|
2347
|
+
alias: {
|
|
2348
|
+
dnsName: domain.appSyncDomainName,
|
|
2349
|
+
hostedZoneId: domain.hostedZoneId
|
|
2350
|
+
}
|
|
2351
|
+
}).dependsOn(domain, association);
|
|
2352
|
+
bootstrap2.add(domain, association, record);
|
|
2166
2353
|
}
|
|
2167
2354
|
}
|
|
2168
2355
|
},
|
|
@@ -2173,7 +2360,7 @@ var graphqlPlugin = definePlugin({
|
|
|
2173
2360
|
for (const [typeAndField, functionProps] of Object.entries(props.resolvers || {})) {
|
|
2174
2361
|
const [typeName, fieldName] = typeAndField.split(/[\s]+/g);
|
|
2175
2362
|
const entryId = paramCase3(`${id}-${typeName}-${fieldName}`);
|
|
2176
|
-
const lambda = toLambdaFunction(ctx, entryId
|
|
2363
|
+
const lambda = toLambdaFunction(ctx, `graphql-${entryId}`, functionProps);
|
|
2177
2364
|
const source = new AppsyncEventSource(entryId, lambda, {
|
|
2178
2365
|
apiId,
|
|
2179
2366
|
typeName,
|
|
@@ -2209,7 +2396,7 @@ var HostedZone = class extends Resource {
|
|
|
2209
2396
|
|
|
2210
2397
|
// src/formation/resource/certificate-manager/certificate.ts
|
|
2211
2398
|
var Certificate = class extends Resource {
|
|
2212
|
-
constructor(logicalId, props
|
|
2399
|
+
constructor(logicalId, props) {
|
|
2213
2400
|
super("AWS::CertificateManager::Certificate", logicalId);
|
|
2214
2401
|
this.props = props;
|
|
2215
2402
|
this.name = this.props.domainName || logicalId;
|
|
@@ -2221,8 +2408,12 @@ var Certificate = class extends Resource {
|
|
|
2221
2408
|
properties() {
|
|
2222
2409
|
return {
|
|
2223
2410
|
DomainName: this.name,
|
|
2411
|
+
SubjectAlternativeNames: this.props.alternativeNames || [],
|
|
2224
2412
|
ValidationMethod: "DNS",
|
|
2225
|
-
|
|
2413
|
+
DomainValidationOptions: [{
|
|
2414
|
+
DomainName: this.name,
|
|
2415
|
+
HostedZoneId: this.props.hostedZoneId
|
|
2416
|
+
}]
|
|
2226
2417
|
};
|
|
2227
2418
|
}
|
|
2228
2419
|
};
|
|
@@ -2254,51 +2445,998 @@ var RecordSetGroup = class extends Resource {
|
|
|
2254
2445
|
}
|
|
2255
2446
|
};
|
|
2256
2447
|
|
|
2257
|
-
// src/
|
|
2258
|
-
var
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
schema: z15.object({
|
|
2262
|
-
domains: z15.record(DomainNameSchema, z15.object({
|
|
2263
|
-
name: DomainNameSchema.optional(),
|
|
2264
|
-
type: z15.enum(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV", "TXT"]),
|
|
2265
|
-
ttl: DurationSchema,
|
|
2266
|
-
records: z15.string().array()
|
|
2267
|
-
}).array()).optional()
|
|
2268
|
-
}),
|
|
2269
|
-
onApp({ config, bootstrap: bootstrap2, usEastBootstrap }) {
|
|
2270
|
-
for (const [domain, records] of Object.entries(config.domains || {})) {
|
|
2271
|
-
const hostedZone = new HostedZone(domain);
|
|
2272
|
-
const certificate = new Certificate(domain, {
|
|
2273
|
-
alternativeNames: [`*.${domain}`]
|
|
2274
|
-
});
|
|
2275
|
-
bootstrap2.add(certificate);
|
|
2276
|
-
usEastBootstrap.add(hostedZone).add(certificate).export(`certificate-${domain}-arn`, certificate.arn);
|
|
2277
|
-
if (records.length > 0) {
|
|
2278
|
-
const group = new RecordSetGroup(domain, {
|
|
2279
|
-
hostedZoneId: hostedZone.id,
|
|
2280
|
-
records
|
|
2281
|
-
}).dependsOn(hostedZone);
|
|
2282
|
-
usEastBootstrap.add(group);
|
|
2283
|
-
}
|
|
2284
|
-
}
|
|
2285
|
-
}
|
|
2286
|
-
});
|
|
2448
|
+
// src/custom/delete-hosted-zone/handler.ts
|
|
2449
|
+
var deleteHostedZoneRecordsHandlerCode = (
|
|
2450
|
+
/* JS */
|
|
2451
|
+
`
|
|
2287
2452
|
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2453
|
+
const { Route53Client, ListResourceRecordSetsCommand, ChangeResourceRecordSetsCommand } = require('@aws-sdk/client-route-53')
|
|
2454
|
+
|
|
2455
|
+
const client = new Route53Client({})
|
|
2456
|
+
|
|
2457
|
+
exports.handler = async (event) => {
|
|
2458
|
+
const type = event.RequestType
|
|
2459
|
+
const hostedZoneId = event.ResourceProperties.hostedZoneId
|
|
2460
|
+
|
|
2461
|
+
try {
|
|
2462
|
+
if(type === 'Delete') {
|
|
2463
|
+
const records = await listHostedZoneRecords(hostedZoneId)
|
|
2464
|
+
console.log(records)
|
|
2465
|
+
|
|
2466
|
+
await deleteHostedZoneRecords(hostedZoneId, records)
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
await send(event, hostedZoneId, 'SUCCESS')
|
|
2470
|
+
}
|
|
2471
|
+
catch(error) {
|
|
2472
|
+
if (error instanceof Error) {
|
|
2473
|
+
await send(event, hostedZoneId, 'FAILED', {}, error.message)
|
|
2474
|
+
} else {
|
|
2475
|
+
await send(event, hostedZoneId, 'FAILED', {}, 'Unknown error')
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
const send = async (event, id, status, data = {}, reason = '') => {
|
|
2481
|
+
const body = JSON.stringify({
|
|
2482
|
+
Status: status,
|
|
2483
|
+
Reason: reason,
|
|
2484
|
+
PhysicalResourceId: id,
|
|
2485
|
+
StackId: event.StackId,
|
|
2486
|
+
RequestId: event.RequestId,
|
|
2487
|
+
LogicalResourceId: event.LogicalResourceId,
|
|
2488
|
+
NoEcho: false,
|
|
2489
|
+
Data: data
|
|
2490
|
+
})
|
|
2491
|
+
|
|
2492
|
+
await fetch(event.ResponseURL, {
|
|
2493
|
+
method: 'PUT',
|
|
2494
|
+
port: 443,
|
|
2495
|
+
body,
|
|
2496
|
+
headers: {
|
|
2497
|
+
'content-type': '',
|
|
2498
|
+
'content-length': Buffer.from(body).byteLength,
|
|
2499
|
+
},
|
|
2500
|
+
})
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
const deleteHostedZoneRecords = async (hostedZoneId, records) => {
|
|
2504
|
+
records = records.filter(record => ![ 'SOA', 'NS' ].includes(record.Type))
|
|
2505
|
+
if(records.length === 0) {
|
|
2506
|
+
return
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
const chunkSize = 100;
|
|
2510
|
+
for (let i = 0; i < records.length; i += chunkSize) {
|
|
2511
|
+
const chunk = records.slice(i, i + chunkSize);
|
|
2512
|
+
|
|
2513
|
+
await client.send(new ChangeResourceRecordSetsCommand({
|
|
2514
|
+
HostedZoneId: hostedZoneId,
|
|
2515
|
+
ChangeBatch: {
|
|
2516
|
+
Changes: chunk.map(record => ({
|
|
2517
|
+
Action: 'DELETE',
|
|
2518
|
+
ResourceRecordSet: record
|
|
2519
|
+
}))
|
|
2520
|
+
}
|
|
2521
|
+
}))
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
const listHostedZoneRecords = async (hostedZoneId) => {
|
|
2526
|
+
|
|
2527
|
+
const records = []
|
|
2528
|
+
let token
|
|
2529
|
+
|
|
2530
|
+
while(true) {
|
|
2531
|
+
const result = await client.send(new ListResourceRecordSetsCommand({
|
|
2532
|
+
HostedZoneId: hostedZoneId,
|
|
2533
|
+
NextRecordName: token
|
|
2534
|
+
}))
|
|
2535
|
+
|
|
2536
|
+
if(result.ResourceRecordSets && result.ResourceRecordSets.length) {
|
|
2537
|
+
records.push(...result.ResourceRecordSets)
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
if(result.NextRecordName) {
|
|
2541
|
+
token = result.NextRecordName
|
|
2542
|
+
} else {
|
|
2543
|
+
return records
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
`
|
|
2548
|
+
);
|
|
2549
|
+
|
|
2550
|
+
// src/formation/resource/cloud-formation/custom-resource.ts
|
|
2551
|
+
var CustomResource = class extends Resource {
|
|
2552
|
+
constructor(logicalId, props) {
|
|
2553
|
+
super("AWS::CloudFormation::CustomResource", logicalId);
|
|
2554
|
+
this.props = props;
|
|
2555
|
+
}
|
|
2556
|
+
getAtt(name) {
|
|
2557
|
+
return getAtt(this.logicalId, name);
|
|
2558
|
+
}
|
|
2559
|
+
properties() {
|
|
2560
|
+
return {
|
|
2561
|
+
ServiceToken: this.props.serviceToken,
|
|
2562
|
+
...this.props.properties
|
|
2563
|
+
};
|
|
2564
|
+
}
|
|
2565
|
+
};
|
|
2566
|
+
|
|
2567
|
+
// src/plugins/domain.ts
|
|
2568
|
+
var DomainNameSchema = z15.string().regex(/[a-z\-\_\.]/g, "Invalid domain name");
|
|
2569
|
+
var domainPlugin = definePlugin({
|
|
2570
|
+
name: "domain",
|
|
2571
|
+
schema: z15.object({
|
|
2572
|
+
/** Define the domains for your application.
|
|
2573
|
+
* @example
|
|
2574
|
+
* {
|
|
2575
|
+
* domains: {
|
|
2576
|
+
* 'example.com': [{
|
|
2577
|
+
* name: 'www',
|
|
2578
|
+
* type: 'TXT',
|
|
2579
|
+
* ttl: '60 seconds',
|
|
2580
|
+
* records: [ 'value' ]
|
|
2581
|
+
* }]
|
|
2582
|
+
* }
|
|
2583
|
+
* }
|
|
2584
|
+
*/
|
|
2585
|
+
domains: z15.record(DomainNameSchema, z15.object({
|
|
2586
|
+
/** Enter a fully qualified domain name, for example, www.example.com.
|
|
2587
|
+
* You can optionally include a trailing dot.
|
|
2588
|
+
* If you omit the trailing dot, Amazon Route 53 assumes that the domain name that you specify is fully qualified.
|
|
2589
|
+
* This means that Route 53 treats www.example.com (without a trailing dot) and www.example.com. (with a trailing dot) as identical.
|
|
2590
|
+
*/
|
|
2591
|
+
name: DomainNameSchema.optional(),
|
|
2592
|
+
/** The DNS record type. */
|
|
2593
|
+
type: z15.enum(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV", "TXT"]),
|
|
2594
|
+
/** The resource record cache time to live (TTL) */
|
|
2595
|
+
ttl: DurationSchema,
|
|
2596
|
+
/** One or more values that correspond with the value that you specified for the Type property. */
|
|
2597
|
+
records: z15.string().array()
|
|
2598
|
+
}).array()).optional()
|
|
2599
|
+
}),
|
|
2600
|
+
onApp({ config, bootstrap: bootstrap2, usEastBootstrap }) {
|
|
2601
|
+
const domains = Object.entries(config.domains || {});
|
|
2602
|
+
if (domains.length === 0) {
|
|
2603
|
+
return;
|
|
2604
|
+
}
|
|
2605
|
+
const lambda = new Function("delete-hosted-zone", {
|
|
2606
|
+
name: `${config.name}-delete-hosted-zone`,
|
|
2607
|
+
code: Code.fromInline(deleteHostedZoneRecordsHandlerCode, "index.handler")
|
|
2608
|
+
});
|
|
2609
|
+
lambda.addPermissions({
|
|
2610
|
+
actions: [
|
|
2611
|
+
"route53:ListResourceRecordSets",
|
|
2612
|
+
"route53:ChangeResourceRecordSets"
|
|
2613
|
+
],
|
|
2614
|
+
resources: ["*"]
|
|
2615
|
+
});
|
|
2616
|
+
usEastBootstrap.add(lambda);
|
|
2617
|
+
for (const [domain, records] of domains) {
|
|
2618
|
+
const hostedZone = new HostedZone(domain);
|
|
2619
|
+
const usEastCertificate = new Certificate(domain, {
|
|
2620
|
+
hostedZoneId: hostedZone.id,
|
|
2621
|
+
alternativeNames: [`*.${domain}`]
|
|
2622
|
+
});
|
|
2623
|
+
const custom = new CustomResource(domain, {
|
|
2624
|
+
serviceToken: lambda.arn,
|
|
2625
|
+
properties: {
|
|
2626
|
+
hostedZoneId: hostedZone.id
|
|
2627
|
+
}
|
|
2628
|
+
}).dependsOn(hostedZone);
|
|
2629
|
+
usEastBootstrap.add(custom).add(hostedZone).add(usEastCertificate).export(`certificate-${domain}-arn`, usEastCertificate.arn).export(`hosted-zone-${domain}-id`, hostedZone.id);
|
|
2630
|
+
const certificate = new Certificate(domain, {
|
|
2631
|
+
hostedZoneId: usEastBootstrap.import(`hosted-zone-${domain}-id`),
|
|
2632
|
+
alternativeNames: [`*.${domain}`]
|
|
2633
|
+
});
|
|
2634
|
+
bootstrap2.add(certificate).export(`certificate-${domain}-arn`, certificate.arn);
|
|
2635
|
+
if (records.length > 0) {
|
|
2636
|
+
const group = new RecordSetGroup(domain, {
|
|
2637
|
+
hostedZoneId: hostedZone.id,
|
|
2638
|
+
records
|
|
2639
|
+
}).dependsOn(hostedZone);
|
|
2640
|
+
usEastBootstrap.add(group);
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
});
|
|
2645
|
+
|
|
2646
|
+
// src/plugins/on-failure.ts
|
|
2647
|
+
import { z as z16 } from "zod";
|
|
2648
|
+
var hasOnFailure = (config) => {
|
|
2649
|
+
const onFailure = config.stacks.find((stack) => {
|
|
2650
|
+
return typeof stack.onFailure !== "undefined";
|
|
2651
|
+
});
|
|
2652
|
+
return !!onFailure;
|
|
2653
|
+
};
|
|
2654
|
+
var onFailurePlugin = definePlugin({
|
|
2655
|
+
name: "on-failure",
|
|
2656
|
+
schema: z16.object({
|
|
2657
|
+
stacks: z16.object({
|
|
2658
|
+
/** Defining a onFailure handler will add a global onFailure handler for the following resources:
|
|
2659
|
+
* - Async lambda functions
|
|
2660
|
+
* - SQS queues
|
|
2661
|
+
* - DynamoDB streams
|
|
2662
|
+
* @example
|
|
2663
|
+
* {
|
|
2664
|
+
* onFailure: 'on-failure.ts'
|
|
2665
|
+
* }
|
|
2666
|
+
*/
|
|
2667
|
+
onFailure: FunctionSchema.optional()
|
|
2668
|
+
}).array()
|
|
2669
|
+
}),
|
|
2670
|
+
onApp({ config, bootstrap: bootstrap2 }) {
|
|
2671
|
+
if (!hasOnFailure(config)) {
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
const queue2 = new Queue("on-failure", {
|
|
2675
|
+
name: `${config.name}-failure`
|
|
2676
|
+
});
|
|
2677
|
+
bootstrap2.add(queue2).export("on-failure-queue-arn", queue2.arn);
|
|
2678
|
+
},
|
|
2679
|
+
onStack(ctx) {
|
|
2680
|
+
const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
|
|
2681
|
+
const onFailure = stackConfig.onFailure;
|
|
2682
|
+
if (!onFailure) {
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2685
|
+
const queueArn = bootstrap2.import("on-failure-queue-arn");
|
|
2686
|
+
const lambda = toLambdaFunction(ctx, "on-failure", onFailure);
|
|
2687
|
+
const source = new SqsEventSource("on-failure", lambda, {
|
|
2688
|
+
queueArn
|
|
2689
|
+
});
|
|
2690
|
+
lambda.addPermissions({
|
|
2691
|
+
actions: [
|
|
2692
|
+
"sqs:SendMessage",
|
|
2693
|
+
"sqs:ReceiveMessage",
|
|
2694
|
+
"sqs:GetQueueUrl",
|
|
2695
|
+
"sqs:GetQueueAttributes"
|
|
2696
|
+
],
|
|
2697
|
+
resources: [queueArn]
|
|
2698
|
+
});
|
|
2699
|
+
stack.add(lambda, source);
|
|
2700
|
+
},
|
|
2701
|
+
onResource({ config, resource, bootstrap: bootstrap2 }) {
|
|
2702
|
+
if (!hasOnFailure(config)) {
|
|
2703
|
+
return;
|
|
2704
|
+
}
|
|
2705
|
+
const queueArn = bootstrap2.import("on-failure-queue-arn");
|
|
2706
|
+
if (resource instanceof Queue) {
|
|
2707
|
+
resource.setDeadLetter(queueArn);
|
|
2708
|
+
}
|
|
2709
|
+
if (resource instanceof EventInvokeConfig) {
|
|
2710
|
+
resource.setOnFailure(queueArn);
|
|
2711
|
+
}
|
|
2712
|
+
if (resource instanceof EventSourceMapping) {
|
|
2713
|
+
resource.setOnFailure(queueArn);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
});
|
|
2717
|
+
|
|
2718
|
+
// src/formation/resource/ec2/vpc.ts
|
|
2719
|
+
var Vpc = class extends Resource {
|
|
2720
|
+
constructor(logicalId, props) {
|
|
2721
|
+
super("AWS::EC2::VPC", logicalId);
|
|
2722
|
+
this.props = props;
|
|
2723
|
+
}
|
|
2724
|
+
get id() {
|
|
2725
|
+
return ref(this.logicalId);
|
|
2726
|
+
}
|
|
2727
|
+
properties() {
|
|
2728
|
+
return {
|
|
2729
|
+
CidrBlock: this.props.cidrBlock.ip
|
|
2730
|
+
};
|
|
2731
|
+
}
|
|
2732
|
+
};
|
|
2733
|
+
var RouteTable = class extends Resource {
|
|
2734
|
+
constructor(logicalId, props) {
|
|
2735
|
+
super("AWS::EC2::RouteTable", logicalId);
|
|
2736
|
+
this.props = props;
|
|
2737
|
+
this.name = formatName(props.name || logicalId);
|
|
2738
|
+
}
|
|
2739
|
+
name;
|
|
2740
|
+
get id() {
|
|
2741
|
+
return ref(this.logicalId);
|
|
2742
|
+
}
|
|
2743
|
+
properties() {
|
|
2744
|
+
return {
|
|
2745
|
+
VpcId: this.props.vpcId,
|
|
2746
|
+
Tags: [{
|
|
2747
|
+
Key: "name",
|
|
2748
|
+
Value: this.name
|
|
2749
|
+
}]
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
};
|
|
2753
|
+
var InternetGateway = class extends Resource {
|
|
2754
|
+
constructor(logicalId) {
|
|
2755
|
+
super("AWS::EC2::InternetGateway", logicalId);
|
|
2756
|
+
}
|
|
2757
|
+
get id() {
|
|
2758
|
+
return ref(this.logicalId);
|
|
2759
|
+
}
|
|
2760
|
+
properties() {
|
|
2761
|
+
return {};
|
|
2762
|
+
}
|
|
2763
|
+
};
|
|
2764
|
+
var VPCGatewayAttachment = class extends Resource {
|
|
2765
|
+
constructor(logicalId, props) {
|
|
2766
|
+
super("AWS::EC2::VPCGatewayAttachment", logicalId);
|
|
2767
|
+
this.props = props;
|
|
2768
|
+
}
|
|
2769
|
+
get id() {
|
|
2770
|
+
return ref(this.logicalId);
|
|
2771
|
+
}
|
|
2772
|
+
properties() {
|
|
2773
|
+
return {
|
|
2774
|
+
VpcId: this.props.vpcId,
|
|
2775
|
+
InternetGatewayId: this.props.internetGatewayId
|
|
2776
|
+
};
|
|
2777
|
+
}
|
|
2778
|
+
};
|
|
2779
|
+
var Route = class extends Resource {
|
|
2780
|
+
constructor(logicalId, props) {
|
|
2781
|
+
super("AWS::EC2::Route", logicalId);
|
|
2782
|
+
this.props = props;
|
|
2783
|
+
}
|
|
2784
|
+
get id() {
|
|
2785
|
+
return ref(this.logicalId);
|
|
2786
|
+
}
|
|
2787
|
+
properties() {
|
|
2788
|
+
return {
|
|
2789
|
+
GatewayId: this.props.gatewayId,
|
|
2790
|
+
RouteTableId: this.props.routeTableId,
|
|
2791
|
+
DestinationCidrBlock: this.props.destination.ip
|
|
2792
|
+
};
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2795
|
+
var Subnet = class extends Resource {
|
|
2796
|
+
constructor(logicalId, props) {
|
|
2797
|
+
super("AWS::EC2::Subnet", logicalId);
|
|
2798
|
+
this.props = props;
|
|
2799
|
+
}
|
|
2800
|
+
get id() {
|
|
2801
|
+
return ref(this.logicalId);
|
|
2802
|
+
}
|
|
2803
|
+
properties() {
|
|
2804
|
+
return {
|
|
2805
|
+
VpcId: this.props.vpcId,
|
|
2806
|
+
CidrBlock: this.props.cidrBlock.ip,
|
|
2807
|
+
AvailabilityZone: this.props.availabilityZone
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
};
|
|
2811
|
+
var SubnetRouteTableAssociation = class extends Resource {
|
|
2812
|
+
constructor(logicalId, props) {
|
|
2813
|
+
super("AWS::EC2::SubnetRouteTableAssociation", logicalId);
|
|
2814
|
+
this.props = props;
|
|
2815
|
+
}
|
|
2816
|
+
get id() {
|
|
2817
|
+
return ref(this.logicalId);
|
|
2818
|
+
}
|
|
2819
|
+
properties() {
|
|
2820
|
+
return {
|
|
2821
|
+
SubnetId: this.props.subnetId,
|
|
2822
|
+
RouteTableId: this.props.routeTableId
|
|
2823
|
+
};
|
|
2824
|
+
}
|
|
2825
|
+
};
|
|
2826
|
+
|
|
2827
|
+
// src/formation/resource/ec2/peer.ts
|
|
2828
|
+
var Peer = class {
|
|
2829
|
+
constructor(ip, type) {
|
|
2830
|
+
this.ip = ip;
|
|
2831
|
+
this.type = type;
|
|
2832
|
+
}
|
|
2833
|
+
static ipv4(cidrIp) {
|
|
2834
|
+
const cidrMatch = cidrIp.match(/^(\d{1,3}\.){3}\d{1,3}(\/\d+)?$/);
|
|
2835
|
+
if (!cidrMatch) {
|
|
2836
|
+
throw new Error(`Invalid IPv4 CIDR: "${cidrIp}"`);
|
|
2837
|
+
}
|
|
2838
|
+
if (!cidrMatch[2]) {
|
|
2839
|
+
throw new Error(`CIDR mask is missing in IPv4: "${cidrIp}". Did you mean "${cidrIp}/32"?`);
|
|
2840
|
+
}
|
|
2841
|
+
return new Peer(cidrIp, "v4");
|
|
2842
|
+
}
|
|
2843
|
+
static anyIpv4() {
|
|
2844
|
+
return new Peer("0.0.0.0/0", "v4");
|
|
2845
|
+
}
|
|
2846
|
+
static ipv6(cidrIpv6) {
|
|
2847
|
+
const cidrMatch = cidrIpv6.match(/^([\da-f]{0,4}:){2,7}([\da-f]{0,4})?(\/\d+)?$/);
|
|
2848
|
+
if (!cidrMatch) {
|
|
2849
|
+
throw new Error(`Invalid IPv6 CIDR: "${cidrIpv6}"`);
|
|
2850
|
+
}
|
|
2851
|
+
if (!cidrMatch[3]) {
|
|
2852
|
+
throw new Error(`CIDR mask is missing in IPv6: "${cidrIpv6}". Did you mean "${cidrIpv6}/128"?`);
|
|
2853
|
+
}
|
|
2854
|
+
return new Peer(cidrIpv6, "v6");
|
|
2855
|
+
}
|
|
2856
|
+
static anyIpv6() {
|
|
2857
|
+
return new Peer("::/0", "v6");
|
|
2858
|
+
}
|
|
2859
|
+
toRuleJson() {
|
|
2860
|
+
switch (this.type) {
|
|
2861
|
+
case "v4":
|
|
2862
|
+
return { CidrIp: this.ip };
|
|
2863
|
+
case "v6":
|
|
2864
|
+
return { CidrIpv6: this.ip };
|
|
2865
|
+
}
|
|
2866
|
+
}
|
|
2867
|
+
toString() {
|
|
2868
|
+
return this.ip;
|
|
2869
|
+
}
|
|
2870
|
+
};
|
|
2871
|
+
|
|
2872
|
+
// src/plugins/vpc.ts
|
|
2873
|
+
var vpcPlugin = definePlugin({
|
|
2874
|
+
name: "vpc",
|
|
2875
|
+
// schema: z.object({
|
|
2876
|
+
// defaults: z.object({
|
|
2877
|
+
// vpc: z.boolean().default(false),
|
|
2878
|
+
// }).default({}),
|
|
2879
|
+
// }),
|
|
2880
|
+
onApp({ config, bootstrap: bootstrap2 }) {
|
|
2881
|
+
const vpc = new Vpc("main", {
|
|
2882
|
+
cidrBlock: Peer.ipv4("10.0.0.0/16")
|
|
2883
|
+
});
|
|
2884
|
+
const privateRouteTable = new RouteTable("private", {
|
|
2885
|
+
vpcId: vpc.id,
|
|
2886
|
+
name: "private"
|
|
2887
|
+
}).dependsOn(vpc);
|
|
2888
|
+
const publicRouteTable = new RouteTable("public", {
|
|
2889
|
+
vpcId: vpc.id,
|
|
2890
|
+
name: "public"
|
|
2891
|
+
}).dependsOn(vpc);
|
|
2892
|
+
const gateway = new InternetGateway("");
|
|
2893
|
+
const attachment = new VPCGatewayAttachment("", {
|
|
2894
|
+
vpcId: vpc.id,
|
|
2895
|
+
internetGatewayId: gateway.id
|
|
2896
|
+
}).dependsOn(vpc, gateway);
|
|
2897
|
+
const route = new Route("", {
|
|
2898
|
+
gatewayId: gateway.id,
|
|
2899
|
+
routeTableId: publicRouteTable.id,
|
|
2900
|
+
destination: Peer.anyIpv4()
|
|
2901
|
+
}).dependsOn(gateway, publicRouteTable);
|
|
2902
|
+
bootstrap2.export(`vpc-id`, vpc.id);
|
|
2903
|
+
bootstrap2.add(
|
|
2904
|
+
vpc,
|
|
2905
|
+
privateRouteTable,
|
|
2906
|
+
publicRouteTable,
|
|
2907
|
+
gateway,
|
|
2908
|
+
attachment,
|
|
2909
|
+
route
|
|
2910
|
+
);
|
|
2911
|
+
const zones = ["a", "b"];
|
|
2912
|
+
const tables = [privateRouteTable, publicRouteTable];
|
|
2913
|
+
let block = 0;
|
|
2914
|
+
for (const table of tables) {
|
|
2915
|
+
for (const i in zones) {
|
|
2916
|
+
const id = `${table.name}-${i}`;
|
|
2917
|
+
const subnet = new Subnet(id, {
|
|
2918
|
+
vpcId: vpc.id,
|
|
2919
|
+
cidrBlock: Peer.ipv4(`10.0.${block++}.0/24`),
|
|
2920
|
+
availabilityZone: config.region + zones[i]
|
|
2921
|
+
}).dependsOn(vpc);
|
|
2922
|
+
const association = new SubnetRouteTableAssociation(id, {
|
|
2923
|
+
routeTableId: table.id,
|
|
2924
|
+
subnetId: subnet.id
|
|
2925
|
+
}).dependsOn(subnet, table);
|
|
2926
|
+
bootstrap2.export(`${table.name}-subnet-${Number(i) + 1}`, subnet.id);
|
|
2927
|
+
bootstrap2.add(
|
|
2928
|
+
subnet,
|
|
2929
|
+
association
|
|
2930
|
+
);
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
});
|
|
2935
|
+
|
|
2936
|
+
// src/plugins/http.ts
|
|
2937
|
+
import { z as z17 } from "zod";
|
|
2938
|
+
|
|
2939
|
+
// src/formation/resource/ec2/security-group.ts
|
|
2940
|
+
var SecurityGroup = class extends Resource {
|
|
2941
|
+
constructor(logicalId, props) {
|
|
2942
|
+
super("AWS::EC2::SecurityGroup", logicalId);
|
|
2943
|
+
this.props = props;
|
|
2944
|
+
}
|
|
2945
|
+
ingress = [];
|
|
2946
|
+
egress = [];
|
|
2947
|
+
get id() {
|
|
2948
|
+
return ref(this.logicalId);
|
|
2949
|
+
}
|
|
2950
|
+
addIngressRule(peer, port, description) {
|
|
2951
|
+
this.ingress.push({
|
|
2952
|
+
peer,
|
|
2953
|
+
port,
|
|
2954
|
+
description
|
|
2955
|
+
});
|
|
2956
|
+
return this;
|
|
2957
|
+
}
|
|
2958
|
+
addEgressRule(peer, port, description) {
|
|
2959
|
+
this.egress.push({
|
|
2960
|
+
peer,
|
|
2961
|
+
port,
|
|
2962
|
+
description
|
|
2963
|
+
});
|
|
2964
|
+
return this;
|
|
2965
|
+
}
|
|
2966
|
+
properties() {
|
|
2967
|
+
return {
|
|
2968
|
+
VpcId: this.props.vpcId,
|
|
2969
|
+
GroupName: this.logicalId,
|
|
2970
|
+
GroupDescription: this.props.description,
|
|
2971
|
+
SecurityGroupIngress: this.ingress.map((rule) => ({
|
|
2972
|
+
Description: rule.description || "",
|
|
2973
|
+
...rule.port.toRuleJson(),
|
|
2974
|
+
...rule.peer.toRuleJson()
|
|
2975
|
+
})),
|
|
2976
|
+
SecurityGroupEgress: this.egress.map((rule) => ({
|
|
2977
|
+
Description: rule.description || "",
|
|
2978
|
+
...rule.port.toRuleJson(),
|
|
2979
|
+
...rule.peer.toRuleJson()
|
|
2980
|
+
}))
|
|
2981
|
+
};
|
|
2982
|
+
}
|
|
2983
|
+
};
|
|
2984
|
+
|
|
2985
|
+
// src/formation/resource/ec2/port.ts
|
|
2986
|
+
var Port = class {
|
|
2987
|
+
static tcp(port) {
|
|
2988
|
+
return new Port({
|
|
2989
|
+
protocol: "tcp" /* TCP */,
|
|
2990
|
+
from: port,
|
|
2991
|
+
to: port
|
|
2992
|
+
});
|
|
2993
|
+
}
|
|
2994
|
+
static tcpRange(startPort, endPort) {
|
|
2995
|
+
return new Port({
|
|
2996
|
+
protocol: "tcp" /* TCP */,
|
|
2997
|
+
from: startPort,
|
|
2998
|
+
to: endPort
|
|
2999
|
+
});
|
|
3000
|
+
}
|
|
3001
|
+
static allTcp() {
|
|
3002
|
+
return new Port({
|
|
3003
|
+
protocol: "tcp" /* TCP */,
|
|
3004
|
+
from: 0,
|
|
3005
|
+
to: 65535
|
|
3006
|
+
});
|
|
3007
|
+
}
|
|
3008
|
+
static allTraffic() {
|
|
3009
|
+
return new Port({
|
|
3010
|
+
protocol: "-1" /* ALL */
|
|
3011
|
+
});
|
|
3012
|
+
}
|
|
3013
|
+
protocol;
|
|
3014
|
+
from;
|
|
3015
|
+
to;
|
|
3016
|
+
constructor(props) {
|
|
3017
|
+
this.protocol = props.protocol;
|
|
3018
|
+
this.from = props.from;
|
|
3019
|
+
this.to = props.to;
|
|
3020
|
+
}
|
|
3021
|
+
toRuleJson() {
|
|
3022
|
+
return {
|
|
3023
|
+
IpProtocol: this.protocol,
|
|
3024
|
+
FromPort: this.from,
|
|
3025
|
+
ToPort: this.to
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
3028
|
+
};
|
|
3029
|
+
|
|
3030
|
+
// src/formation/resource/elb/load-balancer.ts
|
|
3031
|
+
var LoadBalancer = class extends Resource {
|
|
3032
|
+
constructor(logicalId, props) {
|
|
3033
|
+
super("AWS::ElasticLoadBalancingV2::LoadBalancer", logicalId);
|
|
3034
|
+
this.props = props;
|
|
3035
|
+
this.name = this.props.name || logicalId;
|
|
3036
|
+
}
|
|
3037
|
+
name;
|
|
3038
|
+
get arn() {
|
|
3039
|
+
return ref(this.logicalId);
|
|
3040
|
+
}
|
|
3041
|
+
get dnsName() {
|
|
3042
|
+
return getAtt(this.logicalId, "DNSName");
|
|
3043
|
+
}
|
|
3044
|
+
get hostedZoneId() {
|
|
3045
|
+
return getAtt(this.logicalId, "CanonicalHostedZoneID");
|
|
3046
|
+
}
|
|
3047
|
+
properties() {
|
|
3048
|
+
return {
|
|
3049
|
+
Name: this.name,
|
|
3050
|
+
Type: this.props.type,
|
|
3051
|
+
Scheme: this.props.schema || "internet-facing",
|
|
3052
|
+
SecurityGroups: this.props.securityGroups,
|
|
3053
|
+
Subnets: this.props.subnets
|
|
3054
|
+
};
|
|
3055
|
+
}
|
|
3056
|
+
};
|
|
3057
|
+
|
|
3058
|
+
// src/formation/resource/elb/listener.ts
|
|
3059
|
+
import { constantCase as constantCase4 } from "change-case";
|
|
3060
|
+
var Listener = class extends Resource {
|
|
3061
|
+
constructor(logicalId, props) {
|
|
3062
|
+
super("AWS::ElasticLoadBalancingV2::Listener", logicalId);
|
|
3063
|
+
this.props = props;
|
|
3064
|
+
}
|
|
3065
|
+
get id() {
|
|
3066
|
+
return ref(this.logicalId);
|
|
3067
|
+
}
|
|
3068
|
+
get arn() {
|
|
3069
|
+
return getAtt(this.logicalId, "ListenerArn");
|
|
3070
|
+
}
|
|
3071
|
+
properties() {
|
|
3072
|
+
return {
|
|
3073
|
+
LoadBalancerArn: this.props.loadBalancerArn,
|
|
3074
|
+
Port: this.props.port,
|
|
3075
|
+
Protocol: constantCase4(this.props.protocol),
|
|
3076
|
+
Certificates: this.props.certificates.map((arn) => ({
|
|
3077
|
+
CertificateArn: arn
|
|
3078
|
+
})),
|
|
3079
|
+
...this.attr("DefaultActions", this.props.defaultActions?.map((action) => action.toJSON()))
|
|
3080
|
+
};
|
|
3081
|
+
}
|
|
3082
|
+
};
|
|
3083
|
+
var ListenerAction = class {
|
|
3084
|
+
constructor(props) {
|
|
3085
|
+
this.props = props;
|
|
3086
|
+
}
|
|
3087
|
+
static fixedResponse(statusCode, props = {}) {
|
|
3088
|
+
return new ListenerAction({
|
|
3089
|
+
type: "fixed-response",
|
|
3090
|
+
fixedResponse: {
|
|
3091
|
+
statusCode,
|
|
3092
|
+
...props
|
|
3093
|
+
}
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
static forward(targets) {
|
|
3097
|
+
return new ListenerAction({
|
|
3098
|
+
type: "forward",
|
|
3099
|
+
forward: {
|
|
3100
|
+
targetGroups: targets
|
|
3101
|
+
}
|
|
3102
|
+
});
|
|
3103
|
+
}
|
|
3104
|
+
toJSON() {
|
|
3105
|
+
return {
|
|
3106
|
+
// AuthenticateCognitoConfig: AuthenticateCognitoConfig,
|
|
3107
|
+
// AuthenticateOidcConfig: AuthenticateOidcConfig,
|
|
3108
|
+
// RedirectConfig: RedirectConfig,
|
|
3109
|
+
Type: this.props.type,
|
|
3110
|
+
// Order: Integer,
|
|
3111
|
+
...this.props.type === "fixed-response" ? {
|
|
3112
|
+
FixedResponseConfig: {
|
|
3113
|
+
StatusCode: this.props.fixedResponse.statusCode,
|
|
3114
|
+
...this.props.fixedResponse.contentType ? {
|
|
3115
|
+
ContentType: this.props.fixedResponse.contentType
|
|
3116
|
+
} : {},
|
|
3117
|
+
...this.props.fixedResponse.messageBody ? {
|
|
3118
|
+
MessageBody: this.props.fixedResponse.messageBody
|
|
3119
|
+
} : {}
|
|
3120
|
+
}
|
|
3121
|
+
} : {},
|
|
3122
|
+
...this.props.type === "forward" ? {
|
|
3123
|
+
ForwardConfig: {
|
|
3124
|
+
TargetGroups: this.props.forward.targetGroups.map((target) => ({
|
|
3125
|
+
TargetGroupArn: target
|
|
3126
|
+
}))
|
|
3127
|
+
}
|
|
3128
|
+
} : {}
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3131
|
+
};
|
|
3132
|
+
|
|
3133
|
+
// src/formation/resource/elb/listener-rule.ts
|
|
3134
|
+
var ListenerRule = class extends Resource {
|
|
3135
|
+
constructor(logicalId, props) {
|
|
3136
|
+
super("AWS::ElasticLoadBalancingV2::ListenerRule", logicalId);
|
|
3137
|
+
this.props = props;
|
|
3138
|
+
}
|
|
3139
|
+
get id() {
|
|
3140
|
+
return ref(this.logicalId);
|
|
3141
|
+
}
|
|
3142
|
+
get arn() {
|
|
3143
|
+
return getAtt(this.logicalId, "ListenerArn");
|
|
3144
|
+
}
|
|
3145
|
+
properties() {
|
|
3146
|
+
return {
|
|
3147
|
+
ListenerArn: this.props.listenerArn,
|
|
3148
|
+
Priority: this.props.priority,
|
|
3149
|
+
Conditions: this.props.conditions.map((condition) => condition.toJSON()),
|
|
3150
|
+
Actions: this.props.actions.map((action) => action.toJSON())
|
|
3151
|
+
};
|
|
3152
|
+
}
|
|
3153
|
+
};
|
|
3154
|
+
var ListenerCondition = class {
|
|
3155
|
+
constructor(props) {
|
|
3156
|
+
this.props = props;
|
|
3157
|
+
}
|
|
3158
|
+
static httpRequestMethods(methods) {
|
|
3159
|
+
return new ListenerCondition({
|
|
3160
|
+
field: "http-request-method",
|
|
3161
|
+
methods
|
|
3162
|
+
});
|
|
3163
|
+
}
|
|
3164
|
+
static pathPatterns(paths) {
|
|
3165
|
+
return new ListenerCondition({
|
|
3166
|
+
field: "path-pattern",
|
|
3167
|
+
paths
|
|
3168
|
+
});
|
|
3169
|
+
}
|
|
3170
|
+
toJSON() {
|
|
3171
|
+
return {
|
|
3172
|
+
Field: this.props.field,
|
|
3173
|
+
...this.props.field === "http-request-method" ? {
|
|
3174
|
+
HttpRequestMethodConfig: {
|
|
3175
|
+
Values: this.props.methods
|
|
3176
|
+
}
|
|
3177
|
+
} : {},
|
|
3178
|
+
...this.props.field === "path-pattern" ? {
|
|
3179
|
+
PathPatternConfig: {
|
|
3180
|
+
Values: this.props.paths
|
|
3181
|
+
}
|
|
3182
|
+
} : {}
|
|
3183
|
+
};
|
|
3184
|
+
}
|
|
3185
|
+
};
|
|
3186
|
+
|
|
3187
|
+
// src/formation/resource/elb/target-group.ts
|
|
3188
|
+
var TargetGroup = class extends Resource {
|
|
3189
|
+
constructor(logicalId, props) {
|
|
3190
|
+
super("AWS::ElasticLoadBalancingV2::TargetGroup", logicalId);
|
|
3191
|
+
this.props = props;
|
|
3192
|
+
this.name = formatName(this.props.name || logicalId);
|
|
3193
|
+
}
|
|
3194
|
+
name;
|
|
3195
|
+
get arn() {
|
|
3196
|
+
return ref(this.logicalId);
|
|
3197
|
+
}
|
|
3198
|
+
get fullName() {
|
|
3199
|
+
return getAtt(this.logicalId, "TargetGroupFullName");
|
|
3200
|
+
}
|
|
3201
|
+
properties() {
|
|
3202
|
+
return {
|
|
3203
|
+
Name: this.name,
|
|
3204
|
+
TargetType: this.props.type,
|
|
3205
|
+
Targets: this.props.targets.map((target) => ({
|
|
3206
|
+
Id: target
|
|
3207
|
+
}))
|
|
3208
|
+
};
|
|
3209
|
+
}
|
|
3210
|
+
};
|
|
3211
|
+
|
|
3212
|
+
// src/formation/resource/lambda/event-source/elb.ts
|
|
3213
|
+
var ElbEventSource = class extends Group {
|
|
3214
|
+
constructor(id, lambda, props) {
|
|
3215
|
+
const name = formatName(id);
|
|
3216
|
+
const permission = new Permission2(id, {
|
|
3217
|
+
action: "lambda:InvokeFunction",
|
|
3218
|
+
principal: "elasticloadbalancing.amazonaws.com",
|
|
3219
|
+
functionArn: lambda.arn,
|
|
3220
|
+
sourceArn: sub("arn:${AWS::Partition}:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:targetgroup/${name}/*", {
|
|
3221
|
+
name
|
|
3222
|
+
})
|
|
3223
|
+
}).dependsOn(lambda);
|
|
3224
|
+
const target = new TargetGroup(id, {
|
|
3225
|
+
name,
|
|
3226
|
+
type: "lambda",
|
|
3227
|
+
targets: [lambda.arn]
|
|
3228
|
+
}).dependsOn(lambda, permission);
|
|
3229
|
+
const rule = new ListenerRule(id, {
|
|
3230
|
+
listenerArn: props.listenerArn,
|
|
3231
|
+
priority: props.priority,
|
|
3232
|
+
conditions: props.conditions,
|
|
3233
|
+
actions: [
|
|
3234
|
+
ListenerAction.forward([target.arn])
|
|
3235
|
+
]
|
|
3236
|
+
}).dependsOn(target);
|
|
3237
|
+
super([target, rule, permission]);
|
|
3238
|
+
}
|
|
3239
|
+
};
|
|
3240
|
+
|
|
3241
|
+
// src/plugins/http.ts
|
|
3242
|
+
var RouteSchema = z17.custom((route) => {
|
|
3243
|
+
return z17.string().regex(/^(POST|GET|PUT|DELETE|HEAD|OPTIONS)(\s\/[a-z0-9\+\_\-\/]*)$/ig).safeParse(route).success;
|
|
3244
|
+
}, "Invalid route");
|
|
3245
|
+
var parseRoute = (route) => {
|
|
3246
|
+
const [method, ...paths] = route.split(" ");
|
|
3247
|
+
const path = paths.join(" ");
|
|
3248
|
+
return { method, path };
|
|
3249
|
+
};
|
|
3250
|
+
var strToInt = (str) => {
|
|
3251
|
+
return parseInt(Buffer.from(str, "utf8").toString("hex"), 16);
|
|
3252
|
+
};
|
|
3253
|
+
var generatePriority = (stackName, route) => {
|
|
3254
|
+
const start = strToInt(stackName) % 500 + 1;
|
|
3255
|
+
const end = strToInt(route) % 100;
|
|
3256
|
+
const priority = start + "" + end;
|
|
3257
|
+
return parseInt(priority, 10);
|
|
3258
|
+
};
|
|
3259
|
+
var httpPlugin = definePlugin({
|
|
3260
|
+
name: "http",
|
|
3261
|
+
schema: z17.object({
|
|
3262
|
+
defaults: z17.object({
|
|
3263
|
+
/** Define your global http api's.
|
|
3264
|
+
* @example
|
|
3265
|
+
* {
|
|
3266
|
+
* http: {
|
|
3267
|
+
* HTTP_API_NAME: {
|
|
3268
|
+
* domain: 'example.com',
|
|
3269
|
+
* subDomain: 'api',
|
|
3270
|
+
* }
|
|
3271
|
+
* }
|
|
3272
|
+
* }
|
|
3273
|
+
*/
|
|
3274
|
+
http: z17.record(
|
|
3275
|
+
ResourceIdSchema,
|
|
3276
|
+
z17.object({
|
|
3277
|
+
/** The domain to link your api with. */
|
|
3278
|
+
domain: z17.string(),
|
|
3279
|
+
subDomain: z17.string().optional()
|
|
3280
|
+
})
|
|
3281
|
+
).optional()
|
|
3282
|
+
}).default({}),
|
|
3283
|
+
stacks: z17.object({
|
|
3284
|
+
/** Define routes in your stack for your global http api.
|
|
3285
|
+
* @example
|
|
3286
|
+
* {
|
|
3287
|
+
* http: {
|
|
3288
|
+
* HTTP_API_NAME: {
|
|
3289
|
+
* 'GET /': 'index.ts',
|
|
3290
|
+
* 'POST /posts': 'create-post.ts',
|
|
3291
|
+
* }
|
|
3292
|
+
* }
|
|
3293
|
+
* }
|
|
3294
|
+
*/
|
|
3295
|
+
http: z17.record(
|
|
3296
|
+
ResourceIdSchema,
|
|
3297
|
+
z17.record(RouteSchema, FunctionSchema)
|
|
3298
|
+
).optional()
|
|
3299
|
+
}).array()
|
|
3300
|
+
}),
|
|
3301
|
+
onApp({ config, bootstrap: bootstrap2, usEastBootstrap }) {
|
|
3302
|
+
if (Object.keys(config.defaults?.http || {}).length === 0) {
|
|
3303
|
+
return;
|
|
3304
|
+
}
|
|
3305
|
+
const vpcId = bootstrap2.get("vpc-id");
|
|
3306
|
+
const securityGroup = new SecurityGroup("http", {
|
|
3307
|
+
description: "http security group",
|
|
3308
|
+
vpcId
|
|
3309
|
+
});
|
|
3310
|
+
securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(443));
|
|
3311
|
+
securityGroup.addIngressRule(Peer.anyIpv6(), Port.tcp(443));
|
|
3312
|
+
bootstrap2.add(securityGroup);
|
|
3313
|
+
for (const [id, props] of Object.entries(config.defaults?.http || {})) {
|
|
3314
|
+
const loadBalancer = new LoadBalancer(id, {
|
|
3315
|
+
name: `${config.name}-${id}`,
|
|
3316
|
+
type: "application",
|
|
3317
|
+
securityGroups: [securityGroup.id],
|
|
3318
|
+
subnets: [
|
|
3319
|
+
bootstrap2.get("public-subnet-1"),
|
|
3320
|
+
bootstrap2.get("public-subnet-2")
|
|
3321
|
+
]
|
|
3322
|
+
}).dependsOn(securityGroup);
|
|
3323
|
+
const listener = new Listener(id, {
|
|
3324
|
+
loadBalancerArn: loadBalancer.arn,
|
|
3325
|
+
port: 443,
|
|
3326
|
+
protocol: "https",
|
|
3327
|
+
certificates: [
|
|
3328
|
+
bootstrap2.get(`certificate-${props.domain}-arn`)
|
|
3329
|
+
],
|
|
3330
|
+
defaultActions: [
|
|
3331
|
+
ListenerAction.fixedResponse(404, {
|
|
3332
|
+
contentType: "application/json",
|
|
3333
|
+
messageBody: JSON.stringify({
|
|
3334
|
+
message: "Route not found"
|
|
3335
|
+
})
|
|
3336
|
+
})
|
|
3337
|
+
]
|
|
3338
|
+
}).dependsOn(loadBalancer);
|
|
3339
|
+
const record = new RecordSet(`${id}-http`, {
|
|
3340
|
+
hostedZoneId: usEastBootstrap.import(`hosted-zone-${props.domain}-id`),
|
|
3341
|
+
name: props.subDomain ? `${props.subDomain}.${props.domain}` : props.domain,
|
|
3342
|
+
type: "A",
|
|
3343
|
+
alias: {
|
|
3344
|
+
hostedZoneId: loadBalancer.hostedZoneId,
|
|
3345
|
+
dnsName: loadBalancer.dnsName
|
|
3346
|
+
}
|
|
3347
|
+
}).dependsOn(loadBalancer);
|
|
3348
|
+
bootstrap2.add(loadBalancer, listener, record).export(`http-${id}-listener-arn`, listener.arn);
|
|
3349
|
+
}
|
|
3350
|
+
},
|
|
3351
|
+
onStack(ctx) {
|
|
3352
|
+
const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
|
|
3353
|
+
for (const [id, routes] of Object.entries(stackConfig.http || {})) {
|
|
3354
|
+
for (const [route, props] of Object.entries(routes)) {
|
|
3355
|
+
const { method, path } = parseRoute(route);
|
|
3356
|
+
const lambda = toLambdaFunction(ctx, `http-${id}`, props);
|
|
3357
|
+
const source = new ElbEventSource(`http-${id}-${route}`, lambda, {
|
|
3358
|
+
listenerArn: bootstrap2.import(`http-${id}-listener-arn`),
|
|
3359
|
+
priority: generatePriority(stackConfig.name, route),
|
|
3360
|
+
conditions: [
|
|
3361
|
+
ListenerCondition.httpRequestMethods([method]),
|
|
3362
|
+
ListenerCondition.pathPatterns([path])
|
|
3363
|
+
]
|
|
3364
|
+
});
|
|
3365
|
+
stack.add(lambda, source);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
});
|
|
3370
|
+
|
|
3371
|
+
// src/plugins/search.ts
|
|
3372
|
+
import { z as z18 } from "zod";
|
|
3373
|
+
|
|
3374
|
+
// src/formation/resource/open-search-serverless/collection.ts
|
|
3375
|
+
var Collection = class extends Resource {
|
|
3376
|
+
constructor(logicalId, props) {
|
|
3377
|
+
super("AWS::OpenSearchServerless::Collection", logicalId);
|
|
3378
|
+
this.props = props;
|
|
3379
|
+
this.name = this.props.name || logicalId;
|
|
3380
|
+
}
|
|
3381
|
+
name;
|
|
3382
|
+
get id() {
|
|
3383
|
+
return ref(this.logicalId);
|
|
3384
|
+
}
|
|
3385
|
+
get arn() {
|
|
3386
|
+
return getAtt(this.logicalId, "Arn");
|
|
3387
|
+
}
|
|
3388
|
+
get endpoint() {
|
|
3389
|
+
return getAtt(this.logicalId, "CollectionEndpoint");
|
|
3390
|
+
}
|
|
3391
|
+
properties() {
|
|
3392
|
+
return {
|
|
3393
|
+
Name: this.name,
|
|
3394
|
+
Type: this.props.type.toUpperCase(),
|
|
3395
|
+
...this.attr("Description", this.props.description)
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
3398
|
+
};
|
|
3399
|
+
|
|
3400
|
+
// src/plugins/search.ts
|
|
3401
|
+
var searchPlugin = definePlugin({
|
|
3402
|
+
name: "search",
|
|
3403
|
+
schema: z18.object({
|
|
3404
|
+
stacks: z18.object({
|
|
3405
|
+
searchs: z18.array(ResourceIdSchema).optional()
|
|
3406
|
+
}).array()
|
|
3407
|
+
}),
|
|
3408
|
+
onStack({ config, stack, stackConfig, bind }) {
|
|
3409
|
+
for (const id of stackConfig.searchs || []) {
|
|
3410
|
+
const collection = new Collection(id, {
|
|
3411
|
+
name: `${config.name}-${stack.name}-${id}`,
|
|
3412
|
+
type: "search"
|
|
3413
|
+
});
|
|
3414
|
+
bind((lambda) => {
|
|
3415
|
+
lambda.addPermissions({
|
|
3416
|
+
actions: ["aoss:APIAccessAll"],
|
|
3417
|
+
resources: [collection.arn]
|
|
3418
|
+
});
|
|
3419
|
+
});
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
});
|
|
3423
|
+
|
|
3424
|
+
// src/plugins/index.ts
|
|
3425
|
+
var defaultPlugins = [
|
|
3426
|
+
extendPlugin,
|
|
3427
|
+
vpcPlugin,
|
|
3428
|
+
functionPlugin,
|
|
2292
3429
|
cronPlugin,
|
|
2293
3430
|
queuePlugin,
|
|
2294
3431
|
tablePlugin,
|
|
2295
3432
|
storePlugin,
|
|
2296
3433
|
topicPlugin,
|
|
2297
3434
|
pubsubPlugin,
|
|
2298
|
-
|
|
3435
|
+
searchPlugin,
|
|
2299
3436
|
domainPlugin,
|
|
2300
|
-
graphqlPlugin
|
|
2301
|
-
|
|
3437
|
+
graphqlPlugin,
|
|
3438
|
+
httpPlugin,
|
|
3439
|
+
onFailurePlugin
|
|
2302
3440
|
];
|
|
2303
3441
|
|
|
2304
3442
|
// src/formation/app.ts
|
|
@@ -2325,23 +3463,6 @@ var App = class {
|
|
|
2325
3463
|
// }
|
|
2326
3464
|
};
|
|
2327
3465
|
|
|
2328
|
-
// src/formation/resource/cloud-formation/custom-resource.ts
|
|
2329
|
-
var CustomResource = class extends Resource {
|
|
2330
|
-
constructor(logicalId, props) {
|
|
2331
|
-
super("AWS::CloudFormation::CustomResource", logicalId);
|
|
2332
|
-
this.props = props;
|
|
2333
|
-
}
|
|
2334
|
-
getAtt(name) {
|
|
2335
|
-
return getAtt(this.logicalId, name);
|
|
2336
|
-
}
|
|
2337
|
-
properties() {
|
|
2338
|
-
return {
|
|
2339
|
-
ServiceToken: this.props.serviceToken,
|
|
2340
|
-
...this.props.properties
|
|
2341
|
-
};
|
|
2342
|
-
}
|
|
2343
|
-
};
|
|
2344
|
-
|
|
2345
3466
|
// src/custom/global-export/handler.ts
|
|
2346
3467
|
var globalExportsHandlerCode = (
|
|
2347
3468
|
/* JS */
|
|
@@ -2436,7 +3557,7 @@ var extendWithGlobalExports = (appName, importable, exportable) => {
|
|
|
2436
3557
|
region: importable.region
|
|
2437
3558
|
}
|
|
2438
3559
|
});
|
|
2439
|
-
exportable.add(crossRegionExports);
|
|
3560
|
+
exportable.add(lambda, crossRegionExports);
|
|
2440
3561
|
}
|
|
2441
3562
|
return crossRegionExports.getAtt(name);
|
|
2442
3563
|
};
|
|
@@ -2497,6 +3618,20 @@ var toApp = async (config, filters) => {
|
|
|
2497
3618
|
app.add(stack);
|
|
2498
3619
|
stacks.push({ stack, config: stackConfig });
|
|
2499
3620
|
}
|
|
3621
|
+
for (const plugin of plugins) {
|
|
3622
|
+
for (const stack of app.stacks) {
|
|
3623
|
+
for (const resource of stack) {
|
|
3624
|
+
plugin.onResource?.({
|
|
3625
|
+
config,
|
|
3626
|
+
app,
|
|
3627
|
+
stack,
|
|
3628
|
+
bootstrap: bootstrap2,
|
|
3629
|
+
usEastBootstrap,
|
|
3630
|
+
resource
|
|
3631
|
+
});
|
|
3632
|
+
}
|
|
3633
|
+
}
|
|
3634
|
+
}
|
|
2500
3635
|
const functions = app.find(Function);
|
|
2501
3636
|
for (const bind2 of bindings) {
|
|
2502
3637
|
for (const fn of functions) {
|
|
@@ -2543,17 +3678,17 @@ var getCredentials = (profile) => {
|
|
|
2543
3678
|
};
|
|
2544
3679
|
|
|
2545
3680
|
// src/schema/app.ts
|
|
2546
|
-
import { z as
|
|
3681
|
+
import { z as z22 } from "zod";
|
|
2547
3682
|
|
|
2548
3683
|
// src/schema/stack.ts
|
|
2549
|
-
import { z as
|
|
2550
|
-
var StackSchema =
|
|
3684
|
+
import { z as z19 } from "zod";
|
|
3685
|
+
var StackSchema = z19.object({
|
|
2551
3686
|
name: ResourceIdSchema,
|
|
2552
|
-
depends:
|
|
3687
|
+
depends: z19.array(z19.lazy(() => StackSchema)).optional()
|
|
2553
3688
|
});
|
|
2554
3689
|
|
|
2555
3690
|
// src/schema/region.ts
|
|
2556
|
-
import { z as
|
|
3691
|
+
import { z as z20 } from "zod";
|
|
2557
3692
|
var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
|
|
2558
3693
|
var AF = ["af-south-1"];
|
|
2559
3694
|
var AP = ["ap-east-1", "ap-south-2", "ap-southeast-3", "ap-southeast-4", "ap-south-1", "ap-northeast-3", "ap-northeast-2", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1"];
|
|
@@ -2570,46 +3705,91 @@ var regions = [
|
|
|
2570
3705
|
...ME,
|
|
2571
3706
|
...SA
|
|
2572
3707
|
];
|
|
2573
|
-
var RegionSchema =
|
|
3708
|
+
var RegionSchema = z20.enum(regions);
|
|
2574
3709
|
|
|
2575
3710
|
// src/schema/plugin.ts
|
|
2576
|
-
import { z as
|
|
2577
|
-
var PluginSchema =
|
|
2578
|
-
name:
|
|
2579
|
-
schema:
|
|
3711
|
+
import { z as z21 } from "zod";
|
|
3712
|
+
var PluginSchema = z21.object({
|
|
3713
|
+
name: z21.string(),
|
|
3714
|
+
schema: z21.custom().optional(),
|
|
2580
3715
|
// depends: z.array(z.lazy(() => PluginSchema)).optional(),
|
|
2581
|
-
|
|
2582
|
-
onStack:
|
|
2583
|
-
|
|
3716
|
+
onApp: z21.function().returns(z21.void()).optional(),
|
|
3717
|
+
onStack: z21.function().returns(z21.any()).optional(),
|
|
3718
|
+
onResource: z21.function().returns(z21.any()).optional()
|
|
2584
3719
|
// bind: z.function().optional(),
|
|
2585
3720
|
});
|
|
2586
3721
|
|
|
2587
3722
|
// src/schema/app.ts
|
|
2588
|
-
var AppSchema =
|
|
3723
|
+
var AppSchema = z22.object({
|
|
3724
|
+
/** App name */
|
|
2589
3725
|
name: ResourceIdSchema,
|
|
3726
|
+
/** The AWS region to deploy to. */
|
|
2590
3727
|
region: RegionSchema,
|
|
2591
|
-
profile
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
3728
|
+
/** The AWS profile to deploy to. */
|
|
3729
|
+
profile: z22.string(),
|
|
3730
|
+
/** The deployment stage.
|
|
3731
|
+
* @default 'prod'
|
|
3732
|
+
*/
|
|
3733
|
+
stage: z22.string().regex(/[a-z]+/).default("prod"),
|
|
3734
|
+
/** Default properties. */
|
|
3735
|
+
defaults: z22.object({}).default({}),
|
|
3736
|
+
/** The application stacks. */
|
|
3737
|
+
stacks: z22.array(StackSchema).min(1).refine((stacks) => {
|
|
2595
3738
|
const unique = new Set(stacks.map((stack) => stack.name));
|
|
2596
3739
|
return unique.size === stacks.length;
|
|
2597
3740
|
}, "Must be an array of unique stacks"),
|
|
2598
|
-
plugins
|
|
3741
|
+
/** Custom plugins. */
|
|
3742
|
+
plugins: z22.array(PluginSchema).optional()
|
|
2599
3743
|
});
|
|
2600
3744
|
|
|
2601
3745
|
// src/util/import.ts
|
|
2602
3746
|
import { transformFile } from "@swc/core";
|
|
2603
3747
|
import { dirname, join as join2 } from "path";
|
|
2604
|
-
import { lstat, mkdir, writeFile } from "fs/promises";
|
|
3748
|
+
import { lstat as lstat2, mkdir, writeFile } from "fs/promises";
|
|
2605
3749
|
|
|
2606
3750
|
// src/util/path.ts
|
|
2607
|
-
import {
|
|
2608
|
-
|
|
2609
|
-
var
|
|
2610
|
-
var
|
|
2611
|
-
|
|
2612
|
-
|
|
3751
|
+
import { lstat } from "fs/promises";
|
|
3752
|
+
import { join, normalize } from "path";
|
|
3753
|
+
var root = process.cwd();
|
|
3754
|
+
var directories = {
|
|
3755
|
+
root,
|
|
3756
|
+
get output() {
|
|
3757
|
+
return join(this.root, ".awsless");
|
|
3758
|
+
},
|
|
3759
|
+
get cache() {
|
|
3760
|
+
return join(this.output, "cache");
|
|
3761
|
+
},
|
|
3762
|
+
get asset() {
|
|
3763
|
+
return join(this.output, "asset");
|
|
3764
|
+
},
|
|
3765
|
+
get template() {
|
|
3766
|
+
return join(this.output, "template");
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3769
|
+
var setRoot = (path = root) => {
|
|
3770
|
+
directories.root = path;
|
|
3771
|
+
};
|
|
3772
|
+
var findRootDir = async (path, configFile, level = 5) => {
|
|
3773
|
+
if (!level) {
|
|
3774
|
+
throw new TypeError("No awsless project found");
|
|
3775
|
+
}
|
|
3776
|
+
const file = join(path, configFile);
|
|
3777
|
+
const exists = await fileExist(file);
|
|
3778
|
+
if (exists) {
|
|
3779
|
+
return path;
|
|
3780
|
+
}
|
|
3781
|
+
return findRootDir(normalize(join(path, "..")), configFile, level - 1);
|
|
3782
|
+
};
|
|
3783
|
+
var fileExist = async (file) => {
|
|
3784
|
+
try {
|
|
3785
|
+
const stat = await lstat(file);
|
|
3786
|
+
if (stat.isFile()) {
|
|
3787
|
+
return true;
|
|
3788
|
+
}
|
|
3789
|
+
} catch (error) {
|
|
3790
|
+
}
|
|
3791
|
+
return false;
|
|
3792
|
+
};
|
|
2613
3793
|
|
|
2614
3794
|
// src/util/import.ts
|
|
2615
3795
|
var resolveFileNameExtension = async (path) => {
|
|
@@ -2624,7 +3804,7 @@ var resolveFileNameExtension = async (path) => {
|
|
|
2624
3804
|
const file = path.replace(/\.js$/, "") + option;
|
|
2625
3805
|
let stat;
|
|
2626
3806
|
try {
|
|
2627
|
-
stat = await
|
|
3807
|
+
stat = await lstat2(file);
|
|
2628
3808
|
} catch (error) {
|
|
2629
3809
|
continue;
|
|
2630
3810
|
}
|
|
@@ -2635,11 +3815,11 @@ var resolveFileNameExtension = async (path) => {
|
|
|
2635
3815
|
throw new Error(`Failed to load file: ${path}`);
|
|
2636
3816
|
};
|
|
2637
3817
|
var resolveDir = (path) => {
|
|
2638
|
-
return dirname(path).replace(
|
|
3818
|
+
return dirname(path).replace(directories.root + "/", "");
|
|
2639
3819
|
};
|
|
2640
3820
|
var importFile = async (path) => {
|
|
2641
3821
|
const load = async (file) => {
|
|
2642
|
-
debug("Load file", file);
|
|
3822
|
+
debug("Load file:", style.info(file));
|
|
2643
3823
|
let { code: code2 } = await transformFile(file, {
|
|
2644
3824
|
isModule: true
|
|
2645
3825
|
});
|
|
@@ -2659,16 +3839,22 @@ var importFile = async (path) => {
|
|
|
2659
3839
|
return code2;
|
|
2660
3840
|
};
|
|
2661
3841
|
const code = await load(path);
|
|
2662
|
-
const outputFile = join2(
|
|
2663
|
-
await mkdir(
|
|
3842
|
+
const outputFile = join2(directories.cache, "config.js");
|
|
3843
|
+
await mkdir(directories.cache, { recursive: true });
|
|
2664
3844
|
await writeFile(outputFile, code);
|
|
3845
|
+
debug("Save config file:", style.info(outputFile));
|
|
2665
3846
|
return import(outputFile);
|
|
2666
3847
|
};
|
|
2667
3848
|
|
|
2668
3849
|
// src/config.ts
|
|
2669
3850
|
var importConfig = async (options) => {
|
|
3851
|
+
debug("Find the root directory");
|
|
3852
|
+
const configFile = options.configFile || "awsless.config.ts";
|
|
3853
|
+
const root2 = await findRootDir(process.cwd(), configFile);
|
|
3854
|
+
setRoot(root2);
|
|
3855
|
+
debug("CWD:", style.info(root2));
|
|
2670
3856
|
debug("Import config file");
|
|
2671
|
-
const fileName = join3(
|
|
3857
|
+
const fileName = join3(root2, configFile);
|
|
2672
3858
|
const module = await importFile(fileName);
|
|
2673
3859
|
const appConfig = typeof module.default === "function" ? await module.default(options) : module.default;
|
|
2674
3860
|
debug("Validate config file");
|
|
@@ -3242,7 +4428,7 @@ var assetBuilder = (app) => {
|
|
|
3242
4428
|
derive([details], (details2) => {
|
|
3243
4429
|
return Object.entries(details2).map(([key, value]) => {
|
|
3244
4430
|
return `${style.label(key)} ${value}`;
|
|
3245
|
-
}).join("
|
|
4431
|
+
}).join(style.placeholder(" \u2500 "));
|
|
3246
4432
|
}),
|
|
3247
4433
|
br()
|
|
3248
4434
|
]);
|
|
@@ -3250,7 +4436,7 @@ var assetBuilder = (app) => {
|
|
|
3250
4436
|
const timer = createTimer();
|
|
3251
4437
|
const data = await asset.build({
|
|
3252
4438
|
async write(file, data2) {
|
|
3253
|
-
const fullpath = join4(
|
|
4439
|
+
const fullpath = join4(directories.asset, asset.type, app.name, stack.name, asset.id, file);
|
|
3254
4440
|
const basepath = dirname2(fullpath);
|
|
3255
4441
|
await mkdir2(basepath, { recursive: true });
|
|
3256
4442
|
await writeFile2(fullpath, data2);
|
|
@@ -3274,9 +4460,9 @@ import { mkdir as mkdir3, rm } from "fs/promises";
|
|
|
3274
4460
|
var cleanUp = async () => {
|
|
3275
4461
|
debug("Clean up template, cache, and asset files");
|
|
3276
4462
|
const paths = [
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
4463
|
+
directories.asset,
|
|
4464
|
+
directories.cache,
|
|
4465
|
+
directories.template
|
|
3280
4466
|
];
|
|
3281
4467
|
await Promise.all(paths.map((path) => rm(path, {
|
|
3282
4468
|
recursive: true,
|
|
@@ -3296,7 +4482,7 @@ var templateBuilder = (app) => {
|
|
|
3296
4482
|
const done = term.out.write(loadingDialog("Building stack templates..."));
|
|
3297
4483
|
await Promise.all(app.stacks.map(async (stack) => {
|
|
3298
4484
|
const template = stack.toString(true);
|
|
3299
|
-
const path = join5(
|
|
4485
|
+
const path = join5(directories.template, app.name);
|
|
3300
4486
|
const file = join5(path, `${stack.name}.json`);
|
|
3301
4487
|
await mkdir4(path, { recursive: true });
|
|
3302
4488
|
await writeFile3(file, template);
|
|
@@ -3425,19 +4611,26 @@ var StackClient = class {
|
|
|
3425
4611
|
async update(stack, capabilities) {
|
|
3426
4612
|
debug("Update the", style.info(stack.name), "stack");
|
|
3427
4613
|
const client = this.getClient(stack.region);
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
4614
|
+
try {
|
|
4615
|
+
await client.send(new UpdateStackCommand({
|
|
4616
|
+
StackName: this.stackName(stack.name),
|
|
4617
|
+
Capabilities: capabilities,
|
|
4618
|
+
Tags: this.tags(stack),
|
|
4619
|
+
...this.templateProp(stack)
|
|
4620
|
+
}));
|
|
4621
|
+
await waitUntilStackUpdateComplete({
|
|
4622
|
+
client,
|
|
4623
|
+
maxWaitTime: this.maxWaitTime,
|
|
4624
|
+
maxDelay: this.maxDelay
|
|
4625
|
+
}, {
|
|
4626
|
+
StackName: this.stackName(stack.name)
|
|
4627
|
+
});
|
|
4628
|
+
} catch (error) {
|
|
4629
|
+
if (error instanceof Error && error.name === "ValidationError" && error.message.toLowerCase().includes("no updates")) {
|
|
4630
|
+
return;
|
|
4631
|
+
}
|
|
4632
|
+
throw error;
|
|
4633
|
+
}
|
|
3441
4634
|
}
|
|
3442
4635
|
async validate(stack) {
|
|
3443
4636
|
debug("Validate the", style.info(stack.name), "stack");
|
|
@@ -3737,7 +4930,7 @@ var assetPublisher = (config, app) => {
|
|
|
3737
4930
|
await Promise.all([...stack.assets].map(async (asset) => {
|
|
3738
4931
|
await asset.publish?.({
|
|
3739
4932
|
async read(file) {
|
|
3740
|
-
const path = join6(
|
|
4933
|
+
const path = join6(directories.asset, asset.type, app.name, stack.name, asset.id, file);
|
|
3741
4934
|
const data = await readFile3(path);
|
|
3742
4935
|
return data;
|
|
3743
4936
|
},
|