@vielzeug/pulse 26.10.14 → 26.10.16
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.
- package/dist/_connection.cjs +1 -1
- package/dist/_connection.cjs.map +1 -1
- package/dist/_connection.js +98 -1
- package/dist/_connection.js.map +1 -1
- package/dist/_store.cjs.map +1 -1
- package/dist/_store.js +23 -1
- package/dist/_store.js.map +1 -1
- package/dist/_utils.cjs.map +1 -1
- package/dist/_utils.js +24 -1
- package/dist/_utils.js.map +1 -1
- package/dist/_wait.cjs.map +1 -1
- package/dist/_wait.js +27 -1
- package/dist/_wait.js.map +1 -1
- package/dist/channel.cjs.map +1 -1
- package/dist/channel.js +56 -1
- package/dist/channel.js.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +37 -1
- package/dist/errors.js.map +1 -1
- package/dist/heartbeat.cjs.map +1 -1
- package/dist/heartbeat.js +35 -1
- package/dist/heartbeat.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/protocol.cjs.map +1 -1
- package/dist/protocol.js +121 -1
- package/dist/protocol.js.map +1 -1
- package/dist/pulse.cjs +1 -1
- package/dist/pulse.cjs.map +1 -1
- package/dist/pulse.iife.js +1 -1
- package/dist/pulse.iife.js.map +1 -1
- package/dist/pulse.js +855 -1
- package/dist/pulse.js.map +1 -1
- package/dist/room.cjs +1 -1
- package/dist/room.cjs.map +1 -1
- package/dist/room.js +250 -1
- package/dist/room.js.map +1 -1
- package/dist/testing/mock-websocket.cjs +1 -1
- package/dist/testing/mock-websocket.cjs.map +1 -1
- package/dist/testing/mock-websocket.js +54 -1
- package/dist/testing/mock-websocket.js.map +1 -1
- package/dist/testing.js +1 -1
- package/package.json +5 -5
package/dist/pulse.js
CHANGED
|
@@ -1,2 +1,856 @@
|
|
|
1
|
-
import{backoff as e,tapper as t}from"@vielzeug/arsenal";var PulseError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},PulseConnectionError=class extends PulseError{url;constructor(e,t,n){super(e,n),this.url=t}},PulseTimeoutError=class extends PulseError{event;constructor(e,t){super(`Timed out waiting for "${e}"`,t),this.event=e}},PulseRoomTimeoutError=class extends PulseError{room;constructor(e,t){super(`Timed out waiting for room "${e}"`,t),this.room=e}},PulseAbortError=class extends PulseError{constructor(e){super(`Aborted`,e)}},PulseDisposedError=class extends PulseError{constructor(e=`Pulse`,t){super(`${e} instance is disposed`,t)}},PulseProtocolError=class extends PulseError{raw;constructor(e,t,n){super(e,n),this.raw=t}};function deriveAbortController(e){let t=new AbortController;return e.aborted?t.abort(e.reason):e.addEventListener(`abort`,()=>t.abort(e.reason),{once:!0}),t}function sleep(e,t){return new Promise(n=>{if(t?.aborted){n();return}let r=setTimeout(n,e);t?.addEventListener(`abort`,()=>{clearTimeout(r),n()},{once:!0})})}function defaultReconnectDelay(t,n=3e4){return Math.random()*e(t,n)}var n=5;function createConnection(e,t,r,i){let a=r===!0?{}:r||void 0,o=a?a.maxAttempts??n:0,s=a?.delay,c=typeof s==`function`?s:typeof s==`number`?()=>s:defaultReconnectDelay,l=!1,u=!1,d=!1,f,p,m,h=new Set;function setStatus(e){i.onStatus(e)}function rejectWaiters(e){for(let t of h)t.reject(e);h.clear()}function resolveWaiters(){for(let e of h)e.resolve();h.clear()}function rejectOpening(e,t){m?.socket===e&&(m.reject(t),m=void 0)}function openSocket(){if(p?.readyState===WebSocket.OPEN)return Promise.resolve();if(m)return m.promise;let n=new WebSocket(e,t);p=n;let r,a,o=new Promise((e,t)=>{r=e,a=t});return m={promise:o,reject:a,resolve:r,socket:n},n.onopen=()=>{if(p!==n||!u){n.close(1e3,`stale connection`);return}m?.socket===n&&(m.resolve(),m=void 0),setStatus(`open`),i.onOpen(),resolveWaiters()},n.onmessage=e=>{p===n&&i.onMessage(e)},n.onerror=()=>{if(p!==n)return;let t=new PulseConnectionError(`WebSocket error`,e);i.onError(t),rejectOpening(n,t),rejectWaiters(t)},n.onclose=()=>{if(p!==n)return;p=void 0,i.onClose();let t=new PulseConnectionError(`Connection closed`,e);if(rejectOpening(n,t),!u||l){setStatus(`closed`);return}d||reconnectLoop()},o}async function reconnectLoop(){if(d||l||!u)return;d=!0;let t=new AbortController;f=t,setStatus(`reconnecting`);let n;for(let r=0;r<o&&u&&!l&&(await sleep(c(r),t.signal),!(t.signal.aborted||!u||l));r++)try{await openSocket(),d=!1;return}catch(t){n=t instanceof PulseConnectionError?t:new PulseConnectionError(`Reconnect failed`,e)}!t.signal.aborted&&u&&!l&&(u=!1,setStatus(`closed`),i.onError(n??new PulseConnectionError(`Reconnect budget exhausted`,e)),rejectWaiters(n??new PulseConnectionError(`Reconnect budget exhausted`,e))),f===t&&(f=void 0),d=!1}function cancelRetry(){f?.abort(),f=void 0,d=!1}return{connect(){if(l)return Promise.reject(new PulseDisposedError);if(u=!0,p?.readyState===WebSocket.OPEN)return Promise.resolve();let e=new Promise((e,t)=>{h.add({reject:t,resolve:e})});return!m&&!d&&(setStatus(`connecting`),openSocket().catch(()=>{})),e},disconnect(t=1e3,n=``){u=!1,cancelRetry(),rejectWaiters(new PulseConnectionError(`Connection closed`,e)),setStatus(`closed`),p?.close(t,n)},dispose(){l||(l=!0,u=!1,cancelRetry(),rejectWaiters(new PulseDisposedError),p?.close(1e3,`disposed`),p=void 0,m=void 0,setStatus(`closed`))},forceReconnect(e,t){u&&p?.readyState===WebSocket.OPEN&&p.close(e,t)},get open(){return p?.readyState===WebSocket.OPEN},send(t){if(p?.readyState!==WebSocket.OPEN)throw new PulseConnectionError(`Connection is not open`,e);p.send(t)}}}function createStore(e,t){let n=e,r=new Set;return{getSnapshot:()=>n,set(e){if(!Object.is(n,e)){n=e;for(let e of[...r])try{e()}catch(e){t(e)}}},subscribe(e){return r.add(e),()=>r.delete(e)}}}function createWaitPromise(e,t,n,r){return new Promise((i,a)=>{let o=[t],s;if(n?.signal&&o.push(n.signal),n?.timeout!==void 0){let t=new AbortController;s=setTimeout(()=>{t.abort(new PulseTimeoutError(e))},n.timeout),o.push(t.signal)}let c=AbortSignal.any(o);if(c.aborted){clearTimeout(s),a(c.reason instanceof PulseTimeoutError?c.reason:new PulseAbortError);return}let unsub=()=>{},onAbort=()=>{clearTimeout(s),unsub(),a(c.reason instanceof PulseTimeoutError?c.reason:new PulseAbortError)};c.addEventListener(`abort`,onAbort,{once:!0}),unsub=r(e,e=>{clearTimeout(s),c.removeEventListener(`abort`,onAbort),i(e)})})}function createChannel(e,t,n,r,i){let a=deriveAbortController(r),o=new Set,s=a.signal.aborted;return a.signal.addEventListener(`abort`,()=>{s=!0},{once:!0}),{get disposalSignal(){return a.signal},dispose(){if(!s){s=!0,a.abort();for(let e of o)e();o.clear(),i?.()}},get disposed(){return s},get name(){return e},on(t,r){if(s)throw new PulseDisposedError(`Channel "${e}"`);let i=n(e,t,r);return o.add(i),()=>{i(),o.delete(i)}},once(t,r){if(s)throw new PulseDisposedError(`Channel "${e}"`);let unsub=()=>{},wrapped=e=>{o.delete(unsub),unsub(),r(e)};return unsub=n(e,t,wrapped),o.add(unsub),()=>{o.delete(unsub),unsub()}},send(n,r){if(s)throw new PulseDisposedError(`Channel "${e}"`);t(e,n,r)},[Symbol.dispose](){this.dispose()},wait(t,r){return createWaitPromise(t,a.signal,r,(t,r)=>n(e,t,r))}}}function encode(e){return JSON.stringify(e)}function decode(e){if(typeof e!=`string`)throw new PulseProtocolError(`Expected string message data`,e);let t;try{t=JSON.parse(e)}catch{throw new PulseProtocolError(`JSON parse error`,e)}if(typeof t!=`object`||!t||!(`type`in t)||typeof t.type!=`string`)throw new PulseProtocolError(`Missing or invalid "type" field`,t);return t}function isObjectRecord(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function getStringProp(e,t){if(!isObjectRecord(e))return null;let n=e[t];return typeof n==`string`?n:null}function hasRoom(e){return getStringProp(e,`room`)!==null}function isErrorFrame(e){return getStringProp(e,`code`)!==null&&getStringProp(e,`message`)!==null}function isMessageFrame(e){return!isObjectRecord(e)||getStringProp(e,`event`)===null||!(`payload`in e)?!1:e.channel===void 0||typeof e.channel==`string`}function isPongFrame(e){return isObjectRecord(e)&&typeof e.ts==`number`}function isPresenceJoinFrame(e){return isObjectRecord(e)&&getStringProp(e,`id`)!==null&&getStringProp(e,`room`)!==null&&`state`in e}function isPresenceLeaveFrame(e){return getStringProp(e,`id`)!==null&&getStringProp(e,`room`)!==null}function isPresenceStateFrame(e){return isObjectRecord(e)&&getStringProp(e,`room`)!==null&&isObjectRecord(e.members)}function hasChannel(e){return getStringProp(e,`channel`)!==null}function decodeValidated(e){let t=decode(e);switch(t.type){case`error`:if(isErrorFrame(t))return{frame:t,kind:`known`};break;case`joined`:if(hasRoom(t))return{frame:t,kind:`known`};break;case`left`:if(hasRoom(t))return{frame:t,kind:`known`};break;case`message`:if(isMessageFrame(t))return{frame:t,kind:`known`};break;case`pong`:if(isPongFrame(t))return{frame:t,kind:`known`};break;case`presence_join`:if(isPresenceJoinFrame(t))return{frame:t,kind:`known`};break;case`presence_leave`:if(isPresenceLeaveFrame(t))return{frame:t,kind:`known`};break;case`presence_state`:if(isPresenceStateFrame(t))return{frame:t,kind:`known`};break;case`subscribed`:if(hasChannel(t))return{frame:t,kind:`known`};break;case`unsubscribed`:if(hasChannel(t))return{frame:t,kind:`known`};break;default:return{kind:`unknown`,type:t.type}}throw new PulseProtocolError(`Malformed "${t.type}" frame`,t)}var r=3e4,i=5e3;function createHeartbeat(e,t,n){if(!e)return{onPong(){},start(){},stop(){}};let a=e===!0?{}:e,o=a.interval??r,s=a.timeout??i,c,l,u=!1;function schedulePing(){c=setTimeout(()=>{t(encode({ts:Date.now(),type:`ping`})),l=setTimeout(()=>{n()},s)},o)}return{onPong(){clearTimeout(l),l=void 0,u&&schedulePing()},start(){u=!0,clearTimeout(c),clearTimeout(l),l=void 0,schedulePing()},stop(){u=!1,clearTimeout(c),clearTimeout(l),c=void 0,l=void 0}}}function createRoomRegistry(e){let t=createStore(new Set,e.onStoreError),n=new Map;function getOrCreate(e){let t=n.get(e);return t||(t={confirmed:!1,joinSent:!1,leaveAfterJoin:!1,localPresence:void 0,refs:0,scopes:new Set},n.set(e,t)),t}function setRoom(e,n){let r=new Set(t.getSnapshot());n?r.add(e):r.delete(e),t.set(r)}function sendJoin(t){e.send(encode({room:t,type:`join`}))}function sendLeave(t){e.send(encode({room:t,type:`leave`}))}function updatePresence(t,r){let i=n.get(t);if(!i)throw new PulseDisposedError(`Room "${t}"`);if(!e.isOpen())throw new PulseConnectionError(`Connection is not open`,t);e.send(encode({room:t,state:r,type:`presence`})),i.localPresence=r}function releaseScope(t,r){let i=n.get(t);i&&i.scopes.has(r)&&(i.scopes.delete(r),i.refs--,i.refs===0&&(i.confirmed?(i.confirmed=!1,i.localPresence=void 0,setRoom(t,!1),e.isOpen()&&sendLeave(t),n.delete(t)):i.joinSent?i.leaveAfterJoin=!0:n.delete(t)))}return{createScope(t,n,r){if(e.disposalSignal.aborted)return createDeadScope(t,n);let i=getOrCreate(t),a=deriveAbortController(e.disposalSignal),resolveJoined,rejectJoined,o=!1,s=new Promise((e,t)=>{resolveJoined=()=>{o||(o=!0,clearTimeout(c),u.removeEventListener(`abort`,onAbort),e())},rejectJoined=e=>{o||(o=!0,clearTimeout(c),u.removeEventListener(`abort`,onAbort),t(e))}});s.catch(()=>{});let c,l=[a.signal];if(r?.signal&&l.push(r.signal),r?.timeout!==void 0){let e=new AbortController;c=setTimeout(()=>e.abort(new PulseRoomTimeoutError(t)),r.timeout),l.push(e.signal)}let u=AbortSignal.any(l),d,f,p=new Set,m=new Set;n&&(d=createStore(new Map,e.onStoreError),f={onJoin(e,t){let n=new Map(d.getSnapshot());n.set(e,t),d.set(n);for(let n of p)n(e,t)},onLeave(e){let t=new Map(d.getSnapshot());t.delete(e),d.set(t);for(let t of m)t(e)},onState(e){let t=new Map;for(let[n,r]of Object.entries(e))t.set(n,r);d.set(t)},reset(){d.set(new Map)}});let h={isSettled:()=>o,joined:s,name:t,presenceHandlers:f,rejectJoined,resolveJoined},onAbort=()=>{clearTimeout(c),a.abort();let e=u.reason,n=e instanceof PulseError?e:new PulseAbortError;h.rejectJoined(n),releaseScope(t,h)};if(i.refs++,i.scopes.add(h),u.aborted)return onAbort(),buildPublicScope(h,a.signal,d,p,m,t,updatePresence);if(u.addEventListener(`abort`,onAbort,{once:!0}),i.confirmed)h.resolveJoined();else if(!i.joinSent&&e.isOpen()){i.joinSent=!0;try{sendJoin(t)}catch(e){i.joinSent=!1,h.rejectJoined(e instanceof PulseError?e:new PulseConnectionError(`Failed to send join`,t))}}let disposeScope=()=>{a.signal.aborted||(clearTimeout(c),u.removeEventListener(`abort`,onAbort),a.abort(),o||h.rejectJoined(new PulseDisposedError(`Room "${t}"`)),releaseScope(t,h))};return buildPublicScope(h,a.signal,d,p,m,t,updatePresence,disposeScope)},dispose(){t.set(new Set),n.clear()},handleJoined(t){let r=n.get(t);if(r){if(r.confirmed=!0,r.joinSent=!1,setRoom(t,!0),r.leaveAfterJoin){r.leaveAfterJoin=!1,r.confirmed=!1,setRoom(t,!1),e.isOpen()&&sendLeave(t),n.delete(t);return}for(let e of r.scopes)e.isSettled()||e.resolveJoined()}},handleLeft(e){let t=n.get(e);t&&(t.confirmed=!1,t.joinSent=!1,setRoom(e,!1))},handlePresenceJoin(e,t,r){let i=n.get(e);if(i)for(let e of i.scopes)e.presenceHandlers?.onJoin(t,r)},handlePresenceLeave(e,t){let r=n.get(e);if(r)for(let e of r.scopes)e.presenceHandlers?.onLeave(t)},handlePresenceState(e,t){let r=n.get(e);if(r)for(let e of r.scopes)e.presenceHandlers?.onState(t)},rejectAll(e){for(let t of n.values())for(let n of t.scopes)n.isSettled()||n.rejectJoined(e)},reset(){t.set(new Set);for(let[e,t]of n){if(t.refs===0){n.delete(e);continue}t.confirmed=!1,t.joinSent=!1;for(let e of t.scopes)e.presenceHandlers?.reset()}},restore(){for(let[t,r]of n)if(r.refs!==0){r.confirmed=!1,r.joinSent=!0;try{sendJoin(t)}catch{r.joinSent=!1}if(r.localPresence!==void 0)try{e.send(encode({room:t,state:r.localPresence,type:`presence`}))}catch{}}},rooms:t}}function buildPublicScope(e,t,n,r,i,a,o,s){let c=s??(()=>{});return n?{get disposalSignal(){return t},dispose:c,get disposed(){return t.aborted},get joined(){return e.joined},get name(){return e.name},onJoin(e){if(t.aborted)throw new PulseDisposedError(`Room "${a}"`);return r.add(e),()=>r.delete(e)},onLeave(e){if(t.aborted)throw new PulseDisposedError(`Room "${a}"`);return i.add(e),()=>i.delete(e)},get presence(){return n},updatePresence(e){o(a,e)},[Symbol.dispose]:c}:{get disposalSignal(){return t},dispose:c,get disposed(){return t.aborted},get joined(){return e.joined},get name(){return e.name},[Symbol.dispose]:c}}function createDeadScope(e,t){let n=new AbortController;n.abort(new PulseDisposedError);let r=Promise.reject(new PulseDisposedError(`Room "${e}"`));return r.catch(()=>{}),t?{disposalSignal:n.signal,dispose(){},disposed:!0,joined:r,name:e,onJoin(){throw new PulseDisposedError(`Room "${e}"`)},onLeave(){throw new PulseDisposedError(`Room "${e}"`)},presence:createStore(new Map,()=>{}),updatePresence(){throw new PulseDisposedError(`Room "${e}"`)},[Symbol.dispose](){}}:{disposalSignal:n.signal,dispose(){},disposed:!0,joined:r,name:e,[Symbol.dispose](){}}}var ListenerMap=class{map=new Map;add(e,t,n){let r=this.map.get(e);r||(r=new Map,this.map.set(e,r));let i=r.get(t);return i||(i=new Set,r.set(t,i)),i.add(n),()=>{i.delete(n),i.size===0&&r.delete(t),r.size===0&&this.map.delete(e)}}clear(){this.map.clear()}dispatch(e,t,n){for(let r of this.map.get(e)?.get(t)??[])r(n)}};function createPulse(e,n={}){let r=new AbortController,i=new ListenerMap,a=new Map,o=t(),s=!1,c=!0,l=`closed`;function emitTap(e){o.emit(e)}function report(e){emitTap({error:e,type:`error`})}let reportStoreError=e=>report(new PulseError(`External store subscriber threw`,{cause:e})),u=createStore(`closed`,reportStoreError);function sendInternal(e){f.send(e)}let d=createRoomRegistry({disposalSignal:r.signal,isOpen:()=>f.open,onStoreError:reportStoreError,send:sendInternal});function restoreSession(){for(let e of a.keys())sendInternal(encode({channel:e,type:`subscribe`}));d.restore()}function resetRemoteSession(){d.reset()}function handleTransportClose(){c||(c=!0,p.stop(),resetRemoteSession(),d.rejectAll(new PulseConnectionError(`Connection closed before room confirmation`,e)))}let f=createConnection(e,n.protocols,n.reconnect,{onClose(){handleTransportClose()},onError:report,onMessage(e){let t;try{t=decodeValidated(e.data)}catch(t){report(t instanceof PulseProtocolError?t:new PulseProtocolError(`Failed to decode frame`,e.data));return}if(t.kind===`unknown`){report(new PulseProtocolError(`Unknown frame type "${t.type}"`,e.data));return}try{handleFrame(t)}catch(e){report(new PulseProtocolError(`Frame handler threw`,t.frame,{cause:e}))}},onOpen(){c=!1;try{restoreSession(),p.start()}catch(t){report(t instanceof PulseError?t:new PulseConnectionError(`Session restore failed`,e))}},onStatus(e){u.set(e),e!==l&&(l=e,emitTap({status:e,type:`status-change`}))}}),p=createHeartbeat(n.heartbeat,e=>{f.open&&sendInternal(e)},()=>f.forceReconnect(4e3,`heartbeat timeout`));function handleFrame(e){let t=e.frame;switch(t.type){case`error`:report(new PulseProtocolError(`Server error [${t.code}]: ${t.message}`,t));break;case`joined`:d.handleJoined(t.room);break;case`left`:d.handleLeft(t.room);break;case`message`:i.dispatch(t.channel??null,t.event,t.payload);break;case`pong`:p.onPong();break;case`presence_join`:d.handlePresenceJoin(t.room,t.id,t.state);break;case`presence_leave`:d.handlePresenceLeave(t.room,t.id);break;case`presence_state`:d.handlePresenceState(t.room,t.members)}}function acquireChannel(e){let t=a.get(e)??0;a.set(e,t+1),t===0&&f.open&&sendInternal(encode({channel:e,type:`subscribe`}))}function releaseChannel(e){let t=a.get(e);if(t){if(t>1){a.set(e,t-1);return}a.delete(e),f.open&&sendInternal(encode({channel:e,type:`unsubscribe`}))}}return{channel(e){if(s)throw new PulseDisposedError;return acquireChannel(e),createChannel(e,(e,t,r)=>{let i=n.transform?n.transform({channel:e,event:t,payload:r}):{channel:e,event:t,payload:r};i!==null&&sendInternal(encode({...i,type:`message`}))},(e,t,n)=>i.add(e,t,n),r.signal,()=>releaseChannel(e))},connect(){return s?Promise.reject(new PulseDisposedError):f.connect()},disconnect(e=1e3,t=``){s||(handleTransportClose(),f.disconnect(e,t))},get disposalSignal(){return r.signal},dispose(){s||(s=!0,p.stop(),c=!0,d.reset(),d.rejectAll(new PulseDisposedError),f.dispose(),a.clear(),d.dispose(),i.clear(),emitTap({type:`dispose`}),o.clear(),r.abort())},get disposed(){return s},on(e,t){if(s)throw new PulseDisposedError;return i.add(null,e,t)},once(e,t){if(s)throw new PulseDisposedError;let unsubscribe=()=>{};return unsubscribe=i.add(null,e,e=>{unsubscribe(),t(e)}),unsubscribe},room(e,t){if(s)throw new PulseDisposedError;return d.createScope(e,!0,t)},get rooms(){return d.rooms},send(e,t){if(s)throw new PulseDisposedError;let r=n.transform?n.transform({event:e,payload:t}):{event:e,payload:t};r!==null&&sendInternal(encode({...r,type:`message`}))},get status(){return u},tap(e,t){if(s)throw new PulseDisposedError;return o.tap(e,t)},[Symbol.dispose](){this.dispose()},wait(e,t){return createWaitPromise(e,r.signal,t,(e,t)=>i.add(null,e,t))}}}export{PulseAbortError,PulseConnectionError,PulseDisposedError,PulseError,PulseProtocolError,PulseRoomTimeoutError,PulseTimeoutError,createPulse};
|
|
1
|
+
import { backoff as e, tapper as t } from "@vielzeug/arsenal";
|
|
2
|
+
//#region src/errors.ts
|
|
3
|
+
var PulseError = class extends Error {
|
|
4
|
+
constructor(e, t) {
|
|
5
|
+
super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
|
|
6
|
+
}
|
|
7
|
+
}, PulseConnectionError = class extends PulseError {
|
|
8
|
+
url;
|
|
9
|
+
constructor(e, t, n) {
|
|
10
|
+
super(e, n), this.url = t;
|
|
11
|
+
}
|
|
12
|
+
}, PulseTimeoutError = class extends PulseError {
|
|
13
|
+
event;
|
|
14
|
+
constructor(e, t) {
|
|
15
|
+
super(`Timed out waiting for "${e}"`, t), this.event = e;
|
|
16
|
+
}
|
|
17
|
+
}, PulseRoomTimeoutError = class extends PulseError {
|
|
18
|
+
room;
|
|
19
|
+
constructor(e, t) {
|
|
20
|
+
super(`Timed out waiting for room "${e}"`, t), this.room = e;
|
|
21
|
+
}
|
|
22
|
+
}, PulseAbortError = class extends PulseError {
|
|
23
|
+
constructor(e) {
|
|
24
|
+
super("Aborted", e);
|
|
25
|
+
}
|
|
26
|
+
}, PulseDisposedError = class extends PulseError {
|
|
27
|
+
constructor(e = "Pulse", t) {
|
|
28
|
+
super(`${e} instance is disposed`, t);
|
|
29
|
+
}
|
|
30
|
+
}, PulseProtocolError = class extends PulseError {
|
|
31
|
+
raw;
|
|
32
|
+
constructor(e, t, n) {
|
|
33
|
+
super(e, n), this.raw = t;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/_utils.ts
|
|
38
|
+
function deriveAbortController(e) {
|
|
39
|
+
let t = new AbortController();
|
|
40
|
+
return e.aborted ? t.abort(e.reason) : e.addEventListener("abort", () => t.abort(e.reason), { once: !0 }), t;
|
|
41
|
+
}
|
|
42
|
+
function sleep(e, t) {
|
|
43
|
+
return new Promise((n) => {
|
|
44
|
+
if (t?.aborted) {
|
|
45
|
+
n();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let r = setTimeout(n, e);
|
|
49
|
+
t?.addEventListener("abort", () => {
|
|
50
|
+
clearTimeout(r), n();
|
|
51
|
+
}, { once: !0 });
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function defaultReconnectDelay(t, n = 3e4) {
|
|
55
|
+
return Math.random() * e(t, n);
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
//#region src/_connection.ts
|
|
59
|
+
var n = 5;
|
|
60
|
+
function createConnection(e, t, r, i) {
|
|
61
|
+
let a = r === !0 ? {} : r || void 0, o = a ? a.maxAttempts ?? n : 0, s = a?.delay, c = typeof s == "function" ? s : typeof s == "number" ? () => s : defaultReconnectDelay, l = !1, u = !1, d = !1, f, p, m, h = /* @__PURE__ */ new Set();
|
|
62
|
+
function setStatus(e) {
|
|
63
|
+
i.onStatus(e);
|
|
64
|
+
}
|
|
65
|
+
function rejectWaiters(e) {
|
|
66
|
+
for (let t of h) t.reject(e);
|
|
67
|
+
h.clear();
|
|
68
|
+
}
|
|
69
|
+
function resolveWaiters() {
|
|
70
|
+
for (let e of h) e.resolve();
|
|
71
|
+
h.clear();
|
|
72
|
+
}
|
|
73
|
+
function rejectOpening(e, t) {
|
|
74
|
+
m?.socket === e && (m.reject(t), m = void 0);
|
|
75
|
+
}
|
|
76
|
+
function openSocket() {
|
|
77
|
+
if (p?.readyState === WebSocket.OPEN) return Promise.resolve();
|
|
78
|
+
if (m) return m.promise;
|
|
79
|
+
let n = new WebSocket(e, t);
|
|
80
|
+
p = n;
|
|
81
|
+
let r, a, o = new Promise((e, t) => {
|
|
82
|
+
r = e, a = t;
|
|
83
|
+
});
|
|
84
|
+
return m = {
|
|
85
|
+
promise: o,
|
|
86
|
+
reject: a,
|
|
87
|
+
resolve: r,
|
|
88
|
+
socket: n
|
|
89
|
+
}, n.onopen = () => {
|
|
90
|
+
p !== n || !u ? n.close(1e3, "stale connection") : (m?.socket === n && (m.resolve(), m = void 0), setStatus("open"), i.onOpen(), resolveWaiters());
|
|
91
|
+
}, n.onmessage = (e) => {
|
|
92
|
+
p === n && i.onMessage(e);
|
|
93
|
+
}, n.onerror = () => {
|
|
94
|
+
if (p !== n) return;
|
|
95
|
+
let t = new PulseConnectionError("WebSocket error", e);
|
|
96
|
+
i.onError(t), rejectOpening(n, t), rejectWaiters(t);
|
|
97
|
+
}, n.onclose = () => {
|
|
98
|
+
if (p !== n) return;
|
|
99
|
+
p = void 0, i.onClose();
|
|
100
|
+
let t = new PulseConnectionError("Connection closed", e);
|
|
101
|
+
rejectOpening(n, t), !u || l ? setStatus("closed") : d || reconnectLoop();
|
|
102
|
+
}, o;
|
|
103
|
+
}
|
|
104
|
+
async function reconnectLoop() {
|
|
105
|
+
if (d || l || !u) return;
|
|
106
|
+
d = !0;
|
|
107
|
+
let t = new AbortController();
|
|
108
|
+
f = t, setStatus("reconnecting");
|
|
109
|
+
let n;
|
|
110
|
+
for (let r = 0; r < o && u && !l && (await sleep(c(r), t.signal), !(t.signal.aborted || !u || l)); r++) try {
|
|
111
|
+
await openSocket(), d = !1;
|
|
112
|
+
return;
|
|
113
|
+
} catch (t) {
|
|
114
|
+
n = t instanceof PulseConnectionError ? t : new PulseConnectionError("Reconnect failed", e);
|
|
115
|
+
}
|
|
116
|
+
!t.signal.aborted && u && !l && (u = !1, setStatus("closed"), i.onError(n ?? new PulseConnectionError("Reconnect budget exhausted", e)), rejectWaiters(n ?? new PulseConnectionError("Reconnect budget exhausted", e))), f === t && (f = void 0), d = !1;
|
|
117
|
+
}
|
|
118
|
+
function cancelRetry() {
|
|
119
|
+
f?.abort(), f = void 0, d = !1;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
connect() {
|
|
123
|
+
if (l) return Promise.reject(new PulseDisposedError());
|
|
124
|
+
if (u = !0, p?.readyState === WebSocket.OPEN) return Promise.resolve();
|
|
125
|
+
let e = new Promise((e, t) => {
|
|
126
|
+
h.add({
|
|
127
|
+
reject: t,
|
|
128
|
+
resolve: e
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
return !m && !d && (setStatus("connecting"), openSocket().catch(() => {})), e;
|
|
132
|
+
},
|
|
133
|
+
disconnect(t = 1e3, n = "") {
|
|
134
|
+
u = !1, cancelRetry(), rejectWaiters(new PulseConnectionError("Connection closed", e)), setStatus("closed"), p?.close(t, n);
|
|
135
|
+
},
|
|
136
|
+
dispose() {
|
|
137
|
+
l || (l = !0, u = !1, cancelRetry(), rejectWaiters(new PulseDisposedError()), p?.close(1e3, "disposed"), p = void 0, m = void 0, setStatus("closed"));
|
|
138
|
+
},
|
|
139
|
+
forceReconnect(e, t) {
|
|
140
|
+
u && p?.readyState === WebSocket.OPEN && p.close(e, t);
|
|
141
|
+
},
|
|
142
|
+
get open() {
|
|
143
|
+
return p?.readyState === WebSocket.OPEN;
|
|
144
|
+
},
|
|
145
|
+
send(t) {
|
|
146
|
+
if (p?.readyState !== WebSocket.OPEN) throw new PulseConnectionError("Connection is not open", e);
|
|
147
|
+
p.send(t);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/_store.ts
|
|
153
|
+
function createStore(e, t) {
|
|
154
|
+
let n = e, r = /* @__PURE__ */ new Set();
|
|
155
|
+
return {
|
|
156
|
+
getSnapshot: () => n,
|
|
157
|
+
set(e) {
|
|
158
|
+
if (!Object.is(n, e)) {
|
|
159
|
+
n = e;
|
|
160
|
+
for (let e of [...r]) try {
|
|
161
|
+
e();
|
|
162
|
+
} catch (e) {
|
|
163
|
+
t(e);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
subscribe(e) {
|
|
168
|
+
return r.add(e), () => r.delete(e);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region src/_wait.ts
|
|
174
|
+
function createWaitPromise(e, t, n, r) {
|
|
175
|
+
return new Promise((i, a) => {
|
|
176
|
+
let o = [t], s;
|
|
177
|
+
if (n?.signal && o.push(n.signal), n?.timeout !== void 0) {
|
|
178
|
+
let t = new AbortController();
|
|
179
|
+
s = setTimeout(() => {
|
|
180
|
+
t.abort(new PulseTimeoutError(e));
|
|
181
|
+
}, n.timeout), o.push(t.signal);
|
|
182
|
+
}
|
|
183
|
+
let c = AbortSignal.any(o);
|
|
184
|
+
if (c.aborted) {
|
|
185
|
+
clearTimeout(s), a(c.reason instanceof PulseTimeoutError ? c.reason : new PulseAbortError());
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
let unsub = () => {}, onAbort = () => {
|
|
189
|
+
clearTimeout(s), unsub(), a(c.reason instanceof PulseTimeoutError ? c.reason : new PulseAbortError());
|
|
190
|
+
};
|
|
191
|
+
c.addEventListener("abort", onAbort, { once: !0 }), unsub = r(e, (e) => {
|
|
192
|
+
clearTimeout(s), c.removeEventListener("abort", onAbort), i(e);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
//#endregion
|
|
197
|
+
//#region src/channel.ts
|
|
198
|
+
function createChannel(e, t, n, r, i) {
|
|
199
|
+
let a = deriveAbortController(r), o = /* @__PURE__ */ new Set(), s = a.signal.aborted;
|
|
200
|
+
return a.signal.addEventListener("abort", () => {
|
|
201
|
+
s = !0;
|
|
202
|
+
}, { once: !0 }), {
|
|
203
|
+
get disposalSignal() {
|
|
204
|
+
return a.signal;
|
|
205
|
+
},
|
|
206
|
+
dispose() {
|
|
207
|
+
if (!s) {
|
|
208
|
+
s = !0, a.abort();
|
|
209
|
+
for (let e of o) e();
|
|
210
|
+
o.clear(), i?.();
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
get disposed() {
|
|
214
|
+
return s;
|
|
215
|
+
},
|
|
216
|
+
get name() {
|
|
217
|
+
return e;
|
|
218
|
+
},
|
|
219
|
+
on(t, r) {
|
|
220
|
+
if (s) throw new PulseDisposedError(`Channel "${e}"`);
|
|
221
|
+
let i = n(e, t, r);
|
|
222
|
+
return o.add(i), () => {
|
|
223
|
+
i(), o.delete(i);
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
once(t, r) {
|
|
227
|
+
if (s) throw new PulseDisposedError(`Channel "${e}"`);
|
|
228
|
+
let unsub = () => {}, wrapped = (e) => {
|
|
229
|
+
o.delete(unsub), unsub(), r(e);
|
|
230
|
+
};
|
|
231
|
+
return unsub = n(e, t, wrapped), o.add(unsub), () => {
|
|
232
|
+
o.delete(unsub), unsub();
|
|
233
|
+
};
|
|
234
|
+
},
|
|
235
|
+
send(n, r) {
|
|
236
|
+
if (s) throw new PulseDisposedError(`Channel "${e}"`);
|
|
237
|
+
t(e, n, r);
|
|
238
|
+
},
|
|
239
|
+
[Symbol.dispose]() {
|
|
240
|
+
this.dispose();
|
|
241
|
+
},
|
|
242
|
+
wait(t, r) {
|
|
243
|
+
return createWaitPromise(t, a.signal, r, (t, r) => n(e, t, r));
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
//#region src/protocol.ts
|
|
249
|
+
function encode(e) {
|
|
250
|
+
return JSON.stringify(e);
|
|
251
|
+
}
|
|
252
|
+
function decode(e) {
|
|
253
|
+
if (typeof e != "string") throw new PulseProtocolError("Expected string message data", e);
|
|
254
|
+
let t;
|
|
255
|
+
try {
|
|
256
|
+
t = JSON.parse(e);
|
|
257
|
+
} catch {
|
|
258
|
+
throw new PulseProtocolError("JSON parse error", e);
|
|
259
|
+
}
|
|
260
|
+
if (typeof t != "object" || !t || !("type" in t) || typeof t.type != "string") throw new PulseProtocolError("Missing or invalid \"type\" field", t);
|
|
261
|
+
return t;
|
|
262
|
+
}
|
|
263
|
+
function isObjectRecord(e) {
|
|
264
|
+
return typeof e == "object" && !!e && !Array.isArray(e);
|
|
265
|
+
}
|
|
266
|
+
function getStringProp(e, t) {
|
|
267
|
+
if (!isObjectRecord(e)) return null;
|
|
268
|
+
let n = e[t];
|
|
269
|
+
return typeof n == "string" ? n : null;
|
|
270
|
+
}
|
|
271
|
+
function hasRoom(e) {
|
|
272
|
+
return getStringProp(e, "room") !== null;
|
|
273
|
+
}
|
|
274
|
+
function isErrorFrame(e) {
|
|
275
|
+
return getStringProp(e, "code") !== null && getStringProp(e, "message") !== null;
|
|
276
|
+
}
|
|
277
|
+
function isMessageFrame(e) {
|
|
278
|
+
return !isObjectRecord(e) || getStringProp(e, "event") === null || !("payload" in e) ? !1 : e.channel === void 0 || typeof e.channel == "string";
|
|
279
|
+
}
|
|
280
|
+
function isPongFrame(e) {
|
|
281
|
+
return isObjectRecord(e) && typeof e.ts == "number";
|
|
282
|
+
}
|
|
283
|
+
function isPresenceJoinFrame(e) {
|
|
284
|
+
return isObjectRecord(e) && getStringProp(e, "id") !== null && getStringProp(e, "room") !== null && "state" in e;
|
|
285
|
+
}
|
|
286
|
+
function isPresenceLeaveFrame(e) {
|
|
287
|
+
return getStringProp(e, "id") !== null && getStringProp(e, "room") !== null;
|
|
288
|
+
}
|
|
289
|
+
function isPresenceStateFrame(e) {
|
|
290
|
+
return isObjectRecord(e) && getStringProp(e, "room") !== null && isObjectRecord(e.members);
|
|
291
|
+
}
|
|
292
|
+
function hasChannel(e) {
|
|
293
|
+
return getStringProp(e, "channel") !== null;
|
|
294
|
+
}
|
|
295
|
+
function decodeValidated(e) {
|
|
296
|
+
let t = decode(e);
|
|
297
|
+
switch (t.type) {
|
|
298
|
+
case "error":
|
|
299
|
+
if (isErrorFrame(t)) return {
|
|
300
|
+
frame: t,
|
|
301
|
+
kind: "known"
|
|
302
|
+
};
|
|
303
|
+
break;
|
|
304
|
+
case "joined":
|
|
305
|
+
if (hasRoom(t)) return {
|
|
306
|
+
frame: t,
|
|
307
|
+
kind: "known"
|
|
308
|
+
};
|
|
309
|
+
break;
|
|
310
|
+
case "left":
|
|
311
|
+
if (hasRoom(t)) return {
|
|
312
|
+
frame: t,
|
|
313
|
+
kind: "known"
|
|
314
|
+
};
|
|
315
|
+
break;
|
|
316
|
+
case "message":
|
|
317
|
+
if (isMessageFrame(t)) return {
|
|
318
|
+
frame: t,
|
|
319
|
+
kind: "known"
|
|
320
|
+
};
|
|
321
|
+
break;
|
|
322
|
+
case "pong":
|
|
323
|
+
if (isPongFrame(t)) return {
|
|
324
|
+
frame: t,
|
|
325
|
+
kind: "known"
|
|
326
|
+
};
|
|
327
|
+
break;
|
|
328
|
+
case "presence_join":
|
|
329
|
+
if (isPresenceJoinFrame(t)) return {
|
|
330
|
+
frame: t,
|
|
331
|
+
kind: "known"
|
|
332
|
+
};
|
|
333
|
+
break;
|
|
334
|
+
case "presence_leave":
|
|
335
|
+
if (isPresenceLeaveFrame(t)) return {
|
|
336
|
+
frame: t,
|
|
337
|
+
kind: "known"
|
|
338
|
+
};
|
|
339
|
+
break;
|
|
340
|
+
case "presence_state":
|
|
341
|
+
if (isPresenceStateFrame(t)) return {
|
|
342
|
+
frame: t,
|
|
343
|
+
kind: "known"
|
|
344
|
+
};
|
|
345
|
+
break;
|
|
346
|
+
case "subscribed":
|
|
347
|
+
if (hasChannel(t)) return {
|
|
348
|
+
frame: t,
|
|
349
|
+
kind: "known"
|
|
350
|
+
};
|
|
351
|
+
break;
|
|
352
|
+
case "unsubscribed":
|
|
353
|
+
if (hasChannel(t)) return {
|
|
354
|
+
frame: t,
|
|
355
|
+
kind: "known"
|
|
356
|
+
};
|
|
357
|
+
break;
|
|
358
|
+
default: return {
|
|
359
|
+
kind: "unknown",
|
|
360
|
+
type: t.type
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
throw new PulseProtocolError(`Malformed "${t.type}" frame`, t);
|
|
364
|
+
}
|
|
365
|
+
//#endregion
|
|
366
|
+
//#region src/heartbeat.ts
|
|
367
|
+
var r = 3e4, i = 5e3;
|
|
368
|
+
function createHeartbeat(e, t, n) {
|
|
369
|
+
if (!e) return {
|
|
370
|
+
onPong() {},
|
|
371
|
+
start() {},
|
|
372
|
+
stop() {}
|
|
373
|
+
};
|
|
374
|
+
let a = e === !0 ? {} : e, o = a.interval ?? r, s = a.timeout ?? i, c, l, u = !1;
|
|
375
|
+
function schedulePing() {
|
|
376
|
+
c = setTimeout(() => {
|
|
377
|
+
t(encode({
|
|
378
|
+
ts: Date.now(),
|
|
379
|
+
type: "ping"
|
|
380
|
+
})), l = setTimeout(() => {
|
|
381
|
+
n();
|
|
382
|
+
}, s);
|
|
383
|
+
}, o);
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
onPong() {
|
|
387
|
+
clearTimeout(l), l = void 0, u && schedulePing();
|
|
388
|
+
},
|
|
389
|
+
start() {
|
|
390
|
+
u = !0, clearTimeout(c), clearTimeout(l), l = void 0, schedulePing();
|
|
391
|
+
},
|
|
392
|
+
stop() {
|
|
393
|
+
u = !1, clearTimeout(c), clearTimeout(l), c = void 0, l = void 0;
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
//#endregion
|
|
398
|
+
//#region src/room.ts
|
|
399
|
+
function createRoomRegistry(e) {
|
|
400
|
+
let t = createStore(/* @__PURE__ */ new Set(), e.onStoreError), n = /* @__PURE__ */ new Map();
|
|
401
|
+
function getOrCreate(e) {
|
|
402
|
+
let t = n.get(e);
|
|
403
|
+
return t || (t = {
|
|
404
|
+
confirmed: !1,
|
|
405
|
+
joinSent: !1,
|
|
406
|
+
leaveAfterJoin: !1,
|
|
407
|
+
localPresence: void 0,
|
|
408
|
+
refs: 0,
|
|
409
|
+
scopes: /* @__PURE__ */ new Set()
|
|
410
|
+
}, n.set(e, t)), t;
|
|
411
|
+
}
|
|
412
|
+
function setRoom(e, n) {
|
|
413
|
+
let r = new Set(t.getSnapshot());
|
|
414
|
+
n ? r.add(e) : r.delete(e), t.set(r);
|
|
415
|
+
}
|
|
416
|
+
function sendJoin(t) {
|
|
417
|
+
e.send(encode({
|
|
418
|
+
room: t,
|
|
419
|
+
type: "join"
|
|
420
|
+
}));
|
|
421
|
+
}
|
|
422
|
+
function sendLeave(t) {
|
|
423
|
+
e.send(encode({
|
|
424
|
+
room: t,
|
|
425
|
+
type: "leave"
|
|
426
|
+
}));
|
|
427
|
+
}
|
|
428
|
+
function updatePresence(t, r) {
|
|
429
|
+
let i = n.get(t);
|
|
430
|
+
if (!i) throw new PulseDisposedError(`Room "${t}"`);
|
|
431
|
+
if (!e.isOpen()) throw new PulseConnectionError("Connection is not open", t);
|
|
432
|
+
e.send(encode({
|
|
433
|
+
room: t,
|
|
434
|
+
state: r,
|
|
435
|
+
type: "presence"
|
|
436
|
+
})), i.localPresence = r;
|
|
437
|
+
}
|
|
438
|
+
function releaseScope(t, r) {
|
|
439
|
+
let i = n.get(t);
|
|
440
|
+
i && i.scopes.has(r) && (i.scopes.delete(r), i.refs--, i.refs === 0 && (i.confirmed ? (i.confirmed = !1, i.localPresence = void 0, setRoom(t, !1), e.isOpen() && sendLeave(t), n.delete(t)) : i.joinSent ? i.leaveAfterJoin = !0 : n.delete(t)));
|
|
441
|
+
}
|
|
442
|
+
return {
|
|
443
|
+
createScope(t, n, r) {
|
|
444
|
+
if (e.disposalSignal.aborted) return createDeadScope(t, n);
|
|
445
|
+
let i = getOrCreate(t), a = deriveAbortController(e.disposalSignal), resolveJoined, rejectJoined, o = !1, s = new Promise((e, t) => {
|
|
446
|
+
resolveJoined = () => {
|
|
447
|
+
o || (o = !0, clearTimeout(c), u.removeEventListener("abort", onAbort), e());
|
|
448
|
+
}, rejectJoined = (e) => {
|
|
449
|
+
o || (o = !0, clearTimeout(c), u.removeEventListener("abort", onAbort), t(e));
|
|
450
|
+
};
|
|
451
|
+
});
|
|
452
|
+
s.catch(() => {});
|
|
453
|
+
let c, l = [a.signal];
|
|
454
|
+
if (r?.signal && l.push(r.signal), r?.timeout !== void 0) {
|
|
455
|
+
let e = new AbortController();
|
|
456
|
+
c = setTimeout(() => e.abort(new PulseRoomTimeoutError(t)), r.timeout), l.push(e.signal);
|
|
457
|
+
}
|
|
458
|
+
let u = AbortSignal.any(l), d, f, p = /* @__PURE__ */ new Set(), m = /* @__PURE__ */ new Set();
|
|
459
|
+
n && (d = createStore(/* @__PURE__ */ new Map(), e.onStoreError), f = {
|
|
460
|
+
onJoin(e, t) {
|
|
461
|
+
let n = new Map(d.getSnapshot());
|
|
462
|
+
n.set(e, t), d.set(n);
|
|
463
|
+
for (let n of p) n(e, t);
|
|
464
|
+
},
|
|
465
|
+
onLeave(e) {
|
|
466
|
+
let t = new Map(d.getSnapshot());
|
|
467
|
+
t.delete(e), d.set(t);
|
|
468
|
+
for (let t of m) t(e);
|
|
469
|
+
},
|
|
470
|
+
onState(e) {
|
|
471
|
+
let t = /* @__PURE__ */ new Map();
|
|
472
|
+
for (let [n, r] of Object.entries(e)) t.set(n, r);
|
|
473
|
+
d.set(t);
|
|
474
|
+
},
|
|
475
|
+
reset() {
|
|
476
|
+
d.set(/* @__PURE__ */ new Map());
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
let h = {
|
|
480
|
+
isSettled: () => o,
|
|
481
|
+
joined: s,
|
|
482
|
+
name: t,
|
|
483
|
+
presenceHandlers: f,
|
|
484
|
+
rejectJoined,
|
|
485
|
+
resolveJoined
|
|
486
|
+
}, onAbort = () => {
|
|
487
|
+
clearTimeout(c), a.abort();
|
|
488
|
+
let e = u.reason, n = e instanceof PulseError ? e : new PulseAbortError();
|
|
489
|
+
h.rejectJoined(n), releaseScope(t, h);
|
|
490
|
+
};
|
|
491
|
+
if (i.refs++, i.scopes.add(h), u.aborted) return onAbort(), buildPublicScope(h, a.signal, d, p, m, t, updatePresence);
|
|
492
|
+
if (u.addEventListener("abort", onAbort, { once: !0 }), i.confirmed) h.resolveJoined();
|
|
493
|
+
else if (!i.joinSent && e.isOpen()) {
|
|
494
|
+
i.joinSent = !0;
|
|
495
|
+
try {
|
|
496
|
+
sendJoin(t);
|
|
497
|
+
} catch (e) {
|
|
498
|
+
i.joinSent = !1, h.rejectJoined(e instanceof PulseError ? e : new PulseConnectionError("Failed to send join", t));
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
let disposeScope = () => {
|
|
502
|
+
a.signal.aborted || (clearTimeout(c), u.removeEventListener("abort", onAbort), a.abort(), o || h.rejectJoined(new PulseDisposedError(`Room "${t}"`)), releaseScope(t, h));
|
|
503
|
+
};
|
|
504
|
+
return buildPublicScope(h, a.signal, d, p, m, t, updatePresence, disposeScope);
|
|
505
|
+
},
|
|
506
|
+
dispose() {
|
|
507
|
+
t.set(/* @__PURE__ */ new Set()), n.clear();
|
|
508
|
+
},
|
|
509
|
+
handleJoined(t) {
|
|
510
|
+
let r = n.get(t);
|
|
511
|
+
if (r) {
|
|
512
|
+
if (r.confirmed = !0, r.joinSent = !1, setRoom(t, !0), r.leaveAfterJoin) r.leaveAfterJoin = !1, r.confirmed = !1, setRoom(t, !1), e.isOpen() && sendLeave(t), n.delete(t);
|
|
513
|
+
else for (let e of r.scopes) e.isSettled() || e.resolveJoined();
|
|
514
|
+
}
|
|
515
|
+
},
|
|
516
|
+
handleLeft(e) {
|
|
517
|
+
let t = n.get(e);
|
|
518
|
+
t && (t.confirmed = !1, t.joinSent = !1, setRoom(e, !1));
|
|
519
|
+
},
|
|
520
|
+
handlePresenceJoin(e, t, r) {
|
|
521
|
+
let i = n.get(e);
|
|
522
|
+
if (i) for (let e of i.scopes) e.presenceHandlers?.onJoin(t, r);
|
|
523
|
+
},
|
|
524
|
+
handlePresenceLeave(e, t) {
|
|
525
|
+
let r = n.get(e);
|
|
526
|
+
if (r) for (let e of r.scopes) e.presenceHandlers?.onLeave(t);
|
|
527
|
+
},
|
|
528
|
+
handlePresenceState(e, t) {
|
|
529
|
+
let r = n.get(e);
|
|
530
|
+
if (r) for (let e of r.scopes) e.presenceHandlers?.onState(t);
|
|
531
|
+
},
|
|
532
|
+
rejectAll(e) {
|
|
533
|
+
for (let t of n.values()) for (let n of t.scopes) n.isSettled() || n.rejectJoined(e);
|
|
534
|
+
},
|
|
535
|
+
reset() {
|
|
536
|
+
t.set(/* @__PURE__ */ new Set());
|
|
537
|
+
for (let [e, t] of n) if (t.refs === 0) n.delete(e);
|
|
538
|
+
else {
|
|
539
|
+
t.confirmed = !1, t.joinSent = !1;
|
|
540
|
+
for (let e of t.scopes) e.presenceHandlers?.reset();
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
restore() {
|
|
544
|
+
for (let [t, r] of n) if (r.refs !== 0) {
|
|
545
|
+
r.confirmed = !1, r.joinSent = !0;
|
|
546
|
+
try {
|
|
547
|
+
sendJoin(t);
|
|
548
|
+
} catch {
|
|
549
|
+
r.joinSent = !1;
|
|
550
|
+
}
|
|
551
|
+
if (r.localPresence !== void 0) try {
|
|
552
|
+
e.send(encode({
|
|
553
|
+
room: t,
|
|
554
|
+
state: r.localPresence,
|
|
555
|
+
type: "presence"
|
|
556
|
+
}));
|
|
557
|
+
} catch {}
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
rooms: t
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
function buildPublicScope(e, t, n, r, i, a, o, s) {
|
|
564
|
+
let c = s ?? (() => {});
|
|
565
|
+
return n ? {
|
|
566
|
+
get disposalSignal() {
|
|
567
|
+
return t;
|
|
568
|
+
},
|
|
569
|
+
dispose: c,
|
|
570
|
+
get disposed() {
|
|
571
|
+
return t.aborted;
|
|
572
|
+
},
|
|
573
|
+
get joined() {
|
|
574
|
+
return e.joined;
|
|
575
|
+
},
|
|
576
|
+
get name() {
|
|
577
|
+
return e.name;
|
|
578
|
+
},
|
|
579
|
+
onJoin(e) {
|
|
580
|
+
if (t.aborted) throw new PulseDisposedError(`Room "${a}"`);
|
|
581
|
+
return r.add(e), () => r.delete(e);
|
|
582
|
+
},
|
|
583
|
+
onLeave(e) {
|
|
584
|
+
if (t.aborted) throw new PulseDisposedError(`Room "${a}"`);
|
|
585
|
+
return i.add(e), () => i.delete(e);
|
|
586
|
+
},
|
|
587
|
+
get presence() {
|
|
588
|
+
return n;
|
|
589
|
+
},
|
|
590
|
+
updatePresence(e) {
|
|
591
|
+
o(a, e);
|
|
592
|
+
},
|
|
593
|
+
[Symbol.dispose]: c
|
|
594
|
+
} : {
|
|
595
|
+
get disposalSignal() {
|
|
596
|
+
return t;
|
|
597
|
+
},
|
|
598
|
+
dispose: c,
|
|
599
|
+
get disposed() {
|
|
600
|
+
return t.aborted;
|
|
601
|
+
},
|
|
602
|
+
get joined() {
|
|
603
|
+
return e.joined;
|
|
604
|
+
},
|
|
605
|
+
get name() {
|
|
606
|
+
return e.name;
|
|
607
|
+
},
|
|
608
|
+
[Symbol.dispose]: c
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
function createDeadScope(e, t) {
|
|
612
|
+
let n = new AbortController();
|
|
613
|
+
n.abort(new PulseDisposedError());
|
|
614
|
+
let r = Promise.reject(new PulseDisposedError(`Room "${e}"`));
|
|
615
|
+
return r.catch(() => {}), t ? {
|
|
616
|
+
disposalSignal: n.signal,
|
|
617
|
+
dispose() {},
|
|
618
|
+
disposed: !0,
|
|
619
|
+
joined: r,
|
|
620
|
+
name: e,
|
|
621
|
+
onJoin() {
|
|
622
|
+
throw new PulseDisposedError(`Room "${e}"`);
|
|
623
|
+
},
|
|
624
|
+
onLeave() {
|
|
625
|
+
throw new PulseDisposedError(`Room "${e}"`);
|
|
626
|
+
},
|
|
627
|
+
presence: createStore(/* @__PURE__ */ new Map(), () => {}),
|
|
628
|
+
updatePresence() {
|
|
629
|
+
throw new PulseDisposedError(`Room "${e}"`);
|
|
630
|
+
},
|
|
631
|
+
[Symbol.dispose]() {}
|
|
632
|
+
} : {
|
|
633
|
+
disposalSignal: n.signal,
|
|
634
|
+
dispose() {},
|
|
635
|
+
disposed: !0,
|
|
636
|
+
joined: r,
|
|
637
|
+
name: e,
|
|
638
|
+
[Symbol.dispose]() {}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
//#endregion
|
|
642
|
+
//#region src/pulse.ts
|
|
643
|
+
var ListenerMap = class {
|
|
644
|
+
map = /* @__PURE__ */ new Map();
|
|
645
|
+
add(e, t, n) {
|
|
646
|
+
let r = this.map.get(e);
|
|
647
|
+
r || (r = /* @__PURE__ */ new Map(), this.map.set(e, r));
|
|
648
|
+
let i = r.get(t);
|
|
649
|
+
return i || (i = /* @__PURE__ */ new Set(), r.set(t, i)), i.add(n), () => {
|
|
650
|
+
i.delete(n), i.size === 0 && r.delete(t), r.size === 0 && this.map.delete(e);
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
clear() {
|
|
654
|
+
this.map.clear();
|
|
655
|
+
}
|
|
656
|
+
dispatch(e, t, n) {
|
|
657
|
+
for (let r of this.map.get(e)?.get(t) ?? []) r(n);
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
function createPulse(e, n = {}) {
|
|
661
|
+
let r = new AbortController(), i = new ListenerMap(), a = /* @__PURE__ */ new Map(), o = t(), s = !1, c = !0, l = "closed";
|
|
662
|
+
function emitTap(e) {
|
|
663
|
+
o.emit(e);
|
|
664
|
+
}
|
|
665
|
+
function report(e) {
|
|
666
|
+
emitTap({
|
|
667
|
+
error: e,
|
|
668
|
+
type: "error"
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
let reportStoreError = (e) => report(new PulseError("External store subscriber threw", { cause: e })), u = createStore("closed", reportStoreError);
|
|
672
|
+
function sendInternal(e) {
|
|
673
|
+
f.send(e);
|
|
674
|
+
}
|
|
675
|
+
let d = createRoomRegistry({
|
|
676
|
+
disposalSignal: r.signal,
|
|
677
|
+
isOpen: () => f.open,
|
|
678
|
+
onStoreError: reportStoreError,
|
|
679
|
+
send: sendInternal
|
|
680
|
+
});
|
|
681
|
+
function restoreSession() {
|
|
682
|
+
for (let e of a.keys()) sendInternal(encode({
|
|
683
|
+
channel: e,
|
|
684
|
+
type: "subscribe"
|
|
685
|
+
}));
|
|
686
|
+
d.restore();
|
|
687
|
+
}
|
|
688
|
+
function resetRemoteSession() {
|
|
689
|
+
d.reset();
|
|
690
|
+
}
|
|
691
|
+
function handleTransportClose() {
|
|
692
|
+
c || (c = !0, p.stop(), resetRemoteSession(), d.rejectAll(new PulseConnectionError("Connection closed before room confirmation", e)));
|
|
693
|
+
}
|
|
694
|
+
let f = createConnection(e, n.protocols, n.reconnect, {
|
|
695
|
+
onClose() {
|
|
696
|
+
handleTransportClose();
|
|
697
|
+
},
|
|
698
|
+
onError: report,
|
|
699
|
+
onMessage(e) {
|
|
700
|
+
let t;
|
|
701
|
+
try {
|
|
702
|
+
t = decodeValidated(e.data);
|
|
703
|
+
} catch (t) {
|
|
704
|
+
report(t instanceof PulseProtocolError ? t : new PulseProtocolError("Failed to decode frame", e.data));
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
if (t.kind === "unknown") report(new PulseProtocolError(`Unknown frame type "${t.type}"`, e.data));
|
|
708
|
+
else try {
|
|
709
|
+
handleFrame(t);
|
|
710
|
+
} catch (e) {
|
|
711
|
+
report(new PulseProtocolError("Frame handler threw", t.frame, { cause: e }));
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
onOpen() {
|
|
715
|
+
c = !1;
|
|
716
|
+
try {
|
|
717
|
+
restoreSession(), p.start();
|
|
718
|
+
} catch (t) {
|
|
719
|
+
report(t instanceof PulseError ? t : new PulseConnectionError("Session restore failed", e));
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
onStatus(e) {
|
|
723
|
+
u.set(e), e !== l && (l = e, emitTap({
|
|
724
|
+
status: e,
|
|
725
|
+
type: "status-change"
|
|
726
|
+
}));
|
|
727
|
+
}
|
|
728
|
+
}), p = createHeartbeat(n.heartbeat, (e) => {
|
|
729
|
+
f.open && sendInternal(e);
|
|
730
|
+
}, () => f.forceReconnect(4e3, "heartbeat timeout"));
|
|
731
|
+
function handleFrame(e) {
|
|
732
|
+
let t = e.frame;
|
|
733
|
+
switch (t.type) {
|
|
734
|
+
case "error":
|
|
735
|
+
report(new PulseProtocolError(`Server error [${t.code}]: ${t.message}`, t));
|
|
736
|
+
break;
|
|
737
|
+
case "joined":
|
|
738
|
+
d.handleJoined(t.room);
|
|
739
|
+
break;
|
|
740
|
+
case "left":
|
|
741
|
+
d.handleLeft(t.room);
|
|
742
|
+
break;
|
|
743
|
+
case "message":
|
|
744
|
+
i.dispatch(t.channel ?? null, t.event, t.payload);
|
|
745
|
+
break;
|
|
746
|
+
case "pong":
|
|
747
|
+
p.onPong();
|
|
748
|
+
break;
|
|
749
|
+
case "presence_join":
|
|
750
|
+
d.handlePresenceJoin(t.room, t.id, t.state);
|
|
751
|
+
break;
|
|
752
|
+
case "presence_leave":
|
|
753
|
+
d.handlePresenceLeave(t.room, t.id);
|
|
754
|
+
break;
|
|
755
|
+
case "presence_state": d.handlePresenceState(t.room, t.members);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
function acquireChannel(e) {
|
|
759
|
+
let t = a.get(e) ?? 0;
|
|
760
|
+
a.set(e, t + 1), t === 0 && f.open && sendInternal(encode({
|
|
761
|
+
channel: e,
|
|
762
|
+
type: "subscribe"
|
|
763
|
+
}));
|
|
764
|
+
}
|
|
765
|
+
function releaseChannel(e) {
|
|
766
|
+
let t = a.get(e);
|
|
767
|
+
t && (t > 1 ? a.set(e, t - 1) : (a.delete(e), f.open && sendInternal(encode({
|
|
768
|
+
channel: e,
|
|
769
|
+
type: "unsubscribe"
|
|
770
|
+
}))));
|
|
771
|
+
}
|
|
772
|
+
return {
|
|
773
|
+
channel(e) {
|
|
774
|
+
if (s) throw new PulseDisposedError();
|
|
775
|
+
return acquireChannel(e), createChannel(e, (e, t, r) => {
|
|
776
|
+
let i = n.transform ? n.transform({
|
|
777
|
+
channel: e,
|
|
778
|
+
event: t,
|
|
779
|
+
payload: r
|
|
780
|
+
}) : {
|
|
781
|
+
channel: e,
|
|
782
|
+
event: t,
|
|
783
|
+
payload: r
|
|
784
|
+
};
|
|
785
|
+
i !== null && sendInternal(encode({
|
|
786
|
+
...i,
|
|
787
|
+
type: "message"
|
|
788
|
+
}));
|
|
789
|
+
}, (e, t, n) => i.add(e, t, n), r.signal, () => releaseChannel(e));
|
|
790
|
+
},
|
|
791
|
+
connect() {
|
|
792
|
+
return s ? Promise.reject(new PulseDisposedError()) : f.connect();
|
|
793
|
+
},
|
|
794
|
+
disconnect(e = 1e3, t = "") {
|
|
795
|
+
s || (handleTransportClose(), f.disconnect(e, t));
|
|
796
|
+
},
|
|
797
|
+
get disposalSignal() {
|
|
798
|
+
return r.signal;
|
|
799
|
+
},
|
|
800
|
+
dispose() {
|
|
801
|
+
s || (s = !0, p.stop(), c = !0, d.reset(), d.rejectAll(new PulseDisposedError()), f.dispose(), a.clear(), d.dispose(), i.clear(), emitTap({ type: "dispose" }), o.clear(), r.abort());
|
|
802
|
+
},
|
|
803
|
+
get disposed() {
|
|
804
|
+
return s;
|
|
805
|
+
},
|
|
806
|
+
on(e, t) {
|
|
807
|
+
if (s) throw new PulseDisposedError();
|
|
808
|
+
return i.add(null, e, t);
|
|
809
|
+
},
|
|
810
|
+
once(e, t) {
|
|
811
|
+
if (s) throw new PulseDisposedError();
|
|
812
|
+
let unsubscribe = () => {};
|
|
813
|
+
return unsubscribe = i.add(null, e, (e) => {
|
|
814
|
+
unsubscribe(), t(e);
|
|
815
|
+
}), unsubscribe;
|
|
816
|
+
},
|
|
817
|
+
room(e, t) {
|
|
818
|
+
if (s) throw new PulseDisposedError();
|
|
819
|
+
return d.createScope(e, !0, t);
|
|
820
|
+
},
|
|
821
|
+
get rooms() {
|
|
822
|
+
return d.rooms;
|
|
823
|
+
},
|
|
824
|
+
send(e, t) {
|
|
825
|
+
if (s) throw new PulseDisposedError();
|
|
826
|
+
let r = n.transform ? n.transform({
|
|
827
|
+
event: e,
|
|
828
|
+
payload: t
|
|
829
|
+
}) : {
|
|
830
|
+
event: e,
|
|
831
|
+
payload: t
|
|
832
|
+
};
|
|
833
|
+
r !== null && sendInternal(encode({
|
|
834
|
+
...r,
|
|
835
|
+
type: "message"
|
|
836
|
+
}));
|
|
837
|
+
},
|
|
838
|
+
get status() {
|
|
839
|
+
return u;
|
|
840
|
+
},
|
|
841
|
+
tap(e, t) {
|
|
842
|
+
if (s) throw new PulseDisposedError();
|
|
843
|
+
return o.tap(e, t);
|
|
844
|
+
},
|
|
845
|
+
[Symbol.dispose]() {
|
|
846
|
+
this.dispose();
|
|
847
|
+
},
|
|
848
|
+
wait(e, t) {
|
|
849
|
+
return createWaitPromise(e, r.signal, t, (e, t) => i.add(null, e, t));
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
//#endregion
|
|
854
|
+
export { PulseAbortError, PulseConnectionError, PulseDisposedError, PulseError, PulseProtocolError, PulseRoomTimeoutError, PulseTimeoutError, createPulse };
|
|
855
|
+
|
|
2
856
|
//# sourceMappingURL=pulse.js.map
|