rfc6902
Version:
Complete implementation of RFC6902 (patch and diff)
12 lines (11 loc) • 5.57 kB
JavaScript
(function(p,r){typeof exports==="object"&&typeof module!=="undefined"?r(exports):typeof define==="function"&&define.amd?define(["exports"],r):(p=typeof globalThis!=="undefined"?globalThis:p||self,r(p.rfc6902={}))})(this,function(p){function r(a){return a.replace(/~1/g,"/").replace(/~0/g,"~")}function F(a){return a.replace(/~/g,"~0").replace(/\//g,"~1")}function x(a){return a===void 0?"undefined":a===null?"null":Array.isArray(a)?"array":typeof a}function t(a){if(a==null||typeof a!="object")return a;
if(a.constructor==Array){var b=a.length,c=Array(b);for(let d=0;d<b;d++)c[d]=t(a[d]);return c}if(a.constructor==Date)return new Date(+a);b={};for(c in a)u.call(a,c)&&(b[c]=t(a[c]));return b}function G({op:a}){return a==="remove"||a==="replace"||a==="copy"||a==="move"}function A(a,b){const c=[];for(const d in a)!u.call(a,d)||a[d]===void 0||u.call(b,d)&&b[d]!==void 0||c.push(d);return c}function H(a,b){const c=[];for(const d in a)u.call(a,d)&&a[d]!==void 0&&u.call(b,d)&&b[d]!==void 0&&c.push(d);return c}
function y(a,b){return{operations:a.operations.concat(b),cost:a.cost+1}}function I(a,b,c,d=q){function e(h,f){const g=h*k+f;var l=B.get(g);if(l===void 0){if(h>0&&f>0&&!d(a[h-1],b[f-1],c.add(String(h-1))).length)l=e(h-1,f-1);else{l=[];if(h>0){var v=e(h-1,f);l.push(y(v,{op:"remove",index:h-1}))}f>0&&(v=e(h,f-1),l.push(y(v,{op:"add",index:h-1,value:b[f-1]})));h>0&&f>0&&(v=e(h-1,f-1),l.push(y(v,{op:"replace",index:h-1,original:a[h-1],value:b[f-1]})));l=l.sort((J,K)=>J.cost-K.cost)[0]}B.set(g,l)}return l}
const k=Math.max(a.length,b.length),B=new Map([[0,{operations:[],cost:0}]]),C=isNaN(a.length)||a.length<=0?0:a.length;var w=isNaN(b.length)||b.length<=0?0:b.length;w=e(C,w).operations;[w]=w.reduce(([h,f],g)=>{if(g.op==="add"){var l=g.index+1+f;g={op:g.op,path:c.add(l<C+f?String(l):"-").toString(),value:g.value};return[h.concat(g),f+1]}if(g.op==="remove")return g={op:g.op,path:c.add(String(g.index+f)).toString()},[h.concat(g),f-1];l=c.add(String(g.index+f));g=d(g.original,g.value,l);return[h.concat(...g),
f]},[[],0]);return w}function L(a,b,c,d=q){const e=[];A(a,b).forEach(k=>{e.push({op:"remove",path:c.add(k).toString()})});A(b,a).forEach(k=>{e.push({op:"add",path:c.add(k).toString(),value:b[k]})});H(a,b).forEach(k=>{e.push(...d(a[k],b[k],c.add(k)))});return e}function q(a,b,c,d=q){if(a===b)return[];const e=x(a),k=x(b);return e=="array"&&k=="array"?I(a,b,c,d):e=="object"&&k=="object"?L(a,b,c,d):[{op:"replace",path:c.toString(),value:b}]}function z(a,b,c){Array.isArray(a)?b=="-"?a.push(c):(b=parseInt(b,
10),a.splice(b,0,c)):a[b]=c}function D(a,b){Array.isArray(a)?(b=parseInt(b,10),a.splice(b,1)):delete a[b]}function M(a,b,c){switch(b.op){case "add":{const d=m.fromJSON(b.path);if((c===null||c===void 0?0:c.implicitArrayCreation)&&d.tokens[d.tokens.length-1]==="-"){const e=d.parent().evaluate(a);e.value===void 0&&x(e.parent)==="object"&&(e.parent[e.key]=[])}a=d.evaluate(a);a.parent===void 0?b=new n(b.path):(c===null||c===void 0?0:c.implicitArrayCreation)&&a.key==="-"&&!Array.isArray(a.parent)?b=new n(b.path):
(z(a.parent,a.key,t(b.value)),b=null)}return b;case "remove":return a=m.fromJSON(b.path).evaluate(a),a.value===void 0?b=new n(b.path):(D(a.parent,a.key),b=null),b;case "replace":a:if(a=m.fromJSON(b.path).evaluate(a),a.parent===null)b=new n(b.path);else{if(Array.isArray(a.parent)){if(parseInt(a.key,10)>=a.parent.length){b=new n(b.path);break a}}else if(a.value===void 0){b=new n(b.path);break a}a.parent[a.key]=t(b.value);b=null}return b;case "move":return c=m.fromJSON(b.from).evaluate(a),c.value===
void 0?b=new n(b.from):(a=m.fromJSON(b.path).evaluate(a),a.parent===void 0?b=new n(b.path):(D(c.parent,c.key),z(a.parent,a.key,c.value),b=null)),b;case "copy":return c=m.fromJSON(b.from).evaluate(a),c.value===void 0?b=new n(b.from):(a=m.fromJSON(b.path).evaluate(a),a.parent===void 0?b=new n(b.path):(z(a.parent,a.key,t(c.value)),b=null)),b;case "test":return a=m.fromJSON(b.path).evaluate(a),b=q(a.value,b.value,new m).length?new N(a.value,b.value):null,b}return new O(b)}function P(a){function b(c,d,
e){const k=a(c,d,e);return Array.isArray(k)?k:q(c,d,e,b)}return b}function E(a,b){a=m.fromJSON(b).evaluate(a);if(a!==void 0)return{op:"test",path:b,value:a.value}}class m{constructor(a=[""]){this.tokens=a}static fromJSON(a){const b=a.split("/").map(r);if(b[0]!=="")throw Error(`Invalid JSON Pointer: ${a}`);return new m(b)}toString(){return this.tokens.map(F).join("/")}evaluate(a){let b=null,c="";for(let d=1,e=this.tokens.length;d<e;d++)b=a,c=this.tokens[d],c!="__proto__"&&c!="constructor"&&c!="prototype"&&
(a=(b||{})[c]);return{parent:b,key:c,value:a}}get(a){return this.evaluate(a).value}set(a,b){a=this.evaluate(a);a.parent&&(a.parent[a.key]=b)}push(a){this.tokens.push(a)}add(a){a=this.tokens.concat(String(a));return new m(a)}parent(){const a=this.tokens.length>1?this.tokens.slice(0,-1):[""];return new m(a)}}const u=Object.prototype.hasOwnProperty;class n extends Error{constructor(a){super(`Value required at path: ${a}`);this.path=a;this.name="MissingError"}}class N extends Error{constructor(a,b){super(`Test failed: ${a} != ${b}`);
this.actual=a;this.expected=b;this.name="TestError"}}class O extends Error{constructor(a){super(`Invalid operation: ${a.op}`);this.operation=a;this.name="InvalidOperationError"}}p.Pointer=m;p.applyPatch=function(a,b,c){return b.map(d=>M(a,d,c))};p.createPatch=function(a,b,c){const d=new m;return(c?P(c):q)(a,b,d)};p.createTests=function(a,b){const c=[];b.filter(G).forEach(d=>{const e=E(a,d.path);e&&c.push(e);"from"in d&&(d=E(a,d.from))&&c.push(d)});return c}});