@awsless/cli 0.0.30 → 0.0.32

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
+ ]
5717
+ });
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
5101
5753
  });
5102
- ctx.bind(`PUBSUB_${constantCase5(id)}_AUTHORIZER`, name);
5103
- const endpoint = aws11.iot.getEndpoint(group, "endpoint", {
5104
- endpointType: "iot:Data-ATS"
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]
5105
5763
  });
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);
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,34 +5897,33 @@ 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 onFailure = ctx.shared.get("on-failure", "queue-arn");
5240
- const queue2 = new aws12.sqs.Queue(group, "queue", {
5907
+ const queue2 = new aws13.sqs.Queue(group, "queue", {
5241
5908
  name: `${baseName}.fifo`,
5242
- visibilityTimeoutSeconds: toSeconds6(props.visibilityTimeout),
5243
- receiveWaitTimeSeconds: toSeconds6(props.receiveMessageWaitTime ?? seconds7(0)),
5244
- messageRetentionSeconds: toSeconds6(props.retentionPeriod),
5909
+ visibilityTimeoutSeconds: toSeconds5(props.visibilityTimeout),
5910
+ receiveWaitTimeSeconds: toSeconds5(props.receiveMessageWaitTime ?? seconds8(0)),
5911
+ messageRetentionSeconds: toSeconds5(props.retentionPeriod),
5245
5912
  maxMessageSize: toBytes(props.maxMessageSize),
5246
5913
  fifoQueue: true,
5247
5914
  deduplicationScope: "messageGroup",
5248
- fifoThroughputLimit: "perMessageGroupId",
5249
- redrivePolicy: onFailure.pipe(
5250
- (arn) => JSON.stringify({
5251
- deadLetterTargetArn: arn,
5252
- maxReceiveCount: props.retryAttempts + 1
5253
- })
5254
- )
5915
+ fifoThroughputLimit: "perMessageGroupId"
5916
+ // redrivePolicy: onFailure.pipe(arn =>
5917
+ // JSON.stringify({
5918
+ // deadLetterTargetArn: arn,
5919
+ // maxReceiveCount: props.retryAttempts + 1,
5920
+ // })
5921
+ // ),
5255
5922
  });
5256
5923
  if (local.consumer) {
5257
5924
  const lambdaConsumer = createLambdaFunction(group, ctx, `queue`, id, local.consumer);
5258
5925
  lambdaConsumer.setEnvironment("THROW_EXPECTED_ERRORS", "1");
5259
- new aws12.lambda.EventSourceMapping(
5926
+ new aws13.lambda.EventSourceMapping(
5260
5927
  group,
5261
5928
  "event",
5262
5929
  {
@@ -5290,24 +5957,24 @@ var queueFeature = defineFeature({
5290
5957
  });
5291
5958
 
5292
5959
  // src/feature/rest/index.ts
5293
- import { Group as Group12 } from "@terraforge/core";
5294
- import { aws as aws13 } from "@terraforge/aws";
5960
+ import { Group as Group13 } from "@terraforge/core";
5961
+ import { aws as aws14 } from "@terraforge/aws";
5295
5962
  import { constantCase as constantCase7 } from "change-case";
5296
5963
  var restFeature = defineFeature({
5297
5964
  name: "rest",
5298
5965
  onApp(ctx) {
5299
5966
  for (const [id, props] of Object.entries(ctx.appConfig.defaults?.rest ?? {})) {
5300
- const group = new Group12(ctx.base, "rest", id);
5967
+ const group = new Group13(ctx.base, "rest", id);
5301
5968
  const name = formatGlobalResourceName({
5302
5969
  appName: ctx.app.name,
5303
5970
  resourceType: "rest",
5304
5971
  resourceName: id
5305
5972
  });
5306
- const api = new aws13.apigatewayv2.Api(group, "api", {
5973
+ const api = new aws14.apigatewayv2.Api(group, "api", {
5307
5974
  name,
5308
5975
  protocolType: "HTTP"
5309
5976
  });
5310
- const stage = new aws13.apigatewayv2.Stage(group, "stage", {
5977
+ const stage = new aws14.apigatewayv2.Stage(group, "stage", {
5311
5978
  name: "v1",
5312
5979
  apiId: api.id,
5313
5980
  autoDeploy: true
@@ -5317,7 +5984,7 @@ var restFeature = defineFeature({
5317
5984
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
5318
5985
  const zoneId = ctx.shared.entry("domain", `zone-id`, props.domain);
5319
5986
  const certificateArn = ctx.shared.entry("domain", `certificate-arn`, props.domain);
5320
- const domain2 = new aws13.apigatewayv2.DomainName(group, "domain", {
5987
+ const domain2 = new aws14.apigatewayv2.DomainName(group, "domain", {
5321
5988
  domainName,
5322
5989
  domainNameConfiguration: {
5323
5990
  certificateArn,
@@ -5325,12 +5992,12 @@ var restFeature = defineFeature({
5325
5992
  securityPolicy: "TLS_1_2"
5326
5993
  }
5327
5994
  });
5328
- const mapping = new aws13.apigatewayv2.ApiMapping(group, "mapping", {
5995
+ const mapping = new aws14.apigatewayv2.ApiMapping(group, "mapping", {
5329
5996
  apiId: api.id,
5330
5997
  domainName: domain2.domainName,
5331
5998
  stage: stage.name
5332
5999
  });
5333
- new aws13.route53.Record(
6000
+ new aws14.route53.Record(
5334
6001
  group,
5335
6002
  "record",
5336
6003
  {
@@ -5358,21 +6025,21 @@ var restFeature = defineFeature({
5358
6025
  },
5359
6026
  onStack(ctx) {
5360
6027
  for (const [id, routes] of Object.entries(ctx.stackConfig.rest ?? {})) {
5361
- const restGroup = new Group12(ctx.stack, "rest", id);
6028
+ const restGroup = new Group13(ctx.stack, "rest", id);
5362
6029
  for (const [routeKey, props] of Object.entries(routes)) {
5363
- const group = new Group12(restGroup, "route", routeKey);
6030
+ const group = new Group13(restGroup, "route", routeKey);
5364
6031
  const apiId = ctx.shared.entry("rest", "id", id);
5365
6032
  const routeId = shortId(routeKey);
5366
6033
  const { lambda } = createLambdaFunction(group, ctx, "rest", `${id}-${routeId}`, {
5367
6034
  ...props,
5368
6035
  description: `${id} ${routeKey}`
5369
6036
  });
5370
- const permission = new aws13.lambda.Permission(group, "permission", {
6037
+ const permission = new aws14.lambda.Permission(group, "permission", {
5371
6038
  action: "lambda:InvokeFunction",
5372
6039
  principal: "apigateway.amazonaws.com",
5373
6040
  functionName: lambda.functionName
5374
6041
  });
5375
- const integration = new aws13.apigatewayv2.Integration(group, "integration", {
6042
+ const integration = new aws14.apigatewayv2.Integration(group, "integration", {
5376
6043
  apiId,
5377
6044
  description: `${id} ${routeKey}`,
5378
6045
  integrationType: "AWS_PROXY",
@@ -5382,7 +6049,7 @@ var restFeature = defineFeature({
5382
6049
  return `arn:aws:apigateway:${ctx.appConfig.region}:lambda:path/2015-03-31/functions/${arn}/invocations`;
5383
6050
  })
5384
6051
  });
5385
- new aws13.apigatewayv2.Route(
6052
+ new aws14.apigatewayv2.Route(
5386
6053
  group,
5387
6054
  "route",
5388
6055
  {
@@ -5401,13 +6068,13 @@ var restFeature = defineFeature({
5401
6068
 
5402
6069
  // src/feature/rpc/index.ts
5403
6070
  import { camelCase as camelCase6, constantCase as constantCase8, kebabCase as kebabCase6 } from "change-case";
5404
- import { Group as Group13 } from "@terraforge/core";
5405
- import { aws as aws14 } from "@terraforge/aws";
5406
- import { mebibytes as mebibytes5 } from "@awsless/size";
5407
- import { dirname as dirname5, join as join12, relative as relative6 } from "path";
5408
- import { fileURLToPath } from "url";
5409
- import { toSeconds as toSeconds7 } from "@awsless/duration";
5410
- 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));
5411
6078
  var rpcFeature = defineFeature({
5412
6079
  name: "rpc",
5413
6080
  async onTypeGen(ctx) {
@@ -5451,8 +6118,8 @@ var rpcFeature = defineFeature({
5451
6118
  } else {
5452
6119
  list3.add(name);
5453
6120
  }
5454
- const timeout = toSeconds7(props.function.timeout ?? ctx.appConfig.defaults.function.timeout);
5455
- 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;
5456
6123
  if (timeout > maxTimeout) {
5457
6124
  throw new FileError(
5458
6125
  stack.file,
@@ -5465,19 +6132,19 @@ var rpcFeature = defineFeature({
5465
6132
  },
5466
6133
  onApp(ctx) {
5467
6134
  for (const [id, props] of Object.entries(ctx.appConfig.defaults.rpc ?? {})) {
5468
- const group = new Group13(ctx.base, "rpc", id);
6135
+ const group = new Group14(ctx.base, "rpc", id);
5469
6136
  const result = createPrebuildLambdaFunction(group, ctx, "rpc", id, {
5470
- bundleFile: join12(__dirname2, "/prebuild/rpc/bundle.zip"),
5471
- bundleHash: join12(__dirname2, "/prebuild/rpc/HASH"),
5472
- memorySize: mebibytes5(256),
6137
+ bundleFile: join15(__dirname4, "/prebuild/rpc/bundle.zip"),
6138
+ bundleHash: join15(__dirname4, "/prebuild/rpc/HASH"),
6139
+ memorySize: mebibytes6(256),
5473
6140
  timeout: props.timeout,
5474
6141
  handler: "index.default",
5475
6142
  runtime: "nodejs24.x",
5476
6143
  warm: 3,
5477
6144
  log: props.log
5478
6145
  });
5479
- result.setEnvironment("TIMEOUT", toSeconds7(props.timeout).toString());
5480
- 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", {
5481
6148
  name: formatGlobalResourceName({
5482
6149
  appName: ctx.app.name,
5483
6150
  resourceType: "rpc-schema",
@@ -5499,7 +6166,7 @@ var rpcFeature = defineFeature({
5499
6166
  resources: [schemaTable.arn]
5500
6167
  });
5501
6168
  ctx.shared.add("rpc", `schema-table`, id, schemaTable);
5502
- const lockTable = new aws14.dynamodb.Table(group, "lock", {
6169
+ const lockTable = new aws15.dynamodb.Table(group, "lock", {
5503
6170
  name: formatGlobalResourceName({
5504
6171
  appName: ctx.app.name,
5505
6172
  resourceType: "rpc-lock",
@@ -5525,7 +6192,7 @@ var rpcFeature = defineFeature({
5525
6192
  resources: [lockTable.arn]
5526
6193
  });
5527
6194
  if (props.auth) {
5528
- const authGroup = new Group13(group, "auth", "authorizer");
6195
+ const authGroup = new Group14(group, "auth", "authorizer");
5529
6196
  const auth2 = createLambdaFunction(authGroup, ctx, "rpc", `${id}-auth`, props.auth);
5530
6197
  result.setEnvironment("AUTH", auth2.name);
5531
6198
  for (const [authId, userPoolId] of ctx.shared.list("auth", "user-pool-id")) {
@@ -5544,14 +6211,14 @@ var rpcFeature = defineFeature({
5544
6211
  imageUri: result.lambda.imageUri
5545
6212
  });
5546
6213
  }
5547
- const permission = new aws14.lambda.Permission(group, "permission", {
6214
+ const permission = new aws15.lambda.Permission(group, "permission", {
5548
6215
  principal: "cloudfront.amazonaws.com",
5549
6216
  action: "lambda:InvokeFunctionUrl",
5550
6217
  functionName: result.lambda.functionName,
5551
6218
  functionUrlAuthType: "AWS_IAM",
5552
6219
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
5553
6220
  });
5554
- const url = new aws14.lambda.FunctionUrl(
6221
+ const url = new aws15.lambda.FunctionUrl(
5555
6222
  group,
5556
6223
  "url",
5557
6224
  {
@@ -5586,15 +6253,15 @@ var rpcFeature = defineFeature({
5586
6253
  throw new FileError(ctx.stackConfig.file, `RPC definition is not defined on app level for "${id}"`);
5587
6254
  }
5588
6255
  const table = ctx.shared.entry("rpc", "schema-table", id);
5589
- const group = new Group13(ctx.stack, "rpc", id);
6256
+ const group = new Group14(ctx.stack, "rpc", id);
5590
6257
  for (const [name, props] of Object.entries(queries ?? {})) {
5591
- const queryGroup = new Group13(group, "query", name);
6258
+ const queryGroup = new Group14(group, "query", name);
5592
6259
  const entryId = kebabCase6(`${id}-${shortId(name)}`);
5593
6260
  createLambdaFunction(queryGroup, ctx, `rpc`, entryId, {
5594
6261
  ...props.function,
5595
6262
  description: `${id} ${name}`
5596
6263
  });
5597
- new aws14.dynamodb.TableItem(queryGroup, "query", {
6264
+ new aws15.dynamodb.TableItem(queryGroup, "query", {
5598
6265
  tableName: table.name,
5599
6266
  hashKey: table.hashKey,
5600
6267
  rangeKey: table.rangeKey,
@@ -5626,11 +6293,11 @@ var rpcFeature = defineFeature({
5626
6293
  });
5627
6294
 
5628
6295
  // src/feature/search/index.ts
5629
- import { Group as Group14 } from "@terraforge/core";
5630
- import { aws as aws15 } from "@terraforge/aws";
6296
+ import { Group as Group15 } from "@terraforge/core";
6297
+ import { aws as aws16 } from "@terraforge/aws";
5631
6298
  import { constantCase as constantCase9 } from "change-case";
5632
6299
  import { toGibibytes as toGibibytes2 } from "@awsless/size";
5633
- var typeGenCode5 = `
6300
+ var typeGenCode6 = `
5634
6301
  import { AnyStruct, Table } from '@awsless/open-search'
5635
6302
 
5636
6303
  type Search = {
@@ -5650,15 +6317,15 @@ var searchFeature = defineFeature({
5650
6317
  }
5651
6318
  resources2.addType(stack.name, list3);
5652
6319
  }
5653
- gen.addCode(typeGenCode5);
6320
+ gen.addCode(typeGenCode6);
5654
6321
  gen.addInterface("SearchResources", resources2);
5655
6322
  await ctx.write("search.d.ts", gen, true);
5656
6323
  },
5657
6324
  onStack(ctx) {
5658
6325
  for (const [id, props] of Object.entries(ctx.stackConfig.searchs ?? {})) {
5659
- const group = new Group14(ctx.stack, "search", id);
6326
+ const group = new Group15(ctx.stack, "search", id);
5660
6327
  const name = `${ctx.app.name}-${shortId([ctx.app.name, ctx.stack.name, "search", id].join("--"))}`;
5661
- const openSearch = new aws15.opensearch.Domain(
6328
+ const openSearch = new aws16.opensearch.Domain(
5662
6329
  group,
5663
6330
  "domain",
5664
6331
  {
@@ -5733,10 +6400,10 @@ var searchFeature = defineFeature({
5733
6400
  });
5734
6401
 
5735
6402
  // src/feature/site/index.ts
5736
- import { Group as Group15 } from "@terraforge/core";
5737
- import { aws as aws16 } from "@terraforge/aws";
6403
+ import { Group as Group16 } from "@terraforge/core";
6404
+ import { aws as aws17 } from "@terraforge/aws";
5738
6405
  import { glob as glob2 } from "glob";
5739
- import { dirname as dirname6, join as join13 } from "path";
6406
+ import { dirname as dirname8, join as join16 } from "path";
5740
6407
 
5741
6408
  // src/feature/site/util.ts
5742
6409
  import { contentType, lookup } from "mime-types";
@@ -5764,7 +6431,7 @@ var siteFeature = defineFeature({
5764
6431
  name: "site",
5765
6432
  onStack(ctx) {
5766
6433
  for (const [id, props] of Object.entries(ctx.stackConfig.sites ?? {})) {
5767
- const group = new Group15(ctx.stack, "site", id);
6434
+ const group = new Group16(ctx.stack, "site", id);
5768
6435
  const name = formatLocalResourceName({
5769
6436
  appName: ctx.app.name,
5770
6437
  stackName: ctx.stack.name,
@@ -5782,7 +6449,7 @@ var siteFeature = defineFeature({
5782
6449
  return build3(fingerprint, async (write) => {
5783
6450
  const credentialProvider = await getCredentials(ctx.appConfig.profile);
5784
6451
  const credentials = await credentialProvider();
5785
- const cwd = join13(directories.root, dirname6(ctx.stackConfig.file));
6452
+ const cwd = join16(directories.root, dirname8(ctx.stackConfig.file));
5786
6453
  const env = {
5787
6454
  ...process.env,
5788
6455
  // Pass the app config name
@@ -5829,14 +6496,14 @@ var siteFeature = defineFeature({
5829
6496
  ctx.onBind((name2, value) => {
5830
6497
  result.setEnvironment(name2, value);
5831
6498
  });
5832
- new aws16.lambda.Permission(group, "ssr-permission", {
6499
+ new aws17.lambda.Permission(group, "ssr-permission", {
5833
6500
  principal: "cloudfront.amazonaws.com",
5834
6501
  action: "lambda:InvokeFunctionUrl",
5835
6502
  functionName: result.lambda.functionName,
5836
6503
  functionUrlAuthType: "AWS_IAM",
5837
6504
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
5838
6505
  });
5839
- const functionUrl = new aws16.lambda.FunctionUrl(group, "url", {
6506
+ const functionUrl = new aws17.lambda.FunctionUrl(group, "url", {
5840
6507
  functionName: result.lambda.functionName,
5841
6508
  authorizationType: "AWS_IAM"
5842
6509
  });
@@ -5850,7 +6517,7 @@ var siteFeature = defineFeature({
5850
6517
  });
5851
6518
  }
5852
6519
  if (props.static) {
5853
- const bucket = new aws16.s3.Bucket(group, "bucket", {
6520
+ const bucket = new aws17.s3.Bucket(group, "bucket", {
5854
6521
  bucket: formatLocalResourceName({
5855
6522
  appName: ctx.app.name,
5856
6523
  stackName: ctx.stack.name,
@@ -5872,7 +6539,7 @@ var siteFeature = defineFeature({
5872
6539
  }
5873
6540
  ]
5874
6541
  });
5875
- new aws16.s3.BucketPolicy(
6542
+ new aws17.s3.BucketPolicy(
5876
6543
  group,
5877
6544
  `policy`,
5878
6545
  {
@@ -5927,20 +6594,20 @@ var siteFeature = defineFeature({
5927
6594
  });
5928
6595
  const staticRoutes = {};
5929
6596
  for (const file of files) {
5930
- const prefixedFile = join13("/", file);
5931
- const object = new aws16.s3.BucketObject(group, prefixedFile, {
6597
+ const prefixedFile = join16("/", file);
6598
+ const object = new aws17.s3.BucketObject(group, prefixedFile, {
5932
6599
  bucket: bucket.bucket,
5933
6600
  key: prefixedFile,
5934
6601
  cacheControl: getCacheControl(file),
5935
6602
  contentType: getContentType(file),
5936
- source: join13(props.static, file),
5937
- sourceHash: $hash(join13(props.static, file))
6603
+ source: join16(props.static, file),
6604
+ sourceHash: $hash(join16(props.static, file))
5938
6605
  });
5939
6606
  versions.push(object.key);
5940
6607
  versions.push(object.sourceHash);
5941
6608
  const strippedHtmlFile = file.endsWith("index.html") ? file.slice(0, -11) : file.endsWith(".html") ? file.slice(0, -5) : file;
5942
6609
  const urlFriendlyFile = strippedHtmlFile.endsWith("/") ? strippedHtmlFile.slice(0, -1) : strippedHtmlFile;
5943
- const routeFileKey = join13(props.path, urlFriendlyFile);
6610
+ const routeFileKey = join16(props.path, urlFriendlyFile);
5944
6611
  staticRoutes[routeFileKey] = {
5945
6612
  type: "s3",
5946
6613
  domainName: bucket.bucketRegionalDomainName,
@@ -5957,10 +6624,10 @@ var siteFeature = defineFeature({
5957
6624
  });
5958
6625
 
5959
6626
  // src/feature/store/index.ts
5960
- import { Group as Group16 } from "@terraforge/core";
5961
- import { toDays as toDays7 } from "@awsless/duration";
6627
+ import { Group as Group17 } from "@terraforge/core";
6628
+ import { toDays as toDays8 } from "@awsless/duration";
5962
6629
  import { kebabCase as kebabCase7 } from "change-case";
5963
- import { aws as aws17 } from "@terraforge/aws";
6630
+ import { aws as aws18 } from "@terraforge/aws";
5964
6631
  import { glob as glob3 } from "glob";
5965
6632
 
5966
6633
  // src/feature/store/util.ts
@@ -5984,8 +6651,8 @@ var getContentType2 = (file) => {
5984
6651
  };
5985
6652
 
5986
6653
  // src/feature/store/index.ts
5987
- import { join as join14 } from "path";
5988
- var typeGenCode6 = `
6654
+ import { join as join17 } from "path";
6655
+ var typeGenCode7 = `
5989
6656
  import { Body, PutObjectProps, BodyStream } from '@awsless/s3'
5990
6657
 
5991
6658
  type Store<Name extends string> = {
@@ -6014,7 +6681,7 @@ var storeFeature = defineFeature({
6014
6681
  }
6015
6682
  resources2.addType(stack.name, list3);
6016
6683
  }
6017
- gen.addCode(typeGenCode6);
6684
+ gen.addCode(typeGenCode7);
6018
6685
  gen.addInterface("StoreResources", resources2);
6019
6686
  await ctx.write("store.d.ts", gen, true);
6020
6687
  },
@@ -6052,7 +6719,7 @@ var storeFeature = defineFeature({
6052
6719
  },
6053
6720
  onStack(ctx) {
6054
6721
  for (const [id, props] of Object.entries(ctx.stackConfig.stores ?? {})) {
6055
- const group = new Group16(ctx.stack, "store", id);
6722
+ const group = new Group17(ctx.stack, "store", id);
6056
6723
  const name = formatLocalResourceName({
6057
6724
  appName: ctx.app.name,
6058
6725
  stackName: ctx.stack.name,
@@ -6064,9 +6731,9 @@ var storeFeature = defineFeature({
6064
6731
  id: rule.prefix ? `expire-${kebabCase7(rule.prefix)}` : `expire-rule-${index}`,
6065
6732
  enabled: true,
6066
6733
  ...rule.prefix ? { prefix: rule.prefix } : {},
6067
- expiration: { days: toDays7(rule.expiration) }
6734
+ expiration: { days: toDays8(rule.expiration) }
6068
6735
  }));
6069
- const bucket = new aws17.s3.Bucket(
6736
+ const bucket = new aws18.s3.Bucket(
6070
6737
  group,
6071
6738
  "store",
6072
6739
  {
@@ -6098,13 +6765,13 @@ var storeFeature = defineFeature({
6098
6765
  nodir: true
6099
6766
  });
6100
6767
  for (const file of files) {
6101
- new aws17.s3.BucketObject(group, file, {
6768
+ new aws18.s3.BucketObject(group, file, {
6102
6769
  bucket: bucket.bucket,
6103
6770
  key: file,
6104
6771
  cacheControl: getCacheControl2(file),
6105
6772
  contentType: getContentType2(file),
6106
- source: join14(props.static, file),
6107
- sourceHash: $hash(join14(props.static, file))
6773
+ source: join17(props.static, file),
6774
+ sourceHash: $hash(join17(props.static, file))
6108
6775
  });
6109
6776
  }
6110
6777
  }
@@ -6120,7 +6787,7 @@ var storeFeature = defineFeature({
6120
6787
  "removed:marker": "s3:ObjectRemoved:DeleteMarkerCreated"
6121
6788
  };
6122
6789
  for (const [event, taskProps] of Object.entries(props.events ?? {})) {
6123
- const eventGroup = new Group16(group, "event", event);
6790
+ const eventGroup = new Group17(group, "event", event);
6124
6791
  const eventId = kebabCase7(`${id}-${shortId(event)}`);
6125
6792
  const { lambda } = createAsyncLambdaFunction(eventGroup, ctx, `store`, eventId, {
6126
6793
  ...taskProps,
@@ -6129,13 +6796,13 @@ var storeFeature = defineFeature({
6129
6796
  description: `${id} event "${event}"`
6130
6797
  }
6131
6798
  });
6132
- new aws17.lambda.Permission(eventGroup, "permission", {
6799
+ new aws18.lambda.Permission(eventGroup, "permission", {
6133
6800
  action: "lambda:InvokeFunction",
6134
6801
  principal: "s3.amazonaws.com",
6135
6802
  functionName: lambda.functionName,
6136
6803
  sourceArn: `arn:aws:s3:::${name}`
6137
6804
  });
6138
- new aws17.s3.BucketNotification(eventGroup, "notification", {
6805
+ new aws18.s3.BucketNotification(eventGroup, "notification", {
6139
6806
  bucket: bucket.bucket,
6140
6807
  lambdaFunction: [
6141
6808
  {
@@ -6174,10 +6841,10 @@ var storeFeature = defineFeature({
6174
6841
  });
6175
6842
 
6176
6843
  // src/feature/table/index.ts
6177
- import { Group as Group17 } from "@terraforge/core";
6178
- import { aws as aws18 } from "@terraforge/aws";
6844
+ import { Group as Group18 } from "@terraforge/core";
6845
+ import { aws as aws19 } from "@terraforge/aws";
6179
6846
  import { constantCase as constantCase11 } from "change-case";
6180
- import { toSeconds as toSeconds8 } from "@awsless/duration";
6847
+ import { toSeconds as toSeconds7 } from "@awsless/duration";
6181
6848
  var tableFeature = defineFeature({
6182
6849
  name: "table",
6183
6850
  async onTypeGen(ctx) {
@@ -6226,7 +6893,7 @@ var tableFeature = defineFeature({
6226
6893
  },
6227
6894
  onStack(ctx) {
6228
6895
  for (const [id, props] of Object.entries(ctx.stackConfig.tables ?? {})) {
6229
- const group = new Group17(ctx.stack, "table", id);
6896
+ const group = new Group18(ctx.stack, "table", id);
6230
6897
  const name = formatLocalResourceName({
6231
6898
  appName: ctx.app.name,
6232
6899
  stackName: ctx.stack.name,
@@ -6255,7 +6922,7 @@ var tableFeature = defineFeature({
6255
6922
  type: types2[props.fields?.[name2] ?? "string"]
6256
6923
  }));
6257
6924
  };
6258
- const table = new aws18.dynamodb.Table(
6925
+ const table = new aws19.dynamodb.Table(
6259
6926
  group,
6260
6927
  "table",
6261
6928
  {
@@ -6299,7 +6966,7 @@ var tableFeature = defineFeature({
6299
6966
  const result = createLambdaFunction(group, ctx, "table", id, props.stream.consumer);
6300
6967
  result.setEnvironment("THROW_EXPECTED_ERRORS", "1");
6301
6968
  const onFailure = getGlobalOnFailure(ctx);
6302
- new aws18.lambda.EventSourceMapping(
6969
+ new aws19.lambda.EventSourceMapping(
6303
6970
  group,
6304
6971
  id,
6305
6972
  {
@@ -6309,7 +6976,7 @@ var tableFeature = defineFeature({
6309
6976
  // maximumRecordAgeInSeconds: toSeconds(props.stream.maxRecordAge),
6310
6977
  // bisectBatchOnFunctionError: true,
6311
6978
  batchSize: props.stream.batchSize,
6312
- maximumBatchingWindowInSeconds: props.stream.batchWindow ? toSeconds8(props.stream.batchWindow) : void 0,
6979
+ maximumBatchingWindowInSeconds: props.stream.batchWindow ? toSeconds7(props.stream.batchWindow) : void 0,
6313
6980
  maximumRetryAttempts: props.stream.retryAttempts,
6314
6981
  parallelizationFactor: props.stream.concurrencyPerShard,
6315
6982
  functionResponseTypes: ["ReportBatchItemFailures"],
@@ -6375,11 +7042,11 @@ var tableFeature = defineFeature({
6375
7042
  });
6376
7043
 
6377
7044
  // src/feature/task/index.ts
6378
- import { Group as Group18 } from "@terraforge/core";
6379
- import { aws as aws19 } from "@terraforge/aws";
7045
+ import { Group as Group19 } from "@terraforge/core";
7046
+ import { aws as aws20 } from "@terraforge/aws";
6380
7047
  import { camelCase as camelCase7 } from "change-case";
6381
7048
  import { relative as relative7 } from "path";
6382
- var typeGenCode7 = `
7049
+ var typeGenCode8 = `
6383
7050
  import { Duration } from '@awsless/duration'
6384
7051
  import { InvokeOptions } from '@awsless/lambda'
6385
7052
  import type { Mock } from 'vitest'
@@ -6437,26 +7104,26 @@ var taskFeature = defineFeature({
6437
7104
  resources2.addType(stack.name, resource);
6438
7105
  mockResponses.addType(stack.name, mockResponse);
6439
7106
  }
6440
- types2.addCode(typeGenCode7);
7107
+ types2.addCode(typeGenCode8);
6441
7108
  types2.addInterface("TaskResources", resources2);
6442
7109
  types2.addInterface("TaskMock", mocks);
6443
7110
  types2.addInterface("TaskMockResponse", mockResponses);
6444
7111
  await ctx.write("task.d.ts", types2, true);
6445
7112
  },
6446
7113
  onApp(ctx) {
6447
- const group = new Group18(ctx.base, "task", "main");
7114
+ const group = new Group19(ctx.base, "task", "main");
6448
7115
  const scheduleGroupName = formatGlobalResourceName({
6449
7116
  appName: ctx.app.name,
6450
7117
  resourceType: "task",
6451
7118
  resourceName: "group"
6452
7119
  });
6453
- new aws19.scheduler.ScheduleGroup(group, "group", {
7120
+ new aws20.scheduler.ScheduleGroup(group, "group", {
6454
7121
  name: scheduleGroupName,
6455
7122
  tags: {
6456
7123
  app: ctx.app.name
6457
7124
  }
6458
7125
  });
6459
- const role = new aws19.iam.Role(group, "schedule", {
7126
+ const role = new aws20.iam.Role(group, "schedule", {
6460
7127
  name: formatGlobalResourceName({
6461
7128
  appName: ctx.app.name,
6462
7129
  resourceType: "task",
@@ -6503,7 +7170,7 @@ var taskFeature = defineFeature({
6503
7170
  },
6504
7171
  onStack(ctx) {
6505
7172
  for (const [id, props] of Object.entries(ctx.stackConfig.tasks ?? {})) {
6506
- const group = new Group18(ctx.stack, "task", id);
7173
+ const group = new Group19(ctx.stack, "task", id);
6507
7174
  createAsyncLambdaFunction(group, ctx, "task", id, props);
6508
7175
  }
6509
7176
  }
@@ -6528,9 +7195,9 @@ var testFeature = defineFeature({
6528
7195
  });
6529
7196
 
6530
7197
  // src/feature/topic/index.ts
6531
- import { Group as Group19 } from "@terraforge/core";
6532
- import { aws as aws20 } from "@terraforge/aws";
6533
- var typeGenCode8 = `
7198
+ import { Group as Group20 } from "@terraforge/core";
7199
+ import { aws as aws21 } from "@terraforge/aws";
7200
+ var typeGenCode9 = `
6534
7201
  import type { PublishOptions } from '@awsless/sns'
6535
7202
 
6536
7203
  type Publish<Name extends string> = {
@@ -6558,7 +7225,7 @@ var topicFeature = defineFeature({
6558
7225
  resources2.addType(topic, `Publish<'${name}'>`);
6559
7226
  mocks.addType(topic, `MockBuilder`);
6560
7227
  }
6561
- gen.addCode(typeGenCode8);
7228
+ gen.addCode(typeGenCode9);
6562
7229
  gen.addInterface("TopicResources", resources2);
6563
7230
  gen.addInterface("TopicMock", mocks);
6564
7231
  gen.addInterface("TopicMockResponse", mockResponses);
@@ -6576,13 +7243,13 @@ var topicFeature = defineFeature({
6576
7243
  },
6577
7244
  onApp(ctx) {
6578
7245
  for (const id of ctx.appConfig.defaults.topics ?? []) {
6579
- const group = new Group19(ctx.base, "topic", id);
7246
+ const group = new Group20(ctx.base, "topic", id);
6580
7247
  const name = formatGlobalResourceName({
6581
7248
  appName: ctx.appConfig.name,
6582
7249
  resourceType: "topic",
6583
7250
  resourceName: id
6584
7251
  });
6585
- const topic = new aws20.sns.Topic(group, "topic", {
7252
+ const topic = new aws21.sns.Topic(group, "topic", {
6586
7253
  name
6587
7254
  });
6588
7255
  ctx.shared.add("topic", `arn`, id, topic.arn);
@@ -6600,15 +7267,15 @@ var topicFeature = defineFeature({
6600
7267
  },
6601
7268
  onStack(ctx) {
6602
7269
  for (const [id, props] of Object.entries(ctx.stackConfig.subscribers ?? {})) {
6603
- const group = new Group19(ctx.stack, "topic", id);
7270
+ const group = new Group20(ctx.stack, "topic", id);
6604
7271
  const topicArn = ctx.shared.entry("topic", "arn", id);
6605
7272
  const { lambda } = createAsyncLambdaFunction(group, ctx, `topic`, id, props);
6606
- new aws20.sns.TopicSubscription(group, id, {
7273
+ new aws21.sns.TopicSubscription(group, id, {
6607
7274
  topicArn,
6608
7275
  protocol: "lambda",
6609
7276
  endpoint: lambda.arn
6610
7277
  });
6611
- new aws20.lambda.Permission(group, id, {
7278
+ new aws21.lambda.Permission(group, id, {
6612
7279
  action: "lambda:InvokeFunction",
6613
7280
  principal: "sns.amazonaws.com",
6614
7281
  functionName: lambda.functionName,
@@ -6619,13 +7286,13 @@ var topicFeature = defineFeature({
6619
7286
  });
6620
7287
 
6621
7288
  // src/feature/vpc/index.ts
6622
- import { Group as Group20 } from "@terraforge/core";
6623
- import { aws as aws21 } from "@terraforge/aws";
7289
+ import { Group as Group21 } from "@terraforge/core";
7290
+ import { aws as aws22 } from "@terraforge/aws";
6624
7291
  var vpcFeature = defineFeature({
6625
7292
  name: "vpc",
6626
7293
  onApp(ctx) {
6627
- const group = new Group20(ctx.base, "vpc", "main");
6628
- const vpc = new aws21.Vpc(group, "vpc", {
7294
+ const group = new Group21(ctx.base, "vpc", "main");
7295
+ const vpc = new aws22.Vpc(group, "vpc", {
6629
7296
  tags: {
6630
7297
  Name: ctx.app.name
6631
7298
  },
@@ -6635,39 +7302,39 @@ var vpcFeature = defineFeature({
6635
7302
  ipv6CidrBlockNetworkBorderGroup: ctx.appConfig.region,
6636
7303
  assignGeneratedIpv6CidrBlock: true
6637
7304
  });
6638
- const privateRouteTable = new aws21.route.Table(group, "private", {
7305
+ const privateRouteTable = new aws22.route.Table(group, "private", {
6639
7306
  vpcId: vpc.id,
6640
7307
  tags: {
6641
7308
  Name: "private"
6642
7309
  }
6643
7310
  });
6644
- const publicRouteTable = new aws21.route.Table(group, "public", {
7311
+ const publicRouteTable = new aws22.route.Table(group, "public", {
6645
7312
  vpcId: vpc.id,
6646
7313
  tags: {
6647
7314
  Name: "public"
6648
7315
  }
6649
7316
  });
6650
- const gateway = new aws21.internet.Gateway(group, "gateway", {
7317
+ const gateway = new aws22.internet.Gateway(group, "gateway", {
6651
7318
  tags: {
6652
7319
  Name: ctx.app.name
6653
7320
  }
6654
7321
  });
6655
- const egressOnlyInternetGateway = new aws21.egress.OnlyInternetGateway(group, "gateway", {
7322
+ const egressOnlyInternetGateway = new aws22.egress.OnlyInternetGateway(group, "gateway", {
6656
7323
  vpcId: vpc.id,
6657
7324
  tags: {
6658
7325
  Name: ctx.app.name
6659
7326
  }
6660
7327
  });
6661
- const attachment = new aws21.internet.GatewayAttachment(group, "attachment", {
7328
+ const attachment = new aws22.internet.GatewayAttachment(group, "attachment", {
6662
7329
  vpcId: vpc.id,
6663
7330
  internetGatewayId: gateway.id
6664
7331
  });
6665
- new aws21.Route(group, "ipv4", {
7332
+ new aws22.Route(group, "ipv4", {
6666
7333
  routeTableId: publicRouteTable.id,
6667
7334
  destinationCidrBlock: "0.0.0.0/0",
6668
7335
  gatewayId: gateway.id
6669
7336
  });
6670
- new aws21.Route(group, "ipv6", {
7337
+ new aws22.Route(group, "ipv6", {
6671
7338
  routeTableId: privateRouteTable.id,
6672
7339
  destinationIpv6CidrBlock: "::/0",
6673
7340
  egressOnlyGatewayId: egressOnlyInternetGateway.id
@@ -6692,7 +7359,7 @@ var vpcFeature = defineFeature({
6692
7359
  for (const i in zones) {
6693
7360
  const index = Number(i) + 1;
6694
7361
  const id = `${type}-${index}`;
6695
- const subnet = new aws21.Subnet(group, id, {
7362
+ const subnet = new aws22.Subnet(group, id, {
6696
7363
  tags: {
6697
7364
  Name: `${ctx.app.name}--${type}-${index}`
6698
7365
  },
@@ -6706,7 +7373,7 @@ var vpcFeature = defineFeature({
6706
7373
  return `${cidrBlock}${ipv6Identifier++}::/64`;
6707
7374
  })
6708
7375
  });
6709
- new aws21.route.TableAssociation(group, id, {
7376
+ new aws22.route.TableAssociation(group, id, {
6710
7377
  routeTableId: table.id,
6711
7378
  subnetId: subnet.id
6712
7379
  });
@@ -6719,9 +7386,9 @@ var vpcFeature = defineFeature({
6719
7386
  });
6720
7387
 
6721
7388
  // src/feature/alert/index.ts
6722
- import { Group as Group21 } from "@terraforge/core";
6723
- import { aws as aws22 } from "@terraforge/aws";
6724
- var typeGenCode9 = `
7389
+ import { Group as Group22 } from "@terraforge/core";
7390
+ import { aws as aws23 } from "@terraforge/aws";
7391
+ var typeGenCode10 = `
6725
7392
  import type { PublishOptions } from '@awsless/sns'
6726
7393
 
6727
7394
  type Alert<Name extends string> = {
@@ -6749,7 +7416,7 @@ var alertFeature = defineFeature({
6749
7416
  mockResponses.addType(alert, "Mock");
6750
7417
  mocks.addType(alert, `MockBuilder`);
6751
7418
  }
6752
- gen.addCode(typeGenCode9);
7419
+ gen.addCode(typeGenCode10);
6753
7420
  gen.addInterface("AlertResources", resources2);
6754
7421
  gen.addInterface("AlertMock", mocks);
6755
7422
  gen.addInterface("AlertMockResponse", mockResponses);
@@ -6757,17 +7424,17 @@ var alertFeature = defineFeature({
6757
7424
  },
6758
7425
  onApp(ctx) {
6759
7426
  for (const [id, emails] of Object.entries(ctx.appConfig.defaults.alerts ?? {})) {
6760
- const group = new Group21(ctx.base, "alert", id);
7427
+ const group = new Group22(ctx.base, "alert", id);
6761
7428
  const name = formatGlobalResourceName({
6762
7429
  appName: ctx.appConfig.name,
6763
7430
  resourceType: "alert",
6764
7431
  resourceName: id
6765
7432
  });
6766
- const topic = new aws22.sns.Topic(group, "topic", {
7433
+ const topic = new aws23.sns.Topic(group, "topic", {
6767
7434
  name
6768
7435
  });
6769
7436
  for (const email of emails) {
6770
- new aws22.sns.TopicSubscription(group, email, {
7437
+ new aws23.sns.TopicSubscription(group, email, {
6771
7438
  topicArn: topic.arn,
6772
7439
  protocol: "email",
6773
7440
  endpoint: email
@@ -6788,13 +7455,13 @@ var alertFeature = defineFeature({
6788
7455
  });
6789
7456
 
6790
7457
  // src/feature/layer/index.ts
6791
- import { Group as Group22 } from "@terraforge/core";
6792
- import { aws as aws23 } from "@terraforge/aws";
7458
+ import { Group as Group23 } from "@terraforge/core";
7459
+ import { aws as aws24 } from "@terraforge/aws";
6793
7460
  var layerFeature = defineFeature({
6794
7461
  name: "layer",
6795
7462
  onBefore(ctx) {
6796
- const group = new Group22(ctx.base, "layer", "asset");
6797
- 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", {
6798
7465
  bucket: formatGlobalResourceName({
6799
7466
  appName: ctx.app.name,
6800
7467
  resourceType: "layer",
@@ -6826,15 +7493,15 @@ var layerFeature = defineFeature({
6826
7493
  }
6827
7494
  for (const [id, _props] of layers) {
6828
7495
  const props = _props;
6829
- const group = new Group22(ctx.base, "layer", id);
6830
- 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", {
6831
7498
  bucket: ctx.shared.get("layer", "bucket-name"),
6832
7499
  key: `/layer/${id}.zip`,
6833
7500
  contentType: "application/zip",
6834
7501
  source: props.file,
6835
7502
  sourceHash: $hash(props.file)
6836
7503
  });
6837
- const layer = new aws23.lambda.LayerVersion(
7504
+ const layer = new aws24.lambda.LayerVersion(
6838
7505
  group,
6839
7506
  "layer",
6840
7507
  {
@@ -6869,14 +7536,14 @@ var layerFeature = defineFeature({
6869
7536
  });
6870
7537
 
6871
7538
  // src/feature/image/index.ts
6872
- import { Group as Group23 } from "@terraforge/core";
6873
- import { aws as aws24 } from "@terraforge/aws";
6874
- import { join as join15, dirname as dirname7 } from "path";
6875
- import { mebibytes as mebibytes6 } from "@awsless/size";
6876
- import { seconds as seconds8, toDays as toDays8 } from "@awsless/duration";
6877
- 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";
6878
7545
  import { glob as glob4 } from "glob";
6879
- var __dirname3 = dirname7(fileURLToPath2(import.meta.url));
7546
+ var __dirname5 = dirname9(fileURLToPath4(import.meta.url));
6880
7547
  var imageFeature = defineFeature({
6881
7548
  name: "image",
6882
7549
  onApp(ctx) {
@@ -6886,21 +7553,21 @@ var imageFeature = defineFeature({
6886
7553
  if (found.length === 0) {
6887
7554
  return;
6888
7555
  }
6889
- const group = new Group23(ctx.base, "image", "layer");
6890
- 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");
6891
7558
  const layerId = formatGlobalResourceName({
6892
7559
  appName: ctx.appConfig.name,
6893
7560
  resourceType: "layer",
6894
7561
  resourceName: "sharp"
6895
7562
  });
6896
- const zipFile = new aws24.s3.BucketObject(group, "layer", {
7563
+ const zipFile = new aws25.s3.BucketObject(group, "layer", {
6897
7564
  bucket: ctx.shared.get("layer", "bucket-name"),
6898
7565
  key: `/layer/${layerId}.zip`,
6899
7566
  contentType: "application/zip",
6900
7567
  source: path,
6901
7568
  sourceHash: $hash(path)
6902
7569
  });
6903
- const layer = new aws24.lambda.LayerVersion(
7570
+ const layer = new aws25.lambda.LayerVersion(
6904
7571
  group,
6905
7572
  "layer",
6906
7573
  {
@@ -6927,7 +7594,7 @@ var imageFeature = defineFeature({
6927
7594
  },
6928
7595
  onStack(ctx) {
6929
7596
  for (const [id, props] of Object.entries(ctx.stackConfig.images ?? {})) {
6930
- const group = new Group23(ctx.stack, "image", id);
7597
+ const group = new Group24(ctx.stack, "image", id);
6931
7598
  const routerId = ctx.shared.entry("router", "id", props.router);
6932
7599
  const addRoutes = ctx.shared.entry("router", "addRoutes", props.router);
6933
7600
  const routeKey = props.path.endsWith("/") ? `${props.path}*` : `${props.path}/*`;
@@ -6937,7 +7604,7 @@ var imageFeature = defineFeature({
6937
7604
  }
6938
7605
  let s3Origin;
6939
7606
  if (props.origin.static) {
6940
- s3Origin = new aws24.s3.Bucket(group, "origin", {
7607
+ s3Origin = new aws25.s3.Bucket(group, "origin", {
6941
7608
  bucket: formatLocalResourceName({
6942
7609
  appName: ctx.app.name,
6943
7610
  stackName: ctx.stack.name,
@@ -6947,7 +7614,7 @@ var imageFeature = defineFeature({
6947
7614
  forceDestroy: true
6948
7615
  });
6949
7616
  }
6950
- const cacheBucket = new aws24.s3.Bucket(group, "cache", {
7617
+ const cacheBucket = new aws25.s3.Bucket(group, "cache", {
6951
7618
  bucket: formatLocalResourceName({
6952
7619
  appName: ctx.app.name,
6953
7620
  stackName: ctx.stack.name,
@@ -6964,7 +7631,7 @@ var imageFeature = defineFeature({
6964
7631
  enabled: true,
6965
7632
  id: "image-cache-duration",
6966
7633
  expiration: {
6967
- days: toDays8(props.cacheDuration)
7634
+ days: toDays9(props.cacheDuration)
6968
7635
  }
6969
7636
  }
6970
7637
  ]
@@ -6976,23 +7643,23 @@ var imageFeature = defineFeature({
6976
7643
  resourceName: "sharp"
6977
7644
  });
6978
7645
  const serverLambda = createPrebuildLambdaFunction(group, ctx, "image", id, {
6979
- bundleFile: join15(__dirname3, "/prebuild/image/bundle.zip"),
6980
- bundleHash: join15(__dirname3, "/prebuild/image/HASH"),
6981
- memorySize: mebibytes6(512),
6982
- 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),
6983
7650
  handler: "index.default",
6984
7651
  runtime: "nodejs24.x",
6985
7652
  log: props.log,
6986
7653
  layers: [sharpLayerId]
6987
7654
  });
6988
- const permission = new aws24.lambda.Permission(group, "permission", {
7655
+ const permission = new aws25.lambda.Permission(group, "permission", {
6989
7656
  principal: "cloudfront.amazonaws.com",
6990
7657
  action: "lambda:InvokeFunctionUrl",
6991
7658
  functionName: serverLambda.lambda.functionName,
6992
7659
  functionUrlAuthType: "AWS_IAM",
6993
7660
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
6994
7661
  });
6995
- const serverLambdaUrl = new aws24.lambda.FunctionUrl(
7662
+ const serverLambdaUrl = new aws25.lambda.FunctionUrl(
6996
7663
  group,
6997
7664
  "url",
6998
7665
  {
@@ -7049,11 +7716,11 @@ var imageFeature = defineFeature({
7049
7716
  nodir: true
7050
7717
  });
7051
7718
  for (const file of files) {
7052
- new aws24.s3.BucketObject(group, `static-${file}`, {
7719
+ new aws25.s3.BucketObject(group, `static-${file}`, {
7053
7720
  bucket: s3Origin.bucket,
7054
7721
  key: file,
7055
- source: join15(props.origin.static, file),
7056
- sourceHash: $hash(join15(props.origin.static, file))
7722
+ source: join18(props.origin.static, file),
7723
+ sourceHash: $hash(join18(props.origin.static, file))
7057
7724
  });
7058
7725
  }
7059
7726
  }
@@ -7066,19 +7733,19 @@ var imageFeature = defineFeature({
7066
7733
  });
7067
7734
 
7068
7735
  // src/feature/icon/index.ts
7069
- import { Group as Group24 } from "@terraforge/core";
7070
- import { aws as aws25 } from "@terraforge/aws";
7071
- import { join as join16, dirname as dirname8 } from "path";
7072
- import { mebibytes as mebibytes7 } from "@awsless/size";
7073
- import { seconds as seconds9, toDays as toDays9 } from "@awsless/duration";
7074
- 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";
7075
7742
  import { glob as glob5 } from "glob";
7076
- var __dirname4 = dirname8(fileURLToPath3(import.meta.url));
7743
+ var __dirname6 = dirname10(fileURLToPath5(import.meta.url));
7077
7744
  var iconFeature = defineFeature({
7078
7745
  name: "icon",
7079
7746
  onStack(ctx) {
7080
7747
  for (const [id, props] of Object.entries(ctx.stackConfig.icons ?? {})) {
7081
- const group = new Group24(ctx.stack, "icon", id);
7748
+ const group = new Group25(ctx.stack, "icon", id);
7082
7749
  const routerId = ctx.shared.entry("router", "id", props.router);
7083
7750
  const addRoutes = ctx.shared.entry("router", "addRoutes", props.router);
7084
7751
  const routeKey = props.path.endsWith("/") ? `${props.path}*` : `${props.path}/*`;
@@ -7088,7 +7755,7 @@ var iconFeature = defineFeature({
7088
7755
  }
7089
7756
  let s3Origin;
7090
7757
  if (props.origin.static) {
7091
- s3Origin = new aws25.s3.Bucket(group, "origin", {
7758
+ s3Origin = new aws26.s3.Bucket(group, "origin", {
7092
7759
  bucket: formatLocalResourceName({
7093
7760
  appName: ctx.app.name,
7094
7761
  stackName: ctx.stack.name,
@@ -7098,7 +7765,7 @@ var iconFeature = defineFeature({
7098
7765
  forceDestroy: true
7099
7766
  });
7100
7767
  }
7101
- const cacheBucket = new aws25.s3.Bucket(group, "cache", {
7768
+ const cacheBucket = new aws26.s3.Bucket(group, "cache", {
7102
7769
  bucket: formatLocalResourceName({
7103
7770
  appName: ctx.app.name,
7104
7771
  stackName: ctx.stack.name,
@@ -7115,29 +7782,29 @@ var iconFeature = defineFeature({
7115
7782
  enabled: true,
7116
7783
  id: "icon-cache-duration",
7117
7784
  expiration: {
7118
- days: toDays9(props.cacheDuration)
7785
+ days: toDays10(props.cacheDuration)
7119
7786
  }
7120
7787
  }
7121
7788
  ]
7122
7789
  } : {}
7123
7790
  });
7124
7791
  const serverLambda = createPrebuildLambdaFunction(group, ctx, "icon", id, {
7125
- bundleFile: join16(__dirname4, "/prebuild/icon/bundle.zip"),
7126
- bundleHash: join16(__dirname4, "/prebuild/icon/HASH"),
7127
- memorySize: mebibytes7(512),
7128
- 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),
7129
7796
  handler: "index.default",
7130
7797
  runtime: "nodejs24.x",
7131
7798
  log: props.log
7132
7799
  });
7133
- const permission = new aws25.lambda.Permission(group, "permission", {
7800
+ const permission = new aws26.lambda.Permission(group, "permission", {
7134
7801
  principal: "cloudfront.amazonaws.com",
7135
7802
  action: "lambda:InvokeFunctionUrl",
7136
7803
  functionName: serverLambda.lambda.functionName,
7137
7804
  functionUrlAuthType: "AWS_IAM",
7138
7805
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
7139
7806
  });
7140
- const serverLambdaUrl = new aws25.lambda.FunctionUrl(
7807
+ const serverLambdaUrl = new aws26.lambda.FunctionUrl(
7141
7808
  group,
7142
7809
  "url",
7143
7810
  {
@@ -7197,11 +7864,11 @@ var iconFeature = defineFeature({
7197
7864
  if (!file.endsWith(".svg")) {
7198
7865
  throw new Error(`Icon file "${file}" in "${props.origin.static}" is not an SVG file.`);
7199
7866
  }
7200
- new aws25.s3.BucketObject(group, `static-${file}`, {
7867
+ new aws26.s3.BucketObject(group, `static-${file}`, {
7201
7868
  bucket: s3Origin.bucket,
7202
7869
  key: file,
7203
- source: join16(props.origin.static, file),
7204
- sourceHash: $hash(join16(props.origin.static, file))
7870
+ source: join19(props.origin.static, file),
7871
+ sourceHash: $hash(join19(props.origin.static, file))
7205
7872
  });
7206
7873
  }
7207
7874
  }
@@ -7214,30 +7881,30 @@ var iconFeature = defineFeature({
7214
7881
  });
7215
7882
 
7216
7883
  // src/feature/job/index.ts
7217
- import { Group as Group26, Output as Output7, resolveInputs as resolveInputs4, findInputDeps as findInputDeps4 } from "@terraforge/core";
7218
- 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";
7219
7886
  import { camelCase as camelCase8 } from "change-case";
7220
7887
  import deepmerge5 from "deepmerge";
7221
7888
  import { relative as relative8 } from "path";
7222
7889
 
7223
7890
  // src/feature/job/util.ts
7224
- import { createHash as createHash4 } from "crypto";
7225
- import { toDays as toDays10, toSeconds as toSeconds9 } from "@awsless/duration";
7226
- 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";
7227
7894
  import { toMebibytes as toMebibytes5 } from "@awsless/size";
7228
7895
  import { generateFileHash as generateFileHash2 } from "@awsless/ts-file-cache";
7229
- import { aws as aws26 } from "@terraforge/aws";
7230
- import { Group as Group25, Output as Output6, findInputDeps as findInputDeps3, resolveInputs as resolveInputs3 } from "@terraforge/core";
7231
- 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";
7232
7899
  import deepmerge4 from "deepmerge";
7233
7900
 
7234
7901
  // src/feature/job/build/executable.ts
7235
- import { createHash as createHash3 } from "crypto";
7236
- import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
7237
- 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";
7238
7905
  var buildJobExecutable = async (input, outputPath, architecture) => {
7239
- const filePath = join17(outputPath, "program");
7240
- const wrapperPath = join17(outputPath, "wrapper.ts");
7906
+ const filePath = join20(outputPath, "program");
7907
+ const wrapperPath = join20(outputPath, "wrapper.ts");
7241
7908
  const handlerPath = resolve(input);
7242
7909
  await writeFile3(
7243
7910
  wrapperPath,
@@ -7292,16 +7959,16 @@ await handler(payload)
7292
7959
  throw new ExpectedError(`Job executable build failed:
7293
7960
  ${result.logs?.map((log35) => log35.message).join("\n")}`);
7294
7961
  }
7295
- const file = await readFile4(filePath);
7962
+ const file = await readFile6(filePath);
7296
7963
  return {
7297
- hash: createHash3("sha1").update(file).update(target).digest("hex"),
7964
+ hash: createHash5("sha1").update(file).update(target).digest("hex"),
7298
7965
  file
7299
7966
  };
7300
7967
  };
7301
7968
 
7302
7969
  // src/feature/job/util.ts
7303
7970
  var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7304
- const group = new Group25(parentGroup, "job", ns);
7971
+ const group = new Group26(parentGroup, "job", ns);
7305
7972
  const name = formatLocalResourceName({
7306
7973
  appName: ctx.app.name,
7307
7974
  stackName: ctx.stack.name,
@@ -7327,13 +7994,13 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7327
7994
  };
7328
7995
  });
7329
7996
  });
7330
- const code = new aws26.s3.BucketObject(group, "code", {
7997
+ const code = new aws27.s3.BucketObject(group, "code", {
7331
7998
  bucket: ctx.shared.get("job", "bucket-name"),
7332
7999
  key: name,
7333
8000
  source: relativePath(getBuildPath("job", name, "program")),
7334
8001
  sourceHash: $file(getBuildPath("job", name, "HASH"))
7335
8002
  });
7336
- const executionRole = new aws26.iam.Role(group, "execution-role", {
8003
+ const executionRole = new aws27.iam.Role(group, "execution-role", {
7337
8004
  name: shortId(`${shortName}:execution-role`),
7338
8005
  description: name,
7339
8006
  assumeRolePolicy: JSON.stringify({
@@ -7350,7 +8017,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7350
8017
  }),
7351
8018
  managedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
7352
8019
  });
7353
- const role = new aws26.iam.Role(
8020
+ const role = new aws27.iam.Role(
7354
8021
  group,
7355
8022
  "task-role",
7356
8023
  {
@@ -7376,7 +8043,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7376
8043
  Version: "2012-10-17",
7377
8044
  Statement: [
7378
8045
  {
7379
- Effect: pascalCase3("allow"),
8046
+ Effect: pascalCase4("allow"),
7380
8047
  Action: ["s3:GetObject", "s3:HeadObject"],
7381
8048
  Resource: [`arn:aws:s3:::${bucket}/${key}`, `arn:aws:s3:::${bucket}/payloads/*`]
7382
8049
  }
@@ -7392,16 +8059,16 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7392
8059
  );
7393
8060
  const statements = [];
7394
8061
  const statementDeps = /* @__PURE__ */ new Set();
7395
- const policy = new aws26.iam.RolePolicy(group, "policy", {
8062
+ const policy = new aws27.iam.RolePolicy(group, "policy", {
7396
8063
  role: role.name,
7397
8064
  name: "task-policy",
7398
- policy: new Output6(statementDeps, async (resolve2) => {
7399
- const list3 = await resolveInputs3(statements);
8065
+ policy: new Output7(statementDeps, async (resolve2) => {
8066
+ const list3 = await resolveInputs4(statements);
7400
8067
  resolve2(
7401
8068
  JSON.stringify({
7402
8069
  Version: "2012-10-17",
7403
8070
  Statement: list3.map((statement) => ({
7404
- Effect: pascalCase3(statement.effect ?? "allow"),
8071
+ Effect: pascalCase4(statement.effect ?? "allow"),
7405
8072
  Action: statement.actions,
7406
8073
  Resource: statement.resources
7407
8074
  }))
@@ -7411,7 +8078,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7411
8078
  });
7412
8079
  const addPermission = (...permissions) => {
7413
8080
  statements.push(...permissions);
7414
- for (const dep of findInputDeps3(permissions)) {
8081
+ for (const dep of findInputDeps4(permissions)) {
7415
8082
  statementDeps.add(dep);
7416
8083
  }
7417
8084
  };
@@ -7420,12 +8087,12 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7420
8087
  });
7421
8088
  let logGroup;
7422
8089
  if (props.log.retention && props.log.retention.value > 0n) {
7423
- logGroup = new aws26.cloudwatch.LogGroup(group, "log", {
8090
+ logGroup = new aws27.cloudwatch.LogGroup(group, "log", {
7424
8091
  name: `/aws/ecs/${name}`,
7425
- retentionInDays: toDays10(props.log.retention)
8092
+ retentionInDays: toDays11(props.log.retention)
7426
8093
  });
7427
8094
  if (ctx.shared.has("on-error-log", "subscriber-arn")) {
7428
- new aws26.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
8095
+ new aws27.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
7429
8096
  name: "error-log-subscription",
7430
8097
  destinationArn: ctx.shared.get("on-error-log", "subscriber-arn"),
7431
8098
  logGroupName: logGroup.name,
@@ -7443,7 +8110,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7443
8110
  const taskDependsOn = [code];
7444
8111
  const accessPoint = props.persistentStorage ? (() => {
7445
8112
  const fileSystemId = ctx.shared.get("job", "persistent-storage-file-system-id");
7446
- const accessPoint2 = new aws26.efs.AccessPoint(
8113
+ const accessPoint2 = new aws27.efs.AccessPoint(
7447
8114
  group,
7448
8115
  "access-point",
7449
8116
  {
@@ -7468,7 +8135,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7468
8135
  fileSystemId
7469
8136
  };
7470
8137
  })() : void 0;
7471
- const task2 = new aws26.ecs.TaskDefinition(
8138
+ const task2 = new aws27.ecs.TaskDefinition(
7472
8139
  group,
7473
8140
  "task",
7474
8141
  {
@@ -7498,9 +8165,9 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7498
8165
  }
7499
8166
  ]
7500
8167
  },
7501
- containerDefinitions: new Output6(variableDeps, async (resolve2) => {
7502
- const data = await resolveInputs3(variables);
7503
- const { s3Bucket, s3Key } = await resolveInputs3({
8168
+ containerDefinitions: new Output7(variableDeps, async (resolve2) => {
8169
+ const data = await resolveInputs4(variables);
8170
+ const { s3Bucket, s3Key } = await resolveInputs4({
7504
8171
  s3Bucket: code.bucket,
7505
8172
  s3Key: code.key
7506
8173
  });
@@ -7516,7 +8183,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7516
8183
  [
7517
8184
  ...props.startupCommand?.length ? [props.startupCommand.join(" && ")] : [],
7518
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`,
7519
- `exec timeout --kill-after=10 ${toSeconds9(props.timeout)} /root/program`
8186
+ `exec timeout --kill-after=10 ${toSeconds8(props.timeout)} /root/program`
7520
8187
  ].join(" && ")
7521
8188
  ],
7522
8189
  environment: Object.entries(data).map(([name2, value]) => ({
@@ -7562,7 +8229,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7562
8229
  );
7563
8230
  ctx.onEnv((name2, value) => {
7564
8231
  variables[name2] = value;
7565
- for (const dep of findInputDeps3([value])) {
8232
+ for (const dep of findInputDeps4([value])) {
7566
8233
  variableDeps.add(dep);
7567
8234
  }
7568
8235
  });
@@ -7571,7 +8238,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7571
8238
  variables.AWS_ACCOUNT_ID = ctx.accountId;
7572
8239
  variables.STACK = ctx.stackConfig.name;
7573
8240
  variables.CODE_HASH = code.sourceHash;
7574
- variables.JOB_CONFIG_HASH = createHash4("sha1").update(stringify(props)).digest("hex");
8241
+ variables.JOB_CONFIG_HASH = createHash6("sha1").update(stringify2(props)).digest("hex");
7575
8242
  if (props.environment) {
7576
8243
  for (const [key, value] of Object.entries(props.environment)) {
7577
8244
  variables[key] = value;
@@ -7587,7 +8254,7 @@ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7587
8254
  };
7588
8255
 
7589
8256
  // src/feature/job/index.ts
7590
- var typeGenCode10 = `
8257
+ var typeGenCode11 = `
7591
8258
  import type { Mock } from 'vitest'
7592
8259
 
7593
8260
  type Func = (...args: any[]) => any
@@ -7643,15 +8310,15 @@ var jobFeature = defineFeature({
7643
8310
  resources2.addType(stack.name, resource);
7644
8311
  mockResponses.addType(stack.name, mockResponse);
7645
8312
  }
7646
- types2.addCode(typeGenCode10);
8313
+ types2.addCode(typeGenCode11);
7647
8314
  types2.addInterface("JobResources", resources2);
7648
8315
  types2.addInterface("JobMock", mocks);
7649
8316
  types2.addInterface("JobMockResponse", mockResponses);
7650
8317
  await ctx.write("job.d.ts", types2, true);
7651
8318
  },
7652
8319
  onBefore(ctx) {
7653
- const group = new Group26(ctx.base, "job", "asset");
7654
- 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", {
7655
8322
  bucket: formatGlobalResourceName({
7656
8323
  appName: ctx.app.name,
7657
8324
  resourceType: "job",
@@ -7677,8 +8344,8 @@ var jobFeature = defineFeature({
7677
8344
  if (found.length === 0) {
7678
8345
  return;
7679
8346
  }
7680
- const group = new Group26(ctx.base, "job", "cluster");
7681
- const cluster = new aws27.ecs.Cluster(
8347
+ const group = new Group27(ctx.base, "job", "cluster");
8348
+ const cluster = new aws28.ecs.Cluster(
7682
8349
  group,
7683
8350
  "cluster",
7684
8351
  {
@@ -7690,7 +8357,7 @@ var jobFeature = defineFeature({
7690
8357
  );
7691
8358
  ctx.shared.set("job", "cluster-name", cluster.name);
7692
8359
  ctx.shared.set("job", "cluster-arn", cluster.arn);
7693
- const securityGroup = new aws27.security.Group(group, "security-group", {
8360
+ const securityGroup = new aws28.security.Group(group, "security-group", {
7694
8361
  name: `${ctx.app.name}-job`,
7695
8362
  description: "Shared security group for jobs",
7696
8363
  vpcId: ctx.shared.get("vpc", "id"),
@@ -7699,7 +8366,7 @@ var jobFeature = defineFeature({
7699
8366
  APP: ctx.appConfig.name
7700
8367
  }
7701
8368
  });
7702
- new aws27.vpc.SecurityGroupEgressRule(group, "egress-rule", {
8369
+ new aws28.vpc.SecurityGroupEgressRule(group, "egress-rule", {
7703
8370
  securityGroupId: securityGroup.id,
7704
8371
  description: "Allow all outbound traffic from jobs",
7705
8372
  ipProtocol: "-1",
@@ -7716,8 +8383,8 @@ var jobFeature = defineFeature({
7716
8383
  })
7717
8384
  );
7718
8385
  if (needsPersistentStorage) {
7719
- const storageGroup = new Group26(ctx.base, "job", "persistent-storage");
7720
- 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", {
7721
8388
  securityGroupId: securityGroup.id,
7722
8389
  referencedSecurityGroupId: securityGroup.id,
7723
8390
  description: "Allow NFS traffic from jobs",
@@ -7728,7 +8395,7 @@ var jobFeature = defineFeature({
7728
8395
  APP: ctx.appConfig.name
7729
8396
  }
7730
8397
  });
7731
- const fileSystem = new aws27.efs.FileSystem(storageGroup, "file-system", {
8398
+ const fileSystem = new aws28.efs.FileSystem(storageGroup, "file-system", {
7732
8399
  encrypted: true,
7733
8400
  performanceMode: "generalPurpose",
7734
8401
  throughputMode: "elastic",
@@ -7738,7 +8405,7 @@ var jobFeature = defineFeature({
7738
8405
  }
7739
8406
  });
7740
8407
  for (const [index, subnetId] of ctx.shared.get("vpc", "public-subnets").entries()) {
7741
- new aws27.efs.MountTarget(storageGroup, `mount-target-${index + 1}`, {
8408
+ new aws28.efs.MountTarget(storageGroup, `mount-target-${index + 1}`, {
7742
8409
  fileSystemId: fileSystem.id,
7743
8410
  subnetId,
7744
8411
  securityGroups: [securityGroup.id]
@@ -7753,15 +8420,15 @@ var jobFeature = defineFeature({
7753
8420
  const subnets = ctx.shared.get("vpc", "public-subnets");
7754
8421
  ctx.addEnv(
7755
8422
  "JOB_SUBNETS",
7756
- new Output7(new Set(findInputDeps4(subnets)), async (resolve2) => {
7757
- const resolved = await resolveInputs4(subnets);
8423
+ new Output8(new Set(findInputDeps5(subnets)), async (resolve2) => {
8424
+ const resolved = await resolveInputs5(subnets);
7758
8425
  resolve2(JSON.stringify(resolved));
7759
8426
  })
7760
8427
  );
7761
8428
  ctx.addEnv("JOB_SECURITY_GROUP", ctx.shared.get("job", "security-group-id"));
7762
8429
  ctx.addEnv("JOB_PAYLOAD_BUCKET", ctx.shared.get("job", "bucket-name"));
7763
8430
  for (const [id, props] of jobs) {
7764
- const group = new Group26(ctx.stack, "job", id);
8431
+ const group = new Group27(ctx.stack, "job", id);
7765
8432
  createFargateJob(group, ctx, "job", id, props);
7766
8433
  }
7767
8434
  ctx.addStackPermission({
@@ -7794,77 +8461,25 @@ var jobFeature = defineFeature({
7794
8461
  });
7795
8462
 
7796
8463
  // src/feature/instance/index.ts
7797
- 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";
7798
8465
  import { kibibytes as kibibytes2, toBytes as toBytes2 } from "@awsless/size";
7799
- import { Group as Group28 } from "@terraforge/core";
7800
- import { aws as aws29 } from "@terraforge/aws";
8466
+ import { Group as Group29 } from "@terraforge/core";
8467
+ import { aws as aws30 } from "@terraforge/aws";
7801
8468
  import { constantCase as constantCase14 } from "change-case";
7802
8469
 
7803
8470
  // src/feature/instance/util.ts
7804
- import { toDays as toDays11, toSeconds as toSeconds10 } from "@awsless/duration";
7805
- 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";
7806
8473
  import { toMebibytes as toMebibytes6 } from "@awsless/size";
7807
8474
  import { generateFileHash as generateFileHash3 } from "@awsless/ts-file-cache";
7808
- import { aws as aws28 } from "@terraforge/aws";
7809
- import { Group as Group27, Output as Output8, findInputDeps as findInputDeps5, resolveInputs as resolveInputs5 } from "@terraforge/core";
7810
- import { constantCase as constantCase13, pascalCase as pascalCase4 } from "change-case";
7811
- 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";
7812
8479
  import deepmerge6 from "deepmerge";
7813
- import { join as join19 } from "path";
7814
-
7815
- // src/feature/instance/build/executable.ts
7816
- import { createHash as createHash5 } from "crypto";
7817
- import { readFile as readFile5 } from "fs/promises";
7818
- import { join as join18 } from "path";
7819
- var buildExecutable = async (input, outputPath, architecture) => {
7820
- const filePath = join18(outputPath, "program");
7821
- const target = architecture === "x86_64" ? "bun-linux-x64" : "bun-linux-arm64";
7822
- let result;
7823
- try {
7824
- result = await Bun.build({
7825
- entrypoints: [input],
7826
- compile: {
7827
- target,
7828
- outfile: filePath
7829
- },
7830
- target: "bun",
7831
- loader: {
7832
- ".md": "text",
7833
- ".txt": "text",
7834
- ".html": "text",
7835
- ".css": "text",
7836
- ".yaml": "text",
7837
- ".yml": "text",
7838
- ".xml": "text",
7839
- ".csv": "text",
7840
- ".svg": "text",
7841
- ".png": "file",
7842
- ".jpg": "file",
7843
- ".jpeg": "file",
7844
- ".gif": "file",
7845
- ".webp": "file",
7846
- ".wasm": "file"
7847
- }
7848
- });
7849
- } catch (error) {
7850
- throw new ExpectedError(
7851
- `Executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
7852
- );
7853
- }
7854
- if (!result.success) {
7855
- throw new ExpectedError(`Executable build failed:
7856
- ${result.logs?.map((log35) => log35.message).join("\n")}`);
7857
- }
7858
- const file = await readFile5(filePath);
7859
- return {
7860
- hash: createHash5("sha1").update(file).update("x86_64").digest("hex"),
7861
- file
7862
- };
7863
- };
7864
-
7865
- // src/feature/instance/util.ts
8480
+ import { join as join21 } from "path";
7866
8481
  var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7867
- const group = new Group27(parentGroup, "instance", ns);
8482
+ const group = new Group28(parentGroup, "instance", ns);
7868
8483
  const name = formatLocalResourceName({
7869
8484
  appName: ctx.app.name,
7870
8485
  stackName: ctx.stack.name,
@@ -7890,13 +8505,13 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7890
8505
  };
7891
8506
  });
7892
8507
  });
7893
- const code = new aws28.s3.BucketObject(group, "code", {
8508
+ const code = new aws29.s3.BucketObject(group, "code", {
7894
8509
  bucket: ctx.shared.get("instance", "bucket-name"),
7895
8510
  key: name,
7896
8511
  source: relativePath(getBuildPath("instance", name, "program")),
7897
8512
  sourceHash: $file(getBuildPath("instance", name, "HASH"))
7898
8513
  });
7899
- const executionRole = new aws28.iam.Role(group, "execution-role", {
8514
+ const executionRole = new aws29.iam.Role(group, "execution-role", {
7900
8515
  name: shortId(`${shortName}:execution-role`),
7901
8516
  description: name,
7902
8517
  assumeRolePolicy: JSON.stringify({
@@ -7913,7 +8528,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7913
8528
  }),
7914
8529
  managedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
7915
8530
  });
7916
- const role = new aws28.iam.Role(
8531
+ const role = new aws29.iam.Role(
7917
8532
  group,
7918
8533
  "task-role",
7919
8534
  {
@@ -7939,7 +8554,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7939
8554
  Version: "2012-10-17",
7940
8555
  Statement: [
7941
8556
  {
7942
- Effect: pascalCase4("allow"),
8557
+ Effect: pascalCase5("allow"),
7943
8558
  Action: ["s3:getObject", "s3:HeadObject"],
7944
8559
  Resource: `arn:aws:s3:::${bucket}/${key}`
7945
8560
  }
@@ -7955,16 +8570,16 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7955
8570
  );
7956
8571
  const statements = [];
7957
8572
  const statementDeps = /* @__PURE__ */ new Set();
7958
- const policy = new aws28.iam.RolePolicy(group, "policy", {
8573
+ const policy = new aws29.iam.RolePolicy(group, "policy", {
7959
8574
  role: role.name,
7960
8575
  name: "task-policy",
7961
- policy: new Output8(statementDeps, async (resolve2) => {
7962
- const list3 = await resolveInputs5(statements);
8576
+ policy: new Output9(statementDeps, async (resolve2) => {
8577
+ const list3 = await resolveInputs6(statements);
7963
8578
  resolve2(
7964
8579
  JSON.stringify({
7965
8580
  Version: "2012-10-17",
7966
8581
  Statement: list3.map((statement) => ({
7967
- Effect: pascalCase4(statement.effect ?? "allow"),
8582
+ Effect: pascalCase5(statement.effect ?? "allow"),
7968
8583
  Action: statement.actions,
7969
8584
  Resource: statement.resources
7970
8585
  }))
@@ -7974,7 +8589,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7974
8589
  });
7975
8590
  const addPermission = (...permissions) => {
7976
8591
  statements.push(...permissions);
7977
- for (const dep of findInputDeps5(permissions)) {
8592
+ for (const dep of findInputDeps6(permissions)) {
7978
8593
  statementDeps.add(dep);
7979
8594
  }
7980
8595
  };
@@ -7983,13 +8598,13 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7983
8598
  });
7984
8599
  let logGroup;
7985
8600
  if (props.log.retention && props.log.retention.value > 0n) {
7986
- logGroup = new aws28.cloudwatch.LogGroup(group, "log", {
8601
+ logGroup = new aws29.cloudwatch.LogGroup(group, "log", {
7987
8602
  name: `/aws/ecs/${name}`,
7988
8603
  // name: `/aws/lambda/${name}`,
7989
- retentionInDays: toDays11(props.log.retention)
8604
+ retentionInDays: toDays12(props.log.retention)
7990
8605
  });
7991
8606
  if (ctx.shared.has("on-error-log", "subscriber-arn")) {
7992
- new aws28.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
8607
+ new aws29.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
7993
8608
  name: "error-log-subscription",
7994
8609
  destinationArn: ctx.shared.get("on-error-log", "subscriber-arn"),
7995
8610
  logGroupName: logGroup.name,
@@ -8004,7 +8619,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8004
8619
  };
8005
8620
  const variables = {};
8006
8621
  const variableDeps = /* @__PURE__ */ new Set();
8007
- const task2 = new aws28.ecs.TaskDefinition(
8622
+ const task2 = new aws29.ecs.TaskDefinition(
8008
8623
  group,
8009
8624
  "task",
8010
8625
  {
@@ -8020,9 +8635,9 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8020
8635
  operatingSystemFamily: "LINUX"
8021
8636
  },
8022
8637
  trackLatest: true,
8023
- containerDefinitions: new Output8(variableDeps, async (resolve2) => {
8024
- const data = await resolveInputs5(variables);
8025
- const { s3Bucket, s3Key } = await resolveInputs5({
8638
+ containerDefinitions: new Output9(variableDeps, async (resolve2) => {
8639
+ const data = await resolveInputs6(variables);
8640
+ const { s3Bucket, s3Key } = await resolveInputs6({
8026
8641
  s3Bucket: code.bucket,
8027
8642
  s3Key: code.key
8028
8643
  });
@@ -8077,12 +8692,12 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8077
8692
  healthCheck: props.healthCheck ? {
8078
8693
  command: [
8079
8694
  "CMD-SHELL",
8080
- `curl -f http://${join19("localhost", props.healthCheck.path)} || exit 1`
8695
+ `curl -f http://${join21("localhost", props.healthCheck.path)} || exit 1`
8081
8696
  ],
8082
- interval: toSeconds10(props.healthCheck.interval),
8697
+ interval: toSeconds9(props.healthCheck.interval),
8083
8698
  retries: props.healthCheck.retries,
8084
- startPeriod: toSeconds10(props.healthCheck.startPeriod),
8085
- timeout: toSeconds10(props.healthCheck.timeout)
8699
+ startPeriod: toSeconds9(props.healthCheck.startPeriod),
8700
+ timeout: toSeconds9(props.healthCheck.timeout)
8086
8701
  } : void 0
8087
8702
  }
8088
8703
  ])
@@ -8102,14 +8717,14 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8102
8717
  dependsOn: [code]
8103
8718
  }
8104
8719
  );
8105
- const securityGroup = new aws28.security.Group(group, "security-group", {
8720
+ const securityGroup = new aws29.security.Group(group, "security-group", {
8106
8721
  name,
8107
8722
  description: "Security group for the instance",
8108
8723
  vpcId: ctx.shared.get("vpc", "id"),
8109
8724
  revokeRulesOnDelete: true,
8110
8725
  tags
8111
8726
  });
8112
- new aws28.vpc.SecurityGroupEgressRule(group, "egress-rule", {
8727
+ new aws29.vpc.SecurityGroupEgressRule(group, "egress-rule", {
8113
8728
  securityGroupId: securityGroup.id,
8114
8729
  description: `Allow all outbound traffic from the ${name} instance`,
8115
8730
  ipProtocol: "-1",
@@ -8118,7 +8733,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8118
8733
  });
8119
8734
  const clusterName = ctx.shared.get("instance", "cluster-name");
8120
8735
  const clusterArn = ctx.shared.get("instance", "cluster-arn");
8121
- const service = new aws28.ecs.Service(
8736
+ const service = new aws29.ecs.Service(
8122
8737
  group,
8123
8738
  "service",
8124
8739
  {
@@ -8154,7 +8769,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8154
8769
  replaceOnChanges: ["cluster"]
8155
8770
  }
8156
8771
  );
8157
- new aws28.appautoscaling.Target(
8772
+ new aws29.appautoscaling.Target(
8158
8773
  group,
8159
8774
  "autoscaling-target",
8160
8775
  {
@@ -8173,7 +8788,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8173
8788
  );
8174
8789
  ctx.onEnv((name2, value) => {
8175
8790
  variables[name2] = value;
8176
- for (const dep of findInputDeps5([value])) {
8791
+ for (const dep of findInputDeps6([value])) {
8177
8792
  variableDeps.add(dep);
8178
8793
  }
8179
8794
  });
@@ -8182,7 +8797,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8182
8797
  variables.AWS_ACCOUNT_ID = ctx.accountId;
8183
8798
  variables.STACK = ctx.stackConfig.name;
8184
8799
  variables.CODE_HASH = code.sourceHash;
8185
- variables.INSTANCE_CONFIG_HASH = createHash6("sha1").update(stringify2(props)).digest("hex");
8800
+ variables.INSTANCE_CONFIG_HASH = createHash7("sha1").update(stringify3(props)).digest("hex");
8186
8801
  if (props.environment) {
8187
8802
  for (const [key, value] of Object.entries(props.environment)) {
8188
8803
  variables[key] = value;
@@ -8198,7 +8813,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
8198
8813
  };
8199
8814
 
8200
8815
  // src/feature/instance/index.ts
8201
- var typeGenCode11 = `
8816
+ var typeGenCode12 = `
8202
8817
  import { SendMessageOptions } from '@awsless/sqs'
8203
8818
  import type { Mock } from 'vitest'
8204
8819
 
@@ -8237,15 +8852,15 @@ var instanceFeature = defineFeature({
8237
8852
  mocks.addType(stack.name, mock);
8238
8853
  mockResponses.addType(stack.name, mockResponse);
8239
8854
  }
8240
- gen.addCode(typeGenCode11);
8855
+ gen.addCode(typeGenCode12);
8241
8856
  gen.addInterface("InstanceResources", resources2);
8242
8857
  gen.addInterface("InstanceMock", mocks);
8243
8858
  gen.addInterface("InstanceMockResponse", mockResponses);
8244
8859
  await ctx.write("instance.d.ts", gen, true);
8245
8860
  },
8246
8861
  onBefore(ctx) {
8247
- const group = new Group28(ctx.base, "instance", "asset");
8248
- 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", {
8249
8864
  bucket: formatGlobalResourceName({
8250
8865
  appName: ctx.app.name,
8251
8866
  resourceType: "instance",
@@ -8263,8 +8878,8 @@ var instanceFeature = defineFeature({
8263
8878
  if (found.length === 0) {
8264
8879
  return;
8265
8880
  }
8266
- const group = new Group28(ctx.base, "instance", "cluster");
8267
- const cluster = new aws29.ecs.Cluster(
8881
+ const group = new Group29(ctx.base, "instance", "cluster");
8882
+ const cluster = new aws30.ecs.Cluster(
8268
8883
  group,
8269
8884
  "cluster",
8270
8885
  {
@@ -8279,14 +8894,14 @@ var instanceFeature = defineFeature({
8279
8894
  },
8280
8895
  onStack(ctx) {
8281
8896
  for (const [id, props] of Object.entries(ctx.stackConfig.instances ?? {})) {
8282
- const group = new Group28(ctx.stack, "instance", id);
8897
+ const group = new Group29(ctx.stack, "instance", id);
8283
8898
  const task2 = createFargateTask(group, ctx, "instance", id, props);
8284
- const queue2 = new aws29.sqs.Queue(group, "queue", {
8899
+ const queue2 = new aws30.sqs.Queue(group, "queue", {
8285
8900
  name: task2.name,
8286
- visibilityTimeoutSeconds: toSeconds11(seconds10(30)),
8287
- messageRetentionSeconds: toSeconds11(days10(4)),
8901
+ visibilityTimeoutSeconds: toSeconds10(seconds11(30)),
8902
+ messageRetentionSeconds: toSeconds10(days11(4)),
8288
8903
  maxMessageSize: toBytes2(kibibytes2(256)),
8289
- receiveWaitTimeSeconds: toSeconds11(seconds10(20))
8904
+ receiveWaitTimeSeconds: toSeconds10(seconds11(20))
8290
8905
  });
8291
8906
  task2.addPermission({
8292
8907
  actions: [
@@ -8308,11 +8923,11 @@ var instanceFeature = defineFeature({
8308
8923
  });
8309
8924
 
8310
8925
  // src/feature/metric/index.ts
8311
- import { Group as Group29 } from "@terraforge/core";
8312
- import { aws as aws30 } from "@terraforge/aws";
8926
+ import { Group as Group30 } from "@terraforge/core";
8927
+ import { aws as aws31 } from "@terraforge/aws";
8313
8928
  import { kebabCase as kebabCase8, constantCase as constantCase15 } from "change-case";
8314
- import { toSeconds as toSeconds12 } from "@awsless/duration";
8315
- var typeGenCode12 = `
8929
+ import { toSeconds as toSeconds11 } from "@awsless/duration";
8930
+ var typeGenCode13 = `
8316
8931
  import { type PutDataProps, putData, batchPutData } from '@awsless/cloudwatch'
8317
8932
  import { type Duration } from '@awsless/duration'
8318
8933
  import { type Size } from '@awsless/size'
@@ -8360,7 +8975,7 @@ var metricFeature = defineFeature({
8360
8975
  resources2.addType(stack.name, stackResources);
8361
8976
  }
8362
8977
  resources2.addType("batch", "Batch");
8363
- gen.addCode(typeGenCode12);
8978
+ gen.addCode(typeGenCode13);
8364
8979
  gen.addInterface("MetricResources", resources2);
8365
8980
  await ctx.write("metric.d.ts", gen, true);
8366
8981
  },
@@ -8377,16 +8992,16 @@ var metricFeature = defineFeature({
8377
8992
  }
8378
8993
  });
8379
8994
  for (const [id, props] of Object.entries(ctx.stackConfig.metrics ?? {})) {
8380
- const group = new Group29(ctx.stack, "metric", id);
8995
+ const group = new Group30(ctx.stack, "metric", id);
8381
8996
  ctx.addEnv(`METRIC_${constantCase15(id)}`, props.type);
8382
8997
  for (const alarmId in props.alarms ?? []) {
8383
- const alarmGroup = new Group29(group, "alarm", alarmId);
8998
+ const alarmGroup = new Group30(group, "alarm", alarmId);
8384
8999
  const alarmName = kebabCase8(`${id}-${alarmId}`);
8385
9000
  const alarmProps = props.alarms[alarmId];
8386
9001
  let alarmAction;
8387
9002
  let alarmLambda;
8388
9003
  if (Array.isArray(alarmProps.trigger)) {
8389
- const topic = new aws30.sns.Topic(alarmGroup, "alarm-trigger", {
9004
+ const topic = new aws31.sns.Topic(alarmGroup, "alarm-trigger", {
8390
9005
  name: formatLocalResourceName({
8391
9006
  appName: ctx.app.name,
8392
9007
  stackName: ctx.stack.name,
@@ -8396,7 +9011,7 @@ var metricFeature = defineFeature({
8396
9011
  });
8397
9012
  alarmAction = topic.arn;
8398
9013
  for (const email of alarmProps.trigger) {
8399
- new aws30.sns.TopicSubscription(alarmGroup, email, {
9014
+ new aws31.sns.TopicSubscription(alarmGroup, email, {
8400
9015
  topicArn: topic.arn,
8401
9016
  protocol: "email",
8402
9017
  endpoint: email
@@ -8407,7 +9022,7 @@ var metricFeature = defineFeature({
8407
9022
  alarmLambda = lambda;
8408
9023
  alarmAction = lambda.arn;
8409
9024
  }
8410
- const alarm = new aws30.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
9025
+ const alarm = new aws31.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
8411
9026
  namespace,
8412
9027
  metricName: kebabCase8(id),
8413
9028
  alarmName: formatLocalResourceName({
@@ -8419,13 +9034,13 @@ var metricFeature = defineFeature({
8419
9034
  alarmDescription: alarmProps.description,
8420
9035
  statistic: alarmProps.where.stat,
8421
9036
  threshold: alarmProps.where.value,
8422
- period: toSeconds12(alarmProps.period),
9037
+ period: toSeconds11(alarmProps.period),
8423
9038
  evaluationPeriods: alarmProps.minDataPoints,
8424
9039
  comparisonOperator: alarmProps.where.op,
8425
9040
  alarmActions: [alarmAction]
8426
9041
  });
8427
9042
  if (alarmLambda) {
8428
- new aws30.lambda.Permission(alarmGroup, "permission", {
9043
+ new aws31.lambda.Permission(alarmGroup, "permission", {
8429
9044
  action: "lambda:InvokeFunction",
8430
9045
  principal: "lambda.alarms.cloudwatch.amazonaws.com",
8431
9046
  functionName: alarmLambda.functionName,
@@ -8438,9 +9053,9 @@ var metricFeature = defineFeature({
8438
9053
  });
8439
9054
 
8440
9055
  // src/feature/router/index.ts
8441
- import { days as days11, seconds as seconds11, toSeconds as toSeconds13, years } from "@awsless/duration";
8442
- import { Future, Group as Group30 } from "@terraforge/core";
8443
- 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";
8444
9059
  import { camelCase as camelCase9, constantCase as constantCase16 } from "change-case";
8445
9060
 
8446
9061
  // src/feature/router/router-code.ts
@@ -8689,18 +9304,18 @@ async function handler(event) {
8689
9304
  `;
8690
9305
 
8691
9306
  // src/feature/router/index.ts
8692
- import { createHash as createHash7 } from "crypto";
9307
+ import { createHash as createHash8 } from "crypto";
8693
9308
  var routerFeature = defineFeature({
8694
9309
  name: "router",
8695
9310
  onApp(ctx) {
8696
9311
  for (const [id, props] of Object.entries(ctx.appConfig.defaults.router ?? {})) {
8697
- const group = new Group30(ctx.base, "router", id);
9312
+ const group = new Group31(ctx.base, "router", id);
8698
9313
  const name = formatGlobalResourceName({
8699
9314
  appName: ctx.app.name,
8700
9315
  resourceType: "router",
8701
9316
  resourceName: id
8702
9317
  });
8703
- const routeStore = new aws31.cloudfront.KeyValueStore(group, "routes", {
9318
+ const routeStore = new aws32.cloudfront.KeyValueStore(group, "routes", {
8704
9319
  name,
8705
9320
  comment: "Store for routes"
8706
9321
  });
@@ -8730,11 +9345,11 @@ var routerFeature = defineFeature({
8730
9345
  );
8731
9346
  importedRoutes.push(importKeys);
8732
9347
  });
8733
- const cache = new aws31.cloudfront.CachePolicy(group, "cache", {
9348
+ const cache = new aws32.cloudfront.CachePolicy(group, "cache", {
8734
9349
  name,
8735
- minTtl: toSeconds13(seconds11(0)),
8736
- maxTtl: toSeconds13(days11(365)),
8737
- defaultTtl: toSeconds13(days11(0)),
9350
+ minTtl: toSeconds12(seconds12(0)),
9351
+ maxTtl: toSeconds12(days12(365)),
9352
+ defaultTtl: toSeconds12(days12(0)),
8738
9353
  parametersInCacheKeyAndForwardedToOrigin: {
8739
9354
  enableAcceptEncodingBrotli: true,
8740
9355
  enableAcceptEncodingGzip: true,
@@ -8762,7 +9377,7 @@ var routerFeature = defineFeature({
8762
9377
  }
8763
9378
  }
8764
9379
  });
8765
- const originRequest = new aws31.cloudfront.OriginRequestPolicy(group, "request", {
9380
+ const originRequest = new aws32.cloudfront.OriginRequestPolicy(group, "request", {
8766
9381
  name,
8767
9382
  headersConfig: {
8768
9383
  headerBehavior: camelCase9("all-except"),
@@ -8780,11 +9395,11 @@ var routerFeature = defineFeature({
8780
9395
  queryStringBehavior: "all"
8781
9396
  }
8782
9397
  });
8783
- const responseHeaders = new aws31.cloudfront.ResponseHeadersPolicy(group, "response", {
9398
+ const responseHeaders = new aws32.cloudfront.ResponseHeadersPolicy(group, "response", {
8784
9399
  name,
8785
9400
  corsConfig: {
8786
9401
  originOverride: props.cors?.override ?? true,
8787
- accessControlMaxAgeSec: toSeconds13(props.cors?.maxAge ?? years(1)),
9402
+ accessControlMaxAgeSec: toSeconds12(props.cors?.maxAge ?? years(1)),
8788
9403
  accessControlAllowHeaders: { items: props.cors?.headers ?? ["*"] },
8789
9404
  accessControlAllowMethods: { items: props.cors?.methods ?? ["ALL"] },
8790
9405
  accessControlAllowOrigins: { items: props.cors?.origins ?? ["*"] },
@@ -8809,7 +9424,7 @@ var routerFeature = defineFeature({
8809
9424
  strictTransportSecurity: {
8810
9425
  override: true,
8811
9426
  preload: true,
8812
- accessControlMaxAgeSec: toSeconds13(years(1)),
9427
+ accessControlMaxAgeSec: toSeconds12(years(1)),
8813
9428
  includeSubdomains: true
8814
9429
  },
8815
9430
  xssProtection: {
@@ -8819,7 +9434,7 @@ var routerFeature = defineFeature({
8819
9434
  }
8820
9435
  }
8821
9436
  });
8822
- const viewerRequest = new aws31.cloudfront.Function(group, "viewer-request", {
9437
+ const viewerRequest = new aws32.cloudfront.Function(group, "viewer-request", {
8823
9438
  name,
8824
9439
  runtime: `cloudfront-js-2.0`,
8825
9440
  comment: `Viewer Request - ${name}`,
@@ -8841,7 +9456,7 @@ var routerFeature = defineFeature({
8841
9456
  rateBasedStatement: {
8842
9457
  limit: wafSettingsConfig.rateLimiter.limit,
8843
9458
  aggregateKeyType: "IP",
8844
- evaluationWindowSec: toSeconds13(wafSettingsConfig.rateLimiter.window)
9459
+ evaluationWindowSec: toSeconds12(wafSettingsConfig.rateLimiter.window)
8845
9460
  }
8846
9461
  },
8847
9462
  action: {
@@ -8921,7 +9536,7 @@ var routerFeature = defineFeature({
8921
9536
  }
8922
9537
  let waf;
8923
9538
  if (wafRules.length && wafSettingsConfig) {
8924
- waf = new aws31.wafv2.WebAcl(group, "waf", {
9539
+ waf = new aws32.wafv2.WebAcl(group, "waf", {
8925
9540
  name: `${name}-wafv2`,
8926
9541
  scope: "CLOUDFRONT",
8927
9542
  defaultAction: {
@@ -8931,12 +9546,12 @@ var routerFeature = defineFeature({
8931
9546
  rule: wafRules,
8932
9547
  captchaConfig: {
8933
9548
  immunityTimeProperty: {
8934
- immunityTime: toSeconds13(wafSettingsConfig.captchaImmunityTime)
9549
+ immunityTime: toSeconds12(wafSettingsConfig.captchaImmunityTime)
8935
9550
  }
8936
9551
  },
8937
9552
  challengeConfig: {
8938
9553
  immunityTimeProperty: {
8939
- immunityTime: toSeconds13(wafSettingsConfig.challengeImmunityTime)
9554
+ immunityTime: toSeconds12(wafSettingsConfig.challengeImmunityTime)
8940
9555
  }
8941
9556
  },
8942
9557
  visibilityConfig: {
@@ -8946,7 +9561,7 @@ var routerFeature = defineFeature({
8946
9561
  }
8947
9562
  });
8948
9563
  }
8949
- const distribution = new aws31.cloudfront.MultitenantDistribution(group, "distribution", {
9564
+ const distribution = new aws32.cloudfront.MultitenantDistribution(group, "distribution", {
8950
9565
  tags: {
8951
9566
  name
8952
9567
  },
@@ -8992,7 +9607,7 @@ var routerFeature = defineFeature({
8992
9607
  }
8993
9608
  return {
8994
9609
  errorCode: Number(errorCode),
8995
- errorCachingMinTtl: item.minTTL ? toSeconds13(item.minTTL) : void 0,
9610
+ errorCachingMinTtl: item.minTTL ? toSeconds12(item.minTTL) : void 0,
8996
9611
  responseCode: item.statusCode?.toString() ?? errorCode,
8997
9612
  responsePagePath: item.path
8998
9613
  };
@@ -9043,7 +9658,7 @@ var routerFeature = defineFeature({
9043
9658
  version: new Future((resolve2) => {
9044
9659
  $combine(...versions).then((versions2) => {
9045
9660
  const combined = versions2.filter((v) => !!v).sort().join(",");
9046
- const version = createHash7("sha1").update(combined).digest("hex");
9661
+ const version = createHash8("sha1").update(combined).digest("hex");
9047
9662
  resolve2(version);
9048
9663
  });
9049
9664
  })
@@ -9058,10 +9673,10 @@ var routerFeature = defineFeature({
9058
9673
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
9059
9674
  const certificateArn = ctx.shared.entry("domain", `global-certificate-arn`, props.domain);
9060
9675
  const zoneId = ctx.shared.entry("domain", "zone-id", props.domain);
9061
- const connectionGroup = new aws31.cloudfront.ConnectionGroup(group, "connection-group", {
9676
+ const connectionGroup = new aws32.cloudfront.ConnectionGroup(group, "connection-group", {
9062
9677
  name
9063
9678
  });
9064
- new aws31.cloudfront.DistributionTenant(group, `tenant`, {
9679
+ new aws32.cloudfront.DistributionTenant(group, `tenant`, {
9065
9680
  name,
9066
9681
  enabled: true,
9067
9682
  distributionId: distribution.id,
@@ -9069,7 +9684,7 @@ var routerFeature = defineFeature({
9069
9684
  domain: [{ domain: domainName }],
9070
9685
  customizations: [{ certificate: [{ arn: certificateArn }] }]
9071
9686
  });
9072
- new aws31.route53.Record(group, `record`, {
9687
+ new aws32.route53.Record(group, `record`, {
9073
9688
  zoneId,
9074
9689
  type: "A",
9075
9690
  name: domainName,
@@ -10192,21 +10807,21 @@ import wildstring4 from "wildstring";
10192
10807
 
10193
10808
  // src/cli/ui/complex/run-tests.ts
10194
10809
  import { log as log18 } from "@awsless/clui";
10195
- import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
10196
- 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";
10197
10812
  import wildstring3 from "wildstring";
10198
- import { parse as parse4, stringify as stringify3 } from "@awsless/json";
10813
+ import { parse as parse4, stringify as stringify4 } from "@awsless/json";
10199
10814
  import { generateFolderHash, loadWorkspace as loadWorkspace2 } from "@awsless/ts-file-cache";
10200
10815
 
10201
10816
  // src/test/start.ts
10202
- import { dirname as dirname9, join as join20 } from "path";
10203
- import { fileURLToPath as fileURLToPath4 } from "url";
10817
+ import { dirname as dirname11, join as join22 } from "path";
10818
+ import { fileURLToPath as fileURLToPath6 } from "url";
10204
10819
  import { configDefaults } from "vitest/config";
10205
10820
  import { startVitest } from "vitest/node";
10206
10821
  var NullReporter = class {
10207
10822
  };
10208
10823
  var startTest = async (props) => {
10209
- const __dirname5 = dirname9(fileURLToPath4(import.meta.url));
10824
+ const __dirname7 = dirname11(fileURLToPath6(import.meta.url));
10210
10825
  const startTime = process.hrtime.bigint();
10211
10826
  process.noDeprecation = true;
10212
10827
  const vitest = await startVitest(
@@ -10237,7 +10852,7 @@ var startTest = async (props) => {
10237
10852
  // },
10238
10853
  setupFiles: [
10239
10854
  //
10240
- join20(__dirname5, "test-global-setup.js")
10855
+ join22(__dirname7, "test-global-setup.js")
10241
10856
  ]
10242
10857
  // globalSetup: [
10243
10858
  // //
@@ -10431,12 +11046,12 @@ var logTestErrors = (event) => {
10431
11046
  };
10432
11047
  var runTest = async (stack, dir, filters, workspace, opts) => {
10433
11048
  await mkdir4(directories.test, { recursive: true });
10434
- const file = join21(directories.test, `${stack}.json`);
11049
+ const file = join23(directories.test, `${stack}.json`);
10435
11050
  const fingerprint = await generateFolderHash(workspace, dir);
10436
11051
  if (!process.env.NO_CACHE) {
10437
11052
  const exists = await fileExist(file);
10438
11053
  if (exists) {
10439
- const raw = await readFile6(file, { encoding: "utf8" });
11054
+ const raw = await readFile7(file, { encoding: "utf8" });
10440
11055
  const data = parse4(raw);
10441
11056
  if (data.fingerprint === fingerprint) {
10442
11057
  log18.step(
@@ -10481,7 +11096,7 @@ var runTest = async (stack, dir, filters, workspace, opts) => {
10481
11096
  logTestErrors(result);
10482
11097
  await writeFile4(
10483
11098
  file,
10484
- stringify3({
11099
+ stringify4({
10485
11100
  ...result,
10486
11101
  fingerprint
10487
11102
  })
@@ -11116,7 +11731,7 @@ import { log as log25 } from "@awsless/clui";
11116
11731
 
11117
11732
  // src/type-gen/generate.ts
11118
11733
  import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
11119
- 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";
11120
11735
  var generateTypes = async (props) => {
11121
11736
  const files = [];
11122
11737
  await Promise.all(
@@ -11125,12 +11740,12 @@ var generateTypes = async (props) => {
11125
11740
  ...props,
11126
11741
  async write(file, data, include = false) {
11127
11742
  const code = data?.toString("utf8");
11128
- const path = join22(directories.types, file);
11743
+ const path = join24(directories.types, file);
11129
11744
  if (code) {
11130
11745
  if (include) {
11131
11746
  files.push(relative9(directories.root, path));
11132
11747
  }
11133
- await mkdir5(dirname10(path), { recursive: true });
11748
+ await mkdir5(dirname12(path), { recursive: true });
11134
11749
  await writeFile5(path, code);
11135
11750
  }
11136
11751
  }
@@ -11139,7 +11754,7 @@ var generateTypes = async (props) => {
11139
11754
  );
11140
11755
  if (files.length) {
11141
11756
  const code = files.map((file) => `/// <reference path='${file}' />`).join("\n");
11142
- await writeFile5(join22(directories.root, `awsless.d.ts`), code);
11757
+ await writeFile5(join24(directories.root, `awsless.d.ts`), code);
11143
11758
  }
11144
11759
  };
11145
11760
 
@@ -11565,7 +12180,7 @@ var domain = (program2) => {
11565
12180
  // src/cli/command/logs.ts
11566
12181
  import { CloudWatchLogsClient, StartLiveTailCommand } from "@aws-sdk/client-cloudwatch-logs";
11567
12182
  import { log as log30 } from "@awsless/clui";
11568
- import { aws as aws32 } from "@terraforge/aws";
12183
+ import { aws as aws33 } from "@terraforge/aws";
11569
12184
  import chalk6 from "chalk";
11570
12185
  import chunk2 from "chunk";
11571
12186
  import { formatDate } from "date-fns";
@@ -11588,7 +12203,7 @@ var logs = (program2) => {
11588
12203
  for (const stack of app.stacks) {
11589
12204
  if (filters.find((f) => wildstring7.match(f, stack.name))) {
11590
12205
  for (const resource of stack.resources) {
11591
- if (resource instanceof aws32.cloudwatch.LogGroup) {
12206
+ if (resource instanceof aws33.cloudwatch.LogGroup) {
11592
12207
  logGroupArns.push(await resource.arn);
11593
12208
  }
11594
12209
  }