@awsless/cli 0.0.31 → 0.0.33

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
@@ -1179,7 +1179,7 @@ var LogRetentionSchema = DurationSchema.refine(
1179
1179
  (duration) => {
1180
1180
  return validLogRetentionDays.includes(toDays(duration));
1181
1181
  },
1182
- `Invalid log retention. Valid days are: ${validLogRetentionDays.map((days12) => `${days12}`).join(", ")}`
1182
+ `Invalid log retention. Valid days are: ${validLogRetentionDays.map((days13) => `${days13}`).join(", ")}`
1183
1183
  ).describe("The log retention duration.");
1184
1184
  var LogSchema = z15.union([
1185
1185
  z15.boolean().transform((enabled) => ({ retention: enabled ? days(7) : days(0) })),
@@ -1359,140 +1359,159 @@ var OnFailureDefaultSchema = z19.union([
1359
1359
  );
1360
1360
 
1361
1361
  // src/feature/pubsub/schema.ts
1362
+ import { days as days4 } from "@awsless/duration";
1363
+ import { z as z22 } from "zod";
1364
+
1365
+ // src/feature/instance/schema.ts
1366
+ import { days as days2, toDays as toDays2 } from "@awsless/duration";
1367
+ import { toMebibytes } from "@awsless/size";
1362
1368
  import { z as z20 } from "zod";
1363
- var DomainSchema = ResourceIdSchema.describe("The domain id to link your Pubsub API with.");
1364
- var PubSubDefaultSchema = z20.record(
1365
- ResourceIdSchema,
1366
- z20.object({
1367
- auth: FunctionSchema,
1368
- domain: DomainSchema.optional(),
1369
- subDomain: z20.string().optional()
1370
- // auth: z.union([
1371
- // ResourceIdSchema,
1372
- // z.object({
1373
- // authorizer: FunctionSchema,
1374
- // // ttl: AuthorizerTtl.default('1 hour'),
1375
- // }),
1376
- // ]),
1377
- // policy: z
1378
- // .object({
1379
- // publish: z.array(z.string()).optional(),
1380
- // subscribe: z.array(z.string()).optional(),
1381
- // })
1382
- // .optional(),
1383
- })
1384
- ).optional().describe("Define the pubsub subscriber in your stack.");
1385
- var RetryAttemptsSchema2 = z20.number().int().min(0).max(2).describe(
1386
- "The maximum number of times to retry when the function returns an error. You can specify a number from 0 to 2."
1369
+ var CpuSchema = z20.union([z20.literal(0.25), z20.literal(0.5), z20.literal(1), z20.literal(2), z20.literal(4), z20.literal(8), z20.literal(16)]).transform((v) => `${v} vCPU`).describe(
1370
+ "The number of virtual CPU units (vCPU) used by the instance. Valid values: 0.25, 0.5, 1, 2, 4, 8, 16 vCPU."
1387
1371
  );
1388
- var PubSubSchema = z20.record(
1389
- ResourceIdSchema,
1372
+ var validMemorySize = [
1373
+ // 0.25 vCPU
1374
+ 512,
1375
+ 1024,
1376
+ 2048,
1377
+ // 0.5 vCPU
1378
+ 1024,
1379
+ 2048,
1380
+ 3072,
1381
+ 4096,
1382
+ // 1 vCPU
1383
+ 2048,
1384
+ 3072,
1385
+ 4096,
1386
+ 5120,
1387
+ 6144,
1388
+ 7168,
1389
+ 8192,
1390
+ // 2 vCPU
1391
+ 4096,
1392
+ 5120,
1393
+ 6144,
1394
+ 7168,
1395
+ 8192,
1396
+ 9216,
1397
+ 10240,
1398
+ 11264,
1399
+ 12288,
1400
+ 13312,
1401
+ 14336,
1402
+ 15360,
1403
+ 16384
1404
+ ];
1405
+ var MemorySizeSchema2 = SizeSchema.refine(
1406
+ (s) => validMemorySize.includes(toMebibytes(s)),
1407
+ `Invalid memory size. Allowed sizes: ${validMemorySize.join(", ")} MiB`
1408
+ ).describe("The amount of memory (in MiB) used by the instance. Valid memory values depend on the CPU configuration.");
1409
+ var HealthCheckSchema = z20.object({
1410
+ path: z20.string().describe("The path that the container runs to determine if it is healthy."),
1411
+ interval: DurationSchema.describe("The time period in seconds between each health check execution."),
1412
+ retries: z20.number().int().min(1).max(10).describe(
1413
+ "The number of times to retry a failed health check before the container is considered unhealthy."
1414
+ ),
1415
+ startPeriod: DurationSchema.describe(
1416
+ "The optional grace period to provide containers time to bootstrap before failed health checks count towards the maximum number of retries."
1417
+ ),
1418
+ timeout: DurationSchema.describe(
1419
+ "The time period in seconds to wait for a health check to succeed before it is considered a failure."
1420
+ )
1421
+ }).describe("The health check command and associated configuration parameters for the container.");
1422
+ var EnvironmentSchema2 = z20.record(z20.string(), z20.string()).optional().describe("Environment variable key-value pairs.");
1423
+ var ArchitectureSchema3 = z20.enum(["x86_64", "arm64"]).describe("The instruction set architecture that the instance supports.");
1424
+ var ActionSchema2 = z20.string();
1425
+ var ActionsSchema2 = z20.union([ActionSchema2.transform((v) => [v]), ActionSchema2.array()]);
1426
+ var ArnSchema2 = z20.string().startsWith("arn:");
1427
+ var WildcardSchema2 = z20.literal("*");
1428
+ var ResourceSchema2 = z20.union([ArnSchema2, WildcardSchema2]);
1429
+ var ResourcesSchema2 = z20.union([ResourceSchema2.transform((v) => [v]), ResourceSchema2.array()]);
1430
+ var PermissionSchema2 = z20.object({
1431
+ effect: z20.enum(["allow", "deny"]).default("allow"),
1432
+ actions: ActionsSchema2,
1433
+ resources: ResourcesSchema2
1434
+ });
1435
+ var PermissionsSchema2 = z20.union([PermissionSchema2.transform((v) => [v]), PermissionSchema2.array()]).describe("Add IAM permissions to your instance.");
1436
+ var DescriptionSchema2 = z20.string().describe("A description of the instance.");
1437
+ var ImageSchema = z20.string().optional().describe("The URL of the container image to use. Default: public.ecr.aws/aws-cli/aws-cli:{architecture}");
1438
+ var validLogRetentionDays2 = [
1439
+ ...[1, 3, 5, 7, 14, 30, 60, 90, 120, 150],
1440
+ ...[180, 365, 400, 545, 731, 1096, 1827, 2192],
1441
+ ...[2557, 2922, 3288, 3653]
1442
+ ];
1443
+ var LogRetentionSchema2 = DurationSchema.refine(
1444
+ durationMin(days2(0)),
1445
+ "Minimum log retention is 0 day, which will disable logging."
1446
+ ).refine(
1447
+ (duration) => {
1448
+ return validLogRetentionDays2.includes(toDays2(duration));
1449
+ },
1450
+ `Invalid log retention. Valid days are: ${validLogRetentionDays2.map((days13) => `${days13}`).join(", ")}`
1451
+ ).describe("The log retention duration.");
1452
+ var LogSchema2 = z20.union([
1453
+ z20.boolean().transform((enabled) => ({ retention: enabled ? days2(7) : days2(0) })),
1454
+ LogRetentionSchema2.transform((retention) => ({ retention })),
1390
1455
  z20.object({
1391
- sql: z20.string().describe("The SQL statement used to query the IOT topic."),
1392
- sqlVersion: z20.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."),
1393
- consumer: FunctionSchema.describe("The consuming lambda function properties."),
1394
- retryAttempts: RetryAttemptsSchema2.default(2)
1456
+ retention: LogRetentionSchema2.optional()
1395
1457
  })
1396
- ).optional().describe("Define the pubsub subscriber in your stack.");
1397
-
1398
- // src/feature/queue/schema.ts
1399
- import { days as days2, hours, minutes as minutes2, seconds as seconds2 } from "@awsless/duration";
1400
- import { kibibytes } from "@awsless/size";
1401
- import { z as z21 } from "zod";
1402
- var RetentionPeriodSchema = DurationSchema.refine(durationMin(minutes2(1)), "Minimum retention period is 1 minute").refine(durationMax(days2(14)), "Maximum retention period is 14 days").describe(
1403
- "The number of seconds that Amazon SQS retains a message. You can specify a duration from 1 minute to 14 days."
1404
- );
1405
- var VisibilityTimeoutSchema = DurationSchema.refine(
1406
- durationMax(hours(12)),
1407
- "Maximum visibility timeout is 12 hours"
1408
- ).describe(
1409
- "The length of time during which a message will be unavailable after a message is delivered from the queue. You can specify a duration from 0 to 12 hours."
1410
- );
1411
- var ReceiveMessageWaitTimeSchema = DurationSchema.refine(
1412
- durationMin(seconds2(1)),
1413
- "Minimum receive message wait time is 1 second"
1414
- ).refine(durationMax(seconds2(20)), "Maximum receive message wait time is 20 seconds").describe("Long-polling wait time. You can specify a duration from 1 to 20 seconds.");
1415
- var MaxMessageSizeSchema = SizeSchema.refine(sizeMin(kibibytes(1)), "Minimum max message size is 1 KB").refine(sizeMax(kibibytes(256)), "Maximum max message size is 256 KB").describe("Message size limit. You can specify a size from 1 KB to 256 KB.");
1416
- var BatchSizeSchema = z21.number().int().min(1, "Minimum batch size is 1").max(10, "FIFO queues support a maximum batch size of 10").describe("The maximum number of records per batch. FIFO queues are capped at 10.");
1417
- var RetryAttemptsSchema3 = z21.number().int().min(0).max(999).describe(
1418
- "The maximum number of times to retry when the function returns an error. You can specify a number from 0 to 999."
1419
- );
1420
- var QueueDefaultSchema = z21.object({
1421
- retentionPeriod: RetentionPeriodSchema.default("7 days"),
1422
- visibilityTimeout: VisibilityTimeoutSchema.default("2 minutes"),
1423
- receiveMessageWaitTime: ReceiveMessageWaitTimeSchema.optional(),
1424
- maxMessageSize: MaxMessageSizeSchema.default("256 KB"),
1425
- batchSize: BatchSizeSchema.default(10),
1426
- retryAttempts: RetryAttemptsSchema3.default(2)
1427
- }).default({});
1428
- var QueueSchema = z21.object({
1429
- consumer: FunctionSchema.optional().describe("The consuming lambda function properties."),
1430
- retentionPeriod: RetentionPeriodSchema.optional(),
1431
- visibilityTimeout: VisibilityTimeoutSchema.optional(),
1432
- receiveMessageWaitTime: ReceiveMessageWaitTimeSchema.optional(),
1433
- maxMessageSize: MaxMessageSizeSchema.optional(),
1434
- batchSize: BatchSizeSchema.optional(),
1435
- retryAttempts: RetryAttemptsSchema3.optional()
1458
+ ]).describe("Enable logging to a CloudWatch log group. Providing a duration value will set the log retention time.");
1459
+ var FileCodeSchema2 = z20.object({
1460
+ file: LocalFileSchema.describe("The file path of the instance code.")
1436
1461
  });
1437
- var QueuesSchema = z21.record(
1438
- ResourceIdSchema,
1439
- z21.union([
1440
- LocalFileSchema.transform((consumer) => ({
1441
- consumer
1442
- })).pipe(QueueSchema),
1443
- QueueSchema
1444
- ])
1445
- ).optional().describe(
1446
- "Define the queues in your stack. Queues are FIFO with required per-message groupId: messages with the same groupId are processed strictly in order; different groupIds parallelize."
1447
- );
1448
-
1449
- // src/feature/rest/schema.ts
1450
- import { z as z23 } from "zod";
1451
-
1452
- // src/config/schema/route.ts
1453
- import { z as z22 } from "zod";
1454
- var RouteSchema = z22.union([
1455
- z22.string().regex(/^(POST|GET|PUT|DELETE|HEAD|OPTIONS|ANY)(\s\/[a-z0-9\+\_\-\/\{\}]*)$/gi, "Invalid route"),
1456
- z22.literal("$default")
1462
+ var CodeSchema2 = z20.union([
1463
+ LocalFileSchema.transform((file) => ({
1464
+ file
1465
+ })).pipe(FileCodeSchema2),
1466
+ FileCodeSchema2
1467
+ ]).describe("Specify the code of your instance.");
1468
+ var StartupCommandSchema = z20.union([z20.string().transform((v) => [v]), z20.string().array()]).describe("Optional shell commands to run before the instance program starts.");
1469
+ var ISchema = z20.object({
1470
+ code: CodeSchema2,
1471
+ description: DescriptionSchema2.optional(),
1472
+ image: ImageSchema.optional(),
1473
+ startupCommand: StartupCommandSchema.optional(),
1474
+ log: LogSchema2.optional(),
1475
+ cpu: CpuSchema.optional(),
1476
+ memorySize: MemorySizeSchema2.optional(),
1477
+ architecture: ArchitectureSchema3.optional(),
1478
+ environment: EnvironmentSchema2.optional(),
1479
+ permissions: PermissionsSchema2.optional(),
1480
+ healthCheck: HealthCheckSchema.optional()
1481
+ // restartPolicy: RestartPolicySchema.optional(),
1482
+ });
1483
+ var InstanceSchema = z20.union([
1484
+ LocalFileSchema.transform((code) => ({
1485
+ code
1486
+ })).pipe(ISchema),
1487
+ ISchema
1457
1488
  ]);
1458
-
1459
- // src/feature/rest/schema.ts
1460
- var RestDefaultSchema = z23.record(
1461
- ResourceIdSchema,
1462
- z23.object({
1463
- domain: ResourceIdSchema.describe("The domain id to link your API with.").optional(),
1464
- subDomain: z23.string().optional()
1465
- })
1466
- ).optional().describe("Define your global REST API's.");
1467
- var RestSchema = z23.record(
1468
- ResourceIdSchema,
1469
- z23.record(
1470
- RouteSchema.describe(
1471
- [
1472
- "The REST API route that is comprised by the http method and http path.",
1473
- "The possible http methods are POST, GET,PUT, DELETE, HEAD, OPTIONS, ANY.",
1474
- "Example: GET /posts/{id}"
1475
- ].join("\n")
1476
- ),
1477
- FunctionSchema
1478
- )
1479
- ).optional().describe("Define routes in your stack for your global REST API.");
1480
-
1481
- // src/feature/rpc/schema.ts
1482
- import { minutes as minutes4, seconds as seconds3 } from "@awsless/duration";
1483
- import { z as z25 } from "zod";
1489
+ var InstancesSchema = z20.record(ResourceIdSchema, InstanceSchema).optional().describe("Define the instances in your stack.");
1490
+ var InstanceDefaultSchema = z20.object({
1491
+ image: ImageSchema.optional(),
1492
+ cpu: CpuSchema.default(0.25),
1493
+ memorySize: MemorySizeSchema2.default("512 MB"),
1494
+ architecture: ArchitectureSchema3.default("arm64"),
1495
+ environment: EnvironmentSchema2.optional(),
1496
+ permissions: PermissionsSchema2.optional(),
1497
+ healthCheck: HealthCheckSchema.optional(),
1498
+ // restartPolicy: RestartPolicySchema.default({ enabled: true }),
1499
+ log: LogSchema2.default(true).transform((log35) => ({
1500
+ retention: log35.retention ?? days2(7)
1501
+ }))
1502
+ }).default({});
1484
1503
 
1485
1504
  // src/feature/router/schema.ts
1486
- import { days as days3, minutes as minutes3, parse as parse3 } from "@awsless/duration";
1487
- import { z as z24 } from "zod";
1488
- var ErrorResponsePathSchema = z24.string().describe(
1505
+ import { days as days3, minutes as minutes2, parse as parse3 } from "@awsless/duration";
1506
+ import { z as z21 } from "zod";
1507
+ var ErrorResponsePathSchema = z21.string().describe(
1489
1508
  [
1490
1509
  "The path to the custom error page that you want to return to the viewer when your origin returns the HTTP status code specified.",
1491
1510
  "- We recommend that you store custom error pages in an Amazon S3 bucket.",
1492
1511
  "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."
1493
1512
  ].join("\n")
1494
1513
  );
1495
- var StatusCodeSchema = z24.number().int().positive().optional().describe(
1514
+ var StatusCodeSchema = z21.number().int().positive().optional().describe(
1496
1515
  [
1497
1516
  "The HTTP status code that you want CloudFront to return to the viewer along with the custom error page.",
1498
1517
  "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:",
@@ -1505,26 +1524,26 @@ var StatusCodeSchema = z24.number().int().positive().optional().describe(
1505
1524
  var MinTTLSchema = DurationSchema.describe(
1506
1525
  "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."
1507
1526
  );
1508
- var ErrorResponseSchema = z24.union([
1527
+ var ErrorResponseSchema = z21.union([
1509
1528
  ErrorResponsePathSchema,
1510
- z24.object({
1529
+ z21.object({
1511
1530
  path: ErrorResponsePathSchema,
1512
1531
  statusCode: StatusCodeSchema.optional(),
1513
1532
  minTTL: MinTTLSchema.optional()
1514
1533
  })
1515
1534
  ]).optional();
1516
- var RouteSchema2 = z24.string().regex(/^\//, "Route must start with a slash (/)");
1517
- var VisibilitySchema = z24.boolean().default(false).describe("Whether to enable CloudWatch metrics for the WAF rule.");
1518
- var WafSettingsSchema = z24.object({
1519
- rateLimiter: z24.object({
1520
- limit: z24.number().min(10).max(2e9).default(10).describe(
1535
+ var RouteSchema = z21.string().regex(/^\//, "Route must start with a slash (/)");
1536
+ var VisibilitySchema = z21.boolean().default(false).describe("Whether to enable CloudWatch metrics for the WAF rule.");
1537
+ var WafSettingsSchema = z21.object({
1538
+ rateLimiter: z21.object({
1539
+ limit: z21.number().min(10).max(2e9).default(10).describe(
1521
1540
  "The limit on requests during the specified evaluation window for a single aggregation instance for the rate-based rule."
1522
1541
  ),
1523
- window: z24.union([
1524
- z24.literal("1 minute"),
1525
- z24.literal("2 minutes"),
1526
- z24.literal("5 minutes"),
1527
- z24.literal("10 minutes")
1542
+ window: z21.union([
1543
+ z21.literal("1 minute"),
1544
+ z21.literal("2 minutes"),
1545
+ z21.literal("5 minutes"),
1546
+ z21.literal("10 minutes")
1528
1547
  ]).default("5 minutes").transform((v) => parse3(v)).describe(
1529
1548
  "The amount of time, in seconds, that AWS WAF should include in its request counts, looking back from the current time."
1530
1549
  ),
@@ -1532,39 +1551,39 @@ var WafSettingsSchema = z24.object({
1532
1551
  }).optional().describe(
1533
1552
  "A rate-based rule counts incoming requests and rate limits requests when they are coming at too fast a rate."
1534
1553
  ),
1535
- ddosProtection: z24.object({
1536
- sensitivity: z24.object({
1537
- challenge: z24.enum(["low", "medium", "high"]).default("low").transform((v) => v.toUpperCase()).describe("The sensitivity level for challenge requests."),
1538
- block: z24.enum(["low", "medium", "high"]).default("low").transform((v) => v.toUpperCase()).describe("The sensitivity level for block requests.")
1554
+ ddosProtection: z21.object({
1555
+ sensitivity: z21.object({
1556
+ challenge: z21.enum(["low", "medium", "high"]).default("low").transform((v) => v.toUpperCase()).describe("The sensitivity level for challenge requests."),
1557
+ block: z21.enum(["low", "medium", "high"]).default("low").transform((v) => v.toUpperCase()).describe("The sensitivity level for block requests.")
1539
1558
  }),
1540
- exemptUriRegex: z24.string().default("^$"),
1559
+ exemptUriRegex: z21.string().default("^$"),
1541
1560
  visibility: VisibilitySchema
1542
1561
  }).optional().describe(
1543
1562
  "Provides protection against DDoS attacks targeting the application layer, also known as Layer 7 attacks. Uses 50 WCU."
1544
1563
  ),
1545
- botProtection: z24.object({
1546
- inspectionLevel: z24.enum(["common", "targeted"]).default("common").transform((v) => v.toUpperCase()),
1564
+ botProtection: z21.object({
1565
+ inspectionLevel: z21.enum(["common", "targeted"]).default("common").transform((v) => v.toUpperCase()),
1547
1566
  visibility: VisibilitySchema
1548
1567
  }).optional().describe(
1549
1568
  "Provides protection against automated bots that can consume excess resources, skew business metrics, cause downtime, or perform malicious activities. Bot Control provides additional visibility through Amazon CloudWatch and generates labels that you can use to control bot traffic to your applications. Uses 50 WCU."
1550
1569
  ),
1551
- captchaImmunityTime: DurationSchema.refine(durationMin(minutes3(1)), "Minimum timeout duration is 1 minute").refine(durationMax(days3(3)), "Maximum timeout duration is 3 days").default("5 minutes").describe(
1570
+ captchaImmunityTime: DurationSchema.refine(durationMin(minutes2(1)), "Minimum timeout duration is 1 minute").refine(durationMax(days3(3)), "Maximum timeout duration is 3 days").default("5 minutes").describe(
1552
1571
  "The amount of time that a CAPTCHA timestamp is considered valid by AWS WAF. The default setting is 5 minutes."
1553
1572
  ),
1554
- challengeImmunityTime: DurationSchema.refine(durationMin(minutes3(1)), "Minimum timeout duration is 1 minute").refine(durationMax(days3(3)), "Maximum timeout duration is 3 days").default("5 minutes").describe(
1573
+ challengeImmunityTime: DurationSchema.refine(durationMin(minutes2(1)), "Minimum timeout duration is 1 minute").refine(durationMax(days3(3)), "Maximum timeout duration is 3 days").default("5 minutes").describe(
1555
1574
  "The amount of time that a challenge timestamp is considered valid by AWS WAF. The default setting is 5 minutes."
1556
1575
  )
1557
1576
  }).describe(
1558
1577
  "WAF settings for the router. Each rule consumes Web ACL capacity units (WCUs). The total WCUs for a web ACL can't exceed 5000. Using over 1500 WCUs affects your costs."
1559
1578
  );
1560
- var RouterDefaultSchema = z24.record(
1579
+ var RouterDefaultSchema = z21.record(
1561
1580
  ResourceIdSchema,
1562
- z24.object({
1581
+ z21.object({
1563
1582
  domain: ResourceIdSchema.describe("The domain id to link your Router.").optional(),
1564
- subDomain: z24.string().optional(),
1583
+ subDomain: z21.string().optional(),
1565
1584
  waf: WafSettingsSchema.optional(),
1566
- geoRestrictions: z24.array(z24.string().length(2).toUpperCase()).default([]).describe("Specifies a blacklist of countries that should be blocked."),
1567
- errors: z24.object({
1585
+ geoRestrictions: z21.array(z21.string().length(2).toUpperCase()).default([]).describe("Specifies a blacklist of countries that should be blocked."),
1586
+ errors: z21.object({
1568
1587
  400: ErrorResponseSchema.describe("Customize a `400 Bad Request` response."),
1569
1588
  403: ErrorResponseSchema.describe("Customize a `403 Forbidden` response."),
1570
1589
  404: ErrorResponseSchema.describe("Customize a `404 Not Found` response."),
@@ -1577,26 +1596,26 @@ var RouterDefaultSchema = z24.record(
1577
1596
  503: ErrorResponseSchema.describe("Customize a `503 Service Unavailable` response."),
1578
1597
  504: ErrorResponseSchema.describe("Customize a `504 Gateway Timeout` response.")
1579
1598
  }).optional().describe("Customize the error responses for specific HTTP status codes."),
1580
- cors: z24.object({
1581
- override: z24.boolean().default(false),
1599
+ cors: z21.object({
1600
+ override: z21.boolean().default(false),
1582
1601
  maxAge: DurationSchema.default("365 days"),
1583
- exposeHeaders: z24.string().array().optional(),
1584
- credentials: z24.boolean().default(false),
1585
- headers: z24.string().array().default(["*"]),
1586
- origins: z24.string().array().default(["*"]),
1587
- methods: z24.enum(["GET", "DELETE", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "ALL"]).array().default(["ALL"])
1602
+ exposeHeaders: z21.string().array().optional(),
1603
+ credentials: z21.boolean().default(false),
1604
+ headers: z21.string().array().default(["*"]),
1605
+ origins: z21.string().array().default(["*"]),
1606
+ methods: z21.enum(["GET", "DELETE", "HEAD", "OPTIONS", "PATCH", "POST", "PUT", "ALL"]).array().default(["ALL"])
1588
1607
  }).optional().describe("Specify the cors headers."),
1589
- passwordAuth: z24.object({
1590
- password: z24.string().describe("Password.")
1608
+ passwordAuth: z21.object({
1609
+ password: z21.string().describe("Password.")
1591
1610
  }).optional().describe(
1592
1611
  [
1593
1612
  "Enable password authentication for the router.",
1594
1613
  'You can authenicate by adding a "authorization" header with the value "Password [YOUR_PASSWORD]".'
1595
1614
  ].join("\n")
1596
1615
  ),
1597
- basicAuth: z24.object({
1598
- username: z24.string().describe("Basic auth username."),
1599
- password: z24.string().describe("Basic auth password.")
1616
+ basicAuth: z21.object({
1617
+ username: z21.string().describe("Basic auth username."),
1618
+ password: z21.string().describe("Basic auth password.")
1600
1619
  }).optional().describe("Enable basic authentication for the router."),
1601
1620
  // security: z
1602
1621
  // .object({
@@ -1643,49 +1662,163 @@ var RouterDefaultSchema = z24.record(
1643
1662
  // })
1644
1663
  // .optional()
1645
1664
  // .describe('Specify the security policy.'),
1646
- cache: z24.object({
1647
- cookies: z24.string().array().optional().describe("Specifies the cookies that CloudFront includes in the cache key."),
1648
- headers: z24.string().array().optional().describe("Specifies the headers that CloudFront includes in the cache key."),
1649
- queries: z24.string().array().optional().describe("Specifies the query values that CloudFront includes in the cache key.")
1665
+ cache: z21.object({
1666
+ cookies: z21.string().array().optional().describe("Specifies the cookies that CloudFront includes in the cache key."),
1667
+ headers: z21.string().array().optional().describe("Specifies the headers that CloudFront includes in the cache key."),
1668
+ queries: z21.string().array().optional().describe("Specifies the query values that CloudFront includes in the cache key.")
1650
1669
  }).optional().describe(
1651
1670
  "Specifies the cookies, headers, and query values that CloudFront includes in the cache key."
1652
1671
  )
1653
1672
  })
1654
1673
  ).optional().describe(`Define the global Router. Backed by AWS CloudFront.`);
1655
1674
 
1656
- // src/feature/rpc/schema.ts
1657
- var TimeoutSchema2 = DurationSchema.refine(durationMin(seconds3(10)), "Minimum timeout duration is 10 seconds").refine(durationMax(minutes4(5)), "Maximum timeout duration is 5 minutes").describe(
1658
- [
1659
- "The amount of time that the RPC lambda is allowed run before stopping it.",
1660
- "You can specify a timeout from 10 second to 5 minutes.",
1661
- "The timeouts of all inner RPC functions will be capped at 80% of this timeout."
1662
- ].join(" ")
1675
+ // src/feature/pubsub/schema.ts
1676
+ var PubSubDefaultSchema = z22.record(
1677
+ ResourceIdSchema,
1678
+ z22.object({
1679
+ auth: FunctionSchema.describe(
1680
+ "The authorizer that validates the client auth token and returns the allowed topics."
1681
+ ),
1682
+ router: ResourceIdSchema.describe("The router id to route pubsub traffic through."),
1683
+ path: RouteSchema.default("/ws").describe("The base path on the router that exposes the pubsub endpoint."),
1684
+ log: LogSchema2.default(true).transform((log35) => ({
1685
+ retention: log35.retention ?? days4(7)
1686
+ }))
1687
+ })
1688
+ ).optional().describe("Define the pubsub API for your app. Backed by a websocket server on AWS Fargate.");
1689
+ var PubSubSchema = z22.record(
1690
+ ResourceIdSchema,
1691
+ z22.object({
1692
+ connected: FunctionSchema.optional().describe("Subscribe to the event when a client connects."),
1693
+ disconnected: FunctionSchema.optional().describe("Subscribe to the event when a client disconnects."),
1694
+ subscribed: FunctionSchema.optional().describe(
1695
+ "Subscribe to the event when a client subscribes to topics."
1696
+ ),
1697
+ unsubscribed: FunctionSchema.optional().describe(
1698
+ "Subscribe to the event when a client unsubscribes from topics."
1699
+ )
1700
+ })
1701
+ ).optional().describe("Define the pubsub event listeners in your stack.");
1702
+ var pubsubEventTypes = ["connected", "disconnected", "subscribed", "unsubscribed"];
1703
+
1704
+ // src/feature/queue/schema.ts
1705
+ import { days as days5, hours, minutes as minutes3, seconds as seconds2 } from "@awsless/duration";
1706
+ import { kibibytes } from "@awsless/size";
1707
+ import { z as z23 } from "zod";
1708
+ var RetentionPeriodSchema = DurationSchema.refine(durationMin(minutes3(1)), "Minimum retention period is 1 minute").refine(durationMax(days5(14)), "Maximum retention period is 14 days").describe(
1709
+ "The number of seconds that Amazon SQS retains a message. You can specify a duration from 1 minute to 14 days."
1710
+ );
1711
+ var VisibilityTimeoutSchema = DurationSchema.refine(
1712
+ durationMax(hours(12)),
1713
+ "Maximum visibility timeout is 12 hours"
1714
+ ).describe(
1715
+ "The length of time during which a message will be unavailable after a message is delivered from the queue. You can specify a duration from 0 to 12 hours."
1716
+ );
1717
+ var ReceiveMessageWaitTimeSchema = DurationSchema.refine(
1718
+ durationMin(seconds2(1)),
1719
+ "Minimum receive message wait time is 1 second"
1720
+ ).refine(durationMax(seconds2(20)), "Maximum receive message wait time is 20 seconds").describe("Long-polling wait time. You can specify a duration from 1 to 20 seconds.");
1721
+ var MaxMessageSizeSchema = SizeSchema.refine(sizeMin(kibibytes(1)), "Minimum max message size is 1 KB").refine(sizeMax(kibibytes(256)), "Maximum max message size is 256 KB").describe("Message size limit. You can specify a size from 1 KB to 256 KB.");
1722
+ var BatchSizeSchema = z23.number().int().min(1, "Minimum batch size is 1").max(10, "FIFO queues support a maximum batch size of 10").describe("The maximum number of records per batch. FIFO queues are capped at 10.");
1723
+ var RetryAttemptsSchema2 = z23.number().int().min(0).max(999).describe(
1724
+ "The maximum number of times to retry when the function returns an error. You can specify a number from 0 to 999."
1725
+ );
1726
+ var QueueDefaultSchema = z23.object({
1727
+ retentionPeriod: RetentionPeriodSchema.default("7 days"),
1728
+ visibilityTimeout: VisibilityTimeoutSchema.default("2 minutes"),
1729
+ receiveMessageWaitTime: ReceiveMessageWaitTimeSchema.optional(),
1730
+ maxMessageSize: MaxMessageSizeSchema.default("256 KB"),
1731
+ batchSize: BatchSizeSchema.default(10),
1732
+ retryAttempts: RetryAttemptsSchema2.default(2)
1733
+ }).default({});
1734
+ var QueueSchema = z23.object({
1735
+ consumer: FunctionSchema.optional().describe("The consuming lambda function properties."),
1736
+ retentionPeriod: RetentionPeriodSchema.optional(),
1737
+ visibilityTimeout: VisibilityTimeoutSchema.optional(),
1738
+ receiveMessageWaitTime: ReceiveMessageWaitTimeSchema.optional(),
1739
+ maxMessageSize: MaxMessageSizeSchema.optional(),
1740
+ batchSize: BatchSizeSchema.optional(),
1741
+ retryAttempts: RetryAttemptsSchema2.optional()
1742
+ });
1743
+ var QueuesSchema = z23.record(
1744
+ ResourceIdSchema,
1745
+ z23.union([
1746
+ LocalFileSchema.transform((consumer) => ({
1747
+ consumer
1748
+ })).pipe(QueueSchema),
1749
+ QueueSchema
1750
+ ])
1751
+ ).optional().describe(
1752
+ "Define the queues in your stack. Queues are FIFO with required per-message groupId: messages with the same groupId are processed strictly in order; different groupIds parallelize."
1663
1753
  );
1664
- var RpcDefaultSchema = z25.record(
1754
+
1755
+ // src/feature/rest/schema.ts
1756
+ import { z as z25 } from "zod";
1757
+
1758
+ // src/config/schema/route.ts
1759
+ import { z as z24 } from "zod";
1760
+ var RouteSchema2 = z24.union([
1761
+ z24.string().regex(/^(POST|GET|PUT|DELETE|HEAD|OPTIONS|ANY)(\s\/[a-z0-9\+\_\-\/\{\}]*)$/gi, "Invalid route"),
1762
+ z24.literal("$default")
1763
+ ]);
1764
+
1765
+ // src/feature/rest/schema.ts
1766
+ var RestDefaultSchema = z25.record(
1665
1767
  ResourceIdSchema,
1666
1768
  z25.object({
1667
- // domain: ResourceIdSchema.describe('The domain id to link your RPC API with.').optional(),
1668
- // subDomain: z.string().optional(),
1669
- //
1670
- router: ResourceIdSchema.describe("The router id to link your RPC API with."),
1671
- path: RouteSchema2.describe("The path inside the router to link your RPC API to."),
1672
- auth: FunctionSchema.optional().describe("The authentication handler for your RPC API."),
1673
- log: LogSchema.optional(),
1674
- timeout: TimeoutSchema2.default("1 minutes")
1769
+ domain: ResourceIdSchema.describe("The domain id to link your API with.").optional(),
1770
+ subDomain: z25.string().optional()
1675
1771
  })
1676
- ).describe(`Define the global RPC API's.`).optional();
1677
- var RpcSchema = z25.record(
1772
+ ).optional().describe("Define your global REST API's.");
1773
+ var RestSchema = z25.record(
1678
1774
  ResourceIdSchema,
1679
1775
  z25.record(
1680
- z25.string(),
1681
- z25.union([
1682
- FunctionSchema.transform((f) => ({
1683
- function: f,
1776
+ RouteSchema2.describe(
1777
+ [
1778
+ "The REST API route that is comprised by the http method and http path.",
1779
+ "The possible http methods are POST, GET,PUT, DELETE, HEAD, OPTIONS, ANY.",
1780
+ "Example: GET /posts/{id}"
1781
+ ].join("\n")
1782
+ ),
1783
+ FunctionSchema
1784
+ )
1785
+ ).optional().describe("Define routes in your stack for your global REST API.");
1786
+
1787
+ // src/feature/rpc/schema.ts
1788
+ import { minutes as minutes4, seconds as seconds3 } from "@awsless/duration";
1789
+ import { z as z26 } from "zod";
1790
+ var TimeoutSchema2 = DurationSchema.refine(durationMin(seconds3(10)), "Minimum timeout duration is 10 seconds").refine(durationMax(minutes4(5)), "Maximum timeout duration is 5 minutes").describe(
1791
+ [
1792
+ "The amount of time that the RPC lambda is allowed run before stopping it.",
1793
+ "You can specify a timeout from 10 second to 5 minutes.",
1794
+ "The timeouts of all inner RPC functions will be capped at 80% of this timeout."
1795
+ ].join(" ")
1796
+ );
1797
+ var RpcDefaultSchema = z26.record(
1798
+ ResourceIdSchema,
1799
+ z26.object({
1800
+ // domain: ResourceIdSchema.describe('The domain id to link your RPC API with.').optional(),
1801
+ // subDomain: z.string().optional(),
1802
+ //
1803
+ router: ResourceIdSchema.describe("The router id to link your RPC API with."),
1804
+ path: RouteSchema.describe("The path inside the router to link your RPC API to."),
1805
+ auth: FunctionSchema.optional().describe("The authentication handler for your RPC API."),
1806
+ log: LogSchema.optional(),
1807
+ timeout: TimeoutSchema2.default("1 minutes")
1808
+ })
1809
+ ).describe(`Define the global RPC API's.`).optional();
1810
+ var RpcSchema = z26.record(
1811
+ ResourceIdSchema,
1812
+ z26.record(
1813
+ z26.string(),
1814
+ z26.union([
1815
+ FunctionSchema.transform((f) => ({
1816
+ function: f,
1684
1817
  lock: false
1685
1818
  })),
1686
- z25.object({
1819
+ z26.object({
1687
1820
  function: FunctionSchema.describe("The RPC function to execute."),
1688
- lock: z25.boolean().describe(
1821
+ lock: z26.boolean().describe(
1689
1822
  [
1690
1823
  "Specify if the function should be locked on the `lockKey` returned from the auth function.",
1691
1824
  "An example would be returning the user ID as `lockKey`."
@@ -1697,136 +1830,10 @@ var RpcSchema = z25.record(
1697
1830
  ).describe("Define the schema in your stack for your global RPC API.").optional();
1698
1831
 
1699
1832
  // src/feature/job/schema.ts
1700
- import { days as days4, toDays as toDays2 } from "@awsless/duration";
1701
- import { toMebibytes } from "@awsless/size";
1702
- import { z as z26 } from "zod";
1703
- var CpuSchema = z26.union([z26.literal(0.25), z26.literal(0.5), z26.literal(1), z26.literal(2), z26.literal(4), z26.literal(8), z26.literal(16)]).transform((v) => `${v} vCPU`).describe("The number of virtual CPU units (vCPU) used by the job. Valid values: 0.25, 0.5, 1, 2, 4, 8, 16 vCPU.");
1704
- var validMemorySize = [
1705
- // 0.25 vCPU
1706
- 512,
1707
- 1024,
1708
- 2048,
1709
- // 0.5 vCPU
1710
- 1024,
1711
- 2048,
1712
- 3072,
1713
- 4096,
1714
- // 1 vCPU
1715
- 2048,
1716
- 3072,
1717
- 4096,
1718
- 5120,
1719
- 6144,
1720
- 7168,
1721
- 8192,
1722
- // 2 vCPU
1723
- 4096,
1724
- 5120,
1725
- 6144,
1726
- 7168,
1727
- 8192,
1728
- 9216,
1729
- 10240,
1730
- 11264,
1731
- 12288,
1732
- 13312,
1733
- 14336,
1734
- 15360,
1735
- 16384
1736
- ];
1737
- var MemorySizeSchema2 = SizeSchema.refine(
1738
- (s) => validMemorySize.includes(toMebibytes(s)),
1739
- `Invalid memory size. Allowed sizes: ${validMemorySize.join(", ")} MiB`
1740
- ).describe("The amount of memory (in MiB) used by the job. Valid memory values depend on the CPU configuration.");
1741
- var EnvironmentSchema2 = z26.record(z26.string(), z26.string()).optional().describe("Environment variable key-value pairs.");
1742
- var ArchitectureSchema3 = z26.enum(["x86_64", "arm64"]).describe("The instruction set architecture that the job supports.");
1743
- var ActionSchema2 = z26.string();
1744
- var ActionsSchema2 = z26.union([ActionSchema2.transform((v) => [v]), ActionSchema2.array()]);
1745
- var ArnSchema2 = z26.string().startsWith("arn:");
1746
- var WildcardSchema2 = z26.literal("*");
1747
- var ResourceSchema2 = z26.union([ArnSchema2, WildcardSchema2]);
1748
- var ResourcesSchema2 = z26.union([ResourceSchema2.transform((v) => [v]), ResourceSchema2.array()]);
1749
- var PermissionSchema2 = z26.object({
1750
- effect: z26.enum(["allow", "deny"]).default("allow"),
1751
- actions: ActionsSchema2,
1752
- resources: ResourcesSchema2
1753
- });
1754
- var PermissionsSchema2 = z26.union([PermissionSchema2.transform((v) => [v]), PermissionSchema2.array()]).describe("Add IAM permissions to your job.");
1755
- var validLogRetentionDays2 = [
1756
- ...[1, 3, 5, 7, 14, 30, 60, 90, 120, 150],
1757
- ...[180, 365, 400, 545, 731, 1096, 1827, 2192],
1758
- ...[2557, 2922, 3288, 3653]
1759
- ];
1760
- var LogRetentionSchema2 = DurationSchema.refine(
1761
- durationMin(days4(0)),
1762
- "Minimum log retention is 0 day, which will disable logging."
1763
- ).refine(
1764
- (duration) => {
1765
- return validLogRetentionDays2.includes(toDays2(duration));
1766
- },
1767
- `Invalid log retention. Valid days are: ${validLogRetentionDays2.map((days12) => `${days12}`).join(", ")}`
1768
- ).describe("The log retention duration.");
1769
- var LogSchema2 = z26.union([
1770
- z26.boolean().transform((enabled) => ({ retention: enabled ? days4(7) : days4(0) })),
1771
- LogRetentionSchema2.transform((retention) => ({ retention })),
1772
- z26.object({
1773
- retention: LogRetentionSchema2.optional()
1774
- })
1775
- ]).describe("Enable logging to a CloudWatch log group. Providing a duration value will set the log retention time.");
1776
- var FileCodeSchema2 = z26.object({
1777
- file: LocalFileSchema.describe("The file path of the job code.")
1778
- });
1779
- var CodeSchema2 = z26.union([
1780
- LocalFileSchema.transform((file) => ({
1781
- file
1782
- })).pipe(FileCodeSchema2),
1783
- FileCodeSchema2
1784
- ]).describe("Specify the code of your job.");
1785
- var TimeoutSchema3 = DurationSchema.describe("The maximum time the job is allowed to run before being stopped.");
1786
- var ImageSchema = z26.string().describe("The URL of the container image to use. Default: public.ecr.aws/aws-cli/aws-cli:{architecture}");
1787
- var PersistentStorageSchema = z26.boolean().describe("Mount persistent storage for the job at a fixed internal path.");
1788
- var StartupCommandSchema = z26.union([z26.string().transform((v) => [v]), z26.string().array()]).describe("Optional shell commands to run before the job executable is downloaded and started.");
1789
- var ASchema = z26.object({
1790
- code: CodeSchema2,
1791
- image: ImageSchema.optional(),
1792
- persistentStorage: PersistentStorageSchema.optional(),
1793
- startupCommand: StartupCommandSchema.optional(),
1794
- log: LogSchema2.optional(),
1795
- cpu: CpuSchema.optional(),
1796
- memorySize: MemorySizeSchema2.optional(),
1797
- architecture: ArchitectureSchema3.optional(),
1798
- environment: EnvironmentSchema2.optional(),
1799
- permissions: PermissionsSchema2.optional(),
1800
- timeout: TimeoutSchema3.default("30 minutes").describe("The maximum time the job is allowed to run before being stopped. Default: 30 minutes.")
1801
- });
1802
- var JobSchema = z26.union([
1803
- LocalFileSchema.transform((code) => ({
1804
- code
1805
- })).pipe(ASchema),
1806
- ASchema
1807
- ]);
1808
- var JobsSchema = z26.record(ResourceIdSchema, JobSchema).optional().describe("Define the jobs in your stack.");
1809
- var JobDefaultSchema = z26.object({
1810
- image: ImageSchema.optional(),
1811
- persistentStorage: PersistentStorageSchema.optional(),
1812
- cpu: CpuSchema.default(0.25),
1813
- memorySize: MemorySizeSchema2.default("512 MB"),
1814
- architecture: ArchitectureSchema3.default("arm64"),
1815
- environment: EnvironmentSchema2.optional(),
1816
- permissions: PermissionsSchema2.optional(),
1817
- timeout: TimeoutSchema3.optional(),
1818
- log: LogSchema2.default(true).transform((log35) => ({
1819
- retention: log35.retention ?? days4(7)
1820
- }))
1821
- }).default({});
1822
-
1823
- // src/feature/instance/schema.ts
1824
- import { days as days5, toDays as toDays3 } from "@awsless/duration";
1833
+ import { days as days6, toDays as toDays3 } from "@awsless/duration";
1825
1834
  import { toMebibytes as toMebibytes2 } from "@awsless/size";
1826
1835
  import { z as z27 } from "zod";
1827
- var CpuSchema2 = z27.union([z27.literal(0.25), z27.literal(0.5), z27.literal(1), z27.literal(2), z27.literal(4), z27.literal(8), z27.literal(16)]).transform((v) => `${v} vCPU`).describe(
1828
- "The number of virtual CPU units (vCPU) used by the instance. Valid values: 0.25, 0.5, 1, 2, 4, 8, 16 vCPU."
1829
- );
1836
+ var CpuSchema2 = z27.union([z27.literal(0.25), z27.literal(0.5), z27.literal(1), z27.literal(2), z27.literal(4), z27.literal(8), z27.literal(16)]).transform((v) => `${v} vCPU`).describe("The number of virtual CPU units (vCPU) used by the job. Valid values: 0.25, 0.5, 1, 2, 4, 8, 16 vCPU.");
1830
1837
  var validMemorySize2 = [
1831
1838
  // 0.25 vCPU
1832
1839
  512,
@@ -1863,22 +1870,9 @@ var validMemorySize2 = [
1863
1870
  var MemorySizeSchema3 = SizeSchema.refine(
1864
1871
  (s) => validMemorySize2.includes(toMebibytes2(s)),
1865
1872
  `Invalid memory size. Allowed sizes: ${validMemorySize2.join(", ")} MiB`
1866
- ).describe("The amount of memory (in MiB) used by the instance. Valid memory values depend on the CPU configuration.");
1867
- var HealthCheckSchema = z27.object({
1868
- path: z27.string().describe("The path that the container runs to determine if it is healthy."),
1869
- interval: DurationSchema.describe("The time period in seconds between each health check execution."),
1870
- retries: z27.number().int().min(1).max(10).describe(
1871
- "The number of times to retry a failed health check before the container is considered unhealthy."
1872
- ),
1873
- startPeriod: DurationSchema.describe(
1874
- "The optional grace period to provide containers time to bootstrap before failed health checks count towards the maximum number of retries."
1875
- ),
1876
- timeout: DurationSchema.describe(
1877
- "The time period in seconds to wait for a health check to succeed before it is considered a failure."
1878
- )
1879
- }).describe("The health check command and associated configuration parameters for the container.");
1873
+ ).describe("The amount of memory (in MiB) used by the job. Valid memory values depend on the CPU configuration.");
1880
1874
  var EnvironmentSchema3 = z27.record(z27.string(), z27.string()).optional().describe("Environment variable key-value pairs.");
1881
- var ArchitectureSchema4 = z27.enum(["x86_64", "arm64"]).describe("The instruction set architecture that the instance supports.");
1875
+ var ArchitectureSchema4 = z27.enum(["x86_64", "arm64"]).describe("The instruction set architecture that the job supports.");
1882
1876
  var ActionSchema3 = z27.string();
1883
1877
  var ActionsSchema3 = z27.union([ActionSchema3.transform((v) => [v]), ActionSchema3.array()]);
1884
1878
  var ArnSchema3 = z27.string().startsWith("arn:");
@@ -1890,44 +1884,45 @@ var PermissionSchema3 = z27.object({
1890
1884
  actions: ActionsSchema3,
1891
1885
  resources: ResourcesSchema3
1892
1886
  });
1893
- var PermissionsSchema3 = z27.union([PermissionSchema3.transform((v) => [v]), PermissionSchema3.array()]).describe("Add IAM permissions to your instance.");
1894
- var DescriptionSchema2 = z27.string().describe("A description of the instance.");
1895
- var ImageSchema2 = z27.string().optional().describe("The URL of the container image to use. Default: public.ecr.aws/aws-cli/aws-cli:{architecture}");
1887
+ var PermissionsSchema3 = z27.union([PermissionSchema3.transform((v) => [v]), PermissionSchema3.array()]).describe("Add IAM permissions to your job.");
1896
1888
  var validLogRetentionDays3 = [
1897
1889
  ...[1, 3, 5, 7, 14, 30, 60, 90, 120, 150],
1898
1890
  ...[180, 365, 400, 545, 731, 1096, 1827, 2192],
1899
1891
  ...[2557, 2922, 3288, 3653]
1900
1892
  ];
1901
1893
  var LogRetentionSchema3 = DurationSchema.refine(
1902
- durationMin(days5(0)),
1894
+ durationMin(days6(0)),
1903
1895
  "Minimum log retention is 0 day, which will disable logging."
1904
1896
  ).refine(
1905
1897
  (duration) => {
1906
1898
  return validLogRetentionDays3.includes(toDays3(duration));
1907
1899
  },
1908
- `Invalid log retention. Valid days are: ${validLogRetentionDays3.map((days12) => `${days12}`).join(", ")}`
1900
+ `Invalid log retention. Valid days are: ${validLogRetentionDays3.map((days13) => `${days13}`).join(", ")}`
1909
1901
  ).describe("The log retention duration.");
1910
1902
  var LogSchema3 = z27.union([
1911
- z27.boolean().transform((enabled) => ({ retention: enabled ? days5(7) : days5(0) })),
1903
+ z27.boolean().transform((enabled) => ({ retention: enabled ? days6(7) : days6(0) })),
1912
1904
  LogRetentionSchema3.transform((retention) => ({ retention })),
1913
1905
  z27.object({
1914
1906
  retention: LogRetentionSchema3.optional()
1915
1907
  })
1916
1908
  ]).describe("Enable logging to a CloudWatch log group. Providing a duration value will set the log retention time.");
1917
1909
  var FileCodeSchema3 = z27.object({
1918
- file: LocalFileSchema.describe("The file path of the instance code.")
1910
+ file: LocalFileSchema.describe("The file path of the job code.")
1919
1911
  });
1920
1912
  var CodeSchema3 = z27.union([
1921
1913
  LocalFileSchema.transform((file) => ({
1922
1914
  file
1923
1915
  })).pipe(FileCodeSchema3),
1924
1916
  FileCodeSchema3
1925
- ]).describe("Specify the code of your instance.");
1926
- var StartupCommandSchema2 = z27.union([z27.string().transform((v) => [v]), z27.string().array()]).describe("Optional shell commands to run before the instance program starts.");
1927
- var ISchema = z27.object({
1917
+ ]).describe("Specify the code of your job.");
1918
+ var TimeoutSchema3 = DurationSchema.describe("The maximum time the job is allowed to run before being stopped.");
1919
+ var ImageSchema2 = z27.string().describe("The URL of the container image to use. Default: public.ecr.aws/aws-cli/aws-cli:{architecture}");
1920
+ var PersistentStorageSchema = z27.boolean().describe("Mount persistent storage for the job at a fixed internal path.");
1921
+ var StartupCommandSchema2 = z27.union([z27.string().transform((v) => [v]), z27.string().array()]).describe("Optional shell commands to run before the job executable is downloaded and started.");
1922
+ var ASchema = z27.object({
1928
1923
  code: CodeSchema3,
1929
- description: DescriptionSchema2.optional(),
1930
1924
  image: ImageSchema2.optional(),
1925
+ persistentStorage: PersistentStorageSchema.optional(),
1931
1926
  startupCommand: StartupCommandSchema2.optional(),
1932
1927
  log: LogSchema3.optional(),
1933
1928
  cpu: CpuSchema2.optional(),
@@ -1935,27 +1930,26 @@ var ISchema = z27.object({
1935
1930
  architecture: ArchitectureSchema4.optional(),
1936
1931
  environment: EnvironmentSchema3.optional(),
1937
1932
  permissions: PermissionsSchema3.optional(),
1938
- healthCheck: HealthCheckSchema.optional()
1939
- // restartPolicy: RestartPolicySchema.optional(),
1933
+ timeout: TimeoutSchema3.default("30 minutes").describe("The maximum time the job is allowed to run before being stopped. Default: 30 minutes.")
1940
1934
  });
1941
- var InstanceSchema = z27.union([
1935
+ var JobSchema = z27.union([
1942
1936
  LocalFileSchema.transform((code) => ({
1943
1937
  code
1944
- })).pipe(ISchema),
1945
- ISchema
1938
+ })).pipe(ASchema),
1939
+ ASchema
1946
1940
  ]);
1947
- var InstancesSchema = z27.record(ResourceIdSchema, InstanceSchema).optional().describe("Define the instances in your stack.");
1948
- var InstanceDefaultSchema = z27.object({
1941
+ var JobsSchema = z27.record(ResourceIdSchema, JobSchema).optional().describe("Define the jobs in your stack.");
1942
+ var JobDefaultSchema = z27.object({
1949
1943
  image: ImageSchema2.optional(),
1944
+ persistentStorage: PersistentStorageSchema.optional(),
1950
1945
  cpu: CpuSchema2.default(0.25),
1951
1946
  memorySize: MemorySizeSchema3.default("512 MB"),
1952
1947
  architecture: ArchitectureSchema4.default("arm64"),
1953
1948
  environment: EnvironmentSchema3.optional(),
1954
1949
  permissions: PermissionsSchema3.optional(),
1955
- healthCheck: HealthCheckSchema.optional(),
1956
- // restartPolicy: RestartPolicySchema.default({ enabled: true }),
1950
+ timeout: TimeoutSchema3.optional(),
1957
1951
  log: LogSchema3.default(true).transform((log35) => ({
1958
- retention: log35.retention ?? days5(7)
1952
+ retention: log35.retention ?? days6(7)
1959
1953
  }))
1960
1954
  }).default({});
1961
1955
 
@@ -2153,7 +2147,7 @@ var CronExpressionSchema = z34.custom(
2153
2147
  var ScheduleExpressionSchema = RateExpressionSchema.or(CronExpressionSchema);
2154
2148
 
2155
2149
  // src/feature/cron/schema/index.ts
2156
- var RetryAttemptsSchema4 = z35.number().int().min(0).max(2).describe(
2150
+ var RetryAttemptsSchema3 = z35.number().int().min(0).max(2).describe(
2157
2151
  "The maximum number of times to retry when the function returns an error. You can specify a number from 0 to 2."
2158
2152
  );
2159
2153
  var CronsSchema = z35.record(
@@ -2165,7 +2159,7 @@ var CronsSchema = z35.record(
2165
2159
  'The scheduling expression.\n\nexample: "0 20 * * ? *"\nexample: "5 minutes"'
2166
2160
  ),
2167
2161
  payload: z35.unknown().optional().describe("The JSON payload that will be passed to the consumer."),
2168
- retryAttempts: RetryAttemptsSchema4.default(2)
2162
+ retryAttempts: RetryAttemptsSchema3.default(2)
2169
2163
  })
2170
2164
  ).optional().describe(`Define the cron jobs in your stack.`);
2171
2165
 
@@ -2295,7 +2289,7 @@ var SitesSchema = z38.record(
2295
2289
  ResourceIdSchema,
2296
2290
  z38.object({
2297
2291
  router: ResourceIdSchema.describe("The router id to link your site with."),
2298
- path: RouteSchema2.describe("The path inside the router to link your site to."),
2292
+ path: RouteSchema.describe("The path inside the router to link your site to."),
2299
2293
  build: z38.object({
2300
2294
  command: z38.string().describe(
2301
2295
  `Specifies the files and directories to generate the cache key for your custom build command.`
@@ -2313,11 +2307,11 @@ var SitesSchema = z38.record(
2313
2307
  ).optional().describe("Define the sites in your stack.");
2314
2308
 
2315
2309
  // src/feature/store/schema.ts
2316
- import { days as days6 } from "@awsless/duration";
2310
+ import { days as days7 } from "@awsless/duration";
2317
2311
  import { z as z39 } from "zod";
2318
2312
  var LifecycleRuleSchema = z39.object({
2319
2313
  prefix: z39.string().optional().describe("Object-key prefix this rule applies to. Omit to apply bucket-wide."),
2320
- expiration: DurationSchema.refine(durationMin(days6(1)), "Minimum expiration is 1 day").describe(
2314
+ expiration: DurationSchema.refine(durationMin(days7(1)), "Minimum expiration is 1 day").describe(
2321
2315
  "How long objects matching this rule live before S3 deletes them."
2322
2316
  )
2323
2317
  });
@@ -2381,7 +2375,7 @@ var IconsSchema = z40.record(
2381
2375
  // domain: ResourceIdSchema.describe('The domain id to link your site with.').optional(),
2382
2376
  // subDomain: z.string().optional(),
2383
2377
  router: ResourceIdSchema.describe("The router id to link your icon proxy."),
2384
- path: RouteSchema2.describe("The path inside the router to link your icon proxy to."),
2378
+ path: RouteSchema.describe("The path inside the router to link your icon proxy to."),
2385
2379
  log: LogSchema.optional(),
2386
2380
  cacheDuration: DurationSchema.optional().describe("The cache duration of the cached icons."),
2387
2381
  preserveIds: z40.boolean().optional().default(false).describe("Preserve the IDs of the icons."),
@@ -2438,7 +2432,7 @@ var ImagesSchema = z41.record(
2438
2432
  // domain: ResourceIdSchema.describe('The domain id to link your site with.').optional(),
2439
2433
  // subDomain: z.string().optional(),
2440
2434
  router: ResourceIdSchema.describe("The router id to link your image proxy."),
2441
- path: RouteSchema2.describe("The path inside the router to link your image proxy to."),
2435
+ path: RouteSchema.describe("The path inside the router to link your image proxy to."),
2442
2436
  log: LogSchema.optional(),
2443
2437
  cacheDuration: DurationSchema.optional().describe("Cache duration of the cached images."),
2444
2438
  presets: z41.record(z41.string(), transformationOptionsSchema).describe("Named presets for image transformations"),
@@ -3690,13 +3684,15 @@ var bundleTypeScriptWithRolldown = async ({
3690
3684
  file,
3691
3685
  nativeDir,
3692
3686
  external,
3687
+ externalAwsSdk = true,
3688
+ codeSplitting = true,
3693
3689
  importAsString: importAsStringList
3694
3690
  }) => {
3695
3691
  const bundle = await rolldown({
3696
3692
  input: file,
3697
3693
  platform: "node",
3698
3694
  external: (importee) => {
3699
- return importee.startsWith("@aws-sdk") || importee.startsWith("aws-sdk") || external?.includes(importee);
3695
+ return externalAwsSdk && (importee.startsWith("@aws-sdk") || importee.startsWith("aws-sdk")) || external?.includes(importee);
3700
3696
  },
3701
3697
  onwarn: (error) => {
3702
3698
  debugError(error.message);
@@ -3725,6 +3721,7 @@ var bundleTypeScriptWithRolldown = async ({
3725
3721
  exports: "auto",
3726
3722
  entryFileNames: `index.${ext}`,
3727
3723
  chunkFileNames: `[name].${ext}`,
3724
+ codeSplitting,
3728
3725
  minify
3729
3726
  });
3730
3727
  const hash = createHash2("sha1");
@@ -4549,7 +4546,7 @@ var functionFeature = defineFeature({
4549
4546
  });
4550
4547
 
4551
4548
  // src/feature/function/prebuild.ts
4552
- import { days as days7, seconds as seconds5, toDays as toDays6, toSeconds as toSeconds3 } from "@awsless/duration";
4549
+ import { days as days8, seconds as seconds5, toDays as toDays6, toSeconds as toSeconds3 } from "@awsless/duration";
4553
4550
  import { mebibytes as mebibytes2, toMebibytes as toMebibytes4 } from "@awsless/size";
4554
4551
  import { aws as aws8 } from "@terraforge/aws";
4555
4552
  import { Output as Output4, findInputDeps as findInputDeps2, resolveInputs as resolveInputs2 } from "@terraforge/core";
@@ -4614,6 +4611,28 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, props) => {
4614
4611
  statementDeps.add(dep);
4615
4612
  }
4616
4613
  };
4614
+ if (props.vpc) {
4615
+ new aws8.iam.RolePolicy(group, "vpc-policy", {
4616
+ role: role.name,
4617
+ name: "lambda-vpc-policy",
4618
+ policy: JSON.stringify({
4619
+ Version: "2012-10-17",
4620
+ Statement: [
4621
+ {
4622
+ Effect: "Allow",
4623
+ Action: [
4624
+ "ec2:CreateNetworkInterface",
4625
+ "ec2:DescribeNetworkInterfaces",
4626
+ "ec2:DeleteNetworkInterface",
4627
+ "ec2:AssignPrivateIpAddresses",
4628
+ "ec2:UnassignPrivateIpAddresses"
4629
+ ],
4630
+ Resource: ["*"]
4631
+ }
4632
+ ]
4633
+ })
4634
+ });
4635
+ }
4617
4636
  ctx.onPermission((statement) => {
4618
4637
  addPermission(statement);
4619
4638
  });
@@ -4662,6 +4681,11 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, props) => {
4662
4681
  environment: {
4663
4682
  variables
4664
4683
  },
4684
+ vpcConfig: props.vpc ? {
4685
+ securityGroupIds: [ctx.shared.get("vpc", "security-group-id")],
4686
+ subnetIds: ctx.shared.get("vpc", "private-subnets"),
4687
+ ipv6AllowedForDualStack: true
4688
+ } : void 0,
4665
4689
  loggingConfig: {
4666
4690
  logGroup: `/aws/lambda/${name}`,
4667
4691
  logFormat: logFormats[props.log && "format" in props.log && props.log.format || "json"],
@@ -4678,10 +4702,13 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, props) => {
4678
4702
  if ("stackConfig" in ctx) {
4679
4703
  variables.STACK = ctx.stackConfig.name;
4680
4704
  }
4705
+ if (props.vpc) {
4706
+ variables.AWS_USE_DUALSTACK_ENDPOINT = "true";
4707
+ }
4681
4708
  if (props.log?.retention && props.log?.retention?.value > 0n) {
4682
4709
  const logGroup = new aws8.cloudwatch.LogGroup(group, "log", {
4683
4710
  name: `/aws/lambda/${name}`,
4684
- retentionInDays: toDays6(props.log.retention ?? days7(7))
4711
+ retentionInDays: toDays6(props.log.retention ?? days8(7))
4685
4712
  });
4686
4713
  addPermission({
4687
4714
  actions: ["logs:PutLogEvents", "logs:CreateLogStream"],
@@ -4738,7 +4765,7 @@ import { Group as Group8 } from "@terraforge/core";
4738
4765
  import { aws as aws9 } from "@terraforge/aws";
4739
4766
  import { join as join10 } from "path";
4740
4767
  import { mebibytes as mebibytes3 } from "@awsless/size";
4741
- import { days as days8, seconds as seconds6 } from "@awsless/duration";
4768
+ import { days as days9, seconds as seconds6 } from "@awsless/duration";
4742
4769
  var onErrorLogFeature = defineFeature({
4743
4770
  name: "on-error-log",
4744
4771
  onApp(ctx) {
@@ -4753,7 +4780,7 @@ var onErrorLogFeature = defineFeature({
4753
4780
  log: {
4754
4781
  format: "json",
4755
4782
  level: "warn",
4756
- retention: days8(3),
4783
+ retention: days9(3),
4757
4784
  system: "warn"
4758
4785
  }
4759
4786
  });
@@ -4810,7 +4837,7 @@ import { Group as Group9 } from "@terraforge/core";
4810
4837
  import { aws as aws10 } from "@terraforge/aws";
4811
4838
  import { join as join11 } from "path";
4812
4839
  import { mebibytes as mebibytes4 } from "@awsless/size";
4813
- import { days as days9, toSeconds as toSeconds4 } from "@awsless/duration";
4840
+ import { days as days10, toSeconds as toSeconds4 } from "@awsless/duration";
4814
4841
  var onFailureFeature = defineFeature({
4815
4842
  name: "on-failure",
4816
4843
  onApp(ctx) {
@@ -4821,7 +4848,7 @@ var onFailureFeature = defineFeature({
4821
4848
  resourceType: "on-failure",
4822
4849
  resourceName: "deadletter"
4823
4850
  }),
4824
- messageRetentionSeconds: toSeconds4(days9(14))
4851
+ messageRetentionSeconds: toSeconds4(days10(14))
4825
4852
  });
4826
4853
  const queue2 = new aws10.sqs.Queue(group, "on-failure", {
4827
4854
  name: formatGlobalResourceName({
@@ -4969,7 +4996,7 @@ var onFailureFeature = defineFeature({
4969
4996
  log: {
4970
4997
  format: "json",
4971
4998
  level: "warn",
4972
- retention: days9(3),
4999
+ retention: days10(3),
4973
5000
  system: "warn"
4974
5001
  }
4975
5002
  });
@@ -5044,9 +5071,14 @@ var onFailureFeature = defineFeature({
5044
5071
  });
5045
5072
 
5046
5073
  // src/feature/pubsub/index.ts
5047
- import { Group as Group10 } from "@terraforge/core";
5048
- import { aws as aws11 } from "@terraforge/aws";
5074
+ import { seconds as seconds7 } from "@awsless/duration";
5075
+ import { mebibytes as mebibytes5 } from "@awsless/size";
5076
+ import { aws as aws12 } from "@terraforge/aws";
5077
+ import { Group as Group11 } from "@terraforge/core";
5049
5078
  import { constantCase as constantCase5 } from "change-case";
5079
+ import { createHmac as createHmac2 } from "crypto";
5080
+ import { dirname as dirname6, join as join14 } from "path";
5081
+ import { fileURLToPath as fileURLToPath2 } from "url";
5050
5082
 
5051
5083
  // src/feature/domain/util.ts
5052
5084
  var getDomainNameById = (config2, id) => {
@@ -5066,103 +5098,739 @@ var formatFullDomainName = (config2, id, subDomain) => {
5066
5098
  return domain2;
5067
5099
  };
5068
5100
 
5069
- // src/feature/pubsub/index.ts
5070
- import { minutes as minutes7, toSeconds as toSeconds5 } from "@awsless/duration";
5071
- var pubsubFeature = defineFeature({
5072
- name: "pubsub",
5073
- onApp(ctx) {
5074
- ctx.addGlobalPermission({
5075
- actions: ["iot:Publish"],
5076
- resources: [
5077
- // `arn:aws:iot:${ctx.appConfig.region}:${ctx.accountId}:topic/*`,
5078
- `arn:aws:iot:${ctx.appConfig.region}:${ctx.accountId}:topic/${ctx.app.name}/pubsub/*`
5079
- ]
5101
+ // src/feature/pubsub/util.ts
5102
+ import { toDays as toDays7 } from "@awsless/duration";
5103
+ import { stringify } from "@awsless/json";
5104
+ import { aws as aws11 } from "@terraforge/aws";
5105
+ import { Group as Group10, Output as Output5, findInputDeps as findInputDeps3, resolveInputs as resolveInputs3 } from "@terraforge/core";
5106
+ import { pascalCase as pascalCase3 } from "change-case";
5107
+ import { createHash as createHash4 } from "crypto";
5108
+ import { readFile as readFile5 } from "fs/promises";
5109
+ import { fileURLToPath } from "url";
5110
+ import { dirname as dirname5, join as join13 } from "path";
5111
+
5112
+ // src/feature/instance/build/executable.ts
5113
+ import { createHash as createHash3 } from "crypto";
5114
+ import { readFile as readFile4 } from "fs/promises";
5115
+ import { join as join12 } from "path";
5116
+ var buildExecutable = async (input, outputPath, architecture) => {
5117
+ const filePath = join12(outputPath, "program");
5118
+ const target = architecture === "x86_64" ? "bun-linux-x64" : "bun-linux-arm64";
5119
+ let result;
5120
+ try {
5121
+ result = await Bun.build({
5122
+ entrypoints: [input],
5123
+ compile: {
5124
+ target,
5125
+ outfile: filePath
5126
+ },
5127
+ target: "bun",
5128
+ loader: {
5129
+ ".md": "text",
5130
+ ".txt": "text",
5131
+ ".html": "text",
5132
+ ".css": "text",
5133
+ ".yaml": "text",
5134
+ ".yml": "text",
5135
+ ".xml": "text",
5136
+ ".csv": "text",
5137
+ ".svg": "text",
5138
+ ".png": "file",
5139
+ ".jpg": "file",
5140
+ ".jpeg": "file",
5141
+ ".gif": "file",
5142
+ ".webp": "file",
5143
+ ".wasm": "file"
5144
+ }
5080
5145
  });
5081
- for (const [id, props] of Object.entries(ctx.appConfig.defaults.pubsub ?? {})) {
5082
- const group = new Group10(ctx.base, "pubsub", id);
5083
- const { lambda } = createLambdaFunction(group, ctx, "pubsub-authorizer", id, props.auth);
5084
- const name = formatGlobalResourceName({
5085
- appName: ctx.app.name,
5086
- resourceType: "pubsub",
5087
- resourceName: id
5088
- });
5089
- const authorizer = new aws11.iot.Authorizer(group, "authorizer", {
5090
- name,
5091
- authorizerFunctionArn: lambda.arn,
5092
- status: "ACTIVE",
5093
- signingDisabled: true,
5094
- enableCachingForHttp: false
5095
- });
5096
- new aws11.lambda.Permission(group, "permission", {
5097
- functionName: lambda.functionName,
5098
- action: "lambda:InvokeFunction",
5099
- principal: "iot.amazonaws.com",
5100
- sourceArn: authorizer.arn
5146
+ } catch (error) {
5147
+ throw new ExpectedError(
5148
+ `Executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
5149
+ );
5150
+ }
5151
+ if (!result.success) {
5152
+ throw new ExpectedError(`Executable build failed:
5153
+ ${result.logs?.map((log35) => log35.message).join("\n")}`);
5154
+ }
5155
+ const file = await readFile4(filePath);
5156
+ return {
5157
+ hash: createHash3("sha1").update(file).update("x86_64").digest("hex"),
5158
+ file
5159
+ };
5160
+ };
5161
+
5162
+ // src/feature/pubsub/util.ts
5163
+ var __dirname2 = dirname5(fileURLToPath(import.meta.url));
5164
+ var WS_PORT = 3e3;
5165
+ var ARCHITECTURE = "arm64";
5166
+ var CPU = "0.25 vCPU";
5167
+ var MEMORY = "512";
5168
+ var MIN_CAPACITY = 1;
5169
+ var MAX_CAPACITY = 10;
5170
+ var createPubSubService = (parentGroup, ctx, id, props, inputs) => {
5171
+ const group = new Group10(parentGroup, "service", id);
5172
+ const name = formatGlobalResourceName({
5173
+ appName: ctx.app.name,
5174
+ resourceType: "pubsub",
5175
+ resourceName: id
5176
+ });
5177
+ const shortName = shortId(`${ctx.app.name}:pubsub:${id}:${ctx.appId}`);
5178
+ const image2 = "public.ecr.aws/aws-cli/aws-cli:arm64";
5179
+ const bundleFile = join13(__dirname2, "prebuild/pubsub-server/index.mjs");
5180
+ const bundleHash = join13(__dirname2, "prebuild/pubsub-server/HASH");
5181
+ ctx.registerBuild("pubsub", name, async (build3) => {
5182
+ const hash = await readFile5(bundleHash, "utf8");
5183
+ const fingerprint = `${hash.trim()}-${ARCHITECTURE}`;
5184
+ return build3(fingerprint, async (write) => {
5185
+ const temp = await createTempFolder(`pubsub--${name}`);
5186
+ const executable = await buildExecutable(bundleFile, temp.path, ARCHITECTURE);
5187
+ await Promise.all([
5188
+ //
5189
+ write("HASH", executable.hash),
5190
+ write("program", executable.file),
5191
+ temp.delete()
5192
+ ]);
5193
+ return {
5194
+ size: formatByteSize(executable.file.byteLength)
5195
+ };
5196
+ });
5197
+ });
5198
+ const code = new aws11.s3.BucketObject(group, "code", {
5199
+ bucket: ctx.shared.get("pubsub", "bucket-name"),
5200
+ key: name,
5201
+ source: relativePath(getBuildPath("pubsub", name, "program")),
5202
+ sourceHash: $file(getBuildPath("pubsub", name, "HASH"))
5203
+ });
5204
+ const executionRole = new aws11.iam.Role(group, "execution-role", {
5205
+ name: shortId(`${shortName}:execution-role`),
5206
+ description: name,
5207
+ assumeRolePolicy: JSON.stringify({
5208
+ Version: "2012-10-17",
5209
+ Statement: [
5210
+ {
5211
+ Effect: "Allow",
5212
+ Action: "sts:AssumeRole",
5213
+ Principal: {
5214
+ Service: ["ecs-tasks.amazonaws.com"]
5215
+ }
5216
+ }
5217
+ ]
5218
+ }),
5219
+ managedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
5220
+ });
5221
+ const role = new aws11.iam.Role(
5222
+ group,
5223
+ "task-role",
5224
+ {
5225
+ name: shortId(`${shortName}:task-role`),
5226
+ description: name,
5227
+ assumeRolePolicy: JSON.stringify({
5228
+ Version: "2012-10-17",
5229
+ Statement: [
5230
+ {
5231
+ Effect: "Allow",
5232
+ Action: "sts:AssumeRole",
5233
+ Principal: {
5234
+ Service: ["ecs-tasks.amazonaws.com"]
5235
+ }
5236
+ }
5237
+ ]
5238
+ }),
5239
+ inlinePolicy: [
5240
+ {
5241
+ name: "s3-code-access",
5242
+ policy: $resolve([code.bucket, code.key], (bucket, key) => {
5243
+ return JSON.stringify({
5244
+ Version: "2012-10-17",
5245
+ Statement: [
5246
+ {
5247
+ Effect: pascalCase3("allow"),
5248
+ Action: ["s3:getObject", "s3:HeadObject"],
5249
+ Resource: `arn:aws:s3:::${bucket}/${key}`
5250
+ }
5251
+ ]
5252
+ });
5253
+ })
5254
+ }
5255
+ ]
5256
+ },
5257
+ {
5258
+ dependsOn: [code]
5259
+ }
5260
+ );
5261
+ const statements = [];
5262
+ const statementDeps = /* @__PURE__ */ new Set();
5263
+ const policy = new aws11.iam.RolePolicy(group, "policy", {
5264
+ role: role.name,
5265
+ name: "task-policy",
5266
+ policy: new Output5(statementDeps, async (resolve2) => {
5267
+ const list3 = await resolveInputs3(statements);
5268
+ resolve2(
5269
+ JSON.stringify({
5270
+ Version: "2012-10-17",
5271
+ Statement: list3.map((statement) => ({
5272
+ Effect: pascalCase3(statement.effect ?? "allow"),
5273
+ Action: statement.actions,
5274
+ Resource: statement.resources
5275
+ }))
5276
+ })
5277
+ );
5278
+ })
5279
+ });
5280
+ const addPermission = (...permissions) => {
5281
+ statements.push(...permissions);
5282
+ for (const dep of findInputDeps3(permissions)) {
5283
+ statementDeps.add(dep);
5284
+ }
5285
+ };
5286
+ ctx.onPermission((statement) => {
5287
+ addPermission(statement);
5288
+ });
5289
+ let logGroup;
5290
+ if (props.log.retention && props.log.retention.value > 0n) {
5291
+ logGroup = new aws11.cloudwatch.LogGroup(group, "log", {
5292
+ name: `/aws/ecs/${name}`,
5293
+ retentionInDays: toDays7(props.log.retention)
5294
+ });
5295
+ if (ctx.shared.has("on-error-log", "subscriber-arn")) {
5296
+ new aws11.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
5297
+ name: "error-log-subscription",
5298
+ destinationArn: ctx.shared.get("on-error-log", "subscriber-arn"),
5299
+ logGroupName: logGroup.name,
5300
+ filterPattern
5301
+ });
5302
+ }
5303
+ }
5304
+ const tags = {
5305
+ APP: ctx.appConfig.name,
5306
+ APP_ID: ctx.appId
5307
+ };
5308
+ const variables = {};
5309
+ const variableDeps = /* @__PURE__ */ new Set();
5310
+ const task2 = new aws11.ecs.TaskDefinition(
5311
+ group,
5312
+ "task",
5313
+ {
5314
+ family: name,
5315
+ networkMode: "awsvpc",
5316
+ cpu: CPU,
5317
+ memory: MEMORY,
5318
+ requiresCompatibilities: ["FARGATE"],
5319
+ executionRoleArn: executionRole.arn,
5320
+ taskRoleArn: role.arn,
5321
+ runtimePlatform: {
5322
+ cpuArchitecture: "ARM64",
5323
+ operatingSystemFamily: "LINUX"
5324
+ },
5325
+ trackLatest: true,
5326
+ containerDefinitions: new Output5(variableDeps, async (resolve2) => {
5327
+ const data = await resolveInputs3(variables);
5328
+ const { s3Bucket, s3Key } = await resolveInputs3({
5329
+ s3Bucket: code.bucket,
5330
+ s3Key: code.key
5331
+ });
5332
+ resolve2(
5333
+ JSON.stringify([
5334
+ {
5335
+ name: `container-${id}`,
5336
+ essential: true,
5337
+ image: image2,
5338
+ protocol: "tcp",
5339
+ workingDirectory: "/usr/app",
5340
+ entryPoint: ["sh", "-c"],
5341
+ command: [
5342
+ [
5343
+ `aws s3 cp s3://${s3Bucket}/${s3Key} /usr/app/program`,
5344
+ `chmod +x /usr/app/program`,
5345
+ `exec /usr/app/program`
5346
+ ].join(" && ")
5347
+ ],
5348
+ environment: Object.entries(data).map(([name2, value]) => ({
5349
+ name: name2,
5350
+ value
5351
+ })),
5352
+ portMappings: [
5353
+ {
5354
+ name: "ws",
5355
+ protocol: "tcp",
5356
+ appProtocol: "http",
5357
+ containerPort: WS_PORT,
5358
+ hostPort: WS_PORT
5359
+ }
5360
+ ],
5361
+ restartPolicy: {
5362
+ enabled: true,
5363
+ restartAttemptPeriod: 60
5364
+ },
5365
+ ...logGroup && {
5366
+ logConfiguration: {
5367
+ logDriver: "awslogs",
5368
+ options: {
5369
+ "awslogs-group": `/aws/ecs/${name}`,
5370
+ "awslogs-region": ctx.appConfig.region,
5371
+ "awslogs-stream-prefix": "ecs",
5372
+ mode: "non-blocking"
5373
+ }
5374
+ }
5375
+ }
5376
+ }
5377
+ ])
5378
+ );
5379
+ }),
5380
+ tags
5381
+ },
5382
+ {
5383
+ replaceOnChanges: [
5384
+ "containerDefinitions",
5385
+ "cpu",
5386
+ "memory",
5387
+ "runtimePlatform",
5388
+ "executionRoleArn",
5389
+ "taskRoleArn"
5390
+ ],
5391
+ dependsOn: [code]
5392
+ }
5393
+ );
5394
+ const service = new aws11.ecs.Service(
5395
+ group,
5396
+ "service",
5397
+ {
5398
+ name,
5399
+ cluster: inputs.clusterArn,
5400
+ taskDefinition: task2.arn,
5401
+ launchType: "FARGATE",
5402
+ networkConfiguration: {
5403
+ subnets: ctx.shared.get("vpc", "public-subnets"),
5404
+ securityGroups: [inputs.securityGroupId],
5405
+ assignPublicIp: true
5406
+ // https://stackoverflow.com/questions/76398247/cannotpullcontainererror-pull-image-manifest-has-been-retried-5-times-failed
5407
+ },
5408
+ loadBalancer: [
5409
+ {
5410
+ containerName: `container-${id}`,
5411
+ containerPort: WS_PORT,
5412
+ targetGroupArn: inputs.targetGroupArn
5413
+ }
5414
+ ],
5415
+ healthCheckGracePeriodSeconds: 30,
5416
+ forceNewDeployment: true,
5417
+ forceDelete: true,
5418
+ tags,
5419
+ // ------------------------------------------------------------
5420
+ // Zero-downtime deploys: spin up the new tasks before the old
5421
+ // ones are drained.
5422
+ // The desired count is intentionally not set, so that deploys
5423
+ // never reset the capacity the autoscaler picked.
5424
+ schedulingStrategy: "REPLICA",
5425
+ deploymentMaximumPercent: 200,
5426
+ deploymentMinimumHealthyPercent: 100,
5427
+ deploymentCircuitBreaker: {
5428
+ enable: true,
5429
+ rollback: true
5430
+ },
5431
+ // ------------------------------------------------------------
5432
+ // Tag hygiene: let ECS manage and propagate runtime tags automatically.
5433
+ enableEcsManagedTags: true,
5434
+ propagateTags: "SERVICE"
5435
+ },
5436
+ {
5437
+ replaceOnChanges: ["cluster"]
5438
+ }
5439
+ );
5440
+ const target = new aws11.appautoscaling.Target(
5441
+ group,
5442
+ "autoscaling-target",
5443
+ {
5444
+ serviceNamespace: "ecs",
5445
+ scalableDimension: "ecs:service:DesiredCount",
5446
+ minCapacity: MIN_CAPACITY,
5447
+ maxCapacity: MAX_CAPACITY,
5448
+ resourceId: $resolve([inputs.clusterName, service.name], (clusterName, serviceName) => {
5449
+ return `service/${clusterName}/${serviceName}`;
5450
+ }),
5451
+ tags
5452
+ },
5453
+ {
5454
+ dependsOn: [service]
5455
+ }
5456
+ );
5457
+ new aws11.appautoscaling.Policy(
5458
+ group,
5459
+ "autoscaling-policy",
5460
+ {
5461
+ name: `${name}-cpu`,
5462
+ policyType: "TargetTrackingScaling",
5463
+ serviceNamespace: target.serviceNamespace,
5464
+ scalableDimension: target.scalableDimension,
5465
+ resourceId: target.resourceId,
5466
+ targetTrackingScalingPolicyConfiguration: {
5467
+ predefinedMetricSpecification: {
5468
+ predefinedMetricType: "ECSServiceAverageCPUUtilization"
5469
+ },
5470
+ targetValue: 70,
5471
+ scaleInCooldown: 300,
5472
+ scaleOutCooldown: 60
5473
+ }
5474
+ },
5475
+ {
5476
+ dependsOn: [target]
5477
+ }
5478
+ );
5479
+ ctx.onEnv((name2, value) => {
5480
+ variables[name2] = value;
5481
+ for (const dep of findInputDeps3([value])) {
5482
+ variableDeps.add(dep);
5483
+ }
5484
+ });
5485
+ variables.APP = ctx.appConfig.name;
5486
+ variables.APP_ID = ctx.appId;
5487
+ variables.AWS_ACCOUNT_ID = ctx.accountId;
5488
+ variables.CODE_HASH = code.sourceHash;
5489
+ variables.PUBSUB_CONFIG_HASH = createHash4("sha1").update(stringify(props)).digest("hex");
5490
+ for (const [key, value] of Object.entries(inputs.environment)) {
5491
+ variables[key] = value;
5492
+ for (const dep of findInputDeps3([value])) {
5493
+ variableDeps.add(dep);
5494
+ }
5495
+ }
5496
+ return { name, task: task2, service, policy, code, group, addPermission };
5497
+ };
5498
+
5499
+ // src/feature/pubsub/index.ts
5500
+ var __dirname3 = dirname6(fileURLToPath2(import.meta.url));
5501
+ var typeGenCode4 = `
5502
+ import type { Mock } from 'vitest'
5503
+
5504
+ type Publish = (topic: string, event: string, payload?: unknown) => Promise<void>
5505
+ type PubSubInstance = {
5506
+ readonly publish: Publish
5507
+ }
5508
+
5509
+ type MockHandle = (payload: { topic: string; event: string; payload?: unknown }) => void
5510
+ type MockBuilder = (handle?: MockHandle) => void
5511
+ type MockObject = Mock<[unknown], unknown>
5512
+ `;
5513
+ var pubsubFeature = defineFeature({
5514
+ name: "pubsub",
5515
+ async onTypeGen(ctx) {
5516
+ const gen = new TypeFile("awsless");
5517
+ const resources2 = new TypeObject(1);
5518
+ const mocks = new TypeObject(1);
5519
+ const mockResponses = new TypeObject(1);
5520
+ for (const id of Object.keys(ctx.appConfig.defaults.pubsub ?? {})) {
5521
+ resources2.addType(id, `PubSubInstance`);
5522
+ mocks.addType(id, `MockBuilder`);
5523
+ mockResponses.addType(id, `MockObject`);
5524
+ }
5525
+ gen.addCode(typeGenCode4);
5526
+ gen.addInterface("PubSubResources", resources2);
5527
+ gen.addInterface("PubSubMock", mocks);
5528
+ gen.addInterface("PubSubMockResponse", mockResponses);
5529
+ await ctx.write("pubsub.d.ts", gen, true);
5530
+ },
5531
+ onValidate(ctx) {
5532
+ const pubsubs = ctx.appConfig.defaults.pubsub ?? {};
5533
+ for (const [id, props] of Object.entries(pubsubs)) {
5534
+ if (!(props.router in (ctx.appConfig.defaults.router ?? {}))) {
5535
+ throw new FileError("app.json", `The pubsub "${id}" points to a non existent router "${props.router}"`);
5536
+ }
5537
+ }
5538
+ for (const stack of ctx.stackConfigs) {
5539
+ for (const id of Object.keys(stack.pubsub ?? {})) {
5540
+ if (!(id in pubsubs)) {
5541
+ throw new FileError(stack.file, `Listening to a non existent pubsub "${id}"`);
5542
+ }
5543
+ }
5544
+ }
5545
+ },
5546
+ onBefore(ctx) {
5547
+ const found = Object.keys(ctx.appConfig.defaults.pubsub ?? {}).length > 0;
5548
+ if (!found) {
5549
+ return;
5550
+ }
5551
+ const group = new Group11(ctx.base, "pubsub", "asset");
5552
+ const bucket = new aws12.s3.Bucket(group, "bucket", {
5553
+ bucket: formatGlobalResourceName({
5554
+ appName: ctx.app.name,
5555
+ resourceType: "pubsub",
5556
+ resourceName: "assets",
5557
+ postfix: ctx.appId
5558
+ }),
5559
+ forceDestroy: true
5560
+ });
5561
+ ctx.shared.set("pubsub", "bucket-name", bucket.bucket);
5562
+ },
5563
+ onApp(ctx) {
5564
+ const pubsubs = Object.entries(ctx.appConfig.defaults.pubsub ?? {});
5565
+ if (pubsubs.length === 0) {
5566
+ return;
5567
+ }
5568
+ const clusterGroup = new Group11(ctx.base, "pubsub", "cluster");
5569
+ const cluster = new aws12.ecs.Cluster(
5570
+ clusterGroup,
5571
+ "cluster",
5572
+ {
5573
+ name: `${ctx.app.name}-pubsub`
5574
+ },
5575
+ {
5576
+ replaceOnChanges: ["name"]
5577
+ }
5578
+ );
5579
+ for (const [id, props] of pubsubs) {
5580
+ const group = new Group11(ctx.base, "pubsub", id);
5581
+ const name = formatGlobalResourceName({
5582
+ appName: ctx.app.name,
5583
+ resourceType: "pubsub",
5584
+ resourceName: id
5585
+ });
5586
+ const vpcId = ctx.shared.get("vpc", "id");
5587
+ const auth2 = createLambdaFunction(group, ctx, "pubsub-auth", id, props.auth);
5588
+ const topicName = formatGlobalResourceName({
5589
+ appName: ctx.app.name,
5590
+ resourceType: "pubsub-events",
5591
+ resourceName: id
5592
+ });
5593
+ const topic = new aws12.sns.Topic(group, "events", {
5594
+ name: topicName
5595
+ });
5596
+ ctx.shared.add("pubsub", "events-topic-arn", id, topic.arn);
5597
+ const cacheName = formatGlobalResourceName({
5598
+ appName: ctx.app.name,
5599
+ resourceType: "pubsub-cache",
5600
+ resourceName: id,
5601
+ seperator: "-"
5602
+ });
5603
+ const cacheSecurityGroup = new aws12.security.Group(group, "cache-security", {
5604
+ name: cacheName,
5605
+ vpcId,
5606
+ description: cacheName
5607
+ });
5608
+ const cache = new aws12.elasticache.ServerlessCache(
5609
+ group,
5610
+ "cache",
5611
+ {
5612
+ name: cacheName,
5613
+ engine: "valkey",
5614
+ majorEngineVersion: "8",
5615
+ securityGroupIds: [cacheSecurityGroup.id],
5616
+ subnetIds: ctx.shared.get("vpc", "private-subnets")
5617
+ },
5618
+ {
5619
+ import: ctx.import ? cacheName : void 0
5620
+ }
5621
+ );
5622
+ const redisHost = cache.endpoint.pipe((v) => v.at(0).address);
5623
+ const redisPort = cache.endpoint.pipe((v) => v.at(0).port);
5624
+ new aws12.vpc.SecurityGroupIngressRule(group, "cache-rule-ip-v4", {
5625
+ securityGroupId: cacheSecurityGroup.id,
5626
+ description: redisPort.pipe((port) => `Allow ipv4 on port: ${port}`),
5627
+ ipProtocol: "tcp",
5628
+ cidrIpv4: "0.0.0.0/0",
5629
+ fromPort: redisPort,
5630
+ toPort: redisPort
5631
+ });
5632
+ new aws12.vpc.SecurityGroupIngressRule(group, "cache-rule-ip-v6", {
5633
+ securityGroupId: cacheSecurityGroup.id,
5634
+ description: redisPort.pipe((port) => `Allow ipv6 on port: ${port}`),
5635
+ ipProtocol: "tcp",
5636
+ cidrIpv6: "::/0",
5637
+ fromPort: redisPort,
5638
+ toPort: redisPort
5639
+ });
5640
+ const channel = `pubsub:${id}`;
5641
+ const cloudfrontPrefixList = aws12.ec2.getManagedPrefixList(group, "cloudfront-prefix-list", {
5642
+ name: "com.amazonaws.global.cloudfront.origin-facing"
5643
+ });
5644
+ const lbSecurityGroup = new aws12.security.Group(group, "lb-security", {
5645
+ name: `${name}-lb`,
5646
+ vpcId,
5647
+ description: `${name}-lb`
5648
+ });
5649
+ new aws12.vpc.SecurityGroupIngressRule(group, "lb-ingress", {
5650
+ securityGroupId: lbSecurityGroup.id,
5651
+ description: "Allow http traffic from CloudFront",
5652
+ ipProtocol: "tcp",
5653
+ prefixListId: cloudfrontPrefixList.id,
5654
+ fromPort: 80,
5655
+ toPort: 80
5656
+ });
5657
+ new aws12.vpc.SecurityGroupEgressRule(group, "lb-egress", {
5658
+ securityGroupId: lbSecurityGroup.id,
5659
+ description: "Allow all outbound traffic",
5660
+ ipProtocol: "-1",
5661
+ cidrIpv4: "0.0.0.0/0"
5662
+ });
5663
+ const taskSecurityGroup = new aws12.security.Group(group, "task-security", {
5664
+ name: `${name}-task`,
5665
+ vpcId,
5666
+ description: `${name}-task`
5667
+ });
5668
+ new aws12.vpc.SecurityGroupIngressRule(group, "task-ingress", {
5669
+ securityGroupId: taskSecurityGroup.id,
5670
+ description: "Allow websocket traffic from the load balancer",
5671
+ ipProtocol: "tcp",
5672
+ referencedSecurityGroupId: lbSecurityGroup.id,
5673
+ fromPort: WS_PORT,
5674
+ toPort: WS_PORT
5675
+ });
5676
+ new aws12.vpc.SecurityGroupEgressRule(group, "task-egress", {
5677
+ securityGroupId: taskSecurityGroup.id,
5678
+ description: "Allow all outbound traffic",
5679
+ ipProtocol: "-1",
5680
+ cidrIpv4: "0.0.0.0/0"
5681
+ });
5682
+ const lb = new aws12.Lb(group, "lb", {
5683
+ name: shortId(`${name}:lb:${ctx.appId}`),
5684
+ internal: false,
5685
+ loadBalancerType: "application",
5686
+ subnets: ctx.shared.get("vpc", "public-subnets"),
5687
+ securityGroups: [lbSecurityGroup.id],
5688
+ idleTimeout: 120
5689
+ });
5690
+ const targetGroup = new aws12.lb.TargetGroup(group, "target", {
5691
+ name: shortId(`${name}:target:${ctx.appId}`),
5692
+ targetType: "ip",
5693
+ port: WS_PORT,
5694
+ protocol: "HTTP",
5695
+ vpcId,
5696
+ deregistrationDelay: "30",
5697
+ healthCheck: {
5698
+ enabled: true,
5699
+ path: "/health",
5700
+ interval: 10,
5701
+ timeout: 5,
5702
+ healthyThreshold: 2,
5703
+ unhealthyThreshold: 2,
5704
+ matcher: "200"
5705
+ }
5706
+ });
5707
+ new aws12.lb.Listener(group, "listener", {
5708
+ loadBalancerArn: lb.arn,
5709
+ port: 80,
5710
+ protocol: "HTTP",
5711
+ defaultAction: [
5712
+ {
5713
+ type: "forward",
5714
+ targetGroupArn: targetGroup.arn
5715
+ }
5716
+ ]
5101
5717
  });
5102
- ctx.bind(`PUBSUB_${constantCase5(id)}_AUTHORIZER`, name);
5103
- const endpoint = aws11.iot.getEndpoint(group, "endpoint", {
5104
- endpointType: "iot:Data-ATS"
5718
+ const secret = createHmac2("sha1", ctx.appId).update(name).digest("hex");
5719
+ const service = createPubSubService(group, ctx, id, props, {
5720
+ clusterName: cluster.name,
5721
+ clusterArn: cluster.arn,
5722
+ targetGroupArn: targetGroup.arn,
5723
+ securityGroupId: taskSecurityGroup.id,
5724
+ environment: {
5725
+ AUTH: auth2.name,
5726
+ EVENTS_TOPIC: topicName,
5727
+ PORT: WS_PORT.toString(),
5728
+ REDIS_HOST: redisHost,
5729
+ REDIS_PORT: redisPort.pipe((port) => port.toString()),
5730
+ CHANNEL: channel,
5731
+ ORIGIN_SECRET: secret
5732
+ }
5733
+ });
5734
+ service.addPermission(
5735
+ {
5736
+ actions: ["lambda:InvokeFunction"],
5737
+ resources: [auth2.lambda.arn]
5738
+ },
5739
+ {
5740
+ actions: ["sns:Publish"],
5741
+ resources: [topic.arn]
5742
+ }
5743
+ );
5744
+ const publisher = createPrebuildLambdaFunction(group, ctx, "pubsub-publisher", id, {
5745
+ bundleFile: join14(__dirname3, "/prebuild/pubsub-publisher/bundle.zip"),
5746
+ bundleHash: join14(__dirname3, "/prebuild/pubsub-publisher/HASH"),
5747
+ memorySize: mebibytes5(256),
5748
+ timeout: seconds7(10),
5749
+ handler: "index.default",
5750
+ runtime: "nodejs24.x",
5751
+ vpc: true,
5752
+ log: props.log
5105
5753
  });
5106
- if (props.domain) {
5107
- const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
5108
- new aws11.iot.DomainConfiguration(group, "domain", {
5109
- name,
5110
- domainName,
5111
- serverCertificateArns: [ctx.shared.entry("domain", `certificate-arn`, props.domain)],
5112
- authorizerConfig: {
5113
- defaultAuthorizerName: authorizer.name
5114
- }
5115
- // validationCertificate: ctx.shared.get(`global-certificate-${props.domain}-arn`),
5116
- });
5117
- new aws11.route53.Record(group, "record", {
5118
- zoneId: ctx.shared.entry("domain", `zone-id`, props.domain),
5119
- name: domainName,
5120
- type: "CNAME",
5121
- ttl: toSeconds5(minutes7(5)),
5122
- records: [endpoint.endpointAddress]
5123
- });
5124
- ctx.bind(`PUBSUB_${constantCase5(id)}_ENDPOINT`, domainName);
5125
- } else {
5126
- ctx.bind(`PUBSUB_${constantCase5(id)}_ENDPOINT`, endpoint.endpointAddress);
5754
+ publisher.setEnvironment("REDIS_HOST", redisHost);
5755
+ publisher.setEnvironment(
5756
+ "REDIS_PORT",
5757
+ redisPort.pipe((port) => port.toString())
5758
+ );
5759
+ publisher.setEnvironment("CHANNEL", channel);
5760
+ ctx.addGlobalPermission({
5761
+ actions: ["lambda:InvokeFunction"],
5762
+ resources: [publisher.lambda.arn]
5763
+ });
5764
+ const routerProps = ctx.appConfig.defaults.router?.[props.router];
5765
+ if (!routerProps?.domain) {
5766
+ throw new FileError(
5767
+ "app.json",
5768
+ `The pubsub "${id}" requires the "${props.router}" router to have a domain configured.`
5769
+ );
5127
5770
  }
5771
+ const addRoutes = ctx.shared.entry("router", "addRoutes", props.router);
5772
+ addRoutes(group, `pubsub-${id}`, {
5773
+ [`${props.path}/*`]: {
5774
+ type: "url",
5775
+ domainName: lb.dnsName,
5776
+ readTimeout: 60,
5777
+ customHeaders: {
5778
+ "x-origin-secret": secret
5779
+ },
5780
+ rewrite: {
5781
+ regex: `^${props.path}/(.*)$`,
5782
+ to: "/$1"
5783
+ }
5784
+ }
5785
+ });
5786
+ const domainName = formatFullDomainName(ctx.appConfig, routerProps.domain, routerProps.subDomain);
5787
+ ctx.bind(`PUBSUB_${constantCase5(id)}_ENDPOINT`, `wss://${domainName}${props.path}/ws`);
5788
+ ctx.bind(`PUBSUB_${constantCase5(id)}_PUBLISHER`, publisher.name);
5128
5789
  }
5129
5790
  },
5130
5791
  onStack(ctx) {
5131
5792
  for (const [id, props] of Object.entries(ctx.stackConfig.pubsub ?? {})) {
5132
- const group = new Group10(ctx.stack, "pubsub", id);
5133
- const { lambda } = createAsyncLambdaFunction(group, ctx, `pubsub`, id, props);
5134
- const name = formatLocalResourceName({
5135
- appName: ctx.app.name,
5136
- stackName: ctx.stack.name,
5137
- resourceType: "pubsub",
5138
- resourceName: id
5139
- });
5140
- const topic = new aws11.iot.TopicRule(group, "rule", {
5141
- name: name.replaceAll("-", "_"),
5142
- enabled: true,
5143
- sql: props.sql,
5144
- sqlVersion: props.sqlVersion,
5145
- lambda: [{ functionArn: lambda.arn }]
5146
- });
5147
- new aws11.lambda.Permission(group, "permission", {
5148
- action: "lambda:InvokeFunction",
5149
- principal: "iot.amazonaws.com",
5150
- functionName: lambda.functionName,
5151
- sourceArn: topic.arn
5152
- });
5793
+ const group = new Group11(ctx.stack, "pubsub", id);
5794
+ const topicArn = ctx.shared.entry("pubsub", "events-topic-arn", id);
5795
+ for (const event of pubsubEventTypes) {
5796
+ const consumer = props[event];
5797
+ if (!consumer) {
5798
+ continue;
5799
+ }
5800
+ const eventGroup = new Group11(group, "event", event);
5801
+ const { lambda } = createAsyncLambdaFunction(eventGroup, ctx, `pubsub`, `${id}-${event}`, {
5802
+ consumer,
5803
+ retryAttempts: 2
5804
+ });
5805
+ new aws12.sns.TopicSubscription(eventGroup, "subscription", {
5806
+ topicArn,
5807
+ protocol: "lambda",
5808
+ endpoint: lambda.arn,
5809
+ filterPolicyScope: "MessageAttributes",
5810
+ filterPolicy: JSON.stringify({
5811
+ event: [event]
5812
+ })
5813
+ });
5814
+ new aws12.lambda.Permission(eventGroup, "permission", {
5815
+ action: "lambda:InvokeFunction",
5816
+ principal: "sns.amazonaws.com",
5817
+ functionName: lambda.functionName,
5818
+ sourceArn: topicArn
5819
+ });
5820
+ }
5153
5821
  }
5154
5822
  }
5155
5823
  });
5156
5824
 
5157
5825
  // src/feature/queue/index.ts
5158
- import { Group as Group11 } from "@terraforge/core";
5159
- import { aws as aws12 } from "@terraforge/aws";
5826
+ import { Group as Group12 } from "@terraforge/core";
5827
+ import { aws as aws13 } from "@terraforge/aws";
5160
5828
  import { camelCase as camelCase5, constantCase as constantCase6 } from "change-case";
5161
5829
  import deepmerge3 from "deepmerge";
5162
5830
  import { relative as relative5 } from "path";
5163
- import { seconds as seconds7, toSeconds as toSeconds6 } from "@awsless/duration";
5831
+ import { seconds as seconds8, toSeconds as toSeconds5 } from "@awsless/duration";
5164
5832
  import { toBytes } from "@awsless/size";
5165
- var typeGenCode4 = `
5833
+ var typeGenCode5 = `
5166
5834
  import {
5167
5835
  SendMessageOptions,
5168
5836
  SendMessageBatchOptions,
@@ -5220,7 +5888,7 @@ var queueFeature = defineFeature({
5220
5888
  resources2.addType(stack.name, resource);
5221
5889
  mockResponses.addType(stack.name, mockResponse);
5222
5890
  }
5223
- gen.addCode(typeGenCode4);
5891
+ gen.addCode(typeGenCode5);
5224
5892
  gen.addInterface("QueueResources", resources2);
5225
5893
  gen.addInterface("QueueMock", mocks);
5226
5894
  gen.addInterface("QueueMockResponse", mockResponses);
@@ -5229,18 +5897,18 @@ var queueFeature = defineFeature({
5229
5897
  onStack(ctx) {
5230
5898
  for (const [id, local] of Object.entries(ctx.stackConfig.queues || {})) {
5231
5899
  const props = deepmerge3(ctx.appConfig.defaults.queue, typeof local === "object" ? local : {});
5232
- const group = new Group11(ctx.stack, "queue", id);
5900
+ const group = new Group12(ctx.stack, "queue", id);
5233
5901
  const baseName = formatLocalResourceName({
5234
5902
  appName: ctx.app.name,
5235
5903
  stackName: ctx.stack.name,
5236
5904
  resourceType: "queue",
5237
5905
  resourceName: id
5238
5906
  });
5239
- const queue2 = new aws12.sqs.Queue(group, "queue", {
5907
+ const queue2 = new aws13.sqs.Queue(group, "queue", {
5240
5908
  name: `${baseName}.fifo`,
5241
- visibilityTimeoutSeconds: toSeconds6(props.visibilityTimeout),
5242
- receiveWaitTimeSeconds: toSeconds6(props.receiveMessageWaitTime ?? seconds7(0)),
5243
- messageRetentionSeconds: toSeconds6(props.retentionPeriod),
5909
+ visibilityTimeoutSeconds: toSeconds5(props.visibilityTimeout),
5910
+ receiveWaitTimeSeconds: toSeconds5(props.receiveMessageWaitTime ?? seconds8(0)),
5911
+ messageRetentionSeconds: toSeconds5(props.retentionPeriod),
5244
5912
  maxMessageSize: toBytes(props.maxMessageSize),
5245
5913
  fifoQueue: true,
5246
5914
  deduplicationScope: "messageGroup",
@@ -5255,7 +5923,7 @@ var queueFeature = defineFeature({
5255
5923
  if (local.consumer) {
5256
5924
  const lambdaConsumer = createLambdaFunction(group, ctx, `queue`, id, local.consumer);
5257
5925
  lambdaConsumer.setEnvironment("THROW_EXPECTED_ERRORS", "1");
5258
- new aws12.lambda.EventSourceMapping(
5926
+ new aws13.lambda.EventSourceMapping(
5259
5927
  group,
5260
5928
  "event",
5261
5929
  {
@@ -5289,24 +5957,24 @@ var queueFeature = defineFeature({
5289
5957
  });
5290
5958
 
5291
5959
  // src/feature/rest/index.ts
5292
- import { Group as Group12 } from "@terraforge/core";
5293
- import { aws as aws13 } from "@terraforge/aws";
5960
+ import { Group as Group13 } from "@terraforge/core";
5961
+ import { aws as aws14 } from "@terraforge/aws";
5294
5962
  import { constantCase as constantCase7 } from "change-case";
5295
5963
  var restFeature = defineFeature({
5296
5964
  name: "rest",
5297
5965
  onApp(ctx) {
5298
5966
  for (const [id, props] of Object.entries(ctx.appConfig.defaults?.rest ?? {})) {
5299
- const group = new Group12(ctx.base, "rest", id);
5967
+ const group = new Group13(ctx.base, "rest", id);
5300
5968
  const name = formatGlobalResourceName({
5301
5969
  appName: ctx.app.name,
5302
5970
  resourceType: "rest",
5303
5971
  resourceName: id
5304
5972
  });
5305
- const api = new aws13.apigatewayv2.Api(group, "api", {
5973
+ const api = new aws14.apigatewayv2.Api(group, "api", {
5306
5974
  name,
5307
5975
  protocolType: "HTTP"
5308
5976
  });
5309
- const stage = new aws13.apigatewayv2.Stage(group, "stage", {
5977
+ const stage = new aws14.apigatewayv2.Stage(group, "stage", {
5310
5978
  name: "v1",
5311
5979
  apiId: api.id,
5312
5980
  autoDeploy: true
@@ -5316,7 +5984,7 @@ var restFeature = defineFeature({
5316
5984
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
5317
5985
  const zoneId = ctx.shared.entry("domain", `zone-id`, props.domain);
5318
5986
  const certificateArn = ctx.shared.entry("domain", `certificate-arn`, props.domain);
5319
- const domain2 = new aws13.apigatewayv2.DomainName(group, "domain", {
5987
+ const domain2 = new aws14.apigatewayv2.DomainName(group, "domain", {
5320
5988
  domainName,
5321
5989
  domainNameConfiguration: {
5322
5990
  certificateArn,
@@ -5324,12 +5992,12 @@ var restFeature = defineFeature({
5324
5992
  securityPolicy: "TLS_1_2"
5325
5993
  }
5326
5994
  });
5327
- const mapping = new aws13.apigatewayv2.ApiMapping(group, "mapping", {
5995
+ const mapping = new aws14.apigatewayv2.ApiMapping(group, "mapping", {
5328
5996
  apiId: api.id,
5329
5997
  domainName: domain2.domainName,
5330
5998
  stage: stage.name
5331
5999
  });
5332
- new aws13.route53.Record(
6000
+ new aws14.route53.Record(
5333
6001
  group,
5334
6002
  "record",
5335
6003
  {
@@ -5357,21 +6025,21 @@ var restFeature = defineFeature({
5357
6025
  },
5358
6026
  onStack(ctx) {
5359
6027
  for (const [id, routes] of Object.entries(ctx.stackConfig.rest ?? {})) {
5360
- const restGroup = new Group12(ctx.stack, "rest", id);
6028
+ const restGroup = new Group13(ctx.stack, "rest", id);
5361
6029
  for (const [routeKey, props] of Object.entries(routes)) {
5362
- const group = new Group12(restGroup, "route", routeKey);
6030
+ const group = new Group13(restGroup, "route", routeKey);
5363
6031
  const apiId = ctx.shared.entry("rest", "id", id);
5364
6032
  const routeId = shortId(routeKey);
5365
6033
  const { lambda } = createLambdaFunction(group, ctx, "rest", `${id}-${routeId}`, {
5366
6034
  ...props,
5367
6035
  description: `${id} ${routeKey}`
5368
6036
  });
5369
- const permission = new aws13.lambda.Permission(group, "permission", {
6037
+ const permission = new aws14.lambda.Permission(group, "permission", {
5370
6038
  action: "lambda:InvokeFunction",
5371
6039
  principal: "apigateway.amazonaws.com",
5372
6040
  functionName: lambda.functionName
5373
6041
  });
5374
- const integration = new aws13.apigatewayv2.Integration(group, "integration", {
6042
+ const integration = new aws14.apigatewayv2.Integration(group, "integration", {
5375
6043
  apiId,
5376
6044
  description: `${id} ${routeKey}`,
5377
6045
  integrationType: "AWS_PROXY",
@@ -5381,7 +6049,7 @@ var restFeature = defineFeature({
5381
6049
  return `arn:aws:apigateway:${ctx.appConfig.region}:lambda:path/2015-03-31/functions/${arn}/invocations`;
5382
6050
  })
5383
6051
  });
5384
- new aws13.apigatewayv2.Route(
6052
+ new aws14.apigatewayv2.Route(
5385
6053
  group,
5386
6054
  "route",
5387
6055
  {
@@ -5400,13 +6068,13 @@ var restFeature = defineFeature({
5400
6068
 
5401
6069
  // src/feature/rpc/index.ts
5402
6070
  import { camelCase as camelCase6, constantCase as constantCase8, kebabCase as kebabCase6 } from "change-case";
5403
- import { Group as Group13 } from "@terraforge/core";
5404
- import { aws as aws14 } from "@terraforge/aws";
5405
- import { mebibytes as mebibytes5 } from "@awsless/size";
5406
- import { dirname as dirname5, join as join12, relative as relative6 } from "path";
5407
- import { fileURLToPath } from "url";
5408
- import { toSeconds as toSeconds7 } from "@awsless/duration";
5409
- var __dirname2 = dirname5(fileURLToPath(import.meta.url));
6071
+ import { Group as Group14 } from "@terraforge/core";
6072
+ import { aws as aws15 } from "@terraforge/aws";
6073
+ import { mebibytes as mebibytes6 } from "@awsless/size";
6074
+ import { dirname as dirname7, join as join15, relative as relative6 } from "path";
6075
+ import { fileURLToPath as fileURLToPath3 } from "url";
6076
+ import { toSeconds as toSeconds6 } from "@awsless/duration";
6077
+ var __dirname4 = dirname7(fileURLToPath3(import.meta.url));
5410
6078
  var rpcFeature = defineFeature({
5411
6079
  name: "rpc",
5412
6080
  async onTypeGen(ctx) {
@@ -5450,8 +6118,8 @@ var rpcFeature = defineFeature({
5450
6118
  } else {
5451
6119
  list3.add(name);
5452
6120
  }
5453
- const timeout = toSeconds7(props.function.timeout ?? ctx.appConfig.defaults.function.timeout);
5454
- const maxTimeout = toSeconds7(ctx.appConfig.defaults.rpc[id].timeout) * 0.8;
6121
+ const timeout = toSeconds6(props.function.timeout ?? ctx.appConfig.defaults.function.timeout);
6122
+ const maxTimeout = toSeconds6(ctx.appConfig.defaults.rpc[id].timeout) * 0.8;
5455
6123
  if (timeout > maxTimeout) {
5456
6124
  throw new FileError(
5457
6125
  stack.file,
@@ -5464,19 +6132,19 @@ var rpcFeature = defineFeature({
5464
6132
  },
5465
6133
  onApp(ctx) {
5466
6134
  for (const [id, props] of Object.entries(ctx.appConfig.defaults.rpc ?? {})) {
5467
- const group = new Group13(ctx.base, "rpc", id);
6135
+ const group = new Group14(ctx.base, "rpc", id);
5468
6136
  const result = createPrebuildLambdaFunction(group, ctx, "rpc", id, {
5469
- bundleFile: join12(__dirname2, "/prebuild/rpc/bundle.zip"),
5470
- bundleHash: join12(__dirname2, "/prebuild/rpc/HASH"),
5471
- memorySize: mebibytes5(256),
6137
+ bundleFile: join15(__dirname4, "/prebuild/rpc/bundle.zip"),
6138
+ bundleHash: join15(__dirname4, "/prebuild/rpc/HASH"),
6139
+ memorySize: mebibytes6(256),
5472
6140
  timeout: props.timeout,
5473
6141
  handler: "index.default",
5474
6142
  runtime: "nodejs24.x",
5475
6143
  warm: 3,
5476
6144
  log: props.log
5477
6145
  });
5478
- result.setEnvironment("TIMEOUT", toSeconds7(props.timeout).toString());
5479
- const schemaTable = new aws14.dynamodb.Table(group, "schema", {
6146
+ result.setEnvironment("TIMEOUT", toSeconds6(props.timeout).toString());
6147
+ const schemaTable = new aws15.dynamodb.Table(group, "schema", {
5480
6148
  name: formatGlobalResourceName({
5481
6149
  appName: ctx.app.name,
5482
6150
  resourceType: "rpc-schema",
@@ -5498,7 +6166,7 @@ var rpcFeature = defineFeature({
5498
6166
  resources: [schemaTable.arn]
5499
6167
  });
5500
6168
  ctx.shared.add("rpc", `schema-table`, id, schemaTable);
5501
- const lockTable = new aws14.dynamodb.Table(group, "lock", {
6169
+ const lockTable = new aws15.dynamodb.Table(group, "lock", {
5502
6170
  name: formatGlobalResourceName({
5503
6171
  appName: ctx.app.name,
5504
6172
  resourceType: "rpc-lock",
@@ -5524,7 +6192,7 @@ var rpcFeature = defineFeature({
5524
6192
  resources: [lockTable.arn]
5525
6193
  });
5526
6194
  if (props.auth) {
5527
- const authGroup = new Group13(group, "auth", "authorizer");
6195
+ const authGroup = new Group14(group, "auth", "authorizer");
5528
6196
  const auth2 = createLambdaFunction(authGroup, ctx, "rpc", `${id}-auth`, props.auth);
5529
6197
  result.setEnvironment("AUTH", auth2.name);
5530
6198
  for (const [authId, userPoolId] of ctx.shared.list("auth", "user-pool-id")) {
@@ -5543,14 +6211,14 @@ var rpcFeature = defineFeature({
5543
6211
  imageUri: result.lambda.imageUri
5544
6212
  });
5545
6213
  }
5546
- const permission = new aws14.lambda.Permission(group, "permission", {
6214
+ const permission = new aws15.lambda.Permission(group, "permission", {
5547
6215
  principal: "cloudfront.amazonaws.com",
5548
6216
  action: "lambda:InvokeFunctionUrl",
5549
6217
  functionName: result.lambda.functionName,
5550
6218
  functionUrlAuthType: "AWS_IAM",
5551
6219
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
5552
6220
  });
5553
- const url = new aws14.lambda.FunctionUrl(
6221
+ const url = new aws15.lambda.FunctionUrl(
5554
6222
  group,
5555
6223
  "url",
5556
6224
  {
@@ -5585,15 +6253,15 @@ var rpcFeature = defineFeature({
5585
6253
  throw new FileError(ctx.stackConfig.file, `RPC definition is not defined on app level for "${id}"`);
5586
6254
  }
5587
6255
  const table = ctx.shared.entry("rpc", "schema-table", id);
5588
- const group = new Group13(ctx.stack, "rpc", id);
6256
+ const group = new Group14(ctx.stack, "rpc", id);
5589
6257
  for (const [name, props] of Object.entries(queries ?? {})) {
5590
- const queryGroup = new Group13(group, "query", name);
6258
+ const queryGroup = new Group14(group, "query", name);
5591
6259
  const entryId = kebabCase6(`${id}-${shortId(name)}`);
5592
6260
  createLambdaFunction(queryGroup, ctx, `rpc`, entryId, {
5593
6261
  ...props.function,
5594
6262
  description: `${id} ${name}`
5595
6263
  });
5596
- new aws14.dynamodb.TableItem(queryGroup, "query", {
6264
+ new aws15.dynamodb.TableItem(queryGroup, "query", {
5597
6265
  tableName: table.name,
5598
6266
  hashKey: table.hashKey,
5599
6267
  rangeKey: table.rangeKey,
@@ -5625,11 +6293,11 @@ var rpcFeature = defineFeature({
5625
6293
  });
5626
6294
 
5627
6295
  // src/feature/search/index.ts
5628
- import { Group as Group14 } from "@terraforge/core";
5629
- import { aws as aws15 } from "@terraforge/aws";
6296
+ import { Group as Group15 } from "@terraforge/core";
6297
+ import { aws as aws16 } from "@terraforge/aws";
5630
6298
  import { constantCase as constantCase9 } from "change-case";
5631
6299
  import { toGibibytes as toGibibytes2 } from "@awsless/size";
5632
- var typeGenCode5 = `
6300
+ var typeGenCode6 = `
5633
6301
  import { AnyStruct, Table } from '@awsless/open-search'
5634
6302
 
5635
6303
  type Search = {
@@ -5649,15 +6317,15 @@ var searchFeature = defineFeature({
5649
6317
  }
5650
6318
  resources2.addType(stack.name, list3);
5651
6319
  }
5652
- gen.addCode(typeGenCode5);
6320
+ gen.addCode(typeGenCode6);
5653
6321
  gen.addInterface("SearchResources", resources2);
5654
6322
  await ctx.write("search.d.ts", gen, true);
5655
6323
  },
5656
6324
  onStack(ctx) {
5657
6325
  for (const [id, props] of Object.entries(ctx.stackConfig.searchs ?? {})) {
5658
- const group = new Group14(ctx.stack, "search", id);
6326
+ const group = new Group15(ctx.stack, "search", id);
5659
6327
  const name = `${ctx.app.name}-${shortId([ctx.app.name, ctx.stack.name, "search", id].join("--"))}`;
5660
- const openSearch = new aws15.opensearch.Domain(
6328
+ const openSearch = new aws16.opensearch.Domain(
5661
6329
  group,
5662
6330
  "domain",
5663
6331
  {
@@ -5732,10 +6400,10 @@ var searchFeature = defineFeature({
5732
6400
  });
5733
6401
 
5734
6402
  // src/feature/site/index.ts
5735
- import { Group as Group15 } from "@terraforge/core";
5736
- import { aws as aws16 } from "@terraforge/aws";
6403
+ import { Group as Group16 } from "@terraforge/core";
6404
+ import { aws as aws17 } from "@terraforge/aws";
5737
6405
  import { glob as glob2 } from "glob";
5738
- import { dirname as dirname6, join as join13 } from "path";
6406
+ import { dirname as dirname8, join as join16 } from "path";
5739
6407
 
5740
6408
  // src/feature/site/util.ts
5741
6409
  import { contentType, lookup } from "mime-types";
@@ -5763,7 +6431,7 @@ var siteFeature = defineFeature({
5763
6431
  name: "site",
5764
6432
  onStack(ctx) {
5765
6433
  for (const [id, props] of Object.entries(ctx.stackConfig.sites ?? {})) {
5766
- const group = new Group15(ctx.stack, "site", id);
6434
+ const group = new Group16(ctx.stack, "site", id);
5767
6435
  const name = formatLocalResourceName({
5768
6436
  appName: ctx.app.name,
5769
6437
  stackName: ctx.stack.name,
@@ -5781,7 +6449,7 @@ var siteFeature = defineFeature({
5781
6449
  return build3(fingerprint, async (write) => {
5782
6450
  const credentialProvider = await getCredentials(ctx.appConfig.profile);
5783
6451
  const credentials = await credentialProvider();
5784
- const cwd = join13(directories.root, dirname6(ctx.stackConfig.file));
6452
+ const cwd = join16(directories.root, dirname8(ctx.stackConfig.file));
5785
6453
  const env = {
5786
6454
  ...process.env,
5787
6455
  // Pass the app config name
@@ -5828,14 +6496,14 @@ var siteFeature = defineFeature({
5828
6496
  ctx.onBind((name2, value) => {
5829
6497
  result.setEnvironment(name2, value);
5830
6498
  });
5831
- new aws16.lambda.Permission(group, "ssr-permission", {
6499
+ new aws17.lambda.Permission(group, "ssr-permission", {
5832
6500
  principal: "cloudfront.amazonaws.com",
5833
6501
  action: "lambda:InvokeFunctionUrl",
5834
6502
  functionName: result.lambda.functionName,
5835
6503
  functionUrlAuthType: "AWS_IAM",
5836
6504
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
5837
6505
  });
5838
- const functionUrl = new aws16.lambda.FunctionUrl(group, "url", {
6506
+ const functionUrl = new aws17.lambda.FunctionUrl(group, "url", {
5839
6507
  functionName: result.lambda.functionName,
5840
6508
  authorizationType: "AWS_IAM"
5841
6509
  });
@@ -5849,7 +6517,7 @@ var siteFeature = defineFeature({
5849
6517
  });
5850
6518
  }
5851
6519
  if (props.static) {
5852
- const bucket = new aws16.s3.Bucket(group, "bucket", {
6520
+ const bucket = new aws17.s3.Bucket(group, "bucket", {
5853
6521
  bucket: formatLocalResourceName({
5854
6522
  appName: ctx.app.name,
5855
6523
  stackName: ctx.stack.name,
@@ -5871,7 +6539,7 @@ var siteFeature = defineFeature({
5871
6539
  }
5872
6540
  ]
5873
6541
  });
5874
- new aws16.s3.BucketPolicy(
6542
+ new aws17.s3.BucketPolicy(
5875
6543
  group,
5876
6544
  `policy`,
5877
6545
  {
@@ -5926,20 +6594,20 @@ var siteFeature = defineFeature({
5926
6594
  });
5927
6595
  const staticRoutes = {};
5928
6596
  for (const file of files) {
5929
- const prefixedFile = join13("/", file);
5930
- const object = new aws16.s3.BucketObject(group, prefixedFile, {
6597
+ const prefixedFile = join16("/", file);
6598
+ const object = new aws17.s3.BucketObject(group, prefixedFile, {
5931
6599
  bucket: bucket.bucket,
5932
6600
  key: prefixedFile,
5933
6601
  cacheControl: getCacheControl(file),
5934
6602
  contentType: getContentType(file),
5935
- source: join13(props.static, file),
5936
- sourceHash: $hash(join13(props.static, file))
6603
+ source: join16(props.static, file),
6604
+ sourceHash: $hash(join16(props.static, file))
5937
6605
  });
5938
6606
  versions.push(object.key);
5939
6607
  versions.push(object.sourceHash);
5940
6608
  const strippedHtmlFile = file.endsWith("index.html") ? file.slice(0, -11) : file.endsWith(".html") ? file.slice(0, -5) : file;
5941
6609
  const urlFriendlyFile = strippedHtmlFile.endsWith("/") ? strippedHtmlFile.slice(0, -1) : strippedHtmlFile;
5942
- const routeFileKey = join13(props.path, urlFriendlyFile);
6610
+ const routeFileKey = join16(props.path, urlFriendlyFile);
5943
6611
  staticRoutes[routeFileKey] = {
5944
6612
  type: "s3",
5945
6613
  domainName: bucket.bucketRegionalDomainName,
@@ -5956,10 +6624,10 @@ var siteFeature = defineFeature({
5956
6624
  });
5957
6625
 
5958
6626
  // src/feature/store/index.ts
5959
- import { Group as Group16 } from "@terraforge/core";
5960
- import { toDays as toDays7 } from "@awsless/duration";
6627
+ import { Group as Group17 } from "@terraforge/core";
6628
+ import { toDays as toDays8 } from "@awsless/duration";
5961
6629
  import { kebabCase as kebabCase7 } from "change-case";
5962
- import { aws as aws17 } from "@terraforge/aws";
6630
+ import { aws as aws18 } from "@terraforge/aws";
5963
6631
  import { glob as glob3 } from "glob";
5964
6632
 
5965
6633
  // src/feature/store/util.ts
@@ -5983,8 +6651,8 @@ var getContentType2 = (file) => {
5983
6651
  };
5984
6652
 
5985
6653
  // src/feature/store/index.ts
5986
- import { join as join14 } from "path";
5987
- var typeGenCode6 = `
6654
+ import { join as join17 } from "path";
6655
+ var typeGenCode7 = `
5988
6656
  import { Body, PutObjectProps, BodyStream } from '@awsless/s3'
5989
6657
 
5990
6658
  type Store<Name extends string> = {
@@ -6013,7 +6681,7 @@ var storeFeature = defineFeature({
6013
6681
  }
6014
6682
  resources2.addType(stack.name, list3);
6015
6683
  }
6016
- gen.addCode(typeGenCode6);
6684
+ gen.addCode(typeGenCode7);
6017
6685
  gen.addInterface("StoreResources", resources2);
6018
6686
  await ctx.write("store.d.ts", gen, true);
6019
6687
  },
@@ -6051,7 +6719,7 @@ var storeFeature = defineFeature({
6051
6719
  },
6052
6720
  onStack(ctx) {
6053
6721
  for (const [id, props] of Object.entries(ctx.stackConfig.stores ?? {})) {
6054
- const group = new Group16(ctx.stack, "store", id);
6722
+ const group = new Group17(ctx.stack, "store", id);
6055
6723
  const name = formatLocalResourceName({
6056
6724
  appName: ctx.app.name,
6057
6725
  stackName: ctx.stack.name,
@@ -6063,9 +6731,9 @@ var storeFeature = defineFeature({
6063
6731
  id: rule.prefix ? `expire-${kebabCase7(rule.prefix)}` : `expire-rule-${index}`,
6064
6732
  enabled: true,
6065
6733
  ...rule.prefix ? { prefix: rule.prefix } : {},
6066
- expiration: { days: toDays7(rule.expiration) }
6734
+ expiration: { days: toDays8(rule.expiration) }
6067
6735
  }));
6068
- const bucket = new aws17.s3.Bucket(
6736
+ const bucket = new aws18.s3.Bucket(
6069
6737
  group,
6070
6738
  "store",
6071
6739
  {
@@ -6097,13 +6765,13 @@ var storeFeature = defineFeature({
6097
6765
  nodir: true
6098
6766
  });
6099
6767
  for (const file of files) {
6100
- new aws17.s3.BucketObject(group, file, {
6768
+ new aws18.s3.BucketObject(group, file, {
6101
6769
  bucket: bucket.bucket,
6102
6770
  key: file,
6103
6771
  cacheControl: getCacheControl2(file),
6104
6772
  contentType: getContentType2(file),
6105
- source: join14(props.static, file),
6106
- sourceHash: $hash(join14(props.static, file))
6773
+ source: join17(props.static, file),
6774
+ sourceHash: $hash(join17(props.static, file))
6107
6775
  });
6108
6776
  }
6109
6777
  }
@@ -6119,7 +6787,7 @@ var storeFeature = defineFeature({
6119
6787
  "removed:marker": "s3:ObjectRemoved:DeleteMarkerCreated"
6120
6788
  };
6121
6789
  for (const [event, taskProps] of Object.entries(props.events ?? {})) {
6122
- const eventGroup = new Group16(group, "event", event);
6790
+ const eventGroup = new Group17(group, "event", event);
6123
6791
  const eventId = kebabCase7(`${id}-${shortId(event)}`);
6124
6792
  const { lambda } = createAsyncLambdaFunction(eventGroup, ctx, `store`, eventId, {
6125
6793
  ...taskProps,
@@ -6128,13 +6796,13 @@ var storeFeature = defineFeature({
6128
6796
  description: `${id} event "${event}"`
6129
6797
  }
6130
6798
  });
6131
- new aws17.lambda.Permission(eventGroup, "permission", {
6799
+ new aws18.lambda.Permission(eventGroup, "permission", {
6132
6800
  action: "lambda:InvokeFunction",
6133
6801
  principal: "s3.amazonaws.com",
6134
6802
  functionName: lambda.functionName,
6135
6803
  sourceArn: `arn:aws:s3:::${name}`
6136
6804
  });
6137
- new aws17.s3.BucketNotification(eventGroup, "notification", {
6805
+ new aws18.s3.BucketNotification(eventGroup, "notification", {
6138
6806
  bucket: bucket.bucket,
6139
6807
  lambdaFunction: [
6140
6808
  {
@@ -6173,10 +6841,10 @@ var storeFeature = defineFeature({
6173
6841
  });
6174
6842
 
6175
6843
  // src/feature/table/index.ts
6176
- import { Group as Group17 } from "@terraforge/core";
6177
- import { aws as aws18 } from "@terraforge/aws";
6844
+ import { Group as Group18 } from "@terraforge/core";
6845
+ import { aws as aws19 } from "@terraforge/aws";
6178
6846
  import { constantCase as constantCase11 } from "change-case";
6179
- import { toSeconds as toSeconds8 } from "@awsless/duration";
6847
+ import { toSeconds as toSeconds7 } from "@awsless/duration";
6180
6848
  var tableFeature = defineFeature({
6181
6849
  name: "table",
6182
6850
  async onTypeGen(ctx) {
@@ -6225,7 +6893,7 @@ var tableFeature = defineFeature({
6225
6893
  },
6226
6894
  onStack(ctx) {
6227
6895
  for (const [id, props] of Object.entries(ctx.stackConfig.tables ?? {})) {
6228
- const group = new Group17(ctx.stack, "table", id);
6896
+ const group = new Group18(ctx.stack, "table", id);
6229
6897
  const name = formatLocalResourceName({
6230
6898
  appName: ctx.app.name,
6231
6899
  stackName: ctx.stack.name,
@@ -6254,7 +6922,7 @@ var tableFeature = defineFeature({
6254
6922
  type: types2[props.fields?.[name2] ?? "string"]
6255
6923
  }));
6256
6924
  };
6257
- const table = new aws18.dynamodb.Table(
6925
+ const table = new aws19.dynamodb.Table(
6258
6926
  group,
6259
6927
  "table",
6260
6928
  {
@@ -6298,7 +6966,7 @@ var tableFeature = defineFeature({
6298
6966
  const result = createLambdaFunction(group, ctx, "table", id, props.stream.consumer);
6299
6967
  result.setEnvironment("THROW_EXPECTED_ERRORS", "1");
6300
6968
  const onFailure = getGlobalOnFailure(ctx);
6301
- new aws18.lambda.EventSourceMapping(
6969
+ new aws19.lambda.EventSourceMapping(
6302
6970
  group,
6303
6971
  id,
6304
6972
  {
@@ -6308,7 +6976,7 @@ var tableFeature = defineFeature({
6308
6976
  // maximumRecordAgeInSeconds: toSeconds(props.stream.maxRecordAge),
6309
6977
  // bisectBatchOnFunctionError: true,
6310
6978
  batchSize: props.stream.batchSize,
6311
- maximumBatchingWindowInSeconds: props.stream.batchWindow ? toSeconds8(props.stream.batchWindow) : void 0,
6979
+ maximumBatchingWindowInSeconds: props.stream.batchWindow ? toSeconds7(props.stream.batchWindow) : void 0,
6312
6980
  maximumRetryAttempts: props.stream.retryAttempts,
6313
6981
  parallelizationFactor: props.stream.concurrencyPerShard,
6314
6982
  functionResponseTypes: ["ReportBatchItemFailures"],
@@ -6374,11 +7042,11 @@ var tableFeature = defineFeature({
6374
7042
  });
6375
7043
 
6376
7044
  // src/feature/task/index.ts
6377
- import { Group as Group18 } from "@terraforge/core";
6378
- import { aws as aws19 } from "@terraforge/aws";
7045
+ import { Group as Group19 } from "@terraforge/core";
7046
+ import { aws as aws20 } from "@terraforge/aws";
6379
7047
  import { camelCase as camelCase7 } from "change-case";
6380
7048
  import { relative as relative7 } from "path";
6381
- var typeGenCode7 = `
7049
+ var typeGenCode8 = `
6382
7050
  import { Duration } from '@awsless/duration'
6383
7051
  import { InvokeOptions } from '@awsless/lambda'
6384
7052
  import type { Mock } from 'vitest'
@@ -6436,26 +7104,26 @@ var taskFeature = defineFeature({
6436
7104
  resources2.addType(stack.name, resource);
6437
7105
  mockResponses.addType(stack.name, mockResponse);
6438
7106
  }
6439
- types2.addCode(typeGenCode7);
7107
+ types2.addCode(typeGenCode8);
6440
7108
  types2.addInterface("TaskResources", resources2);
6441
7109
  types2.addInterface("TaskMock", mocks);
6442
7110
  types2.addInterface("TaskMockResponse", mockResponses);
6443
7111
  await ctx.write("task.d.ts", types2, true);
6444
7112
  },
6445
7113
  onApp(ctx) {
6446
- const group = new Group18(ctx.base, "task", "main");
7114
+ const group = new Group19(ctx.base, "task", "main");
6447
7115
  const scheduleGroupName = formatGlobalResourceName({
6448
7116
  appName: ctx.app.name,
6449
7117
  resourceType: "task",
6450
7118
  resourceName: "group"
6451
7119
  });
6452
- new aws19.scheduler.ScheduleGroup(group, "group", {
7120
+ new aws20.scheduler.ScheduleGroup(group, "group", {
6453
7121
  name: scheduleGroupName,
6454
7122
  tags: {
6455
7123
  app: ctx.app.name
6456
7124
  }
6457
7125
  });
6458
- const role = new aws19.iam.Role(group, "schedule", {
7126
+ const role = new aws20.iam.Role(group, "schedule", {
6459
7127
  name: formatGlobalResourceName({
6460
7128
  appName: ctx.app.name,
6461
7129
  resourceType: "task",
@@ -6502,7 +7170,7 @@ var taskFeature = defineFeature({
6502
7170
  },
6503
7171
  onStack(ctx) {
6504
7172
  for (const [id, props] of Object.entries(ctx.stackConfig.tasks ?? {})) {
6505
- const group = new Group18(ctx.stack, "task", id);
7173
+ const group = new Group19(ctx.stack, "task", id);
6506
7174
  createAsyncLambdaFunction(group, ctx, "task", id, props);
6507
7175
  }
6508
7176
  }
@@ -6527,9 +7195,9 @@ var testFeature = defineFeature({
6527
7195
  });
6528
7196
 
6529
7197
  // src/feature/topic/index.ts
6530
- import { Group as Group19 } from "@terraforge/core";
6531
- import { aws as aws20 } from "@terraforge/aws";
6532
- var typeGenCode8 = `
7198
+ import { Group as Group20 } from "@terraforge/core";
7199
+ import { aws as aws21 } from "@terraforge/aws";
7200
+ var typeGenCode9 = `
6533
7201
  import type { PublishOptions } from '@awsless/sns'
6534
7202
 
6535
7203
  type Publish<Name extends string> = {
@@ -6557,7 +7225,7 @@ var topicFeature = defineFeature({
6557
7225
  resources2.addType(topic, `Publish<'${name}'>`);
6558
7226
  mocks.addType(topic, `MockBuilder`);
6559
7227
  }
6560
- gen.addCode(typeGenCode8);
7228
+ gen.addCode(typeGenCode9);
6561
7229
  gen.addInterface("TopicResources", resources2);
6562
7230
  gen.addInterface("TopicMock", mocks);
6563
7231
  gen.addInterface("TopicMockResponse", mockResponses);
@@ -6575,13 +7243,13 @@ var topicFeature = defineFeature({
6575
7243
  },
6576
7244
  onApp(ctx) {
6577
7245
  for (const id of ctx.appConfig.defaults.topics ?? []) {
6578
- const group = new Group19(ctx.base, "topic", id);
7246
+ const group = new Group20(ctx.base, "topic", id);
6579
7247
  const name = formatGlobalResourceName({
6580
7248
  appName: ctx.appConfig.name,
6581
7249
  resourceType: "topic",
6582
7250
  resourceName: id
6583
7251
  });
6584
- const topic = new aws20.sns.Topic(group, "topic", {
7252
+ const topic = new aws21.sns.Topic(group, "topic", {
6585
7253
  name
6586
7254
  });
6587
7255
  ctx.shared.add("topic", `arn`, id, topic.arn);
@@ -6599,15 +7267,15 @@ var topicFeature = defineFeature({
6599
7267
  },
6600
7268
  onStack(ctx) {
6601
7269
  for (const [id, props] of Object.entries(ctx.stackConfig.subscribers ?? {})) {
6602
- const group = new Group19(ctx.stack, "topic", id);
7270
+ const group = new Group20(ctx.stack, "topic", id);
6603
7271
  const topicArn = ctx.shared.entry("topic", "arn", id);
6604
7272
  const { lambda } = createAsyncLambdaFunction(group, ctx, `topic`, id, props);
6605
- new aws20.sns.TopicSubscription(group, id, {
7273
+ new aws21.sns.TopicSubscription(group, id, {
6606
7274
  topicArn,
6607
7275
  protocol: "lambda",
6608
7276
  endpoint: lambda.arn
6609
7277
  });
6610
- new aws20.lambda.Permission(group, id, {
7278
+ new aws21.lambda.Permission(group, id, {
6611
7279
  action: "lambda:InvokeFunction",
6612
7280
  principal: "sns.amazonaws.com",
6613
7281
  functionName: lambda.functionName,
@@ -6618,13 +7286,13 @@ var topicFeature = defineFeature({
6618
7286
  });
6619
7287
 
6620
7288
  // src/feature/vpc/index.ts
6621
- import { Group as Group20 } from "@terraforge/core";
6622
- import { aws as aws21 } from "@terraforge/aws";
7289
+ import { Group as Group21 } from "@terraforge/core";
7290
+ import { aws as aws22 } from "@terraforge/aws";
6623
7291
  var vpcFeature = defineFeature({
6624
7292
  name: "vpc",
6625
7293
  onApp(ctx) {
6626
- const group = new Group20(ctx.base, "vpc", "main");
6627
- const vpc = new aws21.Vpc(group, "vpc", {
7294
+ const group = new Group21(ctx.base, "vpc", "main");
7295
+ const vpc = new aws22.Vpc(group, "vpc", {
6628
7296
  tags: {
6629
7297
  Name: ctx.app.name
6630
7298
  },
@@ -6634,39 +7302,39 @@ var vpcFeature = defineFeature({
6634
7302
  ipv6CidrBlockNetworkBorderGroup: ctx.appConfig.region,
6635
7303
  assignGeneratedIpv6CidrBlock: true
6636
7304
  });
6637
- const privateRouteTable = new aws21.route.Table(group, "private", {
7305
+ const privateRouteTable = new aws22.route.Table(group, "private", {
6638
7306
  vpcId: vpc.id,
6639
7307
  tags: {
6640
7308
  Name: "private"
6641
7309
  }
6642
7310
  });
6643
- const publicRouteTable = new aws21.route.Table(group, "public", {
7311
+ const publicRouteTable = new aws22.route.Table(group, "public", {
6644
7312
  vpcId: vpc.id,
6645
7313
  tags: {
6646
7314
  Name: "public"
6647
7315
  }
6648
7316
  });
6649
- const gateway = new aws21.internet.Gateway(group, "gateway", {
7317
+ const gateway = new aws22.internet.Gateway(group, "gateway", {
6650
7318
  tags: {
6651
7319
  Name: ctx.app.name
6652
7320
  }
6653
7321
  });
6654
- const egressOnlyInternetGateway = new aws21.egress.OnlyInternetGateway(group, "gateway", {
7322
+ const egressOnlyInternetGateway = new aws22.egress.OnlyInternetGateway(group, "gateway", {
6655
7323
  vpcId: vpc.id,
6656
7324
  tags: {
6657
7325
  Name: ctx.app.name
6658
7326
  }
6659
7327
  });
6660
- const attachment = new aws21.internet.GatewayAttachment(group, "attachment", {
7328
+ const attachment = new aws22.internet.GatewayAttachment(group, "attachment", {
6661
7329
  vpcId: vpc.id,
6662
7330
  internetGatewayId: gateway.id
6663
7331
  });
6664
- new aws21.Route(group, "ipv4", {
7332
+ new aws22.Route(group, "ipv4", {
6665
7333
  routeTableId: publicRouteTable.id,
6666
7334
  destinationCidrBlock: "0.0.0.0/0",
6667
7335
  gatewayId: gateway.id
6668
7336
  });
6669
- new aws21.Route(group, "ipv6", {
7337
+ new aws22.Route(group, "ipv6", {
6670
7338
  routeTableId: privateRouteTable.id,
6671
7339
  destinationIpv6CidrBlock: "::/0",
6672
7340
  egressOnlyGatewayId: egressOnlyInternetGateway.id
@@ -6691,7 +7359,7 @@ var vpcFeature = defineFeature({
6691
7359
  for (const i in zones) {
6692
7360
  const index = Number(i) + 1;
6693
7361
  const id = `${type}-${index}`;
6694
- const subnet = new aws21.Subnet(group, id, {
7362
+ const subnet = new aws22.Subnet(group, id, {
6695
7363
  tags: {
6696
7364
  Name: `${ctx.app.name}--${type}-${index}`
6697
7365
  },
@@ -6705,7 +7373,7 @@ var vpcFeature = defineFeature({
6705
7373
  return `${cidrBlock}${ipv6Identifier++}::/64`;
6706
7374
  })
6707
7375
  });
6708
- new aws21.route.TableAssociation(group, id, {
7376
+ new aws22.route.TableAssociation(group, id, {
6709
7377
  routeTableId: table.id,
6710
7378
  subnetId: subnet.id
6711
7379
  });
@@ -6718,9 +7386,9 @@ var vpcFeature = defineFeature({
6718
7386
  });
6719
7387
 
6720
7388
  // src/feature/alert/index.ts
6721
- import { Group as Group21 } from "@terraforge/core";
6722
- import { aws as aws22 } from "@terraforge/aws";
6723
- var typeGenCode9 = `
7389
+ import { Group as Group22 } from "@terraforge/core";
7390
+ import { aws as aws23 } from "@terraforge/aws";
7391
+ var typeGenCode10 = `
6724
7392
  import type { PublishOptions } from '@awsless/sns'
6725
7393
 
6726
7394
  type Alert<Name extends string> = {
@@ -6748,7 +7416,7 @@ var alertFeature = defineFeature({
6748
7416
  mockResponses.addType(alert, "Mock");
6749
7417
  mocks.addType(alert, `MockBuilder`);
6750
7418
  }
6751
- gen.addCode(typeGenCode9);
7419
+ gen.addCode(typeGenCode10);
6752
7420
  gen.addInterface("AlertResources", resources2);
6753
7421
  gen.addInterface("AlertMock", mocks);
6754
7422
  gen.addInterface("AlertMockResponse", mockResponses);
@@ -6756,17 +7424,17 @@ var alertFeature = defineFeature({
6756
7424
  },
6757
7425
  onApp(ctx) {
6758
7426
  for (const [id, emails] of Object.entries(ctx.appConfig.defaults.alerts ?? {})) {
6759
- const group = new Group21(ctx.base, "alert", id);
7427
+ const group = new Group22(ctx.base, "alert", id);
6760
7428
  const name = formatGlobalResourceName({
6761
7429
  appName: ctx.appConfig.name,
6762
7430
  resourceType: "alert",
6763
7431
  resourceName: id
6764
7432
  });
6765
- const topic = new aws22.sns.Topic(group, "topic", {
7433
+ const topic = new aws23.sns.Topic(group, "topic", {
6766
7434
  name
6767
7435
  });
6768
7436
  for (const email of emails) {
6769
- new aws22.sns.TopicSubscription(group, email, {
7437
+ new aws23.sns.TopicSubscription(group, email, {
6770
7438
  topicArn: topic.arn,
6771
7439
  protocol: "email",
6772
7440
  endpoint: email
@@ -6787,13 +7455,13 @@ var alertFeature = defineFeature({
6787
7455
  });
6788
7456
 
6789
7457
  // src/feature/layer/index.ts
6790
- import { Group as Group22 } from "@terraforge/core";
6791
- import { aws as aws23 } from "@terraforge/aws";
7458
+ import { Group as Group23 } from "@terraforge/core";
7459
+ import { aws as aws24 } from "@terraforge/aws";
6792
7460
  var layerFeature = defineFeature({
6793
7461
  name: "layer",
6794
7462
  onBefore(ctx) {
6795
- const group = new Group22(ctx.base, "layer", "asset");
6796
- const bucket = new aws23.s3.Bucket(group, "bucket", {
7463
+ const group = new Group23(ctx.base, "layer", "asset");
7464
+ const bucket = new aws24.s3.Bucket(group, "bucket", {
6797
7465
  bucket: formatGlobalResourceName({
6798
7466
  appName: ctx.app.name,
6799
7467
  resourceType: "layer",
@@ -6825,15 +7493,15 @@ var layerFeature = defineFeature({
6825
7493
  }
6826
7494
  for (const [id, _props] of layers) {
6827
7495
  const props = _props;
6828
- const group = new Group22(ctx.base, "layer", id);
6829
- const zip = new aws23.s3.BucketObject(group, "zip", {
7496
+ const group = new Group23(ctx.base, "layer", id);
7497
+ const zip = new aws24.s3.BucketObject(group, "zip", {
6830
7498
  bucket: ctx.shared.get("layer", "bucket-name"),
6831
7499
  key: `/layer/${id}.zip`,
6832
7500
  contentType: "application/zip",
6833
7501
  source: props.file,
6834
7502
  sourceHash: $hash(props.file)
6835
7503
  });
6836
- const layer = new aws23.lambda.LayerVersion(
7504
+ const layer = new aws24.lambda.LayerVersion(
6837
7505
  group,
6838
7506
  "layer",
6839
7507
  {
@@ -6868,14 +7536,14 @@ var layerFeature = defineFeature({
6868
7536
  });
6869
7537
 
6870
7538
  // src/feature/image/index.ts
6871
- import { Group as Group23 } from "@terraforge/core";
6872
- import { aws as aws24 } from "@terraforge/aws";
6873
- import { join as join15, dirname as dirname7 } from "path";
6874
- import { mebibytes as mebibytes6 } from "@awsless/size";
6875
- import { seconds as seconds8, toDays as toDays8 } from "@awsless/duration";
6876
- import { fileURLToPath as fileURLToPath2 } from "url";
7539
+ import { Group as Group24 } from "@terraforge/core";
7540
+ import { aws as aws25 } from "@terraforge/aws";
7541
+ import { join as join18, dirname as dirname9 } from "path";
7542
+ import { mebibytes as mebibytes7 } from "@awsless/size";
7543
+ import { seconds as seconds9, toDays as toDays9 } from "@awsless/duration";
7544
+ import { fileURLToPath as fileURLToPath4 } from "url";
6877
7545
  import { glob as glob4 } from "glob";
6878
- var __dirname3 = dirname7(fileURLToPath2(import.meta.url));
7546
+ var __dirname5 = dirname9(fileURLToPath4(import.meta.url));
6879
7547
  var imageFeature = defineFeature({
6880
7548
  name: "image",
6881
7549
  onApp(ctx) {
@@ -6885,21 +7553,21 @@ var imageFeature = defineFeature({
6885
7553
  if (found.length === 0) {
6886
7554
  return;
6887
7555
  }
6888
- const group = new Group23(ctx.base, "image", "layer");
6889
- const path = join15(__dirname3, "/layers/sharp-arm.zip");
7556
+ const group = new Group24(ctx.base, "image", "layer");
7557
+ const path = join18(__dirname5, "/layers/sharp-arm.zip");
6890
7558
  const layerId = formatGlobalResourceName({
6891
7559
  appName: ctx.appConfig.name,
6892
7560
  resourceType: "layer",
6893
7561
  resourceName: "sharp"
6894
7562
  });
6895
- const zipFile = new aws24.s3.BucketObject(group, "layer", {
7563
+ const zipFile = new aws25.s3.BucketObject(group, "layer", {
6896
7564
  bucket: ctx.shared.get("layer", "bucket-name"),
6897
7565
  key: `/layer/${layerId}.zip`,
6898
7566
  contentType: "application/zip",
6899
7567
  source: path,
6900
7568
  sourceHash: $hash(path)
6901
7569
  });
6902
- const layer = new aws24.lambda.LayerVersion(
7570
+ const layer = new aws25.lambda.LayerVersion(
6903
7571
  group,
6904
7572
  "layer",
6905
7573
  {
@@ -6926,7 +7594,7 @@ var imageFeature = defineFeature({
6926
7594
  },
6927
7595
  onStack(ctx) {
6928
7596
  for (const [id, props] of Object.entries(ctx.stackConfig.images ?? {})) {
6929
- const group = new Group23(ctx.stack, "image", id);
7597
+ const group = new Group24(ctx.stack, "image", id);
6930
7598
  const routerId = ctx.shared.entry("router", "id", props.router);
6931
7599
  const addRoutes = ctx.shared.entry("router", "addRoutes", props.router);
6932
7600
  const routeKey = props.path.endsWith("/") ? `${props.path}*` : `${props.path}/*`;
@@ -6936,7 +7604,7 @@ var imageFeature = defineFeature({
6936
7604
  }
6937
7605
  let s3Origin;
6938
7606
  if (props.origin.static) {
6939
- s3Origin = new aws24.s3.Bucket(group, "origin", {
7607
+ s3Origin = new aws25.s3.Bucket(group, "origin", {
6940
7608
  bucket: formatLocalResourceName({
6941
7609
  appName: ctx.app.name,
6942
7610
  stackName: ctx.stack.name,
@@ -6946,7 +7614,7 @@ var imageFeature = defineFeature({
6946
7614
  forceDestroy: true
6947
7615
  });
6948
7616
  }
6949
- const cacheBucket = new aws24.s3.Bucket(group, "cache", {
7617
+ const cacheBucket = new aws25.s3.Bucket(group, "cache", {
6950
7618
  bucket: formatLocalResourceName({
6951
7619
  appName: ctx.app.name,
6952
7620
  stackName: ctx.stack.name,
@@ -6963,7 +7631,7 @@ var imageFeature = defineFeature({
6963
7631
  enabled: true,
6964
7632
  id: "image-cache-duration",
6965
7633
  expiration: {
6966
- days: toDays8(props.cacheDuration)
7634
+ days: toDays9(props.cacheDuration)
6967
7635
  }
6968
7636
  }
6969
7637
  ]
@@ -6975,23 +7643,23 @@ var imageFeature = defineFeature({
6975
7643
  resourceName: "sharp"
6976
7644
  });
6977
7645
  const serverLambda = createPrebuildLambdaFunction(group, ctx, "image", id, {
6978
- bundleFile: join15(__dirname3, "/prebuild/image/bundle.zip"),
6979
- bundleHash: join15(__dirname3, "/prebuild/image/HASH"),
6980
- memorySize: mebibytes6(512),
6981
- timeout: seconds8(10),
7646
+ bundleFile: join18(__dirname5, "/prebuild/image/bundle.zip"),
7647
+ bundleHash: join18(__dirname5, "/prebuild/image/HASH"),
7648
+ memorySize: mebibytes7(512),
7649
+ timeout: seconds9(10),
6982
7650
  handler: "index.default",
6983
7651
  runtime: "nodejs24.x",
6984
7652
  log: props.log,
6985
7653
  layers: [sharpLayerId]
6986
7654
  });
6987
- const permission = new aws24.lambda.Permission(group, "permission", {
7655
+ const permission = new aws25.lambda.Permission(group, "permission", {
6988
7656
  principal: "cloudfront.amazonaws.com",
6989
7657
  action: "lambda:InvokeFunctionUrl",
6990
7658
  functionName: serverLambda.lambda.functionName,
6991
7659
  functionUrlAuthType: "AWS_IAM",
6992
7660
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
6993
7661
  });
6994
- const serverLambdaUrl = new aws24.lambda.FunctionUrl(
7662
+ const serverLambdaUrl = new aws25.lambda.FunctionUrl(
6995
7663
  group,
6996
7664
  "url",
6997
7665
  {
@@ -7048,11 +7716,11 @@ var imageFeature = defineFeature({
7048
7716
  nodir: true
7049
7717
  });
7050
7718
  for (const file of files) {
7051
- new aws24.s3.BucketObject(group, `static-${file}`, {
7719
+ new aws25.s3.BucketObject(group, `static-${file}`, {
7052
7720
  bucket: s3Origin.bucket,
7053
7721
  key: file,
7054
- source: join15(props.origin.static, file),
7055
- sourceHash: $hash(join15(props.origin.static, file))
7722
+ source: join18(props.origin.static, file),
7723
+ sourceHash: $hash(join18(props.origin.static, file))
7056
7724
  });
7057
7725
  }
7058
7726
  }
@@ -7065,19 +7733,19 @@ var imageFeature = defineFeature({
7065
7733
  });
7066
7734
 
7067
7735
  // src/feature/icon/index.ts
7068
- import { Group as Group24 } from "@terraforge/core";
7069
- import { aws as aws25 } from "@terraforge/aws";
7070
- import { join as join16, dirname as dirname8 } from "path";
7071
- import { mebibytes as mebibytes7 } from "@awsless/size";
7072
- import { seconds as seconds9, toDays as toDays9 } from "@awsless/duration";
7073
- import { fileURLToPath as fileURLToPath3 } from "url";
7736
+ import { Group as Group25 } from "@terraforge/core";
7737
+ import { aws as aws26 } from "@terraforge/aws";
7738
+ import { join as join19, dirname as dirname10 } from "path";
7739
+ import { mebibytes as mebibytes8 } from "@awsless/size";
7740
+ import { seconds as seconds10, toDays as toDays10 } from "@awsless/duration";
7741
+ import { fileURLToPath as fileURLToPath5 } from "url";
7074
7742
  import { glob as glob5 } from "glob";
7075
- var __dirname4 = dirname8(fileURLToPath3(import.meta.url));
7743
+ var __dirname6 = dirname10(fileURLToPath5(import.meta.url));
7076
7744
  var iconFeature = defineFeature({
7077
7745
  name: "icon",
7078
7746
  onStack(ctx) {
7079
7747
  for (const [id, props] of Object.entries(ctx.stackConfig.icons ?? {})) {
7080
- const group = new Group24(ctx.stack, "icon", id);
7748
+ const group = new Group25(ctx.stack, "icon", id);
7081
7749
  const routerId = ctx.shared.entry("router", "id", props.router);
7082
7750
  const addRoutes = ctx.shared.entry("router", "addRoutes", props.router);
7083
7751
  const routeKey = props.path.endsWith("/") ? `${props.path}*` : `${props.path}/*`;
@@ -7087,7 +7755,7 @@ var iconFeature = defineFeature({
7087
7755
  }
7088
7756
  let s3Origin;
7089
7757
  if (props.origin.static) {
7090
- s3Origin = new aws25.s3.Bucket(group, "origin", {
7758
+ s3Origin = new aws26.s3.Bucket(group, "origin", {
7091
7759
  bucket: formatLocalResourceName({
7092
7760
  appName: ctx.app.name,
7093
7761
  stackName: ctx.stack.name,
@@ -7097,7 +7765,7 @@ var iconFeature = defineFeature({
7097
7765
  forceDestroy: true
7098
7766
  });
7099
7767
  }
7100
- const cacheBucket = new aws25.s3.Bucket(group, "cache", {
7768
+ const cacheBucket = new aws26.s3.Bucket(group, "cache", {
7101
7769
  bucket: formatLocalResourceName({
7102
7770
  appName: ctx.app.name,
7103
7771
  stackName: ctx.stack.name,
@@ -7114,29 +7782,29 @@ var iconFeature = defineFeature({
7114
7782
  enabled: true,
7115
7783
  id: "icon-cache-duration",
7116
7784
  expiration: {
7117
- days: toDays9(props.cacheDuration)
7785
+ days: toDays10(props.cacheDuration)
7118
7786
  }
7119
7787
  }
7120
7788
  ]
7121
7789
  } : {}
7122
7790
  });
7123
7791
  const serverLambda = createPrebuildLambdaFunction(group, ctx, "icon", id, {
7124
- bundleFile: join16(__dirname4, "/prebuild/icon/bundle.zip"),
7125
- bundleHash: join16(__dirname4, "/prebuild/icon/HASH"),
7126
- memorySize: mebibytes7(512),
7127
- timeout: seconds9(10),
7792
+ bundleFile: join19(__dirname6, "/prebuild/icon/bundle.zip"),
7793
+ bundleHash: join19(__dirname6, "/prebuild/icon/HASH"),
7794
+ memorySize: mebibytes8(512),
7795
+ timeout: seconds10(10),
7128
7796
  handler: "index.default",
7129
7797
  runtime: "nodejs24.x",
7130
7798
  log: props.log
7131
7799
  });
7132
- const permission = new aws25.lambda.Permission(group, "permission", {
7800
+ const permission = new aws26.lambda.Permission(group, "permission", {
7133
7801
  principal: "cloudfront.amazonaws.com",
7134
7802
  action: "lambda:InvokeFunctionUrl",
7135
7803
  functionName: serverLambda.lambda.functionName,
7136
7804
  functionUrlAuthType: "AWS_IAM",
7137
7805
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
7138
7806
  });
7139
- const serverLambdaUrl = new aws25.lambda.FunctionUrl(
7807
+ const serverLambdaUrl = new aws26.lambda.FunctionUrl(
7140
7808
  group,
7141
7809
  "url",
7142
7810
  {
@@ -7196,11 +7864,11 @@ var iconFeature = defineFeature({
7196
7864
  if (!file.endsWith(".svg")) {
7197
7865
  throw new Error(`Icon file "${file}" in "${props.origin.static}" is not an SVG file.`);
7198
7866
  }
7199
- new aws25.s3.BucketObject(group, `static-${file}`, {
7867
+ new aws26.s3.BucketObject(group, `static-${file}`, {
7200
7868
  bucket: s3Origin.bucket,
7201
7869
  key: file,
7202
- source: join16(props.origin.static, file),
7203
- sourceHash: $hash(join16(props.origin.static, file))
7870
+ source: join19(props.origin.static, file),
7871
+ sourceHash: $hash(join19(props.origin.static, file))
7204
7872
  });
7205
7873
  }
7206
7874
  }
@@ -7213,30 +7881,30 @@ var iconFeature = defineFeature({
7213
7881
  });
7214
7882
 
7215
7883
  // src/feature/job/index.ts
7216
- import { Group as Group26, Output as Output7, resolveInputs as resolveInputs4, findInputDeps as findInputDeps4 } from "@terraforge/core";
7217
- import { aws as aws27 } from "@terraforge/aws";
7884
+ import { Group as Group27, Output as Output8, resolveInputs as resolveInputs5, findInputDeps as findInputDeps5 } from "@terraforge/core";
7885
+ import { aws as aws28 } from "@terraforge/aws";
7218
7886
  import { camelCase as camelCase8 } from "change-case";
7219
7887
  import deepmerge5 from "deepmerge";
7220
7888
  import { relative as relative8 } from "path";
7221
7889
 
7222
7890
  // src/feature/job/util.ts
7223
- import { createHash as createHash4 } from "crypto";
7224
- import { toDays as toDays10, toSeconds as toSeconds9 } from "@awsless/duration";
7225
- import { stringify } from "@awsless/json";
7891
+ import { createHash as createHash6 } from "crypto";
7892
+ import { toDays as toDays11, toSeconds as toSeconds8 } from "@awsless/duration";
7893
+ import { stringify as stringify2 } from "@awsless/json";
7226
7894
  import { toMebibytes as toMebibytes5 } from "@awsless/size";
7227
7895
  import { generateFileHash as generateFileHash2 } from "@awsless/ts-file-cache";
7228
- import { aws as aws26 } from "@terraforge/aws";
7229
- import { Group as Group25, Output as Output6, findInputDeps as findInputDeps3, resolveInputs as resolveInputs3 } from "@terraforge/core";
7230
- import { constantCase as constantCase12, pascalCase as pascalCase3 } from "change-case";
7896
+ import { aws as aws27 } from "@terraforge/aws";
7897
+ import { Group as Group26, Output as Output7, findInputDeps as findInputDeps4, resolveInputs as resolveInputs4 } from "@terraforge/core";
7898
+ import { constantCase as constantCase12, pascalCase as pascalCase4 } from "change-case";
7231
7899
  import deepmerge4 from "deepmerge";
7232
7900
 
7233
7901
  // src/feature/job/build/executable.ts
7234
- import { createHash as createHash3 } from "crypto";
7235
- import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
7236
- import { join as join17, resolve } from "path";
7902
+ import { createHash as createHash5 } from "crypto";
7903
+ import { readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
7904
+ import { join as join20, resolve } from "path";
7237
7905
  var buildJobExecutable = async (input, outputPath, architecture) => {
7238
- const filePath = join17(outputPath, "program");
7239
- const wrapperPath = join17(outputPath, "wrapper.ts");
7906
+ const filePath = join20(outputPath, "program");
7907
+ const wrapperPath = join20(outputPath, "wrapper.ts");
7240
7908
  const handlerPath = resolve(input);
7241
7909
  await writeFile3(
7242
7910
  wrapperPath,
@@ -7291,16 +7959,16 @@ await handler(payload)
7291
7959
  throw new ExpectedError(`Job executable build failed:
7292
7960
  ${result.logs?.map((log35) => log35.message).join("\n")}`);
7293
7961
  }
7294
- const file = await readFile4(filePath);
7962
+ const file = await readFile6(filePath);
7295
7963
  return {
7296
- hash: createHash3("sha1").update(file).update(target).digest("hex"),
7964
+ hash: createHash5("sha1").update(file).update(target).digest("hex"),
7297
7965
  file
7298
7966
  };
7299
7967
  };
7300
7968
 
7301
7969
  // src/feature/job/util.ts
7302
7970
  var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7303
- const group = new Group25(parentGroup, "job", ns);
7971
+ const group = new Group26(parentGroup, "job", ns);
7304
7972
  const name = formatLocalResourceName({
7305
7973
  appName: ctx.app.name,
7306
7974
  stackName: ctx.stack.name,
@@ -7326,13 +7994,13 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7326
7994
  };
7327
7995
  });
7328
7996
  });
7329
- const code = new aws26.s3.BucketObject(group, "code", {
7997
+ const code = new aws27.s3.BucketObject(group, "code", {
7330
7998
  bucket: ctx.shared.get("job", "bucket-name"),
7331
7999
  key: name,
7332
8000
  source: relativePath(getBuildPath("job", name, "program")),
7333
8001
  sourceHash: $file(getBuildPath("job", name, "HASH"))
7334
8002
  });
7335
- const executionRole = new aws26.iam.Role(group, "execution-role", {
8003
+ const executionRole = new aws27.iam.Role(group, "execution-role", {
7336
8004
  name: shortId(`${shortName}:execution-role`),
7337
8005
  description: name,
7338
8006
  assumeRolePolicy: JSON.stringify({
@@ -7349,7 +8017,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7349
8017
  }),
7350
8018
  managedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
7351
8019
  });
7352
- const role = new aws26.iam.Role(
8020
+ const role = new aws27.iam.Role(
7353
8021
  group,
7354
8022
  "task-role",
7355
8023
  {
@@ -7375,7 +8043,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7375
8043
  Version: "2012-10-17",
7376
8044
  Statement: [
7377
8045
  {
7378
- Effect: pascalCase3("allow"),
8046
+ Effect: pascalCase4("allow"),
7379
8047
  Action: ["s3:GetObject", "s3:HeadObject"],
7380
8048
  Resource: [`arn:aws:s3:::${bucket}/${key}`, `arn:aws:s3:::${bucket}/payloads/*`]
7381
8049
  }
@@ -7391,16 +8059,16 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7391
8059
  );
7392
8060
  const statements = [];
7393
8061
  const statementDeps = /* @__PURE__ */ new Set();
7394
- const policy = new aws26.iam.RolePolicy(group, "policy", {
8062
+ const policy = new aws27.iam.RolePolicy(group, "policy", {
7395
8063
  role: role.name,
7396
8064
  name: "task-policy",
7397
- policy: new Output6(statementDeps, async (resolve2) => {
7398
- const list3 = await resolveInputs3(statements);
8065
+ policy: new Output7(statementDeps, async (resolve2) => {
8066
+ const list3 = await resolveInputs4(statements);
7399
8067
  resolve2(
7400
8068
  JSON.stringify({
7401
8069
  Version: "2012-10-17",
7402
8070
  Statement: list3.map((statement) => ({
7403
- Effect: pascalCase3(statement.effect ?? "allow"),
8071
+ Effect: pascalCase4(statement.effect ?? "allow"),
7404
8072
  Action: statement.actions,
7405
8073
  Resource: statement.resources
7406
8074
  }))
@@ -7410,7 +8078,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7410
8078
  });
7411
8079
  const addPermission = (...permissions) => {
7412
8080
  statements.push(...permissions);
7413
- for (const dep of findInputDeps3(permissions)) {
8081
+ for (const dep of findInputDeps4(permissions)) {
7414
8082
  statementDeps.add(dep);
7415
8083
  }
7416
8084
  };
@@ -7419,12 +8087,12 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7419
8087
  });
7420
8088
  let logGroup;
7421
8089
  if (props.log.retention && props.log.retention.value > 0n) {
7422
- logGroup = new aws26.cloudwatch.LogGroup(group, "log", {
8090
+ logGroup = new aws27.cloudwatch.LogGroup(group, "log", {
7423
8091
  name: `/aws/ecs/${name}`,
7424
- retentionInDays: toDays10(props.log.retention)
8092
+ retentionInDays: toDays11(props.log.retention)
7425
8093
  });
7426
8094
  if (ctx.shared.has("on-error-log", "subscriber-arn")) {
7427
- new aws26.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
8095
+ new aws27.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
7428
8096
  name: "error-log-subscription",
7429
8097
  destinationArn: ctx.shared.get("on-error-log", "subscriber-arn"),
7430
8098
  logGroupName: logGroup.name,
@@ -7442,7 +8110,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7442
8110
  const taskDependsOn = [code];
7443
8111
  const accessPoint = props.persistentStorage ? (() => {
7444
8112
  const fileSystemId = ctx.shared.get("job", "persistent-storage-file-system-id");
7445
- const accessPoint2 = new aws26.efs.AccessPoint(
8113
+ const accessPoint2 = new aws27.efs.AccessPoint(
7446
8114
  group,
7447
8115
  "access-point",
7448
8116
  {
@@ -7467,7 +8135,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7467
8135
  fileSystemId
7468
8136
  };
7469
8137
  })() : void 0;
7470
- const task2 = new aws26.ecs.TaskDefinition(
8138
+ const task2 = new aws27.ecs.TaskDefinition(
7471
8139
  group,
7472
8140
  "task",
7473
8141
  {
@@ -7497,9 +8165,9 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7497
8165
  }
7498
8166
  ]
7499
8167
  },
7500
- containerDefinitions: new Output6(variableDeps, async (resolve2) => {
7501
- const data = await resolveInputs3(variables);
7502
- const { s3Bucket, s3Key } = await resolveInputs3({
8168
+ containerDefinitions: new Output7(variableDeps, async (resolve2) => {
8169
+ const data = await resolveInputs4(variables);
8170
+ const { s3Bucket, s3Key } = await resolveInputs4({
7503
8171
  s3Bucket: code.bucket,
7504
8172
  s3Key: code.key
7505
8173
  });
@@ -7515,7 +8183,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7515
8183
  [
7516
8184
  ...props.startupCommand?.length ? [props.startupCommand.join(" && ")] : [],
7517
8185
  `if [ "$(cat /root/.code-hash 2>/dev/null)" != "$CODE_HASH" ]; then command -v aws >/dev/null 2>&1 || dnf install -y awscli && aws s3 cp s3://${s3Bucket}/${s3Key} /root/program.tmp && mv /root/program.tmp /root/program && chmod +x /root/program && echo "$CODE_HASH" > /root/.code-hash; fi`,
7518
- `exec timeout --kill-after=10 ${toSeconds9(props.timeout)} /root/program`
8186
+ `exec timeout --kill-after=10 ${toSeconds8(props.timeout)} /root/program`
7519
8187
  ].join(" && ")
7520
8188
  ],
7521
8189
  environment: Object.entries(data).map(([name2, value]) => ({
@@ -7561,7 +8229,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7561
8229
  );
7562
8230
  ctx.onEnv((name2, value) => {
7563
8231
  variables[name2] = value;
7564
- for (const dep of findInputDeps3([value])) {
8232
+ for (const dep of findInputDeps4([value])) {
7565
8233
  variableDeps.add(dep);
7566
8234
  }
7567
8235
  });
@@ -7570,7 +8238,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7570
8238
  variables.AWS_ACCOUNT_ID = ctx.accountId;
7571
8239
  variables.STACK = ctx.stackConfig.name;
7572
8240
  variables.CODE_HASH = code.sourceHash;
7573
- variables.JOB_CONFIG_HASH = createHash4("sha1").update(stringify(props)).digest("hex");
8241
+ variables.JOB_CONFIG_HASH = createHash6("sha1").update(stringify2(props)).digest("hex");
7574
8242
  if (props.environment) {
7575
8243
  for (const [key, value] of Object.entries(props.environment)) {
7576
8244
  variables[key] = value;
@@ -7586,7 +8254,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7586
8254
  };
7587
8255
 
7588
8256
  // src/feature/job/index.ts
7589
- var typeGenCode10 = `
8257
+ var typeGenCode11 = `
7590
8258
  import type { Mock } from 'vitest'
7591
8259
 
7592
8260
  type Func = (...args: any[]) => any
@@ -7642,15 +8310,15 @@ var jobFeature = defineFeature({
7642
8310
  resources2.addType(stack.name, resource);
7643
8311
  mockResponses.addType(stack.name, mockResponse);
7644
8312
  }
7645
- types2.addCode(typeGenCode10);
8313
+ types2.addCode(typeGenCode11);
7646
8314
  types2.addInterface("JobResources", resources2);
7647
8315
  types2.addInterface("JobMock", mocks);
7648
8316
  types2.addInterface("JobMockResponse", mockResponses);
7649
8317
  await ctx.write("job.d.ts", types2, true);
7650
8318
  },
7651
8319
  onBefore(ctx) {
7652
- const group = new Group26(ctx.base, "job", "asset");
7653
- const bucket = new aws27.s3.Bucket(group, "bucket", {
8320
+ const group = new Group27(ctx.base, "job", "asset");
8321
+ const bucket = new aws28.s3.Bucket(group, "bucket", {
7654
8322
  bucket: formatGlobalResourceName({
7655
8323
  appName: ctx.app.name,
7656
8324
  resourceType: "job",
@@ -7676,8 +8344,8 @@ var jobFeature = defineFeature({
7676
8344
  if (found.length === 0) {
7677
8345
  return;
7678
8346
  }
7679
- const group = new Group26(ctx.base, "job", "cluster");
7680
- const cluster = new aws27.ecs.Cluster(
8347
+ const group = new Group27(ctx.base, "job", "cluster");
8348
+ const cluster = new aws28.ecs.Cluster(
7681
8349
  group,
7682
8350
  "cluster",
7683
8351
  {
@@ -7689,7 +8357,7 @@ var jobFeature = defineFeature({
7689
8357
  );
7690
8358
  ctx.shared.set("job", "cluster-name", cluster.name);
7691
8359
  ctx.shared.set("job", "cluster-arn", cluster.arn);
7692
- const securityGroup = new aws27.security.Group(group, "security-group", {
8360
+ const securityGroup = new aws28.security.Group(group, "security-group", {
7693
8361
  name: `${ctx.app.name}-job`,
7694
8362
  description: "Shared security group for jobs",
7695
8363
  vpcId: ctx.shared.get("vpc", "id"),
@@ -7698,7 +8366,7 @@ var jobFeature = defineFeature({
7698
8366
  APP: ctx.appConfig.name
7699
8367
  }
7700
8368
  });
7701
- new aws27.vpc.SecurityGroupEgressRule(group, "egress-rule", {
8369
+ new aws28.vpc.SecurityGroupEgressRule(group, "egress-rule", {
7702
8370
  securityGroupId: securityGroup.id,
7703
8371
  description: "Allow all outbound traffic from jobs",
7704
8372
  ipProtocol: "-1",
@@ -7715,8 +8383,8 @@ var jobFeature = defineFeature({
7715
8383
  })
7716
8384
  );
7717
8385
  if (needsPersistentStorage) {
7718
- const storageGroup = new Group26(ctx.base, "job", "persistent-storage");
7719
- new aws27.vpc.SecurityGroupIngressRule(storageGroup, "ingress-rule", {
8386
+ const storageGroup = new Group27(ctx.base, "job", "persistent-storage");
8387
+ new aws28.vpc.SecurityGroupIngressRule(storageGroup, "ingress-rule", {
7720
8388
  securityGroupId: securityGroup.id,
7721
8389
  referencedSecurityGroupId: securityGroup.id,
7722
8390
  description: "Allow NFS traffic from jobs",
@@ -7727,7 +8395,7 @@ var jobFeature = defineFeature({
7727
8395
  APP: ctx.appConfig.name
7728
8396
  }
7729
8397
  });
7730
- const fileSystem = new aws27.efs.FileSystem(storageGroup, "file-system", {
8398
+ const fileSystem = new aws28.efs.FileSystem(storageGroup, "file-system", {
7731
8399
  encrypted: true,
7732
8400
  performanceMode: "generalPurpose",
7733
8401
  throughputMode: "elastic",
@@ -7737,7 +8405,7 @@ var jobFeature = defineFeature({
7737
8405
  }
7738
8406
  });
7739
8407
  for (const [index, subnetId] of ctx.shared.get("vpc", "public-subnets").entries()) {
7740
- new aws27.efs.MountTarget(storageGroup, `mount-target-${index + 1}`, {
8408
+ new aws28.efs.MountTarget(storageGroup, `mount-target-${index + 1}`, {
7741
8409
  fileSystemId: fileSystem.id,
7742
8410
  subnetId,
7743
8411
  securityGroups: [securityGroup.id]
@@ -7752,15 +8420,15 @@ var jobFeature = defineFeature({
7752
8420
  const subnets = ctx.shared.get("vpc", "public-subnets");
7753
8421
  ctx.addEnv(
7754
8422
  "JOB_SUBNETS",
7755
- new Output7(new Set(findInputDeps4(subnets)), async (resolve2) => {
7756
- const resolved = await resolveInputs4(subnets);
8423
+ new Output8(new Set(findInputDeps5(subnets)), async (resolve2) => {
8424
+ const resolved = await resolveInputs5(subnets);
7757
8425
  resolve2(JSON.stringify(resolved));
7758
8426
  })
7759
8427
  );
7760
8428
  ctx.addEnv("JOB_SECURITY_GROUP", ctx.shared.get("job", "security-group-id"));
7761
8429
  ctx.addEnv("JOB_PAYLOAD_BUCKET", ctx.shared.get("job", "bucket-name"));
7762
8430
  for (const [id, props] of jobs) {
7763
- const group = new Group26(ctx.stack, "job", id);
8431
+ const group = new Group27(ctx.stack, "job", id);
7764
8432
  createFargateJob(group, ctx, "job", id, props);
7765
8433
  }
7766
8434
  ctx.addStackPermission({
@@ -7793,77 +8461,25 @@ var jobFeature = defineFeature({
7793
8461
  });
7794
8462
 
7795
8463
  // src/feature/instance/index.ts
7796
- import { days as days10, seconds as seconds10, toSeconds as toSeconds11 } from "@awsless/duration";
8464
+ import { days as days11, seconds as seconds11, toSeconds as toSeconds10 } from "@awsless/duration";
7797
8465
  import { kibibytes as kibibytes2, toBytes as toBytes2 } from "@awsless/size";
7798
- import { Group as Group28 } from "@terraforge/core";
7799
- import { aws as aws29 } from "@terraforge/aws";
8466
+ import { Group as Group29 } from "@terraforge/core";
8467
+ import { aws as aws30 } from "@terraforge/aws";
7800
8468
  import { constantCase as constantCase14 } from "change-case";
7801
8469
 
7802
8470
  // src/feature/instance/util.ts
7803
- import { toDays as toDays11, toSeconds as toSeconds10 } from "@awsless/duration";
7804
- import { stringify as stringify2 } from "@awsless/json";
8471
+ import { toDays as toDays12, toSeconds as toSeconds9 } from "@awsless/duration";
8472
+ import { stringify as stringify3 } from "@awsless/json";
7805
8473
  import { toMebibytes as toMebibytes6 } from "@awsless/size";
7806
8474
  import { generateFileHash as generateFileHash3 } from "@awsless/ts-file-cache";
7807
- import { aws as aws28 } from "@terraforge/aws";
7808
- import { Group as Group27, Output as Output8, findInputDeps as findInputDeps5, resolveInputs as resolveInputs5 } from "@terraforge/core";
7809
- import { constantCase as constantCase13, pascalCase as pascalCase4 } from "change-case";
7810
- import { createHash as createHash6 } from "crypto";
8475
+ import { aws as aws29 } from "@terraforge/aws";
8476
+ import { Group as Group28, Output as Output9, findInputDeps as findInputDeps6, resolveInputs as resolveInputs6 } from "@terraforge/core";
8477
+ import { constantCase as constantCase13, pascalCase as pascalCase5 } from "change-case";
8478
+ import { createHash as createHash7 } from "crypto";
7811
8479
  import deepmerge6 from "deepmerge";
7812
- import { join as join19 } from "path";
7813
-
7814
- // src/feature/instance/build/executable.ts
7815
- import { createHash as createHash5 } from "crypto";
7816
- import { readFile as readFile5 } from "fs/promises";
7817
- import { join as join18 } from "path";
7818
- var buildExecutable = async (input, outputPath, architecture) => {
7819
- const filePath = join18(outputPath, "program");
7820
- const target = architecture === "x86_64" ? "bun-linux-x64" : "bun-linux-arm64";
7821
- let result;
7822
- try {
7823
- result = await Bun.build({
7824
- entrypoints: [input],
7825
- compile: {
7826
- target,
7827
- outfile: filePath
7828
- },
7829
- target: "bun",
7830
- loader: {
7831
- ".md": "text",
7832
- ".txt": "text",
7833
- ".html": "text",
7834
- ".css": "text",
7835
- ".yaml": "text",
7836
- ".yml": "text",
7837
- ".xml": "text",
7838
- ".csv": "text",
7839
- ".svg": "text",
7840
- ".png": "file",
7841
- ".jpg": "file",
7842
- ".jpeg": "file",
7843
- ".gif": "file",
7844
- ".webp": "file",
7845
- ".wasm": "file"
7846
- }
7847
- });
7848
- } catch (error) {
7849
- throw new ExpectedError(
7850
- `Executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
7851
- );
7852
- }
7853
- if (!result.success) {
7854
- throw new ExpectedError(`Executable build failed:
7855
- ${result.logs?.map((log35) => log35.message).join("\n")}`);
7856
- }
7857
- const file = await readFile5(filePath);
7858
- return {
7859
- hash: createHash5("sha1").update(file).update("x86_64").digest("hex"),
7860
- file
7861
- };
7862
- };
7863
-
7864
- // src/feature/instance/util.ts
8480
+ import { join as join21 } from "path";
7865
8481
  var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7866
- const group = new Group27(parentGroup, "instance", ns);
8482
+ const group = new Group28(parentGroup, "instance", ns);
7867
8483
  const name = formatLocalResourceName({
7868
8484
  appName: ctx.app.name,
7869
8485
  stackName: ctx.stack.name,
@@ -7889,13 +8505,13 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7889
8505
  };
7890
8506
  });
7891
8507
  });
7892
- const code = new aws28.s3.BucketObject(group, "code", {
8508
+ const code = new aws29.s3.BucketObject(group, "code", {
7893
8509
  bucket: ctx.shared.get("instance", "bucket-name"),
7894
8510
  key: name,
7895
8511
  source: relativePath(getBuildPath("instance", name, "program")),
7896
8512
  sourceHash: $file(getBuildPath("instance", name, "HASH"))
7897
8513
  });
7898
- const executionRole = new aws28.iam.Role(group, "execution-role", {
8514
+ const executionRole = new aws29.iam.Role(group, "execution-role", {
7899
8515
  name: shortId(`${shortName}:execution-role`),
7900
8516
  description: name,
7901
8517
  assumeRolePolicy: JSON.stringify({
@@ -7912,7 +8528,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7912
8528
  }),
7913
8529
  managedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
7914
8530
  });
7915
- const role = new aws28.iam.Role(
8531
+ const role = new aws29.iam.Role(
7916
8532
  group,
7917
8533
  "task-role",
7918
8534
  {
@@ -7938,7 +8554,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7938
8554
  Version: "2012-10-17",
7939
8555
  Statement: [
7940
8556
  {
7941
- Effect: pascalCase4("allow"),
8557
+ Effect: pascalCase5("allow"),
7942
8558
  Action: ["s3:getObject", "s3:HeadObject"],
7943
8559
  Resource: `arn:aws:s3:::${bucket}/${key}`
7944
8560
  }
@@ -7954,16 +8570,16 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7954
8570
  );
7955
8571
  const statements = [];
7956
8572
  const statementDeps = /* @__PURE__ */ new Set();
7957
- const policy = new aws28.iam.RolePolicy(group, "policy", {
8573
+ const policy = new aws29.iam.RolePolicy(group, "policy", {
7958
8574
  role: role.name,
7959
8575
  name: "task-policy",
7960
- policy: new Output8(statementDeps, async (resolve2) => {
7961
- const list3 = await resolveInputs5(statements);
8576
+ policy: new Output9(statementDeps, async (resolve2) => {
8577
+ const list3 = await resolveInputs6(statements);
7962
8578
  resolve2(
7963
8579
  JSON.stringify({
7964
8580
  Version: "2012-10-17",
7965
8581
  Statement: list3.map((statement) => ({
7966
- Effect: pascalCase4(statement.effect ?? "allow"),
8582
+ Effect: pascalCase5(statement.effect ?? "allow"),
7967
8583
  Action: statement.actions,
7968
8584
  Resource: statement.resources
7969
8585
  }))
@@ -7973,7 +8589,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7973
8589
  });
7974
8590
  const addPermission = (...permissions) => {
7975
8591
  statements.push(...permissions);
7976
- for (const dep of findInputDeps5(permissions)) {
8592
+ for (const dep of findInputDeps6(permissions)) {
7977
8593
  statementDeps.add(dep);
7978
8594
  }
7979
8595
  };
@@ -7982,13 +8598,13 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7982
8598
  });
7983
8599
  let logGroup;
7984
8600
  if (props.log.retention && props.log.retention.value > 0n) {
7985
- logGroup = new aws28.cloudwatch.LogGroup(group, "log", {
8601
+ logGroup = new aws29.cloudwatch.LogGroup(group, "log", {
7986
8602
  name: `/aws/ecs/${name}`,
7987
8603
  // name: `/aws/lambda/${name}`,
7988
- retentionInDays: toDays11(props.log.retention)
8604
+ retentionInDays: toDays12(props.log.retention)
7989
8605
  });
7990
8606
  if (ctx.shared.has("on-error-log", "subscriber-arn")) {
7991
- new aws28.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
8607
+ new aws29.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
7992
8608
  name: "error-log-subscription",
7993
8609
  destinationArn: ctx.shared.get("on-error-log", "subscriber-arn"),
7994
8610
  logGroupName: logGroup.name,
@@ -8003,7 +8619,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8003
8619
  };
8004
8620
  const variables = {};
8005
8621
  const variableDeps = /* @__PURE__ */ new Set();
8006
- const task2 = new aws28.ecs.TaskDefinition(
8622
+ const task2 = new aws29.ecs.TaskDefinition(
8007
8623
  group,
8008
8624
  "task",
8009
8625
  {
@@ -8019,9 +8635,9 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8019
8635
  operatingSystemFamily: "LINUX"
8020
8636
  },
8021
8637
  trackLatest: true,
8022
- containerDefinitions: new Output8(variableDeps, async (resolve2) => {
8023
- const data = await resolveInputs5(variables);
8024
- const { s3Bucket, s3Key } = await resolveInputs5({
8638
+ containerDefinitions: new Output9(variableDeps, async (resolve2) => {
8639
+ const data = await resolveInputs6(variables);
8640
+ const { s3Bucket, s3Key } = await resolveInputs6({
8025
8641
  s3Bucket: code.bucket,
8026
8642
  s3Key: code.key
8027
8643
  });
@@ -8076,12 +8692,12 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8076
8692
  healthCheck: props.healthCheck ? {
8077
8693
  command: [
8078
8694
  "CMD-SHELL",
8079
- `curl -f http://${join19("localhost", props.healthCheck.path)} || exit 1`
8695
+ `curl -f http://${join21("localhost", props.healthCheck.path)} || exit 1`
8080
8696
  ],
8081
- interval: toSeconds10(props.healthCheck.interval),
8697
+ interval: toSeconds9(props.healthCheck.interval),
8082
8698
  retries: props.healthCheck.retries,
8083
- startPeriod: toSeconds10(props.healthCheck.startPeriod),
8084
- timeout: toSeconds10(props.healthCheck.timeout)
8699
+ startPeriod: toSeconds9(props.healthCheck.startPeriod),
8700
+ timeout: toSeconds9(props.healthCheck.timeout)
8085
8701
  } : void 0
8086
8702
  }
8087
8703
  ])
@@ -8101,14 +8717,14 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8101
8717
  dependsOn: [code]
8102
8718
  }
8103
8719
  );
8104
- const securityGroup = new aws28.security.Group(group, "security-group", {
8720
+ const securityGroup = new aws29.security.Group(group, "security-group", {
8105
8721
  name,
8106
8722
  description: "Security group for the instance",
8107
8723
  vpcId: ctx.shared.get("vpc", "id"),
8108
8724
  revokeRulesOnDelete: true,
8109
8725
  tags
8110
8726
  });
8111
- new aws28.vpc.SecurityGroupEgressRule(group, "egress-rule", {
8727
+ new aws29.vpc.SecurityGroupEgressRule(group, "egress-rule", {
8112
8728
  securityGroupId: securityGroup.id,
8113
8729
  description: `Allow all outbound traffic from the ${name} instance`,
8114
8730
  ipProtocol: "-1",
@@ -8117,7 +8733,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8117
8733
  });
8118
8734
  const clusterName = ctx.shared.get("instance", "cluster-name");
8119
8735
  const clusterArn = ctx.shared.get("instance", "cluster-arn");
8120
- const service = new aws28.ecs.Service(
8736
+ const service = new aws29.ecs.Service(
8121
8737
  group,
8122
8738
  "service",
8123
8739
  {
@@ -8153,7 +8769,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8153
8769
  replaceOnChanges: ["cluster"]
8154
8770
  }
8155
8771
  );
8156
- new aws28.appautoscaling.Target(
8772
+ new aws29.appautoscaling.Target(
8157
8773
  group,
8158
8774
  "autoscaling-target",
8159
8775
  {
@@ -8172,7 +8788,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8172
8788
  );
8173
8789
  ctx.onEnv((name2, value) => {
8174
8790
  variables[name2] = value;
8175
- for (const dep of findInputDeps5([value])) {
8791
+ for (const dep of findInputDeps6([value])) {
8176
8792
  variableDeps.add(dep);
8177
8793
  }
8178
8794
  });
@@ -8181,7 +8797,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8181
8797
  variables.AWS_ACCOUNT_ID = ctx.accountId;
8182
8798
  variables.STACK = ctx.stackConfig.name;
8183
8799
  variables.CODE_HASH = code.sourceHash;
8184
- variables.INSTANCE_CONFIG_HASH = createHash6("sha1").update(stringify2(props)).digest("hex");
8800
+ variables.INSTANCE_CONFIG_HASH = createHash7("sha1").update(stringify3(props)).digest("hex");
8185
8801
  if (props.environment) {
8186
8802
  for (const [key, value] of Object.entries(props.environment)) {
8187
8803
  variables[key] = value;
@@ -8197,7 +8813,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8197
8813
  };
8198
8814
 
8199
8815
  // src/feature/instance/index.ts
8200
- var typeGenCode11 = `
8816
+ var typeGenCode12 = `
8201
8817
  import { SendMessageOptions } from '@awsless/sqs'
8202
8818
  import type { Mock } from 'vitest'
8203
8819
 
@@ -8236,15 +8852,15 @@ var instanceFeature = defineFeature({
8236
8852
  mocks.addType(stack.name, mock);
8237
8853
  mockResponses.addType(stack.name, mockResponse);
8238
8854
  }
8239
- gen.addCode(typeGenCode11);
8855
+ gen.addCode(typeGenCode12);
8240
8856
  gen.addInterface("InstanceResources", resources2);
8241
8857
  gen.addInterface("InstanceMock", mocks);
8242
8858
  gen.addInterface("InstanceMockResponse", mockResponses);
8243
8859
  await ctx.write("instance.d.ts", gen, true);
8244
8860
  },
8245
8861
  onBefore(ctx) {
8246
- const group = new Group28(ctx.base, "instance", "asset");
8247
- const bucket = new aws29.s3.Bucket(group, "bucket", {
8862
+ const group = new Group29(ctx.base, "instance", "asset");
8863
+ const bucket = new aws30.s3.Bucket(group, "bucket", {
8248
8864
  bucket: formatGlobalResourceName({
8249
8865
  appName: ctx.app.name,
8250
8866
  resourceType: "instance",
@@ -8262,8 +8878,8 @@ var instanceFeature = defineFeature({
8262
8878
  if (found.length === 0) {
8263
8879
  return;
8264
8880
  }
8265
- const group = new Group28(ctx.base, "instance", "cluster");
8266
- const cluster = new aws29.ecs.Cluster(
8881
+ const group = new Group29(ctx.base, "instance", "cluster");
8882
+ const cluster = new aws30.ecs.Cluster(
8267
8883
  group,
8268
8884
  "cluster",
8269
8885
  {
@@ -8278,14 +8894,14 @@ var instanceFeature = defineFeature({
8278
8894
  },
8279
8895
  onStack(ctx) {
8280
8896
  for (const [id, props] of Object.entries(ctx.stackConfig.instances ?? {})) {
8281
- const group = new Group28(ctx.stack, "instance", id);
8897
+ const group = new Group29(ctx.stack, "instance", id);
8282
8898
  const task2 = createFargateTask(group, ctx, "instance", id, props);
8283
- const queue2 = new aws29.sqs.Queue(group, "queue", {
8899
+ const queue2 = new aws30.sqs.Queue(group, "queue", {
8284
8900
  name: task2.name,
8285
- visibilityTimeoutSeconds: toSeconds11(seconds10(30)),
8286
- messageRetentionSeconds: toSeconds11(days10(4)),
8901
+ visibilityTimeoutSeconds: toSeconds10(seconds11(30)),
8902
+ messageRetentionSeconds: toSeconds10(days11(4)),
8287
8903
  maxMessageSize: toBytes2(kibibytes2(256)),
8288
- receiveWaitTimeSeconds: toSeconds11(seconds10(20))
8904
+ receiveWaitTimeSeconds: toSeconds10(seconds11(20))
8289
8905
  });
8290
8906
  task2.addPermission({
8291
8907
  actions: [
@@ -8307,11 +8923,11 @@ var instanceFeature = defineFeature({
8307
8923
  });
8308
8924
 
8309
8925
  // src/feature/metric/index.ts
8310
- import { Group as Group29 } from "@terraforge/core";
8311
- import { aws as aws30 } from "@terraforge/aws";
8926
+ import { Group as Group30 } from "@terraforge/core";
8927
+ import { aws as aws31 } from "@terraforge/aws";
8312
8928
  import { kebabCase as kebabCase8, constantCase as constantCase15 } from "change-case";
8313
- import { toSeconds as toSeconds12 } from "@awsless/duration";
8314
- var typeGenCode12 = `
8929
+ import { toSeconds as toSeconds11 } from "@awsless/duration";
8930
+ var typeGenCode13 = `
8315
8931
  import { type PutDataProps, putData, batchPutData } from '@awsless/cloudwatch'
8316
8932
  import { type Duration } from '@awsless/duration'
8317
8933
  import { type Size } from '@awsless/size'
@@ -8359,7 +8975,7 @@ var metricFeature = defineFeature({
8359
8975
  resources2.addType(stack.name, stackResources);
8360
8976
  }
8361
8977
  resources2.addType("batch", "Batch");
8362
- gen.addCode(typeGenCode12);
8978
+ gen.addCode(typeGenCode13);
8363
8979
  gen.addInterface("MetricResources", resources2);
8364
8980
  await ctx.write("metric.d.ts", gen, true);
8365
8981
  },
@@ -8376,16 +8992,16 @@ var metricFeature = defineFeature({
8376
8992
  }
8377
8993
  });
8378
8994
  for (const [id, props] of Object.entries(ctx.stackConfig.metrics ?? {})) {
8379
- const group = new Group29(ctx.stack, "metric", id);
8995
+ const group = new Group30(ctx.stack, "metric", id);
8380
8996
  ctx.addEnv(`METRIC_${constantCase15(id)}`, props.type);
8381
8997
  for (const alarmId in props.alarms ?? []) {
8382
- const alarmGroup = new Group29(group, "alarm", alarmId);
8998
+ const alarmGroup = new Group30(group, "alarm", alarmId);
8383
8999
  const alarmName = kebabCase8(`${id}-${alarmId}`);
8384
9000
  const alarmProps = props.alarms[alarmId];
8385
9001
  let alarmAction;
8386
9002
  let alarmLambda;
8387
9003
  if (Array.isArray(alarmProps.trigger)) {
8388
- const topic = new aws30.sns.Topic(alarmGroup, "alarm-trigger", {
9004
+ const topic = new aws31.sns.Topic(alarmGroup, "alarm-trigger", {
8389
9005
  name: formatLocalResourceName({
8390
9006
  appName: ctx.app.name,
8391
9007
  stackName: ctx.stack.name,
@@ -8395,7 +9011,7 @@ var metricFeature = defineFeature({
8395
9011
  });
8396
9012
  alarmAction = topic.arn;
8397
9013
  for (const email of alarmProps.trigger) {
8398
- new aws30.sns.TopicSubscription(alarmGroup, email, {
9014
+ new aws31.sns.TopicSubscription(alarmGroup, email, {
8399
9015
  topicArn: topic.arn,
8400
9016
  protocol: "email",
8401
9017
  endpoint: email
@@ -8406,7 +9022,7 @@ var metricFeature = defineFeature({
8406
9022
  alarmLambda = lambda;
8407
9023
  alarmAction = lambda.arn;
8408
9024
  }
8409
- const alarm = new aws30.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
9025
+ const alarm = new aws31.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
8410
9026
  namespace,
8411
9027
  metricName: kebabCase8(id),
8412
9028
  alarmName: formatLocalResourceName({
@@ -8418,13 +9034,13 @@ var metricFeature = defineFeature({
8418
9034
  alarmDescription: alarmProps.description,
8419
9035
  statistic: alarmProps.where.stat,
8420
9036
  threshold: alarmProps.where.value,
8421
- period: toSeconds12(alarmProps.period),
9037
+ period: toSeconds11(alarmProps.period),
8422
9038
  evaluationPeriods: alarmProps.minDataPoints,
8423
9039
  comparisonOperator: alarmProps.where.op,
8424
9040
  alarmActions: [alarmAction]
8425
9041
  });
8426
9042
  if (alarmLambda) {
8427
- new aws30.lambda.Permission(alarmGroup, "permission", {
9043
+ new aws31.lambda.Permission(alarmGroup, "permission", {
8428
9044
  action: "lambda:InvokeFunction",
8429
9045
  principal: "lambda.alarms.cloudwatch.amazonaws.com",
8430
9046
  functionName: alarmLambda.functionName,
@@ -8437,9 +9053,9 @@ var metricFeature = defineFeature({
8437
9053
  });
8438
9054
 
8439
9055
  // src/feature/router/index.ts
8440
- import { days as days11, seconds as seconds11, toSeconds as toSeconds13, years } from "@awsless/duration";
8441
- import { Future, Group as Group30 } from "@terraforge/core";
8442
- import { aws as aws31 } from "@terraforge/aws";
9056
+ import { days as days12, seconds as seconds12, toSeconds as toSeconds12, years } from "@awsless/duration";
9057
+ import { Future, Group as Group31 } from "@terraforge/core";
9058
+ import { aws as aws32 } from "@terraforge/aws";
8443
9059
  import { camelCase as camelCase9, constantCase as constantCase16 } from "change-case";
8444
9060
 
8445
9061
  // src/feature/router/router-code.ts
@@ -8688,18 +9304,18 @@ async function handler(event) {
8688
9304
  `;
8689
9305
 
8690
9306
  // src/feature/router/index.ts
8691
- import { createHash as createHash7 } from "crypto";
9307
+ import { createHash as createHash8 } from "crypto";
8692
9308
  var routerFeature = defineFeature({
8693
9309
  name: "router",
8694
9310
  onApp(ctx) {
8695
9311
  for (const [id, props] of Object.entries(ctx.appConfig.defaults.router ?? {})) {
8696
- const group = new Group30(ctx.base, "router", id);
9312
+ const group = new Group31(ctx.base, "router", id);
8697
9313
  const name = formatGlobalResourceName({
8698
9314
  appName: ctx.app.name,
8699
9315
  resourceType: "router",
8700
9316
  resourceName: id
8701
9317
  });
8702
- const routeStore = new aws31.cloudfront.KeyValueStore(group, "routes", {
9318
+ const routeStore = new aws32.cloudfront.KeyValueStore(group, "routes", {
8703
9319
  name,
8704
9320
  comment: "Store for routes"
8705
9321
  });
@@ -8729,11 +9345,11 @@ var routerFeature = defineFeature({
8729
9345
  );
8730
9346
  importedRoutes.push(importKeys);
8731
9347
  });
8732
- const cache = new aws31.cloudfront.CachePolicy(group, "cache", {
9348
+ const cache = new aws32.cloudfront.CachePolicy(group, "cache", {
8733
9349
  name,
8734
- minTtl: toSeconds13(seconds11(0)),
8735
- maxTtl: toSeconds13(days11(365)),
8736
- defaultTtl: toSeconds13(days11(0)),
9350
+ minTtl: toSeconds12(seconds12(0)),
9351
+ maxTtl: toSeconds12(days12(365)),
9352
+ defaultTtl: toSeconds12(days12(0)),
8737
9353
  parametersInCacheKeyAndForwardedToOrigin: {
8738
9354
  enableAcceptEncodingBrotli: true,
8739
9355
  enableAcceptEncodingGzip: true,
@@ -8761,7 +9377,7 @@ var routerFeature = defineFeature({
8761
9377
  }
8762
9378
  }
8763
9379
  });
8764
- const originRequest = new aws31.cloudfront.OriginRequestPolicy(group, "request", {
9380
+ const originRequest = new aws32.cloudfront.OriginRequestPolicy(group, "request", {
8765
9381
  name,
8766
9382
  headersConfig: {
8767
9383
  headerBehavior: camelCase9("all-except"),
@@ -8779,11 +9395,11 @@ var routerFeature = defineFeature({
8779
9395
  queryStringBehavior: "all"
8780
9396
  }
8781
9397
  });
8782
- const responseHeaders = new aws31.cloudfront.ResponseHeadersPolicy(group, "response", {
9398
+ const responseHeaders = new aws32.cloudfront.ResponseHeadersPolicy(group, "response", {
8783
9399
  name,
8784
9400
  corsConfig: {
8785
9401
  originOverride: props.cors?.override ?? true,
8786
- accessControlMaxAgeSec: toSeconds13(props.cors?.maxAge ?? years(1)),
9402
+ accessControlMaxAgeSec: toSeconds12(props.cors?.maxAge ?? years(1)),
8787
9403
  accessControlAllowHeaders: { items: props.cors?.headers ?? ["*"] },
8788
9404
  accessControlAllowMethods: { items: props.cors?.methods ?? ["ALL"] },
8789
9405
  accessControlAllowOrigins: { items: props.cors?.origins ?? ["*"] },
@@ -8808,7 +9424,7 @@ var routerFeature = defineFeature({
8808
9424
  strictTransportSecurity: {
8809
9425
  override: true,
8810
9426
  preload: true,
8811
- accessControlMaxAgeSec: toSeconds13(years(1)),
9427
+ accessControlMaxAgeSec: toSeconds12(years(1)),
8812
9428
  includeSubdomains: true
8813
9429
  },
8814
9430
  xssProtection: {
@@ -8818,7 +9434,7 @@ var routerFeature = defineFeature({
8818
9434
  }
8819
9435
  }
8820
9436
  });
8821
- const viewerRequest = new aws31.cloudfront.Function(group, "viewer-request", {
9437
+ const viewerRequest = new aws32.cloudfront.Function(group, "viewer-request", {
8822
9438
  name,
8823
9439
  runtime: `cloudfront-js-2.0`,
8824
9440
  comment: `Viewer Request - ${name}`,
@@ -8840,7 +9456,7 @@ var routerFeature = defineFeature({
8840
9456
  rateBasedStatement: {
8841
9457
  limit: wafSettingsConfig.rateLimiter.limit,
8842
9458
  aggregateKeyType: "IP",
8843
- evaluationWindowSec: toSeconds13(wafSettingsConfig.rateLimiter.window)
9459
+ evaluationWindowSec: toSeconds12(wafSettingsConfig.rateLimiter.window)
8844
9460
  }
8845
9461
  },
8846
9462
  action: {
@@ -8920,7 +9536,7 @@ var routerFeature = defineFeature({
8920
9536
  }
8921
9537
  let waf;
8922
9538
  if (wafRules.length && wafSettingsConfig) {
8923
- waf = new aws31.wafv2.WebAcl(group, "waf", {
9539
+ waf = new aws32.wafv2.WebAcl(group, "waf", {
8924
9540
  name: `${name}-wafv2`,
8925
9541
  scope: "CLOUDFRONT",
8926
9542
  defaultAction: {
@@ -8930,12 +9546,12 @@ var routerFeature = defineFeature({
8930
9546
  rule: wafRules,
8931
9547
  captchaConfig: {
8932
9548
  immunityTimeProperty: {
8933
- immunityTime: toSeconds13(wafSettingsConfig.captchaImmunityTime)
9549
+ immunityTime: toSeconds12(wafSettingsConfig.captchaImmunityTime)
8934
9550
  }
8935
9551
  },
8936
9552
  challengeConfig: {
8937
9553
  immunityTimeProperty: {
8938
- immunityTime: toSeconds13(wafSettingsConfig.challengeImmunityTime)
9554
+ immunityTime: toSeconds12(wafSettingsConfig.challengeImmunityTime)
8939
9555
  }
8940
9556
  },
8941
9557
  visibilityConfig: {
@@ -8945,7 +9561,7 @@ var routerFeature = defineFeature({
8945
9561
  }
8946
9562
  });
8947
9563
  }
8948
- const distribution = new aws31.cloudfront.MultitenantDistribution(group, "distribution", {
9564
+ const distribution = new aws32.cloudfront.MultitenantDistribution(group, "distribution", {
8949
9565
  tags: {
8950
9566
  name
8951
9567
  },
@@ -8991,7 +9607,7 @@ var routerFeature = defineFeature({
8991
9607
  }
8992
9608
  return {
8993
9609
  errorCode: Number(errorCode),
8994
- errorCachingMinTtl: item.minTTL ? toSeconds13(item.minTTL) : void 0,
9610
+ errorCachingMinTtl: item.minTTL ? toSeconds12(item.minTTL) : void 0,
8995
9611
  responseCode: item.statusCode?.toString() ?? errorCode,
8996
9612
  responsePagePath: item.path
8997
9613
  };
@@ -9042,7 +9658,7 @@ var routerFeature = defineFeature({
9042
9658
  version: new Future((resolve2) => {
9043
9659
  $combine(...versions).then((versions2) => {
9044
9660
  const combined = versions2.filter((v) => !!v).sort().join(",");
9045
- const version = createHash7("sha1").update(combined).digest("hex");
9661
+ const version = createHash8("sha1").update(combined).digest("hex");
9046
9662
  resolve2(version);
9047
9663
  });
9048
9664
  })
@@ -9057,10 +9673,10 @@ var routerFeature = defineFeature({
9057
9673
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
9058
9674
  const certificateArn = ctx.shared.entry("domain", `global-certificate-arn`, props.domain);
9059
9675
  const zoneId = ctx.shared.entry("domain", "zone-id", props.domain);
9060
- const connectionGroup = new aws31.cloudfront.ConnectionGroup(group, "connection-group", {
9676
+ const connectionGroup = new aws32.cloudfront.ConnectionGroup(group, "connection-group", {
9061
9677
  name
9062
9678
  });
9063
- new aws31.cloudfront.DistributionTenant(group, `tenant`, {
9679
+ new aws32.cloudfront.DistributionTenant(group, `tenant`, {
9064
9680
  name,
9065
9681
  enabled: true,
9066
9682
  distributionId: distribution.id,
@@ -9068,7 +9684,7 @@ var routerFeature = defineFeature({
9068
9684
  domain: [{ domain: domainName }],
9069
9685
  customizations: [{ certificate: [{ arn: certificateArn }] }]
9070
9686
  });
9071
- new aws31.route53.Record(group, `record`, {
9687
+ new aws32.route53.Record(group, `record`, {
9072
9688
  zoneId,
9073
9689
  type: "A",
9074
9690
  name: domainName,
@@ -10191,21 +10807,21 @@ import wildstring4 from "wildstring";
10191
10807
 
10192
10808
  // src/cli/ui/complex/run-tests.ts
10193
10809
  import { log as log18 } from "@awsless/clui";
10194
- import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
10195
- import { join as join21 } from "path";
10810
+ import { mkdir as mkdir4, readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
10811
+ import { join as join23 } from "path";
10196
10812
  import wildstring3 from "wildstring";
10197
- import { parse as parse4, stringify as stringify3 } from "@awsless/json";
10813
+ import { parse as parse4, stringify as stringify4 } from "@awsless/json";
10198
10814
  import { generateFolderHash, loadWorkspace as loadWorkspace2 } from "@awsless/ts-file-cache";
10199
10815
 
10200
10816
  // src/test/start.ts
10201
- import { dirname as dirname9, join as join20 } from "path";
10202
- import { fileURLToPath as fileURLToPath4 } from "url";
10817
+ import { dirname as dirname11, join as join22 } from "path";
10818
+ import { fileURLToPath as fileURLToPath6 } from "url";
10203
10819
  import { configDefaults } from "vitest/config";
10204
10820
  import { startVitest } from "vitest/node";
10205
10821
  var NullReporter = class {
10206
10822
  };
10207
10823
  var startTest = async (props) => {
10208
- const __dirname5 = dirname9(fileURLToPath4(import.meta.url));
10824
+ const __dirname7 = dirname11(fileURLToPath6(import.meta.url));
10209
10825
  const startTime = process.hrtime.bigint();
10210
10826
  process.noDeprecation = true;
10211
10827
  const vitest = await startVitest(
@@ -10236,7 +10852,7 @@ var startTest = async (props) => {
10236
10852
  // },
10237
10853
  setupFiles: [
10238
10854
  //
10239
- join20(__dirname5, "test-global-setup.js")
10855
+ join22(__dirname7, "test-global-setup.js")
10240
10856
  ]
10241
10857
  // globalSetup: [
10242
10858
  // //
@@ -10430,12 +11046,12 @@ var logTestErrors = (event) => {
10430
11046
  };
10431
11047
  var runTest = async (stack, dir, filters, workspace, opts) => {
10432
11048
  await mkdir4(directories.test, { recursive: true });
10433
- const file = join21(directories.test, `${stack}.json`);
11049
+ const file = join23(directories.test, `${stack}.json`);
10434
11050
  const fingerprint = await generateFolderHash(workspace, dir);
10435
11051
  if (!process.env.NO_CACHE) {
10436
11052
  const exists = await fileExist(file);
10437
11053
  if (exists) {
10438
- const raw = await readFile6(file, { encoding: "utf8" });
11054
+ const raw = await readFile7(file, { encoding: "utf8" });
10439
11055
  const data = parse4(raw);
10440
11056
  if (data.fingerprint === fingerprint) {
10441
11057
  log18.step(
@@ -10480,7 +11096,7 @@ var runTest = async (stack, dir, filters, workspace, opts) => {
10480
11096
  logTestErrors(result);
10481
11097
  await writeFile4(
10482
11098
  file,
10483
- stringify3({
11099
+ stringify4({
10484
11100
  ...result,
10485
11101
  fingerprint
10486
11102
  })
@@ -11115,7 +11731,7 @@ import { log as log25 } from "@awsless/clui";
11115
11731
 
11116
11732
  // src/type-gen/generate.ts
11117
11733
  import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
11118
- import { dirname as dirname10, join as join22, relative as relative9 } from "path";
11734
+ import { dirname as dirname12, join as join24, relative as relative9 } from "path";
11119
11735
  var generateTypes = async (props) => {
11120
11736
  const files = [];
11121
11737
  await Promise.all(
@@ -11124,12 +11740,12 @@ var generateTypes = async (props) => {
11124
11740
  ...props,
11125
11741
  async write(file, data, include = false) {
11126
11742
  const code = data?.toString("utf8");
11127
- const path = join22(directories.types, file);
11743
+ const path = join24(directories.types, file);
11128
11744
  if (code) {
11129
11745
  if (include) {
11130
11746
  files.push(relative9(directories.root, path));
11131
11747
  }
11132
- await mkdir5(dirname10(path), { recursive: true });
11748
+ await mkdir5(dirname12(path), { recursive: true });
11133
11749
  await writeFile5(path, code);
11134
11750
  }
11135
11751
  }
@@ -11138,7 +11754,7 @@ var generateTypes = async (props) => {
11138
11754
  );
11139
11755
  if (files.length) {
11140
11756
  const code = files.map((file) => `/// <reference path='${file}' />`).join("\n");
11141
- await writeFile5(join22(directories.root, `awsless.d.ts`), code);
11757
+ await writeFile5(join24(directories.root, `awsless.d.ts`), code);
11142
11758
  }
11143
11759
  };
11144
11760
 
@@ -11564,7 +12180,7 @@ var domain = (program2) => {
11564
12180
  // src/cli/command/logs.ts
11565
12181
  import { CloudWatchLogsClient, StartLiveTailCommand } from "@aws-sdk/client-cloudwatch-logs";
11566
12182
  import { log as log30 } from "@awsless/clui";
11567
- import { aws as aws32 } from "@terraforge/aws";
12183
+ import { aws as aws33 } from "@terraforge/aws";
11568
12184
  import chalk6 from "chalk";
11569
12185
  import chunk2 from "chunk";
11570
12186
  import { formatDate } from "date-fns";
@@ -11587,7 +12203,7 @@ var logs = (program2) => {
11587
12203
  for (const stack of app.stacks) {
11588
12204
  if (filters.find((f) => wildstring7.match(f, stack.name))) {
11589
12205
  for (const resource of stack.resources) {
11590
- if (resource instanceof aws32.cloudwatch.LogGroup) {
12206
+ if (resource instanceof aws33.cloudwatch.LogGroup) {
11591
12207
  logGroupArns.push(await resource.arn);
11592
12208
  }
11593
12209
  }