@awsless/awsless 0.0.509 → 0.0.511

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
@@ -833,7 +833,7 @@ var LogRetentionSchema = DurationSchema.refine(
833
833
  (duration) => {
834
834
  return validLogRetentionDays.includes(toDays(duration));
835
835
  },
836
- `Invalid log retention. Valid days are: ${validLogRetentionDays.map((days7) => `${days7}`).join(", ")}`
836
+ `Invalid log retention. Valid days are: ${validLogRetentionDays.map((days8) => `${days8}`).join(", ")}`
837
837
  ).describe("The log retention duration.");
838
838
  var LogSchema = z14.union([
839
839
  z14.boolean().transform((enabled) => ({ retention: enabled ? days(7) : days(0) })),
@@ -912,11 +912,11 @@ var FunctionDefaultSchema = z14.object({
912
912
  // container
913
913
  warm: WarmSchema.default(0),
914
914
  vpc: VPCSchema.default(false),
915
- log: LogSchema.default(true).transform((log23) => ({
916
- retention: log23.retention ?? days(7),
917
- level: "level" in log23 ? log23.level : "error",
918
- system: "system" in log23 ? log23.system : "warn",
919
- format: "format" in log23 ? log23.format : "json"
915
+ log: LogSchema.default(true).transform((log25) => ({
916
+ retention: log25.retention ?? days(7),
917
+ level: "level" in log25 ? log25.level : "error",
918
+ system: "system" in log25 ? log25.system : "warn",
919
+ format: "format" in log25 ? log25.format : "json"
920
920
  })),
921
921
  timeout: TimeoutSchema.default("10 seconds"),
922
922
  memorySize: MemorySizeSchema.default("128 MB"),
@@ -1213,7 +1213,7 @@ var AppSchema = z24.object({
1213
1213
  });
1214
1214
 
1215
1215
  // src/config/stack.ts
1216
- import { z as z39 } from "zod";
1216
+ import { z as z40 } from "zod";
1217
1217
 
1218
1218
  // src/feature/cache/schema.ts
1219
1219
  import { gibibytes as gibibytes2 } from "@awsless/size";
@@ -1657,44 +1657,22 @@ var StoresSchema = z33.union([
1657
1657
  )
1658
1658
  ]).optional().describe("Define the stores in your stack.");
1659
1659
 
1660
- // src/feature/images/schema.ts
1660
+ // src/feature/icon/schema.ts
1661
1661
  import { z as z34 } from "zod";
1662
- var transformationOptionsSchema = z34.object({
1663
- width: z34.number().int().positive().optional(),
1664
- height: z34.number().int().positive().optional(),
1665
- fit: z34.enum(["cover", "contain", "fill", "inside", "outside"]).optional(),
1666
- position: z34.enum(["top", "right top", "right", "right bottom", "bottom", "left bottom", "left", "left top", "center"]).optional(),
1667
- quality: z34.number().int().min(1).max(100).optional()
1668
- });
1669
1662
  var staticOriginSchema = LocalDirectorySchema.describe(
1670
- "Specifies the path to a image directory that will be uploaded in S3."
1663
+ "Specifies the path to a local image directory that will be uploaded in S3."
1671
1664
  );
1672
1665
  var functionOriginSchema = FunctionSchema.describe(
1673
- "Specifies the file that will be called when an image isn't found in the S3 bucket."
1666
+ "Specifies the file that will be called when an image isn't found in the (cache) bucket."
1674
1667
  );
1675
- var ImagesSchema = z34.record(
1668
+ var IconsSchema = z34.record(
1676
1669
  ResourceIdSchema,
1677
1670
  z34.object({
1678
1671
  domain: ResourceIdSchema.describe("The domain id to link your site with.").optional(),
1679
1672
  subDomain: z34.string().optional(),
1680
1673
  log: LogSchema.optional(),
1681
- presets: z34.record(z34.string(), transformationOptionsSchema).describe("Named presets for image transformations"),
1682
- extensions: z34.object({
1683
- jpeg: z34.object({
1684
- mozjpeg: z34.boolean().optional(),
1685
- progressive: z34.boolean().optional()
1686
- }).optional(),
1687
- webp: z34.object({
1688
- effort: z34.number().int().min(0).max(6).optional(),
1689
- lossless: z34.boolean().optional(),
1690
- nearLossless: z34.boolean().optional()
1691
- }).optional(),
1692
- png: z34.object({
1693
- compressionLevel: z34.number().int().min(0).max(9).optional()
1694
- }).optional()
1695
- }).refine((data) => {
1696
- return Object.keys(data).length > 0;
1697
- }, "At least one extension must be defined.").describe("Image format-specific options for transformations"),
1674
+ preserveId: z34.boolean().optional().default(false).describe("Preserve the IDs of the icons."),
1675
+ symbols: z34.boolean().optional().default(false).describe("Use SVG symbols for icons."),
1698
1676
  origin: z34.union([
1699
1677
  z34.object({
1700
1678
  static: staticOriginSchema,
@@ -1711,29 +1689,86 @@ var ImagesSchema = z34.record(
1711
1689
  ]).describe(
1712
1690
  "Image transformation will be applied from a base image. Base images orginates from a local directory that will be uploaded to S3 or from a lambda function."
1713
1691
  ),
1714
- version: z34.number().int().min(1).optional().describe("Version of the image configuration.")
1715
- // postprocess: FunctionSchema.optional()
1692
+ version: z34.number().int().min(1).optional().describe("Version of the icon configuration.")
1716
1693
  })
1717
- ).optional().describe("Define image CDN & transformations in your stack.");
1694
+ ).optional().describe("Define an icon proxy in your stack. Store, optimize, and deliver icons at scale.");
1718
1695
 
1719
- // src/feature/table/schema.ts
1720
- import { minutes as minutes4, seconds as seconds4 } from "@awsless/duration";
1696
+ // src/feature/image/schema.ts
1721
1697
  import { z as z35 } from "zod";
1722
- var KeySchema = z35.string().min(1).max(255);
1723
- var TablesSchema = z35.record(
1698
+ var transformationOptionsSchema = z35.object({
1699
+ width: z35.number().int().positive().optional(),
1700
+ height: z35.number().int().positive().optional(),
1701
+ fit: z35.enum(["cover", "contain", "fill", "inside", "outside"]).optional(),
1702
+ position: z35.enum(["top", "right top", "right", "right bottom", "bottom", "left bottom", "left", "left top", "center"]).optional(),
1703
+ quality: z35.number().int().min(1).max(100).optional()
1704
+ });
1705
+ var staticOriginSchema2 = LocalDirectorySchema.describe(
1706
+ "Specifies the path to a local image directory that will be uploaded in S3."
1707
+ );
1708
+ var functionOriginSchema2 = FunctionSchema.describe(
1709
+ "Specifies the file that will be called when an image isn't found in the (cache) bucket."
1710
+ );
1711
+ var ImagesSchema = z35.record(
1724
1712
  ResourceIdSchema,
1725
1713
  z35.object({
1714
+ domain: ResourceIdSchema.describe("The domain id to link your site with.").optional(),
1715
+ subDomain: z35.string().optional(),
1716
+ log: LogSchema.optional(),
1717
+ presets: z35.record(z35.string(), transformationOptionsSchema).describe("Named presets for image transformations"),
1718
+ extensions: z35.object({
1719
+ jpeg: z35.object({
1720
+ mozjpeg: z35.boolean().optional(),
1721
+ progressive: z35.boolean().optional()
1722
+ }).optional(),
1723
+ webp: z35.object({
1724
+ effort: z35.number().int().min(1).max(10).default(7).optional(),
1725
+ lossless: z35.boolean().optional(),
1726
+ nearLossless: z35.boolean().optional()
1727
+ }).optional(),
1728
+ png: z35.object({
1729
+ compressionLevel: z35.number().int().min(0).max(9).default(6).optional()
1730
+ }).optional()
1731
+ }).refine((data) => {
1732
+ return Object.keys(data).length > 0;
1733
+ }, "At least one extension must be defined.").describe("Specify the allowed extensions."),
1734
+ origin: z35.union([
1735
+ z35.object({
1736
+ static: staticOriginSchema2,
1737
+ function: functionOriginSchema2.optional()
1738
+ }),
1739
+ z35.object({
1740
+ static: staticOriginSchema2.optional(),
1741
+ function: functionOriginSchema2
1742
+ }),
1743
+ z35.object({
1744
+ static: staticOriginSchema2,
1745
+ function: functionOriginSchema2
1746
+ })
1747
+ ]).describe(
1748
+ "Specify the origin of your images. Image transformation will be applied from a base image. Base images can be loaded from a S3 bucket (that is synced from a local directory) or dynamicly from a lambda function."
1749
+ ),
1750
+ version: z35.number().int().min(1).optional().describe("Version of the image configuration.")
1751
+ })
1752
+ ).optional().describe("Define an image proxy in your stack. Store, transform, optimize, and deliver images at scale.");
1753
+
1754
+ // src/feature/table/schema.ts
1755
+ import { minutes as minutes4, seconds as seconds4 } from "@awsless/duration";
1756
+ import { z as z36 } from "zod";
1757
+ var KeySchema = z36.string().min(1).max(255);
1758
+ var TablesSchema = z36.record(
1759
+ ResourceIdSchema,
1760
+ z36.object({
1726
1761
  hash: KeySchema.describe(
1727
1762
  "Specifies the name of the partition / hash key that makes up the primary key for the table."
1728
1763
  ),
1729
1764
  sort: KeySchema.optional().describe(
1730
1765
  "Specifies the name of the range / sort key that makes up the primary key for the table."
1731
1766
  ),
1732
- fields: z35.record(z35.string(), z35.enum(["string", "number", "binary"])).optional().describe(
1767
+ fields: z36.record(z36.string(), z36.enum(["string", "number", "binary"])).optional().describe(
1733
1768
  'A list of attributes that describe the key schema for the table and indexes. If no attribute field is defined we default to "string".'
1734
1769
  ),
1735
- class: z35.enum(["standard", "standard-infrequent-access"]).default("standard").describe("The table class of the table."),
1736
- pointInTimeRecovery: z35.boolean().default(false).describe("Indicates whether point in time recovery is enabled on the table."),
1770
+ class: z36.enum(["standard", "standard-infrequent-access"]).default("standard").describe("The table class of the table."),
1771
+ pointInTimeRecovery: z36.boolean().default(false).describe("Indicates whether point in time recovery is enabled on the table."),
1737
1772
  ttl: KeySchema.optional().describe(
1738
1773
  [
1739
1774
  "The name of the TTL attribute used to store the expiration time for items in the table.",
@@ -1741,8 +1776,8 @@ var TablesSchema = z35.record(
1741
1776
  ].join("\n")
1742
1777
  ),
1743
1778
  // deletionProtection: DeletionProtectionSchema.optional(),
1744
- stream: z35.object({
1745
- type: z35.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]).describe(
1779
+ stream: z36.object({
1780
+ type: z36.enum(["keys-only", "new-image", "old-image", "new-and-old-images"]).describe(
1746
1781
  [
1747
1782
  "When an item in the table is modified, you can determines what information is written to the stream for this table.",
1748
1783
  "Valid values are:",
@@ -1752,7 +1787,7 @@ var TablesSchema = z35.record(
1752
1787
  "- new-and-old-images - Both the new and the old item images of the item are written to the stream."
1753
1788
  ].join("\n")
1754
1789
  ),
1755
- batchSize: z35.number().min(1).max(1e4).default(1).describe(
1790
+ batchSize: z36.number().min(1).max(1e4).default(1).describe(
1756
1791
  [
1757
1792
  "The maximum number of records in each batch that Lambda pulls from your stream and sends to your function.",
1758
1793
  "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).",
@@ -1782,7 +1817,7 @@ var TablesSchema = z35.record(
1782
1817
  // 'You can specify a number from -1 to 10000.',
1783
1818
  // ].join('\n')
1784
1819
  // ),
1785
- retryAttempts: z35.number().min(-1).max(1e4).default(-1).describe(
1820
+ retryAttempts: z36.number().min(-1).max(1e4).default(-1).describe(
1786
1821
  [
1787
1822
  "Discard records after the specified number of retries.",
1788
1823
  "The default value is -1, which sets the maximum number of retries to infinite.",
@@ -1790,7 +1825,7 @@ var TablesSchema = z35.record(
1790
1825
  "You can specify a number from -1 to 10000."
1791
1826
  ].join("\n")
1792
1827
  ),
1793
- concurrencyPerShard: z35.number().min(1).max(10).default(1).describe(
1828
+ concurrencyPerShard: z36.number().min(1).max(10).default(1).describe(
1794
1829
  [
1795
1830
  "The number of batches to process concurrently from each shard.",
1796
1831
  "You can specify a number from 1 to 10."
@@ -1800,16 +1835,16 @@ var TablesSchema = z35.record(
1800
1835
  }).optional().describe(
1801
1836
  "The settings for the DynamoDB table stream, which capture changes to items stored in the table."
1802
1837
  ),
1803
- indexes: z35.record(
1804
- z35.string(),
1805
- z35.object({
1838
+ indexes: z36.record(
1839
+ z36.string(),
1840
+ z36.object({
1806
1841
  hash: KeySchema.describe(
1807
1842
  "Specifies the name of the partition / hash key that makes up the primary key for the global secondary index."
1808
1843
  ),
1809
1844
  sort: KeySchema.optional().describe(
1810
1845
  "Specifies the name of the range / sort key that makes up the primary key for the global secondary index."
1811
1846
  ),
1812
- projection: z35.enum(["all", "keys-only"]).default("all").describe(
1847
+ projection: z36.enum(["all", "keys-only"]).default("all").describe(
1813
1848
  [
1814
1849
  "The set of attributes that are projected into the index:",
1815
1850
  "- all - All of the table attributes are projected into the index.",
@@ -1823,11 +1858,11 @@ var TablesSchema = z35.record(
1823
1858
  ).optional().describe("Define the tables in your stack.");
1824
1859
 
1825
1860
  // src/feature/task/schema.ts
1826
- import { z as z36 } from "zod";
1827
- var RetryAttemptsSchema2 = z36.number().int().min(0).max(2).describe(
1861
+ import { z as z37 } from "zod";
1862
+ var RetryAttemptsSchema2 = z37.number().int().min(0).max(2).describe(
1828
1863
  "The maximum number of times to retry when the function returns an error. You can specify a number from 0 to 2."
1829
1864
  );
1830
- var TaskSchema = z36.union([
1865
+ var TaskSchema = z37.union([
1831
1866
  LocalFileSchema.transform((file) => ({
1832
1867
  consumer: {
1833
1868
  code: {
@@ -1838,33 +1873,33 @@ var TaskSchema = z36.union([
1838
1873
  },
1839
1874
  retryAttempts: void 0
1840
1875
  })),
1841
- z36.object({
1876
+ z37.object({
1842
1877
  consumer: FunctionSchema,
1843
1878
  retryAttempts: RetryAttemptsSchema2.optional()
1844
1879
  })
1845
1880
  ]);
1846
- var TasksSchema = z36.record(ResourceIdSchema, TaskSchema).optional().describe("Define the tasks in your stack.");
1881
+ var TasksSchema = z37.record(ResourceIdSchema, TaskSchema).optional().describe("Define the tasks in your stack.");
1847
1882
 
1848
1883
  // src/feature/test/schema.ts
1849
- import { z as z37 } from "zod";
1850
- var TestsSchema = z37.union([LocalDirectorySchema.transform((v) => [v]), LocalDirectorySchema.array()]).describe("Define the location of your tests for your stack.").optional();
1884
+ import { z as z38 } from "zod";
1885
+ var TestsSchema = z38.union([LocalDirectorySchema.transform((v) => [v]), LocalDirectorySchema.array()]).describe("Define the location of your tests for your stack.").optional();
1851
1886
 
1852
1887
  // src/feature/topic/schema.ts
1853
1888
  import { kebabCase as kebabCase3 } from "change-case";
1854
- import { z as z38 } from "zod";
1855
- var TopicNameSchema = z38.string().min(3).max(256).regex(/^[a-z0-9\-]+$/i, "Invalid topic name").transform((value) => kebabCase3(value)).describe("Define event topic name.");
1856
- var TopicsSchema = z38.array(TopicNameSchema).refine((topics) => {
1889
+ import { z as z39 } from "zod";
1890
+ var TopicNameSchema = z39.string().min(3).max(256).regex(/^[a-z0-9\-]+$/i, "Invalid topic name").transform((value) => kebabCase3(value)).describe("Define event topic name.");
1891
+ var TopicsSchema = z39.array(TopicNameSchema).refine((topics) => {
1857
1892
  return topics.length === new Set(topics).size;
1858
1893
  }, "Must be a list of unique topic names").optional().describe("Define the event topics to publish too in your stack.");
1859
- var SubscribersSchema = z38.record(TopicNameSchema, FunctionSchema).optional().describe("Define the event topics to subscribe too in your stack.");
1894
+ var SubscribersSchema = z39.record(TopicNameSchema, FunctionSchema).optional().describe("Define the event topics to subscribe too in your stack.");
1860
1895
 
1861
1896
  // src/config/stack.ts
1862
1897
  var DependsSchema = ResourceIdSchema.array().optional().describe("Define the stacks that this stack is depended on.");
1863
1898
  var NameSchema = ResourceIdSchema.refine((name) => !["base", "hostedzones"].includes(name), {
1864
1899
  message: `Stack name can't be a reserved name.`
1865
1900
  }).describe("Stack name.");
1866
- var StackSchema = z39.object({
1867
- $schema: z39.string().optional(),
1901
+ var StackSchema = z40.object({
1902
+ $schema: z40.string().optional(),
1868
1903
  name: NameSchema,
1869
1904
  depends: DependsSchema,
1870
1905
  commands: CommandsSchema,
@@ -1890,7 +1925,8 @@ var StackSchema = z39.object({
1890
1925
  searchs: SearchsSchema,
1891
1926
  sites: SitesSchema,
1892
1927
  tests: TestsSchema,
1893
- images: ImagesSchema
1928
+ images: ImagesSchema,
1929
+ icons: IconsSchema
1894
1930
  });
1895
1931
 
1896
1932
  // src/config/load/read.ts
@@ -1932,13 +1968,13 @@ var readConfigWithStage = async (file, stage) => {
1932
1968
  };
1933
1969
 
1934
1970
  // src/config/load/validate.ts
1935
- import { z as z40 } from "zod";
1971
+ import { z as z41 } from "zod";
1936
1972
  var validateConfig = async (schema, file, data) => {
1937
1973
  try {
1938
1974
  const result = await schema.parseAsync(data);
1939
1975
  return result;
1940
1976
  } catch (error) {
1941
- if (error instanceof z40.ZodError) {
1977
+ if (error instanceof z41.ZodError) {
1942
1978
  throw new ConfigError(file, error, data);
1943
1979
  }
1944
1980
  throw error;
@@ -5944,7 +5980,7 @@ var layerFeature = defineFeature({
5944
5980
  }
5945
5981
  });
5946
5982
 
5947
- // src/feature/images/index.ts
5983
+ // src/feature/image/index.ts
5948
5984
  import { $ as $22, Group as Group23 } from "@awsless/formation";
5949
5985
  import { join as join12, dirname as dirname8 } from "path";
5950
5986
  import { mebibytes as mebibytes4 } from "@awsless/size";
@@ -5953,8 +5989,8 @@ import { constantCase as constantCase11 } from "change-case";
5953
5989
  import { fileURLToPath as fileURLToPath2 } from "url";
5954
5990
  import { glob as glob3 } from "glob";
5955
5991
  var __dirname2 = dirname8(fileURLToPath2(import.meta.url));
5956
- var imagesFeature = defineFeature({
5957
- name: "images",
5992
+ var imageFeature = defineFeature({
5993
+ name: "image",
5958
5994
  onApp(ctx) {
5959
5995
  const found = ctx.stackConfigs.filter((stack) => {
5960
5996
  return Object.keys(stack.images ?? {}).length > 0;
@@ -5978,7 +6014,7 @@ var imagesFeature = defineFeature({
5978
6014
  });
5979
6015
  const layer = new $22.aws.lambda.LayerVersion(group, "layer", {
5980
6016
  layerName: layerId,
5981
- description: "sharp-arm.zip for the awsless images feature.",
6017
+ description: "sharp-arm.zip for the awsless image feature.",
5982
6018
  compatibleArchitectures: ["arm64"],
5983
6019
  s3Bucket: zipFile.bucket,
5984
6020
  s3ObjectVersion: zipFile.versionId,
@@ -5994,11 +6030,11 @@ var imagesFeature = defineFeature({
5994
6030
  },
5995
6031
  onStack(ctx) {
5996
6032
  for (const [id, props] of Object.entries(ctx.stackConfig.images ?? {})) {
5997
- const group = new Group23(ctx.stack, "images", id);
6033
+ const group = new Group23(ctx.stack, "image", id);
5998
6034
  const name = formatLocalResourceName({
5999
6035
  appName: ctx.app.name,
6000
6036
  stackName: ctx.stack.name,
6001
- resourceType: "images",
6037
+ resourceType: "image",
6002
6038
  resourceName: id
6003
6039
  });
6004
6040
  let lambdaOrigin = void 0;
@@ -6011,7 +6047,7 @@ var imagesFeature = defineFeature({
6011
6047
  bucket: formatLocalResourceName({
6012
6048
  appName: ctx.app.name,
6013
6049
  stackName: ctx.stack.name,
6014
- resourceType: "images",
6050
+ resourceType: "image",
6015
6051
  resourceName: id,
6016
6052
  postfix: ctx.appId
6017
6053
  }),
@@ -6022,9 +6058,9 @@ var imagesFeature = defineFeature({
6022
6058
  bucket: formatLocalResourceName({
6023
6059
  appName: ctx.app.name,
6024
6060
  stackName: ctx.stack.name,
6025
- resourceType: "images",
6026
- resourceName: `cache-${id}`,
6027
- postfix: ctx.appId
6061
+ resourceType: "image",
6062
+ resourceName: `cache-${id}`
6063
+ // postfix: ctx.appId,
6028
6064
  }),
6029
6065
  forceDestroy: true
6030
6066
  });
@@ -6033,9 +6069,9 @@ var imagesFeature = defineFeature({
6033
6069
  resourceType: "layer",
6034
6070
  resourceName: "sharp"
6035
6071
  });
6036
- const transformFn = createPrebuildLambdaFunction(group, ctx, "images", id, {
6037
- bundleFile: join12(__dirname2, "/prebuild/images/bundle.zip"),
6038
- bundleHash: join12(__dirname2, "/prebuild/images/HASH"),
6072
+ const serverLambda = createPrebuildLambdaFunction(group, ctx, "image", id, {
6073
+ bundleFile: join12(__dirname2, "/prebuild/image/bundle.zip"),
6074
+ bundleHash: join12(__dirname2, "/prebuild/image/HASH"),
6039
6075
  memorySize: mebibytes4(512),
6040
6076
  timeout: seconds9(10),
6041
6077
  handler: "index.default",
@@ -6046,20 +6082,20 @@ var imagesFeature = defineFeature({
6046
6082
  const permission = new $22.aws.lambda.Permission(group, "permission", {
6047
6083
  principal: "cloudfront.amazonaws.com",
6048
6084
  action: "lambda:InvokeFunctionUrl",
6049
- functionName: transformFn.lambda.functionName,
6085
+ functionName: serverLambda.lambda.functionName,
6050
6086
  functionUrlAuthType: "AWS_IAM",
6051
6087
  sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
6052
6088
  });
6053
- const transformFnUrl = new $22.aws.lambda.FunctionUrl(
6089
+ const serverLambdaUrl = new $22.aws.lambda.FunctionUrl(
6054
6090
  group,
6055
6091
  "url",
6056
6092
  {
6057
- functionName: transformFn.lambda.functionName,
6093
+ functionName: serverLambda.lambda.functionName,
6058
6094
  authorizationType: "AWS_IAM"
6059
6095
  },
6060
6096
  { dependsOn: [permission] }
6061
6097
  );
6062
- transformFn.addPermission({
6098
+ serverLambda.addPermission({
6063
6099
  actions: [
6064
6100
  "s3:ListBucket",
6065
6101
  "s3:ListBucketV2",
@@ -6076,20 +6112,20 @@ var imagesFeature = defineFeature({
6076
6112
  ...s3Origin ? [s3Origin.arn, s3Origin.arn.pipe((arn) => `${arn}/*`)] : []
6077
6113
  ]
6078
6114
  });
6079
- transformFn.setEnvironment(
6080
- "IMAGES_CONFIG",
6115
+ serverLambda.setEnvironment(
6116
+ "IMAGE_CONFIG",
6081
6117
  JSON.stringify({
6082
6118
  presets: props.presets,
6083
6119
  extensions: props.extensions,
6084
6120
  version: props.version
6085
6121
  })
6086
6122
  );
6087
- transformFn.setEnvironment("IMAGES_CACHE_BUCKET", cacheBucket.bucket);
6123
+ serverLambda.setEnvironment("IMAGE_CACHE_BUCKET", cacheBucket.bucket);
6088
6124
  if (lambdaOrigin) {
6089
- transformFn.setEnvironment("IMAGES_ORIGIN_LAMBDA", lambdaOrigin.name);
6125
+ serverLambda.setEnvironment("IMAGE_ORIGIN_LAMBDA", lambdaOrigin.name);
6090
6126
  }
6091
6127
  if (s3Origin) {
6092
- transformFn.setEnvironment("IMAGES_ORIGIN_S3", s3Origin.bucket);
6128
+ serverLambda.setEnvironment("IMAGE_ORIGIN_S3", s3Origin.bucket);
6093
6129
  }
6094
6130
  ctx.onReady(() => {
6095
6131
  if (props.origin.static && s3Origin) {
@@ -6111,14 +6147,14 @@ var imagesFeature = defineFeature({
6111
6147
  const certificateArn = props.domain ? ctx.shared.entry("domain", `global-certificate-arn`, props.domain) : void 0;
6112
6148
  const s3AccessControl = new $22.aws.cloudfront.OriginAccessControl(group, `s3`, {
6113
6149
  name: `${name}-s3`,
6114
- description: "Policy for Images cache in S3",
6150
+ description: `Policy for the ${id} image cache in S3`,
6115
6151
  originAccessControlOriginType: "s3",
6116
6152
  signingBehavior: "always",
6117
6153
  signingProtocol: "sigv4"
6118
6154
  });
6119
6155
  const lambdaAccessControl = new $22.aws.cloudfront.OriginAccessControl(group, "lambda", {
6120
6156
  name: `${name}-lambda`,
6121
- description: "Policy for Images Lambda Transformation Function URL",
6157
+ description: `Policy for the ${id} image lambda server function URL`,
6122
6158
  originAccessControlOriginType: "lambda",
6123
6159
  signingBehavior: "always",
6124
6160
  signingProtocol: "sigv4"
@@ -6151,8 +6187,8 @@ var imagesFeature = defineFeature({
6151
6187
  }
6152
6188
  },
6153
6189
  {
6154
- originId: "transform",
6155
- domainName: transformFnUrl.functionUrl.pipe((url) => url.split("/")[2]),
6190
+ originId: "server",
6191
+ domainName: serverLambdaUrl.functionUrl.pipe((url) => url.split("/")[2]),
6156
6192
  originAccessControlId: lambdaAccessControl.id,
6157
6193
  customOriginConfig: {
6158
6194
  originProtocolPolicy: "https-only",
@@ -6165,7 +6201,7 @@ var imagesFeature = defineFeature({
6165
6201
  originGroup: [
6166
6202
  {
6167
6203
  originId: "group",
6168
- member: [{ originId: "cache" }, { originId: "transform" }],
6204
+ member: [{ originId: "cache" }, { originId: "server" }],
6169
6205
  failoverCriteria: {
6170
6206
  statusCodes: [403, 404]
6171
6207
  }
@@ -6223,9 +6259,260 @@ var imagesFeature = defineFeature({
6223
6259
  });
6224
6260
  }
6225
6261
  ctx.bind(
6226
- `IMAGES_${constantCase11(ctx.stack.name)}_${constantCase11(id)}_ENDPOINT`,
6262
+ `IMAGE_${constantCase11(ctx.stack.name)}_${constantCase11(id)}_ENDPOINT`,
6227
6263
  domainName ?? distribution.domainName
6228
6264
  );
6265
+ ctx.shared.add("image", "distribution-id", id, distribution.id);
6266
+ ctx.shared.add("image", "cache-bucket", id, cacheBucket.bucket);
6267
+ }
6268
+ }
6269
+ });
6270
+
6271
+ // src/feature/icon/index.ts
6272
+ import { $ as $23, Group as Group24 } from "@awsless/formation";
6273
+ import { join as join13, dirname as dirname9 } from "path";
6274
+ import { mebibytes as mebibytes5 } from "@awsless/size";
6275
+ import { days as days7, seconds as seconds10, toSeconds as toSeconds9 } from "@awsless/duration";
6276
+ import { constantCase as constantCase12 } from "change-case";
6277
+ import { fileURLToPath as fileURLToPath3 } from "url";
6278
+ import { glob as glob4 } from "glob";
6279
+ var __dirname3 = dirname9(fileURLToPath3(import.meta.url));
6280
+ var iconFeature = defineFeature({
6281
+ name: "icon",
6282
+ onStack(ctx) {
6283
+ for (const [id, props] of Object.entries(ctx.stackConfig.icons ?? {})) {
6284
+ const group = new Group24(ctx.stack, "icon", id);
6285
+ const name = formatLocalResourceName({
6286
+ appName: ctx.app.name,
6287
+ stackName: ctx.stack.name,
6288
+ resourceType: "icon",
6289
+ resourceName: id
6290
+ });
6291
+ let lambdaOrigin = void 0;
6292
+ if (props.origin.function) {
6293
+ lambdaOrigin = createLambdaFunction(group, ctx, "origin", id, props.origin.function);
6294
+ }
6295
+ let s3Origin;
6296
+ if (props.origin.static) {
6297
+ s3Origin = new $23.aws.s3.Bucket(group, "origin", {
6298
+ bucket: formatLocalResourceName({
6299
+ appName: ctx.app.name,
6300
+ stackName: ctx.stack.name,
6301
+ resourceType: "icon",
6302
+ resourceName: id,
6303
+ postfix: ctx.appId
6304
+ }),
6305
+ forceDestroy: true
6306
+ });
6307
+ }
6308
+ const cacheBucket = new $23.aws.s3.Bucket(group, "cache", {
6309
+ bucket: formatLocalResourceName({
6310
+ appName: ctx.app.name,
6311
+ stackName: ctx.stack.name,
6312
+ resourceType: "icon",
6313
+ resourceName: `cache-${id}`
6314
+ // postfix: ctx.appId,
6315
+ }),
6316
+ forceDestroy: true
6317
+ });
6318
+ const serverLambda = createPrebuildLambdaFunction(group, ctx, "icon", id, {
6319
+ bundleFile: join13(__dirname3, "/prebuild/icon/bundle.zip"),
6320
+ bundleHash: join13(__dirname3, "/prebuild/icon/HASH"),
6321
+ memorySize: mebibytes5(1024),
6322
+ timeout: seconds10(60),
6323
+ architecture: "x86_64",
6324
+ handler: "index.default",
6325
+ runtime: "nodejs22.x",
6326
+ log: props.log
6327
+ });
6328
+ const permission = new $23.aws.lambda.Permission(group, "permission", {
6329
+ principal: "cloudfront.amazonaws.com",
6330
+ action: "lambda:InvokeFunctionUrl",
6331
+ functionName: serverLambda.lambda.functionName,
6332
+ functionUrlAuthType: "AWS_IAM",
6333
+ sourceArn: `arn:aws:cloudfront::${ctx.accountId}:distribution/*`
6334
+ });
6335
+ const serverLambdaUrl = new $23.aws.lambda.FunctionUrl(
6336
+ group,
6337
+ "url",
6338
+ {
6339
+ functionName: serverLambda.lambda.functionName,
6340
+ authorizationType: "AWS_IAM"
6341
+ },
6342
+ { dependsOn: [permission] }
6343
+ );
6344
+ serverLambda.addPermission({
6345
+ actions: [
6346
+ "s3:ListBucket",
6347
+ "s3:ListBucketV2",
6348
+ "s3:HeadObject",
6349
+ "s3:GetObject",
6350
+ "s3:PutObject",
6351
+ "s3:DeleteObject",
6352
+ "s3:GetObjectAttributes"
6353
+ ],
6354
+ resources: [
6355
+ //
6356
+ cacheBucket.arn,
6357
+ cacheBucket.arn.pipe((arn) => `${arn}/*`),
6358
+ ...s3Origin ? [s3Origin.arn, s3Origin.arn.pipe((arn) => `${arn}/*`)] : []
6359
+ ]
6360
+ });
6361
+ serverLambda.setEnvironment(
6362
+ "ICON_CONFIG",
6363
+ JSON.stringify({
6364
+ preserveId: props.preserveId,
6365
+ symbols: props.symbols,
6366
+ version: props.version
6367
+ })
6368
+ );
6369
+ serverLambda.setEnvironment("ICON_CACHE_BUCKET", cacheBucket.bucket);
6370
+ if (lambdaOrigin) {
6371
+ serverLambda.setEnvironment("ICON_ORIGIN_LAMBDA", lambdaOrigin.name);
6372
+ }
6373
+ if (s3Origin) {
6374
+ serverLambda.setEnvironment("ICON_ORIGIN_S3", s3Origin.bucket);
6375
+ }
6376
+ ctx.onReady(() => {
6377
+ if (props.origin.static && s3Origin) {
6378
+ const files = glob4.sync("**", {
6379
+ cwd: props.origin.static,
6380
+ nodir: true
6381
+ });
6382
+ for (const file of files) {
6383
+ if (!file.endsWith(".svg")) {
6384
+ throw new Error(`Icon file "${file}" in "${props.origin.static}" is not an SVG file.`);
6385
+ }
6386
+ new $23.aws.s3.BucketObject(group, `static-${file}`, {
6387
+ bucket: s3Origin.bucket,
6388
+ key: file,
6389
+ source: join13(props.origin.static, file),
6390
+ sourceHash: $hash(join13(props.origin.static, file))
6391
+ });
6392
+ }
6393
+ }
6394
+ });
6395
+ const domainName = props.domain ? formatFullDomainName(ctx.appConfig, props.domain, props.subDomain) : void 0;
6396
+ const certificateArn = props.domain ? ctx.shared.entry("domain", `global-certificate-arn`, props.domain) : void 0;
6397
+ const s3AccessControl = new $23.aws.cloudfront.OriginAccessControl(group, `s3`, {
6398
+ name: `${name}-s3`,
6399
+ description: `Policy for the ${id} icon cache in S3`,
6400
+ originAccessControlOriginType: "s3",
6401
+ signingBehavior: "always",
6402
+ signingProtocol: "sigv4"
6403
+ });
6404
+ const lambdaAccessControl = new $23.aws.cloudfront.OriginAccessControl(group, "lambda", {
6405
+ name: `${name}-lambda`,
6406
+ description: `Policy for the ${id} icon lambda server function URL`,
6407
+ originAccessControlOriginType: "lambda",
6408
+ signingBehavior: "always",
6409
+ signingProtocol: "sigv4"
6410
+ });
6411
+ const cache = new $23.aws.cloudfront.CachePolicy(group, "cache", {
6412
+ name,
6413
+ defaultTtl: toSeconds9(days7(365))
6414
+ });
6415
+ const distribution = new $23.aws.cloudfront.Distribution(group, "distribution", {
6416
+ comment: name,
6417
+ enabled: true,
6418
+ aliases: domainName ? [domainName] : void 0,
6419
+ priceClass: "PriceClass_All",
6420
+ httpVersion: "http2and3",
6421
+ viewerCertificate: certificateArn ? {
6422
+ sslSupportMethod: "sni-only",
6423
+ minimumProtocolVersion: "TLSv1.2_2021",
6424
+ acmCertificateArn: certificateArn
6425
+ } : {
6426
+ cloudfrontDefaultCertificate: true
6427
+ },
6428
+ origin: [
6429
+ {
6430
+ originId: "cache",
6431
+ domainName: cacheBucket.bucketRegionalDomainName,
6432
+ originAccessControlId: s3AccessControl.id,
6433
+ s3OriginConfig: {
6434
+ // is required to have an value for s3 origins when using origin access control
6435
+ originAccessIdentity: ""
6436
+ }
6437
+ },
6438
+ {
6439
+ originId: "server",
6440
+ domainName: serverLambdaUrl.functionUrl.pipe((url) => url.split("/")[2]),
6441
+ originAccessControlId: lambdaAccessControl.id,
6442
+ customOriginConfig: {
6443
+ originProtocolPolicy: "https-only",
6444
+ httpPort: 80,
6445
+ httpsPort: 443,
6446
+ originSslProtocols: ["TLSv1.2"]
6447
+ }
6448
+ }
6449
+ ],
6450
+ originGroup: [
6451
+ {
6452
+ originId: "group",
6453
+ member: [{ originId: "cache" }, { originId: "server" }],
6454
+ failoverCriteria: {
6455
+ statusCodes: [403, 404]
6456
+ }
6457
+ }
6458
+ ],
6459
+ defaultCacheBehavior: {
6460
+ compress: true,
6461
+ targetOriginId: "group",
6462
+ cachePolicyId: cache.id,
6463
+ viewerProtocolPolicy: "redirect-to-https",
6464
+ allowedMethods: ["GET", "HEAD"],
6465
+ cachedMethods: ["GET", "HEAD"]
6466
+ }
6467
+ });
6468
+ new $23.aws.s3.BucketPolicy(
6469
+ group,
6470
+ `policy`,
6471
+ {
6472
+ bucket: cacheBucket.bucket,
6473
+ policy: $resolve([cacheBucket.arn, distribution.id], (arn, id2) => {
6474
+ return JSON.stringify({
6475
+ Version: "2012-10-17",
6476
+ Statement: [
6477
+ {
6478
+ Effect: "Allow",
6479
+ Action: "s3:GetObject",
6480
+ Resource: `${arn}/*`,
6481
+ Principal: {
6482
+ Service: "cloudfront.amazonaws.com"
6483
+ },
6484
+ Condition: {
6485
+ StringEquals: {
6486
+ "AWS:SourceArn": `arn:aws:cloudfront::${ctx.accountId}:distribution/${id2}`
6487
+ }
6488
+ }
6489
+ }
6490
+ ]
6491
+ });
6492
+ })
6493
+ },
6494
+ {
6495
+ dependsOn: [cacheBucket, distribution]
6496
+ }
6497
+ );
6498
+ if (domainName) {
6499
+ new $23.aws.route53.Record(group, `record`, {
6500
+ zoneId: ctx.shared.entry("domain", "zone-id", props.domain),
6501
+ type: "A",
6502
+ name: domainName,
6503
+ alias: {
6504
+ name: distribution.domainName,
6505
+ zoneId: distribution.hostedZoneId,
6506
+ evaluateTargetHealth: false
6507
+ }
6508
+ });
6509
+ }
6510
+ ctx.bind(
6511
+ `ICON_${constantCase12(ctx.stack.name)}_${constantCase12(id)}_ENDPOINT`,
6512
+ domainName ?? distribution.domainName
6513
+ );
6514
+ ctx.shared.add("icon", "distribution-id", id, distribution.id);
6515
+ ctx.shared.add("icon", "cache-bucket", id, cacheBucket.bucket);
6229
6516
  }
6230
6517
  }
6231
6518
  });
@@ -6262,7 +6549,8 @@ var features = [
6262
6549
  // httpFeature,
6263
6550
  restFeature,
6264
6551
  siteFeature,
6265
- imagesFeature,
6552
+ imageFeature,
6553
+ iconFeature,
6266
6554
  // 4
6267
6555
  rpcFeature
6268
6556
  ];
@@ -6608,7 +6896,7 @@ var createApp = (props) => {
6608
6896
  region: props.appConfig.region,
6609
6897
  appName: props.appConfig.name
6610
6898
  });
6611
- const commands7 = [];
6899
+ const commands9 = [];
6612
6900
  const configs = /* @__PURE__ */ new Set();
6613
6901
  const tests = [];
6614
6902
  const builders = [];
@@ -6668,7 +6956,7 @@ var createApp = (props) => {
6668
6956
  });
6669
6957
  },
6670
6958
  registerCommand(command) {
6671
- commands7.push(command);
6959
+ commands9.push(command);
6672
6960
  },
6673
6961
  registerDomainZone(zone) {
6674
6962
  domainZones.push(zone);
@@ -6760,7 +7048,7 @@ var createApp = (props) => {
6760
7048
  configs.add(name);
6761
7049
  },
6762
7050
  registerCommand(command) {
6763
- commands7.push(command);
7051
+ commands9.push(command);
6764
7052
  },
6765
7053
  registerDomainZone(zone) {
6766
7054
  domainZones.push(zone);
@@ -6853,7 +7141,7 @@ var createApp = (props) => {
6853
7141
  shared,
6854
7142
  configs,
6855
7143
  builders,
6856
- commands: commands7
7144
+ commands: commands9
6857
7145
  // deploymentLine,
6858
7146
  };
6859
7147
  };
@@ -7127,13 +7415,13 @@ import wildstring4 from "wildstring";
7127
7415
  import { log as log15 } from "@awsless/clui";
7128
7416
  import chalk4 from "chalk";
7129
7417
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
7130
- import { join as join15 } from "path";
7418
+ import { join as join16 } from "path";
7131
7419
  import wildstring3 from "wildstring";
7132
7420
 
7133
7421
  // src/build/__fingerprint.ts
7134
7422
  import { createHash as createHash5 } from "crypto";
7135
7423
  import { readdir as readdir4, readFile as readFile4, stat as stat4 } from "fs/promises";
7136
- import { basename as basename4, dirname as dirname9, extname as extname3, join as join13 } from "path";
7424
+ import { basename as basename4, dirname as dirname10, extname as extname3, join as join14 } from "path";
7137
7425
  import parseStaticImports from "parse-static-imports";
7138
7426
  var extensions = ["js", "mjs", "jsx", "ts", "mts", "tsx"];
7139
7427
  var generateFileHashes = async (file, hashes) => {
@@ -7155,7 +7443,7 @@ var fingerprintFromDirectory = async (dir) => {
7155
7443
  const files = await readdir4(dir, { recursive: true });
7156
7444
  for (const file of files) {
7157
7445
  if (extensions.includes(extname3(file).substring(1)) && file.at(0) !== "_") {
7158
- await generateFileHashes(join13(dir, file), hashes);
7446
+ await generateFileHashes(join14(dir, file), hashes);
7159
7447
  }
7160
7448
  }
7161
7449
  const merge2 = Buffer.concat(Array.from(hashes.values()).sort());
@@ -7169,7 +7457,7 @@ var readModuleFile = (file) => {
7169
7457
  return readFiles([
7170
7458
  file,
7171
7459
  ...extensions.map((exp) => `${file}.${exp}`),
7172
- ...extensions.map((exp) => join13(file, `/index.${exp}`))
7460
+ ...extensions.map((exp) => join14(file, `/index.${exp}`))
7173
7461
  ]);
7174
7462
  }
7175
7463
  return readFile4(file, "utf8");
@@ -7189,7 +7477,7 @@ var readFiles = async (files) => {
7189
7477
  };
7190
7478
  var findDependencies = async (file, code) => {
7191
7479
  const imports = await parseStaticImports(code);
7192
- return imports.map((entry) => entry.moduleName).filter(Boolean).map((value) => value?.startsWith(".") ? join13(dirname9(file), value) : value);
7480
+ return imports.map((entry) => entry.moduleName).filter(Boolean).map((value) => value?.startsWith(".") ? join14(dirname10(file), value) : value);
7193
7481
  };
7194
7482
 
7195
7483
  // src/test/reporter.ts
@@ -7247,8 +7535,8 @@ var CustomReporter = class {
7247
7535
  this.cache = cache;
7248
7536
  }
7249
7537
  }
7250
- onUserConsoleLog(log23) {
7251
- this.logs.push(log23.content.trimEnd());
7538
+ onUserConsoleLog(log25) {
7539
+ this.logs.push(log25.content.trimEnd());
7252
7540
  }
7253
7541
  runningTask(tasks) {
7254
7542
  return tasks.find((t) => t.result?.state === "run");
@@ -7269,13 +7557,13 @@ var CustomReporter = class {
7269
7557
  import commonjs2 from "@rollup/plugin-commonjs";
7270
7558
  import json2 from "@rollup/plugin-json";
7271
7559
  import nodeResolve2 from "@rollup/plugin-node-resolve";
7272
- import { dirname as dirname10, join as join14 } from "path";
7560
+ import { dirname as dirname11, join as join15 } from "path";
7273
7561
  import { swc as swc2 } from "rollup-plugin-swc3";
7274
- import { fileURLToPath as fileURLToPath3 } from "url";
7562
+ import { fileURLToPath as fileURLToPath4 } from "url";
7275
7563
  import { configDefaults } from "vitest/config";
7276
7564
  import { startVitest } from "vitest/node";
7277
7565
  var startTest = async (props) => {
7278
- const __dirname3 = dirname10(fileURLToPath3(import.meta.url));
7566
+ const __dirname4 = dirname11(fileURLToPath4(import.meta.url));
7279
7567
  const result = await startVitest(
7280
7568
  "test",
7281
7569
  props.filters,
@@ -7291,7 +7579,7 @@ var startTest = async (props) => {
7291
7579
  reporters: props.reporter,
7292
7580
  setupFiles: [
7293
7581
  //
7294
- join14(__dirname3, "test-global-setup.js")
7582
+ join15(__dirname4, "test-global-setup.js")
7295
7583
  ]
7296
7584
  // globalSetup: [
7297
7585
  // //
@@ -7350,7 +7638,7 @@ var formatResult = (props) => {
7350
7638
  var logTestLogs = (event) => {
7351
7639
  if (event.logs.length > 0) {
7352
7640
  log15.message(color.info.bold.inverse(" LOGS "), color.dim(icon.dot));
7353
- log15.message(event.logs.map((log23) => wrap(log23, { hard: true })).join("\n"));
7641
+ log15.message(event.logs.map((log25) => wrap(log25, { hard: true })).join("\n"));
7354
7642
  }
7355
7643
  };
7356
7644
  var logTestErrors = (event) => {
@@ -7381,7 +7669,7 @@ var logTestErrors = (event) => {
7381
7669
  var runTest = async (stack, dir, filters) => {
7382
7670
  await mkdir4(directories.test, { recursive: true });
7383
7671
  const fingerprint = await fingerprintFromDirectory(dir);
7384
- const file = join15(directories.test, `${stack}.json`);
7672
+ const file = join16(directories.test, `${stack}.json`);
7385
7673
  const exists = await fileExist(file);
7386
7674
  if (exists && !process.env.NO_CACHE) {
7387
7675
  const raw = await readFile5(file, { encoding: "utf8" });
@@ -7618,10 +7906,10 @@ var auth = (program2) => {
7618
7906
 
7619
7907
  // src/cli/command/bind.ts
7620
7908
  import { log as log17 } from "@awsless/clui";
7621
- import { constantCase as constantCase12 } from "change-case";
7909
+ import { constantCase as constantCase13 } from "change-case";
7622
7910
  import { spawn } from "child_process";
7623
7911
  var bind = (program2) => {
7624
- 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 (commands7 = [], opts) => {
7912
+ program2.command("bind").argument("[command...]", "The command to execute").option("--config <string...>", "List of config values that will be accessable", (v) => v.split(",")).description(`Bind your site environment variables to a command`).action(async (commands9 = [], opts) => {
7625
7913
  await layout("bind", async ({ appConfig, stackConfigs }) => {
7626
7914
  const region = appConfig.region;
7627
7915
  const profile = appConfig.profile;
@@ -7647,15 +7935,15 @@ var bind = (program2) => {
7647
7935
  const configList = opts.config ?? [];
7648
7936
  const configs = {};
7649
7937
  for (const name of configList) {
7650
- configs[`CONFIG_${constantCase12(name)}`] = name;
7938
+ configs[`CONFIG_${constantCase13(name)}`] = name;
7651
7939
  }
7652
7940
  if (configList.length ?? 0 > 0) {
7653
- log17.note("Bind Config", configList.map((v) => color.label(constantCase12(v))).join("\n"));
7941
+ log17.note("Bind Config", configList.map((v) => color.label(constantCase13(v))).join("\n"));
7654
7942
  }
7655
- if (commands7.length === 0) {
7943
+ if (commands9.length === 0) {
7656
7944
  return "No command to execute.";
7657
7945
  }
7658
- const command = commands7.join(" ");
7946
+ const command = commands9.join(" ");
7659
7947
  const freshCred = await credentials();
7660
7948
  spawn(command, {
7661
7949
  env: {
@@ -7713,7 +8001,7 @@ import { log as log18 } from "@awsless/clui";
7713
8001
 
7714
8002
  // src/type-gen/generate.ts
7715
8003
  import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
7716
- import { dirname as dirname11, join as join16, relative as relative7 } from "path";
8004
+ import { dirname as dirname12, join as join17, relative as relative7 } from "path";
7717
8005
  var generateTypes = async (props) => {
7718
8006
  const files = [];
7719
8007
  await Promise.all(
@@ -7722,12 +8010,12 @@ var generateTypes = async (props) => {
7722
8010
  ...props,
7723
8011
  async write(file, data, include = false) {
7724
8012
  const code = data?.toString("utf8");
7725
- const path = join16(directories.types, file);
8013
+ const path = join17(directories.types, file);
7726
8014
  if (code) {
7727
8015
  if (include) {
7728
8016
  files.push(relative7(directories.root, path));
7729
8017
  }
7730
- await mkdir5(dirname11(path), { recursive: true });
8018
+ await mkdir5(dirname12(path), { recursive: true });
7731
8019
  await writeFile4(path, code);
7732
8020
  }
7733
8021
  }
@@ -7736,7 +8024,7 @@ var generateTypes = async (props) => {
7736
8024
  );
7737
8025
  if (files.length) {
7738
8026
  const code = files.map((file) => `/// <reference path='${file}' />`).join("\n");
7739
- await writeFile4(join16(directories.root, `awsless.d.ts`), code);
8027
+ await writeFile4(join17(directories.root, `awsless.d.ts`), code);
7740
8028
  }
7741
8029
  };
7742
8030
 
@@ -7811,17 +8099,17 @@ var run = (program2) => {
7811
8099
  const region = appConfig.region;
7812
8100
  const credentials = getCredentials(appConfig.profile);
7813
8101
  const accountId = await getAccountId(credentials, region);
7814
- const { commands: commands7 } = createApp({ appConfig, stackConfigs, accountId });
8102
+ const { commands: commands9 } = createApp({ appConfig, stackConfigs, accountId });
7815
8103
  let command;
7816
8104
  if (selected) {
7817
- command = commands7.find((cmd) => {
8105
+ command = commands9.find((cmd) => {
7818
8106
  return cmd.name === selected;
7819
8107
  });
7820
8108
  } else {
7821
8109
  command = await prompt7.select({
7822
8110
  message: "Pick the command you want to run:",
7823
- initialValue: commands7[0],
7824
- options: commands7.map((cmd) => ({
8111
+ initialValue: commands9[0],
8112
+ options: commands9.map((cmd) => ({
7825
8113
  value: cmd,
7826
8114
  label: cmd.name,
7827
8115
  hint: cmd.description
@@ -8191,8 +8479,262 @@ var parseJsonLog = (message) => {
8191
8479
  };
8192
8480
  };
8193
8481
 
8482
+ // src/cli/command/image/clear-cache.ts
8483
+ import { CloudFrontClient as CloudFrontClient2, CreateInvalidationCommand as CreateInvalidationCommand2 } from "@aws-sdk/client-cloudfront";
8484
+ import { DeleteObjectsCommand, ListObjectsV2Command, S3Client as S3Client3 } from "@aws-sdk/client-s3";
8485
+ import { Cancelled as Cancelled2, log as log23, prompt as prompt10 } from "@awsless/clui";
8486
+ var clearCache = (program2) => {
8487
+ program2.command("cache-clear").argument("[stack]", "The stack name of the image proxy").argument("[name]", "The name of the image proxy").description("Clears the cache of the image proxy").action(async (stack, name) => {
8488
+ await layout("image cache-clear", async ({ appConfig, stackConfigs }) => {
8489
+ const region = appConfig.region;
8490
+ const profile = appConfig.profile;
8491
+ const credentials = getCredentials(profile);
8492
+ const accountId = await getAccountId(credentials, region);
8493
+ if (!stack) {
8494
+ const imageStacks = stackConfigs.filter((stack2) => {
8495
+ if (Object.keys(stack2.images ?? {}).length > 0) {
8496
+ return stack2;
8497
+ }
8498
+ return;
8499
+ });
8500
+ stack = await prompt10.select({
8501
+ message: "Select the image proxy:",
8502
+ options: imageStacks.map((stack2) => ({
8503
+ label: stack2.name,
8504
+ value: stack2.name
8505
+ }))
8506
+ });
8507
+ }
8508
+ if (!name) {
8509
+ const stackConfig = stackConfigs.find((s) => s.name === stack);
8510
+ if (!stackConfig) {
8511
+ throw new ExpectedError(`The stack "${stack}" doesn't exist.`);
8512
+ }
8513
+ const names = Object.keys(stackConfig.images ?? {});
8514
+ if (!names) {
8515
+ throw new ExpectedError(`No images are defined in stack "${stack}".`);
8516
+ }
8517
+ name = await prompt10.select({
8518
+ message: "Select the image:",
8519
+ options: names.map((name2) => ({
8520
+ label: name2,
8521
+ value: name2
8522
+ }))
8523
+ });
8524
+ }
8525
+ const ok = await prompt10.confirm({
8526
+ message: `Are you sure you want to clear the cache`
8527
+ });
8528
+ if (!ok) {
8529
+ throw new Cancelled2();
8530
+ }
8531
+ const { shared, app } = createApp({ appConfig, stackConfigs, accountId });
8532
+ const { workspace } = await createWorkSpace({
8533
+ credentials,
8534
+ accountId,
8535
+ profile,
8536
+ region
8537
+ });
8538
+ await workspace.hydrate(app);
8539
+ let distributionId;
8540
+ let cacheBucket;
8541
+ try {
8542
+ distributionId = await shared.entry("image", "distribution-id", name);
8543
+ cacheBucket = await shared.entry("image", "cache-bucket", name);
8544
+ } catch (_) {
8545
+ throw new ExpectedError(`The image proxy hasn't been deployed yet.`);
8546
+ }
8547
+ const s3Client2 = new S3Client3({
8548
+ credentials,
8549
+ region
8550
+ });
8551
+ let continuationToken;
8552
+ let totalDeleted = 0;
8553
+ let hasMore = true;
8554
+ while (hasMore) {
8555
+ const result = await s3Client2.send(
8556
+ new ListObjectsV2Command({
8557
+ Bucket: cacheBucket,
8558
+ ContinuationToken: continuationToken,
8559
+ MaxKeys: 1e3
8560
+ // Maximum allowed per request
8561
+ })
8562
+ );
8563
+ if (result.Contents && result.Contents.length > 0) {
8564
+ await s3Client2.send(
8565
+ new DeleteObjectsCommand({
8566
+ Bucket: cacheBucket,
8567
+ Delete: {
8568
+ Objects: result.Contents.map((obj) => ({
8569
+ Key: obj.Key
8570
+ })),
8571
+ Quiet: true
8572
+ }
8573
+ })
8574
+ );
8575
+ totalDeleted += result.Contents.length;
8576
+ log23.info(`${result.Contents.length} objects deleted from cache.`);
8577
+ }
8578
+ continuationToken = result.NextContinuationToken;
8579
+ hasMore = !!continuationToken;
8580
+ }
8581
+ const cloudFrontClient = new CloudFrontClient2({
8582
+ credentials,
8583
+ region: "us-east-1"
8584
+ });
8585
+ await cloudFrontClient.send(
8586
+ new CreateInvalidationCommand2({
8587
+ DistributionId: distributionId,
8588
+ InvalidationBatch: {
8589
+ CallerReference: `image-cache-clear-${Date.now()}`,
8590
+ Paths: {
8591
+ Quantity: 1,
8592
+ Items: ["/*"]
8593
+ }
8594
+ }
8595
+ })
8596
+ );
8597
+ return "Cache has been cleared";
8598
+ });
8599
+ });
8600
+ };
8601
+
8602
+ // src/cli/command/image/index.ts
8603
+ var commands6 = [clearCache];
8604
+ var image = (program2) => {
8605
+ const command = program2.command("image").description(`Manage image proxies`);
8606
+ commands6.forEach((cb) => cb(command));
8607
+ };
8608
+
8609
+ // src/cli/command/icon/clear-cache.ts
8610
+ import { CloudFrontClient as CloudFrontClient3, CreateInvalidationCommand as CreateInvalidationCommand3 } from "@aws-sdk/client-cloudfront";
8611
+ import { DeleteObjectsCommand as DeleteObjectsCommand2, ListObjectsV2Command as ListObjectsV2Command2, S3Client as S3Client4 } from "@aws-sdk/client-s3";
8612
+ import { Cancelled as Cancelled3, log as log24, prompt as prompt11 } from "@awsless/clui";
8613
+ var clearCache2 = (program2) => {
8614
+ program2.command("cache-clear").argument("[stack]", "The stack name of the icon proxy").argument("[name]", "The name of the icon proxy").description("Clears the cache of the icon proxy").action(async (stack, name) => {
8615
+ await layout("icon cache-clear", async ({ appConfig, stackConfigs }) => {
8616
+ const region = appConfig.region;
8617
+ const profile = appConfig.profile;
8618
+ const credentials = getCredentials(profile);
8619
+ const accountId = await getAccountId(credentials, region);
8620
+ if (!stack) {
8621
+ const iconStacks = stackConfigs.filter((stack2) => {
8622
+ if (Object.keys(stack2.icons ?? {}).length > 0) {
8623
+ return stack2;
8624
+ }
8625
+ return;
8626
+ });
8627
+ stack = await prompt11.select({
8628
+ message: "Select the icon proxy:",
8629
+ options: iconStacks.map((stack2) => ({
8630
+ label: stack2.name,
8631
+ value: stack2.name
8632
+ }))
8633
+ });
8634
+ }
8635
+ if (!name) {
8636
+ const stackConfig = stackConfigs.find((s) => s.name === stack);
8637
+ if (!stackConfig) {
8638
+ throw new ExpectedError(`The stack "${stack}" doesn't exist.`);
8639
+ }
8640
+ const names = Object.keys(stackConfig.icons ?? {});
8641
+ if (!names) {
8642
+ throw new ExpectedError(`No icons are defined in stack "${stack}".`);
8643
+ }
8644
+ name = await prompt11.select({
8645
+ message: "Select the icon:",
8646
+ options: names.map((name2) => ({
8647
+ label: name2,
8648
+ value: name2
8649
+ }))
8650
+ });
8651
+ }
8652
+ const ok = await prompt11.confirm({
8653
+ message: `Are you sure you want to clear the cache`
8654
+ });
8655
+ if (!ok) {
8656
+ throw new Cancelled3();
8657
+ }
8658
+ const { shared, app } = createApp({ appConfig, stackConfigs, accountId });
8659
+ const { workspace } = await createWorkSpace({
8660
+ credentials,
8661
+ accountId,
8662
+ profile,
8663
+ region
8664
+ });
8665
+ await workspace.hydrate(app);
8666
+ let distributionId;
8667
+ let cacheBucket;
8668
+ try {
8669
+ distributionId = await shared.entry("icon", "distribution-id", name);
8670
+ cacheBucket = await shared.entry("icon", "cache-bucket", name);
8671
+ } catch (_) {
8672
+ throw new ExpectedError(`The icon proxy hasn't been deployed yet.`);
8673
+ }
8674
+ const s3Client2 = new S3Client4({
8675
+ credentials,
8676
+ region
8677
+ });
8678
+ let continuationToken;
8679
+ let totalDeleted = 0;
8680
+ let hasMore = true;
8681
+ while (hasMore) {
8682
+ const result = await s3Client2.send(
8683
+ new ListObjectsV2Command2({
8684
+ Bucket: cacheBucket,
8685
+ ContinuationToken: continuationToken,
8686
+ MaxKeys: 1e3
8687
+ // Maximum allowed per request
8688
+ })
8689
+ );
8690
+ if (result.Contents && result.Contents.length > 0) {
8691
+ await s3Client2.send(
8692
+ new DeleteObjectsCommand2({
8693
+ Bucket: cacheBucket,
8694
+ Delete: {
8695
+ Objects: result.Contents.map((obj) => ({
8696
+ Key: obj.Key
8697
+ })),
8698
+ Quiet: true
8699
+ }
8700
+ })
8701
+ );
8702
+ totalDeleted += result.Contents.length;
8703
+ log24.info(`${result.Contents.length} objects deleted from cache.`);
8704
+ }
8705
+ continuationToken = result.NextContinuationToken;
8706
+ hasMore = !!continuationToken;
8707
+ }
8708
+ const cloudFrontClient = new CloudFrontClient3({
8709
+ credentials,
8710
+ region: "us-east-1"
8711
+ });
8712
+ await cloudFrontClient.send(
8713
+ new CreateInvalidationCommand3({
8714
+ DistributionId: distributionId,
8715
+ InvalidationBatch: {
8716
+ CallerReference: `icon-cache-clear-${Date.now()}`,
8717
+ Paths: {
8718
+ Quantity: 1,
8719
+ Items: ["/*"]
8720
+ }
8721
+ }
8722
+ })
8723
+ );
8724
+ return "Cache has been cleared";
8725
+ });
8726
+ });
8727
+ };
8728
+
8729
+ // src/cli/command/icon/index.ts
8730
+ var commands7 = [clearCache2];
8731
+ var icon2 = (program2) => {
8732
+ const command = program2.command("icon").description(`Manage icon proxies`);
8733
+ commands7.forEach((cb) => cb(command));
8734
+ };
8735
+
8194
8736
  // src/cli/command/index.ts
8195
- var commands6 = [
8737
+ var commands8 = [
8196
8738
  bootstrap,
8197
8739
  types,
8198
8740
  build2,
@@ -8208,7 +8750,9 @@ var commands6 = [
8208
8750
  state,
8209
8751
  resources,
8210
8752
  config,
8211
- test
8753
+ test,
8754
+ image,
8755
+ icon2
8212
8756
  ];
8213
8757
 
8214
8758
  // src/cli/program.ts
@@ -8231,7 +8775,7 @@ program.on("option:skip-prompt", () => {
8231
8775
  program.on("option:no-cache", () => {
8232
8776
  process.env.NO_CACHE = program.opts().noCache ? "1" : void 0;
8233
8777
  });
8234
- commands6.forEach((fn) => fn(program));
8778
+ commands8.forEach((fn) => fn(program));
8235
8779
 
8236
8780
  // src/bin.ts
8237
8781
  program.parse(process.argv);