src/local-storage-ports.js
function register(ports, log) {
  // Mapped to Storage API: https://developer.mozilla.org/en-US/docs/Web/API/Storage
  ports.storageGetItem.subscribe(storageGetItem);
  ports.storageSetItem.subscribe(storageSetItem);
  ports.storageRemoveItem.subscribe(storageRemoveItem);
  ports.storageClear.subscribe(storageClear);

  // Not in Storage API
  ports.storagePushToSet.subscribe(storagePushToSet);
  ports.storageRemoveFromSet.subscribe(storageRemoveFromSet);

  // StorageEvent API
  storageEventListenerPorts.push(ports);

  log = log || function() {};

  function storageGetItem(key) {
    log('storageGetItem', key);
    const response = getLocalStorageItem(key);

    log('storageGetItemResponse', key, response);
    ports.storageGetItemResponse.send([key, response]);
  }

  function storageSetItem([key, value]) {
    log('storageSetItem', key, value);
    setLocalStorageItem(key, value);
  }

  function storageRemoveItem(key) {
    log('storageRemoveItem', key);
    window.localStorage.removeItem(key);
  }

  function storageClear() {
    log('storageClear');
    window.localStorage.clear();
  }

  // A Set is a list with only unique values. (No duplication.)
  function storagePushToSet([key, value]) {
    log('storagePushToSet', key, value);

    const item = getLocalStorageItem(key);
    const list = Array.isArray(item) ? item : [];

    if (list.indexOf(value) === -1) {
      list.push(value);
    }

    setLocalStorageItem(key, list);
  }

  function storageRemoveFromSet([key, value]) {
    log('storageRemoveFromSet', key, value);

    const list = getLocalStorageItem(key);

    if (!Array.isArray(list)) {
      log('storageRemoveFromSet [aborting; not a list]', key, value, list);
      return;
    }

    // Filter based on JSON strings in to ensure equality-by-value instead of equality-by-reference
    const jsonValue = JSON.stringify(value);
    const updatedSet = list.filter(item => jsonValue !== JSON.stringify(item));

    setLocalStorageItem(key, updatedSet);
  }
}
function register(ports, log) {
  // Mapped to Storage API: https://developer.mozilla.org/en-US/docs/Web/API/Storage
  ports.storageGetItem.subscribe(storageGetItem);
  ports.storageSetItem.subscribe(storageSetItem);
  ports.storageRemoveItem.subscribe(storageRemoveItem);
  ports.storageClear.subscribe(storageClear);

  // Not in Storage API
  ports.storagePushToSet.subscribe(storagePushToSet);
  ports.storageRemoveFromSet.subscribe(storageRemoveFromSet);

  // StorageEvent API
  storageEventListenerPorts.push(ports);

  log = log || function() {};

  function storageGetItem(key) {
    log('storageGetItem', key);
    const response = getLocalStorageItem(key);

    log('storageGetItemResponse', key, response);
    ports.storageGetItemResponse.send([key, response]);
  }

  function storageSetItem([key, value]) {
    log('storageSetItem', key, value);
    setLocalStorageItem(key, value);
  }

  function storageRemoveItem(key) {
    log('storageRemoveItem', key);
    window.localStorage.removeItem(key);
  }

  function storageClear() {
    log('storageClear');
    window.localStorage.clear();
  }

  // A Set is a list with only unique values. (No duplication.)
  function storagePushToSet([key, value]) {
    log('storagePushToSet', key, value);

    const item = getLocalStorageItem(key);
    const list = Array.isArray(item) ? item : [];

    if (list.indexOf(value) === -1) {
      list.push(value);
    }

    setLocalStorageItem(key, list);
  }

  function storageRemoveFromSet([key, value]) {
    log('storageRemoveFromSet', key, value);

    const list = getLocalStorageItem(key);

    if (!Array.isArray(list)) {
      log('storageRemoveFromSet [aborting; not a list]', key, value, list);
      return;
    }

    // Filter based on JSON strings in to ensure equality-by-value instead of equality-by-reference
    const jsonValue = JSON.stringify(value);
    const updatedSet = list.filter(item => jsonValue !== JSON.stringify(item));

    setLocalStorageItem(key, updatedSet);
  }
}
Subscribe the given Elm app ports to ports from the Elm LocalStorage ports module.

@param  {Object}   ports  Ports object from an Elm app
@param  {Function} log    Function to log ports for the given Elm app