text: "use strict";
  
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  
  exports.__esModule = true;
  exports.AnalyticsTracker = void 0;
  
  var fs = _interopRequireWildcard(require("fs-extra"));
  
  var _os = _interopRequireDefault(require("os"));
  
  var _gatsbyCoreUtils = require("gatsby-core-utils");
  
  var _repositoryId = require("./repository-id");
  
  var _createFlush = require("./create-flush");
  
  var _eventStorage = require("./event-storage");
  
  var _showAnalyticsNotification = require("./show-analytics-notification");
  
  var _errorHelpers = require("./error-helpers");
  
  var _getDependencies = require("./get-dependencies");
  
  var _isDocker = _interopRequireDefault(require("is-docker"));
  
  var _lodash = _interopRequireDefault(require("lodash"));
  
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
  
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
  
  const typedUUIDv4 = _gatsbyCoreUtils.uuid.v4;
  const finalEventRegex = /(END|STOP)$/;
  const dbEngine = `redux`;
  
  class AnalyticsTracker {
    store = new _eventStorage.EventStorage();
    debouncer = {};
    metadataCache = {};
    defaultTags = {};
    sessionId = this.getSessionId();
    features = new Set();
    siteHash = (0, _gatsbyCoreUtils.createContentDigest)(process.cwd());
    lastEnvTagsFromFileTime = 0;
    lastEnvTagsFromFileValue = {};
  
    constructor({
      componentId,
      gatsbyCliVersion,
      trackingEnabled
    } = {}) {
      this.componentId = componentId || `gatsby-cli`;
  
      try {
        if (this.store.isTrackingDisabled()) {
          this.trackingEnabled = false;
        }
  
        if (trackingEnabled !== undefined) {
          this.trackingEnabled = trackingEnabled;
        }
  
        this.defaultTags = this.getTagsFromEnv(); // These may throw and should be last
  
        this.componentVersion = require(`../package.json`).version;
        this.gatsbyCliVersion = gatsbyCliVersion || this.getGatsbyCliVersion();
        this.installedGatsbyVersion = this.getGatsbyVersion();
      } catch (e) {// ignore
      }
  
      this.machineId = this.getMachineId();
      this.captureMetadataEvent();
    } // We might have two instances of this lib loaded, one from globally installed gatsby-cli and one from local gatsby.
    // Hence we need to use process level globals that are not scoped to this module.
    // Due to the forking on develop process, we also need to pass this via process.env so that child processes have the same sessionId
  
  
    getSessionId() {
      const p = process;
  
      if (!p.gatsbyTelemetrySessionId) {
        const inherited = process.env.INTERNAL_GATSBY_TELEMETRY_SESSION_ID;
  
        if (inherited) {
          p.gatsbyTelemetrySessionId = inherited;
        } else {
          p.gatsbyTelemetrySessionId = _gatsbyCoreUtils.uuid.v4();
          process.env.INTERNAL_GATSBY_TELEMETRY_SESSION_ID = p.gatsbyTelemetrySessionId;
        }
      } else if (!process.env.INTERNAL_GATSBY_TELEMETRY_SESSION_ID) {
        // in case older `gatsby-telemetry` already set `gatsbyTelemetrySessionId` property on process
        // but didn't set env var - let's make sure env var is set
        process.env.INTERNAL_GATSBY_TELEMETRY_SESSION_ID = p.gatsbyTelemetrySessionId;
      }
  
      return p.gatsbyTelemetrySessionId;
    }
  
    getRepositoryId() {
      if (!this.repositoryId) {
        this.repositoryId = (0, _repositoryId.getRepositoryId)();
      }
  
      return this.repositoryId;
    }
  
    getTagsFromEnv() {
      if (process.env.GATSBY_TELEMETRY_TAGS) {
        try {
          return JSON.parse(process.env.GATSBY_TELEMETRY_TAGS);
        } catch (_) {// ignore
        }
      }
  
      return {};
    }
  
    getGatsbyVersion() {
      try {
        const packageJson = require.resolve(`gatsby/package.json`);
  
        const {
          version
        } = JSON.parse(fs.readFileSync(packageJson, `utf-8`));
        return version;
      } catch (e) {// ignore
      }
  
      return `-0.0.0`;
    }
  
    getGatsbyCliVersion() {
      try {
        const jsonfile = require.resolve(`gatsby-cli/package.json`);
  
        const {
          version
        } = JSON.parse(fs.readFileSync(jsonfile, `utf-8`));
        return version;
      } catch (e) {// ignore
      }
  
      return `-0.0.0`;
    }
  
    trackCli(type = ``, tags = {}, opts = {
      debounce: false
    }) {
      if (!this.isTrackingEnabled()) {
        return;
      }
  
      if (typeof tags.siteHash === `undefined`) {
        tags.siteHash = this.siteHash;
      }
  
      this.captureEvent(type, tags, opts);
    }
  
    captureEvent(type = ``, tags = {}, opts = {
      debounce: false
    }) {
      if (!this.isTrackingEnabled()) {
        return;
      }
  
      let baseEventType = `CLI_COMMAND`;
  
      if (Array.isArray(type)) {
        type = type.length > 2 ? type[2].toUpperCase() : ``;
        baseEventType = `CLI_RAW_COMMAND`;
      }
  
      const decoration = this.metadataCache[type];
      const eventType = `${baseEventType}_${type}`;
  
      if (opts.debounce) {
        const debounceTime = 5 * 1000;
        const now = Date.now();
        const debounceKey = JSON.stringify({
          type,
          decoration,
          tags
        });
        const last = this.debouncer[debounceKey] || 0;
  
        if (now - last < debounceTime) {
          return;
        }
  
        this.debouncer[debounceKey] = now;
      }
  
      delete this.metadataCache[type];
      this.buildAndStoreEvent(eventType, _lodash.default.merge({}, tags, decoration));
    }
  
    isFinalEvent(event) {
      return finalEventRegex.test(event);
    }
  
    captureError(type, tags = {}) {
      if (!this.isTrackingEnabled()) {
        return;
      }
  
      const decoration = this.metadataCache[type];
      delete this.metadataCache[type];
      const eventType = `CLI_ERROR_${type}`;
      this.formatErrorAndStoreEvent(eventType, _lodash.default.merge({}, tags, decoration));
    }
  
    captureBuildError(type, tags = {}) {
      if (!this.isTrackingEnabled()) {
        return;
      }
  
      const decoration = this.metadataCache[type];
      delete this.metadataCache[type];
      const eventType = `BUILD_ERROR_${type}`;
      this.formatErrorAndStoreEvent(eventType, _lodash.default.merge({}, tags, decoration));
    }
  
    formatErrorAndStoreEvent(eventType, tags) {
      if (tags.error) {
        var _tags$error, _tags$error2, _tags$error2$error, _tags$error3;
  
        // `error` ought to have been `errors` but is `error` in the database
        if (Array.isArray(tags.error)) {
          const {
            error,
            ...restOfTags
          } = tags;
          error.forEach(err => {
            this.formatErrorAndStoreEvent(eventType, {
              error: err,
              ...restOfTags
            });
          });
          return;
        }
  
        tags.errorV2 = {
          // errorCode field was changed from `id` to `code`
          id: tags.error.code || tags.error.id,
          text: (0, _errorHelpers.cleanPaths)(tags.error.text),
          level: tags.error.level,
          type: (_tags$error = tags.error) === null || _tags$error === void 0 ? void 0 : _tags$error.type,
          // see if we need empty string or can just use NULL
          stack: (0, _errorHelpers.cleanPaths)(((_tags$error2 = tags.error) === null || _tags$error2 === void 0 ? void 0 : (_tags$error2$error = _tags$error2.error) === null || _tags$error2$error === void 0 ? void 0 : _tags$error2$error.stack) || ``),
          context: (0, _errorHelpers.cleanPaths)(JSON.stringify((_tags$error3 = tags.error) === null || _tags$error3 === void 0 ? void 0 : _tags$error3.context))
        };
        delete tags.error;
      }
  
      this.buildAndStoreEvent(eventType, tags);
    }
  
    buildAndStoreEvent(eventType, tags) {
      const event = {
        installedGatsbyVersion: this.installedGatsbyVersion,
        gatsbyCliVersion: this.gatsbyCliVersion,
        ..._lodash.default.merge({}, this.defaultTags, tags),
        // The schema must include these
        eventType,
        sessionId: this.sessionId,
        time: new Date(),
        machineId: this.getMachineId(),
        componentId: this.componentId,
        osInformation: this.getOsInfo(),
        componentVersion: this.componentVersion,
        dbEngine,
        features: Array.from(this.features),
        ...this.getRepositoryId(),
        ...this.getTagsFromPath()
      };
      this.store.addEvent(event);
  
      if (this.isFinalEvent(eventType)) {
        // call create flush
        const flush = (0, _createFlush.createFlush)(this.isTrackingEnabled());
        flush();
      }
    }
  
    getTagsFromPath() {
      const path = process.env.GATSBY_TELEMETRY_METADATA_PATH;
  
      if (!path) {
        return {};
      }
  
      try {
        const stat = fs.statSync(path);
  
        if (this.lastEnvTagsFromFileTime < stat.mtimeMs) {
          this.lastEnvTagsFromFileTime = stat.mtimeMs;
          const data = fs.readFileSync(path, `utf8`);
          this.lastEnvTagsFromFileValue = JSON.parse(data);
        }
      } catch (e) {
        // nop
        return {};
      }
  
      return this.lastEnvTagsFromFileValue;
    }
  
    getIsTTY() {
      var _process$stdout;
  
      return Boolean((_process$stdout = process.stdout) === null || _process$stdout === void 0 ? void 0 : _process$stdout.isTTY);
    }
  
    getMachineId() {
      // Cache the result
      if (this.machineId) {
        return this.machineId;
      }
  
      let machineId = this.store.getConfig(`telemetry.machineId`);
  
      if (typeof machineId !== `string`) {
        machineId = typedUUIDv4();
        this.store.updateConfig(`telemetry.machineId`, machineId);
      }
  
      this.machineId = machineId;
      return machineId;
    }
  
    isTrackingEnabled() {
      // Cache the result
      if (this.trackingEnabled !== undefined) {
        return this.trackingEnabled;
      }
  
      let enabled = this.store.getConfig(`telemetry.enabled`);
  
      if (enabled === undefined || enabled === null) {
        if (!(0, _gatsbyCoreUtils.isCI)()) {
          (0, _showAnalyticsNotification.showAnalyticsNotification)();
        }
  
        enabled = true;
        this.store.updateConfig(`telemetry.enabled`, enabled);
      }
  
      this.trackingEnabled = enabled;
      return enabled;
    }
  
    getOsInfo() {
      if (this.osInfo) {
        return this.osInfo;
      }
  
      const cpus = _os.default.cpus();
  
      const osInfo = {
        nodeVersion: process.version,
        platform: _os.default.platform(),
        release: _os.default.release(),
        cpus: cpus && cpus.length > 0 && cpus[0].model || undefined,
        arch: _os.default.arch(),
        ci: (0, _gatsbyCoreUtils.isCI)(),
        ciName: (0, _gatsbyCoreUtils.getCIName)(),
        docker: (0, _isDocker.default)(),
        termProgram: (0, _gatsbyCoreUtils.getTermProgram)(),
        isTTY: this.getIsTTY()
      };
      this.osInfo = osInfo;
      return osInfo;
    }
  
    trackActivity(source, tags = {}) {
      if (!this.isTrackingEnabled()) {
        return;
      } // debounce by sending only the first event within a rolling window
  
  
      const now = Date.now();
      const last = this.debouncer[source] || 0;
      const debounceTime = 5 * 1000; // 5 sec
  
      if (now - last > debounceTime) {
        this.captureEvent(source, tags);
      }
  
      this.debouncer[source] = now;
    }
  
    decorateNextEvent(event, obj) {
      const cached = this.metadataCache[event] || {};
      this.metadataCache[event] = Object.assign(cached, obj);
    }
  
    addSiteMeasurement(event, obj) {
      const cachedEvent = this.metadataCache[event] || {};
      const cachedMeasurements = cachedEvent.siteMeasurements || {};
      this.metadataCache[event] = Object.assign(cachedEvent, {
        siteMeasurements: Object.assign(cachedMeasurements, obj)
      });
    }
  
    decorateAll(tags) {
      this.defaultTags = Object.assign(this.defaultTags, tags);
    }
  
    setTelemetryEnabled(enabled) {
      this.trackingEnabled = enabled;
      this.store.updateConfig(`telemetry.enabled`, enabled);
    }
  
    aggregateStats(data) {
      const sum = data.reduce((acc, x) => acc + x, 0);
      const mean = sum / data.length || 0;
      const median = data.sort()[Math.floor((data.length - 1) / 2)] || 0;
      const stdDev = Math.sqrt(data.reduce((acc, x) => acc + Math.pow(x - mean, 2), 0) / (data.length - 1)) || 0;
      const skewness = data.reduce((acc, x) => acc + Math.pow(x - mean, 3), 0) / data.length / Math.pow(stdDev, 3);
      return {
        count: data.length,
        min: data.reduce((acc, x) => x < acc ? x : acc, data[0] || 0),
        max: data.reduce((acc, x) => x > acc ? x : acc, 0),
        sum: sum,
        mean: mean,
        median: median,
        stdDev: stdDev,
        skewness: !Number.isNaN(skewness) ? skewness : 0
      };
    }
  
    captureMetadataEvent() {
      if (!this.isTrackingEnabled()) {
        return;
      }
  
      const deps = (0, _getDependencies.getDependencies)();
      const evt = {
        dependencies: deps === null || deps === void 0 ? void 0 : deps.dependencies,
        devDependencies: deps === null || deps === void 0 ? void 0 : deps.devDependencies
      };
      this.captureEvent(`METADATA`, evt);
    }
  
    async sendEvents() {
      if (!this.isTrackingEnabled()) {
        return true;
      }
  
      return this.store.sendEvents();
    }
  
    trackFeatureIsUsed(name) {
      this.features.add(name);
    }
  
  }
  
  exports.AnalyticsTracker = AnalyticsTracker;