@hot-updater/aws 0.25.11 → 0.25.14

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.
@@ -6457,6 +6457,221 @@ function omit(obj, keys) {
6457
6457
  return result;
6458
6458
  }
6459
6459
 
6460
+ //#endregion
6461
+ //#region iac/cloudfrontDistributionConfig.ts
6462
+ const HOT_UPDATER_LEGACY_CHECK_UPDATE_HEADERS = [
6463
+ "x-bundle-id",
6464
+ "x-app-version",
6465
+ "x-app-platform",
6466
+ "x-min-bundle-id",
6467
+ "x-channel",
6468
+ "x-fingerprint-hash"
6469
+ ];
6470
+ const HOT_UPDATER_LEGACY_CHECK_UPDATE_CACHE_POLICY_CONFIG = {
6471
+ Name: "HotUpdaterLegacyCheckUpdateNoCache",
6472
+ Comment: "Forward legacy check-update headers to origin-request Lambda with effectively no cache",
6473
+ DefaultTTL: 0,
6474
+ MaxTTL: 1,
6475
+ MinTTL: 0,
6476
+ ParametersInCacheKeyAndForwardedToOrigin: {
6477
+ EnableAcceptEncodingBrotli: false,
6478
+ EnableAcceptEncodingGzip: false,
6479
+ HeadersConfig: {
6480
+ HeaderBehavior: "whitelist",
6481
+ Headers: {
6482
+ Quantity: HOT_UPDATER_LEGACY_CHECK_UPDATE_HEADERS.length,
6483
+ Items: [...HOT_UPDATER_LEGACY_CHECK_UPDATE_HEADERS]
6484
+ }
6485
+ },
6486
+ CookiesConfig: { CookieBehavior: "none" },
6487
+ QueryStringsConfig: { QueryStringBehavior: "none" }
6488
+ }
6489
+ };
6490
+ const HOT_UPDATER_SHARED_CACHE_POLICY_CONFIG = {
6491
+ Name: "HotUpdaterOriginCacheControl",
6492
+ Comment: "Honor origin Cache-Control without forwarding viewer Host/cookies/query strings",
6493
+ DefaultTTL: 0,
6494
+ MaxTTL: 31536e3,
6495
+ MinTTL: 0,
6496
+ ParametersInCacheKeyAndForwardedToOrigin: {
6497
+ EnableAcceptEncodingBrotli: true,
6498
+ EnableAcceptEncodingGzip: true,
6499
+ HeadersConfig: { HeaderBehavior: "none" },
6500
+ CookiesConfig: { CookieBehavior: "none" },
6501
+ QueryStringsConfig: { QueryStringBehavior: "none" }
6502
+ }
6503
+ };
6504
+ const READ_ONLY_METHODS = {
6505
+ Quantity: 2,
6506
+ Items: ["HEAD", "GET"],
6507
+ CachedMethods: {
6508
+ Quantity: 2,
6509
+ Items: ["HEAD", "GET"]
6510
+ }
6511
+ };
6512
+ const EMPTY_FUNCTION_ASSOCIATIONS = { Quantity: 0 };
6513
+ const EMPTY_LAMBDA_FUNCTION_ASSOCIATIONS = { Quantity: 0 };
6514
+ const HOT_UPDATER_BEHAVIOR_BASE = {
6515
+ ViewerProtocolPolicy: "redirect-to-https",
6516
+ SmoothStreaming: false,
6517
+ Compress: true,
6518
+ FunctionAssociations: EMPTY_FUNCTION_ASSOCIATIONS,
6519
+ FieldLevelEncryptionId: "",
6520
+ AllowedMethods: READ_ONLY_METHODS
6521
+ };
6522
+ const HOT_UPDATER_CACHE_BEHAVIOR_TEMPLATES = [{
6523
+ pathPattern: "/api/check-update",
6524
+ cachePolicy: "legacy"
6525
+ }, {
6526
+ pathPattern: "/api/check-update/*",
6527
+ cachePolicy: "shared"
6528
+ }];
6529
+ const omitLegacyCacheFields = (value) => {
6530
+ const { ForwardedValues: _forwardedValues, MinTTL: _minTTL, DefaultTTL: _defaultTTL, MaxTTL: _maxTTL, OriginRequestPolicyId: _originRequestPolicyId,...rest } = value;
6531
+ return rest;
6532
+ };
6533
+ const sanitizeDefaultBehavior = (behavior) => ({
6534
+ ...omitLegacyCacheFields(behavior),
6535
+ LambdaFunctionAssociations: behavior.LambdaFunctionAssociations ?? EMPTY_LAMBDA_FUNCTION_ASSOCIATIONS,
6536
+ FunctionAssociations: behavior.FunctionAssociations ?? EMPTY_FUNCTION_ASSOCIATIONS
6537
+ });
6538
+ const sanitizeCacheBehavior = (behavior) => ({
6539
+ ...omitLegacyCacheFields(behavior),
6540
+ LambdaFunctionAssociations: behavior.LambdaFunctionAssociations ?? EMPTY_LAMBDA_FUNCTION_ASSOCIATIONS,
6541
+ FunctionAssociations: behavior.FunctionAssociations ?? EMPTY_FUNCTION_ASSOCIATIONS
6542
+ });
6543
+ const sanitizeDistributionConfig = (distributionConfig) => ({
6544
+ ...distributionConfig,
6545
+ DefaultCacheBehavior: distributionConfig.DefaultCacheBehavior ? sanitizeDefaultBehavior(distributionConfig.DefaultCacheBehavior) : distributionConfig.DefaultCacheBehavior,
6546
+ CacheBehaviors: distributionConfig.CacheBehaviors ? {
6547
+ Quantity: distributionConfig.CacheBehaviors.Quantity,
6548
+ Items: (distributionConfig.CacheBehaviors.Items ?? []).map((behavior) => sanitizeCacheBehavior(behavior))
6549
+ } : distributionConfig.CacheBehaviors
6550
+ });
6551
+ const buildOriginRequestLambdaAssociations = (functionArn) => ({
6552
+ Quantity: 1,
6553
+ Items: [{
6554
+ EventType: "origin-request",
6555
+ LambdaFunctionARN: functionArn
6556
+ }]
6557
+ });
6558
+ const buildS3Origin = (options) => ({
6559
+ Id: options.bucketName,
6560
+ DomainName: options.bucketDomain,
6561
+ OriginAccessControlId: options.oacId,
6562
+ S3OriginConfig: { OriginAccessIdentity: "" },
6563
+ CustomHeaders: { Quantity: 0 }
6564
+ });
6565
+ const buildSharedBehavior = (targetOriginId) => ({
6566
+ TargetOriginId: targetOriginId,
6567
+ ...HOT_UPDATER_BEHAVIOR_BASE
6568
+ });
6569
+ const buildDefaultCacheBehavior = (options) => ({
6570
+ ...buildSharedBehavior(options.bucketName),
6571
+ TrustedKeyGroups: {
6572
+ Enabled: true,
6573
+ Quantity: 1,
6574
+ Items: [options.keyGroupId]
6575
+ },
6576
+ CachePolicyId: options.sharedCachePolicyId,
6577
+ LambdaFunctionAssociations: EMPTY_LAMBDA_FUNCTION_ASSOCIATIONS
6578
+ });
6579
+ const resolveCachePolicyId = (cachePolicy, { legacyCachePolicyId, sharedCachePolicyId }) => {
6580
+ if (cachePolicy === "legacy") return legacyCachePolicyId;
6581
+ if (cachePolicy === "shared") return sharedCachePolicyId;
6582
+ };
6583
+ const buildCacheBehavior = (template, options) => ({
6584
+ ...buildSharedBehavior(options.bucketName),
6585
+ PathPattern: template.pathPattern,
6586
+ CachePolicyId: resolveCachePolicyId(template.cachePolicy, {
6587
+ legacyCachePolicyId: options.legacyCachePolicyId,
6588
+ sharedCachePolicyId: options.sharedCachePolicyId
6589
+ }),
6590
+ LambdaFunctionAssociations: buildOriginRequestLambdaAssociations(options.functionArn)
6591
+ });
6592
+ const mergeOriginWithExisting = (existingOrigin, overrideOrigin) => ({
6593
+ ...existingOrigin,
6594
+ ...overrideOrigin,
6595
+ CustomHeaders: existingOrigin?.CustomHeaders ?? { Quantity: 0 }
6596
+ });
6597
+ const mergeBehaviorWithExisting = (existingBehavior, overrideBehavior) => ({
6598
+ ...omitLegacyCacheFields(existingBehavior ?? {}),
6599
+ ...overrideBehavior,
6600
+ LambdaFunctionAssociations: overrideBehavior.LambdaFunctionAssociations ?? existingBehavior?.LambdaFunctionAssociations ?? EMPTY_LAMBDA_FUNCTION_ASSOCIATIONS,
6601
+ FunctionAssociations: overrideBehavior.FunctionAssociations ?? existingBehavior?.FunctionAssociations ?? EMPTY_FUNCTION_ASSOCIATIONS
6602
+ });
6603
+ const buildDistributionConfigOverrides = (options) => ({
6604
+ Origins: {
6605
+ Quantity: 1,
6606
+ Items: [buildS3Origin({
6607
+ bucketName: options.bucketName,
6608
+ bucketDomain: options.bucketDomain,
6609
+ oacId: options.oacId
6610
+ })]
6611
+ },
6612
+ DefaultCacheBehavior: buildDefaultCacheBehavior({
6613
+ bucketName: options.bucketName,
6614
+ keyGroupId: options.keyGroupId,
6615
+ sharedCachePolicyId: options.sharedCachePolicyId
6616
+ }),
6617
+ CacheBehaviors: {
6618
+ Quantity: HOT_UPDATER_CACHE_BEHAVIOR_TEMPLATES.length,
6619
+ Items: HOT_UPDATER_CACHE_BEHAVIOR_TEMPLATES.map((template) => buildCacheBehavior(template, {
6620
+ bucketName: options.bucketName,
6621
+ functionArn: options.functionArn,
6622
+ legacyCachePolicyId: options.legacyCachePolicyId,
6623
+ sharedCachePolicyId: options.sharedCachePolicyId
6624
+ }))
6625
+ }
6626
+ });
6627
+ const applyDistributionConfigOverrides = (distributionConfig, overrides) => {
6628
+ return sanitizeDistributionConfig({
6629
+ ...distributionConfig,
6630
+ Origins: {
6631
+ Quantity: overrides.Origins.Quantity,
6632
+ Items: (overrides.Origins.Items ?? []).map((overrideOrigin) => {
6633
+ return mergeOriginWithExisting((distributionConfig.Origins?.Items ?? []).find((origin) => origin.Id === overrideOrigin.Id || origin.DomainName === overrideOrigin.DomainName), overrideOrigin);
6634
+ })
6635
+ },
6636
+ DefaultCacheBehavior: mergeBehaviorWithExisting(distributionConfig.DefaultCacheBehavior, overrides.DefaultCacheBehavior),
6637
+ CacheBehaviors: {
6638
+ Quantity: overrides.CacheBehaviors.Quantity,
6639
+ Items: (overrides.CacheBehaviors.Items ?? []).map((overrideBehavior) => {
6640
+ return mergeBehaviorWithExisting((distributionConfig.CacheBehaviors?.Items ?? []).find((behavior) => behavior.PathPattern === overrideBehavior.PathPattern), overrideBehavior);
6641
+ })
6642
+ }
6643
+ });
6644
+ };
6645
+ const buildDistributionConfig = (options) => sanitizeDistributionConfig({
6646
+ CallerReference: (/* @__PURE__ */ new Date()).toISOString(),
6647
+ Comment: "Hot Updater CloudFront distribution",
6648
+ Enabled: true,
6649
+ ...buildDistributionConfigOverrides(options),
6650
+ DefaultRootObject: "index.html",
6651
+ ViewerCertificate: { CloudFrontDefaultCertificate: true },
6652
+ Restrictions: { GeoRestriction: {
6653
+ RestrictionType: "none",
6654
+ Quantity: 0
6655
+ } },
6656
+ PriceClass: "PriceClass_All",
6657
+ Aliases: {
6658
+ Quantity: 0,
6659
+ Items: []
6660
+ }
6661
+ });
6662
+
6663
+ //#endregion
6664
+ //#region iac/cloudfrontPagination.ts
6665
+ const findInPaginatedCloudFrontList = async ({ listPage, matches }) => {
6666
+ let marker;
6667
+ do {
6668
+ const { items, nextMarker } = await listPage(marker);
6669
+ const matchedItem = items.find(matches);
6670
+ if (matchedItem) return matchedItem;
6671
+ marker = nextMarker;
6672
+ } while (marker);
6673
+ };
6674
+
6460
6675
  //#endregion
6461
6676
  //#region iac/cloudfront.ts
6462
6677
  var CloudFrontManager = class {
@@ -6466,6 +6681,44 @@ var CloudFrontManager = class {
6466
6681
  this.region = region;
6467
6682
  this.credentials = credentials;
6468
6683
  }
6684
+ async getOrCreateSharedCachePolicy(cloudfrontClient) {
6685
+ const existingPolicyId = (await findInPaginatedCloudFrontList({
6686
+ listPage: async (marker) => {
6687
+ const listPoliciesResponse = await cloudfrontClient.listCachePolicies({
6688
+ Type: "custom",
6689
+ ...marker ? { Marker: marker } : {}
6690
+ });
6691
+ return {
6692
+ items: listPoliciesResponse.CachePolicyList?.Items ?? [],
6693
+ nextMarker: listPoliciesResponse.CachePolicyList?.NextMarker
6694
+ };
6695
+ },
6696
+ matches: (policy) => policy.CachePolicy?.CachePolicyConfig?.Name === HOT_UPDATER_SHARED_CACHE_POLICY_CONFIG.Name
6697
+ }))?.CachePolicy?.Id;
6698
+ if (existingPolicyId) return existingPolicyId;
6699
+ const cachePolicyId = (await cloudfrontClient.createCachePolicy({ CachePolicyConfig: HOT_UPDATER_SHARED_CACHE_POLICY_CONFIG })).CachePolicy?.Id;
6700
+ if (!cachePolicyId) throw new Error("Failed to create shared cache policy");
6701
+ return cachePolicyId;
6702
+ }
6703
+ async getOrCreateLegacyCheckUpdateCachePolicy(cloudfrontClient) {
6704
+ const existingPolicyId = (await findInPaginatedCloudFrontList({
6705
+ listPage: async (marker) => {
6706
+ const listPoliciesResponse = await cloudfrontClient.listCachePolicies({
6707
+ Type: "custom",
6708
+ ...marker ? { Marker: marker } : {}
6709
+ });
6710
+ return {
6711
+ items: listPoliciesResponse.CachePolicyList?.Items ?? [],
6712
+ nextMarker: listPoliciesResponse.CachePolicyList?.NextMarker
6713
+ };
6714
+ },
6715
+ matches: (policy) => policy.CachePolicy?.CachePolicyConfig?.Name === HOT_UPDATER_LEGACY_CHECK_UPDATE_CACHE_POLICY_CONFIG.Name
6716
+ }))?.CachePolicy?.Id;
6717
+ if (existingPolicyId) return existingPolicyId;
6718
+ const cachePolicyId = (await cloudfrontClient.createCachePolicy({ CachePolicyConfig: HOT_UPDATER_LEGACY_CHECK_UPDATE_CACHE_POLICY_CONFIG })).CachePolicy?.Id;
6719
+ if (!cachePolicyId) throw new Error("Failed to create legacy check-update cache policy");
6720
+ return cachePolicyId;
6721
+ }
6469
6722
  async getOrCreateKeyGroup(publicKey) {
6470
6723
  const publicKeyHash = crypto.default.createHash("sha256").update(publicKey).digest("hex").slice(0, 16);
6471
6724
  const cloudfrontClient = new __aws_sdk_client_cloudfront.CloudFront({
@@ -6528,6 +6781,18 @@ var CloudFrontManager = class {
6528
6781
  }
6529
6782
  if (!oacId) throw new Error("Failed to get Origin Access Control ID");
6530
6783
  const bucketDomain = `${options.bucketName}.s3.${this.region}.amazonaws.com`;
6784
+ let legacyCachePolicyId;
6785
+ let sharedCachePolicyId;
6786
+ try {
6787
+ legacyCachePolicyId = await this.getOrCreateLegacyCheckUpdateCachePolicy(cloudfrontClient);
6788
+ } catch (error) {
6789
+ throw new Error(`Failed to get or create legacy check-update cache policy: ${error instanceof Error ? error.message : String(error)}`);
6790
+ }
6791
+ try {
6792
+ sharedCachePolicyId = await this.getOrCreateSharedCachePolicy(cloudfrontClient);
6793
+ } catch (error) {
6794
+ throw new Error(`Failed to get or create shared cache policy: ${error instanceof Error ? error.message : String(error)}`);
6795
+ }
6531
6796
  const matchingDistributions = [];
6532
6797
  try {
6533
6798
  const items = (await cloudfrontClient.listDistributions({})).DistributionList?.Items || [];
@@ -6551,136 +6816,21 @@ var CloudFrontManager = class {
6551
6816
  if (__hot_updater_cli_tools.p.isCancel(selectedDistributionStr)) process.exit(0);
6552
6817
  selectedDistribution = JSON.parse(selectedDistributionStr);
6553
6818
  }
6554
- const newOverrides = {
6555
- Origins: {
6556
- Quantity: 1,
6557
- Items: [{
6558
- Id: options.bucketName,
6559
- DomainName: bucketDomain,
6560
- OriginAccessControlId: oacId,
6561
- S3OriginConfig: { OriginAccessIdentity: "" }
6562
- }]
6563
- },
6564
- DefaultCacheBehavior: {
6565
- TargetOriginId: options.bucketName,
6566
- ViewerProtocolPolicy: "redirect-to-https",
6567
- TrustedKeyGroups: {
6568
- Enabled: true,
6569
- Quantity: 1,
6570
- Items: [options.keyGroupId]
6571
- },
6572
- ForwardedValues: {
6573
- QueryString: true,
6574
- Cookies: { Forward: "none" },
6575
- QueryStringCacheKeys: {
6576
- Quantity: 0,
6577
- Items: []
6578
- }
6579
- },
6580
- MinTTL: 0,
6581
- SmoothStreaming: false,
6582
- Compress: true,
6583
- FieldLevelEncryptionId: "",
6584
- AllowedMethods: {
6585
- Quantity: 2,
6586
- Items: ["HEAD", "GET"],
6587
- CachedMethods: {
6588
- Quantity: 2,
6589
- Items: ["HEAD", "GET"]
6590
- }
6591
- }
6592
- },
6593
- CacheBehaviors: {
6594
- Quantity: 2,
6595
- Items: [{
6596
- PathPattern: "/api/check-update",
6597
- TargetOriginId: options.bucketName,
6598
- ViewerProtocolPolicy: "redirect-to-https",
6599
- LambdaFunctionAssociations: {
6600
- Quantity: 1,
6601
- Items: [{
6602
- EventType: "origin-request",
6603
- LambdaFunctionARN: options.functionArn
6604
- }]
6605
- },
6606
- MinTTL: 0,
6607
- DefaultTTL: 0,
6608
- MaxTTL: 0,
6609
- SmoothStreaming: false,
6610
- Compress: true,
6611
- FieldLevelEncryptionId: "",
6612
- AllowedMethods: {
6613
- Quantity: 2,
6614
- Items: ["HEAD", "GET"],
6615
- CachedMethods: {
6616
- Quantity: 2,
6617
- Items: ["HEAD", "GET"]
6618
- }
6619
- },
6620
- ForwardedValues: {
6621
- QueryString: false,
6622
- Cookies: { Forward: "none" },
6623
- Headers: {
6624
- Quantity: 6,
6625
- Items: [
6626
- "x-bundle-id",
6627
- "x-app-version",
6628
- "x-app-platform",
6629
- "x-min-bundle-id",
6630
- "x-channel",
6631
- "x-fingerprint-hash"
6632
- ]
6633
- },
6634
- QueryStringCacheKeys: {
6635
- Quantity: 0,
6636
- Items: []
6637
- }
6638
- }
6639
- }, {
6640
- PathPattern: "/api/check-update/*",
6641
- TargetOriginId: options.bucketName,
6642
- ViewerProtocolPolicy: "redirect-to-https",
6643
- LambdaFunctionAssociations: {
6644
- Quantity: 1,
6645
- Items: [{
6646
- EventType: "origin-request",
6647
- LambdaFunctionARN: options.functionArn
6648
- }]
6649
- },
6650
- MinTTL: 0,
6651
- DefaultTTL: 31536e3,
6652
- MaxTTL: 31536e3,
6653
- SmoothStreaming: false,
6654
- Compress: true,
6655
- FieldLevelEncryptionId: "",
6656
- AllowedMethods: {
6657
- Quantity: 2,
6658
- Items: ["HEAD", "GET"],
6659
- CachedMethods: {
6660
- Quantity: 2,
6661
- Items: ["HEAD", "GET"]
6662
- }
6663
- },
6664
- ForwardedValues: {
6665
- QueryString: false,
6666
- Cookies: { Forward: "none" },
6667
- Headers: {
6668
- Quantity: 0,
6669
- Items: []
6670
- },
6671
- QueryStringCacheKeys: {
6672
- Quantity: 0,
6673
- Items: []
6674
- }
6675
- }
6676
- }]
6677
- }
6678
- };
6819
+ const newOverrides = buildDistributionConfigOverrides({
6820
+ bucketName: options.bucketName,
6821
+ bucketDomain,
6822
+ functionArn: options.functionArn,
6823
+ keyGroupId: options.keyGroupId,
6824
+ oacId,
6825
+ legacyCachePolicyId,
6826
+ sharedCachePolicyId
6827
+ });
6679
6828
  if (selectedDistribution) {
6680
6829
  __hot_updater_cli_tools.p.log.success(`Existing CloudFront distribution selected. Distribution ID: ${selectedDistribution.Id}.`);
6681
6830
  try {
6682
6831
  const { DistributionConfig, ETag } = await cloudfrontClient.getDistributionConfig({ Id: selectedDistribution.Id });
6683
- const finalConfig = merge(DistributionConfig ?? {}, newOverrides);
6832
+ if (!DistributionConfig) throw new Error("CloudFront distribution config was not returned");
6833
+ const finalConfig = applyDistributionConfigOverrides(DistributionConfig, newOverrides);
6684
6834
  await cloudfrontClient.updateDistribution({
6685
6835
  Id: selectedDistribution.Id,
6686
6836
  IfMatch: ETag,
@@ -6707,145 +6857,15 @@ var CloudFrontManager = class {
6707
6857
  throw err;
6708
6858
  }
6709
6859
  }
6710
- const finalDistributionConfig = {
6711
- CallerReference: (/* @__PURE__ */ new Date()).toISOString(),
6712
- Comment: "Hot Updater CloudFront distribution",
6713
- Enabled: true,
6714
- Origins: {
6715
- Quantity: 1,
6716
- Items: [{
6717
- Id: options.bucketName,
6718
- DomainName: bucketDomain,
6719
- OriginAccessControlId: oacId,
6720
- S3OriginConfig: { OriginAccessIdentity: "" }
6721
- }]
6722
- },
6723
- DefaultCacheBehavior: {
6724
- TargetOriginId: options.bucketName,
6725
- ViewerProtocolPolicy: "redirect-to-https",
6726
- TrustedKeyGroups: {
6727
- Enabled: true,
6728
- Quantity: 1,
6729
- Items: [options.keyGroupId]
6730
- },
6731
- ForwardedValues: {
6732
- QueryString: true,
6733
- Cookies: { Forward: "none" },
6734
- QueryStringCacheKeys: {
6735
- Quantity: 0,
6736
- Items: []
6737
- }
6738
- },
6739
- MinTTL: 0,
6740
- SmoothStreaming: false,
6741
- Compress: true,
6742
- FieldLevelEncryptionId: "",
6743
- AllowedMethods: {
6744
- Quantity: 2,
6745
- Items: ["HEAD", "GET"],
6746
- CachedMethods: {
6747
- Quantity: 2,
6748
- Items: ["HEAD", "GET"]
6749
- }
6750
- }
6751
- },
6752
- CacheBehaviors: {
6753
- Quantity: 2,
6754
- Items: [{
6755
- PathPattern: "/api/check-update",
6756
- TargetOriginId: options.bucketName,
6757
- ViewerProtocolPolicy: "redirect-to-https",
6758
- LambdaFunctionAssociations: {
6759
- Quantity: 1,
6760
- Items: [{
6761
- EventType: "origin-request",
6762
- LambdaFunctionARN: options.functionArn
6763
- }]
6764
- },
6765
- MinTTL: 0,
6766
- DefaultTTL: 0,
6767
- MaxTTL: 0,
6768
- SmoothStreaming: false,
6769
- Compress: true,
6770
- FieldLevelEncryptionId: "",
6771
- AllowedMethods: {
6772
- Quantity: 2,
6773
- Items: ["HEAD", "GET"],
6774
- CachedMethods: {
6775
- Quantity: 2,
6776
- Items: ["HEAD", "GET"]
6777
- }
6778
- },
6779
- ForwardedValues: {
6780
- QueryString: false,
6781
- Cookies: { Forward: "none" },
6782
- Headers: {
6783
- Quantity: 6,
6784
- Items: [
6785
- "x-bundle-id",
6786
- "x-app-version",
6787
- "x-app-platform",
6788
- "x-min-bundle-id",
6789
- "x-channel",
6790
- "x-fingerprint-hash"
6791
- ]
6792
- },
6793
- QueryStringCacheKeys: {
6794
- Quantity: 0,
6795
- Items: []
6796
- }
6797
- }
6798
- }, {
6799
- PathPattern: "/api/check-update/*",
6800
- TargetOriginId: options.bucketName,
6801
- ViewerProtocolPolicy: "redirect-to-https",
6802
- LambdaFunctionAssociations: {
6803
- Quantity: 1,
6804
- Items: [{
6805
- EventType: "origin-request",
6806
- LambdaFunctionARN: options.functionArn
6807
- }]
6808
- },
6809
- MinTTL: 0,
6810
- DefaultTTL: 31536e3,
6811
- MaxTTL: 31536e3,
6812
- SmoothStreaming: false,
6813
- Compress: true,
6814
- FieldLevelEncryptionId: "",
6815
- AllowedMethods: {
6816
- Quantity: 2,
6817
- Items: ["HEAD", "GET"],
6818
- CachedMethods: {
6819
- Quantity: 2,
6820
- Items: ["HEAD", "GET"]
6821
- }
6822
- },
6823
- ForwardedValues: {
6824
- QueryString: false,
6825
- Cookies: { Forward: "none" },
6826
- Headers: {
6827
- Quantity: 0,
6828
- Items: []
6829
- },
6830
- QueryStringCacheKeys: {
6831
- Quantity: 0,
6832
- Items: []
6833
- }
6834
- }
6835
- }]
6836
- },
6837
- DefaultRootObject: "index.html",
6838
- ViewerCertificate: { CloudFrontDefaultCertificate: true },
6839
- Restrictions: { GeoRestriction: {
6840
- RestrictionType: "none",
6841
- Quantity: 0
6842
- } },
6843
- PriceClass: "PriceClass_All",
6844
- Aliases: {
6845
- Quantity: 0,
6846
- Items: []
6847
- }
6848
- };
6860
+ const finalDistributionConfig = buildDistributionConfig({
6861
+ bucketName: options.bucketName,
6862
+ bucketDomain,
6863
+ functionArn: options.functionArn,
6864
+ keyGroupId: options.keyGroupId,
6865
+ oacId,
6866
+ legacyCachePolicyId,
6867
+ sharedCachePolicyId
6868
+ });
6849
6869
  try {
6850
6870
  const distResp = await cloudfrontClient.createDistribution({ DistributionConfig: finalDistributionConfig });
6851
6871
  if (!distResp.Distribution?.Id || !distResp.Distribution?.DomainName) throw new Error("Failed to create CloudFront distribution: No ID or DomainName returned");
@@ -6887,6 +6907,14 @@ var IAMManager = class {
6887
6907
  this.region = region;
6888
6908
  this.credentials = credentials;
6889
6909
  }
6910
+ async ensureManagedPolicies(iamClient, roleName) {
6911
+ const attachedPolicies = await iamClient.listAttachedRolePolicies({ RoleName: roleName });
6912
+ const attachedPolicyArns = new Set((attachedPolicies.AttachedPolicies ?? []).map((policy) => policy.PolicyArn));
6913
+ for (const policyArn of ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"]) if (!attachedPolicyArns.has(policyArn)) await iamClient.attachRolePolicy({
6914
+ RoleName: roleName,
6915
+ PolicyArn: policyArn
6916
+ });
6917
+ }
6890
6918
  async createOrSelectRole() {
6891
6919
  const iamClient = new __aws_sdk_client_iam.IAM({
6892
6920
  region: this.region,
@@ -6917,6 +6945,7 @@ var IAMManager = class {
6917
6945
  try {
6918
6946
  const { Role: existingRole } = await iamClient.getRole({ RoleName: roleName });
6919
6947
  if (existingRole?.Arn) {
6948
+ await this.ensureManagedPolicies(iamClient, roleName);
6920
6949
  try {
6921
6950
  await iamClient.putRolePolicy({
6922
6951
  RoleName: roleName,
@@ -6940,14 +6969,7 @@ var IAMManager = class {
6940
6969
  if (!createRoleResp.Role?.Arn) throw new Error("Failed to create IAM role: No ARN returned");
6941
6970
  const lambdaRoleArn = createRoleResp.Role.Arn;
6942
6971
  __hot_updater_cli_tools.p.log.info(`Created IAM role: ${roleName} (${lambdaRoleArn})`);
6943
- await iamClient.attachRolePolicy({
6944
- RoleName: roleName,
6945
- PolicyArn: "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
6946
- });
6947
- await iamClient.attachRolePolicy({
6948
- RoleName: roleName,
6949
- PolicyArn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
6950
- });
6972
+ await this.ensureManagedPolicies(iamClient, roleName);
6951
6973
  __hot_updater_cli_tools.p.log.info(`Attached managed policies to ${roleName}`);
6952
6974
  await iamClient.putRolePolicy({
6953
6975
  RoleName: roleName,
@@ -6986,7 +7008,8 @@ var LambdaEdgeDeployer = class {
6986
7008
  const code = (0, __hot_updater_cli_tools.transformEnv)(indexPath, {
6987
7009
  CLOUDFRONT_KEY_PAIR_ID: config.publicKeyId,
6988
7010
  SSM_PARAMETER_NAME: config.ssmParameterName,
6989
- SSM_REGION: config.ssmRegion
7011
+ SSM_REGION: config.ssmRegion,
7012
+ S3_BUCKET_NAME: config.bucketName
6990
7013
  });
6991
7014
  await fs_promises.default.writeFile(indexPath, code);
6992
7015
  const lambdaClient = new __aws_sdk_client_lambda.Lambda({
@@ -7754,6 +7777,7 @@ const runInit = async ({ build }) => {
7754
7777
  const lambdaEdgeDeployer = new LambdaEdgeDeployer(credentials);
7755
7778
  const ssmParameterName = `/hot-updater/${bucketName}/keypair`;
7756
7779
  const { functionArn } = await lambdaEdgeDeployer.deploy(lambdaRoleArn, {
7780
+ bucketName,
7757
7781
  publicKeyId,
7758
7782
  ssmParameterName,
7759
7783
  ssmRegion: bucketRegion