UNPKG

@directus/sdk

Version:

Directus JavaScript SDK

2 lines 5.67 kB
import{queryToParams as e}from"../utils/query-to-params.js";import{auth as t}from"./commands/auth.js";import{pong as n}from"./commands/pong.js";import{generateUid as r}from"./utils/generate-uid.js";import{messageCallback as i}from"./utils/message-callback.js";const a={authMode:`handshake`,heartbeat:!0,debug:!1,connect:{timeout:1e4},reconnect:{delay:1e3,retries:10}};function o(o={}){return s=>{o={...a,...o};let c=r(),l={code:`closed`},u={attempts:0,active:!1},d=!1,f=new Set,p=e=>`getToken`in e,m=(e,...t)=>o.debug&&s.globals.logger[e](`[Directus SDK]`,...t),h=async(e,t)=>{let n=new s.globals.URL(e);if(o.authMode===`strict`&&p(t)){let e=await t.getToken();e&&n.searchParams.set(`access_token`,e)}return n.toString()},g=async e=>{if(`url`in o)return await h(o.url,e);if([`ws:`,`wss:`].includes(s.url.protocol))return await h(s.url,e);let t=new s.globals.URL(s.url.toString());return t.protocol=s.url.protocol===`https:`?`wss:`:`ws:`,t.pathname=`/websocket`,await h(t,e)},_=e=>{let t=new Promise((t,n)=>{if(!o.reconnect||d)return n();if(m(`info`,`reconnect #${u.attempts} `+(u.attempts>=o.reconnect.retries?`maximum retries reached`:`trying again in ${Math.max(100,o.reconnect.delay)}ms`)),u.active)return u.active;if(u.attempts>=o.reconnect.retries)return u.attempts=-1,n();setTimeout(()=>e.connect().then(t=>(f.forEach(t=>{e.sendMessage(t)}),t)).then(t).catch(n),Math.max(100,o.reconnect.delay))});u.attempts+=1,u.active=t.catch(()=>{}).finally(()=>{u.active=!1})},v={open:new Set([]),error:new Set([]),close:new Set([]),message:new Set([])};function y(e){return`type`in e&&`status`in e&&`error`in e&&`code`in e.error&&`message`in e.error&&e.type===`auth`&&e.status===`error`}async function b(e,n){if(l.code===`open`){if(e.error.code===`TOKEN_EXPIRED`&&(m(`warn`,`Authentication token expired!`),p(n))){let e=await n.getToken();if(!e)throw Error(`No token for re-authenticating the websocket`);l.connection.send(t({access_token:e}))}if(e.error.code===`AUTH_TIMEOUT`)return l.firstMessage&&o.authMode===`public`?(m(`warn`,`Authentication failed! Currently the "authMode" is "public" try using "handshake" instead`),o.reconnect=!1):m(`warn`,`Authentication timed out!`),l.connection.close();if(e.error.code===`AUTH_FAILED`){if(l.firstMessage&&o.authMode===`public`)return m(`warn`,`Authentication failed! Currently the "authMode" is "public" try using "handshake" instead`),o.reconnect=!1,l.connection.close();m(`warn`,`Authentication failed!`)}}}let x=async e=>{for(;l.code===`open`;){let t=await i(l.connection).catch(()=>{});if(t){if(y(t)){await b(t,e),l.firstMessage=!1;continue}if(o.heartbeat&&t.type===`ping`){l.connection.send(n()),l.firstMessage=!1;continue}v.message.forEach(e=>{l.code===`open`&&e.call(l.connection,t)}),l.firstMessage=!1}}};return{async isConnected(){if(l.code===`connecting`)try{await l.connection}catch{return!1}return l.code===`open`},async connect(){if(d=!1,l.code===`connecting`)return await l.connection;if(l.code!==`closed`)throw Error(`Cannot connect when state is "${l.code}"`);let e,n,a=new Promise((t,r)=>{e=t,n=r});l={code:`connecting`,connection:a};let f=this,h;try{let e=await g(f);m(`info`,`Connecting to ${e}...`),h=new s.globals.WebSocket(e)}catch(e){throw l={code:`closed`},n(e),e}let y=!1,b;return o.connect&&(b=setTimeout(()=>{n(`Connection attempt timed out.`)},o.connect.timeout??1e4)),h.addEventListener(`open`,async r=>{if(m(`info`,`Connection open.`),l={code:`open`,connection:h,firstMessage:!0},u.attempts=0,u.active=!1,clearTimeout(b),x(f),o.authMode===`handshake`&&p(f)){let e=await f.getToken();if(!e)return n(`No token for authenticating the websocket. Make sure to provide one or call the login() function beforehand.`);h.send(t({access_token:e}));let r=await i(h);if(r&&`type`in r&&`status`in r&&r.type===`auth`&&r.status===`ok`)m(`info`,`Authentication successful!`);else return n(`Authentication failed while opening websocket connection`)}v.open.forEach(e=>e.call(h,r)),y=!0,e(h)}),h.addEventListener(`error`,e=>{m(`warn`,`Connection errored.`),v.error.forEach(t=>t.call(h,e)),h.close(),l={code:`error`},y||n(e)}),h.addEventListener(`close`,e=>{m(`info`,`Connection closed.`),v.close.forEach(t=>t.call(h,e)),c=r(),l={code:`closed`},_(this),y||n(e)}),a},disconnect(){d=!0,l.code===`open`&&l.connection.close()},onWebSocket(e,t){if(e===`message`){let n=function(e){if(typeof e.data!=`string`)return t.call(this,e);try{return t.call(this,JSON.parse(e.data))}catch{return t.call(this,e)}};return v[e].add(n),()=>v[e].delete(n)}return v[e].add(t),()=>v[e].delete(t)},sendMessage(e){if(l.code!==`open`)throw Error(`Cannot send messages without an open connection. Make sure you are calling "await client.connect()".`);if(typeof e==`string`)return l.connection.send(e);`uid`in e||(e.uid=c.next().value),l.connection.send(JSON.stringify(e))},async subscribe(t,n={}){`uid`in n||(n.uid=c.next().value),n.query&&=e(n.query),f.add({...n,collection:t,type:`subscribe`}),l.code!==`open`&&(m(`info`,`No connection available for subscribing!`),await this.connect()),this.sendMessage({...n,collection:t,type:`subscribe`});let r=!0;async function*a(){for(;r&&l.code===`open`;){let e=await i(l.connection).catch(()=>{});if(e){if(`type`in e&&`status`in e&&e.type===`subscribe`&&e.status===`error`)throw e;`type`in e&&`uid`in e&&e.type===`subscription`&&e.uid===n.uid&&(yield e)}}o.reconnect&&u.active&&(await u.active,l.code===`open`&&(l.connection.send(JSON.stringify({...n,collection:t,type:`subscribe`})),yield*a()))}return{subscription:a(),unsubscribe:()=>{f.delete({...n,collection:t,type:`subscribe`}),this.sendMessage({uid:n.uid,type:`unsubscribe`}),r=!1}}}}}}export{o as realtime}; //# sourceMappingURL=composable.js.map