@awsless/awsless 0.0.12 → 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
@@ -63,15 +63,15 @@ var flushDebug = () => {
63
63
 
64
64
  // src/util/param.ts
65
65
  import { DeleteParameterCommand, GetParameterCommand, GetParametersByPathCommand, ParameterType, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
66
- var configParameterPrefix = (config2) => {
67
- return `/${config2.stage}/awsless/${config2.name}`;
66
+ var configParameterPrefix = (config) => {
67
+ return `/${config.stage}/awsless/${config.name}`;
68
68
  };
69
69
  var Params = class {
70
- constructor(config2) {
71
- this.config = config2;
70
+ constructor(config) {
71
+ this.config = config;
72
72
  this.client = new SSMClient({
73
- credentials: config2.credentials,
74
- region: config2.region
73
+ credentials: config.credentials,
74
+ region: config.region
75
75
  });
76
76
  }
77
77
  client;
@@ -146,13 +146,17 @@ var Params = class {
146
146
  };
147
147
 
148
148
  // src/stack.ts
149
- var toStack = ({ config: config2, assets, app, stackConfig, plugins }) => {
150
- const stackName = `${config2.name}-${stackConfig.name}`;
149
+ var toStack = ({ config, assets, app, stackConfig, plugins }) => {
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
- APP: config2.name,
155
- STAGE: config2.stage,
158
+ APP: config.name,
159
+ STAGE: config.stage,
156
160
  STACK: stackConfig.name
157
161
  }
158
162
  });
@@ -163,7 +167,7 @@ var toStack = ({ config: config2, assets, app, stackConfig, plugins }) => {
163
167
  };
164
168
  debug("Run plugin onStack listeners");
165
169
  const functions = plugins.map((plugin) => plugin.onStack?.({
166
- config: config2,
170
+ config,
167
171
  assets,
168
172
  app,
169
173
  stack,
@@ -182,12 +186,12 @@ var toStack = ({ config: config2, assets, app, stackConfig, plugins }) => {
182
186
  ],
183
187
  resources: [
184
188
  Arn.format({
185
- region: config2.region,
186
- account: config2.account,
189
+ region: config.region,
190
+ account: config.account,
187
191
  partition: "aws",
188
192
  service: "ssm",
189
193
  resource: "parameter",
190
- resourceName: configParameterPrefix(config2)
194
+ resourceName: configParameterPrefix(config)
191
195
  })
192
196
  // Fn.sub('arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter' + configParameterPrefix(config)),
193
197
  ]
@@ -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) => {
@@ -377,22 +384,22 @@ var zipFiles = (files) => {
377
384
  }
378
385
  });
379
386
  };
380
- var writeBuildHash = async (config2, stack, id, hash) => {
381
- const funcPath = join2(assetDir, "function", config2.name, stack.artifactId, id);
387
+ var writeBuildHash = async (config, stack, id, hash) => {
388
+ const funcPath = join2(assetDir, "function", config.name, stack.artifactId, id);
382
389
  const versionFile = join2(funcPath, "HASH");
383
390
  await writeFile(versionFile, hash);
384
391
  };
385
- var writeBuildFiles = async (config2, stack, id, files) => {
392
+ var writeBuildFiles = async (config, stack, id, files) => {
386
393
  const bundle = await zipFiles(files);
387
- const funcPath = join2(assetDir, "function", config2.name, stack.artifactId, id);
394
+ const funcPath = join2(assetDir, "function", config.name, stack.artifactId, id);
388
395
  const filesPath = join2(funcPath, "files");
389
396
  const bundleFile = join2(funcPath, "bundle.zip");
390
- debug("Bundle size of", style.info(join2(config2.name, stack.artifactId, id)), "is", style.attr(filesize(bundle.byteLength)));
397
+ debug("Bundle size of", style.info(join2(config.name, stack.artifactId, id)), "is", style.attr(filesize(bundle.byteLength)));
391
398
  await mkdir(filesPath, { recursive: true });
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`);
@@ -413,20 +420,20 @@ import { GetObjectCommand, ObjectCannedACL, PutObjectCommand, S3Client, StorageC
413
420
  // src/stack/bootstrap.ts
414
421
  import { CfnOutput, RemovalPolicy, Stack as Stack2 } from "aws-cdk-lib";
415
422
  import { Bucket, BucketAccessControl } from "aws-cdk-lib/aws-s3";
416
- var assetBucketName = (config2) => {
417
- return `awsless-bootstrap-${config2.account}-${config2.region}`;
423
+ var assetBucketName = (config) => {
424
+ return `awsless-bootstrap-${config.account}-${config.region}`;
418
425
  };
419
- var assetBucketUrl = (config2, stackName) => {
420
- const bucket = assetBucketName(config2);
421
- return `https://s3-${config2.region}.amazonaws.com/${bucket}/${stackName}/cloudformation.json`;
426
+ var assetBucketUrl = (config, stackName) => {
427
+ const bucket = assetBucketName(config);
428
+ return `https://s3-${config.region}.amazonaws.com/${bucket}/${stackName}/cloudformation.json`;
422
429
  };
423
- var version = "2";
424
- var bootstrapStack = (config2, app) => {
430
+ var version = "1";
431
+ var bootstrapStack = (config, app) => {
425
432
  const stack = new Stack2(app, "bootstrap", {
426
433
  stackName: `awsless-bootstrap`
427
434
  });
428
435
  new Bucket(stack, "assets", {
429
- bucketName: assetBucketName(config2),
436
+ bucketName: assetBucketName(config),
430
437
  versioned: true,
431
438
  accessControl: BucketAccessControl.PRIVATE,
432
439
  removalPolicy: RemovalPolicy.DESTROY
@@ -444,17 +451,17 @@ var shouldDeployBootstrap = async (client, name) => {
444
451
  };
445
452
 
446
453
  // src/plugins/function/util/publish.ts
447
- var publishFunctionAsset = async (config2, stack, id) => {
448
- const bucket = assetBucketName(config2);
449
- const key = `${config2.name}/${stack.artifactId}/function/${id}.zip`;
450
- const funcPath = join3(assetDir, "function", config2.name, stack.artifactId, id);
454
+ var publishFunctionAsset = async (config, stack, id) => {
455
+ const bucket = assetBucketName(config);
456
+ const key = `${config.name}/${stack.artifactId}/function/${id}.zip`;
457
+ const funcPath = join3(assetDir, "function", config.name, stack.artifactId, id);
451
458
  const bundleFile = join3(funcPath, "bundle.zip");
452
459
  const hashFile = join3(funcPath, "HASH");
453
460
  const hash = await readFile(hashFile, "utf8");
454
461
  const file = await readFile(bundleFile);
455
462
  const client = new S3Client({
456
- credentials: config2.credentials,
457
- region: config2.region
463
+ credentials: config.credentials,
464
+ region: config.region
458
465
  });
459
466
  let getResult;
460
467
  try {
@@ -589,8 +596,8 @@ var functionPlugin = definePlugin({
589
596
  });
590
597
  }
591
598
  });
592
- var toFunction = ({ config: config2, stack, assets }, id, fileOrProps) => {
593
- const props = typeof fileOrProps === "string" ? { ...config2.defaults?.function, file: fileOrProps } : { ...config2.defaults?.function, ...fileOrProps };
599
+ var toFunction = ({ config, stack, assets }, id, fileOrProps) => {
600
+ const props = typeof fileOrProps === "string" ? { ...config.defaults?.function, file: fileOrProps } : { ...config.defaults?.function, ...fileOrProps };
594
601
  const lambda = new Function(stack, toId("function", id), {
595
602
  functionName: toName(stack, id),
596
603
  handler: "index.default",
@@ -598,8 +605,8 @@ var toFunction = ({ config: config2, stack, assets }, id, fileOrProps) => {
598
605
  ...props,
599
606
  memorySize: props.memorySize.toMebibytes()
600
607
  });
601
- lambda.addEnvironment("APP", config2.name, { removeInEdge: true });
602
- lambda.addEnvironment("STAGE", config2.stage, { removeInEdge: true });
608
+ lambda.addEnvironment("APP", config.name, { removeInEdge: true });
609
+ lambda.addEnvironment("STAGE", config.stage, { removeInEdge: true });
603
610
  lambda.addEnvironment("STACK", stack.artifactId, { removeInEdge: true });
604
611
  if (lambda.runtime.toString().startsWith("nodejs")) {
605
612
  lambda.addEnvironment("AWS_NODEJS_CONNECTION_REUSE_ENABLED", "1", {
@@ -612,8 +619,8 @@ var toFunction = ({ config: config2, stack, assets }, id, fileOrProps) => {
612
619
  resourceName: id,
613
620
  async build() {
614
621
  const result = await rollupBuild(props.file);
615
- const bundle = await writeBuildFiles(config2, stack, id, result.files);
616
- await writeBuildHash(config2, stack, id, result.hash);
622
+ const bundle = await writeBuildFiles(config, stack, id, result.files);
623
+ await writeBuildHash(config, stack, id, result.hash);
617
624
  const func = lambda.node.defaultChild;
618
625
  func.handler = result.handler;
619
626
  return {
@@ -621,11 +628,11 @@ var toFunction = ({ config: config2, stack, assets }, id, fileOrProps) => {
621
628
  };
622
629
  },
623
630
  async publish() {
624
- const version2 = await publishFunctionAsset(config2, stack, id);
631
+ const version2 = await publishFunctionAsset(config, stack, id);
625
632
  const func = lambda.node.defaultChild;
626
633
  func.code = {
627
- s3Bucket: assetBucketName(config2),
628
- s3Key: `${config2.name}/${stack.artifactId}/function/${id}.zip`,
634
+ s3Bucket: assetBucketName(config),
635
+ s3Key: `${config.name}/${stack.artifactId}/function/${id}.zip`,
629
636
  s3ObjectVersion: version2
630
637
  };
631
638
  }
@@ -694,9 +701,9 @@ var queuePlugin = definePlugin({
694
701
  }).array()
695
702
  }),
696
703
  onStack(ctx) {
697
- const { stack, config: config2, stackConfig, bind } = ctx;
704
+ const { stack, config, stackConfig, bind } = ctx;
698
705
  return Object.entries(stackConfig.queues || {}).map(([id, functionOrProps]) => {
699
- const props = typeof functionOrProps === "string" ? { ...config2.defaults.queue, consumer: functionOrProps } : { ...config2.defaults.queue, ...functionOrProps };
706
+ const props = typeof functionOrProps === "string" ? { ...config.defaults.queue, consumer: functionOrProps } : { ...config.defaults.queue, ...functionOrProps };
700
707
  const queue2 = new Queue(stack, toId("queue", id), {
701
708
  queueName: toName(stack, id),
702
709
  ...props,
@@ -860,20 +867,20 @@ var topicPlugin = definePlugin({
860
867
  topics: z18.record(ResourceIdSchema, FunctionSchema).optional()
861
868
  }).array()
862
869
  }),
863
- onBootstrap({ config: config2, stack }) {
864
- const allTopicNames = config2.stacks.map((stack2) => {
870
+ onBootstrap({ config, stack }) {
871
+ const allTopicNames = config.stacks.map((stack2) => {
865
872
  return Object.keys(stack2.topics || {});
866
873
  }).flat();
867
874
  const uniqueTopicNames = [...new Set(allTopicNames)];
868
875
  uniqueTopicNames.forEach((id) => {
869
876
  new Topic(stack, toId("topic", id), {
870
- topicName: `${config2.name}-${id}`,
877
+ topicName: `${config.name}-${id}`,
871
878
  displayName: id
872
879
  });
873
880
  });
874
881
  },
875
882
  onStack(ctx) {
876
- const { config: config2, stack, stackConfig, bind } = ctx;
883
+ const { config, stack, stackConfig, bind } = ctx;
877
884
  return Object.entries(stackConfig.topics || {}).map(([id, props]) => {
878
885
  const lambda = toFunction(ctx, id, props);
879
886
  const topic = Topic.fromTopicArn(
@@ -882,7 +889,7 @@ var topicPlugin = definePlugin({
882
889
  Arn2.format({
883
890
  arnFormat: ArnFormat.NO_RESOURCE_NAME,
884
891
  service: "sns",
885
- resource: `${config2.name}-${id}`
892
+ resource: `${config.name}-${id}`
886
893
  }, stack)
887
894
  );
888
895
  lambda.addEventSource(new SnsEventSource(topic));
@@ -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,56 +917,56 @@ 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
  }),
977
- onBootstrap({ config: config2, stack, assets }) {
957
+ onBootstrap({ config, stack, assets }) {
978
958
  const list3 = /* @__PURE__ */ new Set();
979
- Object.values(config2.stacks).forEach((stackConfig) => {
959
+ Object.values(config.stacks).forEach((stackConfig) => {
980
960
  Object.keys(stackConfig.graphql || {}).forEach((id) => {
981
961
  list3.add(id);
982
962
  });
983
963
  });
984
964
  list3.forEach((id) => {
985
- const file = join4(assetDir, "graphql", config2.name, id, "schema.graphql");
986
- const authorization = config2.defaults.graphql?.[id]?.authorization;
965
+ const file = join4(assetDir, "graphql", config.name, id, "schema.graphql");
966
+ const authorization = config.defaults.graphql?.[id]?.authorization;
987
967
  const authProps = {};
988
968
  if (authorization) {
989
- const authorizer = toFunction({ config: config2, assets, stack }, `${id}-authorizer`, authorization.authorizer);
969
+ const authorizer = toFunction({ config, assets, stack }, `${id}-authorizer`, authorization.authorizer);
990
970
  authProps.additionalAuthenticationProviders = [{
991
971
  authenticationType: AuthorizationType.LAMBDA,
992
972
  lambdaAuthorizerConfig: {
@@ -1010,7 +990,7 @@ var graphqlPlugin = definePlugin({
1010
990
  resourceName: id,
1011
991
  async build() {
1012
992
  const schemas = [];
1013
- await Promise.all(Object.values(config2.stacks).map(async (stackConfig) => {
993
+ await Promise.all(Object.values(config.stacks).map(async (stackConfig) => {
1014
994
  const schemaFiles = toArray(stackConfig.graphql?.[id].schema || []);
1015
995
  await Promise.all(schemaFiles.map(async (schemaFile) => {
1016
996
  const schema3 = await readFile2(schemaFile, "utf8");
@@ -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,
@@ -1029,9 +1009,9 @@ var graphqlPlugin = definePlugin({
1029
1009
  });
1030
1010
  },
1031
1011
  onStack(ctx) {
1032
- const { config: config2, stack, stackConfig } = ctx;
1012
+ const { config, stack, stackConfig } = ctx;
1033
1013
  return Object.entries(stackConfig.graphql || {}).map(([id, props]) => {
1034
- const defaults = config2.defaults.graphql?.[id] || {};
1014
+ const defaults = config.defaults.graphql?.[id] || {};
1035
1015
  return Object.entries(props.resolvers || {}).map(([typeAndField, functionProps]) => {
1036
1016
  const api = GraphqlApi.fromGraphqlApiAttributes(stack, toId("graphql", id), {
1037
1017
  graphqlApiId: Fn.importValue(toId("graphql", id))
@@ -1054,6 +1034,272 @@ var graphqlPlugin = definePlugin({
1054
1034
  }
1055
1035
  });
1056
1036
 
1037
+ // src/plugins/pubsub.ts
1038
+ import { z as z21 } from "zod";
1039
+ import { CfnTopicRule } from "aws-cdk-lib/aws-iot";
1040
+ import { PolicyStatement as PolicyStatement2 } from "aws-cdk-lib/aws-iam";
1041
+ import { snakeCase } from "change-case";
1042
+ var pubsubPlugin = definePlugin({
1043
+ name: "pubsub",
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"),
1049
+ consumer: FunctionSchema
1050
+ })).optional()
1051
+ }).array()
1052
+ }),
1053
+ onStack(ctx) {
1054
+ const { stack, stackConfig, bind } = ctx;
1055
+ bind((lambda) => {
1056
+ lambda.addToRolePolicy(new PolicyStatement2({
1057
+ actions: ["iot:publish"],
1058
+ resources: ["*"]
1059
+ }));
1060
+ });
1061
+ return Object.entries(stackConfig.pubsub || {}).map(([id, props]) => {
1062
+ const lambda = toFunction(ctx, id, props.consumer);
1063
+ new CfnTopicRule(stack, toId("pubsub", id), {
1064
+ ruleName: snakeCase(toName(stack, id)),
1065
+ topicRulePayload: {
1066
+ sql: props.sql,
1067
+ awsIotSqlVersion: props.sqlVersion,
1068
+ actions: [{
1069
+ lambda: {
1070
+ functionArn: lambda.functionArn
1071
+ }
1072
+ }]
1073
+ }
1074
+ });
1075
+ return lambda;
1076
+ });
1077
+ }
1078
+ });
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
+
1057
1303
  // src/plugins/index.ts
1058
1304
  var defaultPlugins = [
1059
1305
  functionPlugin,
@@ -1062,22 +1308,25 @@ var defaultPlugins = [
1062
1308
  tablePlugin,
1063
1309
  storePlugin,
1064
1310
  topicPlugin,
1065
- searchPlugin,
1066
- graphqlPlugin
1311
+ // searchPlugin,
1312
+ graphqlPlugin,
1313
+ pubsubPlugin,
1314
+ domainPlugin,
1315
+ httpPlugin
1067
1316
  ];
1068
1317
 
1069
1318
  // src/stack/app-bootstrap.ts
1070
- var appBootstrapStack = ({ config: config2, app, assets }) => {
1319
+ var appBootstrapStack = ({ config, app, assets }) => {
1071
1320
  const stack = new Stack3(app, "bootstrap", {
1072
- stackName: `${config2.name}-bootstrap`
1321
+ stackName: `${config.name}-bootstrap`
1073
1322
  });
1074
1323
  const plugins = [
1075
1324
  ...defaultPlugins,
1076
- ...config2.plugins || []
1325
+ ...config.plugins || []
1077
1326
  ];
1078
1327
  debug("Run plugin onBootstrap listeners");
1079
1328
  const functions = plugins.map((plugin) => plugin.onBootstrap?.({
1080
- config: config2,
1329
+ config,
1081
1330
  app,
1082
1331
  stack,
1083
1332
  assets
@@ -1101,9 +1350,9 @@ var flattenDependencyTree = (stacks) => {
1101
1350
  return list3;
1102
1351
  };
1103
1352
  var createDependencyTree = (stacks, startingLevel) => {
1104
- const list3 = stacks.map(({ stack, config: config2 }) => ({
1353
+ const list3 = stacks.map(({ stack, config }) => ({
1105
1354
  stack,
1106
- depends: config2?.depends?.map((dep) => dep.name) || []
1355
+ depends: config?.depends?.map((dep) => dep.name) || []
1107
1356
  }));
1108
1357
  const findChildren = (list4, parents, level) => {
1109
1358
  const children = [];
@@ -1175,11 +1424,11 @@ var Assets = class {
1175
1424
  };
1176
1425
 
1177
1426
  // src/app.ts
1178
- var makeApp = (config2) => {
1427
+ var makeApp = (config) => {
1179
1428
  return new App4({
1180
1429
  outdir: assemblyDir,
1181
1430
  defaultStackSynthesizer: new DefaultStackSynthesizer({
1182
- fileAssetsBucketName: assetBucketName(config2),
1431
+ fileAssetsBucketName: assetBucketName(config),
1183
1432
  fileAssetPublishingRoleArn: "",
1184
1433
  generateBootstrapVersionRule: false
1185
1434
  })
@@ -1196,26 +1445,26 @@ var getAllDepends = (filters) => {
1196
1445
  walk(filters);
1197
1446
  return list3;
1198
1447
  };
1199
- var toApp = async (config2, filters) => {
1448
+ var toApp = async (config, filters) => {
1200
1449
  const assets = new Assets();
1201
- const app = makeApp(config2);
1450
+ const app = makeApp(config);
1202
1451
  const stacks = [];
1203
1452
  const plugins = [
1204
1453
  ...defaultPlugins,
1205
- ...config2.plugins || []
1454
+ ...config.plugins || []
1206
1455
  ];
1207
1456
  debug("Plugins detected:", plugins.map((plugin) => style.info(plugin.name)).join(", "));
1208
1457
  debug("Run plugin onApp listeners");
1209
- plugins.forEach((plugin) => plugin.onApp?.({ config: config2, app, assets }));
1210
- const bootstrap2 = appBootstrapStack({ config: config2, app, assets });
1458
+ plugins.forEach((plugin) => plugin.onApp?.({ config, app, assets }));
1459
+ const bootstrap2 = appBootstrapStack({ config, app, assets });
1211
1460
  debug("Stack filters:", filters.map((filter) => style.info(filter)).join(", "));
1212
- const filterdStacks = filters.length === 0 ? config2.stacks : getAllDepends(
1461
+ const filterdStacks = filters.length === 0 ? config.stacks : getAllDepends(
1213
1462
  // config.stacks,
1214
- config2.stacks.filter((stack) => filters.includes(stack.name))
1463
+ config.stacks.filter((stack) => filters.includes(stack.name))
1215
1464
  );
1216
1465
  for (const stackConfig of filterdStacks) {
1217
1466
  const { stack, bindings } = toStack({
1218
- config: config2,
1467
+ config,
1219
1468
  stackConfig,
1220
1469
  assets,
1221
1470
  plugins,
@@ -1243,63 +1492,6 @@ var toApp = async (config2, filters) => {
1243
1492
  };
1244
1493
  };
1245
1494
 
1246
- // src/cli/ui/layout/basic.ts
1247
- var br = () => {
1248
- return "\n";
1249
- };
1250
- var hr = () => {
1251
- return (term) => {
1252
- term.out.write([
1253
- style.placeholder("\u2500".repeat(term.out.width())),
1254
- br()
1255
- ]);
1256
- };
1257
- };
1258
-
1259
- // src/cli/ui/layout/logs.ts
1260
- import wrapAnsi from "wrap-ansi";
1261
- var previous = /* @__PURE__ */ new Date();
1262
- var logs = () => {
1263
- if (!process.env.VERBOSE) {
1264
- return [];
1265
- }
1266
- const logs2 = flushDebug();
1267
- return (term) => {
1268
- term.out.write([
1269
- hr(),
1270
- br(),
1271
- " ".repeat(3),
1272
- style.label("Debug Logs:"),
1273
- br(),
1274
- br(),
1275
- logs2.map((log) => {
1276
- const diff = log.date.getTime() - previous.getTime();
1277
- const time = `+${diff}`.padStart(8);
1278
- previous = log.date;
1279
- return wrapAnsi([
1280
- style.attr(`${time}${style.attr.dim("ms")}`),
1281
- " [ ",
1282
- log.type,
1283
- " ] ",
1284
- log.message,
1285
- br(),
1286
- log.type === "error" ? br() : ""
1287
- ].join(""), term.out.width(), { hard: true, trim: false });
1288
- }),
1289
- br(),
1290
- hr()
1291
- ]);
1292
- };
1293
- };
1294
-
1295
- // src/cli/ui/layout/footer.ts
1296
- var footer = () => {
1297
- return [
1298
- br(),
1299
- logs()
1300
- ];
1301
- };
1302
-
1303
1495
  // src/config.ts
1304
1496
  import { join as join6 } from "path";
1305
1497
 
@@ -1320,17 +1512,17 @@ var getCredentials = (profile) => {
1320
1512
  };
1321
1513
 
1322
1514
  // src/schema/app.ts
1323
- import { z as z25 } from "zod";
1515
+ import { z as z30 } from "zod";
1324
1516
 
1325
1517
  // src/schema/stack.ts
1326
- import { z as z22 } from "zod";
1327
- var StackSchema = z22.object({
1518
+ import { z as z27 } from "zod";
1519
+ var StackSchema = z27.object({
1328
1520
  name: ResourceIdSchema,
1329
- depends: z22.array(z22.lazy(() => StackSchema)).optional()
1521
+ depends: z27.array(z27.lazy(() => StackSchema)).optional()
1330
1522
  });
1331
1523
 
1332
1524
  // src/schema/region.ts
1333
- import { z as z23 } from "zod";
1525
+ import { z as z28 } from "zod";
1334
1526
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
1335
1527
  var AF = ["af-south-1"];
1336
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"];
@@ -1347,34 +1539,34 @@ var regions = [
1347
1539
  ...ME,
1348
1540
  ...SA
1349
1541
  ];
1350
- var RegionSchema = z23.enum(regions);
1542
+ var RegionSchema = z28.enum(regions);
1351
1543
 
1352
1544
  // src/schema/plugin.ts
1353
- import { z as z24 } from "zod";
1354
- var PluginSchema = z24.object({
1355
- name: z24.string(),
1356
- schema: z24.custom().optional(),
1545
+ import { z as z29 } from "zod";
1546
+ var PluginSchema = z29.object({
1547
+ name: z29.string(),
1548
+ schema: z29.custom().optional(),
1357
1549
  // depends: z.array(z.lazy(() => PluginSchema)).optional(),
1358
- onBootstrap: z24.function().returns(z24.any()).optional(),
1359
- onStack: z24.function().returns(z24.any()).optional(),
1360
- onApp: z24.function().returns(z24.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()
1361
1553
  // bind: z.function().optional(),
1362
1554
  });
1363
1555
 
1364
1556
  // src/schema/app.ts
1365
- var AppSchema = z25.object({
1557
+ var AppSchema = z30.object({
1366
1558
  name: ResourceIdSchema,
1367
1559
  region: RegionSchema,
1368
- profile: z25.string(),
1369
- stage: z25.string().regex(/[a-z]+/).default("prod"),
1370
- defaults: z25.object({}).default({}),
1371
- stacks: z25.array(StackSchema).min(1),
1372
- plugins: z25.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()
1373
1565
  });
1374
1566
 
1375
1567
  // src/util/import.ts
1376
1568
  import { transformFile } from "@swc/core";
1377
- import { dirname as dirname2, join as join5 } from "path";
1569
+ import { dirname as dirname3, join as join5 } from "path";
1378
1570
  import { lstat, mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
1379
1571
  var resolveFileNameExtension = async (path) => {
1380
1572
  const options = [
@@ -1385,7 +1577,7 @@ var resolveFileNameExtension = async (path) => {
1385
1577
  "/index.js"
1386
1578
  ];
1387
1579
  for (const option of options) {
1388
- const file = path + option;
1580
+ const file = path.replace(/\.js$/, "") + option;
1389
1581
  let stat;
1390
1582
  try {
1391
1583
  stat = await lstat(file);
@@ -1399,21 +1591,22 @@ var resolveFileNameExtension = async (path) => {
1399
1591
  throw new Error(`Failed to load file: ${path}`);
1400
1592
  };
1401
1593
  var resolveDir = (path) => {
1402
- return dirname2(path).replace(rootDir + "/", "");
1594
+ return dirname3(path).replace(rootDir + "/", "");
1403
1595
  };
1404
1596
  var importFile = async (path) => {
1405
1597
  const load = async (file) => {
1598
+ debug("Load file", file);
1406
1599
  let { code: code2 } = await transformFile(file, {
1407
1600
  isModule: true
1408
1601
  });
1409
- const path2 = dirname2(file);
1602
+ const path2 = dirname3(file);
1410
1603
  const dir = resolveDir(file);
1411
1604
  code2 = code2.replaceAll("__dirname", `"${dir}"`);
1412
- 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);
1413
1606
  if (!matches)
1414
1607
  return code2;
1415
1608
  await Promise.all(matches?.map(async (match) => {
1416
- const parts = /('|")(\.[\/a-z0-9\_\-]+)('|")/ig.exec(match);
1609
+ const parts = /('|")(\.\.?[\/a-z0-9\_\-\.]+)('|")/ig.exec(match);
1417
1610
  const from = parts[2];
1418
1611
  const file2 = await resolveFileNameExtension(join5(path2, from));
1419
1612
  const result = await load(file2);
@@ -1433,11 +1626,7 @@ var importConfig = async (options) => {
1433
1626
  debug("Import config file");
1434
1627
  const fileName = join6(process.cwd(), options.configFile || "awsless.config.ts");
1435
1628
  const module = await importFile(fileName);
1436
- const appConfig = typeof module.default === "function" ? await module.default({
1437
- profile: options.profile,
1438
- region: options.region,
1439
- stage: options.stage
1440
- }) : module.default;
1629
+ const appConfig = typeof module.default === "function" ? await module.default(options) : module.default;
1441
1630
  debug("Validate config file");
1442
1631
  const plugins = [
1443
1632
  ...defaultPlugins,
@@ -1449,19 +1638,32 @@ var importConfig = async (options) => {
1449
1638
  schema2 = schema2.and(plugin.schema);
1450
1639
  }
1451
1640
  }
1452
- const config2 = await schema2.parseAsync(appConfig);
1453
- debug("Load credentials", style.info(config2.profile));
1454
- const credentials = getCredentials(config2.profile);
1641
+ const config = await schema2.parseAsync(appConfig);
1642
+ debug("Load credentials", style.info(config.profile));
1643
+ const credentials = getCredentials(config.profile);
1455
1644
  debug("Load AWS account ID");
1456
- const account = await getAccountId(credentials, config2.region);
1645
+ const account = await getAccountId(credentials, config.region);
1457
1646
  debug("Account ID:", style.info(account));
1458
1647
  return {
1459
- ...config2,
1648
+ ...config,
1460
1649
  account,
1461
1650
  credentials
1462
1651
  };
1463
1652
  };
1464
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
+
1465
1667
  // src/cli/ui/layout/list.ts
1466
1668
  var list = (data) => {
1467
1669
  const padding = 3;
@@ -1469,26 +1671,26 @@ var list = (data) => {
1469
1671
  const size = Object.keys(data).reduce((total, name) => {
1470
1672
  return name.length > total ? name.length : total;
1471
1673
  }, 0);
1472
- return Object.entries(data).map(([name, value]) => [
1473
- " ".repeat(padding),
1474
- style.label((name + ":").padEnd(size + gap + 1)),
1475
- value,
1476
- br()
1477
- ]);
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
+ };
1478
1684
  };
1479
1685
 
1480
1686
  // src/cli/ui/layout/header.ts
1481
- var header = (config2) => {
1482
- return [
1483
- br(),
1484
- list({
1485
- App: config2.name,
1486
- Stage: config2.stage,
1487
- Region: config2.region,
1488
- Profile: config2.profile
1489
- }),
1490
- br()
1491
- ];
1687
+ var header = (config) => {
1688
+ return list({
1689
+ App: config.name,
1690
+ Stage: config.stage,
1691
+ Region: config.region,
1692
+ Profile: config.profile
1693
+ });
1492
1694
  };
1493
1695
 
1494
1696
  // src/util/timer.ts
@@ -1555,16 +1757,16 @@ var createSpinner = () => {
1555
1757
  };
1556
1758
 
1557
1759
  // src/cli/ui/layout/dialog.ts
1558
- import wrapAnsi2 from "wrap-ansi";
1760
+ import wrapAnsi from "wrap-ansi";
1559
1761
  var dialog = (type, lines) => {
1560
1762
  const padding = 3;
1561
1763
  const icon = style[type](symbol[type].padEnd(padding));
1562
1764
  return (term) => {
1563
1765
  term.out.write(lines.map((line, i) => {
1564
1766
  if (i === 0) {
1565
- return icon + wrapAnsi2(line, term.out.width(), { hard: true });
1767
+ return icon + wrapAnsi(line, term.out.width(), { hard: true });
1566
1768
  }
1567
- return wrapAnsi2(" ".repeat(padding) + line, term.out.width(), { hard: true });
1769
+ return wrapAnsi(" ".repeat(padding) + line, term.out.width(), { hard: true });
1568
1770
  }).join(br()) + br());
1569
1771
  };
1570
1772
  };
@@ -1706,6 +1908,7 @@ var Renderer = class {
1706
1908
  fragments = [];
1707
1909
  unsubs = [];
1708
1910
  timeout;
1911
+ flushing = false;
1709
1912
  screen = [];
1710
1913
  width() {
1711
1914
  return this.output.columns;
@@ -1725,14 +1928,58 @@ var Renderer = class {
1725
1928
  this.update();
1726
1929
  return fragment;
1727
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
+ }
1728
1950
  update() {
1729
1951
  clearTimeout(this.timeout);
1730
1952
  this.timeout = setTimeout(() => {
1731
1953
  this.flush();
1732
1954
  }, 0);
1733
1955
  }
1734
- 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() {
1735
1978
  clearTimeout(this.timeout);
1979
+ if (this.flushing) {
1980
+ this.update();
1981
+ return;
1982
+ }
1736
1983
  const walk = (fragment) => {
1737
1984
  if (typeof fragment === "string") {
1738
1985
  return fragment;
@@ -1748,34 +1995,40 @@ var Renderer = class {
1748
1995
  this.unsubs.forEach((unsub) => unsub());
1749
1996
  this.unsubs = [];
1750
1997
  const screen = walk(this.fragments).split("\n");
1998
+ const height = this.height();
1751
1999
  const oldSize = this.screen.length;
1752
2000
  const newSize = screen.length;
1753
2001
  const size = Math.max(oldSize, newSize);
1754
- const height = this.height();
1755
2002
  const start = Math.max(oldSize - height, 0);
2003
+ this.flushing = true;
1756
2004
  for (let y = start; y < size; y++) {
1757
- const line = screen[y];
1758
- if (line !== this.screen[y]) {
1759
- if (y > oldSize) {
1760
- const x = (this.screen[y - 1]?.length || 0) - 1;
1761
- this.output.cursorTo?.(x, y - 1 - start);
1762
- 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);
1763
2013
  } else {
1764
- this.output.cursorTo?.(0, y - start);
1765
- this.output.write?.(line);
2014
+ await this.setCursor(0, y - start);
2015
+ await this.writeString(newLine);
2016
+ await this.clearLine();
1766
2017
  }
1767
- this.output.clearLine?.(1);
1768
2018
  }
1769
2019
  }
1770
2020
  this.screen = screen;
1771
- }
1772
- clear() {
1773
- let count = this.output.rows;
1774
- while (count--) {
1775
- 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
+ });
1776
2031
  }
1777
- this.output.cursorTo?.(0, 0);
1778
- this.output.clearScreenDown?.();
1779
2032
  }
1780
2033
  };
1781
2034
 
@@ -1791,22 +2044,62 @@ var logo = () => {
1791
2044
  return [
1792
2045
  style.warning("\u26A1\uFE0F "),
1793
2046
  style.primary("AWS"),
1794
- style.primary.dim("LESS"),
1795
- br()
2047
+ style.primary.dim("LESS")
1796
2048
  ];
1797
2049
  };
1798
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
+
1799
2088
  // src/cli/ui/layout/layout.ts
1800
2089
  var layout = async (cb) => {
1801
2090
  const term = createTerminal();
1802
- term.out.clear();
2091
+ await term.out.clear();
2092
+ term.out.write(br());
1803
2093
  term.out.write(logo());
2094
+ term.out.gap();
1804
2095
  try {
1805
2096
  const options = program.optsWithGlobals();
1806
- const config2 = await importConfig(options);
1807
- term.out.write(header(config2));
1808
- await cb(config2, term.out.write.bind(term.out), term);
2097
+ const config = await importConfig(options);
2098
+ term.out.write(header(config));
2099
+ term.out.gap();
2100
+ await cb(config, term.out.write.bind(term.out), term);
1809
2101
  } catch (error) {
2102
+ term.out.gap();
1810
2103
  if (error instanceof Error) {
1811
2104
  term.out.write(dialog("error", [error.message]));
1812
2105
  } else if (typeof error === "string") {
@@ -1817,7 +2110,9 @@ var layout = async (cb) => {
1817
2110
  debugError(error);
1818
2111
  } finally {
1819
2112
  debug("Exit");
1820
- term.out.write(footer());
2113
+ term.out.gap();
2114
+ term.out.write(logs());
2115
+ await term.out.end();
1821
2116
  term.in.unref();
1822
2117
  setTimeout(() => {
1823
2118
  process.exit(0);
@@ -1849,7 +2144,8 @@ var flexLine = (term, left, right, reserveSpace = 0) => {
1849
2144
  var assetBuilder = (assets) => {
1850
2145
  return async (term) => {
1851
2146
  const done = term.out.write(loadingDialog("Building stack assets..."));
1852
- const groups = new Signal([br()]);
2147
+ const groups = new Signal([""]);
2148
+ term.out.gap();
1853
2149
  term.out.write(groups);
1854
2150
  const stackNameSize = Math.max(...Object.keys(assets.list()).map((stack) => stack.length));
1855
2151
  const resourceSize = Math.max(...Object.values(assets.list()).map((assets2) => assets2.map((asset) => asset.resource.length)).flat());
@@ -1895,6 +2191,7 @@ var assetBuilder = (assets) => {
1895
2191
  }));
1896
2192
  }));
1897
2193
  done("Done building stack assets");
2194
+ term.out.gap();
1898
2195
  };
1899
2196
  };
1900
2197
 
@@ -1920,8 +2217,8 @@ var cleanUp = async () => {
1920
2217
  // src/cli/command/build.ts
1921
2218
  var build = (program2) => {
1922
2219
  program2.command("build").argument("[stack...]", "Optionally filter stacks to build").description("Build your app").action(async (filters) => {
1923
- await layout(async (config2, write) => {
1924
- const { app, assets } = await toApp(config2, filters);
2220
+ await layout(async (config, write) => {
2221
+ const { app, assets } = await toApp(config, filters);
1925
2222
  await cleanUp();
1926
2223
  await write(assetBuilder(assets));
1927
2224
  app.synth();
@@ -1934,11 +2231,11 @@ import { CloudFormationClient, CreateStackCommand, DeleteStackCommand, DescribeS
1934
2231
  import { S3Client as S3Client2, PutObjectCommand as PutObjectCommand2, ObjectCannedACL as ObjectCannedACL2, StorageClass as StorageClass2 } from "@aws-sdk/client-s3";
1935
2232
  var StackClient = class {
1936
2233
  // 30 seconds
1937
- constructor(config2) {
1938
- this.config = config2;
2234
+ constructor(config) {
2235
+ this.config = config;
1939
2236
  this.client = new CloudFormationClient({
1940
- credentials: config2.credentials,
1941
- region: config2.region
2237
+ credentials: config.credentials,
2238
+ region: config.region
1942
2239
  });
1943
2240
  }
1944
2241
  client;
@@ -2167,12 +2464,12 @@ var confirmPrompt = (label, options = {}) => {
2167
2464
  };
2168
2465
 
2169
2466
  // src/cli/ui/complex/bootstrap.ts
2170
- var bootstrapDeployer = (config2) => {
2467
+ var bootstrapDeployer = (config) => {
2171
2468
  return async (term) => {
2172
2469
  debug("Initializing bootstrap");
2173
- const app = makeApp(config2);
2174
- const client = new StackClient(config2);
2175
- const bootstrap2 = bootstrapStack(config2, app);
2470
+ const app = makeApp(config);
2471
+ const client = new StackClient(config);
2472
+ const bootstrap2 = bootstrapStack(config, app);
2176
2473
  const shouldDeploy = await shouldDeployBootstrap(client, bootstrap2.stackName);
2177
2474
  if (shouldDeploy) {
2178
2475
  term.out.write(dialog("warning", [`Your app hasn't been bootstrapped yet`]));
@@ -2196,8 +2493,8 @@ var bootstrapDeployer = (config2) => {
2196
2493
  // src/cli/command/bootstrap.ts
2197
2494
  var bootstrap = (program2) => {
2198
2495
  program2.command("bootstrap").description("Create the awsless bootstrap stack").action(async () => {
2199
- await layout(async (config2, write) => {
2200
- await write(bootstrapDeployer(config2));
2496
+ await layout(async (config, write) => {
2497
+ await write(bootstrapDeployer(config));
2201
2498
  });
2202
2499
  });
2203
2500
  };
@@ -2234,29 +2531,28 @@ var stackTree = (nodes, statuses) => {
2234
2531
  render(node.children, deep + 1, [...parents, more]);
2235
2532
  });
2236
2533
  };
2534
+ term.out.gap();
2237
2535
  render(nodes);
2536
+ term.out.gap();
2238
2537
  };
2239
2538
  };
2240
2539
 
2241
2540
  // src/cli/command/status.ts
2242
2541
  var status = (program2) => {
2243
2542
  program2.command("status").argument("[stacks...]", "Optionally filter stacks to lookup status").description("View the application status").action(async (filters) => {
2244
- await layout(async (config2, write) => {
2245
- const { app, assets, dependencyTree } = await toApp(config2, filters);
2543
+ await layout(async (config, write) => {
2544
+ const { app, assets, dependencyTree } = await toApp(config, filters);
2246
2545
  await cleanUp();
2247
2546
  await write(assetBuilder(assets));
2248
- write(br());
2249
2547
  const assembly = app.synth();
2250
2548
  const doneLoading = write(loadingDialog("Loading stack information..."));
2251
- const client = new StackClient(config2);
2549
+ const client = new StackClient(config);
2252
2550
  const statuses = [];
2253
2551
  const stackStatuses = {};
2254
2552
  assembly.stacks.forEach((stack) => {
2255
2553
  stackStatuses[stack.id] = new Signal(style.info("Loading..."));
2256
2554
  });
2257
- write(br());
2258
2555
  write(stackTree(dependencyTree, stackStatuses));
2259
- write(br());
2260
2556
  debug("Load metadata for all deployed stacks on AWS");
2261
2557
  await Promise.all(assembly.stacks.map(async (stack, i) => {
2262
2558
  const info = await client.get(stack.stackName);
@@ -2288,9 +2584,9 @@ var status = (program2) => {
2288
2584
  // src/cli/command/deploy.ts
2289
2585
  var deploy = (program2) => {
2290
2586
  program2.command("deploy").argument("[stacks...]", "Optionally filter stacks to deploy").description("Deploy your app to AWS").action(async (filters) => {
2291
- await layout(async (config2, write) => {
2292
- await write(bootstrapDeployer(config2));
2293
- const { app, stackNames, assets, dependencyTree } = await toApp(config2, filters);
2587
+ await layout(async (config, write) => {
2588
+ await write(bootstrapDeployer(config));
2589
+ const { app, stackNames, assets, dependencyTree } = await toApp(config, filters);
2294
2590
  const formattedFilter = stackNames.map((i) => style.info(i)).join(style.placeholder(", "));
2295
2591
  debug("Stacks to deploy", formattedFilter);
2296
2592
  const deployAll = filters.length === 0;
@@ -2301,8 +2597,6 @@ var deploy = (program2) => {
2301
2597
  }
2302
2598
  await cleanUp();
2303
2599
  await write(assetBuilder(assets));
2304
- write(br());
2305
- write(br());
2306
2600
  const donePublishing = write(loadingDialog("Publishing stack assets to AWS..."));
2307
2601
  await Promise.all(assets.map(async (_, assets2) => {
2308
2602
  await Promise.all(assets2.map(async (asset) => {
@@ -2316,10 +2610,8 @@ var deploy = (program2) => {
2316
2610
  statuses[stack.id] = new Signal(style.info("waiting"));
2317
2611
  });
2318
2612
  const doneDeploying = write(loadingDialog("Deploying stacks to AWS..."));
2319
- write(br());
2320
2613
  write(stackTree(dependencyTree, statuses));
2321
- write(br());
2322
- const client = new StackClient(config2);
2614
+ const client = new StackClient(config);
2323
2615
  const deploymentLine = createDeploymentLine(dependencyTree);
2324
2616
  for (const stacks of deploymentLine) {
2325
2617
  const results = await Promise.allSettled(stacks.map(async (stack) => {
@@ -2408,36 +2700,34 @@ var textPrompt = (label, options = {}) => {
2408
2700
  };
2409
2701
  };
2410
2702
 
2411
- // src/cli/command/config/set.ts
2703
+ // src/cli/command/secrets/set.ts
2412
2704
  var set = (program2) => {
2413
- program2.command("set <name>").description("Set a config value").action(async (name) => {
2414
- await layout(async (config2, write) => {
2415
- const params = new Params(config2);
2705
+ program2.command("set <name>").description("Set a secret value").action(async (name) => {
2706
+ await layout(async (config, write) => {
2707
+ const params = new Params(config);
2416
2708
  write(list({
2417
- "Set config parameter": style.info(name)
2709
+ "Set secret parameter": style.info(name)
2418
2710
  }));
2419
- write(br());
2420
- const value = await write(textPrompt("Enter config value"));
2711
+ const value = await write(textPrompt("Enter secret value"));
2421
2712
  if (value === "") {
2422
- write(dialog("error", [`Provided config value can't be empty`]));
2713
+ write(dialog("error", [`Provided secret value can't be empty`]));
2423
2714
  } else {
2424
- const done = write(loadingDialog(`Saving remote config parameter`));
2715
+ const done = write(loadingDialog(`Saving remote secret parameter`));
2425
2716
  await params.set(name, value);
2426
- done(`Done saving remote config parameter`);
2717
+ done(`Done saving remote secret parameter`);
2427
2718
  }
2428
2719
  });
2429
2720
  });
2430
2721
  };
2431
2722
 
2432
- // src/cli/command/config/get.ts
2723
+ // src/cli/command/secrets/get.ts
2433
2724
  var get = (program2) => {
2434
- program2.command("get <name>").description("Get a config value").action(async (name) => {
2435
- await layout(async (config2, write) => {
2436
- const params = new Params(config2);
2437
- const done = write(loadingDialog(`Getting remote config parameter`));
2725
+ program2.command("get <name>").description("Get a secret value").action(async (name) => {
2726
+ await layout(async (config, write) => {
2727
+ const params = new Params(config);
2728
+ const done = write(loadingDialog(`Getting remote secret parameter`));
2438
2729
  const value = await params.get(name);
2439
- done(`Done getting remote config parameter`);
2440
- write(br());
2730
+ done(`Done getting remote secret parameter`);
2441
2731
  write(list({
2442
2732
  Name: name,
2443
2733
  Value: value || style.error("(empty)")
@@ -2446,21 +2736,20 @@ var get = (program2) => {
2446
2736
  });
2447
2737
  };
2448
2738
 
2449
- // src/cli/command/config/delete.ts
2739
+ // src/cli/command/secrets/delete.ts
2450
2740
  var del = (program2) => {
2451
- program2.command("delete <name>").description("Delete a config value").action(async (name) => {
2452
- await layout(async (config2, write) => {
2453
- const params = new Params(config2);
2454
- write(dialog("warning", [`Your deleting the ${style.info(name)} config parameter`]));
2741
+ program2.command("delete <name>").description("Delete a secret value").action(async (name) => {
2742
+ await layout(async (config, write) => {
2743
+ const params = new Params(config);
2744
+ write(dialog("warning", [`Your deleting the ${style.info(name)} secret parameter`]));
2455
2745
  const confirm = await write(confirmPrompt("Are you sure?"));
2456
2746
  if (!confirm) {
2457
2747
  throw new Cancelled();
2458
2748
  }
2459
- const done = write(loadingDialog(`Deleting remote config parameter`));
2749
+ const done = write(loadingDialog(`Deleting remote secret parameter`));
2460
2750
  const value = await params.get(name);
2461
2751
  await params.delete(name);
2462
- done(`Done deleting remote config parameter`);
2463
- write(br());
2752
+ done(`Done deleting remote secret parameter`);
2464
2753
  write(list({
2465
2754
  Name: name,
2466
2755
  Value: value || style.error("(empty)")
@@ -2469,45 +2758,47 @@ var del = (program2) => {
2469
2758
  });
2470
2759
  };
2471
2760
 
2472
- // src/cli/command/config/list.ts
2761
+ // src/cli/command/secrets/list.ts
2473
2762
  var list2 = (program2) => {
2474
- program2.command("list").description(`List all config value's`).action(async () => {
2475
- await layout(async (config2, write) => {
2476
- const params = new Params(config2);
2477
- const done = write(loadingDialog("Loading config parameters..."));
2763
+ program2.command("list").description(`List all secret value's`).action(async () => {
2764
+ await layout(async (config, write) => {
2765
+ const params = new Params(config);
2766
+ const done = write(loadingDialog("Loading secret parameters..."));
2478
2767
  const values = await params.list();
2479
- done("Done loading config values");
2768
+ done("Done loading secret values");
2480
2769
  if (Object.keys(values).length > 0) {
2481
- write(br());
2482
2770
  write(list(values));
2483
2771
  } else {
2484
- write(dialog("warning", ["No config parameters found"]));
2772
+ write(dialog("warning", ["No secret parameters found"]));
2485
2773
  }
2486
2774
  });
2487
2775
  });
2488
2776
  };
2489
2777
 
2490
- // src/cli/command/config/index.ts
2778
+ // src/cli/command/secrets/index.ts
2491
2779
  var commands = [
2492
2780
  set,
2493
2781
  get,
2494
2782
  del,
2495
2783
  list2
2496
2784
  ];
2497
- var config = (program2) => {
2498
- const command = program2.command("config").description("Manage config values");
2785
+ var secrets = (program2) => {
2786
+ const command = program2.command("secrets").description(`Manage app secrets`);
2499
2787
  commands.forEach((cb) => cb(command));
2500
2788
  };
2501
2789
 
2502
2790
  // src/cli/program.ts
2503
2791
  var program = new Command();
2504
- program.name("awsless");
2792
+ program.name(logo().join("").replace(/\s+/, ""));
2505
2793
  program.option("--config-file <string>", "The config file location");
2506
2794
  program.option("--stage <string>", "The stage to use, defaults to prod stage", "prod");
2507
2795
  program.option("--profile <string>", "The AWS profile to use");
2508
2796
  program.option("--region <string>", "The AWS region to use");
2509
2797
  program.option("-m --mute", "Mute sound effects");
2510
2798
  program.option("-v --verbose", "Print verbose logs");
2799
+ program.exitOverride(() => {
2800
+ process.exit(0);
2801
+ });
2511
2802
  program.on("option:verbose", () => {
2512
2803
  process.env.VERBOSE = program.opts().verbose ? "1" : void 0;
2513
2804
  });
@@ -2516,13 +2807,11 @@ var commands2 = [
2516
2807
  status,
2517
2808
  build,
2518
2809
  deploy,
2519
- config
2810
+ secrets
2520
2811
  // diff,
2521
2812
  // remove,
2522
- // test,
2523
- // test2,
2524
2813
  ];
2525
- commands2.forEach((command) => command(program));
2814
+ commands2.forEach((fn) => fn(program));
2526
2815
 
2527
2816
  // src/bin.ts
2528
2817
  program.parse(process.argv);