@hot-updater/aws 0.25.13 → 0.26.0

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