<commit_before>import { easyStore as originalEasyStore } from 'react-easy-state'
import { observe, unobserve, unqueue } from '@nx-js/observer-util'
import { syncStoreWithUrl, syncUrlWithStore } from './url'
import { syncStoreWithHistory, syncHistoryWithStore } from './history'
import { syncStoreWithStorage, syncStorageWithStore } from './storage'
import setupConfig from './setupConfig'

const stores = new Map()
const activeStores = new Map()
const synchronizers = new Map()

export function easyStore (store, config) {
  store = originalEasyStore(store)

  if (config === undefined) {
    return store
  }
  if (typeof config !== 'object' || config === null) {
    throw new TypeError(
      'The second argument must be undefined or a config object.'
    )
  }

  config = setupConfig(config)
  stores.set(store, config)
  // activate the store (sync with url, history and storage) on init
  activate(store)
  return store
}

export function routeParams (params, store) {
  // assign the passed params between to the passed store based on its config keys
  // the url/history/localStorage will update automatically
  if (store) {
    const config = stores.get(store)
    for (let key of config.keys) {
      if (key in params) {
        store[key] = params[key]
      }
    }
  } else {
    // distibute the passed params between the active stores
    activeStores.forEach((config, store) => routeParams(params, store))
  }
}

export function getParams (store) {
  const params = {}

  // get params from the passed store
  if (store) {
    const config = stores.get(store)
    for (let key of config.keys) {
      params[key] = store[key]
    }
  } else {
    // fetch the params from all of the active stores and merge them into a single object
    activeStores.forEach((config, store) => {
      Object.assign(params, getParams(store))
    })
  }
  return params
}

export function activate (store) {
  if (!stores.has(store)) {
    throw new Error('The first argument must be a param synced store.')
  }
  if (activeStores.has(store)) {
    return
  }
  const config = stores.get(store)
  activeStores.set(store, config)

  // init the store based on the localStorage/url/history
  syncStoreWithStorage(config, store)
  syncStoreWithHistory(config, store)
  syncStoreWithUrl(config, store)

  let initing = true
  // these run now once and will automatically rerun when the store changes
  synchronizers.set(store, {
    // history sync must come before url sync as it can push a new history item
    // the new item must be pushed asap, so that the later replaceStates mutate it rather than the old one
    storage: observe(() => syncStorageWithStore(config, store)),
    history: observe(() => syncHistoryWithStore(config, store, initing)),
    url: observe(() => syncUrlWithStore(config, store))
  })
  initing = false
}

export function deactivate (store) {
  if (!activeStores.has(store)) {
    return
  }
  activeStores.delete(store)

  const synchronizer = synchronizers.get(store)
  unobserve(synchronizer.storage)
  unobserve(synchronizer.history)
  unobserve(synchronizer.url)
  synchronizers.delete(store)

  // I should sync again...
  // activeStores.forEach(syncStorageWithStore)
  // activeStores.forEach(syncHistoryWithStore)
  // activeStores.forEach(syncUrlWithStore)
}

window.addEventListener('popstate', () => {
  activeStores.forEach(syncStoreWithHistory)
  activeStores.forEach(syncStoreWithUrl)

  // the url/history change triggered the store update
  // there is no need to update the url/history again
  synchronizers.forEach(unqueuePopstateSynchronizer)
})

// the localStorage should still be synchronized, as it was not the source of the change
// it is not unqueued from the reaction because of this
function unqueuePopstateSynchronizer (synchronizer) {
  unqueue(synchronizer.url)
  unqueue(synchronizer.history)
}<commit_msg>feat (deactivate): remove unnecessary routing<commit_after>import { easyStore as originalEasyStore } from 'react-easy-state'
import { observe, unobserve, unqueue } from '@nx-js/observer-util'
import { syncStoreWithUrl, syncUrlWithStore } from './url'
import { syncStoreWithHistory, syncHistoryWithStore } from './history'
import { syncStoreWithStorage, syncStorageWithStore } from './storage'
import setupConfig from './setupConfig'

const stores = new Map()
const activeStores = new Map()
const synchronizers = new Map()

export function easyStore (store, config) {
  store = originalEasyStore(store)

  if (config === undefined) {
    return store
  }
  if (typeof config !== 'object' || config === null) {
    throw new TypeError(
      'The second argument must be undefined or a config object.'
    )
  }

  config = setupConfig(config)
  stores.set(store, config)
  // activate the store (sync with url, history and storage) on init
  activate(store)
  return store
}

export function routeParams (params, store) {
  // assign the passed params between to the passed store based on its config keys
  // the url/history/localStorage will update automatically
  if (store) {
    const config = stores.get(store)
    for (let key of config.keys) {
      if (key in params) {
        store[key] = params[key]
      }
    }
  } else {
    // distibute the passed params between the active stores
    activeStores.forEach((config, store) => routeParams(params, store))
  }
}

export function getParams (store) {
  const params = {}

  // get params from the passed store
  if (store) {
    const config = stores.get(store)
    for (let key of config.keys) {
      params[key] = store[key]
    }
  } else {
    // fetch the params from all of the active stores and merge them into a single object
    activeStores.forEach((config, store) => {
      Object.assign(params, getParams(store))
    })
  }
  return params
}

export function activate (store) {
  if (!stores.has(store)) {
    throw new Error('The first argument must be a param synced store.')
  }
  if (activeStores.has(store)) {
    return
  }
  const config = stores.get(store)
  activeStores.set(store, config)

  // init the store based on the localStorage/url/history
  syncStoreWithStorage(config, store)
  syncStoreWithHistory(config, store)
  syncStoreWithUrl(config, store)

  let initing = true
  // these run now once and will automatically rerun when the store changes
  synchronizers.set(store, {
    // history sync must come before url sync as it can push a new history item
    // the new item must be pushed asap, so that the later replaceStates mutate it rather than the old one
    storage: observe(() => syncStorageWithStore(config, store)),
    history: observe(() => syncHistoryWithStore(config, store, initing)),
    url: observe(() => syncUrlWithStore(config, store))
  })
  initing = false
}

export function deactivate (store) {
  if (!activeStores.has(store)) {
    return
  }
  activeStores.delete(store)

  const synchronizer = synchronizers.get(store)
  unobserve(synchronizer.storage)
  unobserve(synchronizer.history)
  unobserve(synchronizer.url)
  synchronizers.delete(store)
}

window.addEventListener('popstate', () => {
  activeStores.forEach(syncStoreWithHistory)
  activeStores.forEach(syncStoreWithUrl)

  // the url/history change triggered the store update
  // there is no need to update the url/history again
  synchronizers.forEach(unqueuePopstateSynchronizer)
})

// the localStorage should still be synchronized, as it was not the source of the change
// it is not unqueued from the reaction because of this
function unqueuePopstateSynchronizer (synchronizer) {
  unqueue(synchronizer.url)
  unqueue(synchronizer.history)
}
{'fraction_non_alphanumeric': 0.09832828748190076, 'fraction_numerical': 0.0, 'mean_word_length': 3.811906269791007, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, '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': '5468233', 'n_tokens_mistral': 2221, 'n_tokens_neox': 2180, 'n_words': 731}