@awsless/awsless 0.0.19 → 0.0.20

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
@@ -191,6 +191,12 @@ var formatLogicalId = (id) => {
191
191
  var formatName = (name) => {
192
192
  return (0, import_change_case2.paramCase)(name);
193
193
  };
194
+ var formatArn = (props) => {
195
+ return sub("arn:${AWS::Partition}:${service}:${AWS::Region}:${AWS::AccountId}:${resource}${seperator}${resourceName}", {
196
+ seperator: "/",
197
+ ...props
198
+ });
199
+ };
194
200
 
195
201
  // src/formation/resource.ts
196
202
  var Resource = class {
@@ -200,13 +206,19 @@ var Resource = class {
200
206
  this.logicalId = formatLogicalId(`${logicalId}-${type.replace(/^AWS::/, "")}`);
201
207
  }
202
208
  logicalId;
209
+ tags = /* @__PURE__ */ new Map();
203
210
  deps = /* @__PURE__ */ new Set();
211
+ stack;
204
212
  dependsOn(...dependencies) {
205
213
  for (const dependency of dependencies) {
206
214
  this.deps.add(dependency);
207
215
  }
208
216
  return this;
209
217
  }
218
+ tag(key, value) {
219
+ this.tags.set(key, value);
220
+ return this;
221
+ }
210
222
  attr(name, value) {
211
223
  if (typeof value === "undefined") {
212
224
  return {};
@@ -215,12 +227,41 @@ var Resource = class {
215
227
  [name]: value
216
228
  };
217
229
  }
230
+ setStack(stack) {
231
+ this.stack = stack;
232
+ return this;
233
+ }
234
+ ref() {
235
+ return this.getAtt("ref");
236
+ }
237
+ getAtt(attr) {
238
+ return new Lazy((stack) => {
239
+ if (!this.stack) {
240
+ throw new TypeError("Resource stack not defined before building template");
241
+ }
242
+ const value = attr === "ref" ? ref(this.logicalId) : getAtt(this.logicalId, attr);
243
+ if (stack === this.stack) {
244
+ return value;
245
+ }
246
+ const name = `${this.stack.name}-${this.logicalId}-${attr}`;
247
+ this.stack.export(name, value);
248
+ return this.stack.import(name);
249
+ });
250
+ }
218
251
  toJSON() {
219
252
  return {
220
253
  [this.logicalId]: {
221
254
  Type: this.type,
222
255
  DependsOn: [...this.deps].map((dep) => dep.logicalId),
223
- Properties: this.properties()
256
+ Properties: {
257
+ ...this.tags.size ? {
258
+ Tags: Array.from(this.tags.entries()).map(([key, value]) => ({
259
+ Key: key,
260
+ Value: value
261
+ }))
262
+ } : {},
263
+ ...this.properties()
264
+ }
224
265
  }
225
266
  };
226
267
  }
@@ -230,6 +271,11 @@ var Group = class {
230
271
  this.children = children;
231
272
  }
232
273
  };
274
+ var Lazy = class {
275
+ constructor(callback) {
276
+ this.callback = callback;
277
+ }
278
+ };
233
279
 
234
280
  // src/formation/resource/iam/inline-policy.ts
235
281
  var InlinePolicy = class {
@@ -335,6 +381,7 @@ var Function = class extends Resource {
335
381
  this.policy = policy;
336
382
  this.name = formatName(this.props.name || logicalId);
337
383
  this.environmentVariables = props.environment ? { ...props.environment } : {};
384
+ this.tag("name", this.name);
338
385
  }
339
386
  name;
340
387
  role;
@@ -348,11 +395,15 @@ var Function = class extends Resource {
348
395
  this.environmentVariables[name] = value;
349
396
  return this;
350
397
  }
398
+ setVpc(vpc) {
399
+ this.props.vpc = vpc;
400
+ return this;
401
+ }
351
402
  get id() {
352
- return ref(this.logicalId);
403
+ return this.ref();
353
404
  }
354
405
  get arn() {
355
- return getAtt(this.logicalId, "Arn");
406
+ return this.getAtt("Arn");
356
407
  }
357
408
  get permissions() {
358
409
  return {
@@ -360,7 +411,14 @@ var Function = class extends Resource {
360
411
  "lambda:InvokeFunction",
361
412
  "lambda:InvokeAsync"
362
413
  ],
363
- resources: [this.arn]
414
+ resources: [
415
+ formatArn({
416
+ service: "lambda",
417
+ resource: "function",
418
+ resourceName: this.name,
419
+ seperator: ":"
420
+ })
421
+ ]
364
422
  };
365
423
  }
366
424
  properties() {
@@ -376,6 +434,12 @@ var Function = class extends Resource {
376
434
  EphemeralStorage: {
377
435
  Size: this.props.ephemeralStorageSize?.toMegaBytes() ?? 512
378
436
  },
437
+ ...this.props.vpc ? {
438
+ VpcConfig: {
439
+ SecurityGroupIds: this.props.vpc.securityGroupIds,
440
+ SubnetIds: this.props.vpc.subnetIds
441
+ }
442
+ } : {},
379
443
  Environment: {
380
444
  Variables: this.environmentVariables
381
445
  }
@@ -400,6 +464,7 @@ var Stack = class {
400
464
  } else {
401
465
  this.add(...item.children);
402
466
  if (item instanceof Resource) {
467
+ item.setStack(this);
403
468
  this.resources.add(item);
404
469
  }
405
470
  }
@@ -443,8 +508,24 @@ var Stack = class {
443
508
  toJSON() {
444
509
  const resources = {};
445
510
  const outputs = {};
511
+ const walk = (object) => {
512
+ for (const [key, value] of Object.entries(object)) {
513
+ if (!object.hasOwnProperty(key)) {
514
+ continue;
515
+ }
516
+ if (value instanceof Lazy) {
517
+ object[key] = value.callback(this);
518
+ continue;
519
+ }
520
+ if (typeof value === "object" && value !== null) {
521
+ walk(value);
522
+ }
523
+ }
524
+ };
446
525
  for (const resource of this) {
447
- Object.assign(resources, resource.toJSON());
526
+ const json2 = resource.toJSON();
527
+ walk(json2);
528
+ Object.assign(resources, json2);
448
529
  }
449
530
  for (const [name, value] of this.exports.entries()) {
450
531
  Object.assign(outputs, {
@@ -512,54 +593,33 @@ var toStack = ({ config, app, stackConfig, bootstrap: bootstrap2, usEastBootstra
512
593
  }
513
594
  return {
514
595
  stack,
515
- depends: stackConfig.depends
596
+ bindings
597
+ // depends: stackConfig.depends,
516
598
  };
517
599
  };
518
600
 
519
601
  // src/util/deployment.ts
520
- var createDependencyTree = (stacks) => {
602
+ var createDeploymentLine = (stacks) => {
521
603
  const list3 = stacks.map(({ stack, config }) => ({
522
604
  stack,
523
605
  depends: config?.depends?.map((dep) => dep.name) || []
524
606
  }));
525
- const findChildren = (list4, parents) => {
526
- const children = [];
527
- const rests = [];
528
- for (const item of list4) {
529
- const isChild = item.depends.filter((dep) => !parents.includes(dep)).length === 0;
530
- if (isChild) {
531
- children.push(item);
532
- } else {
533
- rests.push(item);
607
+ const line = [];
608
+ const deps = [];
609
+ let limit = 10;
610
+ while (deps.length < list3.length) {
611
+ const local = [];
612
+ for (const { stack, depends } of list3) {
613
+ if (!deps.includes(stack.name) && depends.filter((dep) => !deps.includes(dep)).length === 0) {
614
+ local.push(stack);
534
615
  }
535
616
  }
536
- if (!rests.length) {
537
- return children.map(({ stack }) => ({
538
- stack,
539
- children: []
540
- }));
617
+ if (limit-- <= 0) {
618
+ throw new Error(`Circular stack dependencies arn't allowed.`);
541
619
  }
542
- return children.map(({ stack }) => {
543
- return {
544
- stack,
545
- children: findChildren(rests, [...parents, stack.name])
546
- };
547
- });
548
- };
549
- return findChildren(list3, []);
550
- };
551
- var createDeploymentLine = (stacks) => {
552
- const line = [];
553
- const walk = (stacks2, level) => {
554
- stacks2.forEach((node) => {
555
- if (!line[level]) {
556
- line[level] = [];
557
- }
558
- line[level].push(node.stack);
559
- walk(node.children, level + 1);
560
- });
561
- };
562
- walk(stacks, 0);
620
+ deps.push(...local.map((stack) => stack.name));
621
+ line.push(local);
622
+ }
563
623
  return line;
564
624
  };
565
625
 
@@ -959,8 +1019,12 @@ var RuntimeSchema = import_zod6.z.enum([
959
1019
  var FunctionSchema = import_zod6.z.union([
960
1020
  LocalFileSchema,
961
1021
  import_zod6.z.object({
962
- /** The file path ofthe function code. */
1022
+ /** The file path of the function code. */
963
1023
  file: LocalFileSchema,
1024
+ /** Put the function inside your global VPC.
1025
+ * @default false
1026
+ */
1027
+ vpc: import_zod6.z.boolean().optional(),
964
1028
  /** The amount of time that Lambda allows a function to run before stopping it.
965
1029
  * You can specify a size value from 1 second to 15 minutes.
966
1030
  * @default '10 seconds'
@@ -1010,6 +1074,10 @@ var FunctionSchema = import_zod6.z.union([
1010
1074
  var schema = import_zod6.z.object({
1011
1075
  defaults: import_zod6.z.object({
1012
1076
  function: import_zod6.z.object({
1077
+ /** Put the function inside your global VPC.
1078
+ * @default false
1079
+ */
1080
+ vpc: import_zod6.z.boolean().default(false),
1013
1081
  /** The amount of time that Lambda allows a function to run before stopping it.
1014
1082
  * You can specify a size value from 1 second to 15 minutes.
1015
1083
  * @default '10 seconds'
@@ -1101,12 +1169,36 @@ var toLambdaFunction = (ctx, id, fileOrProps) => {
1101
1169
  const lambda = new Function(id, {
1102
1170
  name: `${config.name}-${stack.name}-${id}`,
1103
1171
  code: Code.fromFile(id, props.file),
1104
- ...props
1172
+ ...props,
1173
+ vpc: void 0
1105
1174
  });
1106
1175
  lambda.addEnvironment("APP", config.name).addEnvironment("STAGE", config.stage).addEnvironment("STACK", stack.name);
1176
+ if (props.vpc) {
1177
+ lambda.setVpc({
1178
+ securityGroupIds: [
1179
+ ctx.bootstrap.import(`vpc-security-group-id`)
1180
+ ],
1181
+ subnetIds: [
1182
+ ctx.bootstrap.import(`public-subnet-1`),
1183
+ ctx.bootstrap.import(`public-subnet-2`)
1184
+ ]
1185
+ }).addPermissions({
1186
+ actions: [
1187
+ "ec2:CreateNetworkInterface",
1188
+ "ec2:DescribeNetworkInterfaces",
1189
+ "ec2:DeleteNetworkInterface",
1190
+ "ec2:AssignPrivateIpAddresses",
1191
+ "ec2:UnassignPrivateIpAddresses"
1192
+ ],
1193
+ resources: ["*"]
1194
+ });
1195
+ }
1107
1196
  if (props.runtime.startsWith("nodejs")) {
1108
1197
  lambda.addEnvironment("AWS_NODEJS_CONNECTION_REUSE_ENABLED", "1");
1109
1198
  }
1199
+ ctx.bind((other) => {
1200
+ other.addPermissions(lambda.permissions);
1201
+ });
1110
1202
  return lambda;
1111
1203
  };
1112
1204
 
@@ -1230,6 +1322,7 @@ var Queue = class extends Resource {
1230
1322
  super("AWS::SQS::Queue", logicalId);
1231
1323
  this.props = props;
1232
1324
  this.name = formatName(this.props.name || logicalId);
1325
+ this.tag("name", this.name);
1233
1326
  }
1234
1327
  name;
1235
1328
  setDeadLetter(arn) {
@@ -1250,7 +1343,13 @@ var Queue = class extends Resource {
1250
1343
  "sqs:GetQueueUrl",
1251
1344
  "sqs:GetQueueAttributes"
1252
1345
  ],
1253
- resources: [this.arn]
1346
+ resources: [
1347
+ formatArn({
1348
+ service: "sqs",
1349
+ resource: "queue",
1350
+ resourceName: this.name
1351
+ })
1352
+ ]
1254
1353
  };
1255
1354
  }
1256
1355
  properties() {
@@ -1477,6 +1576,7 @@ var Table = class extends Resource {
1477
1576
  this.props = props;
1478
1577
  this.name = formatName(this.props.name || logicalId);
1479
1578
  this.indexes = { ...this.props.indexes || {} };
1579
+ this.tag("name", this.name);
1480
1580
  }
1481
1581
  name;
1482
1582
  indexes;
@@ -1509,7 +1609,13 @@ var Table = class extends Resource {
1509
1609
  "dynamodb:Query",
1510
1610
  "dynamodb:Scan"
1511
1611
  ],
1512
- resources: [this.arn]
1612
+ resources: [
1613
+ formatArn({
1614
+ service: "dynamodb",
1615
+ resource: "table",
1616
+ resourceName: this.name
1617
+ })
1618
+ ]
1513
1619
  };
1514
1620
  }
1515
1621
  attributeDefinitions() {
@@ -1743,6 +1849,7 @@ var Bucket = class extends Resource {
1743
1849
  super("AWS::S3::Bucket", logicalId);
1744
1850
  this.props = props;
1745
1851
  this.name = formatName(this.props.name || logicalId);
1852
+ this.tag("name", this.name);
1746
1853
  }
1747
1854
  name;
1748
1855
  get arn() {
@@ -1759,7 +1866,13 @@ var Bucket = class extends Resource {
1759
1866
  "s3:GetQueueUrl",
1760
1867
  "s3:GetQueueAttributes"
1761
1868
  ],
1762
- resources: [this.arn]
1869
+ resources: [
1870
+ formatArn({
1871
+ service: "s3",
1872
+ resource: "bucket",
1873
+ resourceName: this.name
1874
+ })
1875
+ ]
1763
1876
  };
1764
1877
  }
1765
1878
  properties() {
@@ -1812,6 +1925,7 @@ var Topic = class extends Resource {
1812
1925
  super("AWS::SNS::Topic", logicalId);
1813
1926
  this.props = props;
1814
1927
  this.name = formatName(this.props.name || logicalId);
1928
+ this.tag("name", this.name);
1815
1929
  }
1816
1930
  name;
1817
1931
  get arn() {
@@ -1820,7 +1934,13 @@ var Topic = class extends Resource {
1820
1934
  get permissions() {
1821
1935
  return {
1822
1936
  actions: ["sns:Publish"],
1823
- resources: [this.arn]
1937
+ resources: [
1938
+ formatArn({
1939
+ service: "sns",
1940
+ resource: "topic",
1941
+ resourceName: this.name
1942
+ })
1943
+ ]
1824
1944
  };
1825
1945
  }
1826
1946
  properties() {
@@ -2053,6 +2173,7 @@ var GraphQLApi = class extends Resource {
2053
2173
  super("AWS::AppSync::GraphQLApi", logicalId);
2054
2174
  this.props = props;
2055
2175
  this.name = formatName(this.props.name || logicalId);
2176
+ this.tag("name", this.name);
2056
2177
  }
2057
2178
  name;
2058
2179
  lambdaAuthProviders = [];
@@ -2817,6 +2938,12 @@ var Vpc = class extends Resource {
2817
2938
  get id() {
2818
2939
  return ref(this.logicalId);
2819
2940
  }
2941
+ get defaultNetworkAcl() {
2942
+ return getAtt(this.logicalId, "DefaultNetworkAcl");
2943
+ }
2944
+ get defaultSecurityGroup() {
2945
+ return getAtt(this.logicalId, "DefaultSecurityGroup");
2946
+ }
2820
2947
  properties() {
2821
2948
  return {
2822
2949
  CidrBlock: this.props.cidrBlock.ip
@@ -2828,6 +2955,7 @@ var RouteTable = class extends Resource {
2828
2955
  super("AWS::EC2::RouteTable", logicalId);
2829
2956
  this.props = props;
2830
2957
  this.name = formatName(props.name || logicalId);
2958
+ this.tag("name", this.name);
2831
2959
  }
2832
2960
  name;
2833
2961
  get id() {
@@ -2835,11 +2963,7 @@ var RouteTable = class extends Resource {
2835
2963
  }
2836
2964
  properties() {
2837
2965
  return {
2838
- VpcId: this.props.vpcId,
2839
- Tags: [{
2840
- Key: "name",
2841
- Value: this.name
2842
- }]
2966
+ VpcId: this.props.vpcId
2843
2967
  };
2844
2968
  }
2845
2969
  };
@@ -2992,6 +3116,7 @@ var vpcPlugin = definePlugin({
2992
3116
  routeTableId: publicRouteTable.id,
2993
3117
  destination: Peer.anyIpv4()
2994
3118
  }).dependsOn(gateway, publicRouteTable);
3119
+ bootstrap2.export("vpc-security-group-id", vpc.defaultSecurityGroup);
2995
3120
  bootstrap2.export(`vpc-id`, vpc.id);
2996
3121
  bootstrap2.add(
2997
3122
  vpc,
@@ -3034,7 +3159,9 @@ var SecurityGroup = class extends Resource {
3034
3159
  constructor(logicalId, props) {
3035
3160
  super("AWS::EC2::SecurityGroup", logicalId);
3036
3161
  this.props = props;
3162
+ this.name = formatName(props.name ?? logicalId);
3037
3163
  }
3164
+ name;
3038
3165
  ingress = [];
3039
3166
  egress = [];
3040
3167
  get id() {
@@ -3059,7 +3186,7 @@ var SecurityGroup = class extends Resource {
3059
3186
  properties() {
3060
3187
  return {
3061
3188
  VpcId: this.props.vpcId,
3062
- GroupName: this.logicalId,
3189
+ GroupName: this.name,
3063
3190
  GroupDescription: this.props.description,
3064
3191
  SecurityGroupIngress: this.ingress.map((rule) => ({
3065
3192
  Description: rule.description || "",
@@ -3470,6 +3597,7 @@ var Collection = class extends Resource {
3470
3597
  super("AWS::OpenSearchServerless::Collection", logicalId);
3471
3598
  this.props = props;
3472
3599
  this.name = this.props.name || logicalId;
3600
+ this.tag("name", this.name);
3473
3601
  }
3474
3602
  name;
3475
3603
  get id() {
@@ -3481,6 +3609,18 @@ var Collection = class extends Resource {
3481
3609
  get endpoint() {
3482
3610
  return getAtt(this.logicalId, "CollectionEndpoint");
3483
3611
  }
3612
+ get permissions() {
3613
+ return {
3614
+ actions: ["aoss:APIAccessAll"],
3615
+ resources: [
3616
+ formatArn({
3617
+ service: "aoss",
3618
+ resource: "collection",
3619
+ resourceName: this.name
3620
+ })
3621
+ ]
3622
+ };
3623
+ }
3484
3624
  properties() {
3485
3625
  return {
3486
3626
  Name: this.name,
@@ -3505,10 +3645,155 @@ var searchPlugin = definePlugin({
3505
3645
  type: "search"
3506
3646
  });
3507
3647
  bind((lambda) => {
3508
- lambda.addPermissions({
3509
- actions: ["aoss:APIAccessAll"],
3510
- resources: [collection.arn]
3511
- });
3648
+ lambda.addPermissions(collection.permissions);
3649
+ });
3650
+ }
3651
+ }
3652
+ });
3653
+
3654
+ // src/plugins/cache.ts
3655
+ var import_zod19 = require("zod");
3656
+
3657
+ // src/formation/resource/memorydb/cluster.ts
3658
+ var Cluster = class extends Resource {
3659
+ constructor(logicalId, props) {
3660
+ super("AWS::MemoryDB::Cluster", logicalId);
3661
+ this.props = props;
3662
+ this.name = formatName(this.props.name || logicalId);
3663
+ this.tag("name", this.name);
3664
+ }
3665
+ name;
3666
+ get status() {
3667
+ return this.getAtt("Status");
3668
+ }
3669
+ get arn() {
3670
+ return this.getAtt("ARN");
3671
+ }
3672
+ get address() {
3673
+ return this.getAtt("ClusterEndpoint.Address");
3674
+ }
3675
+ get port() {
3676
+ return this.getAtt("ClusterEndpoint.Port");
3677
+ }
3678
+ properties() {
3679
+ return {
3680
+ ClusterName: this.name,
3681
+ ClusterEndpoint: {
3682
+ Port: this.props.port
3683
+ },
3684
+ Port: this.props.port,
3685
+ ...this.attr("Description", this.props.description),
3686
+ ACLName: this.props.aclName,
3687
+ EngineVersion: this.props.engine ?? "7.0",
3688
+ ...this.attr("SubnetGroupName", this.props.subnetGroupName),
3689
+ ...this.attr("SecurityGroupIds", this.props.securityGroupIds),
3690
+ NodeType: "db." + this.props.type,
3691
+ NumReplicasPerShard: this.props.replicasPerShard ?? 1,
3692
+ NumShards: this.props.shards ?? 1,
3693
+ TLSEnabled: this.props.tls ?? false,
3694
+ DataTiering: this.props.dataTiering ? "true" : "false",
3695
+ AutoMinorVersionUpgrade: this.props.autoMinorVersionUpgrade ?? true,
3696
+ MaintenanceWindow: this.props.maintenanceWindow ?? "Sat:02:00-Sat:05:00"
3697
+ };
3698
+ }
3699
+ };
3700
+
3701
+ // src/formation/resource/memorydb/subnet-group.ts
3702
+ var SubnetGroup = class extends Resource {
3703
+ constructor(logicalId, props) {
3704
+ super("AWS::MemoryDB::SubnetGroup", logicalId);
3705
+ this.props = props;
3706
+ this.name = formatName(this.props.name || logicalId);
3707
+ }
3708
+ name;
3709
+ get arn() {
3710
+ return getAtt(this.logicalId, "Arn");
3711
+ }
3712
+ properties() {
3713
+ return {
3714
+ SubnetGroupName: this.name,
3715
+ SubnetIds: this.props.subnetIds,
3716
+ ...this.attr("Description", this.props.description)
3717
+ };
3718
+ }
3719
+ };
3720
+
3721
+ // src/plugins/cache.ts
3722
+ var TypeSchema = import_zod19.z.enum([
3723
+ "t4g.small",
3724
+ "t4g.medium",
3725
+ "r6g.large",
3726
+ "r6g.xlarge",
3727
+ "r6g.2xlarge",
3728
+ "r6g.4xlarge",
3729
+ "r6g.8xlarge",
3730
+ "r6g.12xlarge",
3731
+ "r6g.16xlarge",
3732
+ "r6gd.xlarge",
3733
+ "r6gd.2xlarge",
3734
+ "r6gd.4xlarge",
3735
+ "r6gd.8xlarge"
3736
+ ]);
3737
+ var PortSchema = import_zod19.z.number().int().min(1).max(5e4);
3738
+ var ShardsSchema = import_zod19.z.number().int().min(0).max(100);
3739
+ var ReplicasPerShardSchema = import_zod19.z.number().int().min(0).max(5);
3740
+ var EngineSchema = import_zod19.z.enum(["7.0", "6.2"]);
3741
+ var cachePlugin = definePlugin({
3742
+ name: "cache",
3743
+ schema: import_zod19.z.object({
3744
+ stacks: import_zod19.z.object({
3745
+ /** Define the caches in your stack.
3746
+ * For access to the cache put your functions inside the global VPC.
3747
+ * @example
3748
+ * {
3749
+ * caches: {
3750
+ * CACHE_NAME: {
3751
+ * type: 't4g.small'
3752
+ * }
3753
+ * }
3754
+ * }
3755
+ */
3756
+ caches: import_zod19.z.record(
3757
+ ResourceIdSchema,
3758
+ import_zod19.z.object({
3759
+ type: TypeSchema.default("t4g.small"),
3760
+ port: PortSchema.default(6379),
3761
+ shards: ShardsSchema.default(1),
3762
+ replicasPerShard: ReplicasPerShardSchema.default(1),
3763
+ engine: EngineSchema.default("7.0"),
3764
+ dataTiering: import_zod19.z.boolean().default(false)
3765
+ })
3766
+ ).optional()
3767
+ }).array()
3768
+ }),
3769
+ onStack({ config, stack, stackConfig, bootstrap: bootstrap2, bind }) {
3770
+ for (const [id, props] of Object.entries(stackConfig.caches || {})) {
3771
+ const name = `${config.name}-${stack.name}-${id}`;
3772
+ const subnetGroup = new SubnetGroup(id, {
3773
+ name,
3774
+ subnetIds: [
3775
+ bootstrap2.import(`private-subnet-1`),
3776
+ bootstrap2.import(`private-subnet-2`)
3777
+ ]
3778
+ });
3779
+ const securityGroup = new SecurityGroup(id, {
3780
+ name,
3781
+ vpcId: bootstrap2.import(`vpc-id`),
3782
+ description: name
3783
+ });
3784
+ const port = Port.tcp(props.port);
3785
+ securityGroup.addIngressRule(Peer.anyIpv4(), port);
3786
+ securityGroup.addIngressRule(Peer.anyIpv6(), port);
3787
+ const cluster = new Cluster(id, {
3788
+ name,
3789
+ aclName: "open-access",
3790
+ securityGroupIds: [securityGroup.id],
3791
+ subnetGroupName: subnetGroup.name,
3792
+ ...props
3793
+ }).dependsOn(subnetGroup, securityGroup);
3794
+ stack.add(subnetGroup, securityGroup, cluster);
3795
+ bind((lambda) => {
3796
+ lambda.addEnvironment(`CACHE_${stack.name}_${id}_HOST`, cluster.address).addEnvironment(`CACHE_${stack.name}_${id}_PORT`, props.port.toString());
3512
3797
  });
3513
3798
  }
3514
3799
  }
@@ -3519,6 +3804,7 @@ var defaultPlugins = [
3519
3804
  extendPlugin,
3520
3805
  vpcPlugin,
3521
3806
  functionPlugin,
3807
+ cachePlugin,
3522
3808
  cronPlugin,
3523
3809
  queuePlugin,
3524
3810
  tablePlugin,
@@ -3700,7 +3986,7 @@ var toApp = async (config, filters) => {
3700
3986
  config.stacks.filter((stack) => filters.includes(stack.name))
3701
3987
  );
3702
3988
  for (const stackConfig of filterdStacks) {
3703
- const { stack } = toStack({
3989
+ const { stack, bindings: bindings2 } = toStack({
3704
3990
  config,
3705
3991
  stackConfig,
3706
3992
  bootstrap: bootstrap2,
@@ -3709,7 +3995,7 @@ var toApp = async (config, filters) => {
3709
3995
  app
3710
3996
  });
3711
3997
  app.add(stack);
3712
- stacks.push({ stack, config: stackConfig });
3998
+ stacks.push({ stack, config: stackConfig, bindings: bindings2 });
3713
3999
  }
3714
4000
  for (const plugin of plugins) {
3715
4001
  for (const stack of app.stacks) {
@@ -3731,23 +4017,31 @@ var toApp = async (config, filters) => {
3731
4017
  bind2(fn);
3732
4018
  }
3733
4019
  }
3734
- let dependencyTree = createDependencyTree(stacks);
4020
+ for (const entry of stacks) {
4021
+ for (const dep of entry.config.depends || []) {
4022
+ const depStack = stacks.find((entry2) => entry2.config.name === dep.name);
4023
+ if (!depStack) {
4024
+ throw new Error(`Stack dependency not found: ${dep.name}`);
4025
+ }
4026
+ const functions2 = entry.stack.find(Function);
4027
+ for (const bind2 of depStack.bindings) {
4028
+ for (const fn of functions2) {
4029
+ bind2(fn);
4030
+ }
4031
+ }
4032
+ }
4033
+ }
4034
+ const deploymentLine = createDeploymentLine(stacks);
3735
4035
  if (bootstrap2.size > 0) {
3736
- dependencyTree = [{
3737
- stack: bootstrap2,
3738
- children: dependencyTree
3739
- }];
4036
+ deploymentLine.unshift([bootstrap2]);
3740
4037
  }
3741
4038
  if (usEastBootstrap.size > 0) {
3742
- dependencyTree = [{
3743
- stack: usEastBootstrap,
3744
- children: dependencyTree
3745
- }];
4039
+ deploymentLine.unshift([usEastBootstrap]);
3746
4040
  }
3747
4041
  return {
3748
4042
  app,
3749
4043
  plugins,
3750
- dependencyTree
4044
+ deploymentLine
3751
4045
  };
3752
4046
  };
3753
4047
 
@@ -3771,17 +4065,17 @@ var getCredentials = (profile) => {
3771
4065
  };
3772
4066
 
3773
4067
  // src/schema/app.ts
3774
- var import_zod22 = require("zod");
4068
+ var import_zod23 = require("zod");
3775
4069
 
3776
4070
  // src/schema/stack.ts
3777
- var import_zod19 = require("zod");
3778
- var StackSchema = import_zod19.z.object({
4071
+ var import_zod20 = require("zod");
4072
+ var StackSchema = import_zod20.z.object({
3779
4073
  name: ResourceIdSchema,
3780
- depends: import_zod19.z.array(import_zod19.z.lazy(() => StackSchema)).optional()
4074
+ depends: import_zod20.z.array(import_zod20.z.lazy(() => StackSchema)).optional()
3781
4075
  });
3782
4076
 
3783
4077
  // src/schema/region.ts
3784
- var import_zod20 = require("zod");
4078
+ var import_zod21 = require("zod");
3785
4079
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
3786
4080
  var AF = ["af-south-1"];
3787
4081
  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"];
@@ -3798,45 +4092,47 @@ var regions = [
3798
4092
  ...ME,
3799
4093
  ...SA
3800
4094
  ];
3801
- var RegionSchema = import_zod20.z.enum(regions);
4095
+ var RegionSchema = import_zod21.z.enum(regions);
3802
4096
 
3803
4097
  // src/schema/plugin.ts
3804
- var import_zod21 = require("zod");
3805
- var PluginSchema = import_zod21.z.object({
3806
- name: import_zod21.z.string(),
3807
- schema: import_zod21.z.custom().optional(),
4098
+ var import_zod22 = require("zod");
4099
+ var PluginSchema = import_zod22.z.object({
4100
+ name: import_zod22.z.string(),
4101
+ schema: import_zod22.z.custom().optional(),
3808
4102
  // depends: z.array(z.lazy(() => PluginSchema)).optional(),
3809
- onApp: import_zod21.z.function().returns(import_zod21.z.void()).optional(),
3810
- onStack: import_zod21.z.function().returns(import_zod21.z.any()).optional(),
3811
- onResource: import_zod21.z.function().returns(import_zod21.z.any()).optional()
4103
+ onApp: import_zod22.z.function().returns(import_zod22.z.void()).optional(),
4104
+ onStack: import_zod22.z.function().returns(import_zod22.z.any()).optional(),
4105
+ onResource: import_zod22.z.function().returns(import_zod22.z.any()).optional()
3812
4106
  // bind: z.function().optional(),
3813
4107
  });
3814
4108
 
3815
4109
  // src/schema/app.ts
3816
- var AppSchema = import_zod22.z.object({
4110
+ var AppSchema = import_zod23.z.object({
3817
4111
  /** App name */
3818
4112
  name: ResourceIdSchema,
3819
4113
  /** The AWS region to deploy to. */
3820
4114
  region: RegionSchema,
3821
4115
  /** The AWS profile to deploy to. */
3822
- profile: import_zod22.z.string(),
4116
+ profile: import_zod23.z.string(),
3823
4117
  /** The deployment stage.
3824
4118
  * @default 'prod'
3825
4119
  */
3826
- stage: import_zod22.z.string().regex(/[a-z]+/).default("prod"),
4120
+ stage: import_zod23.z.string().regex(/[a-z]+/).default("prod"),
3827
4121
  /** Default properties. */
3828
- defaults: import_zod22.z.object({}).default({}),
4122
+ defaults: import_zod23.z.object({}).default({}),
3829
4123
  /** The application stacks. */
3830
- stacks: import_zod22.z.array(StackSchema).min(1).refine((stacks) => {
4124
+ stacks: import_zod23.z.array(StackSchema).min(1).refine((stacks) => {
3831
4125
  const unique = new Set(stacks.map((stack) => stack.name));
3832
4126
  return unique.size === stacks.length;
3833
4127
  }, "Must be an array of unique stacks"),
3834
4128
  /** Custom plugins. */
3835
- plugins: import_zod22.z.array(PluginSchema).optional()
4129
+ plugins: import_zod23.z.array(PluginSchema).optional()
3836
4130
  });
3837
4131
 
3838
4132
  // src/util/import.ts
3839
- var import_core = require("@swc/core");
4133
+ var import_rollup3 = require("rollup");
4134
+ var import_rollup_plugin_swc32 = require("rollup-plugin-swc3");
4135
+ var import_rollup_plugin_replace = __toESM(require("rollup-plugin-replace"), 1);
3840
4136
  var import_path2 = require("path");
3841
4137
  var import_promises5 = require("fs/promises");
3842
4138
 
@@ -3885,54 +4181,25 @@ var fileExist = async (file) => {
3885
4181
  };
3886
4182
 
3887
4183
  // src/util/import.ts
3888
- var resolveFileNameExtension = async (path) => {
3889
- const options = [
3890
- "",
3891
- ".ts",
3892
- ".js",
3893
- "/index.ts",
3894
- "/index.js"
3895
- ];
3896
- for (const option of options) {
3897
- const file = path.replace(/\.js$/, "") + option;
3898
- let stat;
3899
- try {
3900
- stat = await (0, import_promises5.lstat)(file);
3901
- } catch (error) {
3902
- continue;
3903
- }
3904
- if (stat.isFile()) {
3905
- return file;
3906
- }
3907
- }
3908
- throw new Error(`Failed to load file: ${path}`);
3909
- };
3910
- var resolveDir = (path) => {
3911
- return (0, import_path2.dirname)(path).replace(directories.root + "/", "");
3912
- };
3913
4184
  var importFile = async (path) => {
3914
- const load = async (file) => {
3915
- debug("Load file:", style.info(file));
3916
- let { code: code2 } = await (0, import_core.transformFile)(file, {
3917
- isModule: true
3918
- });
3919
- const path2 = (0, import_path2.dirname)(file);
3920
- const dir = resolveDir(file);
3921
- code2 = code2.replaceAll("__dirname", `"${dir}"`);
3922
- const matches = code2.match(/(import|export)\s*{\s*[a-z0-9\_\,\s\*]+\s*}\s*from\s*('|")(\.\.?[\/a-z0-9\_\-\.]+)('|");?/ig);
3923
- if (!matches)
3924
- return code2;
3925
- await Promise.all(matches?.map(async (match) => {
3926
- const parts = /('|")(\.\.?[\/a-z0-9\_\-\.]+)('|")/ig.exec(match);
3927
- const from = parts[2];
3928
- const file2 = await resolveFileNameExtension((0, import_path2.join)(path2, from));
3929
- const result = await load(file2);
3930
- code2 = code2.replace(match, result);
3931
- }));
3932
- return code2;
3933
- };
3934
- const code = await load(path);
4185
+ const bundle = await (0, import_rollup3.rollup)({
4186
+ input: path,
4187
+ plugins: [
4188
+ (0, import_rollup_plugin_replace.default)({
4189
+ __dirname: (id) => `'${(0, import_path2.dirname)(id)}'`
4190
+ }),
4191
+ (0, import_rollup_plugin_swc32.swc)({
4192
+ minify: false
4193
+ })
4194
+ ]
4195
+ });
3935
4196
  const outputFile = (0, import_path2.join)(directories.cache, "config.js");
4197
+ const result = await bundle.generate({
4198
+ format: "esm",
4199
+ exports: "default"
4200
+ });
4201
+ const output = result.output[0];
4202
+ const code = output.code;
3936
4203
  await (0, import_promises5.mkdir)(directories.cache, { recursive: true });
3937
4204
  await (0, import_promises5.writeFile)(outputFile, code);
3938
4205
  debug("Save config file:", style.info(outputFile));
@@ -4234,7 +4501,7 @@ var Renderer = class {
4234
4501
  flushing = false;
4235
4502
  screen = [];
4236
4503
  width() {
4237
- return this.output.columns;
4504
+ return this.output.columns - 1;
4238
4505
  }
4239
4506
  height() {
4240
4507
  return this.output.rows;
@@ -5072,12 +5339,73 @@ var assetPublisher = (config, app) => {
5072
5339
  };
5073
5340
  };
5074
5341
 
5342
+ // src/cli/ui/complex/deployer.ts
5343
+ var stacksDeployer = (deploymentLine) => {
5344
+ const stackNames = deploymentLine.map((line) => line.map((stack) => stack.name)).flat();
5345
+ const stackNameSize = Math.max(...stackNames.map((name) => name.length));
5346
+ return (term) => {
5347
+ const ui = {};
5348
+ term.out.gap();
5349
+ for (const i in deploymentLine) {
5350
+ const line = flexLine(
5351
+ term,
5352
+ [" "],
5353
+ [
5354
+ " ",
5355
+ style.placeholder(Number(i) + 1),
5356
+ style.placeholder(" \u2500\u2500")
5357
+ ]
5358
+ );
5359
+ term.out.write(line);
5360
+ term.out.write(br());
5361
+ for (const stack of deploymentLine[i]) {
5362
+ const icon = new Signal(" ");
5363
+ const name = new Signal(style.label.dim(stack.name));
5364
+ const status2 = new Signal(style.info.dim("waiting"));
5365
+ let stopSpinner;
5366
+ term.out.write([
5367
+ icon,
5368
+ " ",
5369
+ name,
5370
+ " ".repeat(stackNameSize - stack.name.length),
5371
+ " ",
5372
+ style.placeholder(symbol.pointerSmall),
5373
+ " ",
5374
+ status2,
5375
+ br()
5376
+ ]);
5377
+ ui[stack.name] = {
5378
+ start: (value) => {
5379
+ const [spinner, stop] = createSpinner();
5380
+ name.set(style.label(stack.name));
5381
+ icon.set(spinner);
5382
+ status2.set(style.warning(value));
5383
+ stopSpinner = stop;
5384
+ },
5385
+ done(value) {
5386
+ stopSpinner();
5387
+ icon.set(style.success(symbol.success));
5388
+ status2.set(style.success(value));
5389
+ },
5390
+ fail(value) {
5391
+ stopSpinner();
5392
+ icon.set(style.error(symbol.error));
5393
+ status2.set(style.error(value));
5394
+ }
5395
+ };
5396
+ }
5397
+ }
5398
+ term.out.gap();
5399
+ return ui;
5400
+ };
5401
+ };
5402
+
5075
5403
  // src/cli/command/deploy.ts
5076
5404
  var deploy = (program2) => {
5077
5405
  program2.command("deploy").argument("[stacks...]", "Optionally filter stacks to deploy").description("Deploy your app to AWS").action(async (filters) => {
5078
5406
  await layout(async (config, write) => {
5079
5407
  await write(bootstrapDeployer(config));
5080
- const { app, dependencyTree } = await toApp(config, filters);
5408
+ const { app, deploymentLine } = await toApp(config, filters);
5081
5409
  const stackNames = app.stacks.map((stack) => stack.name);
5082
5410
  const formattedFilter = stackNames.map((i) => style.info(i)).join(style.placeholder(", "));
5083
5411
  debug("Stacks to deploy", formattedFilter);
@@ -5091,26 +5419,21 @@ var deploy = (program2) => {
5091
5419
  await write(assetBuilder(app));
5092
5420
  await write(assetPublisher(config, app));
5093
5421
  await write(templateBuilder(app));
5094
- const statuses = {};
5095
- for (const stack of app) {
5096
- statuses[stack.name] = new Signal(style.info("waiting"));
5097
- }
5098
5422
  const doneDeploying = write(loadingDialog("Deploying stacks to AWS..."));
5099
- write(stackTree(dependencyTree, statuses));
5100
5423
  const client = new StackClient(app, config.account, config.region, config.credentials);
5101
- const deploymentLine = createDeploymentLine(dependencyTree);
5102
- for (const stacks of deploymentLine) {
5103
- const results = await Promise.allSettled(stacks.map(async (stack) => {
5104
- const signal = statuses[stack.name];
5105
- signal.set(style.warning("deploying"));
5424
+ const ui = write(stacksDeployer(deploymentLine));
5425
+ for (const line of deploymentLine) {
5426
+ const results = await Promise.allSettled(line.map(async (stack) => {
5427
+ const item = ui[stack.name];
5428
+ item.start("deploying");
5106
5429
  try {
5107
5430
  await client.deploy(stack);
5108
5431
  } catch (error) {
5109
5432
  debugError(error);
5110
- signal.set(style.error("failed"));
5433
+ item.fail("failed");
5111
5434
  throw error;
5112
5435
  }
5113
- signal.set(style.success("deployed"));
5436
+ item.done("deployed");
5114
5437
  }));
5115
5438
  for (const result of results) {
5116
5439
  if (result.status === "rejected") {