gitlab-acebase
Version:
AceBase realtime database server (webserver endpoint to allow remote connections)
2 lines • 257 kB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.acebaseclient=f()}})((function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,(function(r){var n=e[i][1][r];return o(n||r)}),p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r}()({1:[function(require,module,exports){(function(process){(function(){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.AceBaseClient=exports.ConnectionSettings=void 0;const acebase_core_1=require("acebase-core");const api_web_1=require("./api-web");const auth_1=require("./auth");const server_date_1=require("./server-date");class ConnectionSettings{constructor(settings){var _a,_b,_c,_d,_e,_f,_g,_h,_j,_k;this.https=true;this.rootPath="";this.autoConnect=true;this.autoConnectDelay=0;this.logLevel="log";this.dbname=settings.dbname;this.host=settings.host;this.port=settings.port;this.https=typeof settings.https==="boolean"?settings.https:true;if(typeof settings.rootPath==="string"){this.rootPath=settings.rootPath.replace(/^\/|\/$/g,"")}this.autoConnect=typeof settings.autoConnect==="boolean"?settings.autoConnect:true;this.autoConnectDelay=typeof settings.autoConnectDelay==="number"?settings.autoConnectDelay:0;this.logLevel=typeof settings.logLevel==="string"?settings.logLevel:"log";this.sponsor=typeof settings.sponsor==="boolean"?settings.sponsor:false;this.cache={enabled:typeof((_a=settings.cache)===null||_a===void 0?void 0:_a.db)!=="object"?false:typeof((_b=settings.cache)===null||_b===void 0?void 0:_b.enabled)==="boolean"?settings.cache.enabled:true,db:typeof((_c=settings.cache)===null||_c===void 0?void 0:_c.db)==="object"?settings.cache.db:null,priority:typeof((_d=settings.cache)===null||_d===void 0?void 0:_d.priority)==="string"&&["server","cache"].includes(settings.cache.priority)?settings.cache.priority:"server"};this.sync={timing:typeof((_e=settings.sync)===null||_e===void 0?void 0:_e.timing)==="string"&&["connect","signin","auto","manual"].includes(settings.sync.timing)?settings.sync.timing:"auto",useCursor:typeof((_f=settings.sync)===null||_f===void 0?void 0:_f.useCursor)==="boolean"?settings.sync.useCursor:true};const realtime=typeof((_g=settings.network)===null||_g===void 0?void 0:_g.realtime)==="boolean"?settings.network.realtime:true;this.network={transports:((_h=settings.network)===null||_h===void 0?void 0:_h.transports)instanceof Array?settings.network.transports:["websocket"],realtime:realtime,monitor:typeof((_j=settings.network)===null||_j===void 0?void 0:_j.monitor)==="boolean"?settings.network.monitor:!realtime,interval:typeof((_k=settings.network)===null||_k===void 0?void 0:_k.interval)==="number"?settings.network.interval:60}}}exports.ConnectionSettings=ConnectionSettings;class AceBaseClient extends acebase_core_1.AceBaseBase{constructor(init){var _a;if(typeof init!=="object"){const[host,port,dbname,https]=arguments;init={host:host,port:port,dbname:dbname,https:https}}const settings=new ConnectionSettings(init);super(settings.dbname,{info:"realtime database client",sponsor:settings.sponsor});const cacheDb=(_a=settings.cache)===null||_a===void 0?void 0:_a.db;const cacheReadyPromise=cacheDb?cacheDb.ready():Promise.resolve();let ready=false;this.on("ready",(()=>{ready=true}));this.debug=new acebase_core_1.DebugLogger(settings.logLevel,`[${settings.dbname}]`.colorize(acebase_core_1.ColorStyle.blue));const synchronizeClocks=async()=>{const info=await this.api.getServerInfo();const now=Date.now(),bias=info.time-now;(0,server_date_1.setServerBias)(bias)};this.on("connect",(()=>{if(cacheDb&&"settings"in cacheDb){cacheDb.settings.ipcEvents=false}synchronizeClocks()}));this.on("disconnect",(()=>{if(cacheDb&&"settings"in cacheDb){cacheDb.settings.ipcEvents=true}}));this.sync=async()=>{const result=await syncPendingChanges(true);return result};let syncRunning=false,firstSync=true;const syncPendingChanges=async(throwErrors=false)=>{if(syncRunning){if(throwErrors){throw new Error("sync already running")}return}if(!this.api.isConnected){if(throwErrors){throw new Error("not connected")}return}syncRunning=true;try{await cacheReadyPromise;return await this.api.sync({firstSync:firstSync,fetchFreshData:!firstSync,eventCallback:(eventName,args)=>{this.debug.log(eventName,args||"");this.emit(eventName,args)}})}catch(err){if(throwErrors){throw err}else{console.error(`Failed to synchronize:`,err)}}finally{syncRunning=false;firstSync=false}};let syncTimeout;this.on("connect",(()=>{if(settings.sync.timing==="connect"||settings.sync.timing==="signin"&&this.auth.accessToken){syncPendingChanges()}else if(settings.sync.timing==="auto"){syncTimeout&&clearTimeout(syncTimeout);syncTimeout=setTimeout(syncPendingChanges,2500)}}));this.on("signin",(()=>{if(settings.sync.timing==="auto"){syncTimeout&&clearTimeout(syncTimeout)}if(["auto","signin"].includes(settings.sync.timing)){syncPendingChanges()}}));const emitClientReady=async()=>{if(cacheDb){await cacheDb.ready()}this.emit("ready")};if(typeof process==="object"&&typeof(process===null||process===void 0?void 0:process.on)==="function"){process.on("SIGINT",(()=>{if(this.connected){this.debug.log("Received SIGINT, closing connection");this.disconnect()}}))}this.api=new api_web_1.WebApi(settings.dbname,{network:settings.network,sync:settings.sync,logLevel:settings.logLevel,autoConnect:settings.autoConnect,autoConnectDelay:settings.autoConnectDelay,cache:settings.cache,debug:this.debug,url:`http${settings.https?"s":""}://${settings.host}:${settings.port}`,rootPath:settings.rootPath},((evt,data)=>{if(evt==="connect"){this.emit("connect");if(!ready){emitClientReady()}}else if(evt==="connect_error"){this.emit("connect_error",data);if(!ready&&cacheDb){emitClientReady()}}else if(evt==="disconnect"){this.emit("disconnect")}}));this.auth=new auth_1.AceBaseClientAuth(this,((event,arg)=>{this.emit(event,arg)}))}async sync(){throw new Error("Must be set by constructor")}get connected(){return this.api.isConnected}get connectionState(){return this.api.connectionState}connect(retry=true){return this.api.connect(retry)}disconnect(){this.api.disconnect()}close(){this.disconnect()}callExtension(method,path,data){return this.api.callExtension(method,path,data)}getCursor(){return this.api.getSyncCursor()}setCursor(cursor){this.api.setSyncCursor(cursor)}get cache(){const clear=async(path="")=>{await this.api.clearCache(path)};const update=(path="",cursor=null)=>this.api.updateCache(path,cursor);const get=async(path,cursor=null)=>{const options=Object.freeze(cursor?{cache_mode:"allow",cache_cursor:cursor}:{cache_mode:"force"});const snap=await this.ref(path).get(options);return snap};return{clear:clear,update:update,get:get}}}exports.AceBaseClient=AceBaseClient}).call(this)}).call(this,require("_process"))},{"./api-web":2,"./auth":3,"./server-date":12,_process:40,"acebase-core":25}],2:[function(require,module,exports){"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.WebApi=void 0;const acebase_core_1=require("acebase-core");const socket_io_client_1=require("socket.io-client");const Base64=require("./base64");const error_1=require("./request/error");const errors_1=require("./errors");const promise_timeout_1=require("./promise-timeout");const request_1=require("./request");const _websocketRequest=(socket,event,data,accessToken)=>{if(!socket){throw new Error(`Cannot send request because websocket connection is not open`)}const requestId=acebase_core_1.ID.generate();const request=Object.assign(Object.assign({},data),{req_id:requestId,access_token:accessToken});return new Promise(((resolve,reject)=>{if(!socket){return reject(new error_1.AceBaseRequestError(request,null,"websocket","No open websocket connection"))}let timeout;const send=(retry=0)=>{socket.emit(event,request);timeout=setTimeout((()=>{if(retry<2){return send(retry+1)}socket.off("result",handle);const err=new error_1.AceBaseRequestError(request,null,"timeout",`Server did not respond to "${event}" request after ${retry+1} tries`);reject(err)}),1e3)};const handle=response=>{if(response.req_id===requestId){clearTimeout(timeout);socket.off("result",handle);if(response.success){return resolve(response)}const code=typeof response.reason==="object"?response.reason.code:response.reason;const message=typeof response.reason==="object"?response.reason.message:`request failed: ${code}`;const err=new error_1.AceBaseRequestError(request,response,code,message);reject(err)}};socket.on("result",handle);send()}))};class EventSubscription{constructor(path,event,callback,settings){this.path=path;this.event=event;this.callback=callback;this.settings=settings;this.state="requested";this.added=Date.now();this.activated=0;this.lastEvent=0;this.lastSynced=0;this.cursor=null;this.cacheCallback=null}activate(){this.state="active";if(this.activated===0){this.activated=Date.now()}}cancel(reason){this.state="canceled";this.settings.cancelCallback(reason)}}const CONNECTION_STATE_DISCONNECTED="disconnected";const CONNECTION_STATE_CONNECTING="connecting";const CONNECTION_STATE_CONNECTED="connected";const CONNECTION_STATE_DISCONNECTING="disconnecting";const NOOP=()=>{};class WebApi extends acebase_core_1.Api{constructor(dbname="default",settings,callback){super();this.dbname=dbname;this.settings=settings;this._id=acebase_core_1.ID.generate();this.socket=null;this._serverVersion="unknown";this._cursor={current:null,sync:null};this._eventTimeline={init:Date.now(),connect:0,signIn:0,sync:0,disconnect:0};this._subscriptions={};this._realtimeQueries={};this.accessToken=null;this.manualConnectionMonitor=new acebase_core_1.SimpleEventEmitter;this._id=acebase_core_1.ID.generate();this._autoConnect=typeof settings.autoConnect==="boolean"?settings.autoConnect:true;this._autoConnectDelay=typeof settings.autoConnectDelay==="number"?settings.autoConnectDelay:0;this._connectionState=CONNECTION_STATE_DISCONNECTED;if(settings.cache.enabled!==false){this._cache={db:settings.cache.db,priority:settings.cache.priority}}if(settings.network.monitor){const interval=setInterval((()=>this.checkConnection()),settings.network.interval*1e3);interval.unref&&interval.unref()}this.debug=settings.debug;this.eventCallback=(event,...args)=>{if(event==="disconnect"){this._cursor.sync=this._cursor.current}callback&&callback(event,...args)};if(this._autoConnect){if(this._autoConnectDelay){setTimeout((()=>this.connect().catch(NOOP)),this._autoConnectDelay)}else{this.connect().catch(NOOP)}}}setSyncCursor(cursor){this._cursor.sync=cursor}getSyncCursor(){return this._cursor.sync}get host(){return this.settings.url}get url(){return`${this.settings.url}${this.settings.rootPath?`/${this.settings.rootPath}`:""}`}async _updateCursor(cursor){if(!cursor||this._cursor.current&&cursor<this._cursor.current){return}this._cursor.current=cursor}get hasCache(){return!!this._cache}get cache(){if(!this._cache){throw new Error("DEV ERROR: no cache db is used")}return this._cache}async checkConnection(){var _a,_b;if(((_a=this.settings.network)===null||_a===void 0?void 0:_a.realtime)&&!this.isConnected){return}if(!((_b=this.settings.network)===null||_b===void 0?void 0:_b.realtime)&&![CONNECTION_STATE_CONNECTING,CONNECTION_STATE_CONNECTED].includes(this._connectionState)){return}const wasConnected=this.isConnected;try{await this._request({url:this.serverPingUrl,ignoreConnectionState:true});if(!wasConnected){this.manualConnectionMonitor.emit("connect")}}catch(err){}}_handleDetectedDisconnect(err){var _a;if((_a=this.settings.network)===null||_a===void 0?void 0:_a.realtime){this._connectionState===CONNECTION_STATE_DISCONNECTED;this.connect().catch(NOOP)}else{if(this._connectionState===CONNECTION_STATE_CONNECTING){this.manualConnectionMonitor.emit("connect_error",err)}else if(this._connectionState===CONNECTION_STATE_CONNECTED){this.manualConnectionMonitor.emit("disconnect")}}}connect(retry=true){var _a;if(this.socket!==null&&typeof this.socket==="object"){this.disconnect()}this._connectionState=CONNECTION_STATE_CONNECTING;this.debug.log(`Connecting to AceBase server "${this.url}"`);if(!this.url.startsWith("https")){this.debug.warn(`WARNING: The server you are connecting to does not use https, any data transferred may be intercepted!`.colorize(acebase_core_1.ColorStyle.red))}const transports=((_a=this.settings.network)===null||_a===void 0?void 0:_a.transports)instanceof Array?this.settings.network.transports:["websocket"];this.debug.log(`Using ${transports.join(",")} transport${transports.length>1?"s":""} for socket.io`);return new Promise(((resolve,reject)=>{var _a;if(!((_a=this.settings.network)===null||_a===void 0?void 0:_a.realtime)){this.manualConnectionMonitor.off("connect");this.manualConnectionMonitor.off("connect_error");this.manualConnectionMonitor.off("disconnect");this.manualConnectionMonitor.on("connect",(()=>{this._connectionState=CONNECTION_STATE_CONNECTED;this._eventTimeline.connect=Date.now();this.manualConnectionMonitor.off("connect_error");this.eventCallback("connect");resolve()}));this.manualConnectionMonitor.on("connect_error",(err=>{this.debug.error(`API connection error: ${err.message||err}`);this.eventCallback("connect_error",err);reject(err)}));this.manualConnectionMonitor.on("disconnect",(()=>{if(this._connectionState===CONNECTION_STATE_DISCONNECTING){this._connectionState=CONNECTION_STATE_DISCONNECTED;this.manualConnectionMonitor.off("connect");this.manualConnectionMonitor.off("disconnect");this.manualConnectionMonitor.off("connect_error")}else{this._connectionState=CONNECTION_STATE_CONNECTING;this._eventTimeline.disconnect=Date.now()}this.eventCallback("disconnect")}));this._connectionState=CONNECTION_STATE_CONNECTING;return setTimeout((()=>this.checkConnection()),0)}const socket=this.socket=(0,socket_io_client_1.connect)(this.host,{path:`/${this.settings.rootPath?`${this.settings.rootPath}/`:""}socket.io`,autoConnect:true,reconnection:retry,reconnectionAttempts:retry?Infinity:0,reconnectionDelay:1e3,reconnectionDelayMax:5e3,timeout:2e4,randomizationFactor:.5,transports:transports});socket.on("connect_error",(err=>{this.debug.error(`Websocket connection error: ${err}`);this.eventCallback("connect_error",err);reject(err)}));socket.on("connect",(async()=>{this._connectionState=CONNECTION_STATE_CONNECTED;this._eventTimeline.connect=Date.now();if(this.accessToken){const isFirstSignIn=this._eventTimeline.signIn===0;try{await this.signInWithToken(this.accessToken,isFirstSignIn)}catch(err){this.debug.error(`Could not automatically sign in user with access token upon reconnect: ${err.code||err.message}`)}}const subscribeTo=async sub=>{const subs=this._subscriptions[sub.path].filter((s=>s.event===sub.event));try{const result=await _websocketRequest(this.socket,"subscribe",{path:sub.path,event:sub.event},this.accessToken);subs.forEach((s=>s.activate()))}catch(err){if(err.code==="access_denied"&&!this.accessToken){this.debug.error(`Could not subscribe to event "${sub.event}" on path "${sub.path}" because you are not signed in. If you added this event while offline and have a user access token, you can prevent this by using client.auth.setAccessToken(token) to automatically try signing in after connecting`)}else{this.debug.error(err)}subs.forEach((s=>s.cancel(err)))}};const subscribePromises=[];Object.keys(this._subscriptions).forEach((path=>{const events=[];this._subscriptions[path].forEach((sub=>{if(sub.event==="mutated"){return}const serverAlreadyNotifying=events.includes(sub.event);if(!serverAlreadyNotifying){events.push(sub.event);const promise=subscribeTo(sub);subscribePromises.push(promise)}}))}));const subscribeToMutatedEvents=async()=>{let retry=false;const promises=Object.keys(this._subscriptions).filter((path=>this._subscriptions[path].some((sub=>sub.event==="mutated"&&sub.state!=="canceled")))).filter(((path,i,arr)=>!arr.some((otherPath=>acebase_core_1.PathInfo.get(otherPath).isAncestorOf(path))))).reduce(((topPaths,path)=>(topPaths.includes(path)||topPaths.push(path))&&topPaths),[]).map((topEventPath=>{const sub=this._subscriptions[topEventPath].find((s=>s.event==="mutated"));const promise=subscribeTo(sub).then((()=>{if(sub.state==="canceled"){retry=true}}));return promise}));await Promise.all(promises);if(retry){await subscribeToMutatedEvents()}};subscribePromises.push(subscribeToMutatedEvents());await Promise.all(subscribePromises);this.eventCallback("connect");resolve()}));socket.on("disconnect",(reason=>{this.debug.warn(`Websocket disconnected: ${reason}`);if(this._connectionState===CONNECTION_STATE_DISCONNECTING){this._connectionState=CONNECTION_STATE_DISCONNECTED}else{this._connectionState=CONNECTION_STATE_CONNECTING;this._eventTimeline.disconnect=Date.now();if(reason==="io server disconnect"){this.socket=null;this.connect().catch((err=>{}))}}this.eventCallback("disconnect")}));socket.on("data-event",(data=>{var _a;const val=acebase_core_1.Transport.deserialize(data.val);const context=data.context||{};context.acebase_event_source="server";this._updateCursor(context.acebase_cursor);const causedByUs=((_a=context.acebase_mutation)===null||_a===void 0?void 0:_a.client_id)===this._id;const cacheEnabled=this.hasCache;//!!this._cache?.db;
const fireThisEvent=!causedByUs||!cacheEnabled;const updateCache=!causedByUs&&cacheEnabled;const fireCacheEvents=false;const pathSubs=this._subscriptions[data.subscr_path];if(!pathSubs&&data.event!=="mutated"){return}if(updateCache){if(data.path.startsWith("__")){}else if(data.event==="mutations"){const mutations=val.current;mutations.forEach((m=>{const path=m.target.reduce(((path,key)=>acebase_core_1.PathInfo.getChildPath(path,key)),acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,data.path));this.cache.db.api.set(path,m.val,{suppress_events:!fireCacheEvents,context:context})}))}else if(data.event==="notify_child_removed"){this.cache.db.api.set(acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,data.path),null,{suppress_events:!fireCacheEvents,context:context})}else if(!data.event.startsWith("notify_")){this.cache.db.api.set(acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,data.path),val.current,{suppress_events:!fireCacheEvents,context:context})}}if(!fireThisEvent){return}const targetSubs=data.event==="mutated"?Object.keys(this._subscriptions).filter((path=>{const pathInfo=acebase_core_1.PathInfo.get(path);return path===data.path||pathInfo.equals(data.subscr_path)||pathInfo.isAncestorOf(data.path)})).reduce(((subs,path)=>{const add=this._subscriptions[path].filter((sub=>sub.event==="mutated"));subs.push(...add);return subs}),[]):pathSubs.filter((sub=>sub.event===data.event));targetSubs.forEach((subscr=>{subscr.lastEvent=Date.now();subscr.cursor=context.acebase_cursor;subscr.callback(null,data.path,val.current,val.previous,context)}))}));socket.on("query-event",(data=>{data=acebase_core_1.Transport.deserialize(data);const query=this._realtimeQueries[data.query_id];let keepMonitoring=true;try{keepMonitoring=query.options.eventHandler(data)}catch(err){keepMonitoring=false}if(keepMonitoring===false){delete this._realtimeQueries[data.query_id];socket.emit("query-unsubscribe",{query_id:data.query_id})}}))}))}disconnect(){var _a;if(!((_a=this.settings.network)===null||_a===void 0?void 0:_a.realtime)){this._connectionState=CONNECTION_STATE_DISCONNECTING;this._eventTimeline.disconnect=Date.now();this.manualConnectionMonitor.emit("disconnect")}else if(this.socket!==null&&typeof this.socket==="object"){if(this._connectionState===CONNECTION_STATE_CONNECTED){this._eventTimeline.disconnect=Date.now()}this._connectionState=CONNECTION_STATE_DISCONNECTING;this.socket.close();this.socket=null}}async subscribe(path,event,callback,settings){var _a;if(!((_a=this.settings.network)===null||_a===void 0?void 0:_a.realtime)){throw new Error(`Cannot subscribe to realtime events because it has been disabled in the network settings`)}let pathSubs=this._subscriptions[path];if(!pathSubs){pathSubs=this._subscriptions[path]=[]}const serverAlreadyNotifying=pathSubs.some((sub=>sub.event===event))||event==="mutated"&&Object.keys(this._subscriptions).some((otherPath=>acebase_core_1.PathInfo.get(otherPath).isAncestorOf(path)&&this._subscriptions[otherPath].some((sub=>sub.event===event&&sub.state==="active"))));const subscr=new EventSubscription(path,event,callback,settings);pathSubs.push(subscr);if(this.hasCache){subscr.cacheCallback=(err,path,newValue,oldValue,context)=>subscr.callback(err,path.slice(`${this.dbname}/cache/`.length),newValue,oldValue,context);this.cache.db.api.subscribe(acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,path),event,subscr.cacheCallback)}if(serverAlreadyNotifying||!this.isConnected){return}if(event==="mutated"){Object.keys(this._subscriptions).filter((otherPath=>acebase_core_1.PathInfo.get(otherPath).isDescendantOf(path)&&this._subscriptions[otherPath].some((sub=>sub.event==="mutated")))).map((path=>_websocketRequest(this.socket,"unsubscribe",{path:path,event:"mutated"},this.accessToken))).map((promise=>promise.catch((err=>console.error(err)))))}const result=await _websocketRequest(this.socket,"subscribe",{path:path,event:event},this.accessToken);subscr.activate()}async unsubscribe(path,event,callback){var _a;if(!((_a=this.settings.network)===null||_a===void 0?void 0:_a.realtime)){throw new Error(`Cannot unsubscribe from realtime events because it has been disabled in the network settings`)}const pathSubs=this._subscriptions[path];if(!pathSubs){return Promise.resolve()}const unsubscribeFrom=subscriptions=>{subscriptions.forEach((subscr=>{pathSubs.splice(pathSubs.indexOf(subscr),1);if(this.hasCache){if(typeof subscr.cacheCallback!=="function"){throw new Error("DEV ERROR: When subscription was added, cacheCallback must have been set")}this.cache.db.api.unsubscribe(acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,path),subscr.event,subscr.cacheCallback)}}))};const hadMutatedEvents=pathSubs.some((sub=>sub.event==="mutated"));if(!event){unsubscribeFrom(pathSubs)}else if(!callback){const subscriptions=pathSubs.filter((subscr=>subscr.event===event));unsubscribeFrom(subscriptions)}else{const subscriptions=pathSubs.filter((subscr=>subscr.event===event&&subscr.callback===callback));unsubscribeFrom(subscriptions)}const hasMutatedEvents=pathSubs.some((sub=>sub.event==="mutated"));let promise=Promise.resolve();if(pathSubs.length===0){delete this._subscriptions[path];if(this.isConnected){promise=_websocketRequest(this.socket,"unsubscribe",{path:path,access_token:this.accessToken},this.accessToken).catch((err=>this.debug.error(`Failed to unsubscribe from event(s) on "${path}": ${err.message}`)))}}else if(this.isConnected&&!pathSubs.some((subscr=>subscr.event===event))){promise=_websocketRequest(this.socket,"unsubscribe",{path:path,event:event,access_token:this.accessToken},this.accessToken).catch((err=>this.debug.error(`Failed to unsubscribe from event "${event}" on "${path}": ${err.message}`)))}if(this.isConnected&&hadMutatedEvents&&!hasMutatedEvents){const promises=Object.keys(this._subscriptions).filter((otherPath=>acebase_core_1.PathInfo.get(otherPath).isDescendantOf(path)&&this._subscriptions[otherPath].some((sub=>sub.event==="mutated")))).map((path=>_websocketRequest(this.socket,"subscribe",{path:path,event:"mutated"},this.accessToken))).map((promise=>promise.catch((err=>this.debug.error(`Failed to subscribe to event "${event}" on path "${path}": ${err.message}`)))));promise=Promise.all([promise,...promises])}await promise}transaction(path,callback,options={context:{}}){const id=acebase_core_1.ID.generate();options.context=options.context||{};options.context.acebase_mutation={client_id:this._id,id:id,op:"transaction",path:path,flow:"server"};const cachePath=acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,path);return new Promise((async(resolve,reject)=>{var _a;let cacheUpdateVal;const handleSuccess=async context=>{if(this.hasCache&&typeof cacheUpdateVal!=="undefined"){await this.cache.db.api.set(cachePath,cacheUpdateVal)}resolve({cursor:context===null||context===void 0?void 0:context.acebase_cursor})};if(this.isConnected&&((_a=this.settings.network)===null||_a===void 0?void 0:_a.realtime)){const socket=this.socket;const startedCallback=async data=>{if(data.id===id){socket.off("tx_started",startedCallback);const currentValue=acebase_core_1.Transport.deserialize(data.value);let newValue=callback(currentValue);if(newValue instanceof Promise){newValue=await newValue}socket.emit("transaction",{action:"finish",id:id,path:path,value:acebase_core_1.Transport.serialize(newValue),access_token:this.accessToken});if(this.hasCache){cacheUpdateVal=newValue}}};const completedCallback=data=>{if(data.id===id){socket.off("tx_completed",completedCallback);socket.off("tx_error",errorCallback);handleSuccess(data.context)}};const errorCallback=data=>{if(data.id===id){socket.off("tx_started",startedCallback);socket.off("tx_completed",completedCallback);socket.off("tx_error",errorCallback);reject(new Error(data.reason))}};socket.on("tx_started",startedCallback);socket.on("tx_completed",completedCallback);socket.on("tx_error",errorCallback);socket.emit("transaction",{action:"start",id:id,path:path,access_token:this.accessToken,context:options.context})}else{const startData=JSON.stringify({path:path});try{const tx=await this._request({ignoreConnectionState:true,method:"POST",url:`${this.url}/transaction/${this.dbname}/start`,data:startData,context:options.context});const id=tx.id;const currentValue=acebase_core_1.Transport.deserialize(tx.value);let newValue=callback(currentValue);if(newValue instanceof Promise){newValue=await newValue}if(this.hasCache){cacheUpdateVal=newValue}const finishData=JSON.stringify({id:id,value:acebase_core_1.Transport.serialize(newValue)});const{context:context}=await this._request({ignoreConnectionState:true,method:"POST",url:`${this.url}/transaction/${this.dbname}/finish`,data:finishData,context:options.context,includeContext:true});await handleSuccess(context)}catch(err){if(["ETIMEDOUT","ENOTFOUND","ECONNRESET","ECONNREFUSED","EPIPE","fetch_failed"].includes(err.code)){err.message=error_1.NOT_CONNECTED_ERROR_MESSAGE}reject(err)}}}))}async _request(options){var _a;if(this.isConnected||options.ignoreConnectionState===true){const result=await(async()=>{try{return await(0,request_1.default)(options.method||"GET",options.url,{data:options.data,accessToken:this.accessToken,dataReceivedCallback:options.dataReceivedCallback,dataRequestCallback:options.dataRequestCallback,context:options.context})}catch(err){if(this.isConnected&&err.isNetworkError){this.debug.warn(`A network error occurred loading ${options.url}`);this._handleDetectedDisconnect(err)}throw err}})();if(result.context&&result.context.acebase_cursor){this._updateCursor(result.context.acebase_cursor)}if(options.includeContext===true){if(!result.context){result.context={}}return result}else{return result.data}}else{if(!this.isConnecting||!((_a=this.settings.network)===null||_a===void 0?void 0:_a.realtime)){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const connectPromise=new Promise((resolve=>{var _a;return(_a=this.socket)===null||_a===void 0?void 0:_a.once("connect",resolve)}));await(0,promise_timeout_1.promiseTimeout)(connectPromise,1e3,"Waiting for connection").catch((err=>{throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}));return this._request(options)}}handleSignInResult(result,emitEvent=true){var _a;this._eventTimeline.signIn=Date.now();const details={user:result.user,accessToken:result.access_token,provider:result.provider||"acebase"};this.accessToken=details.accessToken;(_a=this.socket)===null||_a===void 0?void 0:_a.emit("signin",details.accessToken);emitEvent&&this.eventCallback("signin",details);return details}async signIn(username,password){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/signin`,data:{method:"account",username:username,password:password,client_id:this.socket&&this.socket.id}});return this.handleSignInResult(result)}async signInWithEmail(email,password){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/signin`,data:{method:"email",email:email,password:password,client_id:this.socket&&this.socket.id}});return this.handleSignInResult(result)}async signInWithToken(token,emitEvent=true){if(!this.isConnected){throw new Error("Cannot sign in because client is not connected to the server. If you want to automatically sign in the user with this access token once a connection is established, use client.auth.setAccessToken(token)")}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/signin`,data:{method:"token",access_token:token,client_id:this.socket&&this.socket.id}});return this.handleSignInResult(result,emitEvent)}setAccessToken(token){this.accessToken=token}async startAuthProviderSignIn(providerName,callbackUrl,options){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const optionParams=typeof options==="object"?"&"+Object.keys(options).map((key=>`option_${key}=${encodeURIComponent(options[key])}`)).join("&"):"";const result=await this._request({url:`${this.url}/oauth2/${this.dbname}/init?provider=${providerName}&callbackUrl=${callbackUrl}${optionParams}`});return{redirectUrl:result.redirectUrl}}async finishAuthProviderSignIn(callbackResult){let result;try{result=JSON.parse(Base64.decode(callbackResult))}catch(err){throw new Error(`Invalid result`)}if(!result.user){this.accessToken=result.access_token;const authState=await this._request({url:`${this.url}/auth/${this.dbname}/state`});if(!authState.signed_in){this.accessToken=null;throw new Error(`Invalid access token received: not signed in`)}result.user=authState.user}return this.handleSignInResult(result)}async refreshAuthProviderToken(providerName,refreshToken){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({url:`${this.url}/oauth2/${this.dbname}/refresh?provider=${providerName}&refresh_token=${refreshToken}`});return result}async signOut(options){if(typeof options==="boolean"){options={everywhere:options}}else if(typeof options!=="object"){throw new TypeError("options must be an object")}if(typeof options.everywhere!=="boolean"){options.everywhere=false}if(typeof options.clearCache!=="boolean"){options.clearCache=false}if(!this.accessToken){return}if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/signout`,data:{client_id:this.socket&&this.socket.id,everywhere:options.everywhere}});this.socket&&this.socket.emit("signout",this.accessToken);this.accessToken=null;if(this.hasCache&&options.clearCache){this.clearCache().catch((err=>{console.error(`Could not clear cache:`,err)}))}this.eventCallback("signout")}async changePassword(uid,currentPassword,newPassword){if(!this.accessToken){throw new Error(`not_signed_in`)}if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/change_password`,data:{uid:uid,password:currentPassword,new_password:newPassword}});this.accessToken=result.access_token;return{accessToken:this.accessToken}}async forgotPassword(email){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/forgot_password`,data:{email:email}});return result}async verifyEmailAddress(verificationCode){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/verify_email`,data:{code:verificationCode}});return result}async resetPassword(resetCode,newPassword){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/reset_password`,data:{code:resetCode,password:newPassword}});return result}async signUp(details,signIn=true){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/signup`,data:details});if(signIn){return this.handleSignInResult(result)}return{user:result.user,accessToken:this.accessToken}}async updateUserDetails(details){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/update`,data:details});return{user:result.user}}async deleteAccount(uid,signOut=true){if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}const result=await this._request({method:"POST",url:`${this.url}/auth/${this.dbname}/delete`,data:{uid:uid}});if(signOut){this.socket&&this.socket.emit("signout",this.accessToken);this.accessToken=null;this.eventCallback("signout")}return true}get isConnected(){return this._connectionState===CONNECTION_STATE_CONNECTED}get isConnecting(){return this._connectionState===CONNECTION_STATE_CONNECTING}get connectionState(){return this._connectionState}stats(options){return this._request({url:`${this.url}/stats/${this.dbname}`})}async sync(options={firstSync:false,fetchFreshData:true,eventCallback:null}){var _a,_b,_c;if(!this.isConnected){throw new Error(error_1.NOT_CONNECTED_ERROR_MESSAGE)}if(this.hasCache&&!this.cache.db.isReady){throw new Error(`cache database is not ready yet`)}this._eventTimeline.sync=Date.now();(_a=options.eventCallback)===null||_a===void 0?void 0:_a.call(options,"sync_start");const handleStatsUpdateError=err=>{this.debug.error(`Failed to update cache db stats:`,err)};try{let totalPendingChanges=0;const useCursor=((_b=this.settings.sync)===null||_b===void 0?void 0:_b.useCursor)!==false;const cursor=useCursor?this._cursor.sync:null;if(this.hasCache){const cacheApi=this.cache.db.api;const{value:value,context:context}=await cacheApi.get(`${this.dbname}/pending`);const pendingChanges=value;cacheApi.set(`${this.dbname}/stats/last_sync_start`,new Date).catch(handleStatsUpdateError);try{const ids=Object.keys(pendingChanges||{}).sort();const compatibilityMode=ids.map((id=>pendingChanges[id])).some((m=>m.type==="update"));const mutations=compatibilityMode?ids.map((id=>{const mutation=pendingChanges[id];mutation.ids=[id];return mutation})):ids.reduce(((mutations,id)=>{const change=pendingChanges[id];console.assert(["set","remove"].includes(change.type),'Only "set" and "remove" mutations should be present');if(change.path===""){const rootUpdate=mutations.find((u=>u.path===""));if(rootUpdate){rootUpdate.data=change.data}else{change.ids=[id];mutations.push(change)}}else{const pathInfo=acebase_core_1.PathInfo.get(change.path);const parentPath=pathInfo.parentPath;const parentUpdate=mutations.find((u=>u.path===parentPath));const value=change.type==="remove"||change.data===null||typeof change.data==="undefined"?null:change.data;if(!parentUpdate){mutations.push({ids:[id],type:"update",path:parentPath,data:{[pathInfo.key]:value},context:change.context})}else{parentUpdate.data[pathInfo.key]=value;parentUpdate.ids.push(id)}}return mutations}),[]);for(const m of mutations){const ids=m.ids;this.debug.verbose(`SYNC pushing mutations ${ids.join(",")}: `,m);totalPendingChanges++;try{if(m.type==="update"){await this.update(m.path,m.data,{allow_cache:false,context:m.context})}else if(m.type==="set"){if(!m.data){m.data=null}await this.set(m.path,m.data,{allow_cache:false,context:m.context})}else if(m.type==="remove"){await this.set(m.path,null,{allow_cache:false,context:m.context})}else{throw new Error(`unsupported mutation type "${m.type}"`)}this.debug.verbose(`SYNC mutation ${ids.join(",")} processed ok`);const updates=ids.reduce(((updates,id)=>(updates[id]=null,updates)),{});cacheApi.update(`${this.dbname}/pending`,updates)}catch(err){this.debug.error(`SYNC mutations ${ids.join(",")} failed: ${err.message}`);if(!this.isConnected){throw err}if(typeof err==="string"){err={code:"unknown",message:err,stack:"n/a"}}const errorReport={date:new Date,code:err.code||"unknown",message:err.message,stack:err.stack};ids.forEach((id=>{cacheApi.transaction(`${this.dbname}/pending/${id}`,(m=>{if(!m.error){m.error={first:errorReport,last:errorReport,retries:0}}else{m.error.last=errorReport;m.error.retries++}if(m.error.retries===3){cacheApi.set(`${this.dbname}/failed/${id}`,m);return null}return m}))}));cacheApi.set(`${this.dbname}/stats/last_sync_error`,errorReport).catch(handleStatsUpdateError);(_c=options.eventCallback)===null||_c===void 0?void 0:_c.call(options,"sync_change_error",{error:err,change:m})}}this.debug.verbose(`SYNC push done`);cacheApi.set(`${this.dbname}/stats/last_sync_end`,new Date).catch(handleStatsUpdateError)}catch(err){this.debug.error(`SYNC push error: ${err.message}`);if(typeof err==="string"){err={code:"unknown",message:err,stack:"n/a"}}cacheApi.set(`${this.dbname}/stats/last_sync_error`,{date:new Date,code:err.code||"unknown",message:err.message,stack:err.stack}).catch(handleStatsUpdateError);throw err}}let totalRemoteChanges=0,usedSyncMethod="reload";const subscriptionPaths=Object.keys(this._subscriptions);const subscriptions=subscriptionPaths.reduce(((subs,path)=>{this._subscriptions[path].forEach((sub=>subs.push(sub)));return subs}),[]);const subscriptionsFor=path=>subscriptions.filter((sub=>sub.path===path));if(this.hasCache){const cacheApi=this.cache.db.api;subscriptions.forEach((sub=>{sub.tempCallback=()=>{totalRemoteChanges++};cacheApi.subscribe(acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,sub.path),sub.event,sub.tempCallback)}));const strategy={reload:[],cursor:[],fallback:[],warn:[],noop:[]};const hasStaleValue=sub=>{const addedWhileOffline=sub.added>this._eventTimeline.disconnect&&sub.added<this._eventTimeline.connect;const addedBeforeDisconnection=sub.added<this._eventTimeline.disconnect;if(addedWhileOffline){return true}if(addedBeforeDisconnection){return cursor?false:true}return false};strategy.reload=subscriptionPaths.filter((path=>{if(path.includes("*")||path.includes("$")){return false}return subscriptionsFor(path).some((sub=>{if(hasStaleValue(sub)){if(typeof sub.settings.syncFallback==="function"){return false}if(sub.settings.syncFallback==="reload"){return true}if(sub.event==="value"){return true}if(sub.event==="child_added"&&!sub.settings.newOnly){return true}}return false}))})).reduce(((reloadPaths,path)=>{!reloadPaths.some((p=>p===path||acebase_core_1.PathInfo.get(p).isAncestorOf(path)))&&reloadPaths.push(path);return reloadPaths}),[]);strategy.fallback=subscriptionPaths.filter((path=>!strategy.reload.some((p=>p===path||acebase_core_1.PathInfo.get(p).isAncestorOf(path))))).reduce(((fallbackItems,path)=>{subscriptionsFor(path).forEach((sub=>{if(hasStaleValue(sub)&&typeof sub.settings.syncFallback==="function"){fallbackItems.push(sub)}}));return fallbackItems}),[]);strategy.cursor=!cursor?[]:subscriptionPaths.filter((path=>!strategy.reload.some((p=>p===path||acebase_core_1.PathInfo.get(p).isAncestorOf(path))))).reduce(((cursorItems,path)=>{const subs=subscriptionsFor(path);const events=subs.filter((sub=>!hasStaleValue(sub)&&!strategy.fallback.includes(sub))).reduce(((events,sub)=>(events.includes(sub.event)||events.push(sub.event))&&events),[]);events.length>0&&cursorItems.push({path:path,events:events});return cursorItems}),[]);strategy.warn=subscriptionPaths.filter((path=>!strategy.reload.some((p=>p===path||acebase_core_1.PathInfo.get(p).isAncestorOf(path))))).reduce(((warnItems,path)=>{const subs=subscriptionsFor(path).filter((sub=>!strategy.fallback.includes(sub)));subs.forEach((sub=>{if(typeof sub.settings.syncFallback==="function"||sub.added>this._eventTimeline.connect){strategy.noop.push(sub)}else if(!strategy.cursor.some((item=>item.path===sub.path&&item.events.includes(sub.event)))){const item=warnItems.find((item=>item.path===sub.path));if(!item){warnItems.push({path:sub.path,events:[sub.event]})}else if(!item.events.includes(sub.event)){item.events.push(sub.event)}}}));return warnItems}),[]);console.log(`SYNC strategy`,strategy);const syncPromises=[];if(strategy.cursor.length>0){this.debug.log(`SYNC using cursor "${cursor}" for event(s) ${strategy.cursor.map((item=>`${item.events.join(", ")} on "/${item.path}"`)).join(", ")}`);const cursorPromise=(async()=>{let remoteMutations;try{const result=await this.getChanges({for:strategy.cursor,cursor:cursor});remoteMutations=result.changes;this._updateCursor(result.new_cursor)}catch(err){this.debug.error(`SYNC: Could not load remote changes`,err);options.eventCallback&&options.eventCallback("sync_cursor_error",err);if(err.code==="no_transaction_logging"){this._updateCursor(null)}strategy.cursor.forEach((item=>{if(item.events.includes("value")){strategy.reload.push(item.path)}else{strategy.warn.push(item)}}))}if(remoteMutations){usedSyncMethod="cursor";this.debug.log(`SYNC: Got ${remoteMutations.length} remote mutations`,remoteMutations);const promises=remoteMutations.map((m=>{const cachePath=`${this.dbname}/cache/${m.path}`;if(m.type==="update"){return cacheApi.update(cachePath,m.value,{context:m.context})}else if(m.type==="set"){return cacheApi.set(cachePath,m.value,{context:m.context})}}));await Promise.all(promises)}})();syncPromises.push(cursorPromise)}if(strategy.reload.length>0){this.debug.log(`SYNC reloading data for event paths ${strategy.reload.map((path=>`"/${path}"`)).join(", ")}`);const reloadPromise=(async()=>{const promises=strategy.reload.map((path=>{this.debug.verbose(`SYNC: load "/${path}"`);return this.get(path,{cache_mode:"bypass"}).catch((err=>{this.debug.error(`SYNC: could not load "/${path}"`,err);options.eventCallback&&options.eventCallback("sync_pull_error",err)}))}));await Promise.all(promises)})();syncPromises.push(reloadPromise)}if(strategy.fallback.length>0){this.debug.log(`SYNC using fallback functions for event(s) ${strategy.fallback.map((sub=>`${sub.event} on "/${sub.path}"`)).join(", ")}`);const fallbackPromise=(async()=>{const promises=strategy.fallback.map((async sub=>{this.debug.verbose(`SYNC: running fallback for event ${sub.event} on "/${sub.path}"`);try{if(sub.settings.syncFallback==="reload"){throw new Error(`DEV ERROR: Not expecting "reload" as fallback`)}await sub.settings.syncFallback()}catch(err){this.debug.error(`SYNC: error running fallback function for ${sub.event} on "/${sub.path}"`,err);options.eventCallback&&options.eventCallback("sync_fallback_error",err)}}));await Promise.all(promises)})();syncPromises.push(fallbackPromise)}if(strategy.warn.length>0){this.debug.warn(`SYNC warning: unable to sync event(s) ${strategy.warn.map((item=>`${item.events.map((event=>`"${event}"`)).join(", ")} on "/${item.path}"`)).join(", ")}. To resolve this, provide syncFallback functions for these events`)}await Promise.all(syncPromises);await new Promise((resolve=>setTimeout(resolve,10)));subscriptions.forEach((sub=>{if(typeof sub.tempCallback!=="function"){throw new Error("DEV ERROR: tempCallback must be a function")}cacheApi.unsubscribe(acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,sub.path),sub.event,sub.tempCallback);delete sub.tempCallback}))}else if(!this._cache){const syncPromises=[];subscriptionPaths.forEach((path=>{const subs=subscriptionsFor(path);const warnEvents=[];subs.filter((sub=>sub.event!=="value")).forEach((sub=>{if(typeof sub.settings.syncFallback==="function"){syncPromises.push(sub.settings.syncFallback())}else{!warnEvents.includes(sub.event)&&warnEvents.push(sub.event)}}));if(warnEvents.length>0){this.debug.warn(`Subscriptions ${warnEvents.join(", ")} on path "${path}" might have missed events while offline. Data should be reloaded!`)}const valueSubscriptions=subs.filter((sub=>sub.event==="value"));if(valueSubscriptions.length>0){const p=this.get(path,{allow_cache:false}).then((value=>{valueSubscriptions.forEach((subscr=>subscr.callback(null,path,value)))}));syncPromises.push(p)}}));await Promise.all(syncPromises)}subscriptions.forEach((sub=>sub.lastSynced=Date.now()));this.debug.verbose(`SYNC done`);const info={local:totalPendingChanges,remote:totalRemoteChanges,method:usedSyncMethod,cursor:cursor};options.eventCallback&&options.eventCallback("sync_done",info);return info}catch(err){this.debug.error(`SYNC error`,err);options.eventCallback&&options.eventCallback("sync_error",err);throw err}}async getMutations(filter){var _a;if(typeof filter!=="object"){throw new Error("No filter specified")}if(typeof filter.cursor!=="string"&&typeof filter.timestamp!=="number"){throw new Error("No cursor or timestamp given")}const query=Object.keys(filter).map((key=>{let val=filter[key];if(key==="for"){val=encodeURIComponent(JSON.stringify(val))}return typeof val!=="undefined"?`${key}=${val}`:null})).filter((p=>p!=null)).join("&");const{data:data,context:context}=await this._request({url:`${this.url}/sync/mutations/${this.dbname}?${query}`,includeContext:true});const mutations=acebase_core_1.Transport.deserialize2(data);return{used_cursor:(_a=filter.cursor)!==null&&_a!==void 0?_a:null,new_cursor:context.acebase_cursor,mutations:mutations}}async getChanges(filter){var _a;if(typeof filter!=="object"){throw new Error("No filter specified")}if(typeof filter.cursor!=="string"&&typeof filter.timestamp!=="number"){throw new Error("No cursor or timestamp given")}const query=Object.keys(filter).map((key=>{let val=filter[key];if(key==="for"){val=encodeURIComponent(JSON.stringify(val))}return typeof val!=="undefined"?`${key}=${val}`:null})).filter((p=>p!=null)).join("&");const{data:data,context:context}=await this._request({url:`${this.url}/sync/changes/${this.dbname}?${query}`,includeContext:true});const changes=acebase_core_1.Transport.deserialize2(data);return{used_cursor:(_a=filter.cursor)!==null&&_a!==void 0?_a:null,new_cursor:context.acebase_cursor,changes:changes}}async _addCacheSetMutation(path,value,context){var _a,_b;const escapedPath=path.replace(/([.*+?\\$^\(\)\[\]\{\}])/g,"\\$1");const re=new RegExp(`^${escapedPath}(?:\\[|/|$)`);await((_a=this._cache)===null||_a===void 0?void 0:_a.db.query(`${this.dbname}/pending`).filter("path","matches",re).remove());return(_b=this._cache)===null||_b===void 0?void 0:_b.db.api.set(`${this.dbname}/pending/${acebase_core_1.ID.generate()}`,{type:value!==null?"set":"remove",path:path,data:value,context:context})}set(path,value,options={allow_cache:true,context:{}}){var _a;if(!options.context){options.context={}}const useCache=this._cache&&options.allow_cache!==false;const useServer=this.isConnected;options.context.acebase_mutation=options.context.acebase_mutation||{client_id:this._id,id:acebase_core_1.ID.generate(),op:"set",path:path,flow:useCache?useServer?"parallel":"cache":"server"};const updateServer=async()=>{const data=JSON.stringify(acebase_core_1.Transport.serialize(value));const{context:context}=await this._request({method:"PUT",url:`${this.url}/data/${this.dbname}/${path}`,data:data,context:options.context,includeContext:true});Object.assign(options.context,context);const cursor=context===null||context===void 0?void 0:context.acebase_cursor;return{cursor:cursor}};if(!useCache){return updateServer()}const cachePath=acebase_core_1.PathInfo.getChildPath(`${this.dbname}/cache`,path);let rollbackValue;const updateCache=()=>this.cache.db.api.transaction(cachePath,(currentValue=>{rollbackValue=currentValue;return value}),{context:options.context});const rollbackCache=async()=>{await cachePromise;return this.cache.db.api.set(cachePath,rollbackValue,{context:options.context})};const addPendingTransaction=async()=>{await this._addCacheSetMutation(path,value,options.context)};const cachePromise=updateCache();const tryCachePromise=cachePromise.then((()=>({success:true,error:null}))).catch((err=>({success:false,error:err})));const serverPromise=!useServer?null:updateServer();const tryServerPromise=!useServer?null:serverPromise.then((()=>({success:true,error:null}))).catch((err=>({success:false,error:err})));Promise.all([tryCachePromise,tryServerPromise]).then((([cacheResult,serverResult])=>{var _a;const networkError=serverPromise&&!(serverResult===null||serverResult===void 0?void 0:serverResult.success)&&((_a=serverResult===null||serverResult===void 0?void 0:serverResult.error)===null||_a===void 0?void 0:_a.isNetworkError)===true;if(serverPromise&&!networkError){if(serverResult===null||serverResult===void 0?void 0:serverResult.success){if(!cacheResult.success){this.debug.error(`Failed to set cache for "${path}". Error: `,cacheResult.error)}}else{if(cacheResult.success){this.debug.error(`Failed to set server value for "${path}", rolling back cache to previous value. Error:`,serverResult===null||serverResult===void 0?void 0:serverResult.error);rollbackCache().catch((err=>{this.debug.error(`Failed to roll back cache? Error:`,err)}))}}}else if(cacheResult.success){addPendingTransaction().catch((err=>{this.debug.error(`Failed to add pending sync action for "${path}", rolling back cache to previous value. Error:`,err);rollbackCache().catch((err=>{this.debug.error(`Failed to roll back cache? Error:`,err)}))}))}}));if(!useServer){return cachePromise}return((_a=this._cache)===null||_a===void 0?void 0:_a.priority)==="cache"?cachePromise:serverPromise}update(path,updates,options={allow_cache:true,context:{}}){var _a,_b;const useCache=this._cache&&options&&options.allow_cache!==false;const useSer