UNPKG

rdb-client

Version:
1,729 lines (1,490 loc) 117 kB
void !function() { typeof self === 'undefined' && typeof global === 'object' ? global.self = global : null; }();var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function getAugmentedNamespace(n) { if (n.__esModule) return n; var a = Object.defineProperty({}, '__esModule', {value: true}); Object.keys(n).forEach(function (k) { var d = Object.getOwnPropertyDescriptor(n, k); Object.defineProperty(a, k, d.get ? d : { enumerable: true, get: function () { return n[k]; } }); }); return a; } const PATH_SEPARATOR = '.'; const TARGET = Symbol('target'); const UNSUBSCRIBE = Symbol('unsubscribe'); function isBuiltinWithMutableMethods(value) { return value instanceof Date || value instanceof Set || value instanceof Map || value instanceof WeakSet || value instanceof WeakMap || ArrayBuffer.isView(value); } function isBuiltinWithoutMutableMethods(value) { return (typeof value === 'object' ? value === null : typeof value !== 'function') || value instanceof RegExp; } var isArray = Array.isArray; function isSymbol(value) { return typeof value === 'symbol'; } const path = { after: (path, subPath) => { if (isArray(path)) { return path.slice(subPath.length); } if (subPath === '') { return path; } return path.slice(subPath.length + 1); }, concat: (path, key) => { if (isArray(path)) { path = [...path]; if (key) { path.push(key); } return path; } if (key && key.toString !== undefined) { if (path !== '') { path += PATH_SEPARATOR; } if (isSymbol(key)) { return path + key.toString(); } return path + key; } return path; }, initial: path => { if (isArray(path)) { return path.slice(0, -1); } if (path === '') { return path; } const index = path.lastIndexOf(PATH_SEPARATOR); if (index === -1) { return ''; } return path.slice(0, index); }, last: path => { if (isArray(path)) { return path[path.length - 1] || ''; } if (path === '') { return path; } const index = path.lastIndexOf(PATH_SEPARATOR); if (index === -1) { return path; } return path.slice(index + 1); }, walk: (path, callback) => { if (isArray(path)) { for (const key of path) { callback(key); } } else if (path !== '') { let position = 0; let index = path.indexOf(PATH_SEPARATOR); if (index === -1) { callback(path); } else { while (position < path.length) { if (index === -1) { index = path.length; } callback(path.slice(position, index)); position = index + 1; index = path.indexOf(PATH_SEPARATOR, position); } } } }, get(object, path) { this.walk(path, key => { if (object) { object = object[key]; } }); return object; }, }; function isIterator(value) { return typeof value === 'object' && typeof value.next === 'function'; } // eslint-disable-next-line max-params function wrapIterator(iterator, target, thisArg, applyPath, prepareValue) { const originalNext = iterator.next; if (target.name === 'entries') { iterator.next = function () { const result = originalNext.call(this); if (result.done === false) { result.value[0] = prepareValue( result.value[0], target, result.value[0], applyPath, ); result.value[1] = prepareValue( result.value[1], target, result.value[0], applyPath, ); } return result; }; } else if (target.name === 'values') { const keyIterator = thisArg[TARGET].keys(); iterator.next = function () { const result = originalNext.call(this); if (result.done === false) { result.value = prepareValue( result.value, target, keyIterator.next().value, applyPath, ); } return result; }; } else { iterator.next = function () { const result = originalNext.call(this); if (result.done === false) { result.value = prepareValue( result.value, target, result.value, applyPath, ); } return result; }; } return iterator; } function ignoreProperty(cache, options, property) { return cache.isUnsubscribed || (options.ignoreSymbols && isSymbol(property)) || (options.ignoreUnderscores && property.charAt(0) === '_') || ('ignoreKeys' in options && options.ignoreKeys.includes(property)); } /** @class Cache @private */ class Cache { constructor(equals) { this._equals = equals; this._proxyCache = new WeakMap(); this._pathCache = new WeakMap(); this.isUnsubscribed = false; } _getDescriptorCache() { if (this._descriptorCache === undefined) { this._descriptorCache = new WeakMap(); } return this._descriptorCache; } _getProperties(target) { const descriptorCache = this._getDescriptorCache(); let properties = descriptorCache.get(target); if (properties === undefined) { properties = {}; descriptorCache.set(target, properties); } return properties; } _getOwnPropertyDescriptor(target, property) { if (this.isUnsubscribed) { return Reflect.getOwnPropertyDescriptor(target, property); } const properties = this._getProperties(target); let descriptor = properties[property]; if (descriptor === undefined) { descriptor = Reflect.getOwnPropertyDescriptor(target, property); properties[property] = descriptor; } return descriptor; } getProxy(target, path, handler, proxyTarget) { if (this.isUnsubscribed) { return target; } const reflectTarget = target[proxyTarget]; const source = reflectTarget || target; this._pathCache.set(source, path); let proxy = this._proxyCache.get(source); if (proxy === undefined) { proxy = reflectTarget === undefined ? new Proxy(target, handler) : target; this._proxyCache.set(source, proxy); } return proxy; } getPath(target) { return this.isUnsubscribed ? undefined : this._pathCache.get(target); } isDetached(target, object) { return !Object.is(target, path.get(object, this.getPath(target))); } defineProperty(target, property, descriptor) { if (!Reflect.defineProperty(target, property, descriptor)) { return false; } if (!this.isUnsubscribed) { this._getProperties(target)[property] = descriptor; } return true; } setProperty(target, property, value, receiver, previous) { // eslint-disable-line max-params if (!this._equals(previous, value) || !(property in target)) { const descriptor = this._getOwnPropertyDescriptor(target, property); if (descriptor !== undefined && 'set' in descriptor) { return Reflect.set(target, property, value, receiver); } return Reflect.set(target, property, value); } return true; } deleteProperty(target, property, previous) { if (Reflect.deleteProperty(target, property)) { if (!this.isUnsubscribed) { const properties = this._getDescriptorCache().get(target); if (properties) { delete properties[property]; this._pathCache.delete(previous); } } return true; } return false; } isSameDescriptor(a, target, property) { const b = this._getOwnPropertyDescriptor(target, property); return a !== undefined && b !== undefined && Object.is(a.value, b.value) && (a.writable || false) === (b.writable || false) && (a.enumerable || false) === (b.enumerable || false) && (a.configurable || false) === (b.configurable || false) && a.get === b.get && a.set === b.set; } isGetInvariant(target, property) { const descriptor = this._getOwnPropertyDescriptor(target, property); return descriptor !== undefined && descriptor.configurable !== true && descriptor.writable !== true; } unsubscribe() { this._descriptorCache = null; this._pathCache = null; this._proxyCache = null; this.isUnsubscribed = true; } } function isObject(value) { return toString.call(value) === '[object Object]'; } function isDiffCertain() { return true; } function isDiffArrays(clone, value) { return clone.length !== value.length || clone.some((item, index) => value[index] !== item); } const IMMUTABLE_OBJECT_METHODS = new Set([ 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf', ]); const IMMUTABLE_ARRAY_METHODS = new Set([ 'concat', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', ]); const MUTABLE_ARRAY_METHODS = { push: isDiffCertain, pop: isDiffCertain, shift: isDiffCertain, unshift: isDiffCertain, copyWithin: isDiffArrays, reverse: isDiffArrays, sort: isDiffArrays, splice: isDiffArrays, flat: isDiffArrays, fill: isDiffArrays, }; const HANDLED_ARRAY_METHODS = new Set([ ...IMMUTABLE_OBJECT_METHODS, ...IMMUTABLE_ARRAY_METHODS, ...Object.keys(MUTABLE_ARRAY_METHODS), ]); function isDiffSets(clone, value) { if (clone.size !== value.size) { return true; } for (const element of clone) { if (!value.has(element)) { return true; } } return false; } const COLLECTION_ITERATOR_METHODS = [ 'keys', 'values', 'entries', ]; const IMMUTABLE_SET_METHODS = new Set([ 'has', 'toString', ]); const MUTABLE_SET_METHODS = { add: isDiffSets, clear: isDiffSets, delete: isDiffSets, forEach: isDiffSets, }; const HANDLED_SET_METHODS = new Set([ ...IMMUTABLE_SET_METHODS, ...Object.keys(MUTABLE_SET_METHODS), ...COLLECTION_ITERATOR_METHODS, ]); function isDiffMaps(clone, value) { if (clone.size !== value.size) { return true; } let bValue; for (const [key, aValue] of clone) { bValue = value.get(key); if (bValue !== aValue || (bValue === undefined && !value.has(key))) { return true; } } return false; } const IMMUTABLE_MAP_METHODS = new Set([...IMMUTABLE_SET_METHODS, 'get']); const MUTABLE_MAP_METHODS = { set: isDiffMaps, clear: isDiffMaps, delete: isDiffMaps, forEach: isDiffMaps, }; const HANDLED_MAP_METHODS = new Set([ ...IMMUTABLE_MAP_METHODS, ...Object.keys(MUTABLE_MAP_METHODS), ...COLLECTION_ITERATOR_METHODS, ]); class CloneObject { constructor(value, path, argumentsList, hasOnValidate) { this._path = path; this._isChanged = false; this._clonedCache = new Set(); this._hasOnValidate = hasOnValidate; this._changes = hasOnValidate ? [] : null; this.clone = path === undefined ? value : this._shallowClone(value); } static isHandledMethod(name) { return IMMUTABLE_OBJECT_METHODS.has(name); } _shallowClone(value) { let clone = value; if (isObject(value)) { clone = {...value}; } else if (isArray(value)) { clone = [...value]; } else if (value instanceof Date) { clone = new Date(value); } else if (value instanceof Set) { clone = new Set([...value].map(item => this._shallowClone(item))); } else if (value instanceof Map) { clone = new Map(); for (const [key, item] of value.entries()) { clone.set(key, this._shallowClone(item)); } } this._clonedCache.add(clone); return clone; } preferredThisArg(isHandledMethod, name, thisArg, thisProxyTarget) { if (isHandledMethod) { if (isArray(thisProxyTarget)) { this._onIsChanged = MUTABLE_ARRAY_METHODS[name]; } else if (thisProxyTarget instanceof Set) { this._onIsChanged = MUTABLE_SET_METHODS[name]; } else if (thisProxyTarget instanceof Map) { this._onIsChanged = MUTABLE_MAP_METHODS[name]; } return thisProxyTarget; } return thisArg; } update(fullPath, property, value) { const changePath = path.after(fullPath, this._path); if (property !== 'length') { let object = this.clone; path.walk(changePath, key => { if (object && object[key]) { if (!this._clonedCache.has(object[key])) { object[key] = this._shallowClone(object[key]); } object = object[key]; } }); if (this._hasOnValidate) { this._changes.push({ path: changePath, property, previous: value, }); } if (object && object[property]) { object[property] = value; } } this._isChanged = true; } undo(object) { let change; for (let index = this._changes.length - 1; index !== -1; index--) { change = this._changes[index]; path.get(object, change.path)[change.property] = change.previous; } } isChanged(value) { return this._onIsChanged === undefined ? this._isChanged : this._onIsChanged(this.clone, value); } } class CloneArray extends CloneObject { static isHandledMethod(name) { return HANDLED_ARRAY_METHODS.has(name); } } class CloneDate extends CloneObject { undo(object) { object.setTime(this.clone.getTime()); } isChanged(value, equals) { return !equals(this.clone.valueOf(), value.valueOf()); } } class CloneSet extends CloneObject { static isHandledMethod(name) { return HANDLED_SET_METHODS.has(name); } undo(object) { for (const value of this.clone) { object.add(value); } for (const value of object) { if (!this.clone.has(value)) { object.delete(value); } } } } class CloneMap extends CloneObject { static isHandledMethod(name) { return HANDLED_MAP_METHODS.has(name); } undo(object) { for (const [key, value] of this.clone.entries()) { object.set(key, value); } for (const key of object.keys()) { if (!this.clone.has(key)) { object.delete(key); } } } } class CloneWeakSet extends CloneObject { constructor(value, path, argumentsList, hasOnValidate) { super(undefined, path, argumentsList, hasOnValidate); this._arg1 = argumentsList[0]; this._weakValue = value.has(this._arg1); } isChanged(value) { return this._weakValue !== value.has(this._arg1); } undo(object) { if (this._weakValue && !object.has(this._arg1)) { object.add(this._arg1); } else { object.delete(this._arg1); } } } class CloneWeakMap extends CloneObject { constructor(value, path, argumentsList, hasOnValidate) { super(undefined, path, argumentsList, hasOnValidate); this._weakKey = argumentsList[0]; this._weakHas = value.has(this._weakKey); this._weakValue = value.get(this._weakKey); } isChanged(value) { return this._weakValue !== value.get(this._weakKey); } undo(object) { const weakHas = object.has(this._weakKey); if (this._weakHas && !weakHas) { object.set(this._weakKey, this._weakValue); } else if (!this._weakHas && weakHas) { object.delete(this._weakKey); } else if (this._weakValue !== object.get(this._weakKey)) { object.set(this._weakKey, this._weakValue); } } } class SmartClone { constructor(hasOnValidate) { this._stack = []; this._hasOnValidate = hasOnValidate; } static isHandledType(value) { return isObject(value) || isArray(value) || isBuiltinWithMutableMethods(value); } static isHandledMethod(target, name) { if (isObject(target)) { return CloneObject.isHandledMethod(name); } if (isArray(target)) { return CloneArray.isHandledMethod(name); } if (target instanceof Set) { return CloneSet.isHandledMethod(name); } if (target instanceof Map) { return CloneMap.isHandledMethod(name); } return isBuiltinWithMutableMethods(target); } get isCloning() { return this._stack.length > 0; } start(value, path, argumentsList) { let CloneClass = CloneObject; if (isArray(value)) { CloneClass = CloneArray; } else if (value instanceof Date) { CloneClass = CloneDate; } else if (value instanceof Set) { CloneClass = CloneSet; } else if (value instanceof Map) { CloneClass = CloneMap; } else if (value instanceof WeakSet) { CloneClass = CloneWeakSet; } else if (value instanceof WeakMap) { CloneClass = CloneWeakMap; } this._stack.push(new CloneClass(value, path, argumentsList, this._hasOnValidate)); } update(fullPath, property, value) { this._stack[this._stack.length - 1].update(fullPath, property, value); } preferredThisArg(target, thisArg, thisProxyTarget) { const {name} = target; const isHandledMethod = SmartClone.isHandledMethod(thisProxyTarget, name); return this._stack[this._stack.length - 1] .preferredThisArg(isHandledMethod, name, thisArg, thisProxyTarget); } isChanged(isMutable, value, equals) { return this._stack[this._stack.length - 1].isChanged(isMutable, value, equals); } undo(object) { if (this._previousClone !== undefined) { this._previousClone.undo(object); } } stop() { this._previousClone = this._stack.pop(); return this._previousClone.clone; } } /* eslint-disable unicorn/prefer-spread */ const defaultOptions = { equals: Object.is, isShallow: false, pathAsArray: false, ignoreSymbols: false, ignoreUnderscores: false, ignoreDetached: false, details: false, }; const onChange$1 = (object, onChange, options = {}) => { options = { ...defaultOptions, ...options, }; const proxyTarget = Symbol('ProxyTarget'); const {equals, isShallow, ignoreDetached, details} = options; const cache = new Cache(equals); const hasOnValidate = typeof options.onValidate === 'function'; const smartClone = new SmartClone(hasOnValidate); // eslint-disable-next-line max-params const validate = (target, property, value, previous, applyData) => !hasOnValidate || smartClone.isCloning || options.onValidate(path.concat(cache.getPath(target), property), value, previous, applyData) === true; const handleChangeOnTarget = (target, property, value, previous) => { if ( !ignoreProperty(cache, options, property) && !(ignoreDetached && cache.isDetached(target, object)) ) { handleChange(cache.getPath(target), property, value, previous); } }; // eslint-disable-next-line max-params const handleChange = (changePath, property, value, previous, applyData) => { if (smartClone.isCloning) { smartClone.update(changePath, property, previous); } else { onChange(path.concat(changePath, property), value, previous, applyData); } }; const getProxyTarget = value => value ? (value[proxyTarget] || value) : value; const prepareValue = (value, target, property, basePath) => { if ( isBuiltinWithoutMutableMethods(value) || property === 'constructor' || (isShallow && !SmartClone.isHandledMethod(target, property)) || ignoreProperty(cache, options, property) || cache.isGetInvariant(target, property) || (ignoreDetached && cache.isDetached(target, object)) ) { return value; } if (basePath === undefined) { basePath = cache.getPath(target); } return cache.getProxy(value, path.concat(basePath, property), handler, proxyTarget); }; const handler = { get(target, property, receiver) { if (isSymbol(property)) { if (property === proxyTarget || property === TARGET) { return target; } if ( property === UNSUBSCRIBE && !cache.isUnsubscribed && cache.getPath(target).length === 0 ) { cache.unsubscribe(); return target; } } const value = isBuiltinWithMutableMethods(target) ? Reflect.get(target, property) : Reflect.get(target, property, receiver); return prepareValue(value, target, property); }, set(target, property, value, receiver) { value = getProxyTarget(value); const reflectTarget = target[proxyTarget] || target; const previous = reflectTarget[property]; if (equals(previous, value) && property in target) { return true; } const isValid = validate(target, property, value, previous); if ( isValid && cache.setProperty(reflectTarget, property, value, receiver, previous) ) { handleChangeOnTarget(target, property, target[property], previous); return true; } return !isValid; }, defineProperty(target, property, descriptor) { if (!cache.isSameDescriptor(descriptor, target, property)) { const previous = target[property]; if ( validate(target, property, descriptor.value, previous) && cache.defineProperty(target, property, descriptor, previous) ) { handleChangeOnTarget(target, property, descriptor.value, previous); } } return true; }, deleteProperty(target, property) { if (!Reflect.has(target, property)) { return true; } const previous = Reflect.get(target, property); const isValid = validate(target, property, undefined, previous); if ( isValid && cache.deleteProperty(target, property, previous) ) { handleChangeOnTarget(target, property, undefined, previous); return true; } return !isValid; }, apply(target, thisArg, argumentsList) { const thisProxyTarget = thisArg[proxyTarget] || thisArg; if (cache.isUnsubscribed) { return Reflect.apply(target, thisProxyTarget, argumentsList); } if ( (details === false || (details !== true && !details.includes(target.name))) && SmartClone.isHandledType(thisProxyTarget) ) { let applyPath = path.initial(cache.getPath(target)); const isHandledMethod = SmartClone.isHandledMethod(thisProxyTarget, target.name); smartClone.start(thisProxyTarget, applyPath, argumentsList); let result = Reflect.apply( target, smartClone.preferredThisArg(target, thisArg, thisProxyTarget), isHandledMethod ? argumentsList.map(argument => getProxyTarget(argument)) : argumentsList, ); const isChanged = smartClone.isChanged(thisProxyTarget, equals); const previous = smartClone.stop(); if (SmartClone.isHandledType(result) && isHandledMethod) { if (thisArg instanceof Map && target.name === 'get') { applyPath = path.concat(applyPath, argumentsList[0]); } result = cache.getProxy(result, applyPath, handler); } if (isChanged) { const applyData = { name: target.name, args: argumentsList, result, }; const changePath = smartClone.isCloning ? path.initial(applyPath) : applyPath; const property = smartClone.isCloning ? path.last(applyPath) : ''; if (validate(path.get(object, changePath), property, thisProxyTarget, previous, applyData)) { handleChange(changePath, property, thisProxyTarget, previous, applyData); } else { smartClone.undo(thisProxyTarget); } } if ( (thisArg instanceof Map || thisArg instanceof Set) && isIterator(result) ) { return wrapIterator(result, target, thisArg, applyPath, prepareValue); } return result; } return Reflect.apply(target, thisArg, argumentsList); }, }; const proxy = cache.getProxy(object, options.pathAsArray ? [] : '', handler); onChange = onChange.bind(proxy); if (hasOnValidate) { options.onValidate = options.onValidate.bind(proxy); // eslint-disable-line unicorn/prefer-prototype-methods } return proxy; }; onChange$1.target = proxy => (proxy && proxy[TARGET]) || proxy; onChange$1.unsubscribe = proxy => proxy[UNSUBSCRIBE] || proxy; var onChange_1 = onChange$1; var rfc6902 = {}; var pointer = {}; Object.defineProperty(pointer, "__esModule", { value: true }); pointer.Pointer = void 0; /** Unescape token part of a JSON Pointer string `token` should *not* contain any '/' characters. > Evaluation of each reference token begins by decoding any escaped > character sequence. This is performed by first transforming any > occurrence of the sequence '~1' to '/', and then transforming any > occurrence of the sequence '~0' to '~'. By performing the > substitutions in this order, an implementation avoids the error of > turning '~01' first into '~1' and then into '/', which would be > incorrect (the string '~01' correctly becomes '~1' after > transformation). Here's my take: ~1 is unescaped with higher priority than ~0 because it is a lower-order escape character. I say "lower order" because '/' needs escaping due to the JSON Pointer serialization technique. Whereas, '~' is escaped because escaping '/' uses the '~' character. */ function unescape$1(token) { return token.replace(/~1/g, '/').replace(/~0/g, '~'); } /** Escape token part of a JSON Pointer string > '~' needs to be encoded as '~0' and '/' > needs to be encoded as '~1' when these characters appear in a > reference token. This is the exact inverse of `unescape()`, so the reverse replacements must take place in reverse order. */ function escape(token) { return token.replace(/~/g, '~0').replace(/\//g, '~1'); } /** JSON Pointer representation */ var Pointer = /** @class */ (function () { function Pointer(tokens) { if (tokens === void 0) { tokens = ['']; } this.tokens = tokens; } /** `path` *must* be a properly escaped string. */ Pointer.fromJSON = function (path) { var tokens = path.split('/').map(unescape$1); if (tokens[0] !== '') throw new Error("Invalid JSON Pointer: " + path); return new Pointer(tokens); }; Pointer.prototype.toString = function () { return this.tokens.map(escape).join('/'); }; /** Returns an object with 'parent', 'key', and 'value' properties. In the special case that this Pointer's path == "", this object will be {parent: null, key: '', value: object}. Otherwise, parent and key will have the property such that parent[key] == value. */ Pointer.prototype.evaluate = function (object) { var parent = null; var key = ''; var value = object; for (var i = 1, l = this.tokens.length; i < l; i++) { parent = value; key = this.tokens[i]; // not sure if this the best way to handle non-existant paths... value = (parent || {})[key]; } return { parent: parent, key: key, value: value }; }; Pointer.prototype.get = function (object) { return this.evaluate(object).value; }; Pointer.prototype.set = function (object, value) { var cursor = object; for (var i = 1, l = this.tokens.length - 1, token = this.tokens[i]; i < l; i++) { // not sure if this the best way to handle non-existant paths... cursor = (cursor || {})[token]; } if (cursor) { cursor[this.tokens[this.tokens.length - 1]] = value; } }; Pointer.prototype.push = function (token) { // mutable this.tokens.push(token); }; /** `token` should be a String. It'll be coerced to one anyway. immutable (shallowly) */ Pointer.prototype.add = function (token) { var tokens = this.tokens.concat(String(token)); return new Pointer(tokens); }; return Pointer; }()); pointer.Pointer = Pointer; var patch = {}; var util = {}; (function (exports) { Object.defineProperty(exports, "__esModule", { value: true }); exports.clone = exports.objectType = exports.hasOwnProperty = void 0; exports.hasOwnProperty = Object.prototype.hasOwnProperty; function objectType(object) { if (object === undefined) { return 'undefined'; } if (object === null) { return 'null'; } if (Array.isArray(object)) { return 'array'; } return typeof object; } exports.objectType = objectType; /** Recursively copy a value. @param source - should be a JavaScript primitive, Array, or (plain old) Object. @returns copy of source where every Array and Object have been recursively reconstructed from their constituent elements */ function clone(source) { // loose-equality checking for null is faster than strict checking for each of null/undefined/true/false // checking null first, then calling typeof, is faster than vice-versa if (source == null || typeof source != 'object') { // short-circuiting is faster than a single return return source; } // x.constructor == Array is the fastest way to check if x is an Array if (source.constructor == Array) { // construction via imperative for-loop is faster than source.map(arrayVsObject) var length_1 = source.length; // setting the Array length during construction is faster than just `[]` or `new Array()` var arrayTarget = new Array(length_1); for (var i = 0; i < length_1; i++) { arrayTarget[i] = clone(source[i]); } return arrayTarget; } // Object var objectTarget = {}; // declaring the variable (with const) inside the loop is faster for (var key in source) { // hasOwnProperty costs a bit of performance, but it's semantically necessary // using a global helper is MUCH faster than calling source.hasOwnProperty(key) if (exports.hasOwnProperty.call(source, key)) { objectTarget[key] = clone(source[key]); } } return objectTarget; } exports.clone = clone; }(util)); var equal = {}; Object.defineProperty(equal, "__esModule", { value: true }); equal.compare = void 0; var util_1$2 = util; /** Evaluate `left === right`, treating `left` and `right` as ordered lists. @returns true iff `left` and `right` have identical lengths, and every element of `left` is equal to the corresponding element of `right`. Equality is determined recursivly, via `compare`. */ function compareArrays(left, right) { var length = left.length; if (length !== right.length) { return false; } for (var i = 0; i < length; i++) { if (!compare(left[i], right[i])) { return false; } } return true; } /** Evaluate `left === right`, treating `left` and `right` as property maps. @returns true iff every property in `left` has a value equal to the value of the corresponding property in `right`, and vice-versa, stopping as soon as possible. Equality is determined recursivly, via `compare`. */ function compareObjects(left, right) { var left_keys = Object.keys(left); var right_keys = Object.keys(right); var length = left_keys.length; // quick exit if the number of keys don't match up if (length !== right_keys.length) { return false; } // we don't know for sure that Set(left_keys) is equal to Set(right_keys), // much less that their values in left and right are equal, but if right // contains each key in left, we know it can't have any additional keys for (var i = 0; i < length; i++) { var key = left_keys[i]; if (!util_1$2.hasOwnProperty.call(right, key) || !compare(left[key], right[key])) { return false; } } return true; } /** `compare()` returns true if `left` and `right` are materially equal (i.e., would produce equivalent JSON), false otherwise. > Here, "equal" means that the value at the target location and the > value conveyed by "value" are of the same JSON type, and that they > are considered equal by the following rules for that type: > o strings: are considered equal if they contain the same number of > Unicode characters and their code points are byte-by-byte equal. > o numbers: are considered equal if their values are numerically > equal. > o arrays: are considered equal if they contain the same number of > values, and if each value can be considered equal to the value at > the corresponding position in the other array, using this list of > type-specific rules. > o objects: are considered equal if they contain the same number of > members, and if each member can be considered equal to a member in > the other object, by comparing their keys (as strings) and their > values (using this list of type-specific rules). > o literals (false, true, and null): are considered equal if they are > the same. */ function compare(left, right) { // strict equality handles literals, numbers, and strings (a sufficient but not necessary cause) if (left === right) { return true; } var left_type = util_1$2.objectType(left); var right_type = util_1$2.objectType(right); // check arrays if (left_type == 'array' && right_type == 'array') { return compareArrays(left, right); } // check objects if (left_type == 'object' && right_type == 'object') { return compareObjects(left, right); } // mismatched arrays & objects, etc., are always inequal return false; } equal.compare = compare; var __extends = (commonjsGlobal && commonjsGlobal.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); Object.defineProperty(patch, "__esModule", { value: true }); patch.apply = patch.InvalidOperationError = patch.test = patch.copy = patch.move = patch.replace = patch.remove = patch.add = patch.TestError = patch.MissingError = void 0; var pointer_1$1 = pointer; var util_1$1 = util; var equal_1$1 = equal; var MissingError = /** @class */ (function (_super) { __extends(MissingError, _super); function MissingError(path) { var _this = _super.call(this, "Value required at path: " + path) || this; _this.path = path; _this.name = 'MissingError'; return _this; } return MissingError; }(Error)); patch.MissingError = MissingError; var TestError = /** @class */ (function (_super) { __extends(TestError, _super); function TestError(actual, expected) { var _this = _super.call(this, "Test failed: " + actual + " != " + expected) || this; _this.actual = actual; _this.expected = expected; _this.name = 'TestError'; return _this; } return TestError; }(Error)); patch.TestError = TestError; function _add(object, key, value) { if (Array.isArray(object)) { // `key` must be an index if (key == '-') { object.push(value); } else { var index = parseInt(key, 10); object.splice(index, 0, value); } } else { object[key] = value; } } function _remove(object, key) { if (Array.isArray(object)) { // '-' syntax doesn't make sense when removing var index = parseInt(key, 10); object.splice(index, 1); } else { // not sure what the proper behavior is when path = '' delete object[key]; } } /** > o If the target location specifies an array index, a new value is > inserted into the array at the specified index. > o If the target location specifies an object member that does not > already exist, a new member is added to the object. > o If the target location specifies an object member that does exist, > that member's value is replaced. */ function add(object, operation) { var endpoint = pointer_1$1.Pointer.fromJSON(operation.path).evaluate(object); // it's not exactly a "MissingError" in the same way that `remove` is -- more like a MissingParent, or something if (endpoint.parent === undefined) { return new MissingError(operation.path); } _add(endpoint.parent, endpoint.key, util_1$1.clone(operation.value)); return null; } patch.add = add; /** > The "remove" operation removes the value at the target location. > The target location MUST exist for the operation to be successful. */ function remove(object, operation) { // endpoint has parent, key, and value properties var endpoint = pointer_1$1.Pointer.fromJSON(operation.path).evaluate(object); if (endpoint.value === undefined) { return new MissingError(operation.path); } // not sure what the proper behavior is when path = '' _remove(endpoint.parent, endpoint.key); return null; } patch.remove = remove; /** > The "replace" operation replaces the value at the target location > with a new value. The operation object MUST contain a "value" member > whose content specifies the replacement value. > The target location MUST exist for the operation to be successful. > This operation is functionally identical to a "remove" operation for > a value, followed immediately by an "add" operation at the same > location with the replacement value. Even more simply, it's like the add operation with an existence check. */ function replace(object, operation) { var endpoint = pointer_1$1.Pointer.fromJSON(operation.path).evaluate(object); if (endpoint.parent === null) { return new MissingError(operation.path); } // this existence check treats arrays as a special case if (Array.isArray(endpoint.parent)) { if (parseInt(endpoint.key, 10) >= endpoint.parent.length) { return new MissingError(operation.path); } } else if (endpoint.value === undefined) { return new MissingError(operation.path); } endpoint.parent[endpoint.key] = operation.value; return null; } patch.replace = replace; /** > The "move" operation removes the value at a specified location and > adds it to the target location. > The operation object MUST contain a "from" member, which is a string > containing a JSON Pointer value that references the location in the > target document to move the value from. > This operation is functionally identical to a "remove" operation on > the "from" location, followed immediately by an "add" operation at > the target location with the value that was just removed. > The "from" location MUST NOT be a proper prefix of the "path" > location; i.e., a location cannot be moved into one of its children. TODO: throw if the check described in the previous paragraph fails. */ function move(object, operation) { var from_endpoint = pointer_1$1.Pointer.fromJSON(operation.from).evaluate(object); if (from_endpoint.value === undefined) { return new MissingError(operation.from); } var endpoint = pointer_1$1.Pointer.fromJSON(operation.path).evaluate(object); if (endpoint.parent === undefined) { return new MissingError(operation.path); } _remove(from_endpoint.parent, from_endpoint.key); _add(endpoint.parent, endpoint.key, from_endpoint.value); return null; } patch.move = move; /** > The "copy" operation copies the value at a specified location to the > target location. > The operation object MUST contain a "from" member, which is a string > containing a JSON Pointer value that references the location in the > target document to copy the value from. > The "from" location MUST exist for the operation to be successful. > This operation is functionally identical to an "add" operation at the > target location using the value specified in the "from" member. Alternatively, it's like 'move' without the 'remove'. */ function copy(object, operation) { var from_endpoint = pointer_1$1.Pointer.fromJSON(operation.from).evaluate(object); if (from_endpoint.value === undefined) { return new MissingError(operation.from); } var endpoint = pointer_1$1.Pointer.fromJSON(operation.path).evaluate(object); if (endpoint.parent === undefined) { return new MissingError(operation.path); } _add(endpoint.parent, endpoint.key, util_1$1.clone(from_endpoint.value)); return null; } patch.copy = copy; /** > The "test" operation tests that a value at the target location is > equal to a specified value. > The operation object MUST contain a "value" member that conveys the > value to be compared to the target location's value. > The target location MUST be equal to the "value" value for the > operation to be considered successful. */ function test(object, operation) { var endpoint = pointer_1$1.Pointer.fromJSON(operation.path).evaluate(object); var result = equal_1$1.compare(endpoint.value, operation.value); if (!result) { return new TestError(endpoint.value, operation.value); } return null; } patch.test = test; var InvalidOperationError = /** @class */ (function (_super) { __extends(InvalidOperationError, _super); function InvalidOperationError(operation) { var _this = _super.call(this, "Invalid operation: " + operation.op) || this; _this.operation = operation; _this.name = 'InvalidOperationError'; return _this; } return InvalidOperationError; }(Error)); patch.InvalidOperationError = InvalidOperationError; /** Switch on `operation.op`, applying the corresponding patch function for each case to `object`. */ function apply(object, operation) { // not sure why TypeScript can't infer typesafety of: // {add, remove, replace, move, copy, test}[operation.op](object, operation) // (seems like a bug) switch (operation.op) { case 'add': return add(object, operation); case 'remove': return remove(object, operation); case 'replace': return replace(object, operation); case 'move': return move(object, operation); case 'copy': return copy(object, operation); case 'test': return test(object, operation); } return new InvalidOperationError(operation); } patch.apply = apply; var diff = {}; Object.defineProperty(diff, "__esModule", { value: true }); diff.diffAny = diff.diffObjects = diff.diffArrays = diff.intersection = diff.subtract = diff.isDestructive = void 0; var equal_1 = equal; var util_1 = util; function isDestructive(_a) { var op = _a.op; return op === 'remove' || op === 'replace' || op === 'copy' || op === 'move'; } diff.isDestructive = isDestructive; /** List the keys in `minuend` that are not in `subtrahend`. A key is only considered if it is both 1) an own-property (o.hasOwnProperty(k)) of the object, and 2) has a value that is not undefined. This is to match JSON semantics, where JSON object serialization drops keys with undefined values. @param minuend Object of interest @param subtrahend Object of comparison @returns Array of keys that are in `minuend` but not in `subtrahend`. */ function subtract(minuend, subtrahend) { // initialize empty object; we only care about the keys, the values can be anything var obj = {}; // build up obj with all the properties of minuend for (var add_key in minuend) { if (util_1.hasOwnProperty.call(minuend, add_key) && minuend[add_key] !== undefined) { obj[add_key] = 1; } } // now delete all the properties of subtrahend from obj // (deleting a missing key has no effect) for (var del_key in subtrahend) { if (util_1.hasOwnProperty.call(subtrahend, del_key) && subtrahend[del_key] !== undefined) { delete obj[del_key]; } } // finally, extract whatever keys remain in obj return Object.keys(obj); } diff.subtract = subtract; /** List the keys that shared by all `objects`. The semantics of what constitutes a "key" is described in {@link subtract}. @param objects Array of objects to compare @returns Array of keys that are in ("own-properties" of) every object in `objects`. */ function intersection(objects) { var length = objects.length; // prepare empty counter to keep track of how many objects each key occurred in var counter = {}; // go through each object and increment the counter for each key in that object for (var i = 0; i < length; i++) { var object = objects[i]; for (var key in object) { if (util_1.hasOwnProperty.call(object, key) && object[key] !== undefined) { counter[key] = (counter[key] || 0) + 1; } } } // now delete all keys from the counter that were not seen in every object for (var key in counter) { if (counter[key] < length) { delete counter[key]; } } // finally, extract whatever keys remain in the counter return Object.keys(counter); } diff.intersection = intersection; function isArrayAdd(array_operation) { return array_operation.op === 'add'; } function isArrayRemove(array_operation) { return array_operation.op === 'remove'; } function appendArrayOperation(base, operation) { return { // the new operation must be pushed on the end operations: base.operations.concat(operation), cost: base.cost + 1, }; } /** Calculate the shortest sequence of operations to get from `input` to `output`, using a dynamic programming implementation of the Levenshtein distance algorithm. To get from the input ABC to the output AZ we could just delete all the input and say "insert A, insert Z" and be done with it. That's what we do if the input is empty. But we can be smarter. output A Z - - [0] 1 2 input A | 1 [0] 1 B | 2 [1] 1 C | 3 2 [2] 1) start at 0,0 (+0) 2) keep A (+0) 3) remove B (+1) 4) replace C with Z (+1) If the `input` (source) is empty, they'll all be in the top row, resulting in an array of 'add' operations. If the `output` (target) is empty, everything will be in the left column, resulting in an array of 'remove' operations. @returns A list of add/remove/replace operations. */ function diffArrays(input, output, ptr, diff) { if (diff === void 0) { diff = diffAny; } // set up cost matrix (very simple initialization: just a map) var memo = { '0,0': { operations: [], cost: 0 }, }; /** Calculate the cheapest sequence of operations required to get from input.slice(0, i) to output.slice(0, j). There may be other valid sequences with the same cost, but none cheaper. @param i The row in the layout above @param j The column in the layout above @returns An object containing a list of operations, along with the total cost of applying them (+1 for each add/remove/replace operation) */ function dist(i, j) { // memoized var memo_key = i + "," + j; var memoized = memo[memo_key]; if (memoized === undefined) { if (i > 0 && j > 0 && equal_1.compare(input[i - 1], output[j - 1])) { // equal (no operations => no cost) memoized = dist(i - 1, j - 1); } else { var alternatives = []; if (i > 0) { // NOT topmost row var remove_base = dist(i - 1, j); var remove_operation = { op: 'remove', index: i - 1, }; alternatives.push(appendArrayOperation(remove_base, remove_operation)); } if (j > 0) { // NOT leftmost column var add_base = dist(i, j - 1); var add_operation = { op: 'add', index: i - 1, value: output[j - 1], }; alternatives.push(appendArrayOperation(add_base, add_operation)); } if (i > 0 && j > 0) { // TABLE MIDDLE // supposing we replaced it, compute the rest of the costs: var replace_base = dist(i - 1, j - 1); // okay, the general plan is to replace it, but we can be smarter, // recursing into the structure and replacing only part of it if // possible, but to do so we'll need the original value var replace_operation = { op: 'replace', index: i - 1, original: input[i - 1], value: output[j - 1], }; alternatives.push(appendArrayOperation(replace_base, replace_operation)); } // the only other case, i === 0 && j === 0, has already been memoized // the meat of the algorithm: // sort by cost to find the lowest one (might be several ties for lowest) // [4, 6, 7, 1, 2].sort((a, b) => a - b) -> [ 1, 2, 4, 6, 7 ] var best = alternatives.sort(function (a, b) { return a.cost - b.cost; })[0]; memoized = best; } memo[memo_key] = memoized; } return memoized; } // handle weird objects masquerading as Arrays that don't have proper length // properties by using 0 for everything but positive numbers var input_length = (isNaN(input.length) || input.length <= 0) ? 0 : input.length; var output_length = (isNaN(output.length) || output.length <= 0) ? 0 : output.length; var array_operations = dist(input_length, output_length).operations; var padded_operations = array_operations.reduce(function (_a, array_operation) { var operations = _a[0], padding = _a[1]; if (isArrayAdd(array_operation)) { var padded_index = array_operation.index + 1 + padding; var index_token = padded_index < (input_length + padding) ? String(padded_index) : '-'; var operation = { op: array_operation.op, path: ptr.add(index_token).toString(), value: array_operation.value, }; // padding++ // maybe only if array_operation.index > -1 ? return [operations.concat(operation), padding + 1]; } else if (isArrayRemove(array_operation)) { var operation = { op: array_operation.op, path: ptr.add(String(array_operation.index + padding)).toString(), }; // padding-- return [operations.concat(operation), padding - 1]; } else { // replace var replace_ptr = ptr.add(String(array_operation.index + padding)); var replace_operations = diff(array_operation.original, array_operation.value, replace_ptr); return [operations.concat.apply(operations