@awsless/awsless 0.0.43 → 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
@@ -749,7 +652,7 @@ var LocalFileSchema = z3.string().refine(async (path) => {
749
652
  // src/schema/resource-id.ts
750
653
  import { paramCase as paramCase3 } from "change-case";
751
654
  import { z as z4 } from "zod";
752
- var ResourceIdSchema = z4.string().min(3).max(24).regex(/^[a-z\-]+$/i, "Invalid resource ID").transform((value) => paramCase3(value));
655
+ var ResourceIdSchema = z4.string().min(3).max(24).regex(/^[a-z0-9\-]+$/i, "Invalid resource ID").transform((value) => paramCase3(value));
753
656
 
754
657
  // src/schema/size.ts
755
658
  import { z as z5 } from "zod";
@@ -1063,7 +966,7 @@ import { relative as relative2 } from "path";
1063
966
  // src/util/type-gen.ts
1064
967
  import { mkdir, writeFile } from "fs/promises";
1065
968
  import { join as join2, relative } from "path";
1066
- import { camelCase } from "change-case";
969
+ import { camelCase, constantCase } from "change-case";
1067
970
  var generateResourceTypes = async (config) => {
1068
971
  const plugins = [
1069
972
  ...defaultPlugins,
@@ -1085,9 +988,10 @@ var generateResourceTypes = async (config) => {
1085
988
  }
1086
989
  };
1087
990
  var TypeGen = class {
1088
- constructor(module, interfaceName) {
991
+ constructor(module, interfaceName, readonly = true) {
1089
992
  this.module = module;
1090
993
  this.interfaceName = interfaceName;
994
+ this.readonly = readonly;
1091
995
  }
1092
996
  codes = /* @__PURE__ */ new Set();
1093
997
  types = /* @__PURE__ */ new Map();
@@ -1102,7 +1006,13 @@ var TypeGen = class {
1102
1006
  }
1103
1007
  addType(name, type) {
1104
1008
  if (type) {
1105
- 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);
1106
1016
  }
1107
1017
  return this;
1108
1018
  }
@@ -1133,7 +1043,7 @@ var TypeGen = class {
1133
1043
  `declare module '${this.module}' {`,
1134
1044
  ` interface ${this.interfaceName} {`,
1135
1045
  ...Array.from(this.types.entries()).map(([propName, type]) => {
1136
- return ` readonly ${camelCase(propName)}: ${type}`;
1046
+ return ` ${this.readonly ? "readonly " : ""}${propName}: ${type}`;
1137
1047
  }),
1138
1048
  ` }`,
1139
1049
  `}`,
@@ -1146,7 +1056,15 @@ var TypeGen = class {
1146
1056
  var TypeObject = class {
1147
1057
  types = /* @__PURE__ */ new Map();
1148
1058
  addType(name, type) {
1149
- 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
+ }
1150
1068
  return this;
1151
1069
  }
1152
1070
  toString() {
@@ -1156,7 +1074,7 @@ var TypeObject = class {
1156
1074
  return [
1157
1075
  "{",
1158
1076
  ...Array.from(this.types.entries()).map(([propName, type]) => {
1159
- return ` readonly ${camelCase(propName)}: ${type}`;
1077
+ return ` readonly ${propName}: ${type}`;
1160
1078
  }),
1161
1079
  " }"
1162
1080
  ].join("\n");
@@ -1230,6 +1148,9 @@ var FunctionSchema = z6.union([
1230
1148
  // onFailure: ResourceIdSchema.optional(),
1231
1149
  })
1232
1150
  ]);
1151
+ var isFunctionProps = (input) => {
1152
+ return typeof input === "string" || typeof input.file === "string";
1153
+ };
1233
1154
  var schema = z6.object({
1234
1155
  defaults: z6.object({
1235
1156
  function: z6.object({
@@ -1429,7 +1350,7 @@ var Permission2 = class extends Resource {
1429
1350
  FunctionName: this.props.functionArn,
1430
1351
  Action: this.props.action || "lambda:InvokeFunction",
1431
1352
  Principal: this.props.principal,
1432
- SourceArn: this.props.sourceArn
1353
+ ...this.attr("SourceArn", this.props.sourceArn)
1433
1354
  };
1434
1355
  }
1435
1356
  };
@@ -1555,7 +1476,7 @@ var Queue = class extends Resource {
1555
1476
  };
1556
1477
 
1557
1478
  // src/formation/resource/lambda/event-source-mapping.ts
1558
- import { constantCase } from "change-case";
1479
+ import { constantCase as constantCase2 } from "change-case";
1559
1480
  var EventSourceMapping = class extends Resource {
1560
1481
  constructor(logicalId, props) {
1561
1482
  super("AWS::Lambda::EventSourceMapping", logicalId);
@@ -1577,7 +1498,7 @@ var EventSourceMapping = class extends Resource {
1577
1498
  ...this.attr("ParallelizationFactor", this.props.parallelizationFactor),
1578
1499
  ...this.attr("TumblingWindowInSeconds", this.props.tumblingWindow?.toSeconds()),
1579
1500
  ...this.attr("BisectBatchOnFunctionError", this.props.bisectBatchOnError),
1580
- ...this.attr("StartingPosition", this.props.startingPosition && constantCase(this.props.startingPosition)),
1501
+ ...this.attr("StartingPosition", this.props.startingPosition && constantCase2(this.props.startingPosition)),
1581
1502
  ...this.attr("StartingPositionTimestamp", this.props.startingPositionTimestamp),
1582
1503
  ...this.props.maxConcurrency ? {
1583
1504
  ScalingConfig: {
@@ -1618,7 +1539,7 @@ var SqsEventSource = class extends Group {
1618
1539
  };
1619
1540
 
1620
1541
  // src/plugins/queue.ts
1621
- import { camelCase as camelCase3, constantCase as constantCase2 } from "change-case";
1542
+ import { camelCase as camelCase3, constantCase as constantCase3 } from "change-case";
1622
1543
  import { relative as relative3 } from "path";
1623
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");
1624
1545
  var VisibilityTimeoutSchema = DurationSchema.refine(durationMax(Duration.hours(12)), "Maximum visibility timeout is 12 hours");
@@ -1773,7 +1694,7 @@ var queuePlugin = definePlugin({
1773
1694
  stack.add(queue2, lambda, source);
1774
1695
  bind((lambda2) => {
1775
1696
  lambda2.addPermissions(queue2.permissions);
1776
- lambda2.addEnvironment(`QUEUE_${constantCase2(stack.name)}_${constantCase2(id)}_URL`, queue2.url);
1697
+ lambda2.addEnvironment(`QUEUE_${constantCase3(stack.name)}_${constantCase3(id)}_URL`, queue2.url);
1777
1698
  });
1778
1699
  }
1779
1700
  }
@@ -1783,7 +1704,7 @@ var queuePlugin = definePlugin({
1783
1704
  import { z as z9 } from "zod";
1784
1705
 
1785
1706
  // src/formation/resource/dynamodb/table.ts
1786
- import { constantCase as constantCase3 } from "change-case";
1707
+ import { constantCase as constantCase4 } from "change-case";
1787
1708
  var Table = class extends Resource {
1788
1709
  constructor(logicalId, props) {
1789
1710
  super("AWS::DynamoDB::Table", logicalId);
@@ -1856,7 +1777,7 @@ var Table = class extends Resource {
1856
1777
  return {
1857
1778
  TableName: this.name,
1858
1779
  BillingMode: "PAY_PER_REQUEST",
1859
- TableClass: constantCase3(this.props.class || "standard"),
1780
+ TableClass: constantCase4(this.props.class || "standard"),
1860
1781
  PointInTimeRecoverySpecification: {
1861
1782
  PointInTimeRecoveryEnabled: this.props.pointInTimeRecovery || false
1862
1783
  },
@@ -1867,7 +1788,7 @@ var Table = class extends Resource {
1867
1788
  AttributeDefinitions: this.attributeDefinitions(),
1868
1789
  ...this.props.stream ? {
1869
1790
  StreamSpecification: {
1870
- StreamViewType: constantCase3(this.props.stream)
1791
+ StreamViewType: constantCase4(this.props.stream)
1871
1792
  }
1872
1793
  } : {},
1873
1794
  ...this.props.timeToLiveAttribute ? {
@@ -1884,7 +1805,7 @@ var Table = class extends Resource {
1884
1805
  ...props.sort ? [{ KeyType: "RANGE", AttributeName: props.sort }] : []
1885
1806
  ],
1886
1807
  Projection: {
1887
- ProjectionType: constantCase3(props.projection || "all")
1808
+ ProjectionType: constantCase4(props.projection || "all")
1888
1809
  }
1889
1810
  }))
1890
1811
  } : {}
@@ -2234,54 +2155,81 @@ var topicPlugin = definePlugin({
2234
2155
  name: "topic",
2235
2156
  schema: z11.object({
2236
2157
  stacks: z11.object({
2237
- /** Define the topics to listen too in your stack.
2158
+ /** Define the events to publish too in your stack.
2238
2159
  * @example
2239
2160
  * {
2240
- * 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: {
2241
2171
  * TOPIC_NAME: 'function.ts'
2242
2172
  * }
2243
2173
  * }
2244
2174
  */
2245
- topics: z11.record(ResourceIdSchema, FunctionSchema).optional()
2246
- }).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
+ })
2247
2194
  }),
2248
2195
  onTypeGen({ config }) {
2249
2196
  const gen = new TypeGen("@awsless/awsless", "TopicResources");
2250
2197
  gen.addCode(typeGenCode3);
2251
2198
  for (const stack of config.stacks) {
2252
- for (const topic of Object.keys(stack.topics || {})) {
2199
+ for (const topic of stack.topics || []) {
2253
2200
  const name = formatName(`${config.name}-${topic}`);
2254
2201
  gen.addType(topic, `Publish<'${name}'>`);
2255
2202
  }
2256
2203
  }
2257
2204
  return gen.toString();
2258
2205
  },
2259
- onApp({ config, bootstrap: bootstrap2, bind }) {
2260
- const allTopicNames = config.stacks.map((stack) => {
2261
- return Object.keys(stack.topics || {});
2262
- }).flat();
2263
- const uniqueTopicNames = [...new Set(allTopicNames)];
2264
- for (const id of uniqueTopicNames) {
2265
- const topic = new Topic(id, {
2266
- name: `${config.name}-${id}`
2267
- });
2268
- bootstrap2.add(topic);
2269
- 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
+ }
2270
2215
  }
2271
- bind((lambda) => {
2272
- lambda.addPermissions({
2273
- actions: ["sns:Publish"],
2274
- resources: [
2275
- sub("arn:${AWS::Partition}:sns:${AWS::Region}:${AWS::AccountId}:${app}-*", {
2276
- app: config.name
2277
- })
2278
- ]
2279
- });
2280
- });
2281
2216
  },
2282
2217
  onStack(ctx) {
2283
- const { stack, stackConfig, bootstrap: bootstrap2 } = ctx;
2284
- 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 || {})) {
2285
2233
  const lambda = toLambdaFunction(ctx, `topic-${id}`, props);
2286
2234
  const source = new SnsEventSource(id, lambda, {
2287
2235
  topicArn: bootstrap2.import(`topic-${id}-arn`)
@@ -2423,7 +2371,7 @@ var toArray = (value) => {
2423
2371
  import { paramCase as paramCase4 } from "change-case";
2424
2372
 
2425
2373
  // src/formation/resource/appsync/graphql-api.ts
2426
- import { constantCase as constantCase4 } from "change-case";
2374
+ import { constantCase as constantCase5 } from "change-case";
2427
2375
  var GraphQLApi = class extends Resource {
2428
2376
  constructor(logicalId, props) {
2429
2377
  super("AWS::AppSync::GraphQLApi", logicalId);
@@ -2455,7 +2403,7 @@ var GraphQLApi = class extends Resource {
2455
2403
  properties() {
2456
2404
  return {
2457
2405
  Name: this.name,
2458
- AuthenticationType: constantCase4(this.props.authenticationType || "api-key"),
2406
+ AuthenticationType: constantCase5(this.props.authenticationType || "api-key"),
2459
2407
  AdditionalAuthenticationProviders: this.lambdaAuthProviders.map((provider) => ({
2460
2408
  AuthenticationType: "AWS_LAMBDA",
2461
2409
  LambdaAuthorizerConfig: {
@@ -2784,10 +2732,18 @@ var graphqlPlugin = definePlugin({
2784
2732
  z14.array(LocalFileSchema).min(1)
2785
2733
  ]).optional(),
2786
2734
  resolvers: z14.record(
2735
+ // TypeName
2787
2736
  z14.string(),
2788
2737
  z14.record(
2738
+ // FieldName
2789
2739
  z14.string(),
2790
- FunctionSchema
2740
+ z14.union([
2741
+ FunctionSchema,
2742
+ z14.object({
2743
+ consumer: FunctionSchema,
2744
+ resolver: LocalFileSchema
2745
+ })
2746
+ ])
2791
2747
  )
2792
2748
  ).optional()
2793
2749
  })).optional()
@@ -2855,14 +2811,15 @@ var graphqlPlugin = definePlugin({
2855
2811
  for (const [id, props] of Object.entries(stackConfig.graphql || {})) {
2856
2812
  const apiId = bootstrap2.import(`graphql-${id}`);
2857
2813
  for (const [typeName, fields] of Object.entries(props.resolvers || {})) {
2858
- for (const [fieldName, functionProps] of Object.entries(fields || {})) {
2814
+ for (const [fieldName, resolverProps] of Object.entries(fields || {})) {
2815
+ const props2 = isFunctionProps(resolverProps) ? { consumer: resolverProps } : resolverProps;
2859
2816
  const entryId = paramCase4(`${id}-${typeName}-${fieldName}`);
2860
- const lambda = toLambdaFunction(ctx, `graphql-${entryId}`, functionProps);
2817
+ const lambda = toLambdaFunction(ctx, `graphql-${entryId}`, props2.consumer);
2861
2818
  const source = new AppsyncEventSource(entryId, lambda, {
2862
2819
  apiId,
2863
2820
  typeName,
2864
2821
  fieldName,
2865
- code: Code2.fromInline(entryId, defaultResolver)
2822
+ code: Code2.fromInline(entryId, props2.resolver || defaultResolver)
2866
2823
  });
2867
2824
  stack.add(lambda, source);
2868
2825
  }
@@ -2943,6 +2900,37 @@ var RecordSetGroup = class extends Resource {
2943
2900
  }
2944
2901
  };
2945
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
+
2946
2934
  // src/custom/delete-hosted-zone/handler.ts
2947
2935
  var deleteHostedZoneRecordsHandlerCode = (
2948
2936
  /* JS */
@@ -2952,6 +2940,8 @@ const { Route53Client, ListResourceRecordSetsCommand, ChangeResourceRecordSetsCo
2952
2940
 
2953
2941
  const client = new Route53Client({})
2954
2942
 
2943
+ ${sendCode}
2944
+
2955
2945
  exports.handler = async (event) => {
2956
2946
  const type = event.RequestType
2957
2947
  const hostedZoneId = event.ResourceProperties.hostedZoneId
@@ -2975,29 +2965,6 @@ exports.handler = async (event) => {
2975
2965
  }
2976
2966
  }
2977
2967
 
2978
- const send = async (event, id, status, data = {}, reason = '') => {
2979
- const body = JSON.stringify({
2980
- Status: status,
2981
- Reason: reason,
2982
- PhysicalResourceId: id,
2983
- StackId: event.StackId,
2984
- RequestId: event.RequestId,
2985
- LogicalResourceId: event.LogicalResourceId,
2986
- NoEcho: false,
2987
- Data: data
2988
- })
2989
-
2990
- await fetch(event.ResponseURL, {
2991
- method: 'PUT',
2992
- port: 443,
2993
- body,
2994
- headers: {
2995
- 'content-type': '',
2996
- 'content-length': Buffer.from(body).byteLength,
2997
- },
2998
- })
2999
- }
3000
-
3001
2968
  const deleteHostedZoneRecords = async (hostedZoneId, records) => {
3002
2969
  records = records.filter(record => ![ 'SOA', 'NS' ].includes(record.Type))
3003
2970
  if(records.length === 0) {
@@ -3541,7 +3508,7 @@ var LoadBalancer = class extends Resource {
3541
3508
  };
3542
3509
 
3543
3510
  // src/formation/resource/elb/listener.ts
3544
- import { constantCase as constantCase5 } from "change-case";
3511
+ import { constantCase as constantCase6 } from "change-case";
3545
3512
  var Listener = class extends Resource {
3546
3513
  constructor(logicalId, props) {
3547
3514
  super("AWS::ElasticLoadBalancingV2::Listener", logicalId);
@@ -3557,7 +3524,7 @@ var Listener = class extends Resource {
3557
3524
  return {
3558
3525
  LoadBalancerArn: this.props.loadBalancerArn,
3559
3526
  Port: this.props.port,
3560
- Protocol: constantCase5(this.props.protocol),
3527
+ Protocol: constantCase6(this.props.protocol),
3561
3528
  Certificates: this.props.certificates.map((arn) => ({
3562
3529
  CertificateArn: arn
3563
3530
  })),
@@ -3996,7 +3963,7 @@ var SubnetGroup = class extends Resource {
3996
3963
  };
3997
3964
 
3998
3965
  // src/plugins/cache.ts
3999
- import { constantCase as constantCase6 } from "change-case";
3966
+ import { constantCase as constantCase7 } from "change-case";
4000
3967
  var TypeSchema = z19.enum([
4001
3968
  "t4g.small",
4002
3969
  "t4g.medium",
@@ -4082,9 +4049,420 @@ var cachePlugin = definePlugin({
4082
4049
  }).dependsOn(subnetGroup, securityGroup);
4083
4050
  stack.add(subnetGroup, securityGroup, cluster);
4084
4051
  bind((lambda) => {
4085
- 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"
4086
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
+ }
4318
+ }
4319
+ }
4320
+ });
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
+ }
4087
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
+ });
4088
4466
  }
4089
4467
  });
4090
4468
 
@@ -4093,6 +4471,7 @@ var defaultPlugins = [
4093
4471
  extendPlugin,
4094
4472
  vpcPlugin,
4095
4473
  functionPlugin,
4474
+ configPlugin,
4096
4475
  cachePlugin,
4097
4476
  cronPlugin,
4098
4477
  queuePlugin,
@@ -4104,6 +4483,7 @@ var defaultPlugins = [
4104
4483
  domainPlugin,
4105
4484
  graphqlPlugin,
4106
4485
  httpPlugin,
4486
+ restPlugin,
4107
4487
  onFailurePlugin
4108
4488
  ];
4109
4489
 
@@ -4141,6 +4521,8 @@ var globalExportsHandlerCode = (
4141
4521
 
4142
4522
  const { CloudFormationClient, ListExportsCommand } = require('@aws-sdk/client-cloudformation')
4143
4523
 
4524
+ ${sendCode}
4525
+
4144
4526
  exports.handler = async (event) => {
4145
4527
  const region = event.ResourceProperties.region
4146
4528
 
@@ -4157,29 +4539,6 @@ exports.handler = async (event) => {
4157
4539
  }
4158
4540
  }
4159
4541
 
4160
- const send = async (event, id, status, data, reason = '') => {
4161
- const body = JSON.stringify({
4162
- Status: status,
4163
- Reason: reason,
4164
- PhysicalResourceId: id,
4165
- StackId: event.StackId,
4166
- RequestId: event.RequestId,
4167
- LogicalResourceId: event.LogicalResourceId,
4168
- NoEcho: false,
4169
- Data: data
4170
- })
4171
-
4172
- await fetch(event.ResponseURL, {
4173
- method: 'PUT',
4174
- port: 443,
4175
- body,
4176
- headers: {
4177
- 'content-type': '',
4178
- 'content-length': Buffer.from(body).byteLength,
4179
- },
4180
- })
4181
- }
4182
-
4183
4542
  const listExports = async (region) => {
4184
4543
  const client = new CloudFormationClient({ region })
4185
4544
  const data = {}
@@ -4357,17 +4716,17 @@ var getCredentials = (profile) => {
4357
4716
  };
4358
4717
 
4359
4718
  // src/schema/app.ts
4360
- import { z as z23 } from "zod";
4719
+ import { z as z26 } from "zod";
4361
4720
 
4362
4721
  // src/schema/stack.ts
4363
- import { z as z20 } from "zod";
4364
- var StackSchema = z20.object({
4722
+ import { z as z23 } from "zod";
4723
+ var StackSchema = z23.object({
4365
4724
  name: ResourceIdSchema,
4366
- depends: z20.array(z20.lazy(() => StackSchema)).optional()
4725
+ depends: z23.array(z23.lazy(() => StackSchema)).optional()
4367
4726
  });
4368
4727
 
4369
4728
  // src/schema/region.ts
4370
- import { z as z21 } from "zod";
4729
+ import { z as z24 } from "zod";
4371
4730
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
4372
4731
  var AF = ["af-south-1"];
4373
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"];
@@ -4384,41 +4743,41 @@ var regions = [
4384
4743
  ...ME,
4385
4744
  ...SA
4386
4745
  ];
4387
- var RegionSchema = z21.enum(regions);
4746
+ var RegionSchema = z24.enum(regions);
4388
4747
 
4389
4748
  // src/schema/plugin.ts
4390
- import { z as z22 } from "zod";
4391
- var PluginSchema = z22.object({
4392
- name: z22.string(),
4393
- 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(),
4394
4753
  // depends: z.array(z.lazy(() => PluginSchema)).optional(),
4395
- onApp: z22.function().returns(z22.void()).optional(),
4396
- onStack: z22.function().returns(z22.any()).optional(),
4397
- 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()
4398
4757
  // bind: z.function().optional(),
4399
4758
  });
4400
4759
 
4401
4760
  // src/schema/app.ts
4402
- var AppSchema = z23.object({
4761
+ var AppSchema = z26.object({
4403
4762
  /** App name */
4404
4763
  name: ResourceIdSchema,
4405
4764
  /** The AWS region to deploy to. */
4406
4765
  region: RegionSchema,
4407
4766
  /** The AWS profile to deploy to. */
4408
- profile: z23.string(),
4767
+ profile: z26.string(),
4409
4768
  /** The deployment stage.
4410
4769
  * @default 'prod'
4411
4770
  */
4412
- stage: z23.string().regex(/^[a-z]+$/).default("prod"),
4771
+ stage: z26.string().regex(/^[a-z]+$/).default("prod"),
4413
4772
  /** Default properties. */
4414
- defaults: z23.object({}).default({}),
4773
+ defaults: z26.object({}).default({}),
4415
4774
  /** The application stacks. */
4416
- stacks: z23.array(StackSchema).min(1).refine((stacks) => {
4775
+ stacks: z26.array(StackSchema).min(1).refine((stacks) => {
4417
4776
  const unique = new Set(stacks.map((stack) => stack.name));
4418
4777
  return unique.size === stacks.length;
4419
4778
  }, "Must be an array of unique stacks"),
4420
4779
  /** Custom plugins. */
4421
- plugins: z23.array(PluginSchema).optional()
4780
+ plugins: z26.array(PluginSchema).optional()
4422
4781
  });
4423
4782
 
4424
4783
  // src/util/import.ts
@@ -4515,7 +4874,7 @@ var watchFile = (path) => {
4515
4874
  };
4516
4875
 
4517
4876
  // src/config.ts
4518
- import { z as z24 } from "zod";
4877
+ import { z as z27 } from "zod";
4519
4878
  var ConfigError = class extends Error {
4520
4879
  constructor(error, data) {
4521
4880
  super(error.message);
@@ -4548,7 +4907,7 @@ var importConfig = async (options) => {
4548
4907
  try {
4549
4908
  config = await schema2.parseAsync(appConfig);
4550
4909
  } catch (error) {
4551
- if (error instanceof z24.ZodError) {
4910
+ if (error instanceof z27.ZodError) {
4552
4911
  throw new ConfigError(error, appConfig);
4553
4912
  }
4554
4913
  throw error;
@@ -4589,7 +4948,7 @@ var watchConfig = async function* (options) {
4589
4948
  try {
4590
4949
  config = await schema2.parseAsync(appConfig);
4591
4950
  } catch (error) {
4592
- if (error instanceof z24.ZodError) {
4951
+ if (error instanceof z27.ZodError) {
4593
4952
  throw new ConfigError(error, appConfig);
4594
4953
  }
4595
4954
  throw error;
@@ -5080,7 +5439,6 @@ var format = (value) => {
5080
5439
  };
5081
5440
  var zodError = (error, data) => {
5082
5441
  return (term) => {
5083
- term.out.write(JSON.stringify(error.errors));
5084
5442
  for (const issue of error.issues) {
5085
5443
  term.out.gap();
5086
5444
  term.out.write(dialog("error", [
@@ -5365,7 +5723,7 @@ var shouldDeployBootstrap = async (client, stack) => {
5365
5723
  // src/formation/client.ts
5366
5724
  import { CloudFormationClient, CreateStackCommand, DeleteStackCommand, DescribeStackEventsCommand, DescribeStacksCommand, GetTemplateCommand, OnFailure, TemplateStage, UpdateStackCommand, ValidateTemplateCommand, waitUntilStackCreateComplete, waitUntilStackDeleteComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
5367
5725
  import { S3Client, PutObjectCommand, ObjectCannedACL, StorageClass } from "@aws-sdk/client-s3";
5368
- import { paramCase as paramCase5 } from "change-case";
5726
+ import { paramCase as paramCase6 } from "change-case";
5369
5727
  var StackClient = class {
5370
5728
  constructor(app, account, region, credentials) {
5371
5729
  this.app = app;
@@ -5398,7 +5756,7 @@ var StackClient = class {
5398
5756
  };
5399
5757
  }
5400
5758
  stackName(stackName) {
5401
- return paramCase5(`${this.app.name}-${stackName}`);
5759
+ return paramCase6(`${this.app.name}-${stackName}`);
5402
5760
  }
5403
5761
  tags(stack) {
5404
5762
  const tags = [];
@@ -5411,7 +5769,8 @@ var StackClient = class {
5411
5769
  debug("Upload the", style.info(stack.name), "stack to awsless assets bucket");
5412
5770
  const client = new S3Client({
5413
5771
  credentials: this.credentials,
5414
- region: stack.region
5772
+ region: stack.region,
5773
+ maxAttempts: 5
5415
5774
  });
5416
5775
  await client.send(new PutObjectCommand({
5417
5776
  Bucket: this.assetBucketName,
@@ -5831,7 +6190,8 @@ import { GetObjectCommand, ObjectCannedACL as ObjectCannedACL2, PutObjectCommand
5831
6190
  var assetPublisher = (config, app) => {
5832
6191
  const client = new S3Client2({
5833
6192
  credentials: config.credentials,
5834
- region: config.region
6193
+ region: config.region,
6194
+ maxAttempts: 5
5835
6195
  });
5836
6196
  return async (term) => {
5837
6197
  const done = term.out.write(loadingDialog("Publishing stack assets to AWS..."));