@awsless/awsless 0.0.548 → 0.0.550

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
@@ -370,7 +370,7 @@ var createWorkSpace = async (props) => {
370
370
  if (process.env.VERBOSE) {
371
371
  enableDebug();
372
372
  }
373
- const aws = await terraform.install("hashicorp", "aws", "5.98.0");
373
+ const aws = await terraform.install("hashicorp", "aws", "6.9.0");
374
374
  const workspace = new WorkSpace({
375
375
  providers: [
376
376
  createLambdaProvider(props),
@@ -609,7 +609,7 @@ var debug = (...parts) => {
609
609
  };
610
610
 
611
611
  // src/config/app.ts
612
- import { z as z25 } from "zod";
612
+ import { z as z26 } from "zod";
613
613
 
614
614
  // src/feature/alert/schema.ts
615
615
  import { kebabCase } from "change-case";
@@ -969,7 +969,13 @@ var LayerSchema = z16.record(
969
969
 
970
970
  // src/feature/on-failure/schema.ts
971
971
  var OnFailureDefaultSchema = FunctionSchema.optional().describe(
972
- "Defining a onFailure handler will add a global onFailure handler for the following resources:\n- Async lambda functions\n- SQS queues\n- DynamoDB streams"
972
+ [
973
+ "Defining a onFailure handler will add a global onFailure handler for the following resources:",
974
+ "- CloudWatch Scheduler",
975
+ "- Async lambda functions",
976
+ "- SQS queues",
977
+ "- DynamoDB streams"
978
+ ].join("\n")
973
979
  );
974
980
 
975
981
  // src/feature/on-log/schema.ts
@@ -992,32 +998,30 @@ var DomainSchema = ResourceIdSchema.describe("The domain id to link your Pubsub
992
998
  var PubSubDefaultSchema = z18.record(
993
999
  ResourceIdSchema,
994
1000
  z18.object({
995
- auth: FunctionSchema,
1001
+ auth: FunctionSchema.describe("The authentication function for the pubsub API."),
996
1002
  domain: DomainSchema.optional(),
997
- subDomain: z18.string().optional()
998
- // auth: z.union([
999
- // ResourceIdSchema,
1000
- // z.object({
1001
- // authorizer: FunctionSchema,
1002
- // // ttl: AuthorizerTtl.default('1 hour'),
1003
- // }),
1004
- // ]),
1005
- // policy: z
1006
- // .object({
1007
- // publish: z.array(z.string()).optional(),
1008
- // subscribe: z.array(z.string()).optional(),
1009
- // })
1010
- // .optional(),
1003
+ subDomain: z18.string().optional(),
1004
+ namespaces: z18.array(z18.string()).optional().describe('The namespaces for the PubSub API. If not set, a single "default" namespace is created.'),
1005
+ logLevel: z18.enum(["none", "info", "error", "debug", "all"]).optional().describe("The logging level for AppSync API. When set, logging is enabled.")
1011
1006
  })
1012
- ).optional().describe("Define the pubsub subscriber in your stack.");
1007
+ ).optional().describe("Define the pubsub API configuration in your stack.");
1013
1008
  var PubSubSchema = z18.record(
1014
1009
  ResourceIdSchema,
1015
1010
  z18.object({
1016
- sql: z18.string().describe("The SQL statement used to query the IOT topic."),
1017
- sqlVersion: z18.enum(["2015-10-08", "2016-03-23", "beta"]).default("2016-03-23").describe("The version of the SQL rules engine to use when evaluating the rule."),
1018
- consumer: FunctionSchema.describe("The consuming lambda function properties.")
1011
+ channels: z18.array(z18.string()).describe("The event channels this subscriber listens to."),
1012
+ filter: z18.object({
1013
+ eventType: z18.string().optional().describe("Filter events by event type.")
1014
+ // Add more filter options as needed
1015
+ // userId: z.string().optional(),
1016
+ // custom: z.record(z.string(), z.any()).optional(),
1017
+ }).optional().describe("Event filtering options."),
1018
+ consumer: FunctionSchema.describe("The consuming lambda function properties."),
1019
+ batchSize: z18.number().int().min(1).max(100).default(1).describe("Number of events to batch before invoking the consumer function."),
1020
+ retryPolicy: z18.object({
1021
+ maxRetries: z18.number().int().min(0).max(3).default(2).describe("Maximum number of retry attempts.")
1022
+ }).optional().describe("Retry policy for failed event processing.")
1019
1023
  })
1020
- ).optional().describe("Define the pubsub subscriber in your stack.");
1024
+ ).optional().describe("Define the pubsub event subscribers in your stack.");
1021
1025
 
1022
1026
  // src/feature/queue/schema.ts
1023
1027
  import { days as days2, hours, minutes as minutes2, seconds as seconds2 } from "@awsless/duration";
@@ -1285,8 +1289,17 @@ var InstanceDefaultSchema = z23.object({
1285
1289
  }))
1286
1290
  }).default({});
1287
1291
 
1288
- // src/config/schema/region.ts
1292
+ // src/feature/topic/schema.ts
1293
+ import { kebabCase as kebabCase3 } from "change-case";
1289
1294
  import { z as z24 } from "zod";
1295
+ var TopicNameSchema = z24.string().min(3).max(256).regex(/^[a-z0-9\-]+$/i, "Invalid topic name").transform((value) => kebabCase3(value)).describe("Define event topic name.");
1296
+ var TopicsDefaultSchema = z24.array(TopicNameSchema).refine((topics) => {
1297
+ return topics.length === new Set(topics).size;
1298
+ }, "Must be a list of unique topic names").optional().describe("Define the event topics for your app.");
1299
+ var SubscribersSchema = z24.record(TopicNameSchema, FunctionSchema).optional().describe("Define the event topics to subscribe too in your stack.");
1300
+
1301
+ // src/config/schema/region.ts
1302
+ import { z as z25 } from "zod";
1290
1303
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
1291
1304
  var AF = ["af-south-1"];
1292
1305
  var AP = [
@@ -1315,16 +1328,16 @@ var EU = [
1315
1328
  var ME = ["me-south-1", "me-central-1"];
1316
1329
  var SA = ["sa-east-1"];
1317
1330
  var regions = [...US, ...AF, ...AP, ...CA, ...EU, ...ME, ...SA];
1318
- var RegionSchema = z24.enum(regions);
1331
+ var RegionSchema = z25.enum(regions);
1319
1332
 
1320
1333
  // src/config/app.ts
1321
- var AppSchema = z25.object({
1322
- $schema: z25.string().optional(),
1334
+ var AppSchema = z26.object({
1335
+ $schema: z26.string().optional(),
1323
1336
  name: ResourceIdSchema.describe("App name."),
1324
1337
  region: RegionSchema.describe("The AWS region to deploy to."),
1325
- profile: z25.string().describe("The AWS profile to deploy to."),
1326
- protect: z25.boolean().default(false).describe("Protect your app & stacks from being deleted."),
1327
- removal: z25.enum(["remove", "retain"]).default("remove").describe(
1338
+ profile: z26.string().describe("The AWS profile to deploy to."),
1339
+ protect: z26.boolean().default(false).describe("Protect your app & stacks from being deleted."),
1340
+ removal: z26.enum(["remove", "retain"]).default("remove").describe(
1328
1341
  [
1329
1342
  "Configure how your resources are handled when they have to be removed.",
1330
1343
  "",
@@ -1338,7 +1351,7 @@ var AppSchema = z25.object({
1338
1351
  // .default('prod')
1339
1352
  // .describe('The deployment stage.'),
1340
1353
  // onFailure: OnFailureSchema,
1341
- defaults: z25.object({
1354
+ defaults: z26.object({
1342
1355
  onFailure: OnFailureDefaultSchema,
1343
1356
  onLog: OnLogDefaultSchema,
1344
1357
  auth: AuthDefaultSchema,
@@ -1354,17 +1367,18 @@ var AppSchema = z25.object({
1354
1367
  // table: TableDefaultSchema,
1355
1368
  // store: StoreDefaultSchema,
1356
1369
  alerts: AlertsDefaultSchema,
1370
+ topics: TopicsDefaultSchema,
1357
1371
  layers: LayerSchema
1358
1372
  // dataRetention: z.boolean().describe('Configure how your resources are handled on delete.').default(false),
1359
1373
  }).default({}).describe("Default properties")
1360
1374
  });
1361
1375
 
1362
1376
  // src/config/stack.ts
1363
- import { z as z41 } from "zod";
1377
+ import { z as z42 } from "zod";
1364
1378
 
1365
1379
  // src/feature/cache/schema.ts
1366
1380
  import { gibibytes as gibibytes2 } from "@awsless/size";
1367
- import { z as z26 } from "zod";
1381
+ import { z as z27 } from "zod";
1368
1382
  var StorageSchema = SizeSchema.refine(sizeMin(gibibytes2(1)), "Minimum storage size is 1 GB").refine(
1369
1383
  sizeMax(gibibytes2(5e3)),
1370
1384
  "Maximum storage size is 5000 GB"
@@ -1375,31 +1389,31 @@ var MinimumStorageSchema = StorageSchema.describe(
1375
1389
  var MaximumStorageSchema = StorageSchema.describe(
1376
1390
  "The upper limit for data storage the cache is set to use. You can specify a size value from 1 GB to 5000 GB."
1377
1391
  );
1378
- var EcpuSchema = z26.number().int().min(1e3).max(15e6);
1392
+ var EcpuSchema = z27.number().int().min(1e3).max(15e6);
1379
1393
  var MinimumEcpuSchema = EcpuSchema.describe(
1380
1394
  "The minimum number of ECPUs the cache can consume per second. You can specify a integer from 1,000 to 15,000,000."
1381
1395
  );
1382
1396
  var MaximumEcpuSchema = EcpuSchema.describe(
1383
1397
  "The maximum number of ECPUs the cache can consume per second. You can specify a integer from 1,000 to 15,000,000."
1384
1398
  );
1385
- var CachesSchema = z26.record(
1399
+ var CachesSchema = z27.record(
1386
1400
  ResourceIdSchema,
1387
- z26.object({
1401
+ z27.object({
1388
1402
  minStorage: MinimumStorageSchema.optional(),
1389
1403
  maxStorage: MaximumStorageSchema.optional(),
1390
1404
  minECPU: MinimumEcpuSchema.optional(),
1391
1405
  maxECPU: MaximumEcpuSchema.optional(),
1392
- snapshotRetentionLimit: z26.number().int().positive().default(1)
1406
+ snapshotRetentionLimit: z27.number().int().positive().default(1)
1393
1407
  })
1394
1408
  ).optional().describe("Define the caches in your stack. For access to the cache put your functions inside the global VPC.");
1395
1409
 
1396
1410
  // src/feature/command/schema.ts
1397
- import { z as z27 } from "zod";
1398
- var CommandSchema = z27.union([
1399
- z27.object({
1411
+ import { z as z28 } from "zod";
1412
+ var CommandSchema = z28.union([
1413
+ z28.object({
1400
1414
  file: LocalFileSchema,
1401
- handler: z27.string().default("default").describe("The name of the handler that needs to run"),
1402
- description: z27.string().optional().describe("A description of the command")
1415
+ handler: z28.string().default("default").describe("The name of the handler that needs to run"),
1416
+ description: z28.string().optional().describe("A description of the command")
1403
1417
  // options: z.record(ResourceIdSchema, OptionSchema).optional(),
1404
1418
  // arguments: z.record(ResourceIdSchema, ArgumentSchema).optional(),
1405
1419
  }),
@@ -1409,22 +1423,22 @@ var CommandSchema = z27.union([
1409
1423
  description: void 0
1410
1424
  }))
1411
1425
  ]);
1412
- var CommandsSchema = z27.record(ResourceIdSchema, CommandSchema).optional().describe("Define the custom commands for your stack.");
1426
+ var CommandsSchema = z28.record(ResourceIdSchema, CommandSchema).optional().describe("Define the custom commands for your stack.");
1413
1427
 
1414
1428
  // src/feature/config/schema.ts
1415
- import { z as z28 } from "zod";
1416
- var ConfigNameSchema = z28.string().regex(/[a-z0-9\-]/g, "Invalid config name");
1417
- var ConfigsSchema = z28.array(ConfigNameSchema).optional().describe("Define the config values for your stack.");
1429
+ import { z as z29 } from "zod";
1430
+ var ConfigNameSchema = z29.string().regex(/[a-z0-9\-]/g, "Invalid config name");
1431
+ var ConfigsSchema = z29.array(ConfigNameSchema).optional().describe("Define the config values for your stack.");
1418
1432
 
1419
1433
  // src/feature/cron/schema/index.ts
1420
- import { z as z30 } from "zod";
1434
+ import { z as z31 } from "zod";
1421
1435
 
1422
1436
  // src/feature/cron/schema/schedule.ts
1423
- import { z as z29 } from "zod";
1437
+ import { z as z30 } from "zod";
1424
1438
  import { awsCronExpressionValidator } from "aws-cron-expression-validator";
1425
- var RateExpressionSchema = z29.custom(
1439
+ var RateExpressionSchema = z30.custom(
1426
1440
  (value) => {
1427
- return z29.string().regex(/^[0-9]+ (seconds?|minutes?|hours?|days?)$/).refine((rate) => {
1441
+ return z30.string().regex(/^[0-9]+ (seconds?|minutes?|hours?|days?)$/).refine((rate) => {
1428
1442
  const [str] = rate.split(" ");
1429
1443
  const number = parseInt(str);
1430
1444
  return number > 0;
@@ -1440,9 +1454,9 @@ var RateExpressionSchema = z29.custom(
1440
1454
  }
1441
1455
  return `rate(${rate})`;
1442
1456
  });
1443
- var CronExpressionSchema = z29.custom(
1457
+ var CronExpressionSchema = z30.custom(
1444
1458
  (value) => {
1445
- return z29.string().safeParse(value).success;
1459
+ return z30.string().safeParse(value).success;
1446
1460
  },
1447
1461
  { message: "Invalid cron expression" }
1448
1462
  ).superRefine((value, ctx) => {
@@ -1451,12 +1465,12 @@ var CronExpressionSchema = z29.custom(
1451
1465
  } catch (error) {
1452
1466
  if (error instanceof Error) {
1453
1467
  ctx.addIssue({
1454
- code: z29.ZodIssueCode.custom,
1468
+ code: z30.ZodIssueCode.custom,
1455
1469
  message: `Invalid cron expression: ${error.message}`
1456
1470
  });
1457
1471
  } else {
1458
1472
  ctx.addIssue({
1459
- code: z29.ZodIssueCode.custom,
1473
+ code: z30.ZodIssueCode.custom,
1460
1474
  message: "Invalid cron expression"
1461
1475
  });
1462
1476
  }
@@ -1467,28 +1481,28 @@ var CronExpressionSchema = z29.custom(
1467
1481
  var ScheduleExpressionSchema = RateExpressionSchema.or(CronExpressionSchema);
1468
1482
 
1469
1483
  // src/feature/cron/schema/index.ts
1470
- var CronsSchema = z30.record(
1484
+ var CronsSchema = z31.record(
1471
1485
  ResourceIdSchema,
1472
- z30.object({
1473
- enabled: z30.boolean().default(true).describe("If the cron is enabled."),
1486
+ z31.object({
1487
+ enabled: z31.boolean().default(true).describe("If the cron is enabled."),
1474
1488
  consumer: FunctionSchema.describe("The consuming lambda function properties."),
1475
1489
  schedule: ScheduleExpressionSchema.describe(
1476
1490
  'The scheduling expression.\n\nexample: "0 20 * * ? *"\nexample: "5 minutes"'
1477
1491
  ),
1478
- payload: z30.unknown().optional().describe("The JSON payload that will be passed to the consumer.")
1492
+ payload: z31.unknown().optional().describe("The JSON payload that will be passed to the consumer.")
1479
1493
  })
1480
1494
  ).optional().describe(`Define the cron jobs in your stack.`);
1481
1495
 
1482
1496
  // src/feature/search/schema.ts
1483
1497
  import { gibibytes as gibibytes3 } from "@awsless/size";
1484
- import { z as z31 } from "zod";
1485
- var VersionSchema = z31.union([
1498
+ import { z as z32 } from "zod";
1499
+ var VersionSchema = z32.union([
1486
1500
  //
1487
- z31.enum(["2.13", "2.11", "2.9", "2.7", "2.5", "2.3", "1.3"]),
1488
- z31.string()
1501
+ z32.enum(["2.13", "2.11", "2.9", "2.7", "2.5", "2.3", "1.3"]),
1502
+ z32.string()
1489
1503
  ]).describe("Specify the OpenSearch engine version.");
1490
- var TypeSchema = z31.union([
1491
- z31.enum([
1504
+ var TypeSchema = z32.union([
1505
+ z32.enum([
1492
1506
  "t3.small",
1493
1507
  "t3.medium",
1494
1508
  "m3.medium",
@@ -1562,13 +1576,13 @@ var TypeSchema = z31.union([
1562
1576
  "r6gd.12xlarge",
1563
1577
  "r6gd.16xlarge"
1564
1578
  ]),
1565
- z31.string()
1579
+ z32.string()
1566
1580
  ]).describe("Instance type of data nodes in the cluster.");
1567
- var CountSchema = z31.number().int().min(1).describe("Number of instances in the cluster.");
1581
+ var CountSchema = z32.number().int().min(1).describe("Number of instances in the cluster.");
1568
1582
  var StorageSizeSchema = SizeSchema.refine(sizeMin(gibibytes3(10)), "Minimum storage size is 10 GB").refine(sizeMax(gibibytes3(100)), "Maximum storage size is 100 GB").describe("The size of the function's /tmp directory. You can specify a size value from 512 MB to 10 GiB.");
1569
- var SearchsSchema = z31.record(
1583
+ var SearchsSchema = z32.record(
1570
1584
  ResourceIdSchema,
1571
- z31.object({
1585
+ z32.object({
1572
1586
  type: TypeSchema.default("t3.small"),
1573
1587
  count: CountSchema.default(1),
1574
1588
  version: VersionSchema.default("2.13"),
@@ -1579,12 +1593,12 @@ var SearchsSchema = z31.record(
1579
1593
  ).optional().describe("Define the search instances in your stack. Backed by OpenSearch.");
1580
1594
 
1581
1595
  // src/feature/site/schema.ts
1582
- import { z as z33 } from "zod";
1596
+ import { z as z34 } from "zod";
1583
1597
 
1584
1598
  // src/config/schema/local-entry.ts
1585
1599
  import { stat as stat3 } from "fs/promises";
1586
- import { z as z32 } from "zod";
1587
- var LocalEntrySchema = z32.union([
1600
+ import { z as z33 } from "zod";
1601
+ var LocalEntrySchema = z33.union([
1588
1602
  RelativePathSchema.refine(async (path) => {
1589
1603
  try {
1590
1604
  const s = await stat3(path);
@@ -1593,7 +1607,7 @@ var LocalEntrySchema = z32.union([
1593
1607
  return false;
1594
1608
  }
1595
1609
  }, `File or directory doesn't exist`),
1596
- z32.object({
1610
+ z33.object({
1597
1611
  nocheck: RelativePathSchema.describe(
1598
1612
  "Specifies a local file or directory without checking if the file or directory exists."
1599
1613
  )
@@ -1601,14 +1615,14 @@ var LocalEntrySchema = z32.union([
1601
1615
  ]);
1602
1616
 
1603
1617
  // src/feature/site/schema.ts
1604
- var ErrorResponsePathSchema = z33.string().describe(
1618
+ var ErrorResponsePathSchema = z34.string().describe(
1605
1619
  [
1606
1620
  "The path to the custom error page that you want to return to the viewer when your origin returns the HTTP status code specified.",
1607
1621
  "- We recommend that you store custom error pages in an Amazon S3 bucket.",
1608
1622
  "If you store custom error pages on an HTTP server and the server starts to return 5xx errors, CloudFront can't get the files that you want to return to viewers because the origin server is unavailable."
1609
1623
  ].join("\n")
1610
1624
  );
1611
- var StatusCodeSchema = z33.number().int().positive().optional().describe(
1625
+ var StatusCodeSchema = z34.number().int().positive().optional().describe(
1612
1626
  [
1613
1627
  "The HTTP status code that you want CloudFront to return to the viewer along with the custom error page.",
1614
1628
  "There are a variety of reasons that you might want CloudFront to return a status code different from the status code that your origin returned to CloudFront, for example:",
@@ -1621,19 +1635,19 @@ var StatusCodeSchema = z33.number().int().positive().optional().describe(
1621
1635
  var MinTTLSchema = DurationSchema.describe(
1622
1636
  "The minimum amount of time, that you want to cache the error response. When this time period has elapsed, CloudFront queries your origin to see whether the problem that caused the error has been resolved and the requested object is now available."
1623
1637
  );
1624
- var ErrorResponseSchema = z33.union([
1638
+ var ErrorResponseSchema = z34.union([
1625
1639
  ErrorResponsePathSchema,
1626
- z33.object({
1640
+ z34.object({
1627
1641
  path: ErrorResponsePathSchema,
1628
1642
  statusCode: StatusCodeSchema.optional(),
1629
1643
  minTTL: MinTTLSchema.optional()
1630
1644
  })
1631
1645
  ]).optional();
1632
- var SitesSchema = z33.record(
1646
+ var SitesSchema = z34.record(
1633
1647
  ResourceIdSchema,
1634
- z33.object({
1648
+ z34.object({
1635
1649
  domain: ResourceIdSchema.describe("The domain id to link your site with.").optional(),
1636
- subDomain: z33.string().optional(),
1650
+ subDomain: z34.string().optional(),
1637
1651
  // bind: z
1638
1652
  // .object({
1639
1653
  // auth: z.array(ResourceIdSchema),
@@ -1642,16 +1656,16 @@ var SitesSchema = z33.record(
1642
1656
  // // rest: z.array(ResourceIdSchema),
1643
1657
  // })
1644
1658
  // .optional(),
1645
- build: z33.object({
1646
- command: z33.string().describe(
1659
+ build: z34.object({
1660
+ command: z34.string().describe(
1647
1661
  `Specifies the files and directories to generate the cache key for your custom build command.`
1648
1662
  ),
1649
- cacheKey: z33.union([LocalEntrySchema.transform((v) => [v]), LocalEntrySchema.array()]).describe(
1663
+ cacheKey: z34.union([LocalEntrySchema.transform((v) => [v]), LocalEntrySchema.array()]).describe(
1650
1664
  `Specifies the files and directories to generate the cache key for your custom build command.`
1651
1665
  ),
1652
- configs: z33.string().array().describe("Define the config values for your build command.")
1666
+ configs: z34.string().array().describe("Define the config values for your build command.")
1653
1667
  }).optional().describe(`Specifies the build process for sites that need a build step.`),
1654
- static: z33.union([LocalDirectorySchema, z33.boolean()]).optional().describe(
1668
+ static: z34.union([LocalDirectorySchema, z34.boolean()]).optional().describe(
1655
1669
  "Specifies the path to the static files directory. Additionally you can also pass `true` when you don't have local static files, but still want to make an S3 bucket."
1656
1670
  ),
1657
1671
  ssr: FunctionSchema.optional().describe("Specifies the file that will render the site on the server."),
@@ -1672,7 +1686,7 @@ var SitesSchema = z33.record(
1672
1686
  // build: z.string().optional(),
1673
1687
  // }),
1674
1688
  // ]),
1675
- geoRestrictions: z33.array(z33.string().length(2).toUpperCase()).default([]).describe("Specifies a blacklist of countries that should be blocked."),
1689
+ geoRestrictions: z34.array(z34.string().length(2).toUpperCase()).default([]).describe("Specifies a blacklist of countries that should be blocked."),
1676
1690
  // forwardHost: z
1677
1691
  // .boolean()
1678
1692
  // .default(false)
@@ -1683,7 +1697,7 @@ var SitesSchema = z33.record(
1683
1697
  // 'Keep in mind that this requires an extra CloudFront Function.',
1684
1698
  // ].join('\n')
1685
1699
  // ),
1686
- errors: z33.object({
1700
+ errors: z34.object({
1687
1701
  400: ErrorResponseSchema.describe("Customize a `400 Bad Request` response."),
1688
1702
  403: ErrorResponseSchema.describe("Customize a `403 Forbidden` response."),
1689
1703
  404: ErrorResponseSchema.describe("Customize a `404 Not Found` response."),
@@ -1696,20 +1710,20 @@ var SitesSchema = z33.record(
1696
1710
  503: ErrorResponseSchema.describe("Customize a `503 Service Unavailable` response."),
1697
1711
  504: ErrorResponseSchema.describe("Customize a `504 Gateway Timeout` response.")
1698
1712
  }).optional().describe("Customize the error responses for specific HTTP status codes."),
1699
- cors: z33.object({
1700
- override: z33.boolean().default(false),
1713
+ cors: z34.object({
1714
+ override: z34.boolean().default(false),
1701
1715
  maxAge: DurationSchema.default("365 days"),
1702
- exposeHeaders: z33.string().array().optional(),
1703
- credentials: z33.boolean().default(false),
1704
- headers: z33.string().array().default(["*"]),
1705
- origins: z33.string().array().default(["*"]),
1706
- methods: z33.enum(["GET", "DELETE", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "ALL"]).array().default(["ALL"])
1716
+ exposeHeaders: z34.string().array().optional(),
1717
+ credentials: z34.boolean().default(false),
1718
+ headers: z34.string().array().default(["*"]),
1719
+ origins: z34.string().array().default(["*"]),
1720
+ methods: z34.enum(["GET", "DELETE", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "ALL"]).array().default(["ALL"])
1707
1721
  }).optional().describe("Specify the cors headers."),
1708
- auth: z33.object({
1709
- username: z33.string().describe("Basic auth username"),
1710
- password: z33.string().describe("Basic auth password")
1722
+ auth: z34.object({
1723
+ username: z34.string().describe("Basic auth username"),
1724
+ password: z34.string().describe("Basic auth password")
1711
1725
  }).optional().describe("Enable basic authentication for the site"),
1712
- security: z33.object({
1726
+ security: z34.object({
1713
1727
  // contentSecurityPolicy: z.object({
1714
1728
  // override: z.boolean().default(false),
1715
1729
  // policy: z.string(),
@@ -1751,10 +1765,10 @@ var SitesSchema = z33.record(
1751
1765
  // reportUri?: string
1752
1766
  // }
1753
1767
  }).optional().describe("Specify the security policy."),
1754
- cache: z33.object({
1755
- cookies: z33.string().array().optional().describe("Specifies the cookies that CloudFront includes in the cache key."),
1756
- headers: z33.string().array().optional().describe("Specifies the headers that CloudFront includes in the cache key."),
1757
- queries: z33.string().array().optional().describe("Specifies the query values that CloudFront includes in the cache key.")
1768
+ cache: z34.object({
1769
+ cookies: z34.string().array().optional().describe("Specifies the cookies that CloudFront includes in the cache key."),
1770
+ headers: z34.string().array().optional().describe("Specifies the headers that CloudFront includes in the cache key."),
1771
+ queries: z34.string().array().optional().describe("Specifies the query values that CloudFront includes in the cache key.")
1758
1772
  }).optional().describe(
1759
1773
  "Specifies the cookies, headers, and query values that CloudFront includes in the cache key."
1760
1774
  )
@@ -1762,22 +1776,21 @@ var SitesSchema = z33.record(
1762
1776
  ).optional().describe("Define the sites in your stack.");
1763
1777
 
1764
1778
  // src/feature/store/schema.ts
1765
- import { z as z34 } from "zod";
1766
- var StoresSchema = z34.union([
1767
- z34.array(ResourceIdSchema).transform((list3) => {
1779
+ import { z as z35 } from "zod";
1780
+ var StoresSchema = z35.union([
1781
+ z35.array(ResourceIdSchema).transform((list3) => {
1768
1782
  const stores = {};
1769
1783
  for (const key of list3) {
1770
1784
  stores[key] = {};
1771
1785
  }
1772
1786
  return stores;
1773
1787
  }),
1774
- z34.record(
1788
+ z35.record(
1775
1789
  ResourceIdSchema,
1776
- z34.object({
1777
- // cors: CorsSchema,
1778
- // deletionProtection: DeletionProtectionSchema.optional(),
1779
- versioning: z34.boolean().default(false).describe("Enable versioning of your store."),
1780
- events: z34.object({
1790
+ z35.object({
1791
+ static: LocalDirectorySchema.optional().describe("Specifies the path to the static files directory."),
1792
+ versioning: z35.boolean().default(false).describe("Enable versioning of your store."),
1793
+ events: z35.object({
1781
1794
  // create
1782
1795
  "created:*": FunctionSchema.optional().describe(
1783
1796
  "Subscribe to notifications regardless of the API that was used to create an object."
@@ -1810,58 +1823,58 @@ var StoresSchema = z34.union([
1810
1823
  ]).optional().describe("Define the stores in your stack.");
1811
1824
 
1812
1825
  // src/feature/icon/schema.ts
1813
- import { z as z35 } from "zod";
1826
+ import { z as z36 } from "zod";
1814
1827
  var staticOriginSchema = LocalDirectorySchema.describe(
1815
1828
  "Specifies the path to a local image directory that will be uploaded in S3."
1816
1829
  );
1817
1830
  var functionOriginSchema = FunctionSchema.describe(
1818
1831
  "Specifies the file that will be called when an image isn't found in the (cache) bucket."
1819
1832
  );
1820
- var IconsSchema = z35.record(
1833
+ var IconsSchema = z36.record(
1821
1834
  ResourceIdSchema,
1822
- z35.object({
1835
+ z36.object({
1823
1836
  domain: ResourceIdSchema.describe("The domain id to link your site with.").optional(),
1824
- subDomain: z35.string().optional(),
1837
+ subDomain: z36.string().optional(),
1825
1838
  log: LogSchema.optional(),
1826
1839
  cacheDuration: DurationSchema.optional().describe("The cache duration of the cached icons."),
1827
- preserveId: z35.boolean().optional().default(false).describe("Preserve the IDs of the icons."),
1828
- symbols: z35.boolean().optional().default(false).describe("Use SVG symbols for icons."),
1829
- origin: z35.union([
1830
- z35.object({
1840
+ preserveId: z36.boolean().optional().default(false).describe("Preserve the IDs of the icons."),
1841
+ symbols: z36.boolean().optional().default(false).describe("Use SVG symbols for icons."),
1842
+ origin: z36.union([
1843
+ z36.object({
1831
1844
  static: staticOriginSchema,
1832
1845
  function: functionOriginSchema.optional()
1833
1846
  }),
1834
- z35.object({
1847
+ z36.object({
1835
1848
  static: staticOriginSchema.optional(),
1836
1849
  function: functionOriginSchema
1837
1850
  }),
1838
- z35.object({
1851
+ z36.object({
1839
1852
  static: staticOriginSchema,
1840
1853
  function: functionOriginSchema
1841
1854
  })
1842
1855
  ]).describe(
1843
1856
  "Image transformation will be applied from a base image. Base images orginates from a local directory that will be uploaded to S3 or from a lambda function."
1844
1857
  ),
1845
- cors: z35.object({
1846
- override: z35.boolean().default(true),
1858
+ cors: z36.object({
1859
+ override: z36.boolean().default(true),
1847
1860
  maxAge: DurationSchema.default("365 days"),
1848
- exposeHeaders: z35.string().array().optional(),
1849
- credentials: z35.boolean().default(false),
1850
- headers: z35.string().array().default(["*"]),
1851
- origins: z35.string().array().default(["*"])
1861
+ exposeHeaders: z36.string().array().optional(),
1862
+ credentials: z36.boolean().default(false),
1863
+ headers: z36.string().array().default(["*"]),
1864
+ origins: z36.string().array().default(["*"])
1852
1865
  }).optional().describe("Specify the cors headers.")
1853
1866
  // version: z.number().int().min(1).optional().describe('Version of the icon configuration.'),
1854
1867
  })
1855
1868
  ).optional().describe("Define an icon proxy in your stack. Store, optimize, and deliver icons at scale.");
1856
1869
 
1857
1870
  // src/feature/image/schema.ts
1858
- import { z as z36 } from "zod";
1859
- var transformationOptionsSchema = z36.object({
1860
- width: z36.number().int().positive().optional(),
1861
- height: z36.number().int().positive().optional(),
1862
- fit: z36.enum(["cover", "contain", "fill", "inside", "outside"]).optional(),
1863
- position: z36.enum(["top", "right top", "right", "right bottom", "bottom", "left bottom", "left", "left top", "center"]).optional(),
1864
- quality: z36.number().int().min(1).max(100).optional()
1871
+ import { z as z37 } from "zod";
1872
+ var transformationOptionsSchema = z37.object({
1873
+ width: z37.number().int().positive().optional(),
1874
+ height: z37.number().int().positive().optional(),
1875
+ fit: z37.enum(["cover", "contain", "fill", "inside", "outside"]).optional(),
1876
+ position: z37.enum(["top", "right top", "right", "right bottom", "bottom", "left bottom", "left", "left top", "center"]).optional(),
1877
+ quality: z37.number().int().min(1).max(100).optional()
1865
1878
  });
1866
1879
  var staticOriginSchema2 = LocalDirectorySchema.describe(
1867
1880
  "Specifies the path to a local image directory that will be uploaded in S3."
@@ -1869,68 +1882,115 @@ var staticOriginSchema2 = LocalDirectorySchema.describe(
1869
1882
  var functionOriginSchema2 = FunctionSchema.describe(
1870
1883
  "Specifies the file that will be called when an image isn't found in the (cache) bucket."
1871
1884
  );
1872
- var ImagesSchema = z36.record(
1885
+ var ImagesSchema = z37.record(
1873
1886
  ResourceIdSchema,
1874
- z36.object({
1887
+ z37.object({
1875
1888
  domain: ResourceIdSchema.describe("The domain id to link your site with.").optional(),
1876
- subDomain: z36.string().optional(),
1889
+ subDomain: z37.string().optional(),
1877
1890
  log: LogSchema.optional(),
1878
1891
  cacheDuration: DurationSchema.optional().describe("Cache duration of the cached images."),
1879
- presets: z36.record(z36.string(), transformationOptionsSchema).describe("Named presets for image transformations"),
1880
- extensions: z36.object({
1881
- jpeg: z36.object({
1882
- mozjpeg: z36.boolean().optional(),
1883
- progressive: z36.boolean().optional()
1892
+ presets: z37.record(z37.string(), transformationOptionsSchema).describe("Named presets for image transformations"),
1893
+ extensions: z37.object({
1894
+ jpeg: z37.object({
1895
+ mozjpeg: z37.boolean().optional(),
1896
+ progressive: z37.boolean().optional()
1884
1897
  }).optional(),
1885
- webp: z36.object({
1886
- effort: z36.number().int().min(1).max(10).default(7).optional(),
1887
- lossless: z36.boolean().optional(),
1888
- nearLossless: z36.boolean().optional()
1898
+ webp: z37.object({
1899
+ effort: z37.number().int().min(1).max(10).default(7).optional(),
1900
+ lossless: z37.boolean().optional(),
1901
+ nearLossless: z37.boolean().optional()
1889
1902
  }).optional(),
1890
- png: z36.object({
1891
- compressionLevel: z36.number().int().min(0).max(9).default(6).optional()
1903
+ png: z37.object({
1904
+ compressionLevel: z37.number().int().min(0).max(9).default(6).optional()
1892
1905
  }).optional()
1893
1906
  }).refine((data) => {
1894
1907
  return Object.keys(data).length > 0;
1895
1908
  }, "At least one extension must be defined.").describe("Specify the allowed extensions."),
1896
- origin: z36.union([
1897
- z36.object({
1909
+ origin: z37.union([
1910
+ z37.object({
1898
1911
  static: staticOriginSchema2,
1899
1912
  function: functionOriginSchema2.optional()
1900
1913
  }),
1901
- z36.object({
1914
+ z37.object({
1902
1915
  static: staticOriginSchema2.optional(),
1903
1916
  function: functionOriginSchema2
1904
1917
  }),
1905
- z36.object({
1918
+ z37.object({
1906
1919
  static: staticOriginSchema2,
1907
1920
  function: functionOriginSchema2
1908
1921
  })
1909
1922
  ]).describe(
1910
1923
  "Specify the origin of your images. Image transformation will be applied from a base image. Base images can be loaded from a S3 bucket (that is synced from a local directory) or dynamicly from a lambda function."
1911
1924
  ),
1912
- version: z36.number().int().min(1).optional().describe("Version of the image configuration.")
1925
+ version: z37.number().int().min(1).optional().describe("Version of the image configuration.")
1913
1926
  })
1914
1927
  ).optional().describe("Define an image proxy in your stack. Store, transform, optimize, and deliver images at scale.");
1915
1928
 
1929
+ // src/feature/metric/schema.ts
1930
+ import { z as z38 } from "zod";
1931
+ var ops = {
1932
+ ">": "GreaterThanThreshold",
1933
+ ">=": "GreaterThanOrEqualToThreshold",
1934
+ "<": "LessThanThreshold",
1935
+ "<=": "LessThanOrEqualToThreshold"
1936
+ };
1937
+ var stats = {
1938
+ count: "SampleCount",
1939
+ avg: "Average",
1940
+ sum: "Sum",
1941
+ min: "Minimum",
1942
+ max: "Maximum"
1943
+ };
1944
+ var WhereSchema = z38.union([
1945
+ z38.string().regex(/(count|avg|sum|min|max) (>|>=|<|<=) (\d)/, "Invalid where query").transform((where) => {
1946
+ const [stat5, op, value] = where.split(" ");
1947
+ return { stat: stat5, op, value: parseFloat(value) };
1948
+ }),
1949
+ z38.object({
1950
+ stat: z38.enum(["count", "avg", "sum", "min", "max"]),
1951
+ op: z38.enum([">", ">=", "<", "<="]),
1952
+ value: z38.number()
1953
+ })
1954
+ ]).transform((where) => {
1955
+ return {
1956
+ stat: stats[where.stat],
1957
+ op: ops[where.op],
1958
+ value: where.value
1959
+ };
1960
+ });
1961
+ var AlarmSchema = z38.object({
1962
+ description: z38.string().optional(),
1963
+ where: WhereSchema,
1964
+ period: DurationSchema,
1965
+ minDataPoints: z38.number().int().default(1),
1966
+ trigger: z38.union([EmailSchema.transform((v) => [v]), EmailSchema.array(), FunctionSchema])
1967
+ });
1968
+ var MetricsSchema = z38.record(
1969
+ ResourceIdSchema,
1970
+ z38.object({
1971
+ type: z38.enum(["number", "size", "duration"]),
1972
+ alarms: AlarmSchema.array().optional()
1973
+ })
1974
+ ).optional().describe("Define the metrics in your stack.");
1975
+
1916
1976
  // src/feature/table/schema.ts
1917
1977
  import { minutes as minutes4, seconds as seconds4 } from "@awsless/duration";
1918
- import { z as z37 } from "zod";
1919
- var KeySchema = z37.string().min(1).max(255);
1920
- var TablesSchema = z37.record(
1978
+ import { z as z39 } from "zod";
1979
+ var KeySchema = z39.string().min(1).max(255);
1980
+ var TablesSchema = z39.record(
1921
1981
  ResourceIdSchema,
1922
- z37.object({
1982
+ z39.object({
1923
1983
  hash: KeySchema.describe(
1924
1984
  "Specifies the name of the partition / hash key that makes up the primary key for the table."
1925
1985
  ),
1926
1986
  sort: KeySchema.optional().describe(
1927
1987
  "Specifies the name of the range / sort key that makes up the primary key for the table."
1928
1988
  ),
1929
- fields: z37.record(z37.string(), z37.enum(["string", "number", "binary"])).optional().describe(
1989
+ fields: z39.record(z39.string(), z39.enum(["string", "number", "binary"])).optional().describe(
1930
1990
  'A list of attributes that describe the key schema for the table and indexes. If no attribute field is defined we default to "string".'
1931
1991
  ),
1932
- class: z37.enum(["standard", "standard-infrequent-access"]).default("standard").describe("The table class of the table."),
1933
- pointInTimeRecovery: z37.boolean().default(false).describe("Indicates whether point in time recovery is enabled on the table."),
1992
+ class: z39.enum(["standard", "standard-infrequent-access"]).default("standard").describe("The table class of the table."),
1993
+ pointInTimeRecovery: z39.boolean().default(false).describe("Indicates whether point in time recovery is enabled on the table."),
1934
1994
  ttl: KeySchema.optional().describe(
1935
1995
  [
1936
1996
  "The name of the TTL attribute used to store the expiration time for items in the table.",
@@ -1938,8 +1998,8 @@ var TablesSchema = z37.record(
1938
1998
  ].join("\n")
1939
1999
  ),
1940
2000
  // deletionProtection: DeletionProtectionSchema.optional(),
1941
- stream: z37.object({
1942
- type: z37.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]).describe(
2001
+ stream: z39.object({
2002
+ type: z39.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]).describe(
1943
2003
  [
1944
2004
  "When an item in the table is modified, you can determines what information is written to the stream for this table.",
1945
2005
  "Valid values are:",
@@ -1949,7 +2009,7 @@ var TablesSchema = z37.record(
1949
2009
  "- new-and-old-images - Both the new and the old item images of the item are written to the stream."
1950
2010
  ].join("\n")
1951
2011
  ),
1952
- batchSize: z37.number().min(1).max(1e4).default(1).describe(
2012
+ batchSize: z39.number().min(1).max(1e4).default(1).describe(
1953
2013
  [
1954
2014
  "The maximum number of records in each batch that Lambda pulls from your stream and sends to your function.",
1955
2015
  "Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation (6 MB).",
@@ -1979,7 +2039,7 @@ var TablesSchema = z37.record(
1979
2039
  // 'You can specify a number from -1 to 10000.',
1980
2040
  // ].join('\n')
1981
2041
  // ),
1982
- retryAttempts: z37.number().min(-1).max(1e4).default(-1).describe(
2042
+ retryAttempts: z39.number().min(-1).max(1e4).default(-1).describe(
1983
2043
  [
1984
2044
  "Discard records after the specified number of retries.",
1985
2045
  "The default value is -1, which sets the maximum number of retries to infinite.",
@@ -1987,7 +2047,7 @@ var TablesSchema = z37.record(
1987
2047
  "You can specify a number from -1 to 10000."
1988
2048
  ].join("\n")
1989
2049
  ),
1990
- concurrencyPerShard: z37.number().min(1).max(10).default(1).describe(
2050
+ concurrencyPerShard: z39.number().min(1).max(10).default(1).describe(
1991
2051
  [
1992
2052
  "The number of batches to process concurrently from each shard.",
1993
2053
  "You can specify a number from 1 to 10."
@@ -1997,16 +2057,16 @@ var TablesSchema = z37.record(
1997
2057
  }).optional().describe(
1998
2058
  "The settings for the DynamoDB table stream, which capture changes to items stored in the table."
1999
2059
  ),
2000
- indexes: z37.record(
2001
- z37.string(),
2002
- z37.object({
2060
+ indexes: z39.record(
2061
+ z39.string(),
2062
+ z39.object({
2003
2063
  hash: KeySchema.describe(
2004
2064
  "Specifies the name of the partition / hash key that makes up the primary key for the global secondary index."
2005
2065
  ),
2006
2066
  sort: KeySchema.optional().describe(
2007
2067
  "Specifies the name of the range / sort key that makes up the primary key for the global secondary index."
2008
2068
  ),
2009
- projection: z37.enum(["all", "keys-only"]).default("all").describe(
2069
+ projection: z39.enum(["all", "keys-only"]).default("all").describe(
2010
2070
  [
2011
2071
  "The set of attributes that are projected into the index:",
2012
2072
  "- all - All of the table attributes are projected into the index.",
@@ -2020,11 +2080,11 @@ var TablesSchema = z37.record(
2020
2080
  ).optional().describe("Define the tables in your stack.");
2021
2081
 
2022
2082
  // src/feature/task/schema.ts
2023
- import { z as z38 } from "zod";
2024
- var RetryAttemptsSchema2 = z38.number().int().min(0).max(2).describe(
2083
+ import { z as z40 } from "zod";
2084
+ var RetryAttemptsSchema2 = z40.number().int().min(0).max(2).describe(
2025
2085
  "The maximum number of times to retry when the function returns an error. You can specify a number from 0 to 2."
2026
2086
  );
2027
- var TaskSchema = z38.union([
2087
+ var TaskSchema = z40.union([
2028
2088
  LocalFileSchema.transform((file) => ({
2029
2089
  consumer: {
2030
2090
  code: {
@@ -2035,33 +2095,24 @@ var TaskSchema = z38.union([
2035
2095
  },
2036
2096
  retryAttempts: void 0
2037
2097
  })),
2038
- z38.object({
2098
+ z40.object({
2039
2099
  consumer: FunctionSchema,
2040
2100
  retryAttempts: RetryAttemptsSchema2.optional()
2041
2101
  })
2042
2102
  ]);
2043
- var TasksSchema = z38.record(ResourceIdSchema, TaskSchema).optional().describe("Define the tasks in your stack.");
2103
+ var TasksSchema = z40.record(ResourceIdSchema, TaskSchema).optional().describe("Define the tasks in your stack.");
2044
2104
 
2045
2105
  // src/feature/test/schema.ts
2046
- import { z as z39 } from "zod";
2047
- var TestsSchema = z39.union([LocalDirectorySchema.transform((v) => [v]), LocalDirectorySchema.array()]).describe("Define the location of your tests for your stack.").optional();
2048
-
2049
- // src/feature/topic/schema.ts
2050
- import { kebabCase as kebabCase3 } from "change-case";
2051
- import { z as z40 } from "zod";
2052
- var TopicNameSchema = z40.string().min(3).max(256).regex(/^[a-z0-9\-]+$/i, "Invalid topic name").transform((value) => kebabCase3(value)).describe("Define event topic name.");
2053
- var TopicsSchema = z40.array(TopicNameSchema).refine((topics) => {
2054
- return topics.length === new Set(topics).size;
2055
- }, "Must be a list of unique topic names").optional().describe("Define the event topics to publish too in your stack.");
2056
- var SubscribersSchema = z40.record(TopicNameSchema, FunctionSchema).optional().describe("Define the event topics to subscribe too in your stack.");
2106
+ import { z as z41 } from "zod";
2107
+ var TestsSchema = z41.union([LocalDirectorySchema.transform((v) => [v]), LocalDirectorySchema.array()]).describe("Define the location of your tests for your stack.").optional();
2057
2108
 
2058
2109
  // src/config/stack.ts
2059
2110
  var DependsSchema = ResourceIdSchema.array().optional().describe("Define the stacks that this stack is depended on.");
2060
2111
  var NameSchema = ResourceIdSchema.refine((name) => !["base", "hostedzones"].includes(name), {
2061
2112
  message: `Stack name can't be a reserved name.`
2062
2113
  }).describe("Stack name.");
2063
- var StackSchema = z41.object({
2064
- $schema: z41.string().optional(),
2114
+ var StackSchema = z42.object({
2115
+ $schema: z42.string().optional(),
2065
2116
  name: NameSchema,
2066
2117
  depends: DependsSchema,
2067
2118
  commands: CommandsSchema,
@@ -2074,7 +2125,7 @@ var StackSchema = z41.object({
2074
2125
  configs: ConfigsSchema,
2075
2126
  crons: CronsSchema,
2076
2127
  caches: CachesSchema,
2077
- topics: TopicsSchema,
2128
+ // topics: TopicsSchema,
2078
2129
  subscribers: SubscribersSchema,
2079
2130
  functions: FunctionsSchema,
2080
2131
  instances: InstancesSchema,
@@ -2088,7 +2139,8 @@ var StackSchema = z41.object({
2088
2139
  sites: SitesSchema,
2089
2140
  tests: TestsSchema,
2090
2141
  images: ImagesSchema,
2091
- icons: IconsSchema
2142
+ icons: IconsSchema,
2143
+ metrics: MetricsSchema
2092
2144
  });
2093
2145
 
2094
2146
  // src/config/load/read.ts
@@ -2130,13 +2182,13 @@ var readConfigWithStage = async (file, stage) => {
2130
2182
  };
2131
2183
 
2132
2184
  // src/config/load/validate.ts
2133
- import { z as z42 } from "zod";
2185
+ import { z as z43 } from "zod";
2134
2186
  var validateConfig = async (schema, file, data) => {
2135
2187
  try {
2136
2188
  const result = await schema.parseAsync(data);
2137
2189
  return result;
2138
2190
  } catch (error) {
2139
- if (error instanceof z42.ZodError) {
2191
+ if (error instanceof z43.ZodError) {
2140
2192
  throw new ConfigError(file, error, data);
2141
2193
  }
2142
2194
  throw error;
@@ -2889,7 +2941,7 @@ var formatByteSize = (size) => {
2889
2941
 
2890
2942
  // src/feature/on-failure/util.ts
2891
2943
  var getGlobalOnFailure = (ctx) => {
2892
- return ctx.appConfig.defaults.onFailure ? ctx.shared.get("on-failure", "queue-arn") : void 0;
2944
+ return ctx.shared.get("on-failure", "queue-arn");
2893
2945
  };
2894
2946
 
2895
2947
  // src/feature/function/build/typescript/bundle.ts
@@ -3226,7 +3278,8 @@ var createLambdaFunction = (parentGroup, ctx, ns, id, local) => {
3226
3278
  Statement: list3.map((statement) => ({
3227
3279
  Effect: pascalCase(statement.effect ?? "allow"),
3228
3280
  Action: statement.actions,
3229
- Resource: statement.resources
3281
+ Resource: statement.resources,
3282
+ Condition: statement.conditions
3230
3283
  }))
3231
3284
  })
3232
3285
  );
@@ -3432,19 +3485,17 @@ var createAsyncLambdaFunction = (group, ctx, ns, id, local) => {
3432
3485
  functionName: result.lambda.arn,
3433
3486
  maximumRetryAttempts: props.retryAttempts,
3434
3487
  destinationConfig: {
3435
- onFailure: onFailure ? { destination: onFailure } : void 0
3488
+ onFailure: { destination: onFailure }
3436
3489
  }
3437
3490
  },
3438
3491
  {
3439
3492
  dependsOn: [result.policy]
3440
3493
  }
3441
3494
  );
3442
- if (onFailure) {
3443
- result.addPermission({
3444
- actions: ["sqs:SendMessage", "sqs:GetQueueUrl"],
3445
- resources: [onFailure]
3446
- });
3447
- }
3495
+ result.addPermission({
3496
+ actions: ["sqs:SendMessage", "sqs:GetQueueUrl"],
3497
+ resources: [onFailure]
3498
+ });
3448
3499
  return result;
3449
3500
  };
3450
3501
 
@@ -3796,7 +3847,7 @@ var functionFeature = defineFeature({
3796
3847
  // forceDelete: true,
3797
3848
  });
3798
3849
  ctx.shared.set("function", "bucket-name", bucket.bucket);
3799
- const warmGroup = new $6.aws.scheduler.ScheduleGroup(ctx.base, "warm", {
3850
+ const warmGroup = new $6.aws.scheduler.ScheduleGroup(group, "warm", {
3800
3851
  name: formatGlobalResourceName({
3801
3852
  appName: ctx.app.name,
3802
3853
  resourceType: "function",
@@ -3859,9 +3910,6 @@ var onFailureFeature = defineFeature({
3859
3910
  // }
3860
3911
  // },
3861
3912
  onApp(ctx) {
3862
- if (!ctx.appConfig.defaults.onFailure) {
3863
- return;
3864
- }
3865
3913
  const group = new Group8(ctx.base, "on-failure", "main");
3866
3914
  const queue2 = new $8.aws.sqs.Queue(group, "on-failure", {
3867
3915
  name: formatGlobalResourceName({
@@ -3870,8 +3918,10 @@ var onFailureFeature = defineFeature({
3870
3918
  resourceName: "failure"
3871
3919
  })
3872
3920
  });
3873
- ctx.addEnv("ON_FAILURE_QUEUE_ARN", queue2.arn);
3874
3921
  ctx.shared.set("on-failure", "queue-arn", queue2.arn);
3922
+ if (!ctx.appConfig.defaults.onFailure) {
3923
+ return;
3924
+ }
3875
3925
  const result = createLambdaFunction(group, ctx, "on-failure", "consumer", ctx.appConfig.defaults.onFailure);
3876
3926
  new $8.aws.lambda.EventSourceMapping(
3877
3927
  group,
@@ -3896,31 +3946,6 @@ var onFailureFeature = defineFeature({
3896
3946
  resources: [queue2.arn]
3897
3947
  });
3898
3948
  }
3899
- // onStack(ctx) {
3900
- // const onFailure = ctx.stackConfig.onFailure
3901
- // if (!onFailure) {
3902
- // return
3903
- // }
3904
- // const queueArn = ctx.shared.get<aws.ARN>('on-failure-queue-arn')
3905
- // const group = new Node(ctx.stack, 'on-failure', 'failure')
3906
- // const { lambda, policy } = createLambdaFunction(group, ctx, 'on-failure', 'failure', onFailure)
3907
- // const source = new aws.lambda.EventSourceMapping(group, 'on-failure', {
3908
- // functionArn: lambda.arn,
3909
- // sourceArn: queueArn,
3910
- // batchSize: 10,
3911
- // })
3912
- // source.dependsOn(policy)
3913
- // policy.addStatement({
3914
- // actions: [
3915
- // 'sqs:SendMessage',
3916
- // 'sqs:DeleteMessage',
3917
- // 'sqs:ReceiveMessage',
3918
- // 'sqs:GetQueueUrl',
3919
- // 'sqs:GetQueueAttributes',
3920
- // ],
3921
- // resources: [queueArn],
3922
- // })
3923
- // },
3924
3949
  });
3925
3950
 
3926
3951
  // src/feature/pubsub/index.ts
@@ -3949,87 +3974,137 @@ var formatFullDomainName = (config2, id, subDomain) => {
3949
3974
  var pubsubFeature = defineFeature({
3950
3975
  name: "pubsub",
3951
3976
  onApp(ctx) {
3952
- for (const [id, props] of Object.entries(ctx.appConfig.defaults.pubsub ?? {})) {
3977
+ for (const [id, props] of Object.entries(ctx.appConfig.defaults?.pubsub ?? {})) {
3953
3978
  const group = new Group9(ctx.base, "pubsub", id);
3954
- const { lambda } = createLambdaFunction(group, ctx, "pubsub-authorizer", id, props.auth);
3979
+ const shortName = `${ctx.app.name}--${id}`;
3955
3980
  const name = formatGlobalResourceName({
3956
3981
  appName: ctx.app.name,
3957
3982
  resourceType: "pubsub",
3958
3983
  resourceName: id
3959
3984
  });
3960
- const authorizer = new $9.aws.iot.Authorizer(group, "authorizer", {
3961
- name,
3962
- authorizerFunctionArn: lambda.arn,
3963
- status: "ACTIVE",
3964
- signingDisabled: true,
3965
- enableCachingForHttp: false
3985
+ const authGroup = new Group9(group, "auth", "lambda");
3986
+ const { lambda: authLambda } = createLambdaFunction(authGroup, ctx, "pubsub", `${id}-auth`, {
3987
+ ...props.auth,
3988
+ description: `PubSub ${id} authorizer`
3966
3989
  });
3967
- new $9.aws.lambda.Permission(group, "permission", {
3968
- functionName: lambda.functionName,
3969
- action: "lambda:InvokeFunction",
3970
- principal: "iot.amazonaws.com",
3971
- sourceArn: authorizer.arn
3990
+ let loggingRole;
3991
+ if (props.logLevel) {
3992
+ loggingRole = new $9.aws.iam.Role(group, "logging-role", {
3993
+ name: shortId(`${name}-logging-role`),
3994
+ assumeRolePolicy: JSON.stringify({
3995
+ Version: "2012-10-17",
3996
+ Statement: [
3997
+ {
3998
+ Effect: "Allow",
3999
+ Principal: {
4000
+ Service: "appsync.amazonaws.com"
4001
+ },
4002
+ Action: "sts:AssumeRole"
4003
+ }
4004
+ ]
4005
+ })
4006
+ });
4007
+ new $9.aws.iam.RolePolicyAttachment(group, "logs-policy", {
4008
+ role: loggingRole.name,
4009
+ policyArn: "arn:aws:iam::aws:policy/service-role/AWSAppSyncPushToCloudWatchLogs"
4010
+ });
4011
+ }
4012
+ const api = new $9.aws.appsync.Api(group, "api", {
4013
+ name: shortName,
4014
+ eventConfig: [
4015
+ {
4016
+ connectionAuthMode: [{ authType: "AWS_IAM" }, { authType: "AWS_LAMBDA" }],
4017
+ authProvider: [
4018
+ {
4019
+ authType: "AWS_LAMBDA",
4020
+ lambdaAuthorizerConfig: [
4021
+ {
4022
+ authorizerUri: authLambda.arn,
4023
+ authorizerResultTtlInSeconds: 300
4024
+ }
4025
+ ]
4026
+ },
4027
+ {
4028
+ authType: "AWS_IAM"
4029
+ }
4030
+ ],
4031
+ defaultPublishAuthMode: [
4032
+ {
4033
+ authType: "AWS_IAM"
4034
+ }
4035
+ ],
4036
+ defaultSubscribeAuthMode: [
4037
+ {
4038
+ authType: "AWS_LAMBDA"
4039
+ },
4040
+ {
4041
+ authType: "AWS_IAM"
4042
+ }
4043
+ ],
4044
+ logConfig: props.logLevel ? [
4045
+ {
4046
+ logLevel: props.logLevel.toUpperCase(),
4047
+ cloudwatchLogsRoleArn: loggingRole.arn
4048
+ }
4049
+ ] : void 0
4050
+ }
4051
+ ]
3972
4052
  });
3973
- ctx.bind(`PUBSUB_${constantCase5(id)}_AUTHORIZER`, name);
3974
- const endpoint = $9.aws.iot.getEndpoint(group, "endpoint", {
3975
- endpointType: "iot:Data-ATS"
4053
+ const namespaces = props.namespaces ?? ["default"];
4054
+ for (const namespace of namespaces) {
4055
+ new $9.aws.appsync.ChannelNamespace(group, `namespace-${namespace}`, {
4056
+ name: namespace,
4057
+ apiId: api.apiId
4058
+ });
4059
+ }
4060
+ new $9.aws.lambda.Permission(group, "auth-permission", {
4061
+ action: "lambda:InvokeFunction",
4062
+ principal: "appsync.amazonaws.com",
4063
+ functionName: authLambda.functionName,
4064
+ sourceArn: api.apiArn
3976
4065
  });
3977
4066
  if (props.domain) {
3978
4067
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
3979
- new $9.aws.iot.DomainConfiguration(group, "domain", {
3980
- name,
4068
+ const zoneId = ctx.shared.entry("domain", `zone-id`, props.domain);
4069
+ const certificateArn = ctx.shared.entry("domain", `certificate-arn`, props.domain);
4070
+ const apiDomain = new $9.aws.appsync.DomainName(group, "domain", {
3981
4071
  domainName,
3982
- serverCertificateArns: [ctx.shared.entry("domain", `certificate-arn`, props.domain)],
3983
- authorizerConfig: {
3984
- defaultAuthorizerName: authorizer.name
3985
- }
3986
- // validationCertificate: ctx.shared.get(`global-certificate-${props.domain}-arn`),
4072
+ certificateArn
4073
+ });
4074
+ new $9.aws.appsync.DomainNameApiAssociation(group, "domain-association", {
4075
+ apiId: api.apiArn,
4076
+ domainName: apiDomain.domainName
3987
4077
  });
3988
4078
  new $9.aws.route53.Record(group, "record", {
3989
- zoneId: ctx.shared.entry("domain", `zone-id`, props.domain),
3990
- name: domainName,
4079
+ zoneId,
3991
4080
  type: "CNAME",
3992
- records: [endpoint.endpointAddress]
4081
+ name: domainName,
4082
+ records: [apiDomain.appsyncDomainName],
4083
+ ttl: 300
3993
4084
  });
3994
- ctx.bind(`PUBSUB_${constantCase5(id)}_ENDPOINT`, domainName);
4085
+ ctx.bind(`PUBSUB_${constantCase5(id)}_ENDPOINT`, `${domainName}/event`);
4086
+ ctx.bind(`PUBSUB_${constantCase5(id)}_REALTIME_ENDPOINT`, `${domainName}/event/realtime`);
3995
4087
  } else {
3996
- ctx.bind(`PUBSUB_${constantCase5(id)}_ENDPOINT`, endpoint.endpointAddress);
4088
+ ctx.bind(
4089
+ `PUBSUB_${constantCase5(id)}_ENDPOINT`,
4090
+ api.dns.pipe((dns) => dns.HTTP)
4091
+ );
4092
+ ctx.bind(
4093
+ `PUBSUB_${constantCase5(id)}_REALTIME_ENDPOINT`,
4094
+ api.dns.pipe((dns) => dns.REALTIME)
4095
+ );
3997
4096
  }
3998
4097
  }
3999
- ctx.addGlobalPermission({
4000
- actions: [`iot:Publish`],
4001
- resources: [
4002
- //
4003
- `arn:aws:iot:${ctx.appConfig.region}:${ctx.accountId}:topic/*`,
4004
- `arn:aws:iot:${ctx.appConfig.region}:${ctx.accountId}:topic/${ctx.app.name}/pubsub/*`
4005
- ]
4006
- });
4007
- },
4008
- onStack(ctx) {
4009
- for (const [id, props] of Object.entries(ctx.stackConfig.pubsub ?? {})) {
4010
- const group = new Group9(ctx.stack, "pubsub", id);
4011
- const { lambda } = createAsyncLambdaFunction(group, ctx, `pubsub`, id, props.consumer);
4012
- const name = formatLocalResourceName({
4013
- appName: ctx.app.name,
4014
- stackName: ctx.stack.name,
4015
- resourceType: "pubsub",
4016
- resourceName: id
4017
- });
4018
- const topic = new $9.aws.iot.TopicRule(group, "rule", {
4019
- name: name.replaceAll("-", "_"),
4020
- enabled: true,
4021
- sql: props.sql,
4022
- sqlVersion: props.sqlVersion,
4023
- lambda: [{ functionArn: lambda.arn }]
4024
- });
4025
- new $9.aws.lambda.Permission(group, "permission", {
4026
- action: "lambda:InvokeFunction",
4027
- principal: "iot.amazonaws.com",
4028
- functionName: lambda.functionName,
4029
- sourceArn: topic.arn
4030
- });
4031
- }
4032
4098
  }
4099
+ // Note: The onStack method would handle channel namespaces and subscriptions
4100
+ // but is commented out for now as it needs to be refactored for AppSync Event API
4101
+ // onStack(ctx) {
4102
+ // // Channel namespaces and event handlers would be configured here
4103
+ // // This would include:
4104
+ // // 1. Creating channel namespaces with their own auth modes (overriding defaults)
4105
+ // // 2. Setting up event handlers (Lambda functions) for specific channels
4106
+ // // 3. Configuring event filtering and routing rules
4107
+ // },
4033
4108
  });
4034
4109
 
4035
4110
  // src/feature/queue/index.ts
@@ -4111,7 +4186,7 @@ var queueFeature = defineFeature({
4111
4186
  receiveWaitTimeSeconds: toSeconds3(props.receiveMessageWaitTime ?? seconds5(0)),
4112
4187
  messageRetentionSeconds: toSeconds3(props.retentionPeriod),
4113
4188
  maxMessageSize: toBytes(props.maxMessageSize),
4114
- redrivePolicy: onFailure && onFailure.pipe(
4189
+ redrivePolicy: onFailure.pipe(
4115
4190
  (arn) => JSON.stringify({
4116
4191
  deadLetterTargetArn: arn,
4117
4192
  maxReceiveCount: 100
@@ -5403,6 +5478,30 @@ var siteFeature = defineFeature({
5403
5478
  import { Group as Group16 } from "@awsless/formation";
5404
5479
  import { kebabCase as kebabCase7 } from "change-case";
5405
5480
  import { $ as $16 } from "@awsless/formation";
5481
+ import { glob as glob3 } from "glob";
5482
+
5483
+ // src/feature/store/util.ts
5484
+ import { contentType as contentType2, lookup as lookup2 } from "mime-types";
5485
+ import { extname as extname3 } from "path";
5486
+ var getCacheControl2 = (file) => {
5487
+ switch (lookup2(file)) {
5488
+ case false:
5489
+ case "text/html":
5490
+ case "application/json":
5491
+ case "application/manifest+json":
5492
+ case "application/manifest":
5493
+ case "text/markdown":
5494
+ return "s-maxage=31536000, max-age=0";
5495
+ default:
5496
+ return `public, max-age=31536000, immutable`;
5497
+ }
5498
+ };
5499
+ var getContentType2 = (file) => {
5500
+ return contentType2(extname3(file)) || "text/html; charset=utf-8";
5501
+ };
5502
+
5503
+ // src/feature/store/index.ts
5504
+ import { join as join12 } from "path";
5406
5505
  var typeGenCode5 = `
5407
5506
  import { Body, PutObjectProps, BodyStream } from '@awsless/s3'
5408
5507
 
@@ -5495,6 +5594,24 @@ var storeFeature = defineFeature({
5495
5594
  import: ctx.import ? name : void 0
5496
5595
  }
5497
5596
  );
5597
+ ctx.onReady(() => {
5598
+ if (typeof props.static === "string" && bucket) {
5599
+ const files = glob3.sync("**", {
5600
+ cwd: props.static,
5601
+ nodir: true
5602
+ });
5603
+ for (const file of files) {
5604
+ new $16.aws.s3.BucketObject(group, file, {
5605
+ bucket: bucket.bucket,
5606
+ key: file,
5607
+ cacheControl: getCacheControl2(file),
5608
+ contentType: getContentType2(file),
5609
+ source: join12(props.static, file),
5610
+ sourceHash: $hash(join12(props.static, file))
5611
+ });
5612
+ }
5613
+ }
5614
+ });
5498
5615
  const eventMap = {
5499
5616
  "created:*": "s3:ObjectCreated:*",
5500
5617
  "created:put": "s3:ObjectCreated:Put",
@@ -5678,7 +5795,9 @@ var tableFeature = defineFeature({
5678
5795
  functionResponseTypes: ["ReportBatchItemFailures"],
5679
5796
  startingPosition: "LATEST",
5680
5797
  destinationConfig: {
5681
- onFailure: onFailure ? { destinationArn: onFailure } : void 0
5798
+ onFailure: {
5799
+ destinationArn: onFailure
5800
+ }
5682
5801
  }
5683
5802
  },
5684
5803
  { dependsOn: [result.policy] }
@@ -5692,12 +5811,10 @@ var tableFeature = defineFeature({
5692
5811
  ],
5693
5812
  resources: [table.streamArn]
5694
5813
  });
5695
- if (onFailure) {
5696
- result.addPermission({
5697
- actions: ["sqs:SendMessage", "sqs:GetQueueUrl"],
5698
- resources: [onFailure]
5699
- });
5700
- }
5814
+ result.addPermission({
5815
+ actions: ["sqs:SendMessage", "sqs:GetQueueUrl"],
5816
+ resources: [onFailure]
5817
+ });
5701
5818
  }
5702
5819
  ctx.addStackPermission({
5703
5820
  actions: [
@@ -5891,17 +6008,15 @@ var topicFeature = defineFeature({
5891
6008
  const resources2 = new TypeObject(1);
5892
6009
  const mocks = new TypeObject(1);
5893
6010
  const mockResponses = new TypeObject(1);
5894
- for (const stack of ctx.stackConfigs) {
5895
- for (const topic of stack.topics || []) {
5896
- const name = formatGlobalResourceName({
5897
- appName: ctx.appConfig.name,
5898
- resourceType: "topic",
5899
- resourceName: topic
5900
- });
5901
- mockResponses.addType(topic, "Mock");
5902
- resources2.addType(topic, `Publish<'${name}'>`);
5903
- mocks.addType(topic, `MockBuilder`);
5904
- }
6011
+ for (const topic of ctx.appConfig.defaults.topics ?? []) {
6012
+ const name = formatGlobalResourceName({
6013
+ appName: ctx.appConfig.name,
6014
+ resourceType: "topic",
6015
+ resourceName: topic
6016
+ });
6017
+ mockResponses.addType(topic, "Mock");
6018
+ resources2.addType(topic, `Publish<'${name}'>`);
6019
+ mocks.addType(topic, `MockBuilder`);
5905
6020
  }
5906
6021
  gen.addCode(typeGenCode7);
5907
6022
  gen.addInterface("TopicResources", resources2);
@@ -5910,40 +6025,29 @@ var topicFeature = defineFeature({
5910
6025
  await ctx.write("topic.d.ts", gen, true);
5911
6026
  },
5912
6027
  onValidate(ctx) {
5913
- const unique = [];
5914
- for (const stack of ctx.stackConfigs) {
5915
- for (const topic of stack.topics ?? []) {
5916
- if (unique.includes(topic)) {
5917
- throw new FileError(stack.file, `Duplicate topic "${topic}"`);
5918
- } else {
5919
- unique.push(topic);
5920
- }
5921
- }
5922
- }
6028
+ const topics = ctx.appConfig.defaults.topics ?? [];
5923
6029
  for (const stack of ctx.stackConfigs) {
5924
6030
  for (const topic of Object.keys(stack.subscribers ?? {})) {
5925
- if (!unique.includes(topic)) {
5926
- throw new FileError(stack.file, `Subscription to a undefined topic "${topic}"`);
6031
+ if (!topics.includes(topic)) {
6032
+ throw new FileError(stack.file, `Subscription to a non existent topic "${topic}"`);
5927
6033
  }
5928
6034
  }
5929
6035
  }
5930
6036
  },
5931
6037
  onApp(ctx) {
5932
- for (const stack of ctx.stackConfigs) {
5933
- for (const id of stack.topics ?? []) {
5934
- const group = new Group19(ctx.base, "topic", id);
5935
- const name = formatGlobalResourceName({
5936
- appName: ctx.appConfig.name,
5937
- resourceType: "topic",
5938
- resourceName: id
5939
- });
5940
- const topic = new $19.aws.sns.Topic(group, "topic", {
5941
- name
5942
- });
5943
- ctx.shared.add("topic", `arn`, id, topic.arn);
5944
- }
6038
+ for (const id of ctx.appConfig.defaults.topics ?? []) {
6039
+ const group = new Group19(ctx.base, "topic", id);
6040
+ const name = formatGlobalResourceName({
6041
+ appName: ctx.appConfig.name,
6042
+ resourceType: "topic",
6043
+ resourceName: id
6044
+ });
6045
+ const topic = new $19.aws.sns.Topic(group, "topic", {
6046
+ name
6047
+ });
6048
+ ctx.shared.add("topic", `arn`, id, topic.arn);
5945
6049
  }
5946
- ctx.addAppPermission({
6050
+ ctx.addGlobalPermission({
5947
6051
  actions: ["sns:Publish"],
5948
6052
  resources: [
5949
6053
  `arn:aws:sns:${ctx.appConfig.region}:${ctx.accountId}:${formatGlobalResourceName({
@@ -5955,12 +6059,6 @@ var topicFeature = defineFeature({
5955
6059
  });
5956
6060
  },
5957
6061
  onStack(ctx) {
5958
- for (const id of ctx.stackConfig.topics ?? []) {
5959
- ctx.addStackPermission({
5960
- actions: ["sns:Publish"],
5961
- resources: [ctx.shared.entry("topic", "arn", id)]
5962
- });
5963
- }
5964
6062
  for (const [id, props] of Object.entries(ctx.stackConfig.subscribers ?? {})) {
5965
6063
  const group = new Group19(ctx.stack, "topic", id);
5966
6064
  const topicArn = ctx.shared.entry("topic", "arn", id);
@@ -6107,7 +6205,7 @@ var alertFeature = defineFeature({
6107
6205
  name
6108
6206
  });
6109
6207
  for (const email of emails) {
6110
- new $21.aws.sns.TopicSubscription(group, id, {
6208
+ new $21.aws.sns.TopicSubscription(group, email, {
6111
6209
  topicArn: topic.arn,
6112
6210
  protocol: "email",
6113
6211
  endpoint: email
@@ -6204,12 +6302,12 @@ var layerFeature = defineFeature({
6204
6302
 
6205
6303
  // src/feature/image/index.ts
6206
6304
  import { $ as $23, Group as Group23 } from "@awsless/formation";
6207
- import { join as join12, dirname as dirname8 } from "path";
6305
+ import { join as join13, dirname as dirname8 } from "path";
6208
6306
  import { mebibytes as mebibytes4 } from "@awsless/size";
6209
6307
  import { days as days7, seconds as seconds9, toDays as toDays6, toSeconds as toSeconds8 } from "@awsless/duration";
6210
6308
  import { constantCase as constantCase12 } from "change-case";
6211
6309
  import { fileURLToPath as fileURLToPath2 } from "url";
6212
- import { glob as glob3 } from "glob";
6310
+ import { glob as glob4 } from "glob";
6213
6311
  var __dirname2 = dirname8(fileURLToPath2(import.meta.url));
6214
6312
  var imageFeature = defineFeature({
6215
6313
  name: "image",
@@ -6221,7 +6319,7 @@ var imageFeature = defineFeature({
6221
6319
  return;
6222
6320
  }
6223
6321
  const group = new Group23(ctx.base, "image", "layer");
6224
- const path = join12(__dirname2, "/layers/sharp-arm.zip");
6322
+ const path = join13(__dirname2, "/layers/sharp-arm.zip");
6225
6323
  const layerId = formatGlobalResourceName({
6226
6324
  appName: ctx.appConfig.name,
6227
6325
  resourceType: "layer",
@@ -6304,8 +6402,8 @@ var imageFeature = defineFeature({
6304
6402
  resourceName: "sharp"
6305
6403
  });
6306
6404
  const serverLambda = createPrebuildLambdaFunction(group, ctx, "image", id, {
6307
- bundleFile: join12(__dirname2, "/prebuild/image/bundle.zip"),
6308
- bundleHash: join12(__dirname2, "/prebuild/image/HASH"),
6405
+ bundleFile: join13(__dirname2, "/prebuild/image/bundle.zip"),
6406
+ bundleHash: join13(__dirname2, "/prebuild/image/HASH"),
6309
6407
  memorySize: mebibytes4(512),
6310
6408
  timeout: seconds9(10),
6311
6409
  handler: "index.default",
@@ -6363,7 +6461,7 @@ var imageFeature = defineFeature({
6363
6461
  }
6364
6462
  ctx.onReady(() => {
6365
6463
  if (props.origin.static && s3Origin) {
6366
- const files = glob3.sync("**", {
6464
+ const files = glob4.sync("**", {
6367
6465
  cwd: props.origin.static,
6368
6466
  nodir: true
6369
6467
  });
@@ -6371,8 +6469,8 @@ var imageFeature = defineFeature({
6371
6469
  new $23.aws.s3.BucketObject(group, `static-${file}`, {
6372
6470
  bucket: s3Origin.bucket,
6373
6471
  key: file,
6374
- source: join12(props.origin.static, file),
6375
- sourceHash: $hash(join12(props.origin.static, file))
6472
+ source: join13(props.origin.static, file),
6473
+ sourceHash: $hash(join13(props.origin.static, file))
6376
6474
  });
6377
6475
  }
6378
6476
  }
@@ -6524,12 +6622,12 @@ var imageFeature = defineFeature({
6524
6622
 
6525
6623
  // src/feature/icon/index.ts
6526
6624
  import { $ as $24, Group as Group24 } from "@awsless/formation";
6527
- import { join as join13, dirname as dirname9 } from "path";
6625
+ import { join as join14, dirname as dirname9 } from "path";
6528
6626
  import { mebibytes as mebibytes5 } from "@awsless/size";
6529
6627
  import { days as days8, seconds as seconds10, toDays as toDays7, toSeconds as toSeconds9 } from "@awsless/duration";
6530
6628
  import { constantCase as constantCase13 } from "change-case";
6531
6629
  import { fileURLToPath as fileURLToPath3 } from "url";
6532
- import { glob as glob4 } from "glob";
6630
+ import { glob as glob5 } from "glob";
6533
6631
  var __dirname3 = dirname9(fileURLToPath3(import.meta.url));
6534
6632
  var iconFeature = defineFeature({
6535
6633
  name: "icon",
@@ -6582,8 +6680,8 @@ var iconFeature = defineFeature({
6582
6680
  } : {}
6583
6681
  });
6584
6682
  const serverLambda = createPrebuildLambdaFunction(group, ctx, "icon", id, {
6585
- bundleFile: join13(__dirname3, "/prebuild/icon/bundle.zip"),
6586
- bundleHash: join13(__dirname3, "/prebuild/icon/HASH"),
6683
+ bundleFile: join14(__dirname3, "/prebuild/icon/bundle.zip"),
6684
+ bundleHash: join14(__dirname3, "/prebuild/icon/HASH"),
6587
6685
  memorySize: mebibytes5(512),
6588
6686
  timeout: seconds10(10),
6589
6687
  handler: "index.default",
@@ -6639,7 +6737,7 @@ var iconFeature = defineFeature({
6639
6737
  }
6640
6738
  ctx.onReady(() => {
6641
6739
  if (props.origin.static && s3Origin) {
6642
- const files = glob4.sync("**", {
6740
+ const files = glob5.sync("**", {
6643
6741
  cwd: props.origin.static,
6644
6742
  nodir: true
6645
6743
  });
@@ -6650,8 +6748,8 @@ var iconFeature = defineFeature({
6650
6748
  new $24.aws.s3.BucketObject(group, `static-${file}`, {
6651
6749
  bucket: s3Origin.bucket,
6652
6750
  key: file,
6653
- source: join13(props.origin.static, file),
6654
- sourceHash: $hash(join13(props.origin.static, file))
6751
+ source: join14(props.origin.static, file),
6752
+ sourceHash: $hash(join14(props.origin.static, file))
6655
6753
  });
6656
6754
  }
6657
6755
  }
@@ -6811,15 +6909,15 @@ import { toMebibytes as toMebibytes4 } from "@awsless/size";
6811
6909
  import { generateFileHash as generateFileHash2 } from "@awsless/ts-file-cache";
6812
6910
  import { constantCase as constantCase14, pascalCase as pascalCase3 } from "change-case";
6813
6911
  import deepmerge4 from "deepmerge";
6814
- import { join as join15 } from "path";
6912
+ import { join as join16 } from "path";
6815
6913
 
6816
6914
  // src/feature/instance/build/executable.ts
6817
6915
  import { createHash as createHash4 } from "crypto";
6818
6916
  import { readFile as readFile4 } from "fs/promises";
6819
- import { join as join14 } from "path";
6917
+ import { join as join15 } from "path";
6820
6918
  import { exec as exec2 } from "promisify-child-process";
6821
6919
  var buildExecutable = async (input, outputPath) => {
6822
- const filePath = join14(outputPath, "program");
6920
+ const filePath = join15(outputPath, "program");
6823
6921
  const args = ["build", input, "--compile", "--target", "bun-linux-x64-modern", "--outfile", filePath];
6824
6922
  try {
6825
6923
  await exec2(`bun ${args.join(" ")}`);
@@ -7039,7 +7137,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7039
7137
  healthCheck: props.healthCheck ? {
7040
7138
  command: [
7041
7139
  "CMD-SHELL",
7042
- `curl -f http://${join15("localhost", props.healthCheck.path)} || exit 1`
7140
+ `curl -f http://${join16("localhost", props.healthCheck.path)} || exit 1`
7043
7141
  ],
7044
7142
  interval: toSeconds10(props.healthCheck.interval),
7045
7143
  retries: props.healthCheck.retries,
@@ -7214,6 +7312,133 @@ var instanceFeature = defineFeature({
7214
7312
  }
7215
7313
  });
7216
7314
 
7315
+ // src/feature/metric/index.ts
7316
+ import { $ as $27, Group as Group27 } from "@awsless/formation";
7317
+ import { kebabCase as kebabCase8, constantCase as constantCase15 } from "change-case";
7318
+ import { toSeconds as toSeconds11 } from "@awsless/duration";
7319
+ var typeGenCode9 = `
7320
+ import { type PutDataProps, putData, batchPutData } from '@awsless/cloudwatch'
7321
+
7322
+ type PutResponse = ReturnType<typeof putData>
7323
+ type Batch = typeof batchPutData
7324
+
7325
+ type MetricBase<NS extends string, N extends string> = {
7326
+ readonly namespace: NS
7327
+ readonly name: N
7328
+ }
7329
+
7330
+ type NumberMetric = {
7331
+ readonly unit: 'number'
7332
+ put(value: number | number[], options?: PutDataProps): PutResponse
7333
+ }
7334
+
7335
+ type DurationMetric = {
7336
+ readonly unit: 'duration'
7337
+ put(value: Duration | Duration[], options?: PutDataProps): PutResponse
7338
+ }
7339
+
7340
+ type SizeMetric = {
7341
+ readonly unit: 'size'
7342
+ put(value: Size | Size[], options?: PutDataProps): PutResponse
7343
+ }
7344
+ `;
7345
+ var metricFeature = defineFeature({
7346
+ name: "metric",
7347
+ async onTypeGen(ctx) {
7348
+ const gen = new TypeFile("@awsless/awsless");
7349
+ const resources2 = new TypeObject(1);
7350
+ for (const stack of ctx.stackConfigs ?? []) {
7351
+ const namespace = `awsless/${kebabCase8(ctx.appConfig.name)}/${kebabCase8(stack.name)}`;
7352
+ const stackResources = new TypeObject(2);
7353
+ for (const [id, metric] of Object.entries(stack.metrics ?? {})) {
7354
+ const name = kebabCase8(id);
7355
+ const types2 = {
7356
+ number: "NumberMetric",
7357
+ duration: "DurationMetric",
7358
+ size: "SizeMetric"
7359
+ };
7360
+ stackResources.addType(id, `MetricBase<'${namespace}', '${name}'> & ${types2[metric.type]}`);
7361
+ }
7362
+ resources2.addType(stack.name, stackResources);
7363
+ }
7364
+ resources2.addType("batch", "Batch");
7365
+ gen.addCode(typeGenCode9);
7366
+ gen.addInterface("MetricResources", resources2);
7367
+ await ctx.write("metric.d.ts", gen, true);
7368
+ },
7369
+ onStack(ctx) {
7370
+ const namespace = `awsless/${kebabCase8(ctx.app.name)}/${kebabCase8(ctx.stack.name)}`;
7371
+ ctx.addStackPermission({
7372
+ actions: ["cloudwatch:PutMetricData"],
7373
+ resources: ["*"],
7374
+ conditions: {
7375
+ StringEquals: {
7376
+ // Luckily we can limit access to only the namespace.
7377
+ "cloudwatch:namespace": namespace
7378
+ }
7379
+ }
7380
+ });
7381
+ for (const [id, props] of Object.entries(ctx.stackConfig.metrics ?? {})) {
7382
+ const group = new Group27(ctx.stack, "metric", id);
7383
+ ctx.addEnv(`METRIC_${constantCase15(id)}`, props.type);
7384
+ for (const alarmId in props.alarms ?? []) {
7385
+ const alarmGroup = new Group27(group, "alarm", alarmId);
7386
+ const alarmName = kebabCase8(`${id}-${alarmId}`);
7387
+ const alarmProps = props.alarms[alarmId];
7388
+ let alarmAction;
7389
+ let alarmLambda;
7390
+ if (Array.isArray(alarmProps.trigger)) {
7391
+ const topic = new $27.aws.sns.Topic(alarmGroup, "alarm-trigger", {
7392
+ name: formatLocalResourceName({
7393
+ appName: ctx.app.name,
7394
+ stackName: ctx.stack.name,
7395
+ resourceType: "metric",
7396
+ resourceName: alarmName
7397
+ })
7398
+ });
7399
+ alarmAction = topic.arn;
7400
+ for (const email of alarmProps.trigger) {
7401
+ new $27.aws.sns.TopicSubscription(alarmGroup, email, {
7402
+ topicArn: topic.arn,
7403
+ protocol: "email",
7404
+ endpoint: email
7405
+ });
7406
+ }
7407
+ } else {
7408
+ const { lambda } = createLambdaFunction(alarmGroup, ctx, "metric", alarmName, alarmProps.trigger);
7409
+ alarmLambda = lambda;
7410
+ alarmAction = lambda.arn;
7411
+ }
7412
+ const alarm = new $27.aws.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
7413
+ namespace,
7414
+ metricName: kebabCase8(id),
7415
+ alarmName: formatLocalResourceName({
7416
+ appName: ctx.app.name,
7417
+ stackName: ctx.stack.name,
7418
+ resourceType: "metric",
7419
+ resourceName: alarmName
7420
+ }),
7421
+ alarmDescription: alarmProps.description,
7422
+ statistic: alarmProps.where.stat,
7423
+ threshold: alarmProps.where.value,
7424
+ period: toSeconds11(alarmProps.period),
7425
+ evaluationPeriods: alarmProps.minDataPoints,
7426
+ comparisonOperator: alarmProps.where.op,
7427
+ alarmActions: [alarmAction]
7428
+ });
7429
+ if (alarmLambda) {
7430
+ new $27.aws.lambda.Permission(alarmGroup, "permission", {
7431
+ action: "lambda:InvokeFunction",
7432
+ principal: "lambda.alarms.cloudwatch.amazonaws.com",
7433
+ functionName: alarmLambda.functionName,
7434
+ sourceArn: alarm.arn
7435
+ });
7436
+ }
7437
+ }
7438
+ }
7439
+ }
7440
+ });
7441
+
7217
7442
  // src/feature/index.ts
7218
7443
  var features = [
7219
7444
  // 1
@@ -7233,6 +7458,7 @@ var features = [
7233
7458
  configFeature,
7234
7459
  searchFeature,
7235
7460
  pubsubFeature,
7461
+ metricFeature,
7236
7462
  // streamFeature,
7237
7463
  tableFeature,
7238
7464
  topicFeature,
@@ -8112,13 +8338,13 @@ import wildstring4 from "wildstring";
8112
8338
  import { log as log15 } from "@awsless/clui";
8113
8339
  import chalk4 from "chalk";
8114
8340
  import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
8115
- import { join as join18 } from "path";
8341
+ import { join as join19 } from "path";
8116
8342
  import wildstring3 from "wildstring";
8117
8343
 
8118
8344
  // src/build/__fingerprint.ts
8119
8345
  import { createHash as createHash5 } from "crypto";
8120
8346
  import { readdir as readdir4, readFile as readFile5, stat as stat4 } from "fs/promises";
8121
- import { basename as basename4, dirname as dirname10, extname as extname3, join as join16 } from "path";
8347
+ import { basename as basename4, dirname as dirname10, extname as extname4, join as join17 } from "path";
8122
8348
  import parseStaticImports from "parse-static-imports";
8123
8349
  var extensions = ["js", "mjs", "jsx", "ts", "mts", "tsx"];
8124
8350
  var generateFileHashes = async (file, hashes) => {
@@ -8139,8 +8365,8 @@ var fingerprintFromDirectory = async (dir) => {
8139
8365
  const hashes = /* @__PURE__ */ new Map();
8140
8366
  const files = await readdir4(dir, { recursive: true });
8141
8367
  for (const file of files) {
8142
- if (extensions.includes(extname3(file).substring(1)) && file.at(0) !== "_") {
8143
- await generateFileHashes(join16(dir, file), hashes);
8368
+ if (extensions.includes(extname4(file).substring(1)) && file.at(0) !== "_") {
8369
+ await generateFileHashes(join17(dir, file), hashes);
8144
8370
  }
8145
8371
  }
8146
8372
  const merge2 = Buffer.concat(Array.from(hashes.values()).sort());
@@ -8154,7 +8380,7 @@ var readModuleFile = (file) => {
8154
8380
  return readFiles([
8155
8381
  file,
8156
8382
  ...extensions.map((exp) => `${file}.${exp}`),
8157
- ...extensions.map((exp) => join16(file, `/index.${exp}`))
8383
+ ...extensions.map((exp) => join17(file, `/index.${exp}`))
8158
8384
  ]);
8159
8385
  }
8160
8386
  return readFile5(file, "utf8");
@@ -8174,7 +8400,7 @@ var readFiles = async (files) => {
8174
8400
  };
8175
8401
  var findDependencies = async (file, code) => {
8176
8402
  const imports = await parseStaticImports(code);
8177
- return imports.map((entry) => entry.moduleName).filter(Boolean).map((value) => value?.startsWith(".") ? join16(dirname10(file), value) : value);
8403
+ return imports.map((entry) => entry.moduleName).filter(Boolean).map((value) => value?.startsWith(".") ? join17(dirname10(file), value) : value);
8178
8404
  };
8179
8405
 
8180
8406
  // src/test/reporter.ts
@@ -8254,7 +8480,7 @@ var CustomReporter = class {
8254
8480
  import commonjs2 from "@rollup/plugin-commonjs";
8255
8481
  import json2 from "@rollup/plugin-json";
8256
8482
  import nodeResolve2 from "@rollup/plugin-node-resolve";
8257
- import { dirname as dirname11, join as join17 } from "path";
8483
+ import { dirname as dirname11, join as join18 } from "path";
8258
8484
  import { swc as swc2 } from "rollup-plugin-swc3";
8259
8485
  import { fileURLToPath as fileURLToPath4 } from "url";
8260
8486
  import { configDefaults } from "vitest/config";
@@ -8276,7 +8502,7 @@ var startTest = async (props) => {
8276
8502
  reporters: props.reporter,
8277
8503
  setupFiles: [
8278
8504
  //
8279
- join17(__dirname4, "test-global-setup.js")
8505
+ join18(__dirname4, "test-global-setup.js")
8280
8506
  ]
8281
8507
  // globalSetup: [
8282
8508
  // //
@@ -8366,7 +8592,7 @@ var logTestErrors = (event) => {
8366
8592
  var runTest = async (stack, dir, filters, opts) => {
8367
8593
  await mkdir4(directories.test, { recursive: true });
8368
8594
  const fingerprint = await fingerprintFromDirectory(dir);
8369
- const file = join18(directories.test, `${stack}.json`);
8595
+ const file = join19(directories.test, `${stack}.json`);
8370
8596
  const exists = await fileExist(file);
8371
8597
  if (exists && !process.env.NO_CACHE) {
8372
8598
  const raw = await readFile6(file, { encoding: "utf8" });
@@ -8609,7 +8835,7 @@ var auth = (program2) => {
8609
8835
 
8610
8836
  // src/cli/command/bind.ts
8611
8837
  import { log as log17 } from "@awsless/clui";
8612
- import { constantCase as constantCase15 } from "change-case";
8838
+ import { constantCase as constantCase16 } from "change-case";
8613
8839
  import { spawn } from "child_process";
8614
8840
  var bind = (program2) => {
8615
8841
  program2.command("bind").argument("[command...]", "The command to execute").option("--config <string...>", "List of config values that will be accessable", (v) => v.split(",")).description(`Bind your site environment variables to a command`).action(async (commands9 = [], opts) => {
@@ -8638,10 +8864,10 @@ var bind = (program2) => {
8638
8864
  const configList = opts.config ?? [];
8639
8865
  const configs = {};
8640
8866
  for (const name of configList) {
8641
- configs[`CONFIG_${constantCase15(name)}`] = name;
8867
+ configs[`CONFIG_${constantCase16(name)}`] = name;
8642
8868
  }
8643
8869
  if (configList.length ?? 0 > 0) {
8644
- log17.note("Bind Config", configList.map((v) => color.label(constantCase15(v))).join("\n"));
8870
+ log17.note("Bind Config", configList.map((v) => color.label(constantCase16(v))).join("\n"));
8645
8871
  }
8646
8872
  if (commands9.length === 0) {
8647
8873
  return "No command to execute.";
@@ -8704,7 +8930,7 @@ import { log as log18 } from "@awsless/clui";
8704
8930
 
8705
8931
  // src/type-gen/generate.ts
8706
8932
  import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
8707
- import { dirname as dirname12, join as join19, relative as relative7 } from "path";
8933
+ import { dirname as dirname12, join as join20, relative as relative7 } from "path";
8708
8934
  var generateTypes = async (props) => {
8709
8935
  const files = [];
8710
8936
  await Promise.all(
@@ -8713,7 +8939,7 @@ var generateTypes = async (props) => {
8713
8939
  ...props,
8714
8940
  async write(file, data, include = false) {
8715
8941
  const code = data?.toString("utf8");
8716
- const path = join19(directories.types, file);
8942
+ const path = join20(directories.types, file);
8717
8943
  if (code) {
8718
8944
  if (include) {
8719
8945
  files.push(relative7(directories.root, path));
@@ -8727,7 +8953,7 @@ var generateTypes = async (props) => {
8727
8953
  );
8728
8954
  if (files.length) {
8729
8955
  const code = files.map((file) => `/// <reference path='${file}' />`).join("\n");
8730
- await writeFile4(join19(directories.root, `awsless.d.ts`), code);
8956
+ await writeFile4(join20(directories.root, `awsless.d.ts`), code);
8731
8957
  }
8732
8958
  };
8733
8959
 
@@ -8769,7 +8995,7 @@ var resources = (program2) => {
8769
8995
  const accountId = await getAccountId(credentials, region);
8770
8996
  const { app } = createApp({ appConfig, stackConfigs, accountId });
8771
8997
  const formatResource = (stack, urn) => {
8772
- return urn.replace(stack.urn + ":", "").replace(/\{([a-z0-9\-\s\/\._]+)\}/gi, (_, v) => {
8998
+ return urn.replace(stack.urn + ":", "").replace(/\{([a-z0-9\-\s\/\.\@\_]+)\}/gi, (_, v) => {
8773
8999
  return `${color.dim("{")}${color.warning(v)}${color.dim("}")}`;
8774
9000
  }).replaceAll(":", color.dim(":"));
8775
9001
  };