@awsless/awsless 0.0.42 → 0.0.44

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
@@ -54,90 +54,6 @@ var flushDebug = () => {
54
54
  return queue.splice(0, queue.length);
55
55
  };
56
56
 
57
- // src/util/param.ts
58
- import { DeleteParameterCommand, GetParameterCommand, GetParametersByPathCommand, ParameterType, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
59
- var configParameterPrefix = (config) => {
60
- return `/.awsless/${config.name}`;
61
- };
62
- var Params = class {
63
- constructor(config) {
64
- this.config = config;
65
- this.client = new SSMClient({
66
- credentials: config.credentials,
67
- region: config.region
68
- });
69
- }
70
- client;
71
- getName(name) {
72
- return `${configParameterPrefix(this.config)}/${name}`;
73
- }
74
- async get(name) {
75
- debug("Get remote config value");
76
- debug("Name:", style.info(name));
77
- let result;
78
- try {
79
- result = await this.client.send(new GetParameterCommand({
80
- Name: this.getName(name),
81
- WithDecryption: true
82
- }));
83
- } catch (error) {
84
- if (error instanceof Error && error.name === "ParameterNotFound") {
85
- debug("Parameter not found");
86
- return;
87
- }
88
- throw error;
89
- }
90
- const value = result.Parameter?.Value;
91
- debug("Value:", style.info(value));
92
- debug("Done getting remote config value");
93
- return value;
94
- }
95
- async set(name, value) {
96
- debug("Save remote config value");
97
- debug("Name:", style.info(name));
98
- debug("Value:", style.info(value));
99
- await this.client.send(new PutParameterCommand({
100
- Type: ParameterType.STRING,
101
- Name: this.getName(name),
102
- Value: value,
103
- Overwrite: true
104
- }));
105
- debug("Done saving remote config value");
106
- }
107
- async delete(name) {
108
- debug("Delete remote config value");
109
- debug("Name:", style.info(name));
110
- try {
111
- await this.client.send(new DeleteParameterCommand({
112
- Name: this.getName(name)
113
- }));
114
- } catch (error) {
115
- if (error instanceof Error && error.name === "ParameterNotFound") {
116
- debug("Remote config value was already deleted");
117
- return;
118
- }
119
- throw error;
120
- }
121
- debug("Done deleting remote config value");
122
- }
123
- async list() {
124
- debug("Load remote config values");
125
- const result = await this.client.send(new GetParametersByPathCommand({
126
- Path: configParameterPrefix(this.config),
127
- WithDecryption: true,
128
- MaxResults: 10,
129
- Recursive: true
130
- }));
131
- debug("Done loading remote config values");
132
- const values = {};
133
- result.Parameters?.forEach((param) => {
134
- const name = param.Name.substring(configParameterPrefix(this.config).length).substring(1);
135
- values[name] = param.Value || "";
136
- });
137
- return values;
138
- }
139
- };
140
-
141
57
  // src/formation/asset.ts
142
58
  import { paramCase } from "change-case";
143
59
  var Asset = class {
@@ -261,11 +177,12 @@ var Lazy = class {
261
177
 
262
178
  // src/formation/resource/iam/inline-policy.ts
263
179
  var InlinePolicy = class {
180
+ name;
181
+ statements;
264
182
  constructor(name, props = {}) {
265
- this.name = name;
266
183
  this.statements = props.statements || [];
184
+ this.name = formatName(name);
267
185
  }
268
- statements;
269
186
  addStatement(...statements) {
270
187
  this.statements.push(...statements.flat());
271
188
  return this;
@@ -304,9 +221,7 @@ var Role = class extends Resource {
304
221
  constructor(logicalId, props = {}) {
305
222
  super("AWS::IAM::Role", logicalId);
306
223
  this.props = props;
307
- this.name = formatName(logicalId);
308
224
  }
309
- name;
310
225
  inlinePolicies = /* @__PURE__ */ new Set();
311
226
  managedPolicies = /* @__PURE__ */ new Set();
312
227
  get arn() {
@@ -571,18 +486,6 @@ var toStack = ({ config, app, stackConfig, bootstrap: bootstrap2, usEastBootstra
571
486
  bind2(fn);
572
487
  }
573
488
  }
574
- for (const fn of functions) {
575
- fn.addPermissions({
576
- actions: [
577
- "ssm:GetParameter",
578
- "ssm:GetParameters",
579
- "ssm:GetParametersByPath"
580
- ],
581
- resources: [
582
- sub("arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:parameter" + configParameterPrefix(config))
583
- ]
584
- });
585
- }
586
489
  return {
587
490
  stack,
588
491
  bindings
@@ -747,8 +650,9 @@ var LocalFileSchema = z3.string().refine(async (path) => {
747
650
  }, `File doesn't exist`);
748
651
 
749
652
  // src/schema/resource-id.ts
653
+ import { paramCase as paramCase3 } from "change-case";
750
654
  import { z as z4 } from "zod";
751
- var ResourceIdSchema = z4.string().min(3).max(24).regex(/^[a-z\-]+$/, "Invalid resource ID");
655
+ var ResourceIdSchema = z4.string().min(3).max(24).regex(/^[a-z0-9\-]+$/i, "Invalid resource ID").transform((value) => paramCase3(value));
752
656
 
753
657
  // src/schema/size.ts
754
658
  import { z as z5 } from "zod";
@@ -1062,7 +966,7 @@ import { relative as relative2 } from "path";
1062
966
  // src/util/type-gen.ts
1063
967
  import { mkdir, writeFile } from "fs/promises";
1064
968
  import { join as join2, relative } from "path";
1065
- import { camelCase } from "change-case";
969
+ import { camelCase, constantCase } from "change-case";
1066
970
  var generateResourceTypes = async (config) => {
1067
971
  const plugins = [
1068
972
  ...defaultPlugins,
@@ -1084,9 +988,10 @@ var generateResourceTypes = async (config) => {
1084
988
  }
1085
989
  };
1086
990
  var TypeGen = class {
1087
- constructor(module, interfaceName) {
991
+ constructor(module, interfaceName, readonly = true) {
1088
992
  this.module = module;
1089
993
  this.interfaceName = interfaceName;
994
+ this.readonly = readonly;
1090
995
  }
1091
996
  codes = /* @__PURE__ */ new Set();
1092
997
  types = /* @__PURE__ */ new Map();
@@ -1101,7 +1006,13 @@ var TypeGen = class {
1101
1006
  }
1102
1007
  addType(name, type) {
1103
1008
  if (type) {
1104
- this.types.set(name, type);
1009
+ this.types.set(camelCase(name), type);
1010
+ }
1011
+ return this;
1012
+ }
1013
+ addConst(name, type) {
1014
+ if (type) {
1015
+ this.types.set(constantCase(name), type);
1105
1016
  }
1106
1017
  return this;
1107
1018
  }
@@ -1132,7 +1043,7 @@ var TypeGen = class {
1132
1043
  `declare module '${this.module}' {`,
1133
1044
  ` interface ${this.interfaceName} {`,
1134
1045
  ...Array.from(this.types.entries()).map(([propName, type]) => {
1135
- return ` readonly ${camelCase(propName)}: ${type}`;
1046
+ return ` ${this.readonly ? "readonly " : ""}${propName}: ${type}`;
1136
1047
  }),
1137
1048
  ` }`,
1138
1049
  `}`,
@@ -1145,7 +1056,15 @@ var TypeGen = class {
1145
1056
  var TypeObject = class {
1146
1057
  types = /* @__PURE__ */ new Map();
1147
1058
  addType(name, type) {
1148
- this.types.set(name, type);
1059
+ if (type) {
1060
+ this.types.set(camelCase(name), type);
1061
+ }
1062
+ return this;
1063
+ }
1064
+ addConst(name, type) {
1065
+ if (type) {
1066
+ this.types.set(constantCase(name), type);
1067
+ }
1149
1068
  return this;
1150
1069
  }
1151
1070
  toString() {
@@ -1155,7 +1074,7 @@ var TypeObject = class {
1155
1074
  return [
1156
1075
  "{",
1157
1076
  ...Array.from(this.types.entries()).map(([propName, type]) => {
1158
- return ` readonly ${camelCase(propName)}: ${type}`;
1077
+ return ` readonly ${propName}: ${type}`;
1159
1078
  }),
1160
1079
  " }"
1161
1080
  ].join("\n");
@@ -1229,6 +1148,9 @@ var FunctionSchema = z6.union([
1229
1148
  // onFailure: ResourceIdSchema.optional(),
1230
1149
  })
1231
1150
  ]);
1151
+ var isFunctionProps = (input) => {
1152
+ return typeof input === "string" || typeof input.file === "string";
1153
+ };
1232
1154
  var schema = z6.object({
1233
1155
  defaults: z6.object({
1234
1156
  function: z6.object({
@@ -1428,7 +1350,7 @@ var Permission2 = class extends Resource {
1428
1350
  FunctionName: this.props.functionArn,
1429
1351
  Action: this.props.action || "lambda:InvokeFunction",
1430
1352
  Principal: this.props.principal,
1431
- SourceArn: this.props.sourceArn
1353
+ ...this.attr("SourceArn", this.props.sourceArn)
1432
1354
  };
1433
1355
  }
1434
1356
  };
@@ -1554,7 +1476,7 @@ var Queue = class extends Resource {
1554
1476
  };
1555
1477
 
1556
1478
  // src/formation/resource/lambda/event-source-mapping.ts
1557
- import { constantCase } from "change-case";
1479
+ import { constantCase as constantCase2 } from "change-case";
1558
1480
  var EventSourceMapping = class extends Resource {
1559
1481
  constructor(logicalId, props) {
1560
1482
  super("AWS::Lambda::EventSourceMapping", logicalId);
@@ -1576,7 +1498,7 @@ var EventSourceMapping = class extends Resource {
1576
1498
  ...this.attr("ParallelizationFactor", this.props.parallelizationFactor),
1577
1499
  ...this.attr("TumblingWindowInSeconds", this.props.tumblingWindow?.toSeconds()),
1578
1500
  ...this.attr("BisectBatchOnFunctionError", this.props.bisectBatchOnError),
1579
- ...this.attr("StartingPosition", this.props.startingPosition && constantCase(this.props.startingPosition)),
1501
+ ...this.attr("StartingPosition", this.props.startingPosition && constantCase2(this.props.startingPosition)),
1580
1502
  ...this.attr("StartingPositionTimestamp", this.props.startingPositionTimestamp),
1581
1503
  ...this.props.maxConcurrency ? {
1582
1504
  ScalingConfig: {
@@ -1617,7 +1539,7 @@ var SqsEventSource = class extends Group {
1617
1539
  };
1618
1540
 
1619
1541
  // src/plugins/queue.ts
1620
- import { camelCase as camelCase3, constantCase as constantCase2 } from "change-case";
1542
+ import { camelCase as camelCase3, constantCase as constantCase3 } from "change-case";
1621
1543
  import { relative as relative3 } from "path";
1622
1544
  var RetentionPeriodSchema = DurationSchema.refine(durationMin(Duration.minutes(1)), "Minimum retention period is 1 minute").refine(durationMax(Duration.days(14)), "Maximum retention period is 14 days");
1623
1545
  var VisibilityTimeoutSchema = DurationSchema.refine(durationMax(Duration.hours(12)), "Maximum visibility timeout is 12 hours");
@@ -1772,7 +1694,7 @@ var queuePlugin = definePlugin({
1772
1694
  stack.add(queue2, lambda, source);
1773
1695
  bind((lambda2) => {
1774
1696
  lambda2.addPermissions(queue2.permissions);
1775
- lambda2.addEnvironment(`QUEUE_${constantCase2(stack.name)}_${constantCase2(id)}_URL`, queue2.url);
1697
+ lambda2.addEnvironment(`QUEUE_${constantCase3(stack.name)}_${constantCase3(id)}_URL`, queue2.url);
1776
1698
  });
1777
1699
  }
1778
1700
  }
@@ -1782,7 +1704,7 @@ var queuePlugin = definePlugin({
1782
1704
  import { z as z9 } from "zod";
1783
1705
 
1784
1706
  // src/formation/resource/dynamodb/table.ts
1785
- import { constantCase as constantCase3 } from "change-case";
1707
+ import { constantCase as constantCase4 } from "change-case";
1786
1708
  var Table = class extends Resource {
1787
1709
  constructor(logicalId, props) {
1788
1710
  super("AWS::DynamoDB::Table", logicalId);
@@ -1855,7 +1777,7 @@ var Table = class extends Resource {
1855
1777
  return {
1856
1778
  TableName: this.name,
1857
1779
  BillingMode: "PAY_PER_REQUEST",
1858
- TableClass: constantCase3(this.props.class || "standard"),
1780
+ TableClass: constantCase4(this.props.class || "standard"),
1859
1781
  PointInTimeRecoverySpecification: {
1860
1782
  PointInTimeRecoveryEnabled: this.props.pointInTimeRecovery || false
1861
1783
  },
@@ -1866,7 +1788,7 @@ var Table = class extends Resource {
1866
1788
  AttributeDefinitions: this.attributeDefinitions(),
1867
1789
  ...this.props.stream ? {
1868
1790
  StreamSpecification: {
1869
- StreamViewType: constantCase3(this.props.stream)
1791
+ StreamViewType: constantCase4(this.props.stream)
1870
1792
  }
1871
1793
  } : {},
1872
1794
  ...this.props.timeToLiveAttribute ? {
@@ -1883,7 +1805,7 @@ var Table = class extends Resource {
1883
1805
  ...props.sort ? [{ KeyType: "RANGE", AttributeName: props.sort }] : []
1884
1806
  ],
1885
1807
  Projection: {
1886
- ProjectionType: constantCase3(props.projection || "all")
1808
+ ProjectionType: constantCase4(props.projection || "all")
1887
1809
  }
1888
1810
  }))
1889
1811
  } : {}
@@ -2233,54 +2155,81 @@ var topicPlugin = definePlugin({
2233
2155
  name: "topic",
2234
2156
  schema: z11.object({
2235
2157
  stacks: z11.object({
2236
- /** Define the topics to listen too in your stack.
2158
+ /** Define the events to publish too in your stack.
2237
2159
  * @example
2238
2160
  * {
2239
- * topics: {
2161
+ * topics: [ 'TOPIC_NAME' ]
2162
+ * }
2163
+ */
2164
+ topics: z11.array(ResourceIdSchema).refine((topics) => {
2165
+ return topics.length === new Set(topics).size;
2166
+ }, "Must be a list of unique topic names").optional(),
2167
+ /** Define the events to subscribe too in your stack.
2168
+ * @example
2169
+ * {
2170
+ * subscribers: {
2240
2171
  * TOPIC_NAME: 'function.ts'
2241
2172
  * }
2242
2173
  * }
2243
2174
  */
2244
- topics: z11.record(ResourceIdSchema, FunctionSchema).optional()
2245
- }).array()
2175
+ subscribers: z11.record(ResourceIdSchema, FunctionSchema).optional()
2176
+ }).array().superRefine((stacks, ctx) => {
2177
+ const topics = [];
2178
+ for (const stack of stacks) {
2179
+ topics.push(...stack.topics || []);
2180
+ }
2181
+ for (const index in stacks) {
2182
+ const stack = stacks[index];
2183
+ for (const sub2 of Object.keys(stack.subscribers || {})) {
2184
+ if (!topics.includes(sub2)) {
2185
+ ctx.addIssue({
2186
+ code: z11.ZodIssueCode.custom,
2187
+ message: `Topic subscription to "${sub2}" is undefined`,
2188
+ path: [Number(index), "subscribers"]
2189
+ });
2190
+ }
2191
+ }
2192
+ }
2193
+ })
2246
2194
  }),
2247
2195
  onTypeGen({ config }) {
2248
2196
  const gen = new TypeGen("@awsless/awsless", "TopicResources");
2249
2197
  gen.addCode(typeGenCode3);
2250
2198
  for (const stack of config.stacks) {
2251
- for (const topic of Object.keys(stack.topics || {})) {
2199
+ for (const topic of stack.topics || []) {
2252
2200
  const name = formatName(`${config.name}-${topic}`);
2253
2201
  gen.addType(topic, `Publish<'${name}'>`);
2254
2202
  }
2255
2203
  }
2256
2204
  return gen.toString();
2257
2205
  },
2258
- onApp({ config, bootstrap: bootstrap2, bind }) {
2259
- const allTopicNames = config.stacks.map((stack) => {
2260
- return Object.keys(stack.topics || {});
2261
- }).flat();
2262
- const uniqueTopicNames = [...new Set(allTopicNames)];
2263
- for (const id of uniqueTopicNames) {
2264
- const topic = new Topic(id, {
2265
- name: `${config.name}-${id}`
2266
- });
2267
- bootstrap2.add(topic);
2268
- bootstrap2.export(`topic-${id}-arn`, topic.arn);
2206
+ onApp({ config, bootstrap: bootstrap2 }) {
2207
+ for (const stack of config.stacks) {
2208
+ for (const id of stack.topics || []) {
2209
+ const topic = new Topic(id, {
2210
+ name: `${config.name}-${id}`
2211
+ });
2212
+ bootstrap2.add(topic);
2213
+ bootstrap2.export(`topic-${id}-arn`, topic.arn);
2214
+ }
2269
2215
  }
2270
- bind((lambda) => {
2271
- lambda.addPermissions({
2272
- actions: ["sns:Publish"],
2273
- resources: [
2274
- sub("arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:${app}-*", {
2275
- app: config.name
2276
- })
2277
- ]
2278
- });
2279
- });
2280
2216
  },
2281
2217
  onStack(ctx) {
2282
- const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
2283
- for (const [id, props] of Object.entries(stackConfig.topics || {})) {
2218
+ const { config, stack, stackConfig, bootstrap: bootstrap2, bind } = ctx;
2219
+ for (const id of stackConfig.topics || []) {
2220
+ bind((lambda) => {
2221
+ lambda.addPermissions({
2222
+ actions: ["sns:Publish"],
2223
+ resources: [
2224
+ sub("arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:${app}-${topic}", {
2225
+ app: config.name,
2226
+ topic: id
2227
+ })
2228
+ ]
2229
+ });
2230
+ });
2231
+ }
2232
+ for (const [id, props] of Object.entries(stackConfig.subscribers || {})) {
2284
2233
  const lambda = toLambdaFunction(ctx, `topic-${id}`, props);
2285
2234
  const source = new SnsEventSource(id, lambda, {
2286
2235
  topicArn: bootstrap2.import(`topic-${id}-arn`)
@@ -2419,10 +2368,10 @@ var toArray = (value) => {
2419
2368
  };
2420
2369
 
2421
2370
  // src/plugins/graphql.ts
2422
- import { paramCase as paramCase3 } from "change-case";
2371
+ import { paramCase as paramCase4 } from "change-case";
2423
2372
 
2424
2373
  // src/formation/resource/appsync/graphql-api.ts
2425
- import { constantCase as constantCase4 } from "change-case";
2374
+ import { constantCase as constantCase5 } from "change-case";
2426
2375
  var GraphQLApi = class extends Resource {
2427
2376
  constructor(logicalId, props) {
2428
2377
  super("AWS::AppSync::GraphQLApi", logicalId);
@@ -2454,7 +2403,7 @@ var GraphQLApi = class extends Resource {
2454
2403
  properties() {
2455
2404
  return {
2456
2405
  Name: this.name,
2457
- AuthenticationType: constantCase4(this.props.authenticationType || "api-key"),
2406
+ AuthenticationType: constantCase5(this.props.authenticationType || "api-key"),
2458
2407
  AdditionalAuthenticationProviders: this.lambdaAuthProviders.map((provider) => ({
2459
2408
  AuthenticationType: "AWS_LAMBDA",
2460
2409
  LambdaAuthorizerConfig: {
@@ -2783,10 +2732,18 @@ var graphqlPlugin = definePlugin({
2783
2732
  z14.array(LocalFileSchema).min(1)
2784
2733
  ]).optional(),
2785
2734
  resolvers: z14.record(
2735
+ // TypeName
2786
2736
  z14.string(),
2787
2737
  z14.record(
2738
+ // FieldName
2788
2739
  z14.string(),
2789
- FunctionSchema
2740
+ z14.union([
2741
+ FunctionSchema,
2742
+ z14.object({
2743
+ consumer: FunctionSchema,
2744
+ resolver: LocalFileSchema
2745
+ })
2746
+ ])
2790
2747
  )
2791
2748
  ).optional()
2792
2749
  })).optional()
@@ -2854,14 +2811,15 @@ var graphqlPlugin = definePlugin({
2854
2811
  for (const [id, props] of Object.entries(stackConfig.graphql || {})) {
2855
2812
  const apiId = bootstrap2.import(`graphql-${id}`);
2856
2813
  for (const [typeName, fields] of Object.entries(props.resolvers || {})) {
2857
- for (const [fieldName, functionProps] of Object.entries(fields || {})) {
2858
- const entryId = paramCase3(`${id}-${typeName}-${fieldName}`);
2859
- const lambda = toLambdaFunction(ctx, `graphql-${entryId}`, functionProps);
2814
+ for (const [fieldName, resolverProps] of Object.entries(fields || {})) {
2815
+ const props2 = isFunctionProps(resolverProps) ? { consumer: resolverProps } : resolverProps;
2816
+ const entryId = paramCase4(`${id}-${typeName}-${fieldName}`);
2817
+ const lambda = toLambdaFunction(ctx, `graphql-${entryId}`, props2.consumer);
2860
2818
  const source = new AppsyncEventSource(entryId, lambda, {
2861
2819
  apiId,
2862
2820
  typeName,
2863
2821
  fieldName,
2864
- code: Code2.fromInline(entryId, defaultResolver)
2822
+ code: Code2.fromInline(entryId, props2.resolver || defaultResolver)
2865
2823
  });
2866
2824
  stack.add(lambda, source);
2867
2825
  }
@@ -2942,6 +2900,37 @@ var RecordSetGroup = class extends Resource {
2942
2900
  }
2943
2901
  };
2944
2902
 
2903
+ // src/custom/util.ts
2904
+ var sendCode = (
2905
+ /* JS */
2906
+ `
2907
+
2908
+ const send = async (event, id, status, data, reason = '') => {
2909
+ const body = JSON.stringify({
2910
+ Status: status,
2911
+ Reason: reason,
2912
+ PhysicalResourceId: id,
2913
+ StackId: event.StackId,
2914
+ RequestId: event.RequestId,
2915
+ LogicalResourceId: event.LogicalResourceId,
2916
+ NoEcho: false,
2917
+ Data: data
2918
+ })
2919
+
2920
+ await fetch(event.ResponseURL, {
2921
+ method: 'PUT',
2922
+ port: 443,
2923
+ body,
2924
+ headers: {
2925
+ 'content-type': '',
2926
+ 'content-length': Buffer.from(body).byteLength,
2927
+ },
2928
+ })
2929
+ }
2930
+
2931
+ `
2932
+ );
2933
+
2945
2934
  // src/custom/delete-hosted-zone/handler.ts
2946
2935
  var deleteHostedZoneRecordsHandlerCode = (
2947
2936
  /* JS */
@@ -2951,6 +2940,8 @@ const { Route53Client, ListResourceRecordSetsCommand, ChangeResourceRecordSetsCo
2951
2940
 
2952
2941
  const client = new Route53Client({})
2953
2942
 
2943
+ ${sendCode}
2944
+
2954
2945
  exports.handler = async (event) => {
2955
2946
  const type = event.RequestType
2956
2947
  const hostedZoneId = event.ResourceProperties.hostedZoneId
@@ -2974,29 +2965,6 @@ exports.handler = async (event) => {
2974
2965
  }
2975
2966
  }
2976
2967
 
2977
- const send = async (event, id, status, data = {}, reason = '') => {
2978
- const body = JSON.stringify({
2979
- Status: status,
2980
- Reason: reason,
2981
- PhysicalResourceId: id,
2982
- StackId: event.StackId,
2983
- RequestId: event.RequestId,
2984
- LogicalResourceId: event.LogicalResourceId,
2985
- NoEcho: false,
2986
- Data: data
2987
- })
2988
-
2989
- await fetch(event.ResponseURL, {
2990
- method: 'PUT',
2991
- port: 443,
2992
- body,
2993
- headers: {
2994
- 'content-type': '',
2995
- 'content-length': Buffer.from(body).byteLength,
2996
- },
2997
- })
2998
- }
2999
-
3000
2968
  const deleteHostedZoneRecords = async (hostedZoneId, records) => {
3001
2969
  records = records.filter(record => ![ 'SOA', 'NS' ].includes(record.Type))
3002
2970
  if(records.length === 0) {
@@ -3540,7 +3508,7 @@ var LoadBalancer = class extends Resource {
3540
3508
  };
3541
3509
 
3542
3510
  // src/formation/resource/elb/listener.ts
3543
- import { constantCase as constantCase5 } from "change-case";
3511
+ import { constantCase as constantCase6 } from "change-case";
3544
3512
  var Listener = class extends Resource {
3545
3513
  constructor(logicalId, props) {
3546
3514
  super("AWS::ElasticLoadBalancingV2::Listener", logicalId);
@@ -3556,7 +3524,7 @@ var Listener = class extends Resource {
3556
3524
  return {
3557
3525
  LoadBalancerArn: this.props.loadBalancerArn,
3558
3526
  Port: this.props.port,
3559
- Protocol: constantCase5(this.props.protocol),
3527
+ Protocol: constantCase6(this.props.protocol),
3560
3528
  Certificates: this.props.certificates.map((arn) => ({
3561
3529
  CertificateArn: arn
3562
3530
  })),
@@ -3995,7 +3963,7 @@ var SubnetGroup = class extends Resource {
3995
3963
  };
3996
3964
 
3997
3965
  // src/plugins/cache.ts
3998
- import { constantCase as constantCase6 } from "change-case";
3966
+ import { constantCase as constantCase7 } from "change-case";
3999
3967
  var TypeSchema = z19.enum([
4000
3968
  "t4g.small",
4001
3969
  "t4g.medium",
@@ -4081,17 +4049,429 @@ var cachePlugin = definePlugin({
4081
4049
  }).dependsOn(subnetGroup, securityGroup);
4082
4050
  stack.add(subnetGroup, securityGroup, cluster);
4083
4051
  bind((lambda) => {
4084
- lambda.addEnvironment(`CACHE_${constantCase6(stack.name)}_${constantCase6(id)}_HOST`, cluster.address).addEnvironment(`CACHE_${constantCase6(stack.name)}_${constantCase6(id)}_PORT`, props.port.toString());
4052
+ lambda.addEnvironment(`CACHE_${constantCase7(stack.name)}_${constantCase7(id)}_HOST`, cluster.address).addEnvironment(`CACHE_${constantCase7(stack.name)}_${constantCase7(id)}_PORT`, props.port.toString());
4053
+ });
4054
+ }
4055
+ }
4056
+ });
4057
+
4058
+ // src/plugins/rest.ts
4059
+ import { z as z21 } from "zod";
4060
+
4061
+ // src/schema/route.ts
4062
+ import { z as z20 } from "zod";
4063
+ var RouteSchema2 = z20.custom((route) => {
4064
+ return z20.string().regex(/^(POST|GET|PUT|DELETE|HEAD|OPTIONS)(\s\/[a-z0-9\+\_\-\/\{\}]*)$/ig).safeParse(route).success;
4065
+ }, "Invalid route");
4066
+
4067
+ // src/formation/resource/api-gateway-v2/api.ts
4068
+ var Api = class extends Resource {
4069
+ constructor(logicalId, props) {
4070
+ super("AWS::ApiGatewayV2::Api", logicalId);
4071
+ this.props = props;
4072
+ this.name = formatName(this.props.name || logicalId);
4073
+ }
4074
+ name;
4075
+ get endpoint() {
4076
+ return getAtt(this.logicalId, "ApiEndpoint");
4077
+ }
4078
+ get id() {
4079
+ return getAtt(this.logicalId, "ApiId");
4080
+ }
4081
+ properties() {
4082
+ return {
4083
+ Name: this.name,
4084
+ ProtocolType: this.props.protocolType,
4085
+ ...this.attr("Description", this.props.description),
4086
+ CorsConfiguration: {
4087
+ ...this.attr("AllowCredentials", this.props.cors?.allow?.credentials),
4088
+ ...this.attr("AllowHeaders", this.props.cors?.allow?.headers),
4089
+ ...this.attr("AllowMethods", this.props.cors?.allow?.methods),
4090
+ ...this.attr("AllowOrigins", this.props.cors?.allow?.origins),
4091
+ ...this.attr("ExposeHeaders", this.props.cors?.expose?.headers),
4092
+ ...this.attr("MaxAge", this.props.cors?.maxAge?.toSeconds())
4093
+ }
4094
+ };
4095
+ }
4096
+ };
4097
+
4098
+ // src/formation/resource/api-gateway-v2/integration.ts
4099
+ var Integration = class extends Resource {
4100
+ constructor(logicalId, props) {
4101
+ super("AWS::ApiGatewayV2::Integration", logicalId);
4102
+ this.props = props;
4103
+ }
4104
+ get id() {
4105
+ return ref(this.logicalId);
4106
+ }
4107
+ properties() {
4108
+ return {
4109
+ ApiId: this.props.apiId,
4110
+ IntegrationType: this.props.type,
4111
+ IntegrationUri: this.props.uri,
4112
+ IntegrationMethod: this.props.method,
4113
+ PayloadFormatVersion: this.props.payloadFormatVersion ?? "2.0",
4114
+ ...this.attr("Description", this.props.description)
4115
+ };
4116
+ }
4117
+ };
4118
+
4119
+ // src/formation/resource/api-gateway-v2/route.ts
4120
+ var Route2 = class extends Resource {
4121
+ constructor(logicalId, props) {
4122
+ super("AWS::ApiGatewayV2::Route", logicalId);
4123
+ this.props = props;
4124
+ }
4125
+ get id() {
4126
+ return getAtt(this.logicalId, "RouteId");
4127
+ }
4128
+ properties() {
4129
+ return {
4130
+ ApiId: this.props.apiId,
4131
+ RouteKey: this.props.routeKey,
4132
+ Target: this.props.target
4133
+ };
4134
+ }
4135
+ };
4136
+
4137
+ // src/formation/resource/lambda/event-source/api-gateway-v2.ts
4138
+ var ApiGatewayV2EventSource = class extends Group {
4139
+ constructor(id, lambda, props) {
4140
+ const name = formatName(id);
4141
+ const permission = new Permission2(id, {
4142
+ action: "lambda:InvokeFunction",
4143
+ principal: "apigateway.amazonaws.com",
4144
+ functionArn: lambda.arn
4145
+ }).dependsOn(lambda);
4146
+ const integration = new Integration(id, {
4147
+ apiId: props.apiId,
4148
+ description: name,
4149
+ method: "POST",
4150
+ payloadFormatVersion: "2.0",
4151
+ type: "AWS_PROXY",
4152
+ uri: sub("arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambda}/invocations", {
4153
+ lambda: lambda.arn
4154
+ })
4155
+ });
4156
+ const route = new Route2(id, {
4157
+ apiId: props.apiId,
4158
+ routeKey: props.routeKey,
4159
+ target: sub("integrations/${id}", { id: integration.id })
4160
+ }).dependsOn(lambda, permission, integration);
4161
+ super([integration, route, permission]);
4162
+ }
4163
+ };
4164
+
4165
+ // src/formation/resource/api-gateway-v2/domain-name.ts
4166
+ var DomainName2 = class extends Resource {
4167
+ constructor(logicalId, props) {
4168
+ super("AWS::ApiGatewayV2::DomainName", logicalId);
4169
+ this.props = props;
4170
+ }
4171
+ get name() {
4172
+ return ref(this.logicalId);
4173
+ }
4174
+ get regionalDomainName() {
4175
+ return getAtt(this.logicalId, "RegionalDomainName");
4176
+ }
4177
+ get regionalHostedZoneId() {
4178
+ return getAtt(this.logicalId, "RegionalHostedZoneId");
4179
+ }
4180
+ properties() {
4181
+ return {
4182
+ DomainName: this.props.name,
4183
+ DomainNameConfigurations: [{
4184
+ CertificateArn: this.props.certificateArn
4185
+ }]
4186
+ };
4187
+ }
4188
+ };
4189
+
4190
+ // src/formation/resource/api-gateway-v2/stage.ts
4191
+ var Stage = class extends Resource {
4192
+ constructor(logicalId, props) {
4193
+ super("AWS::ApiGatewayV2::Stage", logicalId);
4194
+ this.props = props;
4195
+ this.name = formatName(this.props.name || logicalId);
4196
+ }
4197
+ name;
4198
+ properties() {
4199
+ return {
4200
+ ApiId: this.props.apiId,
4201
+ StageName: this.name,
4202
+ AutoDeploy: this.props.autoDeploy ?? true,
4203
+ ...this.attr("DeploymentId", this.props.deploymentId),
4204
+ ...this.attr("Description", this.props.description)
4205
+ };
4206
+ }
4207
+ };
4208
+
4209
+ // src/formation/resource/api-gateway-v2/api-mapping.ts
4210
+ var ApiMapping = class extends Resource {
4211
+ constructor(logicalId, props) {
4212
+ super("AWS::ApiGatewayV2::ApiMapping", logicalId);
4213
+ this.props = props;
4214
+ }
4215
+ get id() {
4216
+ return getAtt(this.logicalId, "ApiMappingId");
4217
+ }
4218
+ properties() {
4219
+ return {
4220
+ DomainName: this.props.domainName,
4221
+ ApiId: this.props.apiId,
4222
+ Stage: this.props.stage
4223
+ };
4224
+ }
4225
+ };
4226
+
4227
+ // src/plugins/rest.ts
4228
+ var restPlugin = definePlugin({
4229
+ name: "rest",
4230
+ schema: z21.object({
4231
+ defaults: z21.object({
4232
+ /** Define your global REST API's.
4233
+ * @example
4234
+ * {
4235
+ * rest: {
4236
+ * REST_API_NAME: {
4237
+ * domain: 'example.com',
4238
+ * subDomain: 'api',
4239
+ * }
4240
+ * }
4241
+ * }
4242
+ */
4243
+ rest: z21.record(
4244
+ ResourceIdSchema,
4245
+ z21.object({
4246
+ /** The domain to link your API with. */
4247
+ domain: z21.string(),
4248
+ subDomain: z21.string().optional()
4249
+ })
4250
+ ).optional()
4251
+ }).default({}),
4252
+ stacks: z21.object({
4253
+ /** Define routes in your stack for your global REST API.
4254
+ * @example
4255
+ * {
4256
+ * rest: {
4257
+ * REST_API_NAME: {
4258
+ * 'GET /': 'index.ts',
4259
+ * 'POST /posts': 'create-post.ts',
4260
+ * }
4261
+ * }
4262
+ * }
4263
+ */
4264
+ rest: z21.record(
4265
+ ResourceIdSchema,
4266
+ z21.record(RouteSchema2, FunctionSchema)
4267
+ ).optional()
4268
+ }).array()
4269
+ }),
4270
+ onApp({ config, bootstrap: bootstrap2, usEastBootstrap }) {
4271
+ for (const [id, props] of Object.entries(config.defaults?.rest || {})) {
4272
+ const api = new Api(id, {
4273
+ name: `${config.name}-${id}`,
4274
+ protocolType: "HTTP"
4085
4275
  });
4276
+ const stage = new Stage(id, {
4277
+ name: "v1",
4278
+ apiId: api.id
4279
+ }).dependsOn(api);
4280
+ bootstrap2.add(api, stage).export(`rest-${id}-id`, api.id);
4281
+ if (props.domain) {
4282
+ const domainName = props.subDomain ? `${props.subDomain}.${props.domain}` : props.domain;
4283
+ const hostedZoneId = usEastBootstrap.import(`hosted-zone-${props.domain}-id`);
4284
+ const certificateArn = bootstrap2.import(`certificate-${props.domain}-arn`);
4285
+ const domain = new DomainName2(id, {
4286
+ name: domainName,
4287
+ certificateArn
4288
+ });
4289
+ const mapping = new ApiMapping(id, {
4290
+ apiId: api.id,
4291
+ domainName: domain.name,
4292
+ stage: stage.name
4293
+ }).dependsOn(api, domain, stage);
4294
+ const record = new RecordSet(`${id}-rest`, {
4295
+ hostedZoneId,
4296
+ type: "A",
4297
+ name: domainName,
4298
+ alias: {
4299
+ dnsName: domain.regionalDomainName,
4300
+ hostedZoneId: domain.regionalHostedZoneId
4301
+ }
4302
+ }).dependsOn(domain, mapping);
4303
+ bootstrap2.add(domain, mapping, record);
4304
+ }
4305
+ }
4306
+ },
4307
+ onStack(ctx) {
4308
+ const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
4309
+ for (const [id, routes] of Object.entries(stackConfig.rest || {})) {
4310
+ for (const [routeKey, props] of Object.entries(routes)) {
4311
+ const lambda = toLambdaFunction(ctx, `rest-${id}-${routeKey}`, props);
4312
+ const source = new ApiGatewayV2EventSource(`rest-${id}-${routeKey}`, lambda, {
4313
+ apiId: bootstrap2.import(`rest-${id}-id`),
4314
+ routeKey
4315
+ });
4316
+ stack.add(lambda, source);
4317
+ }
4086
4318
  }
4087
4319
  }
4088
4320
  });
4089
4321
 
4322
+ // src/plugins/config.ts
4323
+ import { z as z22 } from "zod";
4324
+
4325
+ // src/util/param.ts
4326
+ import { DeleteParameterCommand, GetParameterCommand, GetParametersByPathCommand, ParameterType, PutParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
4327
+ var configParameterPrefix = (config) => {
4328
+ return `/.awsless/${config.name}`;
4329
+ };
4330
+ var Params = class {
4331
+ constructor(config) {
4332
+ this.config = config;
4333
+ this.client = new SSMClient({
4334
+ credentials: config.credentials,
4335
+ region: config.region
4336
+ });
4337
+ }
4338
+ client;
4339
+ getName(name) {
4340
+ return `${configParameterPrefix(this.config)}/${name}`;
4341
+ }
4342
+ async get(name) {
4343
+ debug("Get remote config value");
4344
+ debug("Name:", style.info(name));
4345
+ let result;
4346
+ try {
4347
+ result = await this.client.send(new GetParameterCommand({
4348
+ Name: this.getName(name),
4349
+ WithDecryption: true
4350
+ }));
4351
+ } catch (error) {
4352
+ if (error instanceof Error && error.name === "ParameterNotFound") {
4353
+ debug("Parameter not found");
4354
+ return;
4355
+ }
4356
+ throw error;
4357
+ }
4358
+ const value = result.Parameter?.Value;
4359
+ debug("Value:", style.info(value));
4360
+ debug("Done getting remote config value");
4361
+ return value;
4362
+ }
4363
+ async set(name, value) {
4364
+ debug("Save remote config value");
4365
+ debug("Name:", style.info(name));
4366
+ debug("Value:", style.info(value));
4367
+ await this.client.send(new PutParameterCommand({
4368
+ Type: ParameterType.STRING,
4369
+ Name: this.getName(name),
4370
+ Value: value,
4371
+ Overwrite: true
4372
+ }));
4373
+ debug("Done saving remote config value");
4374
+ }
4375
+ async delete(name) {
4376
+ debug("Delete remote config value");
4377
+ debug("Name:", style.info(name));
4378
+ try {
4379
+ await this.client.send(new DeleteParameterCommand({
4380
+ Name: this.getName(name)
4381
+ }));
4382
+ } catch (error) {
4383
+ if (error instanceof Error && error.name === "ParameterNotFound") {
4384
+ debug("Remote config value was already deleted");
4385
+ return;
4386
+ }
4387
+ throw error;
4388
+ }
4389
+ debug("Done deleting remote config value");
4390
+ }
4391
+ async list() {
4392
+ debug("Load remote config values");
4393
+ const result = await this.client.send(new GetParametersByPathCommand({
4394
+ Path: configParameterPrefix(this.config),
4395
+ WithDecryption: true,
4396
+ MaxResults: 10,
4397
+ Recursive: true
4398
+ }));
4399
+ debug("Done loading remote config values");
4400
+ const values = {};
4401
+ result.Parameters?.forEach((param) => {
4402
+ const name = param.Name.substring(configParameterPrefix(this.config).length).substring(1);
4403
+ values[name] = param.Value || "";
4404
+ });
4405
+ return values;
4406
+ }
4407
+ };
4408
+
4409
+ // src/plugins/config.ts
4410
+ import { paramCase as paramCase5 } from "change-case";
4411
+ var ConfigNameSchema = z22.string().regex(/[a-z0-9\-]/g, "Invalid config name");
4412
+ var configPlugin = definePlugin({
4413
+ name: "config",
4414
+ schema: z22.object({
4415
+ stacks: z22.object({
4416
+ /** Define the config values for your stack.
4417
+ * @example
4418
+ * ```
4419
+ * {
4420
+ * configs: [ 'your-secret' ]
4421
+ * }
4422
+ * ```
4423
+ *
4424
+ * You can access the config values via:
4425
+ * @example
4426
+ * ```
4427
+ * import { Config } from '@awsless/awsless'
4428
+ *
4429
+ * Config.YOUR_SECRET
4430
+ * ```
4431
+ */
4432
+ configs: z22.array(ConfigNameSchema).optional()
4433
+ }).array()
4434
+ }),
4435
+ onTypeGen({ config }) {
4436
+ const types2 = new TypeGen("@awsless/awsless", "ConfigResources", false);
4437
+ for (const stack of config.stacks) {
4438
+ for (const name of stack.configs || []) {
4439
+ types2.addConst(name, "string");
4440
+ }
4441
+ }
4442
+ return types2.toString();
4443
+ },
4444
+ onStack({ bind, config, stackConfig }) {
4445
+ const configs = stackConfig.configs;
4446
+ bind((lambda) => {
4447
+ if (configs && configs.length) {
4448
+ lambda.addEnvironment("AWSLESS_CONFIG", configs.join(","));
4449
+ lambda.addPermissions({
4450
+ actions: [
4451
+ "ssm:GetParameter",
4452
+ "ssm:GetParameters",
4453
+ "ssm:GetParametersByPath"
4454
+ ],
4455
+ resources: configs.map((name) => {
4456
+ return formatArn({
4457
+ service: "ssm",
4458
+ resource: "parameter",
4459
+ resourceName: configParameterPrefix(config) + "/" + paramCase5(name),
4460
+ seperator: ""
4461
+ });
4462
+ })
4463
+ });
4464
+ }
4465
+ });
4466
+ }
4467
+ });
4468
+
4090
4469
  // src/plugins/index.ts
4091
4470
  var defaultPlugins = [
4092
4471
  extendPlugin,
4093
4472
  vpcPlugin,
4094
4473
  functionPlugin,
4474
+ configPlugin,
4095
4475
  cachePlugin,
4096
4476
  cronPlugin,
4097
4477
  queuePlugin,
@@ -4103,6 +4483,7 @@ var defaultPlugins = [
4103
4483
  domainPlugin,
4104
4484
  graphqlPlugin,
4105
4485
  httpPlugin,
4486
+ restPlugin,
4106
4487
  onFailurePlugin
4107
4488
  ];
4108
4489
 
@@ -4140,6 +4521,8 @@ var globalExportsHandlerCode = (
4140
4521
 
4141
4522
  const { CloudFormationClient, ListExportsCommand } = require('@aws-sdk/client-cloudformation')
4142
4523
 
4524
+ ${sendCode}
4525
+
4143
4526
  exports.handler = async (event) => {
4144
4527
  const region = event.ResourceProperties.region
4145
4528
 
@@ -4156,29 +4539,6 @@ exports.handler = async (event) => {
4156
4539
  }
4157
4540
  }
4158
4541
 
4159
- const send = async (event, id, status, data, reason = '') => {
4160
- const body = JSON.stringify({
4161
- Status: status,
4162
- Reason: reason,
4163
- PhysicalResourceId: id,
4164
- StackId: event.StackId,
4165
- RequestId: event.RequestId,
4166
- LogicalResourceId: event.LogicalResourceId,
4167
- NoEcho: false,
4168
- Data: data
4169
- })
4170
-
4171
- await fetch(event.ResponseURL, {
4172
- method: 'PUT',
4173
- port: 443,
4174
- body,
4175
- headers: {
4176
- 'content-type': '',
4177
- 'content-length': Buffer.from(body).byteLength,
4178
- },
4179
- })
4180
- }
4181
-
4182
4542
  const listExports = async (region) => {
4183
4543
  const client = new CloudFormationClient({ region })
4184
4544
  const data = {}
@@ -4356,17 +4716,17 @@ var getCredentials = (profile) => {
4356
4716
  };
4357
4717
 
4358
4718
  // src/schema/app.ts
4359
- import { z as z23 } from "zod";
4719
+ import { z as z26 } from "zod";
4360
4720
 
4361
4721
  // src/schema/stack.ts
4362
- import { z as z20 } from "zod";
4363
- var StackSchema = z20.object({
4722
+ import { z as z23 } from "zod";
4723
+ var StackSchema = z23.object({
4364
4724
  name: ResourceIdSchema,
4365
- depends: z20.array(z20.lazy(() => StackSchema)).optional()
4725
+ depends: z23.array(z23.lazy(() => StackSchema)).optional()
4366
4726
  });
4367
4727
 
4368
4728
  // src/schema/region.ts
4369
- import { z as z21 } from "zod";
4729
+ import { z as z24 } from "zod";
4370
4730
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
4371
4731
  var AF = ["af-south-1"];
4372
4732
  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"];
@@ -4383,41 +4743,41 @@ var regions = [
4383
4743
  ...ME,
4384
4744
  ...SA
4385
4745
  ];
4386
- var RegionSchema = z21.enum(regions);
4746
+ var RegionSchema = z24.enum(regions);
4387
4747
 
4388
4748
  // src/schema/plugin.ts
4389
- import { z as z22 } from "zod";
4390
- var PluginSchema = z22.object({
4391
- name: z22.string(),
4392
- schema: z22.custom().optional(),
4749
+ import { z as z25 } from "zod";
4750
+ var PluginSchema = z25.object({
4751
+ name: z25.string(),
4752
+ schema: z25.custom().optional(),
4393
4753
  // depends: z.array(z.lazy(() => PluginSchema)).optional(),
4394
- onApp: z22.function().returns(z22.void()).optional(),
4395
- onStack: z22.function().returns(z22.any()).optional(),
4396
- onResource: z22.function().returns(z22.any()).optional()
4754
+ onApp: z25.function().returns(z25.void()).optional(),
4755
+ onStack: z25.function().returns(z25.any()).optional(),
4756
+ onResource: z25.function().returns(z25.any()).optional()
4397
4757
  // bind: z.function().optional(),
4398
4758
  });
4399
4759
 
4400
4760
  // src/schema/app.ts
4401
- var AppSchema = z23.object({
4761
+ var AppSchema = z26.object({
4402
4762
  /** App name */
4403
4763
  name: ResourceIdSchema,
4404
4764
  /** The AWS region to deploy to. */
4405
4765
  region: RegionSchema,
4406
4766
  /** The AWS profile to deploy to. */
4407
- profile: z23.string(),
4767
+ profile: z26.string(),
4408
4768
  /** The deployment stage.
4409
4769
  * @default 'prod'
4410
4770
  */
4411
- stage: z23.string().regex(/^[a-z]+$/).default("prod"),
4771
+ stage: z26.string().regex(/^[a-z]+$/).default("prod"),
4412
4772
  /** Default properties. */
4413
- defaults: z23.object({}).default({}),
4773
+ defaults: z26.object({}).default({}),
4414
4774
  /** The application stacks. */
4415
- stacks: z23.array(StackSchema).min(1).refine((stacks) => {
4775
+ stacks: z26.array(StackSchema).min(1).refine((stacks) => {
4416
4776
  const unique = new Set(stacks.map((stack) => stack.name));
4417
4777
  return unique.size === stacks.length;
4418
4778
  }, "Must be an array of unique stacks"),
4419
4779
  /** Custom plugins. */
4420
- plugins: z23.array(PluginSchema).optional()
4780
+ plugins: z26.array(PluginSchema).optional()
4421
4781
  });
4422
4782
 
4423
4783
  // src/util/import.ts
@@ -4514,7 +4874,7 @@ var watchFile = (path) => {
4514
4874
  };
4515
4875
 
4516
4876
  // src/config.ts
4517
- import { z as z24 } from "zod";
4877
+ import { z as z27 } from "zod";
4518
4878
  var ConfigError = class extends Error {
4519
4879
  constructor(error, data) {
4520
4880
  super(error.message);
@@ -4547,7 +4907,7 @@ var importConfig = async (options) => {
4547
4907
  try {
4548
4908
  config = await schema2.parseAsync(appConfig);
4549
4909
  } catch (error) {
4550
- if (error instanceof z24.ZodError) {
4910
+ if (error instanceof z27.ZodError) {
4551
4911
  throw new ConfigError(error, appConfig);
4552
4912
  }
4553
4913
  throw error;
@@ -4588,7 +4948,7 @@ var watchConfig = async function* (options) {
4588
4948
  try {
4589
4949
  config = await schema2.parseAsync(appConfig);
4590
4950
  } catch (error) {
4591
- if (error instanceof z24.ZodError) {
4951
+ if (error instanceof z27.ZodError) {
4592
4952
  throw new ConfigError(error, appConfig);
4593
4953
  }
4594
4954
  throw error;
@@ -5363,7 +5723,7 @@ var shouldDeployBootstrap = async (client, stack) => {
5363
5723
  // src/formation/client.ts
5364
5724
  import { CloudFormationClient, CreateStackCommand, DeleteStackCommand, DescribeStackEventsCommand, DescribeStacksCommand, GetTemplateCommand, OnFailure, TemplateStage, UpdateStackCommand, ValidateTemplateCommand, waitUntilStackCreateComplete, waitUntilStackDeleteComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
5365
5725
  import { S3Client, PutObjectCommand, ObjectCannedACL, StorageClass } from "@aws-sdk/client-s3";
5366
- import { paramCase as paramCase4 } from "change-case";
5726
+ import { paramCase as paramCase6 } from "change-case";
5367
5727
  var StackClient = class {
5368
5728
  constructor(app, account, region, credentials) {
5369
5729
  this.app = app;
@@ -5396,7 +5756,7 @@ var StackClient = class {
5396
5756
  };
5397
5757
  }
5398
5758
  stackName(stackName) {
5399
- return paramCase4(`${this.app.name}-${stackName}`);
5759
+ return paramCase6(`${this.app.name}-${stackName}`);
5400
5760
  }
5401
5761
  tags(stack) {
5402
5762
  const tags = [];
@@ -5409,7 +5769,8 @@ var StackClient = class {
5409
5769
  debug("Upload the", style.info(stack.name), "stack to awsless assets bucket");
5410
5770
  const client = new S3Client({
5411
5771
  credentials: this.credentials,
5412
- region: stack.region
5772
+ region: stack.region,
5773
+ maxAttempts: 5
5413
5774
  });
5414
5775
  await client.send(new PutObjectCommand({
5415
5776
  Bucket: this.assetBucketName,
@@ -5829,7 +6190,8 @@ import { GetObjectCommand, ObjectCannedACL as ObjectCannedACL2, PutObjectCommand
5829
6190
  var assetPublisher = (config, app) => {
5830
6191
  const client = new S3Client2({
5831
6192
  credentials: config.credentials,
5832
- region: config.region
6193
+ region: config.region,
6194
+ maxAttempts: 5
5833
6195
  });
5834
6196
  return async (term) => {
5835
6197
  const done = term.out.write(loadingDialog("Publishing stack assets to AWS..."));