@awsless/awsless 0.0.98 → 0.0.99

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
@@ -14,6 +14,7 @@ var symbol = {
14
14
  warning: "\u26A0",
15
15
  question: "?",
16
16
  error: "\u2716",
17
+ dot: "\xB7",
17
18
  ellipsis: "\u2026",
18
19
  pointerSmall: "\u203A",
19
20
  // line: '─',
@@ -594,8 +595,8 @@ var Stack = class {
594
595
  return node;
595
596
  };
596
597
  for (const resource of this.resources) {
597
- const json3 = walk(resource.toJSON());
598
- Object.assign(resources, json3);
598
+ const json4 = walk(resource.toJSON());
599
+ Object.assign(resources, json4);
599
600
  }
600
601
  for (let [name, value] of this.exports.entries()) {
601
602
  Object.assign(outputs, {
@@ -622,9 +623,9 @@ var Stack = class {
622
623
  };
623
624
 
624
625
  // src/stack.ts
625
- var toStack = ({ config, app, stackConfig, bootstrap: bootstrap2, usEastBootstrap, plugins }) => {
626
+ var toStack = ({ config: config2, app, stackConfig, bootstrap: bootstrap2, usEastBootstrap, plugins, tests }) => {
626
627
  const name = stackConfig.name;
627
- const stack = new Stack(name, config.region).tag("app", config.name).tag("stage", config.stage).tag("stack", name);
628
+ const stack = new Stack(name, config2.region).tag("app", config2.name).tag("stage", config2.stage).tag("stack", name);
628
629
  debug("Define stack:", style.info(name));
629
630
  debug("Run plugin onStack listeners");
630
631
  const bindings = [];
@@ -633,12 +634,13 @@ var toStack = ({ config, app, stackConfig, bootstrap: bootstrap2, usEastBootstra
633
634
  };
634
635
  for (const plugin of plugins) {
635
636
  plugin.onStack?.({
636
- config,
637
+ config: config2,
637
638
  app,
638
639
  stack,
639
640
  stackConfig,
640
641
  bootstrap: bootstrap2,
641
642
  usEastBootstrap,
643
+ tests,
642
644
  bind
643
645
  });
644
646
  }
@@ -660,9 +662,9 @@ var toStack = ({ config, app, stackConfig, bootstrap: bootstrap2, usEastBootstra
660
662
 
661
663
  // src/util/deployment.ts
662
664
  var createDeploymentLine = (stacks) => {
663
- const list3 = stacks.map(({ stack, config }) => ({
665
+ const list3 = stacks.map(({ stack, config: config2 }) => ({
664
666
  stack,
665
- depends: config?.depends?.map((dep) => dep.name) || []
667
+ depends: config2?.depends?.map((dep) => dep.name) || []
666
668
  }));
667
669
  const names = stacks.map(({ stack }) => stack.name);
668
670
  const line2 = [];
@@ -984,9 +986,68 @@ var zipFiles = (files) => {
984
986
  };
985
987
 
986
988
  // src/formation/resource/lambda/code.ts
987
- import { readFile } from "fs/promises";
989
+ import { readFile as readFile2 } from "fs/promises";
988
990
  import { fileURLToPath } from "url";
989
- import { join } from "path";
991
+ import { join as join2 } from "path";
992
+
993
+ // src/util/fingerprint.ts
994
+ import { createHash as createHash2 } from "crypto";
995
+ import { readFile, stat as stat2 } from "fs/promises";
996
+ import { basename, dirname as dirname2, join } from "path";
997
+ import parseImports from "parse-imports";
998
+ var generateFingerprint = async (file) => {
999
+ const hashes = /* @__PURE__ */ new Map();
1000
+ const generate = async (file2) => {
1001
+ if (hashes.has(file2)) {
1002
+ return;
1003
+ }
1004
+ const code = await readModuleFile(file2);
1005
+ const deps = await findDependencies(file2, code);
1006
+ const hash = createHash2("sha1").update(code).digest();
1007
+ hashes.set(file2, hash);
1008
+ for (const dep of deps) {
1009
+ if (dep.startsWith("/")) {
1010
+ await generate(dep);
1011
+ }
1012
+ }
1013
+ };
1014
+ await generate(file);
1015
+ const merge = Buffer.concat(Array.from(hashes.values()));
1016
+ return createHash2("sha1").update(merge).digest("hex");
1017
+ };
1018
+ var readModuleFile = (file) => {
1019
+ if (file.endsWith(".js")) {
1020
+ return readFiles([file, file.substring(0, file.length - 3) + ".ts"]);
1021
+ }
1022
+ if (!basename(file).includes(".")) {
1023
+ const extensions = ["js", "mjs", "jsx", "ts", "mts", "tsx"];
1024
+ return readFiles([
1025
+ file,
1026
+ ...extensions.map((exp) => `${file}.${exp}`),
1027
+ ...extensions.map((exp) => join(file, `/index.${exp}`))
1028
+ ]);
1029
+ }
1030
+ return readFile(file, "utf8");
1031
+ };
1032
+ var readFiles = async (files) => {
1033
+ for (const file of files) {
1034
+ try {
1035
+ const s = await stat2(file);
1036
+ if (s.isFile()) {
1037
+ return readFile(file, "utf8");
1038
+ }
1039
+ } catch (_) {
1040
+ continue;
1041
+ }
1042
+ }
1043
+ throw new Error(`No such file: ${files.join(", ")}`);
1044
+ };
1045
+ var findDependencies = async (file, code) => {
1046
+ const imports = Array.from(await parseImports(code));
1047
+ return imports.map((entry) => entry.moduleSpecifier.value).filter(Boolean).map((value) => value?.startsWith(".") ? join(dirname2(file), value) : value);
1048
+ };
1049
+
1050
+ // src/formation/resource/lambda/code.ts
990
1051
  var Code = class {
991
1052
  static fromFile(id, file, bundler) {
992
1053
  return new FileCode(id, file, bundler);
@@ -1029,18 +1090,28 @@ var InlineFileCode = class extends Asset {
1029
1090
  }
1030
1091
  code;
1031
1092
  handler;
1032
- async build({ write }) {
1033
- const bundler = this.bundler ?? rollupBundle();
1034
- const {
1035
- hash,
1036
- files: [file],
1037
- handler
1038
- } = await bundler(this.file);
1039
- await Promise.all([write("HASH", hash), write("file.js", file.code)]);
1040
- this.handler = handler;
1041
- this.code = file.code.toString("utf8");
1093
+ async build({ read, write }) {
1094
+ const fingerprint = await generateFingerprint(this.file);
1095
+ await write(fingerprint, async (write2) => {
1096
+ const bundler = this.bundler ?? rollupBundle();
1097
+ const {
1098
+ hash,
1099
+ files: [file],
1100
+ handler: handler2
1101
+ } = await bundler(this.file);
1102
+ await Promise.all([
1103
+ write2("HASH", hash),
1104
+ write2("SIZE", formatByteSize(file.code.byteLength)),
1105
+ write2("HANDLER", handler2),
1106
+ write2("file.js", file.code),
1107
+ file.map && write2("file.map", file.map)
1108
+ ]);
1109
+ });
1110
+ const [handler, size, code] = await read(fingerprint, ["HANDLER", "SIZE", "file.js"]);
1111
+ this.handler = handler.toString("utf8");
1112
+ this.code = code.toString("utf8");
1042
1113
  return {
1043
- size: formatByteSize(file.code.byteLength)
1114
+ size: size.toString("utf8")
1044
1115
  };
1045
1116
  }
1046
1117
  toCodeJson() {
@@ -1058,33 +1129,39 @@ var FileCode = class extends Asset {
1058
1129
  this.file = file;
1059
1130
  this.bundler = bundler;
1060
1131
  }
1132
+ fingerprint;
1061
1133
  handler;
1062
- hash;
1063
- bundle;
1134
+ // private hash?: string
1135
+ // private bundle?: Buffer
1064
1136
  s3;
1065
- async build({ write }) {
1066
- const bundler = this.bundler ?? rollupBundle();
1067
- const { hash, files, handler } = await bundler(this.file);
1068
- const bundle = await zipFiles(files);
1069
- await Promise.all([
1070
- write("HASH", hash),
1071
- write("bundle.zip", bundle),
1072
- ...files.map((file) => write(`files/${file.name}`, file.code)),
1073
- ...files.map((file) => file.map ? write(`files/${file.name}.map`, file.map) : void 0)
1074
- ]);
1075
- this.handler = handler;
1076
- this.bundle = bundle;
1077
- this.hash = hash;
1137
+ async build({ write, read }) {
1138
+ this.fingerprint = await generateFingerprint(this.file);
1139
+ await write(this.fingerprint, async (write2) => {
1140
+ const bundler = this.bundler ?? rollupBundle();
1141
+ const { hash, files, handler } = await bundler(this.file);
1142
+ const bundle = await zipFiles(files);
1143
+ await Promise.all([
1144
+ write2("HASH", hash),
1145
+ write2("SIZE", formatByteSize(bundle.byteLength)),
1146
+ write2("HANDLER", handler),
1147
+ write2("bundle.zip", bundle),
1148
+ ...files.map((file) => write2(`files/${file.name}`, file.code)),
1149
+ ...files.map((file) => file.map && write2(`files/${file.name}.map`, file.map))
1150
+ ]);
1151
+ });
1152
+ const [size] = await read(this.fingerprint, ["SIZE"]);
1078
1153
  return {
1079
- size: formatByteSize(bundle.byteLength)
1154
+ size: size.toString("utf8")
1080
1155
  };
1081
1156
  }
1082
- async publish({ publish }) {
1083
- this.s3 = await publish(`${this.id}.zip`, this.bundle, this.hash);
1157
+ async publish({ publish, read }) {
1158
+ const [hash, handler, bundle] = await read(this.fingerprint, ["HASH", "HANDLER", "bundle.zip"]);
1159
+ this.handler = handler.toString("utf8");
1160
+ this.s3 = await publish(`${this.id}.zip`, bundle, hash);
1084
1161
  }
1085
1162
  toCodeJson() {
1086
1163
  return {
1087
- Handler: this.handler,
1164
+ Handler: this.handler ?? "",
1088
1165
  Code: {
1089
1166
  S3Bucket: this.s3?.bucket ?? "",
1090
1167
  S3Key: this.s3?.key ?? "",
@@ -1100,9 +1177,9 @@ var FeatureCode = class extends Asset {
1100
1177
  }
1101
1178
  async publish({ publish }) {
1102
1179
  const root2 = fileURLToPath(new URL(".", import.meta.url));
1103
- const path = join(root2, `features/${this.id}`);
1104
- const bundle = await readFile(join(path, "bundle.zip"));
1105
- const hash = await readFile(join(path, "HASH"));
1180
+ const path = join2(root2, `features/${this.id}`);
1181
+ const bundle = await readFile2(join2(path, "bundle.zip"));
1182
+ const hash = await readFile2(join2(path, "HASH"));
1106
1183
  this.s3 = await publish(`${this.id}.zip`, bundle, hash.toString("utf8"));
1107
1184
  }
1108
1185
  toCodeJson() {
@@ -1123,8 +1200,8 @@ var InlineFeatureCode = class extends Asset {
1123
1200
  }
1124
1201
  async publish() {
1125
1202
  const root2 = fileURLToPath(new URL(".", import.meta.url));
1126
- const path = join(root2, `features/${this.id}`);
1127
- const file = await readFile(join(path, "index.js"));
1203
+ const path = join2(root2, `features/${this.id}`);
1204
+ const file = await readFile2(join2(path, "index.js"));
1128
1205
  this.code = file.toString("utf8");
1129
1206
  }
1130
1207
  toCodeJson() {
@@ -1176,11 +1253,11 @@ var EventInvokeConfig = class extends Resource {
1176
1253
  };
1177
1254
 
1178
1255
  // src/plugins/on-failure/util.ts
1179
- var getGlobalOnFailure = ({ config, bootstrap: bootstrap2 }) => {
1180
- return hasOnFailure(config) ? bootstrap2.import("on-failure-queue-arn") : void 0;
1256
+ var getGlobalOnFailure = ({ config: config2, bootstrap: bootstrap2 }) => {
1257
+ return hasOnFailure(config2) ? bootstrap2.import("on-failure-queue-arn") : void 0;
1181
1258
  };
1182
- var hasOnFailure = (config) => {
1183
- const onFailure = config.stacks.find((stack) => {
1259
+ var hasOnFailure = (config2) => {
1260
+ const onFailure = config2.stacks.find((stack) => {
1184
1261
  return typeof stack.onFailure !== "undefined";
1185
1262
  });
1186
1263
  return !!onFailure;
@@ -1191,24 +1268,24 @@ import { camelCase as camelCase2 } from "change-case";
1191
1268
 
1192
1269
  // src/util/path.ts
1193
1270
  import { lstat } from "fs/promises";
1194
- import { join as join2, normalize } from "path";
1271
+ import { join as join3, normalize } from "path";
1195
1272
  var root = process.cwd();
1196
1273
  var directories = {
1197
1274
  root,
1198
1275
  get output() {
1199
- return join2(this.root, ".awsless");
1276
+ return join3(this.root, ".awsless");
1200
1277
  },
1201
1278
  get cache() {
1202
- return join2(this.output, "cache");
1279
+ return join3(this.output, "cache");
1203
1280
  },
1204
1281
  get asset() {
1205
- return join2(this.output, "asset");
1282
+ return join3(this.output, "asset");
1206
1283
  },
1207
1284
  get types() {
1208
- return join2(this.output, "types");
1285
+ return join3(this.output, "types");
1209
1286
  },
1210
1287
  get template() {
1211
- return join2(this.output, "template");
1288
+ return join3(this.output, "template");
1212
1289
  }
1213
1290
  };
1214
1291
  var setRoot = (path = root) => {
@@ -1218,17 +1295,17 @@ var findRootDir = async (path, configFile, level = 5) => {
1218
1295
  if (!level) {
1219
1296
  throw new TypeError("No awsless project found");
1220
1297
  }
1221
- const file = join2(path, configFile);
1298
+ const file = join3(path, configFile);
1222
1299
  const exists = await fileExist(file);
1223
1300
  if (exists) {
1224
1301
  return path;
1225
1302
  }
1226
- return findRootDir(normalize(join2(path, "..")), configFile, level - 1);
1303
+ return findRootDir(normalize(join3(path, "..")), configFile, level - 1);
1227
1304
  };
1228
1305
  var fileExist = async (file) => {
1229
1306
  try {
1230
- const stat3 = await lstat(file);
1231
- if (stat3.isFile()) {
1307
+ const stat4 = await lstat(file);
1308
+ if (stat4.isFile()) {
1232
1309
  return true;
1233
1310
  }
1234
1311
  } catch (error) {
@@ -1241,15 +1318,15 @@ import { relative as relative2 } from "path";
1241
1318
 
1242
1319
  // src/util/type-gen.ts
1243
1320
  import { mkdir, writeFile } from "fs/promises";
1244
- import { join as join3, relative } from "path";
1321
+ import { join as join4, relative } from "path";
1245
1322
  import { camelCase, constantCase as constantCase3 } from "change-case";
1246
- var generateResourceTypes = async (config) => {
1247
- const plugins = [...defaultPlugins, ...config.plugins || []];
1323
+ var generateResourceTypes = async (config2) => {
1324
+ const plugins = [...defaultPlugins, ...config2.plugins || []];
1248
1325
  const files = [];
1249
1326
  for (const plugin of plugins) {
1250
- const code = plugin.onTypeGen?.({ config });
1327
+ const code = plugin.onTypeGen?.({ config: config2 });
1251
1328
  if (code) {
1252
- const file = join3(directories.types, `${plugin.name}.d.ts`);
1329
+ const file = join4(directories.types, `${plugin.name}.d.ts`);
1253
1330
  files.push(relative(directories.root, file));
1254
1331
  await mkdir(directories.types, { recursive: true });
1255
1332
  await writeFile(file, code);
@@ -1257,7 +1334,7 @@ var generateResourceTypes = async (config) => {
1257
1334
  }
1258
1335
  if (files.length) {
1259
1336
  const code = files.map((file) => `/// <reference path='${file}' />`).join("\n");
1260
- await writeFile(join3(directories.root, `awsless.d.ts`), code);
1337
+ await writeFile(join4(directories.root, `awsless.d.ts`), code);
1261
1338
  }
1262
1339
  };
1263
1340
  var TypeGen = class {
@@ -1592,18 +1669,18 @@ type MockObject<F extends Func> = Mock<Parameters<F>, ReturnType<F>>
1592
1669
  var functionPlugin = definePlugin({
1593
1670
  name: "function",
1594
1671
  schema,
1595
- onTypeGen({ config }) {
1672
+ onTypeGen({ config: config2 }) {
1596
1673
  const types2 = new TypeGen("@awsless/awsless");
1597
1674
  const resources = new TypeObject(1);
1598
1675
  const mocks = new TypeObject(1);
1599
1676
  const mockResponses = new TypeObject(1);
1600
- for (const stack of config.stacks) {
1677
+ for (const stack of config2.stacks) {
1601
1678
  const resource = new TypeObject(2);
1602
1679
  const mock = new TypeObject(2);
1603
1680
  const mockResponse = new TypeObject(2);
1604
1681
  for (const [name, fileOrProps] of Object.entries(stack.functions || {})) {
1605
1682
  const varName = camelCase2(`${stack.name}-${name}`);
1606
- const funcName = formatName(`${config.name}-${stack.name}-${name}`);
1683
+ const funcName = formatName(`${config2.name}-${stack.name}-${name}`);
1607
1684
  const file = typeof fileOrProps === "string" ? fileOrProps : fileOrProps.file;
1608
1685
  const relFile = relative2(directories.types, file);
1609
1686
  types2.addImport(varName, relFile);
@@ -1622,9 +1699,9 @@ var functionPlugin = definePlugin({
1622
1699
  return types2.toString();
1623
1700
  },
1624
1701
  onStack(ctx) {
1625
- const { config, stack } = ctx;
1702
+ const { config: config2, stack } = ctx;
1626
1703
  for (const [id, fileOrProps] of Object.entries(ctx.stackConfig.functions || {})) {
1627
- const props = typeof fileOrProps === "string" ? { ...config.defaults?.function, file: fileOrProps } : { ...config.defaults?.function, ...fileOrProps };
1704
+ const props = typeof fileOrProps === "string" ? { ...config2.defaults?.function, file: fileOrProps } : { ...config2.defaults?.function, ...fileOrProps };
1628
1705
  const lambda = toLambdaFunction(ctx, id, fileOrProps);
1629
1706
  const invoke = new EventInvokeConfig(id, {
1630
1707
  functionName: lambda.name,
@@ -1642,12 +1719,12 @@ var functionPlugin = definePlugin({
1642
1719
  }
1643
1720
  });
1644
1721
  var toLambdaFunction = (ctx, id, fileOrProps) => {
1645
- const config = ctx.config;
1722
+ const config2 = ctx.config;
1646
1723
  const stack = ctx.stack ?? ctx.bootstrap;
1647
1724
  const bootstrap2 = ctx.bootstrap;
1648
- const props = typeof fileOrProps === "string" ? { ...config.defaults?.function, file: fileOrProps } : { ...config.defaults?.function, ...fileOrProps };
1725
+ const props = typeof fileOrProps === "string" ? { ...config2.defaults?.function, file: fileOrProps } : { ...config2.defaults?.function, ...fileOrProps };
1649
1726
  const lambda = new Function(id, {
1650
- name: `${config.name}-${stack.name}-${id}`,
1727
+ name: `${config2.name}-${stack.name}-${id}`,
1651
1728
  code: Code.fromFile(
1652
1729
  id,
1653
1730
  props.file,
@@ -1659,13 +1736,13 @@ var toLambdaFunction = (ctx, id, fileOrProps) => {
1659
1736
  ...props,
1660
1737
  vpc: void 0
1661
1738
  });
1662
- if (config.defaults?.function?.permissions) {
1663
- lambda.addPermissions(config.defaults?.function?.permissions);
1739
+ if (config2.defaults?.function?.permissions) {
1740
+ lambda.addPermissions(config2.defaults?.function?.permissions);
1664
1741
  }
1665
1742
  if (typeof fileOrProps === "object" && fileOrProps.permissions) {
1666
1743
  lambda.addPermissions(fileOrProps.permissions);
1667
1744
  }
1668
- lambda.addEnvironment("APP", config.name).addEnvironment("STAGE", config.stage).addEnvironment("STACK", stack.name);
1745
+ lambda.addEnvironment("APP", config2.name).addEnvironment("STAGE", config2.stage).addEnvironment("STACK", stack.name);
1669
1746
  if (props.log) {
1670
1747
  lambda.enableLogs(props.log instanceof Duration ? props.log : void 0);
1671
1748
  }
@@ -2003,18 +2080,18 @@ var queuePlugin = definePlugin({
2003
2080
  ).optional()
2004
2081
  }).array()
2005
2082
  }),
2006
- onTypeGen({ config }) {
2083
+ onTypeGen({ config: config2 }) {
2007
2084
  const gen = new TypeGen("@awsless/awsless");
2008
2085
  const resources = new TypeObject(1);
2009
2086
  const mocks = new TypeObject(1);
2010
2087
  const mockResponses = new TypeObject(1);
2011
- for (const stack of config.stacks) {
2088
+ for (const stack of config2.stacks) {
2012
2089
  const resource = new TypeObject(2);
2013
2090
  const mock = new TypeObject(2);
2014
2091
  const mockResponse = new TypeObject(2);
2015
2092
  for (const [name, fileOrProps] of Object.entries(stack.queues || {})) {
2016
2093
  const varName = camelCase3(`${stack.name}-${name}`);
2017
- const queueName = formatName(`${config.name}-${stack.name}-${name}`);
2094
+ const queueName = formatName(`${config2.name}-${stack.name}-${name}`);
2018
2095
  const file = typeof fileOrProps === "string" ? fileOrProps : typeof fileOrProps.consumer === "string" ? fileOrProps.consumer : fileOrProps.consumer.file;
2019
2096
  const relFile = relative3(directories.types, file);
2020
2097
  gen.addImport(varName, relFile);
@@ -2033,11 +2110,11 @@ var queuePlugin = definePlugin({
2033
2110
  return gen.toString();
2034
2111
  },
2035
2112
  onStack(ctx) {
2036
- const { stack, config, stackConfig, bind } = ctx;
2113
+ const { stack, config: config2, stackConfig, bind } = ctx;
2037
2114
  for (const [id, functionOrProps] of Object.entries(stackConfig.queues || {})) {
2038
- const props = typeof functionOrProps === "string" ? { ...config.defaults.queue, consumer: functionOrProps } : { ...config.defaults.queue, ...functionOrProps };
2115
+ const props = typeof functionOrProps === "string" ? { ...config2.defaults.queue, consumer: functionOrProps } : { ...config2.defaults.queue, ...functionOrProps };
2039
2116
  const queue2 = new Queue(id, {
2040
- name: `${config.name}-${stack.name}-${id}`,
2117
+ name: `${config2.name}-${stack.name}-${id}`,
2041
2118
  deadLetterArn: getGlobalOnFailure(ctx),
2042
2119
  ...props
2043
2120
  });
@@ -2303,13 +2380,13 @@ var tablePlugin = definePlugin({
2303
2380
  ).optional()
2304
2381
  }).array()
2305
2382
  }),
2306
- onTypeGen({ config }) {
2383
+ onTypeGen({ config: config2 }) {
2307
2384
  const gen = new TypeGen("@awsless/awsless");
2308
2385
  const resources = new TypeObject(1);
2309
- for (const stack of config.stacks) {
2386
+ for (const stack of config2.stacks) {
2310
2387
  const list3 = new TypeObject(2);
2311
2388
  for (const name of Object.keys(stack.tables || {})) {
2312
- const tableName = formatName(`${config.name}-${stack.name}-${name}`);
2389
+ const tableName = formatName(`${config2.name}-${stack.name}-${name}`);
2313
2390
  list3.addType(name, `'${tableName}'`);
2314
2391
  }
2315
2392
  resources.addType(stack.name, list3);
@@ -2318,11 +2395,11 @@ var tablePlugin = definePlugin({
2318
2395
  return gen.toString();
2319
2396
  },
2320
2397
  onStack(ctx) {
2321
- const { config, stack, stackConfig, bind } = ctx;
2398
+ const { config: config2, stack, stackConfig, bind } = ctx;
2322
2399
  for (const [id, props] of Object.entries(stackConfig.tables || {})) {
2323
2400
  const table = new Table(id, {
2324
2401
  ...props,
2325
- name: `${config.name}-${stack.name}-${id}`,
2402
+ name: `${config2.name}-${stack.name}-${id}`,
2326
2403
  stream: props.stream?.type
2327
2404
  });
2328
2405
  stack.add(table);
@@ -2437,13 +2514,13 @@ var storePlugin = definePlugin({
2437
2514
  stores: z10.array(ResourceIdSchema).optional()
2438
2515
  }).array()
2439
2516
  }),
2440
- onTypeGen({ config }) {
2517
+ onTypeGen({ config: config2 }) {
2441
2518
  const gen = new TypeGen("@awsless/awsless");
2442
2519
  const resources = new TypeObject(1);
2443
- for (const stack of config.stacks) {
2520
+ for (const stack of config2.stacks) {
2444
2521
  const list3 = new TypeObject(2);
2445
2522
  for (const name of stack.stores || []) {
2446
- const storeName = formatName(`${config.name}-${stack.name}-${name}`);
2523
+ const storeName = formatName(`${config2.name}-${stack.name}-${name}`);
2447
2524
  list3.addType(name, `{ readonly name: '${storeName}' }`);
2448
2525
  }
2449
2526
  resources.addType(stack.name, list3);
@@ -2451,10 +2528,10 @@ var storePlugin = definePlugin({
2451
2528
  gen.addInterface("StoreResources", resources);
2452
2529
  return gen.toString();
2453
2530
  },
2454
- onStack({ config, stack, stackConfig, bootstrap: bootstrap2, bind }) {
2531
+ onStack({ config: config2, stack, stackConfig, bootstrap: bootstrap2, bind }) {
2455
2532
  for (const id of stackConfig.stores || []) {
2456
2533
  const bucket = new Bucket(id, {
2457
- name: `store-${config.name}-${stack.name}-${id}`,
2534
+ name: `store-${config2.name}-${stack.name}-${id}`,
2458
2535
  accessControl: "private"
2459
2536
  });
2460
2537
  const custom = new CustomResource(id, {
@@ -2608,14 +2685,14 @@ var topicPlugin = definePlugin({
2608
2685
  }
2609
2686
  })
2610
2687
  }),
2611
- onTypeGen({ config }) {
2688
+ onTypeGen({ config: config2 }) {
2612
2689
  const gen = new TypeGen("@awsless/awsless");
2613
2690
  const resources = new TypeObject(1);
2614
2691
  const mocks = new TypeObject(1);
2615
2692
  const mockResponses = new TypeObject(1);
2616
- for (const stack of config.stacks) {
2693
+ for (const stack of config2.stacks) {
2617
2694
  for (const topic of stack.topics || []) {
2618
- const name = formatName(`${config.name}-${topic}`);
2695
+ const name = formatName(`${config2.name}-${topic}`);
2619
2696
  mockResponses.addType(topic, "Mock");
2620
2697
  resources.addType(topic, `Publish<'${name}'>`);
2621
2698
  mocks.addType(topic, `MockBuilder`);
@@ -2627,11 +2704,11 @@ var topicPlugin = definePlugin({
2627
2704
  gen.addInterface("TopicMockResponse", mockResponses);
2628
2705
  return gen.toString();
2629
2706
  },
2630
- onApp({ config, bootstrap: bootstrap2 }) {
2631
- for (const stack of config.stacks) {
2707
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
2708
+ for (const stack of config2.stacks) {
2632
2709
  for (const id of stack.topics || []) {
2633
2710
  const topic = new Topic(id, {
2634
- name: `${config.name}-${id}`
2711
+ name: `${config2.name}-${id}`
2635
2712
  });
2636
2713
  bootstrap2.add(topic);
2637
2714
  bootstrap2.export(`topic-${id}-arn`, topic.arn);
@@ -2639,14 +2716,14 @@ var topicPlugin = definePlugin({
2639
2716
  }
2640
2717
  },
2641
2718
  onStack(ctx) {
2642
- const { config, stack, stackConfig, bootstrap: bootstrap2, bind } = ctx;
2719
+ const { config: config2, stack, stackConfig, bootstrap: bootstrap2, bind } = ctx;
2643
2720
  for (const id of stackConfig.topics || []) {
2644
2721
  bind((lambda) => {
2645
2722
  lambda.addPermissions({
2646
2723
  actions: ["sns:Publish"],
2647
2724
  resources: [
2648
2725
  sub("arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:${app}-${topic}", {
2649
- app: config.name,
2726
+ app: config2.name,
2650
2727
  topic: id
2651
2728
  })
2652
2729
  ]
@@ -2779,11 +2856,11 @@ var pubsubPlugin = definePlugin({
2779
2856
  });
2780
2857
  },
2781
2858
  onStack(ctx) {
2782
- const { config, stack, stackConfig } = ctx;
2859
+ const { config: config2, stack, stackConfig } = ctx;
2783
2860
  for (const [id, props] of Object.entries(stackConfig.pubsub || {})) {
2784
2861
  const lambda = toLambdaFunction(ctx, `pubsub-${id}`, props.consumer);
2785
2862
  const source = new IotEventSource(id, lambda, {
2786
- name: `${config.name}-${stack.name}-${id}`,
2863
+ name: `${config2.name}-${stack.name}-${id}`,
2787
2864
  sql: props.sql,
2788
2865
  sqlVersion: props.sqlVersion
2789
2866
  });
@@ -2924,13 +3001,11 @@ var RecordSet = class extends Resource {
2924
3001
 
2925
3002
  // src/formation/resource/appsync/graphql-schema.ts
2926
3003
  import { print } from "graphql";
2927
- import { readFile as readFile2 } from "fs/promises";
3004
+ import { readFile as readFile3 } from "fs/promises";
2928
3005
  import { mergeTypeDefs } from "@graphql-tools/merge";
2929
3006
  var GraphQLSchema = class extends Resource {
2930
3007
  constructor(logicalId, props) {
2931
- super("AWS::AppSync::GraphQLSchema", logicalId, [
2932
- props.definition
2933
- ]);
3008
+ super("AWS::AppSync::GraphQLSchema", logicalId, [props.definition]);
2934
3009
  this.props = props;
2935
3010
  }
2936
3011
  properties() {
@@ -2948,16 +3023,21 @@ var Definition = class extends Asset {
2948
3023
  schema;
2949
3024
  async build({ write }) {
2950
3025
  const files = [this.files].flat();
2951
- const schemas = await Promise.all(files.map((file) => {
2952
- return readFile2(file, "utf8");
2953
- }));
3026
+ const fingerprint = files.join("");
3027
+ const schemas = await Promise.all(
3028
+ files.map((file) => {
3029
+ return readFile3(file, "utf8");
3030
+ })
3031
+ );
2954
3032
  const defs = mergeTypeDefs(schemas);
2955
3033
  const schema2 = print(defs);
2956
3034
  if (schema2.length === 0) {
2957
3035
  throw new Error(`Graphql schema definition can't be empty. [${this.id}]`);
2958
3036
  }
2959
3037
  const size = Buffer.from(schema2, "utf8").byteLength;
2960
- await write("schema.gql", schema2);
3038
+ await write(fingerprint, async (write2) => {
3039
+ await write2("schema.gql", schema2);
3040
+ });
2961
3041
  this.schema = schema2;
2962
3042
  return {
2963
3043
  size: formatByteSize(size)
@@ -2974,7 +3054,7 @@ import { swc as swc2, minify as swcMinify2 } from "rollup-plugin-swc3";
2974
3054
  import json2 from "@rollup/plugin-json";
2975
3055
  import commonjs2 from "@rollup/plugin-commonjs";
2976
3056
  import nodeResolve2 from "@rollup/plugin-node-resolve";
2977
- import { dirname as dirname2 } from "path";
3057
+ import { dirname as dirname3 } from "path";
2978
3058
  var rollupResolver = ({ minify = true } = {}) => {
2979
3059
  return async (input) => {
2980
3060
  const bundle = await rollup2({
@@ -2997,7 +3077,7 @@ var rollupResolver = ({ minify = true } = {}) => {
2997
3077
  // minify,
2998
3078
  // module: true,
2999
3079
  jsc: {
3000
- baseUrl: dirname2(input),
3080
+ baseUrl: dirname3(input),
3001
3081
  minify: { sourceMap: true }
3002
3082
  },
3003
3083
  sourceMaps: true
@@ -3056,8 +3136,14 @@ var FileCode2 = class extends Asset {
3056
3136
  this.file = file;
3057
3137
  }
3058
3138
  code;
3059
- async build() {
3060
- const code = await rollupResolver({ minify: false })(this.file);
3139
+ async build({ read, write }) {
3140
+ const fingerprint = await generateFingerprint(this.file);
3141
+ await write(fingerprint, async (write2) => {
3142
+ const builder = rollupResolver({ minify: false });
3143
+ const code2 = await builder(this.file);
3144
+ await write2("file.js", code2);
3145
+ });
3146
+ const [code] = await read(fingerprint, ["file.js"]);
3061
3147
  this.code = code.toString("utf8");
3062
3148
  return {
3063
3149
  size: formatByteSize(code.byteLength)
@@ -3186,7 +3272,7 @@ var AppsyncEventSource = class extends Group {
3186
3272
  functionArn: lambda.arn,
3187
3273
  serviceRoleArn: role.arn
3188
3274
  }).dependsOn(role).dependsOn(lambda);
3189
- const config = new FunctionConfiguration(id, {
3275
+ const config2 = new FunctionConfiguration(id, {
3190
3276
  apiId: props.apiId,
3191
3277
  code: props.code,
3192
3278
  dataSourceName: source.name
@@ -3195,10 +3281,10 @@ var AppsyncEventSource = class extends Group {
3195
3281
  apiId: props.apiId,
3196
3282
  typeName: props.typeName,
3197
3283
  fieldName: props.fieldName,
3198
- functions: [config.id],
3284
+ functions: [config2.id],
3199
3285
  code: props.code
3200
- }).dependsOn(config);
3201
- super([role, source, config, resolver]);
3286
+ }).dependsOn(config2);
3287
+ super([role, source, config2, resolver]);
3202
3288
  }
3203
3289
  };
3204
3290
 
@@ -3238,7 +3324,7 @@ var DomainNameApiAssociation = class extends Resource {
3238
3324
  };
3239
3325
 
3240
3326
  // src/plugins/graphql.ts
3241
- import { basename } from "path";
3327
+ import { basename as basename2 } from "path";
3242
3328
  var defaultResolver = Code2.fromInline(
3243
3329
  "graphql-default-resolver",
3244
3330
  `
@@ -3298,21 +3384,21 @@ var graphqlPlugin = definePlugin({
3298
3384
  }).array()
3299
3385
  }),
3300
3386
  onApp(ctx) {
3301
- const { config, bootstrap: bootstrap2 } = ctx;
3387
+ const { config: config2, bootstrap: bootstrap2 } = ctx;
3302
3388
  const apis = /* @__PURE__ */ new Set();
3303
- for (const stackConfig of config.stacks) {
3389
+ for (const stackConfig of config2.stacks) {
3304
3390
  for (const id of Object.keys(stackConfig.graphql || {})) {
3305
3391
  apis.add(id);
3306
3392
  }
3307
3393
  }
3308
3394
  for (const id of apis) {
3309
3395
  const schemaFiles = [];
3310
- for (const stack of config.stacks) {
3396
+ for (const stack of config2.stacks) {
3311
3397
  const files = toArray(stack.graphql?.[id]?.schema || []);
3312
3398
  schemaFiles.push(...files);
3313
3399
  }
3314
3400
  const api = new GraphQLApi(id, {
3315
- name: `${config.name}-${id}`,
3401
+ name: `${config2.name}-${id}`,
3316
3402
  defaultAuthorization: GraphQLAuthorization.withApiKey()
3317
3403
  });
3318
3404
  const schema2 = new GraphQLSchema(id, {
@@ -3320,7 +3406,7 @@ var graphqlPlugin = definePlugin({
3320
3406
  definition: new Definition(id, schemaFiles)
3321
3407
  }).dependsOn(api);
3322
3408
  bootstrap2.add(api).add(schema2).export(`graphql-${id}`, api.id);
3323
- const props = config.defaults.graphql?.[id];
3409
+ const props = config2.defaults.graphql?.[id];
3324
3410
  if (!props) {
3325
3411
  continue;
3326
3412
  }
@@ -3359,10 +3445,10 @@ var graphqlPlugin = definePlugin({
3359
3445
  }
3360
3446
  },
3361
3447
  onStack(ctx) {
3362
- const { config, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
3448
+ const { config: config2, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
3363
3449
  for (const [id, props] of Object.entries(stackConfig.graphql || {})) {
3364
3450
  const apiId = bootstrap2.import(`graphql-${id}`);
3365
- const defaultProps = config.defaults.graphql?.[id];
3451
+ const defaultProps = config2.defaults.graphql?.[id];
3366
3452
  for (const [typeName, fields] of Object.entries(props.resolvers || {})) {
3367
3453
  for (const [fieldName, resolverProps] of Object.entries(fields || {})) {
3368
3454
  const props2 = isFunctionProps(resolverProps) ? { consumer: resolverProps } : resolverProps;
@@ -3372,7 +3458,7 @@ var graphqlPlugin = definePlugin({
3372
3458
  let code = defaultResolver;
3373
3459
  if (resolver) {
3374
3460
  if (!resolverCache.has(resolver)) {
3375
- const fileCode = Code2.fromFile(basename(resolver), resolver);
3461
+ const fileCode = Code2.fromFile(basename2(resolver), resolver);
3376
3462
  resolverCache.set(resolver, fileCode);
3377
3463
  stack.add(fileCode);
3378
3464
  }
@@ -3645,25 +3731,25 @@ var domainPlugin = definePlugin({
3645
3731
  ).optional()
3646
3732
  }).default({})
3647
3733
  }),
3648
- onApp({ config, bootstrap: bootstrap2, usEastBootstrap, bind }) {
3649
- const domains = Object.entries(config.defaults.domains || {});
3734
+ onApp({ config: config2, bootstrap: bootstrap2, usEastBootstrap, bind }) {
3735
+ const domains = Object.entries(config2.defaults.domains || {});
3650
3736
  if (domains.length === 0) {
3651
3737
  return;
3652
3738
  }
3653
3739
  const deleteHostedZoneLambda = new Function("delete-hosted-zone", {
3654
- name: `${config.name}-delete-hosted-zone`,
3740
+ name: `${config2.name}-delete-hosted-zone`,
3655
3741
  code: Code.fromInlineFeature("delete-hosted-zone")
3656
3742
  }).enableLogs(Duration.days(3)).addPermissions({
3657
3743
  actions: ["route53:ListResourceRecordSets", "route53:ChangeResourceRecordSets"],
3658
3744
  resources: ["*"]
3659
3745
  });
3660
3746
  usEastBootstrap.add(deleteHostedZoneLambda);
3661
- const usEastExports = new GlobalExports(`${config.name}-us-east-exports`, {
3747
+ const usEastExports = new GlobalExports(`${config2.name}-us-east-exports`, {
3662
3748
  region: usEastBootstrap.region
3663
3749
  });
3664
3750
  bootstrap2.add(usEastExports);
3665
3751
  const configurationSet = new ConfigurationSet("default", {
3666
- name: config.name,
3752
+ name: config2.name,
3667
3753
  engagementMetrics: true,
3668
3754
  reputationMetrics: true
3669
3755
  });
@@ -3732,12 +3818,12 @@ var onFailurePlugin = definePlugin({
3732
3818
  onFailure: FunctionSchema.optional()
3733
3819
  }).array()
3734
3820
  }),
3735
- onApp({ config, bootstrap: bootstrap2 }) {
3736
- if (!hasOnFailure(config)) {
3821
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
3822
+ if (!hasOnFailure(config2)) {
3737
3823
  return;
3738
3824
  }
3739
3825
  const queue2 = new Queue("on-failure", {
3740
- name: `${config.name}-failure`
3826
+ name: `${config2.name}-failure`
3741
3827
  });
3742
3828
  bootstrap2.add(queue2).export("on-failure-queue-arn", queue2.arn);
3743
3829
  },
@@ -3926,9 +4012,9 @@ var vpcPlugin = definePlugin({
3926
4012
  // vpc: z.boolean().default(false),
3927
4013
  // }).default({}),
3928
4014
  // }),
3929
- onApp({ config, bootstrap: bootstrap2 }) {
4015
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
3930
4016
  const vpc = new Vpc("main", {
3931
- name: config.name,
4017
+ name: config2.name,
3932
4018
  cidrBlock: Peer.ipv4("10.0.0.0/16")
3933
4019
  });
3934
4020
  const privateRouteTable = new RouteTable("private", {
@@ -3961,7 +4047,7 @@ var vpcPlugin = definePlugin({
3961
4047
  const subnet = new Subnet(id, {
3962
4048
  vpcId: vpc.id,
3963
4049
  cidrBlock: Peer.ipv4(`10.0.${block++}.0/24`),
3964
- availabilityZone: config.region + zones[i]
4050
+ availabilityZone: config2.region + zones[i]
3965
4051
  }).dependsOn(vpc);
3966
4052
  const association = new SubnetRouteTableAssociation(id, {
3967
4053
  routeTableId: table.id,
@@ -4375,8 +4461,8 @@ var httpPlugin = definePlugin({
4375
4461
  ).optional()
4376
4462
  }).array()
4377
4463
  }),
4378
- onApp({ config, bootstrap: bootstrap2 }) {
4379
- if (Object.keys(config.defaults?.http || {}).length === 0) {
4464
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
4465
+ if (Object.keys(config2.defaults?.http || {}).length === 0) {
4380
4466
  return;
4381
4467
  }
4382
4468
  const vpcId = bootstrap2.get("vpc-id");
@@ -4387,9 +4473,9 @@ var httpPlugin = definePlugin({
4387
4473
  securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(443));
4388
4474
  securityGroup.addIngressRule(Peer.anyIpv6(), Port.tcp(443));
4389
4475
  bootstrap2.add(securityGroup);
4390
- for (const [id, props] of Object.entries(config.defaults?.http || {})) {
4476
+ for (const [id, props] of Object.entries(config2.defaults?.http || {})) {
4391
4477
  const loadBalancer = new LoadBalancer(id, {
4392
- name: `${config.name}-${id}`,
4478
+ name: `${config2.name}-${id}`,
4393
4479
  type: "application",
4394
4480
  securityGroups: [securityGroup.id],
4395
4481
  subnets: [
@@ -4426,9 +4512,9 @@ var httpPlugin = definePlugin({
4426
4512
  }
4427
4513
  },
4428
4514
  onStack(ctx) {
4429
- const { config, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
4515
+ const { config: config2, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
4430
4516
  for (const [id, routes] of Object.entries(stackConfig.http || {})) {
4431
- const props = config.defaults.http[id];
4517
+ const props = config2.defaults.http[id];
4432
4518
  for (const [route, routeProps] of Object.entries(routes)) {
4433
4519
  const { method, path } = parseRoute(route);
4434
4520
  const lambda = toLambdaFunction(ctx, `http-${id}`, routeProps);
@@ -4505,13 +4591,13 @@ var searchPlugin = definePlugin({
4505
4591
  searchs: z19.array(ResourceIdSchema).optional()
4506
4592
  }).array()
4507
4593
  }),
4508
- onTypeGen({ config }) {
4594
+ onTypeGen({ config: config2 }) {
4509
4595
  const gen = new TypeGen("@awsless/awsless");
4510
4596
  const resources = new TypeObject(1);
4511
- for (const stack of config.stacks) {
4597
+ for (const stack of config2.stacks) {
4512
4598
  const list3 = new TypeObject(2);
4513
4599
  for (const id of stack.searchs || []) {
4514
- const name = formatName(`${config.name}-${stack.name}-${id}`);
4600
+ const name = formatName(`${config2.name}-${stack.name}-${id}`);
4515
4601
  list3.addType(name, `{ readonly name: '${name}' }`);
4516
4602
  }
4517
4603
  resources.addType(stack.name, list3);
@@ -4519,10 +4605,10 @@ var searchPlugin = definePlugin({
4519
4605
  gen.addInterface("SearchResources", resources);
4520
4606
  return gen.toString();
4521
4607
  },
4522
- onStack({ config, stack, stackConfig, bind }) {
4608
+ onStack({ config: config2, stack, stackConfig, bind }) {
4523
4609
  for (const id of stackConfig.searchs || []) {
4524
4610
  const collection = new Collection(id, {
4525
- name: `${config.name}-${stack.name}-${id}`,
4611
+ name: `${config2.name}-${stack.name}-${id}`,
4526
4612
  type: "search"
4527
4613
  });
4528
4614
  bind((lambda) => {
@@ -4659,10 +4745,10 @@ var cachePlugin = definePlugin({
4659
4745
  ).optional()
4660
4746
  }).array()
4661
4747
  }),
4662
- onTypeGen({ config }) {
4748
+ onTypeGen({ config: config2 }) {
4663
4749
  const gen = new TypeGen("@awsless/awsless");
4664
4750
  const resources = new TypeObject(1);
4665
- for (const stack of config.stacks) {
4751
+ for (const stack of config2.stacks) {
4666
4752
  const resource = new TypeObject(2);
4667
4753
  for (const name of Object.keys(stack.caches || {})) {
4668
4754
  resource.addType(name, `Command`);
@@ -4673,9 +4759,9 @@ var cachePlugin = definePlugin({
4673
4759
  gen.addInterface("CacheResources", resources);
4674
4760
  return gen.toString();
4675
4761
  },
4676
- onStack({ config, stack, stackConfig, bootstrap: bootstrap2, bind }) {
4762
+ onStack({ config: config2, stack, stackConfig, bootstrap: bootstrap2, bind }) {
4677
4763
  for (const [id, props] of Object.entries(stackConfig.caches || {})) {
4678
- const name = `${config.name}-${stack.name}-${id}`;
4764
+ const name = `${config2.name}-${stack.name}-${id}`;
4679
4765
  const subnetGroup = new SubnetGroup(id, {
4680
4766
  name,
4681
4767
  subnetIds: [bootstrap2.import(`private-subnet-1`), bootstrap2.import(`private-subnet-2`)]
@@ -4918,10 +5004,10 @@ var restPlugin = definePlugin({
4918
5004
  ).optional()
4919
5005
  }).array()
4920
5006
  }),
4921
- onApp({ config, bootstrap: bootstrap2 }) {
4922
- for (const [id, props] of Object.entries(config.defaults?.rest || {})) {
5007
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
5008
+ for (const [id, props] of Object.entries(config2.defaults?.rest || {})) {
4923
5009
  const api = new Api(id, {
4924
- name: `${config.name}-${id}`,
5010
+ name: `${config2.name}-${id}`,
4925
5011
  protocolType: "HTTP"
4926
5012
  });
4927
5013
  const stage = new Stage(id, {
@@ -4975,15 +5061,15 @@ import { z as z23 } from "zod";
4975
5061
 
4976
5062
  // src/util/param.ts
4977
5063
  import { DeleteParameterCommand, GetParameterCommand, GetParametersByPathCommand, ParameterType, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
4978
- var configParameterPrefix = (config) => {
4979
- return `/.awsless/${config.name}`;
5064
+ var configParameterPrefix = (config2) => {
5065
+ return `/.awsless/${config2.name}`;
4980
5066
  };
4981
5067
  var Params = class {
4982
- constructor(config) {
4983
- this.config = config;
5068
+ constructor(config2) {
5069
+ this.config = config2;
4984
5070
  this.client = new SSMClient({
4985
- credentials: config.credentials,
4986
- region: config.region
5071
+ credentials: config2.credentials,
5072
+ region: config2.region
4987
5073
  });
4988
5074
  }
4989
5075
  client;
@@ -5083,10 +5169,10 @@ var configPlugin = definePlugin({
5083
5169
  configs: z23.array(ConfigNameSchema).optional()
5084
5170
  }).array()
5085
5171
  }),
5086
- onTypeGen({ config }) {
5172
+ onTypeGen({ config: config2 }) {
5087
5173
  const gen = new TypeGen("@awsless/awsless");
5088
5174
  const resources = new TypeObject(0, false);
5089
- for (const stack of config.stacks) {
5175
+ for (const stack of config2.stacks) {
5090
5176
  for (const name of stack.configs || []) {
5091
5177
  resources.addConst(name, "string");
5092
5178
  }
@@ -5094,7 +5180,7 @@ var configPlugin = definePlugin({
5094
5180
  gen.addInterface("ConfigResources", resources.toString());
5095
5181
  return gen.toString();
5096
5182
  },
5097
- onStack({ bind, config, stackConfig }) {
5183
+ onStack({ bind, config: config2, stackConfig }) {
5098
5184
  const configs = stackConfig.configs;
5099
5185
  bind((lambda) => {
5100
5186
  if (configs && configs.length) {
@@ -5105,7 +5191,7 @@ var configPlugin = definePlugin({
5105
5191
  return formatArn({
5106
5192
  service: "ssm",
5107
5193
  resource: "parameter",
5108
- resourceName: configParameterPrefix(config) + "/" + paramCase6(name),
5194
+ resourceName: configParameterPrefix(config2) + "/" + paramCase6(name),
5109
5195
  seperator: ""
5110
5196
  });
5111
5197
  })
@@ -5242,11 +5328,11 @@ var OriginGroup = class {
5242
5328
  };
5243
5329
 
5244
5330
  // src/schema/local-directory.ts
5245
- import { stat as stat2 } from "fs/promises";
5331
+ import { stat as stat3 } from "fs/promises";
5246
5332
  import { z as z24 } from "zod";
5247
5333
  var LocalDirectorySchema = z24.string().refine(async (path) => {
5248
5334
  try {
5249
- const s = await stat2(path);
5335
+ const s = await stat3(path);
5250
5336
  return s.isDirectory();
5251
5337
  } catch (error) {
5252
5338
  return false;
@@ -5328,9 +5414,9 @@ var CachePolicy = class extends Resource {
5328
5414
  // src/formation/resource/s3/files.ts
5329
5415
  import { Glob } from "glob";
5330
5416
  import JSZip2 from "jszip";
5331
- import { createHash as createHash2 } from "crypto";
5417
+ import { createHash as createHash3 } from "crypto";
5332
5418
  import { createReadStream } from "fs";
5333
- import { join as join4 } from "path";
5419
+ import { join as join5 } from "path";
5334
5420
  var Files = class extends Asset {
5335
5421
  constructor(id, props) {
5336
5422
  super("bucket", id);
@@ -5339,7 +5425,7 @@ var Files = class extends Asset {
5339
5425
  hash;
5340
5426
  bundle;
5341
5427
  s3;
5342
- async build({ write }) {
5428
+ async build({ read, write }) {
5343
5429
  const glob = new Glob(this.props.pattern ?? "**/*", {
5344
5430
  nodir: true,
5345
5431
  cwd: this.props.directory
@@ -5348,9 +5434,9 @@ var Files = class extends Asset {
5348
5434
  const hashes = [];
5349
5435
  let count = 0;
5350
5436
  for await (const path of glob) {
5351
- const file = join4(this.props.directory, path);
5437
+ const file = join5(this.props.directory, path);
5352
5438
  const stream = createReadStream(file);
5353
- const hash2 = createHash2("sha1");
5439
+ const hash2 = createHash3("sha1");
5354
5440
  stream.pipe(hash2);
5355
5441
  hashes.push(hash2);
5356
5442
  zip.file(path, stream);
@@ -5363,24 +5449,22 @@ var Files = class extends Asset {
5363
5449
  level: 9
5364
5450
  }
5365
5451
  });
5366
- const hash = createHash2("sha1");
5452
+ const hash = createHash3("sha1");
5367
5453
  for (const item of hashes) {
5368
5454
  hash.update(item.digest());
5369
5455
  }
5370
5456
  this.hash = hash.digest("hex");
5371
- await write("HASH", this.hash);
5372
- await write("bundle.zip", this.bundle);
5457
+ await write(this.hash, async (write2) => {
5458
+ await write2("HASH", this.hash);
5459
+ await write2("bundle.zip", this.bundle);
5460
+ });
5373
5461
  return {
5374
5462
  files: style.success(String(count)),
5375
5463
  size: formatByteSize(this.bundle.byteLength)
5376
5464
  };
5377
5465
  }
5378
5466
  async publish({ publish }) {
5379
- this.s3 = await publish(
5380
- `${this.id}.zip`,
5381
- this.bundle,
5382
- this.hash
5383
- );
5467
+ this.s3 = await publish(`${this.id}.zip`, this.bundle, this.hash);
5384
5468
  }
5385
5469
  get source() {
5386
5470
  return this.s3;
@@ -5675,7 +5759,7 @@ var sitePlugin = definePlugin({
5675
5759
  }).array()
5676
5760
  }),
5677
5761
  onStack(ctx) {
5678
- const { config, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
5762
+ const { config: config2, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
5679
5763
  for (const [id, props] of Object.entries(stackConfig.sites || {})) {
5680
5764
  const origins = [];
5681
5765
  const originGroups = [];
@@ -5707,7 +5791,7 @@ var sitePlugin = definePlugin({
5707
5791
  if (props.static) {
5708
5792
  bucket = new Bucket(`site-${id}`, {
5709
5793
  // name: props.domain,
5710
- name: `site-${config.name}-${stack.name}-${id}`,
5794
+ name: `site-${config2.name}-${stack.name}-${id}`,
5711
5795
  accessControl: "private",
5712
5796
  website: {
5713
5797
  indexDocument: "index.html",
@@ -5755,14 +5839,14 @@ var sitePlugin = definePlugin({
5755
5839
  );
5756
5840
  }
5757
5841
  const cache = new CachePolicy(id, {
5758
- name: `site-${config.name}-${stack.name}-${id}`,
5842
+ name: `site-${config2.name}-${stack.name}-${id}`,
5759
5843
  minTtl: Duration.seconds(1),
5760
5844
  maxTtl: Duration.days(365),
5761
5845
  defaultTtl: Duration.days(1),
5762
5846
  ...props.cache
5763
5847
  });
5764
5848
  const originRequest = new OriginRequestPolicy(id, {
5765
- name: `site-${config.name}-${stack.name}-${id}`,
5849
+ name: `site-${config2.name}-${stack.name}-${id}`,
5766
5850
  header: {
5767
5851
  behavior: "all-except",
5768
5852
  values: ["host", "authorization"]
@@ -5772,12 +5856,12 @@ var sitePlugin = definePlugin({
5772
5856
  const hostedZoneId = bootstrap2.import(`hosted-zone-${props.domain}-id`);
5773
5857
  const certificateArn = bootstrap2.import(`us-east-certificate-${props.domain}-arn`);
5774
5858
  const responseHeaders = new ResponseHeadersPolicy(id, {
5775
- name: `site-${config.name}-${stack.name}-${id}`,
5859
+ name: `site-${config2.name}-${stack.name}-${id}`,
5776
5860
  cors: props.cors,
5777
5861
  remove: ["server"]
5778
5862
  });
5779
5863
  const distribution = new Distribution(id, {
5780
- name: `site-${config.name}-${stack.name}-${id}`,
5864
+ name: `site-${config2.name}-${stack.name}-${id}`,
5781
5865
  certificateArn,
5782
5866
  compress: true,
5783
5867
  aliases: [domainName],
@@ -5847,16 +5931,16 @@ var sitePlugin = definePlugin({
5847
5931
  // src/plugins/feature.ts
5848
5932
  var featurePlugin = definePlugin({
5849
5933
  name: "feature",
5850
- onApp({ config, bootstrap: bootstrap2 }) {
5934
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
5851
5935
  const deleteBucketLambda = new Function("delete-bucket", {
5852
- name: `${config.name}-delete-bucket`,
5936
+ name: `${config2.name}-delete-bucket`,
5853
5937
  code: Code.fromFeature("delete-bucket")
5854
5938
  }).enableLogs(Duration.days(3)).addPermissions({
5855
5939
  actions: ["s3:*"],
5856
5940
  resources: ["*"]
5857
5941
  });
5858
5942
  const uploadBucketAssetLambda = new Function("upload-bucket-asset", {
5859
- name: `${config.name}-upload-bucket-asset`,
5943
+ name: `${config2.name}-upload-bucket-asset`,
5860
5944
  code: Code.fromFeature("upload-bucket-asset"),
5861
5945
  memorySize: Size.gigaBytes(2)
5862
5946
  }).enableLogs(Duration.days(3)).addPermissions({
@@ -5864,7 +5948,7 @@ var featurePlugin = definePlugin({
5864
5948
  resources: ["*"]
5865
5949
  });
5866
5950
  const invalidateCacheLambda = new Function("invalidate-cache", {
5867
- name: `${config.name}-invalidate-cache`,
5951
+ name: `${config2.name}-invalidate-cache`,
5868
5952
  code: Code.fromFeature("invalidate-cache")
5869
5953
  }).enableLogs(Duration.days(3)).addPermissions({
5870
5954
  actions: ["cloudfront:*"],
@@ -6276,11 +6360,11 @@ var authPlugin = definePlugin({
6276
6360
  ).optional()
6277
6361
  }).array()
6278
6362
  }),
6279
- onTypeGen({ config }) {
6363
+ onTypeGen({ config: config2 }) {
6280
6364
  const gen = new TypeGen("@awsless/awsless");
6281
6365
  const resources = new TypeObject(1);
6282
- for (const name of Object.keys(config.defaults.auth)) {
6283
- const authName = formatName(`${config.name}-${name}`);
6366
+ for (const name of Object.keys(config2.defaults.auth)) {
6367
+ const authName = formatName(`${config2.name}-${name}`);
6284
6368
  resources.addType(
6285
6369
  name,
6286
6370
  `{ readonly name: '${authName}', readonly userPoolId: string, readonly clientId: string }`
@@ -6309,12 +6393,12 @@ var authPlugin = definePlugin({
6309
6393
  }
6310
6394
  },
6311
6395
  onApp(ctx) {
6312
- const { config, bootstrap: bootstrap2 } = ctx;
6313
- if (Object.keys(config.defaults.auth).length === 0) {
6396
+ const { config: config2, bootstrap: bootstrap2 } = ctx;
6397
+ if (Object.keys(config2.defaults.auth).length === 0) {
6314
6398
  return;
6315
6399
  }
6316
6400
  const clientSecretLambda = new Function(`auth-client-secret`, {
6317
- name: `${config.name}-auth-client-secret`,
6401
+ name: `${config2.name}-auth-client-secret`,
6318
6402
  code: Code.fromFeature("cognito-client-secret")
6319
6403
  });
6320
6404
  clientSecretLambda.addPermissions({
@@ -6322,7 +6406,7 @@ var authPlugin = definePlugin({
6322
6406
  resources: ["*"]
6323
6407
  });
6324
6408
  bootstrap2.add(clientSecretLambda);
6325
- for (const [id, props] of Object.entries(config.defaults.auth)) {
6409
+ for (const [id, props] of Object.entries(config2.defaults.auth)) {
6326
6410
  const functions = /* @__PURE__ */ new Map();
6327
6411
  const triggers = {};
6328
6412
  for (const [trigger, fnProps] of Object.entries(props.triggers ?? {})) {
@@ -6330,7 +6414,7 @@ var authPlugin = definePlugin({
6330
6414
  functions.set(trigger, lambda);
6331
6415
  triggers[trigger] = lambda.arn;
6332
6416
  }
6333
- for (const stack of config.stacks) {
6417
+ for (const stack of config2.stacks) {
6334
6418
  for (const [trigger, fnProps] of Object.entries(stack.auth?.[id]?.triggers ?? {})) {
6335
6419
  const lambda = toLambdaFunction(ctx, `auth-${id}-${trigger}`, fnProps);
6336
6420
  if (functions.has(trigger)) {
@@ -6356,7 +6440,7 @@ var authPlugin = definePlugin({
6356
6440
  });
6357
6441
  }
6358
6442
  const userPool = new UserPool(id, {
6359
- name: `${config.name}-${id}`,
6443
+ name: `${config2.name}-${id}`,
6360
6444
  allowUserRegistration: props.allowUserRegistration,
6361
6445
  username: props.username,
6362
6446
  password: props.password,
@@ -6364,7 +6448,7 @@ var authPlugin = definePlugin({
6364
6448
  email: emailConfig
6365
6449
  });
6366
6450
  const client = userPool.addClient({
6367
- name: `${config.name}-${id}`,
6451
+ name: `${config2.name}-${id}`,
6368
6452
  validity: props.validity,
6369
6453
  generateSecret: true,
6370
6454
  supportedIdentityProviders: ["cognito"],
@@ -6373,7 +6457,7 @@ var authPlugin = definePlugin({
6373
6457
  }
6374
6458
  });
6375
6459
  const domain = userPool.addDomain({
6376
- domain: `${config.name}-${id}`
6460
+ domain: `${config2.name}-${id}`
6377
6461
  });
6378
6462
  const clientSecret = new CustomResource(`${id}-client-secret`, {
6379
6463
  serviceToken: clientSecretLambda.arn,
@@ -6396,6 +6480,28 @@ var authPlugin = definePlugin({
6396
6480
  }
6397
6481
  });
6398
6482
 
6483
+ // src/plugins/test.ts
6484
+ import { z as z27 } from "zod";
6485
+ var testPlugin = definePlugin({
6486
+ name: "test",
6487
+ schema: z27.object({
6488
+ stacks: z27.object({
6489
+ /** Define the location of your tests for your stack.
6490
+ * @example
6491
+ * {
6492
+ * tests: './test'
6493
+ * }
6494
+ */
6495
+ tests: z27.union([LocalDirectorySchema.transform((v) => [v]), LocalDirectorySchema.array()]).optional()
6496
+ }).array()
6497
+ }),
6498
+ onStack({ tests, stackConfig }) {
6499
+ if (stackConfig.tests) {
6500
+ tests.set(stackConfig.name, stackConfig.tests);
6501
+ }
6502
+ }
6503
+ });
6504
+
6399
6505
  // src/plugins/index.ts
6400
6506
  var defaultPlugins = [
6401
6507
  extendPlugin,
@@ -6418,7 +6524,8 @@ var defaultPlugins = [
6418
6524
  httpPlugin,
6419
6525
  restPlugin,
6420
6526
  sitePlugin,
6421
- onFailurePlugin
6527
+ onFailurePlugin,
6528
+ testPlugin
6422
6529
  ];
6423
6530
 
6424
6531
  // src/formation/app.ts
@@ -6460,12 +6567,13 @@ var getAllDepends = (filters) => {
6460
6567
  walk(filters);
6461
6568
  return list3;
6462
6569
  };
6463
- var toApp = async (config, filters) => {
6464
- const app = new App(config.name);
6570
+ var toApp = async (config2, filters) => {
6571
+ const app = new App(config2.name);
6465
6572
  const stacks = [];
6466
- const plugins = [...defaultPlugins, ...config.plugins || []];
6573
+ const plugins = [...defaultPlugins, ...config2.plugins || []];
6574
+ const tests = /* @__PURE__ */ new Map();
6467
6575
  debug("Plugins detected:", plugins.map((plugin) => style.info(plugin.name)).join(", "));
6468
- const bootstrap2 = new Stack("bootstrap", config.region);
6576
+ const bootstrap2 = new Stack("bootstrap", config2.region);
6469
6577
  const usEastBootstrap = new Stack("us-east-bootstrap", "us-east-1");
6470
6578
  app.add(bootstrap2, usEastBootstrap);
6471
6579
  debug("Run plugin onApp listeners");
@@ -6475,45 +6583,32 @@ var toApp = async (config, filters) => {
6475
6583
  };
6476
6584
  for (const plugin of plugins) {
6477
6585
  plugin.onApp?.({
6478
- config,
6586
+ config: config2,
6479
6587
  app,
6480
6588
  bootstrap: bootstrap2,
6481
6589
  usEastBootstrap,
6482
- bind
6590
+ bind,
6591
+ tests
6483
6592
  });
6484
6593
  }
6485
6594
  debug("Stack filters:", filters.map((filter) => style.info(filter)).join(", "));
6486
- const filterdStacks = filters.length === 0 ? config.stacks : getAllDepends(
6595
+ const filterdStacks = filters.length === 0 ? config2.stacks : getAllDepends(
6487
6596
  // config.stacks,
6488
- config.stacks.filter((stack) => filters.includes(stack.name))
6597
+ config2.stacks.filter((stack) => filters.includes(stack.name))
6489
6598
  );
6490
6599
  for (const stackConfig of filterdStacks) {
6491
6600
  const { stack, bindings: bindings2 } = toStack({
6492
- config,
6601
+ config: config2,
6493
6602
  stackConfig,
6494
6603
  bootstrap: bootstrap2,
6495
6604
  usEastBootstrap,
6496
6605
  plugins,
6606
+ tests,
6497
6607
  app
6498
6608
  });
6499
6609
  app.add(stack);
6500
6610
  stacks.push({ stack, config: stackConfig, bindings: bindings2 });
6501
6611
  }
6502
- debug(app.stacks);
6503
- for (const plugin of plugins) {
6504
- for (const stack of app.stacks) {
6505
- for (const resource of stack) {
6506
- plugin.onResource?.({
6507
- config,
6508
- app,
6509
- stack,
6510
- bootstrap: bootstrap2,
6511
- usEastBootstrap,
6512
- resource
6513
- });
6514
- }
6515
- }
6516
- }
6517
6612
  const functions = app.find(Function);
6518
6613
  for (const bind2 of bindings) {
6519
6614
  for (const fn of functions) {
@@ -6544,12 +6639,13 @@ var toApp = async (config, filters) => {
6544
6639
  return {
6545
6640
  app,
6546
6641
  plugins,
6547
- deploymentLine
6642
+ deploymentLine,
6643
+ tests
6548
6644
  };
6549
6645
  };
6550
6646
 
6551
6647
  // src/config.ts
6552
- import { join as join6 } from "path";
6648
+ import { join as join7 } from "path";
6553
6649
 
6554
6650
  // src/util/account.ts
6555
6651
  import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
@@ -6568,17 +6664,17 @@ var getCredentials = (profile) => {
6568
6664
  };
6569
6665
 
6570
6666
  // src/schema/app.ts
6571
- import { z as z30 } from "zod";
6667
+ import { z as z31 } from "zod";
6572
6668
 
6573
6669
  // src/schema/stack.ts
6574
- import { z as z27 } from "zod";
6575
- var StackSchema = z27.object({
6670
+ import { z as z28 } from "zod";
6671
+ var StackSchema = z28.object({
6576
6672
  name: ResourceIdSchema,
6577
- depends: z27.array(z27.lazy(() => StackSchema)).optional()
6673
+ depends: z28.array(z28.lazy(() => StackSchema)).optional()
6578
6674
  });
6579
6675
 
6580
6676
  // src/schema/region.ts
6581
- import { z as z28 } from "zod";
6677
+ import { z as z29 } from "zod";
6582
6678
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
6583
6679
  var AF = ["af-south-1"];
6584
6680
  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"];
@@ -6595,41 +6691,41 @@ var regions = [
6595
6691
  ...ME,
6596
6692
  ...SA
6597
6693
  ];
6598
- var RegionSchema = z28.enum(regions);
6694
+ var RegionSchema = z29.enum(regions);
6599
6695
 
6600
6696
  // src/schema/plugin.ts
6601
- import { z as z29 } from "zod";
6602
- var PluginSchema = z29.object({
6603
- name: z29.string(),
6604
- schema: z29.custom().optional(),
6697
+ import { z as z30 } from "zod";
6698
+ var PluginSchema = z30.object({
6699
+ name: z30.string(),
6700
+ schema: z30.custom().optional(),
6605
6701
  // depends: z.array(z.lazy(() => PluginSchema)).optional(),
6606
- onApp: z29.function().returns(z29.void()).optional(),
6607
- onStack: z29.function().returns(z29.any()).optional(),
6608
- onResource: z29.function().returns(z29.any()).optional()
6702
+ onApp: z30.function().returns(z30.void()).optional(),
6703
+ onStack: z30.function().returns(z30.any()).optional(),
6704
+ onResource: z30.function().returns(z30.any()).optional()
6609
6705
  // bind: z.function().optional(),
6610
6706
  });
6611
6707
 
6612
6708
  // src/schema/app.ts
6613
- var AppSchema = z30.object({
6709
+ var AppSchema = z31.object({
6614
6710
  /** App name */
6615
6711
  name: ResourceIdSchema,
6616
6712
  /** The AWS region to deploy to. */
6617
6713
  region: RegionSchema,
6618
6714
  /** The AWS profile to deploy to. */
6619
- profile: z30.string(),
6715
+ profile: z31.string(),
6620
6716
  /** The deployment stage.
6621
6717
  * @default 'prod'
6622
6718
  */
6623
- stage: z30.string().regex(/^[a-z]+$/).default("prod"),
6719
+ stage: z31.string().regex(/^[a-z]+$/).default("prod"),
6624
6720
  /** Default properties. */
6625
- defaults: z30.object({}).default({}),
6721
+ defaults: z31.object({}).default({}),
6626
6722
  /** The application stacks. */
6627
- stacks: z30.array(StackSchema).min(1).refine((stacks) => {
6723
+ stacks: z31.array(StackSchema).min(1).refine((stacks) => {
6628
6724
  const unique = new Set(stacks.map((stack) => stack.name));
6629
6725
  return unique.size === stacks.length;
6630
6726
  }, "Must be an array of unique stacks"),
6631
6727
  /** Custom plugins. */
6632
- plugins: z30.array(PluginSchema).optional()
6728
+ plugins: z31.array(PluginSchema).optional()
6633
6729
  });
6634
6730
 
6635
6731
  // src/util/import.ts
@@ -6637,7 +6733,7 @@ import { rollup as rollup3, watch } from "rollup";
6637
6733
  import { swc as swc3 } from "rollup-plugin-swc3";
6638
6734
  import replace from "rollup-plugin-replace";
6639
6735
  import { EventIterator } from "event-iterator";
6640
- import { dirname as dirname3, join as join5 } from "path";
6736
+ import { dirname as dirname4, join as join6 } from "path";
6641
6737
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
6642
6738
  var importFile = async (path) => {
6643
6739
  const bundle = await rollup3({
@@ -6648,18 +6744,18 @@ var importFile = async (path) => {
6648
6744
  plugins: [
6649
6745
  // @ts-ignore
6650
6746
  replace({
6651
- __dirname: (id) => `'${dirname3(id)}'`
6652
- // 'defineStackConfig({': id => `defineStackConfig({ cwd: '${dirname(id)}',`,
6747
+ __dirname: (id) => `'${dirname4(id)}'`
6748
+ // 'defineStackConfig({': (id: string) => `defineStackConfig({ cwd: '${dirname(id)}',`,
6653
6749
  }),
6654
6750
  swc3({
6655
6751
  minify: false,
6656
6752
  jsc: {
6657
- baseUrl: dirname3(path)
6753
+ baseUrl: dirname4(path)
6658
6754
  }
6659
6755
  })
6660
6756
  ]
6661
6757
  });
6662
- const outputFile = join5(directories.cache, "config.js");
6758
+ const outputFile = join6(directories.cache, "config.js");
6663
6759
  const result = await bundle.generate({
6664
6760
  format: "esm",
6665
6761
  exports: "default"
@@ -6685,13 +6781,13 @@ var watchFile = (path) => {
6685
6781
  plugins: [
6686
6782
  // @ts-ignore
6687
6783
  replace({
6688
- __dirname: (id) => `'${dirname3(id)}'`
6689
- // 'defineStackConfig({': id => `defineStackConfig({ cwd: '${dirname(id)}',`,
6784
+ __dirname: (id) => `'${dirname4(id)}'`
6785
+ // 'defineStackConfig({': (id: string) => `defineStackConfig({ cwd: '${dirname(id)}',`,
6690
6786
  }),
6691
6787
  swc3({
6692
6788
  minify: false,
6693
6789
  jsc: {
6694
- baseUrl: dirname3(path)
6790
+ baseUrl: dirname4(path)
6695
6791
  }
6696
6792
  })
6697
6793
  ]
@@ -6713,12 +6809,12 @@ var watchFile = (path) => {
6713
6809
  event.result.close();
6714
6810
  const output = result.output[0];
6715
6811
  const code = output.code;
6716
- const outputFile = join5(directories.cache, "config.js");
6812
+ const outputFile = join6(directories.cache, "config.js");
6717
6813
  await mkdir2(directories.cache, { recursive: true });
6718
6814
  await writeFile2(outputFile, code);
6719
6815
  debug("Save config file:", style.info(outputFile));
6720
- const config = await import(`${outputFile}?${Date.now()}`);
6721
- queue2.push(config);
6816
+ const config2 = await import(`${outputFile}?${Date.now()}`);
6817
+ queue2.push(config2);
6722
6818
  }
6723
6819
  });
6724
6820
  return () => {
@@ -6733,7 +6829,7 @@ var watchFile = (path) => {
6733
6829
  };
6734
6830
 
6735
6831
  // src/config.ts
6736
- import { z as z31 } from "zod";
6832
+ import { z as z32 } from "zod";
6737
6833
  var ConfigError = class extends Error {
6738
6834
  constructor(error, data) {
6739
6835
  super(error.message);
@@ -6748,7 +6844,7 @@ var importConfig = async (options) => {
6748
6844
  setRoot(root2);
6749
6845
  debug("CWD:", style.info(root2));
6750
6846
  debug("Import config file");
6751
- const fileName = join6(root2, configFile);
6847
+ const fileName = join7(root2, configFile);
6752
6848
  const module = await importFile(fileName);
6753
6849
  const appConfig = typeof module.default === "function" ? await module.default(options) : module.default;
6754
6850
  debug("Validate config file");
@@ -6762,22 +6858,22 @@ var importConfig = async (options) => {
6762
6858
  schema2 = schema2.and(plugin.schema);
6763
6859
  }
6764
6860
  }
6765
- let config;
6861
+ let config2;
6766
6862
  try {
6767
- config = await schema2.parseAsync(appConfig);
6863
+ config2 = await schema2.parseAsync(appConfig);
6768
6864
  } catch (error) {
6769
- if (error instanceof z31.ZodError) {
6865
+ if (error instanceof z32.ZodError) {
6770
6866
  throw new ConfigError(error, appConfig);
6771
6867
  }
6772
6868
  throw error;
6773
6869
  }
6774
- debug("Load credentials", style.info(config.profile));
6775
- const credentials = getCredentials(config.profile);
6870
+ debug("Load credentials", style.info(config2.profile));
6871
+ const credentials = getCredentials(config2.profile);
6776
6872
  debug("Load AWS account ID");
6777
- const account = await getAccountId(credentials, config.region);
6873
+ const account = await getAccountId(credentials, config2.region);
6778
6874
  debug("Account ID:", style.info(account));
6779
6875
  return {
6780
- ...config,
6876
+ ...config2,
6781
6877
  account,
6782
6878
  credentials
6783
6879
  };
@@ -6789,7 +6885,7 @@ var watchConfig = async (options, resolve, reject) => {
6789
6885
  setRoot(root2);
6790
6886
  debug("CWD:", style.info(root2));
6791
6887
  debug("Import config file");
6792
- const fileName = join6(root2, configFile);
6888
+ const fileName = join7(root2, configFile);
6793
6889
  for await (const module of watchFile(fileName)) {
6794
6890
  const appConfig = typeof module.default === "function" ? await module.default(options) : module.default;
6795
6891
  debug("Validate config file");
@@ -6803,24 +6899,24 @@ var watchConfig = async (options, resolve, reject) => {
6803
6899
  schema2 = schema2.and(plugin.schema);
6804
6900
  }
6805
6901
  }
6806
- let config;
6902
+ let config2;
6807
6903
  try {
6808
- config = await schema2.parseAsync(appConfig);
6904
+ config2 = await schema2.parseAsync(appConfig);
6809
6905
  } catch (error) {
6810
- if (error instanceof z31.ZodError) {
6906
+ if (error instanceof z32.ZodError) {
6811
6907
  reject(new ConfigError(error, appConfig));
6812
6908
  continue;
6813
6909
  }
6814
6910
  reject(error);
6815
6911
  continue;
6816
6912
  }
6817
- debug("Load credentials", style.info(config.profile));
6818
- const credentials = getCredentials(config.profile);
6913
+ debug("Load credentials", style.info(config2.profile));
6914
+ const credentials = getCredentials(config2.profile);
6819
6915
  debug("Load AWS account ID");
6820
- const account = await getAccountId(credentials, config.region);
6916
+ const account = await getAccountId(credentials, config2.region);
6821
6917
  debug("Account ID:", style.info(account));
6822
6918
  resolve({
6823
- ...config,
6919
+ ...config2,
6824
6920
  account,
6825
6921
  credentials
6826
6922
  });
@@ -6833,39 +6929,38 @@ var br = () => {
6833
6929
  };
6834
6930
  var hr = () => {
6835
6931
  return (term) => {
6836
- term.out.write([
6837
- style.placeholder("\u2500".repeat(term.out.width())),
6838
- br()
6839
- ]);
6932
+ term.out.write([style.placeholder("\u2500".repeat(term.out.width())), br()]);
6840
6933
  };
6841
6934
  };
6842
6935
 
6843
6936
  // src/cli/ui/layout/list.ts
6844
6937
  var list = (data) => {
6845
- const padding = 3;
6938
+ const padding = 2;
6846
6939
  const gap = 1;
6847
6940
  const size = Object.keys(data).reduce((total, name) => {
6848
6941
  return name.length > total ? name.length : total;
6849
6942
  }, 0);
6850
6943
  return (term) => {
6851
6944
  term.out.gap();
6852
- term.out.write(Object.entries(data).map(([name, value]) => [
6853
- " ".repeat(padding),
6854
- style.label((name + ":").padEnd(size + gap + 1)),
6855
- value,
6856
- br()
6857
- ]));
6945
+ term.out.write(
6946
+ Object.entries(data).map(([name, value]) => [
6947
+ " ".repeat(padding),
6948
+ style.label((name + ":").padEnd(size + gap + 1)),
6949
+ value,
6950
+ br()
6951
+ ])
6952
+ );
6858
6953
  term.out.gap();
6859
6954
  };
6860
6955
  };
6861
6956
 
6862
6957
  // src/cli/ui/layout/header.ts
6863
- var header = (config) => {
6958
+ var header = (config2) => {
6864
6959
  return list({
6865
- App: config.name,
6866
- Stage: config.stage,
6867
- Region: config.region,
6868
- Profile: config.profile
6960
+ App: config2.name,
6961
+ Stage: config2.stage,
6962
+ Region: config2.region,
6963
+ Profile: config2.profile
6869
6964
  });
6870
6965
  };
6871
6966
 
@@ -6935,15 +7030,17 @@ var createSpinner = () => {
6935
7030
  // src/cli/ui/layout/dialog.ts
6936
7031
  import wrapAnsi from "wrap-ansi";
6937
7032
  var dialog = (type, lines) => {
6938
- const padding = 3;
7033
+ const padding = 2;
6939
7034
  const icon = style[type](symbol[type].padEnd(padding));
6940
7035
  return (term) => {
6941
- term.out.write(lines.map((line2, i) => {
6942
- if (i === 0) {
6943
- return icon + wrapAnsi(line2, term.out.width(), { hard: true });
6944
- }
6945
- return wrapAnsi(" ".repeat(padding) + line2, term.out.width(), { hard: true });
6946
- }).join(br()) + br());
7036
+ term.out.write(
7037
+ lines.map((line2, i) => {
7038
+ if (i === 0) {
7039
+ return icon + wrapAnsi(line2, term.out.width(), { hard: true });
7040
+ }
7041
+ return wrapAnsi(" ".repeat(padding) + line2, term.out.width(), { hard: true });
7042
+ }).join(br()) + br()
7043
+ );
6947
7044
  };
6948
7045
  };
6949
7046
  var loadingDialog = (message) => {
@@ -6952,14 +7049,7 @@ var loadingDialog = (message) => {
6952
7049
  const time = new Signal("");
6953
7050
  const timer = createTimer();
6954
7051
  return (term) => {
6955
- term.out.write([
6956
- icon,
6957
- " ",
6958
- description,
6959
- " ",
6960
- time,
6961
- br()
6962
- ]);
7052
+ term.out.write([icon, " ", description, " ", time, br()]);
6963
7053
  return (message2) => {
6964
7054
  description.set(message2);
6965
7055
  time.set(timer());
@@ -7166,9 +7256,11 @@ var Renderer = class {
7166
7256
  if (Array.isArray(fragment)) {
7167
7257
  return fragment.map(walk).join("");
7168
7258
  }
7169
- this.unsubs.push(fragment.subscribe(() => {
7170
- this.update();
7171
- }));
7259
+ this.unsubs.push(
7260
+ fragment.subscribe(() => {
7261
+ this.update();
7262
+ })
7263
+ );
7172
7264
  return walk(fragment.get());
7173
7265
  };
7174
7266
  this.unsubs.forEach((unsub) => unsub());
@@ -7181,17 +7273,19 @@ var Renderer = class {
7181
7273
  const start = Math.max(oldSize - height, 0);
7182
7274
  this.flushing = true;
7183
7275
  for (let y = start; y < size; y++) {
7184
- const newLine = screen[y];
7185
- const oldLine = this.screen[y];
7276
+ const newLine = screen.at(y);
7277
+ const oldLine = this.screen.at(y);
7186
7278
  if (newLine !== oldLine) {
7187
7279
  if (y >= oldSize && y !== 0) {
7188
7280
  const p = y - start - 1;
7189
- const x = screen[y - 1]?.length || 0;
7281
+ const x = screen.at(y - 1)?.length ?? 0;
7190
7282
  await this.setCursor(x, p);
7191
- await this.writeString("\n" + newLine);
7283
+ await this.writeString(newLine ? "\n" + newLine : "\n");
7192
7284
  } else {
7193
7285
  await this.setCursor(0, y - start);
7194
- await this.writeString(newLine);
7286
+ if (newLine) {
7287
+ await this.writeString(newLine);
7288
+ }
7195
7289
  await this.clearLine();
7196
7290
  }
7197
7291
  }
@@ -7221,11 +7315,7 @@ var createTerminal = (input = process.stdin, output = process.stdout) => {
7221
7315
 
7222
7316
  // src/cli/ui/layout/logo.ts
7223
7317
  var logo = () => {
7224
- return [
7225
- style.warning("\u26A1\uFE0F "),
7226
- style.primary("AWS"),
7227
- style.primary.dim("LESS")
7228
- ];
7318
+ return [style.warning("\u26A1\uFE0F"), style.primary("AWS"), style.primary.dim("LESS")];
7229
7319
  };
7230
7320
 
7231
7321
  // src/cli/ui/layout/logs.ts
@@ -7241,23 +7331,27 @@ var logs = () => {
7241
7331
  term.out.write([
7242
7332
  hr(),
7243
7333
  br(),
7244
- " ".repeat(3),
7334
+ " ".repeat(2),
7245
7335
  style.label("Debug Logs:"),
7246
7336
  br(),
7247
7337
  br(),
7248
7338
  logs2.map((log) => {
7249
7339
  const diff = log.date.getTime() - previous.getTime();
7250
- const time = `+${diff}`.padStart(8);
7340
+ const time = `+${diff}`.padStart(6);
7251
7341
  previous = log.date;
7252
- return wrapAnsi2([
7253
- style.attr(`${time}${style.attr.dim("ms")}`),
7254
- " [ ",
7255
- log.type,
7256
- " ] ",
7257
- log.message,
7258
- br(),
7259
- log.type === "error" ? br() : ""
7260
- ].join(""), term.out.width(), { hard: true, trim: false });
7342
+ return wrapAnsi2(
7343
+ [
7344
+ style.attr(`${time}${style.attr.dim("ms")}`),
7345
+ " [ ",
7346
+ log.type,
7347
+ " ] ",
7348
+ log.message,
7349
+ br(),
7350
+ log.type === "error" ? br() : ""
7351
+ ].join(""),
7352
+ term.out.width(),
7353
+ { hard: true, trim: false }
7354
+ );
7261
7355
  }),
7262
7356
  br(),
7263
7357
  hr()
@@ -7268,8 +7362,8 @@ var logs = () => {
7268
7362
  // src/cli/ui/layout/zod-error.ts
7269
7363
  var line = (value, level = 0, highlight = false) => {
7270
7364
  return [
7271
- highlight ? style.error(symbol.pointerSmall) + style.placeholder(" | ") : style.placeholder.dim(" | "),
7272
- " ".repeat(level),
7365
+ highlight ? style.error(symbol.pointerSmall) + style.placeholder(" | ") : style.placeholder.dim(" | "),
7366
+ " ".repeat(level),
7273
7367
  value,
7274
7368
  br()
7275
7369
  ];
@@ -7303,9 +7397,7 @@ var zodError = (error, data) => {
7303
7397
  return (term) => {
7304
7398
  for (const issue of error.issues) {
7305
7399
  term.out.gap();
7306
- term.out.write(dialog("error", [
7307
- style.error(issue.message)
7308
- ]));
7400
+ term.out.write(dialog("error", [style.error(issue.message)]));
7309
7401
  term.out.gap();
7310
7402
  term.out.write(line("{"));
7311
7403
  let context = data;
@@ -7355,10 +7447,10 @@ var layout = async (cb) => {
7355
7447
  term.out.gap();
7356
7448
  try {
7357
7449
  const options = program.optsWithGlobals();
7358
- const config = await importConfig(options);
7359
- term.out.write(header(config));
7450
+ const config2 = await importConfig(options);
7451
+ term.out.write(header(config2));
7360
7452
  term.out.gap();
7361
- await cb(config, term.out.write.bind(term.out), term);
7453
+ await cb(config2, term.out.write.bind(term.out), term);
7362
7454
  } catch (error) {
7363
7455
  term.out.gap();
7364
7456
  if (error instanceof ConfigError) {
@@ -7384,7 +7476,7 @@ var layout = async (cb) => {
7384
7476
  };
7385
7477
 
7386
7478
  // src/cli/ui/complex/builder.ts
7387
- import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
7479
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
7388
7480
 
7389
7481
  // src/cli/ui/layout/flex-line.ts
7390
7482
  var stripEscapeCode = (str) => {
@@ -7407,7 +7499,7 @@ var flexLine = (term, left, right, reserveSpace = 0) => {
7407
7499
  };
7408
7500
 
7409
7501
  // src/cli/ui/complex/builder.ts
7410
- import { dirname as dirname4, join as join7 } from "path";
7502
+ import { dirname as dirname5, join as join8 } from "path";
7411
7503
  var assetBuilder = (app) => {
7412
7504
  return async (term) => {
7413
7505
  const assets = [];
@@ -7432,63 +7524,104 @@ var assetBuilder = (app) => {
7432
7524
  }
7433
7525
  const stackNameSize = Math.max(...stacks.map((stack) => stack.name.length));
7434
7526
  const assetTypeSize = Math.max(...assets.map((asset) => asset.type.length));
7435
- await Promise.all(app.stacks.map(async (stack) => {
7436
- const group = new Signal([]);
7437
- groups.update((groups2) => [...groups2, group]);
7438
- await Promise.all([...stack.assets].map(async (asset) => {
7439
- if (!asset.build) {
7440
- return;
7441
- }
7442
- const [icon, stop] = createSpinner();
7443
- const details = new Signal({});
7444
- const line2 = flexLine(term, [
7445
- icon,
7446
- " ",
7447
- style.label(stack.name),
7448
- " ".repeat(stackNameSize - stack.name.length),
7449
- " ",
7450
- style.placeholder(symbol.pointerSmall),
7451
- " ",
7452
- style.warning(asset.type),
7453
- " ".repeat(assetTypeSize - asset.type.length),
7454
- " ",
7455
- style.placeholder(symbol.pointerSmall),
7456
- " ",
7457
- style.info(asset.id),
7458
- " "
7459
- ], [
7460
- " ",
7461
- derive([details], (details2) => {
7462
- return Object.entries(details2).map(([key, value]) => {
7463
- return `${style.label(key)} ${value}`;
7464
- }).join(style.placeholder(" \u2500 "));
7465
- }),
7466
- br()
7467
- ]);
7468
- group.update((group2) => [...group2, line2]);
7469
- const timer = createTimer();
7470
- try {
7471
- const data = await asset.build({
7472
- async write(file, data2) {
7473
- const fullpath = join7(directories.asset, asset.type, app.name, stack.name, asset.id, file);
7474
- const basepath = dirname4(fullpath);
7475
- await mkdir3(basepath, { recursive: true });
7476
- await writeFile3(fullpath, data2);
7527
+ await Promise.all(
7528
+ app.stacks.map(async (stack) => {
7529
+ const group = new Signal([]);
7530
+ groups.update((groups2) => [...groups2, group]);
7531
+ await Promise.all(
7532
+ [...stack.assets].map(async (asset) => {
7533
+ if (!asset.build) {
7534
+ return;
7477
7535
  }
7478
- });
7479
- details.set({
7480
- ...data,
7481
- time: timer()
7482
- });
7483
- icon.set(style.success(symbol.success));
7484
- } catch (error) {
7485
- icon.set(style.error(symbol.error));
7486
- throw error;
7487
- } finally {
7488
- stop();
7489
- }
7490
- }));
7491
- }));
7536
+ const [icon, stop] = createSpinner();
7537
+ const details = new Signal({});
7538
+ const line2 = flexLine(
7539
+ term,
7540
+ [
7541
+ icon,
7542
+ " ",
7543
+ style.label(stack.name),
7544
+ " ".repeat(stackNameSize - stack.name.length),
7545
+ " ",
7546
+ style.placeholder(symbol.pointerSmall),
7547
+ " ",
7548
+ style.warning(asset.type),
7549
+ " ".repeat(assetTypeSize - asset.type.length),
7550
+ " ",
7551
+ style.placeholder(symbol.pointerSmall),
7552
+ " ",
7553
+ style.info(asset.id),
7554
+ " "
7555
+ ],
7556
+ [
7557
+ " ",
7558
+ derive([details], (details2) => {
7559
+ return Object.entries(details2).map(([key, value]) => {
7560
+ return `${style.label(key)} ${value}`;
7561
+ }).join(style.placeholder(" \u2500 "));
7562
+ }),
7563
+ br()
7564
+ ]
7565
+ );
7566
+ group.update((group2) => [...group2, line2]);
7567
+ const timer = createTimer();
7568
+ const getFullPath = (file) => {
7569
+ return join8(directories.asset, asset.type, app.name, stack.name, asset.id, file);
7570
+ };
7571
+ const getFingerPrint = async () => {
7572
+ let value;
7573
+ try {
7574
+ value = await readFile4(getFullPath("FINGER_PRINT"), "utf8");
7575
+ } catch (_) {
7576
+ return void 0;
7577
+ }
7578
+ return value;
7579
+ };
7580
+ try {
7581
+ const data = await asset.build({
7582
+ async write(fingerprint, cb) {
7583
+ const prev = await getFingerPrint();
7584
+ if (prev === fingerprint) {
7585
+ return;
7586
+ }
7587
+ const file = getFullPath("FINGER_PRINT");
7588
+ const basepath = dirname5(file);
7589
+ await mkdir3(basepath, { recursive: true });
7590
+ await writeFile3(file, fingerprint);
7591
+ await cb(async (file2, data2) => {
7592
+ const fullpath = getFullPath(file2);
7593
+ const basepath2 = dirname5(fullpath);
7594
+ await mkdir3(basepath2, { recursive: true });
7595
+ await writeFile3(fullpath, data2);
7596
+ });
7597
+ },
7598
+ async read(fingerprint, files) {
7599
+ const prev = await getFingerPrint();
7600
+ if (prev !== fingerprint) {
7601
+ throw new TypeError(`Outdated fingerprint: ${fingerprint}`);
7602
+ }
7603
+ return Promise.all(
7604
+ files.map((file) => {
7605
+ return readFile4(getFullPath(file));
7606
+ })
7607
+ );
7608
+ }
7609
+ });
7610
+ details.set({
7611
+ ...data,
7612
+ time: timer()
7613
+ });
7614
+ icon.set(style.success(symbol.success));
7615
+ } catch (error) {
7616
+ icon.set(style.error(symbol.error));
7617
+ throw error;
7618
+ } finally {
7619
+ stop();
7620
+ }
7621
+ })
7622
+ );
7623
+ })
7624
+ );
7492
7625
  done("Done building stack assets");
7493
7626
  if (showDetailedView) {
7494
7627
  term.out.gap();
@@ -7501,31 +7634,39 @@ import { mkdir as mkdir4, rm } from "fs/promises";
7501
7634
  var cleanUp = async () => {
7502
7635
  debug("Clean up template, cache, and asset files");
7503
7636
  const paths = [
7504
- directories.asset,
7637
+ // directories.asset,
7505
7638
  directories.cache,
7506
7639
  directories.types,
7507
7640
  directories.template
7508
7641
  ];
7509
- await Promise.all(paths.map((path) => rm(path, {
7510
- recursive: true,
7511
- force: true,
7512
- maxRetries: 2
7513
- })));
7514
- await Promise.all(paths.map((path) => mkdir4(path, {
7515
- recursive: true
7516
- })));
7642
+ await Promise.all(
7643
+ paths.map(
7644
+ (path) => rm(path, {
7645
+ recursive: true,
7646
+ force: true,
7647
+ maxRetries: 2
7648
+ })
7649
+ )
7650
+ );
7651
+ await Promise.all(
7652
+ paths.map(
7653
+ (path) => mkdir4(path, {
7654
+ recursive: true
7655
+ })
7656
+ )
7657
+ );
7517
7658
  };
7518
7659
 
7519
7660
  // src/cli/ui/complex/template.ts
7520
7661
  import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
7521
- import { join as join8 } from "path";
7662
+ import { join as join9 } from "path";
7522
7663
  var templateBuilder = (app) => {
7523
7664
  return async (term) => {
7524
7665
  const done = term.out.write(loadingDialog("Building stack templates..."));
7525
7666
  await Promise.all(app.stacks.map(async (stack) => {
7526
7667
  const template = stack.toString(true);
7527
- const path = join8(directories.template, app.name);
7528
- const file = join8(path, `${stack.name}.json`);
7668
+ const path = join9(directories.template, app.name);
7669
+ const file = join9(path, `${stack.name}.json`);
7529
7670
  await mkdir5(path, { recursive: true });
7530
7671
  await writeFile4(file, template);
7531
7672
  }));
@@ -7534,21 +7675,21 @@ var templateBuilder = (app) => {
7534
7675
  };
7535
7676
 
7536
7677
  // src/cli/ui/complex/types.ts
7537
- var typesGenerator = (config) => {
7678
+ var typesGenerator = (config2) => {
7538
7679
  return async (term) => {
7539
7680
  const done = term.out.write(loadingDialog("Generate type definition files..."));
7540
- await generateResourceTypes(config);
7681
+ await generateResourceTypes(config2);
7541
7682
  done("Done generating type definition files");
7542
7683
  };
7543
7684
  };
7544
7685
 
7545
7686
  // src/cli/command/build.ts
7546
7687
  var build = (program2) => {
7547
- program2.command("build").argument("[stack...]", "Optionally filter stacks to build").description("Build your app").action(async (filters) => {
7548
- await layout(async (config, write) => {
7549
- const { app } = await toApp(config, filters);
7688
+ program2.command("build").argument("[stack...]", "Optionally filter stacks to build").description("Build your app assets").action(async (filters) => {
7689
+ await layout(async (config2, write) => {
7690
+ const { app } = await toApp(config2, filters);
7550
7691
  await cleanUp();
7551
- await write(typesGenerator(config));
7692
+ await write(typesGenerator(config2));
7552
7693
  await write(assetBuilder(app));
7553
7694
  await write(templateBuilder(app));
7554
7695
  });
@@ -7889,7 +8030,20 @@ var togglePrompt = (label, options = {}) => {
7889
8030
  down: deactivate,
7890
8031
  up: activate
7891
8032
  });
7892
- term.out.write([icon, " ", style.label(label), " ", sep, " ", inactiveText, " ", mid, " ", activeText, br()]);
8033
+ term.out.write([
8034
+ icon,
8035
+ " ",
8036
+ style.label(label),
8037
+ " ",
8038
+ sep,
8039
+ " ",
8040
+ inactiveText,
8041
+ " ",
8042
+ mid,
8043
+ " ",
8044
+ activeText,
8045
+ br()
8046
+ ]);
7893
8047
  });
7894
8048
  };
7895
8049
 
@@ -7903,11 +8057,11 @@ var confirmPrompt = (label, options = {}) => {
7903
8057
  };
7904
8058
 
7905
8059
  // src/cli/ui/complex/bootstrap.ts
7906
- var bootstrapDeployer = (config) => {
8060
+ var bootstrapDeployer = (config2) => {
7907
8061
  return async (term) => {
7908
8062
  debug("Initializing bootstrap");
7909
- const { app, stack } = bootstrapStack(config.account, config.region);
7910
- const client = new StackClient(app, config.account, config.region, config.credentials);
8063
+ const { app, stack } = bootstrapStack(config2.account, config2.region);
8064
+ const client = new StackClient(app, config2.account, config2.region, config2.credentials);
7911
8065
  const shouldDeploy = await shouldDeployBootstrap(client, stack);
7912
8066
  if (shouldDeploy) {
7913
8067
  term.out.write(dialog("warning", [`Your app hasn't been bootstrapped yet`]));
@@ -7932,8 +8086,8 @@ var bootstrapDeployer = (config) => {
7932
8086
  // src/cli/command/bootstrap.ts
7933
8087
  var bootstrap = (program2) => {
7934
8088
  program2.command("bootstrap").description("Create the awsless bootstrap stack").action(async () => {
7935
- await layout(async (config, write) => {
7936
- await write(bootstrapDeployer(config));
8089
+ await layout(async (config2, write) => {
8090
+ await write(bootstrapDeployer(config2));
7937
8091
  });
7938
8092
  });
7939
8093
  };
@@ -7946,15 +8100,7 @@ var stacksDeployer = (deploymentLine) => {
7946
8100
  const ui = {};
7947
8101
  term.out.gap();
7948
8102
  for (const i in deploymentLine) {
7949
- const line2 = flexLine(
7950
- term,
7951
- [" "],
7952
- [
7953
- " ",
7954
- style.placeholder(Number(i) + 1),
7955
- style.placeholder(" \u2500\u2500")
7956
- ]
7957
- );
8103
+ const line2 = flexLine(term, [" "], [" ", style.placeholder(Number(i) + 1), style.placeholder(" \u2500\u2500")]);
7958
8104
  term.out.write(line2);
7959
8105
  term.out.write(br());
7960
8106
  for (const stack of deploymentLine[i]) {
@@ -7964,7 +8110,7 @@ var stacksDeployer = (deploymentLine) => {
7964
8110
  let stopSpinner;
7965
8111
  term.out.write([
7966
8112
  icon,
7967
- " ",
8113
+ " ",
7968
8114
  name,
7969
8115
  " ".repeat(stackNameSize - stack.name.length),
7970
8116
  " ",
@@ -7999,11 +8145,7 @@ var stacksDeployer = (deploymentLine) => {
7999
8145
  };
8000
8146
  }
8001
8147
  }
8002
- term.out.write(flexLine(term, [" "], [
8003
- " ",
8004
- style.warning("\u26A1\uFE0F"),
8005
- style.placeholder("\u2500\u2500")
8006
- ]));
8148
+ term.out.write(flexLine(term, [" "], [" ", style.warning("\u26A1\uFE0F"), style.placeholder("\u2500\u2500")]));
8007
8149
  term.out.gap();
8008
8150
  return ui;
8009
8151
  };
@@ -8011,32 +8153,34 @@ var stacksDeployer = (deploymentLine) => {
8011
8153
 
8012
8154
  // src/cli/command/status.ts
8013
8155
  var status = (program2) => {
8014
- program2.command("status").argument("[stacks...]", "Optionally filter stacks to lookup status").description("View the application status").action(async (filters) => {
8015
- await layout(async (config, write) => {
8016
- const { app, deploymentLine } = await toApp(config, filters);
8156
+ program2.command("status").argument("[stacks...]", "Optionally filter stacks to lookup status").description("View the app status").action(async (filters) => {
8157
+ await layout(async (config2, write) => {
8158
+ const { app, deploymentLine } = await toApp(config2, filters);
8017
8159
  await cleanUp();
8018
8160
  await write(assetBuilder(app));
8019
8161
  await write(templateBuilder(app));
8020
8162
  const doneLoading = write(loadingDialog("Loading stack information..."));
8021
- const client = new StackClient(app, config.account, config.region, config.credentials);
8163
+ const client = new StackClient(app, config2.account, config2.region, config2.credentials);
8022
8164
  const statuses = [];
8023
8165
  const ui = write(stacksDeployer(deploymentLine));
8024
8166
  debug("Load metadata for all deployed stacks on AWS");
8025
- await Promise.all(app.stacks.map(async (stack, i) => {
8026
- const item = ui[stack.name];
8027
- item.start("loading");
8028
- const info = await client.get(stack.name, stack.region);
8029
- if (!info) {
8030
- item.fail("NON EXISTENT");
8031
- statuses.push("non-existent");
8032
- } else if (info.template !== stack.toString()) {
8033
- item.warn("OUT OF DATE");
8034
- statuses.push("out-of-date");
8035
- } else {
8036
- item.done("UP TO DATE");
8037
- statuses.push("up-to-date");
8038
- }
8039
- }));
8167
+ await Promise.all(
8168
+ app.stacks.map(async (stack) => {
8169
+ const item = ui[stack.name];
8170
+ item.start("loading");
8171
+ const info = await client.get(stack.name, stack.region);
8172
+ if (!info) {
8173
+ item.fail("NON EXISTENT");
8174
+ statuses.push("non-existent");
8175
+ } else if (info.template !== stack.toString()) {
8176
+ item.warn("OUT OF DATE");
8177
+ statuses.push("out-of-date");
8178
+ } else {
8179
+ item.done("UP TO DATE");
8180
+ statuses.push("up-to-date");
8181
+ }
8182
+ })
8183
+ );
8040
8184
  doneLoading("Done loading stack information");
8041
8185
  debug("Done loading data for all deployed stacks on AWS");
8042
8186
  if (statuses.includes("non-existent") || statuses.includes("out-of-date")) {
@@ -8049,13 +8193,13 @@ var status = (program2) => {
8049
8193
  };
8050
8194
 
8051
8195
  // src/cli/ui/complex/publisher.ts
8052
- import { readFile as readFile3 } from "fs/promises";
8053
- import { join as join9 } from "path";
8196
+ import { readFile as readFile5 } from "fs/promises";
8197
+ import { join as join10 } from "path";
8054
8198
  import { GetObjectCommand, ObjectCannedACL as ObjectCannedACL2, PutObjectCommand as PutObjectCommand2, S3Client as S3Client2, StorageClass as StorageClass2 } from "@aws-sdk/client-s3";
8055
- var assetPublisher = (config, app) => {
8199
+ var assetPublisher = (config2, app) => {
8056
8200
  const client = new S3Client2({
8057
- credentials: config.credentials,
8058
- region: config.region,
8201
+ credentials: config2.credentials,
8202
+ region: config2.region,
8059
8203
  maxAttempts: 5
8060
8204
  });
8061
8205
  return async (term) => {
@@ -8064,22 +8208,24 @@ var assetPublisher = (config, app) => {
8064
8208
  app.stacks.map(async (stack) => {
8065
8209
  await Promise.all(
8066
8210
  [...stack.assets].map(async (asset) => {
8211
+ const getFullPath = (file) => {
8212
+ return join10(directories.asset, asset.type, app.name, stack.name, asset.id, file);
8213
+ };
8067
8214
  await asset.publish?.({
8068
- async read(file) {
8069
- const path = join9(
8070
- directories.asset,
8071
- asset.type,
8072
- app.name,
8073
- stack.name,
8074
- asset.id,
8075
- file
8215
+ async read(fingerprint, files) {
8216
+ const prev = await readFile5(getFullPath("FINGER_PRINT"), "utf8");
8217
+ if (prev !== fingerprint) {
8218
+ throw new TypeError(`Outdated fingerprint: ${fingerprint}`);
8219
+ }
8220
+ return Promise.all(
8221
+ files.map((file) => {
8222
+ return readFile5(getFullPath(file));
8223
+ })
8076
8224
  );
8077
- const data = await readFile3(path);
8078
- return data;
8079
8225
  },
8080
8226
  async publish(name, data, hash) {
8081
8227
  const key = `${app.name}/${stack.name}/${asset.type}/${name}`;
8082
- const bucket = assetBucketName(config.account, config.region);
8228
+ const bucket = assetBucketName(config2.account, config2.region);
8083
8229
  let getResult;
8084
8230
  try {
8085
8231
  getResult = await client.send(
@@ -8109,7 +8255,7 @@ var assetPublisher = (config, app) => {
8109
8255
  ACL: ObjectCannedACL2.private,
8110
8256
  StorageClass: StorageClass2.STANDARD,
8111
8257
  Metadata: {
8112
- hash
8258
+ hash: hash.toString("utf8")
8113
8259
  }
8114
8260
  })
8115
8261
  );
@@ -8128,44 +8274,268 @@ var assetPublisher = (config, app) => {
8128
8274
  };
8129
8275
  };
8130
8276
 
8277
+ // src/cli/ui/complex/tester.ts
8278
+ import { configDefaults } from "vitest/config";
8279
+ import { startVitest } from "vitest/node";
8280
+ import commonjs3 from "@rollup/plugin-commonjs";
8281
+ import nodeResolve3 from "@rollup/plugin-node-resolve";
8282
+ import json3 from "@rollup/plugin-json";
8283
+ import { swc as swc4 } from "rollup-plugin-swc3";
8284
+ import { getTests } from "@vitest/runner/utils";
8285
+ import { basename as basename3, extname } from "path";
8286
+ var CustomReporter = class {
8287
+ constructor(stack, out) {
8288
+ this.stack = stack;
8289
+ this.out = out;
8290
+ }
8291
+ started = false;
8292
+ interval;
8293
+ timer;
8294
+ tasks;
8295
+ ctx;
8296
+ line = new Signal([]);
8297
+ icon;
8298
+ logs = [];
8299
+ stopSpinner;
8300
+ onInit(ctx) {
8301
+ this.ctx = ctx;
8302
+ }
8303
+ onCollected() {
8304
+ this.tasks = this.ctx?.state.getFiles();
8305
+ if (!this.started) {
8306
+ this.start();
8307
+ this.started = true;
8308
+ }
8309
+ }
8310
+ onFinished() {
8311
+ this.stop();
8312
+ }
8313
+ start() {
8314
+ const [icon, stop] = createSpinner();
8315
+ this.icon = icon;
8316
+ this.stopSpinner = stop;
8317
+ this.interval = setInterval(this.update.bind(this), 33);
8318
+ this.timer = createTimer();
8319
+ this.update();
8320
+ this.out.write(this.line);
8321
+ this.out.gap();
8322
+ }
8323
+ stop() {
8324
+ clearInterval(this.interval);
8325
+ this.interval = void 0;
8326
+ this.stopSpinner?.();
8327
+ const tests = getTests(this.tasks);
8328
+ const passed = tests.filter((t) => t.result?.state === "pass").length;
8329
+ const failed = tests.filter((t) => t.result?.state === "fail").length;
8330
+ const icon = failed > 0 ? style.error(symbol.error) : style.success(symbol.success);
8331
+ const values = [icon, " ", style.label(this.stack)];
8332
+ if (passed > 0) {
8333
+ values.push(" ", style.placeholder(symbol.pointerSmall), style.success(` ${passed} passed`));
8334
+ }
8335
+ if (failed > 0) {
8336
+ values.push(" ", style.placeholder(symbol.pointerSmall), style.error(` ${failed} failed`));
8337
+ }
8338
+ this.line.set([
8339
+ ...values,
8340
+ " ",
8341
+ style.placeholder(symbol.pointerSmall),
8342
+ " ",
8343
+ this.timer?.(),
8344
+ br(),
8345
+ this.formatLogs(),
8346
+ this.formatErrors()
8347
+ ]);
8348
+ }
8349
+ update() {
8350
+ const tasks = this.runningTasks(this.tasks);
8351
+ this.line.set([
8352
+ this.icon,
8353
+ " ",
8354
+ style.label(this.stack),
8355
+ // ' ',
8356
+ // style.placeholder(`(${tests.length})`),
8357
+ ...tasks.map((task, i) => [
8358
+ " ",
8359
+ style.placeholder(symbol.pointerSmall),
8360
+ " ",
8361
+ i === 0 ? task.name : style.placeholder(task.name)
8362
+ ]),
8363
+ // style.placeholder(tests.length),
8364
+ // ' ',
8365
+ // style.placeholder(symbol.pointerSmall),
8366
+ // ' ',
8367
+ " ",
8368
+ style.placeholder(symbol.pointerSmall),
8369
+ " ",
8370
+ this.timer?.(),
8371
+ br(),
8372
+ this.formatLogs()
8373
+ // this.formatErrors(),
8374
+ // ...this.renderTask(this.tasks!),
8375
+ ]);
8376
+ }
8377
+ formatLogs() {
8378
+ return this.logs.map((log) => {
8379
+ return [style.placeholder(`${symbol.dot} LOG `), log];
8380
+ });
8381
+ }
8382
+ formatErrors() {
8383
+ const tests = getTests(this.tasks);
8384
+ return tests.map((test2) => {
8385
+ if (!test2.result?.errors || test2.result.errors.length === 0) {
8386
+ return [];
8387
+ }
8388
+ return [
8389
+ br(),
8390
+ style.error(`${symbol.dot} `),
8391
+ style.error.inverse(` FAIL `),
8392
+ " ",
8393
+ style.placeholder(symbol.pointerSmall),
8394
+ " ",
8395
+ test2.file?.name,
8396
+ " ",
8397
+ style.placeholder(`${symbol.pointerSmall} ${test2.name}`),
8398
+ br(),
8399
+ test2.result.errors.map((error) => {
8400
+ const [message, ...comment] = error.message.split("//");
8401
+ const values = [
8402
+ " ",
8403
+ style.error(`${style.error.bold(error.name)}: ${message}`),
8404
+ comment.length > 0 ? style.placeholder(`//${comment}`) : "",
8405
+ br()
8406
+ ];
8407
+ if (error.showDiff && error.diff) {
8408
+ values.push(br(), error.diff, br());
8409
+ }
8410
+ return values;
8411
+ })
8412
+ ];
8413
+ });
8414
+ }
8415
+ onUserConsoleLog(log) {
8416
+ if (log.taskId) {
8417
+ const test2 = this.ctx?.state.idMap.get(log.taskId);
8418
+ if (test2) {
8419
+ test2.name;
8420
+ }
8421
+ }
8422
+ this.logs.push(log.content);
8423
+ }
8424
+ formatFileName(path) {
8425
+ const ext = extname(path);
8426
+ const bas = basename3(path, ext);
8427
+ return `${bas}${style.placeholder(ext)}`;
8428
+ }
8429
+ runningTask(tasks) {
8430
+ return tasks.find((t) => t.result?.state === "run");
8431
+ }
8432
+ runningTasks(tasks) {
8433
+ const task = this.runningTask(tasks);
8434
+ if (!task) {
8435
+ return [];
8436
+ }
8437
+ if (task.type === "suite") {
8438
+ return [task, ...this.runningTasks(task.tasks)];
8439
+ }
8440
+ return [task];
8441
+ }
8442
+ };
8443
+ var singleTester = (stack, dir) => {
8444
+ return async (term) => {
8445
+ await startVitest(
8446
+ "test",
8447
+ [],
8448
+ {
8449
+ // name: config.name,
8450
+ watch: false,
8451
+ ui: false,
8452
+ silent: true,
8453
+ // dir: '',
8454
+ // dir: './test',
8455
+ dir,
8456
+ include: ["**/*.{js,jsx,ts,tsx}"],
8457
+ exclude: ["**/_*", "**/_*/**", ...configDefaults.exclude],
8458
+ globals: true,
8459
+ reporters: new CustomReporter(stack, term.out)
8460
+ // outputFile: {
8461
+ // json: './.awsless/test/output.json',
8462
+ // },
8463
+ },
8464
+ {
8465
+ plugins: [
8466
+ // @ts-ignore
8467
+ commonjs3({ sourceMap: true }),
8468
+ // @ts-ignore
8469
+ nodeResolve3({ preferBuiltins: true }),
8470
+ swc4({
8471
+ jsc: {
8472
+ // baseUrl: dirname(input),
8473
+ minify: { sourceMap: true }
8474
+ },
8475
+ sourceMaps: true
8476
+ }),
8477
+ // @ts-ignore
8478
+ json3()
8479
+ ]
8480
+ }
8481
+ );
8482
+ };
8483
+ };
8484
+ var runTester = (tests) => {
8485
+ return async (term) => {
8486
+ for (const [name, paths] of tests.entries()) {
8487
+ for (const path of paths) {
8488
+ await term.out.write(singleTester(name, path));
8489
+ }
8490
+ }
8491
+ };
8492
+ };
8493
+
8131
8494
  // src/cli/command/deploy.ts
8132
8495
  var deploy = (program2) => {
8133
8496
  program2.command("deploy").argument("[stacks...]", "Optionally filter stacks to deploy").description("Deploy your app to AWS").action(async (filters) => {
8134
- await layout(async (config, write) => {
8135
- await write(bootstrapDeployer(config));
8136
- const { app, deploymentLine } = await toApp(config, filters);
8497
+ await layout(async (config2, write) => {
8498
+ await write(bootstrapDeployer(config2));
8499
+ const { app, deploymentLine, tests } = await toApp(config2, filters);
8137
8500
  const stackNames = app.stacks.map((stack) => stack.name);
8138
8501
  const formattedFilter = stackNames.map((i) => style.info(i)).join(style.placeholder(", "));
8139
8502
  debug("Stacks to deploy", formattedFilter);
8140
8503
  if (!process.env.SKIP_PROMPT) {
8141
8504
  const deployAll = filters.length === 0;
8142
8505
  const deploySingle = filters.length === 1;
8143
- const confirm = await write(confirmPrompt(deployAll ? `Are you sure you want to deploy ${style.warning("all")} stacks?` : deploySingle ? `Are you sure you want to deploy the ${formattedFilter} stack?` : `Are you sure you want to deploy the [ ${formattedFilter} ] stacks?`));
8506
+ const confirm = await write(
8507
+ confirmPrompt(
8508
+ deployAll ? `Are you sure you want to deploy ${style.warning("all")} stacks?` : deploySingle ? `Are you sure you want to deploy the ${formattedFilter} stack?` : `Are you sure you want to deploy the [ ${formattedFilter} ] stacks?`
8509
+ )
8510
+ );
8144
8511
  if (!confirm) {
8145
8512
  throw new Cancelled();
8146
8513
  }
8147
8514
  }
8148
8515
  await cleanUp();
8149
- await write(typesGenerator(config));
8516
+ await write(typesGenerator(config2));
8517
+ await write(runTester(tests));
8150
8518
  await write(assetBuilder(app));
8151
- await write(assetPublisher(config, app));
8519
+ await write(assetPublisher(config2, app));
8152
8520
  await write(templateBuilder(app));
8153
8521
  const doneDeploying = write(loadingDialog("Deploying stacks to AWS..."));
8154
- const client = new StackClient(app, config.account, config.region, config.credentials);
8522
+ const client = new StackClient(app, config2.account, config2.region, config2.credentials);
8155
8523
  const ui = write(stacksDeployer(deploymentLine));
8156
8524
  for (const line2 of deploymentLine) {
8157
- const results = await Promise.allSettled(line2.map(async (stack) => {
8158
- const item = ui[stack.name];
8159
- item.start("deploying");
8160
- try {
8161
- await client.deploy(stack);
8162
- } catch (error) {
8163
- debugError(error);
8164
- item.fail("failed");
8165
- throw error;
8166
- }
8167
- item.done("deployed");
8168
- }));
8525
+ const results = await Promise.allSettled(
8526
+ line2.map(async (stack) => {
8527
+ const item = ui[stack.name];
8528
+ item.start("deploying");
8529
+ try {
8530
+ await client.deploy(stack);
8531
+ } catch (error) {
8532
+ debugError(error);
8533
+ item.fail("failed");
8534
+ throw error;
8535
+ }
8536
+ item.done("deployed");
8537
+ })
8538
+ );
8169
8539
  for (const result of results) {
8170
8540
  if (result.status === "rejected") {
8171
8541
  throw result.reason;
@@ -8216,11 +8586,7 @@ var textPrompt = (label, options = {}) => {
8216
8586
  resolve(value.get().join(""));
8217
8587
  },
8218
8588
  input: (chr) => {
8219
- value.update((value2) => [
8220
- ...value2.slice(0, cursor.get()),
8221
- chr,
8222
- ...value2.slice(cursor.get())
8223
- ]);
8589
+ value.update((value2) => [...value2.slice(0, cursor.get()), chr, ...value2.slice(cursor.get())]);
8224
8590
  cursor.update((cursor2) => cursor2 + 1);
8225
8591
  },
8226
8592
  delete() {
@@ -8234,104 +8600,119 @@ var textPrompt = (label, options = {}) => {
8234
8600
  cursor.update((cursor2) => Math.min(value.get().length, cursor2 + 1));
8235
8601
  }
8236
8602
  });
8237
- term.out.write([icon, " ", style.label(label), " ", sep, " ", formatted, br()]);
8603
+ term.out.write([icon, " ", style.label(label), " ", sep, " ", formatted, br()]);
8238
8604
  });
8239
8605
  };
8240
8606
  };
8241
8607
 
8242
- // src/cli/command/secrets/set.ts
8608
+ // src/cli/command/config/set.ts
8243
8609
  var set = (program2) => {
8244
- program2.command("set <name>").description("Set a secret value").action(async (name) => {
8245
- await layout(async (config, write) => {
8246
- const params = new Params(config);
8247
- write(list({
8248
- "Set secret parameter": style.info(name)
8249
- }));
8250
- const value = await write(textPrompt("Enter secret value"));
8610
+ program2.command("set <name>").description("Set a config value").action(async (name) => {
8611
+ await layout(async (config2, write) => {
8612
+ const params = new Params(config2);
8613
+ write(
8614
+ list({
8615
+ "Set config parameter": style.info(name)
8616
+ })
8617
+ );
8618
+ const value = await write(textPrompt("Enter config value"));
8251
8619
  if (value === "") {
8252
- write(dialog("error", [`Provided secret value can't be empty`]));
8620
+ write(dialog("error", [`Provided config value can't be empty`]));
8253
8621
  } else {
8254
- const done = write(loadingDialog(`Saving remote secret parameter`));
8622
+ const done = write(loadingDialog(`Saving remote config parameter`));
8255
8623
  await params.set(name, value);
8256
- done(`Done saving remote secret parameter`);
8624
+ done(`Done saving remote config parameter`);
8257
8625
  }
8258
8626
  });
8259
8627
  });
8260
8628
  };
8261
8629
 
8262
- // src/cli/command/secrets/get.ts
8630
+ // src/cli/command/config/get.ts
8263
8631
  var get = (program2) => {
8264
- program2.command("get <name>").description("Get a secret value").action(async (name) => {
8265
- await layout(async (config, write) => {
8266
- const params = new Params(config);
8267
- const done = write(loadingDialog(`Getting remote secret parameter`));
8632
+ program2.command("get <name>").description("Get a config value").action(async (name) => {
8633
+ await layout(async (config2, write) => {
8634
+ const params = new Params(config2);
8635
+ const done = write(loadingDialog(`Getting remote config parameter`));
8268
8636
  const value = await params.get(name);
8269
- done(`Done getting remote secret parameter`);
8270
- write(list({
8271
- Name: name,
8272
- Value: value || style.error("(empty)")
8273
- }));
8637
+ done(`Done getting remote config parameter`);
8638
+ write(
8639
+ list({
8640
+ Name: name,
8641
+ Value: value || style.error("(empty)")
8642
+ })
8643
+ );
8274
8644
  });
8275
8645
  });
8276
8646
  };
8277
8647
 
8278
- // src/cli/command/secrets/delete.ts
8648
+ // src/cli/command/config/delete.ts
8279
8649
  var del = (program2) => {
8280
- program2.command("delete <name>").description("Delete a secret value").action(async (name) => {
8281
- await layout(async (config, write) => {
8282
- const params = new Params(config);
8283
- write(dialog("warning", [`Your deleting the ${style.info(name)} secret parameter`]));
8650
+ program2.command("delete <name>").description("Delete a config value").action(async (name) => {
8651
+ await layout(async (config2, write) => {
8652
+ const params = new Params(config2);
8653
+ write(dialog("warning", [`Your deleting the ${style.info(name)} config parameter`]));
8284
8654
  const confirm = await write(confirmPrompt("Are you sure?"));
8285
8655
  if (!confirm) {
8286
8656
  throw new Cancelled();
8287
8657
  }
8288
- const done = write(loadingDialog(`Deleting remote secret parameter`));
8658
+ const done = write(loadingDialog(`Deleting remote config parameter`));
8289
8659
  const value = await params.get(name);
8290
8660
  await params.delete(name);
8291
- done(`Done deleting remote secret parameter`);
8292
- write(list({
8293
- Name: name,
8294
- Value: value || style.error("(empty)")
8295
- }));
8661
+ done(`Done deleting remote config parameter`);
8662
+ write(
8663
+ list({
8664
+ Name: name,
8665
+ Value: value || style.error("(empty)")
8666
+ })
8667
+ );
8296
8668
  });
8297
8669
  });
8298
8670
  };
8299
8671
 
8300
- // src/cli/command/secrets/list.ts
8672
+ // src/cli/command/config/list.ts
8301
8673
  var list2 = (program2) => {
8302
- program2.command("list").description(`List all secret value's`).action(async () => {
8303
- await layout(async (config, write) => {
8304
- const params = new Params(config);
8305
- const done = write(loadingDialog("Loading secret parameters..."));
8674
+ program2.command("list").description(`List all config value's`).action(async () => {
8675
+ await layout(async (config2, write) => {
8676
+ const params = new Params(config2);
8677
+ const done = write(loadingDialog("Loading config parameters..."));
8306
8678
  const values = await params.list();
8307
- done("Done loading secret values");
8679
+ done("Done loading config values");
8308
8680
  if (Object.keys(values).length > 0) {
8309
8681
  write(list(values));
8310
8682
  } else {
8311
- write(dialog("warning", ["No secret parameters found"]));
8683
+ write(dialog("warning", ["No config parameters found"]));
8312
8684
  }
8313
8685
  });
8314
8686
  });
8315
8687
  };
8316
8688
 
8317
- // src/cli/command/secrets/index.ts
8318
- var commands = [
8319
- set,
8320
- get,
8321
- del,
8322
- list2
8323
- ];
8324
- var secrets = (program2) => {
8325
- const command = program2.command("secrets").description(`Manage app secrets`);
8689
+ // src/cli/command/config/index.ts
8690
+ var commands = [set, get, del, list2];
8691
+ var config = (program2) => {
8692
+ const command = program2.command("config").description(`Manage app config`);
8326
8693
  commands.forEach((cb) => cb(command));
8327
8694
  };
8328
8695
 
8696
+ // src/cli/command/test.ts
8697
+ var test = (program2) => {
8698
+ program2.command("test").argument("[stacks...]", "Optionally filter stacks to test").description("Test your app").action(async (filters) => {
8699
+ await layout(async (config2, write) => {
8700
+ const { tests } = await toApp(config2, filters);
8701
+ if (tests.size === 0) {
8702
+ write(dialog("warning", ["No tests found"]));
8703
+ return;
8704
+ }
8705
+ await write(runTester(tests));
8706
+ });
8707
+ });
8708
+ };
8709
+
8329
8710
  // src/cli/command/types.ts
8330
8711
  var types = (program2) => {
8331
8712
  program2.command("types").description("Generate type definition files").action(async () => {
8332
- await layout(async (config, write) => {
8713
+ await layout(async (config2, write) => {
8333
8714
  await cleanUp();
8334
- await write(typesGenerator(config));
8715
+ await write(typesGenerator(config2));
8335
8716
  });
8336
8717
  });
8337
8718
  };
@@ -8341,9 +8722,9 @@ var dev = (program2) => {
8341
8722
  program2.command("dev").description("Start the development service").action(async () => {
8342
8723
  await layout(async (_, write) => {
8343
8724
  const options = program2.optsWithGlobals();
8344
- await watchConfig(options, async (config) => {
8725
+ await watchConfig(options, async (config2) => {
8345
8726
  await cleanUp();
8346
- await write(typesGenerator(config));
8727
+ await write(typesGenerator(config2));
8347
8728
  }, (error) => {
8348
8729
  if (error instanceof ConfigError) {
8349
8730
  write(zodError(error.error, error.data));
@@ -8362,8 +8743,8 @@ var dev = (program2) => {
8362
8743
  // src/cli/command/delete.ts
8363
8744
  var del2 = (program2) => {
8364
8745
  program2.command("delete").argument("[stacks...]", "Optionally filter stacks to delete").description("Delete your app from AWS").action(async (filters) => {
8365
- await layout(async (config, write) => {
8366
- const { app, deploymentLine } = await toApp(config, filters);
8746
+ await layout(async (config2, write) => {
8747
+ const { app, deploymentLine } = await toApp(config2, filters);
8367
8748
  const deletingLine = deploymentLine.reverse();
8368
8749
  const stackNames = app.stacks.map((stack) => stack.name);
8369
8750
  const formattedFilter = stackNames.map((i) => style.info(i)).join(style.placeholder(", "));
@@ -8377,7 +8758,7 @@ var del2 = (program2) => {
8377
8758
  }
8378
8759
  }
8379
8760
  const doneDeploying = write(loadingDialog("Deleting stacks from AWS..."));
8380
- const client = new StackClient(app, config.account, config.region, config.credentials);
8761
+ const client = new StackClient(app, config2.account, config2.region, config2.credentials);
8381
8762
  const ui = write(stacksDeployer(deletingLine));
8382
8763
  for (const line2 of deletingLine) {
8383
8764
  const results = await Promise.allSettled(line2.map(async (stack) => {
@@ -8430,8 +8811,8 @@ var commands2 = [
8430
8811
  deploy,
8431
8812
  del2,
8432
8813
  dev,
8433
- secrets
8434
- // test,
8814
+ config,
8815
+ test
8435
8816
  // diff,
8436
8817
  // remove,
8437
8818
  ];