@awsless/awsless 0.0.23 → 0.0.25

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.cjs CHANGED
@@ -183,7 +183,9 @@ var getAtt = (logicalId, attr) => {
183
183
  return { "Fn::GetAtt": [logicalId, attr] };
184
184
  };
185
185
  var importValue = (name) => {
186
- return { "Fn::ImportValue": name };
186
+ return new Lazy((stack) => ({
187
+ "Fn::ImportValue": `${stack.app.name}-${name}`
188
+ }));
187
189
  };
188
190
  var formatLogicalId = (id) => {
189
191
  return (0, import_change_case2.pascalCase)(id).replaceAll("_", "");
@@ -453,10 +455,18 @@ var Stack = class {
453
455
  this.name = name;
454
456
  this.region = region;
455
457
  }
458
+ parent;
456
459
  exports = /* @__PURE__ */ new Map();
457
460
  resources = /* @__PURE__ */ new Set();
458
461
  tags = /* @__PURE__ */ new Map();
459
462
  assets = /* @__PURE__ */ new Set();
463
+ get app() {
464
+ return this.parent;
465
+ }
466
+ setApp(app) {
467
+ this.parent = app;
468
+ return this;
469
+ }
460
470
  add(...resources) {
461
471
  for (const item of resources) {
462
472
  if (item instanceof Asset) {
@@ -530,7 +540,9 @@ var Stack = class {
530
540
  for (const [name, value] of this.exports.entries()) {
531
541
  Object.assign(outputs, {
532
542
  [formatLogicalId(name)]: {
533
- Export: { Name: name },
543
+ Export: {
544
+ Name: `${this.app?.name || "default"}-${name}`
545
+ },
534
546
  Value: value
535
547
  }
536
548
  });
@@ -2275,6 +2287,9 @@ var Definition = class extends Asset {
2275
2287
  }));
2276
2288
  const defs = (0, import_merge.mergeTypeDefs)(schemas);
2277
2289
  const schema2 = (0, import_graphql.print)(defs);
2290
+ if (schema2.length === 0) {
2291
+ throw new Error(`Graphql schema definition can't be empty. [${this.id}]`);
2292
+ }
2278
2293
  const size = Buffer.from(schema2, "utf8").byteLength;
2279
2294
  await write("schema.gql", schema2);
2280
2295
  this.schema = schema2;
@@ -2940,6 +2955,7 @@ var Vpc = class extends Resource {
2940
2955
  constructor(logicalId, props) {
2941
2956
  super("AWS::EC2::VPC", logicalId);
2942
2957
  this.props = props;
2958
+ this.tag("Name", props.name);
2943
2959
  }
2944
2960
  get id() {
2945
2961
  return ref(this.logicalId);
@@ -3102,6 +3118,7 @@ var vpcPlugin = definePlugin({
3102
3118
  // }),
3103
3119
  onApp({ config, bootstrap: bootstrap2 }) {
3104
3120
  const vpc = new Vpc("main", {
3121
+ name: config.name,
3105
3122
  cidrBlock: Peer.ipv4("10.0.0.0/16")
3106
3123
  });
3107
3124
  const privateRouteTable = new RouteTable("private", {
@@ -3831,7 +3848,10 @@ var App = class {
3831
3848
  }
3832
3849
  list = /* @__PURE__ */ new Map();
3833
3850
  add(...stacks) {
3834
- stacks.forEach((stack) => this.list.set(stack.name, stack));
3851
+ stacks.forEach((stack) => {
3852
+ this.list.set(stack.name, stack);
3853
+ stack.setApp(this);
3854
+ });
3835
3855
  return this;
3836
3856
  }
3837
3857
  find(resourceType) {
@@ -4619,6 +4639,7 @@ var Renderer = class {
4619
4639
  }
4620
4640
  }
4621
4641
  }
4642
+ await this.setCursor(0, size - start);
4622
4643
  this.screen = screen;
4623
4644
  this.flushing = false;
4624
4645
  }
@@ -4803,20 +4824,26 @@ var assetBuilder = (app) => {
4803
4824
  ]);
4804
4825
  group.update((group2) => [...group2, line]);
4805
4826
  const timer = createTimer();
4806
- const data = await asset.build({
4807
- async write(file, data2) {
4808
- const fullpath = (0, import_path8.join)(directories.asset, asset.type, app.name, stack.name, asset.id, file);
4809
- const basepath = (0, import_path8.dirname)(fullpath);
4810
- await (0, import_promises6.mkdir)(basepath, { recursive: true });
4811
- await (0, import_promises6.writeFile)(fullpath, data2);
4812
- }
4813
- });
4814
- details.set({
4815
- ...data,
4816
- time: timer()
4817
- });
4818
- icon.set(style.success(symbol.success));
4819
- stop();
4827
+ try {
4828
+ const data = await asset.build({
4829
+ async write(file, data2) {
4830
+ const fullpath = (0, import_path8.join)(directories.asset, asset.type, app.name, stack.name, asset.id, file);
4831
+ const basepath = (0, import_path8.dirname)(fullpath);
4832
+ await (0, import_promises6.mkdir)(basepath, { recursive: true });
4833
+ await (0, import_promises6.writeFile)(fullpath, data2);
4834
+ }
4835
+ });
4836
+ details.set({
4837
+ ...data,
4838
+ time: timer()
4839
+ });
4840
+ icon.set(style.success(symbol.success));
4841
+ } catch (error) {
4842
+ icon.set(style.error(symbol.error));
4843
+ throw error;
4844
+ } finally {
4845
+ stop();
4846
+ }
4820
4847
  }));
4821
4848
  }));
4822
4849
  done("Done building stack assets");
@@ -4914,7 +4941,7 @@ var StackClient = class {
4914
4941
  maxWaitTime = 60 * 30;
4915
4942
  // 30 minutes
4916
4943
  maxDelay = 30;
4917
- // 30 seconds
4944
+ // 10 seconds
4918
4945
  assetBucketName;
4919
4946
  getClient(region) {
4920
4947
  return new import_client_cloudformation.CloudFormationClient({
@@ -4961,7 +4988,7 @@ var StackClient = class {
4961
4988
  async create(stack, capabilities) {
4962
4989
  debug("Create the", style.info(stack.name), "stack");
4963
4990
  const client = this.getClient(stack.region);
4964
- await client.send(new import_client_cloudformation.CreateStackCommand({
4991
+ const result = await client.send(new import_client_cloudformation.CreateStackCommand({
4965
4992
  StackName: this.stackName(stack.name),
4966
4993
  EnableTerminationProtection: false,
4967
4994
  OnFailure: import_client_cloudformation.OnFailure.DELETE,
@@ -4969,36 +4996,53 @@ var StackClient = class {
4969
4996
  Tags: this.tags(stack),
4970
4997
  ...this.templateProp(stack)
4971
4998
  }));
4972
- await (0, import_client_cloudformation.waitUntilStackCreateComplete)({
4973
- client,
4974
- maxWaitTime: this.maxWaitTime,
4975
- maxDelay: this.maxDelay
4976
- }, {
4977
- StackName: this.stackName(stack.name)
4978
- });
4999
+ try {
5000
+ await (0, import_client_cloudformation.waitUntilStackCreateComplete)({
5001
+ client,
5002
+ maxWaitTime: this.maxWaitTime,
5003
+ maxDelay: this.maxDelay
5004
+ }, {
5005
+ StackName: result.StackId
5006
+ });
5007
+ } catch (_) {
5008
+ const reason = await this.getFailureReason(
5009
+ result.StackId,
5010
+ stack.region
5011
+ );
5012
+ throw new Error(reason);
5013
+ }
4979
5014
  }
4980
5015
  async update(stack, capabilities) {
4981
5016
  debug("Update the", style.info(stack.name), "stack");
4982
5017
  const client = this.getClient(stack.region);
5018
+ let result;
4983
5019
  try {
4984
- await client.send(new import_client_cloudformation.UpdateStackCommand({
5020
+ result = await client.send(new import_client_cloudformation.UpdateStackCommand({
4985
5021
  StackName: this.stackName(stack.name),
4986
5022
  Capabilities: capabilities,
4987
5023
  Tags: this.tags(stack),
4988
5024
  ...this.templateProp(stack)
4989
5025
  }));
5026
+ } catch (error) {
5027
+ if (error instanceof Error && error.name === "ValidationError" && error.message.toLowerCase().includes("no updates")) {
5028
+ return;
5029
+ }
5030
+ throw error;
5031
+ }
5032
+ try {
4990
5033
  await (0, import_client_cloudformation.waitUntilStackUpdateComplete)({
4991
5034
  client,
4992
5035
  maxWaitTime: this.maxWaitTime,
4993
5036
  maxDelay: this.maxDelay
4994
5037
  }, {
4995
- StackName: this.stackName(stack.name)
5038
+ StackName: result.StackId
4996
5039
  });
4997
5040
  } catch (error) {
4998
- if (error instanceof Error && error.name === "ValidationError" && error.message.toLowerCase().includes("no updates")) {
4999
- return;
5000
- }
5001
- throw error;
5041
+ const reason = await this.getFailureReason(
5042
+ result.StackId,
5043
+ stack.region
5044
+ );
5045
+ throw new Error(reason);
5002
5046
  }
5003
5047
  }
5004
5048
  async validate(stack) {
@@ -5079,13 +5123,44 @@ var StackClient = class {
5079
5123
  await client.send(new import_client_cloudformation.DeleteStackCommand({
5080
5124
  StackName: this.stackName(name)
5081
5125
  }));
5082
- await (0, import_client_cloudformation.waitUntilStackDeleteComplete)({
5083
- client,
5084
- maxWaitTime: this.maxWaitTime,
5085
- maxDelay: this.maxDelay
5086
- }, {
5087
- StackName: this.stackName(name)
5088
- });
5126
+ try {
5127
+ await (0, import_client_cloudformation.waitUntilStackDeleteComplete)({
5128
+ client,
5129
+ maxWaitTime: this.maxWaitTime,
5130
+ maxDelay: this.maxDelay
5131
+ }, {
5132
+ StackName: this.stackName(name)
5133
+ });
5134
+ } catch (_) {
5135
+ const reason = await this.getFailureReason(name, region);
5136
+ throw new Error(reason);
5137
+ }
5138
+ }
5139
+ async getFailureReason(name, region) {
5140
+ const client = this.getClient(region);
5141
+ const result = await client.send(await new import_client_cloudformation.DescribeStackEventsCommand({
5142
+ StackName: name
5143
+ }));
5144
+ const failureStatuses = [
5145
+ "UPDATE_ROLLBACK_IN_PROGRESS",
5146
+ "CREATE_FAILED",
5147
+ "UPDATE_FAILED",
5148
+ "DELETE_FAILED"
5149
+ ];
5150
+ let reason = "Unknown failure reason";
5151
+ for (const event of result.StackEvents || []) {
5152
+ if (event.ResourceStatusReason?.toLowerCase() === "user initiated") {
5153
+ break;
5154
+ }
5155
+ if (failureStatuses.includes(event.ResourceStatus || "") && event.ResourceStatusReason) {
5156
+ reason = [
5157
+ `Logical ID: ${event.LogicalResourceId}`,
5158
+ `Type: ${event.ResourceType}`,
5159
+ `Reason: ${event.ResourceStatusReason}`
5160
+ ].join("\n");
5161
+ }
5162
+ }
5163
+ return reason;
5089
5164
  }
5090
5165
  };
5091
5166
 
package/dist/bin.js CHANGED
@@ -163,7 +163,9 @@ var getAtt = (logicalId, attr) => {
163
163
  return { "Fn::GetAtt": [logicalId, attr] };
164
164
  };
165
165
  var importValue = (name) => {
166
- return { "Fn::ImportValue": name };
166
+ return new Lazy((stack) => ({
167
+ "Fn::ImportValue": `${stack.app.name}-${name}`
168
+ }));
167
169
  };
168
170
  var formatLogicalId = (id) => {
169
171
  return pascalCase(id).replaceAll("_", "");
@@ -433,10 +435,18 @@ var Stack = class {
433
435
  this.name = name;
434
436
  this.region = region;
435
437
  }
438
+ parent;
436
439
  exports = /* @__PURE__ */ new Map();
437
440
  resources = /* @__PURE__ */ new Set();
438
441
  tags = /* @__PURE__ */ new Map();
439
442
  assets = /* @__PURE__ */ new Set();
443
+ get app() {
444
+ return this.parent;
445
+ }
446
+ setApp(app) {
447
+ this.parent = app;
448
+ return this;
449
+ }
440
450
  add(...resources) {
441
451
  for (const item of resources) {
442
452
  if (item instanceof Asset) {
@@ -510,7 +520,9 @@ var Stack = class {
510
520
  for (const [name, value] of this.exports.entries()) {
511
521
  Object.assign(outputs, {
512
522
  [formatLogicalId(name)]: {
513
- Export: { Name: name },
523
+ Export: {
524
+ Name: `${this.app?.name || "default"}-${name}`
525
+ },
514
526
  Value: value
515
527
  }
516
528
  });
@@ -2252,6 +2264,9 @@ var Definition = class extends Asset {
2252
2264
  }));
2253
2265
  const defs = mergeTypeDefs(schemas);
2254
2266
  const schema2 = print(defs);
2267
+ if (schema2.length === 0) {
2268
+ throw new Error(`Graphql schema definition can't be empty. [${this.id}]`);
2269
+ }
2255
2270
  const size = Buffer.from(schema2, "utf8").byteLength;
2256
2271
  await write("schema.gql", schema2);
2257
2272
  this.schema = schema2;
@@ -2917,6 +2932,7 @@ var Vpc = class extends Resource {
2917
2932
  constructor(logicalId, props) {
2918
2933
  super("AWS::EC2::VPC", logicalId);
2919
2934
  this.props = props;
2935
+ this.tag("Name", props.name);
2920
2936
  }
2921
2937
  get id() {
2922
2938
  return ref(this.logicalId);
@@ -3079,6 +3095,7 @@ var vpcPlugin = definePlugin({
3079
3095
  // }),
3080
3096
  onApp({ config, bootstrap: bootstrap2 }) {
3081
3097
  const vpc = new Vpc("main", {
3098
+ name: config.name,
3082
3099
  cidrBlock: Peer.ipv4("10.0.0.0/16")
3083
3100
  });
3084
3101
  const privateRouteTable = new RouteTable("private", {
@@ -3808,7 +3825,10 @@ var App = class {
3808
3825
  }
3809
3826
  list = /* @__PURE__ */ new Map();
3810
3827
  add(...stacks) {
3811
- stacks.forEach((stack) => this.list.set(stack.name, stack));
3828
+ stacks.forEach((stack) => {
3829
+ this.list.set(stack.name, stack);
3830
+ stack.setApp(this);
3831
+ });
3812
3832
  return this;
3813
3833
  }
3814
3834
  find(resourceType) {
@@ -4596,6 +4616,7 @@ var Renderer = class {
4596
4616
  }
4597
4617
  }
4598
4618
  }
4619
+ await this.setCursor(0, size - start);
4599
4620
  this.screen = screen;
4600
4621
  this.flushing = false;
4601
4622
  }
@@ -4780,20 +4801,26 @@ var assetBuilder = (app) => {
4780
4801
  ]);
4781
4802
  group.update((group2) => [...group2, line]);
4782
4803
  const timer = createTimer();
4783
- const data = await asset.build({
4784
- async write(file, data2) {
4785
- const fullpath = join4(directories.asset, asset.type, app.name, stack.name, asset.id, file);
4786
- const basepath = dirname3(fullpath);
4787
- await mkdir2(basepath, { recursive: true });
4788
- await writeFile2(fullpath, data2);
4789
- }
4790
- });
4791
- details.set({
4792
- ...data,
4793
- time: timer()
4794
- });
4795
- icon.set(style.success(symbol.success));
4796
- stop();
4804
+ try {
4805
+ const data = await asset.build({
4806
+ async write(file, data2) {
4807
+ const fullpath = join4(directories.asset, asset.type, app.name, stack.name, asset.id, file);
4808
+ const basepath = dirname3(fullpath);
4809
+ await mkdir2(basepath, { recursive: true });
4810
+ await writeFile2(fullpath, data2);
4811
+ }
4812
+ });
4813
+ details.set({
4814
+ ...data,
4815
+ time: timer()
4816
+ });
4817
+ icon.set(style.success(symbol.success));
4818
+ } catch (error) {
4819
+ icon.set(style.error(symbol.error));
4820
+ throw error;
4821
+ } finally {
4822
+ stop();
4823
+ }
4797
4824
  }));
4798
4825
  }));
4799
4826
  done("Done building stack assets");
@@ -4877,7 +4904,7 @@ var shouldDeployBootstrap = async (client, stack) => {
4877
4904
  };
4878
4905
 
4879
4906
  // src/formation/client.ts
4880
- import { CloudFormationClient, CreateStackCommand, DeleteStackCommand, DescribeStacksCommand, GetTemplateCommand, OnFailure, TemplateStage, UpdateStackCommand, ValidateTemplateCommand, waitUntilStackCreateComplete, waitUntilStackDeleteComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
4907
+ import { CloudFormationClient, CreateStackCommand, DeleteStackCommand, DescribeStackEventsCommand, DescribeStacksCommand, GetTemplateCommand, OnFailure, TemplateStage, UpdateStackCommand, ValidateTemplateCommand, waitUntilStackCreateComplete, waitUntilStackDeleteComplete, waitUntilStackUpdateComplete } from "@aws-sdk/client-cloudformation";
4881
4908
  import { S3Client, PutObjectCommand, ObjectCannedACL, StorageClass } from "@aws-sdk/client-s3";
4882
4909
  import { paramCase as paramCase4 } from "change-case";
4883
4910
  var StackClient = class {
@@ -4891,7 +4918,7 @@ var StackClient = class {
4891
4918
  maxWaitTime = 60 * 30;
4892
4919
  // 30 minutes
4893
4920
  maxDelay = 30;
4894
- // 30 seconds
4921
+ // 10 seconds
4895
4922
  assetBucketName;
4896
4923
  getClient(region) {
4897
4924
  return new CloudFormationClient({
@@ -4938,7 +4965,7 @@ var StackClient = class {
4938
4965
  async create(stack, capabilities) {
4939
4966
  debug("Create the", style.info(stack.name), "stack");
4940
4967
  const client = this.getClient(stack.region);
4941
- await client.send(new CreateStackCommand({
4968
+ const result = await client.send(new CreateStackCommand({
4942
4969
  StackName: this.stackName(stack.name),
4943
4970
  EnableTerminationProtection: false,
4944
4971
  OnFailure: OnFailure.DELETE,
@@ -4946,36 +4973,53 @@ var StackClient = class {
4946
4973
  Tags: this.tags(stack),
4947
4974
  ...this.templateProp(stack)
4948
4975
  }));
4949
- await waitUntilStackCreateComplete({
4950
- client,
4951
- maxWaitTime: this.maxWaitTime,
4952
- maxDelay: this.maxDelay
4953
- }, {
4954
- StackName: this.stackName(stack.name)
4955
- });
4976
+ try {
4977
+ await waitUntilStackCreateComplete({
4978
+ client,
4979
+ maxWaitTime: this.maxWaitTime,
4980
+ maxDelay: this.maxDelay
4981
+ }, {
4982
+ StackName: result.StackId
4983
+ });
4984
+ } catch (_) {
4985
+ const reason = await this.getFailureReason(
4986
+ result.StackId,
4987
+ stack.region
4988
+ );
4989
+ throw new Error(reason);
4990
+ }
4956
4991
  }
4957
4992
  async update(stack, capabilities) {
4958
4993
  debug("Update the", style.info(stack.name), "stack");
4959
4994
  const client = this.getClient(stack.region);
4995
+ let result;
4960
4996
  try {
4961
- await client.send(new UpdateStackCommand({
4997
+ result = await client.send(new UpdateStackCommand({
4962
4998
  StackName: this.stackName(stack.name),
4963
4999
  Capabilities: capabilities,
4964
5000
  Tags: this.tags(stack),
4965
5001
  ...this.templateProp(stack)
4966
5002
  }));
5003
+ } catch (error) {
5004
+ if (error instanceof Error && error.name === "ValidationError" && error.message.toLowerCase().includes("no updates")) {
5005
+ return;
5006
+ }
5007
+ throw error;
5008
+ }
5009
+ try {
4967
5010
  await waitUntilStackUpdateComplete({
4968
5011
  client,
4969
5012
  maxWaitTime: this.maxWaitTime,
4970
5013
  maxDelay: this.maxDelay
4971
5014
  }, {
4972
- StackName: this.stackName(stack.name)
5015
+ StackName: result.StackId
4973
5016
  });
4974
5017
  } catch (error) {
4975
- if (error instanceof Error && error.name === "ValidationError" && error.message.toLowerCase().includes("no updates")) {
4976
- return;
4977
- }
4978
- throw error;
5018
+ const reason = await this.getFailureReason(
5019
+ result.StackId,
5020
+ stack.region
5021
+ );
5022
+ throw new Error(reason);
4979
5023
  }
4980
5024
  }
4981
5025
  async validate(stack) {
@@ -5056,13 +5100,44 @@ var StackClient = class {
5056
5100
  await client.send(new DeleteStackCommand({
5057
5101
  StackName: this.stackName(name)
5058
5102
  }));
5059
- await waitUntilStackDeleteComplete({
5060
- client,
5061
- maxWaitTime: this.maxWaitTime,
5062
- maxDelay: this.maxDelay
5063
- }, {
5064
- StackName: this.stackName(name)
5065
- });
5103
+ try {
5104
+ await waitUntilStackDeleteComplete({
5105
+ client,
5106
+ maxWaitTime: this.maxWaitTime,
5107
+ maxDelay: this.maxDelay
5108
+ }, {
5109
+ StackName: this.stackName(name)
5110
+ });
5111
+ } catch (_) {
5112
+ const reason = await this.getFailureReason(name, region);
5113
+ throw new Error(reason);
5114
+ }
5115
+ }
5116
+ async getFailureReason(name, region) {
5117
+ const client = this.getClient(region);
5118
+ const result = await client.send(await new DescribeStackEventsCommand({
5119
+ StackName: name
5120
+ }));
5121
+ const failureStatuses = [
5122
+ "UPDATE_ROLLBACK_IN_PROGRESS",
5123
+ "CREATE_FAILED",
5124
+ "UPDATE_FAILED",
5125
+ "DELETE_FAILED"
5126
+ ];
5127
+ let reason = "Unknown failure reason";
5128
+ for (const event of result.StackEvents || []) {
5129
+ if (event.ResourceStatusReason?.toLowerCase() === "user initiated") {
5130
+ break;
5131
+ }
5132
+ if (failureStatuses.includes(event.ResourceStatus || "") && event.ResourceStatusReason) {
5133
+ reason = [
5134
+ `Logical ID: ${event.LogicalResourceId}`,
5135
+ `Type: ${event.ResourceType}`,
5136
+ `Reason: ${event.ResourceStatusReason}`
5137
+ ].join("\n");
5138
+ }
5139
+ }
5140
+ return reason;
5066
5141
  }
5067
5142
  };
5068
5143
 
package/dist/index.d.ts CHANGED
@@ -2320,11 +2320,14 @@ type ConstructorOf<C> = {
2320
2320
  declare class Stack {
2321
2321
  readonly name: string;
2322
2322
  readonly region: Region;
2323
+ private parent?;
2323
2324
  readonly exports: Map<string, string>;
2324
2325
  readonly resources: Set<Resource>;
2325
2326
  readonly tags: Map<string, string>;
2326
2327
  readonly assets: Set<Asset>;
2327
2328
  constructor(name: string, region: Region);
2329
+ get app(): App | undefined;
2330
+ setApp(app: App): this;
2328
2331
  add(...resources: (Resource | Asset | Group)[]): this;
2329
2332
  export(name: string, value: string): this;
2330
2333
  get(name: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsless/awsless",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {