proxy-live-document
Version:
Mutable, observable, patchable JSON state wrapped in Smart Domains
2 lines (1 loc) • 12.7 kB
JavaScript
const t=t=>"object"==typeof t&&null!==t,e=Symbol("NoValue"),r=t=>{let e=t,r=[];for(;e.p&&(r.push(e.k),e=e.p,!("op"in e)););return"op"in e?[e,r.reverse()]:null},o=(t,e)=>{var r;return t.c=null!==(r=t.c)&&void 0!==r?r:{},t.c[e]||(t.c[e]={p:t,k:e}),t.c[e]},n=(o,n,i,a)=>{var c;if(n===i)return;if("op"in o)return void(i===e?("new"in o&&delete o.new,Object.assign(o,{op:"remove"})):Object.assign(o,{new:i,op:"remove"===o.op?"replace":o.op}));const h=r(o);if(h){const[r,n]=h;if("new"in r){let s=r.new;n.pop(),n.forEach((e=>{if(!t(s))throw new Error(`We tried to merge two new values, but the new place at ${n.join(", ")} encountered a non object at first encounter of the key ${e}`);e in s&&(s=s[e])})),i===e?delete s[o.k.toString()]:s[o.k.toString()]=i}return}let l="replace";n===e&&(l="add"),i===e&&(l="remove"),Object.assign(o,{op:l,opCount:a,d:!0,...n!==e?{old:n}:{},...i!==e?{new:i}:{}}),o.c&&Object.values(null!==(c=o.c)&&void 0!==c?c:{}).forEach((t=>{s(t)}))},s=e=>{const{c:o}=e;if(Object.values(null!=o?o:{}).forEach((t=>{s(t)})),"old"in e){const o=r(e);if(!o)throw new Error("We tried to merge a subtree of mutation but there was no parent with an old value above: Not sure if error");const[n,s]=o;if(!("old"in n))throw new Error("We tried to merge a subtree of mutation but there was no parent with an old value above: Not sure if error");let i=n.old;s.pop(),s.forEach((e=>{if(!t(i))throw new Error(`We tried to merge two old values, but the new place at ${s.join(", ")} encountered a non object at first encounter of the key ${e}`);e in i&&(i=i[e])})),i[e.k.toString()]=e.old,"op"in e&&(delete e.op,delete e.new,delete e.old)}if("op"in e&&"add"===e.op){const o=r(e);if(!o)throw new Error("We tried to merge a subtree of mutation but there was no parent with an old value above: Not sure if error");const[n,s]=o;if("op"in n&&"remove"===n.op){let r=n.old;s.pop(),s.forEach((e=>{if(!t(r))throw new Error(`We tried to merge two old values, but the new place at ${s.join(", ")} encountered a non object at first encounter of the key ${e}`);e in r&&(r=r[e])})),delete r[e.k.toString()]}"op"in e&&(delete e.op,delete e.new,delete e.old)}"op"in e&&(delete e.op,delete e.new,delete e.old)},i=(t,e,r=[])=>{"op"in t?e.push({op:t.op,old:"old"in t?t.old:void 0,value:"new"in t?t.new:void 0,pathArray:r,opCount:t.opCount,path:`/${r.join("/")}`}):t.c&&Object.values(t.c).forEach((t=>{i(t,e,[...r,t.k])}))};const a=new class{constructor(){this.proxies=new WeakSet}cache(t){this.proxies.add(t)}exists(t){return this.proxies.has(t)}};Object.freeze(a);const c=(t,e,r,o)=>{var n,s,i;let a=t,c=[...e];for(;c.length;){const t=c.shift();a.children=null!==(n=a.children)&&void 0!==n?n:{},a.children[t]=null!==(s=a.children[t])&&void 0!==s?s:{propName:t},a=a.children[t]}return a.subs=null!==(i=a.subs)&&void 0!==i?i:[],r.options=o,a.subs.push(r),a},h=(t,e)=>{var r;if(!t.subs)return!1;const o=null===(r=t.subs)||void 0===r?void 0:r.indexOf(e);if(-1===o)return!1;t.subs=[...t.subs.slice(0,o),...t.subs.slice(o+1)]},l=(t,e)=>{const r="**"===t.propName;if(!t.children)return r?[t]:null;const o=[],n=t.children[e];return r&&o.push(t),n&&o.push(n),t.children["*"]&&o.push(t.children["*"]),t.children["**"]&&o.push(t.children["**"]),o.length?o:null},u=Symbol("Patcher"),p=Symbol("WatcherProxy"),d=Symbol("TargetRef"),y=(t,e)=>{const{op:r,pathArray:o,value:n}=t,s=o.length;if(!s)return;let i,a=e,c=a.hasOwnProperty(u)?{entity:a,pathArray:[...o]}:null;for(let t=0;t<s-1;t+=1){if(i=o[t],!a.hasOwnProperty(i))throw new Error(`applyJSONPatchOperation cannot walk json patch path ${o.join("/")}. Cannot access path ${[...o].slice(0,t).join("/")}.`);a=a[i],a.hasOwnProperty(u)&&(c={entity:a,pathArray:[...o].slice(t+1)})}const h=o[s-1];if(c&&"applyPatch"in c.entity&&"function"==typeof c.entity.applyPatch){const e=t.pathArray.filter((t=>-1!==(null==c?void 0:c.pathArray.indexOf(t)))),r=e.join("/");c.entity.applyPatch({...t,path:r,pathArray:e})}else if(Array.isArray(a))switch(r){case"add":"-"===h?a.push(...Array.isArray(n)?n:[n]):a.splice(Number(h),0,n);break;case"replace":a[h]=n;break;case"remove":a.splice(Number(h),1)}else switch(r){case"add":case"replace":Object.assign(a,{[h]:n});break;case"remove":delete a[h]}},f=(t,e)=>{g(t,(t=>{for(let r=0;r<e.length;r+=1)y(e[r],t)}))};class w{constructor(){this.mutationMaps=new Map,this.mutationDirtyPaths=new Map,this.mutationSelectorPointers=new Map,this.mutationChangePointers=new Map,this.getSubProxy=(t,e,r,o,n)=>{const s=this.mutationMaps.get(t);let i=null==s?void 0:s.get(o);i||(i=new Proxy(o,new v({target:o,selectorPointerArray:r,mutationNode:e,dirtyPaths:this.mutationDirtyPaths.get(t),incOpCount:n,proxyfyAccess:(e,r,o)=>this.getSubProxy(t,r,o,e,n)})),null==s||s.set(o,i));const a={target:o,incOpCount:n,selectorPointerArray:r,mutationNode:e,dirtyPaths:this.mutationDirtyPaths.get(t),proxyfyAccess:(e,r,o)=>this.getSubProxy(t,r,o,e,n)},c=Array.isArray(o)?new O(a):new v(a);return i=new Proxy(o,c),null==s||s.set(o,i),i}}startMutation(t){this.mutationMaps.set(t,new WeakMap);const e=new WeakMap,r=new Set,o=new Array(x.getSelectorTree(t)),n={p:null,k:""};this.mutationChangePointers.set(t,n);let s=0;const i=()=>(s+=1,s),a=new Proxy(t,new v({target:t,selectorPointerArray:o,mutationNode:n,dirtyPaths:r,incOpCount:i,proxyfyAccess:(e,r,o)=>this.getSubProxy(t,r,o,e,i)}));e.set(t,a),this.mutationDirtyPaths.set(t,r),this.mutationMaps.set(t,e),this.mutationSelectorPointers.set(t,o)}hasRoot(t){return this.mutationMaps.has(t)}commit(t){const e=this.mutationDirtyPaths.get(t);if(!e)return[];const r=Array.from(e).reduce(((t,e)=>(e.writeSelectorPointerArray.filter((t=>"root"!==t.propName)).forEach((e=>t.add(e))),t)),new Set),o=(t=>{const e=[];return i(t,e),e.sort(((t,e)=>t.opCount-e.opCount)).map((t=>{const{opCount:e,...r}=t;return r}))})(this.mutationChangePointers.get(t));return x.runSelectorPointers(t,r,o),this.mutationMaps.delete(t),this.mutationDirtyPaths.delete(t),o}mutate(t,e){var r;const o=!this.hasRoot(t);o&&this.startMutation(t);const n=null===(r=this.mutationMaps.get(t))||void 0===r?void 0:r.get(t);if(n)return e(n),o?this.commit(t):[]}}const P=new w,g=(t,e)=>P.mutate(t,e),m=(e,r,o,n,s)=>new Proxy(e,{get:(i,a)=>{if("symbol"==typeof a&&a===p)return!0;if("symbol"==typeof a||"hasOwnProperty"===a)return Reflect.get(i,a);const h=Object.getOwnPropertyDescriptor(i.constructor.prototype,a);if(null==h?void 0:h.get)return h.get.call(m(e,r,o,n,s));{const e=i[a];return n.push(c(o,[...r,a],s)),t(e)?m(e,[...r,a],o,n,s):e}},getOwnPropertyDescriptor:(t,e)=>e===p?{configurable:!0,value:!0}:Reflect.getOwnPropertyDescriptor(t,e),ownKeys:t=>(n.push(c(o,[...r,"*"],s)),Reflect.ownKeys(t))}),b=(t,e)=>{const r=x.getSelectorTree(t);let o=[];const n=()=>{o.forEach((t=>{h(t,s)}))},s=(i,a)=>{n(),o=[];const c=m(t,[],r,o,s);e(c,a)};return s(),n};class A{}class v{constructor(t){this.deleted={},this.original={},this.writeSelectorPointerArray=[];const{target:e,proxyfyAccess:r,dirtyPaths:o}=t;this.targetRef=e,this.proxyfyAccess=r,this.dirtyPaths=o,this.selectorPointerArray=t.selectorPointerArray,this.mutationNode=t.mutationNode,this.incOpCount=t.incOpCount}get(t,e){if("symbol"==typeof e&&e===d)return this.targetRef;if("symbol"==typeof e&&e===p)return!0;if("symbol"==typeof e||"hasOwnProperty"===e)return Reflect.get(t,e);if("string"==typeof e&&this.deleted.hasOwnProperty(e))return;const r=t[e];if(a.exists(r))return r;if("object"==typeof r&&null!==r){const{selectorPointerArray:t}=this,n=t.reduce(((t,r)=>{const o=l(r,e);return o&&t.push(...o),t}),[]),s=o(this.mutationNode,e),i=this.proxyfyAccess(r,s,n);return a.exists(i)||a.cache(i),i}return r}set(t,r,s){if("length"===r&&Array.isArray(t))return Reflect.set(t,r,s);this.writeSelectorPointerArray.push(...this.selectorPointerArray.reduce(((t,e)=>{const o=l(e,r);return o&&t.push(...o),t}),[])),this.dirtyPaths.add(this),!this.original.hasOwnProperty(r)&&t.hasOwnProperty(r)&&(this.original[r]=t[r]);let i=s;if("object"==typeof s&&null!==s){const t=s;i=t.hasOwnProperty(p)?t[d]:Array.isArray(s)?[...s]:{...s}}let a=this.original[r];"object"==typeof a&&null!==a&&(a={...a});const c=o(this.mutationNode,r);return n(c,r in t?t[r]:e,i,this.incOpCount()),Reflect.set(t,r,s)}deleteProperty(t,r){if(r in t&&"string"==typeof r){this.writeSelectorPointerArray.push(...this.selectorPointerArray.reduce(((t,e)=>{const o=l(e,r);return o&&t.push(...o),t}),[]));const s=o(this.mutationNode,r);n(s,t[r],e,this.incOpCount()),this.dirtyPaths.add(this),this.deleted[r]=!0,this.original.hasOwnProperty(r)||(this.original[r]=t[r]);let i=this.original[r];"object"==typeof i&&null!==i&&(i={...i})}return Reflect.deleteProperty(t,r)}getOwnPropertyDescriptor(t,e){if("string"!=typeof e||!this.deleted[e])return e===p?{configurable:!0,value:!0}:Reflect.getOwnPropertyDescriptor(t,e)}ownKeys(t){return Reflect.ownKeys(t)}has(t,e){return Reflect.has(t,e)}}class O{constructor(t){this.deleted={},this.original=[],this.writeSelectorPointerArray=[];const{target:e,proxyfyAccess:r,dirtyPaths:o}=t;this.targetRef=e,this.proxyfyAccess=r,this.dirtyPaths=o,this.selectorPointerArray=t.selectorPointerArray,this.mutationNode=t.mutationNode,this.incOpCount=t.incOpCount}get(t,r){switch(r){case"splice":return(...r)=>{const[s,i]=r,a=s+i;for(let r=s;r<a;r+=1){this.writeSelectorPointerArray.push(...this.selectorPointerArray.reduce(((t,e)=>{const o=l(e,r.toString());return o&&t.push(...o),t}),[]));const s=o(this.mutationNode,r.toString());n(s,t[r],e,this.incOpCount()),this.deleted[r]=!0}return this.dirtyPaths.add(this),t.splice(...r)};case"push":return(...r)=>{this.writeSelectorPointerArray.push(...this.selectorPointerArray.reduce(((t,e)=>{const r=l(e,"-");return r&&t.push(...r),t}),[]));const s=o(this.mutationNode,"-");return n(s,e,r,this.incOpCount()),this.dirtyPaths.add(this),t.push(...r)};case"pop":return()=>{const r=t.length-1;this.writeSelectorPointerArray.push(...this.selectorPointerArray.reduce(((t,e)=>{const o=l(e,r.toString());return o&&t.push(...o),t}),[]));const s=o(this.mutationNode,r.toString());return n(s,t[r],e,this.incOpCount()),this.dirtyPaths.add(this),t.pop()};case"shift":return()=>{this.writeSelectorPointerArray.push(...this.selectorPointerArray.reduce(((t,e)=>{const r=l(e,"0");return r&&t.push(...r),t}),[]));const r=o(this.mutationNode,"0");return n(r,t[0],e,this.incOpCount()),this.dirtyPaths.add(this),t.shift()}}return Reflect.get(t,r)}set(t,e,r){this.writeSelectorPointerArray.push(...this.selectorPointerArray.reduce(((t,r)=>{const o=l(r,e);return o&&t.push(...o),t}),[]));const s=o(this.mutationNode,e);return n(s,t[e],r,this.incOpCount()),this.dirtyPaths.add(this),Reflect.set(t,e,r)}deleteProperty(t,r){this.writeSelectorPointerArray.push(...this.selectorPointerArray.reduce(((t,e)=>{const o=l(e,r);return o&&t.push(...o),t}),[]));const s=o(this.mutationNode,r);return n(s,t[r],e,this.incOpCount()),this.dirtyPaths.add(this),Reflect.deleteProperty(t,r)}}const S=(t,e)=>{if(-1===t.indexOf("**")&&t.length!==e.length)return!1;for(let r=0;r<t.length;r+=1)if(t[r]!==e[r]&&("*"!==t[r]||!e[r])){if(r+1===t.length&&"**"===t[r]&&e[r])return!0;if(e[r]!==t[r])return!1}return!0};class j{constructor(){this.selectorTrees=new WeakMap}getSelectorTree(t){if(!this.selectorTrees.has(t)){const e={propName:"root"};return this.selectorTrees.set(t,e),e}return this.selectorTrees.get(t)}runSelectorPointers(t,e,r){const o=new Set,n=o.add.bind(o),s=(t,e=!1)=>{t.forEach((t=>{const{subs:r,children:o}=t;e?null==r||r.forEach(n):null==r||r.filter((t=>{var e;return null===(e=t.options)||void 0===e?void 0:e.reactToAncestorChanges})).forEach(n),o&&s(Object.values(o))}))};s(e,!0),o.forEach((e=>{e(t,r)}))}}const x=new j,N=(t,e,r,o)=>{const n=x.getSelectorTree(t),s=new Set,i=(...t)=>{const e=r(...t);return s.forEach((t=>t(e))),e},a=e.map((t=>c(n,(t=>t.startsWith("/")?t.substring(1).split("/"):t.split("/"))(t),i,o)));return{reshape:()=>{throw new Error("reshape is no longer supported")},observe:t=>(console.warn("observe is depreacated. Use just selectors or autorun instead"),s.add(t),()=>{s.delete(t)}),dispose:()=>{for(const t of a)h(t,i)}}},C=t=>{const{path:e,pathArray:r,op:o,value:n,old:s}=t;switch(o){case"add":return{op:"remove",value:s,old:n,pathArray:r,path:e};case"remove":return{op:"add",value:s,old:n,pathArray:r,path:e};case"replace":return{op:"replace",value:s,old:n,pathArray:r,path:e}}},E="2.0.7beta";export{A as IObservableDomain,E as LIB_VERSION,w as MutationsManager,u as Patcher,O as ProxyMutationArrayHandler,v as ProxyMutationObjectHandler,j as StateTreeSelectorsManager,d as TargetRef,p as WatcherProxy,y as applyJSONPatchOperation,b as autorun,C as inversePatch,g as mutate,f as mutateFromPatches,S as pathMatchesSource,N as select,x as selectorsManager};