text: "use strict";
  var __importDefault = (this && this.__importDefault) || function (mod) {
      return (mod && mod.__esModule) ? mod : { "default": mod };
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.hashLayerVersionContents = exports.hashLayerResource = exports.packageLayer = void 0;
  const archiver_1 = __importDefault(require("archiver"));
  const fs_extra_1 = __importDefault(require("fs-extra"));
  const glob_1 = __importDefault(require("glob"));
  const inquirer_1 = require("inquirer");
  const path_1 = __importDefault(require("path"));
  const lodash_1 = __importDefault(require("lodash"));
  const folder_hash_1 = require("folder-hash");
  const amplify_cli_core_1 = require("amplify-cli-core");
  const constants_1 = require("./constants");
  const layerHelpers_1 = require("./layerHelpers");
  const layerParams_1 = require("./layerParams");
  const layerRuntimes_1 = require("./layerRuntimes");
  const crypto_1 = __importDefault(require("crypto"));
  const storeResources_1 = require("./storeResources");
  const globby_1 = __importDefault(require("globby"));
  const packageLayer = async (context, resource) => {
      await ensureLayerVersion(context, resource.resourceName);
      return zipLayer(context, resource);
  };
  exports.packageLayer = packageLayer;
  async function zipLayer(context, resource) {
      const zipFilename = 'latest-build.zip';
      const layerName = resource.resourceName;
      const layerDirPath = path_1.default.join(amplify_cli_core_1.pathManager.getBackendDirPath(), resource.category, layerName);
      const distDir = path_1.default.join(layerDirPath, 'dist');
      fs_extra_1.default.ensureDirSync(distDir);
      const destination = path_1.default.join(distDir, zipFilename);
      const zip = archiver_1.default.create('zip');
      const output = fs_extra_1.default.createWriteStream(destination);
      return new Promise((resolve, reject) => {
          output.on('close', () => {
              if (validFilesize(destination)) {
                  const zipName = `${layerName}-build.zip`;
                  context.amplify.updateAmplifyMetaAfterPackage(resource, zipName);
                  resolve({ zipFilePath: destination, zipFilename: zipName });
              }
              else {
                  reject(new Error('File size greater than 250MB'));
              }
          });
          output.on('error', () => {
              reject(new Error('Failed to zip code.'));
          });
          const libGlob = glob_1.default.sync(path_1.default.join(layerDirPath, 'lib', '*'));
          const optPath = path_1.default.join(layerDirPath, 'opt');
          let conflicts = [];
          libGlob.forEach(lib => {
              const basename = path_1.default.basename(lib);
              if (fs_extra_1.default.pathExistsSync(path_1.default.join(optPath, basename))) {
                  conflicts.push(basename);
              }
          });
          if (conflicts.length > 0) {
              const libs = conflicts.map(lib => `"/${lib}"`).join(', ');
              const plural = conflicts.length > 1 ? 'ies' : 'y';
              context.print.warning(`${libs} sub director${plural} found in both "/lib" and "/opt". These folders will be merged and the files in "/opt" will take precedence if a conflict exists.`);
          }
          zip.pipe(output);
          [optPath, ...libGlob]
              .filter(folder => fs_extra_1.default.lstatSync(folder).isDirectory())
              .forEach(folder => zip.directory(folder, path_1.default.basename(folder) === 'opt' ? false : path_1.default.basename(folder)));
          zip.finalize();
      });
  }
  async function ensureLayerVersion(context, layerName) {
      const layerState = layerParams_1.getLayerMetadataFactory(context)(layerName);
      const isNewVersion = await layerState.syncVersions();
      const latestVersion = layerState.getLatestVersion();
      if (isNewVersion) {
          context.print.success(`Content changes in Lambda layer ${layerName} detected. Layer version increased to ${latestVersion}`);
          context.print.warning('Note: You need to run "amplify update function" to configure your functions with the latest layer version.');
          await setNewVersionPermissions(context, layerName, layerState);
      }
      await layerState.setNewVersionHash();
      const storedParams = layerState.toStoredLayerParameters();
      const additionalLayerParams = {
          layerName,
          build: true,
          providerContext: {
              provider: constants_1.provider,
              service: "LambdaLayer",
              projectName: context.amplify.getProjectDetails().projectConfig.projectName,
          },
      };
      if (layerParams_1.isMultiEnvLayer(context, layerName)) {
          additionalLayerParams.runtimes = layerRuntimes_1.getLayerRuntimes(amplify_cli_core_1.pathManager.getBackendDirPath(), layerName);
      }
      const layerParameters = { ...storedParams, ...additionalLayerParams };
      storeResources_1.updateLayerArtifacts(context, layerParameters, latestVersion, { cfnFile: isNewVersion });
  }
  async function setNewVersionPermissions(context, layerName, layerState) {
      const defaultPermissions = [{ type: layerParams_1.Permission.private }];
      let usePrevPermissions = true;
      const latestVersion = layerState.getLatestVersion();
      const latestVersionState = layerState.getVersion(latestVersion);
      const hasNonDefaultPerms = latestVersionState.isPublic() || latestVersionState.listAccountAccess().length > 0 || latestVersionState.listOrgAccess().length > 0;
      const yesFlagSet = lodash_1.default.get(context, ['parameters', 'options', 'yes'], false);
      if (yesFlagSet) {
          context.print.warning(`Permissions from previous layer version carried forward to new version by default`);
      }
      else if (hasNonDefaultPerms) {
          usePrevPermissions = (await inquirer_1.prompt(layerHelpers_1.previousPermissionsQuestion(layerName))).usePreviousPermissions;
      }
      if (!usePrevPermissions) {
          layerState.setPermissionsForVersion(latestVersion, defaultPermissions);
      }
  }
  const hashLayerResource = async (layerPath) => {
      return (await globby_1.default(['*'], { cwd: layerPath }))
          .map(filePath => fs_extra_1.default.readFileSync(path_1.default.join(layerPath, filePath), 'utf8'))
          .reduce((acc, it) => acc.update(it), crypto_1.default.createHash('sha256'))
          .update(await exports.hashLayerVersionContents(layerPath))
          .digest('base64');
  };
  exports.hashLayerResource = hashLayerResource;
  const hashLayerVersionContents = async (layerPath) => {
      const nodePath = path_1.default.join(layerPath, 'lib', 'nodejs');
      const nodeHashOptions = {
          files: {
              include: ['package.json'],
          },
      };
      const pyPath = path_1.default.join(layerPath, 'lib', 'python');
      const optPath = path_1.default.join(layerPath, 'opt');
      const joinedHashes = (await Promise.all([safeHash(nodePath, nodeHashOptions), safeHash(pyPath), safeHash(optPath)])).join();
      return crypto_1.default.createHash('sha256').update(joinedHashes).digest('base64');
  };
  exports.hashLayerVersionContents = hashLayerVersionContents;
  const safeHash = async (path, opts) => {
      if (fs_extra_1.default.pathExistsSync(path)) {
          return (await folder_hash_1.hashElement(path, opts).catch(() => {
              throw new Error(`An error occurred hashing directory ${path}`);
          })).hash;
      }
      return '';
  };
  function validFilesize(path, maxSize = 250) {
      try {
          const { size } = fs_extra_1.default.statSync(path);
          const fileSize = Math.round(size / 1024 ** 2);
          return fileSize < maxSize;
      }
      catch (error) {
          return new Error(`Calculating file size failed: ${path}`);
      }
  }
  //# sourceMappingURL=packageLayer.js.map