<commit_before>import { isObservable, observe, 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 synchronizers = new Map()

export function easyParams (store, config) {
  if (!isObservable(store)) {
    throw new TypeError(
      'The first argument must be an observable state store.'
    )
  }
  if (typeof config !== 'object' || config === null) {
    throw new TypeError('The second argument must be a config object.')
  }
  config = setupConfig(config)
  stores.set(store, config)
  sync(config, store)
}

export function routeParams (params) {
  if (typeof params !== 'object' || params === null) {
    throw new TypeError('The first argument must be a params object.')
  }

  // distibute the passed params between the stores based on their config keys
  // the url/history/localStorage will update automatically because of the reactions
  stores.forEach((config, store) => {
    for (let key of config.keys) {
      if (key in params) {
        store[key] = params[key]
      }
    }
  })
}

export function getParams () {
  const params = {}

  // fetch the params from all of the stores and merge them into a single object
  stores.forEach((config, store) => {
    for (let key of config.keys) {
      params[key] = store[key]
    }
  })
  return params
}

function sync (config, store) {
  // 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
}

window.addEventListener('popstate', () => {
  stores.forEach(syncStoreWithHistory)
  stores.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<commit_msg>feat (stores): add store activity toggling<commit_after>import { isObservable, 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 easyParams (store, config) {
  if (!isObservable(store)) {
    throw new TypeError(
      'The first argument must be an observable state store.'
    )
  }
  if (typeof config !== 'object' || config === null) {
    throw new TypeError('The second argument must be a config object.')
  }
  config = setupConfig(config)
  stores.set(store, config)
  activate(store)
}

export function routeParams (params) {
  if (typeof params !== 'object' || params === null) {
    throw new TypeError('The first argument must be a params object.')
  }

  // distibute the passed params between the stores based on their config keys
  // the url/history/localStorage will update automatically because of the reactions
  activeStores.forEach((config, store) => {
    for (let key of config.keys) {
      if (key in params) {
        store[key] = params[key]
      }
    }
  })
}

export function getParams () {
  const params = {}

  // fetch the params from all of the stores and merge them into a single object
  activeStores.forEach((config, store) => {
    for (let key of config.keys) {
      params[key] = store[key]
    }
  })
  return params
}

function activate (store) {
  if (activeStores.has(store)) {
    throw new Error('The store is already active')
  }
  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
}

function deactivate (store) {
  if (!activeStores.has(store)) {
    throw new Error('The store is already inactive')
  }
  activeStores.delete(store)

  const sync = synchronizers.get(store)
  unobserve(sync.storage)
  unobserve(sync.history)
  unobserve(sync.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
{'fraction_non_alphanumeric': 0.10190101044699434, 'fraction_numerical': 0.0, 'mean_word_length': 3.8464730290456433, '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': '1399448', 'n_tokens_mistral': 1658, 'n_tokens_neox': 1643, 'n_words': 593}