@quiltt/core 3.5.4 → 3.5.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +30 -10
  2. package/README.md +1 -1
  3. package/dist/SubscriptionLink-client-VkkcO2Yz.js +300 -0
  4. package/dist/index-client-D55-rzVl.js +561 -0
  5. package/dist/index.d.ts +402 -15
  6. package/dist/index.js +284 -11
  7. package/package.json +18 -19
  8. package/src/api/graphql/links/BatchHttpLink.ts +4 -2
  9. package/src/api/graphql/links/HttpLink.ts +4 -2
  10. package/src/api/graphql/links/actioncable/logger.ts +3 -1
  11. package/src/api/graphql/links/actioncable/subscription_guarantor.ts +1 -1
  12. package/src/api/rest/{AuthAPI.ts → auth.ts} +39 -26
  13. package/src/api/rest/fetchWithRetry.ts +73 -0
  14. package/src/api/rest/index.ts +1 -1
  15. package/dist/Storage/index.cjs +0 -10
  16. package/dist/Storage/index.cjs.map +0 -1
  17. package/dist/Storage/index.d.ts +0 -2
  18. package/dist/Storage/index.js +0 -5
  19. package/dist/Storage/index.js.map +0 -1
  20. package/dist/api/graphql/index.cjs +0 -49
  21. package/dist/api/graphql/index.cjs.map +0 -1
  22. package/dist/api/graphql/index.d.ts +0 -16
  23. package/dist/api/graphql/index.js +0 -15
  24. package/dist/api/graphql/index.js.map +0 -1
  25. package/dist/api/graphql/links/actioncable/index.cjs +0 -18
  26. package/dist/api/graphql/links/actioncable/index.cjs.map +0 -1
  27. package/dist/api/graphql/links/actioncable/index.d.ts +0 -62
  28. package/dist/api/graphql/links/actioncable/index.js +0 -5
  29. package/dist/api/graphql/links/actioncable/index.js.map +0 -1
  30. package/dist/api/graphql/links/index.cjs +0 -28
  31. package/dist/api/graphql/links/index.cjs.map +0 -1
  32. package/dist/api/graphql/links/index.d.ts +0 -56
  33. package/dist/api/graphql/links/index.js +0 -14
  34. package/dist/api/graphql/links/index.js.map +0 -1
  35. package/dist/api/index.cjs +0 -54
  36. package/dist/api/index.cjs.map +0 -1
  37. package/dist/api/index.d.ts +0 -78
  38. package/dist/api/index.js +0 -16
  39. package/dist/api/index.js.map +0 -1
  40. package/dist/api/rest/index.cjs +0 -14
  41. package/dist/api/rest/index.cjs.map +0 -1
  42. package/dist/api/rest/index.d.ts +0 -70
  43. package/dist/api/rest/index.js +0 -7
  44. package/dist/api/rest/index.js.map +0 -1
  45. package/dist/consumer-c13efb94.d.ts +0 -78
  46. package/dist/index-f53b61b3.d.ts +0 -129
  47. package/dist/index.cjs +0 -67
  48. package/dist/index.cjs.map +0 -1
  49. package/dist/index.js.map +0 -1
  50. package/src/api/rest/axios.ts +0 -52
package/dist/index.js CHANGED
@@ -1,16 +1,289 @@
1
+ import { G as GlobalStorage, e as endpointGraphQL, v as version, d as debugging, S as SubscriptionLink, a as endpointAuth } from './SubscriptionLink-client-VkkcO2Yz.js';
2
+ export { L as LocalStorage, M as MemoryStorage, O as Observable, f as Storage, c as cdnBase, b as endpointWebsockets } from './SubscriptionLink-client-VkkcO2Yz.js';
1
3
  import { ApolloLink, ApolloClient } from '@apollo/client/index.js';
2
4
  export { InMemoryCache, gql, useMutation, useQuery, useSubscription } from '@apollo/client/index.js';
3
- import { BatchHttpLink } from '@apollo/client/link/batch-http/index.js';
4
- import Rt from 'cross-fetch';
5
+ import { BatchHttpLink as BatchHttpLink$1 } from '@apollo/client/link/batch-http/index.js';
6
+ import crossfetch from 'cross-fetch';
5
7
  import { onError } from '@apollo/client/link/error/index.js';
6
- import { HttpLink } from '@apollo/client/link/http/index.js';
7
- import { RetryLink } from '@apollo/client/link/retry/index.js';
8
- import { ApolloLink as ApolloLink$1, Observable } from '@apollo/client/core/index.js';
9
- import { print } from 'graphql';
10
- import zt from 'axios';
8
+ import { HttpLink as HttpLink$1 } from '@apollo/client/link/http/index.js';
9
+ import { RetryLink as RetryLink$1 } from '@apollo/client/link/retry/index.js';
11
10
 
12
- var vt=Object.defineProperty,St=Object.defineProperties;var xt=Object.getOwnPropertyDescriptors;var H=Object.getOwnPropertySymbols;var yt=Object.prototype.hasOwnProperty,Ct=Object.prototype.propertyIsEnumerable;var P=(o,t,e)=>t in o?vt(o,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):o[t]=e,l=(o,t)=>{for(var e in t||(t={}))yt.call(t,e)&&P(o,e,t[e]);if(H)for(var e of H(t))Ct.call(t,e)&&P(o,e,t[e]);return o},k=(o,t)=>St(o,xt(t));var n=(o,t,e)=>(P(o,typeof t!="symbol"?t+"":t,e),e);var B="@quiltt/core",z="3.5.4";var kt=(()=>{try{return process.env.QUILTT_API_INSECURE}catch(o){return}})(),V=(()=>{try{return process.env.QUILTT_API_DOMAIN}catch(o){return}})(),Tt=(()=>{try{return !!process.env.QUILTT_DEBUG||process.env.NODE_ENV!=="production"}catch(o){return !1}})(),T=V||"quiltt.io",M=`http${kt?"":"s"}`,At=`ws${V?"":"s"}`,A=Tt,F=`${B}: v${z}`,te=`${M}://cdn.${T}`,f=`${M}://auth.${T}/v1/users/session`,D=`${M}://api.${T}/v1/graphql`,Y=`${At}://api.${T}/websockets`;var Dt=/^(?:[\w-]+\.){2}[\w-]+$/,oe=o=>{if(typeof o=="undefined"||o===null)return o;if(!Dt.test(o)){console.error(`Invalid Session Token: ${o}`);return}let[t,e,r]=o.split(".");try{return {token:o,claims:JSON.parse(atob(e))}}catch(i){console.error(`Invalid Session Token: ${i}`);}};var g=class{constructor(t){n(this,"state");n(this,"observers",[]);n(this,"get",()=>this.state);n(this,"set",t=>{this.state!==t&&(this.state=t,this.observers.forEach(e=>e(t)));});n(this,"subscribe",t=>{this.observers.push(t);});n(this,"unsubscribe",t=>{this.observers=this.observers.filter(e=>e!==t);});this.state=t;}};var O=class{constructor(){n(this,"observers",{});n(this,"isEnabled",()=>{try{return localStorage.setItem("quiltt.ping","pong"),localStorage.removeItem("quiltt.ping"),!0}catch(t){return !1}});n(this,"isDisabled",()=>!this.isEnabled());n(this,"get",t=>{if(!(typeof window=="undefined"||window.expo))try{let e=window.localStorage.getItem(`quiltt.${t}`);return e&&JSON.parse(e)}catch(e){console.warn(`localStorage Error: "quiltt.${t}"`,e);return}});n(this,"set",(t,e)=>{if(!(typeof window=="undefined"||window.expo))try{e?window.localStorage.setItem(`quiltt.${t}`,JSON.stringify(e)):window.localStorage.removeItem(`quiltt.${t}`);}catch(r){console.warn(`localStorage Error: "quiltt.${t}"`,r);}});n(this,"remove",t=>{try{window.localStorage.removeItem(`quiltt.${t}`);}catch(e){console.warn(`localStorage Error: "quiltt.${t}">`,e);}});n(this,"subscribe",(t,e)=>{this.observers[t]||(this.observers[t]=[]),this.observers[t].push(e);});n(this,"unsubscribe",(t,e)=>{var r;this.observers[t]=(r=this.observers[t])==null?void 0:r.filter(i=>i!==e);});n(this,"handleStorageEvent",t=>{if(t.key&&t.key.includes("quiltt.")){let e=t.newValue?JSON.parse(t.newValue):null;this.observers[t.key]&&this.observers[t.key].forEach(r=>r(e));}else Object.entries(this.observers).forEach(([e,r])=>{r.forEach(i=>i(this.get(e)));});});typeof window!="undefined"&&!(window!=null&&window.expo)&&window.addEventListener("storage",this.handleStorageEvent.bind(this));}};var w=class{constructor(){n(this,"observables",{});n(this,"get",t=>{if(this.observables[t])return this.observables[t].get()});n(this,"set",(t,e)=>{this.observables[t]?this.observables[t].set(e):this.observables[t]=new g(e);});n(this,"subscribe",(t,e)=>{this.observables[t]||(this.observables[t]=new g),this.observables[t].subscribe(e);});n(this,"unsubscribe",(t,e)=>{this.observables[t]&&this.observables[t].unsubscribe(e);});}};var $=class{constructor(){n(this,"memoryStore",new w);n(this,"localStore",new O);n(this,"observers",{});n(this,"monitors",new Set);n(this,"get",t=>{this.monitorLocalStorageChanges(t);let e=this.memoryStore.get(t);return e===void 0&&(e=this.localStore.get(t)),e});n(this,"set",(t,e)=>{var r;this.monitorLocalStorageChanges(t),this.localStore.set(t,e),this.memoryStore.set(t,e),(r=this.observers[t])==null||r.forEach(i=>i(e));});n(this,"subscribe",(t,e)=>{this.observers[t]||(this.observers[t]=[]),this.observers[t].push(e);});n(this,"unsubscribe",(t,e)=>{var r;this.observers[t]=(r=this.observers[t])==null?void 0:r.filter(i=>i!==e);});n(this,"monitorLocalStorageChanges",t=>{this.monitors.has(t)||(this.monitors.add(t),this.localStore.subscribe(t,e=>{var c;let r=this.memoryStore.get(t),i=e instanceof Function?e(r):e;this.memoryStore.set(t,i),(c=this.observers[t])==null||c.forEach(p=>p(i));}));});}},m=new $;var X=class{constructor(){n(this,"timeout");n(this,"observers",[]);n(this,"set",(t,e)=>{this.timeout&&clearTimeout(this.timeout),this.observers.push(t),this.timeout=setTimeout(this.broadcast.bind(this),e);});n(this,"clear",t=>{this.observers=this.observers.filter(e=>e!==t);});n(this,"broadcast",()=>{this.observers.length!==0&&this.observers[0](void 0);});}};var Ot=(i=>(i.Load="loaded",i.ExitSuccess="exited.successful",i.ExitAbort="exited.aborted",i.ExitError="exited.errored",i))(Ot||{});var E=class extends ApolloLink{request(t,e){let r=m.get("session");return r?(t.setContext(({headers:i={}})=>({headers:k(l({},i),{authorization:`Bearer ${r}`})})),e(t)):(console.warn("QuilttLink attempted to send an unauthenticated Query"),null)}};var Z=new BatchHttpLink({uri:D,fetch:Rt});var tt=onError(({graphQLErrors:o,networkError:t})=>{o&&o.forEach(({message:e,locations:r,path:i})=>{console.log(`[GraphQL error]: Message: ${e}, Location: ${r}, Path: ${i}`);}),t&&(t.statusCode===401?(console.warn("[Authentication error]:",t),m.set("session",null)):console.warn("[Network error]:",t));});var et=new ApolloLink((o,t)=>t(o));var ot=new HttpLink({uri:D,fetch:Rt});var nt=new RetryLink({attempts:{retryIf:(o,t)=>!!o&&(!o.statusCode||o.statusCode>=500)}});var u={logger:typeof globalThis!="undefined"?globalThis.console:void 0,WebSocket:typeof globalThis!="undefined"?globalThis.WebSocket:void 0};var N=class{constructor(){n(this,"enabled",A);}log(...t){u.logger&&this.enabled&&(t.push(Date.now().toString()),u.logger.log("[ActionCable]",...t));}},Nt=new N,s=Nt;var v=()=>new Date().getTime(),R=o=>(v()-o)/1e3,S=class{constructor(t){this.visibilityDidChange=this.visibilityDidChange.bind(this),this.connection=t,this.reconnectAttempts=0;}start(){this.isRunning()||(this.startedAt=v(),delete this.stoppedAt,this.startPolling(),addEventListener("visibilitychange",this.visibilityDidChange),s.log(`ConnectionMonitor started. stale threshold = ${this.constructor.staleThreshold} s`));}stop(){this.isRunning()&&(this.stoppedAt=v(),this.stopPolling(),removeEventListener("visibilitychange",this.visibilityDidChange),s.log("ConnectionMonitor stopped"));}isRunning(){return this.startedAt&&!this.stoppedAt}recordPing(){this.pingedAt=v();}recordConnect(){this.reconnectAttempts=0,this.recordPing(),delete this.disconnectedAt,s.log("ConnectionMonitor recorded connect");}recordDisconnect(){this.disconnectedAt=v(),s.log("ConnectionMonitor recorded disconnect");}startPolling(){this.stopPolling(),this.poll();}stopPolling(){clearTimeout(this.pollTimeout);}poll(){this.pollTimeout=setTimeout(()=>{this.reconnectIfStale(),this.poll();},this.getPollInterval());}getPollInterval(){let{staleThreshold:t,reconnectionBackoffRate:e}=this.constructor,r=Math.pow(1+e,Math.min(this.reconnectAttempts,10)),c=(this.reconnectAttempts===0?1:e)*Math.random();return t*1e3*r*(1+c)}reconnectIfStale(){this.connectionIsStale()&&(s.log(`ConnectionMonitor detected stale connection. reconnectAttempts = ${this.reconnectAttempts}, time stale = ${R(this.refreshedAt)} s, stale threshold = ${this.constructor.staleThreshold} s`),this.reconnectAttempts++,this.disconnectedRecently()?s.log(`ConnectionMonitor skipping reopening recent disconnect. time disconnected = ${R(this.disconnectedAt)} s`):(s.log("ConnectionMonitor reopening"),this.connection.reopen()));}get refreshedAt(){return this.pingedAt?this.pingedAt:this.startedAt}connectionIsStale(){return R(this.refreshedAt)>this.constructor.staleThreshold}disconnectedRecently(){return this.disconnectedAt&&R(this.disconnectedAt)<this.constructor.staleThreshold}visibilityDidChange(){document.visibilityState==="visible"&&setTimeout(()=>{(this.connectionIsStale()||!this.connection.isOpen())&&(s.log(`ConnectionMonitor reopening stale connection on visibilitychange. visibilityState = ${document.visibilityState}`),this.connection.reopen());},200);}};S.staleThreshold=6;S.reconnectionBackoffRate=.15;var rt=S;var qt={message_types:{welcome:"welcome",disconnect:"disconnect",ping:"ping",confirmation:"confirm_subscription",rejection:"reject_subscription"},disconnect_reasons:{unauthorized:"unauthorized",invalid_request:"invalid_request",server_restart:"server_restart",remote:"remote"},default_mount_path:"/cable",protocols:["actioncable-v1-json","actioncable-unsupported"]},L=qt;var{message_types:x,protocols:q}=L,Kt=q.slice(0,q.length-1),it=[].indexOf,y=class{constructor(t){this.open=this.open.bind(this),this.consumer=t,this.subscriptions=this.consumer.subscriptions,this.monitor=new rt(this),this.disconnected=!0;}send(t){return this.isOpen()?(this.webSocket.send(JSON.stringify(t)),!0):!1}open(){if(this.isActive())return s.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`),!1;{let t=[...q,...this.consumer.subprotocols||[]];return s.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${t}`),this.webSocket&&this.uninstallEventHandlers(),this.webSocket=new u.WebSocket(this.consumer.url,t),this.installEventHandlers(),this.monitor.start(),!0}}close({allowReconnect:t}={allowReconnect:!0}){if(t||this.monitor.stop(),this.isOpen())return this.webSocket.close()}reopen(){if(s.log(`Reopening WebSocket, current state is ${this.getState()}`),this.isActive())try{return this.close()}catch(t){s.log("Failed to reopen WebSocket",t);}finally{s.log(`Reopening WebSocket in ${this.constructor.reopenDelay}ms`),setTimeout(this.open,this.constructor.reopenDelay);}else return this.open()}getProtocol(){if(this.webSocket)return this.webSocket.protocol}isOpen(){return this.isState("open")}isActive(){return this.isState("open","connecting")}triedToReconnect(){return this.monitor.reconnectAttempts>0}isProtocolSupported(){return it.call(Kt,this.getProtocol())>=0}isState(...t){return it.call(t,this.getState())>=0}getState(){if(this.webSocket){for(let t in u.WebSocket)if(u.WebSocket[t]===this.webSocket.readyState)return t.toLowerCase()}return null}installEventHandlers(){for(let t in this.events){let e=this.events[t].bind(this);this.webSocket[`on${t}`]=e;}}uninstallEventHandlers(){for(let t in this.events)this.webSocket[`on${t}`]=function(){};}};y.reopenDelay=500;y.prototype.events={message(o){if(!this.isProtocolSupported())return;let{identifier:t,message:e,reason:r,reconnect:i,type:c}=JSON.parse(o.data);switch(c){case x.welcome:return this.triedToReconnect()&&(this.reconnectAttempted=!0),this.monitor.recordConnect(),this.subscriptions.reload();case x.disconnect:return s.log(`Disconnecting. Reason: ${r}`),this.close({allowReconnect:i});case x.ping:return this.monitor.recordPing();case x.confirmation:return this.subscriptions.confirmSubscription(t),this.reconnectAttempted?(this.reconnectAttempted=!1,this.subscriptions.notify(t,"connected",{reconnected:!0})):this.subscriptions.notify(t,"connected",{reconnected:!1});case x.rejection:return this.subscriptions.reject(t);default:return this.subscriptions.notify(t,"received",e)}},open(){if(s.log(`WebSocket onopen event, using '${this.getProtocol()}' subprotocol`),this.disconnected=!1,!this.isProtocolSupported())return s.log("Protocol is unsupported. Stopping monitor and disconnecting."),this.close({allowReconnect:!1})},close(o){if(s.log("WebSocket onclose event"),!this.disconnected)return this.disconnected=!0,this.monitor.recordDisconnect(),this.subscriptions.notifyAll("disconnected",{willAttemptReconnect:this.monitor.isRunning()})},error(){s.log("WebSocket onerror event");}};var st=y;var jt=function(o,t){if(t!==null)for(let e in t){let r=t[e];o[e]=r;}return o},K=class{constructor(t,e={},r){n(this,"consumer");n(this,"identifier");this.consumer=t,this.identifier=JSON.stringify(e),jt(this,r);}perform(t,e={}){return e.action=t,this.send(e)}send(t){return this.consumer.send({command:"message",identifier:this.identifier,data:JSON.stringify(t)})}unsubscribe(){return this.consumer.subscriptions.remove(this)}},ct=K;var j=class{constructor(t){n(this,"subscriptions");n(this,"pendingSubscriptions");n(this,"retryTimeout");this.subscriptions=t,this.pendingSubscriptions=[];}guarantee(t){this.pendingSubscriptions.indexOf(t)==-1?(s.log(`SubscriptionGuarantor guaranteeing ${t.identifier}`),this.pendingSubscriptions.push(t)):s.log(`SubscriptionGuarantor already guaranteeing ${t.identifier}`),this.startGuaranteeing();}forget(t){s.log(`SubscriptionGuarantor forgetting ${t.identifier}`),this.pendingSubscriptions=this.pendingSubscriptions.filter(e=>e!==t);}startGuaranteeing(){this.stopGuaranteeing(),this.retrySubscribing();}stopGuaranteeing(){clearTimeout(this.retryTimeout);}retrySubscribing(){this.retryTimeout=setTimeout(()=>{this.subscriptions&&typeof this.subscriptions.subscribe=="function"&&this.pendingSubscriptions.map(t=>{s.log(`SubscriptionGuarantor resubscribing ${t.identifier}`),this.subscriptions.subscribe(t);});},500);}},at=j;var _=class{constructor(t){n(this,"consumer");n(this,"guarantor");n(this,"subscriptions");this.consumer=t,this.guarantor=new at(this),this.subscriptions=[];}create(t,e){let r=t,i=typeof r=="object"?r:{channel:r},c=new ct(this.consumer,i,e);return this.add(c)}add(t){return this.subscriptions.push(t),this.consumer.ensureActiveConnection(),this.notify(t,"initialized"),this.subscribe(t),t}remove(t){return this.forget(t),this.findAll(t.identifier).length||this.sendCommand(t,"unsubscribe"),t}reject(t){return this.findAll(t).map(e=>(this.forget(e),this.notify(e,"rejected"),e))}forget(t){return this.guarantor.forget(t),this.subscriptions=this.subscriptions.filter(e=>e!==t),t}findAll(t){return this.subscriptions.filter(e=>e.identifier===t)}reload(){return this.subscriptions.map(t=>this.subscribe(t))}notifyAll(t,...e){return this.subscriptions.map(r=>this.notify(r,t,...e))}notify(t,e,...r){let i;return typeof t=="string"?i=this.findAll(t):i=[t],i.map(c=>typeof c[e]=="function"?c[e](...r):void 0)}subscribe(t){this.sendCommand(t,"subscribe")&&this.guarantor.guarantee(t);}confirmSubscription(t){s.log(`Subscription confirmed ${t}`),this.findAll(t).map(e=>this.guarantor.forget(e));}sendCommand(t,e){let{identifier:r}=t;return this.consumer.send({command:e,identifier:r})}},lt=_;var G=class{constructor(t){n(this,"_url");n(this,"subscriptions");n(this,"connection");n(this,"subprotocols");this._url=t,this.subscriptions=new lt(this),this.connection=new st(this),this.subprotocols=[];}get url(){return pt(this._url)}send(t){return this.connection.send(t)}connect(){return this.connection.open()}disconnect(){return this.connection.close({allowReconnect:!1})}ensureActiveConnection(){if(!this.connection.isActive())return this.connection.open()}addSubProtocol(t){this.subprotocols=[...this.subprotocols,t];}};function pt(o){if(typeof o=="function"&&(o=o()),o&&!/^wss?:/i.test(o)){let t=document.createElement("a");return t.href=o,t.href=t.href,t.protocol=t.protocol.replace("http","ws"),t.href}else return o}var ut=G;function dt(o=_t("url")||L.default_mount_path){return new ut(o)}function _t(o){let t=document.head.querySelector(`meta[name='action-cable-${o}']`);if(t)return t.getAttribute("content")}var U=class extends ApolloLink$1{constructor(e){super();n(this,"cables");n(this,"channelName");n(this,"actionName");n(this,"connectionParams");this.cables={},this.channelName=e.channelName||"GraphqlChannel",this.actionName=e.actionName||"execute",this.connectionParams=e.connectionParams||{};}request(e,r){let i=m.get("session");return i?(this.cables[i]||(this.cables[i]=dt(Y+(i?`?token=${i}`:""))),new Observable(c=>{let p=Math.round(Date.now()+Math.random()*1e5).toString(16),b=this.actionName,h=typeof this.connectionParams=="function"?this.connectionParams(e):this.connectionParams,C=this.cables[i].subscriptions.create(Object.assign({},{channel:this.channelName,channelId:p},h),{connected:()=>{C.perform(b,{query:e.query?print(e.query):null,variables:e.variables,operationId:e.operationId,operationName:e.operationName});},received:a=>{var W,Q;((W=a==null?void 0:a.result)!=null&&W.data||(Q=a==null?void 0:a.result)!=null&&Q.errors)&&c.next(a.result),a.more||c.complete();}});return Object.assign(C,{closed:!1})})):(console.warn("QuilttClient attempted to send an unauthenticated Subscription"),null)}},ht=U;var I=class extends ht{constructor(){super({channelName:"GraphQLChannel"});}};var Do=new ApolloLink(()=>null);var mt=new ApolloLink((o,t)=>(o.setContext(({headers:e={}})=>({headers:k(l({},e),{"Quiltt-Client-Version":F})})),t(o)));var bt=class extends ApolloClient{constructor(t){t.connectToDevTools||(t.connectToDevTools=A);let e=b=>b.query.definitions.some(({kind:h,operation:C})=>h==="OperationDefinition"&&C==="subscription"),r=b=>{var h;return (h=b.getContext().batchable)!=null?h:!0},i=new E,c=new I,p=ApolloLink.from([mt,i,tt,nt]).split(e,c,et).split(r,Z,ot);super(l({link:p},t));}};var Vt=150,ft=10,d=zt.create();d.interceptors.response.use(void 0,o=>{let{config:t,message:e,response:r}=o,i=e.toLowerCase();if(!t||!t.retry||!(i.includes("timeout")||i.includes("network error")||(r==null?void 0:r.status)===429))return Promise.reject(o);if(t.retriesRemaining===void 0)t.retriesRemaining=ft-1;else {if(t.retriesRemaining===1)return Promise.reject(o);t.retriesRemaining-=1;}return new Promise(p=>{setTimeout(()=>p(),Vt*(ft-t.retriesRemaining));}).then(()=>d(t))});var Ft=(e=>(e.Email="email",e.Phone="phone",e))(Ft||{}),gt=class{constructor(t){n(this,"clientId");n(this,"ping",t=>d.get(f,this.config(t)));n(this,"identify",t=>d.post(f,this.body(t),this.config()));n(this,"authenticate",t=>d.put(f,this.body(t),this.config()));n(this,"revoke",t=>d.delete(f,this.config(t)));n(this,"config",t=>{let e={"Content-Type":"application/json",Accept:"application/json"};return t&&(e.Authorization=`Bearer ${t}`),{headers:e,validateStatus:this.validateStatus,retry:!0}});n(this,"validateStatus",t=>t<500);n(this,"body",t=>(this.clientId||console.error("Quiltt Client ID is not set. Unable to identify & authenticate"),{session:l({clientId:this.clientId},t)}));this.clientId=t;}};
11
+ const MATCHER = /^(?:[\w-]+\.){2}[\w-]+$/;
12
+ const JsonWebTokenParse = (token)=>{
13
+ if (typeof token === 'undefined' || token === null) return token;
14
+ if (!MATCHER.test(token)) {
15
+ console.error(`Invalid Session Token: ${token}`);
16
+ return;
17
+ }
18
+ const [_header, payload, _signature] = token.split('.');
19
+ try {
20
+ return {
21
+ token: token,
22
+ claims: JSON.parse(atob(payload))
23
+ };
24
+ } catch (error) {
25
+ console.error(`Invalid Session Token: ${error}`);
26
+ }
27
+ };
13
28
 
14
- export { gt as AuthAPI, E as AuthLink, Ft as AuthStrategies, Z as BatchHttpLink, Ot as ConnectorSDKEventType, tt as ErrorLink, et as ForwardableLink, m as GlobalStorage, ot as HttpLink, oe as JsonWebTokenParse, O as LocalStorage, w as MemoryStorage, g as Observable, bt as QuilttClient, nt as RetryLink, $ as Storage, I as SubscriptionLink, Do as TerminatingLink, X as Timeoutable, mt as VersionLink, te as cdnBase, A as debugging, f as endpointAuth, D as endpointGraphQL, Y as endpointWebsockets, F as version };
15
- //# sourceMappingURL=out.js.map
16
- //# sourceMappingURL=index.js.map
29
+ /**
30
+ * This is designed to support singletons to timeouts that can broadcast
31
+ * to any observers, preventing race conditions with multiple timeouts.
32
+ */ class Timeoutable {
33
+ constructor(){
34
+ this.observers = [];
35
+ this.set = (callback, delay)=>{
36
+ if (this.timeout) {
37
+ clearTimeout(this.timeout);
38
+ }
39
+ this.observers.push(callback);
40
+ this.timeout = setTimeout(this.broadcast.bind(this), delay);
41
+ };
42
+ this.clear = (observer)=>{
43
+ this.observers = this.observers.filter((callback)=>callback !== observer);
44
+ };
45
+ // Only sends to the 1st listener, but ensures that someone is notified
46
+ this.broadcast = ()=>{
47
+ if (this.observers.length === 0) return;
48
+ this.observers[0](undefined);
49
+ };
50
+ }
51
+ }
52
+
53
+ var ConnectorSDKEventType;
54
+ (function(ConnectorSDKEventType) {
55
+ ConnectorSDKEventType["Load"] = "loaded";
56
+ ConnectorSDKEventType["ExitSuccess"] = "exited.successful";
57
+ ConnectorSDKEventType["ExitAbort"] = "exited.aborted";
58
+ ConnectorSDKEventType["ExitError"] = "exited.errored";
59
+ })(ConnectorSDKEventType || (ConnectorSDKEventType = {}));
60
+
61
+ /**
62
+ * unauthorizedCallback only triggers in the event the token is present, and
63
+ * returns the token; This allows sessions to be forgotten without race conditions
64
+ * causing null sessions to kill valid sessions, or invalid sessions for killing
65
+ * valid sessions during rotation and networking weirdness.
66
+ */ class AuthLink extends ApolloLink {
67
+ request(operation, forward) {
68
+ const token = GlobalStorage.get('session');
69
+ if (!token) {
70
+ console.warn(`QuilttLink attempted to send an unauthenticated Query`);
71
+ return null;
72
+ }
73
+ operation.setContext(({ headers = {} })=>({
74
+ headers: {
75
+ ...headers,
76
+ authorization: `Bearer ${token}`
77
+ }
78
+ }));
79
+ return forward(operation);
80
+ }
81
+ }
82
+
83
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
84
+ const effectiveFetch$2 = typeof fetch === 'undefined' ? crossfetch : fetch;
85
+ const BatchHttpLink = new BatchHttpLink$1({
86
+ uri: endpointGraphQL,
87
+ fetch: effectiveFetch$2
88
+ });
89
+
90
+ const ErrorLink = onError(({ graphQLErrors, networkError })=>{
91
+ if (graphQLErrors) {
92
+ graphQLErrors.forEach(({ message, locations, path })=>{
93
+ console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`);
94
+ });
95
+ }
96
+ if (networkError) {
97
+ if (networkError.statusCode === 401) {
98
+ console.warn('[Authentication error]:', networkError);
99
+ GlobalStorage.set('session', null);
100
+ } else {
101
+ console.warn('[Network error]:', networkError);
102
+ }
103
+ }
104
+ });
105
+
106
+ const ForwardableLink = new ApolloLink((operation, forward)=>forward(operation));
107
+
108
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
109
+ const effectiveFetch$1 = typeof fetch === 'undefined' ? crossfetch : fetch;
110
+ const HttpLink = new HttpLink$1({
111
+ uri: endpointGraphQL,
112
+ fetch: effectiveFetch$1
113
+ });
114
+
115
+ const RetryLink = new RetryLink$1({
116
+ attempts: {
117
+ retryIf: (error, _operation)=>!!error && (!error.statusCode || error.statusCode >= 500)
118
+ }
119
+ });
120
+
121
+ const TerminatingLink = new ApolloLink(()=>null);
122
+
123
+ const VersionLink = new ApolloLink((operation, forward)=>{
124
+ operation.setContext(({ headers = {} })=>({
125
+ headers: {
126
+ ...headers,
127
+ 'Quiltt-Client-Version': version
128
+ }
129
+ }));
130
+ return forward(operation);
131
+ });
132
+
133
+ class QuilttClient extends ApolloClient {
134
+ constructor(options){
135
+ if (!options.connectToDevTools) options.connectToDevTools = debugging;
136
+ const isSubscriptionOperation = (operation)=>{
137
+ return operation.query.definitions.some(// @ts-ignore
138
+ ({ kind, operation })=>kind === 'OperationDefinition' && operation === 'subscription');
139
+ };
140
+ const isBatchable = (operation)=>{
141
+ return operation.getContext().batchable ?? true;
142
+ };
143
+ const authLink = new AuthLink();
144
+ const subscriptionsLink = new SubscriptionLink();
145
+ const quilttLink = ApolloLink.from([
146
+ VersionLink,
147
+ authLink,
148
+ ErrorLink,
149
+ RetryLink
150
+ ]).split(isSubscriptionOperation, subscriptionsLink, ForwardableLink).split(isBatchable, BatchHttpLink, HttpLink);
151
+ super({
152
+ link: quilttLink,
153
+ ...options
154
+ });
155
+ }
156
+ }
157
+
158
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
159
+ const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch;
160
+ const RETRY_DELAY = 150 // ms
161
+ ;
162
+ const RETRIES = 10 // 150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500 = 8.250s
163
+ ;
164
+ /**
165
+ * A wrapper around the native `fetch` function that adds automatic retries on failure, including network errors and HTTP 429 responses.
166
+ * Now treats any response with status < 500 as valid.
167
+ */ const fetchWithRetry = async (url, options = {
168
+ retry: false
169
+ })=>{
170
+ const { retry, retriesRemaining, validateStatus = (status)=>status >= 200 && status < 300, ...fetchOptions } = options;
171
+ try {
172
+ const response = await effectiveFetch(url, fetchOptions);
173
+ const isResponseOk = validateStatus(response.status);
174
+ if (isResponseOk) {
175
+ return {
176
+ data: await response.json().catch(()=>null),
177
+ status: response.status,
178
+ statusText: response.statusText,
179
+ headers: response.headers,
180
+ ok: isResponseOk
181
+ };
182
+ }
183
+ // If validateStatus fails, and retry is enabled, prepare to retry for eligible status codes
184
+ if (retry && (response.status >= 500 || response.status === 429)) {
185
+ throw new Error('Retryable failure');
186
+ }
187
+ throw new Error('HTTP error with status ' + response.status);
188
+ } catch (error) {
189
+ if (retry) {
190
+ const currentRetriesRemaining = retriesRemaining !== undefined ? retriesRemaining : RETRIES;
191
+ if (currentRetriesRemaining > 0) {
192
+ const delayTime = RETRY_DELAY * (RETRIES - currentRetriesRemaining);
193
+ await new Promise((resolve)=>setTimeout(resolve, delayTime));
194
+ return fetchWithRetry(url, {
195
+ ...options,
196
+ retriesRemaining: currentRetriesRemaining - 1
197
+ });
198
+ }
199
+ }
200
+ return Promise.reject(error);
201
+ }
202
+ };
203
+
204
+ var AuthStrategies;
205
+ (function(AuthStrategies) {
206
+ AuthStrategies["Email"] = "email";
207
+ AuthStrategies["Phone"] = "phone";
208
+ })(AuthStrategies || (AuthStrategies = {}));
209
+ // https://www.quiltt.dev/api-reference/rest/auth#
210
+ class AuthAPI {
211
+ constructor(clientId){
212
+ /**
213
+ * Response Statuses:
214
+ * - 200: OK -> Session is Valid
215
+ * - 401: Unauthorized -> Session is Invalid
216
+ */ this.ping = async (token)=>{
217
+ const response = await fetchWithRetry(endpointAuth, {
218
+ method: 'GET',
219
+ ...this.config(token)
220
+ });
221
+ return response;
222
+ };
223
+ /**
224
+ * Response Statuses:
225
+ * - 201: Created -> Profile Created, New Session Returned
226
+ * - 202: Accepted -> Profile Found, MFA Code Sent for `authenticate`
227
+ * - 422: Unprocessable Entity -> Invalid Payload
228
+ */ this.identify = async (payload)=>{
229
+ const response = await fetchWithRetry(endpointAuth, {
230
+ method: 'POST',
231
+ body: JSON.stringify(this.body(payload)),
232
+ ...this.config()
233
+ });
234
+ return response;
235
+ };
236
+ /**
237
+ * Response Statuses:
238
+ * - 201: Created -> MFA Validated, New Session Returned
239
+ * - 401: Unauthorized -> MFA Invalid
240
+ * - 422: Unprocessable Entity -> Invalid Payload
241
+ */ this.authenticate = async (payload)=>{
242
+ const response = await fetchWithRetry(endpointAuth, {
243
+ method: 'PUT',
244
+ body: JSON.stringify(this.body(payload)),
245
+ ...this.config()
246
+ });
247
+ return response;
248
+ };
249
+ /**
250
+ * Response Statuses:
251
+ * - 204: No Content -> Session Revoked
252
+ * - 401: Unauthorized -> Session Not Found
253
+ */ this.revoke = async (token)=>{
254
+ const response = await fetchWithRetry(endpointAuth, {
255
+ method: 'DELETE',
256
+ ...this.config(token)
257
+ });
258
+ return response;
259
+ };
260
+ this.config = (token)=>{
261
+ const headers = new Headers();
262
+ headers.set('Content-Type', 'application/json');
263
+ headers.set('Accept', 'application/json');
264
+ if (token) {
265
+ headers.set('Authorization', `Bearer ${token}`);
266
+ }
267
+ return {
268
+ headers,
269
+ validateStatus: this.validateStatus,
270
+ retry: true
271
+ };
272
+ };
273
+ this.validateStatus = (status)=>status < 500 && status !== 429;
274
+ this.body = (payload)=>{
275
+ if (!this.clientId) {
276
+ console.error('Quiltt Client ID is not set. Unable to identify & authenticate');
277
+ }
278
+ return {
279
+ session: {
280
+ clientId: this.clientId,
281
+ ...payload
282
+ }
283
+ };
284
+ };
285
+ this.clientId = clientId;
286
+ }
287
+ }
288
+
289
+ export { AuthAPI, AuthLink, AuthStrategies, BatchHttpLink, ConnectorSDKEventType, ErrorLink, ForwardableLink, GlobalStorage, HttpLink, JsonWebTokenParse, QuilttClient, RetryLink, SubscriptionLink, TerminatingLink, Timeoutable, VersionLink, debugging, endpointAuth, endpointGraphQL, version };
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@quiltt/core",
3
- "version": "3.5.4",
3
+ "version": "3.5.6",
4
4
  "description": "Javascript API client and utilities for Quiltt",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/quiltt/quiltt-public.git",
8
- "directory": "packages/core"
9
- },
10
- "homepage": "https://github.com/quiltt/quiltt-public/tree/main/packages/core#readme",
11
5
  "keywords": [
12
6
  "quiltt",
13
7
  "typescript"
14
8
  ],
9
+ "homepage": "https://github.com/quiltt/quiltt-js/tree/main/packages/core#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/quiltt/quiltt-js.git",
13
+ "directory": "packages/core"
14
+ },
15
15
  "license": "MIT",
16
16
  "sideEffects": [
17
17
  "./src/Storage/Local.ts",
@@ -21,11 +21,10 @@
21
21
  "exports": {
22
22
  ".": {
23
23
  "import": "./dist/index.js",
24
- "require": "./dist/index.cjs",
25
24
  "types": "./dist/index.d.ts"
26
25
  }
27
26
  },
28
- "main": "./dist/index.cjs",
27
+ "main": "./dist/index.js",
29
28
  "module": "./dist/index.js",
30
29
  "types": "./dist/index.d.ts",
31
30
  "files": [
@@ -34,32 +33,32 @@
34
33
  "CHANGELOG.md"
35
34
  ],
36
35
  "dependencies": {
37
- "@apollo/client": "^3.7.16",
38
- "axios": "^1.6.0",
39
- "cross-fetch": "^3.1.8",
36
+ "@apollo/client": "^3.9.9",
37
+ "cross-fetch": "^4.0.0",
40
38
  "graphql": "^16.8.1",
41
- "graphql-ruby-client": "^1.11.8"
39
+ "graphql-ruby-client": "^1.13.3"
42
40
  },
43
41
  "devDependencies": {
44
42
  "@trivago/prettier-plugin-sort-imports": "4.1.1",
45
- "@types/node": "20.11.4",
46
- "@types/react": "18.2.7",
43
+ "@types/node": "20.12.2",
44
+ "@types/react": "18.2.73",
47
45
  "@typescript-eslint/eslint-plugin": "5.60.1",
48
46
  "@typescript-eslint/parser": "5.60.1",
47
+ "bunchee": "4.4.8",
49
48
  "eslint": "8.43.0",
50
49
  "eslint-config-prettier": "8.8.0",
51
50
  "eslint-plugin-prettier": "4.2.1",
52
51
  "prettier": "2.8.8",
53
- "tsup": "6.7.0",
54
- "typescript": "5.1.3"
52
+ "rimraf": "5.0.5",
53
+ "typescript": "5.4.3"
55
54
  },
56
55
  "publishConfig": {
57
56
  "access": "public"
58
57
  },
59
58
  "scripts": {
60
- "build": "tsup",
59
+ "build": "bunchee",
61
60
  "clean": "rimraf .turbo dist",
62
- "dev": "tsup --watch",
61
+ "dev": "bunchee --watch",
63
62
  "lint": "TIMING=1 eslint --ext .js,.jsx,.ts,.tsx src/ --fix",
64
63
  "typecheck": "tsc --project tsconfig.json --noEmit"
65
64
  }
@@ -1,12 +1,14 @@
1
1
  import { BatchHttpLink as ApolloHttpLink } from '@apollo/client/link/batch-http/index.js'
2
+ import crossfetch from 'cross-fetch'
2
3
 
3
- import fetch from 'cross-fetch'
4
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
5
+ const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch
4
6
 
5
7
  import { endpointGraphQL } from '../../../configuration'
6
8
 
7
9
  export const BatchHttpLink = new ApolloHttpLink({
8
10
  uri: endpointGraphQL,
9
- fetch,
11
+ fetch: effectiveFetch,
10
12
  })
11
13
 
12
14
  export default BatchHttpLink
@@ -1,12 +1,14 @@
1
1
  import { HttpLink as ApolloHttpLink } from '@apollo/client/link/http/index.js'
2
+ import crossfetch from 'cross-fetch'
2
3
 
3
- import fetch from 'cross-fetch'
4
+ // Use `cross-fetch` only if `fetch` is not available on the `globalThis` object
5
+ const effectiveFetch = typeof fetch === 'undefined' ? crossfetch : fetch
4
6
 
5
7
  import { endpointGraphQL } from '../../../configuration'
6
8
 
7
9
  export const HttpLink = new ApolloHttpLink({
8
10
  uri: endpointGraphQL,
9
- fetch,
11
+ fetch: effectiveFetch,
10
12
  })
11
13
 
12
14
  export default HttpLink
@@ -2,7 +2,9 @@ import { debugging } from '../../../../configuration'
2
2
  import adapters from './adapters'
3
3
 
4
4
  class Logger {
5
- enabled = debugging
5
+ get enabled() {
6
+ return debugging
7
+ }
6
8
 
7
9
  log(...messages: Array<string>) {
8
10
  if (adapters.logger && this.enabled) {
@@ -16,7 +16,7 @@ class SubscriptionGuarantor {
16
16
  }
17
17
 
18
18
  guarantee(subscription: Subscription) {
19
- if (this.pendingSubscriptions.indexOf(subscription) == -1) {
19
+ if (this.pendingSubscriptions.indexOf(subscription) === -1) {
20
20
  logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`)
21
21
  this.pendingSubscriptions.push(subscription)
22
22
  } else {
@@ -1,7 +1,6 @@
1
- import type { AxiosRequestConfig, AxiosResponse } from './axios'
2
- import { axios } from './axios'
3
-
4
- import { endpointAuth } from '../../configuration'
1
+ import { endpointAuth } from '@/configuration'
2
+ import type { FetchResponse } from './fetchWithRetry'
3
+ import { fetchWithRetry } from './fetchWithRetry'
5
4
 
6
5
  export enum AuthStrategies {
7
6
  Email = 'email',
@@ -22,7 +21,7 @@ export type UsernamePayload = EmailInput | PhoneInput
22
21
  export type PasscodePayload = UsernamePayload & { passcode: string }
23
22
 
24
23
  type SessionData = { token: string }
25
- type NoContentData = void
24
+ type NoContentData = null
26
25
  type UnauthorizedData = { message: string; instruction: string }
27
26
  export type UnprocessableData = { [attribute: string]: Array<string> }
28
27
 
@@ -31,8 +30,8 @@ type Identify = SessionData | NoContentData | UnprocessableData
31
30
  type Authenticate = SessionData | UnauthorizedData | UnprocessableData
32
31
  type Revoke = NoContentData | UnauthorizedData
33
32
 
34
- export type SessionResponse = AxiosResponse<SessionData>
35
- export type UnprocessableResponse = AxiosResponse<UnprocessableData>
33
+ export type SessionResponse = FetchResponse<SessionData>
34
+ export type UnprocessableResponse = FetchResponse<UnprocessableData>
36
35
 
37
36
  // https://www.quiltt.dev/api-reference/rest/auth#
38
37
  export class AuthAPI {
@@ -47,8 +46,12 @@ export class AuthAPI {
47
46
  * - 200: OK -> Session is Valid
48
47
  * - 401: Unauthorized -> Session is Invalid
49
48
  */
50
- ping = (token: string) => {
51
- return axios.get<Ping>(endpointAuth, this.config(token))
49
+ ping = async (token: string) => {
50
+ const response = await fetchWithRetry<Ping>(endpointAuth, {
51
+ method: 'GET',
52
+ ...this.config(token),
53
+ })
54
+ return response
52
55
  }
53
56
 
54
57
  /**
@@ -57,8 +60,13 @@ export class AuthAPI {
57
60
  * - 202: Accepted -> Profile Found, MFA Code Sent for `authenticate`
58
61
  * - 422: Unprocessable Entity -> Invalid Payload
59
62
  */
60
- identify = (payload: UsernamePayload) => {
61
- return axios.post<Identify>(endpointAuth, this.body(payload), this.config())
63
+ identify = async (payload: UsernamePayload) => {
64
+ const response = await fetchWithRetry<Identify>(endpointAuth, {
65
+ method: 'POST',
66
+ body: JSON.stringify(this.body(payload)),
67
+ ...this.config(),
68
+ })
69
+ return response
62
70
  }
63
71
 
64
72
  /**
@@ -67,8 +75,13 @@ export class AuthAPI {
67
75
  * - 401: Unauthorized -> MFA Invalid
68
76
  * - 422: Unprocessable Entity -> Invalid Payload
69
77
  */
70
- authenticate = (payload: PasscodePayload) => {
71
- return axios.put<Authenticate>(endpointAuth, this.body(payload), this.config())
78
+ authenticate = async (payload: PasscodePayload): Promise<FetchResponse<Authenticate>> => {
79
+ const response = await fetchWithRetry<Authenticate>(endpointAuth, {
80
+ method: 'PUT',
81
+ body: JSON.stringify(this.body(payload)),
82
+ ...this.config(),
83
+ })
84
+ return response
72
85
  }
73
86
 
74
87
  /**
@@ -76,28 +89,30 @@ export class AuthAPI {
76
89
  * - 204: No Content -> Session Revoked
77
90
  * - 401: Unauthorized -> Session Not Found
78
91
  */
79
- revoke = (token: string) => {
80
- return axios.delete<Revoke>(endpointAuth, this.config(token))
92
+ revoke = async (token: string): Promise<FetchResponse<Revoke>> => {
93
+ const response = await fetchWithRetry<Revoke>(endpointAuth, {
94
+ method: 'DELETE',
95
+ ...this.config(token),
96
+ })
97
+ return response
81
98
  }
82
99
 
83
- private config = (token?: string): AxiosRequestConfig => {
84
- const headers: { [id: string]: string } = {
85
- 'Content-Type': 'application/json',
86
- Accept: 'application/json',
87
- }
100
+ private config = (token?: string) => {
101
+ const headers = new Headers()
102
+ headers.set('Content-Type', 'application/json')
103
+ headers.set('Accept', 'application/json')
88
104
 
89
105
  if (token) {
90
- headers.Authorization = `Bearer ${token}`
106
+ headers.set('Authorization', `Bearer ${token}`)
91
107
  }
92
108
 
93
109
  return {
94
- headers: headers,
110
+ headers,
95
111
  validateStatus: this.validateStatus,
96
112
  retry: true,
97
113
  }
98
114
  }
99
-
100
- private validateStatus = (status: number) => status < 500
115
+ private validateStatus = (status: number) => status < 500 && status !== 429
101
116
 
102
117
  private body = (payload: any) => {
103
118
  if (!this.clientId) {
@@ -112,5 +127,3 @@ export class AuthAPI {
112
127
  }
113
128
  }
114
129
  }
115
-
116
- export default AuthAPI