diff --git a/.github/workflows/reusable/setup.yml b/.github/workflows/reusable/setup.yml new file mode 100644 index 0000000..4ceed5d --- /dev/null +++ b/.github/workflows/reusable/setup.yml @@ -0,0 +1,24 @@ +name: Build + +on: + workflow_call: + inputs: + node-version: + description: Version of Node to use + default: "20.x" + pnpm-version: + description: Version of pnpm to use + default: "10.8.0" + +jobs: + setup: + name: Setup + runs-on: ubuntu-latest + steps: + - name: ๐Ÿ— Checkout code + uses: actions/checkout@v4 + + - name: ๐Ÿ— Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ inputs.pnpm-version }} \ No newline at end of file diff --git a/.github/workflows/wp-engine.yml b/.github/workflows/wp-engine.yml index 31aa134..034f039 100644 --- a/.github/workflows/wp-engine.yml +++ b/.github/workflows/wp-engine.yml @@ -2,8 +2,6 @@ name: Deploy to WP Engine on: push: - branches: - - main jobs: build: diff --git a/assets/fonts/Inter-Medium.4401ca19be5f325e28ee45d96516a35e.ttf b/assets/fonts/Inter-Medium.4401ca19be5f325e28ee45d96516a35e.ttf new file mode 100644 index 0000000..9da6019 Binary files /dev/null and b/assets/fonts/Inter-Medium.4401ca19be5f325e28ee45d96516a35e.ttf differ diff --git a/assets/img/wcpos-icon.png b/assets/img/wcpos-icon.png new file mode 100644 index 0000000..c84cdc4 Binary files /dev/null and b/assets/img/wcpos-icon.png differ diff --git a/assets/img/wcpos-pro-icon.png b/assets/img/wcpos-pro-icon.png new file mode 100644 index 0000000..79b4cd0 Binary files /dev/null and b/assets/img/wcpos-pro-icon.png differ diff --git a/assets/js/indexeddb.worker.js b/assets/js/indexeddb.worker.js index c5bb931..cd777ad 100644 --- a/assets/js/indexeddb.worker.js +++ b/assets/js/indexeddb.worker.js @@ -1 +1 @@ -(()=>{function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(t){var n=function(t,n){if("object"!=e(t)||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var o=r.call(t,n||"default");if("object"!=e(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===n?String:Number)(t)}(t,"string");return"symbol"==e(n)?n:n+""}function n(e,n){for(var r=0;r!1,deepFreezeWhenDevMode:e=>e,tunnelErrorMessage:e=>"RxDB Error-Code "+e+".\n Error messages are not included in RxDB core to reduce build size.\n "};function c(e,t,n){return"RxError ("+t+"):\n"+e+"\n"+function(e){var t="";return 0===Object.keys(e).length?t:(t+="Given parameters: {\n",t+=Object.keys(e).map((t=>{var n="[object Object]";try{n="errors"===t?e[t].map((e=>JSON.stringify(e,Object.getOwnPropertyNames(e)))):JSON.stringify(e[t],(function(e,t){return void 0===t?null:t}),2)}catch(e){}return t+":"+n})).join("\n"),t+="}")}(n)}var u=function(e){function t(t,n,r={}){var o,i=c(n,t,r);return(o=e.call(this,i)||this).code=t,o.message=i,o.url=l(t),o.parameters=r,o.rxdb=!0,o}var o,i;return i=e,(o=t).prototype=Object.create(i.prototype),o.prototype.constructor=o,r(o,i),t.prototype.toString=function(){return this.message},function(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}(t,[{key:"name",get:function(){return"RxError ("+this.code+")"}},{key:"typeError",get:function(){return!1}}])}(s(Error));function l(e){return"https://rxdb.info/errors.html?console=errors#"+e}function f(e){return"\n You can find out more about this error here: "+l(e)+" "}function d(e,t){return new u(e,a.tunnelErrorMessage(e)+f(e),t)}var h=/\./g,p="abcdefghijklmnopqrstuvwxyz",m=e=>{var t=typeof e;return null!==e&&("object"===t||"function"===t)},v=new Set(["__proto__","prototype","constructor"]),y=new Set("0123456789");function b(e){var t=[],n="",r="start",o=!1;for(var i of e)switch(i){case"\\":if("index"===r)throw new Error("Invalid character in an index");if("indexEnd"===r)throw new Error("Invalid character after an index");o&&(n+=i),r="property",o=!o;break;case".":if("index"===r)throw new Error("Invalid character in an index");if("indexEnd"===r){r="property";break}if(o){o=!1,n+=i;break}if(v.has(n))return[];t.push(n),n="",r="property";break;case"[":if("index"===r)throw new Error("Invalid character in an index");if("indexEnd"===r){r="index";break}if(o){o=!1,n+=i;break}if("property"===r){if(v.has(n))return[];t.push(n),n=""}r="index";break;case"]":if("index"===r){t.push(Number.parseInt(n,10)),n="",r="indexEnd";break}if("indexEnd"===r)throw new Error("Invalid character after an index");default:if("index"===r&&!y.has(i))throw new Error("Invalid character in an index");if("indexEnd"===r)throw new Error("Invalid character after an index");"start"===r&&(r="property"),o&&(o=!1,n+="\\"),n+=i}switch(o&&(n+="\\"),r){case"property":if(v.has(n))return[];t.push(n);break;case"index":throw new Error("Index was not closed");case"start":t.push("")}return t}function g(e,t){if("number"!=typeof t&&Array.isArray(e)){var n=Number.parseInt(t,10);return Number.isInteger(n)&&e[n]===e[t]}return!1}function w(e,t){var n=function(e,t,n){if(Array.isArray(t)&&(t=t.join(".")),!t.includes(".")&&!t.includes("["))return e[t];if(!m(e)||"string"!=typeof t)return void 0===n?e:n;var r=b(t);if(0===r.length)return n;for(var o=0;ot[e]:e=>{for(var r=e,o=0;o"desc"===Object.values(e)[0])),i=new Set;Object.keys(n).forEach((t=>{var r=w(e,t);r&&"boolean"===r.type&&Object.prototype.hasOwnProperty.call(n[t],"$eq")&&i.add(t)}));var s,a=t.sort.map((e=>Object.keys(e)[0])).filter((e=>!i.has(e))).join(","),c=-1;if(r.forEach((e=>{var r=!0,u=!0,l=e.map((e=>{var t=n[e],o=t?Object.keys(t):[],i={};return t&&o.length?o.forEach((e=>{if(D.has(e)){var n=function(e,t){switch(e){case"$eq":return{startKey:t,endKey:t,inclusiveEnd:!0,inclusiveStart:!0};case"$lte":return{endKey:t,inclusiveEnd:!0};case"$gte":return{startKey:t,inclusiveStart:!0};case"$lt":return{endKey:t,inclusiveEnd:!1};case"$gt":return{startKey:t,inclusiveStart:!1};default:throw new Error("SNH")}}(e,t[e]);i=Object.assign(i,n)}})):i={startKey:u?A:N,endKey:r?N:A,inclusiveStart:!0,inclusiveEnd:!0},void 0===i.startKey&&(i.startKey=A),void 0===i.endKey&&(i.endKey=N),void 0===i.inclusiveStart&&(i.inclusiveStart=!0),void 0===i.inclusiveEnd&&(i.inclusiveEnd=!0),u&&!i.inclusiveStart&&(u=!1),r&&!i.inclusiveEnd&&(r=!1),i})),f=l.map((e=>e.startKey)),d=l.map((e=>e.endKey)),h={index:e,startKeys:f,endKeys:d,inclusiveEnd:r,inclusiveStart:u,sortSatisfiedByIndex:!o&&a===e.filter((e=>!i.has(e))).join(","),selectorSatisfiedByIndex:C(e,t.selector,f,d)},p=function(e,t,n){var r=0,o=e=>{e>0&&(r+=e)},i=10,s=P(n.startKeys,(e=>e!==A&&e!==N));o(s*i);var a=P(n.startKeys,(e=>e!==N&&e!==A));o(a*i);var c=P(n.startKeys,((e,t)=>e===n.endKeys[t]));return o(c*i*1.5),o(n.sortSatisfiedByIndex?5:0),r}(0,0,h);(p>=c||t.index)&&(c=p,s=h)})),!s)throw d("SNH",{query:t});return s}var D=new Set(["$eq","$gt","$gte","$lt","$lte"]),M=new Set(["$eq","$gt","$gte"]),T=new Set(["$eq","$lt","$lte"]);function C(e,t,n,r){var o=Object.entries(t).find((([t,n])=>!e.includes(t)||Object.entries(n).find((([e,t])=>!D.has(e)))));if(o)return!1;if(t.$and||t.$or)return!1;var i=[],s=new Set;for(var[a,c]of Object.entries(t)){if(!e.includes(a))return!1;var u=Object.keys(c).filter((e=>M.has(e)));if(u.length>1)return!1;var l=u[0];if(l&&s.add(a),"$eq"!==l){if(i.length>0)return!1;i.push(l)}}var f=[],d=new Set;for(var[h,p]of Object.entries(t)){if(!e.includes(h))return!1;var m=Object.keys(p).filter((e=>T.has(e)));if(m.length>1)return!1;var v=m[0];if(v&&d.add(h),"$eq"!==v){if(f.length>0)return!1;f.push(v)}}var y=0;for(var b of e){for(var g of[s,d]){if(!g.has(b)&&g.size>0)return!1;g.delete(b)}if(n[y]!==r[y]&&s.size>0&&d.size>0)return!1;y++}return!0}function B(e,t){if(!t.sort)throw d("SNH",{query:t});return{query:t,queryPlan:$(e,t)}}class R extends Error{}const L=Number.MAX_SAFE_INTEGER,K=Number.MIN_SAFE_INTEGER,q=Symbol("missing"),F=Object.freeze(new Error("mingo: cycle detected while processing object/array")),U=/^\[object ([a-zA-Z0-9]+)\]$/,z=e=>{const t=Se(e);let n=0,r=t.length;for(;r;)n=(n<<5)-n^t.charCodeAt(--r);return n>>>0},W=new Set(["null","undefined","boolean","number","string","date","regexp"]),J={null:0,undefined:0,number:1,string:2,object:3,array:4,boolean:5,date:6,regexp:7,function:8},V=(e,t)=>{e===q&&(e=void 0),t===q&&(t=void 0);const[n,r]=[e,t].map((e=>J[G(e).toLowerCase()]));return n!==r?n-r:1===n||2===n||6===n?et?1:0:we(e,t)?0:et?1:0};function H(e,t){if(!e)throw new R(t)}const G=e=>U.exec(Object.prototype.toString.call(e))[1],Y=e=>"boolean"==typeof e,Q=e=>"string"==typeof e,X=e=>!isNaN(e)&&"number"==typeof e,Z=Array.isArray,ee=e=>{if(!e)return!1;const t=Object.getPrototypeOf(e);return(t===Object.prototype||null===t)&&"[object Object]"===Object.prototype.toString.call(e)},te=e=>e===Object(e),ne=e=>e instanceof Date,re=e=>e instanceof RegExp,oe=e=>"function"==typeof e,ie=e=>null==e,se=(e,t)=>e.includes(t),ae=(e,t)=>!se(e,t),ce=e=>ie(e)||Q(e)&&!e||e instanceof Array&&0===e.length||ee(e)&&0===Object.keys(e).length,ue=e=>e===q,le=e=>e instanceof Array?e:[e],fe=(e,t)=>!!e&&Object.prototype.hasOwnProperty.call(e,t),de=e=>"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView(e),he=[ne,re,de],pe=(e,t)=>{if(ie(e))return e;if(t.has(e))throw F;const n=e.constructor;if(he.some((t=>t(e))))return new n(e);try{if(t.add(e),Z(e))return e.map((e=>pe(e,t)));if(ee(e)){const n={};for(const r in e)n[r]=pe(e[r],t);return n}}finally{t.delete(e)}return e},me=e=>pe(e,new Set);function ve(e,t,n){if(n=n||{flatten:!1},ue(e)||ie(e))return t;if(ue(t)||ie(t))return e;if(o=t,!(ee(r=e)&&ee(o)||Z(r)&&Z(o))){if(n.skipValidation)return t||e;throw Error("mismatched types. must both be array or object")}var r,o;if(n.skipValidation=!0,Z(e)){const r=e,o=t;if(n.flatten){let e=0,i=0;for(;e{const i=Oe(r,t);n.has(i)?n.get(i).some((t=>we(e[t],r)))||n.get(i).push(o):n.set(i,[o])})),n}function be(e,t=z){if(e.some((e=>0==e.length)))return[];if(1===e.length)return Array.from(e);const n=function(e,t,n=V){if(ce(e))return e;const r=new Array,o=new Array;for(let n=0;nn(e[0],t[0]))),_e(o,r.map((e=>e[1])))}(e.map(((e,t)=>[t,e.length])),(e=>e[1])),r=e[n[0][0]],o=ye(r,t),i=new Map,s=new Array;return o.forEach(((t,o)=>{const a=t.map((e=>r[e])),c=a.map((e=>0)),u=a.map((e=>[n[0][0],0]));let l=!1;for(let t=1;tf[e]));l=a.map(((n,s)=>e.some(((e,a)=>{const l=c[s];return we(n,e)&&(c[s]++,rt===e.length-1?[a[n],u[n]]:q)).filter((e=>e!==q)))})),s.sort(((e,t)=>{const[n,[r,o]]=e,[i,[s,a]]=t,c=V(r,s);return 0!==c?c:V(o,a)})).map((e=>e[0]))}function ge(e,t=0){const n=new Array;return function e(t,r){for(let o=0,i=t.length;o0||r<0)?e(t[o],Math.max(-1,r-1)):n.push(t[o])}(e,t),n}function we(e,t){if(e===t||Object.is(e,t))return!0;const n=!!e&&e.constructor||e;if(null===e||null===t||void 0===e||void 0===t||n!==t.constructor||n===Function)return!1;if(n===Array||n===Object){const n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;if(new Set([...n,...r]).size!=n.length)return!1;for(const r of n)if(!we(e[r],t[r]))return!1;return!0}const r=Object.getPrototypeOf(e);return(de(e)||r!==Object.prototype&&r!==Array.prototype&&Object.prototype.hasOwnProperty.call(r,"toString"))&&e.toString()===t.toString()}const xe=(e,t)=>{if(null===e)return"null";if(void 0===e)return"undefined";const n=e.constructor;switch(n){case RegExp:case Number:case Boolean:case Function:case Symbol:return e.toString();case String:return JSON.stringify(e);case Date:return e.toISOString()}if(de(e))return n.name+"["+e.toString()+"]";if(t.has(e))throw F;try{if(t.add(e),Z(e))return"["+e.map((e=>xe(e,t))).join(",")+"]";if(n===Object)return"{"+Object.keys(e).sort().map((n=>n+":"+xe(e[n],t))).join(",")+"}";const r=Object.getPrototypeOf(e);if(r!==Object.prototype&&r!==Array.prototype&&Object.prototype.hasOwnProperty.call(r,"toString"))return n.name+"("+JSON.stringify(e.toString())+")";const[o,i]=(e=>{let[t,n]=[Object.getPrototypeOf(e),Object.getOwnPropertyNames(e)],r=t;for(;!n.length&&t!==Object.prototype&&t!==Array.prototype;)r=t,n=Object.getOwnPropertyNames(t),t=Object.getPrototypeOf(t);const o={};return n.forEach((t=>o[t]=e[t])),[o,r]})(e);return n.name+xe(o,t)}finally{t.delete(e)}},Se=e=>xe(e,new Set);function Oe(e,t){return t=t||z,ie(e)?null:t(e).toString()}function Ie(e,t,n=z){if(e.length<1)return new Map;const r=new Map,o=new Map;for(let i=0;iwe(e,a))):null;ie(e)?(o.set(a,[s]),r.has(c)?r.get(c).push(a):r.set(c,[a])):o.get(e).push(s)}}return o}const Ee=5e4;function _e(e,...t){return e instanceof Array?t.reduce(((e,t)=>{let n=Math.ceil(t.length/Ee),r=0;for(;n-- >0;)Array.prototype.push.apply(e,t.slice(r,r+Ee)),r+=Ee;return e}),e):t.filter(te).reduce(((e,t)=>(Object.assign(e,t),e)),e)}function je(e,t){return te(e)?e[t]:void 0}function ke(e,t,n){let r=0;const o=W.has(G(e).toLowerCase())?e:function e(t,n){let o=t;for(let t=0;t0)break;r+=1;const i=n.slice(t);o=o.reduce(((t,n)=>{const r=e(n,i);return void 0!==r&&t.push(r),t}),[]);break}if(o=je(o,i),void 0===o)break}return o}(e,t.split("."));return o instanceof Array&&n?.unwrapArray?function(e,t){if(t<1)return e;for(;t--&&1===e.length;)e=e[0];return e}(o,r):o}function Pe(e,t,n){const r=t.split("."),o=r[0],i=r.slice(1).join("."),s=null!==/^\d+$/.exec(o),a=r.length>1;let c,u;if(e instanceof Array)if(s)c=je(e,Number(o)),a&&(c=Pe(c,i,n)),c=[c];else{c=[];for(const r of e)u=Pe(r,t,n),n?.preserveMissing?(void 0===u&&(u=q),c.push(u)):void 0!==u&&c.push(u)}else{if(u=je(e,o),a&&(u=Pe(u,i,n)),void 0===u)return;c=n?.preserveKeys?{...e}:{},c[o]=u}return c}function Ne(e){if(e instanceof Array)for(let t=e.length-1;t>=0;t--)e[t]===q?e.splice(t,1):Ne(e[t]);else if(ee(e))for(const t in e)fe(e,t)&&Ne(e[t])}const Ae=/^\d+$/;function $e(e,t,n,r){const o=t.split("."),i=o[0],s=o.slice(1).join(".");if(1===o.length)(ee(e)||Z(e)&&Ae.test(i))&&n(e,i);else{r?.buildGraph&&ie(e[i])&&(e[i]={});const t=e[i];if(!t)return;const a=!!(o.length>1&&Ae.test(o[1]));t instanceof Array&&r?.descendArray&&!a?t.forEach((e=>$e(e,s,n,r))):$e(t,s,n,r)}}function De(e,t,n){$e(e,t,((e,t)=>{e[t]=oe(n)?n(e[t]):n}),{buildGraph:!0})}function Me(e,t,n){$e(e,t,((e,t)=>{if(e instanceof Array){if(/^\d+$/.test(t))e.splice(parseInt(t),1);else if(n&&n.descendArray)for(const n of e)ee(n)&&delete n[t]}else ee(e)&&delete e[t]}),n)}const Te=/^\$[a-zA-Z0-9_]+$/;function Ce(e){return Te.test(e)}function Be(e){if(W.has(G(e).toLowerCase()))return re(e)?{$regex:e}:{$eq:e};if(te(e)){const t=e;if(!Object.keys(t).some(Ce))return{$eq:e};if(fe(e,"$regex")){const t={...e};return t.$regex=new RegExp(e.$regex,e.$options),delete t.$options,t}}return e}var Re=(e=>(e.CLONE_ALL="CLONE_ALL",e.CLONE_INPUT="CLONE_INPUT",e.CLONE_OUTPUT="CLONE_OUTPUT",e.CLONE_OFF="CLONE_OFF",e))(Re||{});class Le{constructor(e,t,n,r=Date.now()){this._opts=e,this._root=t,this._local=n,this.timestamp=r,this.update(t,n)}static init(e,t,n){return e instanceof Le?new Le(e._opts,ie(e.root)?t:e.root,Object.assign({},e.local,n)):new Le(e,t,n)}update(e,t){return this._root=e,this._local=t?Object.assign({},t,{variables:Object.assign({},this._local?.variables,t?.variables)}):t,this}getOptions(){return Object.freeze({...this._opts,context:Fe.from(this._opts.context)})}get root(){return this._root}get local(){return this._local}get idKey(){return this._opts.idKey}get collation(){return this._opts?.collation}get processingMode(){return this._opts?.processingMode||"CLONE_OFF"}get useStrictMode(){return this._opts?.useStrictMode}get scriptEnabled(){return this._opts?.scriptEnabled}get useGlobalContext(){return this._opts?.useGlobalContext}get hashFunction(){return this._opts?.hashFunction}get collectionResolver(){return this._opts?.collectionResolver}get jsonSchemaValidator(){return this._opts?.jsonSchemaValidator}get variables(){return this._opts?.variables}get context(){return this._opts?.context}}function Ke(e){return e instanceof Le?e.getOptions():Object.freeze({idKey:"_id",scriptEnabled:!0,useStrictMode:!0,useGlobalContext:!0,processingMode:"CLONE_OFF",...e,context:e?.context?Fe.from(e?.context):Fe.init({})})}var qe=(e=>(e.ACCUMULATOR="accumulator",e.EXPRESSION="expression",e.PIPELINE="pipeline",e.PROJECTION="projection",e.QUERY="query",e.WINDOW="window",e))(qe||{});class Fe{constructor(e){this.operators={accumulator:{},expression:{},pipeline:{},projection:{},query:{},window:{}};for(const[t,n]of Object.entries(e))this.addOperators(t,n)}static init(e={}){return new Fe(e)}static from(e){return new Fe(e.operators)}addOperators(e,t){for(const[n,r]of Object.entries(t))this.getOperator(e,n)||(this.operators[e][n]=r);return this}addAccumulatorOps(e){return this.addOperators("accumulator",e)}addExpressionOps(e){return this.addOperators("expression",e)}addQueryOps(e){return this.addOperators("query",e)}addPipelineOps(e){return this.addOperators("pipeline",e)}addProjectionOps(e){return this.addOperators("projection",e)}addWindowOps(e){return this.addOperators("window",e)}getOperator(e,t){return e in this.operators&&this.operators[e][t]||null}}const Ue=Fe.init();function ze(e,t){for(const[n,r]of Object.entries(t)){H(oe(r)&&Ce(n),`'${n}' is not a valid operator`);const t=We(e,n,null);H(!t||r===t,`${n} already exists for '${e}' operators. Cannot change operator function once registered.`)}switch(e){case"accumulator":Ue.addAccumulatorOps(t);break;case"expression":Ue.addExpressionOps(t);break;case"pipeline":Ue.addPipelineOps(t);break;case"projection":Ue.addProjectionOps(t);break;case"query":Ue.addQueryOps(t);break;case"window":Ue.addWindowOps(t)}}function We(e,t,n){const{context:r,useGlobalContext:o}=n||{},i=r?r.getOperator(e,t):null;return!i&&o?Ue.getOperator(e,t):i}const Je={$$ROOT:(e,t,n)=>n.root,$$CURRENT:(e,t,n)=>e,$$REMOVE(e,t,n){},$$NOW:(e,t,n)=>new Date(n.timestamp)},Ve={$$KEEP:(e,t,n)=>e,$$PRUNE(e,t,n){},$$DESCEND(e,t,n){if(!fe(t,"$cond"))return e;let r;for(const[o,i]of Object.entries(e))if(te(i)){if(i instanceof Array){const e=[];for(let r of i)ee(r)&&(r=Ge(r,t,n.update(r))),ie(r)||e.push(r);r=e}else r=Ge(i,t,n.update(i));ie(r)?delete e[o]:e[o]=r}return e}};function He(e,t,n,r){const o=Le.init(r,e);if(Ce(n=n||"")){const i=We("expression",n,r);if(i)return i(e,t,o);const s=We("accumulator",n,r);if(s)return e instanceof Array||(e=He(e,t,null,o),t=null),H(e instanceof Array,`'${n}' target must be an array.`),s(e,t,o.update(null,o.local));throw new R(`operator '${n}' is not registered`)}if(Q(t)&&t.length>0&&"$"===t[0]){if(fe(Ve,t))return t;let n=o.root;const r=t.split(".");if(fe(Je,r[0]))n=Je[r[0]](e,null,o),t=t.slice(r[0].length+1);else if("$$"===r[0].slice(0,2)){n=Object.assign({},o.variables,{this:e},o.local?.variables);const i=r[0].slice(2);H(fe(n,i),`Use of undefined variable: ${i}`),t=t.slice(2)}else t=t.slice(1);return""===t?n:ke(n,t)}if(Z(t))return t.map((t=>He(e,t,null,o)));if(ee(t)){const n={};for(const[i,s]of Object.entries(t))if(n[i]=He(e,s,i,o),["expression","accumulator"].some((e=>!!We(e,i,r))))return H(1===Object.keys(t).length,"Invalid aggregation expression '"+JSON.stringify(t)+"'"),n[i];return n}return t}function Ge(e,t,n){const r=He(e,t,null,n);return fe(Ve,r)?Ve[r](e,t,n):r}function Ye(e){return e instanceof Ze?e:new Ze(e)}function Qe(e,t){const n=e.slice(t+1);e.splice(t),Array.prototype.push.apply(e,n)}const Xe=new Error;class Ze{constructor(e){let t;if(this.iteratees=[],this.yieldedValues=[],this.isDone=!1,e instanceof Function&&(e={next:e}),(n=e)&&"object"==typeof n&&n?.next instanceof Function){const n=e;t=()=>{const e=n.next();if(e.done)throw Xe;return e.value}}else if(e instanceof Array){const n=e,r=n.length;let o=0;t=()=>{if(o0?this.push(2,e):this}drop(e){return e>0?this.push(3,e):this}transform(e){const t=this;let n;return Ye((()=>(n||(n=Ye(e(t.value()))),n.next())))}value(){return this.isDone||(this.isDone=this.getNext(!0).done),this.yieldedValues}each(e){for(;;){const t=this.next();if(t.done)break;if(!1===e(t.value))return!1}return!0}reduce(e,t){let n=this.next();for(void 0!==t||n.done||(t=n.value,n=this.next());!n.done;)t=e(t,n.value),n=this.next();return t}size(){return this.reduce(((e,t)=>++e),0)}[Symbol.iterator](){return this}}class et{constructor(e,t){this.pipeline=e,this.options=Ke(t)}stream(e){let t=Ye(e);const n=this.options.processingMode;n!=Re.CLONE_ALL&&n!=Re.CLONE_INPUT||t.map(me);const r=new Array;if(!ce(this.pipeline))for(const e of this.pipeline){const n=Object.keys(e),o=n[0],i=We(qe.PIPELINE,o,this.options);H(1===n.length&&!!i,`invalid pipeline operator ${o}`),r.push(o),t=i(t,e[o],this.options)}return(n==Re.CLONE_OUTPUT||n==Re.CLONE_ALL&&be([["$group","$unwind"],r]).length)&&t.map(me),t}run(e){return this.stream(e).value()}}class tt{constructor(e,t,n,r){this.source=e,this.predicate=t,this.projection=n,this.options=r,this.operators=[],this.result=null,this.buffer=[]}fetch(){return this.result||(ee(this.projection)&&this.operators.push({$project:this.projection}),this.result=Ye(this.source).filter(this.predicate),this.operators.length>0&&(this.result=new et(this.operators,this.options).stream(this.result))),this.result}fetchAll(){const e=Ye([...this.buffer]);return this.buffer=[],function(...e){let t=0;return Ye((()=>{for(;t0)return this.buffer.pop();const e=this.fetch().next();return e.done?void 0:e.value}hasNext(){if(this.buffer.length>0)return!0;const e=this.fetch().next();return!e.done&&(this.buffer.push(e.value),!0)}map(e){return this.all().map(e)}forEach(e){this.all().forEach(e)}[Symbol.iterator](){return this.fetchAll()}}class nt{constructor(e,t){this.condition=e,this.options=Ke(t),this.compiled=[],this.compile()}compile(){H(ee(this.condition),`query criteria must be an object: ${JSON.stringify(this.condition)}`);const e={};for(const[t,n]of Object.entries(this.condition)){if("$where"===t)Object.assign(e,{field:t,expr:n});else if(se(["$and","$or","$nor","$expr","$jsonSchema"],t))this.processOperator(t,t,n);else{H(!Ce(t),`unknown top level operator: ${t}`);for(const[e,r]of Object.entries(Be(n)))this.processOperator(t,e,r)}e.field&&this.processOperator(e.field,e.field,e.expr)}}processOperator(e,t,n){const r=We(qe.QUERY,t,this.options);if(!r)throw new R(`unknown query operator ${t}`);const o=r(e,n,this.options);this.compiled.push(o)}test(e){for(let t=0,n=this.compiled.length;tthis.test(e)),t||{},this.options)}remove(e){return e.reduce(((e,t)=>(this.test(t)||e.push(t),e)),[])}}const rt=(e,t,n)=>{if(ce(t)||!ee(t))return e;let r=V;const o=n.collation;return ee(o)&&Q(o.locale)&&(r=function(e){const t={sensitivity:ot[e.strength||3],caseFirst:"off"===e.caseFirst?"false":e.caseFirst||"false",numeric:e.numericOrdering||!1,ignorePunctuation:"shifted"===e.alternate};!0===(e.caseLevel||!1)&&("base"===t.sensitivity&&(t.sensitivity="case"),"accent"===t.sensitivity&&(t.sensitivity="variant"));const n=new Intl.Collator(e.locale,t);return(e,t)=>{if(!Q(e)||!Q(t))return V(e,t);const r=n.compare(e,t);return r<0?-1:r>0?1:0}}(o)),e.transform((e=>{const o=Object.keys(t);for(const i of o.reverse()){const o=Ie(e,(e=>ke(e,i)),n.hashFunction),s=Array.from(o.keys()).sort(r);-1===t[i]&&s.reverse(),e=[],s.reduce(((e,t)=>_e(e,o.get(t))),e)}return e}))},ot={1:"base",2:"accent",3:"variant"};function it(e){const t=(t,n,r)=>{const o={unwrapArray:!0},i=Math.max(1,t.split(".").length-1);return s=>{const a=ke(s,t,o);return e(a,n,{...r,depth:i})}};return t.op="query",t}function st(e){return(t,n,r)=>{const o=He(t,n,null,r);return e(...o)}}function at(e,t,n){if(we(e,t))return!0;if(ie(e)&&ie(t))return!0;if(e instanceof Array){const r=we.bind(null,t);return e.some(r)||ge(e,n?.depth).some(r)}return!1}function ct(e,t,n){return!at(e,t,n)}function ut(e,t,n){return ie(e)?t.some((e=>null===e)):be([le(e),t],n?.hashFunction).length>0}function lt(e,t,n){return!ut(e,t,n)}function ft(e,t,n){return St(e,t,((e,t)=>V(e,t)<0))}function dt(e,t,n){return St(e,t,((e,t)=>V(e,t)<=0))}function ht(e,t,n){return St(e,t,((e,t)=>V(e,t)>0))}function pt(e,t,n){return St(e,t,((e,t)=>V(e,t)>=0))}function mt(e){return Ce(e)&&-1===["$and","$or","$nor"].indexOf(e)}function vt(e,t,n){if(Z(e)&&!ce(e)){let r=e=>e,o=t;Object.keys(t).every(mt)&&(o={temp:t},r=e=>({temp:e}));const i=new nt(o,n);for(let t=0,n=e.length;tnull===e,bt=e=>X(e)&&e>=-2147483648&&e<=2147483647&&-1===e.toString().indexOf("."),gt=e=>X(e)&&e>=K&&e<=L&&-1===e.toString().indexOf("."),wt={array:Z,bool:Y,boolean:Y,date:ne,decimal:X,double:X,int:bt,long:gt,number:X,null:yt,object:ee,regex:re,regexp:re,string:Q,undefined:ie,function:e=>{throw new R("unsupported type key `function`.")},1:X,2:Q,3:ee,4:Z,6:ie,8:Y,9:ne,10:yt,11:re,16:bt,18:gt,19:X};function xt(e,t,n){const r=wt[t];return!!r&&r(e)}function St(e,t,n){return le(e).some((e=>G(e)===G(t)&&n(e,t)))}st(lt);const Ot=(e,t)=>(n,r,o)=>{H(Z(r),`${e}: expression must be an array.`);const i=He(n,r,null,o);return i.some(ie)?null:(H(i.every(X),`${e}: expression must evalue to array of numbers.`),t(i))};Ot("$bitAnd",(e=>e.reduce(((e,t)=>e&t),-1))),Ot("$bitOr",(e=>e.reduce(((e,t)=>e|t),0))),Ot("$bitXor",(e=>e.reduce(((e,t)=>e^t),0))),st(at),st(ht),st(pt),st(ft),st(dt),st(ct);const It=(e,t)=>{const n={};return e.split("").forEach(((e,r)=>n[e]=t*(r+1))),n};It("ABCDEFGHIKLM",1),It("NOPQRSTUVWXY",-1);const Et={undefined:null,null:null,NaN:NaN,Infinity:new Error,"-Infinity":new Error};function _t(e,t=Et){const n=Object.assign({},Et,t),r=new Set(Object.keys(n));return(t,o,i)=>{const s=He(t,o,null,i);if(r.has(`${s}`)){const t=n[`${s}`];if(t instanceof Error)throw new R(`cannot apply $${e.name} to -inf, value must in (-inf,inf)`);return t}return e(s)}}_t(Math.acos,{Infinity:1/0,0:new Error}),_t(Math.acosh,{Infinity:1/0,0:new Error}),_t(Math.asin),_t(Math.asinh,{Infinity:1/0,"-Infinity":-1/0}),_t(Math.atan),_t(Math.atanh,{1:1/0,"-1":-1/0}),_t(Math.cos),_t(Math.cosh,{"-Infinity":1/0,Infinity:1/0});const jt=Math.PI/180,kt=(_t((e=>e*jt),{Infinity:1/0,"-Infinity":1/0}),180/Math.PI);_t((e=>e*kt),{Infinity:1/0,"-Infinity":-1/0}),_t(Math.sin),_t(Math.sinh,{"-Infinity":-1/0,Infinity:1/0}),_t(Math.tan);const Pt=(e,t,n)=>{if(ce(t))return e;let r=Object.keys(t),o=!1;At(t,n);const i=n.idKey;if(se(r,i)){const e=t[i];0!==e&&!1!==e||(r=r.filter(ae.bind(null,[i])),o=0==r.length)}else r.push(i);const s=Le.init(n);return e.map((e=>Nt(e,t,s.update(e),r,o)))};function Nt(e,t,n,r,o){let i={},s=!1,a=!1;const c=[];o&&c.push(n.idKey);for(const o of r){let r;const u=t[o];if(o!==n.idKey&&se([0,!1],u)&&(a=!0),o===n.idKey&&ce(u))r=e[o];else if(Q(u))r=He(e,u,o,n);else if(se([1,!0],u));else if(u instanceof Array)r=u.map((t=>{const r=He(e,t,null,n);return ie(r)?null:r}));else{if(!ee(u)){c.push(o);continue}{const t=u,i=Object.keys(u),a=1==i.length?i[0]:"",c=We(qe.PROJECTION,a,n);if(c)"$slice"===a?le(t[a]).every(X)?(r=c(e,t[a],o,n),s=!0):r=He(e,t,o,n):r=c(e,t[a],o,n);else if(Ce(a))r=He(e,t[a],a,n);else if(fe(e,o)){At(t,n);let s=e[o];s instanceof Array?r=s.map((e=>Nt(e,t,n,i,!1))):(s=ee(s)?s:e,r=Nt(s,t,n,i,!1))}else r=He(e,u,null,n)}}const l=Pe(e,o,{preserveMissing:!0});void 0!==l&&ve(i,l,{flatten:!0}),ae([0,1,!1,!0],u)&&(void 0===r?Me(i,o,{descendArray:!0}):De(i,o,r))}if(Ne(i),(s||a||o)&&(i=_e({},e,i),c.length>0))for(const e of c)Me(i,e,{descendArray:!0});return i}function At(e,t){const n=[!1,!1];for(const[r,o]of Object.entries(e)){if(r===t?.idKey)return;0===o||!1===o?n[0]=!0:1!==o&&!0!==o||(n[1]=!0),H(!(n[0]&&n[1]),"Projection cannot have a mix of inclusion and exclusion.")}}const $t=(e,t,n)=>{H(Z(t),"Invalid expression: $and expects value to be an Array.");const r=t.map((e=>new nt(e,n)));return e=>r.every((t=>t.test(e)))},Dt=(e,t,n)=>{H(Z(t),"Invalid expression. $or expects value to be an Array");const r=t.map((e=>new nt(e,n)));return e=>r.some((t=>t.test(e)))},Mt=(e,t,n)=>{H(Z(t),"Invalid expression. $nor expects value to be an array.");const r=Dt(0,t,n);return e=>!r(e)},Tt=(e,t,n)=>{const r={};r[e]=Be(t);const o=new nt(r,n);return e=>!o.test(e)},Ct=it(at),Bt=it(ht),Rt=it(pt),Lt=it(ut),Kt=it(ft),qt=it(dt),Ft=it(ct),Ut=it(lt),zt=it((function(e,t,n){return le(e).some((e=>2===t.length&&e%t[0]===t[1]))})),Wt=it((function(e,t,n){const r=le(e),o=e=>Q(e)&&((e,t=!0)=>!!e||t&&""===e)(t.exec(e),n?.useStrictMode);return r.some(o)||ge(r,1).some(o)}));it((function(e,t,n){if(!(Z(e)&&Z(t)&&e.length&&t.length))return!1;let r=!0;for(const o of t){if(!r)break;r=ee(o)&&se(Object.keys(o),"$elemMatch")?vt(e,o.$elemMatch,n):o instanceof RegExp?e.some((e=>"string"==typeof e&&o.test(e))):e.some((e=>we(o,e)))}return r}));const Jt=it(vt),Vt=it((function(e,t,n){return Array.isArray(e)&&e.length===t})),Ht=it((function(e,t,n){return(!1===t||0===t)&&void 0===e||(!0===t||1===t)&&void 0!==e})),Gt=it((function(e,t,n){return Array.isArray(t)?t.findIndex((t=>xt(e,t)))>=0:xt(e,t)}));var Yt=!1;function Qt(e,t){var n=x(e.primaryKey);t=_(t);var r,o=j(t);if("number"!=typeof o.skip&&(o.skip=0),o.selector?(o.selector=o.selector,Object.entries(o.selector).forEach((([e,t])=>{"object"==typeof t&&null!==t||(o.selector[e]={$eq:t})}))):o.selector={},o.index){var i=(r=o.index,Array.isArray(r)?r.slice(0):[r]);i.includes(n)||i.push(n),o.index=i}if(o.sort){var s=o.sort.find((e=>{return t=e,Object.keys(t)[0]===n;var t}));s||(o.sort=o.sort.slice(0),o.sort.push({[n]:"asc"}))}else if(o.index)o.sort=o.index.map((e=>({[e]:"asc"})));else{if(e.indexes){var a=new Set;Object.entries(o.selector).forEach((([e,t])=>{("object"!=typeof t||null===t||Object.keys(t).find((e=>D.has(e))))&&a.add(e)}));var c,u=-1;e.indexes.forEach((e=>{var t,n=(t=e,Array.isArray(t)?e:[e]),r=n.findIndex((e=>!a.has(e)));r>0&&r>u&&(u=r,c=n)})),c&&(o.sort=c.map((e=>({[e]:"asc"}))))}o.sort||(o.sort=[{[n]:"asc"}])}return o}function Xt(e,t){if(!t.sort)throw d("SNH",{query:t});var n,r=(n=t.selector,Yt||(ze(qe.PIPELINE,{$sort:rt,$project:Pt}),ze(qe.QUERY,{$and:$t,$eq:Ct,$elemMatch:Jt,$exists:Ht,$gt:Bt,$gte:Rt,$in:Lt,$lt:Kt,$lte:qt,$ne:Ft,$nin:Ut,$mod:zt,$nor:Mt,$not:Tt,$or:Dt,$regex:Wt,$size:Vt,$type:Gt}),Yt=!0),new nt(n));return e=>r.test(e)}function Zt(e,t,n,r,o,i,s){for(var a,c=!!e.schema.attachments,u=[],l=[],f=[],h={id:function(e=10){for(var t="",n=0;n0,w=r.length,x=function(){var e,o=r[S],h=o.document,p=o.previous,w=h[t],x=h._deleted,O=p&&p._deleted,E=void 0;if(g&&(E=n.get(w)),E){var _=E._rev;if(!p||p&&_!==p._rev){var j={isError:!0,status:409,documentId:w,writeRow:o,documentInDb:E};return f.push(j),1}var k=c?en(o):o;c&&(x?p&&Object.keys(p._attachments).forEach((e=>{y.push({documentId:w,attachmentId:e,digest:I(p)._attachments[e].digest})})):(Object.entries(h._attachments).find((([t,n])=>((p?p._attachments[t]:void 0)||n.data||(e={documentId:w,documentInDb:E,isError:!0,status:510,writeRow:o,attachmentId:t}),!0))),e||Object.entries(h._attachments).forEach((([e,t])=>{var n=p?p._attachments[e]:void 0;if(n){var r=k.document._attachments[e].digest;t.data&&n.digest!==r&&b.push({documentId:w,attachmentId:e,attachmentData:t,digest:t.digest})}else v.push({documentId:w,attachmentId:e,attachmentData:t,digest:t.digest})})))),e?f.push(e):(c?(l.push(en(k)),s&&s(h)):(l.push(k),s&&s(h)),a=k);var P=null,N=null,A=null;if(O&&!x)A="INSERT",P=c?tn(h):h;else if(!p||O||x){if(!x)throw d("SNH",{args:{writeRow:o}});A="DELETE",P=I(h),N=p}else A="UPDATE",P=c?tn(h):h,N=p;var $={documentId:w,documentData:P,previousDocumentData:N,operation:A};m.push($)}else{var D=!!x;if(c&&Object.entries(h._attachments).forEach((([t,n])=>{n.data?v.push({documentId:w,attachmentId:t,attachmentData:n,digest:n.digest}):(e={documentId:w,isError:!0,status:510,writeRow:o,attachmentId:t},f.push(e))})),e||(c?(u.push(en(o)),i&&i(h)):(u.push(o),i&&i(h)),a=o),!D){var M={documentId:w,operation:"INSERT",documentData:c?tn(h):h,previousDocumentData:c&&p?tn(p):p};m.push(M)}}},S=0;S{var r,o,i;t._attachments[e]=(i=(r=n).data)?{length:(o=i,atob(o).length),digest:r.digest,type:r.type}:r})),t}async function nn(e,t,n){if(e.getChangedDocumentsSince)return e.getChangedDocumentsSince(t,n);var r=x(e.schema.primaryKey),o=B(e.schema,function(e,t,n){var r=x(e.schema.primaryKey),o=n?n.lwt:1,i=n?n.id:"";return Qt(e.schema,{selector:{$or:[{"_meta.lwt":{$gt:o}},{"_meta.lwt":{$eq:o},[r]:{$gt:n?i:""}}],"_meta.lwt":{$gte:o}},sort:[{"_meta.lwt":"asc"},{[r]:"asc"}],skip:0,limit:t})}(e,t,n)),i=(await e.query(o)).documents,s=k(i);return{documents:i,checkpoint:s?{id:s[r],lwt:s._meta.lwt}:n||{id:"",lwt:0}}}new WeakMap;var rn="15.37.0";function on(e,t){var n=e.get(t);if(void 0===n)throw new Error("missing value from map "+t);return n}function sn(e,t,n,r){var o=e.get(t);return void 0===o?(o=n(),e.set(t,o)):r&&r(o),o}function an(e=0){return new Promise((t=>setTimeout(t,e)))}Promise.resolve(!0);var cn=Promise.resolve(!1),un=(Promise.resolve(null),Promise.resolve());function ln(e=1e4){return"function"==typeof requestIdleCallback?new Promise((t=>{requestIdleCallback((()=>t()),{timeout:e})})):an(0)}function fn(e,t){var n=t.map((t=>{var n=w(e,t);if(!n)throw new Error("not in schema: "+t);var r,o=n.type;"number"!==o&&"integer"!==o||(r=dn(n));var i,s=E(t),a=n.maxLength?n.maxLength:0;return i="string"===o?e=>{var t=s(e);return t||(t=""),t.padEnd(a," ")}:"boolean"===o?e=>s(e)?"1":"0":e=>{var t=s(e);return hn(r,t)},{fieldName:t,schemaPart:n,parsedLengths:r,getValue:s,getIndexStringPart:i}}));return n}function dn(e){var t=Math.floor(e.minimum),n=Math.ceil(e.maximum),r=e.multipleOf,o=(n-t).toString().length,i=r.toString().split("."),s=0;return i.length>1&&(s=i[1].length),{minimum:t,maximum:n,nonDecimals:o,decimals:s,roundedMinimum:t}}function hn(e,t){void 0===t&&(t=0),te.maximum&&(t=e.maximum);var n=(Math.floor(t)-e.roundedMinimum).toString().padStart(e.nonDecimals,"0");if(e.decimals>0){var r=t.toString().split(".");n+=(r.length>1?r[1]:"0").padEnd(e.decimals,"0")}return n}function pn(e,t,n){var r="";return t.forEach(((t,o)=>{var i=w(e,t),s=n[o],a=i.type;switch(a){case"string":var c=I(i.maxLength,"maxLength not set");r+="string"==typeof s?s.padEnd(c," "):"".padEnd(c," ");break;case"boolean":r+=null===s||s===A?"0":s===N||s?"1":"0";break;case"number":case"integer":var u=dn(i);if(null===s||s===A)r+="0".repeat(u.nonDecimals+u.decimals);else if(s===N)r+=hn(u,u.maximum);else{var l=hn(u,s);r+=l}break;default:throw new Error("unknown index type "+a)}})),r}function mn(e,t,n){var r="";return t.forEach(((t,o)=>{var i=w(e,t),s=n[o],a=i.type;switch(a){case"string":var c=I(i.maxLength,"maxLength not set");r+="string"==typeof s&&s!==N?s.padEnd(c," "):"".padEnd(c,s===A?" ":N);break;case"boolean":r+=null===s||s?"1":"0";break;case"number":case"integer":var u=dn(i);r+=null===s||s===N?"9".repeat(u.nonDecimals+u.decimals):s===A?"0".repeat(u.nonDecimals+u.decimals):hn(u,s);break;default:throw new Error("unknown index type "+a)}})),r}function vn(e){return e.join("||")}function yn(e,t){var n=vn(t);return on(e.indexIdByName,n)}var bn={locale:null,unique:!1};function gn(e,t,n,r){for(var o={i:n,d:r,i0:void 0,i1:void 0,i2:void 0,i3:void 0,i4:void 0,i5:void 0,i6:void 0,i7:void 0},i=0;iArray.isArray(e)?e.slice(0):[e])):[];n.push(wn);var r=new Map;return n.forEach(((n,o)=>{var i="i"+o,s=vn(n);if(r.has(s))throw new Error("duplicate index "+s);r.set(s,i),t[i]=function(e,t){var n=fn(e,t),r=n.length,o=n.map((e=>e.getIndexStringPart));return function(e){for(var t="",n=0;nun));return n=I(n).then((()=>t())),_n.set(e,n),n}function kn(e,t){t.onversionchange=n=>{e.closed||(t.close(),e.creationPromise=e.refreshIDBDatabase())}}async function Pn(e,t,n,r){var o=await sn(e.indexedDBStates,n.databaseName,(()=>async function(e,t,n){var r=In++,o=await function(e,t){return"function"==typeof t?t(e):t}(e,n.indexedDB),i=async()=>(await an(0),jn(e.databaseName,(()=>new Promise(((t,n)=>{var i=o.open(e.databaseName);i.onerror=function(t){console.error(r+": OPEN IDB DATABASE "+e.databaseName+" ERROR"),n(t)},i.onsuccess=function(e){var n=i.result;t(n),kn(s,n)},$n(s,i)}))))),s={indexedDB:o,debugId:r,closed:!1,storage:t,settings:n,refreshIDBDatabase:i,creationPromise:i(),name:e.databaseName,refCount:0,storesToOpen:[]};return s}(n,e,t)));return o.storesToOpen=o.storesToOpen.concat(r),o.refCount=o.refCount+1,o.creationPromise.then((()=>Nn(I(o)))).then((()=>I(o)))}async function Nn(e){if(0!==e.storesToOpen.length)return e.creationPromise=e.creationPromise.then((async t=>{var n=new Set(Array.from(t.objectStoreNames));if(0===e.storesToOpen.filter((e=>!n.has(An(e.collectionName,e.schema).documentStore))).length)return t;var r=t.version+1;return t.close(),jn(e.name,(()=>new Promise((async(t,n)=>{var o=e.indexedDB.open(e.name,r);o.onerror=function(t){console.error(e.debugId+": ERROR openStoresOnExistingDatabase() openRequest: error "),n(t)},o.onsuccess=function(n){var r=o.result;kn(e,r),t(r)},o.onblocked=e=>{},$n(e,o)}))))})),e.creationPromise}function An(e,t){var n=t.version;return{documentStore:e+"-"+n+"-documents",writeAheadStore:e+"-"+n+"-wal",attachmentsStore:e+"-"+n+"-attachments"}}function $n(e,t){t.onupgradeneeded=function(n){var r=t.result;e.storesToOpen.forEach((e=>{var t=r.objectStoreNames,n=An(e.collectionName,e.schema);if(!t.contains(n.documentStore)){var o=r.createObjectStore(n.documentStore,{keyPath:"i",autoIncrement:!1});r.createObjectStore(n.writeAheadStore,{keyPath:"i",autoIncrement:!1}),function(e,t){t.indexIds.forEach((t=>{e.createIndex(t,t,bn)}))}(o,e),e.schema.attachments&&r.createObjectStore(n.attachmentsStore,{keyPath:"docIdWithAttachmentId",autoIncrement:!1})}})),e.storesToOpen=[]}}function Dn(e){if(e.closed)throw new Error("RxStorageInstanceIndexedDB is closed "+e.databaseName+"-"+e.collectionName)}async function Mn(e,t){var n=e.primaryPath,r=e.internals.indexIds.length,o=e.internals.getIndexableStringByIndexNumber,i=t.objectStore(e.internals.storeNames.writeAheadStore),s=await new Promise(((e,t)=>{var n=i.get("documents");n.onerror=t,n.onsuccess=t=>{var r=n.result;e(r?JSON.parse(r.docsData):void 0)}}));if(s&&s.length>0){for(var a,c=t.objectStore(e.internals.storeNames.documentStore),u=0;u{a.onerror=t,a.onsuccess=()=>e(!0)})),i.delete("documents")}return t}async function Tn(e){var t,n=[e.internals.storeNames.documentStore,e.internals.storeNames.writeAheadStore];e.schema.attachments&&n.push(e.internals.storeNames.attachmentsStore);for(var r=100;r>0;){var o=await e.internals.state.creationPromise;r-=1;try{t=o.transaction(n,"readwrite",Sn)}catch(t){if("InvalidStateError"!==t.name&&"NotFoundError"!==t.name||!(r>0))throw t;"NotFoundError"===t.name?await Nn(e.internals.state):e.internals.state.creationPromise=e.internals.state.refreshIDBDatabase()}}return await Mn(e,I(t)),I(t)}var Cn=function(){function e(e){this.allTasksRuns=[],this.instance=e,this.txPromise=Tn(this.instance).then((t=>Mn(e,t)))}return e.prototype.addTask=function(e){var t=this.txPromise.then((t=>e(t))),n=t.catch((()=>null));this.allTasksRuns.push(n);var r=this.allTasksRuns.length;return n.then((()=>Promise.all(this.allTasksRuns))).then((()=>{this.allTasksRuns.length===r&&(this.instance.openReadonlyTransaction=void 0)})),t.catch((t=>{if("TransactionInactiveError"===t.name)return this.instance.openReadonlyTransaction=void 0,Bn(this.instance,e);throw t}))},e}();function Bn(e,t){return e.openReadonlyTransaction||(e.openReadonlyTransaction=new Cn(e)),e.openReadonlyTransaction.addTask(t)}var Rn=function(e,t){return Rn=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},Rn(e,t)};function Ln(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}Rn(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}function Kn(e,t,n,r){return new(n||(n=Promise))((function(o,i){function s(e){try{c(r.next(e))}catch(e){i(e)}}function a(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))}function qn(e,t){var n,r,o,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},s=Object.create(("function"==typeof Iterator?Iterator:Object).prototype);return s.next=a(0),s.throw=a(1),s.return=a(2),"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;s&&(s=0,a[0]&&(i=0)),i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function Un(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)s.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return s}function zn(e,t,n){if(n||2===arguments.length)for(var r,o=0,i=t.length;o1||a(e,t)}))},t&&(r[e]=t(r[e])))}function a(e,t){try{(n=o[e](t)).value instanceof Wn?Promise.resolve(n.value.v).then(c,u):l(i[0][2],n)}catch(e){l(i[0][3],e)}var n}function c(e){a("next",e)}function u(e){a("throw",e)}function l(e,t){e(t),i.shift(),i.length&&a(i[0][0],i[0][1])}}function Vn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e=Fn(e),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,o){!function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)}(r,o,(t=e[n](t)).done,t.value)}))}}}function Hn(e){return"function"==typeof e}function Gn(e){var t=e((function(e){Error.call(e),e.stack=(new Error).stack}));return t.prototype=Object.create(Error.prototype),t.prototype.constructor=t,t}Object.create,Object.create,"function"==typeof SuppressedError&&SuppressedError;var Yn=Gn((function(e){return function(t){e(this),this.message=t?t.length+" errors occurred during unsubscription:\n"+t.map((function(e,t){return t+1+") "+e.toString()})).join("\n "):"",this.name="UnsubscriptionError",this.errors=t}}));function Qn(e,t){if(e){var n=e.indexOf(t);0<=n&&e.splice(n,1)}}var Xn=function(){function e(e){this.initialTeardown=e,this.closed=!1,this._parentage=null,this._finalizers=null}var t;return e.prototype.unsubscribe=function(){var e,t,n,r,o;if(!this.closed){this.closed=!0;var i=this._parentage;if(i)if(this._parentage=null,Array.isArray(i))try{for(var s=Fn(i),a=s.next();!a.done;a=s.next())a.value.remove(this)}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=s.return)&&t.call(s)}finally{if(e)throw e.error}}else i.remove(this);var c=this.initialTeardown;if(Hn(c))try{c()}catch(e){o=e instanceof Yn?e.errors:[e]}var u=this._finalizers;if(u){this._finalizers=null;try{for(var l=Fn(u),f=l.next();!f.done;f=l.next()){var d=f.value;try{tr(d)}catch(e){o=null!=o?o:[],e instanceof Yn?o=zn(zn([],Un(o)),Un(e.errors)):o.push(e)}}}catch(e){n={error:e}}finally{try{f&&!f.done&&(r=l.return)&&r.call(l)}finally{if(n)throw n.error}}}if(o)throw new Yn(o)}},e.prototype.add=function(t){var n;if(t&&t!==this)if(this.closed)tr(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=null!==(n=this._finalizers)&&void 0!==n?n:[]).push(t)}},e.prototype._hasParent=function(e){var t=this._parentage;return t===e||Array.isArray(t)&&t.includes(e)},e.prototype._addParent=function(e){var t=this._parentage;this._parentage=Array.isArray(t)?(t.push(e),t):t?[t,e]:e},e.prototype._removeParent=function(e){var t=this._parentage;t===e?this._parentage=null:Array.isArray(t)&&Qn(t,e)},e.prototype.remove=function(t){var n=this._finalizers;n&&Qn(n,t),t instanceof e&&t._removeParent(this)},e.EMPTY=((t=new e).closed=!0,t),e}(),Zn=Xn.EMPTY;function er(e){return e instanceof Xn||e&&"closed"in e&&Hn(e.remove)&&Hn(e.add)&&Hn(e.unsubscribe)}function tr(e){Hn(e)?e():e.unsubscribe()}var nr={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1},rr={setTimeout:function(e,t){for(var n=[],r=2;r0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(t){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,t)},t.prototype._subscribe=function(e){return this._throwIfClosed(),this._checkFinalizedStatuses(e),this._innerSubscribe(e)},t.prototype._innerSubscribe=function(e){var t=this,n=this,r=n.hasError,o=n.isStopped,i=n.observers;return r||o?Zn:(this.currentObservers=null,i.push(e),new Xn((function(){t.currentObservers=null,Qn(i,e)})))},t.prototype._checkFinalizedStatuses=function(e){var t=this,n=t.hasError,r=t.thrownError,o=t.isStopped;n?e.error(r):o&&e.complete()},t.prototype.asObservable=function(){var e=new xr;return e.source=this,e},t.create=function(e,t){return new Er(e,t)},t}(xr),Er=function(e){function t(t,n){var r=e.call(this)||this;return r.destination=t,r.source=n,r}return Ln(t,e),t.prototype.next=function(e){var t,n;null===(n=null===(t=this.destination)||void 0===t?void 0:t.next)||void 0===n||n.call(t,e)},t.prototype.error=function(e){var t,n;null===(n=null===(t=this.destination)||void 0===t?void 0:t.error)||void 0===n||n.call(t,e)},t.prototype.complete=function(){var e,t;null===(t=null===(e=this.destination)||void 0===e?void 0:e.complete)||void 0===t||t.call(e)},t.prototype._subscribe=function(e){var t,n;return null!==(n=null===(t=this.source)||void 0===t?void 0:t.subscribe(e))&&void 0!==n?n:Zn},t}(Ir);function _r(e){return function(t){if(function(e){return Hn(null==e?void 0:e.lift)}(t))return t.lift((function(t){try{return e(t,this)}catch(e){this.error(e)}}));throw new TypeError("Unable to lift unknown Observable type")}}var jr=Array.isArray;function kr(e,t,n,r,o){return new Pr(e,t,n,r,o)}var Pr=function(e){function t(t,n,r,o,i,s){var a=e.call(this,t)||this;return a.onFinalize=i,a.shouldUnsubscribe=s,a._next=n?function(e){try{n(e)}catch(e){t.error(e)}}:e.prototype._next,a._error=o?function(e){try{o(e)}catch(e){t.error(e)}finally{this.unsubscribe()}}:e.prototype._error,a._complete=r?function(){try{r()}catch(e){t.error(e)}finally{this.unsubscribe()}}:e.prototype._complete,a}return Ln(t,e),t.prototype.unsubscribe=function(){var t;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){var n=this.closed;e.prototype.unsubscribe.call(this),!n&&(null===(t=this.onFinalize)||void 0===t||t.call(this))}},t}(lr),Nr=function(e){return e&&"number"==typeof e.length&&"function"!=typeof e};function Ar(e){return Hn(null==e?void 0:e.then)}function $r(e){return Hn(e[br])}function Dr(e){return Symbol.asyncIterator&&Hn(null==e?void 0:e[Symbol.asyncIterator])}function Mr(e){return new TypeError("You provided "+(null!==e&&"object"==typeof e?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}var Tr="function"==typeof Symbol&&Symbol.iterator?Symbol.iterator:"@@iterator";function Cr(e){return Hn(null==e?void 0:e[Tr])}function Br(e){return Jn(this,arguments,(function(){var t,n,r;return qn(this,(function(o){switch(o.label){case 0:t=e.getReader(),o.label=1;case 1:o.trys.push([1,,9,10]),o.label=2;case 2:return[4,Wn(t.read())];case 3:return n=o.sent(),r=n.value,n.done?[4,Wn(void 0)]:[3,5];case 4:return[2,o.sent()];case 5:return[4,Wn(r)];case 6:return[4,o.sent()];case 7:return o.sent(),[3,2];case 8:return[3,10];case 9:return t.releaseLock(),[7];case 10:return[2]}}))}))}function Rr(e){return Hn(null==e?void 0:e.getReader)}function Lr(e){if(e instanceof xr)return e;if(null!=e){if($r(e))return o=e,new xr((function(e){var t=o[br]();if(Hn(t.subscribe))return t.subscribe(e);throw new TypeError("Provided object does not correctly implement Symbol.observable")}));if(Nr(e))return r=e,new xr((function(e){for(var t=0;t{this._to=!1,function(e){const t=ro()-e.ttl,n=e.map[Symbol.iterator]();for(;;){const r=n.next().value;if(!r)return;const o=r[0];if(!(r[1]0&&void 0!==arguments[0]?arguments[0]:{},t=JSON.parse(JSON.stringify(e));return void 0===t.webWorkerSupport&&(t.webWorkerSupport=!0),t.idb||(t.idb={}),t.idb.ttl||(t.idb.ttl=45e3),t.idb.fallbackInterval||(t.idb.fallbackInterval=150),e.idb&&"function"==typeof e.idb.onclose&&(t.idb.onclose=e.idb.onclose),t.localstorage||(t.localstorage={}),t.localstorage.removeTimeout||(t.localstorage.removeTimeout=6e4),e.methods&&(t.methods=e.methods),t.node||(t.node={}),t.node.ttl||(t.node.ttl=12e4),t.node.maxParallelWrites||(t.node.maxParallelWrites=2048),void 0===t.node.useFastPath&&(t.node.useFastPath=!0),t}var io="messages",so={durability:"relaxed"};function ao(){if("undefined"!=typeof indexedDB)return indexedDB;if("undefined"!=typeof window){if(void 0!==window.mozIndexedDB)return window.mozIndexedDB;if(void 0!==window.webkitIndexedDB)return window.webkitIndexedDB;if(void 0!==window.msIndexedDB)return window.msIndexedDB}return!1}function co(e){e.commit&&e.commit()}function uo(e,t){var n=e.transaction(io,"readonly",so),r=n.objectStore(io),o=[],i=IDBKeyRange.bound(t+1,1/0);if(r.getAll){var s=r.getAll(i);return new Promise((function(e,t){s.onerror=function(e){return t(e)},s.onsuccess=function(t){e(t.target.result)}}))}return new Promise((function(e,s){var a=function(){try{return i=IDBKeyRange.bound(t+1,1/0),r.openCursor(i)}catch(e){return r.openCursor()}}();a.onerror=function(e){return s(e)},a.onsuccess=function(r){var i=r.target.result;i?i.value.ide.lastCursorId&&(e.lastCursorId=t.id),t})).filter((function(t){return function(e,t){return!(e.uuid===t.uuid||t.eMIs.has(e.id)||e.data.time0||e._addEL.internal.length>0}function Po(e,t,n){e._addEL[t].push(n),function(e){if(!e._iL&&ko(e)){var t=function(t){e._addEL[t.type].forEach((function(e){t.time>=e.time&&e.fn(t.data)}))},n=e.method.microSeconds();e._prepP?e._prepP.then((function(){e._iL=!0,e.method.onMessage(e._state,t,n)})):(e._iL=!0,e.method.onMessage(e._state,t,n))}}(e)}function No(e,t,n){e._addEL[t]=e._addEL[t].filter((function(e){return e!==n})),function(e){if(e._iL&&!ko(e)){e._iL=!1;var t=e.method.microSeconds();e.method.onMessage(e._state,null,t)}}(e)}_o._pubkey=!0,_o.prototype={postMessage:function(e){if(this.closed)throw new Error("BroadcastChannel.postMessage(): Cannot post message after channel has closed "+JSON.stringify(e));return jo(this,"message",e)},postInternal:function(e){return jo(this,"internal",e)},set onmessage(e){var t={time:this.method.microSeconds(),fn:e};No(this,"message",this._onML),e&&"function"==typeof e?(this._onML=t,Po(this,"message",t)):this._onML=null},addEventListener:function(e,t){Po(this,e,{time:this.method.microSeconds(),fn:t})},removeEventListener:function(e,t){No(this,e,this._addEL[e].find((function(e){return e.fn===t})))},close:function(){var e=this;if(!this.closed){Io.delete(this),this.closed=!0;var t=this._prepP?this._prepP:Yr;return this._onML=null,this._addEL.message=[],t.then((function(){return Promise.all(Array.from(e._uMP))})).then((function(){return Promise.all(e._befC.map((function(e){return e()})))})).then((function(){return e.method.close(e._state)}))}},get type(){return this.method.type},get isClosed(){return this.closed}};var Ao=new Map;function $o(e,t){var n=Ao.get(e);if(n)return n.refs.delete(t),0===n.refs.size?(Ao.delete(e),n.bc.close()):void 0}function Do(e,t,n,r){if(t.multiInstance){var o=r||function(e,t,n,r){var o=Ao.get(t);return o||(o={bc:new _o(["RxDB:",e,n].join("|")),refs:new Set},Ao.set(t,o)),o.refs.add(r),o.bc}(e,t.databaseInstanceToken,n.databaseName,n),i=new Ir,s=n=>{n.storageName===e&&n.databaseName===t.databaseName&&n.collectionName===t.collectionName&&n.version===t.schema.version&&i.next(n.eventBulk)};o.addEventListener("message",s);var a=n.changeStream(),c=!1,u=a.subscribe((n=>{c||o.postMessage({storageName:e,databaseName:t.databaseName,collectionName:t.collectionName,version:t.schema.version,eventBulk:n})}));n.changeStream=function(){return i.asObservable().pipe(function(){for(var e=[],t=0;t{var r=t.queryPlan,o=t.query,i=o.skip?o.skip:0,s=i+(o.limit?o.limit:1/0),a=e.internals.storeNames.documentStore,c=e.settings.batchSize?e.settings.batchSize:50,u=!1;r.selectorSatisfiedByIndex||(u=Xt(e.schema,o));var l,f=r.index,h=!r.sortSatisfiedByIndex,p=f,m=r.startKeys,v=pn(e.schema,p,m),y=r.endKeys,b=mn(e.schema,p,y),g=[],w=n.objectStore(a),x=yn(e.internals,p);l=w.index(x),u||t.query.limit||(c=1e5);var S=!1;if(await async function(e,t,n,r,o,i,s,a){var c=e.settings.IDBKeyRange?e.settings.IDBKeyRange:IDBKeyRange,u=e.internals.getIndexableStringByIndexId[s];if("function"==typeof n.getAll&&1!==r)for(var l,f=!0,d=!1,h=async function(){l&&(o=u(l.d));var e=c.bound(o,i,!f||!t.inclusiveStart,!t.inclusiveEnd);f=!1;var s=n.getAll(e,r);await new Promise(((e,t)=>{s.onerror=t,s.onsuccess=t=>{var n=t.target.result;l=k(n),0!==n.length&&!1===a(n)&&(d=!0),n.length{m.onsuccess=function(t){var n=t.target.result;if(n){var r=n.value;a([r])?n.continue():e()}else e()}}))}}(e,r,l,c,v,b,x,(e=>{for(var t=0;t{var t=Object.keys(e)[0],r=Object.values(e)[0];n.push({key:t,direction:r,getValueFn:E(t)})})),(e,t)=>{for(var r=0;r30&&!await function(e,t){var n=(e.settings.IDBKeyRange?e.settings.IDBKeyRange:IDBKeyRange).lowerBound(A,!0),r=t.getKey(n);return new Promise((e=>{r.onsuccess=()=>{e(!!r.result)}}))}(e,u)?new Map:await function(e,t,n){var r=n.length,o=new Map;if(0===r)return Promise.resolve(o);for(var i=new Array(r),s=0;s{u.onerror=n,u.onsuccess=()=>{for(var n=0;n0){var g=c.objectStore(o.storeNames.writeAheadStore).put({i:"documents",docsData:JSON.stringify(h)});await new Promise(((e,t)=>{g.onerror=t,g.onsuccess=e})),e.handleWalIdlePromise||an(100).then((()=>ln())).then((async()=>{e.handleWalIdlePromise=void 0,e.closed||await Bn(e,(()=>cn))}))}var w=void 0;if(e.schema.attachments){var x=c.objectStore(e.internals.storeNames.attachmentsStore);l.attachmentsAdd.forEach((e=>{w=x.put({docIdWithAttachmentId:En(e.documentId,e.attachmentId),length:e.attachmentData.length,type:e.attachmentData.type,data:e.attachmentData.data})})),l.attachmentsUpdate.forEach((e=>{w=x.put({docIdWithAttachmentId:En(e.documentId,e.attachmentId),length:e.attachmentData.length,type:e.attachmentData.type,data:e.attachmentData.data})})),l.attachmentsRemove.forEach((e=>{w=x.delete(En(e.documentId,e.attachmentId))}))}if(w&&await new Promise(((e,t)=>{I(w).onerror=t,I(w).onsuccess=e})),c.commit&&c.commit(),l.eventBulk.events.length>0){var S=I(l.newestRow).document;l.eventBulk.checkpoint={id:S[r],lwt:S._meta.lwt},l.eventBulk.endTime=O(),e.changes$.next(l.eventBulk)}return{error:f}}var Co=O(),Bo=function(){function e(e,t,n,r,o,i,s){this.changes$=new Ir,this.instanceId=Co++,this.storage=e,this.databaseName=t,this.collectionName=n,this.schema=r,this.internals=o,this.options=i,this.settings=s,this.primaryPath=x(this.schema.primaryKey)}var t=e.prototype;return t.updateMinKnownDocs=function(e){this.internals.minKnownDocsAmountfunction(e,t,n){var r=t.length;if(0===r)return Promise.resolve([]);for(var o=new Array,i=0;i{c.onerror=t,c.onsuccess=()=>{for(var t=0;t{var r,o,i=t.queryPlan,s=e.internals.storeNames.documentStore,a=i.index,c=a,u=i.startKeys,l=pn(e.schema,c,u),f=i.endKeys,d=mn(e.schema,c,f),h=n.objectStore(s);o=1===a.length&&a[0]===e.primaryPath?yn(e.internals,["_deleted",e.primaryPath]):yn(e.internals,c),r=h.index(o);var p=(e.settings.IDBKeyRange?e.settings.IDBKeyRange:IDBKeyRange).bound(l,d,!i.inclusiveStart,!i.inclusiveEnd),m=r.count(p);return{count:await new Promise(((e,t)=>{m.onsuccess=function(){e(m.result)},m.onerror=t})),mode:"fast"}}))}(this,e);return this.updateMinKnownDocs(t.count),t}var n=await Mo(this,e);return this.updateMinKnownDocs(n.documents.length),{count:n.documents.length,mode:"slow"}},t.changeStream=function(){return this.changes$.asObservable()},t.cleanup=async function(e){var t=this.internals.state;await t.creationPromise;var n=this.settings.IDBKeyRange;return Dn(this),Bn(this,(async t=>{var r=t.objectStore(this.internals.storeNames.documentStore),o=this.settings.batchSize,i=O()-e,s=yn(this.internals,wn),a=r.index(s),c=pn(this.schema,wn,[!0,1]),u=mn(this.schema,wn,[!0,i]),l=n.bound(c,u,!0,!0),f=await new Promise(((e,t)=>{var n=a.getAll(l,o);n.onerror=t,n.onsuccess=function(t){e(t.target.result)}}));return await Promise.all(f.map((e=>new Promise(((t,n)=>{var o=e.i,i=r.delete(o);i.onerror=n,i.onsuccess=()=>t()}))))),f.length{var t=[e.objectStore(this.internals.storeNames.documentStore),e.objectStore(this.internals.storeNames.writeAheadStore)];return this.schema.attachments&&t.push(e.objectStore(this.internals.storeNames.attachmentsStore)),await Promise.all(t.map((e=>new Promise(((t,n)=>{var r=e.clear();r.onerror=n,r.onsuccess=t}))))),this.close()}))},t.getAttachmentData=async function(e,t){var n=this.internals.state;return await n.creationPromise,Dn(this),Bn(this,(n=>{var r=n.objectStore(this.internals.storeNames.attachmentsStore),o=En(e,t);return new Promise(((n,i)=>{var s=r.get(o);s.onsuccess=()=>{var r=s.result;r?n(r.data):i("attachment missing documentId: "+e+" attachmentId: "+t)}}))}))},t.close=async function(){return this.closed||(this.closed=(async()=>(await this.internals.state.creationPromise,await Bn(this,(async e=>{})),this.changes$.complete(),async function(e){if(!e.closed&&(e.refCount=e.refCount-1,0===e.refCount))return e.closed=!0,e.storage.indexedDBStates.delete(e.name),e.creationPromise.then((e=>e.close()))}(this.internals.state)))()),this.closed},t.conflictResultionTasks=function(){return(new Ir).asObservable()},t.resolveConflictResultionTask=function(e){return un},e}(),Ro=function(){function e(e){this.name=On,this.rxdbVersion=rn,this.indexedDBStates=new Map,this.settings=e}return e.prototype.createStorageInstance=function(e){return function(e){if(e.schema.keyCompression)throw d("UT5",{args:{params:e}});if((t=e.schema).encrypted&&t.encrypted.length>0||t.attachments&&t.attachments.encrypted)throw d("UT6",{args:{params:e}});var t;if(e.schema.attachments&&e.schema.attachments.compression)throw d("UT7",{args:{params:e}})}(e),async function(e,t,n){var r=xn(t.schema),o=Array.from(r.indexIdByName.values()),i=await Pn(e,n,t,[{collectionName:t.collectionName,schema:t.schema,indexIds:o}]);await i.creationPromise;var s={state:i,storeNames:An(t.collectionName,t.schema),getIndexableStringByIndexId:r.monadByIndexId,getIndexableStringByIndexNumber:Object.values(r.monadByIndexId),indexIdByName:r.indexIdByName,indexNames:Object.keys(r),indexIds:o,minKnownDocsAmount:0},a=new Bo(e,t.databaseName,t.collectionName,t.schema,s,t.options,n);return await Do(On,t,a),a}(this,e,Object.assign({},this.settings,e.options))},e}();function Lo(e,t){return _r((function(n,r){var o=0;n.subscribe(kr(r,(function(n){return e.call(t,n,o++)&&r.next(n)})))}))}function Ko(e,t){if(e===t)return!0;if(e&&t&&"object"==typeof e&&"object"==typeof t){if(e.constructor!==t.constructor)return!1;var n,r;if(Array.isArray(e)){if((n=e.length)!==t.length)return!1;for(r=n;0!=r--;)if(!Ko(e[r],t[r]))return!1;return!0}if(e.constructor===RegExp)return e.source===t.source&&e.flags===t.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===t.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===t.toString();var o=Object.keys(e);if((n=o.length)!==Object.keys(t).length)return!1;for(r=n;0!=r--;)if(!Object.prototype.hasOwnProperty.call(t,o[r]))return!1;for(r=n;0!=r--;){var i=o[r];if(!Ko(e[i],t[i]))return!1}return!0}return e!=e&&t!=t}function qo(e,t){return{connectionId:e.connectionId,answerTo:e.requestId,method:e.method,error:(n=t,{name:n.name,message:n.message,rxdb:n.rxdb,parameters:n.parameters,extensions:n.extensions,code:n.code,url:n.url,stack:n.stack?n.stack.replace(/\n/g," \n "):void 0})};var n}function Fo(e,t){return{connectionId:e.connectionId,answerTo:e.requestId,method:e.method,return:t}}!function(e){var t;console.log("exposeWorkerRxStorage()");var n=new Ir;if("undefined"!=typeof self&&"object"==typeof self.onconnect){var r=new Map;self.onconnect=e=>{var t=e.ports[0];t.onmessage=e=>{var o=e.data;r.set(o.connectionId,t),n.next(o)}},t={storage:e.storage,messages$:n,send(e){on(r,e.connectionId).postMessage(e)}}}else addEventListener("message",(e=>{var t=e.data;n.next(t)})),t={storage:e.storage,messages$:n,send(e){postMessage(e)}};!function(e){var t=new Map;function n(t){if(e.storage)return e.storage.createStorageInstance(t);if(e.database){var n=Array.from(e.database.storageInstances),r=t.collectionName,o=n.find((e=>e.collectionName===r));if(!o)throw console.dir(n),new Error("storageInstance does not exist "+JSON.stringify({collectionName:r}));var i=t.schema;if(!Ko(i,o.schema))throw new Error("Wrong schema "+JSON.stringify({schema:i,existingSchema:o.schema}));return Promise.resolve(o)}throw new Error("no base given")}e.messages$.pipe(Lo((e=>"custom"===e.method))).subscribe((async t=>{if(e.customRequestHandler)try{var n=await e.customRequestHandler(t.params);e.send(Fo(t,n))}catch(n){e.send(qo(t,n))}else e.send(qo(t,new Error("Remote storage: cannot resolve custom request because settings.customRequestHandler is not set")))})),e.messages$.pipe(Lo((e=>"create"===e.method))).subscribe((async r=>{var o=r.connectionId;if(!Array.isArray(r.params)){var i=r.params,s=i.collectionName,a=[i.databaseName,i.collectionName,i.schema.version].join("|"),c=t.get(a);if(c){if(!Ko(i.schema,c.params.schema))return void e.send(qo(r,new Error("Remote storage: schema not equal to existing storage")))}else try{c={storageInstancePromise:n(i),connectionIds:new Set,params:i},t.set(a,c),await c.storageInstancePromise}catch(t){return void e.send(qo(r,t))}c.connectionIds.add(r.connectionId);var u=[],l=await c.storageInstancePromise;u.push(l.changeStream().subscribe((t=>{var n={connectionId:o,answerTo:"changestream",method:"changeStream",return:t};e.send(n)}))),u.push(l.conflictResultionTasks().subscribe((t=>{var n={connectionId:o,answerTo:"conflictResultionTasks",method:"conflictResultionTasks",return:t};e.send(n)})));var f=!1;if(e.database){var d=e.database,h=d.collections[s];h?h.onDestroy.push((()=>p())):d.onDestroy.push((()=>p()))}u.push(e.messages$.pipe(Lo((e=>e.connectionId===o))).subscribe((async t=>{var n,r=t;if("create"!==r.method&&"custom"!==r.method&&Array.isArray(r.params))try{if("close"===r.method&&e.database)return void e.send(Fo(r,null));if("close"===r.method&&I(c).connectionIds.size>1)return e.send(Fo(r,null)),I(c).connectionIds.delete(o),void u.forEach((e=>e.unsubscribe()));n="getChangedDocumentsSince"!==r.method||l.getChangedDocumentsSince?await l[r.method](r.params[0],r.params[1],r.params[2],r.params[3]):await nn(l,r.params[0],r.params[1]),"close"!==r.method&&"remove"!==r.method||p(),e.send(Fo(r,n))}catch(t){e.send(qo(r,t))}}))),e.send(Fo(r,"ok"))}function p(){f||(f=!0,u.forEach((e=>e.unsubscribe())),I(c).connectionIds.delete(o),t.delete(a))}}))}(t)}({storage:function(e={}){var t=e.IDBKeyRange?e.IDBKeyRange:IDBKeyRange,n=e.indexedDB?e.indexedDB:indexedDB,r=Object.assign({batchSize:300,transactionDurability:"relaxed"},e,{IDBKeyRange:t,indexedDB:n});return new Ro(r)}({})})})(); \ No newline at end of file +(()=>{function e(t){return(e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(t)}function t(t){var n=function(t,n){if("object"!=e(t)||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var o=r.call(t,n||"default");if("object"!=e(o))return o;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===n?String:Number)(t)}(t,"string");return"symbol"==e(n)?n:n+""}function n(e,n){for(var r=0;r!1,deepFreezeWhenDevMode:e=>e,tunnelErrorMessage:e=>"\n RxDB Error-Code: "+e+".\n Hint: Error messages are not included in RxDB core to reduce build size.\n To show the full error messages and to ensure that you do not make any mistakes when using RxDB,\n use the dev-mode plugin when you are in development mode: https://rxdb.info/dev-mode.html?console=error\n "};function c(e,t,n){return"\n"+e+"\n"+function(e){var t="";return 0===Object.keys(e).length?t:(t+="-".repeat(20)+"\n",t+="Parameters:\n",t+=Object.keys(e).map((t=>{var n="[object Object]";try{n="errors"===t?e[t].map((e=>JSON.stringify(e,Object.getOwnPropertyNames(e)))):JSON.stringify(e[t],(function(e,t){return void 0===t?null:t}),2)}catch(e){}return t+": "+n})).join("\n"),t+="\n")}(n)}var u=function(e){function t(t,n,r={}){var o,i=c(n,0,r);return(o=e.call(this,i)||this).code=t,o.message=i,o.url=l(t),o.parameters=r,o.rxdb=!0,o}var o,i;return i=e,(o=t).prototype=Object.create(i.prototype),o.prototype.constructor=o,r(o,i),t.prototype.toString=function(){return this.message},function(e,t,r){return t&&n(e.prototype,t),r&&n(e,r),Object.defineProperty(e,"prototype",{writable:!1}),e}(t,[{key:"name",get:function(){return"RxError ("+this.code+")"}},{key:"typeError",get:function(){return!1}}])}(s(Error));function l(e){return"https://rxdb.info/errors.html?console=errors#"+e}function f(e){return"\nFind out more about this error here: "+l(e)+" \n"}function d(e,t){return new u(e,a.tunnelErrorMessage(e)+f(e),t)}var h=/\./g,p="abcdefghijklmnopqrstuvwxyz",m=e=>{var t=typeof e;return null!==e&&("object"===t||"function"===t)},v=new Set(["__proto__","prototype","constructor"]),y=new Set("0123456789");function b(e){var t=[],n="",r="start",o=!1;for(var i of e)switch(i){case"\\":if("index"===r)throw new Error("Invalid character in an index");if("indexEnd"===r)throw new Error("Invalid character after an index");o&&(n+=i),r="property",o=!o;break;case".":if("index"===r)throw new Error("Invalid character in an index");if("indexEnd"===r){r="property";break}if(o){o=!1,n+=i;break}if(v.has(n))return[];t.push(n),n="",r="property";break;case"[":if("index"===r)throw new Error("Invalid character in an index");if("indexEnd"===r){r="index";break}if(o){o=!1,n+=i;break}if("property"===r){if(v.has(n))return[];t.push(n),n=""}r="index";break;case"]":if("index"===r){t.push(Number.parseInt(n,10)),n="",r="indexEnd";break}if("indexEnd"===r)throw new Error("Invalid character after an index");default:if("index"===r&&!y.has(i))throw new Error("Invalid character in an index");if("indexEnd"===r)throw new Error("Invalid character after an index");"start"===r&&(r="property"),o&&(o=!1,n+="\\"),n+=i}switch(o&&(n+="\\"),r){case"property":if(v.has(n))return[];t.push(n);break;case"index":throw new Error("Index was not closed");case"start":t.push("")}return t}function g(e,t){if("number"!=typeof t&&Array.isArray(e)){var n=Number.parseInt(t,10);return Number.isInteger(n)&&e[n]===e[t]}return!1}function w(e,t){var n=function(e,t,n){if(Array.isArray(t)&&(t=t.join(".")),!t.includes(".")&&!t.includes("["))return e[t];if(!m(e)||"string"!=typeof t)return void 0===n?e:n;var r=b(t);if(0===r.length)return n;for(var o=0;ot[e]:e=>{for(var r=e,o=0;o"desc"===Object.values(e)[0])),i=new Set;Object.keys(n).forEach((t=>{var r=w(e,t);r&&"boolean"===r.type&&Object.prototype.hasOwnProperty.call(n[t],"$eq")&&i.add(t)}));var s,a=t.sort.map((e=>Object.keys(e)[0])).filter((e=>!i.has(e))).join(","),c=-1;if(r.forEach((e=>{var r=!0,u=!0,l=e.map((e=>{var t=n[e],o=t?Object.keys(t):[],i={};return t&&o.length?o.forEach((e=>{if(D.has(e)){var n=function(e,t){switch(e){case"$eq":return{startKey:t,endKey:t,inclusiveEnd:!0,inclusiveStart:!0};case"$lte":return{endKey:t,inclusiveEnd:!0};case"$gte":return{startKey:t,inclusiveStart:!0};case"$lt":return{endKey:t,inclusiveEnd:!1};case"$gt":return{startKey:t,inclusiveStart:!1};default:throw new Error("SNH")}}(e,t[e]);i=Object.assign(i,n)}})):i={startKey:u?$:_,endKey:r?_:$,inclusiveStart:!0,inclusiveEnd:!0},void 0===i.startKey&&(i.startKey=$),void 0===i.endKey&&(i.endKey=_),void 0===i.inclusiveStart&&(i.inclusiveStart=!0),void 0===i.inclusiveEnd&&(i.inclusiveEnd=!0),u&&!i.inclusiveStart&&(u=!1),r&&!i.inclusiveEnd&&(r=!1),i})),f=l.map((e=>e.startKey)),d=l.map((e=>e.endKey)),h={index:e,startKeys:f,endKeys:d,inclusiveEnd:r,inclusiveStart:u,sortSatisfiedByIndex:!o&&a===e.filter((e=>!i.has(e))).join(","),selectorSatisfiedByIndex:T(e,t.selector,f,d)},p=function(e,t,n){var r=0,o=e=>{e>0&&(r+=e)},i=10,s=P(n.startKeys,(e=>e!==$&&e!==_));o(s*i);var a=P(n.startKeys,(e=>e!==_&&e!==$));o(a*i);var c=P(n.startKeys,((e,t)=>e===n.endKeys[t]));return o(c*i*1.5),o(n.sortSatisfiedByIndex?5:0),r}(0,0,h);(p>=c||t.index)&&(c=p,s=h)})),!s)throw d("SNH",{query:t});return s}var D=new Set(["$eq","$gt","$gte","$lt","$lte"]),A=new Set(["$eq","$gt","$gte"]),M=new Set(["$eq","$lt","$lte"]);function T(e,t,n,r){var o=Object.entries(t).find((([t,n])=>!e.includes(t)||Object.entries(n).find((([e,t])=>!D.has(e)))));if(o)return!1;if(t.$and||t.$or)return!1;var i=[],s=new Set;for(var[a,c]of Object.entries(t)){if(!e.includes(a))return!1;var u=Object.keys(c).filter((e=>A.has(e)));if(u.length>1)return!1;var l=u[0];if(l&&s.add(a),"$eq"!==l){if(i.length>0)return!1;i.push(l)}}var f=[],d=new Set;for(var[h,p]of Object.entries(t)){if(!e.includes(h))return!1;var m=Object.keys(p).filter((e=>M.has(e)));if(m.length>1)return!1;var v=m[0];if(v&&d.add(h),"$eq"!==v){if(f.length>0)return!1;f.push(v)}}var y=0;for(var b of e){for(var g of[s,d]){if(!g.has(b)&&g.size>0)return!1;g.delete(b)}if(n[y]!==r[y]&&s.size>0&&d.size>0)return!1;y++}return!0}class C extends Error{}const B=Symbol("missing"),R=Object.freeze(new Error("mingo: cycle detected while processing object/array")),K=e=>{const t=he(e);let n=0,r=t.length;for(;r;)n=(n<<5)-n^t.charCodeAt(--r);return n>>>0},L=e=>"object"!=typeof e&&"function"!=typeof e||null===e,q=e=>L(e)||ee(e)||te(e),z={undefined:1,null:2,number:3,string:4,symbol:5,object:6,array:7,arraybuffer:8,boolean:9,date:10,regexp:11,function:12},F=(e,t)=>{e===B&&(e=void 0),t===B&&(t=void 0);const[n,r]=[e,t].map((e=>z[H(e)]||0));return n!==r?n-r:de(e,t)?0:et?1:0};class U extends Map{#e=K;#t=new Map;#n=e=>{const t=this.#e(e);return[(this.#t.get(t)||[]).find((t=>de(t,e))),t]};constructor(){super()}static init(e){const t=new U;return e&&(t.#e=e),t}clear(){super.clear(),this.#t.clear()}delete(e){if(L(e))return super.delete(e);const[t,n]=this.#n(e);return!!super.delete(t)&&(this.#t.set(n,this.#t.get(n).filter((e=>!de(e,t)))),!0)}get(e){if(L(e))return super.get(e);const[t,n]=this.#n(e);return super.get(t)}has(e){if(L(e))return super.has(e);const[t,n]=this.#n(e);return super.has(t)}set(e,t){if(L(e))return super.set(e,t);const[n,r]=this.#n(e);if(super.has(n))super.set(n,t);else{super.set(e,t);const n=this.#t.get(r)||[];n.push(e),this.#t.set(r,n)}return this}get size(){return super.size}}function W(e,t){if(!e)throw new C(t)}const V=Object.keys(z).reduce(((e,t)=>(e["[object "+t[0].toUpperCase()+t.substring(1)+"]"]=t,e)),{});function H(e){const t=Object.prototype.toString.call(e);return"[object Object]"===t?e?.constructor?.name?.toLowerCase()||"object":V[t]||t.substring(8,t.length-1).toLowerCase()}const J=e=>"boolean"==typeof e,G=e=>"string"==typeof e,Y=e=>!isNaN(e)&&"number"==typeof e,Q=Array.isArray;function X(e){if(!e)return!1;const t=Object.getPrototypeOf(e);return(t===Object.prototype||null===t)&&"object"===H(e)}const Z=e=>!L(e),ee=e=>e instanceof Date,te=e=>e instanceof RegExp,ne=e=>"function"==typeof e,re=e=>null==e,oe=e=>re(e)||G(e)&&!e||Q(e)&&0===e.length||X(e)&&0===Object.keys(e).length,ie=e=>Q(e)?e:[e],se=(e,t)=>!!e&&Object.prototype.hasOwnProperty.call(e,t),ae=(e,t)=>{if(re(e)||J(e)||Y(e)||G(e))return e;if(ee(e))return new Date(e);if(te(e))return new RegExp(e);if((e=>"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView(e))(e))return new(0,e.constructor)(e);if(t instanceof Set||(t=new Set),t.has(e))throw R;t.add(e);try{if(Q(e)){const n=new Array(e.length);for(let r=0;re===B;function ue(e,t){if(ce(e)||re(e))return t;if(ce(t)||re(t))return e;if(L(e)||L(t))return t;Q(e)&&Q(t)&&W(e.length===t.length,"arrays must be of equal length to merge.");for(const n of Object.keys(t))e[n]=ue(e[n],t[n]);return e}function le(e,t=1){const n=new Array;return function e(t,r){for(let o=0,i=t.length;o0||r<0)?e(t[o],Math.max(-1,r-1)):n.push(t[o])}(e,t),n}function fe(e){for(;e;){if(Object.getOwnPropertyNames(e).includes("toString"))return e.toString!==Object.prototype.toString;e=Object.getPrototypeOf(e)}return!1}function de(e,t){if(e===t||Object.is(e,t))return!0;if(null===e||null===t)return!1;if(typeof e!=typeof t)return!1;if("object"!=typeof e)return!1;if(e.constructor!==t.constructor)return!1;if(e instanceof Date)return+e==+t;if(e instanceof RegExp)return e.toString()===t.toString();const n=e.constructor;if(n===Array||n===Object){const n=Object.keys(e).sort(),r=Object.keys(t).sort();if(n.length!==r.length)return!1;for(let o=0,i=n[o];o{if(null===e)return"null";if(void 0===e)return"undefined";if(G(e)||Y(e)||J(e))return JSON.stringify(e);if(ee(e))return e.toISOString();if(te(e)||(e=>"symbol"==typeof e)(e)||ne(e))return e.toString();if(t instanceof Set||(t=new Set),t.has(e))throw R;try{if(t.add(e),Q(e))return"["+e.map((e=>he(e,t))).join(",")+"]";if(X(e))return"{"+Object.keys(e).sort().map((n=>`${n}:${he(e[n],t)}`)).join()+"}";const n=fe(e)?e.toString():he(function(e){const t={};for(;e;){for(const n of Object.getOwnPropertyNames(e))n in t||(t[n]=e[n]);e=Object.getPrototypeOf(e)}return t}(e),t);return H(e)+"("+n+")"}finally{t.delete(e)}};function pe(e,t){return re(e)?null:(t=t||K)(e)}function me(e,t,n=K){if(e.length<1)return new Map;const r=new Map,o=new Map;for(let i=0;ide(e,a))):null;re(e)?(o.set(a,[s]),r.has(c)?r.get(c).push(a):r.set(c,[a])):o.get(e).push(s)}}return o}function ve(e,t){return Z(e)?e[t]:void 0}function ye(e,t,n){let r=0;const o=q(e)?e:function e(t,n){let o=t;for(let t=0;t0)break;r+=1;const i=n.slice(t);o=o.reduce(((t,n)=>{const r=e(n,i);return void 0!==r&&t.push(r),t}),[]);break}if(o=ve(o,i),void 0===o)break}return o}(e,t.split("."));return Q(o)&&n?.unwrapArray?function(e,t){if(t<1)return e;for(;t--&&1===e.length;)e=e[0];return e}(o,r):o}function be(e,t,n){const r=t.indexOf("."),o=-1==r?t:t.substring(0,r),i=t.substring(r+1),s=-1!=r;if(Q(e)){const r=/^\d+$/.test(o),a=r&&n?.preserveIndex?[...e]:[];if(r){const t=parseInt(o);let r=ve(e,t);s&&(r=be(r,i,n)),n?.preserveIndex?a[t]=r:a.push(r)}else for(const r of e){const e=be(r,t,n);n?.preserveMissing?a.push(null==e?B:e):(null!=e||n?.preserveIndex)&&a.push(e)}return a}const a=n?.preserveKeys?{...e}:{};let c=ve(e,o);if(s&&(c=be(c,i,n)),void 0!==c)return a[o]=c,a}function ge(e){if(Q(e))for(let t=e.length-1;t>=0;t--)e[t]===B?e.splice(t,1):ge(e[t]);else if(X(e))for(const t in e)se(e,t)&&ge(e[t])}const we=/^\d+$/;function xe(e,t,n,r){const o=t.split("."),i=o[0],s=o.slice(1).join(".");if(1===o.length)(X(e)||Q(e)&&we.test(i))&&n(e,i);else{r?.buildGraph&&re(e[i])&&(e[i]={});const t=e[i];if(!t)return;const a=!!(o.length>1&&we.test(o[1]));Q(t)&&r?.descendArray&&!a?t.forEach((e=>xe(e,s,n,r))):xe(t,s,n,r)}}function Se(e,t,n){xe(e,t,((e,t)=>{e[t]=ne(n)?n(e[t]):n}),{buildGraph:!0})}function Oe(e,t,n){xe(e,t,((e,t)=>{if(Q(e)){if(/^\d+$/.test(t))e.splice(parseInt(t),1);else if(n&&n.descendArray)for(const n of e)X(n)&&delete n[t]}else X(e)&&delete e[t]}),n)}const Ie=/^\$[a-zA-Z0-9_]+$/;function Ee(e){return Ie.test(e)}function ke(e){if(q(e))return te(e)?{$regex:e}:{$eq:e};if(Z(e)){if(!Object.keys(e).some(Ee))return{$eq:e};if(se(e,"$regex")){const t={...e};return t.$regex=new RegExp(e.$regex,e.$options),delete t.$options,t}}return e}var je=(e=>(e[e.CLONE_OFF=0]="CLONE_OFF",e[e.CLONE_INPUT=1]="CLONE_INPUT",e[e.CLONE_OUTPUT=2]="CLONE_OUTPUT",e[e.CLONE_ALL=3]="CLONE_ALL",e))(je||{});class Pe{#r;#o;#i;constructor(e,t,n){this.#r=e,this.update(t,n)}static init(e,t,n){return e instanceof Pe?new Pe(e.#r,e.root??t,{...e.#i,...n,variables:Object.assign({},e.#i?.variables,n?.variables)}):new Pe(e,t,n)}update(e,t){this.#o=e;const n=Object.assign({},this.#i?.variables,t?.variables);return Object.keys(n).length?this.#i={...t,variables:n}:this.#i=t??{},this}getOptions(){return Object.freeze({...this.#r,context:_e.from(this.#r.context)})}get root(){return this.#o}get local(){return this.#i}get idKey(){return this.#r.idKey}get collation(){return this.#r?.collation}get processingMode(){return this.#r?.processingMode||0}get useStrictMode(){return this.#r?.useStrictMode}get scriptEnabled(){return this.#r?.scriptEnabled}get useGlobalContext(){return this.#r?.useGlobalContext}get hashFunction(){return this.#r?.hashFunction}get collectionResolver(){return this.#r?.collectionResolver}get jsonSchemaValidator(){return this.#r?.jsonSchemaValidator}get variables(){return this.#r?.variables}get context(){return this.#r?.context}}class _e{#s=new Map;constructor(){}static init(){return new _e}static from(e){const t=_e.init();return re(e)||e.#s.forEach(((e,n)=>t.addOperators(n,e))),t}addOperators(e,t){this.#s.has(e)||this.#s.set(e,{});for(const[n,r]of Object.entries(t))this.getOperator(e,n)||(this.#s.get(e)[n]=r);return this}getOperator(e,t){return(this.#s.get(e)??{})[t]??null}addAccumulatorOps(e){return this.addOperators("accumulator",e)}addExpressionOps(e){return this.addOperators("expression",e)}addQueryOps(e){return this.addOperators("query",e)}addPipelineOps(e){return this.addOperators("pipeline",e)}addProjectionOps(e){return this.addOperators("projection",e)}addWindowOps(e){return this.addOperators("window",e)}}const $e=_e.init();function Ne(e,t){for(const[n,r]of Object.entries(t)){W(ne(r)&&Ee(n),`'${n}' is not a valid operator`);const t=De(e,n,null);W(!t||r===t,`${n} already exists for '${e}' operators. Cannot change operator function once registered.`)}switch(e){case"accumulator":$e.addAccumulatorOps(t);break;case"expression":$e.addExpressionOps(t);break;case"pipeline":$e.addPipelineOps(t);break;case"projection":$e.addProjectionOps(t);break;case"query":$e.addQueryOps(t);break;case"window":$e.addWindowOps(t)}}function De(e,t,n){const{context:r,useGlobalContext:o}=n||{},i=r?r.getOperator(e,t):null;return!i&&o?$e.getOperator(e,t):i}function Ae(e,t,n,r){const o=Pe.init(r,e);return n&&Ee(n)?Ce(e,t,n,o):Te(e,t,o)}const Me=["$$ROOT","$$CURRENT","$$REMOVE","$$NOW"];function Te(e,t,n){if(G(t)&&t.length>0&&"$"===t[0]){if(Be.includes(t))return t;let r=n.root;const o=t.split(".");if(Me.includes(o[0])){switch(o[0]){case"$$ROOT":break;case"$$CURRENT":r=e;break;case"$$REMOVE":r=void 0;break;case"$$NOW":r=new Date}t=t.slice(o[0].length+1)}else if("$$"===o[0].slice(0,2)){r=Object.assign({},n.variables,{this:e},n?.local?.variables);const i=o[0].slice(2);W(se(r,i),`Use of undefined variable: ${i}`),t=t.slice(2)}else t=t.slice(1);return""===t?r:ye(r,t)}if(Q(t))return t.map((t=>Te(e,t,n)));if(X(t)){const r={},o=Object.entries(t);for(const[t,i]of o){if(Ee(t))return W(1==o.length,"expression must have single operator."),Ce(e,i,t,n);r[t]=Te(e,i,n)}return r}return t}function Ce(e,t,n,r){const o=De("expression",n,r);if(o)return o(e,t,r);const i=De("accumulator",n,r);return W(!!i,`accumulator '${n}' is not registered.`),Q(e)||(e=Te(e,t,r),t=null),W(Q(e),`arguments must resolve to array for ${n}.`),i(e,t,r)}const Be=["$$KEEP","$$PRUNE","$$DESCEND"];function Re(e){return e instanceof qe?e:new qe(e)}function Ke(e,t){const n=e.slice(t+1);e.splice(t),Array.prototype.push.apply(e,n)}const Le=new Error;class qe{constructor(e){let t;if(this.#a=[],this.#c=[],this.isDone=!1,e instanceof Function&&(e={next:e}),(n=e)&&"object"==typeof n&&n?.next instanceof Function){const n=e;t=()=>{const e=n.next();if(e.done)throw Le;return e.value}}else if(Q(e)){const n=e,r=n.length;let o=0;t=()=>{if(o0?this.push(2,e):this}drop(e){return e>0?this.push(3,e):this}transform(e){const t=this;let n;return Re((()=>(n||(n=Re(e(t.value()))),n.next())))}value(){return this.isDone||(this.isDone=this.#u(!0).done),this.#c}each(e){for(;;){const t=this.next();if(t.done)break;if(!1===e(t.value))return!1}return!0}reduce(e,t){let n=this.next();for(void 0!==t||n.done||(t=n.value,n=this.next());!n.done;)t=e(t,n.value),n=this.next();return t}size(){return this.reduce(((e,t)=>++e),0)}[Symbol.iterator](){return this}}const ze=(e,t,n)=>oe(t)?e:(Ue(t,n),e.map(Fe(t,Pe.init(n))));function Fe(e,t,n=!0){const r=t.idKey,o=Object.keys(e),i=new Array,s=new Array,a={};for(const r of o){const o=e[r];if(Y(o)||J(o))o?s.push(r):i.push(r);else if(Q(o))a[r]=e=>o.map((n=>Ae(e,n,null,t.update(e))??null));else if(X(o)){const e=Object.keys(o),i=1==e.length?e[0]:"",s=De("projection",i,t);s?"$slice"!==i||ie(o[i]).every(Y)?a[r]=e=>s(e,o[i],r,t.update(e)):a[r]=e=>Ae(e,o,r,t.update(e)):Ee(i)?a[r]=e=>Ae(e,o[i],i,t):(Ue(o,t),a[r]=e=>{if(!se(e,r))return Ae(e,o,null,t);n&&t.update(e);const i=ye(e,r),s=Fe(o,t,!1);return Q(i)?i.map(s):X(i)?s(i):s(e)})}else a[r]=G(o)&&"$"===o[0]?e=>Ae(e,o,r,t):e=>o}const c=Object.keys(a),u=i.includes(r);if(n&&u&&1===i.length&&!s.length&&!c.length)return e=>{const t={...e};return delete t[r],t};const l=n&&!u&&!s.includes(r),f={preserveMissing:!0};return e=>{const t={};if(i.length&&!s.length){ue(t,e);for(const e of i)Oe(t,e,{descendArray:!0})}for(const n of s)ue(t,be(e,n,f)??{});s.length&&ge(t);for(const n of c){const r=a[n](e);void 0===r?Oe(t,n,{descendArray:!0}):Se(t,n,r)}return l&&se(e,r)&&(t[r]=ye(e,r)),t}}function Ue(e,t){let n=!1,r=!1;for(const[o,i]of Object.entries(e))W(!o.startsWith("$"),"Field names may not start with '$'."),W(!o.endsWith(".$"),"Positional projection operator '$' is not supported."),o!==t?.idKey&&(0===i||!1===i?n=!0:1!==i&&!0!==i||(r=!0),W(!(n&&r),"Projection cannot have a mix of inclusion and exclusion."))}const We=(e,t,n)=>{if(oe(t)||!X(t))return e;let r=F;const o=n.collation;return X(o)&&G(o.locale)&&(r=function(e){const t={sensitivity:Ve[e.strength||3],caseFirst:"off"===e.caseFirst?"false":e.caseFirst||"false",numeric:e.numericOrdering||!1,ignorePunctuation:"shifted"===e.alternate};!0===(e.caseLevel||!1)&&("base"===t.sensitivity&&(t.sensitivity="case"),"accent"===t.sensitivity&&(t.sensitivity="variant"));const n=new Intl.Collator(e.locale,t);return(e,t)=>{if(!G(e)||!G(t))return F(e,t);const r=n.compare(e,t);return r<0?-1:r>0?1:0}}(o)),e.transform((e=>{const o=Object.keys(t);for(const i of o.reverse()){const o=me(e,(e=>ye(e,i)),n.hashFunction),s=Array.from(o.keys()).sort(r);-1===t[i]&&s.reverse();let a=0;for(const t of s)for(const n of o.get(t))e[a++]=n;W(a==e.length,"bug: counter must match collection size.")}return e}))},Ve={1:"base",2:"accent",3:"variant"},He={$sort:We,$skip:(e,t,n)=>e.drop(t),$limit:(e,t,n)=>e.take(t)};class Je{#l;#f;#d;#r;#s={};#h=null;#p=[];constructor(e,t,n,r){this.#l=e,this.#f=t,this.#d=n,this.#r=r}fetch(){if(this.#h)return this.#h;this.#h=Re(this.#l).filter(this.#f);const e=this.#r.processingMode;e&je.CLONE_INPUT&&this.#h.map(ae);for(const e of["$sort","$skip","$limit"])se(this.#s,e)&&(this.#h=He[e](this.#h,this.#s[e],this.#r));return Object.keys(this.#d).length&&(this.#h=ze(this.#h,this.#d,this.#r)),e&je.CLONE_OUTPUT&&this.#h.map(ae),this.#h}fetchAll(){const e=Re([...this.#p]);return this.#p=[],function(...e){let t=0;return Re((()=>{for(;t0)return this.#p.pop();const e=this.fetch().next();return e.done?void 0:e.value}hasNext(){if(this.#p.length>0)return!0;const e=this.fetch().next();return!e.done&&(this.#p.push(e.value),!0)}map(e){return this.all().map(e)}forEach(e){this.all().forEach(e)}[Symbol.iterator](){return this.fetchAll()}}const Ge=new Set(Array.from(["$and","$or","$nor","$expr","$jsonSchema"]));class Ye{#m;#r;#v;constructor(e,t){this.#v=ae(e),this.#r=function(e){return e instanceof Pe?e.getOptions():Object.freeze({idKey:"_id",scriptEnabled:!0,useStrictMode:!0,useGlobalContext:!0,processingMode:0,...e,context:e?.context?_e.from(e?.context):_e.init()})}(t),this.#m=[],this.compile()}compile(){W(X(this.#v),`query criteria must be an object: ${JSON.stringify(this.#v)}`);const e={};for(const[t,n]of Object.entries(this.#v)){if("$where"===t)W(this.#r.scriptEnabled,"$where operator requires 'scriptEnabled' option to be true."),Object.assign(e,{field:t,expr:n});else if(Ge.has(t))this.processOperator(t,t,n);else{W(!Ee(t),`unknown top level operator: ${t}`);for(const[e,r]of Object.entries(ke(n)))this.processOperator(t,e,r)}e.field&&this.processOperator(e.field,e.field,e.expr)}}processOperator(e,t,n){const r=De("query",t,this.#r);W(!!r,`unknown query operator ${t}`),this.#m.push(r(e,n,this.#r))}test(e){return this.#m.every((t=>t(e)))}find(e,t){return new Je(e,(e=>this.test(e)),t||{},this.#r)}remove(e){return e.reduce(((e,t)=>(this.test(t)||e.push(t),e)),[])}}function Qe(e){return(t,n,r)=>{const o={unwrapArray:!0},i=Math.max(1,t.split(".").length-1);return s=>{const a=ye(s,t,o);return e(a,n,{...r,depth:i})}}}function Xe(e){return(t,n,r)=>{const o=Ae(t,n,null,r);return e(...o)}}function Ze(e,t,n){return!!de(e,t)||!(!re(e)||!re(t))||!!Q(e)&&(e.some((e=>de(e,t)))||le(e,n?.depth).some((e=>de(e,t))))}function et(e,t,n){return!Ze(e,t,n)}function tt(e,t,n){return re(e)?t.some((e=>null===e)):function(e,t=K){const n=[U.init(t),U.init(t)];if(0===e.length)return[];if(e.some((e=>0===e.length)))return[];if(1===e.length)return[...e];e[e.length-1].forEach((e=>n[0].set(e,!0)));for(let t=e.length-2;t>-1;t--){if(e[t].forEach((e=>{n[0].has(e)&&n[1].set(e,!0)})),0===n[1].size)return[];n.reverse(),n[1].clear()}return Array.from(n[0].keys())}([ie(e),t],n?.hashFunction).length>0}function nt(e,t,n){return!tt(e,t,n)}function rt(e,t,n){return dt(e,t,((e,t)=>F(e,t)<0))}function ot(e,t,n){return dt(e,t,((e,t)=>F(e,t)<=0))}function it(e,t,n){return dt(e,t,((e,t)=>F(e,t)>0))}function st(e,t,n){return dt(e,t,((e,t)=>F(e,t)>=0))}function at(e){return Ee(e)&&-1===["$and","$or","$nor"].indexOf(e)}function ct(e,t,n){if(Q(e)&&!oe(e)){let r=e=>e,o=t;Object.keys(t).every(at)&&(o={temp:t},r=e=>({temp:e}));const i=new Ye(o,n);for(let t=0,n=e.length;tnull===e,lt={array:Q,boolean:J,bool:J,date:ee,number:Y,int:Y,long:Y,double:Y,decimal:Y,null:ut,object:X,regexp:te,regex:te,string:G,undefined:re,function:e=>{throw new C("unsupported type key `function`.")},1:Y,2:G,3:X,4:Q,6:re,8:J,9:ee,10:ut,11:te,16:Y,18:Y,19:Y};function ft(e,t,n){const r=lt[t];return!!r&&r(e)}function dt(e,t,n){return ie(e).some((e=>H(e)===H(t)&&n(e,t)))}Xe(nt);const ht=(e,t)=>(n,r,o)=>{W(Q(r),`${e}: expression must be an array.`);const i=Ae(n,r,null,o);return i.some(re)?null:(W(i.every(Y),`${e}: expression must evalue to array of numbers.`),t(i))},pt=(ht("$bitAnd",(e=>e.reduce(((e,t)=>e&t),-1))),ht("$bitOr",(e=>e.reduce(((e,t)=>e|t),0))),ht("$bitXor",(e=>e.reduce(((e,t)=>e^t),0))),Xe(Ze),Xe(it),Xe(st),Xe(rt),Xe(ot),Xe(et),(e,t)=>{const n={};return e.split("").forEach(((e,r)=>n[e]=t*(r+1))),n});pt("ABCDEFGHIKLM",1),pt("NOPQRSTUVWXY",-1);const mt={undefined:null,null:null,NaN:NaN,Infinity:new Error,"-Infinity":new Error};function vt(e,t=mt){const n=Object.assign({},mt,t),r=new Set(Object.keys(n));return(t,o,i)=>{const s=Ae(t,o,null,i);if(r.has(`${s}`)){const t=n[`${s}`];if(t instanceof Error)throw new C(`cannot apply $${e.name} to -inf, value must in (-inf,inf)`);return t}return e(s)}}vt(Math.acos,{Infinity:1/0,0:new Error}),vt(Math.acosh,{Infinity:1/0,0:new Error}),vt(Math.asin),vt(Math.asinh,{Infinity:1/0,"-Infinity":-1/0}),vt(Math.atan),vt(Math.atanh,{1:1/0,"-1":-1/0}),vt(Math.cos),vt(Math.cosh,{"-Infinity":1/0,Infinity:1/0});const yt=Math.PI/180,bt=(vt((e=>e*yt),{Infinity:1/0,"-Infinity":1/0}),180/Math.PI);vt((e=>e*bt),{Infinity:1/0,"-Infinity":-1/0}),vt(Math.sin),vt(Math.sinh,{"-Infinity":-1/0,Infinity:1/0}),vt(Math.tan),Number.MAX_SAFE_INTEGER,Number.MIN_SAFE_INTEGER;const gt=(e,t,n)=>{W(Q(t),"Invalid expression: $and expects value to be an Array.");const r=t.map((e=>new Ye(e,n)));return e=>r.every((t=>t.test(e)))},wt=(e,t,n)=>{W(Q(t),"Invalid expression. $or expects value to be an Array");const r=t.map((e=>new Ye(e,n)));return e=>r.some((t=>t.test(e)))},xt=(e,t,n)=>{W(Q(t),"Invalid expression. $nor expects value to be an array.");const r=wt(0,t,n);return e=>!r(e)},St=(e,t,n)=>{const r={};r[e]=ke(t);const o=new Ye(r,n);return e=>!o.test(e)},Ot=Qe(Ze),It=Qe(it),Et=Qe(st),kt=Qe(tt),jt=Qe(rt),Pt=Qe(ot),_t=Qe(et),$t=Qe(nt),Nt=Qe((function(e,t,n){return ie(e).some((e=>2===t.length&&e%t[0]===t[1]))})),Dt=Qe((function(e,t,n){const r=ie(e),o=e=>G(e)&&((e,t=!0)=>!!e||t&&""===e)(t.exec(e),n?.useStrictMode);return r.some(o)||le(r,1).some(o)}));Qe((function(e,t,n){if(!(Q(e)&&Q(t)&&e.length&&t.length))return!1;let r=!0;for(const o of t){if(!r)break;r=X(o)&&Object.keys(o).includes("$elemMatch")?ct(e,o.$elemMatch,n):te(o)?e.some((e=>"string"==typeof e&&o.test(e))):e.some((e=>de(o,e)))}return r}));const At=Qe(ct),Mt=Qe((function(e,t,n){return Array.isArray(e)&&e.length===t})),Tt=(e,t,n)=>{const r=e.includes("."),o=!!t;return!r||e.match(/\.\d+$/)?t=>void 0!==ye(t,e)===o:t=>{const n=ye(be(t,e,{preserveIndex:!0}),e.substring(0,e.lastIndexOf(".")));return Q(n)?n.some((e=>void 0!==e))===o:void 0!==n===o}},Ct=Qe((function(e,t,n){return Q(t)?t.findIndex((t=>ft(e,t)))>=0:ft(e,t)}));var Bt=!1;function Rt(e,t){var n=x(e.primaryKey);t=I(t);var r,o=E(t);if("number"!=typeof o.skip&&(o.skip=0),o.selector?(o.selector=o.selector,Object.entries(o.selector).forEach((([e,t])=>{"object"==typeof t&&null!==t||(o.selector[e]={$eq:t})}))):o.selector={},o.index){var i=(r=o.index,Array.isArray(r)?r.slice(0):[r]);i.includes(n)||i.push(n),o.index=i}if(o.sort){var s=o.sort.find((e=>{return t=e,Object.keys(t)[0]===n;var t}));s||(o.sort=o.sort.slice(0),o.sort.push({[n]:"asc"}))}else if(o.index)o.sort=o.index.map((e=>({[e]:"asc"})));else{if(e.indexes){var a=new Set;Object.entries(o.selector).forEach((([e,t])=>{("object"!=typeof t||null===t||Object.keys(t).find((e=>D.has(e))))&&a.add(e)}));var c,u=-1;e.indexes.forEach((e=>{var t=j(e)?e:[e],n=t.findIndex((e=>!a.has(e)));n>0&&n>u&&(u=n,c=t)})),c&&(o.sort=c.map((e=>({[e]:"asc"}))))}if(!o.sort)if(e.indexes&&e.indexes.length>0){var l=e.indexes[0],f=j(l)?l:[l];o.sort=f.map((e=>({[e]:"asc"})))}else o.sort=[{[n]:"asc"}]}return o}function Kt(e,t){if(!t.sort)throw d("SNH",{query:t});var n,r=(n=t.selector,Bt||(Ne("pipeline",{$sort:We,$project:ze}),Ne("query",{$and:gt,$eq:Ot,$elemMatch:At,$exists:Tt,$gt:It,$gte:Et,$in:kt,$lt:jt,$lte:Pt,$ne:_t,$nin:$t,$mod:Nt,$nor:xt,$not:St,$or:wt,$regex:Dt,$size:Mt,$type:Ct}),Bt=!0),new Ye(n));return e=>r.test(e)}function Lt(e,t,n,r,o,i,s){for(var a,c=!!e.schema.attachments,u=[],l=[],f=[],h={id:function(e=10){for(var t="",n=0;n0,w=r.length,x=function(){var e,o=r[O],h=o.document,p=o.previous,w=h[t],x=h._deleted,I=p&&p._deleted,E=void 0;if(g&&(E=n.get(w)),E){var k=E._rev;if(!p||p&&k!==p._rev){var j={isError:!0,status:409,documentId:w,writeRow:o,documentInDb:E};return f.push(j),1}var P=c?qt(o):o;c&&(x?p&&Object.keys(p._attachments).forEach((e=>{y.push({documentId:w,attachmentId:e,digest:S(p)._attachments[e].digest})})):(Object.entries(h._attachments).find((([t,n])=>((p?p._attachments[t]:void 0)||n.data||(e={documentId:w,documentInDb:E,isError:!0,status:510,writeRow:o,attachmentId:t}),!0))),e||Object.entries(h._attachments).forEach((([e,t])=>{var n=p?p._attachments[e]:void 0;if(n){var r=P.document._attachments[e].digest;t.data&&n.digest!==r&&b.push({documentId:w,attachmentId:e,attachmentData:t,digest:t.digest})}else v.push({documentId:w,attachmentId:e,attachmentData:t,digest:t.digest})})))),e?f.push(e):(c?(l.push(qt(P)),s&&s(h)):(l.push(P),s&&s(h)),a=P);var _=null,$=null,N=null;if(I&&!x)N="INSERT",_=c?zt(h):h;else if(!p||I||x){if(!x)throw d("SNH",{args:{writeRow:o}});N="DELETE",_=S(h),$=p}else N="UPDATE",_=c?zt(h):h,$=p;var D={documentId:w,documentData:_,previousDocumentData:$,operation:N};m.push(D)}else{var A=!!x;if(c&&Object.entries(h._attachments).forEach((([t,n])=>{n.data?v.push({documentId:w,attachmentId:t,attachmentData:n,digest:n.digest}):(e={documentId:w,isError:!0,status:510,writeRow:o,attachmentId:t},f.push(e))})),e||(c?(u.push(qt(o)),i&&i(h)):(u.push(o),i&&i(h)),a=o),!A){var M={documentId:w,operation:"INSERT",documentData:c?zt(h):h,previousDocumentData:c&&p?zt(p):p};m.push(M)}}},O=0;O{var r,o,i;t._attachments[e]=(i=(r=n).data)?{length:(o=i,atob(o).length),digest:r.digest,type:r.type}:r})),t}async function Ft(e,t,n){if(e.getChangedDocumentsSince)return e.getChangedDocumentsSince(t,n);var r=x(e.schema.primaryKey),o=function(e,t){if(!t.sort)throw d("SNH",{query:t});return{query:t,queryPlan:N(e,t)}}(e.schema,function(e,t,n){var r=x(e.schema.primaryKey),o=n?n.lwt:1,i=n?n.id:"";return Rt(e.schema,{selector:{$or:[{"_meta.lwt":{$gt:o}},{"_meta.lwt":{$eq:o},[r]:{$gt:n?i:""}}],"_meta.lwt":{$gte:o}},sort:[{"_meta.lwt":"asc"},{[r]:"asc"}],skip:0,limit:t})}(e,t,n)),i=(await e.query(o)).documents,s=k(i);return{documents:i,checkpoint:s?{id:s[r],lwt:s._meta.lwt}:n||{id:"",lwt:0}}}new WeakMap,new WeakMap;var Ut="16.17.2",Wt="16.17.2";function Vt(e,t){var n=e.get(t);if(void 0===n)throw new Error("missing value from map "+t);return n}function Ht(e,t,n,r){var o=e.get(t);return void 0===o?(o=n(),e.set(t,o)):r&&r(o),o}function Jt(e=0){return new Promise((t=>setTimeout(t,e)))}Promise.resolve(!0);var Gt=Promise.resolve(!1),Yt=(Promise.resolve(null),Promise.resolve());function Qt(e=1e4){return"function"==typeof requestIdleCallback?new Promise((t=>{requestIdleCallback((()=>t()),{timeout:e})})):Jt(0)}function Xt(e,t){var n=t.map((t=>{var n=w(e,t);if(!n)throw new Error("not in schema: "+t);var r,o=n.type;"number"!==o&&"integer"!==o||(r=Zt(n));var i,s=O(t),a=n.maxLength?n.maxLength:0;return i="string"===o?e=>{var t=s(e);return t||(t=""),t.padEnd(a," ")}:"boolean"===o?e=>s(e)?"1":"0":e=>{var t=s(e);return en(r,t)},{fieldName:t,schemaPart:n,parsedLengths:r,getValue:s,getIndexStringPart:i}}));return n}function Zt(e){var t=Math.floor(e.minimum),n=Math.ceil(e.maximum),r=e.multipleOf,o=(n-t).toString().length,i=r.toString().split("."),s=0;return i.length>1&&(s=i[1].length),{minimum:t,maximum:n,nonDecimals:o,decimals:s,roundedMinimum:t}}function en(e,t){void 0===t&&(t=0),te.maximum&&(t=e.maximum);var n=(Math.floor(t)-e.roundedMinimum).toString().padStart(e.nonDecimals,"0");if(e.decimals>0){var r=t.toString().split(".");n+=(r.length>1?r[1]:"0").padEnd(e.decimals,"0")}return n}function tn(e,t,n){var r="";return t.forEach(((t,o)=>{var i=w(e,t),s=n[o],a=i.type;switch(a){case"string":var c=S(i.maxLength,"maxLength not set");r+="string"==typeof s?s.padEnd(c," "):"".padEnd(c," ");break;case"boolean":r+=null===s||s===$?"0":s===_||s?"1":"0";break;case"number":case"integer":var u=Zt(i);if(null===s||s===$)r+="0".repeat(u.nonDecimals+u.decimals);else if(s===_)r+=en(u,u.maximum);else{var l=en(u,s);r+=l}break;default:throw new Error("unknown index type "+a)}})),r}function nn(e,t,n){var r="";return t.forEach(((t,o)=>{var i=w(e,t),s=n[o],a=i.type;switch(a){case"string":var c=S(i.maxLength,"maxLength not set");r+="string"==typeof s&&s!==_?s.padEnd(c," "):"".padEnd(c,s===$?" ":_);break;case"boolean":r+=null===s||s?"1":"0";break;case"number":case"integer":var u=Zt(i);r+=null===s||s===_?"9".repeat(u.nonDecimals+u.decimals):s===$?"0".repeat(u.nonDecimals+u.decimals):en(u,s);break;default:throw new Error("unknown index type "+a)}})),r}function rn(e){return e.join("||")}var on=["_deleted","_meta.lwt"];function sn(e,t){var n=rn(t);return Vt(e.indexIdByName,n)}rn(on);var an={locale:null,unique:!1};function cn(e,t,n,r){for(var o={i:n,d:r,i0:void 0,i1:void 0,i2:void 0,i3:void 0,i4:void 0,i5:void 0,i6:void 0,i7:void 0},i=0;iArray.isArray(e)?e.slice(0):[e])):[];n.push(on);var r=new Map;return n.forEach(((n,o)=>{var i="i"+o,s=rn(n);if(r.has(s))throw new Error("duplicate index "+s+" "+JSON.stringify(e,null,4));r.set(s,i),t[i]=function(e,t){var n=Xt(e,t),r=n.length,o=n.map((e=>e.getIndexStringPart));return function(e){for(var t="",n=0;nYt));return n=S(n).then((()=>t())),pn.set(e,n),n}function vn(e,t){t.onversionchange=n=>{e.closed||(t.close(),e.creationPromise=e.refreshIDBDatabase())}}async function yn(e,t,n,r){var o=await Ht(e.indexedDBStates,n.databaseName,(()=>async function(e,t,n){var r=dn++,o=await function(e,t){return"function"==typeof t?t(e):t}(e,n.indexedDB),i=async()=>(await Jt(0),mn(e.databaseName,(()=>new Promise(((t,n)=>{var i=o.open(e.databaseName);i.onerror=function(t){console.error(r+": OPEN IDB DATABASE "+e.databaseName+" ERROR"),n(t)},i.onsuccess=function(e){var n=i.result;t(n),vn(s,n)},wn(s,i)}))))),s={indexedDB:o,debugId:r,closed:!1,storage:t,settings:n,refreshIDBDatabase:i,creationPromise:i(),name:e.databaseName,refCount:0,storesToOpen:[]};return s}(n,e,t)));return o.storesToOpen=o.storesToOpen.concat(r),o.refCount=o.refCount+1,o.creationPromise.then((()=>bn(S(o)))).then((()=>S(o)))}async function bn(e){if(0!==e.storesToOpen.length)return e.creationPromise=e.creationPromise.then((async t=>{var n=new Set(Array.from(t.objectStoreNames));if(0===e.storesToOpen.filter((e=>!n.has(gn(e.collectionName,e.schema).documentStore))).length)return t;var r=t.version+1;return t.close(),mn(e.name,(()=>new Promise(((t,n)=>{var o=e.indexedDB.open(e.name,r);o.onerror=function(t){console.error(e.debugId+": ERROR openStoresOnExistingDatabase() openRequest: error "),n(t)},o.onsuccess=function(n){var r=o.result;vn(e,r),t(r)},o.onblocked=e=>{},wn(e,o)}))))})),e.creationPromise}function gn(e,t){var n=t.version;return{documentStore:e+"-"+n+"-documents",writeAheadStore:e+"-"+n+"-wal",attachmentsStore:e+"-"+n+"-attachments"}}function wn(e,t){t.onupgradeneeded=function(n){var r=t.result;e.storesToOpen.forEach((e=>{var t=r.objectStoreNames,n=gn(e.collectionName,e.schema);if(!t.contains(n.documentStore)){var o=r.createObjectStore(n.documentStore,{keyPath:"i",autoIncrement:!1});r.createObjectStore(n.writeAheadStore,{keyPath:"i",autoIncrement:!1}),function(e,t){t.indexIds.forEach((t=>{e.createIndex(t,t,an)}))}(o,e),e.schema.attachments&&r.createObjectStore(n.attachmentsStore,{keyPath:"docIdWithAttachmentId",autoIncrement:!1})}})),e.storesToOpen=[]}}function xn(e){if(e.closed)throw new Error("RxStorageInstanceIndexedDB is closed "+e.databaseName+"-"+e.collectionName)}async function Sn(e,t){var n=e.primaryPath,r=e.internals.indexIds.length,o=e.internals.getIndexableStringByIndexNumber,i=t.objectStore(e.internals.storeNames.writeAheadStore),s=await new Promise(((e,t)=>{var n=i.get("documents");n.onerror=t,n.onsuccess=t=>{var r=n.result;e(r?JSON.parse(r.docsData):void 0)}}));if(s&&s.length>0){for(var a,c=t.objectStore(e.internals.storeNames.documentStore),u=0;u{a.onerror=t,a.onsuccess=()=>e(!0)})),i.delete("documents")}return t}async function On(e){var t,n=[e.internals.storeNames.documentStore,e.internals.storeNames.writeAheadStore];e.schema.attachments&&n.push(e.internals.storeNames.attachmentsStore);for(var r=100;r>0;){var o=await e.internals.state.creationPromise;r-=1;try{t=o.transaction(n,"readwrite",ln);break}catch(t){if("InvalidStateError"!==t.name&&"NotFoundError"!==t.name||!(r>0))throw t;"NotFoundError"===t.name?await bn(e.internals.state):e.internals.state.creationPromise=e.internals.state.refreshIDBDatabase()}}return await Sn(e,S(t)),S(t)}var In=function(){function e(e){this.allTasksRuns=[],this.instance=e,this.txPromise=On(this.instance).then((t=>Sn(e,t)))}return e.prototype.addTask=function(e){var t=this.txPromise.then((t=>e(t))),n=t.catch((()=>null));this.allTasksRuns.push(n);var r=this.allTasksRuns.length;return n.then((()=>Promise.all(this.allTasksRuns))).then((()=>{this.allTasksRuns.length===r&&(this.instance.openReadonlyTransaction=void 0)})),t.catch((t=>{if("TransactionInactiveError"===t.name)return this.instance.openReadonlyTransaction=void 0,En(this.instance,e);throw t}))},e}();function En(e,t){return e.openReadonlyTransaction||(e.openReadonlyTransaction=new In(e)),e.openReadonlyTransaction.addTask(t)}var kn=0;function jn(){var e=Date.now();(e+=.01)<=kn&&(e=kn+.01);var t=parseFloat(e.toFixed(2));return kn=t,t}var Pn=function(e,t){return Pn=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},Pn(e,t)};function _n(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}Pn(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}function $n(e,t,n,r){return new(n||(n=Promise))((function(o,i){function s(e){try{c(r.next(e))}catch(e){i(e)}}function a(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))}function Nn(e,t){var n,r,o,i={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]},s=Object.create(("function"==typeof Iterator?Iterator:Object).prototype);return s.next=a(0),s.throw=a(1),s.return=a(2),"function"==typeof Symbol&&(s[Symbol.iterator]=function(){return this}),s;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;s&&(s=0,a[0]&&(i=0)),i;)try{if(n=1,r&&(o=2&a[0]?r.return:a[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,a[1])).done)return o;switch(r=0,o&&(a=[2&a[0],o.value]),a[0]){case 0:case 1:o=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,r=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!((o=(o=i.trys).length>0&&o[o.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]=e.length&&(e=void 0),{value:e&&e[r++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function An(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),s=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)s.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return s}function Mn(e,t,n){if(n||2===arguments.length)for(var r,o=0,i=t.length;o1||a(e,t)}))},t&&(r[e]=t(r[e])))}function a(e,t){try{(n=o[e](t)).value instanceof Tn?Promise.resolve(n.value.v).then(c,u):l(i[0][2],n)}catch(e){l(i[0][3],e)}var n}function c(e){a("next",e)}function u(e){a("throw",e)}function l(e,t){e(t),i.shift(),i.length&&a(i[0][0],i[0][1])}}function Bn(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e=Dn(e),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,o){!function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)}(r,o,(t=e[n](t)).done,t.value)}))}}}function Rn(e){return"function"==typeof e}function Kn(e){var t=e((function(e){Error.call(e),e.stack=(new Error).stack}));return t.prototype=Object.create(Error.prototype),t.prototype.constructor=t,t}Object.create,Object.create,"function"==typeof SuppressedError&&SuppressedError;var Ln=Kn((function(e){return function(t){e(this),this.message=t?t.length+" errors occurred during unsubscription:\n"+t.map((function(e,t){return t+1+") "+e.toString()})).join("\n "):"",this.name="UnsubscriptionError",this.errors=t}}));function qn(e,t){if(e){var n=e.indexOf(t);0<=n&&e.splice(n,1)}}var zn=function(){function e(e){this.initialTeardown=e,this.closed=!1,this._parentage=null,this._finalizers=null}var t;return e.prototype.unsubscribe=function(){var e,t,n,r,o;if(!this.closed){this.closed=!0;var i=this._parentage;if(i)if(this._parentage=null,Array.isArray(i))try{for(var s=Dn(i),a=s.next();!a.done;a=s.next())a.value.remove(this)}catch(t){e={error:t}}finally{try{a&&!a.done&&(t=s.return)&&t.call(s)}finally{if(e)throw e.error}}else i.remove(this);var c=this.initialTeardown;if(Rn(c))try{c()}catch(e){o=e instanceof Ln?e.errors:[e]}var u=this._finalizers;if(u){this._finalizers=null;try{for(var l=Dn(u),f=l.next();!f.done;f=l.next()){var d=f.value;try{Wn(d)}catch(e){o=null!=o?o:[],e instanceof Ln?o=Mn(Mn([],An(o)),An(e.errors)):o.push(e)}}}catch(e){n={error:e}}finally{try{f&&!f.done&&(r=l.return)&&r.call(l)}finally{if(n)throw n.error}}}if(o)throw new Ln(o)}},e.prototype.add=function(t){var n;if(t&&t!==this)if(this.closed)Wn(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=null!==(n=this._finalizers)&&void 0!==n?n:[]).push(t)}},e.prototype._hasParent=function(e){var t=this._parentage;return t===e||Array.isArray(t)&&t.includes(e)},e.prototype._addParent=function(e){var t=this._parentage;this._parentage=Array.isArray(t)?(t.push(e),t):t?[t,e]:e},e.prototype._removeParent=function(e){var t=this._parentage;t===e?this._parentage=null:Array.isArray(t)&&qn(t,e)},e.prototype.remove=function(t){var n=this._finalizers;n&&qn(n,t),t instanceof e&&t._removeParent(this)},e.EMPTY=((t=new e).closed=!0,t),e}(),Fn=zn.EMPTY;function Un(e){return e instanceof zn||e&&"closed"in e&&Rn(e.remove)&&Rn(e.add)&&Rn(e.unsubscribe)}function Wn(e){Rn(e)?e():e.unsubscribe()}var Vn={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1},Hn={setTimeout:function(e,t){for(var n=[],r=2;r0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(t){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,t)},t.prototype._subscribe=function(e){return this._throwIfClosed(),this._checkFinalizedStatuses(e),this._innerSubscribe(e)},t.prototype._innerSubscribe=function(e){var t=this,n=this,r=n.hasError,o=n.isStopped,i=n.observers;return r||o?Fn:(this.currentObservers=null,i.push(e),new zn((function(){t.currentObservers=null,qn(i,e)})))},t.prototype._checkFinalizedStatuses=function(e){var t=this,n=t.hasError,r=t.thrownError,o=t.isStopped;n?e.error(r):o&&e.complete()},t.prototype.asObservable=function(){var e=new fr;return e.source=this,e},t.create=function(e,t){return new mr(e,t)},t}(fr),mr=function(e){function t(t,n){var r=e.call(this)||this;return r.destination=t,r.source=n,r}return _n(t,e),t.prototype.next=function(e){var t,n;null===(n=null===(t=this.destination)||void 0===t?void 0:t.next)||void 0===n||n.call(t,e)},t.prototype.error=function(e){var t,n;null===(n=null===(t=this.destination)||void 0===t?void 0:t.error)||void 0===n||n.call(t,e)},t.prototype.complete=function(){var e,t;null===(t=null===(e=this.destination)||void 0===e?void 0:e.complete)||void 0===t||t.call(e)},t.prototype._subscribe=function(e){var t,n;return null!==(n=null===(t=this.source)||void 0===t?void 0:t.subscribe(e))&&void 0!==n?n:Fn},t}(pr);function vr(e){return function(t){if(function(e){return Rn(null==e?void 0:e.lift)}(t))return t.lift((function(t){try{return e(t,this)}catch(e){this.error(e)}}));throw new TypeError("Unable to lift unknown Observable type")}}var yr=Array.isArray;function br(e,t,n,r,o){return new gr(e,t,n,r,o)}var gr=function(e){function t(t,n,r,o,i,s){var a=e.call(this,t)||this;return a.onFinalize=i,a.shouldUnsubscribe=s,a._next=n?function(e){try{n(e)}catch(e){t.error(e)}}:e.prototype._next,a._error=o?function(e){try{o(e)}catch(e){t.error(e)}finally{this.unsubscribe()}}:e.prototype._error,a._complete=r?function(){try{r()}catch(e){t.error(e)}finally{this.unsubscribe()}}:e.prototype._complete,a}return _n(t,e),t.prototype.unsubscribe=function(){var t;if(!this.shouldUnsubscribe||this.shouldUnsubscribe()){var n=this.closed;e.prototype.unsubscribe.call(this),!n&&(null===(t=this.onFinalize)||void 0===t||t.call(this))}},t}(er),wr=function(e){return e&&"number"==typeof e.length&&"function"!=typeof e};function xr(e){return Rn(null==e?void 0:e.then)}function Sr(e){return Rn(e[cr])}function Or(e){return Symbol.asyncIterator&&Rn(null==e?void 0:e[Symbol.asyncIterator])}function Ir(e){return new TypeError("You provided "+(null!==e&&"object"==typeof e?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}var Er="function"==typeof Symbol&&Symbol.iterator?Symbol.iterator:"@@iterator";function kr(e){return Rn(null==e?void 0:e[Er])}function jr(e){return Cn(this,arguments,(function(){var t,n,r;return Nn(this,(function(o){switch(o.label){case 0:t=e.getReader(),o.label=1;case 1:o.trys.push([1,,9,10]),o.label=2;case 2:return[4,Tn(t.read())];case 3:return n=o.sent(),r=n.value,n.done?[4,Tn(void 0)]:[3,5];case 4:return[2,o.sent()];case 5:return[4,Tn(r)];case 6:return[4,o.sent()];case 7:return o.sent(),[3,2];case 8:return[3,10];case 9:return t.releaseLock(),[7];case 10:return[2]}}))}))}function Pr(e){return Rn(null==e?void 0:e.getReader)}function _r(e){if(e instanceof fr)return e;if(null!=e){if(Sr(e))return o=e,new fr((function(e){var t=o[cr]();if(Rn(t.subscribe))return t.subscribe(e);throw new TypeError("Provided object does not correctly implement Symbol.observable")}));if(wr(e))return r=e,new fr((function(e){for(var t=0;t{this._to=!1,function(e){const t=Hr()-e.ttl,n=e.map[Symbol.iterator]();for(;;){const r=n.next().value;if(!r)return;const o=r[0];if(!(r[1]0&&void 0!==arguments[0]?arguments[0]:{},t=JSON.parse(JSON.stringify(e));return void 0===t.webWorkerSupport&&(t.webWorkerSupport=!0),t.idb||(t.idb={}),t.idb.ttl||(t.idb.ttl=45e3),t.idb.fallbackInterval||(t.idb.fallbackInterval=150),e.idb&&"function"==typeof e.idb.onclose&&(t.idb.onclose=e.idb.onclose),t.localstorage||(t.localstorage={}),t.localstorage.removeTimeout||(t.localstorage.removeTimeout=6e4),e.methods&&(t.methods=e.methods),t.node||(t.node={}),t.node.ttl||(t.node.ttl=12e4),t.node.maxParallelWrites||(t.node.maxParallelWrites=2048),void 0===t.node.useFastPath&&(t.node.useFastPath=!0),t}var Gr="messages",Yr={durability:"relaxed"};function Qr(){if("undefined"!=typeof indexedDB)return indexedDB;if("undefined"!=typeof window){if(void 0!==window.mozIndexedDB)return window.mozIndexedDB;if(void 0!==window.webkitIndexedDB)return window.webkitIndexedDB;if(void 0!==window.msIndexedDB)return window.msIndexedDB}return!1}function Xr(e){e.commit&&e.commit()}function Zr(e,t){var n=e.transaction(Gr,"readonly",Yr),r=n.objectStore(Gr),o=[],i=IDBKeyRange.bound(t+1,1/0);if(r.getAll){var s=r.getAll(i);return new Promise((function(e,t){s.onerror=function(e){return t(e)},s.onsuccess=function(t){e(t.target.result)}}))}return new Promise((function(e,s){var a=function(){try{return i=IDBKeyRange.bound(t+1,1/0),r.openCursor(i)}catch(e){return r.openCursor()}}();a.onerror=function(e){return s(e)},a.onsuccess=function(r){var i=r.target.result;i?i.value.ide.lastCursorId&&(e.lastCursorId=t.id),t})).filter((function(t){return function(e,t){return!(e.uuid===t.uuid||t.eMIs.has(e.id)||e.data.time0||e._addEL.internal.length>0}function go(e,t,n){e._addEL[t].push(n),function(e){if(!e._iL&&bo(e)){var t=function(t){e._addEL[t.type].forEach((function(e){t.time>=e.time&&e.fn(t.data)}))},n=e.method.microSeconds();e._prepP?e._prepP.then((function(){e._iL=!0,e.method.onMessage(e._state,t,n)})):(e._iL=!0,e.method.onMessage(e._state,t,n))}}(e)}function wo(e,t,n){e._addEL[t]=e._addEL[t].filter((function(e){return e!==n})),function(e){if(e._iL&&!bo(e)){e._iL=!1;var t=e.method.microSeconds();e.method.onMessage(e._state,null,t)}}(e)}vo._pubkey=!0,vo.prototype={postMessage:function(e){if(this.closed)throw new Error("BroadcastChannel.postMessage(): Cannot post message after channel has closed "+JSON.stringify(e));return yo(this,"message",e)},postInternal:function(e){return yo(this,"internal",e)},set onmessage(e){var t={time:this.method.microSeconds(),fn:e};wo(this,"message",this._onML),e&&"function"==typeof e?(this._onML=t,go(this,"message",t)):this._onML=null},addEventListener:function(e,t){go(this,e,{time:this.method.microSeconds(),fn:t})},removeEventListener:function(e,t){wo(this,e,this._addEL[e].find((function(e){return e.fn===t})))},close:function(){var e=this;if(!this.closed){po.delete(this),this.closed=!0;var t=this._prepP?this._prepP:Lr;return this._onML=null,this._addEL.message=[],t.then((function(){return Promise.all(Array.from(e._uMP))})).then((function(){return Promise.all(e._befC.map((function(e){return e()})))})).then((function(){return e.method.close(e._state)}))}},get type(){return this.method.type},get isClosed(){return this.closed}};var xo=new Map;function So(e,t){var n=xo.get(e);if(n)return n.refs.delete(t),0===n.refs.size?(xo.delete(e),n.bc.close()):void 0}function Oo(e,t,n,r){if(t.multiInstance){var o=r||function(e,t,n,r){var o=xo.get(t);return o||(o={bc:new vo(["RxDB:",e,n].join("|")),refs:new Set},xo.set(t,o)),o.refs.add(r),o.bc}(e,t.databaseInstanceToken,n.databaseName,n),i=new pr,s=n=>{n.storageName===e&&n.databaseName===t.databaseName&&n.collectionName===t.collectionName&&n.version===t.schema.version&&i.next(n.eventBulk)};o.addEventListener("message",s);var a=n.changeStream(),c=!1,u=a.subscribe((n=>{c||o.postMessage({storageName:e,databaseName:t.databaseName,collectionName:t.collectionName,version:t.schema.version,eventBulk:n})}));n.changeStream=function(){return i.asObservable().pipe(function(){for(var e=[],t=0;t{var r=t.queryPlan,o=t.query,i=o.skip?o.skip:0,s=i+(o.limit?o.limit:1/0),a=e.internals.storeNames.documentStore,c=e.settings.batchSize?e.settings.batchSize:50,u=!1;r.selectorSatisfiedByIndex||(u=Kt(e.schema,o));var l=r.index,f=!r.sortSatisfiedByIndex,h=l,p=r.startKeys,m=tn(e.schema,h,p),v=r.endKeys,y=nn(e.schema,h,v),b=[],g=n.objectStore(a),w=sn(e.internals,h),x=g.index(w);u||t.query.limit||(c=1e5);var S=!1;if(await async function(e,t,n,r,o,i,s,a){var c=e.settings.IDBKeyRange?e.settings.IDBKeyRange:IDBKeyRange,u=e.internals.getIndexableStringByIndexId[s];if("function"==typeof n.getAll&&1!==r)for(var l,f=!0,d=!1,h=async function(){l&&(o=u(l.d));var e=c.bound(o,i,!f||!t.inclusiveStart,!t.inclusiveEnd);f=!1;var s=n.getAll(e,r);await new Promise(((e,t)=>{s.onerror=t,s.onsuccess=t=>{var n=t.target.result;l=k(n),0!==n.length&&!1===a(n)&&(d=!0),n.length{m.onsuccess=function(t){var n=t.target.result;if(n){var r=n.value;a([r])?n.continue():e()}else e()}}))}}(e,r,x,c,m,y,w,(e=>{for(var t=0;t{var t=Object.keys(e)[0],r=Object.values(e)[0];n.push({key:t,direction:r,getValueFn:O(t)})})),(e,t)=>{for(var r=0;r30&&!await function(e,t){var n=(e.settings.IDBKeyRange?e.settings.IDBKeyRange:IDBKeyRange).lowerBound($,!0),r=t.getKey(n);return new Promise((e=>{r.onsuccess=()=>{e(!!r.result)}}))}(e,u)?new Map:await function(e,t,n){var r=n.length,o=new Map;if(0===r)return Promise.resolve(o);for(var i=new Array(r),s=0;s{u.onerror=n,u.onsuccess=()=>{for(var n=0;n0){var w=c.objectStore(o.storeNames.writeAheadStore).put({i:"documents",docsData:JSON.stringify(h)});await new Promise(((e,t)=>{w.onerror=t,w.onsuccess=e})),e.handleWalIdlePromise||Jt(100).then((()=>Qt())).then((async()=>{e.handleWalIdlePromise=void 0,e.closed||await En(e,(()=>Gt))}))}if(e.schema.attachments){var x=c.objectStore(e.internals.storeNames.attachmentsStore);l.attachmentsAdd.forEach((e=>{v=x.put({docIdWithAttachmentId:hn(e.documentId,e.attachmentId),length:e.attachmentData.length,type:e.attachmentData.type,data:e.attachmentData.data})})),l.attachmentsUpdate.forEach((e=>{v=x.put({docIdWithAttachmentId:hn(e.documentId,e.attachmentId),length:e.attachmentData.length,type:e.attachmentData.type,data:e.attachmentData.data})})),l.attachmentsRemove.forEach((e=>{v=x.delete(hn(e.documentId,e.attachmentId))}))}if(v&&await new Promise(((e,t)=>{S(v).onerror=t,S(v).onsuccess=e})),c.commit&&c.commit(),l.eventBulk.events.length>0){var O=S(l.newestRow).document;l.eventBulk.checkpoint={id:O[r],lwt:O._meta.lwt},e.changes$.next(l.eventBulk)}return{error:f}}var ko=jn(),jo=function(){function e(e,t,n,r,o,i,s){this.changes$=new pr,this.instanceId=ko++,this.storage=e,this.databaseName=t,this.collectionName=n,this.schema=r,this.internals=o,this.options=i,this.settings=s,this.primaryPath=x(this.schema.primaryKey)}var t=e.prototype;return t.updateMinKnownDocs=function(e){this.internals.minKnownDocsAmountfunction(e,t,n){var r=t.length;if(0===r)return Promise.resolve([]);for(var o=new Array(r),i=0;i{c.onerror=t,c.onsuccess=()=>{for(var t=0;t{var r,o=t.queryPlan,i=e.internals.storeNames.documentStore,s=o.index,a=s,c=o.startKeys,u=tn(e.schema,a,c),l=o.endKeys,f=nn(e.schema,a,l),d=n.objectStore(i);r=1===s.length&&s[0]===e.primaryPath?sn(e.internals,["_deleted",e.primaryPath]):sn(e.internals,a);var h=d.index(r),p=(e.settings.IDBKeyRange?e.settings.IDBKeyRange:IDBKeyRange).bound(u,f,!o.inclusiveStart,!o.inclusiveEnd),m=h.count(p);return{count:await new Promise(((e,t)=>{m.onsuccess=function(){e(m.result)},m.onerror=t})),mode:"fast"}}))}(this,e);return this.updateMinKnownDocs(t.count),t}var n=await Io(this,e);return this.updateMinKnownDocs(n.documents.length),{count:n.documents.length,mode:"slow"}},t.changeStream=function(){return this.changes$.asObservable()},t.cleanup=async function(e){await Jt(0),await Jt(0);var t=this.internals.state;await t.creationPromise;var n=this.settings.IDBKeyRange;xn(this);var r=(await On(this)).objectStore(this.internals.storeNames.documentStore),o=this.settings.batchSize,i=jn()-e,s=sn(this.internals,on),a=r.index(s),c=tn(this.schema,on,[!0,1]),u=nn(this.schema,on,[!0,i]),l=n.bound(c,u,!0,!0),f=await new Promise(((e,t)=>{var n=a.getAll(l,o);n.onerror=t,n.onsuccess=function(t){e(t.target.result)}}));return await Promise.all(f.map((e=>new Promise(((t,n)=>{var o=e.i,i=r.delete(o);i.onerror=n,i.onsuccess=()=>t()}))))),f.length{var t=[e.objectStore(this.internals.storeNames.documentStore),e.objectStore(this.internals.storeNames.writeAheadStore)];return this.schema.attachments&&t.push(e.objectStore(this.internals.storeNames.attachmentsStore)),await Promise.all(t.map((e=>new Promise(((t,n)=>{var r=e.clear();r.onerror=n,r.onsuccess=t}))))),this.close()}))},t.getAttachmentData=async function(e,t){var n=this.internals.state;return await n.creationPromise,xn(this),En(this,(n=>{var r=n.objectStore(this.internals.storeNames.attachmentsStore),o=hn(e,t);return new Promise(((n,i)=>{var s=r.get(o);s.onsuccess=()=>{var r=s.result;r?n(r.data):i("attachment missing documentId: "+e+" attachmentId: "+t)}}))}))},t.close=async function(){return this.closed||(this.closed=(async()=>(await this.internals.state.creationPromise,await En(this,(async e=>{})),this.changes$.complete(),async function(e){if(!e.closed&&(e.refCount=e.refCount-1,0===e.refCount))return e.closed=!0,e.storage.indexedDBStates.delete(e.name),e.creationPromise.then((e=>e.close()))}(this.internals.state)))()),this.closed},e}(),Po=function(){function e(e){this.name=fn,this.rxdbVersion=Wt,this.indexedDBStates=new Map,this.settings=e}return e.prototype.createStorageInstance=function(e){return function(e){if(e.schema.keyCompression)throw d("UT5",{args:{params:e}});if((t=e.schema).encrypted&&t.encrypted.length>0||t.attachments&&t.attachments.encrypted)throw d("UT6",{args:{params:e}});var t;if(e.schema.attachments&&e.schema.attachments.compression)throw d("UT7",{args:{params:e}})}(e),async function(e,t,n){var r=un(t.schema),o=Array.from(r.indexIdByName.values()),i=await yn(e,n,t,[{collectionName:t.collectionName,schema:t.schema,indexIds:o}]);await i.creationPromise;var s={state:i,storeNames:gn(t.collectionName,t.schema),getIndexableStringByIndexId:r.monadByIndexId,getIndexableStringByIndexNumber:Object.values(r.monadByIndexId),indexIdByName:r.indexIdByName,indexNames:Object.keys(r),indexIds:o,minKnownDocsAmount:0},a=new jo(e,t.databaseName,t.collectionName,t.schema,s,t.options,n);return await Oo(fn,t,a),a}(this,e,Object.assign({},this.settings,e.options))},e}();function _o(e,t){return vr((function(n,r){var o=0;n.subscribe(br(r,(function(n){return e.call(t,n,o++)&&r.next(n)})))}))}function $o(e,t){if(e===t)return!0;if(e&&t&&"object"==typeof e&&"object"==typeof t){if(e.constructor!==t.constructor)return!1;var n,r;if(Array.isArray(e)){if((n=e.length)!==t.length)return!1;for(r=n;0!=r--;)if(!$o(e[r],t[r]))return!1;return!0}if(e.constructor===RegExp)return e.source===t.source&&e.flags===t.flags;if(e.valueOf!==Object.prototype.valueOf)return e.valueOf()===t.valueOf();if(e.toString!==Object.prototype.toString)return e.toString()===t.toString();var o=Object.keys(e);if((n=o.length)!==Object.keys(t).length)return!1;for(r=n;0!=r--;)if(!Object.prototype.hasOwnProperty.call(t,o[r]))return!1;for(r=n;0!=r--;){var i=o[r];if(!$o(e[i],t[i]))return!1}return!0}return e!=e&&t!=t}function No(e,t){return{connectionId:e.connectionId,answerTo:e.requestId,method:e.method,error:(n=t,{name:n.name,message:n.message,rxdb:n.rxdb,parameters:n.parameters,extensions:n.extensions,code:n.code,url:n.url,stack:n.stack?n.stack.replace(/\n/g," \n "):void 0})};var n}function Do(e,t){return{connectionId:e.connectionId,answerTo:e.requestId,method:e.method,return:t}}!function(e){var t,n=new pr;if("undefined"!=typeof self&&"object"==typeof self.onconnect){var r=new Map;self.onconnect=e=>{var t=e.ports[0];t.onmessage=e=>{var o=e.data;r.set(o.connectionId,t),n.next(o)}},t={storage:e.storage,messages$:n,send(e){Vt(r,e.connectionId).postMessage(e)}}}else addEventListener("message",(e=>{var t=e.data;n.next(t)})),t={storage:e.storage,messages$:n,send(e){postMessage(e)}};!function(e){var t=new Map;function n(t){if(e.storage)return e.storage.createStorageInstance(t);if(e.database){var n=Array.from(e.database.storageInstances),r=t.collectionName,o=n.find((e=>e.collectionName===r));if(!o)throw console.dir(n),new Error("storageInstance does not exist "+JSON.stringify({collectionName:r}));var i=t.schema;if(!$o(i,o.schema))throw new Error("Wrong schema "+JSON.stringify({schema:i,existingSchema:o.schema}));return Promise.resolve(o)}throw new Error("no base given")}e.messages$.pipe(_o((e=>"custom"===e.method))).subscribe((async t=>{if(e.customRequestHandler)try{var n=await e.customRequestHandler(t.params);e.send(Do(t,n))}catch(n){e.send(No(t,n))}else e.send(No(t,new Error("Remote storage: cannot resolve custom request because settings.customRequestHandler is not set")))}));var r=e.fakeVersion?e.fakeVersion:Ut;e.messages$.pipe(_o((e=>"create"===e.method))).subscribe((async o=>{if(o.version===r){var i=o.connectionId;if(!Array.isArray(o.params)){var s=o.params,a=s.collectionName,c=[s.databaseName,s.collectionName,s.schema.version].join("|"),u=t.get(c);if(u){if(!$o(s.schema,u.params.schema))return void e.send(No(o,new Error("Remote storage: schema not equal to existing storage")))}else try{u={storageInstancePromise:n(s),connectionIds:new Set,params:s},t.set(c,u),await u.storageInstancePromise}catch(t){return void e.send(No(o,t))}u.connectionIds.add(o.connectionId);var l=[],f=await u.storageInstancePromise;l.push(f.changeStream().subscribe((t=>{var n={connectionId:i,answerTo:"changestream",method:"changeStream",return:t};e.send(n)})));var h=!1;if(e.database){var p=e.database,m=p.collections[a];m?m.onClose.push((()=>v())):p.onClose.push((()=>v()))}l.push(e.messages$.pipe(_o((e=>e.connectionId===i))).subscribe((async t=>{var n,r=t;if("create"!==r.method&&"custom"!==r.method&&Array.isArray(r.params))try{if("close"===r.method&&e.database)return void e.send(Do(r,null));if("close"===r.method&&S(u).connectionIds.size>1)return e.send(Do(r,null)),S(u).connectionIds.delete(i),void l.forEach((e=>e.unsubscribe()));n="getChangedDocumentsSince"!==r.method||f.getChangedDocumentsSince?await f[r.method](r.params[0],r.params[1],r.params[2],r.params[3]):await Ft(f,r.params[0],r.params[1]),"close"!==r.method&&"remove"!==r.method||v(),e.send(Do(r,n))}catch(t){e.send(No(r,t))}}))),e.send(Do(o,"ok"))}}else e.send(No(o,d("RM1",{args:{mainVersion:o.version,remoteVersion:r}})));function v(){h||(h=!0,l.forEach((e=>e.unsubscribe())),S(u).connectionIds.delete(i),t.delete(c))}}))}(t)}({storage:function(e={}){var t=e.IDBKeyRange?e.IDBKeyRange:IDBKeyRange,n=e.indexedDB?e.indexedDB:indexedDB,r=Object.assign({batchSize:300,transactionDurability:"relaxed"},e,{IDBKeyRange:t,indexedDB:n});return new Po(r)}({})})})(); \ No newline at end of file diff --git a/includes/API.php b/includes/API.php index 1d632a4..09e1d15 100644 --- a/includes/API.php +++ b/includes/API.php @@ -5,7 +5,6 @@ * @author Paul Kilmurray * * @see http://wcpos.com - * @package WCPOS\WooCommercePOS */ namespace WCPOS\WooCommercePOS; @@ -17,10 +16,6 @@ use WP_REST_Response; use WP_REST_Server; - -/** - * - */ class API { /** * WCPOS REST API namespaces and endpoints. @@ -62,7 +57,7 @@ public function __construct() { /** * Register routes for all controllers. */ - public function register_routes() { + public function register_routes(): void { /** * Filter the list of controller classes used in the WooCommerce POS REST API. * @@ -73,19 +68,19 @@ public function register_routes() { * @since 1.5.0 * * @param array $controllers Associative array of controller identifiers to their corresponding class names. - * - 'auth' => Fully qualified name of the class handling authentication. - * - 'settings' => Fully qualified name of the class handling settings. - * - 'stores' => Fully qualified name of the class handling stores management. - * - 'products' => Fully qualified name of the class handling products. - * - 'product_variations' => Fully qualified name of the class handling product variations. - * - 'orders' => Fully qualified name of the class handling orders. - * - 'customers' => Fully qualified name of the class handling customers. - * - 'product_tags' => Fully qualified name of the class handling product tags. - * - 'product_categories' => Fully qualified name of the class handling product categories. - * - 'taxes' => Fully qualified name of the class handling taxes. - * - 'shipping_methods' => Fully qualified name of the class handling shipping methods. - * - 'tax_classes' => Fully qualified name of the class handling tax classes. - * - 'order_statuses' => Fully qualified name of the class handling order statuses. + * - 'auth' => Fully qualified name of the class handling authentication. + * - 'settings' => Fully qualified name of the class handling settings. + * - 'cashier' => Fully qualified name of the class handling cashier management. + * - 'products' => Fully qualified name of the class handling products. + * - 'product_variations' => Fully qualified name of the class handling product variations. + * - 'orders' => Fully qualified name of the class handling orders. + * - 'customers' => Fully qualified name of the class handling customers. + * - 'product_tags' => Fully qualified name of the class handling product tags. + * - 'product_categories' => Fully qualified name of the class handling product categories. + * - 'taxes' => Fully qualified name of the class handling taxes. + * - 'shipping_methods' => Fully qualified name of the class handling shipping methods. + * - 'tax_classes' => Fully qualified name of the class handling tax classes. + * - 'order_statuses' => Fully qualified name of the class handling order statuses. */ $classes = apply_filters( 'woocommerce_pos_rest_api_controllers', @@ -93,7 +88,7 @@ public function register_routes() { // woocommerce pos rest api controllers. 'auth' => API\Auth::class, 'settings' => API\Settings::class, - 'stores' => API\Stores::class, + 'cashier' => API\Cashier::class, // extend WC REST API controllers. 'products' => API\Products_Controller::class, @@ -102,6 +97,7 @@ public function register_routes() { 'customers' => API\Customers_Controller::class, 'product_tags' => API\Product_Tags_Controller::class, 'product_categories' => API\Product_Categories_Controller::class, + 'product_brands' => API\Product_Brands_Controller::class, 'taxes' => API\Taxes_Controller::class, 'shipping_methods' => API\Shipping_Methods_Controller::class, 'tax_classes' => API\Tax_Classes_Controller::class, @@ -197,7 +193,7 @@ public function rest_authentication_errors( $errors ) { /** * Extract the Authorization Bearer token from the request. * - * @return string|false + * @return false|string */ public function get_auth_header() { // Check if HTTP_AUTHORIZATION is set in $_SERVER @@ -222,7 +218,7 @@ public function get_auth_header() { /** * Adds info to the WP REST API index response. * - UUID - * - Version Info + * - Version Info. * * @param WP_REST_Response $response Response data. * @@ -234,11 +230,20 @@ public function rest_index( WP_REST_Response $response ): WP_REST_Response { $uuid = Uuid::uuid4()->toString(); update_option( 'woocommerce_pos_uuid', $uuid ); } - $response->data['uuid'] = $uuid; - $response->data['wp_version'] = get_bloginfo( 'version' ); - $response->data['wc_version'] = WC()->version; + $response->data['uuid'] = $uuid; + $response->data['wp_version'] = get_bloginfo( 'version' ); + $response->data['wc_version'] = WC()->version; $response->data['wcpos_version'] = VERSION; - $response->data['use_jwt_as_param'] = woocommerce_pos_get_settings( 'tools', 'use_jwt_as_param' ); + + // Add wcpos authentication endpoint + if ( ! isset( $response->data['authentication'] ) ) { + $response->data['authentication'] = array(); + } + $response->data['authentication']['wcpos'] = array( + 'endpoints' => array( + 'authorization' => home_url( 'wcpos-auth' ), + ), + ); /** * Remove the routes from the response. @@ -289,44 +294,6 @@ public function rest_pre_dispatch( $result, $server, $request ) { return $result; } - /** - * Some servers have a limit on the number of include/exclude we can use in a request. - * Worst thing is there is often no error message, the request returns an empty response. - * - * For example, WP Engine has a limit of 1024 characters? - * https://wpengine.com/support/using-dev-tools/#Long_Queries_in_wp_db - * - * @TODO - For long queries, I should find a better solution than this. - * - * @param string|array $param_value - * @param int $max_length - * @return array - */ - private function shorten_param_array( $param_value, $max_length ) { - $param_array = is_array( $param_value ) ? $param_value : explode( ',', $param_value ); - $param_string = implode( ',', $param_array ); - - if ( strlen( $param_string ) > $max_length ) { - shuffle( $param_array ); // Shuffle to randomize - - $new_param_string = ''; - $random_param_array = array(); - - foreach ( $param_array as $id ) { - if ( strlen( $new_param_string . $id ) < $max_length ) { - $new_param_string .= $id . ','; - $random_param_array[] = $id; - } else { - break; // Stop when maximum length is reached - } - } - - return $random_param_array; - } - - return $param_array; - } - /** * Filters the REST API dispatch request result. * @@ -338,13 +305,13 @@ private function shorten_param_array( $param_value, $max_length ) { * @return mixed */ public function rest_dispatch_request( $dispatch_result, $request, $route, $handler ) { - if ( isset( $handler['callback'] ) && is_array( $handler['callback'] ) && isset( $handler['callback'][0] ) ) { + if ( isset( $handler['callback'] ) && \is_array( $handler['callback'] ) && isset( $handler['callback'][0] ) ) { $controller = $handler['callback'][0]; // Check if the controller object is one of our registered controllers. foreach ( $this->controllers as $key => $wcpos_controller ) { if ( $controller === $wcpos_controller ) { - /** + /* * I'm adding some additional PHP settings before the response. Placing them here so they only apply to the POS API. * * - error_reporting(0) - Turn off error reporting @@ -365,6 +332,7 @@ public function rest_dispatch_request( $dispatch_result, $request, $route, $hand if ( method_exists( $controller, 'wcpos_dispatch_request' ) ) { return $controller->wcpos_dispatch_request( $dispatch_result, $request, $route, $handler ); } + break; } } @@ -373,6 +341,45 @@ public function rest_dispatch_request( $dispatch_result, $request, $route, $hand return $dispatch_result; } + /** + * Some servers have a limit on the number of include/exclude we can use in a request. + * Worst thing is there is often no error message, the request returns an empty response. + * + * For example, WP Engine has a limit of 1024 characters? + * https://wpengine.com/support/using-dev-tools/#Long_Queries_in_wp_db + * + * @TODO - For long queries, I should find a better solution than this. + * + * @param array|string $param_value + * @param int $max_length + * + * @return array + */ + private function shorten_param_array( $param_value, $max_length ) { + $param_array = \is_array( $param_value ) ? $param_value : explode( ',', $param_value ); + $param_string = implode( ',', $param_array ); + + if ( \strlen( $param_string ) > $max_length ) { + shuffle( $param_array ); // Shuffle to randomize + + $new_param_string = ''; + $random_param_array = array(); + + foreach ( $param_array as $id ) { + if ( \strlen( $new_param_string . $id ) < $max_length ) { + $new_param_string .= $id . ','; + $random_param_array[] = $id; + } else { + break; // Stop when maximum length is reached + } + } + + return $random_param_array; + } + + return $param_array; + } + /** * Check the Authorization header for a Bearer token. * @@ -383,7 +390,7 @@ public function rest_dispatch_request( $dispatch_result, $request, $route, $hand private function authenticate( $user_id ) { // check if there is an auth header $auth_header = $this->get_auth_header(); - if ( ! is_string( $auth_header ) ) { + if ( ! \is_string( $auth_header ) ) { return $user_id; } @@ -391,17 +398,18 @@ private function authenticate( $user_id ) { list($token) = sscanf( $auth_header, 'Bearer %s' ); if ( $token ) { - $auth_service = Auth::instance(); + $auth_service = Auth::instance(); $decoded_token = $auth_service->validate_token( $token ); // Check if validate_token returned WP_Error and user_id is null - if ( is_wp_error( $decoded_token ) && $user_id === null ) { - return $decoded_token; + if ( is_wp_error( $decoded_token ) && null === $user_id ) { + return $decoded_token; } // If the token is valid, set the user_id if ( ! is_wp_error( $decoded_token ) ) { $user_id = $decoded_token->data->user->id; + return absint( $user_id ); } } diff --git a/includes/API/Auth.php b/includes/API/Auth.php index b358de8..aa48e80 100644 --- a/includes/API/Auth.php +++ b/includes/API/Auth.php @@ -10,23 +10,19 @@ namespace WCPOS\WooCommercePOS\API; -use WP_Error; +use WCPOS\WooCommercePOS\Services\Auth as AuthService; +use const WCPOS\WooCommercePOS\SHORT_NAME; +use WP_REST_Controller; use WP_REST_Request; use WP_REST_Response; use WP_REST_Server; -use WP_REST_Controller; -use WCPOS\WooCommercePOS\Services\Auth as AuthService; -use const WCPOS\WooCommercePOS\SHORT_NAME; -/** - * - */ class Auth extends WP_REST_Controller { - /** - * Endpoint namespace. - * - * @var string - */ + /** + * Endpoint namespace. + * + * @var string + */ protected $namespace = SHORT_NAME . '/v1'; /** @@ -34,7 +30,7 @@ class Auth extends WP_REST_Controller { * * @var string */ - protected $rest_base = 'jwt'; + protected $rest_base = 'auth'; /** * Stores constructor. @@ -42,76 +38,31 @@ class Auth extends WP_REST_Controller { public function __construct() { } - /** - * - */ public function register_routes(): void { - // Generate JWT token + // Test authorization method support (public endpoint) register_rest_route( $this->namespace, - '/' . $this->rest_base . '/authorize', + '/' . $this->rest_base . '/test', array( 'methods' => WP_REST_Server::READABLE, - 'callback' => array( $this, 'generate_token' ), - 'permission_callback' => function ( WP_REST_Request $request ) { - // special case for user=demo param - if ( $request->get_param( 'user' ) === 'demo' ) { - return true; - } - - $authorization = $request->get_header( 'authorization' ); - - return ! is_null( $authorization ); - }, - ) - ); - - // Validate JWT token - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/validate', - array( - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'validate_token' ), - 'permission_callback' => '__return_true', - 'args' => array( - 'jwt' => array( - 'description' => __( 'JWT token.', 'woocommerce-pos' ), - 'type' => 'string', - ), - ), + 'callback' => array( $this, 'test_authorization' ), + 'permission_callback' => '__return_true', // Public endpoint - no authentication required ) ); - // Refresh JWT token + // Refresh access token using refresh token register_rest_route( $this->namespace, '/' . $this->rest_base . '/refresh', array( 'methods' => WP_REST_Server::CREATABLE, 'callback' => array( $this, 'refresh_token' ), - 'permission_callback' => '__return_true', + 'permission_callback' => '__return_true', // Public endpoint - validates refresh token internally 'args' => array( - 'jwt' => array( - 'description' => __( 'JWT token.', 'woocommerce-pos' ), - 'type' => 'string', - ), - ), - ) - ); - - // Revoke JWT token - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/revoke', - array( - 'methods' => WP_REST_Server::CREATABLE, - 'callback' => array( $this, 'revoke_token' ), - 'permission_callback' => '__return_true', - 'args' => array( - 'jwt' => array( - 'description' => __( 'JWT token.', 'woocommerce-pos' ), + 'refresh_token' => array( + 'description' => __( 'The refresh token to use for generating a new access token.', 'woocommerce-pos' ), 'type' => 'string', + 'required' => true, ), ), ) @@ -120,83 +71,116 @@ public function register_routes(): void { /** - * Get the user and password in the request body and generate a JWT. + * Test authorization method endpoint. * - * @NOTE - not allowing REST Auth at the moment + * This public endpoint tests whether the server supports Authorization headers + * or requires query parameters for authorization. This is important because + * some servers block Authorization headers for security reasons. * - * @param WP_REST_Request $request - * @return WP_Error|WP_REST_Response + * @param WP_REST_Request $request The REST request object. + * + * @return WP_REST_Response */ - public function generate_token( WP_REST_Request $request ) { - $token = str_replace( 'Basic ', '', $request->get_header( 'authorization' ) ); - $decoded = base64_decode( $token, true ); - list($username, $password) = explode( ':', $decoded ); - - /** Try to authenticate the user with the passed credentials*/ - $user = wp_authenticate( $username, $password ); - - // If the authentication fails return an error - if ( is_wp_error( $user ) ) { - $error_code = $user->get_error_code(); - - $user_data = new WP_Error( - 'woocommerce_pos_' . $error_code, - $user->get_error_message( $error_code ), - array( - 'status' => 403, - ) - ); + public function test_authorization( WP_REST_Request $request ): WP_REST_Response { + // Check for Authorization header + $header_auth = $request->get_header( 'authorization' ); + $has_header_auth = ! empty( $header_auth ); + + // Check for authorization query parameter + $param_auth = $request->get_param( 'authorization' ); + $has_param_auth = ! empty( $param_auth ); + + // Only return success if we received authorization via at least one method + if ( ! $has_header_auth && ! $has_param_auth ) { + return rest_ensure_response( array( + 'status' => 'error', + 'message' => 'No authorization token detected', + ) ); + } + + $response_data = array( + 'status' => 'success', + 'message' => 'Authorization token detected successfully', + ); + + // Add authorization details + $response_data['received_header_auth'] = $has_header_auth; + if ( $has_header_auth ) { + $response_data['header_value'] = $header_auth; + } + + $response_data['received_param_auth'] = $has_param_auth; + if ( $has_param_auth ) { + $response_data['param_value'] = $param_auth; + } + + // Indicate which method was used + if ( $has_header_auth && $has_param_auth ) { + $response_data['auth_method'] = 'both'; + } elseif ( $has_header_auth ) { + $response_data['auth_method'] = 'header'; } else { - $auth_service = AuthService::instance(); - $user_data = $auth_service->get_user_data( $user ); - $stores = array_map( - function ( $store ) { - return $store->get_data(); - }, - wcpos_get_stores() - ); - $user_data['stores'] = $stores; + $response_data['auth_method'] = 'param'; } - /** - * Let the user modify the data before sending it back - * - * @param {object} $data - * @param {WP_User} $user - * - * @returns {object} Response data - * - * @since 1.0.0 - * - * @hook woocommerce_pos_jwt_auth_token_before_dispatch - */ - $user_data = apply_filters( 'woocommerce_pos_jwt_auth_token_before_dispatch', $user_data, $user ); - - return rest_ensure_response( $user_data ); + return rest_ensure_response( $response_data ); } /** - * Validate JWT Token. + * Refresh access token using a valid refresh token. + * + * This endpoint allows clients to obtain a new access token using a valid refresh token. + * Compatible with the axios-auth-refresh library and follows OAuth 2.0 refresh token flow. + * + * @param WP_REST_Request $request The REST request object. * - * @param WP_REST_Request $request * @return WP_REST_Response */ - public function validate_token( WP_REST_Request $request ): WP_REST_Response { - $token = $request->get_param( 'jwt' ); + public function refresh_token( WP_REST_Request $request ): WP_REST_Response { + $refresh_token = $request->get_param( 'refresh_token' ); + + if ( empty( $refresh_token ) ) { + return rest_ensure_response( array( + 'error' => 'invalid_request', + 'error_description' => 'Missing refresh_token parameter', + ), 400 ); + } + $auth_service = AuthService::instance(); - $result = $auth_service->validate_token( $token ); - return rest_ensure_response( $result ); - } + $result = $auth_service->refresh_access_token( $refresh_token ); + + if ( is_wp_error( $result ) ) { + $error_code = $result->get_error_code(); + $error_msg = $result->get_error_message(); + $status = $result->get_error_data()['status'] ?? 400; + + // Map error codes to OAuth 2.0 standard error responses + $oauth_error = 'invalid_grant'; // Default OAuth error for refresh token issues + + if ( false !== strpos( $error_code, 'invalid_token' ) || false !== strpos( $error_code, 'revoked' ) ) { + $oauth_error = 'invalid_grant'; + } elseif ( false !== strpos( $error_code, 'user_not_found' ) ) { + $oauth_error = 'invalid_grant'; + } + + return rest_ensure_response( array( + 'error' => $oauth_error, + 'error_description' => $error_msg, + ), $status ); + } - /** - * Refresh JWT Token. - */ - public function refresh_token(): void { - } + // Calculate expires_in for axios-auth-refresh compatibility + $current_time = time(); + $expires_in = max( 0, $result['expires_at'] - $current_time ); - /** - * Revoke JWT Token. - */ - public function revoke_token(): void { + // Return response in format compatible with axios-auth-refresh + $response_data = array( + 'access_token' => $result['access_token'], + 'token_type' => $result['token_type'], + 'expires_in' => $expires_in, + 'expires_at' => $result['expires_at'], + ); + + return rest_ensure_response( $response_data ); } } diff --git a/includes/API/Cashier.php b/includes/API/Cashier.php new file mode 100644 index 0000000..9d35154 --- /dev/null +++ b/includes/API/Cashier.php @@ -0,0 +1,297 @@ +namespace, + '/' . $this->rest_base . '/(?P[\d]+)', + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_cashier' ), + 'permission_callback' => array( $this, 'check_cashier_permissions' ), + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the cashier (WordPress user ID).', 'woocommerce-pos' ), + 'type' => 'integer', + 'required' => true, + ), + ), + ) + ); + + // Get cashier stores: /wcpos/v1/cashier/{id}/stores + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[\d]+)/stores', + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_cashier_stores' ), + 'permission_callback' => array( $this, 'check_cashier_permissions' ), + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the cashier (WordPress user ID).', 'woocommerce-pos' ), + 'type' => 'integer', + 'required' => true, + ), + ), + ) + ); + + // Get specific store for cashier: /wcpos/v1/cashier/{id}/stores/{store_id} + register_rest_route( + $this->namespace, + '/' . $this->rest_base . '/(?P[\d]+)/stores/(?P[\d]+)', + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_cashier_store' ), + 'permission_callback' => array( $this, 'check_cashier_permissions' ), + 'args' => array( + 'id' => array( + 'description' => __( 'Unique identifier for the cashier (WordPress user ID).', 'woocommerce-pos' ), + 'type' => 'integer', + 'required' => true, + ), + 'store_id' => array( + 'description' => __( 'Unique identifier for the store.', 'woocommerce-pos' ), + 'type' => 'integer', + 'required' => true, + ), + ), + ) + ); + } + + /** + * Check permissions for cashier endpoints. + * + * Ensures the user is authenticated and can only access their own data. + * + * @param WP_REST_Request $request The REST request object. + * + * @return bool|WP_Error True if authorized, WP_Error otherwise. + */ + public function check_cashier_permissions( WP_REST_Request $request ) { + // Check if user is authenticated + if ( ! is_user_logged_in() ) { + return new WP_Error( + 'woocommerce_pos_rest_unauthorized', + __( 'Authentication required.', 'woocommerce-pos' ), + array( 'status' => 401 ) + ); + } + + $current_user_id = get_current_user_id(); + $requested_id = (int) $request->get_param( 'id' ); + + // Check if the requested user exists + $user = get_user_by( 'id', $requested_id ); + if ( ! $user ) { + return new WP_Error( + 'woocommerce_pos_cashier_not_found', + __( 'Cashier not found.', 'woocommerce-pos' ), + array( 'status' => 404 ) + ); + } + + $cashier_service = CashierService::instance(); + + // Check if user has POS cashier permissions + if ( ! $cashier_service->has_cashier_permissions( $user ) ) { + return new WP_Error( + 'woocommerce_pos_rest_forbidden', + __( 'User does not have POS cashier permissions.', 'woocommerce-pos' ), + array( 'status' => 403 ) + ); + } + + // Validate access permissions + if ( ! $cashier_service->validate_cashier_access( $current_user_id, $requested_id ) ) { + return new WP_Error( + 'woocommerce_pos_rest_forbidden', + __( 'You can only access your own cashier data.', 'woocommerce-pos' ), + array( 'status' => 403 ) + ); + } + + return true; + } + + /** + * Get cashier data. + * + * @param WP_REST_Request $request The REST request object. + * + * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. + */ + public function get_cashier( WP_REST_Request $request ) { + $user_id = (int) $request->get_param( 'id' ); + $user = get_user_by( 'id', $user_id ); + + if ( ! $user ) { + return new WP_Error( + 'woocommerce_pos_cashier_not_found', + __( 'Cashier not found.', 'woocommerce-pos' ), + array( 'status' => 404 ) + ); + } + + $cashier_service = CashierService::instance(); + $data = $cashier_service->get_cashier_data( $user, true ); + + /** + * Filter cashier data for REST API response. + * + * @param array $data Cashier data. + * @param WP_User $user User object. + * @param WP_REST_Request $request Request object. + */ + $data = apply_filters( 'woocommerce_pos_rest_prepare_cashier', $data, $user, $request ); + + return rest_ensure_response( $data ); + } + + /** + * Get stores accessible by the cashier. + * + * @param WP_REST_Request $request The REST request object. + * + * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. + */ + public function get_cashier_stores( WP_REST_Request $request ) { + $user_id = (int) $request->get_param( 'id' ); + $user = get_user_by( 'id', $user_id ); + + if ( ! $user ) { + return new WP_Error( + 'woocommerce_pos_cashier_not_found', + __( 'Cashier not found.', 'woocommerce-pos' ), + array( 'status' => 404 ) + ); + } + + try { + $cashier_service = CashierService::instance(); + $stores = $cashier_service->get_accessible_stores( $user ); + $response = array(); + + foreach ( $stores as $store ) { + $data = $this->prepare_store_for_response( $store, $request ); + $response[] = $this->prepare_response_for_collection( $data ); + } + + $response = rest_ensure_response( $response ); + $response->header( 'X-WP-Total', \count( $stores ) ); + $response->header( 'X-WP-TotalPages', 1 ); + + return $response; + } catch ( Exception $e ) { + return new WP_Error( + 'woocommerce_pos_stores_retrieval_failed', + __( 'Failed to retrieve store data.', 'woocommerce-pos' ), + array( 'status' => 500 ) + ); + } + } + + /** + * Get a specific store for the cashier. + * + * @param WP_REST_Request $request The REST request object. + * + * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure. + */ + public function get_cashier_store( WP_REST_Request $request ) { + $user_id = (int) $request->get_param( 'id' ); + $store_id = (int) $request->get_param( 'store_id' ); + + $user = get_user_by( 'id', $user_id ); + if ( ! $user ) { + return new WP_Error( + 'woocommerce_pos_cashier_not_found', + __( 'Cashier not found.', 'woocommerce-pos' ), + array( 'status' => 404 ) + ); + } + + $cashier_service = CashierService::instance(); + $store = $cashier_service->get_accessible_store( $user, $store_id ); + + if ( ! $store ) { + return new WP_Error( + 'woocommerce_pos_store_not_found', + __( 'Store not found or not accessible by this cashier.', 'woocommerce-pos' ), + array( 'status' => 404 ) + ); + } + + $data = $this->prepare_store_for_response( $store, $request ); + + return rest_ensure_response( $data ); + } + + + + /** + * Prepare store data for response. + * + * @param Store $store Store object. + * @param WP_REST_Request $request Request object. + * + * @return array Prepared store data. + */ + protected function prepare_store_for_response( Store $store, WP_REST_Request $request ): array { + $data = $store->get_data(); + + /* + * Filter store data for REST API response. + * + * @param array $data Store data. + * @param Store $store Store object. + * @param WP_REST_Request $request Request object. + */ + return apply_filters( 'woocommerce_pos_rest_prepare_store', $data, $store, $request ); + } +} diff --git a/includes/API/Product_Brands_Controller.php b/includes/API/Product_Brands_Controller.php new file mode 100644 index 0000000..5d9cd98 --- /dev/null +++ b/includes/API/Product_Brands_Controller.php @@ -0,0 +1,200 @@ +wcpos_request = $request; + + add_filter( 'woocommerce_rest_prepare_product_brand', array( $this, 'wcpos_product_brands_response' ), 10, 3 ); + add_filter( 'woocommerce_rest_product_brand_query', array( $this, 'wcpos_product_brand_query' ), 10, 2 ); + + /* + * Check if the request is for all brands and if the 'posts_per_page' is set to -1. + * Optimised query for getting all brand IDs. + */ + if ( -1 == $request->get_param( 'posts_per_page' ) && null !== $request->get_param( 'fields' ) ) { + return $this->wcpos_get_all_posts( $request ); + } + + return $dispatch_result; + } + + /** + * Filter the brand response. + * + * @param WP_REST_Response $response The response object. + * @param object $item The original term object. + * @param WP_REST_Request $request Request object. + * + * @return WP_REST_Response $response The response object. + */ + public function wcpos_product_brands_response( WP_REST_Response $response, object $item, WP_REST_Request $request ): WP_REST_Response { + $data = $response->get_data(); + + // Make sure the term has a uuid + $data['uuid'] = $this->get_term_uuid( $item ); + + // Reset the new response data + $response->set_data( $data ); + + return $response; + } + + /** + * Filter the brand query. + * + * @param array $args Query arguments. + * @param WP_REST_Request $request Request object. + */ + public function wcpos_product_brand_query( array $args, WP_REST_Request $request ): array { + // Check for wcpos_include/wcpos_exclude parameter. + if ( isset( $request['wcpos_include'] ) || isset( $request['wcpos_exclude'] ) ) { + // Add a custom WHERE clause to the query. + add_filter( 'terms_clauses', array( $this, 'wcpos_terms_clauses_include_exclude' ), 10, 3 ); + } + + return $args; + } + + /** + * Filters the terms query SQL clauses. + * + * @param string[] $clauses { + * Associative array of the clauses for the query. + * + * @var string The SELECT clause of the query. + * @var string The JOIN clause of the query. + * @var string The WHERE clause of the query. + * @var string The DISTINCT clause of the query. + * @var string The ORDER BY clause of the query. + * @var string The ORDER clause of the query. + * @var string The LIMIT clause of the query. + * } + * + * @param string[] $taxonomies An array of taxonomy names. + * @param array $args An array of term query arguments. + * + * @return string[] $clauses + */ + public function wcpos_terms_clauses_include_exclude( array $clauses, array $taxonomies, array $args ) { + global $wpdb; + + // Handle 'wcpos_include' + if ( ! empty( $this->wcpos_request['wcpos_include'] ) ) { + $include_ids = array_map( 'intval', $this->wcpos_request['wcpos_include'] ); + $ids_format = implode( ',', array_fill( 0, \count( $include_ids ), '%d' ) ); + $clauses['where'] .= $wpdb->prepare( " AND t.term_id IN ($ids_format) ", $include_ids ); + } + + // Handle 'wcpos_exclude' + if ( ! empty( $this->wcpos_request['wcpos_exclude'] ) ) { + $exclude_ids = array_map( 'intval', $this->wcpos_request['wcpos_exclude'] ); + $ids_format = implode( ',', array_fill( 0, \count( $exclude_ids ), '%d' ) ); + $clauses['where'] .= $wpdb->prepare( " AND t.term_id NOT IN ($ids_format) ", $exclude_ids ); + } + + return $clauses; + } + + /** + * Returns array of all product brand ids. + * + * @param WP_REST_Request $request Full details about the request. + * + * @return WP_Error|WP_REST_Response + */ + public function wcpos_get_all_posts( $request ) { + // Start timing execution. + $start_time = microtime( true ); + $modified_after = $request->get_param( 'modified_after' ); + + $args = array( + 'taxonomy' => 'product_brand', + 'hide_empty' => false, + 'fields' => 'ids', + ); + + try { + /** + * @TODO - terms don't have a modified date, it would be good to add a term_meta for last_update + * - ideally WooCommerce would provide a modified_after filter for terms + * - for now we'll just return empty for modified terms + */ + $results = $modified_after ? array() : get_terms( $args ); + + // Format the response. + $formatted_results = array_map( + function ( $id ) { + return array( 'id' => (int) $id ); + }, + $results + ); + + // Get the total number of brands for the given criteria. + $total = \count( $formatted_results ); + + // Collect execution time and server load. + $execution_time = microtime( true ) - $start_time; + $execution_time_ms = number_format( $execution_time * 1000, 2 ); + $server_load = $this->get_server_load(); + + $response = rest_ensure_response( $formatted_results ); + $response->header( 'X-WP-Total', (int) $total ); + $response->header( 'X-Execution-Time', $execution_time_ms . ' ms' ); + $response->header( 'X-Server-Load', json_encode( $server_load ) ); + + return $response; + } catch ( Exception $e ) { + Logger::log( 'Error fetching product brand IDs: ' . $e->getMessage() ); + + return new \WP_Error( + 'woocommerce_pos_rest_cannot_fetch', + 'Error fetching product brand IDs.', + array( 'status' => 500 ) + ); + } + } +} diff --git a/includes/API/Stores.php b/includes/API/Stores.php deleted file mode 100644 index 2f45e67..0000000 --- a/includes/API/Stores.php +++ /dev/null @@ -1,178 +0,0 @@ -namespace, - '/' . $this->rest_base, - array( - 'methods' => 'GET', - 'callback' => array( $this, 'get_items' ), - 'permission_callback' => array( $this, 'check_permissions' ), - ) - ); - - register_rest_route( - $this->namespace, - '/' . $this->rest_base . '/(?P[\d]+)', - array( - 'methods' => 'GET', - 'callback' => array( $this, 'get_item' ), - 'permission_callback' => array( $this, 'check_permissions' ), - ) - ); - } - - /** - * Retrieve store data. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function get_items( $request ) { - try { - $stores = wcpos_get_stores(); - - $response = array(); - foreach ( $stores as $store ) { - $data = $this->prepare_item_for_response( $store, $request ); - $response[] = $this->prepare_response_for_collection( $data ); - } - - $response = rest_ensure_response( $response ); - $response->header( 'X-WP-Total', count( $stores ) ); - $response->header( 'X-WP-TotalPages', 1 ); - - return $response; - - } catch ( \Exception $e ) { - return new \WP_Error( - 'woocommerce_pos_store_retrieval_failed', - esc_html__( 'Failed to retrieve store data', 'woocommerce-pos' ), - array( 'status' => 500 ) - ); - } - } - - /** - * Retrieve a single store. - * - * @param WP_REST_Request $request Full details about the request. - * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. - */ - public function get_item( $request ) { - $store = wcpos_get_store( $request['id'] ); - - if ( ! $store ) { - return new \WP_Error( - 'woocommerce_pos_store_not_found', - esc_html__( 'Store not found', 'woocommerce-pos' ), - array( 'status' => 404 ) - ); - } - - $data = $this->prepare_item_for_response( $store, $request ); - - return rest_ensure_response( $data ); - } - - /** - * Prepare a single product output for response. - * - * @param Store $store Store object. - * @param WP_REST_Request $request Request object. - * @return WP_REST_Response - */ - public function prepare_item_for_response( $store, $request ) { - $data = $store->get_data(); - $response = rest_ensure_response( $data ); - $response->add_links( $this->prepare_links( $store, $request ) ); - - /** - * Filter the data for a response. - * - * The dynamic portion of the hook name, $this->post_type, refers to post_type of the post being - * prepared for the response. - * - * @param WP_REST_Response $response The response object. - * @param Store $store Store object. - * @param WP_REST_Request $request Request object. - */ - return apply_filters( 'woocommerce_pos_rest_prepare_store', $response, $store, $request ); - } - - /** - * Check if the user is logged in. - * - * @return bool|WP_Error True if the user is logged in, WP_Error otherwise. - */ - public function check_permissions() { - if ( ! is_user_logged_in() ) { - return new \WP_Error( - 'woocommerce_pos_rest_forbidden', - esc_html__( 'You do not have permissions to view this data.', 'woocommerce-pos' ), - array( 'status' => rest_authorization_required_code() ) - ); - } - - return true; - } - - /** - * Prepare links for the request. - * - * @param WC_Product $product Product object. - * @param WP_REST_Request $request Request object. - * @return array Links for the given product. - */ - protected function prepare_links( $store, $request ) { - $links = array( - 'self' => array( - 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $store->get_id() ) ), - ), - 'collection' => array( - 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), - ), - ); - - return $links; - } -} diff --git a/includes/Activator.php b/includes/Activator.php index 40d1f34..6f9ba6b 100644 --- a/includes/Activator.php +++ b/includes/Activator.php @@ -12,9 +12,6 @@ use const DOING_AJAX; -/** - * - */ class Activator { public function __construct() { register_activation_hook( PLUGIN_FILE, array( $this, 'activate' ) ); @@ -29,7 +26,7 @@ public function init(): void { // Check for min requirements to run if ( $this->php_check() && $this->woocommerce_check() ) { // check permalinks - if ( is_admin() && ( ! defined( '\DOING_AJAX' ) || ! DOING_AJAX ) ) { + if ( is_admin() && ( ! \defined( '\DOING_AJAX' ) || ! DOING_AJAX ) ) { $this->permalink_check(); } @@ -50,7 +47,7 @@ public function init(): void { * @param $network_wide */ public function activate( $network_wide ): void { - if ( function_exists( 'is_multisite' ) && is_multisite() ) { + if ( \function_exists( 'is_multisite' ) && is_multisite() ) { if ( $network_wide ) { // Get all blog ids $blog_ids = $this->get_blog_ids(); @@ -79,7 +76,23 @@ public function single_activate(): void { // add pos capabilities to non POS roles $this->add_pos_capability( array( - 'administrator' => array( 'manage_woocommerce_pos', 'access_woocommerce_pos' ), + 'administrator' => array( + 'manage_woocommerce_pos', + 'access_woocommerce_pos', + 'edit_wcpos_store', + 'read_wcpos_store', + 'delete_wcpos_store', + 'edit_wcpos_stores', + 'edit_others_wcpos_stores', + 'publish_wcpos_stores', + 'read_private_wcpos_stores', + 'delete_wcpos_stores', + 'delete_private_wcpos_stores', + 'delete_published_wcpos_stores', + 'delete_others_wcpos_stores', + 'edit_private_wcpos_stores', + 'edit_published_wcpos_stores', + ), 'shop_manager' => array( 'manage_woocommerce_pos', 'access_woocommerce_pos' ), ) ); @@ -112,7 +125,7 @@ private function php_check() { return true; } - $message = sprintf( + $message = \sprintf( __( 'WooCommerce POS requires PHP %1$s or higher. Read more information about how you can update', 'woocommerce-pos' ), PHP_MIN_VERSION, 'http://www.wpupdatephp.com/update/' @@ -129,7 +142,7 @@ private function woocommerce_check() { return true; } - $message = sprintf( + $message = \sprintf( __( 'WooCommerce POS requires WooCommerce %2$s or higher. Please install and activate WooCommerce', 'woocommerce-pos' ), 'http://wordpress.org/plugins/woocommerce/', WC_MIN_VERSION, @@ -151,7 +164,7 @@ private function permalink_check(): void { } $message = __( 'WooCommerce REST API requires pretty permalinks to work correctly', 'woocommerce-pos' ) . '. '; - $message .= sprintf( '%s', admin_url( 'options-permalink.php' ), __( 'Enable permalinks', 'woocommerce-pos' ) ) . ' »'; + $message .= \sprintf( '%s', admin_url( 'options-permalink.php' ), __( 'Enable permalinks', 'woocommerce-pos' ) ) . ' »'; Admin\Notices::add( $message ); } @@ -263,23 +276,23 @@ private function db_upgrade( $old, $current ): void { } /** - * If \WCPOS\WooCommercePOSPro\ is installed, check the version is above MIN_PRO_VERSION + * If \WCPOS\WooCommercePOSPro\ is installed, check the version is above MIN_PRO_VERSION. */ - private function pro_version_check() { + private function pro_version_check(): void { if ( class_exists( '\WCPOS\WooCommercePOSPro\Activator' ) ) { if ( version_compare( \WCPOS\WooCommercePOSPro\VERSION, MIN_PRO_VERSION, '<' ) ) { - /** + /* * NOTE: the deactivate_plugins function is not available in the frontend or ajax * This is an extreme situation where the Pro plugin could crash the site, so we need to deactivate it */ - if ( ! function_exists( 'deactivate_plugins' ) ) { + if ( ! \function_exists( 'deactivate_plugins' ) ) { require_once ABSPATH . '/wp-admin/includes/plugin.php'; } // WooCommerce POS Pro is activated, but the version is too low deactivate_plugins( 'woocommerce-pos-pro/woocommerce-pos-pro.php' ); - $message = sprintf( + $message = \sprintf( __( 'WooCommerce POS requires WooCommerce POS Pro %2$s or higher. Please install and activate WooCommerce POS Pro', 'woocommerce-pos' ), 'https://wcpos.com/my-account', MIN_PRO_VERSION, diff --git a/includes/Admin/Products/Single_Product.php b/includes/Admin/Products/Single_Product.php index 2e3e990..2cde53d 100644 --- a/includes/Admin/Products/Single_Product.php +++ b/includes/Admin/Products/Single_Product.php @@ -46,7 +46,7 @@ public function __construct() { 'online_only' => __( 'Online Only', 'woocommerce-pos' ), ); - if ( $this->barcode_field && ! \in_array( $this->barcode_field, array( '_sku', '_global_unique_id' ), true ) ) { + if ( $this->barcode_field && ! \in_array( $this->barcode_field, $this->get_excluded_barcode_fields(), true ) ) { add_action( 'woocommerce_product_options_sku', array( $this, 'woocommerce_product_options_sku' ) ); add_action( 'woocommerce_process_product_meta', array( $this, 'woocommerce_process_product_meta' ) ); add_action( 'woocommerce_product_after_variable_attributes', array( $this, 'after_variable_attributes_barcode_field' ), 10, 3 ); @@ -280,4 +280,26 @@ public function save_product_variation_pos_only_products( $variation_id ): void $settings_instance->update_visibility_settings( $args ); } } + + /** + * Get the list of barcode fields that should be excluded from custom barcode field functionality. + * + * These fields are built-in WooCommerce or plugin fields that don't need custom barcode input. + * + * @return array Array of excluded barcode field keys. + */ + private function get_excluded_barcode_fields(): array { + $excluded_fields = array( + '_sku', // default WooCommerce SKU field + '_global_unique_id', // default WooCommerce GTIN, UPC, EAN, or ISBN + '_alg_ean', // https://wpfactory.com/item/ean-barcodes-woocommerce/ + ); + + /* + * Filter the list of barcode fields that should be excluded from custom barcode field functionality. + * + * @param array $excluded_fields Array of field keys to exclude from custom barcode input. + */ + return apply_filters( 'woocommerce_pos_excluded_custom_barcode_fields', $excluded_fields ); + } } diff --git a/includes/Services/Auth.php b/includes/Services/Auth.php index 6fc4a7b..a7e5205 100644 --- a/includes/Services/Auth.php +++ b/includes/Services/Auth.php @@ -2,37 +2,25 @@ namespace WCPOS\WooCommercePOS\Services; +use const DAY_IN_SECONDS; use Exception; +use const HOUR_IN_SECONDS; use WCPOS\Vendor\Firebase\JWT\JWT; use WCPOS\Vendor\Firebase\JWT\Key; -use Ramsey\Uuid\Uuid; use WP_Error; use WP_User; -use const DAY_IN_SECONDS; /** - * Auth Service class + * Auth Service class. */ class Auth { /** * The single instance of the class. * - * @var Auth|null + * @var null|Auth */ private static $instance = null; - /** - * Gets the singleton instance. - * - * @return Auth - */ - public static function instance(): Auth { - if ( null === self::$instance ) { - self::$instance = new self(); - } - return self::$instance; - } - /** * Constructor is private to prevent direct instantiation. * Or Auth::instance() instead. @@ -41,7 +29,20 @@ public function __construct() { } /** - * Generate a secret key if it doesn't exist, or return the existing one + * Gets the singleton instance. + * + * @return Auth + */ + public static function instance(): self { + if ( null === self::$instance ) { + self::$instance = new self(); + } + + return self::$instance; + } + + /** + * Generate a secret key if it doesn't exist, or return the existing one. * * @return string */ @@ -51,19 +52,37 @@ public function get_secret_key(): string { $secret_key = wp_generate_password( 64, true, true ); update_option( 'woocommerce_pos_secret_key', $secret_key ); } + return $secret_key; } /** - * Validate the provided JWT token + * Get refresh token secret key (separate from access token key for security). + * + * @return string + */ + public function get_refresh_secret_key(): string { + $secret_key = get_option( 'woocommerce_pos_refresh_secret_key' ); + if ( false === $secret_key || empty( $secret_key ) ) { + $secret_key = wp_generate_password( 64, true, true ); + update_option( 'woocommerce_pos_refresh_secret_key', $secret_key ); + } + + return $secret_key; + } + + /** + * Validate the provided JWT token. * * @param string $token + * @param string $token_type 'access' or 'refresh' * * @return object|WP_Error */ - public function validate_token( $token = '' ) { + public function validate_token( $token = '', $token_type = 'access' ) { try { - $decoded_token = JWT::decode( $token, new Key( $this->get_secret_key(), 'HS256' ) ); + $secret_key = 'refresh' === $token_type ? $this->get_refresh_secret_key() : $this->get_secret_key(); + $decoded_token = JWT::decode( $token, new Key( $secret_key, 'HS256' ) ); // The Token is decoded now validate the iss if ( get_bloginfo( 'url' ) != $decoded_token->iss ) { @@ -75,6 +94,15 @@ public function validate_token( $token = '' ) { ); } + // Validate token type + if ( ! isset( $decoded_token->type ) || $decoded_token->type !== $token_type ) { + return new WP_Error( + 'woocommmerce_pos_auth_invalid_token_type', + 'Invalid token type', + array( 'status' => 403 ) + ); + } + // So far so good, validate the user id in the token if ( ! isset( $decoded_token->data->user->id ) ) { // No user id in the token, abort!! @@ -87,10 +115,10 @@ public function validate_token( $token = '' ) { ); } - /** Everything looks good return the decoded token */ + // Everything looks good return the decoded token return $decoded_token; } catch ( Exception $e ) { - // Something is wrong trying to decode the token, send back the error + // Something is wrong trying to decode the token, send back the error return new WP_Error( 'woocommmerce_pos_auth_invalid_token', $e->getMessage(), @@ -102,22 +130,79 @@ public function validate_token( $token = '' ) { } /** - * Generate a JWT token for the provided user + * Generate an access token for the provided user (short-lived). * * @param WP_User $user * * @return string|WP_Error */ + public function generate_access_token( WP_User $user ) { + // First thing, check the secret key if not exist return a error + if ( ! $this->get_secret_key() ) { + return new WP_Error( + 'woocommerce_pos_jwt_auth_bad_config', + __( 'JWT is not configured properly, please contact the admin', 'woocommerce-pos' ), + array( + 'status' => 403, + ) + ); + } + + /** Valid credentials, the user exists create the according Token */ + $issued_at = time(); + + /** + * Filters the JWT access token expire time. + * Default: 30 minutes for access tokens. + * + * @param {int} $expire_time + * @param {int} $issued_at + * + * @returns {int} Expire time + * + * @since 1.8.0 + * + * @hook woocommerce_pos_jwt_access_token_expire + */ + $expire = apply_filters( 'woocommerce_pos_jwt_access_token_expire', $issued_at + ( HOUR_IN_SECONDS / 2 ), $issued_at ); + + $token = array( + 'iss' => get_bloginfo( 'url' ), + 'iat' => $issued_at, + 'exp' => $expire, + 'type' => 'access', + 'data' => array( + 'user' => array( + 'id' => $user->data->ID, + ), + ), + ); + + /* + * Let the user modify the access token data before the sign. + * + * @param {array} $token + * @param {WP_User} $user + * + * @returns {array} Token + * + * @since 1.8.0 + * + * @hook woocommerce_pos_jwt_access_token_before_sign + */ + return JWT::encode( apply_filters( 'woocommerce_pos_jwt_access_token_before_sign', $token, $user ), $this->get_secret_key(), 'HS256' ); + } + /** - * Generate a JWT token for the provided user + * Generate a refresh token for the provided user (long-lived). * * @param WP_User $user * * @return string|WP_Error */ - public function generate_token( WP_User $user ) { + public function generate_refresh_token( WP_User $user ) { // First thing, check the secret key if not exist return a error - if ( ! $this->get_secret_key() ) { + if ( ! $this->get_refresh_secret_key() ) { return new WP_Error( 'woocommerce_pos_jwt_auth_bad_config', __( 'JWT is not configured properly, please contact the admin', 'woocommerce-pos' ), @@ -131,30 +216,29 @@ public function generate_token( WP_User $user ) { $issued_at = time(); /** - * Filters the JWT issued at time. + * Filters the JWT refresh token expire time. + * Default: 30 days for refresh tokens. * - * @param {string} $issued_at - * @returns {string} Issued at time - * @since 1.0.0 - * @hook woocommerce_pos_jwt_auth_not_before - */ - $not_before = apply_filters( 'woocommerce_pos_jwt_auth_not_before', $issued_at, $issued_at ); - - /** - * Filters the JWT expire time. + * @param {int} $expire_time + * @param {int} $issued_at * - * @param {string} $issued_at - * @returns {string} Expire time - * @since 1.0.0 - * @hook woocommerce_pos_jwt_auth_expire + * @returns {int} Expire time + * + * @since 1.8.0 + * + * @hook woocommerce_pos_jwt_refresh_token_expire */ - $expire = apply_filters( 'woocommerce_pos_jwt_auth_expire', $issued_at + ( DAY_IN_SECONDS * 7 ), $issued_at ); + $expire = apply_filters( 'woocommerce_pos_jwt_refresh_token_expire', $issued_at + ( DAY_IN_SECONDS * 30 ), $issued_at ); + + // Generate unique JTI (JWT ID) for refresh token tracking + $jti = wp_generate_uuid4(); $token = array( 'iss' => get_bloginfo( 'url' ), 'iat' => $issued_at, - 'nbf' => $not_before, 'exp' => $expire, + 'jti' => $jti, + 'type' => 'refresh', 'data' => array( 'user' => array( 'id' => $user->data->ID, @@ -163,32 +247,83 @@ public function generate_token( WP_User $user ) { ); /** - * Let the user modify the token data before the sign. + * Let the user modify the refresh token data before the sign. * - * @param {string} $token + * @param {array} $token * @param {WP_User} $user - * @returns {string} Token - * @since 1.0.0 - * @hook woocommerce_pos_jwt_auth_token_before_sign + * + * @returns {array} Token + * + * @since 1.8.0 + * + * @hook woocommerce_pos_jwt_refresh_token_before_sign */ - $token = JWT::encode( apply_filters( 'woocommerce_pos_jwt_auth_token_before_sign', $token, $user ), $this->get_secret_key(), 'HS256' ); + $token = JWT::encode( apply_filters( 'woocommerce_pos_jwt_refresh_token_before_sign', $token, $user ), $this->get_refresh_secret_key(), 'HS256' ); + + // Store refresh token JTI for potential revocation + $this->store_refresh_token_jti( $user->ID, $jti, $expire ); return $token; } + /** + * Generate both access and refresh tokens. + * + * @param WP_User $user + * + * @return array|WP_Error + */ + public function generate_token_pair( WP_User $user ) { + $access_token = $this->generate_access_token( $user ); + if ( is_wp_error( $access_token ) ) { + return $access_token; + } + + $refresh_token = $this->generate_refresh_token( $user ); + if ( is_wp_error( $refresh_token ) ) { + return $refresh_token; + } + + $issued_at = time(); + $expire = apply_filters( 'woocommerce_pos_jwt_access_token_expire', $issued_at + ( HOUR_IN_SECONDS / 2 ), $issued_at ); + + return array( + 'access_token' => $access_token, + 'refresh_token' => $refresh_token, + 'token_type' => 'Bearer', + 'expires_at' => (int) $expire, + ); + } + + /** + * Legacy method for backward compatibility. + * + * @deprecated Use generate_access_token() instead + * + * @param WP_User $user + * + * @return string|WP_Error + */ + public function generate_token( WP_User $user ) { + return $this->generate_access_token( $user ); + } /** - * Get user's data + * Get user's data (minimal set for security). * * @param WP_User $user * * @return array */ public function get_user_data( WP_User $user ): array { - $data = array( - 'uuid' => $this->get_user_uuid( $user ), + $tokens = $this->generate_token_pair( $user ); + if ( is_wp_error( $tokens ) ) { + return array(); + } + + return array( + 'uuid' => Cashier::instance()->get_cashier_uuid( $user ), 'id' => $user->ID, - 'jwt' => $this->generate_token( $user ), 'username' => $user->user_login, 'email' => $user->user_email, 'first_name' => $user->user_firstname, @@ -196,45 +331,163 @@ public function get_user_data( WP_User $user ): array { 'nice_name' => $user->user_nicename, 'display_name' => $user->display_name, 'avatar_url' => get_avatar_url( $user->ID ), + // Token data + 'access_token' => $tokens['access_token'], + 'refresh_token' => $tokens['refresh_token'], + 'token_type' => $tokens['token_type'], + 'expires_at' => $tokens['expires_at'], ); - - return $data; } /** - * Note: usermeta is shared across all sites in a network, this can cause issues in the POS. - * We need to make sure that the user uuid is unique per site. + * Get minimal user data for redirect (security-focused). * * @param WP_User $user - * @return string + * + * @return array + */ + public function get_redirect_data( WP_User $user ): array { + $tokens = $this->generate_token_pair( $user ); + if ( is_wp_error( $tokens ) ) { + return array(); + } + + // Only return essential data for redirect URL + return array( + 'access_token' => $tokens['access_token'], + 'refresh_token' => $tokens['refresh_token'], + 'token_type' => $tokens['token_type'], + 'expires_at' => $tokens['expires_at'], + // Get basic user data for display, other data will be fetched from the server. + 'uuid' => Cashier::instance()->get_cashier_uuid( $user ), + 'id' => $user->ID, + 'display_name' => $user->display_name, + ); + } + + /** + * Refresh an access token using a valid refresh token. + * + * @param string $refresh_token + * + * @return array|WP_Error */ - private function get_user_uuid( WP_User $user ): string { - $meta_key = '_woocommerce_pos_uuid'; + public function refresh_access_token( string $refresh_token ) { + $decoded = $this->validate_token( $refresh_token, 'refresh' ); + if ( is_wp_error( $decoded ) ) { + return $decoded; + } + + // Check if refresh token is still valid (not revoked) + if ( ! $this->is_refresh_token_valid( $decoded->data->user->id, $decoded->jti ?? '' ) ) { + return new WP_Error( + 'woocommerce_pos_auth_refresh_token_revoked', + 'Refresh token has been revoked', + array( 'status' => 403 ) + ); + } + + $user = get_user_by( 'id', $decoded->data->user->id ); + if ( ! $user ) { + return new WP_Error( + 'woocommerce_pos_auth_user_not_found', + 'User not found', + array( 'status' => 404 ) + ); + } + + // Generate new access token (refresh token stays the same) + $new_access_token = $this->generate_access_token( $user ); + if ( is_wp_error( $new_access_token ) ) { + return $new_access_token; + } - if ( function_exists( 'is_multisite' ) && is_multisite() ) { - $meta_key = $meta_key . '_' . get_current_blog_id(); + $issued_at = time(); + $expire = apply_filters( 'woocommerce_pos_jwt_access_token_expire', $issued_at + ( HOUR_IN_SECONDS / 2 ), $issued_at ); + + return array( + 'access_token' => $new_access_token, + 'token_type' => 'Bearer', + 'expires_at' => (int) $expire, + ); + } + + /** + * Revoke JWT Token by JTI. + * + * @param int $user_id + * @param string $jti + * + * @return bool + */ + public function revoke_refresh_token( int $user_id, string $jti ): bool { + $refresh_tokens = get_user_meta( $user_id, '_woocommerce_pos_refresh_tokens', true ); + if ( ! \is_array( $refresh_tokens ) ) { + return false; } - $uuid = get_user_meta( $user->ID, $meta_key, true ); - if ( ! $uuid ) { - $uuid = Uuid::uuid4()->toString(); - update_user_meta( $user->ID, $meta_key, $uuid ); + if ( isset( $refresh_tokens[ $jti ] ) ) { + unset( $refresh_tokens[ $jti ] ); + update_user_meta( $user_id, '_woocommerce_pos_refresh_tokens', $refresh_tokens ); + + return true; } - return $uuid; + return false; + } + + /** + * Revoke all refresh tokens for a user. + * + * @param int $user_id + * + * @return bool + */ + public function revoke_all_refresh_tokens( int $user_id ): bool { + return delete_user_meta( $user_id, '_woocommerce_pos_refresh_tokens' ); } /** - * Revoke JWT Token + * Store refresh token JTI for tracking/revocation. + * + * @param int $user_id + * @param string $jti + * @param int $expires */ - public function revoke_token(): void { - // Implementation + private function store_refresh_token_jti( int $user_id, string $jti, int $expires ): void { + $refresh_tokens = get_user_meta( $user_id, '_woocommerce_pos_refresh_tokens', true ); + if ( ! \is_array( $refresh_tokens ) ) { + $refresh_tokens = array(); + } + + // Clean up expired tokens + $refresh_tokens = array_filter( $refresh_tokens, function( $token ) { + return $token['expires'] > time(); + }); + + // Add new token + $refresh_tokens[ $jti ] = array( + 'expires' => $expires, + 'created' => time(), + ); + + update_user_meta( $user_id, '_woocommerce_pos_refresh_tokens', $refresh_tokens ); } /** - * Refresh JWT Token. + * Check if refresh token is still valid (not revoked). + * + * @param int $user_id + * @param string $jti + * + * @return bool */ - public function refresh_token(): void { - // Implementation + private function is_refresh_token_valid( int $user_id, string $jti ): bool { + $refresh_tokens = get_user_meta( $user_id, '_woocommerce_pos_refresh_tokens', true ); + if ( ! \is_array( $refresh_tokens ) ) { + return false; + } + + return isset( $refresh_tokens[ $jti ] ) && $refresh_tokens[ $jti ]['expires'] > time(); } } diff --git a/includes/Services/Cashier.php b/includes/Services/Cashier.php new file mode 100644 index 0000000..6ec9c9c --- /dev/null +++ b/includes/Services/Cashier.php @@ -0,0 +1,216 @@ +ID, $meta_key, true ); + if ( ! $uuid ) { + $uuid = wp_generate_uuid4(); + update_user_meta( $user->ID, $meta_key, $uuid ); + } + + return $uuid; + } + + /** + * Get cashier data for API responses. + * + * @param WP_User $user User object. + * @param bool $include_stores Whether to include stores data. + * + * @return array Cashier data. + */ + public function get_cashier_data( WP_User $user, bool $include_stores = true ): array { + $uuid = $this->get_cashier_uuid( $user ); + $last_access = get_user_meta( $user->ID, '_woocommerce_pos_last_access', true ); + + $data = array( + 'uuid' => $uuid, + 'id' => $user->ID, + 'username' => $user->user_login, + 'first_name' => $user->first_name, + 'last_name' => $user->last_name, + 'email' => $user->user_email, + 'display_name' => $user->display_name, + 'nice_name' => $user->user_nicename, + 'last_access' => $last_access ? $last_access : '', + 'avatar_url' => get_avatar_url( $user->ID ), + ); + + if ( $include_stores ) { + $stores = $this->get_accessible_stores( $user ); + $stores_data = array(); + foreach ( $stores as $store ) { + $stores_data[] = $store->get_data(); + } + $data['stores'] = $stores_data; + } + + /* + * Filter cashier data. + * + * @param array $data Cashier data. + * @param WP_User $user User object. + * @param bool $include_stores Whether stores were included. + */ + return apply_filters( 'woocommerce_pos_cashier_data', $data, $user, $include_stores ); + } + + /** + * Get stores accessible by the cashier. + * + * @TODO - This currently returns all stores. In the future, this should be + * customized based on user meta, roles, or other authorization logic to + * return only the stores the cashier is authorized to access. + * + * @param WP_User $user User object. + * + * @return array Array of Store objects. + */ + public function get_accessible_stores( WP_User $user ): array { + $stores = wcpos_get_stores(); + + /* + * Filter stores accessible by cashier. + * + * @param array $stores Array of Store objects. + * @param WP_User $user User object. + */ + return apply_filters( 'woocommerce_pos_cashier_accessible_stores', $stores, $user ); + } + + /** + * Check if a cashier has access to a specific store. + * + * @param WP_User $user User object. + * @param int $store_id Store ID. + * + * @return bool True if cashier has access, false otherwise. + */ + public function has_store_access( WP_User $user, int $store_id ): bool { + $accessible_stores = $this->get_accessible_stores( $user ); + + foreach ( $accessible_stores as $store ) { + if ( $store->get_id() === $store_id ) { + return true; + } + } + + return false; + } + + /** + * Get a specific store for a cashier if they have access. + * + * @param WP_User $user User object. + * @param int $store_id Store ID. + * + * @return null|Store Store object if accessible, null otherwise. + */ + public function get_accessible_store( WP_User $user, int $store_id ): ?Store { + $accessible_stores = $this->get_accessible_stores( $user ); + + foreach ( $accessible_stores as $store ) { + if ( $store->get_id() === $store_id ) { + return $store; + } + } + + return null; + } + + /** + * Update cashier's last access time. + * + * @param WP_User $user User object. + * @param string $timestamp Optional timestamp, defaults to current time. + * + * @return bool True on success, false on failure. + */ + public function update_last_access( WP_User $user, string $timestamp = '' ): bool { + if ( empty( $timestamp ) ) { + $timestamp = current_time( 'mysql' ); + } + + return update_user_meta( $user->ID, '_woocommerce_pos_last_access', $timestamp ); + } + + /** + * Check if user has cashier permissions. + * + * @param WP_User $user User object. + * + * @return bool True if user has cashier permissions. + */ + public function has_cashier_permissions( WP_User $user ): bool { + return user_can( $user, 'publish_shop_orders' ); + } + + /** + * Validate cashier access for API endpoints. + * + * @param int $current_user_id Current user ID. + * @param int $requested_id Requested cashier ID. + * + * @return bool True if access is allowed. + */ + public function validate_cashier_access( int $current_user_id, int $requested_id ): bool { + // Users can access their own data + if ( $current_user_id === $requested_id ) { + return true; + } + + // Administrators can access any cashier data + return current_user_can( 'manage_woocommerce' ); + } +} diff --git a/includes/Templates.php b/includes/Templates.php index 2150e8d..6c2bbb7 100644 --- a/includes/Templates.php +++ b/includes/Templates.php @@ -9,9 +9,6 @@ use WC_Abstract_Order; -/** - * - */ class Templates { /** * @var string POS frontend slug @@ -23,8 +20,14 @@ class Templates { */ private $pos_login_regex; + /** + * @var string POS auth slug + */ + private $pos_auth_regex; + /** * @var string POS checkout slug + * * @note 'wcpos-checkout' slug is used instead 'checkout' to avoid conflicts with WC checkout * eg: x-frame-options: SAMEORIGIN */ @@ -34,6 +37,7 @@ class Templates { public function __construct() { $this->pos_regex = '^' . Admin\Permalink::get_slug() . '(/(.*))?/?$'; $this->pos_login_regex = '^wcpos-login/?'; + $this->pos_auth_regex = '^wcpos-auth/?'; $this->pos_checkout_regex = '^wcpos-checkout/([a-z-]+)/([0-9]+)[/]?$'; $this->add_rewrite_rules(); @@ -41,26 +45,10 @@ public function __construct() { add_filter( 'option_rewrite_rules', array( $this, 'rewrite_rules' ), 1 ); add_action( 'template_redirect', array( $this, 'template_redirect' ), 1 ); - /** - * Priority 999 to ensure this filter runs after any other plugins that may hijack the order received url - */ + // Priority 999 to ensure this filter runs after any other plugins that may hijack the order received url add_filter( 'woocommerce_get_checkout_order_received_url', array( $this, 'order_received_url' ), 999, 2 ); } - /** - * @NOTE: 'order-pay' and 'order-received' rewrite tags are added by WC - * - * @return void - */ - private function add_rewrite_rules() { - add_rewrite_tag( '%wcpos%', '([^&]+)' ); - add_rewrite_tag( '%wcpos-receipt%', '([^&]+)' ); - add_rewrite_tag( '%wcpos-login%', '([^&]+)' ); - add_rewrite_rule( $this->pos_regex, 'index.php?wcpos=1', 'top' ); - add_rewrite_rule( $this->pos_login_regex, 'index.php?wcpos-login=1', 'top' ); - add_rewrite_rule( $this->pos_checkout_regex, 'index.php?$matches[1]=$matches[2]&wcpos=1', 'top' ); - } - /** * Make sure cache contains POS rewrite rules. * @@ -69,7 +57,7 @@ private function add_rewrite_rules() { * @return array|bool */ public function rewrite_rules( $rules ) { - return isset( $rules[ $this->pos_regex ], $rules[ $this->pos_login_regex ], $rules[ $this->pos_checkout_regex ] ) ? $rules : false; + return isset( $rules[ $this->pos_regex ], $rules[ $this->pos_login_regex ], $rules[ $this->pos_auth_regex ], $rules[ $this->pos_checkout_regex ] ) ? $rules : false; } /** @@ -79,18 +67,19 @@ public function template_redirect(): void { global $wp; $rewrite_rules_to_templates = array( - $this->pos_regex => __NAMESPACE__ . '\\Templates\\Frontend', - $this->pos_login_regex => __NAMESPACE__ . '\\Templates\\Login', + $this->pos_regex => __NAMESPACE__ . '\\Templates\\Frontend', + $this->pos_login_regex => __NAMESPACE__ . '\\Templates\\Login', + $this->pos_auth_regex => __NAMESPACE__ . '\\Templates\\Auth', $this->pos_checkout_regex => array( - 'order-pay' => __NAMESPACE__ . '\\Templates\\Payment', + 'order-pay' => __NAMESPACE__ . '\\Templates\\Payment', 'order-received' => __NAMESPACE__ . '\\Templates\\Received', - 'wcpos-receipt' => __NAMESPACE__ . '\\Templates\\Receipt', + 'wcpos-receipt' => __NAMESPACE__ . '\\Templates\\Receipt', ), ); foreach ( $rewrite_rules_to_templates as $rule => $classname ) { if ( $wp->matched_rule === $rule ) { - if ( is_array( $classname ) ) { + if ( \is_array( $classname ) ) { $this->load_checkout_template( $classname ); } else { $this->load_template( $classname ); @@ -100,8 +89,52 @@ public function template_redirect(): void { } } + /** - * Loads order templates, additionally checks query var is a valid order id + * Just like the checkout/payment.php template, we hijack the order received url so we can display a stripped down + * version of the receipt. + * + * @param string $order_received_url + * @param WC_Abstract_Order $order + * + * @return string + */ + public function order_received_url( string $order_received_url, WC_Abstract_Order $order ): string { + global $wp; + + // check is pos + if ( ! woocommerce_pos_request() || ! isset( $wp->query_vars['order-pay'] ) ) { + return $order_received_url; + } + + $redirect = add_query_arg( + array( + 'key' => $order->get_order_key(), + ), + get_home_url( null, '/wcpos-checkout/order-received/' . $order->get_id() ) + ); + + return $redirect; + } + + /** + * @NOTE: 'order-pay' and 'order-received' rewrite tags are added by WC + * + * @return void + */ + private function add_rewrite_rules(): void { + add_rewrite_tag( '%wcpos%', '([^&]+)' ); + add_rewrite_tag( '%wcpos-receipt%', '([^&]+)' ); + add_rewrite_tag( '%wcpos-login%', '([^&]+)' ); + add_rewrite_tag( '%wcpos-auth%', '([^&]+)' ); + add_rewrite_rule( $this->pos_regex, 'index.php?wcpos=1', 'top' ); + add_rewrite_rule( $this->pos_login_regex, 'index.php?wcpos-login=1', 'top' ); + add_rewrite_rule( $this->pos_auth_regex, 'index.php?wcpos-auth=1', 'top' ); + add_rewrite_rule( $this->pos_checkout_regex, 'index.php?$matches[1]=$matches[2]&wcpos=1', 'top' ); + } + + /** + * Loads order templates, additionally checks query var is a valid order id. * * @param array $classnames * @@ -117,6 +150,7 @@ private function load_checkout_template( array $classnames ): void { if ( class_exists( $classname ) && $order_id ) { $template = new $classname( $order_id ); $template->get_template(); + return; } } @@ -126,7 +160,7 @@ private function load_checkout_template( array $classnames ): void { } /** - * Loads all other templates + * Loads all other templates. * * @param string $classname * @@ -136,37 +170,10 @@ private function load_template( string $classname ): void { if ( class_exists( $classname ) ) { $template = new $classname(); $template->get_template(); + return; } wp_die( esc_html__( 'Template not found.', 'woocommerce-pos' ) ); } - - - /** - * Just like the checkout/payment.php template, we hijack the order received url so we can display a stripped down - * version of the receipt. - * - * @param string $order_received_url - * @param WC_Abstract_Order $order - * - * @return string - */ - public function order_received_url( string $order_received_url, WC_Abstract_Order $order ): string { - global $wp; - - // check is pos - if ( ! woocommerce_pos_request() || ! isset( $wp->query_vars['order-pay'] ) ) { - return $order_received_url; - } - - $redirect = add_query_arg( - array( - 'key' => $order->get_order_key(), - ), - get_home_url( null, '/wcpos-checkout/order-received/' . $order->get_id() ) - ); - - return $redirect; - } } diff --git a/includes/Templates/Auth.php b/includes/Templates/Auth.php new file mode 100644 index 0000000..ea13210 --- /dev/null +++ b/includes/Templates/Auth.php @@ -0,0 +1,176 @@ +redirect_uri = esc_url( $_REQUEST['redirect_uri'] ?? '', array( 'https', 'http', 'wcpos' ) ); + $this->state = sanitize_text_field( $_REQUEST['state'] ?? '' ); + $this->error = ''; + + // Validate required parameters + if ( empty( $this->redirect_uri ) ) { + $this->error = 'Missing or invalid redirect_uri parameter.'; + + return; + } + + if ( empty( $this->state ) ) { + $this->error = 'Missing state parameter.'; + + return; + } + + // Handle form submission + $this->handle_form_submission(); + } + + /** + * Get the redirect URI. + * + * @return string + */ + public function get_redirect_uri(): string { + return $this->redirect_uri; + } + + /** + * Get the state parameter. + * + * @return string + */ + public function get_state(): string { + return $this->state; + } + + /** + * Get the error message. + * + * @return string + */ + public function get_error(): string { + return $this->error; + } + + /** + * @return void + */ + public function get_template(): void { + do_action( 'login_init' ); + do_action( 'woocommerce_pos_auth_template_redirect' ); + + // Make this instance available to the template + global $wcpos_auth_instance; + $wcpos_auth_instance = $this; + + include woocommerce_pos_locate_template( 'auth.php' ); + exit; + } + + /** + * Handle form submission. + * + * @return void + */ + private function handle_form_submission(): void { + if ( 'POST' !== $_SERVER['REQUEST_METHOD'] ) { + return; + } + + // Verify nonce for security + if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'wcpos_auth' ) ) { + $this->error = 'Security check failed. Please try again.'; + + return; + } + + $username = sanitize_user( $_POST['wcpos-log'] ?? '' ); + $password = $_POST['wcpos-pwd'] ?? ''; + + // Authenticate user using WordPress + $user = wp_authenticate( $username, $password ); + + if ( is_wp_error( $user ) ) { + $this->error = $user->get_error_message(); + + return; + } + + // Check if user has access to POS + if ( ! user_can( $user, 'access_woocommerce_pos' ) ) { + $this->error = 'You do not have permission to access the POS.'; + + return; + } + + // Generate JWT token using Services/Auth + $auth_service = AuthService::instance(); + $redirect_data = $auth_service->get_redirect_data( $user ); + + if ( empty( $redirect_data ) ) { + $this->error = 'Failed to generate authentication tokens.'; + + return; + } + + // On success, redirect back to app (or fallback to dashboard) + $redirect_params = array( + 'access_token' => rawurlencode( $redirect_data['access_token'] ), + 'refresh_token' => rawurlencode( $redirect_data['refresh_token'] ), + 'token_type' => rawurlencode( $redirect_data['token_type'] ), + 'expires_at' => \intval( $redirect_data['expires_at'] ), + 'id' => \intval( $redirect_data['id'] ), + 'uuid' => rawurlencode( $redirect_data['uuid'] ), + 'display_name' => rawurlencode( $redirect_data['display_name'] ), + ); + + // Include state parameter if it was provided + if ( ! empty( $this->state ) ) { + $redirect_params['state'] = rawurlencode( $this->state ); + } + + $target = $this->redirect_uri + ? add_query_arg( $redirect_params, $this->redirect_uri ) + : admin_url(); + + wp_redirect( $target ); + exit; + } +} diff --git a/includes/Templates/Frontend.php b/includes/Templates/Frontend.php index d391897..7da1010 100644 --- a/includes/Templates/Frontend.php +++ b/includes/Templates/Frontend.php @@ -3,22 +3,21 @@ * @author Paul Kilmurray * * @see http://wcpos.com - * @package WooCommerce POS */ namespace WCPOS\WooCommercePOS\Templates; use Ramsey\Uuid\Uuid; +use WCPOS\WooCommercePOS\Admin\Permalink; +use const WCPOS\WooCommercePOS\PLUGIN_URL; use WCPOS\WooCommercePOS\Services\Auth; use const WCPOS\WooCommercePOS\SHORT_NAME; use const WCPOS\WooCommercePOS\VERSION; -use const WCPOS\WooCommercePOS\PLUGIN_URL; /** * Frontend class. */ class Frontend { - /** * @return void */ @@ -47,7 +46,7 @@ public function get_template(): void { // last chance before frontend template is rendered do_action( 'woocommerce_pos_frontend_template_redirect' ); - /** + /* * Deprecated action. * * @TODO remove in 1.5.0 @@ -85,11 +84,14 @@ public function head(): void { * Output the footer scripts. */ public function footer(): void { - $development = isset( $_ENV['DEVELOPMENT'] ) && $_ENV['DEVELOPMENT']; - $user = wp_get_current_user(); - $github_url = 'https://cdn.jsdelivr.net/gh/wcpos/web-bundle@1.7/'; - $auth_service = Auth::instance(); - $stores = array_map( + $development = isset( $_ENV['DEVELOPMENT'] ) && $_ENV['DEVELOPMENT']; + $user = wp_get_current_user(); + $cdn_base_url = $development ? 'http://localhost:4567/build/' : 'https://cdn.jsdelivr.net/gh/wcpos/web-bundle@1.8/build/'; + $wcpos_permalink_slug = Permalink::get_slug(); + $wcpos_permalink_slug = empty( $wcpos_permalink_slug ) ? 'pos' : $wcpos_permalink_slug; + $wcpos_permalink_slug = '/' . ltrim( $wcpos_permalink_slug, '/' ); + $auth_service = Auth::instance(); + $stores = array_map( function ( $store ) { return $store->get_data(); }, @@ -110,7 +112,7 @@ function ( $store ) { $vars = array( 'version' => VERSION, - 'manifest' => $github_url . 'metadata.json', + 'manifest' => $cdn_base_url . 'metadata.json', 'homepage' => woocommerce_pos_url(), 'logout_url' => $this->pos_logout_url(), 'site' => array( @@ -147,16 +149,13 @@ function ( $store ) { */ $vars = apply_filters( 'woocommerce_pos_inline_vars', $vars ); $initial_props = wp_json_encode( $vars ); - $dev_bundle = site_url( '/entry.bundle?platform=web&dev=true&hot=false&transform.routerRoot=app' ); /** - * Add path to worker scripts + * Add path to worker scripts. */ $idbWorker = PLUGIN_URL . 'assets/js/indexeddb.worker.js'; - /** - * getScript helper and initialProps - */ + // getScript helper and initialProps echo "" . "\n"; - - /** - * The actual app bundle - */ - if ( $development ) { - // Development Mode - echo "" . "\n"; - } else { - // Production Mode - echo "" . "\n"; - } + + echo "" . "\n"; } - /** - * - */ private function pos_logout_url() { /** - * Get the login URL, allow other plugins to customise the URL. eg: WPS Hide Login + * Get the login URL, allow other plugins to customise the URL. eg: WPS Hide Login. */ $login_url = apply_filters( 'login_url', site_url( '/wp-login.php' ), 'logout', false ); - $redirect_to = urlencode( woocommerce_pos_url() ); - $reauth = 1; - $wcpos = 1; + $redirect_to = urlencode( woocommerce_pos_url() ); + $reauth = 1; + $wcpos = 1; $logout_nonce = wp_create_nonce( 'log-out' ); - $logout_url = "{$login_url}?action=logout&_wpnonce={$logout_nonce}&redirect_to={$redirect_to}&reauth={$reauth}&wcpos={$wcpos}"; - - return $logout_url; + return "{$login_url}?action=logout&_wpnonce={$logout_nonce}&redirect_to={$redirect_to}&reauth={$reauth}&wcpos={$wcpos}"; } diff --git a/package.json b/package.json index 9b3c2bd..d126114 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@wcpos/woocommerce-pos", - "version": "1.7.13", + "version": "1.8.0", "description": "A simple front-end for taking WooCommerce orders at the Point of Sale.", "main": "index.js", "workspaces": { diff --git a/readme.txt b/readme.txt index ec99011..6b35cd1 100644 --- a/readme.txt +++ b/readme.txt @@ -88,6 +88,10 @@ There is more information on our website at [https://wcpos.com](https://wcpos.co == Changelog == + +======= += 1.8.0 - 2025/08/XX = + = 1.7.13 - 2025/08/06 = * Fix: New Order emails to send after order calculations @@ -148,205 +152,4 @@ This small update adds code to prevent WooCommerce POS from being activated if w * Fix: Quick discounts calculation bug affecting some users * Pro: New Reports page for End of Day Reporting (Z-Report) -= 1.6.6 - 2024/09/05 = -* Fix: POS Only Products appearing in the POS ๐Ÿ˜“ - -= 1.6.5 - 2024/09/04 = -* Fix: POS Only Products appearing in the web store - -= 1.6.4 - 2024/09/04 = -* Fix: POS Only Products appearing in the web store -* Fix: Disable wp_footer for POS Order Pay template - -= 1.6.3 - 2024/06/29 = -- Fix: Critical error preventing bulk update of products - -= 1.6.2 - 2024/06/20 = -- Fix: Error preventing resources (products, orders, customers, etc) from loading on Windows servers - -= 1.6.1 - 2024/06/18 = -- Enhancement: Changed the way POS Only and Online Only products are managed - - Moved from using postmeta (`_pos_visibility`) to using centralized settings in the options table (`woocommerce_pos_settings_visibility`) -- Improved: Payment template settings, you can now disable all `wp_head` or `wp_footer` scripts -- Fix: 'Invalid response checking updates for products/tags' error - -= 1.6.0 - 2024/06/12 = -* Improved: Performance for large stores -* Added: Log screen for insights into the POS performance and events -* Added: Cart setting to enable/disable show receipt after checkout -* Added: Cart setting to enable/disable auto-print receipt after checkout -* Fix: Prevent order create duplication from the POS -* Fix: Cart subtotal showing tax when tax display is not enabled - -= 1.5.1 - 2024/06/03 = -* Fix: "Sorry, you cannot list resources." error for cashier role - -= 1.5.0 - 2024/06/03 = -* Fix: the POS will now correctly sync stock quantity after each sale -* Fix: cart tax logic has been improved to fix rounding issues -* Fix: shipping tax will now use the correct class -* Fix: re-opening orders now functions correctly -* Fix: quick discount calculation -* Fix: orders created with different currencies will show the correct currency symbol -* Added: split option to allow multiple cart lines for the same product -* Added: stock filter for products -* Added: Fees can now be a fix percentage of the cart total -* Added: order status can now be changed from the Orders page -* Added: extra display information for Orders, such as 'created_via' and 'cashier' -* Added: cart will only show open orders associated with the logged in cashier -* Pro: cart will only show open orders from the current POS Store -* Pro: current store will stay selected after refresh or application re-open -* ... numerous other bug fixes and performance improvements - -= 1.4.16 - 2024/03/28 = -* Fix: nonce check failing for Guest orders when checking out with the desktop application - -= 1.4.15 - 2024/03/20 = -* Fix: another potential error introduced to Pro updater in previous version ๐Ÿคฆโ€โ™‚๏ธ - -= 1.4.13 - 2024/03/19 = -* Fix: potential error introduced to Pro updater in previous version - -= 1.4.12 - 2024/03/18 = -* Security: Fix Insufficient Verification of Data Authenticity to Authenticated (Customer+) Information Disclosure (reported by Lucio Sรก) -* Fix: Pro plugin not showing updates for some users - -= 1.4.11 - 2024/03/09 = -* Fix: regression in tax calculation when POS settings are different to Online settings -* Fix: regression in product variation images, use 'medium' sized product image instead of full size -* Fix: remove POS Only products from frontend WC REST API response -* Fix: generic get meta method should not be used for '_create_via' -* Fix: add enabled POS gateways to the Order Edit select input -* Fix: other minor PHP warnings - -= 1.4.10 - 2024/01/23 = -* Fix: compatibility issue with WooCommerce < 6.7.0 - -= 1.4.9 - 2024/01/21 = -= 1.4.8 - 2024/01/21 = -* Fix: duplicating Products in WC Admin also duplicated POS UUID, which caused problems - -= 1.4.7 - 2024/01/18 = -* Bump: web application to version 1.4.1 -* Fix: scroll-to-top issue when scrolling data tables -* Fix: variations not loading after first 10 - -= 1.4.6 - 2024/01/16 = -* Fix: decimal quantity for orders -* Fix: load translation files - -= 1.4.5 - 2024/01/14 = -* Add: show change in checkout modal and receipt for the Cash gateway -* Add: use 'medium' sized product image instead of 'thumbnail' -* Fix: compatibility with alternative login urls, eg: WPS Hide Login - -= 1.4.4 - 2024/01/12 = -* Desktop App: fix login for Desktop Application v1.4.0 - -= 1.4.3 - 2024/01/12 = -* Pro: fix update notification for Pro plugin - -= 1.4.2 - 2024/01/12 = -* Urgent Fix: users with role 'cashier' unable to access tax rates API - -= 1.4.1 - 2024/01/12 = -* No change, just messed up the release to WordPress.org - -= 1.4.0 - 2024/01/11 = -* Added: Support for High-Performance Order Storage (HPOS) -* Added: New API for Variation Barcode Search -* Pro: Set unique Product Price and Tax Status for each Store -* Pro: Fix Store login for Authorized Users -* Pro: Fix Analytics for POS vs Online Orders -* Pro: New updater -* Plus: Added a lot of PHPUnit Tests, fixed numerous bugs for a more stable POS! - -= 1.3.12 - 2023/09/29 = -* Fix: WordPress database error Not unique table/alias: 'wp_postmeta' for query - -= 1.3.11 - 2023/09/27 = -* Urgent Fix: product and user search not returning results for some users - -= 1.3.10 - 2023/08/29 = -* Urgent Fix: pages with slug starting with 'pos' redirecting to the POS page - -= 1.3.9 - 2023/08/18 = -* Fix: limit query length for WC REST API, this was resulting in 0 products being returned for some users -* Fix: pos meta data showing in WP Admin order quick view -* Fix: cashier uuid not unique for multisite installs - -= 1.3.8 - 2023/08/08 = -* Fix: login modal for desktop application - -= 1.3.7 - 2023/07/31 = -* Fix: rest_pre_serve_request critical error reported by some users -* Fix: change 'woocommerce_available_payment_gateways' filter priority to 99 -* Add: setting for servers that don't allow Authorization header - -= 1.3.4 and 1.3.5 - 2023/07/29 = -* Urgent Fix: product descriptions being truncated to 100 characters - -= 1.3.3 - 2023/07/28 = -* Fix: "Nonce value cannot be verified" when dequeue-ing scripts and styles in POS checkout modal - -= 1.3.2 - 2023/07/27 = -* Urgent Fix for variations not downloading for some users - -= 1.3.1 - 2023/07/27 = -* Urgent Fix for login modal problems - -= 1.3.0 - 2023/07/27 = -* Major stability improvements! -* Fix: Login is now via JWT - no more 'Cookie nonce' errors -* Fix: Many fixes to local data sync and search should make the POS much more enjoyable to use -* Add: Dequeue WordPress styles and scripts on POS checkout page -* Fix: various fixes to the POS settings in WP Admin - -= 1.2.4 - 2023/07/25 = -* Fix: empty products effecting some users due to malformed meta_data - -= 1.2.3 - 2023/06/21 = -* Fix: coupon form on POS payment modal -* Fix: use woocommerce_gallery_thumbnail instead of full sized images for products and variations - -= 1.2.2 - 2023/06/21 = -* Add: basic support for coupons until a more complete solution is implemented -* Fix: customer select in settings -* Pro: add Analytics for POS vs Online sales - -= 1.2.1 - 2023/06/12 = -* Fix: Critical error introduced with PHP 7.2 compatibility - -= 1.2.0 - 2023/06/12 = -* Refactor: data replication to improve performance -* Refactor: product search, search by sku and barcode for products now works - - Note: barcode search for variations is still not working, this will be addressed in a future release -* Bug Fix: 'Cannot use object of type Closure as array' in the API.php file -* Bug Fix: Creating orders with decimal quantity -* Bug Fix: Update product with decimal quantity -* Fix: remove private meta data from Order Preview modal -* Fix: turn off PHP version check by composer, note that PHP 7.2+ is still required - -= 1.1.0 - 2023/05/19 = -* Fix: disable Lite Speed Cache for POS page -* Fix: add id audit for product categories and tags -* Fix: add min/max price to variable meta data - -= 1.0.2 - 2023/05/05 = -* No change, just messed up the release to WordPress.org. - -= 1.0.1 - 2023/05/05 = -* Fix: Product and Variations not showing in POS - Description: The WC REST API response can be too large in some cases causing a 502 server error. - - Product and Variation descriptions are not truncated to 100 characters for the POS to reduce response size. - - Yoast SEO is now programmatically disabled for the POS to reduce response size. - - A message is now logged when the WC REST API product or variation response is too large. - -= 1.0.0 - 2023/05/03 = -* Complete rewrite of the plugin with improved functionality and performance. -* Although extensive testing has been done, there may still be bugs. -* We recommend updating only when you have time to deal with potential issues. -* You can always revert to the old version if necessary, https://wordpress.org/plugins/woocommerce-pos/advanced/ -* If you need assistance, join our Discord chat at https://wcpos.com/discord for support. - == Upgrade Notice == diff --git a/templates/auth.php b/templates/auth.php new file mode 100644 index 0000000..42c06cb --- /dev/null +++ b/templates/auth.php @@ -0,0 +1,96 @@ +get_redirect_uri(); +$error = $wcpos_auth_instance->get_error(); +?> +> + + <?php _e('WooCommerce POS Login'); ?> + + + ?ver=' media='all' /> + ?ver=' media='all' /> + ?ver=' media='all' /> + ?ver=' media='all' /> + ?ver=' media='all' /> + + + +
+
+ + WooCommerce POS + +
+ +
+ +
array(), 'em' => array(), 'a' => array( 'href' => array() ) ) ); ?>
+ + +

+ + +

+ +
+ +
+ + +
+
+ + + +

+ +

+
+ +
+ +
+ + + + \ No newline at end of file diff --git a/woocommerce-pos.php b/woocommerce-pos.php index 53fe90b..67e71b2 100644 --- a/woocommerce-pos.php +++ b/woocommerce-pos.php @@ -3,7 +3,7 @@ * Plugin Name: WooCommerce POS * Plugin URI: https://wordpress.org/plugins/woocommerce-pos/ * Description: A simple front-end for taking WooCommerce orders at the Point of Sale. Requires WooCommerce. - * Version: 1.7.13 + * Version: 1.8.0 * Author: kilbot * Author URI: http://wcpos.com * Text Domain: woocommerce-pos @@ -23,7 +23,7 @@ namespace WCPOS\WooCommercePOS; // Define plugin constants. -const VERSION = '1.7.13'; +const VERSION = '1.8.0'; const PLUGIN_NAME = 'woocommerce-pos'; const SHORT_NAME = 'wcpos'; \define( __NAMESPACE__ . '\PLUGIN_FILE', plugin_basename( __FILE__ ) ); // 'woocommerce-pos/woocommerce-pos.php'