vue

https://vuejs.org/

https://vuex.vuejs.org/

https://cli.vuejs.org/

applications

i've built some things in vue for work:

https://app.citygro.com

libraries

vdata — sort-of real time wrapper over a REST api

vdata mirror

vdata was later ported to vuex where additional work as done to integrate it with a custom fetch wrapper and a websocket-based pub/sub system. the core concepts that allow this workflow are derived from my study of CRDTs at the time. vdata does not provide a true CRDT, rather something closer to git's rebase. the following is a mostly rolled-up copy of the core algorithm written in ES6, depending only on `lodash`.

/**
 * Copyright 2018 CityGro, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @link https://git.sunshinegardens.org/~xj9/vdata/tree/latest/src/rebase.js
 * @link https://gitlab.com/citygro/vdata/-/blob/latest/LICENSE
 * @link https://archive.is/s034S
 */
import flow from 'lodash/fp/flow'
import isArray from 'lodash/isArray'
import isEqual from 'lodash/isEqual'
import isNil from 'lodash/isNil'
import isObject from 'lodash/isObject'
import mergeWith from 'lodash/mergeWith'
import tail from 'lodash/tail'
import transform from 'lodash/transform'
/**
 * FIXME: jsdoc
 */
const merge = function () {
  const object = arguments[0]
  const sources = tail(arguments)
  return mergeWith(object, ...sources, (objValue, srcValue) => {
    if (isArray(objValue)) {
      return srcValue
    }
  })
}
/**
 * replace all values in an object with `null`. used to generate the ORSet-like
 * for diffing operations.
 *
 * @param {Object} object
 * @return {Object}
 */
const nullify = (object) => transform(object, (result, value, key) => {
  result[key] = (isObject(value) && !isArray(value))
    ? nullify(value)
    : null
})
/**
 * @param {object} base
 * @param {object} object
 * @return {object}
 */
const difference = (base, object) => {
  const changes = (object, base) => {
    return transform(object, (result, value, key) => {
      if (!isEqual(value, base[key])) {
        result[key] = (isObject(value) && isObject(base[key]) && !isArray(value))
          ? changes(value, base[key])
          : value
      }
    })
  }
  return (isNil(base))
    ? object
    : changes(object, base)
}
/**
 * FIXME: jsdoc
 */
const observedRemoveDiff = (base, object) => {
  const diff = difference(base, object)
  const inverseDiff = difference(object, base)
  const nullDiff = nullify(inverseDiff)
  const orDiff = merge({}, nullDiff, diff)
  return orDiff
}
/**
 * FIXME: jsdoc
 */
const jsonClone = flow(JSON.stringify, JSON.parse)
/**
 * @param {Object} base
 * @param {...Object} checkpoints
 * @returns {Object}
 */
const rebase = function () {
  const base = arguments[0]
  const diffs = tail(arguments).map((checkpoint) => observedRemoveDiff(base, checkpoint))
  const patch = merge({}, ...diffs)
  return merge(jsonClone(base), patch)
}