@awsless/awsless 0.0.97 → 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 {
@@ -1587,28 +1664,29 @@ type Response<F extends Func> = PartialDeep<Awaited<InvokeResponse<F>>, { recurs
1587
1664
  type MockHandle<F extends Func> = (payload: Parameters<F>[0]) => Promise<Response<F>> | Response<F> | void | Promise<void> | Promise<Promise<void>>
1588
1665
  type MockHandleOrResponse<F extends Func> = MockHandle<F> | Response<F>
1589
1666
  type MockBuilder<F extends Func> = (handleOrResponse?: MockHandleOrResponse<F>) => void
1667
+ type MockObject<F extends Func> = Mock<Parameters<F>, ReturnType<F>>
1590
1668
  `;
1591
1669
  var functionPlugin = definePlugin({
1592
1670
  name: "function",
1593
1671
  schema,
1594
- onTypeGen({ config }) {
1672
+ onTypeGen({ config: config2 }) {
1595
1673
  const types2 = new TypeGen("@awsless/awsless");
1596
1674
  const resources = new TypeObject(1);
1597
1675
  const mocks = new TypeObject(1);
1598
1676
  const mockResponses = new TypeObject(1);
1599
- for (const stack of config.stacks) {
1677
+ for (const stack of config2.stacks) {
1600
1678
  const resource = new TypeObject(2);
1601
1679
  const mock = new TypeObject(2);
1602
1680
  const mockResponse = new TypeObject(2);
1603
1681
  for (const [name, fileOrProps] of Object.entries(stack.functions || {})) {
1604
1682
  const varName = camelCase2(`${stack.name}-${name}`);
1605
- const funcName = formatName(`${config.name}-${stack.name}-${name}`);
1683
+ const funcName = formatName(`${config2.name}-${stack.name}-${name}`);
1606
1684
  const file = typeof fileOrProps === "string" ? fileOrProps : fileOrProps.file;
1607
1685
  const relFile = relative2(directories.types, file);
1608
1686
  types2.addImport(varName, relFile);
1609
1687
  resource.addType(name, `Invoke<'${funcName}', typeof ${varName}>`);
1610
1688
  mock.addType(name, `MockBuilder<typeof ${varName}>`);
1611
- mockResponse.addType(name, `Mock`);
1689
+ mockResponse.addType(name, `MockObject<typeof ${varName}>`);
1612
1690
  }
1613
1691
  mocks.addType(stack.name, mock);
1614
1692
  resources.addType(stack.name, resource);
@@ -1621,9 +1699,9 @@ var functionPlugin = definePlugin({
1621
1699
  return types2.toString();
1622
1700
  },
1623
1701
  onStack(ctx) {
1624
- const { config, stack } = ctx;
1702
+ const { config: config2, stack } = ctx;
1625
1703
  for (const [id, fileOrProps] of Object.entries(ctx.stackConfig.functions || {})) {
1626
- 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 };
1627
1705
  const lambda = toLambdaFunction(ctx, id, fileOrProps);
1628
1706
  const invoke = new EventInvokeConfig(id, {
1629
1707
  functionName: lambda.name,
@@ -1641,12 +1719,12 @@ var functionPlugin = definePlugin({
1641
1719
  }
1642
1720
  });
1643
1721
  var toLambdaFunction = (ctx, id, fileOrProps) => {
1644
- const config = ctx.config;
1722
+ const config2 = ctx.config;
1645
1723
  const stack = ctx.stack ?? ctx.bootstrap;
1646
1724
  const bootstrap2 = ctx.bootstrap;
1647
- 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 };
1648
1726
  const lambda = new Function(id, {
1649
- name: `${config.name}-${stack.name}-${id}`,
1727
+ name: `${config2.name}-${stack.name}-${id}`,
1650
1728
  code: Code.fromFile(
1651
1729
  id,
1652
1730
  props.file,
@@ -1658,13 +1736,13 @@ var toLambdaFunction = (ctx, id, fileOrProps) => {
1658
1736
  ...props,
1659
1737
  vpc: void 0
1660
1738
  });
1661
- if (config.defaults?.function?.permissions) {
1662
- lambda.addPermissions(config.defaults?.function?.permissions);
1739
+ if (config2.defaults?.function?.permissions) {
1740
+ lambda.addPermissions(config2.defaults?.function?.permissions);
1663
1741
  }
1664
1742
  if (typeof fileOrProps === "object" && fileOrProps.permissions) {
1665
1743
  lambda.addPermissions(fileOrProps.permissions);
1666
1744
  }
1667
- 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);
1668
1746
  if (props.log) {
1669
1747
  lambda.enableLogs(props.log instanceof Duration ? props.log : void 0);
1670
1748
  }
@@ -1901,6 +1979,7 @@ type Send<Name extends string, F extends Func> = {
1901
1979
 
1902
1980
  type MockHandle<F extends Func> = (payload: Parameters<F>[0]) => void
1903
1981
  type MockBuilder<F extends Func> = (handle?: MockHandle<F>) => void
1982
+ type MockObject<F extends Func> = Mock<Parameters<F>, ReturnType<F>>
1904
1983
  `;
1905
1984
  var queuePlugin = definePlugin({
1906
1985
  name: "queue",
@@ -2001,24 +2080,24 @@ var queuePlugin = definePlugin({
2001
2080
  ).optional()
2002
2081
  }).array()
2003
2082
  }),
2004
- onTypeGen({ config }) {
2083
+ onTypeGen({ config: config2 }) {
2005
2084
  const gen = new TypeGen("@awsless/awsless");
2006
2085
  const resources = new TypeObject(1);
2007
2086
  const mocks = new TypeObject(1);
2008
2087
  const mockResponses = new TypeObject(1);
2009
- for (const stack of config.stacks) {
2088
+ for (const stack of config2.stacks) {
2010
2089
  const resource = new TypeObject(2);
2011
2090
  const mock = new TypeObject(2);
2012
2091
  const mockResponse = new TypeObject(2);
2013
2092
  for (const [name, fileOrProps] of Object.entries(stack.queues || {})) {
2014
2093
  const varName = camelCase3(`${stack.name}-${name}`);
2015
- const queueName = formatName(`${config.name}-${stack.name}-${name}`);
2094
+ const queueName = formatName(`${config2.name}-${stack.name}-${name}`);
2016
2095
  const file = typeof fileOrProps === "string" ? fileOrProps : typeof fileOrProps.consumer === "string" ? fileOrProps.consumer : fileOrProps.consumer.file;
2017
2096
  const relFile = relative3(directories.types, file);
2018
2097
  gen.addImport(varName, relFile);
2019
2098
  mock.addType(name, `MockBuilder<typeof ${varName}>`);
2020
2099
  resource.addType(name, `Send<'${queueName}', typeof ${varName}>`);
2021
- mockResponse.addType(name, `Mock`);
2100
+ mockResponse.addType(name, `MockObject<typeof ${varName}>`);
2022
2101
  }
2023
2102
  mocks.addType(stack.name, mock);
2024
2103
  resources.addType(stack.name, resource);
@@ -2031,11 +2110,11 @@ var queuePlugin = definePlugin({
2031
2110
  return gen.toString();
2032
2111
  },
2033
2112
  onStack(ctx) {
2034
- const { stack, config, stackConfig, bind } = ctx;
2113
+ const { stack, config: config2, stackConfig, bind } = ctx;
2035
2114
  for (const [id, functionOrProps] of Object.entries(stackConfig.queues || {})) {
2036
- 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 };
2037
2116
  const queue2 = new Queue(id, {
2038
- name: `${config.name}-${stack.name}-${id}`,
2117
+ name: `${config2.name}-${stack.name}-${id}`,
2039
2118
  deadLetterArn: getGlobalOnFailure(ctx),
2040
2119
  ...props
2041
2120
  });
@@ -2301,13 +2380,13 @@ var tablePlugin = definePlugin({
2301
2380
  ).optional()
2302
2381
  }).array()
2303
2382
  }),
2304
- onTypeGen({ config }) {
2383
+ onTypeGen({ config: config2 }) {
2305
2384
  const gen = new TypeGen("@awsless/awsless");
2306
2385
  const resources = new TypeObject(1);
2307
- for (const stack of config.stacks) {
2386
+ for (const stack of config2.stacks) {
2308
2387
  const list3 = new TypeObject(2);
2309
2388
  for (const name of Object.keys(stack.tables || {})) {
2310
- const tableName = formatName(`${config.name}-${stack.name}-${name}`);
2389
+ const tableName = formatName(`${config2.name}-${stack.name}-${name}`);
2311
2390
  list3.addType(name, `'${tableName}'`);
2312
2391
  }
2313
2392
  resources.addType(stack.name, list3);
@@ -2316,11 +2395,11 @@ var tablePlugin = definePlugin({
2316
2395
  return gen.toString();
2317
2396
  },
2318
2397
  onStack(ctx) {
2319
- const { config, stack, stackConfig, bind } = ctx;
2398
+ const { config: config2, stack, stackConfig, bind } = ctx;
2320
2399
  for (const [id, props] of Object.entries(stackConfig.tables || {})) {
2321
2400
  const table = new Table(id, {
2322
2401
  ...props,
2323
- name: `${config.name}-${stack.name}-${id}`,
2402
+ name: `${config2.name}-${stack.name}-${id}`,
2324
2403
  stream: props.stream?.type
2325
2404
  });
2326
2405
  stack.add(table);
@@ -2435,13 +2514,13 @@ var storePlugin = definePlugin({
2435
2514
  stores: z10.array(ResourceIdSchema).optional()
2436
2515
  }).array()
2437
2516
  }),
2438
- onTypeGen({ config }) {
2517
+ onTypeGen({ config: config2 }) {
2439
2518
  const gen = new TypeGen("@awsless/awsless");
2440
2519
  const resources = new TypeObject(1);
2441
- for (const stack of config.stacks) {
2520
+ for (const stack of config2.stacks) {
2442
2521
  const list3 = new TypeObject(2);
2443
2522
  for (const name of stack.stores || []) {
2444
- const storeName = formatName(`${config.name}-${stack.name}-${name}`);
2523
+ const storeName = formatName(`${config2.name}-${stack.name}-${name}`);
2445
2524
  list3.addType(name, `{ readonly name: '${storeName}' }`);
2446
2525
  }
2447
2526
  resources.addType(stack.name, list3);
@@ -2449,10 +2528,10 @@ var storePlugin = definePlugin({
2449
2528
  gen.addInterface("StoreResources", resources);
2450
2529
  return gen.toString();
2451
2530
  },
2452
- onStack({ config, stack, stackConfig, bootstrap: bootstrap2, bind }) {
2531
+ onStack({ config: config2, stack, stackConfig, bootstrap: bootstrap2, bind }) {
2453
2532
  for (const id of stackConfig.stores || []) {
2454
2533
  const bucket = new Bucket(id, {
2455
- name: `store-${config.name}-${stack.name}-${id}`,
2534
+ name: `store-${config2.name}-${stack.name}-${id}`,
2456
2535
  accessControl: "private"
2457
2536
  });
2458
2537
  const custom = new CustomResource(id, {
@@ -2606,14 +2685,14 @@ var topicPlugin = definePlugin({
2606
2685
  }
2607
2686
  })
2608
2687
  }),
2609
- onTypeGen({ config }) {
2688
+ onTypeGen({ config: config2 }) {
2610
2689
  const gen = new TypeGen("@awsless/awsless");
2611
2690
  const resources = new TypeObject(1);
2612
2691
  const mocks = new TypeObject(1);
2613
2692
  const mockResponses = new TypeObject(1);
2614
- for (const stack of config.stacks) {
2693
+ for (const stack of config2.stacks) {
2615
2694
  for (const topic of stack.topics || []) {
2616
- const name = formatName(`${config.name}-${topic}`);
2695
+ const name = formatName(`${config2.name}-${topic}`);
2617
2696
  mockResponses.addType(topic, "Mock");
2618
2697
  resources.addType(topic, `Publish<'${name}'>`);
2619
2698
  mocks.addType(topic, `MockBuilder`);
@@ -2625,11 +2704,11 @@ var topicPlugin = definePlugin({
2625
2704
  gen.addInterface("TopicMockResponse", mockResponses);
2626
2705
  return gen.toString();
2627
2706
  },
2628
- onApp({ config, bootstrap: bootstrap2 }) {
2629
- for (const stack of config.stacks) {
2707
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
2708
+ for (const stack of config2.stacks) {
2630
2709
  for (const id of stack.topics || []) {
2631
2710
  const topic = new Topic(id, {
2632
- name: `${config.name}-${id}`
2711
+ name: `${config2.name}-${id}`
2633
2712
  });
2634
2713
  bootstrap2.add(topic);
2635
2714
  bootstrap2.export(`topic-${id}-arn`, topic.arn);
@@ -2637,14 +2716,14 @@ var topicPlugin = definePlugin({
2637
2716
  }
2638
2717
  },
2639
2718
  onStack(ctx) {
2640
- const { config, stack, stackConfig, bootstrap: bootstrap2, bind } = ctx;
2719
+ const { config: config2, stack, stackConfig, bootstrap: bootstrap2, bind } = ctx;
2641
2720
  for (const id of stackConfig.topics || []) {
2642
2721
  bind((lambda) => {
2643
2722
  lambda.addPermissions({
2644
2723
  actions: ["sns:Publish"],
2645
2724
  resources: [
2646
2725
  sub("arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:${app}-${topic}", {
2647
- app: config.name,
2726
+ app: config2.name,
2648
2727
  topic: id
2649
2728
  })
2650
2729
  ]
@@ -2777,11 +2856,11 @@ var pubsubPlugin = definePlugin({
2777
2856
  });
2778
2857
  },
2779
2858
  onStack(ctx) {
2780
- const { config, stack, stackConfig } = ctx;
2859
+ const { config: config2, stack, stackConfig } = ctx;
2781
2860
  for (const [id, props] of Object.entries(stackConfig.pubsub || {})) {
2782
2861
  const lambda = toLambdaFunction(ctx, `pubsub-${id}`, props.consumer);
2783
2862
  const source = new IotEventSource(id, lambda, {
2784
- name: `${config.name}-${stack.name}-${id}`,
2863
+ name: `${config2.name}-${stack.name}-${id}`,
2785
2864
  sql: props.sql,
2786
2865
  sqlVersion: props.sqlVersion
2787
2866
  });
@@ -2922,13 +3001,11 @@ var RecordSet = class extends Resource {
2922
3001
 
2923
3002
  // src/formation/resource/appsync/graphql-schema.ts
2924
3003
  import { print } from "graphql";
2925
- import { readFile as readFile2 } from "fs/promises";
3004
+ import { readFile as readFile3 } from "fs/promises";
2926
3005
  import { mergeTypeDefs } from "@graphql-tools/merge";
2927
3006
  var GraphQLSchema = class extends Resource {
2928
3007
  constructor(logicalId, props) {
2929
- super("AWS::AppSync::GraphQLSchema", logicalId, [
2930
- props.definition
2931
- ]);
3008
+ super("AWS::AppSync::GraphQLSchema", logicalId, [props.definition]);
2932
3009
  this.props = props;
2933
3010
  }
2934
3011
  properties() {
@@ -2946,16 +3023,21 @@ var Definition = class extends Asset {
2946
3023
  schema;
2947
3024
  async build({ write }) {
2948
3025
  const files = [this.files].flat();
2949
- const schemas = await Promise.all(files.map((file) => {
2950
- return readFile2(file, "utf8");
2951
- }));
3026
+ const fingerprint = files.join("");
3027
+ const schemas = await Promise.all(
3028
+ files.map((file) => {
3029
+ return readFile3(file, "utf8");
3030
+ })
3031
+ );
2952
3032
  const defs = mergeTypeDefs(schemas);
2953
3033
  const schema2 = print(defs);
2954
3034
  if (schema2.length === 0) {
2955
3035
  throw new Error(`Graphql schema definition can't be empty. [${this.id}]`);
2956
3036
  }
2957
3037
  const size = Buffer.from(schema2, "utf8").byteLength;
2958
- await write("schema.gql", schema2);
3038
+ await write(fingerprint, async (write2) => {
3039
+ await write2("schema.gql", schema2);
3040
+ });
2959
3041
  this.schema = schema2;
2960
3042
  return {
2961
3043
  size: formatByteSize(size)
@@ -2972,7 +3054,7 @@ import { swc as swc2, minify as swcMinify2 } from "rollup-plugin-swc3";
2972
3054
  import json2 from "@rollup/plugin-json";
2973
3055
  import commonjs2 from "@rollup/plugin-commonjs";
2974
3056
  import nodeResolve2 from "@rollup/plugin-node-resolve";
2975
- import { dirname as dirname2 } from "path";
3057
+ import { dirname as dirname3 } from "path";
2976
3058
  var rollupResolver = ({ minify = true } = {}) => {
2977
3059
  return async (input) => {
2978
3060
  const bundle = await rollup2({
@@ -2995,7 +3077,7 @@ var rollupResolver = ({ minify = true } = {}) => {
2995
3077
  // minify,
2996
3078
  // module: true,
2997
3079
  jsc: {
2998
- baseUrl: dirname2(input),
3080
+ baseUrl: dirname3(input),
2999
3081
  minify: { sourceMap: true }
3000
3082
  },
3001
3083
  sourceMaps: true
@@ -3054,8 +3136,14 @@ var FileCode2 = class extends Asset {
3054
3136
  this.file = file;
3055
3137
  }
3056
3138
  code;
3057
- async build() {
3058
- 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"]);
3059
3147
  this.code = code.toString("utf8");
3060
3148
  return {
3061
3149
  size: formatByteSize(code.byteLength)
@@ -3184,7 +3272,7 @@ var AppsyncEventSource = class extends Group {
3184
3272
  functionArn: lambda.arn,
3185
3273
  serviceRoleArn: role.arn
3186
3274
  }).dependsOn(role).dependsOn(lambda);
3187
- const config = new FunctionConfiguration(id, {
3275
+ const config2 = new FunctionConfiguration(id, {
3188
3276
  apiId: props.apiId,
3189
3277
  code: props.code,
3190
3278
  dataSourceName: source.name
@@ -3193,10 +3281,10 @@ var AppsyncEventSource = class extends Group {
3193
3281
  apiId: props.apiId,
3194
3282
  typeName: props.typeName,
3195
3283
  fieldName: props.fieldName,
3196
- functions: [config.id],
3284
+ functions: [config2.id],
3197
3285
  code: props.code
3198
- }).dependsOn(config);
3199
- super([role, source, config, resolver]);
3286
+ }).dependsOn(config2);
3287
+ super([role, source, config2, resolver]);
3200
3288
  }
3201
3289
  };
3202
3290
 
@@ -3236,7 +3324,7 @@ var DomainNameApiAssociation = class extends Resource {
3236
3324
  };
3237
3325
 
3238
3326
  // src/plugins/graphql.ts
3239
- import { basename } from "path";
3327
+ import { basename as basename2 } from "path";
3240
3328
  var defaultResolver = Code2.fromInline(
3241
3329
  "graphql-default-resolver",
3242
3330
  `
@@ -3296,21 +3384,21 @@ var graphqlPlugin = definePlugin({
3296
3384
  }).array()
3297
3385
  }),
3298
3386
  onApp(ctx) {
3299
- const { config, bootstrap: bootstrap2 } = ctx;
3387
+ const { config: config2, bootstrap: bootstrap2 } = ctx;
3300
3388
  const apis = /* @__PURE__ */ new Set();
3301
- for (const stackConfig of config.stacks) {
3389
+ for (const stackConfig of config2.stacks) {
3302
3390
  for (const id of Object.keys(stackConfig.graphql || {})) {
3303
3391
  apis.add(id);
3304
3392
  }
3305
3393
  }
3306
3394
  for (const id of apis) {
3307
3395
  const schemaFiles = [];
3308
- for (const stack of config.stacks) {
3396
+ for (const stack of config2.stacks) {
3309
3397
  const files = toArray(stack.graphql?.[id]?.schema || []);
3310
3398
  schemaFiles.push(...files);
3311
3399
  }
3312
3400
  const api = new GraphQLApi(id, {
3313
- name: `${config.name}-${id}`,
3401
+ name: `${config2.name}-${id}`,
3314
3402
  defaultAuthorization: GraphQLAuthorization.withApiKey()
3315
3403
  });
3316
3404
  const schema2 = new GraphQLSchema(id, {
@@ -3318,7 +3406,7 @@ var graphqlPlugin = definePlugin({
3318
3406
  definition: new Definition(id, schemaFiles)
3319
3407
  }).dependsOn(api);
3320
3408
  bootstrap2.add(api).add(schema2).export(`graphql-${id}`, api.id);
3321
- const props = config.defaults.graphql?.[id];
3409
+ const props = config2.defaults.graphql?.[id];
3322
3410
  if (!props) {
3323
3411
  continue;
3324
3412
  }
@@ -3357,10 +3445,10 @@ var graphqlPlugin = definePlugin({
3357
3445
  }
3358
3446
  },
3359
3447
  onStack(ctx) {
3360
- const { config, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
3448
+ const { config: config2, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
3361
3449
  for (const [id, props] of Object.entries(stackConfig.graphql || {})) {
3362
3450
  const apiId = bootstrap2.import(`graphql-${id}`);
3363
- const defaultProps = config.defaults.graphql?.[id];
3451
+ const defaultProps = config2.defaults.graphql?.[id];
3364
3452
  for (const [typeName, fields] of Object.entries(props.resolvers || {})) {
3365
3453
  for (const [fieldName, resolverProps] of Object.entries(fields || {})) {
3366
3454
  const props2 = isFunctionProps(resolverProps) ? { consumer: resolverProps } : resolverProps;
@@ -3370,7 +3458,7 @@ var graphqlPlugin = definePlugin({
3370
3458
  let code = defaultResolver;
3371
3459
  if (resolver) {
3372
3460
  if (!resolverCache.has(resolver)) {
3373
- const fileCode = Code2.fromFile(basename(resolver), resolver);
3461
+ const fileCode = Code2.fromFile(basename2(resolver), resolver);
3374
3462
  resolverCache.set(resolver, fileCode);
3375
3463
  stack.add(fileCode);
3376
3464
  }
@@ -3643,25 +3731,25 @@ var domainPlugin = definePlugin({
3643
3731
  ).optional()
3644
3732
  }).default({})
3645
3733
  }),
3646
- onApp({ config, bootstrap: bootstrap2, usEastBootstrap, bind }) {
3647
- const domains = Object.entries(config.defaults.domains || {});
3734
+ onApp({ config: config2, bootstrap: bootstrap2, usEastBootstrap, bind }) {
3735
+ const domains = Object.entries(config2.defaults.domains || {});
3648
3736
  if (domains.length === 0) {
3649
3737
  return;
3650
3738
  }
3651
3739
  const deleteHostedZoneLambda = new Function("delete-hosted-zone", {
3652
- name: `${config.name}-delete-hosted-zone`,
3740
+ name: `${config2.name}-delete-hosted-zone`,
3653
3741
  code: Code.fromInlineFeature("delete-hosted-zone")
3654
3742
  }).enableLogs(Duration.days(3)).addPermissions({
3655
3743
  actions: ["route53:ListResourceRecordSets", "route53:ChangeResourceRecordSets"],
3656
3744
  resources: ["*"]
3657
3745
  });
3658
3746
  usEastBootstrap.add(deleteHostedZoneLambda);
3659
- const usEastExports = new GlobalExports(`${config.name}-us-east-exports`, {
3747
+ const usEastExports = new GlobalExports(`${config2.name}-us-east-exports`, {
3660
3748
  region: usEastBootstrap.region
3661
3749
  });
3662
3750
  bootstrap2.add(usEastExports);
3663
3751
  const configurationSet = new ConfigurationSet("default", {
3664
- name: config.name,
3752
+ name: config2.name,
3665
3753
  engagementMetrics: true,
3666
3754
  reputationMetrics: true
3667
3755
  });
@@ -3730,12 +3818,12 @@ var onFailurePlugin = definePlugin({
3730
3818
  onFailure: FunctionSchema.optional()
3731
3819
  }).array()
3732
3820
  }),
3733
- onApp({ config, bootstrap: bootstrap2 }) {
3734
- if (!hasOnFailure(config)) {
3821
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
3822
+ if (!hasOnFailure(config2)) {
3735
3823
  return;
3736
3824
  }
3737
3825
  const queue2 = new Queue("on-failure", {
3738
- name: `${config.name}-failure`
3826
+ name: `${config2.name}-failure`
3739
3827
  });
3740
3828
  bootstrap2.add(queue2).export("on-failure-queue-arn", queue2.arn);
3741
3829
  },
@@ -3924,9 +4012,9 @@ var vpcPlugin = definePlugin({
3924
4012
  // vpc: z.boolean().default(false),
3925
4013
  // }).default({}),
3926
4014
  // }),
3927
- onApp({ config, bootstrap: bootstrap2 }) {
4015
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
3928
4016
  const vpc = new Vpc("main", {
3929
- name: config.name,
4017
+ name: config2.name,
3930
4018
  cidrBlock: Peer.ipv4("10.0.0.0/16")
3931
4019
  });
3932
4020
  const privateRouteTable = new RouteTable("private", {
@@ -3959,7 +4047,7 @@ var vpcPlugin = definePlugin({
3959
4047
  const subnet = new Subnet(id, {
3960
4048
  vpcId: vpc.id,
3961
4049
  cidrBlock: Peer.ipv4(`10.0.${block++}.0/24`),
3962
- availabilityZone: config.region + zones[i]
4050
+ availabilityZone: config2.region + zones[i]
3963
4051
  }).dependsOn(vpc);
3964
4052
  const association = new SubnetRouteTableAssociation(id, {
3965
4053
  routeTableId: table.id,
@@ -4373,8 +4461,8 @@ var httpPlugin = definePlugin({
4373
4461
  ).optional()
4374
4462
  }).array()
4375
4463
  }),
4376
- onApp({ config, bootstrap: bootstrap2 }) {
4377
- if (Object.keys(config.defaults?.http || {}).length === 0) {
4464
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
4465
+ if (Object.keys(config2.defaults?.http || {}).length === 0) {
4378
4466
  return;
4379
4467
  }
4380
4468
  const vpcId = bootstrap2.get("vpc-id");
@@ -4385,9 +4473,9 @@ var httpPlugin = definePlugin({
4385
4473
  securityGroup.addIngressRule(Peer.anyIpv4(), Port.tcp(443));
4386
4474
  securityGroup.addIngressRule(Peer.anyIpv6(), Port.tcp(443));
4387
4475
  bootstrap2.add(securityGroup);
4388
- for (const [id, props] of Object.entries(config.defaults?.http || {})) {
4476
+ for (const [id, props] of Object.entries(config2.defaults?.http || {})) {
4389
4477
  const loadBalancer = new LoadBalancer(id, {
4390
- name: `${config.name}-${id}`,
4478
+ name: `${config2.name}-${id}`,
4391
4479
  type: "application",
4392
4480
  securityGroups: [securityGroup.id],
4393
4481
  subnets: [
@@ -4424,9 +4512,9 @@ var httpPlugin = definePlugin({
4424
4512
  }
4425
4513
  },
4426
4514
  onStack(ctx) {
4427
- const { config, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
4515
+ const { config: config2, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
4428
4516
  for (const [id, routes] of Object.entries(stackConfig.http || {})) {
4429
- const props = config.defaults.http[id];
4517
+ const props = config2.defaults.http[id];
4430
4518
  for (const [route, routeProps] of Object.entries(routes)) {
4431
4519
  const { method, path } = parseRoute(route);
4432
4520
  const lambda = toLambdaFunction(ctx, `http-${id}`, routeProps);
@@ -4503,13 +4591,13 @@ var searchPlugin = definePlugin({
4503
4591
  searchs: z19.array(ResourceIdSchema).optional()
4504
4592
  }).array()
4505
4593
  }),
4506
- onTypeGen({ config }) {
4594
+ onTypeGen({ config: config2 }) {
4507
4595
  const gen = new TypeGen("@awsless/awsless");
4508
4596
  const resources = new TypeObject(1);
4509
- for (const stack of config.stacks) {
4597
+ for (const stack of config2.stacks) {
4510
4598
  const list3 = new TypeObject(2);
4511
4599
  for (const id of stack.searchs || []) {
4512
- const name = formatName(`${config.name}-${stack.name}-${id}`);
4600
+ const name = formatName(`${config2.name}-${stack.name}-${id}`);
4513
4601
  list3.addType(name, `{ readonly name: '${name}' }`);
4514
4602
  }
4515
4603
  resources.addType(stack.name, list3);
@@ -4517,10 +4605,10 @@ var searchPlugin = definePlugin({
4517
4605
  gen.addInterface("SearchResources", resources);
4518
4606
  return gen.toString();
4519
4607
  },
4520
- onStack({ config, stack, stackConfig, bind }) {
4608
+ onStack({ config: config2, stack, stackConfig, bind }) {
4521
4609
  for (const id of stackConfig.searchs || []) {
4522
4610
  const collection = new Collection(id, {
4523
- name: `${config.name}-${stack.name}-${id}`,
4611
+ name: `${config2.name}-${stack.name}-${id}`,
4524
4612
  type: "search"
4525
4613
  });
4526
4614
  bind((lambda) => {
@@ -4657,10 +4745,10 @@ var cachePlugin = definePlugin({
4657
4745
  ).optional()
4658
4746
  }).array()
4659
4747
  }),
4660
- onTypeGen({ config }) {
4748
+ onTypeGen({ config: config2 }) {
4661
4749
  const gen = new TypeGen("@awsless/awsless");
4662
4750
  const resources = new TypeObject(1);
4663
- for (const stack of config.stacks) {
4751
+ for (const stack of config2.stacks) {
4664
4752
  const resource = new TypeObject(2);
4665
4753
  for (const name of Object.keys(stack.caches || {})) {
4666
4754
  resource.addType(name, `Command`);
@@ -4671,9 +4759,9 @@ var cachePlugin = definePlugin({
4671
4759
  gen.addInterface("CacheResources", resources);
4672
4760
  return gen.toString();
4673
4761
  },
4674
- onStack({ config, stack, stackConfig, bootstrap: bootstrap2, bind }) {
4762
+ onStack({ config: config2, stack, stackConfig, bootstrap: bootstrap2, bind }) {
4675
4763
  for (const [id, props] of Object.entries(stackConfig.caches || {})) {
4676
- const name = `${config.name}-${stack.name}-${id}`;
4764
+ const name = `${config2.name}-${stack.name}-${id}`;
4677
4765
  const subnetGroup = new SubnetGroup(id, {
4678
4766
  name,
4679
4767
  subnetIds: [bootstrap2.import(`private-subnet-1`), bootstrap2.import(`private-subnet-2`)]
@@ -4916,10 +5004,10 @@ var restPlugin = definePlugin({
4916
5004
  ).optional()
4917
5005
  }).array()
4918
5006
  }),
4919
- onApp({ config, bootstrap: bootstrap2 }) {
4920
- 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 || {})) {
4921
5009
  const api = new Api(id, {
4922
- name: `${config.name}-${id}`,
5010
+ name: `${config2.name}-${id}`,
4923
5011
  protocolType: "HTTP"
4924
5012
  });
4925
5013
  const stage = new Stage(id, {
@@ -4973,15 +5061,15 @@ import { z as z23 } from "zod";
4973
5061
 
4974
5062
  // src/util/param.ts
4975
5063
  import { DeleteParameterCommand, GetParameterCommand, GetParametersByPathCommand, ParameterType, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
4976
- var configParameterPrefix = (config) => {
4977
- return `/.awsless/${config.name}`;
5064
+ var configParameterPrefix = (config2) => {
5065
+ return `/.awsless/${config2.name}`;
4978
5066
  };
4979
5067
  var Params = class {
4980
- constructor(config) {
4981
- this.config = config;
5068
+ constructor(config2) {
5069
+ this.config = config2;
4982
5070
  this.client = new SSMClient({
4983
- credentials: config.credentials,
4984
- region: config.region
5071
+ credentials: config2.credentials,
5072
+ region: config2.region
4985
5073
  });
4986
5074
  }
4987
5075
  client;
@@ -5081,10 +5169,10 @@ var configPlugin = definePlugin({
5081
5169
  configs: z23.array(ConfigNameSchema).optional()
5082
5170
  }).array()
5083
5171
  }),
5084
- onTypeGen({ config }) {
5172
+ onTypeGen({ config: config2 }) {
5085
5173
  const gen = new TypeGen("@awsless/awsless");
5086
5174
  const resources = new TypeObject(0, false);
5087
- for (const stack of config.stacks) {
5175
+ for (const stack of config2.stacks) {
5088
5176
  for (const name of stack.configs || []) {
5089
5177
  resources.addConst(name, "string");
5090
5178
  }
@@ -5092,7 +5180,7 @@ var configPlugin = definePlugin({
5092
5180
  gen.addInterface("ConfigResources", resources.toString());
5093
5181
  return gen.toString();
5094
5182
  },
5095
- onStack({ bind, config, stackConfig }) {
5183
+ onStack({ bind, config: config2, stackConfig }) {
5096
5184
  const configs = stackConfig.configs;
5097
5185
  bind((lambda) => {
5098
5186
  if (configs && configs.length) {
@@ -5103,7 +5191,7 @@ var configPlugin = definePlugin({
5103
5191
  return formatArn({
5104
5192
  service: "ssm",
5105
5193
  resource: "parameter",
5106
- resourceName: configParameterPrefix(config) + "/" + paramCase6(name),
5194
+ resourceName: configParameterPrefix(config2) + "/" + paramCase6(name),
5107
5195
  seperator: ""
5108
5196
  });
5109
5197
  })
@@ -5240,11 +5328,11 @@ var OriginGroup = class {
5240
5328
  };
5241
5329
 
5242
5330
  // src/schema/local-directory.ts
5243
- import { stat as stat2 } from "fs/promises";
5331
+ import { stat as stat3 } from "fs/promises";
5244
5332
  import { z as z24 } from "zod";
5245
5333
  var LocalDirectorySchema = z24.string().refine(async (path) => {
5246
5334
  try {
5247
- const s = await stat2(path);
5335
+ const s = await stat3(path);
5248
5336
  return s.isDirectory();
5249
5337
  } catch (error) {
5250
5338
  return false;
@@ -5326,9 +5414,9 @@ var CachePolicy = class extends Resource {
5326
5414
  // src/formation/resource/s3/files.ts
5327
5415
  import { Glob } from "glob";
5328
5416
  import JSZip2 from "jszip";
5329
- import { createHash as createHash2 } from "crypto";
5417
+ import { createHash as createHash3 } from "crypto";
5330
5418
  import { createReadStream } from "fs";
5331
- import { join as join4 } from "path";
5419
+ import { join as join5 } from "path";
5332
5420
  var Files = class extends Asset {
5333
5421
  constructor(id, props) {
5334
5422
  super("bucket", id);
@@ -5337,7 +5425,7 @@ var Files = class extends Asset {
5337
5425
  hash;
5338
5426
  bundle;
5339
5427
  s3;
5340
- async build({ write }) {
5428
+ async build({ read, write }) {
5341
5429
  const glob = new Glob(this.props.pattern ?? "**/*", {
5342
5430
  nodir: true,
5343
5431
  cwd: this.props.directory
@@ -5346,9 +5434,9 @@ var Files = class extends Asset {
5346
5434
  const hashes = [];
5347
5435
  let count = 0;
5348
5436
  for await (const path of glob) {
5349
- const file = join4(this.props.directory, path);
5437
+ const file = join5(this.props.directory, path);
5350
5438
  const stream = createReadStream(file);
5351
- const hash2 = createHash2("sha1");
5439
+ const hash2 = createHash3("sha1");
5352
5440
  stream.pipe(hash2);
5353
5441
  hashes.push(hash2);
5354
5442
  zip.file(path, stream);
@@ -5361,24 +5449,22 @@ var Files = class extends Asset {
5361
5449
  level: 9
5362
5450
  }
5363
5451
  });
5364
- const hash = createHash2("sha1");
5452
+ const hash = createHash3("sha1");
5365
5453
  for (const item of hashes) {
5366
5454
  hash.update(item.digest());
5367
5455
  }
5368
5456
  this.hash = hash.digest("hex");
5369
- await write("HASH", this.hash);
5370
- 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
+ });
5371
5461
  return {
5372
5462
  files: style.success(String(count)),
5373
5463
  size: formatByteSize(this.bundle.byteLength)
5374
5464
  };
5375
5465
  }
5376
5466
  async publish({ publish }) {
5377
- this.s3 = await publish(
5378
- `${this.id}.zip`,
5379
- this.bundle,
5380
- this.hash
5381
- );
5467
+ this.s3 = await publish(`${this.id}.zip`, this.bundle, this.hash);
5382
5468
  }
5383
5469
  get source() {
5384
5470
  return this.s3;
@@ -5673,7 +5759,7 @@ var sitePlugin = definePlugin({
5673
5759
  }).array()
5674
5760
  }),
5675
5761
  onStack(ctx) {
5676
- const { config, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
5762
+ const { config: config2, stack, stackConfig, bootstrap: bootstrap2 } = ctx;
5677
5763
  for (const [id, props] of Object.entries(stackConfig.sites || {})) {
5678
5764
  const origins = [];
5679
5765
  const originGroups = [];
@@ -5705,7 +5791,7 @@ var sitePlugin = definePlugin({
5705
5791
  if (props.static) {
5706
5792
  bucket = new Bucket(`site-${id}`, {
5707
5793
  // name: props.domain,
5708
- name: `site-${config.name}-${stack.name}-${id}`,
5794
+ name: `site-${config2.name}-${stack.name}-${id}`,
5709
5795
  accessControl: "private",
5710
5796
  website: {
5711
5797
  indexDocument: "index.html",
@@ -5753,14 +5839,14 @@ var sitePlugin = definePlugin({
5753
5839
  );
5754
5840
  }
5755
5841
  const cache = new CachePolicy(id, {
5756
- name: `site-${config.name}-${stack.name}-${id}`,
5842
+ name: `site-${config2.name}-${stack.name}-${id}`,
5757
5843
  minTtl: Duration.seconds(1),
5758
5844
  maxTtl: Duration.days(365),
5759
5845
  defaultTtl: Duration.days(1),
5760
5846
  ...props.cache
5761
5847
  });
5762
5848
  const originRequest = new OriginRequestPolicy(id, {
5763
- name: `site-${config.name}-${stack.name}-${id}`,
5849
+ name: `site-${config2.name}-${stack.name}-${id}`,
5764
5850
  header: {
5765
5851
  behavior: "all-except",
5766
5852
  values: ["host", "authorization"]
@@ -5770,12 +5856,12 @@ var sitePlugin = definePlugin({
5770
5856
  const hostedZoneId = bootstrap2.import(`hosted-zone-${props.domain}-id`);
5771
5857
  const certificateArn = bootstrap2.import(`us-east-certificate-${props.domain}-arn`);
5772
5858
  const responseHeaders = new ResponseHeadersPolicy(id, {
5773
- name: `site-${config.name}-${stack.name}-${id}`,
5859
+ name: `site-${config2.name}-${stack.name}-${id}`,
5774
5860
  cors: props.cors,
5775
5861
  remove: ["server"]
5776
5862
  });
5777
5863
  const distribution = new Distribution(id, {
5778
- name: `site-${config.name}-${stack.name}-${id}`,
5864
+ name: `site-${config2.name}-${stack.name}-${id}`,
5779
5865
  certificateArn,
5780
5866
  compress: true,
5781
5867
  aliases: [domainName],
@@ -5845,16 +5931,16 @@ var sitePlugin = definePlugin({
5845
5931
  // src/plugins/feature.ts
5846
5932
  var featurePlugin = definePlugin({
5847
5933
  name: "feature",
5848
- onApp({ config, bootstrap: bootstrap2 }) {
5934
+ onApp({ config: config2, bootstrap: bootstrap2 }) {
5849
5935
  const deleteBucketLambda = new Function("delete-bucket", {
5850
- name: `${config.name}-delete-bucket`,
5936
+ name: `${config2.name}-delete-bucket`,
5851
5937
  code: Code.fromFeature("delete-bucket")
5852
5938
  }).enableLogs(Duration.days(3)).addPermissions({
5853
5939
  actions: ["s3:*"],
5854
5940
  resources: ["*"]
5855
5941
  });
5856
5942
  const uploadBucketAssetLambda = new Function("upload-bucket-asset", {
5857
- name: `${config.name}-upload-bucket-asset`,
5943
+ name: `${config2.name}-upload-bucket-asset`,
5858
5944
  code: Code.fromFeature("upload-bucket-asset"),
5859
5945
  memorySize: Size.gigaBytes(2)
5860
5946
  }).enableLogs(Duration.days(3)).addPermissions({
@@ -5862,7 +5948,7 @@ var featurePlugin = definePlugin({
5862
5948
  resources: ["*"]
5863
5949
  });
5864
5950
  const invalidateCacheLambda = new Function("invalidate-cache", {
5865
- name: `${config.name}-invalidate-cache`,
5951
+ name: `${config2.name}-invalidate-cache`,
5866
5952
  code: Code.fromFeature("invalidate-cache")
5867
5953
  }).enableLogs(Duration.days(3)).addPermissions({
5868
5954
  actions: ["cloudfront:*"],
@@ -6274,11 +6360,11 @@ var authPlugin = definePlugin({
6274
6360
  ).optional()
6275
6361
  }).array()
6276
6362
  }),
6277
- onTypeGen({ config }) {
6363
+ onTypeGen({ config: config2 }) {
6278
6364
  const gen = new TypeGen("@awsless/awsless");
6279
6365
  const resources = new TypeObject(1);
6280
- for (const name of Object.keys(config.defaults.auth)) {
6281
- const authName = formatName(`${config.name}-${name}`);
6366
+ for (const name of Object.keys(config2.defaults.auth)) {
6367
+ const authName = formatName(`${config2.name}-${name}`);
6282
6368
  resources.addType(
6283
6369
  name,
6284
6370
  `{ readonly name: '${authName}', readonly userPoolId: string, readonly clientId: string }`
@@ -6307,12 +6393,12 @@ var authPlugin = definePlugin({
6307
6393
  }
6308
6394
  },
6309
6395
  onApp(ctx) {
6310
- const { config, bootstrap: bootstrap2 } = ctx;
6311
- if (Object.keys(config.defaults.auth).length === 0) {
6396
+ const { config: config2, bootstrap: bootstrap2 } = ctx;
6397
+ if (Object.keys(config2.defaults.auth).length === 0) {
6312
6398
  return;
6313
6399
  }
6314
6400
  const clientSecretLambda = new Function(`auth-client-secret`, {
6315
- name: `${config.name}-auth-client-secret`,
6401
+ name: `${config2.name}-auth-client-secret`,
6316
6402
  code: Code.fromFeature("cognito-client-secret")
6317
6403
  });
6318
6404
  clientSecretLambda.addPermissions({
@@ -6320,7 +6406,7 @@ var authPlugin = definePlugin({
6320
6406
  resources: ["*"]
6321
6407
  });
6322
6408
  bootstrap2.add(clientSecretLambda);
6323
- for (const [id, props] of Object.entries(config.defaults.auth)) {
6409
+ for (const [id, props] of Object.entries(config2.defaults.auth)) {
6324
6410
  const functions = /* @__PURE__ */ new Map();
6325
6411
  const triggers = {};
6326
6412
  for (const [trigger, fnProps] of Object.entries(props.triggers ?? {})) {
@@ -6328,7 +6414,7 @@ var authPlugin = definePlugin({
6328
6414
  functions.set(trigger, lambda);
6329
6415
  triggers[trigger] = lambda.arn;
6330
6416
  }
6331
- for (const stack of config.stacks) {
6417
+ for (const stack of config2.stacks) {
6332
6418
  for (const [trigger, fnProps] of Object.entries(stack.auth?.[id]?.triggers ?? {})) {
6333
6419
  const lambda = toLambdaFunction(ctx, `auth-${id}-${trigger}`, fnProps);
6334
6420
  if (functions.has(trigger)) {
@@ -6354,7 +6440,7 @@ var authPlugin = definePlugin({
6354
6440
  });
6355
6441
  }
6356
6442
  const userPool = new UserPool(id, {
6357
- name: `${config.name}-${id}`,
6443
+ name: `${config2.name}-${id}`,
6358
6444
  allowUserRegistration: props.allowUserRegistration,
6359
6445
  username: props.username,
6360
6446
  password: props.password,
@@ -6362,7 +6448,7 @@ var authPlugin = definePlugin({
6362
6448
  email: emailConfig
6363
6449
  });
6364
6450
  const client = userPool.addClient({
6365
- name: `${config.name}-${id}`,
6451
+ name: `${config2.name}-${id}`,
6366
6452
  validity: props.validity,
6367
6453
  generateSecret: true,
6368
6454
  supportedIdentityProviders: ["cognito"],
@@ -6371,7 +6457,7 @@ var authPlugin = definePlugin({
6371
6457
  }
6372
6458
  });
6373
6459
  const domain = userPool.addDomain({
6374
- domain: `${config.name}-${id}`
6460
+ domain: `${config2.name}-${id}`
6375
6461
  });
6376
6462
  const clientSecret = new CustomResource(`${id}-client-secret`, {
6377
6463
  serviceToken: clientSecretLambda.arn,
@@ -6394,6 +6480,28 @@ var authPlugin = definePlugin({
6394
6480
  }
6395
6481
  });
6396
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
+
6397
6505
  // src/plugins/index.ts
6398
6506
  var defaultPlugins = [
6399
6507
  extendPlugin,
@@ -6416,7 +6524,8 @@ var defaultPlugins = [
6416
6524
  httpPlugin,
6417
6525
  restPlugin,
6418
6526
  sitePlugin,
6419
- onFailurePlugin
6527
+ onFailurePlugin,
6528
+ testPlugin
6420
6529
  ];
6421
6530
 
6422
6531
  // src/formation/app.ts
@@ -6458,12 +6567,13 @@ var getAllDepends = (filters) => {
6458
6567
  walk(filters);
6459
6568
  return list3;
6460
6569
  };
6461
- var toApp = async (config, filters) => {
6462
- const app = new App(config.name);
6570
+ var toApp = async (config2, filters) => {
6571
+ const app = new App(config2.name);
6463
6572
  const stacks = [];
6464
- const plugins = [...defaultPlugins, ...config.plugins || []];
6573
+ const plugins = [...defaultPlugins, ...config2.plugins || []];
6574
+ const tests = /* @__PURE__ */ new Map();
6465
6575
  debug("Plugins detected:", plugins.map((plugin) => style.info(plugin.name)).join(", "));
6466
- const bootstrap2 = new Stack("bootstrap", config.region);
6576
+ const bootstrap2 = new Stack("bootstrap", config2.region);
6467
6577
  const usEastBootstrap = new Stack("us-east-bootstrap", "us-east-1");
6468
6578
  app.add(bootstrap2, usEastBootstrap);
6469
6579
  debug("Run plugin onApp listeners");
@@ -6473,45 +6583,32 @@ var toApp = async (config, filters) => {
6473
6583
  };
6474
6584
  for (const plugin of plugins) {
6475
6585
  plugin.onApp?.({
6476
- config,
6586
+ config: config2,
6477
6587
  app,
6478
6588
  bootstrap: bootstrap2,
6479
6589
  usEastBootstrap,
6480
- bind
6590
+ bind,
6591
+ tests
6481
6592
  });
6482
6593
  }
6483
6594
  debug("Stack filters:", filters.map((filter) => style.info(filter)).join(", "));
6484
- const filterdStacks = filters.length === 0 ? config.stacks : getAllDepends(
6595
+ const filterdStacks = filters.length === 0 ? config2.stacks : getAllDepends(
6485
6596
  // config.stacks,
6486
- config.stacks.filter((stack) => filters.includes(stack.name))
6597
+ config2.stacks.filter((stack) => filters.includes(stack.name))
6487
6598
  );
6488
6599
  for (const stackConfig of filterdStacks) {
6489
6600
  const { stack, bindings: bindings2 } = toStack({
6490
- config,
6601
+ config: config2,
6491
6602
  stackConfig,
6492
6603
  bootstrap: bootstrap2,
6493
6604
  usEastBootstrap,
6494
6605
  plugins,
6606
+ tests,
6495
6607
  app
6496
6608
  });
6497
6609
  app.add(stack);
6498
6610
  stacks.push({ stack, config: stackConfig, bindings: bindings2 });
6499
6611
  }
6500
- debug(app.stacks);
6501
- for (const plugin of plugins) {
6502
- for (const stack of app.stacks) {
6503
- for (const resource of stack) {
6504
- plugin.onResource?.({
6505
- config,
6506
- app,
6507
- stack,
6508
- bootstrap: bootstrap2,
6509
- usEastBootstrap,
6510
- resource
6511
- });
6512
- }
6513
- }
6514
- }
6515
6612
  const functions = app.find(Function);
6516
6613
  for (const bind2 of bindings) {
6517
6614
  for (const fn of functions) {
@@ -6542,12 +6639,13 @@ var toApp = async (config, filters) => {
6542
6639
  return {
6543
6640
  app,
6544
6641
  plugins,
6545
- deploymentLine
6642
+ deploymentLine,
6643
+ tests
6546
6644
  };
6547
6645
  };
6548
6646
 
6549
6647
  // src/config.ts
6550
- import { join as join6 } from "path";
6648
+ import { join as join7 } from "path";
6551
6649
 
6552
6650
  // src/util/account.ts
6553
6651
  import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
@@ -6566,17 +6664,17 @@ var getCredentials = (profile) => {
6566
6664
  };
6567
6665
 
6568
6666
  // src/schema/app.ts
6569
- import { z as z30 } from "zod";
6667
+ import { z as z31 } from "zod";
6570
6668
 
6571
6669
  // src/schema/stack.ts
6572
- import { z as z27 } from "zod";
6573
- var StackSchema = z27.object({
6670
+ import { z as z28 } from "zod";
6671
+ var StackSchema = z28.object({
6574
6672
  name: ResourceIdSchema,
6575
- depends: z27.array(z27.lazy(() => StackSchema)).optional()
6673
+ depends: z28.array(z28.lazy(() => StackSchema)).optional()
6576
6674
  });
6577
6675
 
6578
6676
  // src/schema/region.ts
6579
- import { z as z28 } from "zod";
6677
+ import { z as z29 } from "zod";
6580
6678
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
6581
6679
  var AF = ["af-south-1"];
6582
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"];
@@ -6593,41 +6691,41 @@ var regions = [
6593
6691
  ...ME,
6594
6692
  ...SA
6595
6693
  ];
6596
- var RegionSchema = z28.enum(regions);
6694
+ var RegionSchema = z29.enum(regions);
6597
6695
 
6598
6696
  // src/schema/plugin.ts
6599
- import { z as z29 } from "zod";
6600
- var PluginSchema = z29.object({
6601
- name: z29.string(),
6602
- 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(),
6603
6701
  // depends: z.array(z.lazy(() => PluginSchema)).optional(),
6604
- onApp: z29.function().returns(z29.void()).optional(),
6605
- onStack: z29.function().returns(z29.any()).optional(),
6606
- 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()
6607
6705
  // bind: z.function().optional(),
6608
6706
  });
6609
6707
 
6610
6708
  // src/schema/app.ts
6611
- var AppSchema = z30.object({
6709
+ var AppSchema = z31.object({
6612
6710
  /** App name */
6613
6711
  name: ResourceIdSchema,
6614
6712
  /** The AWS region to deploy to. */
6615
6713
  region: RegionSchema,
6616
6714
  /** The AWS profile to deploy to. */
6617
- profile: z30.string(),
6715
+ profile: z31.string(),
6618
6716
  /** The deployment stage.
6619
6717
  * @default 'prod'
6620
6718
  */
6621
- stage: z30.string().regex(/^[a-z]+$/).default("prod"),
6719
+ stage: z31.string().regex(/^[a-z]+$/).default("prod"),
6622
6720
  /** Default properties. */
6623
- defaults: z30.object({}).default({}),
6721
+ defaults: z31.object({}).default({}),
6624
6722
  /** The application stacks. */
6625
- stacks: z30.array(StackSchema).min(1).refine((stacks) => {
6723
+ stacks: z31.array(StackSchema).min(1).refine((stacks) => {
6626
6724
  const unique = new Set(stacks.map((stack) => stack.name));
6627
6725
  return unique.size === stacks.length;
6628
6726
  }, "Must be an array of unique stacks"),
6629
6727
  /** Custom plugins. */
6630
- plugins: z30.array(PluginSchema).optional()
6728
+ plugins: z31.array(PluginSchema).optional()
6631
6729
  });
6632
6730
 
6633
6731
  // src/util/import.ts
@@ -6635,7 +6733,7 @@ import { rollup as rollup3, watch } from "rollup";
6635
6733
  import { swc as swc3 } from "rollup-plugin-swc3";
6636
6734
  import replace from "rollup-plugin-replace";
6637
6735
  import { EventIterator } from "event-iterator";
6638
- import { dirname as dirname3, join as join5 } from "path";
6736
+ import { dirname as dirname4, join as join6 } from "path";
6639
6737
  import { mkdir as mkdir2, writeFile as writeFile2 } from "fs/promises";
6640
6738
  var importFile = async (path) => {
6641
6739
  const bundle = await rollup3({
@@ -6646,18 +6744,18 @@ var importFile = async (path) => {
6646
6744
  plugins: [
6647
6745
  // @ts-ignore
6648
6746
  replace({
6649
- __dirname: (id) => `'${dirname3(id)}'`
6650
- // 'defineStackConfig({': id => `defineStackConfig({ cwd: '${dirname(id)}',`,
6747
+ __dirname: (id) => `'${dirname4(id)}'`
6748
+ // 'defineStackConfig({': (id: string) => `defineStackConfig({ cwd: '${dirname(id)}',`,
6651
6749
  }),
6652
6750
  swc3({
6653
6751
  minify: false,
6654
6752
  jsc: {
6655
- baseUrl: dirname3(path)
6753
+ baseUrl: dirname4(path)
6656
6754
  }
6657
6755
  })
6658
6756
  ]
6659
6757
  });
6660
- const outputFile = join5(directories.cache, "config.js");
6758
+ const outputFile = join6(directories.cache, "config.js");
6661
6759
  const result = await bundle.generate({
6662
6760
  format: "esm",
6663
6761
  exports: "default"
@@ -6683,13 +6781,13 @@ var watchFile = (path) => {
6683
6781
  plugins: [
6684
6782
  // @ts-ignore
6685
6783
  replace({
6686
- __dirname: (id) => `'${dirname3(id)}'`
6687
- // 'defineStackConfig({': id => `defineStackConfig({ cwd: '${dirname(id)}',`,
6784
+ __dirname: (id) => `'${dirname4(id)}'`
6785
+ // 'defineStackConfig({': (id: string) => `defineStackConfig({ cwd: '${dirname(id)}',`,
6688
6786
  }),
6689
6787
  swc3({
6690
6788
  minify: false,
6691
6789
  jsc: {
6692
- baseUrl: dirname3(path)
6790
+ baseUrl: dirname4(path)
6693
6791
  }
6694
6792
  })
6695
6793
  ]
@@ -6711,12 +6809,12 @@ var watchFile = (path) => {
6711
6809
  event.result.close();
6712
6810
  const output = result.output[0];
6713
6811
  const code = output.code;
6714
- const outputFile = join5(directories.cache, "config.js");
6812
+ const outputFile = join6(directories.cache, "config.js");
6715
6813
  await mkdir2(directories.cache, { recursive: true });
6716
6814
  await writeFile2(outputFile, code);
6717
6815
  debug("Save config file:", style.info(outputFile));
6718
- const config = await import(`${outputFile}?${Date.now()}`);
6719
- queue2.push(config);
6816
+ const config2 = await import(`${outputFile}?${Date.now()}`);
6817
+ queue2.push(config2);
6720
6818
  }
6721
6819
  });
6722
6820
  return () => {
@@ -6731,7 +6829,7 @@ var watchFile = (path) => {
6731
6829
  };
6732
6830
 
6733
6831
  // src/config.ts
6734
- import { z as z31 } from "zod";
6832
+ import { z as z32 } from "zod";
6735
6833
  var ConfigError = class extends Error {
6736
6834
  constructor(error, data) {
6737
6835
  super(error.message);
@@ -6746,7 +6844,7 @@ var importConfig = async (options) => {
6746
6844
  setRoot(root2);
6747
6845
  debug("CWD:", style.info(root2));
6748
6846
  debug("Import config file");
6749
- const fileName = join6(root2, configFile);
6847
+ const fileName = join7(root2, configFile);
6750
6848
  const module = await importFile(fileName);
6751
6849
  const appConfig = typeof module.default === "function" ? await module.default(options) : module.default;
6752
6850
  debug("Validate config file");
@@ -6760,22 +6858,22 @@ var importConfig = async (options) => {
6760
6858
  schema2 = schema2.and(plugin.schema);
6761
6859
  }
6762
6860
  }
6763
- let config;
6861
+ let config2;
6764
6862
  try {
6765
- config = await schema2.parseAsync(appConfig);
6863
+ config2 = await schema2.parseAsync(appConfig);
6766
6864
  } catch (error) {
6767
- if (error instanceof z31.ZodError) {
6865
+ if (error instanceof z32.ZodError) {
6768
6866
  throw new ConfigError(error, appConfig);
6769
6867
  }
6770
6868
  throw error;
6771
6869
  }
6772
- debug("Load credentials", style.info(config.profile));
6773
- const credentials = getCredentials(config.profile);
6870
+ debug("Load credentials", style.info(config2.profile));
6871
+ const credentials = getCredentials(config2.profile);
6774
6872
  debug("Load AWS account ID");
6775
- const account = await getAccountId(credentials, config.region);
6873
+ const account = await getAccountId(credentials, config2.region);
6776
6874
  debug("Account ID:", style.info(account));
6777
6875
  return {
6778
- ...config,
6876
+ ...config2,
6779
6877
  account,
6780
6878
  credentials
6781
6879
  };
@@ -6787,7 +6885,7 @@ var watchConfig = async (options, resolve, reject) => {
6787
6885
  setRoot(root2);
6788
6886
  debug("CWD:", style.info(root2));
6789
6887
  debug("Import config file");
6790
- const fileName = join6(root2, configFile);
6888
+ const fileName = join7(root2, configFile);
6791
6889
  for await (const module of watchFile(fileName)) {
6792
6890
  const appConfig = typeof module.default === "function" ? await module.default(options) : module.default;
6793
6891
  debug("Validate config file");
@@ -6801,24 +6899,24 @@ var watchConfig = async (options, resolve, reject) => {
6801
6899
  schema2 = schema2.and(plugin.schema);
6802
6900
  }
6803
6901
  }
6804
- let config;
6902
+ let config2;
6805
6903
  try {
6806
- config = await schema2.parseAsync(appConfig);
6904
+ config2 = await schema2.parseAsync(appConfig);
6807
6905
  } catch (error) {
6808
- if (error instanceof z31.ZodError) {
6906
+ if (error instanceof z32.ZodError) {
6809
6907
  reject(new ConfigError(error, appConfig));
6810
6908
  continue;
6811
6909
  }
6812
6910
  reject(error);
6813
6911
  continue;
6814
6912
  }
6815
- debug("Load credentials", style.info(config.profile));
6816
- const credentials = getCredentials(config.profile);
6913
+ debug("Load credentials", style.info(config2.profile));
6914
+ const credentials = getCredentials(config2.profile);
6817
6915
  debug("Load AWS account ID");
6818
- const account = await getAccountId(credentials, config.region);
6916
+ const account = await getAccountId(credentials, config2.region);
6819
6917
  debug("Account ID:", style.info(account));
6820
6918
  resolve({
6821
- ...config,
6919
+ ...config2,
6822
6920
  account,
6823
6921
  credentials
6824
6922
  });
@@ -6831,39 +6929,38 @@ var br = () => {
6831
6929
  };
6832
6930
  var hr = () => {
6833
6931
  return (term) => {
6834
- term.out.write([
6835
- style.placeholder("\u2500".repeat(term.out.width())),
6836
- br()
6837
- ]);
6932
+ term.out.write([style.placeholder("\u2500".repeat(term.out.width())), br()]);
6838
6933
  };
6839
6934
  };
6840
6935
 
6841
6936
  // src/cli/ui/layout/list.ts
6842
6937
  var list = (data) => {
6843
- const padding = 3;
6938
+ const padding = 2;
6844
6939
  const gap = 1;
6845
6940
  const size = Object.keys(data).reduce((total, name) => {
6846
6941
  return name.length > total ? name.length : total;
6847
6942
  }, 0);
6848
6943
  return (term) => {
6849
6944
  term.out.gap();
6850
- term.out.write(Object.entries(data).map(([name, value]) => [
6851
- " ".repeat(padding),
6852
- style.label((name + ":").padEnd(size + gap + 1)),
6853
- value,
6854
- br()
6855
- ]));
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
+ );
6856
6953
  term.out.gap();
6857
6954
  };
6858
6955
  };
6859
6956
 
6860
6957
  // src/cli/ui/layout/header.ts
6861
- var header = (config) => {
6958
+ var header = (config2) => {
6862
6959
  return list({
6863
- App: config.name,
6864
- Stage: config.stage,
6865
- Region: config.region,
6866
- Profile: config.profile
6960
+ App: config2.name,
6961
+ Stage: config2.stage,
6962
+ Region: config2.region,
6963
+ Profile: config2.profile
6867
6964
  });
6868
6965
  };
6869
6966
 
@@ -6933,15 +7030,17 @@ var createSpinner = () => {
6933
7030
  // src/cli/ui/layout/dialog.ts
6934
7031
  import wrapAnsi from "wrap-ansi";
6935
7032
  var dialog = (type, lines) => {
6936
- const padding = 3;
7033
+ const padding = 2;
6937
7034
  const icon = style[type](symbol[type].padEnd(padding));
6938
7035
  return (term) => {
6939
- term.out.write(lines.map((line2, i) => {
6940
- if (i === 0) {
6941
- return icon + wrapAnsi(line2, term.out.width(), { hard: true });
6942
- }
6943
- return wrapAnsi(" ".repeat(padding) + line2, term.out.width(), { hard: true });
6944
- }).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
+ );
6945
7044
  };
6946
7045
  };
6947
7046
  var loadingDialog = (message) => {
@@ -6950,14 +7049,7 @@ var loadingDialog = (message) => {
6950
7049
  const time = new Signal("");
6951
7050
  const timer = createTimer();
6952
7051
  return (term) => {
6953
- term.out.write([
6954
- icon,
6955
- " ",
6956
- description,
6957
- " ",
6958
- time,
6959
- br()
6960
- ]);
7052
+ term.out.write([icon, " ", description, " ", time, br()]);
6961
7053
  return (message2) => {
6962
7054
  description.set(message2);
6963
7055
  time.set(timer());
@@ -7164,9 +7256,11 @@ var Renderer = class {
7164
7256
  if (Array.isArray(fragment)) {
7165
7257
  return fragment.map(walk).join("");
7166
7258
  }
7167
- this.unsubs.push(fragment.subscribe(() => {
7168
- this.update();
7169
- }));
7259
+ this.unsubs.push(
7260
+ fragment.subscribe(() => {
7261
+ this.update();
7262
+ })
7263
+ );
7170
7264
  return walk(fragment.get());
7171
7265
  };
7172
7266
  this.unsubs.forEach((unsub) => unsub());
@@ -7179,17 +7273,19 @@ var Renderer = class {
7179
7273
  const start = Math.max(oldSize - height, 0);
7180
7274
  this.flushing = true;
7181
7275
  for (let y = start; y < size; y++) {
7182
- const newLine = screen[y];
7183
- const oldLine = this.screen[y];
7276
+ const newLine = screen.at(y);
7277
+ const oldLine = this.screen.at(y);
7184
7278
  if (newLine !== oldLine) {
7185
7279
  if (y >= oldSize && y !== 0) {
7186
7280
  const p = y - start - 1;
7187
- const x = screen[y - 1]?.length || 0;
7281
+ const x = screen.at(y - 1)?.length ?? 0;
7188
7282
  await this.setCursor(x, p);
7189
- await this.writeString("\n" + newLine);
7283
+ await this.writeString(newLine ? "\n" + newLine : "\n");
7190
7284
  } else {
7191
7285
  await this.setCursor(0, y - start);
7192
- await this.writeString(newLine);
7286
+ if (newLine) {
7287
+ await this.writeString(newLine);
7288
+ }
7193
7289
  await this.clearLine();
7194
7290
  }
7195
7291
  }
@@ -7219,11 +7315,7 @@ var createTerminal = (input = process.stdin, output = process.stdout) => {
7219
7315
 
7220
7316
  // src/cli/ui/layout/logo.ts
7221
7317
  var logo = () => {
7222
- return [
7223
- style.warning("\u26A1\uFE0F "),
7224
- style.primary("AWS"),
7225
- style.primary.dim("LESS")
7226
- ];
7318
+ return [style.warning("\u26A1\uFE0F"), style.primary("AWS"), style.primary.dim("LESS")];
7227
7319
  };
7228
7320
 
7229
7321
  // src/cli/ui/layout/logs.ts
@@ -7239,23 +7331,27 @@ var logs = () => {
7239
7331
  term.out.write([
7240
7332
  hr(),
7241
7333
  br(),
7242
- " ".repeat(3),
7334
+ " ".repeat(2),
7243
7335
  style.label("Debug Logs:"),
7244
7336
  br(),
7245
7337
  br(),
7246
7338
  logs2.map((log) => {
7247
7339
  const diff = log.date.getTime() - previous.getTime();
7248
- const time = `+${diff}`.padStart(8);
7340
+ const time = `+${diff}`.padStart(6);
7249
7341
  previous = log.date;
7250
- return wrapAnsi2([
7251
- style.attr(`${time}${style.attr.dim("ms")}`),
7252
- " [ ",
7253
- log.type,
7254
- " ] ",
7255
- log.message,
7256
- br(),
7257
- log.type === "error" ? br() : ""
7258
- ].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
+ );
7259
7355
  }),
7260
7356
  br(),
7261
7357
  hr()
@@ -7266,8 +7362,8 @@ var logs = () => {
7266
7362
  // src/cli/ui/layout/zod-error.ts
7267
7363
  var line = (value, level = 0, highlight = false) => {
7268
7364
  return [
7269
- highlight ? style.error(symbol.pointerSmall) + style.placeholder(" | ") : style.placeholder.dim(" | "),
7270
- " ".repeat(level),
7365
+ highlight ? style.error(symbol.pointerSmall) + style.placeholder(" | ") : style.placeholder.dim(" | "),
7366
+ " ".repeat(level),
7271
7367
  value,
7272
7368
  br()
7273
7369
  ];
@@ -7301,9 +7397,7 @@ var zodError = (error, data) => {
7301
7397
  return (term) => {
7302
7398
  for (const issue of error.issues) {
7303
7399
  term.out.gap();
7304
- term.out.write(dialog("error", [
7305
- style.error(issue.message)
7306
- ]));
7400
+ term.out.write(dialog("error", [style.error(issue.message)]));
7307
7401
  term.out.gap();
7308
7402
  term.out.write(line("{"));
7309
7403
  let context = data;
@@ -7353,10 +7447,10 @@ var layout = async (cb) => {
7353
7447
  term.out.gap();
7354
7448
  try {
7355
7449
  const options = program.optsWithGlobals();
7356
- const config = await importConfig(options);
7357
- term.out.write(header(config));
7450
+ const config2 = await importConfig(options);
7451
+ term.out.write(header(config2));
7358
7452
  term.out.gap();
7359
- await cb(config, term.out.write.bind(term.out), term);
7453
+ await cb(config2, term.out.write.bind(term.out), term);
7360
7454
  } catch (error) {
7361
7455
  term.out.gap();
7362
7456
  if (error instanceof ConfigError) {
@@ -7382,7 +7476,7 @@ var layout = async (cb) => {
7382
7476
  };
7383
7477
 
7384
7478
  // src/cli/ui/complex/builder.ts
7385
- import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
7479
+ import { mkdir as mkdir3, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
7386
7480
 
7387
7481
  // src/cli/ui/layout/flex-line.ts
7388
7482
  var stripEscapeCode = (str) => {
@@ -7405,7 +7499,7 @@ var flexLine = (term, left, right, reserveSpace = 0) => {
7405
7499
  };
7406
7500
 
7407
7501
  // src/cli/ui/complex/builder.ts
7408
- import { dirname as dirname4, join as join7 } from "path";
7502
+ import { dirname as dirname5, join as join8 } from "path";
7409
7503
  var assetBuilder = (app) => {
7410
7504
  return async (term) => {
7411
7505
  const assets = [];
@@ -7430,63 +7524,104 @@ var assetBuilder = (app) => {
7430
7524
  }
7431
7525
  const stackNameSize = Math.max(...stacks.map((stack) => stack.name.length));
7432
7526
  const assetTypeSize = Math.max(...assets.map((asset) => asset.type.length));
7433
- await Promise.all(app.stacks.map(async (stack) => {
7434
- const group = new Signal([]);
7435
- groups.update((groups2) => [...groups2, group]);
7436
- await Promise.all([...stack.assets].map(async (asset) => {
7437
- if (!asset.build) {
7438
- return;
7439
- }
7440
- const [icon, stop] = createSpinner();
7441
- const details = new Signal({});
7442
- const line2 = flexLine(term, [
7443
- icon,
7444
- " ",
7445
- style.label(stack.name),
7446
- " ".repeat(stackNameSize - stack.name.length),
7447
- " ",
7448
- style.placeholder(symbol.pointerSmall),
7449
- " ",
7450
- style.warning(asset.type),
7451
- " ".repeat(assetTypeSize - asset.type.length),
7452
- " ",
7453
- style.placeholder(symbol.pointerSmall),
7454
- " ",
7455
- style.info(asset.id),
7456
- " "
7457
- ], [
7458
- " ",
7459
- derive([details], (details2) => {
7460
- return Object.entries(details2).map(([key, value]) => {
7461
- return `${style.label(key)} ${value}`;
7462
- }).join(style.placeholder(" \u2500 "));
7463
- }),
7464
- br()
7465
- ]);
7466
- group.update((group2) => [...group2, line2]);
7467
- const timer = createTimer();
7468
- try {
7469
- const data = await asset.build({
7470
- async write(file, data2) {
7471
- const fullpath = join7(directories.asset, asset.type, app.name, stack.name, asset.id, file);
7472
- const basepath = dirname4(fullpath);
7473
- await mkdir3(basepath, { recursive: true });
7474
- 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;
7475
7535
  }
7476
- });
7477
- details.set({
7478
- ...data,
7479
- time: timer()
7480
- });
7481
- icon.set(style.success(symbol.success));
7482
- } catch (error) {
7483
- icon.set(style.error(symbol.error));
7484
- throw error;
7485
- } finally {
7486
- stop();
7487
- }
7488
- }));
7489
- }));
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
+ );
7490
7625
  done("Done building stack assets");
7491
7626
  if (showDetailedView) {
7492
7627
  term.out.gap();
@@ -7499,31 +7634,39 @@ import { mkdir as mkdir4, rm } from "fs/promises";
7499
7634
  var cleanUp = async () => {
7500
7635
  debug("Clean up template, cache, and asset files");
7501
7636
  const paths = [
7502
- directories.asset,
7637
+ // directories.asset,
7503
7638
  directories.cache,
7504
7639
  directories.types,
7505
7640
  directories.template
7506
7641
  ];
7507
- await Promise.all(paths.map((path) => rm(path, {
7508
- recursive: true,
7509
- force: true,
7510
- maxRetries: 2
7511
- })));
7512
- await Promise.all(paths.map((path) => mkdir4(path, {
7513
- recursive: true
7514
- })));
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
+ );
7515
7658
  };
7516
7659
 
7517
7660
  // src/cli/ui/complex/template.ts
7518
7661
  import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
7519
- import { join as join8 } from "path";
7662
+ import { join as join9 } from "path";
7520
7663
  var templateBuilder = (app) => {
7521
7664
  return async (term) => {
7522
7665
  const done = term.out.write(loadingDialog("Building stack templates..."));
7523
7666
  await Promise.all(app.stacks.map(async (stack) => {
7524
7667
  const template = stack.toString(true);
7525
- const path = join8(directories.template, app.name);
7526
- const file = join8(path, `${stack.name}.json`);
7668
+ const path = join9(directories.template, app.name);
7669
+ const file = join9(path, `${stack.name}.json`);
7527
7670
  await mkdir5(path, { recursive: true });
7528
7671
  await writeFile4(file, template);
7529
7672
  }));
@@ -7532,21 +7675,21 @@ var templateBuilder = (app) => {
7532
7675
  };
7533
7676
 
7534
7677
  // src/cli/ui/complex/types.ts
7535
- var typesGenerator = (config) => {
7678
+ var typesGenerator = (config2) => {
7536
7679
  return async (term) => {
7537
7680
  const done = term.out.write(loadingDialog("Generate type definition files..."));
7538
- await generateResourceTypes(config);
7681
+ await generateResourceTypes(config2);
7539
7682
  done("Done generating type definition files");
7540
7683
  };
7541
7684
  };
7542
7685
 
7543
7686
  // src/cli/command/build.ts
7544
7687
  var build = (program2) => {
7545
- program2.command("build").argument("[stack...]", "Optionally filter stacks to build").description("Build your app").action(async (filters) => {
7546
- await layout(async (config, write) => {
7547
- 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);
7548
7691
  await cleanUp();
7549
- await write(typesGenerator(config));
7692
+ await write(typesGenerator(config2));
7550
7693
  await write(assetBuilder(app));
7551
7694
  await write(templateBuilder(app));
7552
7695
  });
@@ -7887,7 +8030,20 @@ var togglePrompt = (label, options = {}) => {
7887
8030
  down: deactivate,
7888
8031
  up: activate
7889
8032
  });
7890
- 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
+ ]);
7891
8047
  });
7892
8048
  };
7893
8049
 
@@ -7901,11 +8057,11 @@ var confirmPrompt = (label, options = {}) => {
7901
8057
  };
7902
8058
 
7903
8059
  // src/cli/ui/complex/bootstrap.ts
7904
- var bootstrapDeployer = (config) => {
8060
+ var bootstrapDeployer = (config2) => {
7905
8061
  return async (term) => {
7906
8062
  debug("Initializing bootstrap");
7907
- const { app, stack } = bootstrapStack(config.account, config.region);
7908
- 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);
7909
8065
  const shouldDeploy = await shouldDeployBootstrap(client, stack);
7910
8066
  if (shouldDeploy) {
7911
8067
  term.out.write(dialog("warning", [`Your app hasn't been bootstrapped yet`]));
@@ -7930,8 +8086,8 @@ var bootstrapDeployer = (config) => {
7930
8086
  // src/cli/command/bootstrap.ts
7931
8087
  var bootstrap = (program2) => {
7932
8088
  program2.command("bootstrap").description("Create the awsless bootstrap stack").action(async () => {
7933
- await layout(async (config, write) => {
7934
- await write(bootstrapDeployer(config));
8089
+ await layout(async (config2, write) => {
8090
+ await write(bootstrapDeployer(config2));
7935
8091
  });
7936
8092
  });
7937
8093
  };
@@ -7944,15 +8100,7 @@ var stacksDeployer = (deploymentLine) => {
7944
8100
  const ui = {};
7945
8101
  term.out.gap();
7946
8102
  for (const i in deploymentLine) {
7947
- const line2 = flexLine(
7948
- term,
7949
- [" "],
7950
- [
7951
- " ",
7952
- style.placeholder(Number(i) + 1),
7953
- style.placeholder(" \u2500\u2500")
7954
- ]
7955
- );
8103
+ const line2 = flexLine(term, [" "], [" ", style.placeholder(Number(i) + 1), style.placeholder(" \u2500\u2500")]);
7956
8104
  term.out.write(line2);
7957
8105
  term.out.write(br());
7958
8106
  for (const stack of deploymentLine[i]) {
@@ -7962,7 +8110,7 @@ var stacksDeployer = (deploymentLine) => {
7962
8110
  let stopSpinner;
7963
8111
  term.out.write([
7964
8112
  icon,
7965
- " ",
8113
+ " ",
7966
8114
  name,
7967
8115
  " ".repeat(stackNameSize - stack.name.length),
7968
8116
  " ",
@@ -7997,11 +8145,7 @@ var stacksDeployer = (deploymentLine) => {
7997
8145
  };
7998
8146
  }
7999
8147
  }
8000
- term.out.write(flexLine(term, [" "], [
8001
- " ",
8002
- style.warning("\u26A1\uFE0F"),
8003
- style.placeholder("\u2500\u2500")
8004
- ]));
8148
+ term.out.write(flexLine(term, [" "], [" ", style.warning("\u26A1\uFE0F"), style.placeholder("\u2500\u2500")]));
8005
8149
  term.out.gap();
8006
8150
  return ui;
8007
8151
  };
@@ -8009,32 +8153,34 @@ var stacksDeployer = (deploymentLine) => {
8009
8153
 
8010
8154
  // src/cli/command/status.ts
8011
8155
  var status = (program2) => {
8012
- program2.command("status").argument("[stacks...]", "Optionally filter stacks to lookup status").description("View the application status").action(async (filters) => {
8013
- await layout(async (config, write) => {
8014
- 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);
8015
8159
  await cleanUp();
8016
8160
  await write(assetBuilder(app));
8017
8161
  await write(templateBuilder(app));
8018
8162
  const doneLoading = write(loadingDialog("Loading stack information..."));
8019
- const client = new StackClient(app, config.account, config.region, config.credentials);
8163
+ const client = new StackClient(app, config2.account, config2.region, config2.credentials);
8020
8164
  const statuses = [];
8021
8165
  const ui = write(stacksDeployer(deploymentLine));
8022
8166
  debug("Load metadata for all deployed stacks on AWS");
8023
- await Promise.all(app.stacks.map(async (stack, i) => {
8024
- const item = ui[stack.name];
8025
- item.start("loading");
8026
- const info = await client.get(stack.name, stack.region);
8027
- if (!info) {
8028
- item.fail("NON EXISTENT");
8029
- statuses.push("non-existent");
8030
- } else if (info.template !== stack.toString()) {
8031
- item.warn("OUT OF DATE");
8032
- statuses.push("out-of-date");
8033
- } else {
8034
- item.done("UP TO DATE");
8035
- statuses.push("up-to-date");
8036
- }
8037
- }));
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
+ );
8038
8184
  doneLoading("Done loading stack information");
8039
8185
  debug("Done loading data for all deployed stacks on AWS");
8040
8186
  if (statuses.includes("non-existent") || statuses.includes("out-of-date")) {
@@ -8047,13 +8193,13 @@ var status = (program2) => {
8047
8193
  };
8048
8194
 
8049
8195
  // src/cli/ui/complex/publisher.ts
8050
- import { readFile as readFile3 } from "fs/promises";
8051
- import { join as join9 } from "path";
8196
+ import { readFile as readFile5 } from "fs/promises";
8197
+ import { join as join10 } from "path";
8052
8198
  import { GetObjectCommand, ObjectCannedACL as ObjectCannedACL2, PutObjectCommand as PutObjectCommand2, S3Client as S3Client2, StorageClass as StorageClass2 } from "@aws-sdk/client-s3";
8053
- var assetPublisher = (config, app) => {
8199
+ var assetPublisher = (config2, app) => {
8054
8200
  const client = new S3Client2({
8055
- credentials: config.credentials,
8056
- region: config.region,
8201
+ credentials: config2.credentials,
8202
+ region: config2.region,
8057
8203
  maxAttempts: 5
8058
8204
  });
8059
8205
  return async (term) => {
@@ -8062,22 +8208,24 @@ var assetPublisher = (config, app) => {
8062
8208
  app.stacks.map(async (stack) => {
8063
8209
  await Promise.all(
8064
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
+ };
8065
8214
  await asset.publish?.({
8066
- async read(file) {
8067
- const path = join9(
8068
- directories.asset,
8069
- asset.type,
8070
- app.name,
8071
- stack.name,
8072
- asset.id,
8073
- 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
+ })
8074
8224
  );
8075
- const data = await readFile3(path);
8076
- return data;
8077
8225
  },
8078
8226
  async publish(name, data, hash) {
8079
8227
  const key = `${app.name}/${stack.name}/${asset.type}/${name}`;
8080
- const bucket = assetBucketName(config.account, config.region);
8228
+ const bucket = assetBucketName(config2.account, config2.region);
8081
8229
  let getResult;
8082
8230
  try {
8083
8231
  getResult = await client.send(
@@ -8107,7 +8255,7 @@ var assetPublisher = (config, app) => {
8107
8255
  ACL: ObjectCannedACL2.private,
8108
8256
  StorageClass: StorageClass2.STANDARD,
8109
8257
  Metadata: {
8110
- hash
8258
+ hash: hash.toString("utf8")
8111
8259
  }
8112
8260
  })
8113
8261
  );
@@ -8126,44 +8274,268 @@ var assetPublisher = (config, app) => {
8126
8274
  };
8127
8275
  };
8128
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
+
8129
8494
  // src/cli/command/deploy.ts
8130
8495
  var deploy = (program2) => {
8131
8496
  program2.command("deploy").argument("[stacks...]", "Optionally filter stacks to deploy").description("Deploy your app to AWS").action(async (filters) => {
8132
- await layout(async (config, write) => {
8133
- await write(bootstrapDeployer(config));
8134
- 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);
8135
8500
  const stackNames = app.stacks.map((stack) => stack.name);
8136
8501
  const formattedFilter = stackNames.map((i) => style.info(i)).join(style.placeholder(", "));
8137
8502
  debug("Stacks to deploy", formattedFilter);
8138
8503
  if (!process.env.SKIP_PROMPT) {
8139
8504
  const deployAll = filters.length === 0;
8140
8505
  const deploySingle = filters.length === 1;
8141
- 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
+ );
8142
8511
  if (!confirm) {
8143
8512
  throw new Cancelled();
8144
8513
  }
8145
8514
  }
8146
8515
  await cleanUp();
8147
- await write(typesGenerator(config));
8516
+ await write(typesGenerator(config2));
8517
+ await write(runTester(tests));
8148
8518
  await write(assetBuilder(app));
8149
- await write(assetPublisher(config, app));
8519
+ await write(assetPublisher(config2, app));
8150
8520
  await write(templateBuilder(app));
8151
8521
  const doneDeploying = write(loadingDialog("Deploying stacks to AWS..."));
8152
- const client = new StackClient(app, config.account, config.region, config.credentials);
8522
+ const client = new StackClient(app, config2.account, config2.region, config2.credentials);
8153
8523
  const ui = write(stacksDeployer(deploymentLine));
8154
8524
  for (const line2 of deploymentLine) {
8155
- const results = await Promise.allSettled(line2.map(async (stack) => {
8156
- const item = ui[stack.name];
8157
- item.start("deploying");
8158
- try {
8159
- await client.deploy(stack);
8160
- } catch (error) {
8161
- debugError(error);
8162
- item.fail("failed");
8163
- throw error;
8164
- }
8165
- item.done("deployed");
8166
- }));
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
+ );
8167
8539
  for (const result of results) {
8168
8540
  if (result.status === "rejected") {
8169
8541
  throw result.reason;
@@ -8214,11 +8586,7 @@ var textPrompt = (label, options = {}) => {
8214
8586
  resolve(value.get().join(""));
8215
8587
  },
8216
8588
  input: (chr) => {
8217
- value.update((value2) => [
8218
- ...value2.slice(0, cursor.get()),
8219
- chr,
8220
- ...value2.slice(cursor.get())
8221
- ]);
8589
+ value.update((value2) => [...value2.slice(0, cursor.get()), chr, ...value2.slice(cursor.get())]);
8222
8590
  cursor.update((cursor2) => cursor2 + 1);
8223
8591
  },
8224
8592
  delete() {
@@ -8232,104 +8600,119 @@ var textPrompt = (label, options = {}) => {
8232
8600
  cursor.update((cursor2) => Math.min(value.get().length, cursor2 + 1));
8233
8601
  }
8234
8602
  });
8235
- term.out.write([icon, " ", style.label(label), " ", sep, " ", formatted, br()]);
8603
+ term.out.write([icon, " ", style.label(label), " ", sep, " ", formatted, br()]);
8236
8604
  });
8237
8605
  };
8238
8606
  };
8239
8607
 
8240
- // src/cli/command/secrets/set.ts
8608
+ // src/cli/command/config/set.ts
8241
8609
  var set = (program2) => {
8242
- program2.command("set <name>").description("Set a secret value").action(async (name) => {
8243
- await layout(async (config, write) => {
8244
- const params = new Params(config);
8245
- write(list({
8246
- "Set secret parameter": style.info(name)
8247
- }));
8248
- 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"));
8249
8619
  if (value === "") {
8250
- write(dialog("error", [`Provided secret value can't be empty`]));
8620
+ write(dialog("error", [`Provided config value can't be empty`]));
8251
8621
  } else {
8252
- const done = write(loadingDialog(`Saving remote secret parameter`));
8622
+ const done = write(loadingDialog(`Saving remote config parameter`));
8253
8623
  await params.set(name, value);
8254
- done(`Done saving remote secret parameter`);
8624
+ done(`Done saving remote config parameter`);
8255
8625
  }
8256
8626
  });
8257
8627
  });
8258
8628
  };
8259
8629
 
8260
- // src/cli/command/secrets/get.ts
8630
+ // src/cli/command/config/get.ts
8261
8631
  var get = (program2) => {
8262
- program2.command("get <name>").description("Get a secret value").action(async (name) => {
8263
- await layout(async (config, write) => {
8264
- const params = new Params(config);
8265
- 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`));
8266
8636
  const value = await params.get(name);
8267
- done(`Done getting remote secret parameter`);
8268
- write(list({
8269
- Name: name,
8270
- Value: value || style.error("(empty)")
8271
- }));
8637
+ done(`Done getting remote config parameter`);
8638
+ write(
8639
+ list({
8640
+ Name: name,
8641
+ Value: value || style.error("(empty)")
8642
+ })
8643
+ );
8272
8644
  });
8273
8645
  });
8274
8646
  };
8275
8647
 
8276
- // src/cli/command/secrets/delete.ts
8648
+ // src/cli/command/config/delete.ts
8277
8649
  var del = (program2) => {
8278
- program2.command("delete <name>").description("Delete a secret value").action(async (name) => {
8279
- await layout(async (config, write) => {
8280
- const params = new Params(config);
8281
- 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`]));
8282
8654
  const confirm = await write(confirmPrompt("Are you sure?"));
8283
8655
  if (!confirm) {
8284
8656
  throw new Cancelled();
8285
8657
  }
8286
- const done = write(loadingDialog(`Deleting remote secret parameter`));
8658
+ const done = write(loadingDialog(`Deleting remote config parameter`));
8287
8659
  const value = await params.get(name);
8288
8660
  await params.delete(name);
8289
- done(`Done deleting remote secret parameter`);
8290
- write(list({
8291
- Name: name,
8292
- Value: value || style.error("(empty)")
8293
- }));
8661
+ done(`Done deleting remote config parameter`);
8662
+ write(
8663
+ list({
8664
+ Name: name,
8665
+ Value: value || style.error("(empty)")
8666
+ })
8667
+ );
8294
8668
  });
8295
8669
  });
8296
8670
  };
8297
8671
 
8298
- // src/cli/command/secrets/list.ts
8672
+ // src/cli/command/config/list.ts
8299
8673
  var list2 = (program2) => {
8300
- program2.command("list").description(`List all secret value's`).action(async () => {
8301
- await layout(async (config, write) => {
8302
- const params = new Params(config);
8303
- 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..."));
8304
8678
  const values = await params.list();
8305
- done("Done loading secret values");
8679
+ done("Done loading config values");
8306
8680
  if (Object.keys(values).length > 0) {
8307
8681
  write(list(values));
8308
8682
  } else {
8309
- write(dialog("warning", ["No secret parameters found"]));
8683
+ write(dialog("warning", ["No config parameters found"]));
8310
8684
  }
8311
8685
  });
8312
8686
  });
8313
8687
  };
8314
8688
 
8315
- // src/cli/command/secrets/index.ts
8316
- var commands = [
8317
- set,
8318
- get,
8319
- del,
8320
- list2
8321
- ];
8322
- var secrets = (program2) => {
8323
- 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`);
8324
8693
  commands.forEach((cb) => cb(command));
8325
8694
  };
8326
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
+
8327
8710
  // src/cli/command/types.ts
8328
8711
  var types = (program2) => {
8329
8712
  program2.command("types").description("Generate type definition files").action(async () => {
8330
- await layout(async (config, write) => {
8713
+ await layout(async (config2, write) => {
8331
8714
  await cleanUp();
8332
- await write(typesGenerator(config));
8715
+ await write(typesGenerator(config2));
8333
8716
  });
8334
8717
  });
8335
8718
  };
@@ -8339,9 +8722,9 @@ var dev = (program2) => {
8339
8722
  program2.command("dev").description("Start the development service").action(async () => {
8340
8723
  await layout(async (_, write) => {
8341
8724
  const options = program2.optsWithGlobals();
8342
- await watchConfig(options, async (config) => {
8725
+ await watchConfig(options, async (config2) => {
8343
8726
  await cleanUp();
8344
- await write(typesGenerator(config));
8727
+ await write(typesGenerator(config2));
8345
8728
  }, (error) => {
8346
8729
  if (error instanceof ConfigError) {
8347
8730
  write(zodError(error.error, error.data));
@@ -8360,8 +8743,8 @@ var dev = (program2) => {
8360
8743
  // src/cli/command/delete.ts
8361
8744
  var del2 = (program2) => {
8362
8745
  program2.command("delete").argument("[stacks...]", "Optionally filter stacks to delete").description("Delete your app from AWS").action(async (filters) => {
8363
- await layout(async (config, write) => {
8364
- const { app, deploymentLine } = await toApp(config, filters);
8746
+ await layout(async (config2, write) => {
8747
+ const { app, deploymentLine } = await toApp(config2, filters);
8365
8748
  const deletingLine = deploymentLine.reverse();
8366
8749
  const stackNames = app.stacks.map((stack) => stack.name);
8367
8750
  const formattedFilter = stackNames.map((i) => style.info(i)).join(style.placeholder(", "));
@@ -8375,7 +8758,7 @@ var del2 = (program2) => {
8375
8758
  }
8376
8759
  }
8377
8760
  const doneDeploying = write(loadingDialog("Deleting stacks from AWS..."));
8378
- const client = new StackClient(app, config.account, config.region, config.credentials);
8761
+ const client = new StackClient(app, config2.account, config2.region, config2.credentials);
8379
8762
  const ui = write(stacksDeployer(deletingLine));
8380
8763
  for (const line2 of deletingLine) {
8381
8764
  const results = await Promise.allSettled(line2.map(async (stack) => {
@@ -8428,8 +8811,8 @@ var commands2 = [
8428
8811
  deploy,
8429
8812
  del2,
8430
8813
  dev,
8431
- secrets
8432
- // test,
8814
+ config,
8815
+ test
8433
8816
  // diff,
8434
8817
  // remove,
8435
8818
  ];