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