<commit_before>/* eslint no-underscore-dangle: 0 */
import EventEmitter from 'eventemitter3';
import memoize from 'memoizerific';
import debounce from 'lodash/debounce';
import { stripIndents } from 'common-tags';

import { Channel } from '@storybook/channels';
import Events from '@storybook/core-events';
import { logger } from '@storybook/client-logger';
import { StoryFn, Parameters } from '@storybook/addons';
import {
  DecoratorFunction,
  LegacyData,
  LegacyItem,
  StoreData,
  AddStoryArgs,
  StoreItem,
  ErrorLike,
} from './types';

// TODO: these are copies from components/nav/lib
// refactor to DRY
const toKey = (input: string) =>
  input.replace(/[^a-z0-9]+([a-z0-9])/gi, (...params) => params[1].toUpperCase());

let count = 0;

const getId = (): number => {
  count += 1;
  return count;
};

const toExtracted = <T>(obj: T) =>
  Object.entries(obj).reduce((acc, [key, value]) => {
    if (typeof value === 'function') {
      return acc;
    }
    if (Array.isArray(value)) {
      return Object.assign(acc, { [key]: value.slice().sort() });
    }
    return Object.assign(acc, { [key]: value });
  }, {});

interface Selection {
  storyId: string;
  viewMode: string;
}

export default class StoryStore extends EventEmitter {
  _error?: ErrorLike;

  _channel: Channel;

  _data: StoreData;

  _legacyData?: LegacyData;

  _legacydata: LegacyData;

  _revision: number;

  _selection: Selection;

  constructor(params: { channel: Channel }) {
    super();

    this._legacydata = {} as any;
    this._data = {} as any;
    this._revision = 0;
    this._selection = {} as any;
    this._channel = params.channel;
    this._error = undefined;
  }

  setChannel = (channel: Channel) => {
    this._channel = channel;
  };

  // NEW apis
  fromId = (id: string): StoreItem | null => {
    try {
      const data = this._data[id as string];

      if (!data || !data.getDecorated) {
        return null;
      }

      return data;
    } catch (e) {
      logger.warn('failed to get story:', this._data);
      logger.error(e);
      return null;
    }
  };

  raw() {
    return Object.values(this._data)
      .filter(i => !!i.getDecorated)
      .map(({ id }) => this.fromId(id));
  }

  extract() {
    const stories = Object.entries(this._data);
    // determine if we should apply a sort to the stories or just use default import order
    if (Object.values(this._data).length > 0) {
      const index = Object.keys(this._data).find(
        key =>
          !!(this._data[key] && this._data[key].parameters && this._data[key].parameters.options)
      );
      if (index && this._data[index].parameters.options.storySort) {
        const sortFn = this._data[index].parameters.options.storySort;
        stories.sort(sortFn);
      }
    }
    // removes function values from all stories so they are safe to transport over the channel
    return stories.reduce((a, [k, v]) => Object.assign(a, { [k]: toExtracted(v) }), {});
  }

  setSelection(data: Selection | undefined, error: ErrorLike): void {
    this._selection =
      data === undefined ? this._selection : { storyId: data.storyId, viewMode: data.viewMode };
    this._error = error === undefined ? this._error : error;

    setTimeout(() => {
      // preferred method to emit event.
      if (this._channel) {
        this._channel.emit(Events.STORY_RENDER);
      }

      // should be deprecated in future.
      this.emit(Events.STORY_RENDER);
    }, 1);
  }

  getSelection = (): Selection => this._selection;

  getError = (): ErrorLike | undefined => this._error;

  remove = (id: string): void => {<commit_msg>Core: Clear story_store error when data is set<commit_after>/* eslint no-underscore-dangle: 0 */
import EventEmitter from 'eventemitter3';
import memoize from 'memoizerific';
import debounce from 'lodash/debounce';
import { stripIndents } from 'common-tags';

import { Channel } from '@storybook/channels';
import Events from '@storybook/core-events';
import { logger } from '@storybook/client-logger';
import { StoryFn, Parameters } from '@storybook/addons';
import {
  DecoratorFunction,
  LegacyData,
  LegacyItem,
  StoreData,
  AddStoryArgs,
  StoreItem,
  ErrorLike,
} from './types';

// TODO: these are copies from components/nav/lib
// refactor to DRY
const toKey = (input: string) =>
  input.replace(/[^a-z0-9]+([a-z0-9])/gi, (...params) => params[1].toUpperCase());

let count = 0;

const getId = (): number => {
  count += 1;
  return count;
};

const toExtracted = <T>(obj: T) =>
  Object.entries(obj).reduce((acc, [key, value]) => {
    if (typeof value === 'function') {
      return acc;
    }
    if (Array.isArray(value)) {
      return Object.assign(acc, { [key]: value.slice().sort() });
    }
    return Object.assign(acc, { [key]: value });
  }, {});

interface Selection {
  storyId: string;
  viewMode: string;
}

export default class StoryStore extends EventEmitter {
  _error?: ErrorLike;

  _channel: Channel;

  _data: StoreData;

  _legacyData?: LegacyData;

  _legacydata: LegacyData;

  _revision: number;

  _selection: Selection;

  constructor(params: { channel: Channel }) {
    super();

    this._legacydata = {} as any;
    this._data = {} as any;
    this._revision = 0;
    this._selection = {} as any;
    this._channel = params.channel;
    this._error = undefined;
  }

  setChannel = (channel: Channel) => {
    this._channel = channel;
  };

  // NEW apis
  fromId = (id: string): StoreItem | null => {
    try {
      const data = this._data[id as string];

      if (!data || !data.getDecorated) {
        return null;
      }

      return data;
    } catch (e) {
      logger.warn('failed to get story:', this._data);
      logger.error(e);
      return null;
    }
  };

  raw() {
    return Object.values(this._data)
      .filter(i => !!i.getDecorated)
      .map(({ id }) => this.fromId(id));
  }

  extract() {
    const stories = Object.entries(this._data);
    // determine if we should apply a sort to the stories or just use default import order
    if (Object.values(this._data).length > 0) {
      const index = Object.keys(this._data).find(
        key =>
          !!(this._data[key] && this._data[key].parameters && this._data[key].parameters.options)
      );
      if (index && this._data[index].parameters.options.storySort) {
        const sortFn = this._data[index].parameters.options.storySort;
        stories.sort(sortFn);
      }
    }
    // removes function values from all stories so they are safe to transport over the channel
    return stories.reduce((a, [k, v]) => Object.assign(a, { [k]: toExtracted(v) }), {});
  }

  setSelection(data: Selection | undefined, error: ErrorLike): void {
    this._selection =
      data === undefined ? this._selection : { storyId: data.storyId, viewMode: data.viewMode };
    this._error = error === undefined && data === undefined ? this._error : error;

    setTimeout(() => {
      // preferred method to emit event.
      if (this._channel) {
        this._channel.emit(Events.STORY_RENDER);
      }

      // should be deprecated in future.
      this.emit(Events.STORY_RENDER);
    }, 1);
  }

  getSelection = (): Selection => this._selection;

  getError = (): ErrorLike | undefined => this._error;

  remove = (id: string): void => {
{'fraction_non_alphanumeric': 0.15075099903541408, 'fraction_numerical': 0.0033071517155849523, 'mean_word_length': 2.9617903930131004, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 37, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3810046', 'n_tokens_mistral': 2343, 'n_tokens_neox': 2282, 'n_words': 587}