@awsless/awsless 0.0.15 → 0.0.17
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 +212 -0
- package/dist/bin.cjs +1485 -297
- package/dist/bin.js +1469 -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,55 @@ 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
|
+
|
|
912
|
+
// src/plugins/on-failure/util.ts
|
|
913
|
+
var getGlobalOnFailure = ({ config, bootstrap: bootstrap2 }) => {
|
|
914
|
+
return hasOnFailure(config) ? bootstrap2.import("on-failure-queue-arn") : void 0;
|
|
915
|
+
};
|
|
916
|
+
var hasOnFailure = (config) => {
|
|
917
|
+
const onFailure = config.stacks.find((stack) => {
|
|
918
|
+
return typeof stack.onFailure !== "undefined";
|
|
919
|
+
});
|
|
920
|
+
return !!onFailure;
|
|
921
|
+
};
|
|
922
|
+
|
|
899
923
|
// src/plugins/function.ts
|
|
900
924
|
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
925
|
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 +934,104 @@ var RuntimeSchema = z6.enum([
|
|
|
910
934
|
var FunctionSchema = z6.union([
|
|
911
935
|
LocalFileSchema,
|
|
912
936
|
z6.object({
|
|
937
|
+
/** The file path ofthe function code. */
|
|
913
938
|
file: LocalFileSchema,
|
|
939
|
+
/** The amount of time that Lambda allows a function to run before stopping it.
|
|
940
|
+
* You can specify a size value from 1 second to 15 minutes.
|
|
941
|
+
* @default '10 seconds'
|
|
942
|
+
*/
|
|
914
943
|
timeout: TimeoutSchema.optional(),
|
|
944
|
+
/** The identifier of the function's runtime.
|
|
945
|
+
* @default 'nodejs18.x'
|
|
946
|
+
*/
|
|
915
947
|
runtime: RuntimeSchema.optional(),
|
|
948
|
+
/** The amount of memory available to the function at runtime.
|
|
949
|
+
* Increasing the function memory also increases its CPU allocation.
|
|
950
|
+
* The value can be any multiple of 1 MB.
|
|
951
|
+
* You can specify a size value from 128 MB to 10 GB.
|
|
952
|
+
* @default '128 MB'
|
|
953
|
+
*/
|
|
916
954
|
memorySize: MemorySizeSchema.optional(),
|
|
955
|
+
/** The instruction set architecture that the function supports.
|
|
956
|
+
* @default 'arm64'
|
|
957
|
+
*/
|
|
917
958
|
architecture: ArchitectureSchema.optional(),
|
|
959
|
+
/** The size of the function's /tmp directory.
|
|
960
|
+
* You can specify a size value from 512 MB to 10 GB.
|
|
961
|
+
* @default 512 MB
|
|
962
|
+
*/
|
|
918
963
|
ephemeralStorageSize: EphemeralStorageSizeSchema.optional(),
|
|
964
|
+
/** The maximum number of times to retry when the function returns an error.
|
|
965
|
+
* You can specify a number from 0 to 2.
|
|
966
|
+
* @default 2
|
|
967
|
+
*/
|
|
919
968
|
retryAttempts: RetryAttemptsSchema.optional(),
|
|
969
|
+
/** Environment variable key-value pairs.
|
|
970
|
+
* @example
|
|
971
|
+
* {
|
|
972
|
+
* environment: {
|
|
973
|
+
* name: 'value'
|
|
974
|
+
* }
|
|
975
|
+
* }
|
|
976
|
+
*/
|
|
920
977
|
environment: EnvironmentSchema.optional()
|
|
978
|
+
// onFailure: ResourceIdSchema.optional(),
|
|
921
979
|
})
|
|
922
980
|
]);
|
|
923
981
|
var schema = z6.object({
|
|
924
982
|
defaults: z6.object({
|
|
925
983
|
function: z6.object({
|
|
984
|
+
/** The amount of time that Lambda allows a function to run before stopping it.
|
|
985
|
+
* You can specify a size value from 1 second to 15 minutes.
|
|
986
|
+
* @default '10 seconds'
|
|
987
|
+
*/
|
|
926
988
|
timeout: TimeoutSchema.default("10 seconds"),
|
|
989
|
+
/** The identifier of the function's runtime.
|
|
990
|
+
* @default 'nodejs18.x'
|
|
991
|
+
*/
|
|
927
992
|
runtime: RuntimeSchema.default("nodejs18.x"),
|
|
993
|
+
/** The amount of memory available to the function at runtime.
|
|
994
|
+
* Increasing the function memory also increases its CPU allocation.
|
|
995
|
+
* The value can be any multiple of 1 MB.
|
|
996
|
+
* You can specify a size value from 128 MB to 10 GB.
|
|
997
|
+
* @default '128 MB'
|
|
998
|
+
*/
|
|
928
999
|
memorySize: MemorySizeSchema.default("128 MB"),
|
|
1000
|
+
/** The instruction set architecture that the function supports.
|
|
1001
|
+
* @default 'arm64'
|
|
1002
|
+
*/
|
|
929
1003
|
architecture: ArchitectureSchema.default("arm64"),
|
|
1004
|
+
/** The size of the function's /tmp directory.
|
|
1005
|
+
* You can specify a size value from 512 MB to 10 GB.
|
|
1006
|
+
* @default 512 MB
|
|
1007
|
+
*/
|
|
930
1008
|
ephemeralStorageSize: EphemeralStorageSizeSchema.default("512 MB"),
|
|
1009
|
+
/** The maximum number of times to retry when the function returns an error.
|
|
1010
|
+
* You can specify a number from 0 to 2.
|
|
1011
|
+
* @default 2
|
|
1012
|
+
*/
|
|
931
1013
|
retryAttempts: RetryAttemptsSchema.default(2),
|
|
1014
|
+
/** Environment variable key-value pairs.
|
|
1015
|
+
* @example
|
|
1016
|
+
* {
|
|
1017
|
+
* environment: {
|
|
1018
|
+
* name: 'value'
|
|
1019
|
+
* }
|
|
1020
|
+
* }
|
|
1021
|
+
*/
|
|
932
1022
|
environment: EnvironmentSchema.optional()
|
|
1023
|
+
// onFailure: ResourceIdSchema.optional(),
|
|
933
1024
|
}).default({})
|
|
934
1025
|
}).default({}),
|
|
935
1026
|
stacks: z6.object({
|
|
1027
|
+
/** Define the functions in your stack.
|
|
1028
|
+
* @example
|
|
1029
|
+
* {
|
|
1030
|
+
* functions: {
|
|
1031
|
+
* FUNCTION_NAME: 'function.ts'
|
|
1032
|
+
* }
|
|
1033
|
+
* }
|
|
1034
|
+
*/
|
|
936
1035
|
functions: z6.record(
|
|
937
1036
|
ResourceIdSchema,
|
|
938
1037
|
FunctionSchema
|
|
@@ -943,9 +1042,16 @@ var functionPlugin = definePlugin({
|
|
|
943
1042
|
name: "function",
|
|
944
1043
|
schema,
|
|
945
1044
|
onStack(ctx) {
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1045
|
+
const { config, stack } = ctx;
|
|
1046
|
+
for (const [id, fileOrProps] of Object.entries(ctx.stackConfig.functions || {})) {
|
|
1047
|
+
const props = typeof fileOrProps === "string" ? { ...config.defaults?.function, file: fileOrProps } : { ...config.defaults?.function, ...fileOrProps };
|
|
1048
|
+
const lambda = toLambdaFunction(ctx, id, fileOrProps);
|
|
1049
|
+
const invoke = new EventInvokeConfig(id, {
|
|
1050
|
+
functionName: lambda.name,
|
|
1051
|
+
retryAttempts: props.retryAttempts,
|
|
1052
|
+
onFailure: getGlobalOnFailure(ctx)
|
|
1053
|
+
}).dependsOn(lambda);
|
|
1054
|
+
stack.add(invoke, lambda);
|
|
949
1055
|
}
|
|
950
1056
|
}
|
|
951
1057
|
});
|
|
@@ -958,9 +1064,7 @@ var toLambdaFunction = (ctx, id, fileOrProps) => {
|
|
|
958
1064
|
code: Code.fromFile(id, props.file),
|
|
959
1065
|
...props
|
|
960
1066
|
});
|
|
961
|
-
lambda.addEnvironment("APP", config.name);
|
|
962
|
-
lambda.addEnvironment("STAGE", config.stage);
|
|
963
|
-
lambda.addEnvironment("STACK", stack.name);
|
|
1067
|
+
lambda.addEnvironment("APP", config.name).addEnvironment("STAGE", config.stage).addEnvironment("STACK", stack.name);
|
|
964
1068
|
if (props.runtime.startsWith("nodejs")) {
|
|
965
1069
|
lambda.addEnvironment("AWS_NODEJS_CONNECTION_REUSE_ENABLED", "1");
|
|
966
1070
|
}
|
|
@@ -1041,9 +1145,26 @@ var cronPlugin = definePlugin({
|
|
|
1041
1145
|
name: "cron",
|
|
1042
1146
|
schema: z7.object({
|
|
1043
1147
|
stacks: z7.object({
|
|
1148
|
+
/** Define the crons in your stack
|
|
1149
|
+
* @example
|
|
1150
|
+
* {
|
|
1151
|
+
* crons: {
|
|
1152
|
+
* CRON_NAME: {
|
|
1153
|
+
* consumer: 'function.ts',
|
|
1154
|
+
* schedule: 'rate(5 minutes)',
|
|
1155
|
+
* }
|
|
1156
|
+
* }
|
|
1157
|
+
* }
|
|
1158
|
+
* */
|
|
1044
1159
|
crons: z7.record(ResourceIdSchema, z7.object({
|
|
1160
|
+
/** The consuming lambda function properties */
|
|
1045
1161
|
consumer: FunctionSchema,
|
|
1162
|
+
/** The scheduling expression.
|
|
1163
|
+
* @example 'cron(0 20 * * ? *)'
|
|
1164
|
+
* @example 'rate(5 minutes)'
|
|
1165
|
+
*/
|
|
1046
1166
|
schedule: ScheduleExpressionSchema,
|
|
1167
|
+
// Valid JSON passed to the consumer.
|
|
1047
1168
|
payload: z7.unknown().optional()
|
|
1048
1169
|
})).optional()
|
|
1049
1170
|
}).array()
|
|
@@ -1072,6 +1193,10 @@ var Queue = class extends Resource {
|
|
|
1072
1193
|
this.name = formatName(this.props.name || logicalId);
|
|
1073
1194
|
}
|
|
1074
1195
|
name;
|
|
1196
|
+
setDeadLetter(arn) {
|
|
1197
|
+
this.props.deadLetterArn = arn;
|
|
1198
|
+
return this;
|
|
1199
|
+
}
|
|
1075
1200
|
get arn() {
|
|
1076
1201
|
return getAtt(this.logicalId, "Arn");
|
|
1077
1202
|
}
|
|
@@ -1096,7 +1221,12 @@ var Queue = class extends Resource {
|
|
|
1096
1221
|
MaximumMessageSize: this.props.maxMessageSize?.toBytes() ?? Size.kiloBytes(256).toBytes(),
|
|
1097
1222
|
MessageRetentionPeriod: this.props.retentionPeriod?.toSeconds() ?? Duration.days(4).toSeconds(),
|
|
1098
1223
|
ReceiveMessageWaitTimeSeconds: this.props.receiveMessageWaitTime?.toSeconds() ?? 0,
|
|
1099
|
-
VisibilityTimeout: this.props.visibilityTimeout?.toSeconds() ?? 30
|
|
1224
|
+
VisibilityTimeout: this.props.visibilityTimeout?.toSeconds() ?? 30,
|
|
1225
|
+
...this.props.deadLetterArn ? {
|
|
1226
|
+
RedrivePolicy: {
|
|
1227
|
+
deadLetterTargetArn: this.props.deadLetterArn
|
|
1228
|
+
}
|
|
1229
|
+
} : {}
|
|
1100
1230
|
};
|
|
1101
1231
|
}
|
|
1102
1232
|
};
|
|
@@ -1108,6 +1238,10 @@ var EventSourceMapping = class extends Resource {
|
|
|
1108
1238
|
super("AWS::Lambda::EventSourceMapping", logicalId);
|
|
1109
1239
|
this.props = props;
|
|
1110
1240
|
}
|
|
1241
|
+
setOnFailure(arn) {
|
|
1242
|
+
this.props.onFailure = arn;
|
|
1243
|
+
return this;
|
|
1244
|
+
}
|
|
1111
1245
|
properties() {
|
|
1112
1246
|
return {
|
|
1113
1247
|
Enabled: true,
|
|
@@ -1166,7 +1300,7 @@ var queuePlugin = definePlugin({
|
|
|
1166
1300
|
name: "queue",
|
|
1167
1301
|
schema: z8.object({
|
|
1168
1302
|
defaults: z8.object({
|
|
1169
|
-
/** Define the defaults properties for all queue's in your app */
|
|
1303
|
+
/** Define the defaults properties for all queue's in your app. */
|
|
1170
1304
|
queue: z8.object({
|
|
1171
1305
|
/** The number of seconds that Amazon SQS retains a message.
|
|
1172
1306
|
* You can specify a duration value from 1 minute to 14 days.
|
|
@@ -1195,20 +1329,20 @@ var queuePlugin = definePlugin({
|
|
|
1195
1329
|
}).default({})
|
|
1196
1330
|
}).default({}),
|
|
1197
1331
|
stacks: z8.object({
|
|
1198
|
-
/** Define the queues in your stack
|
|
1332
|
+
/** Define the queues in your stack.
|
|
1199
1333
|
* @example
|
|
1200
1334
|
* {
|
|
1201
1335
|
* queues: {
|
|
1202
1336
|
* QUEUE_NAME: 'function.ts'
|
|
1203
1337
|
* }
|
|
1204
1338
|
* }
|
|
1205
|
-
|
|
1339
|
+
*/
|
|
1206
1340
|
queues: z8.record(
|
|
1207
1341
|
ResourceIdSchema,
|
|
1208
1342
|
z8.union([
|
|
1209
1343
|
LocalFileSchema,
|
|
1210
1344
|
z8.object({
|
|
1211
|
-
/** The consuming lambda function properties */
|
|
1345
|
+
/** The consuming lambda function properties. */
|
|
1212
1346
|
consumer: FunctionSchema,
|
|
1213
1347
|
/** The number of seconds that Amazon SQS retains a message.
|
|
1214
1348
|
* You can specify a duration value from 1 minute to 14 days.
|
|
@@ -1226,7 +1360,6 @@ var queuePlugin = definePlugin({
|
|
|
1226
1360
|
/** Specifies the duration, in seconds,
|
|
1227
1361
|
* that the ReceiveMessage action call waits until a message is in the queue in order to include it in the response,
|
|
1228
1362
|
* rather than returning an empty response if a message isn't yet available.
|
|
1229
|
-
* You can specify an integer from 1 to 20.
|
|
1230
1363
|
* You can specify a duration value from 1 to 20 seconds.
|
|
1231
1364
|
* @default '0 seconds' */
|
|
1232
1365
|
receiveMessageWaitTime: DurationSchema.optional(),
|
|
@@ -1247,9 +1380,10 @@ var queuePlugin = definePlugin({
|
|
|
1247
1380
|
name: `${config.name}-${stack.name}-${id}`,
|
|
1248
1381
|
...props
|
|
1249
1382
|
});
|
|
1250
|
-
const lambda = toLambdaFunction(ctx, id
|
|
1383
|
+
const lambda = toLambdaFunction(ctx, `queue-${id}`, props.consumer);
|
|
1251
1384
|
const source = new SqsEventSource(id, lambda, {
|
|
1252
|
-
queueArn: queue2.arn
|
|
1385
|
+
queueArn: queue2.arn,
|
|
1386
|
+
onFailure: getGlobalOnFailure(ctx)
|
|
1253
1387
|
});
|
|
1254
1388
|
stack.add(queue2, lambda, source);
|
|
1255
1389
|
bind((lambda2) => {
|
|
@@ -1273,12 +1407,21 @@ var Table = class extends Resource {
|
|
|
1273
1407
|
}
|
|
1274
1408
|
name;
|
|
1275
1409
|
indexes;
|
|
1410
|
+
enableStream(viewType) {
|
|
1411
|
+
this.props.stream = viewType;
|
|
1412
|
+
}
|
|
1276
1413
|
addIndex(name, props) {
|
|
1277
1414
|
this.indexes[name] = props;
|
|
1278
1415
|
}
|
|
1279
|
-
get
|
|
1416
|
+
get id() {
|
|
1280
1417
|
return ref(this.logicalId);
|
|
1281
1418
|
}
|
|
1419
|
+
get arn() {
|
|
1420
|
+
return getAtt(this.logicalId, "Arn");
|
|
1421
|
+
}
|
|
1422
|
+
get streamArn() {
|
|
1423
|
+
return getAtt(this.logicalId, "StreamArn");
|
|
1424
|
+
}
|
|
1282
1425
|
get permissions() {
|
|
1283
1426
|
return {
|
|
1284
1427
|
actions: [
|
|
@@ -1312,6 +1455,11 @@ var Table = class extends Resource {
|
|
|
1312
1455
|
AttributeName: name,
|
|
1313
1456
|
AttributeType: type[0].toUpperCase()
|
|
1314
1457
|
})),
|
|
1458
|
+
...this.props.stream ? {
|
|
1459
|
+
StreamSpecification: {
|
|
1460
|
+
StreamViewType: constantCase2(this.props.stream)
|
|
1461
|
+
}
|
|
1462
|
+
} : {},
|
|
1315
1463
|
...this.props.timeToLiveAttribute ? {
|
|
1316
1464
|
TimeToLiveSpecification: {
|
|
1317
1465
|
AttributeName: this.props.timeToLiveAttribute,
|
|
@@ -1334,13 +1482,43 @@ var Table = class extends Resource {
|
|
|
1334
1482
|
}
|
|
1335
1483
|
};
|
|
1336
1484
|
|
|
1485
|
+
// src/formation/resource/lambda/event-source/dynamodb.ts
|
|
1486
|
+
var DynamoDBEventSource = class extends Group {
|
|
1487
|
+
constructor(id, lambda, props) {
|
|
1488
|
+
const source = new EventSourceMapping(id, {
|
|
1489
|
+
functionArn: lambda.arn,
|
|
1490
|
+
sourceArn: props.tableArn,
|
|
1491
|
+
batchSize: props.batchSize ?? 100,
|
|
1492
|
+
bisectBatchOnError: props.bisectBatchOnError ?? true,
|
|
1493
|
+
maxBatchingWindow: props.maxBatchingWindow,
|
|
1494
|
+
maxRecordAge: props.maxRecordAge,
|
|
1495
|
+
retryAttempts: props.retryAttempts ?? -1,
|
|
1496
|
+
parallelizationFactor: props.parallelizationFactor ?? 1,
|
|
1497
|
+
startingPosition: props.startingPosition,
|
|
1498
|
+
startingPositionTimestamp: props.startingPositionTimestamp,
|
|
1499
|
+
tumblingWindow: props.tumblingWindow,
|
|
1500
|
+
onFailure: props.onFailure
|
|
1501
|
+
});
|
|
1502
|
+
lambda.addPermissions({
|
|
1503
|
+
actions: [
|
|
1504
|
+
"dynamodb:ListStreams",
|
|
1505
|
+
"dynamodb:DescribeStream",
|
|
1506
|
+
"dynamodb:GetRecords",
|
|
1507
|
+
"dynamodb:GetShardIterator"
|
|
1508
|
+
],
|
|
1509
|
+
resources: [props.tableArn]
|
|
1510
|
+
});
|
|
1511
|
+
super([source]);
|
|
1512
|
+
}
|
|
1513
|
+
};
|
|
1514
|
+
|
|
1337
1515
|
// src/plugins/table.ts
|
|
1338
1516
|
var KeySchema = z9.string().min(1).max(255);
|
|
1339
1517
|
var tablePlugin = definePlugin({
|
|
1340
1518
|
name: "table",
|
|
1341
1519
|
schema: z9.object({
|
|
1342
1520
|
stacks: z9.object({
|
|
1343
|
-
/** Define the tables in your stack
|
|
1521
|
+
/** Define the tables in your stack.
|
|
1344
1522
|
* @example
|
|
1345
1523
|
* {
|
|
1346
1524
|
* tables: {
|
|
@@ -1367,20 +1545,34 @@ var tablePlugin = definePlugin({
|
|
|
1367
1545
|
* id: 'string'
|
|
1368
1546
|
* }
|
|
1369
1547
|
* }
|
|
1370
|
-
|
|
1548
|
+
*/
|
|
1371
1549
|
fields: z9.record(z9.string(), z9.enum(["string", "number", "binary"])),
|
|
1372
1550
|
/** The table class of the table.
|
|
1373
1551
|
* @default 'standard'
|
|
1374
|
-
|
|
1552
|
+
*/
|
|
1375
1553
|
class: z9.enum(["standard", "standard-infrequent-access"]).default("standard"),
|
|
1376
1554
|
/** Indicates whether point in time recovery is enabled on the table.
|
|
1377
1555
|
* @default false
|
|
1378
|
-
|
|
1556
|
+
*/
|
|
1379
1557
|
pointInTimeRecovery: z9.boolean().default(false),
|
|
1380
1558
|
/** The name of the TTL attribute used to store the expiration time for items in the table.
|
|
1381
1559
|
* - To update this property, you must first disable TTL and then enable TTL with the new attribute name.
|
|
1382
|
-
|
|
1560
|
+
*/
|
|
1383
1561
|
timeToLiveAttribute: KeySchema.optional(),
|
|
1562
|
+
/** The settings for the DynamoDB table stream, which capture changes to items stored in the table. */
|
|
1563
|
+
stream: z9.object({
|
|
1564
|
+
/** When an item in the table is modified,
|
|
1565
|
+
* stream.type determines what information is written to the stream for this table.
|
|
1566
|
+
* Valid values are:
|
|
1567
|
+
* - keys-only - Only the key attributes of the modified item are written to the stream.
|
|
1568
|
+
* - new-image - The entire item, as it appears after it was modified, is written to the stream.
|
|
1569
|
+
* - old-image - The entire item, as it appeared before it was modified, is written to the stream.
|
|
1570
|
+
* - new-and-old-images - Both the new and the old item images of the item are written to the stream.
|
|
1571
|
+
*/
|
|
1572
|
+
type: z9.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]),
|
|
1573
|
+
/** The consuming lambda function for the stream */
|
|
1574
|
+
consumer: FunctionSchema
|
|
1575
|
+
}).optional(),
|
|
1384
1576
|
/** Specifies the global secondary indexes to be created on the table.
|
|
1385
1577
|
* @example
|
|
1386
1578
|
* {
|
|
@@ -1390,7 +1582,7 @@ var tablePlugin = definePlugin({
|
|
|
1390
1582
|
* }
|
|
1391
1583
|
* }
|
|
1392
1584
|
* }
|
|
1393
|
-
|
|
1585
|
+
*/
|
|
1394
1586
|
indexes: z9.record(z9.string(), z9.object({
|
|
1395
1587
|
/** Specifies the name of the partition / hash key that makes up the primary key for the global secondary index. */
|
|
1396
1588
|
hash: KeySchema,
|
|
@@ -1418,13 +1610,24 @@ var tablePlugin = definePlugin({
|
|
|
1418
1610
|
).optional()
|
|
1419
1611
|
}).array()
|
|
1420
1612
|
}),
|
|
1421
|
-
onStack(
|
|
1613
|
+
onStack(ctx) {
|
|
1614
|
+
const { config, stack, stackConfig, bind } = ctx;
|
|
1422
1615
|
for (const [id, props] of Object.entries(stackConfig.tables || {})) {
|
|
1423
1616
|
const table = new Table(id, {
|
|
1617
|
+
...props,
|
|
1424
1618
|
name: `${config.name}-${stack.name}-${id}`,
|
|
1425
|
-
|
|
1619
|
+
stream: props.stream?.type
|
|
1426
1620
|
});
|
|
1427
1621
|
stack.add(table);
|
|
1622
|
+
if (props.stream) {
|
|
1623
|
+
const lambda = toLambdaFunction(ctx, `stream-${id}`, props.stream.consumer);
|
|
1624
|
+
const source = new DynamoDBEventSource(id, lambda, {
|
|
1625
|
+
tableArn: table.arn,
|
|
1626
|
+
onFailure: getGlobalOnFailure(ctx),
|
|
1627
|
+
...props.stream
|
|
1628
|
+
});
|
|
1629
|
+
stack.add(lambda, source);
|
|
1630
|
+
}
|
|
1428
1631
|
bind((lambda) => {
|
|
1429
1632
|
lambda.addPermissions(table.permissions);
|
|
1430
1633
|
});
|
|
@@ -1479,12 +1682,12 @@ var storePlugin = definePlugin({
|
|
|
1479
1682
|
name: "store",
|
|
1480
1683
|
schema: z10.object({
|
|
1481
1684
|
stacks: z10.object({
|
|
1482
|
-
/** Define the stores in your stack
|
|
1685
|
+
/** Define the stores in your stack.
|
|
1483
1686
|
* @example
|
|
1484
1687
|
* {
|
|
1485
1688
|
* stores: [ 'STORE_NAME' ]
|
|
1486
1689
|
* }
|
|
1487
|
-
|
|
1690
|
+
*/
|
|
1488
1691
|
stores: z10.array(ResourceIdSchema).optional()
|
|
1489
1692
|
}).array()
|
|
1490
1693
|
}),
|
|
@@ -1568,14 +1771,14 @@ var topicPlugin = definePlugin({
|
|
|
1568
1771
|
name: "topic",
|
|
1569
1772
|
schema: z11.object({
|
|
1570
1773
|
stacks: z11.object({
|
|
1571
|
-
/** Define the topics to listen too in your stack
|
|
1774
|
+
/** Define the topics to listen too in your stack.
|
|
1572
1775
|
* @example
|
|
1573
1776
|
* {
|
|
1574
1777
|
* topics: {
|
|
1575
1778
|
* TOPIC_NAME: 'function.ts'
|
|
1576
1779
|
* }
|
|
1577
1780
|
* }
|
|
1578
|
-
|
|
1781
|
+
*/
|
|
1579
1782
|
topics: z11.record(ResourceIdSchema, FunctionSchema).optional()
|
|
1580
1783
|
}).array()
|
|
1581
1784
|
}),
|
|
@@ -1605,7 +1808,7 @@ var topicPlugin = definePlugin({
|
|
|
1605
1808
|
onStack(ctx) {
|
|
1606
1809
|
const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
|
|
1607
1810
|
for (const [id, props] of Object.entries(stackConfig.topics || {})) {
|
|
1608
|
-
const lambda = toLambdaFunction(ctx, id
|
|
1811
|
+
const lambda = toLambdaFunction(ctx, `topic-${id}`, props);
|
|
1609
1812
|
const source = new SnsEventSource(id, lambda, {
|
|
1610
1813
|
topicArn: bootstrap2.import(`topic-${id}-arn`)
|
|
1611
1814
|
});
|
|
@@ -1619,10 +1822,10 @@ import { z as z12 } from "zod";
|
|
|
1619
1822
|
var extendPlugin = definePlugin({
|
|
1620
1823
|
name: "extend",
|
|
1621
1824
|
schema: z12.object({
|
|
1622
|
-
/** Extend your app with custom resources */
|
|
1825
|
+
/** Extend your app with custom resources. */
|
|
1623
1826
|
extend: z12.custom().optional(),
|
|
1624
1827
|
stacks: z12.object({
|
|
1625
|
-
/** Extend your stack with custom resources */
|
|
1828
|
+
/** Extend your stack with custom resources. */
|
|
1626
1829
|
extend: z12.custom().optional()
|
|
1627
1830
|
}).array()
|
|
1628
1831
|
}),
|
|
@@ -1688,7 +1891,7 @@ var pubsubPlugin = definePlugin({
|
|
|
1688
1891
|
name: "pubsub",
|
|
1689
1892
|
schema: z13.object({
|
|
1690
1893
|
stacks: z13.object({
|
|
1691
|
-
/** Define the pubsub subscriber in your stack
|
|
1894
|
+
/** Define the pubsub subscriber in your stack.
|
|
1692
1895
|
* @example
|
|
1693
1896
|
* {
|
|
1694
1897
|
* pubsub: {
|
|
@@ -1700,11 +1903,11 @@ var pubsubPlugin = definePlugin({
|
|
|
1700
1903
|
* }
|
|
1701
1904
|
*/
|
|
1702
1905
|
pubsub: z13.record(ResourceIdSchema, z13.object({
|
|
1703
|
-
/** The SQL statement used to query the iot topic */
|
|
1906
|
+
/** The SQL statement used to query the iot topic. */
|
|
1704
1907
|
sql: z13.string(),
|
|
1705
|
-
/** The version of the SQL rules engine to use when evaluating the rule */
|
|
1908
|
+
/** The version of the SQL rules engine to use when evaluating the rule. */
|
|
1706
1909
|
sqlVersion: z13.enum(["2015-10-08", "2016-03-23", "beta"]).default("2016-03-23"),
|
|
1707
|
-
/** The consuming lambda function properties */
|
|
1910
|
+
/** The consuming lambda function properties. */
|
|
1708
1911
|
consumer: FunctionSchema
|
|
1709
1912
|
})).optional()
|
|
1710
1913
|
}).array()
|
|
@@ -1720,7 +1923,7 @@ var pubsubPlugin = definePlugin({
|
|
|
1720
1923
|
onStack(ctx) {
|
|
1721
1924
|
const { config, stack, stackConfig } = ctx;
|
|
1722
1925
|
for (const [id, props] of Object.entries(stackConfig.pubsub || {})) {
|
|
1723
|
-
const lambda = toLambdaFunction(ctx, id
|
|
1926
|
+
const lambda = toLambdaFunction(ctx, `pubsub-${id}`, props.consumer);
|
|
1724
1927
|
const source = new IotEventSource(id, lambda, {
|
|
1725
1928
|
name: `${config.name}-${stack.name}-${id}`,
|
|
1726
1929
|
sql: props.sql,
|
|
@@ -1747,34 +1950,6 @@ import { paramCase as paramCase3 } from "change-case";
|
|
|
1747
1950
|
|
|
1748
1951
|
// src/formation/resource/appsync/graphql-api.ts
|
|
1749
1952
|
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
1953
|
var GraphQLApi = class extends Resource {
|
|
1779
1954
|
constructor(logicalId, props) {
|
|
1780
1955
|
super("AWS::AppSync::GraphQLApi", logicalId);
|
|
@@ -1816,44 +1991,6 @@ var GraphQLApi = class extends Resource {
|
|
|
1816
1991
|
};
|
|
1817
1992
|
}
|
|
1818
1993
|
};
|
|
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
1994
|
|
|
1858
1995
|
// src/formation/resource/route53/record-set.ts
|
|
1859
1996
|
var RecordSet = class extends Resource {
|
|
@@ -1874,19 +2011,33 @@ var RecordSet = class extends Resource {
|
|
|
1874
2011
|
} : {},
|
|
1875
2012
|
...this.props.alias ? {
|
|
1876
2013
|
AliasTarget: {
|
|
1877
|
-
DNSName: this.props.alias,
|
|
1878
|
-
HostedZoneId: this.props.hostedZoneId
|
|
2014
|
+
DNSName: this.props.alias.dnsName,
|
|
2015
|
+
HostedZoneId: this.props.alias.hostedZoneId
|
|
1879
2016
|
}
|
|
1880
2017
|
} : {}
|
|
1881
2018
|
};
|
|
1882
2019
|
}
|
|
1883
2020
|
};
|
|
1884
2021
|
|
|
1885
|
-
// src/formation/resource/appsync/schema.ts
|
|
2022
|
+
// src/formation/resource/appsync/graphql-schema.ts
|
|
1886
2023
|
import { print } from "graphql";
|
|
1887
2024
|
import { readFile } from "fs/promises";
|
|
1888
2025
|
import { mergeTypeDefs } from "@graphql-tools/merge";
|
|
1889
|
-
var
|
|
2026
|
+
var GraphQLSchema = class extends Resource {
|
|
2027
|
+
constructor(logicalId, props) {
|
|
2028
|
+
super("AWS::AppSync::GraphQLSchema", logicalId, [
|
|
2029
|
+
props.definition
|
|
2030
|
+
]);
|
|
2031
|
+
this.props = props;
|
|
2032
|
+
}
|
|
2033
|
+
properties() {
|
|
2034
|
+
return {
|
|
2035
|
+
ApiId: this.props.apiId,
|
|
2036
|
+
Definition: this.props.definition.toString()
|
|
2037
|
+
};
|
|
2038
|
+
}
|
|
2039
|
+
};
|
|
2040
|
+
var Definition = class extends Asset {
|
|
1890
2041
|
constructor(id, files) {
|
|
1891
2042
|
super("graphql", id);
|
|
1892
2043
|
this.files = files;
|
|
@@ -1899,10 +2050,14 @@ var Schema = class extends Asset {
|
|
|
1899
2050
|
}));
|
|
1900
2051
|
const defs = mergeTypeDefs(schemas);
|
|
1901
2052
|
const schema2 = print(defs);
|
|
2053
|
+
const size = Buffer.from(schema2, "utf8").byteLength;
|
|
1902
2054
|
await write("schema.gql", schema2);
|
|
1903
2055
|
this.schema = schema2;
|
|
2056
|
+
return {
|
|
2057
|
+
size: formatByteSize(size)
|
|
2058
|
+
};
|
|
1904
2059
|
}
|
|
1905
|
-
|
|
2060
|
+
toString() {
|
|
1906
2061
|
return this.schema;
|
|
1907
2062
|
}
|
|
1908
2063
|
};
|
|
@@ -2082,6 +2237,41 @@ var AppsyncEventSource = class extends Group {
|
|
|
2082
2237
|
}
|
|
2083
2238
|
};
|
|
2084
2239
|
|
|
2240
|
+
// src/formation/resource/appsync/domain-name.ts
|
|
2241
|
+
var DomainName = class extends Resource {
|
|
2242
|
+
constructor(logicalId, props) {
|
|
2243
|
+
super("AWS::AppSync::DomainName", logicalId);
|
|
2244
|
+
this.props = props;
|
|
2245
|
+
}
|
|
2246
|
+
get appSyncDomainName() {
|
|
2247
|
+
return getAtt(this.logicalId, "AppSyncDomainName");
|
|
2248
|
+
}
|
|
2249
|
+
get domainName() {
|
|
2250
|
+
return getAtt(this.logicalId, "DomainName");
|
|
2251
|
+
}
|
|
2252
|
+
get hostedZoneId() {
|
|
2253
|
+
return getAtt(this.logicalId, "HostedZoneId");
|
|
2254
|
+
}
|
|
2255
|
+
properties() {
|
|
2256
|
+
return {
|
|
2257
|
+
DomainName: this.props.domainName,
|
|
2258
|
+
CertificateArn: this.props.certificateArn
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
};
|
|
2262
|
+
var DomainNameApiAssociation = class extends Resource {
|
|
2263
|
+
constructor(logicalId, props) {
|
|
2264
|
+
super("AWS::AppSync::DomainNameApiAssociation", logicalId);
|
|
2265
|
+
this.props = props;
|
|
2266
|
+
}
|
|
2267
|
+
properties() {
|
|
2268
|
+
return {
|
|
2269
|
+
ApiId: this.props.apiId,
|
|
2270
|
+
DomainName: this.props.domainName
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2274
|
+
|
|
2085
2275
|
// src/plugins/graphql.ts
|
|
2086
2276
|
var defaultResolver = `
|
|
2087
2277
|
export function request(ctx) {
|
|
@@ -2131,38 +2321,51 @@ var graphqlPlugin = definePlugin({
|
|
|
2131
2321
|
}
|
|
2132
2322
|
}
|
|
2133
2323
|
for (const id of apis) {
|
|
2134
|
-
const
|
|
2324
|
+
const schemaFiles = [];
|
|
2135
2325
|
for (const stack of config.stacks) {
|
|
2136
2326
|
const files = toArray(stack.graphql?.[id]?.schema || []);
|
|
2137
|
-
|
|
2327
|
+
schemaFiles.push(...files);
|
|
2138
2328
|
}
|
|
2139
|
-
const
|
|
2329
|
+
const api = new GraphQLApi(id, {
|
|
2140
2330
|
name: `${config.name}-${id}`,
|
|
2141
|
-
authenticationType: "api-key"
|
|
2142
|
-
schema: new Schema(id, schema2)
|
|
2331
|
+
authenticationType: "api-key"
|
|
2143
2332
|
});
|
|
2144
|
-
|
|
2333
|
+
const schema2 = new GraphQLSchema(id, {
|
|
2334
|
+
apiId: api.id,
|
|
2335
|
+
definition: new Definition(id, schemaFiles)
|
|
2336
|
+
}).dependsOn(api);
|
|
2337
|
+
bootstrap2.add(api).add(schema2).export(`graphql-${id}`, api.id);
|
|
2145
2338
|
const props = config.defaults.graphql?.[id];
|
|
2146
2339
|
if (!props) {
|
|
2147
2340
|
continue;
|
|
2148
2341
|
}
|
|
2149
2342
|
if (props.authorization) {
|
|
2150
2343
|
const lambda = toLambdaFunction(ctx, `${id}-authorizer`, props.authorization.authorizer);
|
|
2151
|
-
|
|
2344
|
+
api.addLambdaAuthProvider(lambda.arn, props.authorization.ttl);
|
|
2152
2345
|
bootstrap2.add(lambda);
|
|
2153
2346
|
}
|
|
2154
2347
|
if (props.domain) {
|
|
2155
2348
|
const domainName = props.subDomain ? `${props.subDomain}.${props.domain}` : props.domain;
|
|
2156
|
-
const hostedZoneId =
|
|
2349
|
+
const hostedZoneId = usEastBootstrap.import(`hosted-zone-${props.domain}-id`);
|
|
2157
2350
|
const certificateArn = usEastBootstrap.import(`certificate-${props.domain}-arn`);
|
|
2158
|
-
|
|
2159
|
-
|
|
2351
|
+
const domain = new DomainName(id, {
|
|
2352
|
+
domainName,
|
|
2353
|
+
certificateArn
|
|
2354
|
+
});
|
|
2355
|
+
const association = new DomainNameApiAssociation(id, {
|
|
2356
|
+
apiId: api.id,
|
|
2357
|
+
domainName: domain.domainName
|
|
2358
|
+
}).dependsOn(api, domain);
|
|
2359
|
+
const record = new RecordSet(`${id}-graphql`, {
|
|
2160
2360
|
hostedZoneId,
|
|
2161
2361
|
type: "A",
|
|
2162
2362
|
name: domainName,
|
|
2163
|
-
alias:
|
|
2164
|
-
|
|
2165
|
-
|
|
2363
|
+
alias: {
|
|
2364
|
+
dnsName: domain.appSyncDomainName,
|
|
2365
|
+
hostedZoneId: domain.hostedZoneId
|
|
2366
|
+
}
|
|
2367
|
+
}).dependsOn(domain, association);
|
|
2368
|
+
bootstrap2.add(domain, association, record);
|
|
2166
2369
|
}
|
|
2167
2370
|
}
|
|
2168
2371
|
},
|
|
@@ -2173,7 +2376,7 @@ var graphqlPlugin = definePlugin({
|
|
|
2173
2376
|
for (const [typeAndField, functionProps] of Object.entries(props.resolvers || {})) {
|
|
2174
2377
|
const [typeName, fieldName] = typeAndField.split(/[\s]+/g);
|
|
2175
2378
|
const entryId = paramCase3(`${id}-${typeName}-${fieldName}`);
|
|
2176
|
-
const lambda = toLambdaFunction(ctx, entryId
|
|
2379
|
+
const lambda = toLambdaFunction(ctx, `graphql-${entryId}`, functionProps);
|
|
2177
2380
|
const source = new AppsyncEventSource(entryId, lambda, {
|
|
2178
2381
|
apiId,
|
|
2179
2382
|
typeName,
|
|
@@ -2209,7 +2412,7 @@ var HostedZone = class extends Resource {
|
|
|
2209
2412
|
|
|
2210
2413
|
// src/formation/resource/certificate-manager/certificate.ts
|
|
2211
2414
|
var Certificate = class extends Resource {
|
|
2212
|
-
constructor(logicalId, props
|
|
2415
|
+
constructor(logicalId, props) {
|
|
2213
2416
|
super("AWS::CertificateManager::Certificate", logicalId);
|
|
2214
2417
|
this.props = props;
|
|
2215
2418
|
this.name = this.props.domainName || logicalId;
|
|
@@ -2221,8 +2424,12 @@ var Certificate = class extends Resource {
|
|
|
2221
2424
|
properties() {
|
|
2222
2425
|
return {
|
|
2223
2426
|
DomainName: this.name,
|
|
2427
|
+
SubjectAlternativeNames: this.props.alternativeNames || [],
|
|
2224
2428
|
ValidationMethod: "DNS",
|
|
2225
|
-
|
|
2429
|
+
DomainValidationOptions: [{
|
|
2430
|
+
DomainName: this.name,
|
|
2431
|
+
HostedZoneId: this.props.hostedZoneId
|
|
2432
|
+
}]
|
|
2226
2433
|
};
|
|
2227
2434
|
}
|
|
2228
2435
|
};
|
|
@@ -2254,40 +2461,965 @@ var RecordSetGroup = class extends Resource {
|
|
|
2254
2461
|
}
|
|
2255
2462
|
};
|
|
2256
2463
|
|
|
2257
|
-
// src/
|
|
2258
|
-
var
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
}
|
|
2464
|
+
// src/custom/delete-hosted-zone/handler.ts
|
|
2465
|
+
var deleteHostedZoneRecordsHandlerCode = (
|
|
2466
|
+
/* JS */
|
|
2467
|
+
`
|
|
2468
|
+
|
|
2469
|
+
const { Route53Client, ListResourceRecordSetsCommand, ChangeResourceRecordSetsCommand } = require('@aws-sdk/client-route-53')
|
|
2470
|
+
|
|
2471
|
+
const client = new Route53Client({})
|
|
2472
|
+
|
|
2473
|
+
exports.handler = async (event) => {
|
|
2474
|
+
const type = event.RequestType
|
|
2475
|
+
const hostedZoneId = event.ResourceProperties.hostedZoneId
|
|
2476
|
+
|
|
2477
|
+
try {
|
|
2478
|
+
if(type === 'Delete') {
|
|
2479
|
+
const records = await listHostedZoneRecords(hostedZoneId)
|
|
2480
|
+
console.log(records)
|
|
2481
|
+
|
|
2482
|
+
await deleteHostedZoneRecords(hostedZoneId, records)
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
await send(event, hostedZoneId, 'SUCCESS')
|
|
2486
|
+
}
|
|
2487
|
+
catch(error) {
|
|
2488
|
+
if (error instanceof Error) {
|
|
2489
|
+
await send(event, hostedZoneId, 'FAILED', {}, error.message)
|
|
2490
|
+
} else {
|
|
2491
|
+
await send(event, hostedZoneId, 'FAILED', {}, 'Unknown error')
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
|
|
2496
|
+
const send = async (event, id, status, data = {}, reason = '') => {
|
|
2497
|
+
const body = JSON.stringify({
|
|
2498
|
+
Status: status,
|
|
2499
|
+
Reason: reason,
|
|
2500
|
+
PhysicalResourceId: id,
|
|
2501
|
+
StackId: event.StackId,
|
|
2502
|
+
RequestId: event.RequestId,
|
|
2503
|
+
LogicalResourceId: event.LogicalResourceId,
|
|
2504
|
+
NoEcho: false,
|
|
2505
|
+
Data: data
|
|
2506
|
+
})
|
|
2507
|
+
|
|
2508
|
+
await fetch(event.ResponseURL, {
|
|
2509
|
+
method: 'PUT',
|
|
2510
|
+
port: 443,
|
|
2511
|
+
body,
|
|
2512
|
+
headers: {
|
|
2513
|
+
'content-type': '',
|
|
2514
|
+
'content-length': Buffer.from(body).byteLength,
|
|
2515
|
+
},
|
|
2516
|
+
})
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
const deleteHostedZoneRecords = async (hostedZoneId, records) => {
|
|
2520
|
+
records = records.filter(record => ![ 'SOA', 'NS' ].includes(record.Type))
|
|
2521
|
+
if(records.length === 0) {
|
|
2522
|
+
return
|
|
2523
|
+
}
|
|
2524
|
+
|
|
2525
|
+
const chunkSize = 100;
|
|
2526
|
+
for (let i = 0; i < records.length; i += chunkSize) {
|
|
2527
|
+
const chunk = records.slice(i, i + chunkSize);
|
|
2528
|
+
|
|
2529
|
+
await client.send(new ChangeResourceRecordSetsCommand({
|
|
2530
|
+
HostedZoneId: hostedZoneId,
|
|
2531
|
+
ChangeBatch: {
|
|
2532
|
+
Changes: chunk.map(record => ({
|
|
2533
|
+
Action: 'DELETE',
|
|
2534
|
+
ResourceRecordSet: record
|
|
2535
|
+
}))
|
|
2536
|
+
}
|
|
2537
|
+
}))
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
const listHostedZoneRecords = async (hostedZoneId) => {
|
|
2542
|
+
|
|
2543
|
+
const records = []
|
|
2544
|
+
let token
|
|
2545
|
+
|
|
2546
|
+
while(true) {
|
|
2547
|
+
const result = await client.send(new ListResourceRecordSetsCommand({
|
|
2548
|
+
HostedZoneId: hostedZoneId,
|
|
2549
|
+
NextRecordName: token
|
|
2550
|
+
}))
|
|
2551
|
+
|
|
2552
|
+
if(result.ResourceRecordSets && result.ResourceRecordSets.length) {
|
|
2553
|
+
records.push(...result.ResourceRecordSets)
|
|
2554
|
+
}
|
|
2555
|
+
|
|
2556
|
+
if(result.NextRecordName) {
|
|
2557
|
+
token = result.NextRecordName
|
|
2558
|
+
} else {
|
|
2559
|
+
return records
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
}
|
|
2563
|
+
`
|
|
2564
|
+
);
|
|
2565
|
+
|
|
2566
|
+
// src/formation/resource/cloud-formation/custom-resource.ts
|
|
2567
|
+
var CustomResource = class extends Resource {
|
|
2568
|
+
constructor(logicalId, props) {
|
|
2569
|
+
super("AWS::CloudFormation::CustomResource", logicalId);
|
|
2570
|
+
this.props = props;
|
|
2571
|
+
}
|
|
2572
|
+
getAtt(name) {
|
|
2573
|
+
return getAtt(this.logicalId, name);
|
|
2574
|
+
}
|
|
2575
|
+
properties() {
|
|
2576
|
+
return {
|
|
2577
|
+
ServiceToken: this.props.serviceToken,
|
|
2578
|
+
...this.props.properties
|
|
2579
|
+
};
|
|
2580
|
+
}
|
|
2581
|
+
};
|
|
2582
|
+
|
|
2583
|
+
// src/plugins/domain.ts
|
|
2584
|
+
var DomainNameSchema = z15.string().regex(/[a-z\-\_\.]/g, "Invalid domain name");
|
|
2585
|
+
var domainPlugin = definePlugin({
|
|
2586
|
+
name: "domain",
|
|
2587
|
+
schema: z15.object({
|
|
2588
|
+
/** Define the domains for your application.
|
|
2589
|
+
* @example
|
|
2590
|
+
* {
|
|
2591
|
+
* domains: {
|
|
2592
|
+
* 'example.com': [{
|
|
2593
|
+
* name: 'www',
|
|
2594
|
+
* type: 'TXT',
|
|
2595
|
+
* ttl: '60 seconds',
|
|
2596
|
+
* records: [ 'value' ]
|
|
2597
|
+
* }]
|
|
2598
|
+
* }
|
|
2599
|
+
* }
|
|
2600
|
+
*/
|
|
2601
|
+
domains: z15.record(DomainNameSchema, z15.object({
|
|
2602
|
+
/** Enter a fully qualified domain name, for example, www.example.com.
|
|
2603
|
+
* You can optionally include a trailing dot.
|
|
2604
|
+
* If you omit the trailing dot, Amazon Route 53 assumes that the domain name that you specify is fully qualified.
|
|
2605
|
+
* This means that Route 53 treats www.example.com (without a trailing dot) and www.example.com. (with a trailing dot) as identical.
|
|
2606
|
+
*/
|
|
2607
|
+
name: DomainNameSchema.optional(),
|
|
2608
|
+
/** The DNS record type. */
|
|
2609
|
+
type: z15.enum(["A", "AAAA", "CAA", "CNAME", "DS", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV", "TXT"]),
|
|
2610
|
+
/** The resource record cache time to live (TTL) */
|
|
2611
|
+
ttl: DurationSchema,
|
|
2612
|
+
/** One or more values that correspond with the value that you specified for the Type property. */
|
|
2613
|
+
records: z15.string().array()
|
|
2614
|
+
}).array()).optional()
|
|
2615
|
+
}),
|
|
2616
|
+
onApp({ config, bootstrap: bootstrap2, usEastBootstrap }) {
|
|
2617
|
+
const domains = Object.entries(config.domains || {});
|
|
2618
|
+
if (domains.length === 0) {
|
|
2619
|
+
return;
|
|
2620
|
+
}
|
|
2621
|
+
const lambda = new Function("delete-hosted-zone", {
|
|
2622
|
+
name: `${config.name}-delete-hosted-zone`,
|
|
2623
|
+
code: Code.fromInline(deleteHostedZoneRecordsHandlerCode, "index.handler")
|
|
2624
|
+
});
|
|
2625
|
+
lambda.addPermissions({
|
|
2626
|
+
actions: [
|
|
2627
|
+
"route53:ListResourceRecordSets",
|
|
2628
|
+
"route53:ChangeResourceRecordSets"
|
|
2629
|
+
],
|
|
2630
|
+
resources: ["*"]
|
|
2631
|
+
});
|
|
2632
|
+
usEastBootstrap.add(lambda);
|
|
2633
|
+
for (const [domain, records] of domains) {
|
|
2634
|
+
const hostedZone = new HostedZone(domain);
|
|
2635
|
+
const usEastCertificate = new Certificate(domain, {
|
|
2636
|
+
hostedZoneId: hostedZone.id,
|
|
2637
|
+
alternativeNames: [`*.${domain}`]
|
|
2638
|
+
});
|
|
2639
|
+
const custom = new CustomResource(domain, {
|
|
2640
|
+
serviceToken: lambda.arn,
|
|
2641
|
+
properties: {
|
|
2642
|
+
hostedZoneId: hostedZone.id
|
|
2643
|
+
}
|
|
2644
|
+
}).dependsOn(hostedZone);
|
|
2645
|
+
usEastBootstrap.add(custom).add(hostedZone).add(usEastCertificate).export(`certificate-${domain}-arn`, usEastCertificate.arn).export(`hosted-zone-${domain}-id`, hostedZone.id);
|
|
2646
|
+
const certificate = new Certificate(domain, {
|
|
2647
|
+
hostedZoneId: usEastBootstrap.import(`hosted-zone-${domain}-id`),
|
|
2648
|
+
alternativeNames: [`*.${domain}`]
|
|
2649
|
+
});
|
|
2650
|
+
bootstrap2.add(certificate).export(`certificate-${domain}-arn`, certificate.arn);
|
|
2651
|
+
if (records.length > 0) {
|
|
2652
|
+
const group = new RecordSetGroup(domain, {
|
|
2653
|
+
hostedZoneId: hostedZone.id,
|
|
2654
|
+
records
|
|
2655
|
+
}).dependsOn(hostedZone);
|
|
2656
|
+
usEastBootstrap.add(group);
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
});
|
|
2661
|
+
|
|
2662
|
+
// src/plugins/on-failure/index.ts
|
|
2663
|
+
import { z as z16 } from "zod";
|
|
2664
|
+
var onFailurePlugin = definePlugin({
|
|
2665
|
+
name: "on-failure",
|
|
2666
|
+
schema: z16.object({
|
|
2667
|
+
stacks: z16.object({
|
|
2668
|
+
/** Defining a onFailure handler will add a global onFailure handler for the following resources:
|
|
2669
|
+
* - Async lambda functions
|
|
2670
|
+
* - SQS queues
|
|
2671
|
+
* - DynamoDB streams
|
|
2672
|
+
* @example
|
|
2673
|
+
* {
|
|
2674
|
+
* onFailure: 'on-failure.ts'
|
|
2675
|
+
* }
|
|
2676
|
+
*/
|
|
2677
|
+
onFailure: FunctionSchema.optional()
|
|
2678
|
+
}).array()
|
|
2679
|
+
}),
|
|
2680
|
+
onApp({ config, bootstrap: bootstrap2 }) {
|
|
2681
|
+
if (!hasOnFailure(config)) {
|
|
2682
|
+
return;
|
|
2683
|
+
}
|
|
2684
|
+
const queue2 = new Queue("on-failure", {
|
|
2685
|
+
name: `${config.name}-failure`
|
|
2686
|
+
});
|
|
2687
|
+
bootstrap2.add(queue2).export("on-failure-queue-arn", queue2.arn);
|
|
2688
|
+
},
|
|
2689
|
+
onStack(ctx) {
|
|
2690
|
+
const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
|
|
2691
|
+
const onFailure = stackConfig.onFailure;
|
|
2692
|
+
if (!onFailure) {
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
const queueArn = bootstrap2.import("on-failure-queue-arn");
|
|
2696
|
+
const lambda = toLambdaFunction(ctx, "on-failure", onFailure);
|
|
2697
|
+
const source = new SqsEventSource("on-failure", lambda, {
|
|
2698
|
+
queueArn
|
|
2699
|
+
});
|
|
2700
|
+
lambda.addPermissions({
|
|
2701
|
+
actions: [
|
|
2702
|
+
"sqs:SendMessage",
|
|
2703
|
+
"sqs:ReceiveMessage",
|
|
2704
|
+
"sqs:GetQueueUrl",
|
|
2705
|
+
"sqs:GetQueueAttributes"
|
|
2706
|
+
],
|
|
2707
|
+
resources: [queueArn]
|
|
2708
|
+
});
|
|
2709
|
+
stack.add(lambda, source);
|
|
2710
|
+
}
|
|
2711
|
+
});
|
|
2712
|
+
|
|
2713
|
+
// src/formation/resource/ec2/vpc.ts
|
|
2714
|
+
var Vpc = class extends Resource {
|
|
2715
|
+
constructor(logicalId, props) {
|
|
2716
|
+
super("AWS::EC2::VPC", logicalId);
|
|
2717
|
+
this.props = props;
|
|
2718
|
+
}
|
|
2719
|
+
get id() {
|
|
2720
|
+
return ref(this.logicalId);
|
|
2721
|
+
}
|
|
2722
|
+
properties() {
|
|
2723
|
+
return {
|
|
2724
|
+
CidrBlock: this.props.cidrBlock.ip
|
|
2725
|
+
};
|
|
2726
|
+
}
|
|
2727
|
+
};
|
|
2728
|
+
var RouteTable = class extends Resource {
|
|
2729
|
+
constructor(logicalId, props) {
|
|
2730
|
+
super("AWS::EC2::RouteTable", logicalId);
|
|
2731
|
+
this.props = props;
|
|
2732
|
+
this.name = formatName(props.name || logicalId);
|
|
2733
|
+
}
|
|
2734
|
+
name;
|
|
2735
|
+
get id() {
|
|
2736
|
+
return ref(this.logicalId);
|
|
2737
|
+
}
|
|
2738
|
+
properties() {
|
|
2739
|
+
return {
|
|
2740
|
+
VpcId: this.props.vpcId,
|
|
2741
|
+
Tags: [{
|
|
2742
|
+
Key: "name",
|
|
2743
|
+
Value: this.name
|
|
2744
|
+
}]
|
|
2745
|
+
};
|
|
2746
|
+
}
|
|
2747
|
+
};
|
|
2748
|
+
var InternetGateway = class extends Resource {
|
|
2749
|
+
constructor(logicalId) {
|
|
2750
|
+
super("AWS::EC2::InternetGateway", logicalId);
|
|
2751
|
+
}
|
|
2752
|
+
get id() {
|
|
2753
|
+
return ref(this.logicalId);
|
|
2754
|
+
}
|
|
2755
|
+
properties() {
|
|
2756
|
+
return {};
|
|
2757
|
+
}
|
|
2758
|
+
};
|
|
2759
|
+
var VPCGatewayAttachment = class extends Resource {
|
|
2760
|
+
constructor(logicalId, props) {
|
|
2761
|
+
super("AWS::EC2::VPCGatewayAttachment", logicalId);
|
|
2762
|
+
this.props = props;
|
|
2763
|
+
}
|
|
2764
|
+
get id() {
|
|
2765
|
+
return ref(this.logicalId);
|
|
2766
|
+
}
|
|
2767
|
+
properties() {
|
|
2768
|
+
return {
|
|
2769
|
+
VpcId: this.props.vpcId,
|
|
2770
|
+
InternetGatewayId: this.props.internetGatewayId
|
|
2771
|
+
};
|
|
2772
|
+
}
|
|
2773
|
+
};
|
|
2774
|
+
var Route = class extends Resource {
|
|
2775
|
+
constructor(logicalId, props) {
|
|
2776
|
+
super("AWS::EC2::Route", logicalId);
|
|
2777
|
+
this.props = props;
|
|
2778
|
+
}
|
|
2779
|
+
get id() {
|
|
2780
|
+
return ref(this.logicalId);
|
|
2781
|
+
}
|
|
2782
|
+
properties() {
|
|
2783
|
+
return {
|
|
2784
|
+
GatewayId: this.props.gatewayId,
|
|
2785
|
+
RouteTableId: this.props.routeTableId,
|
|
2786
|
+
DestinationCidrBlock: this.props.destination.ip
|
|
2787
|
+
};
|
|
2788
|
+
}
|
|
2789
|
+
};
|
|
2790
|
+
var Subnet = class extends Resource {
|
|
2791
|
+
constructor(logicalId, props) {
|
|
2792
|
+
super("AWS::EC2::Subnet", logicalId);
|
|
2793
|
+
this.props = props;
|
|
2794
|
+
}
|
|
2795
|
+
get id() {
|
|
2796
|
+
return ref(this.logicalId);
|
|
2797
|
+
}
|
|
2798
|
+
properties() {
|
|
2799
|
+
return {
|
|
2800
|
+
VpcId: this.props.vpcId,
|
|
2801
|
+
CidrBlock: this.props.cidrBlock.ip,
|
|
2802
|
+
AvailabilityZone: this.props.availabilityZone
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
};
|
|
2806
|
+
var SubnetRouteTableAssociation = class extends Resource {
|
|
2807
|
+
constructor(logicalId, props) {
|
|
2808
|
+
super("AWS::EC2::SubnetRouteTableAssociation", logicalId);
|
|
2809
|
+
this.props = props;
|
|
2810
|
+
}
|
|
2811
|
+
get id() {
|
|
2812
|
+
return ref(this.logicalId);
|
|
2813
|
+
}
|
|
2814
|
+
properties() {
|
|
2815
|
+
return {
|
|
2816
|
+
SubnetId: this.props.subnetId,
|
|
2817
|
+
RouteTableId: this.props.routeTableId
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
};
|
|
2821
|
+
|
|
2822
|
+
// src/formation/resource/ec2/peer.ts
|
|
2823
|
+
var Peer = class {
|
|
2824
|
+
constructor(ip, type) {
|
|
2825
|
+
this.ip = ip;
|
|
2826
|
+
this.type = type;
|
|
2827
|
+
}
|
|
2828
|
+
static ipv4(cidrIp) {
|
|
2829
|
+
const cidrMatch = cidrIp.match(/^(\d{1,3}\.){3}\d{1,3}(\/\d+)?$/);
|
|
2830
|
+
if (!cidrMatch) {
|
|
2831
|
+
throw new Error(`Invalid IPv4 CIDR: "${cidrIp}"`);
|
|
2832
|
+
}
|
|
2833
|
+
if (!cidrMatch[2]) {
|
|
2834
|
+
throw new Error(`CIDR mask is missing in IPv4: "${cidrIp}". Did you mean "${cidrIp}/32"?`);
|
|
2835
|
+
}
|
|
2836
|
+
return new Peer(cidrIp, "v4");
|
|
2837
|
+
}
|
|
2838
|
+
static anyIpv4() {
|
|
2839
|
+
return new Peer("0.0.0.0/0", "v4");
|
|
2840
|
+
}
|
|
2841
|
+
static ipv6(cidrIpv6) {
|
|
2842
|
+
const cidrMatch = cidrIpv6.match(/^([\da-f]{0,4}:){2,7}([\da-f]{0,4})?(\/\d+)?$/);
|
|
2843
|
+
if (!cidrMatch) {
|
|
2844
|
+
throw new Error(`Invalid IPv6 CIDR: "${cidrIpv6}"`);
|
|
2845
|
+
}
|
|
2846
|
+
if (!cidrMatch[3]) {
|
|
2847
|
+
throw new Error(`CIDR mask is missing in IPv6: "${cidrIpv6}". Did you mean "${cidrIpv6}/128"?`);
|
|
2848
|
+
}
|
|
2849
|
+
return new Peer(cidrIpv6, "v6");
|
|
2850
|
+
}
|
|
2851
|
+
static anyIpv6() {
|
|
2852
|
+
return new Peer("::/0", "v6");
|
|
2853
|
+
}
|
|
2854
|
+
toRuleJson() {
|
|
2855
|
+
switch (this.type) {
|
|
2856
|
+
case "v4":
|
|
2857
|
+
return { CidrIp: this.ip };
|
|
2858
|
+
case "v6":
|
|
2859
|
+
return { CidrIpv6: this.ip };
|
|
2860
|
+
}
|
|
2861
|
+
}
|
|
2862
|
+
toString() {
|
|
2863
|
+
return this.ip;
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
|
|
2867
|
+
// src/plugins/vpc.ts
|
|
2868
|
+
var vpcPlugin = definePlugin({
|
|
2869
|
+
name: "vpc",
|
|
2870
|
+
// schema: z.object({
|
|
2871
|
+
// defaults: z.object({
|
|
2872
|
+
// vpc: z.boolean().default(false),
|
|
2873
|
+
// }).default({}),
|
|
2874
|
+
// }),
|
|
2875
|
+
onApp({ config, bootstrap: bootstrap2 }) {
|
|
2876
|
+
const vpc = new Vpc("main", {
|
|
2877
|
+
cidrBlock: Peer.ipv4("10.0.0.0/16")
|
|
2878
|
+
});
|
|
2879
|
+
const privateRouteTable = new RouteTable("private", {
|
|
2880
|
+
vpcId: vpc.id,
|
|
2881
|
+
name: "private"
|
|
2882
|
+
}).dependsOn(vpc);
|
|
2883
|
+
const publicRouteTable = new RouteTable("public", {
|
|
2884
|
+
vpcId: vpc.id,
|
|
2885
|
+
name: "public"
|
|
2886
|
+
}).dependsOn(vpc);
|
|
2887
|
+
const gateway = new InternetGateway("");
|
|
2888
|
+
const attachment = new VPCGatewayAttachment("", {
|
|
2889
|
+
vpcId: vpc.id,
|
|
2890
|
+
internetGatewayId: gateway.id
|
|
2891
|
+
}).dependsOn(vpc, gateway);
|
|
2892
|
+
const route = new Route("", {
|
|
2893
|
+
gatewayId: gateway.id,
|
|
2894
|
+
routeTableId: publicRouteTable.id,
|
|
2895
|
+
destination: Peer.anyIpv4()
|
|
2896
|
+
}).dependsOn(gateway, publicRouteTable);
|
|
2897
|
+
bootstrap2.export(`vpc-id`, vpc.id);
|
|
2898
|
+
bootstrap2.add(
|
|
2899
|
+
vpc,
|
|
2900
|
+
privateRouteTable,
|
|
2901
|
+
publicRouteTable,
|
|
2902
|
+
gateway,
|
|
2903
|
+
attachment,
|
|
2904
|
+
route
|
|
2905
|
+
);
|
|
2906
|
+
const zones = ["a", "b"];
|
|
2907
|
+
const tables = [privateRouteTable, publicRouteTable];
|
|
2908
|
+
let block = 0;
|
|
2909
|
+
for (const table of tables) {
|
|
2910
|
+
for (const i in zones) {
|
|
2911
|
+
const id = `${table.name}-${i}`;
|
|
2912
|
+
const subnet = new Subnet(id, {
|
|
2913
|
+
vpcId: vpc.id,
|
|
2914
|
+
cidrBlock: Peer.ipv4(`10.0.${block++}.0/24`),
|
|
2915
|
+
availabilityZone: config.region + zones[i]
|
|
2916
|
+
}).dependsOn(vpc);
|
|
2917
|
+
const association = new SubnetRouteTableAssociation(id, {
|
|
2918
|
+
routeTableId: table.id,
|
|
2919
|
+
subnetId: subnet.id
|
|
2920
|
+
}).dependsOn(subnet, table);
|
|
2921
|
+
bootstrap2.export(`${table.name}-subnet-${Number(i) + 1}`, subnet.id);
|
|
2922
|
+
bootstrap2.add(
|
|
2923
|
+
subnet,
|
|
2924
|
+
association
|
|
2925
|
+
);
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
});
|
|
2930
|
+
|
|
2931
|
+
// src/plugins/http.ts
|
|
2932
|
+
import { z as z17 } from "zod";
|
|
2933
|
+
|
|
2934
|
+
// src/formation/resource/ec2/security-group.ts
|
|
2935
|
+
var SecurityGroup = class extends Resource {
|
|
2936
|
+
constructor(logicalId, props) {
|
|
2937
|
+
super("AWS::EC2::SecurityGroup", logicalId);
|
|
2938
|
+
this.props = props;
|
|
2939
|
+
}
|
|
2940
|
+
ingress = [];
|
|
2941
|
+
egress = [];
|
|
2942
|
+
get id() {
|
|
2943
|
+
return ref(this.logicalId);
|
|
2944
|
+
}
|
|
2945
|
+
addIngressRule(peer, port, description) {
|
|
2946
|
+
this.ingress.push({
|
|
2947
|
+
peer,
|
|
2948
|
+
port,
|
|
2949
|
+
description
|
|
2950
|
+
});
|
|
2951
|
+
return this;
|
|
2952
|
+
}
|
|
2953
|
+
addEgressRule(peer, port, description) {
|
|
2954
|
+
this.egress.push({
|
|
2955
|
+
peer,
|
|
2956
|
+
port,
|
|
2957
|
+
description
|
|
2958
|
+
});
|
|
2959
|
+
return this;
|
|
2960
|
+
}
|
|
2961
|
+
properties() {
|
|
2962
|
+
return {
|
|
2963
|
+
VpcId: this.props.vpcId,
|
|
2964
|
+
GroupName: this.logicalId,
|
|
2965
|
+
GroupDescription: this.props.description,
|
|
2966
|
+
SecurityGroupIngress: this.ingress.map((rule) => ({
|
|
2967
|
+
Description: rule.description || "",
|
|
2968
|
+
...rule.port.toRuleJson(),
|
|
2969
|
+
...rule.peer.toRuleJson()
|
|
2970
|
+
})),
|
|
2971
|
+
SecurityGroupEgress: this.egress.map((rule) => ({
|
|
2972
|
+
Description: rule.description || "",
|
|
2973
|
+
...rule.port.toRuleJson(),
|
|
2974
|
+
...rule.peer.toRuleJson()
|
|
2975
|
+
}))
|
|
2976
|
+
};
|
|
2977
|
+
}
|
|
2978
|
+
};
|
|
2979
|
+
|
|
2980
|
+
// src/formation/resource/ec2/port.ts
|
|
2981
|
+
var Port = class {
|
|
2982
|
+
static tcp(port) {
|
|
2983
|
+
return new Port({
|
|
2984
|
+
protocol: "tcp" /* TCP */,
|
|
2985
|
+
from: port,
|
|
2986
|
+
to: port
|
|
2987
|
+
});
|
|
2988
|
+
}
|
|
2989
|
+
static tcpRange(startPort, endPort) {
|
|
2990
|
+
return new Port({
|
|
2991
|
+
protocol: "tcp" /* TCP */,
|
|
2992
|
+
from: startPort,
|
|
2993
|
+
to: endPort
|
|
2994
|
+
});
|
|
2995
|
+
}
|
|
2996
|
+
static allTcp() {
|
|
2997
|
+
return new Port({
|
|
2998
|
+
protocol: "tcp" /* TCP */,
|
|
2999
|
+
from: 0,
|
|
3000
|
+
to: 65535
|
|
3001
|
+
});
|
|
3002
|
+
}
|
|
3003
|
+
static allTraffic() {
|
|
3004
|
+
return new Port({
|
|
3005
|
+
protocol: "-1" /* ALL */
|
|
3006
|
+
});
|
|
3007
|
+
}
|
|
3008
|
+
protocol;
|
|
3009
|
+
from;
|
|
3010
|
+
to;
|
|
3011
|
+
constructor(props) {
|
|
3012
|
+
this.protocol = props.protocol;
|
|
3013
|
+
this.from = props.from;
|
|
3014
|
+
this.to = props.to;
|
|
3015
|
+
}
|
|
3016
|
+
toRuleJson() {
|
|
3017
|
+
return {
|
|
3018
|
+
IpProtocol: this.protocol,
|
|
3019
|
+
FromPort: this.from,
|
|
3020
|
+
ToPort: this.to
|
|
3021
|
+
};
|
|
3022
|
+
}
|
|
3023
|
+
};
|
|
3024
|
+
|
|
3025
|
+
// src/formation/resource/elb/load-balancer.ts
|
|
3026
|
+
var LoadBalancer = class extends Resource {
|
|
3027
|
+
constructor(logicalId, props) {
|
|
3028
|
+
super("AWS::ElasticLoadBalancingV2::LoadBalancer", logicalId);
|
|
3029
|
+
this.props = props;
|
|
3030
|
+
this.name = this.props.name || logicalId;
|
|
3031
|
+
}
|
|
3032
|
+
name;
|
|
3033
|
+
get arn() {
|
|
3034
|
+
return ref(this.logicalId);
|
|
3035
|
+
}
|
|
3036
|
+
get dnsName() {
|
|
3037
|
+
return getAtt(this.logicalId, "DNSName");
|
|
3038
|
+
}
|
|
3039
|
+
get hostedZoneId() {
|
|
3040
|
+
return getAtt(this.logicalId, "CanonicalHostedZoneID");
|
|
3041
|
+
}
|
|
3042
|
+
properties() {
|
|
3043
|
+
return {
|
|
3044
|
+
Name: this.name,
|
|
3045
|
+
Type: this.props.type,
|
|
3046
|
+
Scheme: this.props.schema || "internet-facing",
|
|
3047
|
+
SecurityGroups: this.props.securityGroups,
|
|
3048
|
+
Subnets: this.props.subnets
|
|
3049
|
+
};
|
|
3050
|
+
}
|
|
3051
|
+
};
|
|
3052
|
+
|
|
3053
|
+
// src/formation/resource/elb/listener.ts
|
|
3054
|
+
import { constantCase as constantCase4 } from "change-case";
|
|
3055
|
+
var Listener = class extends Resource {
|
|
3056
|
+
constructor(logicalId, props) {
|
|
3057
|
+
super("AWS::ElasticLoadBalancingV2::Listener", logicalId);
|
|
3058
|
+
this.props = props;
|
|
3059
|
+
}
|
|
3060
|
+
get id() {
|
|
3061
|
+
return ref(this.logicalId);
|
|
3062
|
+
}
|
|
3063
|
+
get arn() {
|
|
3064
|
+
return getAtt(this.logicalId, "ListenerArn");
|
|
3065
|
+
}
|
|
3066
|
+
properties() {
|
|
3067
|
+
return {
|
|
3068
|
+
LoadBalancerArn: this.props.loadBalancerArn,
|
|
3069
|
+
Port: this.props.port,
|
|
3070
|
+
Protocol: constantCase4(this.props.protocol),
|
|
3071
|
+
Certificates: this.props.certificates.map((arn) => ({
|
|
3072
|
+
CertificateArn: arn
|
|
3073
|
+
})),
|
|
3074
|
+
...this.attr("DefaultActions", this.props.defaultActions?.map((action) => action.toJSON()))
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
};
|
|
3078
|
+
var ListenerAction = class {
|
|
3079
|
+
constructor(props) {
|
|
3080
|
+
this.props = props;
|
|
3081
|
+
}
|
|
3082
|
+
static fixedResponse(statusCode, props = {}) {
|
|
3083
|
+
return new ListenerAction({
|
|
3084
|
+
type: "fixed-response",
|
|
3085
|
+
fixedResponse: {
|
|
3086
|
+
statusCode,
|
|
3087
|
+
...props
|
|
3088
|
+
}
|
|
3089
|
+
});
|
|
3090
|
+
}
|
|
3091
|
+
static forward(targets) {
|
|
3092
|
+
return new ListenerAction({
|
|
3093
|
+
type: "forward",
|
|
3094
|
+
forward: {
|
|
3095
|
+
targetGroups: targets
|
|
3096
|
+
}
|
|
3097
|
+
});
|
|
3098
|
+
}
|
|
3099
|
+
toJSON() {
|
|
3100
|
+
return {
|
|
3101
|
+
// AuthenticateCognitoConfig: AuthenticateCognitoConfig,
|
|
3102
|
+
// AuthenticateOidcConfig: AuthenticateOidcConfig,
|
|
3103
|
+
// RedirectConfig: RedirectConfig,
|
|
3104
|
+
Type: this.props.type,
|
|
3105
|
+
// Order: Integer,
|
|
3106
|
+
...this.props.type === "fixed-response" ? {
|
|
3107
|
+
FixedResponseConfig: {
|
|
3108
|
+
StatusCode: this.props.fixedResponse.statusCode,
|
|
3109
|
+
...this.props.fixedResponse.contentType ? {
|
|
3110
|
+
ContentType: this.props.fixedResponse.contentType
|
|
3111
|
+
} : {},
|
|
3112
|
+
...this.props.fixedResponse.messageBody ? {
|
|
3113
|
+
MessageBody: this.props.fixedResponse.messageBody
|
|
3114
|
+
} : {}
|
|
3115
|
+
}
|
|
3116
|
+
} : {},
|
|
3117
|
+
...this.props.type === "forward" ? {
|
|
3118
|
+
ForwardConfig: {
|
|
3119
|
+
TargetGroups: this.props.forward.targetGroups.map((target) => ({
|
|
3120
|
+
TargetGroupArn: target
|
|
3121
|
+
}))
|
|
3122
|
+
}
|
|
3123
|
+
} : {}
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
3126
|
+
};
|
|
3127
|
+
|
|
3128
|
+
// src/formation/resource/elb/listener-rule.ts
|
|
3129
|
+
var ListenerRule = class extends Resource {
|
|
3130
|
+
constructor(logicalId, props) {
|
|
3131
|
+
super("AWS::ElasticLoadBalancingV2::ListenerRule", logicalId);
|
|
3132
|
+
this.props = props;
|
|
3133
|
+
}
|
|
3134
|
+
get id() {
|
|
3135
|
+
return ref(this.logicalId);
|
|
3136
|
+
}
|
|
3137
|
+
get arn() {
|
|
3138
|
+
return getAtt(this.logicalId, "ListenerArn");
|
|
3139
|
+
}
|
|
3140
|
+
properties() {
|
|
3141
|
+
return {
|
|
3142
|
+
ListenerArn: this.props.listenerArn,
|
|
3143
|
+
Priority: this.props.priority,
|
|
3144
|
+
Conditions: this.props.conditions.map((condition) => condition.toJSON()),
|
|
3145
|
+
Actions: this.props.actions.map((action) => action.toJSON())
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
};
|
|
3149
|
+
var ListenerCondition = class {
|
|
3150
|
+
constructor(props) {
|
|
3151
|
+
this.props = props;
|
|
3152
|
+
}
|
|
3153
|
+
static httpRequestMethods(methods) {
|
|
3154
|
+
return new ListenerCondition({
|
|
3155
|
+
field: "http-request-method",
|
|
3156
|
+
methods
|
|
3157
|
+
});
|
|
3158
|
+
}
|
|
3159
|
+
static pathPatterns(paths) {
|
|
3160
|
+
return new ListenerCondition({
|
|
3161
|
+
field: "path-pattern",
|
|
3162
|
+
paths
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
toJSON() {
|
|
3166
|
+
return {
|
|
3167
|
+
Field: this.props.field,
|
|
3168
|
+
...this.props.field === "http-request-method" ? {
|
|
3169
|
+
HttpRequestMethodConfig: {
|
|
3170
|
+
Values: this.props.methods
|
|
3171
|
+
}
|
|
3172
|
+
} : {},
|
|
3173
|
+
...this.props.field === "path-pattern" ? {
|
|
3174
|
+
PathPatternConfig: {
|
|
3175
|
+
Values: this.props.paths
|
|
3176
|
+
}
|
|
3177
|
+
} : {}
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
3180
|
+
};
|
|
3181
|
+
|
|
3182
|
+
// src/formation/resource/elb/target-group.ts
|
|
3183
|
+
var TargetGroup = class extends Resource {
|
|
3184
|
+
constructor(logicalId, props) {
|
|
3185
|
+
super("AWS::ElasticLoadBalancingV2::TargetGroup", logicalId);
|
|
3186
|
+
this.props = props;
|
|
3187
|
+
this.name = formatName(this.props.name || logicalId);
|
|
3188
|
+
}
|
|
3189
|
+
name;
|
|
3190
|
+
get arn() {
|
|
3191
|
+
return ref(this.logicalId);
|
|
3192
|
+
}
|
|
3193
|
+
get fullName() {
|
|
3194
|
+
return getAtt(this.logicalId, "TargetGroupFullName");
|
|
3195
|
+
}
|
|
3196
|
+
properties() {
|
|
3197
|
+
return {
|
|
3198
|
+
Name: this.name,
|
|
3199
|
+
TargetType: this.props.type,
|
|
3200
|
+
Targets: this.props.targets.map((target) => ({
|
|
3201
|
+
Id: target
|
|
3202
|
+
}))
|
|
3203
|
+
};
|
|
3204
|
+
}
|
|
3205
|
+
};
|
|
3206
|
+
|
|
3207
|
+
// src/formation/resource/lambda/event-source/elb.ts
|
|
3208
|
+
var ElbEventSource = class extends Group {
|
|
3209
|
+
constructor(id, lambda, props) {
|
|
3210
|
+
const name = formatName(id);
|
|
3211
|
+
const permission = new Permission2(id, {
|
|
3212
|
+
action: "lambda:InvokeFunction",
|
|
3213
|
+
principal: "elasticloadbalancing.amazonaws.com",
|
|
3214
|
+
functionArn: lambda.arn,
|
|
3215
|
+
sourceArn: sub("arn:${AWS::Partition}:elasticloadbalancing:${AWS::Region}:${AWS::AccountId}:targetgroup/${name}/*", {
|
|
3216
|
+
name
|
|
3217
|
+
})
|
|
3218
|
+
}).dependsOn(lambda);
|
|
3219
|
+
const target = new TargetGroup(id, {
|
|
3220
|
+
name,
|
|
3221
|
+
type: "lambda",
|
|
3222
|
+
targets: [lambda.arn]
|
|
3223
|
+
}).dependsOn(lambda, permission);
|
|
3224
|
+
const rule = new ListenerRule(id, {
|
|
3225
|
+
listenerArn: props.listenerArn,
|
|
3226
|
+
priority: props.priority,
|
|
3227
|
+
conditions: props.conditions,
|
|
3228
|
+
actions: [
|
|
3229
|
+
ListenerAction.forward([target.arn])
|
|
3230
|
+
]
|
|
3231
|
+
}).dependsOn(target);
|
|
3232
|
+
super([target, rule, permission]);
|
|
3233
|
+
}
|
|
3234
|
+
};
|
|
3235
|
+
|
|
3236
|
+
// src/plugins/http.ts
|
|
3237
|
+
var RouteSchema = z17.custom((route) => {
|
|
3238
|
+
return z17.string().regex(/^(POST|GET|PUT|DELETE|HEAD|OPTIONS)(\s\/[a-z0-9\+\_\-\/]*)$/ig).safeParse(route).success;
|
|
3239
|
+
}, "Invalid route");
|
|
3240
|
+
var parseRoute = (route) => {
|
|
3241
|
+
const [method, ...paths] = route.split(" ");
|
|
3242
|
+
const path = paths.join(" ");
|
|
3243
|
+
return { method, path };
|
|
3244
|
+
};
|
|
3245
|
+
var strToInt = (str) => {
|
|
3246
|
+
return parseInt(Buffer.from(str, "utf8").toString("hex"), 16);
|
|
3247
|
+
};
|
|
3248
|
+
var generatePriority = (stackName, route) => {
|
|
3249
|
+
const start = strToInt(stackName) % 500 + 1;
|
|
3250
|
+
const end = strToInt(route) % 100;
|
|
3251
|
+
const priority = start + "" + end;
|
|
3252
|
+
return parseInt(priority, 10);
|
|
3253
|
+
};
|
|
3254
|
+
var httpPlugin = definePlugin({
|
|
3255
|
+
name: "http",
|
|
3256
|
+
schema: z17.object({
|
|
3257
|
+
defaults: z17.object({
|
|
3258
|
+
/** Define your global http api's.
|
|
3259
|
+
* @example
|
|
3260
|
+
* {
|
|
3261
|
+
* http: {
|
|
3262
|
+
* HTTP_API_NAME: {
|
|
3263
|
+
* domain: 'example.com',
|
|
3264
|
+
* subDomain: 'api',
|
|
3265
|
+
* }
|
|
3266
|
+
* }
|
|
3267
|
+
* }
|
|
3268
|
+
*/
|
|
3269
|
+
http: z17.record(
|
|
3270
|
+
ResourceIdSchema,
|
|
3271
|
+
z17.object({
|
|
3272
|
+
/** The domain to link your api with. */
|
|
3273
|
+
domain: z17.string(),
|
|
3274
|
+
subDomain: z17.string().optional()
|
|
3275
|
+
})
|
|
3276
|
+
).optional()
|
|
3277
|
+
}).default({}),
|
|
3278
|
+
stacks: z17.object({
|
|
3279
|
+
/** Define routes in your stack for your global http api.
|
|
3280
|
+
* @example
|
|
3281
|
+
* {
|
|
3282
|
+
* http: {
|
|
3283
|
+
* HTTP_API_NAME: {
|
|
3284
|
+
* 'GET /': 'index.ts',
|
|
3285
|
+
* 'POST /posts': 'create-post.ts',
|
|
3286
|
+
* }
|
|
3287
|
+
* }
|
|
3288
|
+
* }
|
|
3289
|
+
*/
|
|
3290
|
+
http: z17.record(
|
|
3291
|
+
ResourceIdSchema,
|
|
3292
|
+
z17.record(RouteSchema, FunctionSchema)
|
|
3293
|
+
).optional()
|
|
3294
|
+
}).array()
|
|
3295
|
+
}),
|
|
3296
|
+
onApp({ config, bootstrap: bootstrap2, usEastBootstrap }) {
|
|
3297
|
+
if (Object.keys(config.defaults?.http || {}).length === 0) {
|
|
3298
|
+
return;
|
|
3299
|
+
}
|
|
3300
|
+
const vpcId = bootstrap2.get("vpc-id");
|
|
3301
|
+
const securityGroup = new SecurityGroup("http", {
|
|
3302
|
+
description: "http security group",
|
|
3303
|
+
vpcId
|
|
3304
|
+
});
|
|
3305
|
+
securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(443));
|
|
3306
|
+
securityGroup.addIngressRule(Peer.anyIpv6(), Port.tcp(443));
|
|
3307
|
+
bootstrap2.add(securityGroup);
|
|
3308
|
+
for (const [id, props] of Object.entries(config.defaults?.http || {})) {
|
|
3309
|
+
const loadBalancer = new LoadBalancer(id, {
|
|
3310
|
+
name: `${config.name}-${id}`,
|
|
3311
|
+
type: "application",
|
|
3312
|
+
securityGroups: [securityGroup.id],
|
|
3313
|
+
subnets: [
|
|
3314
|
+
bootstrap2.get("public-subnet-1"),
|
|
3315
|
+
bootstrap2.get("public-subnet-2")
|
|
3316
|
+
]
|
|
3317
|
+
}).dependsOn(securityGroup);
|
|
3318
|
+
const listener = new Listener(id, {
|
|
3319
|
+
loadBalancerArn: loadBalancer.arn,
|
|
3320
|
+
port: 443,
|
|
3321
|
+
protocol: "https",
|
|
3322
|
+
certificates: [
|
|
3323
|
+
bootstrap2.get(`certificate-${props.domain}-arn`)
|
|
3324
|
+
],
|
|
3325
|
+
defaultActions: [
|
|
3326
|
+
ListenerAction.fixedResponse(404, {
|
|
3327
|
+
contentType: "application/json",
|
|
3328
|
+
messageBody: JSON.stringify({
|
|
3329
|
+
message: "Route not found"
|
|
3330
|
+
})
|
|
3331
|
+
})
|
|
3332
|
+
]
|
|
3333
|
+
}).dependsOn(loadBalancer);
|
|
3334
|
+
const record = new RecordSet(`${id}-http`, {
|
|
3335
|
+
hostedZoneId: usEastBootstrap.import(`hosted-zone-${props.domain}-id`),
|
|
3336
|
+
name: props.subDomain ? `${props.subDomain}.${props.domain}` : props.domain,
|
|
3337
|
+
type: "A",
|
|
3338
|
+
alias: {
|
|
3339
|
+
hostedZoneId: loadBalancer.hostedZoneId,
|
|
3340
|
+
dnsName: loadBalancer.dnsName
|
|
3341
|
+
}
|
|
3342
|
+
}).dependsOn(loadBalancer);
|
|
3343
|
+
bootstrap2.add(loadBalancer, listener, record).export(`http-${id}-listener-arn`, listener.arn);
|
|
3344
|
+
}
|
|
3345
|
+
},
|
|
3346
|
+
onStack(ctx) {
|
|
3347
|
+
const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
|
|
3348
|
+
for (const [id, routes] of Object.entries(stackConfig.http || {})) {
|
|
3349
|
+
for (const [route, props] of Object.entries(routes)) {
|
|
3350
|
+
const { method, path } = parseRoute(route);
|
|
3351
|
+
const lambda = toLambdaFunction(ctx, `http-${id}`, props);
|
|
3352
|
+
const source = new ElbEventSource(`http-${id}-${route}`, lambda, {
|
|
3353
|
+
listenerArn: bootstrap2.import(`http-${id}-listener-arn`),
|
|
3354
|
+
priority: generatePriority(stackConfig.name, route),
|
|
3355
|
+
conditions: [
|
|
3356
|
+
ListenerCondition.httpRequestMethods([method]),
|
|
3357
|
+
ListenerCondition.pathPatterns([path])
|
|
3358
|
+
]
|
|
3359
|
+
});
|
|
3360
|
+
stack.add(lambda, source);
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
});
|
|
3365
|
+
|
|
3366
|
+
// src/plugins/search.ts
|
|
3367
|
+
import { z as z18 } from "zod";
|
|
3368
|
+
|
|
3369
|
+
// src/formation/resource/open-search-serverless/collection.ts
|
|
3370
|
+
var Collection = class extends Resource {
|
|
3371
|
+
constructor(logicalId, props) {
|
|
3372
|
+
super("AWS::OpenSearchServerless::Collection", logicalId);
|
|
3373
|
+
this.props = props;
|
|
3374
|
+
this.name = this.props.name || logicalId;
|
|
3375
|
+
}
|
|
3376
|
+
name;
|
|
3377
|
+
get id() {
|
|
3378
|
+
return ref(this.logicalId);
|
|
3379
|
+
}
|
|
3380
|
+
get arn() {
|
|
3381
|
+
return getAtt(this.logicalId, "Arn");
|
|
3382
|
+
}
|
|
3383
|
+
get endpoint() {
|
|
3384
|
+
return getAtt(this.logicalId, "CollectionEndpoint");
|
|
3385
|
+
}
|
|
3386
|
+
properties() {
|
|
3387
|
+
return {
|
|
3388
|
+
Name: this.name,
|
|
3389
|
+
Type: this.props.type.toUpperCase(),
|
|
3390
|
+
...this.attr("Description", this.props.description)
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3393
|
+
};
|
|
3394
|
+
|
|
3395
|
+
// src/plugins/search.ts
|
|
3396
|
+
var searchPlugin = definePlugin({
|
|
3397
|
+
name: "search",
|
|
3398
|
+
schema: z18.object({
|
|
3399
|
+
stacks: z18.object({
|
|
3400
|
+
searchs: z18.array(ResourceIdSchema).optional()
|
|
3401
|
+
}).array()
|
|
3402
|
+
}),
|
|
3403
|
+
onStack({ config, stack, stackConfig, bind }) {
|
|
3404
|
+
for (const id of stackConfig.searchs || []) {
|
|
3405
|
+
const collection = new Collection(id, {
|
|
3406
|
+
name: `${config.name}-${stack.name}-${id}`,
|
|
3407
|
+
type: "search"
|
|
3408
|
+
});
|
|
3409
|
+
bind((lambda) => {
|
|
3410
|
+
lambda.addPermissions({
|
|
3411
|
+
actions: ["aoss:APIAccessAll"],
|
|
3412
|
+
resources: [collection.arn]
|
|
3413
|
+
});
|
|
3414
|
+
});
|
|
3415
|
+
}
|
|
3416
|
+
}
|
|
3417
|
+
});
|
|
2287
3418
|
|
|
2288
3419
|
// src/plugins/index.ts
|
|
2289
3420
|
var defaultPlugins = [
|
|
2290
3421
|
extendPlugin,
|
|
3422
|
+
vpcPlugin,
|
|
2291
3423
|
functionPlugin,
|
|
2292
3424
|
cronPlugin,
|
|
2293
3425
|
queuePlugin,
|
|
@@ -2295,10 +3427,11 @@ var defaultPlugins = [
|
|
|
2295
3427
|
storePlugin,
|
|
2296
3428
|
topicPlugin,
|
|
2297
3429
|
pubsubPlugin,
|
|
2298
|
-
|
|
3430
|
+
searchPlugin,
|
|
2299
3431
|
domainPlugin,
|
|
2300
|
-
graphqlPlugin
|
|
2301
|
-
|
|
3432
|
+
graphqlPlugin,
|
|
3433
|
+
httpPlugin,
|
|
3434
|
+
onFailurePlugin
|
|
2302
3435
|
];
|
|
2303
3436
|
|
|
2304
3437
|
// src/formation/app.ts
|
|
@@ -2325,23 +3458,6 @@ var App = class {
|
|
|
2325
3458
|
// }
|
|
2326
3459
|
};
|
|
2327
3460
|
|
|
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
3461
|
// src/custom/global-export/handler.ts
|
|
2346
3462
|
var globalExportsHandlerCode = (
|
|
2347
3463
|
/* JS */
|
|
@@ -2436,7 +3552,7 @@ var extendWithGlobalExports = (appName, importable, exportable) => {
|
|
|
2436
3552
|
region: importable.region
|
|
2437
3553
|
}
|
|
2438
3554
|
});
|
|
2439
|
-
exportable.add(crossRegionExports);
|
|
3555
|
+
exportable.add(lambda, crossRegionExports);
|
|
2440
3556
|
}
|
|
2441
3557
|
return crossRegionExports.getAtt(name);
|
|
2442
3558
|
};
|
|
@@ -2497,6 +3613,20 @@ var toApp = async (config, filters) => {
|
|
|
2497
3613
|
app.add(stack);
|
|
2498
3614
|
stacks.push({ stack, config: stackConfig });
|
|
2499
3615
|
}
|
|
3616
|
+
for (const plugin of plugins) {
|
|
3617
|
+
for (const stack of app.stacks) {
|
|
3618
|
+
for (const resource of stack) {
|
|
3619
|
+
plugin.onResource?.({
|
|
3620
|
+
config,
|
|
3621
|
+
app,
|
|
3622
|
+
stack,
|
|
3623
|
+
bootstrap: bootstrap2,
|
|
3624
|
+
usEastBootstrap,
|
|
3625
|
+
resource
|
|
3626
|
+
});
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
}
|
|
2500
3630
|
const functions = app.find(Function);
|
|
2501
3631
|
for (const bind2 of bindings) {
|
|
2502
3632
|
for (const fn of functions) {
|
|
@@ -2543,17 +3673,17 @@ var getCredentials = (profile) => {
|
|
|
2543
3673
|
};
|
|
2544
3674
|
|
|
2545
3675
|
// src/schema/app.ts
|
|
2546
|
-
import { z as
|
|
3676
|
+
import { z as z22 } from "zod";
|
|
2547
3677
|
|
|
2548
3678
|
// src/schema/stack.ts
|
|
2549
|
-
import { z as
|
|
2550
|
-
var StackSchema =
|
|
3679
|
+
import { z as z19 } from "zod";
|
|
3680
|
+
var StackSchema = z19.object({
|
|
2551
3681
|
name: ResourceIdSchema,
|
|
2552
|
-
depends:
|
|
3682
|
+
depends: z19.array(z19.lazy(() => StackSchema)).optional()
|
|
2553
3683
|
});
|
|
2554
3684
|
|
|
2555
3685
|
// src/schema/region.ts
|
|
2556
|
-
import { z as
|
|
3686
|
+
import { z as z20 } from "zod";
|
|
2557
3687
|
var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
|
|
2558
3688
|
var AF = ["af-south-1"];
|
|
2559
3689
|
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 +3700,91 @@ var regions = [
|
|
|
2570
3700
|
...ME,
|
|
2571
3701
|
...SA
|
|
2572
3702
|
];
|
|
2573
|
-
var RegionSchema =
|
|
3703
|
+
var RegionSchema = z20.enum(regions);
|
|
2574
3704
|
|
|
2575
3705
|
// src/schema/plugin.ts
|
|
2576
|
-
import { z as
|
|
2577
|
-
var PluginSchema =
|
|
2578
|
-
name:
|
|
2579
|
-
schema:
|
|
3706
|
+
import { z as z21 } from "zod";
|
|
3707
|
+
var PluginSchema = z21.object({
|
|
3708
|
+
name: z21.string(),
|
|
3709
|
+
schema: z21.custom().optional(),
|
|
2580
3710
|
// depends: z.array(z.lazy(() => PluginSchema)).optional(),
|
|
2581
|
-
|
|
2582
|
-
onStack:
|
|
2583
|
-
|
|
3711
|
+
onApp: z21.function().returns(z21.void()).optional(),
|
|
3712
|
+
onStack: z21.function().returns(z21.any()).optional(),
|
|
3713
|
+
onResource: z21.function().returns(z21.any()).optional()
|
|
2584
3714
|
// bind: z.function().optional(),
|
|
2585
3715
|
});
|
|
2586
3716
|
|
|
2587
3717
|
// src/schema/app.ts
|
|
2588
|
-
var AppSchema =
|
|
3718
|
+
var AppSchema = z22.object({
|
|
3719
|
+
/** App name */
|
|
2589
3720
|
name: ResourceIdSchema,
|
|
3721
|
+
/** The AWS region to deploy to. */
|
|
2590
3722
|
region: RegionSchema,
|
|
2591
|
-
profile
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
3723
|
+
/** The AWS profile to deploy to. */
|
|
3724
|
+
profile: z22.string(),
|
|
3725
|
+
/** The deployment stage.
|
|
3726
|
+
* @default 'prod'
|
|
3727
|
+
*/
|
|
3728
|
+
stage: z22.string().regex(/[a-z]+/).default("prod"),
|
|
3729
|
+
/** Default properties. */
|
|
3730
|
+
defaults: z22.object({}).default({}),
|
|
3731
|
+
/** The application stacks. */
|
|
3732
|
+
stacks: z22.array(StackSchema).min(1).refine((stacks) => {
|
|
2595
3733
|
const unique = new Set(stacks.map((stack) => stack.name));
|
|
2596
3734
|
return unique.size === stacks.length;
|
|
2597
3735
|
}, "Must be an array of unique stacks"),
|
|
2598
|
-
plugins
|
|
3736
|
+
/** Custom plugins. */
|
|
3737
|
+
plugins: z22.array(PluginSchema).optional()
|
|
2599
3738
|
});
|
|
2600
3739
|
|
|
2601
3740
|
// src/util/import.ts
|
|
2602
3741
|
import { transformFile } from "@swc/core";
|
|
2603
3742
|
import { dirname, join as join2 } from "path";
|
|
2604
|
-
import { lstat, mkdir, writeFile } from "fs/promises";
|
|
3743
|
+
import { lstat as lstat2, mkdir, writeFile } from "fs/promises";
|
|
2605
3744
|
|
|
2606
3745
|
// src/util/path.ts
|
|
2607
|
-
import {
|
|
2608
|
-
|
|
2609
|
-
var
|
|
2610
|
-
var
|
|
2611
|
-
|
|
2612
|
-
|
|
3746
|
+
import { lstat } from "fs/promises";
|
|
3747
|
+
import { join, normalize } from "path";
|
|
3748
|
+
var root = process.cwd();
|
|
3749
|
+
var directories = {
|
|
3750
|
+
root,
|
|
3751
|
+
get output() {
|
|
3752
|
+
return join(this.root, ".awsless");
|
|
3753
|
+
},
|
|
3754
|
+
get cache() {
|
|
3755
|
+
return join(this.output, "cache");
|
|
3756
|
+
},
|
|
3757
|
+
get asset() {
|
|
3758
|
+
return join(this.output, "asset");
|
|
3759
|
+
},
|
|
3760
|
+
get template() {
|
|
3761
|
+
return join(this.output, "template");
|
|
3762
|
+
}
|
|
3763
|
+
};
|
|
3764
|
+
var setRoot = (path = root) => {
|
|
3765
|
+
directories.root = path;
|
|
3766
|
+
};
|
|
3767
|
+
var findRootDir = async (path, configFile, level = 5) => {
|
|
3768
|
+
if (!level) {
|
|
3769
|
+
throw new TypeError("No awsless project found");
|
|
3770
|
+
}
|
|
3771
|
+
const file = join(path, configFile);
|
|
3772
|
+
const exists = await fileExist(file);
|
|
3773
|
+
if (exists) {
|
|
3774
|
+
return path;
|
|
3775
|
+
}
|
|
3776
|
+
return findRootDir(normalize(join(path, "..")), configFile, level - 1);
|
|
3777
|
+
};
|
|
3778
|
+
var fileExist = async (file) => {
|
|
3779
|
+
try {
|
|
3780
|
+
const stat = await lstat(file);
|
|
3781
|
+
if (stat.isFile()) {
|
|
3782
|
+
return true;
|
|
3783
|
+
}
|
|
3784
|
+
} catch (error) {
|
|
3785
|
+
}
|
|
3786
|
+
return false;
|
|
3787
|
+
};
|
|
2613
3788
|
|
|
2614
3789
|
// src/util/import.ts
|
|
2615
3790
|
var resolveFileNameExtension = async (path) => {
|
|
@@ -2624,7 +3799,7 @@ var resolveFileNameExtension = async (path) => {
|
|
|
2624
3799
|
const file = path.replace(/\.js$/, "") + option;
|
|
2625
3800
|
let stat;
|
|
2626
3801
|
try {
|
|
2627
|
-
stat = await
|
|
3802
|
+
stat = await lstat2(file);
|
|
2628
3803
|
} catch (error) {
|
|
2629
3804
|
continue;
|
|
2630
3805
|
}
|
|
@@ -2635,11 +3810,11 @@ var resolveFileNameExtension = async (path) => {
|
|
|
2635
3810
|
throw new Error(`Failed to load file: ${path}`);
|
|
2636
3811
|
};
|
|
2637
3812
|
var resolveDir = (path) => {
|
|
2638
|
-
return dirname(path).replace(
|
|
3813
|
+
return dirname(path).replace(directories.root + "/", "");
|
|
2639
3814
|
};
|
|
2640
3815
|
var importFile = async (path) => {
|
|
2641
3816
|
const load = async (file) => {
|
|
2642
|
-
debug("Load file", file);
|
|
3817
|
+
debug("Load file:", style.info(file));
|
|
2643
3818
|
let { code: code2 } = await transformFile(file, {
|
|
2644
3819
|
isModule: true
|
|
2645
3820
|
});
|
|
@@ -2659,16 +3834,22 @@ var importFile = async (path) => {
|
|
|
2659
3834
|
return code2;
|
|
2660
3835
|
};
|
|
2661
3836
|
const code = await load(path);
|
|
2662
|
-
const outputFile = join2(
|
|
2663
|
-
await mkdir(
|
|
3837
|
+
const outputFile = join2(directories.cache, "config.js");
|
|
3838
|
+
await mkdir(directories.cache, { recursive: true });
|
|
2664
3839
|
await writeFile(outputFile, code);
|
|
3840
|
+
debug("Save config file:", style.info(outputFile));
|
|
2665
3841
|
return import(outputFile);
|
|
2666
3842
|
};
|
|
2667
3843
|
|
|
2668
3844
|
// src/config.ts
|
|
2669
3845
|
var importConfig = async (options) => {
|
|
3846
|
+
debug("Find the root directory");
|
|
3847
|
+
const configFile = options.configFile || "awsless.config.ts";
|
|
3848
|
+
const root2 = await findRootDir(process.cwd(), configFile);
|
|
3849
|
+
setRoot(root2);
|
|
3850
|
+
debug("CWD:", style.info(root2));
|
|
2670
3851
|
debug("Import config file");
|
|
2671
|
-
const fileName = join3(
|
|
3852
|
+
const fileName = join3(root2, configFile);
|
|
2672
3853
|
const module = await importFile(fileName);
|
|
2673
3854
|
const appConfig = typeof module.default === "function" ? await module.default(options) : module.default;
|
|
2674
3855
|
debug("Validate config file");
|
|
@@ -3242,7 +4423,7 @@ var assetBuilder = (app) => {
|
|
|
3242
4423
|
derive([details], (details2) => {
|
|
3243
4424
|
return Object.entries(details2).map(([key, value]) => {
|
|
3244
4425
|
return `${style.label(key)} ${value}`;
|
|
3245
|
-
}).join("
|
|
4426
|
+
}).join(style.placeholder(" \u2500 "));
|
|
3246
4427
|
}),
|
|
3247
4428
|
br()
|
|
3248
4429
|
]);
|
|
@@ -3250,7 +4431,7 @@ var assetBuilder = (app) => {
|
|
|
3250
4431
|
const timer = createTimer();
|
|
3251
4432
|
const data = await asset.build({
|
|
3252
4433
|
async write(file, data2) {
|
|
3253
|
-
const fullpath = join4(
|
|
4434
|
+
const fullpath = join4(directories.asset, asset.type, app.name, stack.name, asset.id, file);
|
|
3254
4435
|
const basepath = dirname2(fullpath);
|
|
3255
4436
|
await mkdir2(basepath, { recursive: true });
|
|
3256
4437
|
await writeFile2(fullpath, data2);
|
|
@@ -3274,9 +4455,9 @@ import { mkdir as mkdir3, rm } from "fs/promises";
|
|
|
3274
4455
|
var cleanUp = async () => {
|
|
3275
4456
|
debug("Clean up template, cache, and asset files");
|
|
3276
4457
|
const paths = [
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
4458
|
+
directories.asset,
|
|
4459
|
+
directories.cache,
|
|
4460
|
+
directories.template
|
|
3280
4461
|
];
|
|
3281
4462
|
await Promise.all(paths.map((path) => rm(path, {
|
|
3282
4463
|
recursive: true,
|
|
@@ -3296,7 +4477,7 @@ var templateBuilder = (app) => {
|
|
|
3296
4477
|
const done = term.out.write(loadingDialog("Building stack templates..."));
|
|
3297
4478
|
await Promise.all(app.stacks.map(async (stack) => {
|
|
3298
4479
|
const template = stack.toString(true);
|
|
3299
|
-
const path = join5(
|
|
4480
|
+
const path = join5(directories.template, app.name);
|
|
3300
4481
|
const file = join5(path, `${stack.name}.json`);
|
|
3301
4482
|
await mkdir4(path, { recursive: true });
|
|
3302
4483
|
await writeFile3(file, template);
|
|
@@ -3425,19 +4606,26 @@ var StackClient = class {
|
|
|
3425
4606
|
async update(stack, capabilities) {
|
|
3426
4607
|
debug("Update the", style.info(stack.name), "stack");
|
|
3427
4608
|
const client = this.getClient(stack.region);
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
4609
|
+
try {
|
|
4610
|
+
await client.send(new UpdateStackCommand({
|
|
4611
|
+
StackName: this.stackName(stack.name),
|
|
4612
|
+
Capabilities: capabilities,
|
|
4613
|
+
Tags: this.tags(stack),
|
|
4614
|
+
...this.templateProp(stack)
|
|
4615
|
+
}));
|
|
4616
|
+
await waitUntilStackUpdateComplete({
|
|
4617
|
+
client,
|
|
4618
|
+
maxWaitTime: this.maxWaitTime,
|
|
4619
|
+
maxDelay: this.maxDelay
|
|
4620
|
+
}, {
|
|
4621
|
+
StackName: this.stackName(stack.name)
|
|
4622
|
+
});
|
|
4623
|
+
} catch (error) {
|
|
4624
|
+
if (error instanceof Error && error.name === "ValidationError" && error.message.toLowerCase().includes("no updates")) {
|
|
4625
|
+
return;
|
|
4626
|
+
}
|
|
4627
|
+
throw error;
|
|
4628
|
+
}
|
|
3441
4629
|
}
|
|
3442
4630
|
async validate(stack) {
|
|
3443
4631
|
debug("Validate the", style.info(stack.name), "stack");
|
|
@@ -3737,7 +4925,7 @@ var assetPublisher = (config, app) => {
|
|
|
3737
4925
|
await Promise.all([...stack.assets].map(async (asset) => {
|
|
3738
4926
|
await asset.publish?.({
|
|
3739
4927
|
async read(file) {
|
|
3740
|
-
const path = join6(
|
|
4928
|
+
const path = join6(directories.asset, asset.type, app.name, stack.name, asset.id, file);
|
|
3741
4929
|
const data = await readFile3(path);
|
|
3742
4930
|
return data;
|
|
3743
4931
|
},
|