text: "use strict";
  /**
   * Copyright 2014-2017 Google Inc. All Rights Reserved.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  Object.defineProperty(exports, "__esModule", { value: true });
  const common_1 = require("@google-cloud/common");
  const paginator_1 = require("@google-cloud/paginator");
  const promisify_1 = require("@google-cloud/promisify");
  const arrify = require("arrify");
  const async = require("async");
  const extend = require("extend");
  const fs = require("fs");
  const mime = require("mime-types");
  const path = require("path");
  const snakeize = require('snakeize');
  const acl_1 = require("./acl");
  const file_1 = require("./file");
  const iam_1 = require("./iam");
  const notification_1 = require("./notification");
  /**
   * The size of a file (in bytes) must be greater than this number to
   * automatically trigger a resumable upload.
   *
   * @const {number}
   * @private
   */
  const RESUMABLE_THRESHOLD = 5000000;
  /**
   * Create a Bucket object to interact with a Cloud Storage bucket.
   *
   * @class
   * @hideconstructor
   *
   * @param {Storage} storage A {@link Storage} instance.
   * @param {string} name The name of the bucket.
   * @param {object} [options] Configuration object.
   * @param {string} [options.userProject] User project.
   *
   * @example
   * const {Storage} = require('@google-cloud/storage');
   * const storage = new Storage();
   * const bucket = storage.bucket('albums');
   */
  class Bucket extends common_1.ServiceObject {
      constructor(storage, name, options) {
          options = options || {};
          // Allow for "gs://"-style input, and strip any trailing slashes.
          name = name.replace(/^gs:\/\//, '').replace(/\/+$/, '');
          const requestQueryObject = {};
          const userProject = options.userProject;
          if (typeof userProject === 'string') {
              requestQueryObject.userProject = userProject;
          }
          const methods = {
              /**
               * Create a bucket.
               *
               * @method Bucket#create
               * @param {CreateBucketRequest} [metadata] Metadata to set for the bucket.
               * @param {CreateBucketCallback} [callback] Callback function.
               * @returns {Promise<CreateBucketResponse>}
               *
               * @example
               * const {Storage} = require('@google-cloud/storage');
               * const storage = new Storage();
               * const bucket = storage.bucket('albums');
               * bucket.create(function(err, bucket, apiResponse) {
               *   if (!err) {
               *     // The bucket was created successfully.
               *   }
               * });
               *
               * //-
               * // If the callback is omitted, we'll return a Promise.
               * //-
               * bucket.create().then(function(data) {
               *   const bucket = data[0];
               *   const apiResponse = data[1];
               * });
               */
              create: {
                  reqOpts: {
                      qs: requestQueryObject,
                  },
              },
              /**
               * @typedef {object} DeleteBucketOptions Configuration options.
               * @param {string} [userProject] The ID of the project which will be
               *     billed for the request.
               */
              /**
               * @typedef {array} DeleteBucketResponse
               * @property {object} 0 The full API response.
               */
              /**
               * @callback DeleteBucketCallback
               * @param {?Error} err Request error, if any.
               * @param {object} apiResponse The full API response.
               */
              /**
               * Delete the bucket.
               *
               * @see [Buckets: delete API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/delete}
               *
               * @method Bucket#delete
               * @param {DeleteBucketOptions} [options] Configuration options.
               * @param {DeleteBucketCallback} [callback] Callback function.
               * @returns {Promise<DeleteBucketResponse>}
               *
               * @example
               * const {Storage} = require('@google-cloud/storage');
               * const storage = new Storage();
               * const bucket = storage.bucket('albums');
               * bucket.delete(function(err, apiResponse) {});
               *
               * //-
               * // If the callback is omitted, we'll return a Promise.
               * //-
               * bucket.delete().then(function(data) {
               *   const apiResponse = data[0];
               * });
               *
               * @example <caption>include:samples/buckets.js</caption>
               * region_tag:storage_delete_bucket
               * Another example:
               */
              delete: {
                  reqOpts: {
                      qs: requestQueryObject,
                  },
              },
              /**
               * @typedef {object} BucketExistsOptions Configuration options for Bucket#exists().
               * @param {string} [userProject] The ID of the project which will be
               *     billed for the request.
               */
              /**
               * @typedef {array} BucketExistsResponse
               * @property {boolean} 0 Whether the {@link Bucket} exists.
               */
              /**
               * @callback BucketExistsCallback
               * @param {?Error} err Request error, if any.
               * @param {boolean} exists Whether the {@link Bucket} exists.
               */
              /**
               * Check if the bucket exists.
               *
               * @method Bucket#exists
               * @param {BucketExistsOptions} [options] Configuration options.
               * @param {BucketExistsCallback} [callback] Callback function.
               * @returns {Promise<BucketExistsResponse>}
               *
               * @example
               * const {Storage} = require('@google-cloud/storage');
               * const storage = new Storage();
               * const bucket = storage.bucket('albums');
               *
               * bucket.exists(function(err, exists) {});
               *
               * //-
               * // If the callback is omitted, we'll return a Promise.
               * //-
               * bucket.exists().then(function(data) {
               *   const exists = data[0];
               * });
               */
              exists: {
                  reqOpts: {
                      qs: requestQueryObject,
                  },
              },
              /**
               * @typedef {object} [GetBucketOptions] Configuration options for Bucket#get()
               * @property {boolean} [autoCreate] Automatically create the object if
               *     it does not exist. Default: `false`
               * @property {string} [userProject] The ID of the project which will be
               *     billed for the request.
               */
              /**
               * @typedef {array} GetBucketResponse
               * @property {Bucket} 0 The {@link Bucket}.
               * @property {object} 1 The full API response.
               */
              /**
               * @callback GetBucketCallback
               * @param {?Error} err Request error, if any.
               * @param {Bucket} bucket The {@link Bucket}.
               * @param {object} apiResponse The full API response.
               */
              /**
               * Get a bucket if it exists.
               *
               * You may optionally use this to "get or create" an object by providing
               * an object with `autoCreate` set to `true`. Any extra configuration that
               * is normally required for the `create` method must be contained within
               * this object as well.
               *
               * @method Bucket#get
               * @param {GetBucketOptions} [options] Configuration options.
               * @param {GetBucketCallback} [callback] Callback function.
               * @returns {Promise<GetBucketResponse>}
               *
               * @example
               * const {Storage} = require('@google-cloud/storage');
               * const storage = new Storage();
               * const bucket = storage.bucket('albums');
               *
               * bucket.get(function(err, bucket, apiResponse) {
               *   // `bucket.metadata` has been populated.
               * });
               *
               * //-
               * // If the callback is omitted, we'll return a Promise.
               * //-
               * bucket.get().then(function(data) {
               *   const bucket = data[0];
               *   const apiResponse = data[1];
               * });
               */
              get: {
                  reqOpts: {
                      qs: requestQueryObject,
                  },
              },
              /**
               * @typedef {array} GetBucketMetadataResponse
               * @property {object} 0 The bucket metadata.
               * @property {object} 1 The full API response.
               */
              /**
               * @callback GetBucketMetadataCallback
               * @param {?Error} err Request error, if any.
               * @param {object} metadata The bucket metadata.
               * @param {object} apiResponse The full API response.
               */
              /**
               * @typedef {object} GetBucketMetadataOptions Configuration options for Bucket#getMetadata().
               * @property {string} [userProject] The ID of the project which will be
               *     billed for the request.
               */
              /**
               * Get the bucket's metadata.
               *
               * To set metadata, see {@link Bucket#setMetadata}.
               *
               * @see [Buckets: get API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/get}
               *
               * @method Bucket#getMetadata
               * @param {GetBucketMetadataOptions} [options] Configuration options.
               * @param {GetBucketMetadataCallback} [callback] Callback function.
               * @returns {Promise<GetBucketMetadataResponse>}
               *
               * @example
               * const {Storage} = require('@google-cloud/storage');
               * const storage = new Storage();
               * const bucket = storage.bucket('albums');
               *
               * bucket.getMetadata(function(err, metadata, apiResponse) {});
               *
               * //-
               * // If the callback is omitted, we'll return a Promise.
               * //-
               * bucket.getMetadata().then(function(data) {
               *   const metadata = data[0];
               *   const apiResponse = data[1];
               * });
               *
               * @example <caption>include:samples/requesterPays.js</caption>
               * region_tag:storage_get_requester_pays_status
               * Example of retrieving the requester pays status of a bucket:
               */
              getMetadata: {
                  reqOpts: {
                      qs: requestQueryObject,
                  },
              },
              /**
               * @typedef {object} SetBucketMetadataOptions Configuration options for Bucket#setMetadata().
               * @property {string} [userProject] The ID of the project which will be
               *     billed for the request.
               */
              /**
               * @typedef {array} SetBucketMetadataResponse
               * @property {object} apiResponse The full API response.
               */
              /**
               * @callback SetBucketMetadataCallback
               * @param {?Error} err Request error, if any.
               * @param {object} metadata The bucket metadata.
               */
              /**
               * Set the bucket's metadata.
               *
               * @see [Buckets: patch API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/patch}
               *
               * @method Bucket#setMetadata
               * @param {object<string, *>} metadata The metadata you wish to set.
               * @param {SetBucketMetadataOptions} [options] Configuration options.
               * @param {SetBucketMetadataCallback} [callback] Callback function.
               * @returns {Promise<SetBucketMetadataResponse>}
               *
               * @example
               * const {Storage} = require('@google-cloud/storage');
               * const storage = new Storage();
               * const bucket = storage.bucket('albums');
               *
               * //-
               * // Set website metadata field on the bucket.
               * //-
               * const metadata = {
               *   website: {
               *     mainPageSuffix: 'http://example.com',
               *     notFoundPage: 'http://example.com/404.html'
               *   }
               * };
               *
               * bucket.setMetadata(metadata, function(err, apiResponse) {});
               *
               * //-
               * // Enable versioning for your bucket.
               * //-
               * bucket.setMetadata({
               *   versioning: {
               *     enabled: true
               *   }
               * }, function(err, apiResponse) {});
               *
               * //-
               * // Enable KMS encryption for objects within this bucket.
               * //-
               * bucket.setMetadata({
               *   encryption: {
               *     defaultKmsKeyName: 'projects/grape-spaceship-123/...'
               *   }
               * }, function(err, apiResponse) {});
               *
               * //-
               * // Set the default event-based hold value for new objects in this
               * // bucket.
               * //-
               * bucket.setMetadata({
               *   defaultEventBasedHold: true
               * }, function(err, apiResponse) {});
               *
               * //-
               * // Remove object lifecycle rules.
               * //-
               * bucket.setMetadata({
               *   lifecycle: null
               * }, function(err, apiResponse) {});
               *
               * //-
               * // If the callback is omitted, we'll return a Promise.
               * //-
               * bucket.setMetadata(metadata).then(function(data) {
               *   const apiResponse = data[0];
               * });
               */
              setMetadata: {
                  reqOpts: {
                      qs: requestQueryObject,
                  },
              },
          };
          super({
              parent: storage,
              baseUrl: '/b',
              id: name,
              createMethod: storage.createBucket.bind(storage),
              methods
          });
          this.name = name;
          this.storage = storage;
          this.userProject = options.userProject;
          this.acl = new acl_1.Acl({
              request: this.request.bind(this),
              pathPrefix: '/acl',
          });
          this.acl.default = new acl_1.Acl({
              request: this.request.bind(this),
              pathPrefix: '/defaultObjectAcl',
          });
          this.iam = new iam_1.Iam(this);
          this.getFilesStream = paginator_1.paginator.streamify('getFiles');
      }
      /**
       * @typedef {object} AddLifecycleRuleOptions Configuration options for Bucket#addLifecycleRule().
       * @property {string} [append=true] The new rules will be appended to any
       *     pre-existing rules.
       */
      /**
       * Add an object lifecycle management rule to the bucket.
       *
       * By default, an Object Lifecycle Management rule provided to this method
       * will be included to the existing policy. To replace all existing rules,
       * supply the `options` argument, setting `append` to `false`.
       *
       * @see [Object Lifecycle Management]{@link https://cloud.google.com/storage/docs/lifecycle}
       * @see [Buckets: patch API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/patch}
       *
       * @param {LifecycleRule} rule The new lifecycle rule to be added to objects
       *     in this bucket.
       * @param {string} [rule.storageClass] When using the `setStorageClass`
       *     action, provide this option to dictate which storage class the object
       *     should update to.
       * @param {AddLifecycleRuleOptions} [options] Configuration object.
       * @param {boolean} [options.append=true] Append the new rule to the existing
       *     policy.
       * @param {SetBucketMetadataCallback} [callback] Callback function.
       * @returns {Promise<SetBucketMetadataResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * //-
       * // Automatically have an object deleted from this bucket once it is 3 years
       * // of age.
       * //-
       * bucket.addLifecycleRule({
       *   action: 'delete',
       *   condition: {
       *     age: 365 * 3 // Specified in days.
       *   }
       * }, function(err, apiResponse) {
       *   if (err) {
       *     // Error handling omitted.
       *   }
       *
       *   const lifecycleRules = bucket.metadata.lifecycle.rule;
       *
       *   // Iterate over the Object Lifecycle Management rules on this bucket.
       *   lifecycleRules.forEach(lifecycleRule => {});
       * });
       *
       * //-
       * // By default, the rule you provide will be added to the existing policy.
       * // Optionally, you can disable this behavior to replace all of the
       * // pre-existing rules.
       * //-
       * const options = {
       *   append: false
       * };
       *
       * bucket.addLifecycleRule({
       *   action: 'delete',
       *   condition: {
       *     age: 365 * 3 // Specified in days.
       *   }
       * }, options, function(err, apiResponse) {
       *   if (err) {
       *     // Error handling omitted.
       *   }
       *
       *   // All rules have been replaced with the new "delete" rule.
       *
       *   // Iterate over the Object Lifecycle Management rules on this bucket.
       *   lifecycleRules.forEach(lifecycleRule => {});
       * });
       *
       * //-
       * // For objects created before 2018, "downgrade" the storage class.
       * //-
       * bucket.addLifecycleRule({
       *   action: 'setStorageClass',
       *   storageClass: 'COLDLINE',
       *   condition: {
       *     createdBefore: new Date('2018')
       *   }
       * }, function(err, apiResponse) {});
       *
       * //-
       * // Delete objects created before 2016 which have the Coldline storage
       * // class.
       * //-
       * bucket.addLifecycleRule({
       *   action: 'delete',
       *   condition: {
       *     matchesStorageClass: [
       *       'COLDLINE'
       *     ],
       *     createdBefore: new Date('2016')
       *   }
       * }, function(err, apiResponse) {});
       */
      addLifecycleRule(rule, optionsOrCallback, callback) {
          let options;
          if (typeof optionsOrCallback === 'function') {
              callback = optionsOrCallback;
          }
          else if (optionsOrCallback) {
              options = optionsOrCallback;
          }
          options = options || {};
          callback = callback || common_1.util.noop;
          const newLifecycleRules = arrify(rule).map(rule => {
              if (typeof rule.action === 'object') {
                  // This is a raw-formatted rule object, the way the API expects.
                  // Just pass it through as-is.
                  return rule;
              }
              const apiFormattedRule = {};
              apiFormattedRule.condition = {};
              apiFormattedRule.action = {
                  type: rule.action,
              };
              // @TODO: Remove if the API becomes less picky.
              if (rule.action === 'delete') {
                  apiFormattedRule.action.type = 'Delete';
              }
              if (rule.storageClass) {
                  apiFormattedRule.action.storageClass = rule.storageClass;
              }
              for (const condition in rule.condition) {
                  if (rule.condition[condition] instanceof Date) {
                      apiFormattedRule.condition[condition] =
                          rule.condition[condition]
                              .toISOString()
                              .replace(/T.+$/, '');
                  }
                  else {
                      apiFormattedRule.condition[condition] = rule.condition[condition];
                  }
              }
              return apiFormattedRule;
          });
          if (options.append === false) {
              this.setMetadata({ lifecycle: { rule: newLifecycleRules } }, callback);
              return;
          }
          // The default behavior appends the previously-defined lifecycle rules with
          // the new ones just passed in by the user.
          this.getMetadata((err, metadata) => {
              if (err) {
                  callback(err);
                  return;
              }
              const currentLifecycleRules = arrify(metadata.lifecycle && metadata.lifecycle.rule);
              this.setMetadata({
                  lifecycle: {
                      rule: currentLifecycleRules.concat(newLifecycleRules),
                  },
              }, callback);
          });
      }
      /**
       * @typedef {object} CombineOptions
       * @property {string} [kmsKeyName] Resource name of the Cloud KMS key, of
       *     the form
       *     `projects/my-project/locations/location/keyRings/my-kr/cryptoKeys/my-key`,
       *     that will be used to encrypt the object. Overwrites the object
       * metadata's `kms_key_name` value, if any.
       * @property {string} [userProject] The ID of the project which will be
       *     billed for the request.
       */
      /**
       * @callback CombineCallback
       * @param {?Error} err Request error, if any.
       * @param {File} newFile The new {@link File}.
       * @param {object} apiResponse The full API response.
       */
      /**
       * @typedef {array} CombineResponse
       * @property {File} 0 The new {@link File}.
       * @property {object} 1 The full API response.
       */
      /**
       * Combine multiple files into one new file.
       *
       * @see [Objects: compose API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/compose}
       *
       * @throws {Error} if a non-array is provided as sources argument.
       * @throws {Error} if less than two sources are provided.
       * @throws {Error} if no destination is provided.
       *
       * @param {string[]|File[]} sources The source files that will be
       *     combined.
       * @param {string|File} destination The file you would like the
       *     source files combined into.
       * @param {CombineOptions} [options] Configuration options.
       * @param {CombineCallback} [callback] Callback function.
       * @returns {Promise<CombineResponse>}
       *
       * @example
       * const logBucket = storage.bucket('log-bucket');
       *
       * const sources = [
       *   logBucket.file('2013-logs.txt'),
       *   logBucket.file('2014-logs.txt')
       * ];
       *
       * const allLogs = logBucket.file('all-logs.txt');
       *
       * logBucket.combine(sources, allLogs, function(err, newFile, apiResponse) {
       *   // newFile === allLogs
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * logBucket.combine(sources, allLogs).then(function(data) {
       *   const newFile = data[0];
       *   const apiResponse = data[1];
       * });
       */
      combine(sources, destination, optionsOrCallback, callback) {
          if (!Array.isArray(sources) || sources.length < 2) {
              throw new Error('You must provide at least two source files.');
          }
          if (!destination) {
              throw new Error('A destination file must be specified.');
          }
          let options = {};
          if (typeof optionsOrCallback === 'function') {
              callback = optionsOrCallback;
          }
          else if (optionsOrCallback) {
              options = optionsOrCallback;
          }
          const convertToFile = (file) => {
              if (file instanceof file_1.File) {
                  return file;
              }
              return this.file(file);
          };
          // tslint:disable-next-line:no-any
          sources = sources.map(convertToFile);
          const destinationFile = convertToFile(destination);
          callback = callback || common_1.util.noop;
          if (!destinationFile.metadata.contentType) {
              const destinationContentType = mime.contentType(destinationFile.name);
              if (destinationContentType) {
                  destinationFile.metadata.contentType = destinationContentType;
              }
          }
          // Make the request from the destination File object.
          destinationFile.request({
              method: 'POST',
              uri: '/compose',
              json: {
                  destination: {
                      contentType: destinationFile.metadata.contentType,
                  },
                  sourceObjects: sources.map(source => {
                      const sourceObject = {
                          name: source.name,
                      };
                      if (source.metadata && source.metadata.generation) {
                          sourceObject.generation = source.metadata.generation;
                      }
                      return sourceObject;
                  }),
              },
              qs: options,
          }, (err, resp) => {
              if (err) {
                  callback(err, null, resp);
                  return;
              }
              callback(null, destinationFile, resp);
          });
      }
      /**
       * See a [Objects:
       * watchAll request
       * body](https://cloud.google.com/storage/docs/json_api/v1/objects/watchAll).
       *
       * @typedef {object} CreateChannelConfig
       * @property {string} address The address where notifications are
       *     delivered for this channel.
       * @extends WatchAllOptions
       */
      /**
       * @typedef {object} CreateChannelOptions
       * @property {string} [userProject] The ID of the project which will be
       *     billed for the request.
       */
      /**
       * @typedef {array} CreateChannelResponse
       * @property {Channel} 0 The new {@link Channel}.
       * @property {object} 1 The full API response.
       */
      /**
       * @callback CreateChannelCallback
       * @param {?Error} err Request error, if any.
       * @param {Channel} channel The new {@link Channel}.
       * @param {object} apiResponse The full API response.
       */
      /**
       * Create a channel that will be notified when objects in this bucket changes.
       *
       * @throws {Error} If an ID is not provided.
       * @throws {Error} If an address is not provided.
       *
       * @see [Objects: watchAll API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/watchAll}
       *
       * @param {string} id The ID of the channel to create.
       * @param {CreateChannelConfig} config Configuration for creating channel.
       * @param {CreateChannelOptions} [options] Configuration options.
       * @param {CreateChannelCallback} [callback] Callback function.
       * @returns {Promise<CreateChannelResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       * const id = 'new-channel-id';
       *
       * const config = {
       *   address: 'https://...'
       * };
       *
       * bucket.createChannel(id, config, function(err, channel, apiResponse) {
       *   if (!err) {
       *     // Channel created successfully.
       *   }
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.createChannel(id, config).then(function(data) {
       *   const channel = data[0];
       *   const apiResponse = data[1];
       * });
       */
      createChannel(id, config, optionsOrCallback, callback) {
          if (typeof id !== 'string') {
              throw new Error('An ID is required to create a channel.');
          }
          if (typeof config.address !== 'string') {
              throw new Error('An address is required to create a channel.');
          }
          let options = {};
          if (typeof optionsOrCallback === 'function') {
              callback = optionsOrCallback;
          }
          else if (optionsOrCallback) {
              options = optionsOrCallback;
          }
          this.request({
              method: 'POST',
              uri: '/o/watch',
              json: Object.assign({
                  id,
                  type: 'web_hook',
              }, config),
              qs: options,
          }, (err, apiResponse) => {
              if (err) {
                  callback(err, null, apiResponse);
                  return;
              }
              const resourceId = apiResponse.resourceId;
              const channel = this.storage.channel(id, resourceId);
              channel.metadata = apiResponse;
              callback(null, channel, apiResponse);
          });
      }
      /**
       * Metadata to set for the Notification.
       *
       * @typedef {object} CreateNotificationOptions
       * @property {object} [customAttributes] An optional list of additional
       *     attributes to attach to each Cloud PubSub message published for this
       *     notification subscription.
       * @property {string[]} [eventTypes] If present, only send notifications about
       *     listed event types. If empty, sent notifications for all event types.
       * @property {string} [objectNamePrefix] If present, only apply this
       *     notification configuration to object names that begin with this prefix.
       * @property {string} [payloadFormat] The desired content of the Payload.
       *     Defaults to `JSON_API_V1`.
       *
       *     Acceptable values are:
       *     - `JSON_API_V1`
       *
       *     - `NONE`
       * @property {string} [userProject] The ID of the project which will be
       *     billed for the request.
       */
      /**
       * @callback CreateNotificationCallback
       * @param {?Error} err Request error, if any.
       * @param {Notification} notification The new {@link Notification}.
       * @param {object} apiResponse The full API response.
       */
      /**
       * @typedef {array} CreateNotificationResponse
       * @property {Notification} 0 The new {@link Notification}.
       * @property {object} 1 The full API response.
       */
      /**
       * Creates a notification subscription for the bucket.
       *
       * @see [Notifications: insert]{@link https://cloud.google.com/storage/docs/json_api/v1/notifications/insert}
       *
       * @param {Topic|string} topic The Cloud PubSub topic to which this
       *     subscription publishes. If the project ID is omitted, the current
       * project ID will be used.
       *
       *     Acceptable formats are:
       *     - `projects/grape-spaceship-123/topics/my-topic`
       *
       *     - `my-topic`
       * @param {CreateNotificationOptions} [options] Metadata to set for the
       *     notification.
       * @param {CreateNotificationCallback} [callback] Callback function.
       * @returns {Promise<CreateNotificationResponse>}
       * @throws {Error} If a valid topic is not provided.
       * @see Notification#create
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const myBucket = storage.bucket('my-bucket');
       *
       * const callback = function(err, notification, apiResponse) {
       *   if (!err) {
       *     // The notification was created successfully.
       *   }
       * };
       *
       * myBucket.createNotification('my-topic', callback);
       *
       * //-
       * // Configure the nofiication by providing Notification metadata.
       * //-
       * const metadata = {
       *   objectNamePrefix: 'prefix-'
       * };
       *
       * myBucket.createNotification('my-topic', metadata, callback);
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * myBucket.createNotification('my-topic').then(function(data) {
       *   const notification = data[0];
       *   const apiResponse = data[1];
       * });
       *
       * @example <caption>include:samples/notifications.js</caption>
       * region_tag:storage_create_notification
       * Another example:
       */
      createNotification(topic, optionsOrCallback, callback) {
          let options = {};
          if (typeof optionsOrCallback === 'function') {
              callback = optionsOrCallback;
          }
          else if (optionsOrCallback) {
              options = optionsOrCallback;
          }
          const topicIsObject = topic !== null && typeof topic === 'object';
          if (topicIsObject && common_1.util.isCustomType(topic, 'pubsub/topic')) {
              // tslint:disable-next-line:no-any
              topic = topic.name;
          }
          if (typeof topic !== 'string') {
              throw new Error('A valid topic name is required.');
          }
          const body = Object.assign({ topic }, options);
          if (body.topic.indexOf('projects') !== 0) {
              body.topic = 'projects/{{projectId}}/topics/' + body.topic;
          }
          body.topic = '//pubsub.googleapis.com/' + body.topic;
          if (!body.payloadFormat) {
              body.payloadFormat = 'JSON_API_V1';
          }
          const query = {};
          if (body.userProject) {
              query.userProject = body.userProject;
              delete body.userProject;
          }
          this.request({
              method: 'POST',
              uri: '/notificationConfigs',
              json: snakeize(body),
              qs: query,
          }, (err, apiResponse) => {
              if (err) {
                  callback(err, null, apiResponse);
                  return;
              }
              const notification = this.notification(apiResponse.id);
              notification.metadata = apiResponse;
              callback(null, notification, apiResponse);
          });
      }
      /**
       * @typedef {object} DeleteFilesOptions Query object. See {@link Bucket#getFiles}
       *     for all of the supported properties.
       * @property {boolean} [force] Suppress errors until all files have been
       *     processed.
       */
      /**
       * @callback DeleteFilesCallback
       * @param {?Error|?Error[]} err Request error, if any, or array of errors from
       *     files that were not able to be deleted.
       * @param {object} [apiResponse] The full API response.
       */
      /**
       * Iterate over the bucket's files, calling `file.delete()` on each.
       *
       * <strong>This is not an atomic request.</strong> A delete attempt will be
       * made for each file individually. Any one can fail, in which case only a
       * portion of the files you intended to be deleted would have.
       *
       * Operations are performed in parallel, up to 10 at once. The first error
       * breaks the loop and will execute the provided callback with it. Specify
       * `{ force: true }` to suppress the errors until all files have had a chance
       * to be processed.
       *
       * The `query` object passed as the first argument will also be passed to
       * {@link Bucket#getFiles}.
       *
       * @see [Objects: delete API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/delete}
       *
       * @param {DeleteFilesOptions} [query] Query object. See {@link Bucket#getFiles}
       * @param {DeleteFilesCallback} [callback] Callback function.
       * @returns {Promise}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * //-
       * // Delete all of the files in the bucket.
       * //-
       * bucket.deleteFiles(function(err) {});
       *
       * //-
       * // By default, if a file cannot be deleted, this method will stop deleting
       * // files from your bucket. You can override this setting with `force:
       * // true`.
       * //-
       * bucket.deleteFiles({
       *   force: true
       * }, function(errors) {
       *   // `errors`:
       *   //    Array of errors if any occurred, otherwise null.
       * });
       *
       * //-
       * // The first argument to this method acts as a query to
       * // {@link Bucket#getFiles}. As an example, you can delete files
       * // which match a prefix.
       * //-
       * bucket.deleteFiles({
       *   prefix: 'images/'
       * }, function(err) {
       *   if (!err) {
       *     // All files in the `images` directory have been deleted.
       *   }
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.deleteFiles().then(function() {});
       */
      deleteFiles(queryOrCallback, callback) {
          let query = {};
          if (typeof queryOrCallback === 'function') {
              callback = queryOrCallback;
          }
          else if (queryOrCallback) {
              query = queryOrCallback;
          }
          const MAX_PARALLEL_LIMIT = 10;
          const errors = [];
          this.getFiles(query, (err, files) => {
              if (err) {
                  callback(err, {});
                  return;
              }
              const deleteFile = (file, callback) => {
                  file.delete(query, err => {
                      if (err) {
                          if (query.force) {
                              errors.push(err);
                              callback();
                              return;
                          }
                          callback(err);
                          return;
                      }
                      callback(null);
                  });
              };
              // Iterate through each file and attempt to delete it.
              async.eachLimit(files, MAX_PARALLEL_LIMIT, deleteFile, err => {
                  if (err || errors.length > 0) {
                      callback(err || errors);
                      return;
                  }
                  callback(null);
              });
          });
      }
      /**
       * @typedef {array} DeleteLabelsResponse
       * @property {object} 0 The full API response.
       */
      /**
       * @callback DeleteLabelsCallback
       * @param {?Error} err Request error, if any.
       * @param {object} metadata Bucket's metadata.
       */
      /**
       * Delete one or more labels from this bucket.
       *
       * @param {string|string[]} labels The labels to delete. If no labels are
       *     provided, all of the labels are removed.
       * @param {DeleteLabelsCallback} [callback] Callback function.
       * @returns {Promise<DeleteLabelsResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * //-
       * // Delete all of the labels from this bucket.
       * //-
       * bucket.deleteLabels(function(err, apiResponse) {});
       *
       * //-
       * // Delete a single label.
       * //-
       * bucket.deleteLabels('labelone', function(err, apiResponse) {});
       *
       * //-
       * // Delete a specific set of labels.
       * //-
       * bucket.deleteLabels([
       *   'labelone',
       *   'labeltwo'
       * ], function(err, apiResponse) {});
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.deleteLabels().then(function(data) {
       *   const apiResponse = data[0];
       * });
       */
      deleteLabels(labelsOrCallback, callback) {
          let labels = new Array();
          if (typeof labelsOrCallback === 'function') {
              callback = labelsOrCallback;
          }
          else if (labelsOrCallback) {
              labels = arrify(labelsOrCallback);
          }
          const deleteLabels = (labels) => {
              const nullLabelMap = labels.reduce((nullLabelMap, labelKey) => {
                  nullLabelMap[labelKey] = null;
                  return nullLabelMap;
              }, {});
              this.setLabels(nullLabelMap, callback);
          };
          if (labels.length === 0) {
              this.getLabels((err, labels) => {
                  if (err) {
                      callback(err);
                      return;
                  }
                  deleteLabels(Object.keys(labels));
              });
          }
          else {
              deleteLabels(labels);
          }
      }
      /**
       * @typedef {array} DisableRequesterPaysResponse
       * @property {object} 0 The full API response.
       */
      /**
       * @callback DisableRequesterPaysCallback
       * @param {?Error} err Request error, if any.
       * @param {object} apiResponse The full API response.
       */
      /**
       * <div class="notice">
       *   <strong>Early Access Testers Only</strong>
       *   <p>
       *     This feature is not yet widely-available.
       *   </p>
       * </div>
       *
       * Disable `requesterPays` functionality from this bucket.
       *
       * @param {DisableRequesterPaysCallback} [callback] Callback function.
       * @returns {Promise<DisableRequesterPaysCallback>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * bucket.disableRequesterPays(function(err, apiResponse) {
       *   if (!err) {
       *     // requesterPays functionality disabled successfully.
       *   }
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.disableRequesterPays().then(function(data) {
       *   const apiResponse = data[0];
       * });
       *
       * @example <caption>include:samples/requesterPays.js</caption>
       * region_tag:storage_disable_requester_pays
       * Example of disabling requester pays:
       */
      disableRequesterPays(callback) {
          this.setMetadata({
              billing: {
                  requesterPays: false,
              },
          }, callback || common_1.util.noop);
      }
      /**
       * @typedef {array} EnableRequesterPaysResponse
       * @property {object} 0 The full API response.
       */
      /**
       * @callback EnableRequesterPaysCallback
       * @param {?Error} err Request error, if any.
       * @param {object} apiResponse The full API response.
       */
      /**
       * <div class="notice">
       *   <strong>Early Access Testers Only</strong>
       *   <p>
       *     This feature is not yet widely-available.
       *   </p>
       * </div>
       *
       * Enable `requesterPays` functionality for this bucket. This enables you, the
       * bucket owner, to have the requesting user assume the charges for the access
       * to your bucket and its contents.
       *
       * @param {EnableRequesterPaysCallback} [callback] Callback function.
       * @returns {Promise<EnableRequesterPaysResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * bucket.enableRequesterPays(function(err, apiResponse) {
       *   if (!err) {
       *     // requesterPays functionality enabled successfully.
       *   }
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.enableRequesterPays().then(function(data) {
       *   const apiResponse = data[0];
       * });
       *
       * @example <caption>include:samples/requesterPays.js</caption>
       * region_tag:storage_enable_requester_pays
       * Example of enabling requester pays:
       */
      enableRequesterPays(callback) {
          this.setMetadata({
              billing: {
                  requesterPays: true,
              },
          }, callback || common_1.util.noop);
      }
      /**
       * Create a {@link File} object. See {@link File} to see how to handle
       * the different use cases you may have.
       *
       * @param {string} name The name of the file in this bucket.
       * @param {object} [options] Configuration options.
       * @param {string|number} [options.generation] Only use a specific revision of
       *     this file.
       * @param {string} [options.encryptionKey] A custom encryption key. See
       *     [Customer-supplied Encryption
       * Keys](https://cloud.google.com/storage/docs/encryption#customer-supplied).
       * @param {string} [options.kmsKeyName] The name of the Cloud KMS key that will
       *     be used to encrypt the object. Must be in the format:
       *     `projects/my-project/locations/location/keyRings/my-kr/cryptoKeys/my-key`.
       *     KMS key ring must use the same location as the bucket.
       * @returns {File}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       * const file = bucket.file('my-existing-file.png');
       */
      file(name, options) {
          if (!name) {
              throw Error('A file name must be specified.');
          }
          return new file_1.File(this, name, options);
      }
      /**
       * @typedef {array} GetFilesResponse
       * @property {File[]} 0 Array of {@link File} instances.
       */
      /**
       * @callback GetFilesCallback
       * @param {?Error} err Request error, if any.
       * @param {File[]} files Array of {@link File} instances.
       */
      /**
       * Query object for listing files.
       *
       * @typedef {object} GetFilesOptions
       * @property {boolean} [autoPaginate=true] Have pagination handled
       *     automatically.
       * @property {string} [delimiter] Results will contain only objects whose
       *     names, aside from the prefix, do not contain delimiter. Objects whose
       *     names, aside from the prefix, contain delimiter will have their name
       *     truncated after the delimiter, returned in `apiResponse.prefixes`.
       *     Duplicate prefixes are omitted.
       * @property {string} [directory] Filter results based on a directory name, or
       *     more technically, a "prefix".
       * @property {string} [prefix] Filter results to objects whose names begin
       *     with this prefix.
       * @property {number} [maxApiCalls] Maximum number of API calls to make.
       * @property {number} [maxResults] Maximum number of items plus prefixes to
       *     return.
       * @property {string} [pageToken] A previously-returned page token
       *     representing part of the larger set of results to view.
       * @property {string} [userProject] The ID of the project which will be
       *     billed for the request.
       * @property {boolean} [versions] If true, returns File objects scoped to
       *     their versions.
       */
      /**
       * Get {@link File} objects for the files currently in the bucket.
       *
       * @see [Objects: list API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/list}
       *
       * @param {GetFilesOptions} [query] Query object for listing files.
       * @param {GetFilesCallback} [callback] Callback function.
       * @returns {Promise<GetFilesResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * bucket.getFiles(function(err, files) {
       *   if (!err) {
       *     // files is an array of File objects.
       *   }
       * });
       *
       * //-
       * // If your bucket has versioning enabled, you can get all of your files
       * // scoped to their generation.
       * //-
       * bucket.getFiles({
       *   versions: true
       * }, function(err, files) {
       *   // Each file is scoped to its generation.
       * });
       *
       * //-
       * // To control how many API requests are made and page through the results
       * // manually, set `autoPaginate` to `false`.
       * //-
       * const callback = function(err, files, nextQuery, apiResponse) {
       *   if (nextQuery) {
       *     // More results exist.
       *     bucket.getFiles(nextQuery, callback);
       *   }
       *
       *   // The `metadata` property is populated for you with the metadata at the
       *   // time of fetching.
       *   files[0].metadata;
       *
       *   // However, in cases where you are concerned the metadata could have
       *   // changed, use the `getMetadata` method.
       *   files[0].getMetadata(function(err, metadata) {});
       * };
       *
       * bucket.getFiles({
       *   autoPaginate: false
       * }, callback);
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.getFiles().then(function(data) {
       *   const files = data[0];
       * });
       *
       * @example <caption>include:samples/files.js</caption>
       * region_tag:storage_list_files
       * Another example:
       *
       * @example <caption>include:samples/files.js</caption>
       * region_tag:storage_list_files_with_prefix
       * Example of listing files, filtered by a prefix:
       */
      getFiles(queryOrCallback, callback) {
          let query = typeof queryOrCallback === 'object' ? queryOrCallback : {};
          if (!callback) {
              callback = queryOrCallback;
          }
          query = Object.assign({}, query);
          if (query.directory) {
              query.prefix = `${query.directory}/`.replace(/\/*$/, '/');
              delete query.directory;
          }
          this.request({
              uri: '/o',
              qs: query,
          }, (err, resp) => {
              if (err) {
                  // tslint:disable-next-line:no-any
                  callback(err, null, null, resp);
                  return;
              }
              const files = arrify(resp.items).map(file => {
                  const options = {};
                  if (query.versions) {
                      options.generation = file.generation;
                  }
                  if (file.kmsKeyName) {
                      options.kmsKeyName = file.kmsKeyName;
                  }
                  const fileInstance = this.file(file.name, options);
                  fileInstance.metadata = file;
                  return fileInstance;
              });
              let nextQuery = null;
              if (resp.nextPageToken) {
                  nextQuery = Object.assign({}, query, {
                      pageToken: resp.nextPageToken,
                  });
              }
              // tslint:disable-next-line:no-any
              callback(null, files, nextQuery, resp);
          });
      }
      /**
       * @typedef {object} GetLabelsOptions Configuration options for Bucket#getLabels().
       * @param {string} [userProject] The ID of the project which will be
       *     billed for the request.
       */
      /**
       * @typedef {array} GetLabelsResponse
       * @property {object} 0 Object of labels currently set on this bucket.
       */
      /**
       * @callback GetLabelsCallback
       * @param {?Error} err Request error, if any.
       * @param {object} labels Object of labels currently set on this bucket.
       */
      /**
       * Get the labels currently set on this bucket.
       *
       * @param {object} [options] Configuration options.
       * @param {string} [options.userProject] The ID of the project which will be
       *     billed for the request.
       * @param {GetLabelsCallback} [callback] Callback function.
       * @returns {Promise<GetLabelsCallback>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * bucket.getLabels(function(err, labels) {
       *   if (err) {
       *     // Error handling omitted.
       *   }
       *
       *   // labels = {
       *   //   label: 'labelValue',
       *   //   ...
       *   // }
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.getLabels().then(function(data) {
       *   const labels = data[0];
       * });
       */
      getLabels(optionsOrCallback, callback) {
          let options = {};
          if (typeof optionsOrCallback === 'function') {
              callback = optionsOrCallback;
          }
          else if (optionsOrCallback) {
              options = optionsOrCallback;
          }
          this.getMetadata(options, (err, metadata) => {
              if (err) {
                  callback(err, null);
                  return;
              }
              callback(null, metadata.labels || {});
          });
      }
      /**
       * @typedef {object} GetNotificationOptions Configuration options for Bucket#getNotification().
       * @property {string} [userProject] The ID of the project which will be
       *     billed for the request.
       */
      /**
       * @callback GetNotificationsCallback
       * @param {?Error} err Request error, if any.
       * @param {Notification[]} notifications Array of {@link Notification}
       *     instances.
       * @param {object} apiResponse The full API response.
       */
      /**
       * @typedef {array} GetNotificationsResponse
       * @property {Notification[]} 0 Array of {@link Notification} instances.
       * @property {object} 1 The full API response.
       */
      /**
       * Retrieves a list of notification subscriptions for a given bucket.
       *
       * @see [Notifications: list]{@link https://cloud.google.com/storage/docs/json_api/v1/notifications/list}
       *
       * @param {GetNotificationsOptions} [options] Configuration options.
       * @param {GetNotificationsCallback} [callback] Callback function.
       * @returns {Promise<GetNotificationsResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('my-bucket');
       *
       * bucket.getNotifications(function(err, notifications, apiResponse) {
       *   if (!err) {
       *     // notifications is an array of Notification objects.
       *   }
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.getNotifications().then(function(data) {
       *   const notifications = data[0];
       *   const apiResponse = data[1];
       * });
       *
       * @example <caption>include:samples/notifications.js</caption>
       * region_tag:storage_list_notifications
       * Another example:
       */
      getNotifications(optionsOrCallback, callback) {
          let options = {};
          if (typeof optionsOrCallback === 'function') {
              callback = optionsOrCallback;
          }
          else if (optionsOrCallback) {
              options = optionsOrCallback;
          }
          this.request({
              uri: '/notificationConfigs',
              qs: options,
          }, (err, resp) => {
              if (err) {
                  callback(err, null, resp);
                  return;
              }
              const notifications = arrify(resp.items).map(notification => {
                  const notificationInstance = this.notification(notification.id);
                  notificationInstance.metadata = notification;
                  return notificationInstance;
              });
              callback(null, notifications, resp);
          });
      }
      /**
       * @callback BucketLockCallback
       * @param {?Error} err Request error, if any.
       * @param {object} apiResponse The full API response.
       */
      /**
       * Lock a previously-defined retention policy. This will prevent changes to
       * the policy.
       *
       * @throws {Error} if a metageneration is not provided.
       *
       * @param {Number|String} metageneration The bucket's metageneration. This is
       *     accesssible from calling {@link File#getMetadata}.
       * @param {BucketLockCallback} [callback] Callback function.
       * @returns {Promise<BucketLockResponse>}
       *
       * @example
       * const storage = require('@google-cloud/storage')();
       * const bucket = storage.bucket('albums');
       *
       * const metageneration = 2;
       *
       * bucket.lock(metageneration, function(err, apiResponse) {});
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.lock(metageneration).then(function(data) {
       *   const apiResponse = data[0];
       * });
       */
      lock(metageneration, callback) {
          const metatype = typeof metageneration;
          if (metatype !== 'number' && metatype !== 'string') {
              throw new Error('A metageneration must be provided.');
          }
          this.request({
              method: 'POST',
              uri: '/lockRetentionPolicy',
              qs: {
                  ifMetagenerationMatch: metageneration,
              },
          }, callback);
      }
      /**
       * @typedef {array} MakeBucketPrivateResponse
       * @property {File[]} 0 List of files made private.
       */
      /**
       * @callback MakeBucketPrivateCallback
       * @param {?Error} err Request error, if any.
       * @param {File[]} files List of files made private.
       */
      /**
       * @typedef {object} MakeBucketPrivateOptions
       * @param {boolean} [includeFiles=false] Make each file in the bucket
       *     private.
       * @param {boolean} [force] Queue errors occurred while making files
       *     private until all files have been processed.
       * @param {string} [userProject] The ID of the project which will be
       *     billed for the request.
       */
      /**
       * Make the bucket listing private.
       *
       * You may also choose to make the contents of the bucket private by
       * specifying `includeFiles: true`. This will automatically run
       * {@link File#makePrivate} for every file in the bucket.
       *
       * When specifying `includeFiles: true`, use `force: true` to delay execution
       * of your callback until all files have been processed. By default, the
       * callback is executed after the first error. Use `force` to queue such
       * errors until all files have been processed, after which they will be
       * returned as an array as the first argument to your callback.
       *
       * NOTE: This may cause the process to be long-running and use a high number
       * of requests. Use with caution.
       *
       * @see [Buckets: patch API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/patch}
       *
       * @param {MakeBucketPrivateOptions} [options] Configuration options.
       * @param {MakeBucketPrivateCallback} [callback] Callback function.
       * @returns {Promise<MakeBucketPrivateResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * //-
       * // Make the bucket private.
       * //-
       * bucket.makePrivate(function(err) {});
       *
       * //-
       * // Make the bucket and its contents private.
       * //-
       * const opts = {
       *   includeFiles: true
       * };
       *
       * bucket.makePrivate(opts, function(err, files) {
       *   // `err`:
       *   //    The first error to occur, otherwise null.
       *   //
       *   // `files`:
       *   //    Array of files successfully made private in the bucket.
       * });
       *
       * //-
       * // Make the bucket and its contents private, using force to suppress errors
       * // until all files have been processed.
       * //-
       * const opts = {
       *   includeFiles: true,
       *   force: true
       * };
       *
       * bucket.makePrivate(opts, function(errors, files) {
       *   // `errors`:
       *   //    Array of errors if any occurred, otherwise null.
       *   //
       *   // `files`:
       *   //    Array of files successfully made private in the bucket.
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.makePrivate(opts).then(function(data) {
       *   const files = data[0];
       * });
       */
      makePrivate(optionsOrCallback, callback) {
          const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
          callback =
              typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
          options.private = true;
          const setPredefinedAcl = (done) => {
              const query = {
                  predefinedAcl: 'projectPrivate',
              };
              if (options.userProject) {
                  query.userProject = options.userProject;
              }
              this.setMetadata({
                  // You aren't allowed to set both predefinedAcl & acl properties on
                  // a bucket so acl must explicitly be nullified.
                  acl: null,
              }, query, done);
          };
          const makeFilesPrivate = (done) => {
              if (!options.includeFiles) {
                  done();
                  return;
              }
              this.makeAllFilesPublicPrivate_(options, done);
          };
          // tslint:disable-next-line no-any
          async.series([setPredefinedAcl, makeFilesPrivate], callback);
      }
      /**
       * @typedef {object} MakeBucketPublicOptions
       * @param {boolean} [includeFiles=false] Make each file in the bucket
       *     private.
       * @param {boolean} [force] Queue errors occurred while making files
       *     private until all files have been processed.
       */
      /**
       * @callback MakeBucketPublicCallback
       * @param {?Error} err Request error, if any.
       * @param {File[]} files List of files made public.
       */
      /**
       * @typedef {array} MakeBucketPublicResponse
       * @property {File[]} 0 List of files made public.
       */
      /**
       * Make the bucket publicly readable.
       *
       * You may also choose to make the contents of the bucket publicly readable by
       * specifying `includeFiles: true`. This will automatically run
       * {@link File#makePublic} for every file in the bucket.
       *
       * When specifying `includeFiles: true`, use `force: true` to delay execution
       * of your callback until all files have been processed. By default, the
       * callback is executed after the first error. Use `force` to queue such
       * errors until all files have been processed, after which they will be
       * returned as an array as the first argument to your callback.
       *
       * NOTE: This may cause the process to be long-running and use a high number
       * of requests. Use with caution.
       *
       * @see [Buckets: patch API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/buckets/patch}
       *
       * @param {MakeBucketPublicOptions} [options] Configuration options.
       * @param {MakeBucketPublicCallback} [callback] Callback function.
       * @returns {Promise<MakeBucketPublicResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * //-
       * // Make the bucket publicly readable.
       * //-
       * bucket.makePublic(function(err) {});
       *
       * //-
       * // Make the bucket and its contents publicly readable.
       * //-
       * const opts = {
       *   includeFiles: true
       * };
       *
       * bucket.makePublic(opts, function(err, files) {
       *   // `err`:
       *   //    The first error to occur, otherwise null.
       *   //
       *   // `files`:
       *   //    Array of files successfully made public in the bucket.
       * });
       *
       * //-
       * // Make the bucket and its contents publicly readable, using force to
       * // suppress errors until all files have been processed.
       * //-
       * const opts = {
       *   includeFiles: true,
       *   force: true
       * };
       *
       * bucket.makePublic(opts, function(errors, files) {
       *   // `errors`:
       *   //    Array of errors if any occurred, otherwise null.
       *   //
       *   // `files`:
       *   //    Array of files successfully made public in the bucket.
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.makePublic(opts).then(function(data) {
       *   const files = data[0];
       * });
       */
      makePublic(optionsOrCallback, callback) {
          const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
          callback =
              typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
          const req = extend(true, { public: true }, options);
          const addAclPermissions = (done) => {
              // Allow reading bucket contents while preserving original permissions.
              this.acl.add({
                  entity: 'allUsers',
                  role: 'READER',
              }, done);
          };
          const addDefaultAclPermissions = (done) => {
              this.acl.default.add({
                  entity: 'allUsers',
                  role: 'READER',
              }, done);
          };
          const makeFilesPublic = (done) => {
              if (!req.includeFiles) {
                  done();
                  return;
              }
              this.makeAllFilesPublicPrivate_(req, done);
          };
          // tslint:disable-next-line:no-any
          async
              .series([addAclPermissions, addDefaultAclPermissions, makeFilesPublic], callback);
      }
      /**
       * Get a reference to a Cloud Pub/Sub Notification.
       *
       * @param {string} id ID of notification.
       * @returns {Notification}
       * @see Notification
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('my-bucket');
       * const notification = bucket.notification('1');
       */
      notification(id) {
          if (!id) {
              throw new Error('You must supply a notification ID.');
          }
          return new notification_1.Notification(this, id);
      }
      /**
       * Remove an already-existing retention policy from this bucket, if it is not
       * locked.
       *
       * @param {SetBucketMetadataCallback} [callback] Callback function.
       * @returns {Promise<SetBucketMetadataResponse>}
       *
       * @example
       * const storage = require('@google-cloud/storage')();
       * const bucket = storage.bucket('albums');
       *
       * bucket.removeRetentionPeriod(function(err, apiResponse) {});
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.removeRetentionPeriod().then(function(data) {
       *   const apiResponse = data[0];
       * });
       */
      removeRetentionPeriod(callback) {
          this.setMetadata({
              retentionPolicy: null,
          }, callback);
      }
      /**
       * Makes request and applies userProject query parameter if necessary.
       *
       * @private
       *
       * @param {object} reqOpts - The request options.
       * @param {function} callback - The callback function.
       */
      request(reqOpts, callback) {
          if (this.userProject && (!reqOpts.qs || !reqOpts.qs.userProject)) {
              reqOpts.qs = extend(reqOpts.qs, { userProject: this.userProject });
          }
          return super.request(reqOpts, callback);
      }
      /**
       * @typedef {array} SetLabelsResponse
       * @property {object} 0 The bucket metadata.
       */
      /**
       * @callback SetLabelsCallback
       * @param {?Error} err Request error, if any.
       * @param {object} metadata The bucket metadata.
       */
      /**
       * @typedef {object} SetLabelsOptions Configuration options for Bucket#setLabels().
       * @property {string} [userProject] The ID of the project which will be
       *     billed for the request.
       */
      /**
       * Set labels on the bucket.
       *
       * This makes an underlying call to {@link Bucket#setMetadata}, which
       * is a PATCH request. This means an individual label can be overwritten, but
       * unmentioned labels will not be touched.
       *
       * @param {object<string, string>} labels Labels to set on the bucket.
       * @param {object} [options] Configuration options.
       * @param {SetLabelsCallback} [callback] Callback function.
       * @returns {Promise<SetLabelsResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * const labels = {
       *   labelone: 'labelonevalue',
       *   labeltwo: 'labeltwovalue'
       * };
       *
       * bucket.setLabels(labels, function(err, metadata) {
       *   if (!err) {
       *     // Labels set successfully.
       *   }
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.setLabels(labels).then(function(data) {
       *   const metadata = data[0];
       * });
       */
      setLabels(labels, optionsOrCallback, callback) {
          const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
          callback =
              typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
          callback = callback || common_1.util.noop;
          this.setMetadata({ labels }, options, callback);
      }
      /**
       * Lock all objects contained in the bucket, based on their creation time. Any
       * attempt to overwrite or delete objects younger than the retention period
       * will result in a `PERMISSION_DENIED` error.
       *
       * An unlocked retention policy can be modified or removed from the bucket via
       * {@link File#removeRetentionPeriod} and {@link File#setRetentionPeriod}. A
       * locked retention policy cannot be removed or shortened in duration for the
       * lifetime of the bucket. Attempting to remove or decrease period of a locked
       * retention policy will result in a `PERMISSION_DENIED` error. You can still
       * increase the policy.
       *
       * @param {*} duration In seconds, the minimum retention time for all objects
       *     contained in this bucket.
       * @param {SetBucketMetadataCallback} [callback] Callback function.
       * @returns {Promise<SetBucketMetadataResponse>}
       *
       * @example
       * const storage = require('@google-cloud/storage')();
       * const bucket = storage.bucket('albums');
       *
       * const DURATION_SECONDS = 15780000; // 6 months.
       *
       * //-
       * // Lock the objects in this bucket for 6 months.
       * //-
       * bucket.setRetentionPeriod(DURATION_SECONDS, function(err, apiResponse) {});
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.setRetentionPeriod(DURATION_SECONDS).then(function(data) {
       *   const apiResponse = data[0];
       * });
       */
      setRetentionPeriod(duration, callback) {
          this.setMetadata({
              retentionPolicy: {
                  retentionPeriod: duration,
              },
          }, callback);
      }
      /**
       * @typedef {object} SetBucketStorageClassOptions
       * @param {string} [userProject] - The ID of the project which will be
       *     billed for the request.
       */
      /**
       * @callback SetBucketStorageClassCallback
       * @param {?Error} err Request error, if any.
       */
      /**
       * Set the default storage class for new files in this bucket.
       *
       * @see [Storage Classes]{@link https://cloud.google.com/storage/docs/storage-classes}
       *
       * @param {string} storageClass The new storage class. (`multi_regional`,
       *     `regional`, `standard`, `nearline`, `coldline`, or
       *     `durable_reduced_availability`)
       * @param {object} [options] Configuration options.
       * @param {string} [options.userProject] - The ID of the project which will be
       *     billed for the request.
       * @param {SetStorageClassCallback} [callback] Callback function.
       * @returns {Promise}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * bucket.setStorageClass('regional', function(err, apiResponse) {
       *   if (err) {
       *     // Error handling omitted.
       *   }
       *
       *   // The storage class was updated successfully.
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.setStorageClass('regional').then(function() {});
       */
      setStorageClass(storageClass, optionsOrCallback, callback) {
          const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
          callback =
              typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
          // In case we get input like `storageClass`, convert to `storage_class`.
          storageClass = storageClass.replace(/-/g, '_')
              .replace(/([a-z])([A-Z])/g, (_, low, up) => {
              return low + '_' + up;
          })
              .toUpperCase();
          this.setMetadata({ storageClass }, options, callback);
      }
      /**
       * Set a user project to be billed for all requests made from this Bucket
       * object and any files referenced from this Bucket object.
       *
       * @param {string} userProject The user project.
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * bucket.setUserProject('grape-spaceship-123');
       */
      setUserProject(userProject) {
          this.userProject = userProject;
          const methods = ['create', 'delete', 'exists', 'get', 'getMetadata', 'setMetadata'];
          methods.forEach(method => {
              const methodConfig = this.methods[method];
              if (typeof methodConfig === 'object') {
                  if (typeof methodConfig.reqOpts === 'object') {
                      extend(methodConfig.reqOpts.qs, { userProject });
                  }
                  else {
                      methodConfig.reqOpts = {
                          qs: { userProject },
                      };
                  }
              }
          });
      }
      /**
       * @typedef {object} UploadOptions Configuration options for Bucket#upload().
       * @param {string|File} [options.destination] The place to save
       *     your file. If given a string, the file will be uploaded to the bucket
       *     using the string as a filename. When given a File object, your local
       * file will be uploaded to the File object's bucket and under the File
       * object's name. Lastly, when this argument is omitted, the file is uploaded
       * to your bucket using the name of the local file.
       * @param {string} [options.encryptionKey] A custom encryption key. See
       *     [Customer-supplied Encryption
       * Keys](https://cloud.google.com/storage/docs/encryption#customer-supplied).
       * @param {boolean} [options.gzip] Automatically gzip the file. This will set
       *     `options.metadata.contentEncoding` to `gzip`.
       * @param {string} [options.kmsKeyName] The name of the Cloud KMS key that will
       *     be used to encrypt the object. Must be in the format:
       *     `projects/my-project/locations/location/keyRings/my-kr/cryptoKeys/my-key`.
       * @param {object} [options.metadata] See an
       *     [Objects: insert request
       * body](https://cloud.google.com/storage/docs/json_api/v1/objects/insert#request_properties_JSON).
       * @param {string} [options.offset] The starting byte of the upload stream, for
       *     resuming an interrupted upload. Defaults to 0.
       * @param {string} [options.predefinedAcl] Apply a predefined set of access
       *     controls to this object.
       *
       *     Acceptable values are:
       *     - **`authenticatedRead`** - Object owner gets `OWNER` access, and
       *       `allAuthenticatedUsers` get `READER` access.
       *
       *     - **`bucketOwnerFullControl`** - Object owner gets `OWNER` access, and
       *       project team owners get `OWNER` access.
       *
       *     - **`bucketOwnerRead`** - Object owner gets `OWNER` access, and project
       *       team owners get `READER` access.
       *
       *     - **`private`** - Object owner gets `OWNER` access.
       *
       *     - **`projectPrivate`** - Object owner gets `OWNER` access, and project
       *       team members get access according to their roles.
       *
       *     - **`publicRead`** - Object owner gets `OWNER` access, and `allUsers`
       * get `READER` access.
       * @param {boolean} [options.private] Make the uploaded file private. (Alias for
       *     `options.predefinedAcl = 'private'`)
       * @param {boolean} [options.public] Make the uploaded file public. (Alias for
       *     `options.predefinedAcl = 'publicRead'`)
       * @param {boolean} [options.resumable] Force a resumable upload. (default:
       *     true for files larger than 5 MB).
       * @param {string} [options.uri] The URI for an already-created resumable
       *     upload. See {@link File#createResumableUpload}.
       * @param {string} [options.userProject] The ID of the project which will be
       *     billed for the request.
       * @param {string|boolean} [options.validation] Possible values: `"md5"`,
       *     `"crc32c"`, or `false`. By default, data integrity is validated with an
       *     MD5 checksum for maximum reliability. CRC32c will provide better
       *     performance with less reliability. You may also choose to skip
       * validation completely, however this is **not recommended**.
       */
      /**
       * @typedef {array} UploadResponse
       * @property {object} 0 The uploaded {@link File}.
       * @property {object} 1 The full API response.
       */
      /**
       * @callback UploadCallback
       * @param {?Error} err Request error, if any.
       * @param {object} file The uploaded {@link File}.
       * @param {object} apiResponse The full API response.
       */
      /**
       * Upload a file to the bucket. This is a convenience method that wraps
       * {@link File#createWriteStream}.
       *
       * You can specify whether or not an upload is resumable by setting
       * `options.resumable`. *Resumable uploads are enabled by default if your
       * input file is larger than 5 MB.*
       *
       * For faster crc32c computation, you must manually install
       * [`fast-crc32c`](http://www.gitnpm.com/fast-crc32c):
       *
       *     $ npm install --save fast-crc32c
       *
       * @see [Upload Options (Simple or Resumable)]{@link https://cloud.google.com/storage/docs/json_api/v1/how-tos/upload#uploads}
       * @see [Objects: insert API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objects/insert}
       *
       * @param {string} pathString The fully qualified path to the file you
       *     wish to upload to your bucket.
       * @param {UploadOptions} [options] Configuration options.
       * @param {UploadCallback} [callback] Callback function.
       * @returns {Promise<UploadResponse>}
       *
       * @example
       * const {Storage} = require('@google-cloud/storage');
       * const storage = new Storage();
       * const bucket = storage.bucket('albums');
       *
       * //-
       * // Upload a file from a local path.
       * //-
       * bucket.upload('/local/path/image.png', function(err, file, apiResponse) {
       *   // Your bucket now contains:
       *   // - "image.png" (with the contents of `/local/path/image.png')
       *
       *   // `file` is an instance of a File object that refers to your new file.
       * });
       *
       *
       * //-
       * // It's not always that easy. You will likely want to specify the filename
       * // used when your new file lands in your bucket.
       * //
       * // You may also want to set metadata or customize other options.
       * //-
       * const options = {
       *   destination: 'new-image.png',
       *   resumable: true,
       *   validation: 'crc32c',
       *   metadata: {
       *     metadata: {
       *       event: 'Fall trip to the zoo'
       *     }
       *   }
       * };
       *
       * bucket.upload('local-image.png', options, function(err, file) {
       *   // Your bucket now contains:
       *   // - "new-image.png" (with the contents of `local-image.png')
       *
       *   // `file` is an instance of a File object that refers to your new file.
       * });
       *
       * //-
       * // You can also have a file gzip'd on the fly.
       * //-
       * bucket.upload('index.html', { gzip: true }, function(err, file) {
       *   // Your bucket now contains:
       *   // - "index.html" (automatically compressed with gzip)
       *
       *   // Downloading the file with `file.download` will automatically decode
       * the
       *   // file.
       * });
       *
       * //-
       * // You may also re-use a File object, {File}, that references
       * // the file you wish to create or overwrite.
       * //-
       * const options = {
       *   destination: bucket.file('existing-file.png'),
       *   resumable: false
       * };
       *
       * bucket.upload('local-img.png', options, function(err, newFile) {
       *   // Your bucket now contains:
       *   // - "existing-file.png" (with the contents of `local-img.png')
       *
       *   // Note:
       *   // The `newFile` parameter is equal to `file`.
       * });
       *
       * //-
       * // To use
       * // <a
       * href="https://cloud.google.com/storage/docs/encryption#customer-supplied">
       * // Customer-supplied Encryption Keys</a>, provide the `encryptionKey`
       * option.
       * //-
       * const crypto = require('crypto');
       * const encryptionKey = crypto.randomBytes(32);
       *
       * bucket.upload('img.png', {
       *   encryptionKey: encryptionKey
       * }, function(err, newFile) {
       *   // `img.png` was uploaded with your custom encryption key.
       *
       *   // `newFile` is already configured to use the encryption key when making
       *   // operations on the remote object.
       *
       *   // However, to use your encryption key later, you must create a `File`
       *   // instance with the `key` supplied:
       *   const file = bucket.file('img.png', {
       *     encryptionKey: encryptionKey
       *   });
       *
       *   // Or with `file#setEncryptionKey`:
       *   const file = bucket.file('img.png');
       *   file.setEncryptionKey(encryptionKey);
       * });
       *
       * //-
       * // If the callback is omitted, we'll return a Promise.
       * //-
       * bucket.upload('local-image.png').then(function(data) {
       *   const file = data[0];
       * });
       *
       * To upload a file from a URL, use {@link File#createWriteStream}.
       *
       * @example <caption>include:samples/files.js</caption>
       * region_tag:storage_upload_file
       * Another example:
       *
       * @example <caption>include:samples/encryption.js</caption>
       * region_tag:storage_upload_encrypted_file
       * Example of uploading an encrypted file:
       */
      upload(pathString, optionsOrCallback, callback) {
          // tslint:disable-next-line:no-any
          if (global['GCLOUD_SANDBOX_ENV']) {
              return;
          }
          let options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
          callback =
              typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
          options = Object.assign({
              metadata: {},
          }, options);
          let newFile;
          if (options.destination instanceof file_1.File) {
              newFile = options.destination;
          }
          else if (options.destination != null &&
              typeof options.destination === 'string') {
              // Use the string as the name of the file.
              newFile = this.file(options.destination, {
                  encryptionKey: options.encryptionKey,
                  kmsKeyName: options.kmsKeyName,
              });
          }
          else {
              // Resort to using the name of the incoming file.
              const destination = path.basename(pathString);
              newFile = this.file(destination, {
                  encryptionKey: options.encryptionKey,
                  kmsKeyName: options.kmsKeyName,
              });
          }
          const contentType = mime.contentType(path.basename(pathString));
          if (contentType && !options.metadata.contentType) {
              options.metadata.contentType = contentType;
          }
          if (options.resumable != null && typeof options.resumable === 'boolean') {
              upload();
          }
          else {
              // Determine if the upload should be resumable if it's over the threshold.
              fs.stat(pathString, (err, fd) => {
                  if (err) {
                      callback(err);
                      return;
                  }
                  options.resumable = fd.size > RESUMABLE_THRESHOLD;
                  upload();
              });
          }
          function upload() {
              fs.createReadStream(pathString)
                  .on('error', callback)
                  .pipe(newFile.createWriteStream(options))
                  .on('error', callback)
                  .on('finish', () => {
                  callback(null, newFile, newFile.metadata);
              });
          }
      }
      /**
       * @private
       *
       * @typedef {object} MakeAllFilesPublicPrivateOptions
       * @property {boolean} [force] Suppress errors until all files have been
       *     processed.
       * @property {boolean} [private] Make files private.
       * @property {boolean} [public] Make files public.
       * @property {string} [userProject] The ID of the project which will be
       *     billed for the request.
       */
      /**
       * @private
       *
       * @callback SetBucketMetadataCallback
       * @param {?Error} err Request error, if any.
       * @param {File[]} files Files that were updated.
       */
      /**
       * @typedef {array} MakeAllFilesPublicPrivateResponse
       * @property {File[]} 0 List of files affected.
       */
      /**
       * Iterate over all of a bucket's files, calling `file.makePublic()` (public)
       * or `file.makePrivate()` (private) on each.
       *
       * Operations are performed in parallel, up to 10 at once. The first error
       * breaks the loop, and will execute the provided callback with it. Specify
       * `{ force: true }` to suppress the errors.
       *
       * @private
       *
       * @param {MakeAllFilesPublicPrivateOptions} [options] Configuration options.
       * @param {MakeAllFilesPublicPrivateCallback} callback Callback function.
       *
       * @return {Promise<MakeAllFilesPublicPrivateResponse>}
       */
      makeAllFilesPublicPrivate_(optionsOrCallback, callback) {
          const MAX_PARALLEL_LIMIT = 10;
          const errors = [];
          const updatedFiles = [];
          const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : {};
          callback =
              typeof optionsOrCallback === 'function' ? optionsOrCallback : callback;
          this.getFiles(options, (err, files) => {
              if (err) {
                  callback(err);
                  return;
              }
              const processFile = (file, callback) => {
                  const processedCallback = (err) => {
                      if (err) {
                          if (options.force) {
                              errors.push(err);
                              callback();
                              return;
                          }
                          callback(err);
                          return;
                      }
                      updatedFiles.push(file);
                      callback();
                  };
                  if (options.public) {
                      file.makePublic(processedCallback);
                  }
                  else if (options.private) {
                      file.makePrivate(options, processedCallback);
                  }
              };
              // Iterate through each file and make it public or private.
              async.eachLimit(files, MAX_PARALLEL_LIMIT, processFile, (err) => {
                  if (err || errors.length > 0) {
                      callback(err || errors, updatedFiles);
                      return;
                  }
                  callback(null, updatedFiles);
              });
          });
      }
      getId() {
          return this.id;
      }
  }
  exports.Bucket = Bucket;
  /*! Developer Documentation
   *
   * These methods can be auto-paginated.
   */
  paginator_1.paginator.extend(Bucket, 'getFiles');
  /*! Developer Documentation
   *
   * All async methods (except for streams) will return a Promise in the event
   * that a callback is omitted.
   */
  promisify_1.promisifyAll(Bucket, {
      exclude: ['request', 'file', 'notification'],
  });
  //# sourceMappingURL=bucket.js.map