@awsless/awsless 0.0.13 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -150,6 +150,10 @@ var toStack = ({ config, assets, app, stackConfig, plugins }) => {
150
150
  const stackName = `${config.name}-${stackConfig.name}`;
151
151
  const stack = new Stack(app, stackConfig.name, {
152
152
  stackName,
153
+ env: {
154
+ account: config.account,
155
+ region: config.region
156
+ },
153
157
  tags: {
154
158
  APP: config.name,
155
159
  STAGE: config.stage,
@@ -259,6 +263,9 @@ var toId = (resource, id) => {
259
263
  var toName = (stack, id) => {
260
264
  return paramCase(`${stack.stackName}-${id}`);
261
265
  };
266
+ var toExportName = (name) => {
267
+ return paramCase(name);
268
+ };
262
269
  var toEnvKey = (resource, id) => {
263
270
  return `RESOURCE_${resource.toUpperCase()}_${id}`;
264
271
  };
@@ -361,7 +368,7 @@ var SizeSchema = z7.custom((value) => {
361
368
 
362
369
  // src/plugins/function/util/build.ts
363
370
  import JSZip from "jszip";
364
- import { basename, join as join2 } from "path";
371
+ import { dirname, join as join2 } from "path";
365
372
  import { mkdir, writeFile } from "fs/promises";
366
373
  import { filesize } from "filesize";
367
374
  var zipFiles = (files) => {
@@ -392,7 +399,7 @@ var writeBuildFiles = async (config, stack, id, files) => {
392
399
  await writeFile(bundleFile, bundle);
393
400
  await Promise.all(files.map(async (file) => {
394
401
  const fileName = join2(filesPath, file.name);
395
- await mkdir(basename(fileName), { recursive: true });
402
+ await mkdir(dirname(fileName), { recursive: true });
396
403
  await writeFile(fileName, file.code);
397
404
  if (file.map) {
398
405
  const mapName = join2(filesPath, `${file.name}.map`);
@@ -420,7 +427,7 @@ var assetBucketUrl = (config, stackName) => {
420
427
  const bucket = assetBucketName(config);
421
428
  return `https://s3-${config.region}.amazonaws.com/${bucket}/${stackName}/cloudformation.json`;
422
429
  };
423
- var version = "2";
430
+ var version = "1";
424
431
  var bootstrapStack = (config, app) => {
425
432
  const stack = new Stack2(app, "bootstrap", {
426
433
  stackName: `awsless-bootstrap`
@@ -895,35 +902,8 @@ var topicPlugin = definePlugin({
895
902
  }
896
903
  });
897
904
 
898
- // src/plugins/search.ts
899
- import { z as z19 } from "zod";
900
- import { CfnCollection } from "aws-cdk-lib/aws-opensearchserverless";
901
- import { PolicyStatement as PolicyStatement2 } from "aws-cdk-lib/aws-iam";
902
- var searchPlugin = definePlugin({
903
- name: "search",
904
- schema: z19.object({
905
- stacks: z19.object({
906
- searchs: z19.array(ResourceIdSchema).optional()
907
- }).array()
908
- }),
909
- onStack({ stack, stackConfig, bind }) {
910
- (stackConfig.searchs || []).forEach((id) => {
911
- const collection = new CfnCollection(stack, toId("search", id), {
912
- name: toName(stack, id),
913
- type: "SEARCH"
914
- });
915
- bind((lambda) => {
916
- lambda.addToRolePolicy(new PolicyStatement2({
917
- actions: ["aoss:APIAccessAll"],
918
- resources: [collection.attrArn]
919
- }));
920
- });
921
- });
922
- }
923
- });
924
-
925
905
  // src/plugins/graphql/index.ts
926
- import { z as z21 } from "zod";
906
+ import { z as z20 } from "zod";
927
907
  import { AuthorizationType, CfnGraphQLApi, CfnGraphQLSchema, GraphqlApi, MappingTemplate } from "aws-cdk-lib/aws-appsync";
928
908
  import { mergeTypeDefs } from "@graphql-tools/merge";
929
909
  import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
@@ -937,40 +917,40 @@ var toArray = (value) => {
937
917
  };
938
918
 
939
919
  // src/plugins/graphql/index.ts
940
- import { dirname, join as join4 } from "path";
920
+ import { dirname as dirname2, join as join4 } from "path";
941
921
  import { print } from "graphql";
942
922
  import { paramCase as paramCase2 } from "change-case";
943
923
 
944
924
  // src/plugins/graphql/schema/resolver-field.ts
945
- import { z as z20 } from "zod";
946
- var ResolverFieldSchema = z20.custom((value) => {
947
- return z20.string().regex(/([a-z0-9\_]+)(\s){1}([a-z0-9\_]+)/gi).safeParse(value).success;
925
+ import { z as z19 } from "zod";
926
+ var ResolverFieldSchema = z19.custom((value) => {
927
+ return z19.string().regex(/([a-z0-9\_]+)(\s){1}([a-z0-9\_]+)/gi).safeParse(value).success;
948
928
  }, `Invalid resolver field. Valid example: "Query list"`);
949
929
 
950
930
  // src/plugins/graphql/index.ts
951
931
  import { CfnOutput as CfnOutput2, Fn } from "aws-cdk-lib";
952
932
  var graphqlPlugin = definePlugin({
953
933
  name: "graphql",
954
- schema: z21.object({
955
- defaults: z21.object({
956
- graphql: z21.record(ResourceIdSchema, z21.object({
957
- authorization: z21.object({
934
+ schema: z20.object({
935
+ defaults: z20.object({
936
+ graphql: z20.record(ResourceIdSchema, z20.object({
937
+ authorization: z20.object({
958
938
  authorizer: FunctionSchema,
959
939
  ttl: DurationSchema.default("1 hour")
960
940
  }).optional(),
961
- mappingTemplate: z21.object({
941
+ mappingTemplate: z20.object({
962
942
  request: LocalFileSchema.optional(),
963
943
  response: LocalFileSchema.optional()
964
944
  }).optional()
965
945
  })).optional()
966
946
  }).default({}),
967
- stacks: z21.object({
968
- graphql: z21.record(ResourceIdSchema, z21.object({
969
- schema: z21.union([
947
+ stacks: z20.object({
948
+ graphql: z20.record(ResourceIdSchema, z20.object({
949
+ schema: z20.union([
970
950
  LocalFileSchema,
971
- z21.array(LocalFileSchema).min(1)
951
+ z20.array(LocalFileSchema).min(1)
972
952
  ]).optional(),
973
- resolvers: z21.record(ResolverFieldSchema, FunctionSchema).optional()
953
+ resolvers: z20.record(ResolverFieldSchema, FunctionSchema).optional()
974
954
  })).optional()
975
955
  }).array()
976
956
  }),
@@ -1018,7 +998,7 @@ var graphqlPlugin = definePlugin({
1018
998
  }));
1019
999
  }));
1020
1000
  const schema2 = print(mergeTypeDefs(schemas));
1021
- await mkdir2(dirname(file), { recursive: true });
1001
+ await mkdir2(dirname2(file), { recursive: true });
1022
1002
  await writeFile2(file, schema2);
1023
1003
  new CfnGraphQLSchema(stack, toId("schema", id), {
1024
1004
  apiId: api.attrApiId,
@@ -1055,16 +1035,17 @@ var graphqlPlugin = definePlugin({
1055
1035
  });
1056
1036
 
1057
1037
  // src/plugins/pubsub.ts
1058
- import { z as z22 } from "zod";
1038
+ import { z as z21 } from "zod";
1059
1039
  import { CfnTopicRule } from "aws-cdk-lib/aws-iot";
1060
- import { PolicyStatement as PolicyStatement3 } from "aws-cdk-lib/aws-iam";
1040
+ import { PolicyStatement as PolicyStatement2 } from "aws-cdk-lib/aws-iam";
1041
+ import { snakeCase } from "change-case";
1061
1042
  var pubsubPlugin = definePlugin({
1062
1043
  name: "pubsub",
1063
- schema: z22.object({
1064
- stacks: z22.object({
1065
- pubsub: z22.record(ResourceIdSchema, z22.object({
1066
- sql: z22.string(),
1067
- sqlVersion: z22.enum(["2015-10-08", "2016-03-23", "beta"]).default("2016-03-23"),
1044
+ schema: z21.object({
1045
+ stacks: z21.object({
1046
+ pubsub: z21.record(ResourceIdSchema, z21.object({
1047
+ sql: z21.string(),
1048
+ sqlVersion: z21.enum(["2015-10-08", "2016-03-23", "beta"]).default("2016-03-23"),
1068
1049
  consumer: FunctionSchema
1069
1050
  })).optional()
1070
1051
  }).array()
@@ -1072,7 +1053,7 @@ var pubsubPlugin = definePlugin({
1072
1053
  onStack(ctx) {
1073
1054
  const { stack, stackConfig, bind } = ctx;
1074
1055
  bind((lambda) => {
1075
- lambda.addToRolePolicy(new PolicyStatement3({
1056
+ lambda.addToRolePolicy(new PolicyStatement2({
1076
1057
  actions: ["iot:publish"],
1077
1058
  resources: ["*"]
1078
1059
  }));
@@ -1080,7 +1061,7 @@ var pubsubPlugin = definePlugin({
1080
1061
  return Object.entries(stackConfig.pubsub || {}).map(([id, props]) => {
1081
1062
  const lambda = toFunction(ctx, id, props.consumer);
1082
1063
  new CfnTopicRule(stack, toId("pubsub", id), {
1083
- ruleName: toName(stack, id),
1064
+ ruleName: snakeCase(toName(stack, id)),
1084
1065
  topicRulePayload: {
1085
1066
  sql: props.sql,
1086
1067
  awsIotSqlVersion: props.sqlVersion,
@@ -1096,6 +1077,229 @@ var pubsubPlugin = definePlugin({
1096
1077
  }
1097
1078
  });
1098
1079
 
1080
+ // src/plugins/http/index.ts
1081
+ import { z as z23 } from "zod";
1082
+ import { Peer, Port, SecurityGroup, SubnetType, Vpc } from "aws-cdk-lib/aws-ec2";
1083
+ import { ApplicationListener, ApplicationListenerRule, ApplicationLoadBalancer, ApplicationProtocol, ApplicationTargetGroup, ListenerAction, ListenerCondition } from "aws-cdk-lib/aws-elasticloadbalancingv2";
1084
+ import { HostedZone, RecordSet, RecordType, RecordTarget } from "aws-cdk-lib/aws-route53";
1085
+ import { LoadBalancerTarget } from "aws-cdk-lib/aws-route53-targets";
1086
+ import { LambdaTarget } from "aws-cdk-lib/aws-elasticloadbalancingv2-targets";
1087
+ import { CfnOutput as CfnOutput3, Fn as Fn2, Token } from "aws-cdk-lib";
1088
+ import { Certificate } from "aws-cdk-lib/aws-certificatemanager";
1089
+ import { paramCase as paramCase3 } from "change-case";
1090
+
1091
+ // src/plugins/http/schema/route.ts
1092
+ import { z as z22 } from "zod";
1093
+ var RouteSchema = z22.custom((route) => {
1094
+ return z22.string().regex(/^(POST|GET|PUT|DELETE|HEAD|OPTIONS)(\s\/[a-z0-9\+\_\-\/]*)$/ig).safeParse(route).success;
1095
+ }, "Invalid route");
1096
+
1097
+ // src/plugins/http/util/priority.ts
1098
+ var strToInt = (str) => {
1099
+ return parseInt(Buffer.from(str, "utf8").toString("hex"), 16);
1100
+ };
1101
+ var generatePriority = (stackName, route) => {
1102
+ const start = strToInt(stackName) % 500 + 1;
1103
+ const end = strToInt(route) % 100;
1104
+ const priority = start + "" + end;
1105
+ return parseInt(priority, 10);
1106
+ };
1107
+
1108
+ // src/plugins/http/index.ts
1109
+ var httpPlugin = definePlugin({
1110
+ name: "http",
1111
+ schema: z23.object({
1112
+ defaults: z23.object({
1113
+ http: z23.record(
1114
+ ResourceIdSchema,
1115
+ z23.object({
1116
+ domain: z23.string(),
1117
+ subDomain: z23.string()
1118
+ })
1119
+ ).optional()
1120
+ }).default({}),
1121
+ stacks: z23.object({
1122
+ http: z23.record(
1123
+ ResourceIdSchema,
1124
+ z23.record(RouteSchema, FunctionSchema)
1125
+ ).optional()
1126
+ }).array()
1127
+ }),
1128
+ onBootstrap({ stack, config }) {
1129
+ if (Object.keys(config.defaults?.http || {}).length === 0) {
1130
+ return;
1131
+ }
1132
+ const vpc = new Vpc(stack, toId("vpc", "http"), {
1133
+ subnetConfiguration: [{
1134
+ name: "public",
1135
+ subnetType: SubnetType.PUBLIC,
1136
+ cidrMask: 24
1137
+ }],
1138
+ availabilityZones: [
1139
+ config.region + "a",
1140
+ config.region + "b",
1141
+ config.region + "c"
1142
+ ]
1143
+ });
1144
+ const securityGroup = new SecurityGroup(stack, toId("security-group", "http"), {
1145
+ vpc
1146
+ });
1147
+ securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(443));
1148
+ securityGroup.addIngressRule(Peer.anyIpv6(), Port.tcp(443));
1149
+ new CfnOutput3(stack, toId("output", "http-vpc"), {
1150
+ exportName: "http-vpc-id",
1151
+ value: vpc.vpcId
1152
+ });
1153
+ new CfnOutput3(stack, toId("output", "http-security-group"), {
1154
+ exportName: "http-security-group-id",
1155
+ value: securityGroup.securityGroupId
1156
+ });
1157
+ Object.entries(config.defaults?.http || {}).forEach(([id, props]) => {
1158
+ const loadBalancer = new ApplicationLoadBalancer(stack, toId("load-balancer", id), {
1159
+ vpc,
1160
+ securityGroup
1161
+ });
1162
+ const zone = HostedZone.fromHostedZoneAttributes(
1163
+ stack,
1164
+ toId("hosted-zone", id),
1165
+ {
1166
+ hostedZoneId: Token.asString(Fn2.ref(toId("hosted-zone", props.domain))),
1167
+ zoneName: props.domain + "."
1168
+ }
1169
+ );
1170
+ const certificate = Certificate.fromCertificateArn(
1171
+ stack,
1172
+ toId("certificate", id),
1173
+ Token.asString(Fn2.ref(toId("certificate", props.domain)))
1174
+ );
1175
+ const target = RecordTarget.fromAlias(new LoadBalancerTarget(loadBalancer));
1176
+ const recordName = props.subDomain ? `${props.subDomain}.${props.domain}` : props.domain;
1177
+ new RecordSet(stack, toId("record-set", id), {
1178
+ zone,
1179
+ target,
1180
+ recordName,
1181
+ recordType: RecordType.A
1182
+ });
1183
+ const listener = loadBalancer.addListener(toId("listener", id), {
1184
+ port: 443,
1185
+ protocol: ApplicationProtocol.HTTPS,
1186
+ certificates: [certificate],
1187
+ defaultAction: ListenerAction.fixedResponse(404, {
1188
+ contentType: "application/json",
1189
+ messageBody: JSON.stringify({
1190
+ message: "Route not found"
1191
+ })
1192
+ })
1193
+ });
1194
+ new CfnOutput3(stack, toId("output", `http-${id}-listener`), {
1195
+ exportName: `http-${id}-listener-arn`,
1196
+ value: listener.listenerArn
1197
+ });
1198
+ });
1199
+ },
1200
+ onStack(ctx) {
1201
+ const { stack, stackConfig } = ctx;
1202
+ return Object.entries(stackConfig.http || {}).map(([id, routes]) => {
1203
+ const listener = ApplicationListener.fromApplicationListenerAttributes(stack, toId("listener", id), {
1204
+ listenerArn: Fn2.importValue(`http-${id}-listener-arn`),
1205
+ securityGroup: SecurityGroup.fromLookupById(
1206
+ stack,
1207
+ toId("security-group", id),
1208
+ "http-security-group-id"
1209
+ )
1210
+ });
1211
+ return Object.entries(routes).map(([route, props]) => {
1212
+ const lambda = toFunction(ctx, paramCase3(route), props);
1213
+ const [method, ...paths] = route.split(" ");
1214
+ const path = paths.join(" ");
1215
+ new ApplicationListenerRule(stack, toId("listener-rule", route), {
1216
+ listener,
1217
+ priority: generatePriority(stackConfig.name, route),
1218
+ action: ListenerAction.forward([
1219
+ new ApplicationTargetGroup(stack, toId("target-group", route), {
1220
+ targets: [new LambdaTarget(lambda)]
1221
+ })
1222
+ ]),
1223
+ conditions: [
1224
+ ListenerCondition.httpRequestMethods([method]),
1225
+ ListenerCondition.pathPatterns([path])
1226
+ ]
1227
+ });
1228
+ return lambda;
1229
+ });
1230
+ }).flat();
1231
+ }
1232
+ });
1233
+
1234
+ // src/plugins/domain/index.ts
1235
+ import { z as z26 } from "zod";
1236
+ import { HostedZone as HostedZone2, CfnRecordSetGroup } from "aws-cdk-lib/aws-route53";
1237
+ import { Certificate as Certificate2, CertificateValidation } from "aws-cdk-lib/aws-certificatemanager";
1238
+
1239
+ // src/plugins/domain/schema/record-type.ts
1240
+ import { RecordType as RecordType2 } from "aws-cdk-lib/aws-route53";
1241
+ import { z as z24 } from "zod";
1242
+ var types4 = {
1243
+ "A": RecordType2.A,
1244
+ "AAAA": RecordType2.AAAA,
1245
+ "MX": RecordType2.MX,
1246
+ "TXT": RecordType2.TXT,
1247
+ "CNAME": RecordType2.CNAME
1248
+ };
1249
+ var RecordTypeSchema = z24.enum(Object.keys(types4)).transform((value) => types4[value]);
1250
+
1251
+ // src/plugins/domain/schema/domain-name.ts
1252
+ import { z as z25 } from "zod";
1253
+ var DomainNameSchema = z25.string().regex(/[a-z\-\_\.]/g, "Invalid domain name");
1254
+
1255
+ // src/plugins/domain/index.ts
1256
+ import { CfnOutput as CfnOutput4 } from "aws-cdk-lib";
1257
+ var domainPlugin = definePlugin({
1258
+ name: "domain",
1259
+ schema: z26.object({
1260
+ domains: z26.record(DomainNameSchema, z26.object({
1261
+ name: DomainNameSchema.optional(),
1262
+ type: RecordTypeSchema,
1263
+ ttl: DurationSchema,
1264
+ records: z26.string().array()
1265
+ }).array()).optional()
1266
+ }),
1267
+ onBootstrap({ config, stack }) {
1268
+ Object.entries(config.domains || {}).forEach(([domain, dnsRecords]) => {
1269
+ const hostedZone = new HostedZone2(stack, toId("hosted-zone", domain), {
1270
+ zoneName: domain,
1271
+ addTrailingDot: true
1272
+ });
1273
+ hostedZone.node.defaultChild.overrideLogicalId(toId("hosted-zone", domain));
1274
+ const certificate = new Certificate2(stack, toId("certificate", domain), {
1275
+ domainName: domain,
1276
+ validation: CertificateValidation.fromDns(hostedZone),
1277
+ subjectAlternativeNames: [`*.${domain}`]
1278
+ });
1279
+ certificate.node.defaultChild.overrideLogicalId(toId("certificate", domain));
1280
+ new CfnOutput4(stack, toId("output-hosted-zone", domain), {
1281
+ exportName: toExportName(`hosted-zone-${domain}-id`),
1282
+ value: hostedZone.hostedZoneId
1283
+ });
1284
+ new CfnOutput4(stack, toId("output-certificate", domain), {
1285
+ exportName: toExportName(`certificate-${domain}-arn`),
1286
+ value: certificate.certificateArn
1287
+ });
1288
+ if (dnsRecords.length > 0) {
1289
+ new CfnRecordSetGroup(stack, toId("record-set-group", domain), {
1290
+ hostedZoneId: hostedZone.hostedZoneId,
1291
+ recordSets: dnsRecords.map((props) => ({
1292
+ name: props.name || "",
1293
+ type: props.type,
1294
+ ttl: props.ttl.toSeconds().toString(),
1295
+ resourceRecords: props.records
1296
+ }))
1297
+ });
1298
+ }
1299
+ });
1300
+ }
1301
+ });
1302
+
1099
1303
  // src/plugins/index.ts
1100
1304
  var defaultPlugins = [
1101
1305
  functionPlugin,
@@ -1104,9 +1308,11 @@ var defaultPlugins = [
1104
1308
  tablePlugin,
1105
1309
  storePlugin,
1106
1310
  topicPlugin,
1107
- searchPlugin,
1311
+ // searchPlugin,
1108
1312
  graphqlPlugin,
1109
- pubsubPlugin
1313
+ pubsubPlugin,
1314
+ domainPlugin,
1315
+ httpPlugin
1110
1316
  ];
1111
1317
 
1112
1318
  // src/stack/app-bootstrap.ts
@@ -1286,63 +1492,6 @@ var toApp = async (config, filters) => {
1286
1492
  };
1287
1493
  };
1288
1494
 
1289
- // src/cli/ui/layout/basic.ts
1290
- var br = () => {
1291
- return "\n";
1292
- };
1293
- var hr = () => {
1294
- return (term) => {
1295
- term.out.write([
1296
- style.placeholder("\u2500".repeat(term.out.width())),
1297
- br()
1298
- ]);
1299
- };
1300
- };
1301
-
1302
- // src/cli/ui/layout/logs.ts
1303
- import wrapAnsi from "wrap-ansi";
1304
- var previous = /* @__PURE__ */ new Date();
1305
- var logs = () => {
1306
- if (!process.env.VERBOSE) {
1307
- return [];
1308
- }
1309
- const logs2 = flushDebug();
1310
- return (term) => {
1311
- term.out.write([
1312
- hr(),
1313
- br(),
1314
- " ".repeat(3),
1315
- style.label("Debug Logs:"),
1316
- br(),
1317
- br(),
1318
- logs2.map((log) => {
1319
- const diff = log.date.getTime() - previous.getTime();
1320
- const time = `+${diff}`.padStart(8);
1321
- previous = log.date;
1322
- return wrapAnsi([
1323
- style.attr(`${time}${style.attr.dim("ms")}`),
1324
- " [ ",
1325
- log.type,
1326
- " ] ",
1327
- log.message,
1328
- br(),
1329
- log.type === "error" ? br() : ""
1330
- ].join(""), term.out.width(), { hard: true, trim: false });
1331
- }),
1332
- br(),
1333
- hr()
1334
- ]);
1335
- };
1336
- };
1337
-
1338
- // src/cli/ui/layout/footer.ts
1339
- var footer = () => {
1340
- return [
1341
- br(),
1342
- logs()
1343
- ];
1344
- };
1345
-
1346
1495
  // src/config.ts
1347
1496
  import { join as join6 } from "path";
1348
1497
 
@@ -1363,17 +1512,17 @@ var getCredentials = (profile) => {
1363
1512
  };
1364
1513
 
1365
1514
  // src/schema/app.ts
1366
- import { z as z26 } from "zod";
1515
+ import { z as z30 } from "zod";
1367
1516
 
1368
1517
  // src/schema/stack.ts
1369
- import { z as z23 } from "zod";
1370
- var StackSchema = z23.object({
1518
+ import { z as z27 } from "zod";
1519
+ var StackSchema = z27.object({
1371
1520
  name: ResourceIdSchema,
1372
- depends: z23.array(z23.lazy(() => StackSchema)).optional()
1521
+ depends: z27.array(z27.lazy(() => StackSchema)).optional()
1373
1522
  });
1374
1523
 
1375
1524
  // src/schema/region.ts
1376
- import { z as z24 } from "zod";
1525
+ import { z as z28 } from "zod";
1377
1526
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
1378
1527
  var AF = ["af-south-1"];
1379
1528
  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"];
@@ -1390,34 +1539,34 @@ var regions = [
1390
1539
  ...ME,
1391
1540
  ...SA
1392
1541
  ];
1393
- var RegionSchema = z24.enum(regions);
1542
+ var RegionSchema = z28.enum(regions);
1394
1543
 
1395
1544
  // src/schema/plugin.ts
1396
- import { z as z25 } from "zod";
1397
- var PluginSchema = z25.object({
1398
- name: z25.string(),
1399
- schema: z25.custom().optional(),
1545
+ import { z as z29 } from "zod";
1546
+ var PluginSchema = z29.object({
1547
+ name: z29.string(),
1548
+ schema: z29.custom().optional(),
1400
1549
  // depends: z.array(z.lazy(() => PluginSchema)).optional(),
1401
- onBootstrap: z25.function().returns(z25.any()).optional(),
1402
- onStack: z25.function().returns(z25.any()).optional(),
1403
- onApp: z25.function().returns(z25.void()).optional()
1550
+ onBootstrap: z29.function().returns(z29.any()).optional(),
1551
+ onStack: z29.function().returns(z29.any()).optional(),
1552
+ onApp: z29.function().returns(z29.void()).optional()
1404
1553
  // bind: z.function().optional(),
1405
1554
  });
1406
1555
 
1407
1556
  // src/schema/app.ts
1408
- var AppSchema = z26.object({
1557
+ var AppSchema = z30.object({
1409
1558
  name: ResourceIdSchema,
1410
1559
  region: RegionSchema,
1411
- profile: z26.string(),
1412
- stage: z26.string().regex(/[a-z]+/).default("prod"),
1413
- defaults: z26.object({}).default({}),
1414
- stacks: z26.array(StackSchema).min(1),
1415
- plugins: z26.array(PluginSchema).optional()
1560
+ profile: z30.string(),
1561
+ stage: z30.string().regex(/[a-z]+/).default("prod"),
1562
+ defaults: z30.object({}).default({}),
1563
+ stacks: z30.array(StackSchema).min(1),
1564
+ plugins: z30.array(PluginSchema).optional()
1416
1565
  });
1417
1566
 
1418
1567
  // src/util/import.ts
1419
1568
  import { transformFile } from "@swc/core";
1420
- import { dirname as dirname2, join as join5 } from "path";
1569
+ import { dirname as dirname3, join as join5 } from "path";
1421
1570
  import { lstat, mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
1422
1571
  var resolveFileNameExtension = async (path) => {
1423
1572
  const options = [
@@ -1428,7 +1577,7 @@ var resolveFileNameExtension = async (path) => {
1428
1577
  "/index.js"
1429
1578
  ];
1430
1579
  for (const option of options) {
1431
- const file = path + option;
1580
+ const file = path.replace(/\.js$/, "") + option;
1432
1581
  let stat;
1433
1582
  try {
1434
1583
  stat = await lstat(file);
@@ -1442,21 +1591,22 @@ var resolveFileNameExtension = async (path) => {
1442
1591
  throw new Error(`Failed to load file: ${path}`);
1443
1592
  };
1444
1593
  var resolveDir = (path) => {
1445
- return dirname2(path).replace(rootDir + "/", "");
1594
+ return dirname3(path).replace(rootDir + "/", "");
1446
1595
  };
1447
1596
  var importFile = async (path) => {
1448
1597
  const load = async (file) => {
1598
+ debug("Load file", file);
1449
1599
  let { code: code2 } = await transformFile(file, {
1450
1600
  isModule: true
1451
1601
  });
1452
- const path2 = dirname2(file);
1602
+ const path2 = dirname3(file);
1453
1603
  const dir = resolveDir(file);
1454
1604
  code2 = code2.replaceAll("__dirname", `"${dir}"`);
1455
- const matches = code2.match(/import\s*{\s*[a-z0-9\_]+\s*}\s*from\s*('|")(\.[\/a-z0-9\_\-]+)('|");?/ig);
1605
+ const matches = code2.match(/(import|export)\s*{\s*[a-z0-9\_\,\s\*]+\s*}\s*from\s*('|")(\.\.?[\/a-z0-9\_\-\.]+)('|");?/ig);
1456
1606
  if (!matches)
1457
1607
  return code2;
1458
1608
  await Promise.all(matches?.map(async (match) => {
1459
- const parts = /('|")(\.[\/a-z0-9\_\-]+)('|")/ig.exec(match);
1609
+ const parts = /('|")(\.\.?[\/a-z0-9\_\-\.]+)('|")/ig.exec(match);
1460
1610
  const from = parts[2];
1461
1611
  const file2 = await resolveFileNameExtension(join5(path2, from));
1462
1612
  const result = await load(file2);
@@ -1476,11 +1626,7 @@ var importConfig = async (options) => {
1476
1626
  debug("Import config file");
1477
1627
  const fileName = join6(process.cwd(), options.configFile || "awsless.config.ts");
1478
1628
  const module = await importFile(fileName);
1479
- const appConfig = typeof module.default === "function" ? await module.default({
1480
- profile: options.profile,
1481
- region: options.region,
1482
- stage: options.stage
1483
- }) : module.default;
1629
+ const appConfig = typeof module.default === "function" ? await module.default(options) : module.default;
1484
1630
  debug("Validate config file");
1485
1631
  const plugins = [
1486
1632
  ...defaultPlugins,
@@ -1505,6 +1651,19 @@ var importConfig = async (options) => {
1505
1651
  };
1506
1652
  };
1507
1653
 
1654
+ // src/cli/ui/layout/basic.ts
1655
+ var br = () => {
1656
+ return "\n";
1657
+ };
1658
+ var hr = () => {
1659
+ return (term) => {
1660
+ term.out.write([
1661
+ style.placeholder("\u2500".repeat(term.out.width())),
1662
+ br()
1663
+ ]);
1664
+ };
1665
+ };
1666
+
1508
1667
  // src/cli/ui/layout/list.ts
1509
1668
  var list = (data) => {
1510
1669
  const padding = 3;
@@ -1512,26 +1671,26 @@ var list = (data) => {
1512
1671
  const size = Object.keys(data).reduce((total, name) => {
1513
1672
  return name.length > total ? name.length : total;
1514
1673
  }, 0);
1515
- return Object.entries(data).map(([name, value]) => [
1516
- " ".repeat(padding),
1517
- style.label((name + ":").padEnd(size + gap + 1)),
1518
- value,
1519
- br()
1520
- ]);
1674
+ return (term) => {
1675
+ term.out.gap();
1676
+ term.out.write(Object.entries(data).map(([name, value]) => [
1677
+ " ".repeat(padding),
1678
+ style.label((name + ":").padEnd(size + gap + 1)),
1679
+ value,
1680
+ br()
1681
+ ]));
1682
+ term.out.gap();
1683
+ };
1521
1684
  };
1522
1685
 
1523
1686
  // src/cli/ui/layout/header.ts
1524
1687
  var header = (config) => {
1525
- return [
1526
- br(),
1527
- list({
1528
- App: config.name,
1529
- Stage: config.stage,
1530
- Region: config.region,
1531
- Profile: config.profile
1532
- }),
1533
- br()
1534
- ];
1688
+ return list({
1689
+ App: config.name,
1690
+ Stage: config.stage,
1691
+ Region: config.region,
1692
+ Profile: config.profile
1693
+ });
1535
1694
  };
1536
1695
 
1537
1696
  // src/util/timer.ts
@@ -1598,16 +1757,16 @@ var createSpinner = () => {
1598
1757
  };
1599
1758
 
1600
1759
  // src/cli/ui/layout/dialog.ts
1601
- import wrapAnsi2 from "wrap-ansi";
1760
+ import wrapAnsi from "wrap-ansi";
1602
1761
  var dialog = (type, lines) => {
1603
1762
  const padding = 3;
1604
1763
  const icon = style[type](symbol[type].padEnd(padding));
1605
1764
  return (term) => {
1606
1765
  term.out.write(lines.map((line, i) => {
1607
1766
  if (i === 0) {
1608
- return icon + wrapAnsi2(line, term.out.width(), { hard: true });
1767
+ return icon + wrapAnsi(line, term.out.width(), { hard: true });
1609
1768
  }
1610
- return wrapAnsi2(" ".repeat(padding) + line, term.out.width(), { hard: true });
1769
+ return wrapAnsi(" ".repeat(padding) + line, term.out.width(), { hard: true });
1611
1770
  }).join(br()) + br());
1612
1771
  };
1613
1772
  };
@@ -1749,6 +1908,7 @@ var Renderer = class {
1749
1908
  fragments = [];
1750
1909
  unsubs = [];
1751
1910
  timeout;
1911
+ flushing = false;
1752
1912
  screen = [];
1753
1913
  width() {
1754
1914
  return this.output.columns;
@@ -1768,14 +1928,58 @@ var Renderer = class {
1768
1928
  this.update();
1769
1929
  return fragment;
1770
1930
  }
1931
+ gap() {
1932
+ const walk = (fragment) => {
1933
+ if (typeof fragment === "string") {
1934
+ return fragment;
1935
+ }
1936
+ if (Array.isArray(fragment)) {
1937
+ return fragment.map(walk).join("");
1938
+ }
1939
+ return walk(fragment.get());
1940
+ };
1941
+ const end = walk(this.fragments.slice(-2));
1942
+ if (end.endsWith("\n\n")) {
1943
+ } else if (end.endsWith("\n")) {
1944
+ this.fragments.push("\n");
1945
+ } else {
1946
+ this.fragments.push("\n\n");
1947
+ }
1948
+ this.update();
1949
+ }
1771
1950
  update() {
1772
1951
  clearTimeout(this.timeout);
1773
1952
  this.timeout = setTimeout(() => {
1774
1953
  this.flush();
1775
1954
  }, 0);
1776
1955
  }
1777
- flush() {
1956
+ async end() {
1957
+ this.gap();
1958
+ await this.flush();
1959
+ const y = this.screen.length - 1;
1960
+ await this.setCursor(0, y);
1961
+ }
1962
+ setCursor(x, y) {
1963
+ return new Promise((resolve) => {
1964
+ this.output.cursorTo?.(x, y, () => resolve(void 0));
1965
+ });
1966
+ }
1967
+ writeString(value) {
1968
+ return new Promise((resolve) => {
1969
+ this.output.write?.(value, () => resolve(void 0));
1970
+ });
1971
+ }
1972
+ clearLine() {
1973
+ return new Promise((resolve) => {
1974
+ this.output.clearLine?.(1, () => resolve(void 0));
1975
+ });
1976
+ }
1977
+ async flush() {
1778
1978
  clearTimeout(this.timeout);
1979
+ if (this.flushing) {
1980
+ this.update();
1981
+ return;
1982
+ }
1779
1983
  const walk = (fragment) => {
1780
1984
  if (typeof fragment === "string") {
1781
1985
  return fragment;
@@ -1791,34 +1995,40 @@ var Renderer = class {
1791
1995
  this.unsubs.forEach((unsub) => unsub());
1792
1996
  this.unsubs = [];
1793
1997
  const screen = walk(this.fragments).split("\n");
1998
+ const height = this.height();
1794
1999
  const oldSize = this.screen.length;
1795
2000
  const newSize = screen.length;
1796
2001
  const size = Math.max(oldSize, newSize);
1797
- const height = this.height();
1798
2002
  const start = Math.max(oldSize - height, 0);
2003
+ this.flushing = true;
1799
2004
  for (let y = start; y < size; y++) {
1800
- const line = screen[y];
1801
- if (line !== this.screen[y]) {
1802
- if (y > oldSize) {
1803
- const x = (this.screen[y - 1]?.length || 0) - 1;
1804
- this.output.cursorTo?.(x, y - 1 - start);
1805
- this.output.write?.("\n" + line);
2005
+ const newLine = screen[y];
2006
+ const oldLine = this.screen[y];
2007
+ if (newLine !== oldLine) {
2008
+ if (y >= oldSize && y !== 0) {
2009
+ const p = y - start - 1;
2010
+ const x = screen[y - 1]?.length || 0;
2011
+ await this.setCursor(x, p);
2012
+ await this.writeString("\n" + newLine);
1806
2013
  } else {
1807
- this.output.cursorTo?.(0, y - start);
1808
- this.output.write?.(line);
2014
+ await this.setCursor(0, y - start);
2015
+ await this.writeString(newLine);
2016
+ await this.clearLine();
1809
2017
  }
1810
- this.output.clearLine?.(1);
1811
2018
  }
1812
2019
  }
1813
2020
  this.screen = screen;
1814
- }
1815
- clear() {
1816
- let count = this.output.rows;
1817
- while (count--) {
1818
- this.output.write("\n");
2021
+ this.flushing = false;
2022
+ }
2023
+ async clear() {
2024
+ await this.setCursor(0, 0);
2025
+ await this.writeString("\n".repeat(this.height()));
2026
+ await this.setCursor(0, 0);
2027
+ if (this.output.clearScreenDown) {
2028
+ await new Promise((resolve) => {
2029
+ this.output.clearScreenDown(() => resolve(void 0));
2030
+ });
1819
2031
  }
1820
- this.output.cursorTo?.(0, 0);
1821
- this.output.clearScreenDown?.();
1822
2032
  }
1823
2033
  };
1824
2034
 
@@ -1834,22 +2044,62 @@ var logo = () => {
1834
2044
  return [
1835
2045
  style.warning("\u26A1\uFE0F "),
1836
2046
  style.primary("AWS"),
1837
- style.primary.dim("LESS"),
1838
- br()
2047
+ style.primary.dim("LESS")
1839
2048
  ];
1840
2049
  };
1841
2050
 
2051
+ // src/cli/ui/layout/logs.ts
2052
+ import wrapAnsi2 from "wrap-ansi";
2053
+ var previous = /* @__PURE__ */ new Date();
2054
+ var logs = () => {
2055
+ if (!process.env.VERBOSE) {
2056
+ return [];
2057
+ }
2058
+ const logs2 = flushDebug();
2059
+ return (term) => {
2060
+ term.out.gap();
2061
+ term.out.write([
2062
+ hr(),
2063
+ br(),
2064
+ " ".repeat(3),
2065
+ style.label("Debug Logs:"),
2066
+ br(),
2067
+ br(),
2068
+ logs2.map((log) => {
2069
+ const diff = log.date.getTime() - previous.getTime();
2070
+ const time = `+${diff}`.padStart(8);
2071
+ previous = log.date;
2072
+ return wrapAnsi2([
2073
+ style.attr(`${time}${style.attr.dim("ms")}`),
2074
+ " [ ",
2075
+ log.type,
2076
+ " ] ",
2077
+ log.message,
2078
+ br(),
2079
+ log.type === "error" ? br() : ""
2080
+ ].join(""), term.out.width(), { hard: true, trim: false });
2081
+ }),
2082
+ br(),
2083
+ hr()
2084
+ ]);
2085
+ };
2086
+ };
2087
+
1842
2088
  // src/cli/ui/layout/layout.ts
1843
2089
  var layout = async (cb) => {
1844
2090
  const term = createTerminal();
1845
- term.out.clear();
2091
+ await term.out.clear();
2092
+ term.out.write(br());
1846
2093
  term.out.write(logo());
2094
+ term.out.gap();
1847
2095
  try {
1848
2096
  const options = program.optsWithGlobals();
1849
2097
  const config = await importConfig(options);
1850
2098
  term.out.write(header(config));
2099
+ term.out.gap();
1851
2100
  await cb(config, term.out.write.bind(term.out), term);
1852
2101
  } catch (error) {
2102
+ term.out.gap();
1853
2103
  if (error instanceof Error) {
1854
2104
  term.out.write(dialog("error", [error.message]));
1855
2105
  } else if (typeof error === "string") {
@@ -1860,7 +2110,9 @@ var layout = async (cb) => {
1860
2110
  debugError(error);
1861
2111
  } finally {
1862
2112
  debug("Exit");
1863
- term.out.write(footer());
2113
+ term.out.gap();
2114
+ term.out.write(logs());
2115
+ await term.out.end();
1864
2116
  term.in.unref();
1865
2117
  setTimeout(() => {
1866
2118
  process.exit(0);
@@ -1892,7 +2144,8 @@ var flexLine = (term, left, right, reserveSpace = 0) => {
1892
2144
  var assetBuilder = (assets) => {
1893
2145
  return async (term) => {
1894
2146
  const done = term.out.write(loadingDialog("Building stack assets..."));
1895
- const groups = new Signal([br()]);
2147
+ const groups = new Signal([""]);
2148
+ term.out.gap();
1896
2149
  term.out.write(groups);
1897
2150
  const stackNameSize = Math.max(...Object.keys(assets.list()).map((stack) => stack.length));
1898
2151
  const resourceSize = Math.max(...Object.values(assets.list()).map((assets2) => assets2.map((asset) => asset.resource.length)).flat());
@@ -1938,6 +2191,7 @@ var assetBuilder = (assets) => {
1938
2191
  }));
1939
2192
  }));
1940
2193
  done("Done building stack assets");
2194
+ term.out.gap();
1941
2195
  };
1942
2196
  };
1943
2197
 
@@ -2277,7 +2531,9 @@ var stackTree = (nodes, statuses) => {
2277
2531
  render(node.children, deep + 1, [...parents, more]);
2278
2532
  });
2279
2533
  };
2534
+ term.out.gap();
2280
2535
  render(nodes);
2536
+ term.out.gap();
2281
2537
  };
2282
2538
  };
2283
2539
 
@@ -2288,7 +2544,6 @@ var status = (program2) => {
2288
2544
  const { app, assets, dependencyTree } = await toApp(config, filters);
2289
2545
  await cleanUp();
2290
2546
  await write(assetBuilder(assets));
2291
- write(br());
2292
2547
  const assembly = app.synth();
2293
2548
  const doneLoading = write(loadingDialog("Loading stack information..."));
2294
2549
  const client = new StackClient(config);
@@ -2297,9 +2552,7 @@ var status = (program2) => {
2297
2552
  assembly.stacks.forEach((stack) => {
2298
2553
  stackStatuses[stack.id] = new Signal(style.info("Loading..."));
2299
2554
  });
2300
- write(br());
2301
2555
  write(stackTree(dependencyTree, stackStatuses));
2302
- write(br());
2303
2556
  debug("Load metadata for all deployed stacks on AWS");
2304
2557
  await Promise.all(assembly.stacks.map(async (stack, i) => {
2305
2558
  const info = await client.get(stack.stackName);
@@ -2344,8 +2597,6 @@ var deploy = (program2) => {
2344
2597
  }
2345
2598
  await cleanUp();
2346
2599
  await write(assetBuilder(assets));
2347
- write(br());
2348
- write(br());
2349
2600
  const donePublishing = write(loadingDialog("Publishing stack assets to AWS..."));
2350
2601
  await Promise.all(assets.map(async (_, assets2) => {
2351
2602
  await Promise.all(assets2.map(async (asset) => {
@@ -2359,9 +2610,7 @@ var deploy = (program2) => {
2359
2610
  statuses[stack.id] = new Signal(style.info("waiting"));
2360
2611
  });
2361
2612
  const doneDeploying = write(loadingDialog("Deploying stacks to AWS..."));
2362
- write(br());
2363
2613
  write(stackTree(dependencyTree, statuses));
2364
- write(br());
2365
2614
  const client = new StackClient(config);
2366
2615
  const deploymentLine = createDeploymentLine(dependencyTree);
2367
2616
  for (const stacks of deploymentLine) {
@@ -2459,7 +2708,6 @@ var set = (program2) => {
2459
2708
  write(list({
2460
2709
  "Set secret parameter": style.info(name)
2461
2710
  }));
2462
- write(br());
2463
2711
  const value = await write(textPrompt("Enter secret value"));
2464
2712
  if (value === "") {
2465
2713
  write(dialog("error", [`Provided secret value can't be empty`]));
@@ -2480,7 +2728,6 @@ var get = (program2) => {
2480
2728
  const done = write(loadingDialog(`Getting remote secret parameter`));
2481
2729
  const value = await params.get(name);
2482
2730
  done(`Done getting remote secret parameter`);
2483
- write(br());
2484
2731
  write(list({
2485
2732
  Name: name,
2486
2733
  Value: value || style.error("(empty)")
@@ -2503,7 +2750,6 @@ var del = (program2) => {
2503
2750
  const value = await params.get(name);
2504
2751
  await params.delete(name);
2505
2752
  done(`Done deleting remote secret parameter`);
2506
- write(br());
2507
2753
  write(list({
2508
2754
  Name: name,
2509
2755
  Value: value || style.error("(empty)")
@@ -2521,7 +2767,6 @@ var list2 = (program2) => {
2521
2767
  const values = await params.list();
2522
2768
  done("Done loading secret values");
2523
2769
  if (Object.keys(values).length > 0) {
2524
- write(br());
2525
2770
  write(list(values));
2526
2771
  } else {
2527
2772
  write(dialog("warning", ["No secret parameters found"]));
@@ -2544,13 +2789,16 @@ var secrets = (program2) => {
2544
2789
 
2545
2790
  // src/cli/program.ts
2546
2791
  var program = new Command();
2547
- program.name("awsless");
2792
+ program.name(logo().join("").replace(/\s+/, ""));
2548
2793
  program.option("--config-file <string>", "The config file location");
2549
2794
  program.option("--stage <string>", "The stage to use, defaults to prod stage", "prod");
2550
2795
  program.option("--profile <string>", "The AWS profile to use");
2551
2796
  program.option("--region <string>", "The AWS region to use");
2552
2797
  program.option("-m --mute", "Mute sound effects");
2553
2798
  program.option("-v --verbose", "Print verbose logs");
2799
+ program.exitOverride(() => {
2800
+ process.exit(0);
2801
+ });
2554
2802
  program.on("option:verbose", () => {
2555
2803
  process.env.VERBOSE = program.opts().verbose ? "1" : void 0;
2556
2804
  });
@@ -2562,10 +2810,8 @@ var commands2 = [
2562
2810
  secrets
2563
2811
  // diff,
2564
2812
  // remove,
2565
- // test,
2566
- // test2,
2567
2813
  ];
2568
- commands2.forEach((command) => command(program));
2814
+ commands2.forEach((fn) => fn(program));
2569
2815
 
2570
2816
  // src/bin.ts
2571
2817
  program.parse(process.argv);