@awsless/awsless 0.0.666 → 0.0.668

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
@@ -943,7 +943,7 @@ var debug = (...parts) => {
943
943
  };
944
944
 
945
945
  // src/config/app.ts
946
- import { z as z29 } from "zod";
946
+ import { z as z30 } from "zod";
947
947
 
948
948
  // src/feature/alert/schema.ts
949
949
  import { kebabCase } from "change-case";
@@ -1175,7 +1175,7 @@ var LogRetentionSchema = DurationSchema.refine(
1175
1175
  (duration) => {
1176
1176
  return validLogRetentionDays.includes(toDays(duration));
1177
1177
  },
1178
- `Invalid log retention. Valid days are: ${validLogRetentionDays.map((days9) => `${days9}`).join(", ")}`
1178
+ `Invalid log retention. Valid days are: ${validLogRetentionDays.map((days10) => `${days10}`).join(", ")}`
1179
1179
  ).describe("The log retention duration.");
1180
1180
  var LogSchema = z15.union([
1181
1181
  z15.boolean().transform((enabled) => ({ retention: enabled ? days(7) : days(0) })),
@@ -1715,13 +1715,11 @@ var RpcSchema = z25.record(
1715
1715
  ).describe("The queries for your global RPC API.")
1716
1716
  ).describe("Define the schema in your stack for your global RPC API.").optional();
1717
1717
 
1718
- // src/feature/instance/schema.ts
1719
- import { days as days4, toDays as toDays2 } from "@awsless/duration";
1718
+ // src/feature/job/schema.ts
1719
+ import { days as days4, hours as hours2, minutes as minutes5, toDays as toDays2 } from "@awsless/duration";
1720
1720
  import { toMebibytes } from "@awsless/size";
1721
1721
  import { z as z26 } from "zod";
1722
- 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(
1723
- "The number of virtual CPU units (vCPU) used by the instance. Valid values: 0.25, 0.5, 1, 2, 4, 8, 16 vCPU."
1724
- );
1722
+ 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.");
1725
1723
  var validMemorySize = [
1726
1724
  // 0.25 vCPU
1727
1725
  512,
@@ -1758,22 +1756,9 @@ var validMemorySize = [
1758
1756
  var MemorySizeSchema2 = SizeSchema.refine(
1759
1757
  (s) => validMemorySize.includes(toMebibytes(s)),
1760
1758
  `Invalid memory size. Allowed sizes: ${validMemorySize.join(", ")} MiB`
1761
- ).describe("The amount of memory (in MiB) used by the instance. Valid memory values depend on the CPU configuration.");
1762
- var HealthCheckSchema = z26.object({
1763
- path: z26.string().describe("The path that the container runs to determine if it is healthy."),
1764
- interval: DurationSchema.describe("The time period in seconds between each health check execution."),
1765
- retries: z26.number().int().min(1).max(10).describe(
1766
- "The number of times to retry a failed health check before the container is considered unhealthy."
1767
- ),
1768
- startPeriod: DurationSchema.describe(
1769
- "The optional grace period to provide containers time to bootstrap before failed health checks count towards the maximum number of retries."
1770
- ),
1771
- timeout: DurationSchema.describe(
1772
- "The time period in seconds to wait for a health check to succeed before it is considered a failure."
1773
- )
1774
- }).describe("The health check command and associated configuration parameters for the container.");
1759
+ ).describe("The amount of memory (in MiB) used by the job. Valid memory values depend on the CPU configuration.");
1775
1760
  var EnvironmentSchema2 = z26.record(z26.string(), z26.string()).optional().describe("Environment variable key-value pairs.");
1776
- var ArchitectureSchema3 = z26.enum(["x86_64", "arm64"]).describe("The instruction set architecture that the instance supports.");
1761
+ var ArchitectureSchema3 = z26.enum(["x86_64", "arm64"]).describe("The instruction set architecture that the job supports.");
1777
1762
  var ActionSchema2 = z26.string();
1778
1763
  var ActionsSchema2 = z26.union([ActionSchema2.transform((v) => [v]), ActionSchema2.array()]);
1779
1764
  var ArnSchema2 = z26.string().startsWith("arn:");
@@ -1785,9 +1770,7 @@ var PermissionSchema2 = z26.object({
1785
1770
  actions: ActionsSchema2,
1786
1771
  resources: ResourcesSchema2
1787
1772
  });
1788
- var PermissionsSchema2 = z26.union([PermissionSchema2.transform((v) => [v]), PermissionSchema2.array()]).describe("Add IAM permissions to your instance.");
1789
- var DescriptionSchema2 = z26.string().describe("A description of the instance.");
1790
- var ImageSchema = z26.string().optional().describe("The URL of the container image to use.");
1773
+ var PermissionsSchema2 = z26.union([PermissionSchema2.transform((v) => [v]), PermissionSchema2.array()]).describe("Add IAM permissions to your job.");
1791
1774
  var validLogRetentionDays2 = [
1792
1775
  ...[1, 3, 5, 7, 14, 30, 60, 90, 120, 150],
1793
1776
  ...[180, 365, 400, 545, 731, 1096, 1827, 2192],
@@ -1800,7 +1783,7 @@ var LogRetentionSchema2 = DurationSchema.refine(
1800
1783
  (duration) => {
1801
1784
  return validLogRetentionDays2.includes(toDays2(duration));
1802
1785
  },
1803
- `Invalid log retention. Valid days are: ${validLogRetentionDays2.map((days9) => `${days9}`).join(", ")}`
1786
+ `Invalid log retention. Valid days are: ${validLogRetentionDays2.map((days10) => `${days10}`).join(", ")}`
1804
1787
  ).describe("The log retention duration.");
1805
1788
  var LogSchema2 = z26.union([
1806
1789
  z26.boolean().transform((enabled) => ({ retention: enabled ? days4(7) : days4(0) })),
@@ -1810,59 +1793,199 @@ var LogSchema2 = z26.union([
1810
1793
  })
1811
1794
  ]).describe("Enable logging to a CloudWatch log group. Providing a duration value will set the log retention time.");
1812
1795
  var FileCodeSchema2 = z26.object({
1813
- file: LocalFileSchema.describe("The file path of the instance code.")
1796
+ file: LocalFileSchema.describe("The file path of the job code.")
1814
1797
  });
1815
1798
  var CodeSchema2 = z26.union([
1816
1799
  LocalFileSchema.transform((file) => ({
1817
1800
  file
1818
1801
  })).pipe(FileCodeSchema2),
1819
1802
  FileCodeSchema2
1820
- ]).describe("Specify the code of your instance.");
1821
- var ISchema = z26.object({
1803
+ ]).describe("Specify the code of your job.");
1804
+ var TimeoutSchema3 = DurationSchema.refine(durationMin(minutes5(1)), "Minimum timeout is 1 minute.").refine(durationMax(hours2(4)), "Maximum timeout is 4 hours.").describe("The maximum time the job is allowed to run before being stopped.");
1805
+ var ImageSchema = z26.string().describe("The URL of the container image to use.");
1806
+ var StartupCommandSchema = z26.union([z26.string().transform((v) => [v]), z26.string().array()]).describe("Optional shell commands to run before the job program starts.");
1807
+ var ASchema = z26.object({
1822
1808
  code: CodeSchema2,
1823
- description: DescriptionSchema2.optional(),
1824
1809
  image: ImageSchema.optional(),
1810
+ startupCommand: StartupCommandSchema.optional(),
1825
1811
  log: LogSchema2.optional(),
1826
1812
  cpu: CpuSchema.optional(),
1827
1813
  memorySize: MemorySizeSchema2.optional(),
1828
1814
  architecture: ArchitectureSchema3.optional(),
1829
1815
  environment: EnvironmentSchema2.optional(),
1830
1816
  permissions: PermissionsSchema2.optional(),
1831
- healthCheck: HealthCheckSchema.optional()
1832
- // restartPolicy: RestartPolicySchema.optional(),
1817
+ timeout: TimeoutSchema3.default("30 minutes").describe("The maximum time the job is allowed to run before being stopped. Default: 30 minutes.")
1833
1818
  });
1834
- var InstanceSchema = z26.union([
1819
+ var JobSchema = z26.union([
1835
1820
  LocalFileSchema.transform((code) => ({
1836
1821
  code
1837
- })).pipe(ISchema),
1838
- ISchema
1822
+ })).pipe(ASchema),
1823
+ ASchema
1839
1824
  ]);
1840
- var InstancesSchema = z26.record(ResourceIdSchema, InstanceSchema).optional().describe("Define the instances in your stack.");
1841
- var InstanceDefaultSchema = z26.object({
1825
+ var JobsSchema = z26.record(ResourceIdSchema, JobSchema).optional().describe("Define the jobs in your stack.");
1826
+ var JobDefaultSchema = z26.object({
1842
1827
  image: ImageSchema.optional(),
1843
1828
  cpu: CpuSchema.default(0.25),
1844
1829
  memorySize: MemorySizeSchema2.default("512 MB"),
1845
1830
  architecture: ArchitectureSchema3.default("arm64"),
1846
1831
  environment: EnvironmentSchema2.optional(),
1847
1832
  permissions: PermissionsSchema2.optional(),
1848
- healthCheck: HealthCheckSchema.optional(),
1849
- // restartPolicy: RestartPolicySchema.default({ enabled: true }),
1833
+ timeout: TimeoutSchema3.optional(),
1850
1834
  log: LogSchema2.default(true).transform((log35) => ({
1851
1835
  retention: log35.retention ?? days4(7)
1852
1836
  }))
1853
1837
  }).default({});
1854
1838
 
1839
+ // src/feature/instance/schema.ts
1840
+ import { days as days5, toDays as toDays3 } from "@awsless/duration";
1841
+ import { toMebibytes as toMebibytes2 } from "@awsless/size";
1842
+ import { z as z27 } from "zod";
1843
+ 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(
1844
+ "The number of virtual CPU units (vCPU) used by the instance. Valid values: 0.25, 0.5, 1, 2, 4, 8, 16 vCPU."
1845
+ );
1846
+ var validMemorySize2 = [
1847
+ // 0.25 vCPU
1848
+ 512,
1849
+ 1024,
1850
+ 2048,
1851
+ // 0.5 vCPU
1852
+ 1024,
1853
+ 2048,
1854
+ 3072,
1855
+ 4096,
1856
+ // 1 vCPU
1857
+ 2048,
1858
+ 3072,
1859
+ 4096,
1860
+ 5120,
1861
+ 6144,
1862
+ 7168,
1863
+ 8192,
1864
+ // 2 vCPU
1865
+ 4096,
1866
+ 5120,
1867
+ 6144,
1868
+ 7168,
1869
+ 8192,
1870
+ 9216,
1871
+ 10240,
1872
+ 11264,
1873
+ 12288,
1874
+ 13312,
1875
+ 14336,
1876
+ 15360,
1877
+ 16384
1878
+ ];
1879
+ var MemorySizeSchema3 = SizeSchema.refine(
1880
+ (s) => validMemorySize2.includes(toMebibytes2(s)),
1881
+ `Invalid memory size. Allowed sizes: ${validMemorySize2.join(", ")} MiB`
1882
+ ).describe("The amount of memory (in MiB) used by the instance. Valid memory values depend on the CPU configuration.");
1883
+ var HealthCheckSchema = z27.object({
1884
+ path: z27.string().describe("The path that the container runs to determine if it is healthy."),
1885
+ interval: DurationSchema.describe("The time period in seconds between each health check execution."),
1886
+ retries: z27.number().int().min(1).max(10).describe(
1887
+ "The number of times to retry a failed health check before the container is considered unhealthy."
1888
+ ),
1889
+ startPeriod: DurationSchema.describe(
1890
+ "The optional grace period to provide containers time to bootstrap before failed health checks count towards the maximum number of retries."
1891
+ ),
1892
+ timeout: DurationSchema.describe(
1893
+ "The time period in seconds to wait for a health check to succeed before it is considered a failure."
1894
+ )
1895
+ }).describe("The health check command and associated configuration parameters for the container.");
1896
+ var EnvironmentSchema3 = z27.record(z27.string(), z27.string()).optional().describe("Environment variable key-value pairs.");
1897
+ var ArchitectureSchema4 = z27.enum(["x86_64", "arm64"]).describe("The instruction set architecture that the instance supports.");
1898
+ var ActionSchema3 = z27.string();
1899
+ var ActionsSchema3 = z27.union([ActionSchema3.transform((v) => [v]), ActionSchema3.array()]);
1900
+ var ArnSchema3 = z27.string().startsWith("arn:");
1901
+ var WildcardSchema3 = z27.literal("*");
1902
+ var ResourceSchema3 = z27.union([ArnSchema3, WildcardSchema3]);
1903
+ var ResourcesSchema3 = z27.union([ResourceSchema3.transform((v) => [v]), ResourceSchema3.array()]);
1904
+ var PermissionSchema3 = z27.object({
1905
+ effect: z27.enum(["allow", "deny"]).default("allow"),
1906
+ actions: ActionsSchema3,
1907
+ resources: ResourcesSchema3
1908
+ });
1909
+ var PermissionsSchema3 = z27.union([PermissionSchema3.transform((v) => [v]), PermissionSchema3.array()]).describe("Add IAM permissions to your instance.");
1910
+ var DescriptionSchema2 = z27.string().describe("A description of the instance.");
1911
+ var ImageSchema2 = z27.string().optional().describe("The URL of the container image to use.");
1912
+ var validLogRetentionDays3 = [
1913
+ ...[1, 3, 5, 7, 14, 30, 60, 90, 120, 150],
1914
+ ...[180, 365, 400, 545, 731, 1096, 1827, 2192],
1915
+ ...[2557, 2922, 3288, 3653]
1916
+ ];
1917
+ var LogRetentionSchema3 = DurationSchema.refine(
1918
+ durationMin(days5(0)),
1919
+ "Minimum log retention is 0 day, which will disable logging."
1920
+ ).refine(
1921
+ (duration) => {
1922
+ return validLogRetentionDays3.includes(toDays3(duration));
1923
+ },
1924
+ `Invalid log retention. Valid days are: ${validLogRetentionDays3.map((days10) => `${days10}`).join(", ")}`
1925
+ ).describe("The log retention duration.");
1926
+ var LogSchema3 = z27.union([
1927
+ z27.boolean().transform((enabled) => ({ retention: enabled ? days5(7) : days5(0) })),
1928
+ LogRetentionSchema3.transform((retention) => ({ retention })),
1929
+ z27.object({
1930
+ retention: LogRetentionSchema3.optional()
1931
+ })
1932
+ ]).describe("Enable logging to a CloudWatch log group. Providing a duration value will set the log retention time.");
1933
+ var FileCodeSchema3 = z27.object({
1934
+ file: LocalFileSchema.describe("The file path of the instance code.")
1935
+ });
1936
+ var CodeSchema3 = z27.union([
1937
+ LocalFileSchema.transform((file) => ({
1938
+ file
1939
+ })).pipe(FileCodeSchema3),
1940
+ FileCodeSchema3
1941
+ ]).describe("Specify the code of your instance.");
1942
+ var StartupCommandSchema2 = z27.union([z27.string().transform((v) => [v]), z27.string().array()]).describe("Optional shell commands to run before the instance program starts.");
1943
+ var ISchema = z27.object({
1944
+ code: CodeSchema3,
1945
+ description: DescriptionSchema2.optional(),
1946
+ image: ImageSchema2.optional(),
1947
+ startupCommand: StartupCommandSchema2.optional(),
1948
+ log: LogSchema3.optional(),
1949
+ cpu: CpuSchema2.optional(),
1950
+ memorySize: MemorySizeSchema3.optional(),
1951
+ architecture: ArchitectureSchema4.optional(),
1952
+ environment: EnvironmentSchema3.optional(),
1953
+ permissions: PermissionsSchema3.optional(),
1954
+ healthCheck: HealthCheckSchema.optional()
1955
+ // restartPolicy: RestartPolicySchema.optional(),
1956
+ });
1957
+ var InstanceSchema = z27.union([
1958
+ LocalFileSchema.transform((code) => ({
1959
+ code
1960
+ })).pipe(ISchema),
1961
+ ISchema
1962
+ ]);
1963
+ var InstancesSchema = z27.record(ResourceIdSchema, InstanceSchema).optional().describe("Define the instances in your stack.");
1964
+ var InstanceDefaultSchema = z27.object({
1965
+ image: ImageSchema2.optional(),
1966
+ cpu: CpuSchema2.default(0.25),
1967
+ memorySize: MemorySizeSchema3.default("512 MB"),
1968
+ architecture: ArchitectureSchema4.default("arm64"),
1969
+ environment: EnvironmentSchema3.optional(),
1970
+ permissions: PermissionsSchema3.optional(),
1971
+ healthCheck: HealthCheckSchema.optional(),
1972
+ // restartPolicy: RestartPolicySchema.default({ enabled: true }),
1973
+ log: LogSchema3.default(true).transform((log35) => ({
1974
+ retention: log35.retention ?? days5(7)
1975
+ }))
1976
+ }).default({});
1977
+
1855
1978
  // src/feature/topic/schema.ts
1856
1979
  import { kebabCase as kebabCase3 } from "change-case";
1857
- import { z as z27 } from "zod";
1858
- var TopicNameSchema = z27.string().min(3).max(256).regex(/^[a-z0-9\-]+$/i, "Invalid topic name").transform((value) => kebabCase3(value)).describe("Define event topic name.");
1859
- var TopicsDefaultSchema = z27.array(TopicNameSchema).refine((topics) => {
1980
+ import { z as z28 } from "zod";
1981
+ var TopicNameSchema = z28.string().min(3).max(256).regex(/^[a-z0-9\-]+$/i, "Invalid topic name").transform((value) => kebabCase3(value)).describe("Define event topic name.");
1982
+ var TopicsDefaultSchema = z28.array(TopicNameSchema).refine((topics) => {
1860
1983
  return topics.length === new Set(topics).size;
1861
1984
  }, "Must be a list of unique topic names").optional().describe("Define the event topics for your app.");
1862
- var SubscribersSchema = z27.record(TopicNameSchema, TaskSchema).optional().describe("Define the event topics to subscribe too in your stack.");
1985
+ var SubscribersSchema = z28.record(TopicNameSchema, TaskSchema).optional().describe("Define the event topics to subscribe too in your stack.");
1863
1986
 
1864
1987
  // src/config/schema/region.ts
1865
- import { z as z28 } from "zod";
1988
+ import { z as z29 } from "zod";
1866
1989
  var US = ["us-east-2", "us-east-1", "us-west-1", "us-west-2"];
1867
1990
  var AF = ["af-south-1"];
1868
1991
  var AP = [
@@ -1891,16 +2014,16 @@ var EU = [
1891
2014
  var ME = ["me-south-1", "me-central-1"];
1892
2015
  var SA = ["sa-east-1"];
1893
2016
  var regions = [...US, ...AF, ...AP, ...CA, ...EU, ...ME, ...SA];
1894
- var RegionSchema = z28.enum(regions);
2017
+ var RegionSchema = z29.enum(regions);
1895
2018
 
1896
2019
  // src/config/app.ts
1897
- var AppSchema = z29.object({
1898
- $schema: z29.string().optional(),
2020
+ var AppSchema = z30.object({
2021
+ $schema: z30.string().optional(),
1899
2022
  name: ResourceIdSchema.describe("App name."),
1900
2023
  region: RegionSchema.describe("The AWS region to deploy to."),
1901
- profile: z29.string().describe("The AWS profile to deploy to."),
1902
- protect: z29.boolean().default(false).describe("Protect your app & stacks from being deleted."),
1903
- removal: z29.enum(["remove", "retain"]).default("remove").describe(
2024
+ profile: z30.string().describe("The AWS profile to deploy to."),
2025
+ protect: z30.boolean().default(false).describe("Protect your app & stacks from being deleted."),
2026
+ removal: z30.enum(["remove", "retain"]).default("remove").describe(
1904
2027
  [
1905
2028
  "Configure how your resources are handled when they have to be removed.",
1906
2029
  "",
@@ -1914,13 +2037,14 @@ var AppSchema = z29.object({
1914
2037
  // .default('prod')
1915
2038
  // .describe('The deployment stage.'),
1916
2039
  // onFailure: OnFailureSchema,
1917
- defaults: z29.object({
2040
+ defaults: z30.object({
1918
2041
  onFailure: OnFailureDefaultSchema,
1919
2042
  onErrorLog: OnErrorLogDefaultSchema,
1920
2043
  auth: AuthDefaultSchema,
1921
2044
  domains: DomainsDefaultSchema,
1922
2045
  function: FunctionDefaultSchema,
1923
2046
  instance: InstanceDefaultSchema,
2047
+ job: JobDefaultSchema,
1924
2048
  queue: QueueDefaultSchema,
1925
2049
  // graphql: GraphQLDefaultSchema,
1926
2050
  // http: HttpDefaultSchema,
@@ -1938,11 +2062,11 @@ var AppSchema = z29.object({
1938
2062
  });
1939
2063
 
1940
2064
  // src/config/stack.ts
1941
- import { z as z44 } from "zod";
2065
+ import { z as z45 } from "zod";
1942
2066
 
1943
2067
  // src/feature/cache/schema.ts
1944
2068
  import { gibibytes as gibibytes2 } from "@awsless/size";
1945
- import { z as z30 } from "zod";
2069
+ import { z as z31 } from "zod";
1946
2070
  var StorageSchema = SizeSchema.refine(sizeMin(gibibytes2(1)), "Minimum storage size is 1 GB").refine(
1947
2071
  sizeMax(gibibytes2(5e3)),
1948
2072
  "Maximum storage size is 5000 GB"
@@ -1953,31 +2077,31 @@ var MinimumStorageSchema = StorageSchema.describe(
1953
2077
  var MaximumStorageSchema = StorageSchema.describe(
1954
2078
  "The upper limit for data storage the cache is set to use. You can specify a size value from 1 GB to 5000 GB."
1955
2079
  );
1956
- var EcpuSchema = z30.number().int().min(1e3).max(15e6);
2080
+ var EcpuSchema = z31.number().int().min(1e3).max(15e6);
1957
2081
  var MinimumEcpuSchema = EcpuSchema.describe(
1958
2082
  "The minimum number of ECPUs the cache can consume per second. You can specify a integer from 1,000 to 15,000,000."
1959
2083
  );
1960
2084
  var MaximumEcpuSchema = EcpuSchema.describe(
1961
2085
  "The maximum number of ECPUs the cache can consume per second. You can specify a integer from 1,000 to 15,000,000."
1962
2086
  );
1963
- var CachesSchema = z30.record(
2087
+ var CachesSchema = z31.record(
1964
2088
  ResourceIdSchema,
1965
- z30.object({
2089
+ z31.object({
1966
2090
  minStorage: MinimumStorageSchema.optional(),
1967
2091
  maxStorage: MaximumStorageSchema.optional(),
1968
2092
  minECPU: MinimumEcpuSchema.optional(),
1969
2093
  maxECPU: MaximumEcpuSchema.optional(),
1970
- snapshotRetentionLimit: z30.number().int().positive().default(1)
2094
+ snapshotRetentionLimit: z31.number().int().positive().default(1)
1971
2095
  })
1972
2096
  ).optional().describe("Define the caches in your stack. For access to the cache put your functions inside the global VPC.");
1973
2097
 
1974
2098
  // src/feature/command/schema.ts
1975
- import { z as z31 } from "zod";
1976
- var CommandSchema = z31.union([
1977
- z31.object({
2099
+ import { z as z32 } from "zod";
2100
+ var CommandSchema = z32.union([
2101
+ z32.object({
1978
2102
  file: LocalFileSchema,
1979
- handler: z31.string().default("default").describe("The name of the handler that needs to run"),
1980
- description: z31.string().optional().describe("A description of the command")
2103
+ handler: z32.string().default("default").describe("The name of the handler that needs to run"),
2104
+ description: z32.string().optional().describe("A description of the command")
1981
2105
  // options: z.record(ResourceIdSchema, OptionSchema).optional(),
1982
2106
  // arguments: z.record(ResourceIdSchema, ArgumentSchema).optional(),
1983
2107
  }),
@@ -1987,22 +2111,22 @@ var CommandSchema = z31.union([
1987
2111
  description: void 0
1988
2112
  }))
1989
2113
  ]);
1990
- var CommandsSchema = z31.record(ResourceIdSchema, CommandSchema).optional().describe("Define the custom commands for your stack.");
2114
+ var CommandsSchema = z32.record(ResourceIdSchema, CommandSchema).optional().describe("Define the custom commands for your stack.");
1991
2115
 
1992
2116
  // src/feature/config/schema.ts
1993
- import { z as z32 } from "zod";
1994
- var ConfigNameSchema = z32.string().regex(/[a-z0-9\-]/g, "Invalid config name");
1995
- var ConfigsSchema = z32.array(ConfigNameSchema).optional().describe("Define the config values for your stack.");
2117
+ import { z as z33 } from "zod";
2118
+ var ConfigNameSchema = z33.string().regex(/[a-z0-9\-]/g, "Invalid config name");
2119
+ var ConfigsSchema = z33.array(ConfigNameSchema).optional().describe("Define the config values for your stack.");
1996
2120
 
1997
2121
  // src/feature/cron/schema/index.ts
1998
- import { z as z34 } from "zod";
2122
+ import { z as z35 } from "zod";
1999
2123
 
2000
2124
  // src/feature/cron/schema/schedule.ts
2001
- import { z as z33 } from "zod";
2125
+ import { z as z34 } from "zod";
2002
2126
  import { awsCronExpressionValidator } from "aws-cron-expression-validator";
2003
- var RateExpressionSchema = z33.custom(
2127
+ var RateExpressionSchema = z34.custom(
2004
2128
  (value) => {
2005
- return z33.string().regex(/^[0-9]+ (seconds?|minutes?|hours?|days?)$/).refine((rate) => {
2129
+ return z34.string().regex(/^[0-9]+ (seconds?|minutes?|hours?|days?)$/).refine((rate) => {
2006
2130
  const [str] = rate.split(" ");
2007
2131
  const number = parseInt(str);
2008
2132
  return number > 0;
@@ -2018,9 +2142,9 @@ var RateExpressionSchema = z33.custom(
2018
2142
  }
2019
2143
  return `rate(${rate})`;
2020
2144
  });
2021
- var CronExpressionSchema = z33.custom(
2145
+ var CronExpressionSchema = z34.custom(
2022
2146
  (value) => {
2023
- return z33.string().safeParse(value).success;
2147
+ return z34.string().safeParse(value).success;
2024
2148
  },
2025
2149
  { message: "Invalid cron expression" }
2026
2150
  ).superRefine((value, ctx) => {
@@ -2029,12 +2153,12 @@ var CronExpressionSchema = z33.custom(
2029
2153
  } catch (error) {
2030
2154
  if (error instanceof Error) {
2031
2155
  ctx.addIssue({
2032
- code: z33.ZodIssueCode.custom,
2156
+ code: z34.ZodIssueCode.custom,
2033
2157
  message: `Invalid cron expression: ${error.message}`
2034
2158
  });
2035
2159
  } else {
2036
2160
  ctx.addIssue({
2037
- code: z33.ZodIssueCode.custom,
2161
+ code: z34.ZodIssueCode.custom,
2038
2162
  message: "Invalid cron expression"
2039
2163
  });
2040
2164
  }
@@ -2045,32 +2169,32 @@ var CronExpressionSchema = z33.custom(
2045
2169
  var ScheduleExpressionSchema = RateExpressionSchema.or(CronExpressionSchema);
2046
2170
 
2047
2171
  // src/feature/cron/schema/index.ts
2048
- var RetryAttemptsSchema4 = z34.number().int().min(0).max(2).describe(
2172
+ var RetryAttemptsSchema4 = z35.number().int().min(0).max(2).describe(
2049
2173
  "The maximum number of times to retry when the function returns an error. You can specify a number from 0 to 2."
2050
2174
  );
2051
- var CronsSchema = z34.record(
2175
+ var CronsSchema = z35.record(
2052
2176
  ResourceIdSchema,
2053
- z34.object({
2054
- enabled: z34.boolean().default(true).describe("If the cron is enabled."),
2177
+ z35.object({
2178
+ enabled: z35.boolean().default(true).describe("If the cron is enabled."),
2055
2179
  consumer: FunctionSchema.describe("The consuming lambda function properties."),
2056
2180
  schedule: ScheduleExpressionSchema.describe(
2057
2181
  'The scheduling expression.\n\nexample: "0 20 * * ? *"\nexample: "5 minutes"'
2058
2182
  ),
2059
- payload: z34.unknown().optional().describe("The JSON payload that will be passed to the consumer."),
2183
+ payload: z35.unknown().optional().describe("The JSON payload that will be passed to the consumer."),
2060
2184
  retryAttempts: RetryAttemptsSchema4.default(2)
2061
2185
  })
2062
2186
  ).optional().describe(`Define the cron jobs in your stack.`);
2063
2187
 
2064
2188
  // src/feature/search/schema.ts
2065
2189
  import { gibibytes as gibibytes3 } from "@awsless/size";
2066
- import { z as z35 } from "zod";
2067
- var VersionSchema = z35.union([
2190
+ import { z as z36 } from "zod";
2191
+ var VersionSchema = z36.union([
2068
2192
  //
2069
- z35.enum(["2.13", "2.11", "2.9", "2.7", "2.5", "2.3", "1.3"]),
2070
- z35.string()
2193
+ z36.enum(["2.13", "2.11", "2.9", "2.7", "2.5", "2.3", "1.3"]),
2194
+ z36.string()
2071
2195
  ]).describe("Specify the OpenSearch engine version.");
2072
- var TypeSchema = z35.union([
2073
- z35.enum([
2196
+ var TypeSchema = z36.union([
2197
+ z36.enum([
2074
2198
  "t3.small",
2075
2199
  "t3.medium",
2076
2200
  "m3.medium",
@@ -2144,13 +2268,13 @@ var TypeSchema = z35.union([
2144
2268
  "r6gd.12xlarge",
2145
2269
  "r6gd.16xlarge"
2146
2270
  ]),
2147
- z35.string()
2271
+ z36.string()
2148
2272
  ]).describe("Instance type of data nodes in the cluster.");
2149
- var CountSchema = z35.number().int().min(1).describe("Number of instances in the cluster.");
2273
+ var CountSchema = z36.number().int().min(1).describe("Number of instances in the cluster.");
2150
2274
  var StorageSizeSchema = SizeSchema.refine(sizeMin(gibibytes3(10)), "Minimum storage size is 10 GB").refine(sizeMax(gibibytes3(100)), "Maximum storage size is 100 GB").describe("The size of the function's /tmp directory. You can specify a size value from 512 MB to 10 GiB.");
2151
- var SearchsSchema = z35.record(
2275
+ var SearchsSchema = z36.record(
2152
2276
  ResourceIdSchema,
2153
- z35.object({
2277
+ z36.object({
2154
2278
  type: TypeSchema.default("t3.small"),
2155
2279
  count: CountSchema.default(1),
2156
2280
  version: VersionSchema.default("2.13"),
@@ -2161,12 +2285,12 @@ var SearchsSchema = z35.record(
2161
2285
  ).optional().describe("Define the search instances in your stack. Backed by OpenSearch.");
2162
2286
 
2163
2287
  // src/feature/site/schema.ts
2164
- import { z as z37 } from "zod";
2288
+ import { z as z38 } from "zod";
2165
2289
 
2166
2290
  // src/config/schema/local-entry.ts
2167
2291
  import { stat as stat3 } from "fs/promises";
2168
- import { z as z36 } from "zod";
2169
- var LocalEntrySchema = z36.union([
2292
+ import { z as z37 } from "zod";
2293
+ var LocalEntrySchema = z37.union([
2170
2294
  RelativePathSchema.refine(async (path) => {
2171
2295
  try {
2172
2296
  const s = await stat3(path);
@@ -2175,7 +2299,7 @@ var LocalEntrySchema = z36.union([
2175
2299
  return false;
2176
2300
  }
2177
2301
  }, `File or directory doesn't exist`),
2178
- z36.object({
2302
+ z37.object({
2179
2303
  nocheck: RelativePathSchema.describe(
2180
2304
  "Specifies a local file or directory without checking if the file or directory exists."
2181
2305
  )
@@ -2183,21 +2307,21 @@ var LocalEntrySchema = z36.union([
2183
2307
  ]);
2184
2308
 
2185
2309
  // src/feature/site/schema.ts
2186
- var SitesSchema = z37.record(
2310
+ var SitesSchema = z38.record(
2187
2311
  ResourceIdSchema,
2188
- z37.object({
2312
+ z38.object({
2189
2313
  router: ResourceIdSchema.describe("The router id to link your site with."),
2190
2314
  path: RouteSchema2.describe("The path inside the router to link your site to."),
2191
- build: z37.object({
2192
- command: z37.string().describe(
2315
+ build: z38.object({
2316
+ command: z38.string().describe(
2193
2317
  `Specifies the files and directories to generate the cache key for your custom build command.`
2194
2318
  ),
2195
- cacheKey: z37.union([LocalEntrySchema.transform((v) => [v]), LocalEntrySchema.array()]).describe(
2319
+ cacheKey: z38.union([LocalEntrySchema.transform((v) => [v]), LocalEntrySchema.array()]).describe(
2196
2320
  `Specifies the files and directories to generate the cache key for your custom build command.`
2197
2321
  ),
2198
- configs: z37.string().array().optional().describe("Define the config values for your build command.")
2322
+ configs: z38.string().array().optional().describe("Define the config values for your build command.")
2199
2323
  }).optional().describe(`Specifies the build process for sites that need a build step.`),
2200
- static: z37.union([LocalDirectorySchema, z37.boolean()]).optional().describe(
2324
+ static: z38.union([LocalDirectorySchema, z38.boolean()]).optional().describe(
2201
2325
  "Specifies the path to the static files directory. Additionally you can also pass `true` when you don't have local static files, but still want to make an S3 bucket."
2202
2326
  ),
2203
2327
  ssr: FunctionSchema.optional().describe("Specifies the file that will render the site on the server.")
@@ -2205,21 +2329,21 @@ var SitesSchema = z37.record(
2205
2329
  ).optional().describe("Define the sites in your stack.");
2206
2330
 
2207
2331
  // src/feature/store/schema.ts
2208
- import { z as z38 } from "zod";
2209
- var StoresSchema = z38.union([
2210
- z38.array(ResourceIdSchema).transform((list3) => {
2332
+ import { z as z39 } from "zod";
2333
+ var StoresSchema = z39.union([
2334
+ z39.array(ResourceIdSchema).transform((list3) => {
2211
2335
  const stores = {};
2212
2336
  for (const key of list3) {
2213
2337
  stores[key] = {};
2214
2338
  }
2215
2339
  return stores;
2216
2340
  }),
2217
- z38.record(
2341
+ z39.record(
2218
2342
  ResourceIdSchema,
2219
- z38.object({
2343
+ z39.object({
2220
2344
  static: LocalDirectorySchema.optional().describe("Specifies the path to the static files directory."),
2221
- versioning: z38.boolean().default(false).describe("Enable versioning of your store."),
2222
- events: z38.object({
2345
+ versioning: z39.boolean().default(false).describe("Enable versioning of your store."),
2346
+ events: z39.object({
2223
2347
  // create
2224
2348
  "created:*": TaskSchema.optional().describe(
2225
2349
  "Subscribe to notifications regardless of the API that was used to create an object."
@@ -2252,30 +2376,30 @@ var StoresSchema = z38.union([
2252
2376
  ]).optional().describe("Define the stores in your stack.");
2253
2377
 
2254
2378
  // src/feature/icon/schema.ts
2255
- import { z as z39 } from "zod";
2379
+ import { z as z40 } from "zod";
2256
2380
  var staticOriginSchema = LocalDirectorySchema.describe(
2257
2381
  "Specifies the path to a local image directory that will be uploaded in S3."
2258
2382
  );
2259
2383
  var functionOriginSchema = FunctionSchema.describe(
2260
2384
  "Specifies the file that will be called when an image isn't found in the (cache) bucket."
2261
2385
  );
2262
- var IconsSchema = z39.record(
2386
+ var IconsSchema = z40.record(
2263
2387
  ResourceIdSchema,
2264
- z39.object({
2388
+ z40.object({
2265
2389
  // domain: ResourceIdSchema.describe('The domain id to link your site with.').optional(),
2266
2390
  // subDomain: z.string().optional(),
2267
2391
  router: ResourceIdSchema.describe("The router id to link your icon proxy."),
2268
2392
  path: RouteSchema2.describe("The path inside the router to link your icon proxy to."),
2269
2393
  log: LogSchema.optional(),
2270
2394
  cacheDuration: DurationSchema.optional().describe("The cache duration of the cached icons."),
2271
- preserveIds: z39.boolean().optional().default(false).describe("Preserve the IDs of the icons."),
2272
- symbols: z39.boolean().optional().default(false).describe(`Convert the SVG's to SVG symbols.`),
2273
- origin: z39.union([
2274
- z39.object({
2395
+ preserveIds: z40.boolean().optional().default(false).describe("Preserve the IDs of the icons."),
2396
+ symbols: z40.boolean().optional().default(false).describe(`Convert the SVG's to SVG symbols.`),
2397
+ origin: z40.union([
2398
+ z40.object({
2275
2399
  static: staticOriginSchema,
2276
2400
  function: functionOriginSchema.optional()
2277
2401
  }),
2278
- z39.object({
2402
+ z40.object({
2279
2403
  static: staticOriginSchema.optional(),
2280
2404
  function: functionOriginSchema
2281
2405
  })
@@ -2302,13 +2426,13 @@ var IconsSchema = z39.record(
2302
2426
  ).optional().describe("Define an svg icon proxy in your stack. Store, optimize, and deliver svg icons at scale.");
2303
2427
 
2304
2428
  // src/feature/image/schema.ts
2305
- import { z as z40 } from "zod";
2306
- var transformationOptionsSchema = z40.object({
2307
- width: z40.number().int().positive().optional(),
2308
- height: z40.number().int().positive().optional(),
2309
- fit: z40.enum(["cover", "contain", "fill", "inside", "outside"]).optional(),
2310
- position: z40.enum(["top", "right top", "right", "right bottom", "bottom", "left bottom", "left", "left top", "center"]).optional(),
2311
- quality: z40.number().int().min(1).max(100).optional()
2429
+ import { z as z41 } from "zod";
2430
+ var transformationOptionsSchema = z41.object({
2431
+ width: z41.number().int().positive().optional(),
2432
+ height: z41.number().int().positive().optional(),
2433
+ fit: z41.enum(["cover", "contain", "fill", "inside", "outside"]).optional(),
2434
+ position: z41.enum(["top", "right top", "right", "right bottom", "bottom", "left bottom", "left", "left top", "center"]).optional(),
2435
+ quality: z41.number().int().min(1).max(100).optional()
2312
2436
  });
2313
2437
  var staticOriginSchema2 = LocalDirectorySchema.describe(
2314
2438
  "Specifies the path to a local image directory that will be uploaded in S3."
@@ -2316,38 +2440,38 @@ var staticOriginSchema2 = LocalDirectorySchema.describe(
2316
2440
  var functionOriginSchema2 = FunctionSchema.describe(
2317
2441
  "Specifies the file that will be called when an image isn't found in the (cache) bucket."
2318
2442
  );
2319
- var ImagesSchema = z40.record(
2443
+ var ImagesSchema = z41.record(
2320
2444
  ResourceIdSchema,
2321
- z40.object({
2445
+ z41.object({
2322
2446
  // domain: ResourceIdSchema.describe('The domain id to link your site with.').optional(),
2323
2447
  // subDomain: z.string().optional(),
2324
2448
  router: ResourceIdSchema.describe("The router id to link your image proxy."),
2325
2449
  path: RouteSchema2.describe("The path inside the router to link your image proxy to."),
2326
2450
  log: LogSchema.optional(),
2327
2451
  cacheDuration: DurationSchema.optional().describe("Cache duration of the cached images."),
2328
- presets: z40.record(z40.string(), transformationOptionsSchema).describe("Named presets for image transformations"),
2329
- extensions: z40.object({
2330
- jpg: z40.object({
2331
- mozjpeg: z40.boolean().optional(),
2332
- progressive: z40.boolean().optional()
2452
+ presets: z41.record(z41.string(), transformationOptionsSchema).describe("Named presets for image transformations"),
2453
+ extensions: z41.object({
2454
+ jpg: z41.object({
2455
+ mozjpeg: z41.boolean().optional(),
2456
+ progressive: z41.boolean().optional()
2333
2457
  }).optional(),
2334
- webp: z40.object({
2335
- effort: z40.number().int().min(1).max(10).default(7).optional(),
2336
- lossless: z40.boolean().optional(),
2337
- nearLossless: z40.boolean().optional()
2458
+ webp: z41.object({
2459
+ effort: z41.number().int().min(1).max(10).default(7).optional(),
2460
+ lossless: z41.boolean().optional(),
2461
+ nearLossless: z41.boolean().optional()
2338
2462
  }).optional(),
2339
- png: z40.object({
2340
- compressionLevel: z40.number().int().min(0).max(9).default(6).optional()
2463
+ png: z41.object({
2464
+ compressionLevel: z41.number().int().min(0).max(9).default(6).optional()
2341
2465
  }).optional()
2342
2466
  }).refine((data) => {
2343
2467
  return Object.keys(data).length > 0;
2344
2468
  }, "At least one extension must be defined.").describe("Specify the allowed extensions."),
2345
- origin: z40.union([
2346
- z40.object({
2469
+ origin: z41.union([
2470
+ z41.object({
2347
2471
  static: staticOriginSchema2,
2348
2472
  function: functionOriginSchema2.optional()
2349
2473
  }),
2350
- z40.object({
2474
+ z41.object({
2351
2475
  static: staticOriginSchema2.optional(),
2352
2476
  function: functionOriginSchema2
2353
2477
  })
@@ -2362,7 +2486,7 @@ var ImagesSchema = z40.record(
2362
2486
  ).optional().describe("Define an image proxy in your stack. Store, transform, optimize, and deliver images at scale.");
2363
2487
 
2364
2488
  // src/feature/metric/schema.ts
2365
- import { z as z41 } from "zod";
2489
+ import { z as z42 } from "zod";
2366
2490
  var ops = {
2367
2491
  ">": "GreaterThanThreshold",
2368
2492
  ">=": "GreaterThanOrEqualToThreshold",
@@ -2376,15 +2500,15 @@ var stats = {
2376
2500
  min: "Minimum",
2377
2501
  max: "Maximum"
2378
2502
  };
2379
- var WhereSchema = z41.union([
2380
- z41.string().regex(/(count|avg|sum|min|max) (>|>=|<|<=) (\d)/, "Invalid where query").transform((where) => {
2503
+ var WhereSchema = z42.union([
2504
+ z42.string().regex(/(count|avg|sum|min|max) (>|>=|<|<=) (\d)/, "Invalid where query").transform((where) => {
2381
2505
  const [stat4, op, value] = where.split(" ");
2382
2506
  return { stat: stat4, op, value: parseFloat(value) };
2383
2507
  }),
2384
- z41.object({
2385
- stat: z41.enum(["count", "avg", "sum", "min", "max"]),
2386
- op: z41.enum([">", ">=", "<", "<="]),
2387
- value: z41.number()
2508
+ z42.object({
2509
+ stat: z42.enum(["count", "avg", "sum", "min", "max"]),
2510
+ op: z42.enum([">", ">=", "<", "<="]),
2511
+ value: z42.number()
2388
2512
  })
2389
2513
  ]).transform((where) => {
2390
2514
  return {
@@ -2393,39 +2517,39 @@ var WhereSchema = z41.union([
2393
2517
  value: where.value
2394
2518
  };
2395
2519
  });
2396
- var AlarmSchema = z41.object({
2397
- description: z41.string().optional(),
2520
+ var AlarmSchema = z42.object({
2521
+ description: z42.string().optional(),
2398
2522
  where: WhereSchema,
2399
2523
  period: DurationSchema,
2400
- minDataPoints: z41.number().int().default(1),
2401
- trigger: z41.union([EmailSchema.transform((v) => [v]), EmailSchema.array(), FunctionSchema])
2524
+ minDataPoints: z42.number().int().default(1),
2525
+ trigger: z42.union([EmailSchema.transform((v) => [v]), EmailSchema.array(), FunctionSchema])
2402
2526
  });
2403
- var MetricsSchema = z41.record(
2527
+ var MetricsSchema = z42.record(
2404
2528
  ResourceIdSchema,
2405
- z41.object({
2406
- type: z41.enum(["number", "size", "duration"]),
2529
+ z42.object({
2530
+ type: z42.enum(["number", "size", "duration"]),
2407
2531
  alarms: AlarmSchema.array().optional()
2408
2532
  })
2409
2533
  ).optional().describe("Define the metrics in your stack.");
2410
2534
 
2411
2535
  // src/feature/table/schema.ts
2412
- import { minutes as minutes5, seconds as seconds4 } from "@awsless/duration";
2413
- import { z as z42 } from "zod";
2414
- var KeySchema = z42.string().min(1).max(255);
2415
- var TablesSchema = z42.record(
2536
+ import { minutes as minutes6, seconds as seconds4 } from "@awsless/duration";
2537
+ import { z as z43 } from "zod";
2538
+ var KeySchema = z43.string().min(1).max(255);
2539
+ var TablesSchema = z43.record(
2416
2540
  ResourceIdSchema,
2417
- z42.object({
2541
+ z43.object({
2418
2542
  hash: KeySchema.describe(
2419
2543
  "Specifies the name of the partition / hash key that makes up the primary key for the table."
2420
2544
  ),
2421
2545
  sort: KeySchema.optional().describe(
2422
2546
  "Specifies the name of the range / sort key that makes up the primary key for the table."
2423
2547
  ),
2424
- fields: z42.record(z42.string(), z42.enum(["string", "number", "binary"])).optional().describe(
2548
+ fields: z43.record(z43.string(), z43.enum(["string", "number", "binary"])).optional().describe(
2425
2549
  'A list of attributes that describe the key schema for the table and indexes. If no attribute field is defined we default to "string".'
2426
2550
  ),
2427
- class: z42.enum(["standard", "standard-infrequent-access"]).default("standard").describe("The table class of the table."),
2428
- pointInTimeRecovery: z42.boolean().default(false).describe("Indicates whether point in time recovery is enabled on the table."),
2551
+ class: z43.enum(["standard", "standard-infrequent-access"]).default("standard").describe("The table class of the table."),
2552
+ pointInTimeRecovery: z43.boolean().default(false).describe("Indicates whether point in time recovery is enabled on the table."),
2429
2553
  ttl: KeySchema.optional().describe(
2430
2554
  [
2431
2555
  "The name of the TTL attribute used to store the expiration time for items in the table.",
@@ -2433,8 +2557,8 @@ var TablesSchema = z42.record(
2433
2557
  ].join("\n")
2434
2558
  ),
2435
2559
  // deletionProtection: DeletionProtectionSchema.optional(),
2436
- stream: z42.object({
2437
- type: z42.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]).describe(
2560
+ stream: z43.object({
2561
+ type: z43.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]).describe(
2438
2562
  [
2439
2563
  "When an item in the table is modified, you can determines what information is written to the stream for this table.",
2440
2564
  "Valid values are:",
@@ -2444,7 +2568,7 @@ var TablesSchema = z42.record(
2444
2568
  "- new-and-old-images - Both the new and the old item images of the item are written to the stream."
2445
2569
  ].join("\n")
2446
2570
  ),
2447
- batchSize: z42.number().min(1).max(1e4).default(1).describe(
2571
+ batchSize: z43.number().min(1).max(1e4).default(1).describe(
2448
2572
  [
2449
2573
  "The maximum number of records in each batch that Lambda pulls from your stream and sends to your function.",
2450
2574
  "Lambda passes all of the records in the batch to the function in a single call, up to the payload limit for synchronous invocation (6 MB).",
@@ -2454,7 +2578,7 @@ var TablesSchema = z42.record(
2454
2578
  batchWindow: DurationSchema.refine(
2455
2579
  durationMin(seconds4(1)),
2456
2580
  "Minimum batch window duration is 1 second"
2457
- ).refine(durationMax(minutes5(5)), "Maximum batch window duration is 5 minutes").optional().describe(
2581
+ ).refine(durationMax(minutes6(5)), "Maximum batch window duration is 5 minutes").optional().describe(
2458
2582
  [
2459
2583
  "The maximum amount of time that is spend gathering records before invoking the function.",
2460
2584
  "You can specify a duration from 1 seconds to 5 minutes."
@@ -2473,7 +2597,7 @@ var TablesSchema = z42.record(
2473
2597
  // 'The default value is 60s',
2474
2598
  // ].join('\n')
2475
2599
  // ),
2476
- retryAttempts: z42.number().min(-1).max(1e4).default(2).describe(
2600
+ retryAttempts: z43.number().min(-1).max(1e4).default(2).describe(
2477
2601
  [
2478
2602
  "Discard records after the specified number of retries.",
2479
2603
  "-1 will sets the maximum number of retries to infinite.",
@@ -2482,7 +2606,7 @@ var TablesSchema = z42.record(
2482
2606
  "The default value is 2"
2483
2607
  ].join("\n")
2484
2608
  ),
2485
- concurrencyPerShard: z42.number().min(1).max(10).default(1).describe(
2609
+ concurrencyPerShard: z43.number().min(1).max(10).default(1).describe(
2486
2610
  [
2487
2611
  "The number of batches to process concurrently from each shard.",
2488
2612
  "You can specify a number from 1 to 10."
@@ -2492,16 +2616,16 @@ var TablesSchema = z42.record(
2492
2616
  }).optional().describe(
2493
2617
  "The settings for the DynamoDB table stream, which capture changes to items stored in the table."
2494
2618
  ),
2495
- indexes: z42.record(
2496
- z42.string(),
2497
- z42.object({
2498
- hash: z42.union([KeySchema.transform((v) => [v]), KeySchema.array()]).describe(
2619
+ indexes: z43.record(
2620
+ z43.string(),
2621
+ z43.object({
2622
+ hash: z43.union([KeySchema.transform((v) => [v]), KeySchema.array()]).describe(
2499
2623
  "Specifies the name of the partition / hash key that makes up the primary key for the global secondary index."
2500
2624
  ),
2501
- sort: z42.union([KeySchema.transform((v) => [v]), KeySchema.array()]).optional().describe(
2625
+ sort: z43.union([KeySchema.transform((v) => [v]), KeySchema.array()]).optional().describe(
2502
2626
  "Specifies the name of the range / sort key that makes up the primary key for the global secondary index."
2503
2627
  ),
2504
- projection: z42.enum(["all", "keys-only"]).default("all").describe(
2628
+ projection: z43.enum(["all", "keys-only"]).default("all").describe(
2505
2629
  [
2506
2630
  "The set of attributes that are projected into the index:",
2507
2631
  "- all - All of the table attributes are projected into the index.",
@@ -2515,12 +2639,12 @@ var TablesSchema = z42.record(
2515
2639
  ).optional().describe("Define the tables in your stack.");
2516
2640
 
2517
2641
  // src/feature/test/schema.ts
2518
- import { z as z43 } from "zod";
2519
- var TestsSchema = z43.union([
2642
+ import { z as z44 } from "zod";
2643
+ var TestsSchema = z44.union([
2520
2644
  //
2521
2645
  LocalDirectorySchema.transform((v) => [v]),
2522
2646
  LocalDirectorySchema.array(),
2523
- z43.literal(false)
2647
+ z44.literal(false)
2524
2648
  ]).describe("Define the location of your tests for your stack.").optional();
2525
2649
 
2526
2650
  // src/config/stack.ts
@@ -2528,8 +2652,8 @@ var DependsSchema = ResourceIdSchema.array().optional().describe("Define the sta
2528
2652
  var NameSchema = ResourceIdSchema.refine((name) => !["base", "hostedzones"].includes(name), {
2529
2653
  message: `Stack name can't be a reserved name.`
2530
2654
  }).describe("Stack name.");
2531
- var StackSchema = z44.object({
2532
- $schema: z44.string().optional(),
2655
+ var StackSchema = z45.object({
2656
+ $schema: z45.string().optional(),
2533
2657
  name: NameSchema,
2534
2658
  depends: DependsSchema,
2535
2659
  commands: CommandsSchema,
@@ -2546,6 +2670,7 @@ var StackSchema = z44.object({
2546
2670
  subscribers: SubscribersSchema,
2547
2671
  functions: FunctionsSchema,
2548
2672
  instances: InstancesSchema,
2673
+ jobs: JobsSchema,
2549
2674
  tasks: TasksSchema,
2550
2675
  tables: TablesSchema,
2551
2676
  stores: StoresSchema,
@@ -2566,37 +2691,37 @@ import { basename, dirname as dirname2, extname, join as join4 } from "path";
2566
2691
 
2567
2692
  // src/config/stage-patch.ts
2568
2693
  import jsonPatch from "fast-json-patch";
2569
- import { z as z45 } from "zod";
2570
- var AddOperationSchema = z45.object({
2571
- op: z45.literal("add"),
2572
- path: z45.string(),
2573
- value: z45.unknown()
2694
+ import { z as z46 } from "zod";
2695
+ var AddOperationSchema = z46.object({
2696
+ op: z46.literal("add"),
2697
+ path: z46.string(),
2698
+ value: z46.unknown()
2574
2699
  }).strict();
2575
- var RemoveOperationSchema = z45.object({
2576
- op: z45.literal("remove"),
2577
- path: z45.string()
2700
+ var RemoveOperationSchema = z46.object({
2701
+ op: z46.literal("remove"),
2702
+ path: z46.string()
2578
2703
  }).strict();
2579
- var ReplaceOperationSchema = z45.object({
2580
- op: z45.literal("replace"),
2581
- path: z45.string(),
2582
- value: z45.unknown()
2704
+ var ReplaceOperationSchema = z46.object({
2705
+ op: z46.literal("replace"),
2706
+ path: z46.string(),
2707
+ value: z46.unknown()
2583
2708
  }).strict();
2584
- var MoveOperationSchema = z45.object({
2585
- op: z45.literal("move"),
2586
- from: z45.string(),
2587
- path: z45.string()
2709
+ var MoveOperationSchema = z46.object({
2710
+ op: z46.literal("move"),
2711
+ from: z46.string(),
2712
+ path: z46.string()
2588
2713
  }).strict();
2589
- var CopyOperationSchema = z45.object({
2590
- op: z45.literal("copy"),
2591
- from: z45.string(),
2592
- path: z45.string()
2714
+ var CopyOperationSchema = z46.object({
2715
+ op: z46.literal("copy"),
2716
+ from: z46.string(),
2717
+ path: z46.string()
2593
2718
  }).strict();
2594
- var TestOperationSchema = z45.object({
2595
- op: z45.literal("test"),
2596
- path: z45.string(),
2597
- value: z45.unknown()
2719
+ var TestOperationSchema = z46.object({
2720
+ op: z46.literal("test"),
2721
+ path: z46.string(),
2722
+ value: z46.unknown()
2598
2723
  }).strict();
2599
- var JsonPatchOperationSchema = z45.discriminatedUnion("op", [
2724
+ var JsonPatchOperationSchema = z46.discriminatedUnion("op", [
2600
2725
  AddOperationSchema,
2601
2726
  RemoveOperationSchema,
2602
2727
  ReplaceOperationSchema,
@@ -2604,8 +2729,8 @@ var JsonPatchOperationSchema = z45.discriminatedUnion("op", [
2604
2729
  CopyOperationSchema,
2605
2730
  TestOperationSchema
2606
2731
  ]);
2607
- var StagePatchSchema = z45.object({
2608
- $schema: z45.string().optional(),
2732
+ var StagePatchSchema = z46.object({
2733
+ $schema: z46.string().optional(),
2609
2734
  operations: JsonPatchOperationSchema.array()
2610
2735
  }).strict();
2611
2736
  var applyStagePatch = (source, patch, file) => {
@@ -2621,13 +2746,13 @@ var applyStagePatch = (source, patch, file) => {
2621
2746
  };
2622
2747
 
2623
2748
  // src/config/load/validate.ts
2624
- import { z as z46 } from "zod";
2749
+ import { z as z47 } from "zod";
2625
2750
  var validateConfig = async (schema, file, data) => {
2626
2751
  try {
2627
2752
  const result = await schema.parseAsync(data);
2628
2753
  return result;
2629
2754
  } catch (error) {
2630
- if (error instanceof z46.ZodError) {
2755
+ if (error instanceof z47.ZodError) {
2631
2756
  throw new ConfigError(file, error, data);
2632
2757
  }
2633
2758
  throw error;
@@ -2862,7 +2987,7 @@ var generateGlobalAppId = (opt) => {
2862
2987
  };
2863
2988
 
2864
2989
  // src/feature/auth/index.ts
2865
- import { toDays as toDays3, toHours } from "@awsless/duration";
2990
+ import { toDays as toDays4, toHours } from "@awsless/duration";
2866
2991
  var authFeature = defineFeature({
2867
2992
  name: "auth",
2868
2993
  async onTypeGen(ctx) {
@@ -2995,7 +3120,7 @@ var authFeature = defineFeature({
2995
3120
  requireUppercase: props.password.uppercase,
2996
3121
  requireNumbers: props.password.numbers,
2997
3122
  requireSymbols: props.password.symbols,
2998
- temporaryPasswordValidityDays: toDays3(props.password.temporaryPasswordValidity)
3123
+ temporaryPasswordValidityDays: toDays4(props.password.temporaryPasswordValidity)
2999
3124
  },
3000
3125
  deletionProtection: ctx.appConfig.removal === "retain" ? "ACTIVE" : "INACTIVE"
3001
3126
  },
@@ -3009,7 +3134,7 @@ var authFeature = defineFeature({
3009
3134
  name,
3010
3135
  idTokenValidity: toHours(props.validity.idToken),
3011
3136
  accessTokenValidity: toHours(props.validity.accessToken),
3012
- refreshTokenValidity: toDays3(props.validity.refreshToken),
3137
+ refreshTokenValidity: toDays4(props.validity.refreshToken),
3013
3138
  tokenValidityUnits: [
3014
3139
  {
3015
3140
  idToken: "hours",
@@ -3467,8 +3592,8 @@ var zipFiles = (files) => {
3467
3592
  };
3468
3593
 
3469
3594
  // src/feature/function/util.ts
3470
- import { toDays as toDays4, toSeconds } from "@awsless/duration";
3471
- import { toMebibytes as toMebibytes2 } from "@awsless/size";
3595
+ import { toDays as toDays5, toSeconds } from "@awsless/duration";
3596
+ import { toMebibytes as toMebibytes3 } from "@awsless/size";
3472
3597
  import { pascalCase } from "change-case";
3473
3598
 
3474
3599
  // src/util/cache.ts
@@ -3763,9 +3888,9 @@ var createLambdaFunction = (parentGroup, ctx, ns, id, local) => {
3763
3888
  const policy = new aws4.iam.RolePolicy(group, "policy", {
3764
3889
  role: role.name,
3765
3890
  name: "lambda-policy",
3766
- policy: new Output3(statementDeps, async (resolve) => {
3891
+ policy: new Output3(statementDeps, async (resolve2) => {
3767
3892
  const list3 = await resolveInputs(statements);
3768
- resolve(
3893
+ resolve2(
3769
3894
  JSON.stringify({
3770
3895
  Version: "2012-10-17",
3771
3896
  Statement: list3.map((statement) => ({
@@ -3827,7 +3952,7 @@ var createLambdaFunction = (parentGroup, ctx, ns, id, local) => {
3827
3952
  runtime: props.runtime,
3828
3953
  handler: props.handler,
3829
3954
  timeout: toSeconds(props.timeout),
3830
- memorySize: toMebibytes2(props.memorySize),
3955
+ memorySize: toMebibytes3(props.memorySize),
3831
3956
  architectures: [props.architecture],
3832
3957
  timeouts: {
3833
3958
  create: "30s",
@@ -3882,7 +4007,7 @@ var createLambdaFunction = (parentGroup, ctx, ns, id, local) => {
3882
4007
  const logGroup = new aws4.cloudwatch.LogGroup(group, "log", {
3883
4008
  // name: lambda.functionName.pipe(name => `/aws/lambda/${name}`),
3884
4009
  name: `/aws/lambda/${name}`,
3885
- retentionInDays: toDays4(props.log.retention)
4010
+ retentionInDays: toDays5(props.log.retention)
3886
4011
  });
3887
4012
  addPermission({
3888
4013
  actions: ["logs:PutLogEvents", "logs:CreateLogStream"],
@@ -4129,7 +4254,7 @@ var cronFeature = defineFeature({
4129
4254
  });
4130
4255
 
4131
4256
  // src/feature/domain/index.ts
4132
- import { minutes as minutes6, toSeconds as toSeconds2 } from "@awsless/duration";
4257
+ import { minutes as minutes7, toSeconds as toSeconds2 } from "@awsless/duration";
4133
4258
  import { Group as Group5 } from "@terraforge/core";
4134
4259
  import { aws as aws6 } from "@terraforge/aws";
4135
4260
  var domainFeature = defineFeature({
@@ -4171,7 +4296,7 @@ var domainFeature = defineFeature({
4171
4296
  zoneId: zone.id,
4172
4297
  name: option(certificate, 0).pipe((r) => r.resourceRecordName),
4173
4298
  type: option(certificate, 0).pipe((r) => r.resourceRecordType),
4174
- ttl: toSeconds2(minutes6(5)),
4299
+ ttl: toSeconds2(minutes7(5)),
4175
4300
  records: [option(certificate, 0).pipe((r) => r.resourceRecordValue)],
4176
4301
  allowOverwrite: true
4177
4302
  });
@@ -4179,7 +4304,7 @@ var domainFeature = defineFeature({
4179
4304
  zoneId: zone.id,
4180
4305
  name: option(certificate, 1).pipe((r) => r.resourceRecordName),
4181
4306
  type: option(certificate, 1).pipe((r) => r.resourceRecordType),
4182
- ttl: toSeconds2(minutes6(5)),
4307
+ ttl: toSeconds2(minutes7(5)),
4183
4308
  records: [option(certificate, 1).pipe((r) => r.resourceRecordValue)],
4184
4309
  allowOverwrite: true
4185
4310
  });
@@ -4214,7 +4339,7 @@ var domainFeature = defineFeature({
4214
4339
  zoneId: zone.id,
4215
4340
  name: option(globalCertificate, 0).pipe((r) => r.resourceRecordName),
4216
4341
  type: option(globalCertificate, 0).pipe((r) => r.resourceRecordType),
4217
- ttl: toSeconds2(minutes6(5)),
4342
+ ttl: toSeconds2(minutes7(5)),
4218
4343
  records: [option(globalCertificate, 0).pipe((r) => r.resourceRecordValue)],
4219
4344
  allowOverwrite: true
4220
4345
  });
@@ -4222,7 +4347,7 @@ var domainFeature = defineFeature({
4222
4347
  zoneId: zone.id,
4223
4348
  name: option(globalCertificate, 1).pipe((r) => r.resourceRecordName),
4224
4349
  type: option(globalCertificate, 1).pipe((r) => r.resourceRecordType),
4225
- ttl: toSeconds2(minutes6(5)),
4350
+ ttl: toSeconds2(minutes7(5)),
4226
4351
  records: [option(globalCertificate, 1).pipe((r) => r.resourceRecordValue)],
4227
4352
  allowOverwrite: true
4228
4353
  });
@@ -4248,7 +4373,7 @@ var domainFeature = defineFeature({
4248
4373
  zoneId: zone.id,
4249
4374
  name: `_amazonses.${props.domain}`,
4250
4375
  type: "TXT",
4251
- ttl: toSeconds2(minutes6(5)),
4376
+ ttl: toSeconds2(minutes7(5)),
4252
4377
  records: [identity.verificationToken]
4253
4378
  });
4254
4379
  const dkim = new aws6.ses.DomainDkim(group2, "dkim", {
@@ -4259,7 +4384,7 @@ var domainFeature = defineFeature({
4259
4384
  zoneId: zone.id,
4260
4385
  type: "CNAME",
4261
4386
  name: dkim.dkimTokens.pipe((t) => `${t.at(i)}._domainkey`),
4262
- ttl: toSeconds2(minutes6(5)),
4387
+ ttl: toSeconds2(minutes7(5)),
4263
4388
  records: [dkim.dkimTokens.pipe((t) => `${t.at(i)}.dkim.amazonses.com`)]
4264
4389
  });
4265
4390
  }
@@ -4272,21 +4397,21 @@ var domainFeature = defineFeature({
4272
4397
  zoneId: zone.id,
4273
4398
  name: mailFrom.mailFromDomain,
4274
4399
  type: "MX",
4275
- ttl: toSeconds2(minutes6(5)),
4400
+ ttl: toSeconds2(minutes7(5)),
4276
4401
  records: [`10 feedback-smtp.${ctx.appConfig.region}.amazonses.com`]
4277
4402
  });
4278
4403
  new aws6.route53.Record(group2, `SPF`, {
4279
4404
  zoneId: zone.id,
4280
4405
  name: mailFrom.mailFromDomain,
4281
4406
  type: "TXT",
4282
- ttl: toSeconds2(minutes6(5)),
4407
+ ttl: toSeconds2(minutes7(5)),
4283
4408
  records: ["v=spf1 include:amazonses.com -all"]
4284
4409
  });
4285
4410
  new aws6.route53.Record(group2, `DMARC`, {
4286
4411
  zoneId: zone.id,
4287
4412
  name: `_dmarc.${props.domain}`,
4288
4413
  type: "TXT",
4289
- ttl: toSeconds2(minutes6(5)),
4414
+ ttl: toSeconds2(minutes7(5)),
4290
4415
  records: ["v=DMARC1; p=none;"]
4291
4416
  });
4292
4417
  const verification = new aws6.ses.DomainIdentityVerification(
@@ -4443,8 +4568,8 @@ var functionFeature = defineFeature({
4443
4568
  });
4444
4569
 
4445
4570
  // src/feature/function/prebuild.ts
4446
- import { days as days5, seconds as seconds5, toDays as toDays5, toSeconds as toSeconds3 } from "@awsless/duration";
4447
- import { mebibytes as mebibytes2, toMebibytes as toMebibytes3 } from "@awsless/size";
4571
+ import { days as days6, seconds as seconds5, toDays as toDays6, toSeconds as toSeconds3 } from "@awsless/duration";
4572
+ import { mebibytes as mebibytes2, toMebibytes as toMebibytes4 } from "@awsless/size";
4448
4573
  import { aws as aws8 } from "@terraforge/aws";
4449
4574
  import { Output as Output4, findInputDeps as findInputDeps2, resolveInputs as resolveInputs2 } from "@terraforge/core";
4450
4575
  import { pascalCase as pascalCase2 } from "change-case";
@@ -4514,9 +4639,9 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, props) => {
4514
4639
  const policy = new aws8.iam.RolePolicy(group, "policy", {
4515
4640
  role: role.name,
4516
4641
  name: "lambda-policy",
4517
- policy: new Output4(statementDeps, async (resolve) => {
4642
+ policy: new Output4(statementDeps, async (resolve2) => {
4518
4643
  const list3 = await resolveInputs2(statements);
4519
- resolve(
4644
+ resolve2(
4520
4645
  JSON.stringify({
4521
4646
  Version: "2012-10-17",
4522
4647
  Statement: list3.map((statement) => ({
@@ -4541,7 +4666,7 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, props) => {
4541
4666
  runtime: props.runtime,
4542
4667
  handler: props.handler,
4543
4668
  timeout: toSeconds3(props.timeout ?? seconds5(10)),
4544
- memorySize: toMebibytes3(props.memorySize ?? mebibytes2(128)),
4669
+ memorySize: toMebibytes4(props.memorySize ?? mebibytes2(128)),
4545
4670
  architectures: [props.architecture ?? "arm64"],
4546
4671
  layers: props.layers?.map((id2) => ctx.shared.entry("layer", "arn", id2)),
4547
4672
  s3Bucket: code.bucket,
@@ -4575,7 +4700,7 @@ var createPrebuildLambdaFunction = (group, ctx, ns, id, props) => {
4575
4700
  if (props.log?.retention && props.log?.retention?.value > 0n) {
4576
4701
  const logGroup = new aws8.cloudwatch.LogGroup(group, "log", {
4577
4702
  name: `/aws/lambda/${name}`,
4578
- retentionInDays: toDays5(props.log.retention ?? days5(7))
4703
+ retentionInDays: toDays6(props.log.retention ?? days6(7))
4579
4704
  });
4580
4705
  addPermission({
4581
4706
  actions: ["logs:PutLogEvents", "logs:CreateLogStream"],
@@ -4632,7 +4757,7 @@ import { Group as Group8 } from "@terraforge/core";
4632
4757
  import { aws as aws9 } from "@terraforge/aws";
4633
4758
  import { join as join10 } from "path";
4634
4759
  import { mebibytes as mebibytes3 } from "@awsless/size";
4635
- import { days as days6, seconds as seconds6 } from "@awsless/duration";
4760
+ import { days as days7, seconds as seconds6 } from "@awsless/duration";
4636
4761
  var onErrorLogFeature = defineFeature({
4637
4762
  name: "on-error-log",
4638
4763
  onApp(ctx) {
@@ -4647,7 +4772,7 @@ var onErrorLogFeature = defineFeature({
4647
4772
  log: {
4648
4773
  format: "json",
4649
4774
  level: "warn",
4650
- retention: days6(3),
4775
+ retention: days7(3),
4651
4776
  system: "warn"
4652
4777
  }
4653
4778
  });
@@ -4704,7 +4829,7 @@ import { Group as Group9 } from "@terraforge/core";
4704
4829
  import { aws as aws10 } from "@terraforge/aws";
4705
4830
  import { join as join11 } from "path";
4706
4831
  import { mebibytes as mebibytes4 } from "@awsless/size";
4707
- import { days as days7, toSeconds as toSeconds4 } from "@awsless/duration";
4832
+ import { days as days8, toSeconds as toSeconds4 } from "@awsless/duration";
4708
4833
  var onFailureFeature = defineFeature({
4709
4834
  name: "on-failure",
4710
4835
  onApp(ctx) {
@@ -4715,7 +4840,7 @@ var onFailureFeature = defineFeature({
4715
4840
  resourceType: "on-failure",
4716
4841
  resourceName: "deadletter"
4717
4842
  }),
4718
- messageRetentionSeconds: toSeconds4(days7(14))
4843
+ messageRetentionSeconds: toSeconds4(days8(14))
4719
4844
  });
4720
4845
  const queue2 = new aws10.sqs.Queue(group, "on-failure", {
4721
4846
  name: formatGlobalResourceName({
@@ -4863,7 +4988,7 @@ var onFailureFeature = defineFeature({
4863
4988
  log: {
4864
4989
  format: "json",
4865
4990
  level: "warn",
4866
- retention: days7(3),
4991
+ retention: days8(3),
4867
4992
  system: "warn"
4868
4993
  }
4869
4994
  });
@@ -4961,7 +5086,7 @@ var formatFullDomainName = (config2, id, subDomain) => {
4961
5086
  };
4962
5087
 
4963
5088
  // src/feature/pubsub/index.ts
4964
- import { minutes as minutes7, toSeconds as toSeconds5 } from "@awsless/duration";
5089
+ import { minutes as minutes8, toSeconds as toSeconds5 } from "@awsless/duration";
4965
5090
  var pubsubFeature = defineFeature({
4966
5091
  name: "pubsub",
4967
5092
  onApp(ctx) {
@@ -5012,7 +5137,7 @@ var pubsubFeature = defineFeature({
5012
5137
  zoneId: ctx.shared.entry("domain", `zone-id`, props.domain),
5013
5138
  name: domainName,
5014
5139
  type: "CNAME",
5015
- ttl: toSeconds5(minutes7(5)),
5140
+ ttl: toSeconds5(minutes8(5)),
5016
5141
  records: [endpoint.endpointAddress]
5017
5142
  });
5018
5143
  ctx.bind(`PUBSUB_${constantCase5(id)}_ENDPOINT`, domainName);
@@ -6762,7 +6887,7 @@ import { Group as Group23 } from "@terraforge/core";
6762
6887
  import { aws as aws24 } from "@terraforge/aws";
6763
6888
  import { join as join15, dirname as dirname7 } from "path";
6764
6889
  import { mebibytes as mebibytes6 } from "@awsless/size";
6765
- import { seconds as seconds8, toDays as toDays6 } from "@awsless/duration";
6890
+ import { seconds as seconds8, toDays as toDays7 } from "@awsless/duration";
6766
6891
  import { fileURLToPath as fileURLToPath2 } from "url";
6767
6892
  import { glob as glob4 } from "glob";
6768
6893
  var __dirname3 = dirname7(fileURLToPath2(import.meta.url));
@@ -6853,7 +6978,7 @@ var imageFeature = defineFeature({
6853
6978
  enabled: true,
6854
6979
  id: "image-cache-duration",
6855
6980
  expiration: {
6856
- days: toDays6(props.cacheDuration)
6981
+ days: toDays7(props.cacheDuration)
6857
6982
  }
6858
6983
  }
6859
6984
  ]
@@ -6959,7 +7084,7 @@ import { Group as Group24 } from "@terraforge/core";
6959
7084
  import { aws as aws25 } from "@terraforge/aws";
6960
7085
  import { join as join16, dirname as dirname8 } from "path";
6961
7086
  import { mebibytes as mebibytes7 } from "@awsless/size";
6962
- import { seconds as seconds9, toDays as toDays7 } from "@awsless/duration";
7087
+ import { seconds as seconds9, toDays as toDays8 } from "@awsless/duration";
6963
7088
  import { fileURLToPath as fileURLToPath3 } from "url";
6964
7089
  import { glob as glob5 } from "glob";
6965
7090
  var __dirname4 = dirname8(fileURLToPath3(import.meta.url));
@@ -7004,7 +7129,7 @@ var iconFeature = defineFeature({
7004
7129
  enabled: true,
7005
7130
  id: "icon-cache-duration",
7006
7131
  expiration: {
7007
- days: toDays7(props.cacheDuration)
7132
+ days: toDays8(props.cacheDuration)
7008
7133
  }
7009
7134
  }
7010
7135
  ]
@@ -7102,31 +7227,42 @@ var iconFeature = defineFeature({
7102
7227
  }
7103
7228
  });
7104
7229
 
7105
- // src/feature/instance/index.ts
7106
- import { Group as Group26 } from "@terraforge/core";
7230
+ // src/feature/job/index.ts
7231
+ import { Group as Group26, Output as Output7, resolveInputs as resolveInputs4, findInputDeps as findInputDeps4 } from "@terraforge/core";
7107
7232
  import { aws as aws27 } from "@terraforge/aws";
7233
+ import { camelCase as camelCase8 } from "change-case";
7234
+ import { relative as relative8 } from "path";
7108
7235
 
7109
- // src/feature/instance/util.ts
7110
- import { toDays as toDays8, toSeconds as toSeconds9 } from "@awsless/duration";
7111
- import { toMebibytes as toMebibytes4 } from "@awsless/size";
7236
+ // src/feature/job/util.ts
7237
+ import { toDays as toDays9, toSeconds as toSeconds9 } from "@awsless/duration";
7238
+ import { toMebibytes as toMebibytes5 } from "@awsless/size";
7112
7239
  import { generateFileHash as generateFileHash2 } from "@awsless/ts-file-cache";
7113
7240
  import { aws as aws26 } from "@terraforge/aws";
7114
7241
  import { Group as Group25, Output as Output6, findInputDeps as findInputDeps3, resolveInputs as resolveInputs3 } from "@terraforge/core";
7115
7242
  import { constantCase as constantCase12, pascalCase as pascalCase3 } from "change-case";
7116
7243
  import deepmerge4 from "deepmerge";
7117
- import { join as join18 } from "path";
7118
7244
 
7119
- // src/feature/instance/build/executable.ts
7245
+ // src/feature/job/build/executable.ts
7120
7246
  import { createHash as createHash3 } from "crypto";
7121
- import { readFile as readFile4 } from "fs/promises";
7122
- import { join as join17 } from "path";
7123
- var buildExecutable = async (input, outputPath, architecture) => {
7247
+ import { readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
7248
+ import { join as join17, resolve } from "path";
7249
+ var buildJobExecutable = async (input, outputPath, architecture) => {
7124
7250
  const filePath = join17(outputPath, "program");
7251
+ const wrapperPath = join17(outputPath, "wrapper.ts");
7252
+ const handlerPath = resolve(input);
7253
+ await writeFile3(
7254
+ wrapperPath,
7255
+ [
7256
+ `import handler from '${handlerPath}'`,
7257
+ `const payload = JSON.parse(process.env.PAYLOAD || '{}')`,
7258
+ `await handler(payload)`
7259
+ ].join("\n")
7260
+ );
7125
7261
  const target = architecture === "x86_64" ? "bun-linux-x64" : "bun-linux-arm64";
7126
7262
  let result;
7127
7263
  try {
7128
7264
  result = await Bun.build({
7129
- entrypoints: [input],
7265
+ entrypoints: [wrapperPath],
7130
7266
  compile: {
7131
7267
  target,
7132
7268
  outfile: filePath
@@ -7135,23 +7271,23 @@ var buildExecutable = async (input, outputPath, architecture) => {
7135
7271
  });
7136
7272
  } catch (error) {
7137
7273
  throw new ExpectedError(
7138
- `Executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
7274
+ `Job executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
7139
7275
  );
7140
7276
  }
7141
7277
  if (!result.success) {
7142
- throw new ExpectedError(`Executable build failed:
7278
+ throw new ExpectedError(`Job executable build failed:
7143
7279
  ${result.logs?.map((log35) => log35.message).join("\n")}`);
7144
7280
  }
7145
7281
  const file = await readFile4(filePath);
7146
7282
  return {
7147
- hash: createHash3("sha1").update(file).update("x86_64").digest("hex"),
7283
+ hash: createHash3("sha1").update(file).update(architecture).digest("hex"),
7148
7284
  file
7149
7285
  };
7150
7286
  };
7151
7287
 
7152
- // src/feature/instance/util.ts
7153
- var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7154
- const group = new Group25(parentGroup, "instance", ns);
7288
+ // src/feature/job/util.ts
7289
+ var createFargateJob = (parentGroup, ctx, ns, id, local) => {
7290
+ const group = new Group25(parentGroup, "job", ns);
7155
7291
  const name = formatLocalResourceName({
7156
7292
  appName: ctx.app.name,
7157
7293
  stackName: ctx.stack.name,
@@ -7159,13 +7295,13 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7159
7295
  resourceName: id
7160
7296
  });
7161
7297
  const shortName = shortId(`${ctx.app.name}:${ctx.stack.name}:${ns}:${id}:${ctx.appId}`);
7162
- const props = deepmerge4(ctx.appConfig.defaults.instance, local);
7298
+ const props = deepmerge4(ctx.appConfig.defaults.job, local);
7163
7299
  const image2 = props.image || (props.architecture === "arm64" ? "public.ecr.aws/aws-cli/aws-cli:arm64" : "public.ecr.aws/aws-cli/aws-cli:amd64");
7164
- ctx.registerBuild("instance", name, async (build3, { workspace }) => {
7300
+ ctx.registerBuild("job", name, async (build3, { workspace }) => {
7165
7301
  const fingerprint = await generateFileHash2(workspace, local.code.file);
7166
7302
  return build3(fingerprint, async (write) => {
7167
- const temp = await createTempFolder(`instance--${name}`);
7168
- const executable = await buildExecutable(local.code.file, temp.path, props.architecture);
7303
+ const temp = await createTempFolder(`job--${name}`);
7304
+ const executable = await buildJobExecutable(local.code.file, temp.path, props.architecture);
7169
7305
  await Promise.all([
7170
7306
  //
7171
7307
  write("HASH", executable.hash),
@@ -7178,10 +7314,10 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7178
7314
  });
7179
7315
  });
7180
7316
  const code = new aws26.s3.BucketObject(group, "code", {
7181
- bucket: ctx.shared.get("instance", "bucket-name"),
7317
+ bucket: ctx.shared.get("job", "bucket-name"),
7182
7318
  key: name,
7183
- source: relativePath(getBuildPath("instance", name, "program")),
7184
- sourceHash: $file(getBuildPath("instance", name, "HASH"))
7319
+ source: relativePath(getBuildPath("job", name, "program")),
7320
+ sourceHash: $file(getBuildPath("job", name, "HASH"))
7185
7321
  });
7186
7322
  const executionRole = new aws26.iam.Role(group, "execution-role", {
7187
7323
  name: shortId(`${shortName}:execution-role`),
@@ -7245,9 +7381,9 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7245
7381
  const policy = new aws26.iam.RolePolicy(group, "policy", {
7246
7382
  role: role.name,
7247
7383
  name: "task-policy",
7248
- policy: new Output6(statementDeps, async (resolve) => {
7384
+ policy: new Output6(statementDeps, async (resolve2) => {
7249
7385
  const list3 = await resolveInputs3(statements);
7250
- resolve(
7386
+ resolve2(
7251
7387
  JSON.stringify({
7252
7388
  Version: "2012-10-17",
7253
7389
  Statement: list3.map((statement) => ({
@@ -7272,8 +7408,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7272
7408
  if (props.log.retention && props.log.retention.value > 0n) {
7273
7409
  logGroup = new aws26.cloudwatch.LogGroup(group, "log", {
7274
7410
  name: `/aws/ecs/${name}`,
7275
- // name: `/aws/lambda/${name}`,
7276
- retentionInDays: toDays8(props.log.retention)
7411
+ retentionInDays: toDays9(props.log.retention)
7277
7412
  });
7278
7413
  if (ctx.shared.has("on-error-log", "subscriber-arn")) {
7279
7414
  new aws26.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
@@ -7298,7 +7433,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7298
7433
  family: name,
7299
7434
  networkMode: "awsvpc",
7300
7435
  cpu: props.cpu,
7301
- memory: toMebibytes4(props.memorySize).toString(),
7436
+ memory: toMebibytes5(props.memorySize).toString(),
7302
7437
  requiresCompatibilities: ["FARGATE"],
7303
7438
  executionRoleArn: executionRole.arn,
7304
7439
  taskRoleArn: role.arn,
@@ -7307,13 +7442,13 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7307
7442
  operatingSystemFamily: "LINUX"
7308
7443
  },
7309
7444
  trackLatest: true,
7310
- containerDefinitions: new Output6(variableDeps, async (resolve) => {
7445
+ containerDefinitions: new Output6(variableDeps, async (resolve2) => {
7311
7446
  const data = await resolveInputs3(variables);
7312
7447
  const { s3Bucket, s3Key } = await resolveInputs3({
7313
7448
  s3Bucket: code.bucket,
7314
7449
  s3Key: code.key
7315
7450
  });
7316
- resolve(
7451
+ resolve2(
7317
7452
  JSON.stringify([
7318
7453
  {
7319
7454
  name: `container-${id}`,
@@ -7326,50 +7461,25 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7326
7461
  [
7327
7462
  `aws s3 cp s3://${s3Bucket}/${s3Key} /usr/app/program`,
7328
7463
  `chmod +x /usr/app/program`,
7329
- `/usr/app/program`
7464
+ ...props.startupCommand ?? [],
7465
+ `exec timeout ${toSeconds9(props.timeout)} /usr/app/program`
7330
7466
  ].join(" && ")
7331
7467
  ],
7332
7468
  environment: Object.entries(data).map(([name2, value]) => ({
7333
7469
  name: name2,
7334
7470
  value
7335
7471
  })),
7336
- portMappings: [
7337
- {
7338
- name: "http",
7339
- protocol: "tcp",
7340
- appProtocol: "http",
7341
- containerPort: 80,
7342
- hostPort: 80
7343
- }
7344
- ],
7345
- restartPolicy: {
7346
- enabled: true,
7347
- restartAttemptPeriod: 60
7348
- },
7349
7472
  ...logGroup && {
7350
7473
  logConfiguration: {
7351
7474
  logDriver: "awslogs",
7352
7475
  options: {
7353
- // 'awslogs-group': `/aws/ecs/${name}`,
7354
7476
  "awslogs-group": `/aws/ecs/${name}`,
7355
7477
  "awslogs-region": ctx.appConfig.region,
7356
7478
  "awslogs-stream-prefix": "ecs",
7357
7479
  mode: "non-blocking"
7358
- // 'awslogs-multiline-pattern': '',
7359
- // 'max-buffer-size': '100m',
7360
7480
  }
7361
7481
  }
7362
- },
7363
- healthCheck: props.healthCheck ? {
7364
- command: [
7365
- "CMD-SHELL",
7366
- `curl -f http://${join18("localhost", props.healthCheck.path)} || exit 1`
7367
- ],
7368
- interval: toSeconds9(props.healthCheck.interval),
7369
- retries: props.healthCheck.retries,
7370
- startPeriod: toSeconds9(props.healthCheck.startPeriod),
7371
- timeout: toSeconds9(props.healthCheck.timeout)
7372
- } : void 0
7482
+ }
7373
7483
  }
7374
7484
  ])
7375
7485
  );
@@ -7388,52 +7498,496 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7388
7498
  dependsOn: [code]
7389
7499
  }
7390
7500
  );
7391
- const securityGroup = new aws26.security.Group(group, "security-group", {
7392
- name,
7393
- description: "Security group for the instance",
7394
- vpcId: ctx.shared.get("vpc", "id"),
7395
- revokeRulesOnDelete: true,
7396
- tags
7397
- });
7398
- new aws26.vpc.SecurityGroupEgressRule(group, "egress-rule", {
7399
- securityGroupId: securityGroup.id,
7400
- description: `Allow all outbound traffic from the ${name} instance`,
7401
- ipProtocol: "-1",
7402
- cidrIpv4: "0.0.0.0/0",
7403
- tags
7501
+ ctx.onEnv((name2, value) => {
7502
+ variables[name2] = value;
7503
+ for (const dep of findInputDeps3([value])) {
7504
+ variableDeps.add(dep);
7505
+ }
7404
7506
  });
7405
- const clusterName = ctx.shared.get("instance", "cluster-name");
7406
- const clusterArn = ctx.shared.get("instance", "cluster-arn");
7407
- const service = new aws26.ecs.Service(group, "service", {
7408
- name,
7409
- cluster: clusterArn,
7410
- taskDefinition: task2.arn,
7411
- desiredCount: 1,
7412
- launchType: "FARGATE",
7413
- networkConfiguration: {
7414
- subnets: ctx.shared.get("vpc", "public-subnets"),
7415
- securityGroups: [securityGroup.id],
7416
- assignPublicIp: true
7417
- // https://stackoverflow.com/questions/76398247/cannotpullcontainererror-pull-image-manifest-has-been-retried-5-times-failed
7418
- },
7419
- forceNewDeployment: true,
7420
- forceDelete: true,
7421
- tags,
7422
- // ------------------------------------------------------------
7423
- // Deployment safeguards: keep the service pinned to one running task.
7424
- schedulingStrategy: "REPLICA",
7425
- deploymentMaximumPercent: 100,
7426
- deploymentMinimumHealthyPercent: 0,
7427
- deploymentCircuitBreaker: {
7428
- enable: true,
7429
- rollback: true
7430
- },
7431
- // ------------------------------------------------------------
7432
- // Tag hygiene: let ECS manage and propagate runtime tags automatically.
7507
+ variables.APP = ctx.appConfig.name;
7508
+ variables.APP_ID = ctx.appId;
7509
+ variables.AWS_ACCOUNT_ID = ctx.accountId;
7510
+ variables.STACK = ctx.stackConfig.name;
7511
+ variables.CODE_HASH = code.sourceHash;
7512
+ variables.TIMEOUT = toSeconds9(props.timeout).toString();
7513
+ if (props.environment) {
7514
+ for (const [key, value] of Object.entries(props.environment)) {
7515
+ variables[key] = value;
7516
+ }
7517
+ }
7518
+ if (ctx.appConfig.defaults.job.permissions) {
7519
+ statements.push(...ctx.appConfig.defaults.job.permissions);
7520
+ }
7521
+ if ("permissions" in local && local.permissions) {
7522
+ statements.push(...local.permissions);
7523
+ }
7524
+ return { name, task: task2, policy, code, group };
7525
+ };
7526
+
7527
+ // src/feature/job/index.ts
7528
+ var typeGenCode10 = `
7529
+ import type { Mock } from 'vitest'
7530
+
7531
+ type Func = (...args: any[]) => any
7532
+
7533
+ type Invoke<N extends string, F extends Func> = unknown extends Parameters<F>[0] ? InvokeWithoutPayload<N, F> : InvokeWithPayload<N, F>
7534
+
7535
+ type InvokeWithPayload<Name extends string, F extends Func> = {
7536
+ readonly name: Name
7537
+ (payload: Parameters<F>[0]): Promise<{ taskArn: string | undefined }>
7538
+ }
7539
+
7540
+ type InvokeWithoutPayload<Name extends string, F extends Func> = {
7541
+ readonly name: Name
7542
+ (payload?: Parameters<F>[0]): Promise<{ taskArn: string | undefined }>
7543
+ }
7544
+
7545
+ type MockHandle<F extends Func> = (payload: Parameters<F>[0]) => void | Promise<void>
7546
+ type MockBuilder<F extends Func> = (handle?: MockHandle<F>) => void
7547
+ type MockObject<F extends Func> = Mock<Parameters<F>, ReturnType<F>>
7548
+ `;
7549
+ var jobFeature = defineFeature({
7550
+ name: "job",
7551
+ onBefore(ctx) {
7552
+ const group = new Group26(ctx.base, "job", "asset");
7553
+ const bucket = new aws27.s3.Bucket(group, "bucket", {
7554
+ bucket: formatGlobalResourceName({
7555
+ appName: ctx.app.name,
7556
+ resourceType: "job",
7557
+ resourceName: "assets",
7558
+ postfix: ctx.appId
7559
+ }),
7560
+ forceDestroy: true
7561
+ });
7562
+ ctx.shared.set("job", "bucket-name", bucket.bucket);
7563
+ },
7564
+ onApp(ctx) {
7565
+ const found = ctx.stackConfigs.filter((stack) => {
7566
+ return Object.keys(stack.jobs ?? {}).length > 0;
7567
+ });
7568
+ if (found.length === 0) {
7569
+ return;
7570
+ }
7571
+ const group = new Group26(ctx.base, "job", "cluster");
7572
+ const cluster = new aws27.ecs.Cluster(group, "cluster", {
7573
+ name: `${ctx.app.name}-job`
7574
+ });
7575
+ ctx.shared.set("job", "cluster-name", cluster.name);
7576
+ ctx.shared.set("job", "cluster-arn", cluster.arn);
7577
+ const securityGroup = new aws27.security.Group(group, "security-group", {
7578
+ name: `${ctx.app.name}-job`,
7579
+ description: "Shared security group for jobs",
7580
+ vpcId: ctx.shared.get("vpc", "id"),
7581
+ revokeRulesOnDelete: true,
7582
+ tags: {
7583
+ APP: ctx.appConfig.name
7584
+ }
7585
+ });
7586
+ new aws27.vpc.SecurityGroupEgressRule(group, "egress-rule", {
7587
+ securityGroupId: securityGroup.id,
7588
+ description: "Allow all outbound traffic from jobs",
7589
+ ipProtocol: "-1",
7590
+ cidrIpv4: "0.0.0.0/0",
7591
+ tags: {
7592
+ APP: ctx.appConfig.name
7593
+ }
7594
+ });
7595
+ ctx.shared.set("job", "security-group-id", securityGroup.id);
7596
+ ctx.addGlobalPermission({
7597
+ actions: ["ecs:RunTask", "ecs:DescribeTasks", "ecs:StopTask"],
7598
+ resources: ["*"]
7599
+ });
7600
+ ctx.addGlobalPermission({
7601
+ actions: ["iam:PassRole"],
7602
+ resources: ["*"]
7603
+ });
7604
+ },
7605
+ onStack(ctx) {
7606
+ if (!ctx.shared.has("job", "security-group-id")) return;
7607
+ const subnets = ctx.shared.get("vpc", "public-subnets");
7608
+ ctx.addEnv(
7609
+ "JOB_SUBNETS",
7610
+ new Output7(new Set(findInputDeps4(subnets)), async (resolve2) => {
7611
+ const resolved = await resolveInputs4(subnets);
7612
+ resolve2(JSON.stringify(resolved));
7613
+ })
7614
+ );
7615
+ ctx.addEnv("JOB_SECURITY_GROUP", ctx.shared.get("job", "security-group-id"));
7616
+ for (const [id, props] of Object.entries(ctx.stackConfig.jobs ?? {})) {
7617
+ const group = new Group26(ctx.stack, "job", id);
7618
+ createFargateJob(group, ctx, "job", id, props);
7619
+ }
7620
+ },
7621
+ async onTypeGen(ctx) {
7622
+ const types2 = new TypeFile("@awsless/awsless");
7623
+ const resources2 = new TypeObject(1);
7624
+ const mocks = new TypeObject(1);
7625
+ const mockResponses = new TypeObject(1);
7626
+ for (const stack of ctx.stackConfigs) {
7627
+ const resource = new TypeObject(2);
7628
+ const mock = new TypeObject(2);
7629
+ const mockResponse = new TypeObject(2);
7630
+ for (const [name, props] of Object.entries(stack.jobs || {})) {
7631
+ const varName = camelCase8(`${stack.name}-${name}`);
7632
+ const funcName = formatLocalResourceName({
7633
+ appName: ctx.appConfig.name,
7634
+ stackName: stack.name,
7635
+ resourceType: "job",
7636
+ resourceName: name
7637
+ });
7638
+ if ("file" in props.code) {
7639
+ const relFile = relative8(directories.types, props.code.file);
7640
+ types2.addImport(varName, relFile);
7641
+ resource.addType(name, `Invoke<'${funcName}', typeof ${varName}>`);
7642
+ mock.addType(name, `MockBuilder<typeof ${varName}>`);
7643
+ mockResponse.addType(name, `MockObject<typeof ${varName}>`);
7644
+ }
7645
+ }
7646
+ mocks.addType(stack.name, mock);
7647
+ resources2.addType(stack.name, resource);
7648
+ mockResponses.addType(stack.name, mockResponse);
7649
+ }
7650
+ types2.addCode(typeGenCode10);
7651
+ types2.addInterface("JobResources", resources2);
7652
+ types2.addInterface("JobMock", mocks);
7653
+ types2.addInterface("JobMockResponse", mockResponses);
7654
+ await ctx.write("job.d.ts", types2, true);
7655
+ }
7656
+ });
7657
+
7658
+ // src/feature/instance/index.ts
7659
+ import { Group as Group28 } from "@terraforge/core";
7660
+ import { aws as aws29 } from "@terraforge/aws";
7661
+
7662
+ // src/feature/instance/util.ts
7663
+ import { toDays as toDays10, toSeconds as toSeconds10 } from "@awsless/duration";
7664
+ import { toMebibytes as toMebibytes6 } from "@awsless/size";
7665
+ import { generateFileHash as generateFileHash3 } from "@awsless/ts-file-cache";
7666
+ import { aws as aws28 } from "@terraforge/aws";
7667
+ import { Group as Group27, Output as Output8, findInputDeps as findInputDeps5, resolveInputs as resolveInputs5 } from "@terraforge/core";
7668
+ import { constantCase as constantCase13, pascalCase as pascalCase4 } from "change-case";
7669
+ import deepmerge5 from "deepmerge";
7670
+ import { join as join19 } from "path";
7671
+
7672
+ // src/feature/instance/build/executable.ts
7673
+ import { createHash as createHash4 } from "crypto";
7674
+ import { readFile as readFile5 } from "fs/promises";
7675
+ import { join as join18 } from "path";
7676
+ var buildExecutable = async (input, outputPath, architecture) => {
7677
+ const filePath = join18(outputPath, "program");
7678
+ const target = architecture === "x86_64" ? "bun-linux-x64" : "bun-linux-arm64";
7679
+ let result;
7680
+ try {
7681
+ result = await Bun.build({
7682
+ entrypoints: [input],
7683
+ compile: {
7684
+ target,
7685
+ outfile: filePath
7686
+ },
7687
+ target: "bun"
7688
+ });
7689
+ } catch (error) {
7690
+ throw new ExpectedError(
7691
+ `Executable build failed: ${error instanceof Error ? error.message : JSON.stringify(error)}`
7692
+ );
7693
+ }
7694
+ if (!result.success) {
7695
+ throw new ExpectedError(`Executable build failed:
7696
+ ${result.logs?.map((log35) => log35.message).join("\n")}`);
7697
+ }
7698
+ const file = await readFile5(filePath);
7699
+ return {
7700
+ hash: createHash4("sha1").update(file).update("x86_64").digest("hex"),
7701
+ file
7702
+ };
7703
+ };
7704
+
7705
+ // src/feature/instance/util.ts
7706
+ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7707
+ const group = new Group27(parentGroup, "instance", ns);
7708
+ const name = formatLocalResourceName({
7709
+ appName: ctx.app.name,
7710
+ stackName: ctx.stack.name,
7711
+ resourceType: ns,
7712
+ resourceName: id
7713
+ });
7714
+ const shortName = shortId(`${ctx.app.name}:${ctx.stack.name}:${ns}:${id}:${ctx.appId}`);
7715
+ const props = deepmerge5(ctx.appConfig.defaults.instance, local);
7716
+ const image2 = props.image || (props.architecture === "arm64" ? "public.ecr.aws/aws-cli/aws-cli:arm64" : "public.ecr.aws/aws-cli/aws-cli:amd64");
7717
+ ctx.registerBuild("instance", name, async (build3, { workspace }) => {
7718
+ const fingerprint = await generateFileHash3(workspace, local.code.file);
7719
+ return build3(fingerprint, async (write) => {
7720
+ const temp = await createTempFolder(`instance--${name}`);
7721
+ const executable = await buildExecutable(local.code.file, temp.path, props.architecture);
7722
+ await Promise.all([
7723
+ //
7724
+ write("HASH", executable.hash),
7725
+ write("program", executable.file),
7726
+ temp.delete()
7727
+ ]);
7728
+ return {
7729
+ size: formatByteSize(executable.file.byteLength)
7730
+ };
7731
+ });
7732
+ });
7733
+ const code = new aws28.s3.BucketObject(group, "code", {
7734
+ bucket: ctx.shared.get("instance", "bucket-name"),
7735
+ key: name,
7736
+ source: relativePath(getBuildPath("instance", name, "program")),
7737
+ sourceHash: $file(getBuildPath("instance", name, "HASH"))
7738
+ });
7739
+ const executionRole = new aws28.iam.Role(group, "execution-role", {
7740
+ name: shortId(`${shortName}:execution-role`),
7741
+ description: name,
7742
+ assumeRolePolicy: JSON.stringify({
7743
+ Version: "2012-10-17",
7744
+ Statement: [
7745
+ {
7746
+ Effect: "Allow",
7747
+ Action: "sts:AssumeRole",
7748
+ Principal: {
7749
+ Service: ["ecs-tasks.amazonaws.com"]
7750
+ }
7751
+ }
7752
+ ]
7753
+ }),
7754
+ managedPolicyArns: ["arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"]
7755
+ });
7756
+ const role = new aws28.iam.Role(
7757
+ group,
7758
+ "task-role",
7759
+ {
7760
+ name: shortId(`${shortName}:task-role`),
7761
+ description: name,
7762
+ assumeRolePolicy: JSON.stringify({
7763
+ Version: "2012-10-17",
7764
+ Statement: [
7765
+ {
7766
+ Effect: "Allow",
7767
+ Action: "sts:AssumeRole",
7768
+ Principal: {
7769
+ Service: ["ecs-tasks.amazonaws.com"]
7770
+ }
7771
+ }
7772
+ ]
7773
+ }),
7774
+ inlinePolicy: [
7775
+ {
7776
+ name: "s3-code-access",
7777
+ policy: $resolve([code.bucket, code.key], (bucket, key) => {
7778
+ return JSON.stringify({
7779
+ Version: "2012-10-17",
7780
+ Statement: [
7781
+ {
7782
+ Effect: pascalCase4("allow"),
7783
+ Action: ["s3:getObject", "s3:HeadObject"],
7784
+ Resource: `arn:aws:s3:::${bucket}/${key}`
7785
+ }
7786
+ ]
7787
+ });
7788
+ })
7789
+ }
7790
+ ]
7791
+ },
7792
+ {
7793
+ dependsOn: [code]
7794
+ }
7795
+ );
7796
+ const statements = [];
7797
+ const statementDeps = /* @__PURE__ */ new Set();
7798
+ const policy = new aws28.iam.RolePolicy(group, "policy", {
7799
+ role: role.name,
7800
+ name: "task-policy",
7801
+ policy: new Output8(statementDeps, async (resolve2) => {
7802
+ const list3 = await resolveInputs5(statements);
7803
+ resolve2(
7804
+ JSON.stringify({
7805
+ Version: "2012-10-17",
7806
+ Statement: list3.map((statement) => ({
7807
+ Effect: pascalCase4(statement.effect ?? "allow"),
7808
+ Action: statement.actions,
7809
+ Resource: statement.resources
7810
+ }))
7811
+ })
7812
+ );
7813
+ })
7814
+ });
7815
+ const addPermission = (...permissions) => {
7816
+ statements.push(...permissions);
7817
+ for (const dep of findInputDeps5(permissions)) {
7818
+ statementDeps.add(dep);
7819
+ }
7820
+ };
7821
+ ctx.onPermission((statement) => {
7822
+ addPermission(statement);
7823
+ });
7824
+ let logGroup;
7825
+ if (props.log.retention && props.log.retention.value > 0n) {
7826
+ logGroup = new aws28.cloudwatch.LogGroup(group, "log", {
7827
+ name: `/aws/ecs/${name}`,
7828
+ // name: `/aws/lambda/${name}`,
7829
+ retentionInDays: toDays10(props.log.retention)
7830
+ });
7831
+ if (ctx.shared.has("on-error-log", "subscriber-arn")) {
7832
+ new aws28.cloudwatch.LogSubscriptionFilter(group, "on-error-log", {
7833
+ name: "error-log-subscription",
7834
+ destinationArn: ctx.shared.get("on-error-log", "subscriber-arn"),
7835
+ logGroupName: logGroup.name,
7836
+ filterPattern
7837
+ });
7838
+ }
7839
+ }
7840
+ const tags = {
7841
+ APP: ctx.appConfig.name,
7842
+ APP_ID: ctx.appId,
7843
+ STACK: ctx.stackConfig.name
7844
+ };
7845
+ const variables = {};
7846
+ const variableDeps = /* @__PURE__ */ new Set();
7847
+ const task2 = new aws28.ecs.TaskDefinition(
7848
+ group,
7849
+ "task",
7850
+ {
7851
+ family: name,
7852
+ networkMode: "awsvpc",
7853
+ cpu: props.cpu,
7854
+ memory: toMebibytes6(props.memorySize).toString(),
7855
+ requiresCompatibilities: ["FARGATE"],
7856
+ executionRoleArn: executionRole.arn,
7857
+ taskRoleArn: role.arn,
7858
+ runtimePlatform: {
7859
+ cpuArchitecture: constantCase13(props.architecture),
7860
+ operatingSystemFamily: "LINUX"
7861
+ },
7862
+ trackLatest: true,
7863
+ containerDefinitions: new Output8(variableDeps, async (resolve2) => {
7864
+ const data = await resolveInputs5(variables);
7865
+ const { s3Bucket, s3Key } = await resolveInputs5({
7866
+ s3Bucket: code.bucket,
7867
+ s3Key: code.key
7868
+ });
7869
+ resolve2(
7870
+ JSON.stringify([
7871
+ {
7872
+ name: `container-${id}`,
7873
+ essential: true,
7874
+ image: image2,
7875
+ protocol: "tcp",
7876
+ workingDirectory: "/usr/app",
7877
+ entryPoint: ["sh", "-c"],
7878
+ command: [
7879
+ [
7880
+ `aws s3 cp s3://${s3Bucket}/${s3Key} /usr/app/program`,
7881
+ `chmod +x /usr/app/program`,
7882
+ ...props.startupCommand ?? [],
7883
+ `exec /usr/app/program`
7884
+ ].join(" && ")
7885
+ ],
7886
+ environment: Object.entries(data).map(([name2, value]) => ({
7887
+ name: name2,
7888
+ value
7889
+ })),
7890
+ portMappings: [
7891
+ {
7892
+ name: "http",
7893
+ protocol: "tcp",
7894
+ appProtocol: "http",
7895
+ containerPort: 80,
7896
+ hostPort: 80
7897
+ }
7898
+ ],
7899
+ restartPolicy: {
7900
+ enabled: true,
7901
+ restartAttemptPeriod: 60
7902
+ },
7903
+ ...logGroup && {
7904
+ logConfiguration: {
7905
+ logDriver: "awslogs",
7906
+ options: {
7907
+ // 'awslogs-group': `/aws/ecs/${name}`,
7908
+ "awslogs-group": `/aws/ecs/${name}`,
7909
+ "awslogs-region": ctx.appConfig.region,
7910
+ "awslogs-stream-prefix": "ecs",
7911
+ mode: "non-blocking"
7912
+ // 'awslogs-multiline-pattern': '',
7913
+ // 'max-buffer-size': '100m',
7914
+ }
7915
+ }
7916
+ },
7917
+ healthCheck: props.healthCheck ? {
7918
+ command: [
7919
+ "CMD-SHELL",
7920
+ `curl -f http://${join19("localhost", props.healthCheck.path)} || exit 1`
7921
+ ],
7922
+ interval: toSeconds10(props.healthCheck.interval),
7923
+ retries: props.healthCheck.retries,
7924
+ startPeriod: toSeconds10(props.healthCheck.startPeriod),
7925
+ timeout: toSeconds10(props.healthCheck.timeout)
7926
+ } : void 0
7927
+ }
7928
+ ])
7929
+ );
7930
+ }),
7931
+ tags
7932
+ },
7933
+ {
7934
+ replaceOnChanges: [
7935
+ "containerDefinitions",
7936
+ "cpu",
7937
+ "memory",
7938
+ "runtimePlatform",
7939
+ "executionRoleArn",
7940
+ "taskRoleArn"
7941
+ ],
7942
+ dependsOn: [code]
7943
+ }
7944
+ );
7945
+ const securityGroup = new aws28.security.Group(group, "security-group", {
7946
+ name,
7947
+ description: "Security group for the instance",
7948
+ vpcId: ctx.shared.get("vpc", "id"),
7949
+ revokeRulesOnDelete: true,
7950
+ tags
7951
+ });
7952
+ new aws28.vpc.SecurityGroupEgressRule(group, "egress-rule", {
7953
+ securityGroupId: securityGroup.id,
7954
+ description: `Allow all outbound traffic from the ${name} instance`,
7955
+ ipProtocol: "-1",
7956
+ cidrIpv4: "0.0.0.0/0",
7957
+ tags
7958
+ });
7959
+ const clusterName = ctx.shared.get("instance", "cluster-name");
7960
+ const clusterArn = ctx.shared.get("instance", "cluster-arn");
7961
+ const service = new aws28.ecs.Service(group, "service", {
7962
+ name,
7963
+ cluster: clusterArn,
7964
+ taskDefinition: task2.arn,
7965
+ desiredCount: 1,
7966
+ launchType: "FARGATE",
7967
+ networkConfiguration: {
7968
+ subnets: ctx.shared.get("vpc", "public-subnets"),
7969
+ securityGroups: [securityGroup.id],
7970
+ assignPublicIp: true
7971
+ // https://stackoverflow.com/questions/76398247/cannotpullcontainererror-pull-image-manifest-has-been-retried-5-times-failed
7972
+ },
7973
+ forceNewDeployment: true,
7974
+ forceDelete: true,
7975
+ tags,
7976
+ // ------------------------------------------------------------
7977
+ // Deployment safeguards: keep the service pinned to one running task.
7978
+ schedulingStrategy: "REPLICA",
7979
+ deploymentMaximumPercent: 100,
7980
+ deploymentMinimumHealthyPercent: 0,
7981
+ deploymentCircuitBreaker: {
7982
+ enable: true,
7983
+ rollback: true
7984
+ },
7985
+ // ------------------------------------------------------------
7986
+ // Tag hygiene: let ECS manage and propagate runtime tags automatically.
7433
7987
  enableEcsManagedTags: true,
7434
7988
  propagateTags: "SERVICE"
7435
7989
  });
7436
- new aws26.appautoscaling.Target(
7990
+ new aws28.appautoscaling.Target(
7437
7991
  group,
7438
7992
  "autoscaling-target",
7439
7993
  {
@@ -7452,7 +8006,7 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7452
8006
  );
7453
8007
  ctx.onEnv((name2, value) => {
7454
8008
  variables[name2] = value;
7455
- for (const dep of findInputDeps3([value])) {
8009
+ for (const dep of findInputDeps5([value])) {
7456
8010
  variableDeps.add(dep);
7457
8011
  }
7458
8012
  });
@@ -7461,6 +8015,11 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7461
8015
  variables.AWS_ACCOUNT_ID = ctx.accountId;
7462
8016
  variables.STACK = ctx.stackConfig.name;
7463
8017
  variables.CODE_HASH = code.sourceHash;
8018
+ if (props.environment) {
8019
+ for (const [key, value] of Object.entries(props.environment)) {
8020
+ variables[key] = value;
8021
+ }
8022
+ }
7464
8023
  if (ctx.appConfig.defaults.instance.permissions) {
7465
8024
  statements.push(...ctx.appConfig.defaults.instance.permissions);
7466
8025
  }
@@ -7474,8 +8033,8 @@ var createFargateTask = (parentGroup, ctx, ns, id, local) => {
7474
8033
  var instanceFeature = defineFeature({
7475
8034
  name: "instance",
7476
8035
  onBefore(ctx) {
7477
- const group = new Group26(ctx.base, "instance", "asset");
7478
- const bucket = new aws27.s3.Bucket(group, "bucket", {
8036
+ const group = new Group28(ctx.base, "instance", "asset");
8037
+ const bucket = new aws29.s3.Bucket(group, "bucket", {
7479
8038
  bucket: formatGlobalResourceName({
7480
8039
  appName: ctx.app.name,
7481
8040
  resourceType: "instance",
@@ -7493,8 +8052,8 @@ var instanceFeature = defineFeature({
7493
8052
  if (found.length === 0) {
7494
8053
  return;
7495
8054
  }
7496
- const group = new Group26(ctx.base, "instance", "cluster");
7497
- const cluster = new aws27.ecs.Cluster(group, "cluster", {
8055
+ const group = new Group28(ctx.base, "instance", "cluster");
8056
+ const cluster = new aws29.ecs.Cluster(group, "cluster", {
7498
8057
  name: ctx.app.name
7499
8058
  });
7500
8059
  ctx.shared.set("instance", "cluster-name", cluster.name);
@@ -7502,18 +8061,18 @@ var instanceFeature = defineFeature({
7502
8061
  },
7503
8062
  onStack(ctx) {
7504
8063
  for (const [id, props] of Object.entries(ctx.stackConfig.instances ?? {})) {
7505
- const group = new Group26(ctx.stack, "instance", id);
8064
+ const group = new Group28(ctx.stack, "instance", id);
7506
8065
  createFargateTask(group, ctx, "instance", id, props);
7507
8066
  }
7508
8067
  }
7509
8068
  });
7510
8069
 
7511
8070
  // src/feature/metric/index.ts
7512
- import { Group as Group27 } from "@terraforge/core";
7513
- import { aws as aws28 } from "@terraforge/aws";
7514
- import { kebabCase as kebabCase8, constantCase as constantCase13 } from "change-case";
7515
- import { toSeconds as toSeconds10 } from "@awsless/duration";
7516
- var typeGenCode10 = `
8071
+ import { Group as Group29 } from "@terraforge/core";
8072
+ import { aws as aws30 } from "@terraforge/aws";
8073
+ import { kebabCase as kebabCase8, constantCase as constantCase14 } from "change-case";
8074
+ import { toSeconds as toSeconds11 } from "@awsless/duration";
8075
+ var typeGenCode11 = `
7517
8076
  import { type PutDataProps, putData, batchPutData } from '@awsless/cloudwatch'
7518
8077
  import { type Duration } from '@awsless/duration'
7519
8078
  import { type Size } from '@awsless/size'
@@ -7561,7 +8120,7 @@ var metricFeature = defineFeature({
7561
8120
  resources2.addType(stack.name, stackResources);
7562
8121
  }
7563
8122
  resources2.addType("batch", "Batch");
7564
- gen.addCode(typeGenCode10);
8123
+ gen.addCode(typeGenCode11);
7565
8124
  gen.addInterface("MetricResources", resources2);
7566
8125
  await ctx.write("metric.d.ts", gen, true);
7567
8126
  },
@@ -7578,16 +8137,16 @@ var metricFeature = defineFeature({
7578
8137
  }
7579
8138
  });
7580
8139
  for (const [id, props] of Object.entries(ctx.stackConfig.metrics ?? {})) {
7581
- const group = new Group27(ctx.stack, "metric", id);
7582
- ctx.addEnv(`METRIC_${constantCase13(id)}`, props.type);
8140
+ const group = new Group29(ctx.stack, "metric", id);
8141
+ ctx.addEnv(`METRIC_${constantCase14(id)}`, props.type);
7583
8142
  for (const alarmId in props.alarms ?? []) {
7584
- const alarmGroup = new Group27(group, "alarm", alarmId);
8143
+ const alarmGroup = new Group29(group, "alarm", alarmId);
7585
8144
  const alarmName = kebabCase8(`${id}-${alarmId}`);
7586
8145
  const alarmProps = props.alarms[alarmId];
7587
8146
  let alarmAction;
7588
8147
  let alarmLambda;
7589
8148
  if (Array.isArray(alarmProps.trigger)) {
7590
- const topic = new aws28.sns.Topic(alarmGroup, "alarm-trigger", {
8149
+ const topic = new aws30.sns.Topic(alarmGroup, "alarm-trigger", {
7591
8150
  name: formatLocalResourceName({
7592
8151
  appName: ctx.app.name,
7593
8152
  stackName: ctx.stack.name,
@@ -7597,7 +8156,7 @@ var metricFeature = defineFeature({
7597
8156
  });
7598
8157
  alarmAction = topic.arn;
7599
8158
  for (const email of alarmProps.trigger) {
7600
- new aws28.sns.TopicSubscription(alarmGroup, email, {
8159
+ new aws30.sns.TopicSubscription(alarmGroup, email, {
7601
8160
  topicArn: topic.arn,
7602
8161
  protocol: "email",
7603
8162
  endpoint: email
@@ -7608,7 +8167,7 @@ var metricFeature = defineFeature({
7608
8167
  alarmLambda = lambda;
7609
8168
  alarmAction = lambda.arn;
7610
8169
  }
7611
- const alarm = new aws28.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
8170
+ const alarm = new aws30.cloudwatch.MetricAlarm(alarmGroup, "alarm", {
7612
8171
  namespace,
7613
8172
  metricName: kebabCase8(id),
7614
8173
  alarmName: formatLocalResourceName({
@@ -7620,13 +8179,13 @@ var metricFeature = defineFeature({
7620
8179
  alarmDescription: alarmProps.description,
7621
8180
  statistic: alarmProps.where.stat,
7622
8181
  threshold: alarmProps.where.value,
7623
- period: toSeconds10(alarmProps.period),
8182
+ period: toSeconds11(alarmProps.period),
7624
8183
  evaluationPeriods: alarmProps.minDataPoints,
7625
8184
  comparisonOperator: alarmProps.where.op,
7626
8185
  alarmActions: [alarmAction]
7627
8186
  });
7628
8187
  if (alarmLambda) {
7629
- new aws28.lambda.Permission(alarmGroup, "permission", {
8188
+ new aws30.lambda.Permission(alarmGroup, "permission", {
7630
8189
  action: "lambda:InvokeFunction",
7631
8190
  principal: "lambda.alarms.cloudwatch.amazonaws.com",
7632
8191
  functionName: alarmLambda.functionName,
@@ -7639,10 +8198,10 @@ var metricFeature = defineFeature({
7639
8198
  });
7640
8199
 
7641
8200
  // src/feature/router/index.ts
7642
- import { days as days8, seconds as seconds10, toSeconds as toSeconds11, years } from "@awsless/duration";
7643
- import { Future, Group as Group28 } from "@terraforge/core";
7644
- import { aws as aws29 } from "@terraforge/aws";
7645
- import { camelCase as camelCase8, constantCase as constantCase14 } from "change-case";
8201
+ import { days as days9, seconds as seconds10, toSeconds as toSeconds12, years } from "@awsless/duration";
8202
+ import { Future, Group as Group30 } from "@terraforge/core";
8203
+ import { aws as aws31 } from "@terraforge/aws";
8204
+ import { camelCase as camelCase9, constantCase as constantCase15 } from "change-case";
7646
8205
 
7647
8206
  // src/feature/router/router-code.ts
7648
8207
  var getViewerRequestFunctionCode = (props) => {
@@ -7875,18 +8434,18 @@ async function handler(event) {
7875
8434
  `;
7876
8435
 
7877
8436
  // src/feature/router/index.ts
7878
- import { createHash as createHash4 } from "crypto";
8437
+ import { createHash as createHash5 } from "crypto";
7879
8438
  var routerFeature = defineFeature({
7880
8439
  name: "router",
7881
8440
  onApp(ctx) {
7882
8441
  for (const [id, props] of Object.entries(ctx.appConfig.defaults.router ?? {})) {
7883
- const group = new Group28(ctx.base, "router", id);
8442
+ const group = new Group30(ctx.base, "router", id);
7884
8443
  const name = formatGlobalResourceName({
7885
8444
  appName: ctx.app.name,
7886
8445
  resourceType: "router",
7887
8446
  resourceName: id
7888
8447
  });
7889
- const routeStore = new aws29.cloudfront.KeyValueStore(group, "routes", {
8448
+ const routeStore = new aws31.cloudfront.KeyValueStore(group, "routes", {
7890
8449
  name,
7891
8450
  comment: "Store for routes"
7892
8451
  });
@@ -7916,11 +8475,11 @@ var routerFeature = defineFeature({
7916
8475
  );
7917
8476
  importedRoutes.push(importKeys);
7918
8477
  });
7919
- const cache = new aws29.cloudfront.CachePolicy(group, "cache", {
8478
+ const cache = new aws31.cloudfront.CachePolicy(group, "cache", {
7920
8479
  name,
7921
- minTtl: toSeconds11(seconds10(0)),
7922
- maxTtl: toSeconds11(days8(365)),
7923
- defaultTtl: toSeconds11(days8(0)),
8480
+ minTtl: toSeconds12(seconds10(0)),
8481
+ maxTtl: toSeconds12(days9(365)),
8482
+ defaultTtl: toSeconds12(days9(0)),
7924
8483
  parametersInCacheKeyAndForwardedToOrigin: {
7925
8484
  enableAcceptEncodingBrotli: true,
7926
8485
  enableAcceptEncodingGzip: true,
@@ -7948,10 +8507,10 @@ var routerFeature = defineFeature({
7948
8507
  }
7949
8508
  }
7950
8509
  });
7951
- const originRequest = new aws29.cloudfront.OriginRequestPolicy(group, "request", {
8510
+ const originRequest = new aws31.cloudfront.OriginRequestPolicy(group, "request", {
7952
8511
  name,
7953
8512
  headersConfig: {
7954
- headerBehavior: camelCase8("all-except"),
8513
+ headerBehavior: camelCase9("all-except"),
7955
8514
  headers: {
7956
8515
  items: [
7957
8516
  "host"
@@ -7966,11 +8525,11 @@ var routerFeature = defineFeature({
7966
8525
  queryStringBehavior: "all"
7967
8526
  }
7968
8527
  });
7969
- const responseHeaders = new aws29.cloudfront.ResponseHeadersPolicy(group, "response", {
8528
+ const responseHeaders = new aws31.cloudfront.ResponseHeadersPolicy(group, "response", {
7970
8529
  name,
7971
8530
  corsConfig: {
7972
8531
  originOverride: props.cors?.override ?? true,
7973
- accessControlMaxAgeSec: toSeconds11(props.cors?.maxAge ?? years(1)),
8532
+ accessControlMaxAgeSec: toSeconds12(props.cors?.maxAge ?? years(1)),
7974
8533
  accessControlAllowHeaders: { items: props.cors?.headers ?? ["*"] },
7975
8534
  accessControlAllowMethods: { items: props.cors?.methods ?? ["ALL"] },
7976
8535
  accessControlAllowOrigins: { items: props.cors?.origins ?? ["*"] },
@@ -7995,7 +8554,7 @@ var routerFeature = defineFeature({
7995
8554
  strictTransportSecurity: {
7996
8555
  override: true,
7997
8556
  preload: true,
7998
- accessControlMaxAgeSec: toSeconds11(years(1)),
8557
+ accessControlMaxAgeSec: toSeconds12(years(1)),
7999
8558
  includeSubdomains: true
8000
8559
  },
8001
8560
  xssProtection: {
@@ -8005,7 +8564,7 @@ var routerFeature = defineFeature({
8005
8564
  }
8006
8565
  }
8007
8566
  });
8008
- const viewerRequest = new aws29.cloudfront.Function(group, "viewer-request", {
8567
+ const viewerRequest = new aws31.cloudfront.Function(group, "viewer-request", {
8009
8568
  name,
8010
8569
  runtime: `cloudfront-js-2.0`,
8011
8570
  comment: `Viewer Request - ${name}`,
@@ -8027,7 +8586,7 @@ var routerFeature = defineFeature({
8027
8586
  rateBasedStatement: {
8028
8587
  limit: wafSettingsConfig.rateLimiter.limit,
8029
8588
  aggregateKeyType: "IP",
8030
- evaluationWindowSec: toSeconds11(wafSettingsConfig.rateLimiter.window)
8589
+ evaluationWindowSec: toSeconds12(wafSettingsConfig.rateLimiter.window)
8031
8590
  }
8032
8591
  },
8033
8592
  action: {
@@ -8107,7 +8666,7 @@ var routerFeature = defineFeature({
8107
8666
  }
8108
8667
  let waf;
8109
8668
  if (wafRules.length && wafSettingsConfig) {
8110
- waf = new aws29.wafv2.WebAcl(group, "waf", {
8669
+ waf = new aws31.wafv2.WebAcl(group, "waf", {
8111
8670
  name: `${name}-wafv2`,
8112
8671
  scope: "CLOUDFRONT",
8113
8672
  defaultAction: {
@@ -8117,12 +8676,12 @@ var routerFeature = defineFeature({
8117
8676
  rule: wafRules,
8118
8677
  captchaConfig: {
8119
8678
  immunityTimeProperty: {
8120
- immunityTime: toSeconds11(wafSettingsConfig.captchaImmunityTime)
8679
+ immunityTime: toSeconds12(wafSettingsConfig.captchaImmunityTime)
8121
8680
  }
8122
8681
  },
8123
8682
  challengeConfig: {
8124
8683
  immunityTimeProperty: {
8125
- immunityTime: toSeconds11(wafSettingsConfig.challengeImmunityTime)
8684
+ immunityTime: toSeconds12(wafSettingsConfig.challengeImmunityTime)
8126
8685
  }
8127
8686
  },
8128
8687
  visibilityConfig: {
@@ -8132,7 +8691,7 @@ var routerFeature = defineFeature({
8132
8691
  }
8133
8692
  });
8134
8693
  }
8135
- const distribution = new aws29.cloudfront.MultitenantDistribution(group, "distribution", {
8694
+ const distribution = new aws31.cloudfront.MultitenantDistribution(group, "distribution", {
8136
8695
  tags: {
8137
8696
  name
8138
8697
  },
@@ -8178,7 +8737,7 @@ var routerFeature = defineFeature({
8178
8737
  }
8179
8738
  return {
8180
8739
  errorCode: Number(errorCode),
8181
- errorCachingMinTtl: item.minTTL ? toSeconds11(item.minTTL) : void 0,
8740
+ errorCachingMinTtl: item.minTTL ? toSeconds12(item.minTTL) : void 0,
8182
8741
  responseCode: item.statusCode?.toString() ?? errorCode,
8183
8742
  responsePagePath: item.path
8184
8743
  };
@@ -8226,11 +8785,11 @@ var routerFeature = defineFeature({
8226
8785
  {
8227
8786
  distributionId: distribution.id,
8228
8787
  paths,
8229
- version: new Future((resolve) => {
8788
+ version: new Future((resolve2) => {
8230
8789
  $combine(...versions).then((versions2) => {
8231
8790
  const combined = versions2.filter((v) => !!v).sort().join(",");
8232
- const version = createHash4("sha1").update(combined).digest("hex");
8233
- resolve(version);
8791
+ const version = createHash5("sha1").update(combined).digest("hex");
8792
+ resolve2(version);
8234
8793
  });
8235
8794
  })
8236
8795
  },
@@ -8244,10 +8803,10 @@ var routerFeature = defineFeature({
8244
8803
  const domainName = formatFullDomainName(ctx.appConfig, props.domain, props.subDomain);
8245
8804
  const certificateArn = ctx.shared.entry("domain", `global-certificate-arn`, props.domain);
8246
8805
  const zoneId = ctx.shared.entry("domain", "zone-id", props.domain);
8247
- const connectionGroup = new aws29.cloudfront.ConnectionGroup(group, "connection-group", {
8806
+ const connectionGroup = new aws31.cloudfront.ConnectionGroup(group, "connection-group", {
8248
8807
  name
8249
8808
  });
8250
- new aws29.cloudfront.DistributionTenant(group, `tenant`, {
8809
+ new aws31.cloudfront.DistributionTenant(group, `tenant`, {
8251
8810
  name,
8252
8811
  enabled: true,
8253
8812
  distributionId: distribution.id,
@@ -8255,7 +8814,7 @@ var routerFeature = defineFeature({
8255
8814
  domain: [{ domain: domainName }],
8256
8815
  customizations: [{ certificate: [{ arn: certificateArn }] }]
8257
8816
  });
8258
- new aws29.route53.Record(group, `record`, {
8817
+ new aws31.route53.Record(group, `record`, {
8259
8818
  zoneId,
8260
8819
  type: "A",
8261
8820
  name: domainName,
@@ -8265,7 +8824,7 @@ var routerFeature = defineFeature({
8265
8824
  evaluateTargetHealth: false
8266
8825
  }
8267
8826
  });
8268
- ctx.bind(`ROUTER_${constantCase14(id)}_ENDPOINT`, domainName);
8827
+ ctx.bind(`ROUTER_${constantCase15(id)}_ENDPOINT`, domainName);
8269
8828
  }
8270
8829
  }
8271
8830
  }
@@ -8288,6 +8847,7 @@ var features = [
8288
8847
  // 5
8289
8848
  functionFeature,
8290
8849
  instanceFeature,
8850
+ jobFeature,
8291
8851
  // graphqlFeature,
8292
8852
  configFeature,
8293
8853
  searchFeature,
@@ -9376,14 +9936,14 @@ import wildstring4 from "wildstring";
9376
9936
 
9377
9937
  // src/cli/ui/complex/run-tests.ts
9378
9938
  import { log as log18 } from "@awsless/clui";
9379
- import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
9380
- import { join as join20 } from "path";
9939
+ import { mkdir as mkdir4, readFile as readFile6, writeFile as writeFile4 } from "fs/promises";
9940
+ import { join as join21 } from "path";
9381
9941
  import wildstring3 from "wildstring";
9382
9942
  import { parse as parse4, stringify } from "@awsless/json";
9383
9943
  import { generateFolderHash, loadWorkspace as loadWorkspace2 } from "@awsless/ts-file-cache";
9384
9944
 
9385
9945
  // src/test/start.ts
9386
- import { dirname as dirname9, join as join19 } from "path";
9946
+ import { dirname as dirname9, join as join20 } from "path";
9387
9947
  import { fileURLToPath as fileURLToPath4 } from "url";
9388
9948
  import { configDefaults } from "vitest/config";
9389
9949
  import { startVitest } from "vitest/node";
@@ -9421,7 +9981,7 @@ var startTest = async (props) => {
9421
9981
  // },
9422
9982
  setupFiles: [
9423
9983
  //
9424
- join19(__dirname5, "test-global-setup.js")
9984
+ join20(__dirname5, "test-global-setup.js")
9425
9985
  ]
9426
9986
  // globalSetup: [
9427
9987
  // //
@@ -9615,12 +10175,12 @@ var logTestErrors = (event) => {
9615
10175
  };
9616
10176
  var runTest = async (stack, dir, filters, workspace, opts) => {
9617
10177
  await mkdir4(directories.test, { recursive: true });
9618
- const file = join20(directories.test, `${stack}.json`);
10178
+ const file = join21(directories.test, `${stack}.json`);
9619
10179
  const fingerprint = await generateFolderHash(workspace, dir);
9620
10180
  if (!process.env.NO_CACHE) {
9621
10181
  const exists = await fileExist(file);
9622
10182
  if (exists) {
9623
- const raw = await readFile5(file, { encoding: "utf8" });
10183
+ const raw = await readFile6(file, { encoding: "utf8" });
9624
10184
  const data = parse4(raw);
9625
10185
  if (data.fingerprint === fingerprint) {
9626
10186
  log18.step(
@@ -9663,7 +10223,7 @@ var runTest = async (stack, dir, filters, workspace, opts) => {
9663
10223
  logTestLogs(result);
9664
10224
  }
9665
10225
  logTestErrors(result);
9666
- await writeFile3(
10226
+ await writeFile4(
9667
10227
  file,
9668
10228
  stringify({
9669
10229
  ...result,
@@ -10203,7 +10763,7 @@ var auth = (program2) => {
10203
10763
  // src/cli/command/bind.ts
10204
10764
  import { log as log24 } from "@awsless/clui";
10205
10765
  import chalk4 from "chalk";
10206
- import { constantCase as constantCase15 } from "change-case";
10766
+ import { constantCase as constantCase16 } from "change-case";
10207
10767
  var bind = (program2) => {
10208
10768
  program2.command("bind").argument("[command...]", "The command to execute").option("--config <string...>", "List of config values that will be accessable", (v) => v.split(",")).description(`Bind your site environment variables to a command`).action(async (commands11 = [], opts) => {
10209
10769
  await layout("bind", async ({ appConfig, stackConfigs }) => {
@@ -10230,10 +10790,10 @@ var bind = (program2) => {
10230
10790
  const configList = opts.config ?? [];
10231
10791
  const configs = {};
10232
10792
  for (const name of configList) {
10233
- configs[`CONFIG_${constantCase15(name)}`] = name;
10793
+ configs[`CONFIG_${constantCase16(name)}`] = name;
10234
10794
  }
10235
10795
  if (configList.length ?? 0 > 0) {
10236
- log24.note("Bind Config", configList.map((v) => color.label(constantCase15(v))).join("\n"));
10796
+ log24.note("Bind Config", configList.map((v) => color.label(constantCase16(v))).join("\n"));
10237
10797
  }
10238
10798
  if (commands11.length === 0) {
10239
10799
  return "No command to execute.";
@@ -10272,7 +10832,7 @@ var bind = (program2) => {
10272
10832
 
10273
10833
  // src/config/load/watch.ts
10274
10834
  import { watch } from "chokidar";
10275
- var watchConfig = async (options, resolve, reject) => {
10835
+ var watchConfig = async (options, resolve2, reject) => {
10276
10836
  await loadAppConfig(options);
10277
10837
  debug("Start watching...");
10278
10838
  const ext = "{json,jsonc,json5}";
@@ -10287,7 +10847,7 @@ var watchConfig = async (options, resolve, reject) => {
10287
10847
  const appConfig = await loadAppConfig(options);
10288
10848
  const stackConfigs = await loadStackConfigs(options);
10289
10849
  validateFeatures({ appConfig, stackConfigs });
10290
- resolve({ appConfig, stackConfigs });
10850
+ resolve2({ appConfig, stackConfigs });
10291
10851
  } catch (error) {
10292
10852
  reject(error);
10293
10853
  }
@@ -10299,8 +10859,8 @@ var watchConfig = async (options, resolve, reject) => {
10299
10859
  import { log as log25 } from "@awsless/clui";
10300
10860
 
10301
10861
  // src/type-gen/generate.ts
10302
- import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
10303
- import { dirname as dirname10, join as join21, relative as relative8 } from "path";
10862
+ import { mkdir as mkdir5, writeFile as writeFile5 } from "fs/promises";
10863
+ import { dirname as dirname10, join as join22, relative as relative9 } from "path";
10304
10864
  var generateTypes = async (props) => {
10305
10865
  const files = [];
10306
10866
  await Promise.all(
@@ -10309,13 +10869,13 @@ var generateTypes = async (props) => {
10309
10869
  ...props,
10310
10870
  async write(file, data, include = false) {
10311
10871
  const code = data?.toString("utf8");
10312
- const path = join21(directories.types, file);
10872
+ const path = join22(directories.types, file);
10313
10873
  if (code) {
10314
10874
  if (include) {
10315
- files.push(relative8(directories.root, path));
10875
+ files.push(relative9(directories.root, path));
10316
10876
  }
10317
10877
  await mkdir5(dirname10(path), { recursive: true });
10318
- await writeFile4(path, code);
10878
+ await writeFile5(path, code);
10319
10879
  }
10320
10880
  }
10321
10881
  });
@@ -10323,7 +10883,7 @@ var generateTypes = async (props) => {
10323
10883
  );
10324
10884
  if (files.length) {
10325
10885
  const code = files.map((file) => `/// <reference path='${file}' />`).join("\n");
10326
- await writeFile4(join21(directories.root, `awsless.d.ts`), code);
10886
+ await writeFile5(join22(directories.root, `awsless.d.ts`), code);
10327
10887
  }
10328
10888
  };
10329
10889
 
@@ -10347,9 +10907,9 @@ var dev = (program2) => {
10347
10907
  logError(error);
10348
10908
  }
10349
10909
  );
10350
- await new Promise((resolve) => {
10351
- process.once("exit", resolve);
10352
- process.once("SIGINT", resolve);
10910
+ await new Promise((resolve2) => {
10911
+ process.once("exit", resolve2);
10912
+ process.once("SIGINT", resolve2);
10353
10913
  });
10354
10914
  });
10355
10915
  });
@@ -10749,7 +11309,7 @@ var domain = (program2) => {
10749
11309
  // src/cli/command/logs.ts
10750
11310
  import { CloudWatchLogsClient, StartLiveTailCommand } from "@aws-sdk/client-cloudwatch-logs";
10751
11311
  import { log as log30 } from "@awsless/clui";
10752
- import { aws as aws30 } from "@terraforge/aws";
11312
+ import { aws as aws32 } from "@terraforge/aws";
10753
11313
  import chalk6 from "chalk";
10754
11314
  import chunk2 from "chunk";
10755
11315
  import { formatDate } from "date-fns";
@@ -10772,7 +11332,7 @@ var logs = (program2) => {
10772
11332
  for (const stack of app.stacks) {
10773
11333
  if (filters.find((f) => wildstring7.match(f, stack.name))) {
10774
11334
  for (const resource of stack.resources) {
10775
- if (resource instanceof aws30.cloudwatch.LogGroup) {
11335
+ if (resource instanceof aws32.cloudwatch.LogGroup) {
10776
11336
  logGroupArns.push(await resource.arn);
10777
11337
  }
10778
11338
  }