From d28b6f663a38ff5a1654b73dcde85ea25f94ae56 Mon Sep 17 00:00:00 2001 From: oliviasculley Date: Fri, 6 Nov 2026 03:28:21 +0000 Subject: [PATCH 1/2] Send the fallback health check before the buffered join. The join blocks the transport process until the channel returns, so a ping queued behind it is not answered during a slow mount. Any frame on that websocket also passes the check. Fixes #6770. --- CHANGELOG.md | 3 +++ assets/js/phoenix/socket.js | 32 ++++++++++++++++++++++++ assets/test/socket_test.js | 45 ++++++++++++++++++++++++++++++++++ priv/static/phoenix.cjs.js | 30 +++++++++++++++++++++++ priv/static/phoenix.cjs.js.map | 4 +-- priv/static/phoenix.js | 30 +++++++++++++++++++++++ priv/static/phoenix.min.js | 4 +-- priv/static/phoenix.mjs | 30 +++++++++++++++++++++++ priv/static/phoenix.mjs.map | 4 +-- 9 files changed, 176 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87e0d54435..4921baec3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Bug fixes + * [phoenix.js] Send the fallback health check before the buffered join, and + treat any frame on that websocket as a pass + ([#6770](https://github.com/phoenixframework/phoenix/issues/6770)) * [Phoenix.ChannelTest] Fix test sockets violating the Phoenix.Socket.t typespec, which made Dialyzer treat every test join as never returning ([#5509](https://github.com/phoenixframework/phoenix/issues/5509)) diff --git a/assets/js/phoenix/socket.js b/assets/js/phoenix/socket.js index 65b9356611..d064d6be51 100644 --- a/assets/js/phoenix/socket.js +++ b/assets/js/phoenix/socket.js @@ -545,12 +545,41 @@ export default class Socket { if(this.hasLogger()) this.log("transport", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`) this.closeWasClean = false this.establishedConnections++ + // the health-check ping has to leave before the buffered join. The transport + // process blocks in Channel.Server.join until the join returns, so a ping + // sent after the join is not answered while the mount runs, and the fallback + // timer then drops a socket that is fine. + this.pingBeforeBufferedJoin() this.flushSendBuffer() this.reconnectTimer.reset() this.resetHeartbeat() this.stateChangeCallbacks.open.forEach(([, callback]) => callback()) } + /** + * @private + * + * Sends the fallback health check before flushSendBuffer. No-op unless a + * fallback attempt is waiting on the primary transport. + */ + pingBeforeBufferedJoin(){ + if(!this.fallbackTimer || this.transport === LongPoll){ return } + this.ping(() => this.passPrimaryHealthCheck()) + } + + /** + * @private + * + * The primary transport answered. Later frames on this connection also call + * this, because a frame is the proof the ping was waiting for. + */ + passPrimaryHealthCheck(){ + if(this.transport === LongPoll){ return } + this.primaryPassedHealthCheck = true + clearTimeout(this.fallbackTimer) + this.fallbackTimer = null + } + /** * @private */ @@ -854,6 +883,9 @@ export default class Socket { // connection may have been replaced or closed in the meantime (browsers also drop messages // that arrive once a WebSocket is closing) if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return } + // a frame on this connection is the health check. The ping reply is one + // such frame, and so is anything the server already sent. + if(this.fallbackTimer){ this.passPrimaryHealthCheck() } let {topic, event, payload, ref, join_ref} = msg if(ref && ref === this.pendingHeartbeatRef){ this.clearHeartbeats() diff --git a/assets/test/socket_test.js b/assets/test/socket_test.js index af2e2f5759..a74afcba4c 100644 --- a/assets/test/socket_test.js +++ b/assets/test/socket_test.js @@ -197,6 +197,51 @@ describe("with transports", function (){ expect(socket.stateChangeCallbacks.error.length).toBe(1) }) + it("sends the health check before the buffered join", function (){ + const channel = socket.channel("topic") + channel.join() + socket.connect() + connections[0].open() + + expect(connections[0].sent.map(([, , , event]) => event).slice(0, 2)).toEqual(["heartbeat", "phx_join"]) + }) + + it("does not fall back when the health check is answered before the join reply", function (){ + const channel = socket.channel("topic") + channel.join() + socket.connect() + connections[0].open() + const [[, heartbeatRef]] = connections[0].sent.filter(([, , topic, event]) => topic === "phoenix" && event === "heartbeat") + connections[0].onmessage({data: encode({ + ref: heartbeatRef, + topic: "phoenix", + event: "phx_reply", + payload: {status: "ok", response: {}} + })}) + + jest.advanceTimersByTime(2500) + + expect(socket.transport).toBe(StubWebSocket) + expect(socket.primaryPassedHealthCheck).toBe(true) + expect(socket.getSession("phx:fallback:LongPoll")).toBeNull() + }) + + it("does not fall back when any frame arrives on the open websocket", function (){ + socket.connect() + connections[0].open() + connections[0].onmessage({data: encode({ + ref: null, + topic: "phoenix", + event: "phx_reply", + payload: {status: "ok", response: {}} + })}) + + jest.advanceTimersByTime(2500) + + expect(socket.transport).toBe(StubWebSocket) + expect(socket.primaryPassedHealthCheck).toBe(true) + }) + it("does not accumulate the health check pings of connections that dropped", function (){ socket.connect() for(let i = 0; i < 3; i++){ diff --git a/priv/static/phoenix.cjs.js b/priv/static/phoenix.cjs.js index 09f856431d..1096fe6b08 100644 --- a/priv/static/phoenix.cjs.js +++ b/priv/static/phoenix.cjs.js @@ -1617,11 +1617,38 @@ var Socket = class { if (this.hasLogger()) this.log("transport", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`); this.closeWasClean = false; this.establishedConnections++; + this.pingBeforeBufferedJoin(); this.flushSendBuffer(); this.reconnectTimer.reset(); this.resetHeartbeat(); this.stateChangeCallbacks.open.forEach(([, callback]) => callback()); } + /** + * @private + * + * Sends the fallback health check before flushSendBuffer. No-op unless a + * fallback attempt is waiting on the primary transport. + */ + pingBeforeBufferedJoin() { + if (!this.fallbackTimer || this.transport === LongPoll) { + return; + } + this.ping(() => this.passPrimaryHealthCheck()); + } + /** + * @private + * + * The primary transport answered. Later frames on this connection also call + * this, because a frame is the proof the ping was waiting for. + */ + passPrimaryHealthCheck() { + if (this.transport === LongPoll) { + return; + } + this.primaryPassedHealthCheck = true; + clearTimeout(this.fallbackTimer); + this.fallbackTimer = null; + } /** * @private */ @@ -1900,6 +1927,9 @@ var Socket = class { if (connection !== this.connection || wasOpen && !connection.isOpen()) { return; } + if (this.fallbackTimer) { + this.passPrimaryHealthCheck(); + } let { topic, event, payload, ref, join_ref } = msg; if (ref && ref === this.pendingHeartbeatRef) { this.clearHeartbeats(); diff --git a/priv/static/phoenix.cjs.js.map b/priv/static/phoenix.cjs.js.map index f013604845..d34a086e71 100644 --- a/priv/static/phoenix.cjs.js.map +++ b/priv/static/phoenix.cjs.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../assets/js/phoenix/index.js", "../../assets/js/phoenix/utils.js", "../../assets/js/phoenix/constants.js", "../../assets/js/phoenix/push.js", "../../assets/js/phoenix/timer.js", "../../assets/js/phoenix/channel.js", "../../assets/js/phoenix/ajax.js", "../../assets/js/phoenix/longpoll.js", "../../assets/js/phoenix/presence.js", "../../assets/js/phoenix/serializer.js", "../../assets/js/phoenix/connection.js", "../../assets/js/phoenix/socket.js"], - "sourcesContent": ["/**\n * Phoenix Channels JavaScript client\n *\n * ## Socket Connection\n *\n * A single connection is established to the server and\n * channels are multiplexed over the connection.\n * Connect to the server using the `Socket` class:\n *\n * ```javascript\n * let socket = new Socket(\"/socket\", {params: {userToken: \"123\"}})\n * socket.connect()\n * ```\n *\n * The `Socket` constructor takes the mount point of the socket,\n * the authentication params, as well as options that can be found in\n * the Socket docs, such as configuring the `LongPoll` transport, and\n * heartbeat.\n *\n * ## Channels\n *\n * Channels are isolated, concurrent processes on the server that\n * subscribe to topics and broker events between the client and server.\n * To join a channel, you must provide the topic, and channel params for\n * authorization. Here's an example chat room example where `\"new_msg\"`\n * events are listened for, messages are pushed to the server, and\n * the channel is joined with ok/error/timeout matches:\n *\n * ```\n * let channel = socket.channel(\"room:123\", {token: roomToken})\n * channel.on(\"new_msg\", msg => console.log(\"Got message\", msg) )\n * $input.onEnter( e => {\n * channel.push(\"new_msg\", {body: e.target.val}, 10000)\n * .receive(\"ok\", (msg) => console.log(\"created message\", msg) )\n * .receive(\"error\", (reasons) => console.log(\"create failed\", reasons) )\n * .receive(\"timeout\", () => console.log(\"Networking issue...\") )\n * })\n *\n * channel.join()\n * .receive(\"ok\", ({messages}) => console.log(\"catching up\", messages) )\n * .receive(\"error\", ({reason}) => console.log(\"failed join\", reason) )\n * .receive(\"timeout\", () => console.log(\"Networking issue. Still waiting...\"))\n *```\n *\n * ## Joining\n *\n * Creating a channel with `socket.channel(topic, params)`, binds the params to\n * `channel.params`, which are sent up on `channel.join()`.\n * Subsequent rejoins will send up the modified params for\n * updating authorization params, or passing up last_message_id information.\n * Successful joins receive an \"ok\" status, while unsuccessful joins\n * receive \"error\".\n *\n * With the default serializers and WebSocket transport, JSON text frames are\n * used for pushing a JSON object literal. If an `ArrayBuffer` instance is provided,\n * binary encoding will be used and the message will be sent with the binary\n * opcode.\n *\n * *Note*: binary messages are only supported on the WebSocket transport.\n *\n * ## Duplicate Join Subscriptions\n *\n * While the client may join any number of topics on any number of channels,\n * the client may only hold a single subscription for each unique topic at any\n * given time. When attempting to create a duplicate subscription,\n * the server will close the existing channel, log a warning, and\n * spawn a new channel for the topic. The client will have their\n * `channel.onClose` callbacks fired for the existing channel, and the new\n * channel join will have its receive hooks processed as normal.\n *\n * ## Pushing Messages\n *\n * From the previous example, we can see that pushing messages to the server\n * can be done with `channel.push(eventName, payload)` and we can optionally\n * receive responses from the push. Additionally, we can use\n * `receive(\"timeout\", callback)` to abort waiting for our other `receive` hooks\n * and take action after some period of waiting. The default timeout is 10000ms.\n *\n *\n * ## Socket Hooks\n *\n * Lifecycle events of the multiplexed connection can be hooked into via\n * `socket.onError()` and `socket.onClose()` events, ie:\n *\n * ```\n * socket.onError( () => console.log(\"there was an error with the connection!\") )\n * socket.onClose( () => console.log(\"the connection dropped\") )\n * ```\n *\n *\n * ## Channel Hooks\n *\n * For each joined channel, you can bind to `onError` and `onClose` events\n * to monitor the channel lifecycle, ie:\n *\n * ```\n * channel.onError( () => console.log(\"there was an error!\") )\n * channel.onClose( () => console.log(\"the channel has gone away gracefully\") )\n * ```\n *\n * ### onError hooks\n *\n * `onError` hooks are invoked if the socket connection drops, or the channel\n * crashes on the server. In either case, a channel rejoin is attempted\n * automatically in an exponential backoff manner.\n *\n * ### onClose hooks\n *\n * `onClose` hooks are invoked only in two cases. 1) the channel explicitly\n * closed on the server, or 2). The client explicitly closed, by calling\n * `channel.leave()`\n *\n *\n * ## Presence\n *\n * The `Presence` object provides features for syncing presence information\n * from the server with the client and handling presences joining and leaving.\n *\n * ### Syncing state from the server\n *\n * To sync presence state from the server, first instantiate an object and\n * pass your channel in to track lifecycle events:\n *\n * ```\n * let channel = socket.channel(\"some:topic\")\n * let presence = new Presence(channel)\n * ```\n *\n * Next, use the `presence.onSync` callback to react to state changes\n * from the server. For example, to render the list of users every time\n * the list changes, you could write:\n *\n * ```\n * presence.onSync(() => {\n * myRenderUsersFunction(presence.list())\n * })\n * ```\n *\n * ### Listing Presences\n *\n * `presence.list` is used to return a list of presence information\n * based on the local state of metadata. By default, all presence\n * metadata is returned, but a `listBy` function can be supplied to\n * allow the client to select which metadata to use for a given presence.\n * For example, you may have a user online from different devices with\n * a metadata status of \"online\", but they have set themselves to \"away\"\n * on another device. In this case, the app may choose to use the \"away\"\n * status for what appears on the UI. The example below defines a `listBy`\n * function which prioritizes the first metadata which was registered for\n * each user. This could be the first tab they opened, or the first device\n * they came online from:\n *\n * ```\n * let listBy = (id, {metas: [first, ...rest]}) => {\n * first.count = rest.length + 1 // count of this user's presences\n * first.id = id\n * return first\n * }\n * let onlineUsers = presence.list(listBy)\n * ```\n *\n * ### Handling individual presence join and leave events\n *\n * The `presence.onJoin` and `presence.onLeave` callbacks can be used to\n * react to individual presences joining and leaving the app. For example:\n *\n * ```\n * let presence = new Presence(channel)\n *\n * // detect if user has joined for the 1st time or from another tab/device\n * presence.onJoin((id, current, newPres) => {\n * if(!current){\n * console.log(\"user has entered for the first time\", newPres)\n * } else {\n * console.log(\"user additional presence\", newPres)\n * }\n * })\n *\n * // detect if user has left from all tabs/devices, or is still present\n * presence.onLeave((id, current, leftPres) => {\n * if(current.metas.length === 0){\n * console.log(\"user has left from all devices\", leftPres)\n * } else {\n * console.log(\"user left from a device\", leftPres)\n * }\n * })\n * // receive presence data from server\n * presence.onSync(() => {\n * displayUsers(presence.list())\n * })\n * ```\n * @module phoenix\n */\n\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Presence from \"./presence\"\nimport Serializer from \"./serializer\"\nimport Socket from \"./socket\"\n\nexport {\n Channel,\n LongPoll,\n Presence,\n Serializer,\n Socket\n}\n", "// wraps value in closure or returns closure\nexport let closure = (value) => {\n if(typeof value === \"function\"){\n return value\n } else {\n let closure = function (){ return value }\n return closure\n }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || globalThis\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const MAX_LONGPOLL_BATCH_SIZE = 100;\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n closed: \"closed\",\n errored: \"errored\",\n joined: \"joined\",\n joining: \"joining\",\n leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n close: \"phx_close\",\n error: \"phx_error\",\n join: \"phx_join\",\n reply: \"phx_reply\",\n leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n longpoll: \"longpoll\",\n websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n complete: 4\n}\nexport const AUTH_TOKEN_PREFIX = \"base64url.bearer.phx.\"\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n constructor(channel, event, payload, timeout){\n this.channel = channel\n this.event = event\n this.payload = payload || function (){ return {} }\n this.receivedResp = null\n this.timeout = timeout\n this.timeoutTimer = null\n this.recHooks = []\n this.sent = false\n }\n\n /**\n *\n * @param {number} timeout\n */\n resend(timeout){\n this.timeout = timeout\n this.reset()\n this.send()\n }\n\n /**\n *\n */\n send(){\n if(this.hasReceived(\"timeout\")){ return }\n this.startTimeout()\n this.sent = true\n this.channel.socket.push({\n topic: this.channel.topic,\n event: this.event,\n payload: this.payload(),\n ref: this.ref,\n join_ref: this.channel.joinRef()\n }, this.channel)\n }\n\n /**\n *\n * @param {*} status\n * @param {*} callback\n */\n receive(status, callback){\n if(this.hasReceived(status)){\n callback(this.receivedResp.response)\n }\n\n this.recHooks.push({status, callback})\n return this\n }\n\n /**\n * @private\n */\n reset(){\n this.cancelRefEvent()\n this.ref = null\n this.refEvent = null\n this.receivedResp = null\n this.sent = false\n }\n\n /**\n * @private\n */\n matchReceive({status, response, _ref}){\n this.recHooks.filter(h => h.status === status)\n .forEach(h => h.callback(response))\n }\n\n /**\n * @private\n */\n cancelRefEvent(){\n if(!this.refEvent){ return }\n this.channel.off(this.refEvent)\n }\n\n /**\n * @private\n */\n cancelTimeout(){\n clearTimeout(this.timeoutTimer)\n this.timeoutTimer = null\n }\n\n /**\n * @private\n */\n startTimeout(){\n if(this.timeoutTimer){ this.cancelTimeout() }\n this.cancelRefEvent()\n this.ref = this.channel.socket.makeRef()\n this.refEvent = this.channel.replyEventName(this.ref)\n\n this.channel.on(this.refEvent, payload => {\n this.cancelRefEvent()\n this.cancelTimeout()\n this.receivedResp = payload\n this.matchReceive(payload)\n })\n\n this.timeoutTimer = setTimeout(() => {\n this.trigger(\"timeout\", {})\n }, this.timeout)\n }\n\n /**\n * @private\n */\n hasReceived(status){\n return this.receivedResp && this.receivedResp.status === status\n }\n\n /**\n * @private\n */\n trigger(status, response){\n this.channel.trigger(this.refEvent, {status, response})\n }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n * return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n constructor(callback, timerCalc){\n this.callback = callback\n this.timerCalc = timerCalc\n this.timer = null\n this.tries = 0\n }\n\n reset(){\n this.tries = 0\n clearTimeout(this.timer)\n }\n\n /**\n * Cancels any previous scheduleTimeout and schedules callback\n */\n scheduleTimeout(){\n clearTimeout(this.timer)\n\n this.timer = setTimeout(() => {\n this.tries = this.tries + 1\n this.callback()\n }, this.timerCalc(this.tries + 1))\n }\n}\n", "import {closure} from \"./utils\"\nimport {\n CHANNEL_EVENTS,\n CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n constructor(topic, params, socket){\n this.state = CHANNEL_STATES.closed\n this.topic = topic\n this.params = closure(params || {})\n this.socket = socket\n this.bindings = []\n this.bindingRef = 0\n this.timeout = this.socket.timeout\n this.joinedOnce = false\n this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n this.pushBuffer = []\n this.stateChangeRefs = []\n\n this.rejoinTimer = new Timer(() => {\n if(this.socket.isConnected()){ this.rejoin() }\n }, this.socket.rejoinAfterMs)\n this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n this.stateChangeRefs.push(this.socket.onOpen(() => {\n this.rejoinTimer.reset()\n if(this.isErrored()){ this.rejoin() }\n })\n )\n this.joinPush.receive(\"ok\", () => {\n this.state = CHANNEL_STATES.joined\n this.rejoinTimer.reset()\n this.pushBuffer.forEach(pushEvent => pushEvent.send())\n this.pushBuffer = []\n })\n this.joinPush.receive(\"error\", () => {\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.onClose(() => {\n this.rejoinTimer.reset()\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n this.state = CHANNEL_STATES.closed\n this.socket.remove(this)\n })\n this.onError(reason => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n if(this.isJoining()){ this.joinPush.reset() }\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.joinPush.receive(\"timeout\", () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n leavePush.send()\n this.state = CHANNEL_STATES.errored\n this.joinPush.reset()\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n this.trigger(this.replyEventName(ref), payload)\n })\n }\n\n /**\n * Join the channel\n * @param {integer} timeout\n * @returns {Push}\n */\n join(timeout = this.timeout){\n if(this.joinedOnce){\n throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n } else {\n this.timeout = timeout\n this.joinedOnce = true\n this.rejoin()\n return this.joinPush\n }\n }\n\n /**\n * Hook into channel close\n * @param {Function} callback\n */\n onClose(callback){\n this.on(CHANNEL_EVENTS.close, callback)\n }\n\n /**\n * Hook into channel errors\n * @param {Function} callback\n */\n onError(callback){\n return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n }\n\n /**\n * Subscribes on channel events\n *\n * Subscription returns a ref counter, which can be used later to\n * unsubscribe the exact event listener\n *\n * @example\n * const ref1 = channel.on(\"event\", do_stuff)\n * const ref2 = channel.on(\"event\", do_other_stuff)\n * channel.off(\"event\", ref1)\n * // Since unsubscription, do_stuff won't fire,\n * // while do_other_stuff will keep firing on the \"event\"\n *\n * @param {string} event\n * @param {Function} callback\n * @returns {integer} ref\n */\n on(event, callback){\n let ref = this.bindingRef++\n this.bindings.push({event, ref, callback})\n return ref\n }\n\n /**\n * Unsubscribes off of channel events\n *\n * Use the ref returned from a channel.on() to unsubscribe one\n * handler, or pass nothing for the ref to unsubscribe all\n * handlers for the given event.\n *\n * @example\n * // Unsubscribe the do_stuff handler\n * const ref1 = channel.on(\"event\", do_stuff)\n * channel.off(\"event\", ref1)\n *\n * // Unsubscribe all handlers from event\n * channel.off(\"event\")\n *\n * @param {string} event\n * @param {integer} ref\n */\n off(event, ref){\n this.bindings = this.bindings.filter((bind) => {\n return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n })\n }\n\n /**\n * @private\n */\n canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n /**\n * Sends a message `event` to phoenix with the payload `payload`.\n * Phoenix receives this in the `handle_in(event, payload, socket)`\n * function. if phoenix replies or it times out (default 10000ms),\n * then optionally the reply can be received.\n *\n * @example\n * channel.push(\"event\")\n * .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n * .receive(\"error\", err => console.log(\"phoenix errored\", err))\n * .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n * @param {string} event\n * @param {Object} payload\n * @param {number} [timeout]\n * @returns {Push}\n */\n push(event, payload, timeout = this.timeout){\n payload = payload || {}\n if(!this.joinedOnce){\n throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n }\n let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n if(this.canPush()){\n pushEvent.send()\n } else {\n pushEvent.startTimeout()\n this.pushBuffer.push(pushEvent)\n }\n\n return pushEvent\n }\n\n /** Leaves the channel\n *\n * Unsubscribes from server events, and\n * instructs channel to terminate on server\n *\n * Triggers onClose() hooks\n *\n * To receive leave acknowledgements, use the `receive`\n * hook to bind to the server ack, ie:\n *\n * @example\n * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n *\n * @param {integer} timeout\n * @returns {Push}\n */\n leave(timeout = this.timeout){\n this.rejoinTimer.reset()\n this.joinPush.cancelTimeout()\n\n this.state = CHANNEL_STATES.leaving\n let onClose = () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n this.trigger(CHANNEL_EVENTS.close, \"leave\")\n }\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n leavePush.receive(\"ok\", () => onClose())\n .receive(\"timeout\", () => onClose())\n leavePush.send()\n if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n return leavePush\n }\n\n /**\n * Overridable message hook\n *\n * Receives all events for specialized message handling\n * before dispatching to the channel callbacks.\n *\n * Must return the payload, modified or unmodified\n * @param {string} event\n * @param {Object} payload\n * @param {integer} ref\n * @returns {Object}\n */\n onMessage(_event, payload, _ref){ return payload }\n\n /**\n * @private\n */\n isMember(topic, event, payload, joinRef){\n if(this.topic !== topic){ return false }\n\n if(joinRef && joinRef !== this.joinRef()){\n if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n return false\n } else {\n return true\n }\n }\n\n /**\n * @private\n */\n joinRef(){ return this.joinPush.ref }\n\n /**\n * @private\n */\n rejoin(timeout = this.timeout){\n if(this.isLeaving()){ return }\n this.socket.leaveOpenTopic(this.topic)\n this.state = CHANNEL_STATES.joining\n this.joinPush.resend(timeout)\n }\n\n /**\n * @private\n */\n trigger(event, payload, ref, joinRef){\n let handledPayload = this.onMessage(event, payload, ref, joinRef)\n if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n for(let i = 0; i < eventBindings.length; i++){\n let bind = eventBindings[i]\n bind.callback(handledPayload, ref, joinRef || this.joinRef())\n }\n }\n\n /**\n * @private\n */\n replyEventName(ref){ return `chan_reply_${ref}` }\n\n /**\n * @private\n */\n isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n /**\n * @private\n */\n isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n /**\n * @private\n */\n isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n /**\n * @private\n */\n isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n /**\n * @private\n */\n isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n global,\n XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n static request(method, endPoint, headers, body, timeout, ontimeout, callback){\n if(global.XDomainRequest){\n let req = new global.XDomainRequest() // IE8, IE9\n return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n } else if(global.XMLHttpRequest){\n let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)\n } else if(global.fetch && global.AbortController){\n // Fetch with AbortController for modern browsers\n return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)\n } else {\n throw new Error(\"No suitable XMLHttpRequest implementation found\")\n }\n }\n\n static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){\n let options = {\n method,\n headers,\n body,\n }\n let controller = null\n let timeoutId = null\n if(timeout){\n controller = new AbortController()\n timeoutId = setTimeout(() => controller.abort(), timeout)\n options.signal = controller.signal\n }\n global.fetch(endPoint, options)\n .then(response => response.text())\n .then(data => this.parseJSON(data))\n .then(data => {\n if(timeoutId){ clearTimeout(timeoutId) }\n callback && callback(data)\n })\n .catch(err => {\n if(timeoutId){ clearTimeout(timeoutId) }\n if(err.name === \"AbortError\" && ontimeout){\n ontimeout()\n } else {\n callback && callback(null)\n }\n })\n return controller\n }\n\n static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n req.timeout = timeout\n req.open(method, endPoint)\n req.onload = () => {\n let response = this.parseJSON(req.responseText)\n callback && callback(response)\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n // Work around bug in IE9 that requires an attached onprogress handler\n req.onprogress = () => { }\n\n req.send(body)\n return req\n }\n\n static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){\n req.open(method, endPoint, true)\n req.timeout = timeout\n for(let [key, value] of Object.entries(headers)){\n req.setRequestHeader(key, value)\n }\n req.onerror = () => callback && callback(null)\n req.onreadystatechange = () => {\n if(req.readyState === XHR_STATES.complete && callback){\n let response = this.parseJSON(req.responseText)\n callback(response)\n }\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n req.send(body)\n return req\n }\n\n static parseJSON(resp){\n if(!resp || resp === \"\"){ return null }\n\n try {\n return JSON.parse(resp)\n } catch {\n console && console.log(\"failed to parse JSON response\", resp)\n return null\n }\n }\n\n static serialize(obj, parentKey){\n let queryStr = []\n for(var key in obj){\n if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n let paramKey = parentKey ? `${parentKey}[${key}]` : key\n let paramVal = obj[key]\n if(typeof paramVal === \"object\"){\n queryStr.push(this.serialize(paramVal, paramKey))\n } else {\n queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n }\n }\n return queryStr.join(\"&\")\n }\n\n static appendParams(url, params){\n if(Object.keys(params).length === 0){ return url }\n\n let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n return `${url}${prefix}${this.serialize(params)}`\n }\n}\n", "import {\n SOCKET_STATES,\n TRANSPORTS,\n AUTH_TOKEN_PREFIX,\n MAX_LONGPOLL_BATCH_SIZE\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBufferToBase64 = (buffer) => {\n let binary = \"\"\n let bytes = new Uint8Array(buffer)\n let len = bytes.byteLength\n for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }\n return btoa(binary)\n}\n\nexport default class LongPoll {\n\n constructor(endPoint, protocols){\n // we only support subprotocols for authToken\n // [\"phoenix\", \"base64url.bearer.phx.BASE64_ENCODED_TOKEN\"]\n if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){\n this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))\n }\n this.endPoint = null\n this.token = null\n this.skipHeartbeat = true\n // the pending requests and queued messages, which are cancelled when we stop\n this.reqs = new Set()\n this.messageTimers = new Set()\n this.awaitingBatchAck = false\n this.currentBatch = null\n this.currentBatchTimer = null\n this.batchBuffer = []\n this.onopen = function (){ } // noop\n this.onerror = function (){ } // noop\n this.onmessage = function (){ } // noop\n this.onclose = function (){ } // noop\n this.pollEndpoint = this.normalizeEndpoint(endPoint)\n this.readyState = SOCKET_STATES.connecting\n // we must wait for the caller to finish setting up our callbacks and timeout properties,\n // and it might close us before that, e.g. when disconnecting right after connecting\n setTimeout(() => this.poll(), 0)\n }\n\n normalizeEndpoint(endPoint){\n return (endPoint\n .replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")\n .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n }\n\n endpointURL(){\n return this.pollEndpoint\n }\n\n closeAndRetry(error, code, reason, wasClean){\n if(!this.isActive()){ return }\n // like a failed WebSocket, we are no longer open when we emit the error and then the close.\n // We are connecting again before their callbacks run, so that they can still close us for\n // good, e.g. when they disconnect the socket\n this.stop(SOCKET_STATES.connecting)\n this.onerror(error)\n if(this.isActive()){ this.emitClose(code, reason, wasClean) }\n }\n\n ontimeout(){\n this.closeAndRetry(\"timeout\", 1005, \"timeout\", false)\n }\n\n isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }\n\n poll(){\n // onopen can close us before the next poll, just as the caller can close us\n // before the constructor's deferred first poll\n if(!this.isActive()){ return }\n const headers = {\"Accept\": \"application/json\"}\n if(this.authToken){\n headers[\"X-Phoenix-AuthToken\"] = this.authToken\n }\n this.ajax(\"GET\", headers, null, () => this.ontimeout(), resp => {\n if(resp){\n var {status, token, messages} = resp\n if(status === 410 && this.token !== null){\n // In case we already have a token, this means that our existing session\n // is gone. We fail so that the client rejoins its channels.\n this.closeAndRetry(410, 3410, \"session_gone\", false)\n return\n }\n this.token = token\n } else {\n status = 0\n }\n\n switch(status){\n case 200:\n messages.forEach(msg => {\n // Tasks are what things like event handlers, setTimeout callbacks,\n // promise resolves and more are run within.\n // In modern browsers, there are two different kinds of tasks,\n // microtasks and macrotasks.\n // Microtasks are mainly used for Promises, while macrotasks are\n // used for everything else.\n // Microtasks always have priority over macrotasks. If the JS engine\n // is looking for a task to run, it will always try to empty the\n // microtask queue before attempting to run anything from the\n // macrotask queue.\n //\n // For the WebSocket transport, messages always arrive in their own\n // event. This means that if any promises are resolved from within,\n // their callbacks will always finish execution by the time the\n // next message event handler is run.\n //\n // In order to emulate this behaviour, we need to make sure each\n // onmessage handler is run within its own macrotask.\n //\n // We might stop before that, e.g. when a POST times out or a message\n // handler disconnects, which cancels the remaining messages.\n const timer = setTimeout(() => {\n this.messageTimers.delete(timer)\n this.onmessage({data: msg})\n }, 0)\n this.messageTimers.add(timer)\n })\n this.poll()\n break\n case 204:\n this.poll()\n break\n case 410:\n this.readyState = SOCKET_STATES.open\n this.onopen({})\n this.poll()\n break\n case 403:\n this.onerror(403)\n this.close(1008, \"forbidden\", false)\n break\n case 0:\n case 500:\n this.closeAndRetry(500, 1011, \"internal server error\", false)\n break\n default: throw new Error(`unhandled poll status ${status}`)\n }\n })\n }\n\n // we collect all pushes within the current event loop by\n // setTimeout 0, which optimizes back-to-back procedural\n // pushes against an empty buffer\n\n send(body){\n if(typeof(body) !== \"string\"){ body = arrayBufferToBase64(body) }\n if(this.currentBatch){\n this.currentBatch.push(body)\n } else if(this.awaitingBatchAck){\n this.batchBuffer.push(body)\n } else {\n this.currentBatch = [body]\n this.currentBatchTimer = setTimeout(() => {\n this.batchSend(this.currentBatch)\n this.currentBatch = null\n }, 0)\n }\n }\n\n batchSend(messages, offset = 0){\n this.awaitingBatchAck = true\n const next = offset + MAX_LONGPOLL_BATCH_SIZE\n const batch = messages.slice(offset, next)\n this.ajax(\"POST\", {\"Content-Type\": \"application/x-ndjson\"}, batch.join(\"\\n\"), () => this.ontimeout(), resp => {\n if(!resp || resp.status !== 200){\n // this calls stop which already clears awaitingBatchAck\n this.closeAndRetry(resp && resp.status, 1011, \"internal server error\", false)\n } else if(next < messages.length){\n this.batchSend(messages, next)\n } else if(this.batchBuffer.length > 0){\n this.batchSend(this.batchBuffer)\n this.batchBuffer = []\n } else {\n this.awaitingBatchAck = false\n }\n })\n }\n\n close(code, reason, wasClean){\n this.stop(SOCKET_STATES.closed)\n this.emitClose(code, reason, wasClean)\n }\n\n /**\n * Cancels the pending requests, queued messages and outgoing batches.\n */\n stop(readyState){\n this.readyState = readyState\n // the requests are no longer ours before we abort them, as aborting can invoke their\n // callbacks, synchronously for XHR and asynchronously for fetch\n const reqs = this.reqs\n this.reqs = new Set()\n for(let {req} of reqs){ req && req.abort() }\n for(let timer of this.messageTimers){ clearTimeout(timer) }\n this.messageTimers.clear()\n this.batchBuffer = []\n this.awaitingBatchAck = false\n clearTimeout(this.currentBatchTimer)\n this.currentBatchTimer = null\n this.currentBatch = null\n }\n\n /**\n * Emits the close event. Like a WebSocket closed without a code, it reports 1005 (no status)\n * unless one is given, as 1000 would look like the server closing the connection normally.\n */\n emitClose(code = 1005, reason, wasClean = true){\n let opts = {code, reason, wasClean}\n if(typeof(CloseEvent) !== \"undefined\"){\n this.onclose(new CloseEvent(\"close\", opts))\n } else {\n this.onclose(opts)\n }\n }\n\n ajax(method, headers, body, onCallerTimeout, callback){\n // the request is ours until it completes or we stop, and the callbacks of a request\n // that is not ours anymore are ignored. It is ours before it starts, as a request can\n // complete while it starts.\n const request = {req: null}\n this.reqs.add(request)\n let ontimeout = () => {\n if(this.reqs.delete(request)){ onCallerTimeout() }\n }\n if(this.token !== null){\n headers = Object.assign({}, headers, {\"X-Phoenix-Longpoll-Token\": this.token})\n }\n request.req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {\n if(this.reqs.delete(request)){ callback(resp) }\n })\n }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n * for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n constructor(channel, opts = {}){\n let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n this.state = Object.create(null)\n this.pendingDiffs = []\n this.channel = channel\n this.joinRef = null\n this.caller = {\n onJoin: function (){ },\n onLeave: function (){ },\n onSync: function (){ }\n }\n\n this.channel.on(events.state, newState => {\n let {onJoin, onLeave, onSync} = this.caller\n\n this.joinRef = this.channel.joinRef()\n this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n this.pendingDiffs.forEach(diff => {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n })\n this.pendingDiffs = []\n onSync()\n })\n\n this.channel.on(events.diff, diff => {\n let {onJoin, onLeave, onSync} = this.caller\n\n if(this.inPendingSyncState()){\n this.pendingDiffs.push(diff)\n } else {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n onSync()\n }\n })\n }\n\n onJoin(callback){ this.caller.onJoin = callback }\n\n onLeave(callback){ this.caller.onLeave = callback }\n\n onSync(callback){ this.caller.onSync = callback }\n\n list(by){ return Presence.list(this.state, by) }\n\n inPendingSyncState(){\n return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n }\n\n // lower-level public static API\n\n /**\n * Used to sync the list of presences on the server\n * with the client's state. An optional `onJoin` and `onLeave` callback can\n * be provided to react to changes in the client's local presences across\n * disconnects and reconnects with the server.\n *\n * @returns {Presence}\n */\n static syncState(currentState, newState, onJoin, onLeave){\n let state = this.toNullProtoObj(this.clone(currentState))\n newState = this.toNullProtoObj(newState)\n let joins = Object.create(null)\n let leaves = Object.create(null)\n\n this.map(state, (key, presence) => {\n if(!newState[key]){\n leaves[key] = presence\n }\n })\n this.map(newState, (key, newPresence) => {\n let currentPresence = state[key]\n if(currentPresence){\n let newRefs = newPresence.metas.map(m => m.phx_ref)\n let curRefs = currentPresence.metas.map(m => m.phx_ref)\n let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n if(joinedMetas.length > 0){\n joins[key] = newPresence\n joins[key].metas = joinedMetas\n }\n if(leftMetas.length > 0){\n leaves[key] = this.clone(currentPresence)\n leaves[key].metas = leftMetas\n }\n } else {\n joins[key] = newPresence\n }\n })\n return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n }\n\n /**\n *\n * Used to sync a diff of presence join and leave\n * events from the server, as they happen. Like `syncState`, `syncDiff`\n * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n * joining or leaving from a device.\n *\n * @returns {Presence}\n */\n static syncDiff(state, diff, onJoin, onLeave){\n state = this.toNullProtoObj(state)\n let {joins, leaves} = this.clone(diff)\n if(!onJoin){ onJoin = function (){ } }\n if(!onLeave){ onLeave = function (){ } }\n\n this.map(joins, (key, newPresence) => {\n let currentPresence = state[key]\n state[key] = this.clone(newPresence)\n if(currentPresence){\n let joinedRefs = state[key].metas.map(m => m.phx_ref)\n let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n state[key].metas.unshift(...curMetas)\n }\n onJoin(key, currentPresence, newPresence)\n })\n this.map(leaves, (key, leftPresence) => {\n let currentPresence = state[key]\n if(!currentPresence){ return }\n let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n currentPresence.metas = currentPresence.metas.filter(p => {\n return refsToRemove.indexOf(p.phx_ref) < 0\n })\n onLeave(key, currentPresence, leftPresence)\n if(currentPresence.metas.length === 0){\n delete state[key]\n }\n })\n return state\n }\n\n /**\n * Returns the array of presences, with selected metadata.\n *\n * @param {Object} presences\n * @param {Function} chooser\n *\n * @returns {Presence}\n */\n static list(presences, chooser){\n if(!chooser){ chooser = function (key, pres){ return pres } }\n\n return this.map(presences, (key, presence) => {\n return chooser(key, presence)\n })\n }\n\n // private\n\n static map(obj, func){\n return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n }\n\n // Presence keys are chosen on the server and may collide with\n // Object.prototype properties (\"__proto__\", \"constructor\", ...), so any\n // object indexed by presence key must not have a prototype chain\n //\n // TODO: replace the null-prototype objects with Maps in Phoenix 2.0\n // (breaking change for the lower-level static API)\n static toNullProtoObj(obj){\n if(Object.getPrototypeOf(obj) === null){ return obj }\n let cleaned = Object.create(null)\n Object.getOwnPropertyNames(obj).forEach(key => {\n cleaned[key] = obj[key]\n })\n return cleaned\n }\n\n static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n HEADER_LENGTH: 1,\n META_LENGTH: 4,\n KINDS: {push: 0, reply: 1, broadcast: 2},\n\n encode(msg, callback){\n if(msg.payload.constructor === ArrayBuffer){\n return callback(this.binaryEncode(msg))\n } else {\n let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n return callback(JSON.stringify(payload))\n }\n },\n\n decode(rawPayload, callback){\n if(rawPayload.constructor === ArrayBuffer){\n return callback(this.binaryDecode(rawPayload))\n } else {\n let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n return callback({join_ref, ref, topic, event, payload})\n }\n },\n\n // private\n\n binaryEncode(message){\n let {join_ref, ref, event, topic, payload} = message\n let encoder = new TextEncoder()\n let joinRefBytes = encoder.encode(join_ref)\n let refBytes = encoder.encode(ref)\n let topicBytes = encoder.encode(topic)\n let eventBytes = encoder.encode(event)\n\n this.assertFieldSize(joinRefBytes.byteLength, \"join_ref\")\n this.assertFieldSize(refBytes.byteLength, \"ref\")\n this.assertFieldSize(topicBytes.byteLength, \"topic\")\n this.assertFieldSize(eventBytes.byteLength, \"event\")\n\n let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength\n let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n let headerBytes = new Uint8Array(header)\n let view = new DataView(header)\n let offset = 0\n\n view.setUint8(offset++, this.KINDS.push) // kind\n view.setUint8(offset++, joinRefBytes.byteLength)\n view.setUint8(offset++, refBytes.byteLength)\n view.setUint8(offset++, topicBytes.byteLength)\n view.setUint8(offset++, eventBytes.byteLength)\n headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength\n headerBytes.set(refBytes, offset); offset += refBytes.byteLength\n headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength\n headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength\n\n var combined = new Uint8Array(header.byteLength + payload.byteLength)\n combined.set(headerBytes, 0)\n combined.set(new Uint8Array(payload), header.byteLength)\n\n return combined.buffer\n },\n\n assertFieldSize(size, name){\n if(size > 255){\n throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)\n }\n },\n\n binaryDecode(buffer){\n let view = new DataView(buffer)\n let kind = view.getUint8(0)\n let decoder = new TextDecoder()\n switch(kind){\n case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n }\n },\n\n decodePush(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let topicSize = view.getUint8(2)\n let eventSize = view.getUint8(3)\n let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n },\n\n decodeReply(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let refSize = view.getUint8(2)\n let topicSize = view.getUint8(3)\n let eventSize = view.getUint8(4)\n let offset = this.HEADER_LENGTH + this.META_LENGTH\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n offset = offset + refSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n let payload = {status: event, response: data}\n return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n },\n\n decodeBroadcast(buffer, view, decoder){\n let topicSize = view.getUint8(1)\n let eventSize = view.getUint8(2)\n let offset = this.HEADER_LENGTH + 2\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n\n return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n }\n}\n", "import {SOCKET_STATES} from \"./constants\"\n\n/**\n * @private\n *\n * A connection of the socket over a single transport instance.\n *\n * It owns everything that belongs to this connection, so that nothing about it can\n * affect a later connection: the transport events are only handled while it is the\n * socket's current connection, and its state is gone together with it.\n */\nexport default class Connection {\n /**\n * @param {Socket} socket\n * @param {Object} transport - The transport instance, for example a WebSocket\n */\n constructor(socket, transport){\n this.socket = socket\n this.transport = transport\n // the join ref of each channel whose join was sent over this connection, weakly held\n // as a connection can outlive many channels, e.g. when navigating between LiveViews\n this.joins = new WeakMap()\n // the callbacks of the pings sent over this connection by ref, as their replies can only\n // arrive over it, so they are gone together with it instead of accumulating on the socket\n this.pings = new Map()\n // set when we close the connection, as opposed to the server or the network\n this.closing = false\n // set when the socket does not want this connection anymore, e.g. on disconnect,\n // so that connecting again creates a new one instead of waiting for it\n this.ended = false\n // set once the channels were errored after the connection stopped being open\n this.channelsErrored = false\n // set once the close event was handled, as LongPoll can emit it more than once\n this.closeHandled = false\n transport.onopen = () => { if(this.isCurrent()){ socket.onConnOpen() } }\n transport.onerror = error => { if(this.isCurrent()){ socket.onConnError(error) } }\n transport.onmessage = event => { if(this.isCurrent()){ socket.onConnMessage(event) } }\n transport.onclose = event => {\n if(this.isCurrent() && !this.closeHandled){\n this.closeHandled = true\n socket.onConnClose(event)\n }\n }\n }\n\n /**\n * Whether this is the connection the socket currently uses. A replaced connection\n * can still emit events while it closes, but they no longer concern the socket.\n */\n isCurrent(){ return this.socket.connection === this }\n\n isOpen(){ return this.transport.readyState === SOCKET_STATES.open }\n\n isConnecting(){ return this.transport.readyState === SOCKET_STATES.connecting }\n\n /**\n * Whether the channel's current join was sent over this connection.\n */\n carried(channel, joinRef = channel.joinRef()){\n return this.joins.has(channel) && this.joins.get(channel) === joinRef\n }\n\n /**\n * Closes the transport. Its close event then only confirms our close.\n */\n close(code, reason){\n this.closing = true\n // closing a transport that is still connecting makes it emit an error, which is\n // caused by closing it and must not be handled as a transport error\n this.transport.onerror = function (){ } // noop\n if(code){ this.transport.close(code, reason || \"\") } else { this.transport.close() }\n }\n}\n", "import {\n global,\n phxWindow,\n CHANNEL_EVENTS,\n DEFAULT_TIMEOUT,\n DEFAULT_VSN,\n SOCKET_STATES,\n TRANSPORTS,\n WS_CLOSE_NORMAL,\n AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport {\n closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\nimport Connection from \"./connection\"\n\n/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n * `\"wss://example.com\"`\n * `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.\n * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.\n *\n * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport\n * before falling back to the LongPoll transport. Disabled by default.\n *\n * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.\n *\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the\n * socket reconnect interval, in milliseconds.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n * return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {boolean} [opts.reconnectWhenHidden] - When true, the socket also reconnects while the\n * page is hidden, for example in a background tab or on a sleeping phone.\n *\n * Defaults to false. The socket then waits until the page is visible again before it reconnects.\n * This avoids reconnect attempts that stall on mobile devices and delay the connection after the\n * device wakes up.\n *\n * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n * return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n * console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {(Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {(string|function)} [opts.authToken] - the optional authentication token to be exposed on the server\n * under the `:auth_token` connect_info key. Can be a string or a function that returns a string.\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n *\n * @param {Object} [opts.sessionStorage] - An optional Storage compatible object\n * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is\n * useful when Phoenix won't have access to `sessionStorage`. For example, This could\n * happen if a site loads a cross-domain channel in an iframe. Example usage:\n *\n * class InMemoryStorage {\n * constructor() { this.storage = {} }\n * getItem(keyName) { return this.storage[keyName] || null }\n * removeItem(keyName) { delete this.storage[keyName] }\n * setItem(keyName, keyValue) { this.storage[keyName] = keyValue }\n * }\n *\n*/\nexport default class Socket {\n constructor(endPoint, opts = {}){\n this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n this.channels = []\n this.sendBuffer = []\n this.ref = 0\n this.fallbackRef = null\n this.fallbackErrorRef = null\n this.timeout = opts.timeout || DEFAULT_TIMEOUT\n this.transport = opts.transport || global.WebSocket || LongPoll\n this.primaryPassedHealthCheck = false\n this.longPollFallbackMs = opts.longPollFallbackMs\n this.fallbackTimer = null\n this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)\n this.establishedConnections = 0\n this.defaultEncoder = Serializer.encode.bind(Serializer)\n this.defaultDecoder = Serializer.decode.bind(Serializer)\n // We start with closeWasClean true to avoid the visibility change\n // logic from connecting if the socket was never connected in the first place.\n // transportConnect sets it to false on open.\n this.closeWasClean = true\n this.binaryType = opts.binaryType || \"arraybuffer\"\n // the connection over the current transport instance, see Connection\n this.connection = null\n // changes whenever we connect, disconnect or replace the transport, for the code that needs\n // to know whether that happened while there was no connection, see replaceTransport\n this.connectClock = 1\n // the connection created when the page was shown, see handleVisibilityChange\n this.visibilityConnection = null\n if(this.transport !== LongPoll){\n this.encode = opts.encode || this.defaultEncoder\n this.decode = opts.decode || this.defaultDecoder\n } else {\n this.encode = this.defaultEncoder\n this.decode = this.defaultDecoder\n }\n let awaitingConnectionOnPageShow = null\n if(phxWindow && phxWindow.addEventListener){\n phxWindow.addEventListener(\"pagehide\", _e => {\n // an ended connection only finishes closing, e.g. after disconnect\n if(this.connection && !this.connection.ended){\n this.disconnect()\n awaitingConnectionOnPageShow = this.connectClock\n }\n })\n phxWindow.addEventListener(\"pageshow\", _e => {\n if(awaitingConnectionOnPageShow === this.connectClock){\n awaitingConnectionOnPageShow = null\n this.connect()\n }\n })\n phxWindow.addEventListener(\"visibilitychange\", () => {\n this.handleVisibilityChange()\n })\n // Chrome 149 started to not reliably fire visibilitychange after resume,\n // see https://issues.chromium.org/issues/547062449.\n phxWindow.document && phxWindow.document.addEventListener(\"resume\", () => {\n this.handleVisibilityChange()\n })\n }\n this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n this.rejoinAfterMs = (tries) => {\n if(opts.rejoinAfterMs){\n return opts.rejoinAfterMs(tries)\n } else {\n return [1000, 2000, 5000][tries - 1] || 10000\n }\n }\n this.reconnectAfterMs = (tries) => {\n if(opts.reconnectAfterMs){\n return opts.reconnectAfterMs(tries)\n } else {\n return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n }\n }\n this.reconnectWhenHidden = opts.reconnectWhenHidden || false\n this.logger = opts.logger || null\n if(!this.logger && opts.debug){\n this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }\n }\n this.longpollerTimeout = opts.longpollerTimeout || 20000\n this.params = closure(opts.params || {})\n this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n this.vsn = opts.vsn || DEFAULT_VSN\n this.heartbeatTimeoutTimer = null\n this.heartbeatTimer = null\n this.pendingHeartbeatRef = null\n this.reconnectTimer = new Timer(() => {\n // handleVisibilityChange reconnects once the page is visible again\n if(this.pageHidden && !this.reconnectWhenHidden){\n this.log(\"Not reconnecting as page is hidden!\")\n this.teardown()\n return\n }\n this.teardownAndReconnect(() => this.connect())\n }, this.reconnectAfterMs)\n this.authToken = opts.authToken && closure(opts.authToken)\n }\n\n /**\n * The transport instance of the current connection, for example a WebSocket\n */\n get conn(){ return this.connection && this.connection.transport }\n\n /**\n * @internal\n */\n get pageHidden(){\n return phxWindow && phxWindow.document ? phxWindow.document.visibilityState === \"hidden\" : false\n }\n\n /**\n * @internal\n */\n handleVisibilityChange(){\n if(this.pageHidden){\n this.visibilityConnection = null\n return\n }\n // resume and visibilitychange can both fire when the page is shown again, so we keep\n // the connection created for the first one while it is still connecting\n const connection = this.connection\n if(connection && connection === this.visibilityConnection && connection.isConnecting()){ return }\n // reconnect immediately\n if(!this.isConnected() && !this.closeWasClean){\n // a reconnect that was scheduled before would tear down the connection we create here\n this.reconnectTimer.reset()\n this.teardownAndReconnect(() => {\n this.connect()\n this.visibilityConnection = this.connection\n })\n }\n }\n\n /**\n * Returns the LongPoll transport reference\n */\n getLongPollTransport(){ return LongPoll }\n\n /**\n * Disconnects and replaces the active transport\n *\n * @param {Function} newTransport - The new transport class to instantiate\n *\n */\n replaceTransport(newTransport){\n const connectClock = ++this.connectClock\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // replaced first, as channel error callbacks below can already connect again\n this.transport = newTransport\n const connection = this.connection\n if(connection){\n const wasOpen = connection.isOpen()\n // the old transport closes asynchronously, but its events no longer concern us\n this.connection = null\n connection.ended = true\n connection.close()\n this.clearHeartbeats()\n // any in-flight pushes on the old connection are lost, so channels need to rejoin\n if(wasOpen){ this.triggerChanError(\"connection_closed\", connection) }\n }\n return connectClock\n }\n\n /**\n * Returns the socket protocol\n *\n * @returns {string}\n */\n protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n /**\n * The fully qualified socket url\n *\n * @returns {string}\n */\n endPointURL(){\n let uri = Ajax.appendParams(\n Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n if(uri.charAt(0) !== \"/\"){ return uri }\n if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n return `${this.protocol()}://${location.host}${uri}`\n }\n\n /**\n * Disconnects the socket\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n *\n * @param {Function} callback - Optional callback which is called after socket is disconnected.\n * @param {integer} code - A status code for disconnection (Optional).\n * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n */\n disconnect(callback, code, reason){\n this.connectClock++\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // connecting again creates a new connection right away instead of waiting for this one to close\n if(this.connection){ this.connection.ended = true }\n this.teardown(callback, code, reason)\n }\n\n /**\n *\n * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n *\n * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n */\n connect(params){\n if(params){\n console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n this.params = closure(params)\n }\n // the current connection is kept, including while it reconnects, until the socket is done with it\n if(this.connection && !this.connection.ended){ return }\n if(this.longPollFallbackMs && this.transport !== LongPoll){\n this.connectWithFallback(LongPoll, this.longPollFallbackMs)\n } else {\n this.transportConnect()\n }\n }\n\n /**\n * Logs the message. Override `this.logger` for specialized logging. noops by default\n * @param {string} kind\n * @param {string} msg\n * @param {Object} data\n */\n log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }\n\n /**\n * Returns true if a logger has been set on this socket.\n */\n hasLogger(){ return this.logger !== null }\n\n /**\n * Registers callbacks for connection open events\n *\n * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n *\n * @param {Function} callback\n */\n onOpen(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.open.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection close events\n * @param {Function} callback\n */\n onClose(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.close.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection error events\n *\n * @example socket.onError(function(error){ alert(\"An error occurred\") })\n *\n * @param {Function} callback\n */\n onError(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.error.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection message events\n * @param {Function} callback\n */\n onMessage(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.message.push([ref, callback])\n return ref\n }\n\n /**\n * Pings the server and invokes the callback with the RTT in milliseconds\n * @param {Function} callback\n *\n * Returns true if the ping was pushed or false if unable to be pushed.\n */\n ping(callback){\n if(!this.isConnected()){ return false }\n let ref = this.makeRef()\n let startTime = Date.now()\n this.connection.pings.set(ref, () => callback(Date.now() - startTime))\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: ref})\n return true\n }\n\n /**\n * @private\n *\n * @param {Function}\n */\n transportName(transport){\n // JavaScript minification, enabled by default in production in Phoenix\n // projects, renames symbols to reduce code size.\n // See https://esbuild.github.io/api/#keep-names.\n // This helper ensures we return the correct name for the LongPoll transport\n // even after minification. The other common transport is WebSocket, which\n // is native to browsers and does not need special handling.\n switch(transport){\n case LongPoll: return \"LongPoll\"\n default: return transport.name\n }\n }\n\n /**\n * @private\n */\n transportConnect(){\n const previous = this.connection\n this.connectClock++\n this.closeWasClean = false\n // a reconnect scheduled before would tear down the new connection, but we keep\n // its tries, which only reset once a connection opens\n clearTimeout(this.reconnectTimer.timer)\n let protocols = undefined\n // Sec-WebSocket-Protocol based token\n // (longpoll uses Authorization header instead)\n if(this.authToken){\n protocols = [\"phoenix\", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken()).replace(/=/g, \"\")}`]\n }\n const transport = new this.transport(this.endPointURL(), protocols)\n transport.binaryType = this.binaryType\n transport.timeout = this.longpollerTimeout\n this.connection = new Connection(this, transport)\n // connect can replace an ended connection that is still open while its buffer drains, but the\n // channels joined over it need to rejoin on the new connection instead of waiting for it to close\n if(previous && previous.isOpen()){ this.triggerChanError(\"connection_closed\", previous) }\n }\n\n getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }\n\n storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }\n\n connectWithFallback(fallbackTransport, fallbackThreshold = 2500){\n // the callbacks of a previous attempt must not act on this one, e.g. an attempt\n // that never opened would otherwise fall back again on the next error\n this.cancelFallback()\n let established = false\n let primaryTransport = true\n let errorRef\n let connectClock\n const isCurrent = () => connectClock === this.connectClock && !this.closeWasClean\n let fallbackTransportName = this.transportName(fallbackTransport)\n // the fallback timer is cleared whenever the attempt ends, and the callbacks below\n // only fall back while the attempt is current\n let fallback = (reason) => {\n this.log(\"transport\", `falling back to ${fallbackTransportName}...`, reason)\n // the open callback stays registered to remember the fallback once it connects\n this.off([errorRef])\n primaryTransport = false\n const replaceClock = this.replaceTransport(fallbackTransport)\n if(replaceClock !== this.connectClock){ return }\n this.transportConnect()\n }\n if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback(\"memorized\") }\n\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n\n errorRef = this.fallbackErrorRef = this.onError(reason => {\n if(!isCurrent()){ return }\n this.log(\"transport\", \"error\", reason)\n if(primaryTransport && !established){\n clearTimeout(this.fallbackTimer)\n fallback(reason)\n }\n })\n this.fallbackRef = this.onOpen(() => {\n if(!primaryTransport){\n // reconnects keep using the fallback transport, so a later successful connection\n // must still be remembered, unless an earlier callback disconnected it\n if(this.transport !== fallbackTransport || this.closeWasClean){ return }\n let fallbackTransportName = this.transportName(fallbackTransport)\n // only memorize LP if we never connected to primary\n if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, \"true\") }\n return this.log(\"transport\", `established ${fallbackTransportName} fallback`)\n }\n if(!isCurrent()){ return }\n established = true\n // if we've established primary, give the fallback a new period to attempt ping\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n this.ping(rtt => {\n this.log(\"transport\", \"connected to primary after\", rtt)\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n })\n })\n this.transportConnect()\n connectClock = this.connectClock\n }\n\n /**\n * @private\n *\n * Stops the fallback of the current connection attempt, see connectWithFallback\n */\n cancelFallback(){\n clearTimeout(this.fallbackTimer)\n this.off([this.fallbackRef, this.fallbackErrorRef])\n }\n\n clearHeartbeats(){\n clearTimeout(this.heartbeatTimer)\n clearTimeout(this.heartbeatTimeoutTimer)\n // a late reply to the pending heartbeat must not start the heartbeats again\n this.pendingHeartbeatRef = null\n }\n\n onConnOpen(){\n if(this.hasLogger()) this.log(\"transport\", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)\n this.closeWasClean = false\n this.establishedConnections++\n this.flushSendBuffer()\n this.reconnectTimer.reset()\n this.resetHeartbeat()\n this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n }\n\n /**\n * @private\n */\n\n heartbeatTimeout(){\n if(this.pendingHeartbeatRef){\n const connection = this.connection\n this.pendingHeartbeatRef = null\n if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n this.closeWasClean = false\n // the connection is presumed dead, so we close it right away instead of waiting for its\n // buffer to drain, which also ensures that it is no longer considered connected when the\n // channels error (LiveView would report a server error otherwise)\n if(connection){ connection.close(WS_CLOSE_NORMAL, \"heartbeat timeout\") }\n // some transports emit their close event synchronously, and callbacks of it as well as\n // channel error callbacks can disconnect or connect, which takes precedence over reconnecting\n if(!this.stillUses(connection)){ return }\n this.triggerChanError(\"heartbeat_timeout\")\n if(!this.stillUses(connection)){ return }\n this.teardownAndReconnect(() => this.reconnectTimer.scheduleTimeout())\n }\n }\n\n resetHeartbeat(){\n if(this.conn && this.conn.skipHeartbeat){ return }\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n /**\n * @private\n *\n * Tears down the current connection to reconnect afterwards, unless the socket was\n * disconnected or connected again in the meantime, which takes precedence.\n */\n teardownAndReconnect(reconnect){\n const connection = this.connection\n const connectClock = this.connectClock\n this.teardown(() => {\n // the socket was disconnected or connected again in the meantime, which the connection does not\n // tell once an overlapping teardown gave up on it\n if(connectClock !== this.connectClock || (connection && connection.ended)){ return }\n reconnect()\n })\n }\n\n /**\n * @private\n *\n * Whether the socket still uses the connection and did not give up on it. User callbacks\n * can disconnect or connect, so code continuing after them checks this first.\n */\n stillUses(connection){\n return this.connection === connection && !(connection && connection.ended)\n }\n\n teardown(callback, code, reason){\n // If someone calls connect before we finish tearing down,\n // we create a new connection, but we still want to finish tearing down the old one.\n const connection = this.connection\n if(!connection){\n return callback && callback()\n }\n // onConnClose is not called when the close event arrives late, so we stop the\n // heartbeats here, as a heartbeat timeout would otherwise reconnect the socket\n this.clearHeartbeats()\n\n this.waitForBufferDone(connection.transport, () => {\n // a connection we already closed, e.g. on a heartbeat timeout or in an\n // overlapping teardown, also errored its channels already\n if(!connection.closing){\n const wasOpened = !connection.isConnecting()\n connection.close(code, reason)\n // channels joined over this connection need to rejoin on the next one, but we cannot rely\n // on onConnClose for that, as the close event may arrive after we stopped waiting for it\n // or after a new connection already replaced this one\n if(wasOpened){ this.triggerChanError(\"connection_closed\", connection) }\n }\n\n this.waitForSocketClosed(connection.transport, () => {\n if(this.connection === connection){ this.connection = null }\n callback && callback()\n })\n })\n }\n\n waitForBufferDone(conn, callback, tries = 1){\n // there is no point in waiting for a conn that is already closing\n if(tries === 5 || !conn.bufferedAmount || conn.readyState !== SOCKET_STATES.open){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForBufferDone(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n waitForSocketClosed(conn, callback, tries = 1){\n if(tries === 5 || conn.readyState === SOCKET_STATES.closed){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForSocketClosed(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n onConnClose(event){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n this.clearHeartbeats()\n // we only close a connection to disconnect or to reconnect, which the code closing it takes\n // care of, so its close event only confirms our close. Any other close is reconnected, whatever\n // its code, as the server, proxies and load balancers also close with 1000 (normal closure)\n const closedByUs = !!(connection && connection.closing)\n // channel callbacks may disconnect or create a new connection, so finish cleaning up\n // this connection first and do not schedule a reconnect for its replacement\n this.triggerChanError(\"connection_closed\", connection)\n if(this.stillUses(connection) && !closedByUs && !this.closeWasClean){\n this.reconnectTimer.scheduleTimeout()\n }\n this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n }\n\n /**\n * @private\n */\n onConnError(error){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"error\", error)\n let transportBefore = this.transport\n let establishedBefore = this.establishedConnections\n this.stateChangeCallbacks.error.forEach(([, callback]) => {\n callback(error, transportBefore, establishedBefore)\n })\n // callbacks can replace the connection, in which case only its own joins are errored\n this.triggerChanError(\"connection_error\", connection)\n }\n\n /**\n * @private\n *\n * Errors the channels, as they need to rejoin once the given connection stopped being open.\n */\n triggerChanError(reason, connection = this.connection){\n // several places notice that a connection stopped being open (onConnError, onConnClose,\n // teardown and heartbeatTimeout). After the first, only channels whose join it carried\n // are errored, as error callbacks may have joined channels that join over another connection.\n let erroredBefore = false\n if(connection && !connection.isOpen()){\n erroredBefore = connection.channelsErrored\n connection.channelsErrored = true\n }\n const joins = this.channels.map(channel => [channel, channel.joinRef()])\n joins.forEach(([channel, joinRef]) => {\n // an earlier error callback can rejoin a later channel, so check its join\n // again immediately before dispatching rather than filtering the list first\n if(channel.joinRef() !== joinRef){ return }\n // the joins of a replaced connection are not the only ones anymore\n if(connection && (erroredBefore || !connection.isCurrent()) && !connection.carried(channel, joinRef)){ return }\n if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n channel.trigger(CHANNEL_EVENTS.error, {source: \"transport\", reason})\n }\n })\n }\n\n /**\n * @returns {string}\n */\n connectionState(){\n switch(this.conn && this.conn.readyState){\n case SOCKET_STATES.connecting: return \"connecting\"\n case SOCKET_STATES.open: return \"open\"\n case SOCKET_STATES.closing: return \"closing\"\n default: return \"closed\"\n }\n }\n\n /**\n * @returns {boolean}\n */\n isConnected(){ return this.connectionState() === \"open\" }\n\n /**\n * @private\n *\n * @param {Channel}\n */\n remove(channel){\n this.off(channel.stateChangeRefs)\n this.channels = this.channels.filter(c => c !== channel)\n }\n\n /**\n * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n *\n * @param {refs} - list of refs returned by calls to\n * `onOpen`, `onClose`, `onError,` and `onMessage`\n */\n off(refs){\n for(let key in this.stateChangeCallbacks){\n this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n return refs.indexOf(ref) === -1\n })\n }\n }\n\n /**\n * Initiates a new channel for the given topic\n *\n * @param {string} topic\n * @param {Object} chanParams - Parameters for the channel\n * @returns {Channel}\n */\n channel(topic, chanParams = {}){\n let chan = new Channel(topic, chanParams, this)\n this.channels.push(chan)\n return chan\n }\n\n /**\n * @param {Object} data\n * @param {Channel} [channel] - The channel the push belongs to, if any\n */\n push(data, channel){\n if(this.hasLogger()){\n let {topic, event, payload, ref, join_ref} = data\n this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n }\n\n if(this.isConnected()){\n this.send(data, channel)\n } else {\n this.sendBuffer.push(() => {\n // the channel may have errored or left while the push was buffered, e.g. when the\n // connection failed before opening, in which case its rejoin supersedes the buffered join\n if(!this.isOutdated(data, channel)){ this.send(data, channel) }\n })\n }\n }\n\n /**\n * @private\n */\n send(data, channel){\n const connection = this.connection\n // the join counts as carried by this connection while it is still encoded, so that the\n // channel rejoins when the connection is torn down before the encoding finished\n if(channel && data.event === CHANNEL_EVENTS.join){ connection.joins.set(channel, data.join_ref) }\n this.encode(data, result => {\n // let encodes finish while this connection drains, but never send on a\n // replacement or after the original connection has started closing\n if(!connection.isCurrent() || !connection.isOpen()){ return }\n // a leave still needs to reach the server after its channel was removed locally\n if(data.event !== CHANNEL_EVENTS.leave && this.isOutdated(data, channel)){ return }\n connection.transport.send(result)\n })\n }\n\n /**\n * @private\n *\n * Whether the push was made for an earlier join of its channel, or the channel left since.\n */\n isOutdated(data, channel){\n return !!channel && (data.join_ref !== channel.joinRef() || channel.isClosed())\n }\n\n /**\n * Return the next message ref, accounting for overflows\n * @returns {string}\n */\n makeRef(){\n let newRef = this.ref + 1\n if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n return this.ref.toString()\n }\n\n sendHeartbeat(){\n if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n this.pendingHeartbeatRef = this.makeRef()\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n }\n\n flushSendBuffer(){\n if(this.isConnected() && this.sendBuffer.length > 0){\n this.sendBuffer.forEach(callback => callback())\n this.sendBuffer = []\n }\n }\n\n onConnMessage(rawMessage){\n const connection = this.connection\n const wasOpen = connection && connection.isOpen()\n this.decode(rawMessage.data, msg => {\n // the transport event can be current even though its asynchronous decode is not, as the\n // connection may have been replaced or closed in the meantime (browsers also drop messages\n // that arrive once a WebSocket is closing)\n if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return }\n let {topic, event, payload, ref, join_ref} = msg\n if(ref && ref === this.pendingHeartbeatRef){\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n // removing a channel or callback while dispatching replaces the list instead of\n // changing it, so we iterate over the lists from before to not skip any of them\n const channels = this.channels\n for(let i = 0; i < channels.length; i++){\n const channel = channels[i]\n if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n channel.trigger(event, payload, ref, join_ref)\n }\n\n const callbacks = this.stateChangeCallbacks.message\n for(let i = 0; i < callbacks.length; i++){\n let [, callback] = callbacks[i]\n callback(msg)\n }\n\n const ping = ref && connection.pings.get(ref)\n if(ping){\n connection.pings.delete(ref)\n ping()\n }\n })\n }\n\n leaveOpenTopic(topic){\n let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n if(dupChannel){\n if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n dupChannel.leave()\n }\n }\n}\n"], - "mappings": "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", + "sourcesContent": ["/**\n * Phoenix Channels JavaScript client\n *\n * ## Socket Connection\n *\n * A single connection is established to the server and\n * channels are multiplexed over the connection.\n * Connect to the server using the `Socket` class:\n *\n * ```javascript\n * let socket = new Socket(\"/socket\", {params: {userToken: \"123\"}})\n * socket.connect()\n * ```\n *\n * The `Socket` constructor takes the mount point of the socket,\n * the authentication params, as well as options that can be found in\n * the Socket docs, such as configuring the `LongPoll` transport, and\n * heartbeat.\n *\n * ## Channels\n *\n * Channels are isolated, concurrent processes on the server that\n * subscribe to topics and broker events between the client and server.\n * To join a channel, you must provide the topic, and channel params for\n * authorization. Here's an example chat room example where `\"new_msg\"`\n * events are listened for, messages are pushed to the server, and\n * the channel is joined with ok/error/timeout matches:\n *\n * ```\n * let channel = socket.channel(\"room:123\", {token: roomToken})\n * channel.on(\"new_msg\", msg => console.log(\"Got message\", msg) )\n * $input.onEnter( e => {\n * channel.push(\"new_msg\", {body: e.target.val}, 10000)\n * .receive(\"ok\", (msg) => console.log(\"created message\", msg) )\n * .receive(\"error\", (reasons) => console.log(\"create failed\", reasons) )\n * .receive(\"timeout\", () => console.log(\"Networking issue...\") )\n * })\n *\n * channel.join()\n * .receive(\"ok\", ({messages}) => console.log(\"catching up\", messages) )\n * .receive(\"error\", ({reason}) => console.log(\"failed join\", reason) )\n * .receive(\"timeout\", () => console.log(\"Networking issue. Still waiting...\"))\n *```\n *\n * ## Joining\n *\n * Creating a channel with `socket.channel(topic, params)`, binds the params to\n * `channel.params`, which are sent up on `channel.join()`.\n * Subsequent rejoins will send up the modified params for\n * updating authorization params, or passing up last_message_id information.\n * Successful joins receive an \"ok\" status, while unsuccessful joins\n * receive \"error\".\n *\n * With the default serializers and WebSocket transport, JSON text frames are\n * used for pushing a JSON object literal. If an `ArrayBuffer` instance is provided,\n * binary encoding will be used and the message will be sent with the binary\n * opcode.\n *\n * *Note*: binary messages are only supported on the WebSocket transport.\n *\n * ## Duplicate Join Subscriptions\n *\n * While the client may join any number of topics on any number of channels,\n * the client may only hold a single subscription for each unique topic at any\n * given time. When attempting to create a duplicate subscription,\n * the server will close the existing channel, log a warning, and\n * spawn a new channel for the topic. The client will have their\n * `channel.onClose` callbacks fired for the existing channel, and the new\n * channel join will have its receive hooks processed as normal.\n *\n * ## Pushing Messages\n *\n * From the previous example, we can see that pushing messages to the server\n * can be done with `channel.push(eventName, payload)` and we can optionally\n * receive responses from the push. Additionally, we can use\n * `receive(\"timeout\", callback)` to abort waiting for our other `receive` hooks\n * and take action after some period of waiting. The default timeout is 10000ms.\n *\n *\n * ## Socket Hooks\n *\n * Lifecycle events of the multiplexed connection can be hooked into via\n * `socket.onError()` and `socket.onClose()` events, ie:\n *\n * ```\n * socket.onError( () => console.log(\"there was an error with the connection!\") )\n * socket.onClose( () => console.log(\"the connection dropped\") )\n * ```\n *\n *\n * ## Channel Hooks\n *\n * For each joined channel, you can bind to `onError` and `onClose` events\n * to monitor the channel lifecycle, ie:\n *\n * ```\n * channel.onError( () => console.log(\"there was an error!\") )\n * channel.onClose( () => console.log(\"the channel has gone away gracefully\") )\n * ```\n *\n * ### onError hooks\n *\n * `onError` hooks are invoked if the socket connection drops, or the channel\n * crashes on the server. In either case, a channel rejoin is attempted\n * automatically in an exponential backoff manner.\n *\n * ### onClose hooks\n *\n * `onClose` hooks are invoked only in two cases. 1) the channel explicitly\n * closed on the server, or 2). The client explicitly closed, by calling\n * `channel.leave()`\n *\n *\n * ## Presence\n *\n * The `Presence` object provides features for syncing presence information\n * from the server with the client and handling presences joining and leaving.\n *\n * ### Syncing state from the server\n *\n * To sync presence state from the server, first instantiate an object and\n * pass your channel in to track lifecycle events:\n *\n * ```\n * let channel = socket.channel(\"some:topic\")\n * let presence = new Presence(channel)\n * ```\n *\n * Next, use the `presence.onSync` callback to react to state changes\n * from the server. For example, to render the list of users every time\n * the list changes, you could write:\n *\n * ```\n * presence.onSync(() => {\n * myRenderUsersFunction(presence.list())\n * })\n * ```\n *\n * ### Listing Presences\n *\n * `presence.list` is used to return a list of presence information\n * based on the local state of metadata. By default, all presence\n * metadata is returned, but a `listBy` function can be supplied to\n * allow the client to select which metadata to use for a given presence.\n * For example, you may have a user online from different devices with\n * a metadata status of \"online\", but they have set themselves to \"away\"\n * on another device. In this case, the app may choose to use the \"away\"\n * status for what appears on the UI. The example below defines a `listBy`\n * function which prioritizes the first metadata which was registered for\n * each user. This could be the first tab they opened, or the first device\n * they came online from:\n *\n * ```\n * let listBy = (id, {metas: [first, ...rest]}) => {\n * first.count = rest.length + 1 // count of this user's presences\n * first.id = id\n * return first\n * }\n * let onlineUsers = presence.list(listBy)\n * ```\n *\n * ### Handling individual presence join and leave events\n *\n * The `presence.onJoin` and `presence.onLeave` callbacks can be used to\n * react to individual presences joining and leaving the app. For example:\n *\n * ```\n * let presence = new Presence(channel)\n *\n * // detect if user has joined for the 1st time or from another tab/device\n * presence.onJoin((id, current, newPres) => {\n * if(!current){\n * console.log(\"user has entered for the first time\", newPres)\n * } else {\n * console.log(\"user additional presence\", newPres)\n * }\n * })\n *\n * // detect if user has left from all tabs/devices, or is still present\n * presence.onLeave((id, current, leftPres) => {\n * if(current.metas.length === 0){\n * console.log(\"user has left from all devices\", leftPres)\n * } else {\n * console.log(\"user left from a device\", leftPres)\n * }\n * })\n * // receive presence data from server\n * presence.onSync(() => {\n * displayUsers(presence.list())\n * })\n * ```\n * @module phoenix\n */\n\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Presence from \"./presence\"\nimport Serializer from \"./serializer\"\nimport Socket from \"./socket\"\n\nexport {\n Channel,\n LongPoll,\n Presence,\n Serializer,\n Socket\n}\n", "// wraps value in closure or returns closure\nexport let closure = (value) => {\n if(typeof value === \"function\"){\n return value\n } else {\n let closure = function (){ return value }\n return closure\n }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || globalThis\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const MAX_LONGPOLL_BATCH_SIZE = 100;\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n closed: \"closed\",\n errored: \"errored\",\n joined: \"joined\",\n joining: \"joining\",\n leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n close: \"phx_close\",\n error: \"phx_error\",\n join: \"phx_join\",\n reply: \"phx_reply\",\n leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n longpoll: \"longpoll\",\n websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n complete: 4\n}\nexport const AUTH_TOKEN_PREFIX = \"base64url.bearer.phx.\"\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n constructor(channel, event, payload, timeout){\n this.channel = channel\n this.event = event\n this.payload = payload || function (){ return {} }\n this.receivedResp = null\n this.timeout = timeout\n this.timeoutTimer = null\n this.recHooks = []\n this.sent = false\n }\n\n /**\n *\n * @param {number} timeout\n */\n resend(timeout){\n this.timeout = timeout\n this.reset()\n this.send()\n }\n\n /**\n *\n */\n send(){\n if(this.hasReceived(\"timeout\")){ return }\n this.startTimeout()\n this.sent = true\n this.channel.socket.push({\n topic: this.channel.topic,\n event: this.event,\n payload: this.payload(),\n ref: this.ref,\n join_ref: this.channel.joinRef()\n }, this.channel)\n }\n\n /**\n *\n * @param {*} status\n * @param {*} callback\n */\n receive(status, callback){\n if(this.hasReceived(status)){\n callback(this.receivedResp.response)\n }\n\n this.recHooks.push({status, callback})\n return this\n }\n\n /**\n * @private\n */\n reset(){\n this.cancelRefEvent()\n this.ref = null\n this.refEvent = null\n this.receivedResp = null\n this.sent = false\n }\n\n /**\n * @private\n */\n matchReceive({status, response, _ref}){\n this.recHooks.filter(h => h.status === status)\n .forEach(h => h.callback(response))\n }\n\n /**\n * @private\n */\n cancelRefEvent(){\n if(!this.refEvent){ return }\n this.channel.off(this.refEvent)\n }\n\n /**\n * @private\n */\n cancelTimeout(){\n clearTimeout(this.timeoutTimer)\n this.timeoutTimer = null\n }\n\n /**\n * @private\n */\n startTimeout(){\n if(this.timeoutTimer){ this.cancelTimeout() }\n this.cancelRefEvent()\n this.ref = this.channel.socket.makeRef()\n this.refEvent = this.channel.replyEventName(this.ref)\n\n this.channel.on(this.refEvent, payload => {\n this.cancelRefEvent()\n this.cancelTimeout()\n this.receivedResp = payload\n this.matchReceive(payload)\n })\n\n this.timeoutTimer = setTimeout(() => {\n this.trigger(\"timeout\", {})\n }, this.timeout)\n }\n\n /**\n * @private\n */\n hasReceived(status){\n return this.receivedResp && this.receivedResp.status === status\n }\n\n /**\n * @private\n */\n trigger(status, response){\n this.channel.trigger(this.refEvent, {status, response})\n }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n * return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n constructor(callback, timerCalc){\n this.callback = callback\n this.timerCalc = timerCalc\n this.timer = null\n this.tries = 0\n }\n\n reset(){\n this.tries = 0\n clearTimeout(this.timer)\n }\n\n /**\n * Cancels any previous scheduleTimeout and schedules callback\n */\n scheduleTimeout(){\n clearTimeout(this.timer)\n\n this.timer = setTimeout(() => {\n this.tries = this.tries + 1\n this.callback()\n }, this.timerCalc(this.tries + 1))\n }\n}\n", "import {closure} from \"./utils\"\nimport {\n CHANNEL_EVENTS,\n CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n constructor(topic, params, socket){\n this.state = CHANNEL_STATES.closed\n this.topic = topic\n this.params = closure(params || {})\n this.socket = socket\n this.bindings = []\n this.bindingRef = 0\n this.timeout = this.socket.timeout\n this.joinedOnce = false\n this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n this.pushBuffer = []\n this.stateChangeRefs = []\n\n this.rejoinTimer = new Timer(() => {\n if(this.socket.isConnected()){ this.rejoin() }\n }, this.socket.rejoinAfterMs)\n this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n this.stateChangeRefs.push(this.socket.onOpen(() => {\n this.rejoinTimer.reset()\n if(this.isErrored()){ this.rejoin() }\n })\n )\n this.joinPush.receive(\"ok\", () => {\n this.state = CHANNEL_STATES.joined\n this.rejoinTimer.reset()\n this.pushBuffer.forEach(pushEvent => pushEvent.send())\n this.pushBuffer = []\n })\n this.joinPush.receive(\"error\", () => {\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.onClose(() => {\n this.rejoinTimer.reset()\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n this.state = CHANNEL_STATES.closed\n this.socket.remove(this)\n })\n this.onError(reason => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n if(this.isJoining()){ this.joinPush.reset() }\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.joinPush.receive(\"timeout\", () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n leavePush.send()\n this.state = CHANNEL_STATES.errored\n this.joinPush.reset()\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n this.trigger(this.replyEventName(ref), payload)\n })\n }\n\n /**\n * Join the channel\n * @param {integer} timeout\n * @returns {Push}\n */\n join(timeout = this.timeout){\n if(this.joinedOnce){\n throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n } else {\n this.timeout = timeout\n this.joinedOnce = true\n this.rejoin()\n return this.joinPush\n }\n }\n\n /**\n * Hook into channel close\n * @param {Function} callback\n */\n onClose(callback){\n this.on(CHANNEL_EVENTS.close, callback)\n }\n\n /**\n * Hook into channel errors\n * @param {Function} callback\n */\n onError(callback){\n return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n }\n\n /**\n * Subscribes on channel events\n *\n * Subscription returns a ref counter, which can be used later to\n * unsubscribe the exact event listener\n *\n * @example\n * const ref1 = channel.on(\"event\", do_stuff)\n * const ref2 = channel.on(\"event\", do_other_stuff)\n * channel.off(\"event\", ref1)\n * // Since unsubscription, do_stuff won't fire,\n * // while do_other_stuff will keep firing on the \"event\"\n *\n * @param {string} event\n * @param {Function} callback\n * @returns {integer} ref\n */\n on(event, callback){\n let ref = this.bindingRef++\n this.bindings.push({event, ref, callback})\n return ref\n }\n\n /**\n * Unsubscribes off of channel events\n *\n * Use the ref returned from a channel.on() to unsubscribe one\n * handler, or pass nothing for the ref to unsubscribe all\n * handlers for the given event.\n *\n * @example\n * // Unsubscribe the do_stuff handler\n * const ref1 = channel.on(\"event\", do_stuff)\n * channel.off(\"event\", ref1)\n *\n * // Unsubscribe all handlers from event\n * channel.off(\"event\")\n *\n * @param {string} event\n * @param {integer} ref\n */\n off(event, ref){\n this.bindings = this.bindings.filter((bind) => {\n return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n })\n }\n\n /**\n * @private\n */\n canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n /**\n * Sends a message `event` to phoenix with the payload `payload`.\n * Phoenix receives this in the `handle_in(event, payload, socket)`\n * function. if phoenix replies or it times out (default 10000ms),\n * then optionally the reply can be received.\n *\n * @example\n * channel.push(\"event\")\n * .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n * .receive(\"error\", err => console.log(\"phoenix errored\", err))\n * .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n * @param {string} event\n * @param {Object} payload\n * @param {number} [timeout]\n * @returns {Push}\n */\n push(event, payload, timeout = this.timeout){\n payload = payload || {}\n if(!this.joinedOnce){\n throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n }\n let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n if(this.canPush()){\n pushEvent.send()\n } else {\n pushEvent.startTimeout()\n this.pushBuffer.push(pushEvent)\n }\n\n return pushEvent\n }\n\n /** Leaves the channel\n *\n * Unsubscribes from server events, and\n * instructs channel to terminate on server\n *\n * Triggers onClose() hooks\n *\n * To receive leave acknowledgements, use the `receive`\n * hook to bind to the server ack, ie:\n *\n * @example\n * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n *\n * @param {integer} timeout\n * @returns {Push}\n */\n leave(timeout = this.timeout){\n this.rejoinTimer.reset()\n this.joinPush.cancelTimeout()\n\n this.state = CHANNEL_STATES.leaving\n let onClose = () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n this.trigger(CHANNEL_EVENTS.close, \"leave\")\n }\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n leavePush.receive(\"ok\", () => onClose())\n .receive(\"timeout\", () => onClose())\n leavePush.send()\n if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n return leavePush\n }\n\n /**\n * Overridable message hook\n *\n * Receives all events for specialized message handling\n * before dispatching to the channel callbacks.\n *\n * Must return the payload, modified or unmodified\n * @param {string} event\n * @param {Object} payload\n * @param {integer} ref\n * @returns {Object}\n */\n onMessage(_event, payload, _ref){ return payload }\n\n /**\n * @private\n */\n isMember(topic, event, payload, joinRef){\n if(this.topic !== topic){ return false }\n\n if(joinRef && joinRef !== this.joinRef()){\n if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n return false\n } else {\n return true\n }\n }\n\n /**\n * @private\n */\n joinRef(){ return this.joinPush.ref }\n\n /**\n * @private\n */\n rejoin(timeout = this.timeout){\n if(this.isLeaving()){ return }\n this.socket.leaveOpenTopic(this.topic)\n this.state = CHANNEL_STATES.joining\n this.joinPush.resend(timeout)\n }\n\n /**\n * @private\n */\n trigger(event, payload, ref, joinRef){\n let handledPayload = this.onMessage(event, payload, ref, joinRef)\n if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n for(let i = 0; i < eventBindings.length; i++){\n let bind = eventBindings[i]\n bind.callback(handledPayload, ref, joinRef || this.joinRef())\n }\n }\n\n /**\n * @private\n */\n replyEventName(ref){ return `chan_reply_${ref}` }\n\n /**\n * @private\n */\n isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n /**\n * @private\n */\n isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n /**\n * @private\n */\n isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n /**\n * @private\n */\n isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n /**\n * @private\n */\n isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n global,\n XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n static request(method, endPoint, headers, body, timeout, ontimeout, callback){\n if(global.XDomainRequest){\n let req = new global.XDomainRequest() // IE8, IE9\n return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n } else if(global.XMLHttpRequest){\n let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)\n } else if(global.fetch && global.AbortController){\n // Fetch with AbortController for modern browsers\n return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)\n } else {\n throw new Error(\"No suitable XMLHttpRequest implementation found\")\n }\n }\n\n static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){\n let options = {\n method,\n headers,\n body,\n }\n let controller = null\n let timeoutId = null\n if(timeout){\n controller = new AbortController()\n timeoutId = setTimeout(() => controller.abort(), timeout)\n options.signal = controller.signal\n }\n global.fetch(endPoint, options)\n .then(response => response.text())\n .then(data => this.parseJSON(data))\n .then(data => {\n if(timeoutId){ clearTimeout(timeoutId) }\n callback && callback(data)\n })\n .catch(err => {\n if(timeoutId){ clearTimeout(timeoutId) }\n if(err.name === \"AbortError\" && ontimeout){\n ontimeout()\n } else {\n callback && callback(null)\n }\n })\n return controller\n }\n\n static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n req.timeout = timeout\n req.open(method, endPoint)\n req.onload = () => {\n let response = this.parseJSON(req.responseText)\n callback && callback(response)\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n // Work around bug in IE9 that requires an attached onprogress handler\n req.onprogress = () => { }\n\n req.send(body)\n return req\n }\n\n static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){\n req.open(method, endPoint, true)\n req.timeout = timeout\n for(let [key, value] of Object.entries(headers)){\n req.setRequestHeader(key, value)\n }\n req.onerror = () => callback && callback(null)\n req.onreadystatechange = () => {\n if(req.readyState === XHR_STATES.complete && callback){\n let response = this.parseJSON(req.responseText)\n callback(response)\n }\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n req.send(body)\n return req\n }\n\n static parseJSON(resp){\n if(!resp || resp === \"\"){ return null }\n\n try {\n return JSON.parse(resp)\n } catch {\n console && console.log(\"failed to parse JSON response\", resp)\n return null\n }\n }\n\n static serialize(obj, parentKey){\n let queryStr = []\n for(var key in obj){\n if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n let paramKey = parentKey ? `${parentKey}[${key}]` : key\n let paramVal = obj[key]\n if(typeof paramVal === \"object\"){\n queryStr.push(this.serialize(paramVal, paramKey))\n } else {\n queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n }\n }\n return queryStr.join(\"&\")\n }\n\n static appendParams(url, params){\n if(Object.keys(params).length === 0){ return url }\n\n let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n return `${url}${prefix}${this.serialize(params)}`\n }\n}\n", "import {\n SOCKET_STATES,\n TRANSPORTS,\n AUTH_TOKEN_PREFIX,\n MAX_LONGPOLL_BATCH_SIZE\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBufferToBase64 = (buffer) => {\n let binary = \"\"\n let bytes = new Uint8Array(buffer)\n let len = bytes.byteLength\n for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }\n return btoa(binary)\n}\n\nexport default class LongPoll {\n\n constructor(endPoint, protocols){\n // we only support subprotocols for authToken\n // [\"phoenix\", \"base64url.bearer.phx.BASE64_ENCODED_TOKEN\"]\n if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){\n this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))\n }\n this.endPoint = null\n this.token = null\n this.skipHeartbeat = true\n // the pending requests and queued messages, which are cancelled when we stop\n this.reqs = new Set()\n this.messageTimers = new Set()\n this.awaitingBatchAck = false\n this.currentBatch = null\n this.currentBatchTimer = null\n this.batchBuffer = []\n this.onopen = function (){ } // noop\n this.onerror = function (){ } // noop\n this.onmessage = function (){ } // noop\n this.onclose = function (){ } // noop\n this.pollEndpoint = this.normalizeEndpoint(endPoint)\n this.readyState = SOCKET_STATES.connecting\n // we must wait for the caller to finish setting up our callbacks and timeout properties,\n // and it might close us before that, e.g. when disconnecting right after connecting\n setTimeout(() => this.poll(), 0)\n }\n\n normalizeEndpoint(endPoint){\n return (endPoint\n .replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")\n .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n }\n\n endpointURL(){\n return this.pollEndpoint\n }\n\n closeAndRetry(error, code, reason, wasClean){\n if(!this.isActive()){ return }\n // like a failed WebSocket, we are no longer open when we emit the error and then the close.\n // We are connecting again before their callbacks run, so that they can still close us for\n // good, e.g. when they disconnect the socket\n this.stop(SOCKET_STATES.connecting)\n this.onerror(error)\n if(this.isActive()){ this.emitClose(code, reason, wasClean) }\n }\n\n ontimeout(){\n this.closeAndRetry(\"timeout\", 1005, \"timeout\", false)\n }\n\n isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }\n\n poll(){\n // onopen can close us before the next poll, just as the caller can close us\n // before the constructor's deferred first poll\n if(!this.isActive()){ return }\n const headers = {\"Accept\": \"application/json\"}\n if(this.authToken){\n headers[\"X-Phoenix-AuthToken\"] = this.authToken\n }\n this.ajax(\"GET\", headers, null, () => this.ontimeout(), resp => {\n if(resp){\n var {status, token, messages} = resp\n if(status === 410 && this.token !== null){\n // In case we already have a token, this means that our existing session\n // is gone. We fail so that the client rejoins its channels.\n this.closeAndRetry(410, 3410, \"session_gone\", false)\n return\n }\n this.token = token\n } else {\n status = 0\n }\n\n switch(status){\n case 200:\n messages.forEach(msg => {\n // Tasks are what things like event handlers, setTimeout callbacks,\n // promise resolves and more are run within.\n // In modern browsers, there are two different kinds of tasks,\n // microtasks and macrotasks.\n // Microtasks are mainly used for Promises, while macrotasks are\n // used for everything else.\n // Microtasks always have priority over macrotasks. If the JS engine\n // is looking for a task to run, it will always try to empty the\n // microtask queue before attempting to run anything from the\n // macrotask queue.\n //\n // For the WebSocket transport, messages always arrive in their own\n // event. This means that if any promises are resolved from within,\n // their callbacks will always finish execution by the time the\n // next message event handler is run.\n //\n // In order to emulate this behaviour, we need to make sure each\n // onmessage handler is run within its own macrotask.\n //\n // We might stop before that, e.g. when a POST times out or a message\n // handler disconnects, which cancels the remaining messages.\n const timer = setTimeout(() => {\n this.messageTimers.delete(timer)\n this.onmessage({data: msg})\n }, 0)\n this.messageTimers.add(timer)\n })\n this.poll()\n break\n case 204:\n this.poll()\n break\n case 410:\n this.readyState = SOCKET_STATES.open\n this.onopen({})\n this.poll()\n break\n case 403:\n this.onerror(403)\n this.close(1008, \"forbidden\", false)\n break\n case 0:\n case 500:\n this.closeAndRetry(500, 1011, \"internal server error\", false)\n break\n default: throw new Error(`unhandled poll status ${status}`)\n }\n })\n }\n\n // we collect all pushes within the current event loop by\n // setTimeout 0, which optimizes back-to-back procedural\n // pushes against an empty buffer\n\n send(body){\n if(typeof(body) !== \"string\"){ body = arrayBufferToBase64(body) }\n if(this.currentBatch){\n this.currentBatch.push(body)\n } else if(this.awaitingBatchAck){\n this.batchBuffer.push(body)\n } else {\n this.currentBatch = [body]\n this.currentBatchTimer = setTimeout(() => {\n this.batchSend(this.currentBatch)\n this.currentBatch = null\n }, 0)\n }\n }\n\n batchSend(messages, offset = 0){\n this.awaitingBatchAck = true\n const next = offset + MAX_LONGPOLL_BATCH_SIZE\n const batch = messages.slice(offset, next)\n this.ajax(\"POST\", {\"Content-Type\": \"application/x-ndjson\"}, batch.join(\"\\n\"), () => this.ontimeout(), resp => {\n if(!resp || resp.status !== 200){\n // this calls stop which already clears awaitingBatchAck\n this.closeAndRetry(resp && resp.status, 1011, \"internal server error\", false)\n } else if(next < messages.length){\n this.batchSend(messages, next)\n } else if(this.batchBuffer.length > 0){\n this.batchSend(this.batchBuffer)\n this.batchBuffer = []\n } else {\n this.awaitingBatchAck = false\n }\n })\n }\n\n close(code, reason, wasClean){\n this.stop(SOCKET_STATES.closed)\n this.emitClose(code, reason, wasClean)\n }\n\n /**\n * Cancels the pending requests, queued messages and outgoing batches.\n */\n stop(readyState){\n this.readyState = readyState\n // the requests are no longer ours before we abort them, as aborting can invoke their\n // callbacks, synchronously for XHR and asynchronously for fetch\n const reqs = this.reqs\n this.reqs = new Set()\n for(let {req} of reqs){ req && req.abort() }\n for(let timer of this.messageTimers){ clearTimeout(timer) }\n this.messageTimers.clear()\n this.batchBuffer = []\n this.awaitingBatchAck = false\n clearTimeout(this.currentBatchTimer)\n this.currentBatchTimer = null\n this.currentBatch = null\n }\n\n /**\n * Emits the close event. Like a WebSocket closed without a code, it reports 1005 (no status)\n * unless one is given, as 1000 would look like the server closing the connection normally.\n */\n emitClose(code = 1005, reason, wasClean = true){\n let opts = {code, reason, wasClean}\n if(typeof(CloseEvent) !== \"undefined\"){\n this.onclose(new CloseEvent(\"close\", opts))\n } else {\n this.onclose(opts)\n }\n }\n\n ajax(method, headers, body, onCallerTimeout, callback){\n // the request is ours until it completes or we stop, and the callbacks of a request\n // that is not ours anymore are ignored. It is ours before it starts, as a request can\n // complete while it starts.\n const request = {req: null}\n this.reqs.add(request)\n let ontimeout = () => {\n if(this.reqs.delete(request)){ onCallerTimeout() }\n }\n if(this.token !== null){\n headers = Object.assign({}, headers, {\"X-Phoenix-Longpoll-Token\": this.token})\n }\n request.req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {\n if(this.reqs.delete(request)){ callback(resp) }\n })\n }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n * for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n constructor(channel, opts = {}){\n let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n this.state = Object.create(null)\n this.pendingDiffs = []\n this.channel = channel\n this.joinRef = null\n this.caller = {\n onJoin: function (){ },\n onLeave: function (){ },\n onSync: function (){ }\n }\n\n this.channel.on(events.state, newState => {\n let {onJoin, onLeave, onSync} = this.caller\n\n this.joinRef = this.channel.joinRef()\n this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n this.pendingDiffs.forEach(diff => {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n })\n this.pendingDiffs = []\n onSync()\n })\n\n this.channel.on(events.diff, diff => {\n let {onJoin, onLeave, onSync} = this.caller\n\n if(this.inPendingSyncState()){\n this.pendingDiffs.push(diff)\n } else {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n onSync()\n }\n })\n }\n\n onJoin(callback){ this.caller.onJoin = callback }\n\n onLeave(callback){ this.caller.onLeave = callback }\n\n onSync(callback){ this.caller.onSync = callback }\n\n list(by){ return Presence.list(this.state, by) }\n\n inPendingSyncState(){\n return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n }\n\n // lower-level public static API\n\n /**\n * Used to sync the list of presences on the server\n * with the client's state. An optional `onJoin` and `onLeave` callback can\n * be provided to react to changes in the client's local presences across\n * disconnects and reconnects with the server.\n *\n * @returns {Presence}\n */\n static syncState(currentState, newState, onJoin, onLeave){\n let state = this.toNullProtoObj(this.clone(currentState))\n newState = this.toNullProtoObj(newState)\n let joins = Object.create(null)\n let leaves = Object.create(null)\n\n this.map(state, (key, presence) => {\n if(!newState[key]){\n leaves[key] = presence\n }\n })\n this.map(newState, (key, newPresence) => {\n let currentPresence = state[key]\n if(currentPresence){\n let newRefs = newPresence.metas.map(m => m.phx_ref)\n let curRefs = currentPresence.metas.map(m => m.phx_ref)\n let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n if(joinedMetas.length > 0){\n joins[key] = newPresence\n joins[key].metas = joinedMetas\n }\n if(leftMetas.length > 0){\n leaves[key] = this.clone(currentPresence)\n leaves[key].metas = leftMetas\n }\n } else {\n joins[key] = newPresence\n }\n })\n return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n }\n\n /**\n *\n * Used to sync a diff of presence join and leave\n * events from the server, as they happen. Like `syncState`, `syncDiff`\n * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n * joining or leaving from a device.\n *\n * @returns {Presence}\n */\n static syncDiff(state, diff, onJoin, onLeave){\n state = this.toNullProtoObj(state)\n let {joins, leaves} = this.clone(diff)\n if(!onJoin){ onJoin = function (){ } }\n if(!onLeave){ onLeave = function (){ } }\n\n this.map(joins, (key, newPresence) => {\n let currentPresence = state[key]\n state[key] = this.clone(newPresence)\n if(currentPresence){\n let joinedRefs = state[key].metas.map(m => m.phx_ref)\n let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n state[key].metas.unshift(...curMetas)\n }\n onJoin(key, currentPresence, newPresence)\n })\n this.map(leaves, (key, leftPresence) => {\n let currentPresence = state[key]\n if(!currentPresence){ return }\n let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n currentPresence.metas = currentPresence.metas.filter(p => {\n return refsToRemove.indexOf(p.phx_ref) < 0\n })\n onLeave(key, currentPresence, leftPresence)\n if(currentPresence.metas.length === 0){\n delete state[key]\n }\n })\n return state\n }\n\n /**\n * Returns the array of presences, with selected metadata.\n *\n * @param {Object} presences\n * @param {Function} chooser\n *\n * @returns {Presence}\n */\n static list(presences, chooser){\n if(!chooser){ chooser = function (key, pres){ return pres } }\n\n return this.map(presences, (key, presence) => {\n return chooser(key, presence)\n })\n }\n\n // private\n\n static map(obj, func){\n return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n }\n\n // Presence keys are chosen on the server and may collide with\n // Object.prototype properties (\"__proto__\", \"constructor\", ...), so any\n // object indexed by presence key must not have a prototype chain\n //\n // TODO: replace the null-prototype objects with Maps in Phoenix 2.0\n // (breaking change for the lower-level static API)\n static toNullProtoObj(obj){\n if(Object.getPrototypeOf(obj) === null){ return obj }\n let cleaned = Object.create(null)\n Object.getOwnPropertyNames(obj).forEach(key => {\n cleaned[key] = obj[key]\n })\n return cleaned\n }\n\n static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n HEADER_LENGTH: 1,\n META_LENGTH: 4,\n KINDS: {push: 0, reply: 1, broadcast: 2},\n\n encode(msg, callback){\n if(msg.payload.constructor === ArrayBuffer){\n return callback(this.binaryEncode(msg))\n } else {\n let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n return callback(JSON.stringify(payload))\n }\n },\n\n decode(rawPayload, callback){\n if(rawPayload.constructor === ArrayBuffer){\n return callback(this.binaryDecode(rawPayload))\n } else {\n let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n return callback({join_ref, ref, topic, event, payload})\n }\n },\n\n // private\n\n binaryEncode(message){\n let {join_ref, ref, event, topic, payload} = message\n let encoder = new TextEncoder()\n let joinRefBytes = encoder.encode(join_ref)\n let refBytes = encoder.encode(ref)\n let topicBytes = encoder.encode(topic)\n let eventBytes = encoder.encode(event)\n\n this.assertFieldSize(joinRefBytes.byteLength, \"join_ref\")\n this.assertFieldSize(refBytes.byteLength, \"ref\")\n this.assertFieldSize(topicBytes.byteLength, \"topic\")\n this.assertFieldSize(eventBytes.byteLength, \"event\")\n\n let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength\n let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n let headerBytes = new Uint8Array(header)\n let view = new DataView(header)\n let offset = 0\n\n view.setUint8(offset++, this.KINDS.push) // kind\n view.setUint8(offset++, joinRefBytes.byteLength)\n view.setUint8(offset++, refBytes.byteLength)\n view.setUint8(offset++, topicBytes.byteLength)\n view.setUint8(offset++, eventBytes.byteLength)\n headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength\n headerBytes.set(refBytes, offset); offset += refBytes.byteLength\n headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength\n headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength\n\n var combined = new Uint8Array(header.byteLength + payload.byteLength)\n combined.set(headerBytes, 0)\n combined.set(new Uint8Array(payload), header.byteLength)\n\n return combined.buffer\n },\n\n assertFieldSize(size, name){\n if(size > 255){\n throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)\n }\n },\n\n binaryDecode(buffer){\n let view = new DataView(buffer)\n let kind = view.getUint8(0)\n let decoder = new TextDecoder()\n switch(kind){\n case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n }\n },\n\n decodePush(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let topicSize = view.getUint8(2)\n let eventSize = view.getUint8(3)\n let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n },\n\n decodeReply(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let refSize = view.getUint8(2)\n let topicSize = view.getUint8(3)\n let eventSize = view.getUint8(4)\n let offset = this.HEADER_LENGTH + this.META_LENGTH\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n offset = offset + refSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n let payload = {status: event, response: data}\n return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n },\n\n decodeBroadcast(buffer, view, decoder){\n let topicSize = view.getUint8(1)\n let eventSize = view.getUint8(2)\n let offset = this.HEADER_LENGTH + 2\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n\n return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n }\n}\n", "import {SOCKET_STATES} from \"./constants\"\n\n/**\n * @private\n *\n * A connection of the socket over a single transport instance.\n *\n * It owns everything that belongs to this connection, so that nothing about it can\n * affect a later connection: the transport events are only handled while it is the\n * socket's current connection, and its state is gone together with it.\n */\nexport default class Connection {\n /**\n * @param {Socket} socket\n * @param {Object} transport - The transport instance, for example a WebSocket\n */\n constructor(socket, transport){\n this.socket = socket\n this.transport = transport\n // the join ref of each channel whose join was sent over this connection, weakly held\n // as a connection can outlive many channels, e.g. when navigating between LiveViews\n this.joins = new WeakMap()\n // the callbacks of the pings sent over this connection by ref, as their replies can only\n // arrive over it, so they are gone together with it instead of accumulating on the socket\n this.pings = new Map()\n // set when we close the connection, as opposed to the server or the network\n this.closing = false\n // set when the socket does not want this connection anymore, e.g. on disconnect,\n // so that connecting again creates a new one instead of waiting for it\n this.ended = false\n // set once the channels were errored after the connection stopped being open\n this.channelsErrored = false\n // set once the close event was handled, as LongPoll can emit it more than once\n this.closeHandled = false\n transport.onopen = () => { if(this.isCurrent()){ socket.onConnOpen() } }\n transport.onerror = error => { if(this.isCurrent()){ socket.onConnError(error) } }\n transport.onmessage = event => { if(this.isCurrent()){ socket.onConnMessage(event) } }\n transport.onclose = event => {\n if(this.isCurrent() && !this.closeHandled){\n this.closeHandled = true\n socket.onConnClose(event)\n }\n }\n }\n\n /**\n * Whether this is the connection the socket currently uses. A replaced connection\n * can still emit events while it closes, but they no longer concern the socket.\n */\n isCurrent(){ return this.socket.connection === this }\n\n isOpen(){ return this.transport.readyState === SOCKET_STATES.open }\n\n isConnecting(){ return this.transport.readyState === SOCKET_STATES.connecting }\n\n /**\n * Whether the channel's current join was sent over this connection.\n */\n carried(channel, joinRef = channel.joinRef()){\n return this.joins.has(channel) && this.joins.get(channel) === joinRef\n }\n\n /**\n * Closes the transport. Its close event then only confirms our close.\n */\n close(code, reason){\n this.closing = true\n // closing a transport that is still connecting makes it emit an error, which is\n // caused by closing it and must not be handled as a transport error\n this.transport.onerror = function (){ } // noop\n if(code){ this.transport.close(code, reason || \"\") } else { this.transport.close() }\n }\n}\n", "import {\n global,\n phxWindow,\n CHANNEL_EVENTS,\n DEFAULT_TIMEOUT,\n DEFAULT_VSN,\n SOCKET_STATES,\n TRANSPORTS,\n WS_CLOSE_NORMAL,\n AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport {\n closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\nimport Connection from \"./connection\"\n\n/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n * `\"wss://example.com\"`\n * `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.\n * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.\n *\n * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport\n * before falling back to the LongPoll transport. Disabled by default.\n *\n * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.\n *\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the\n * socket reconnect interval, in milliseconds.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n * return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {boolean} [opts.reconnectWhenHidden] - When true, the socket also reconnects while the\n * page is hidden, for example in a background tab or on a sleeping phone.\n *\n * Defaults to false. The socket then waits until the page is visible again before it reconnects.\n * This avoids reconnect attempts that stall on mobile devices and delay the connection after the\n * device wakes up.\n *\n * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n * return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n * console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {(Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {(string|function)} [opts.authToken] - the optional authentication token to be exposed on the server\n * under the `:auth_token` connect_info key. Can be a string or a function that returns a string.\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n *\n * @param {Object} [opts.sessionStorage] - An optional Storage compatible object\n * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is\n * useful when Phoenix won't have access to `sessionStorage`. For example, This could\n * happen if a site loads a cross-domain channel in an iframe. Example usage:\n *\n * class InMemoryStorage {\n * constructor() { this.storage = {} }\n * getItem(keyName) { return this.storage[keyName] || null }\n * removeItem(keyName) { delete this.storage[keyName] }\n * setItem(keyName, keyValue) { this.storage[keyName] = keyValue }\n * }\n *\n*/\nexport default class Socket {\n constructor(endPoint, opts = {}){\n this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n this.channels = []\n this.sendBuffer = []\n this.ref = 0\n this.fallbackRef = null\n this.fallbackErrorRef = null\n this.timeout = opts.timeout || DEFAULT_TIMEOUT\n this.transport = opts.transport || global.WebSocket || LongPoll\n this.primaryPassedHealthCheck = false\n this.longPollFallbackMs = opts.longPollFallbackMs\n this.fallbackTimer = null\n this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)\n this.establishedConnections = 0\n this.defaultEncoder = Serializer.encode.bind(Serializer)\n this.defaultDecoder = Serializer.decode.bind(Serializer)\n // We start with closeWasClean true to avoid the visibility change\n // logic from connecting if the socket was never connected in the first place.\n // transportConnect sets it to false on open.\n this.closeWasClean = true\n this.binaryType = opts.binaryType || \"arraybuffer\"\n // the connection over the current transport instance, see Connection\n this.connection = null\n // changes whenever we connect, disconnect or replace the transport, for the code that needs\n // to know whether that happened while there was no connection, see replaceTransport\n this.connectClock = 1\n // the connection created when the page was shown, see handleVisibilityChange\n this.visibilityConnection = null\n if(this.transport !== LongPoll){\n this.encode = opts.encode || this.defaultEncoder\n this.decode = opts.decode || this.defaultDecoder\n } else {\n this.encode = this.defaultEncoder\n this.decode = this.defaultDecoder\n }\n let awaitingConnectionOnPageShow = null\n if(phxWindow && phxWindow.addEventListener){\n phxWindow.addEventListener(\"pagehide\", _e => {\n // an ended connection only finishes closing, e.g. after disconnect\n if(this.connection && !this.connection.ended){\n this.disconnect()\n awaitingConnectionOnPageShow = this.connectClock\n }\n })\n phxWindow.addEventListener(\"pageshow\", _e => {\n if(awaitingConnectionOnPageShow === this.connectClock){\n awaitingConnectionOnPageShow = null\n this.connect()\n }\n })\n phxWindow.addEventListener(\"visibilitychange\", () => {\n this.handleVisibilityChange()\n })\n // Chrome 149 started to not reliably fire visibilitychange after resume,\n // see https://issues.chromium.org/issues/547062449.\n phxWindow.document && phxWindow.document.addEventListener(\"resume\", () => {\n this.handleVisibilityChange()\n })\n }\n this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n this.rejoinAfterMs = (tries) => {\n if(opts.rejoinAfterMs){\n return opts.rejoinAfterMs(tries)\n } else {\n return [1000, 2000, 5000][tries - 1] || 10000\n }\n }\n this.reconnectAfterMs = (tries) => {\n if(opts.reconnectAfterMs){\n return opts.reconnectAfterMs(tries)\n } else {\n return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n }\n }\n this.reconnectWhenHidden = opts.reconnectWhenHidden || false\n this.logger = opts.logger || null\n if(!this.logger && opts.debug){\n this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }\n }\n this.longpollerTimeout = opts.longpollerTimeout || 20000\n this.params = closure(opts.params || {})\n this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n this.vsn = opts.vsn || DEFAULT_VSN\n this.heartbeatTimeoutTimer = null\n this.heartbeatTimer = null\n this.pendingHeartbeatRef = null\n this.reconnectTimer = new Timer(() => {\n // handleVisibilityChange reconnects once the page is visible again\n if(this.pageHidden && !this.reconnectWhenHidden){\n this.log(\"Not reconnecting as page is hidden!\")\n this.teardown()\n return\n }\n this.teardownAndReconnect(() => this.connect())\n }, this.reconnectAfterMs)\n this.authToken = opts.authToken && closure(opts.authToken)\n }\n\n /**\n * The transport instance of the current connection, for example a WebSocket\n */\n get conn(){ return this.connection && this.connection.transport }\n\n /**\n * @internal\n */\n get pageHidden(){\n return phxWindow && phxWindow.document ? phxWindow.document.visibilityState === \"hidden\" : false\n }\n\n /**\n * @internal\n */\n handleVisibilityChange(){\n if(this.pageHidden){\n this.visibilityConnection = null\n return\n }\n // resume and visibilitychange can both fire when the page is shown again, so we keep\n // the connection created for the first one while it is still connecting\n const connection = this.connection\n if(connection && connection === this.visibilityConnection && connection.isConnecting()){ return }\n // reconnect immediately\n if(!this.isConnected() && !this.closeWasClean){\n // a reconnect that was scheduled before would tear down the connection we create here\n this.reconnectTimer.reset()\n this.teardownAndReconnect(() => {\n this.connect()\n this.visibilityConnection = this.connection\n })\n }\n }\n\n /**\n * Returns the LongPoll transport reference\n */\n getLongPollTransport(){ return LongPoll }\n\n /**\n * Disconnects and replaces the active transport\n *\n * @param {Function} newTransport - The new transport class to instantiate\n *\n */\n replaceTransport(newTransport){\n const connectClock = ++this.connectClock\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // replaced first, as channel error callbacks below can already connect again\n this.transport = newTransport\n const connection = this.connection\n if(connection){\n const wasOpen = connection.isOpen()\n // the old transport closes asynchronously, but its events no longer concern us\n this.connection = null\n connection.ended = true\n connection.close()\n this.clearHeartbeats()\n // any in-flight pushes on the old connection are lost, so channels need to rejoin\n if(wasOpen){ this.triggerChanError(\"connection_closed\", connection) }\n }\n return connectClock\n }\n\n /**\n * Returns the socket protocol\n *\n * @returns {string}\n */\n protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n /**\n * The fully qualified socket url\n *\n * @returns {string}\n */\n endPointURL(){\n let uri = Ajax.appendParams(\n Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n if(uri.charAt(0) !== \"/\"){ return uri }\n if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n return `${this.protocol()}://${location.host}${uri}`\n }\n\n /**\n * Disconnects the socket\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n *\n * @param {Function} callback - Optional callback which is called after socket is disconnected.\n * @param {integer} code - A status code for disconnection (Optional).\n * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n */\n disconnect(callback, code, reason){\n this.connectClock++\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // connecting again creates a new connection right away instead of waiting for this one to close\n if(this.connection){ this.connection.ended = true }\n this.teardown(callback, code, reason)\n }\n\n /**\n *\n * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n *\n * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n */\n connect(params){\n if(params){\n console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n this.params = closure(params)\n }\n // the current connection is kept, including while it reconnects, until the socket is done with it\n if(this.connection && !this.connection.ended){ return }\n if(this.longPollFallbackMs && this.transport !== LongPoll){\n this.connectWithFallback(LongPoll, this.longPollFallbackMs)\n } else {\n this.transportConnect()\n }\n }\n\n /**\n * Logs the message. Override `this.logger` for specialized logging. noops by default\n * @param {string} kind\n * @param {string} msg\n * @param {Object} data\n */\n log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }\n\n /**\n * Returns true if a logger has been set on this socket.\n */\n hasLogger(){ return this.logger !== null }\n\n /**\n * Registers callbacks for connection open events\n *\n * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n *\n * @param {Function} callback\n */\n onOpen(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.open.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection close events\n * @param {Function} callback\n */\n onClose(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.close.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection error events\n *\n * @example socket.onError(function(error){ alert(\"An error occurred\") })\n *\n * @param {Function} callback\n */\n onError(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.error.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection message events\n * @param {Function} callback\n */\n onMessage(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.message.push([ref, callback])\n return ref\n }\n\n /**\n * Pings the server and invokes the callback with the RTT in milliseconds\n * @param {Function} callback\n *\n * Returns true if the ping was pushed or false if unable to be pushed.\n */\n ping(callback){\n if(!this.isConnected()){ return false }\n let ref = this.makeRef()\n let startTime = Date.now()\n this.connection.pings.set(ref, () => callback(Date.now() - startTime))\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: ref})\n return true\n }\n\n /**\n * @private\n *\n * @param {Function}\n */\n transportName(transport){\n // JavaScript minification, enabled by default in production in Phoenix\n // projects, renames symbols to reduce code size.\n // See https://esbuild.github.io/api/#keep-names.\n // This helper ensures we return the correct name for the LongPoll transport\n // even after minification. The other common transport is WebSocket, which\n // is native to browsers and does not need special handling.\n switch(transport){\n case LongPoll: return \"LongPoll\"\n default: return transport.name\n }\n }\n\n /**\n * @private\n */\n transportConnect(){\n const previous = this.connection\n this.connectClock++\n this.closeWasClean = false\n // a reconnect scheduled before would tear down the new connection, but we keep\n // its tries, which only reset once a connection opens\n clearTimeout(this.reconnectTimer.timer)\n let protocols = undefined\n // Sec-WebSocket-Protocol based token\n // (longpoll uses Authorization header instead)\n if(this.authToken){\n protocols = [\"phoenix\", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken()).replace(/=/g, \"\")}`]\n }\n const transport = new this.transport(this.endPointURL(), protocols)\n transport.binaryType = this.binaryType\n transport.timeout = this.longpollerTimeout\n this.connection = new Connection(this, transport)\n // connect can replace an ended connection that is still open while its buffer drains, but the\n // channels joined over it need to rejoin on the new connection instead of waiting for it to close\n if(previous && previous.isOpen()){ this.triggerChanError(\"connection_closed\", previous) }\n }\n\n getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }\n\n storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }\n\n connectWithFallback(fallbackTransport, fallbackThreshold = 2500){\n // the callbacks of a previous attempt must not act on this one, e.g. an attempt\n // that never opened would otherwise fall back again on the next error\n this.cancelFallback()\n let established = false\n let primaryTransport = true\n let errorRef\n let connectClock\n const isCurrent = () => connectClock === this.connectClock && !this.closeWasClean\n let fallbackTransportName = this.transportName(fallbackTransport)\n // the fallback timer is cleared whenever the attempt ends, and the callbacks below\n // only fall back while the attempt is current\n let fallback = (reason) => {\n this.log(\"transport\", `falling back to ${fallbackTransportName}...`, reason)\n // the open callback stays registered to remember the fallback once it connects\n this.off([errorRef])\n primaryTransport = false\n const replaceClock = this.replaceTransport(fallbackTransport)\n if(replaceClock !== this.connectClock){ return }\n this.transportConnect()\n }\n if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback(\"memorized\") }\n\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n\n errorRef = this.fallbackErrorRef = this.onError(reason => {\n if(!isCurrent()){ return }\n this.log(\"transport\", \"error\", reason)\n if(primaryTransport && !established){\n clearTimeout(this.fallbackTimer)\n fallback(reason)\n }\n })\n this.fallbackRef = this.onOpen(() => {\n if(!primaryTransport){\n // reconnects keep using the fallback transport, so a later successful connection\n // must still be remembered, unless an earlier callback disconnected it\n if(this.transport !== fallbackTransport || this.closeWasClean){ return }\n let fallbackTransportName = this.transportName(fallbackTransport)\n // only memorize LP if we never connected to primary\n if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, \"true\") }\n return this.log(\"transport\", `established ${fallbackTransportName} fallback`)\n }\n if(!isCurrent()){ return }\n established = true\n // if we've established primary, give the fallback a new period to attempt ping\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n this.ping(rtt => {\n this.log(\"transport\", \"connected to primary after\", rtt)\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n })\n })\n this.transportConnect()\n connectClock = this.connectClock\n }\n\n /**\n * @private\n *\n * Stops the fallback of the current connection attempt, see connectWithFallback\n */\n cancelFallback(){\n clearTimeout(this.fallbackTimer)\n this.off([this.fallbackRef, this.fallbackErrorRef])\n }\n\n clearHeartbeats(){\n clearTimeout(this.heartbeatTimer)\n clearTimeout(this.heartbeatTimeoutTimer)\n // a late reply to the pending heartbeat must not start the heartbeats again\n this.pendingHeartbeatRef = null\n }\n\n onConnOpen(){\n if(this.hasLogger()) this.log(\"transport\", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)\n this.closeWasClean = false\n this.establishedConnections++\n // the health-check ping has to leave before the buffered join. The transport\n // process blocks in Channel.Server.join until the join returns, so a ping\n // sent after the join is not answered while the mount runs, and the fallback\n // timer then drops a socket that is fine.\n this.pingBeforeBufferedJoin()\n this.flushSendBuffer()\n this.reconnectTimer.reset()\n this.resetHeartbeat()\n this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n }\n\n /**\n * @private\n *\n * Sends the fallback health check before flushSendBuffer. No-op unless a\n * fallback attempt is waiting on the primary transport.\n */\n pingBeforeBufferedJoin(){\n if(!this.fallbackTimer || this.transport === LongPoll){ return }\n this.ping(() => this.passPrimaryHealthCheck())\n }\n\n /**\n * @private\n *\n * The primary transport answered. Later frames on this connection also call\n * this, because a frame is the proof the ping was waiting for.\n */\n passPrimaryHealthCheck(){\n if(this.transport === LongPoll){ return }\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = null\n }\n\n /**\n * @private\n */\n\n heartbeatTimeout(){\n if(this.pendingHeartbeatRef){\n const connection = this.connection\n this.pendingHeartbeatRef = null\n if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n this.closeWasClean = false\n // the connection is presumed dead, so we close it right away instead of waiting for its\n // buffer to drain, which also ensures that it is no longer considered connected when the\n // channels error (LiveView would report a server error otherwise)\n if(connection){ connection.close(WS_CLOSE_NORMAL, \"heartbeat timeout\") }\n // some transports emit their close event synchronously, and callbacks of it as well as\n // channel error callbacks can disconnect or connect, which takes precedence over reconnecting\n if(!this.stillUses(connection)){ return }\n this.triggerChanError(\"heartbeat_timeout\")\n if(!this.stillUses(connection)){ return }\n this.teardownAndReconnect(() => this.reconnectTimer.scheduleTimeout())\n }\n }\n\n resetHeartbeat(){\n if(this.conn && this.conn.skipHeartbeat){ return }\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n /**\n * @private\n *\n * Tears down the current connection to reconnect afterwards, unless the socket was\n * disconnected or connected again in the meantime, which takes precedence.\n */\n teardownAndReconnect(reconnect){\n const connection = this.connection\n const connectClock = this.connectClock\n this.teardown(() => {\n // the socket was disconnected or connected again in the meantime, which the connection does not\n // tell once an overlapping teardown gave up on it\n if(connectClock !== this.connectClock || (connection && connection.ended)){ return }\n reconnect()\n })\n }\n\n /**\n * @private\n *\n * Whether the socket still uses the connection and did not give up on it. User callbacks\n * can disconnect or connect, so code continuing after them checks this first.\n */\n stillUses(connection){\n return this.connection === connection && !(connection && connection.ended)\n }\n\n teardown(callback, code, reason){\n // If someone calls connect before we finish tearing down,\n // we create a new connection, but we still want to finish tearing down the old one.\n const connection = this.connection\n if(!connection){\n return callback && callback()\n }\n // onConnClose is not called when the close event arrives late, so we stop the\n // heartbeats here, as a heartbeat timeout would otherwise reconnect the socket\n this.clearHeartbeats()\n\n this.waitForBufferDone(connection.transport, () => {\n // a connection we already closed, e.g. on a heartbeat timeout or in an\n // overlapping teardown, also errored its channels already\n if(!connection.closing){\n const wasOpened = !connection.isConnecting()\n connection.close(code, reason)\n // channels joined over this connection need to rejoin on the next one, but we cannot rely\n // on onConnClose for that, as the close event may arrive after we stopped waiting for it\n // or after a new connection already replaced this one\n if(wasOpened){ this.triggerChanError(\"connection_closed\", connection) }\n }\n\n this.waitForSocketClosed(connection.transport, () => {\n if(this.connection === connection){ this.connection = null }\n callback && callback()\n })\n })\n }\n\n waitForBufferDone(conn, callback, tries = 1){\n // there is no point in waiting for a conn that is already closing\n if(tries === 5 || !conn.bufferedAmount || conn.readyState !== SOCKET_STATES.open){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForBufferDone(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n waitForSocketClosed(conn, callback, tries = 1){\n if(tries === 5 || conn.readyState === SOCKET_STATES.closed){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForSocketClosed(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n onConnClose(event){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n this.clearHeartbeats()\n // we only close a connection to disconnect or to reconnect, which the code closing it takes\n // care of, so its close event only confirms our close. Any other close is reconnected, whatever\n // its code, as the server, proxies and load balancers also close with 1000 (normal closure)\n const closedByUs = !!(connection && connection.closing)\n // channel callbacks may disconnect or create a new connection, so finish cleaning up\n // this connection first and do not schedule a reconnect for its replacement\n this.triggerChanError(\"connection_closed\", connection)\n if(this.stillUses(connection) && !closedByUs && !this.closeWasClean){\n this.reconnectTimer.scheduleTimeout()\n }\n this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n }\n\n /**\n * @private\n */\n onConnError(error){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"error\", error)\n let transportBefore = this.transport\n let establishedBefore = this.establishedConnections\n this.stateChangeCallbacks.error.forEach(([, callback]) => {\n callback(error, transportBefore, establishedBefore)\n })\n // callbacks can replace the connection, in which case only its own joins are errored\n this.triggerChanError(\"connection_error\", connection)\n }\n\n /**\n * @private\n *\n * Errors the channels, as they need to rejoin once the given connection stopped being open.\n */\n triggerChanError(reason, connection = this.connection){\n // several places notice that a connection stopped being open (onConnError, onConnClose,\n // teardown and heartbeatTimeout). After the first, only channels whose join it carried\n // are errored, as error callbacks may have joined channels that join over another connection.\n let erroredBefore = false\n if(connection && !connection.isOpen()){\n erroredBefore = connection.channelsErrored\n connection.channelsErrored = true\n }\n const joins = this.channels.map(channel => [channel, channel.joinRef()])\n joins.forEach(([channel, joinRef]) => {\n // an earlier error callback can rejoin a later channel, so check its join\n // again immediately before dispatching rather than filtering the list first\n if(channel.joinRef() !== joinRef){ return }\n // the joins of a replaced connection are not the only ones anymore\n if(connection && (erroredBefore || !connection.isCurrent()) && !connection.carried(channel, joinRef)){ return }\n if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n channel.trigger(CHANNEL_EVENTS.error, {source: \"transport\", reason})\n }\n })\n }\n\n /**\n * @returns {string}\n */\n connectionState(){\n switch(this.conn && this.conn.readyState){\n case SOCKET_STATES.connecting: return \"connecting\"\n case SOCKET_STATES.open: return \"open\"\n case SOCKET_STATES.closing: return \"closing\"\n default: return \"closed\"\n }\n }\n\n /**\n * @returns {boolean}\n */\n isConnected(){ return this.connectionState() === \"open\" }\n\n /**\n * @private\n *\n * @param {Channel}\n */\n remove(channel){\n this.off(channel.stateChangeRefs)\n this.channels = this.channels.filter(c => c !== channel)\n }\n\n /**\n * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n *\n * @param {refs} - list of refs returned by calls to\n * `onOpen`, `onClose`, `onError,` and `onMessage`\n */\n off(refs){\n for(let key in this.stateChangeCallbacks){\n this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n return refs.indexOf(ref) === -1\n })\n }\n }\n\n /**\n * Initiates a new channel for the given topic\n *\n * @param {string} topic\n * @param {Object} chanParams - Parameters for the channel\n * @returns {Channel}\n */\n channel(topic, chanParams = {}){\n let chan = new Channel(topic, chanParams, this)\n this.channels.push(chan)\n return chan\n }\n\n /**\n * @param {Object} data\n * @param {Channel} [channel] - The channel the push belongs to, if any\n */\n push(data, channel){\n if(this.hasLogger()){\n let {topic, event, payload, ref, join_ref} = data\n this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n }\n\n if(this.isConnected()){\n this.send(data, channel)\n } else {\n this.sendBuffer.push(() => {\n // the channel may have errored or left while the push was buffered, e.g. when the\n // connection failed before opening, in which case its rejoin supersedes the buffered join\n if(!this.isOutdated(data, channel)){ this.send(data, channel) }\n })\n }\n }\n\n /**\n * @private\n */\n send(data, channel){\n const connection = this.connection\n // the join counts as carried by this connection while it is still encoded, so that the\n // channel rejoins when the connection is torn down before the encoding finished\n if(channel && data.event === CHANNEL_EVENTS.join){ connection.joins.set(channel, data.join_ref) }\n this.encode(data, result => {\n // let encodes finish while this connection drains, but never send on a\n // replacement or after the original connection has started closing\n if(!connection.isCurrent() || !connection.isOpen()){ return }\n // a leave still needs to reach the server after its channel was removed locally\n if(data.event !== CHANNEL_EVENTS.leave && this.isOutdated(data, channel)){ return }\n connection.transport.send(result)\n })\n }\n\n /**\n * @private\n *\n * Whether the push was made for an earlier join of its channel, or the channel left since.\n */\n isOutdated(data, channel){\n return !!channel && (data.join_ref !== channel.joinRef() || channel.isClosed())\n }\n\n /**\n * Return the next message ref, accounting for overflows\n * @returns {string}\n */\n makeRef(){\n let newRef = this.ref + 1\n if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n return this.ref.toString()\n }\n\n sendHeartbeat(){\n if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n this.pendingHeartbeatRef = this.makeRef()\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n }\n\n flushSendBuffer(){\n if(this.isConnected() && this.sendBuffer.length > 0){\n this.sendBuffer.forEach(callback => callback())\n this.sendBuffer = []\n }\n }\n\n onConnMessage(rawMessage){\n const connection = this.connection\n const wasOpen = connection && connection.isOpen()\n this.decode(rawMessage.data, msg => {\n // the transport event can be current even though its asynchronous decode is not, as the\n // connection may have been replaced or closed in the meantime (browsers also drop messages\n // that arrive once a WebSocket is closing)\n if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return }\n // a frame on this connection is the health check. The ping reply is one\n // such frame, and so is anything the server already sent.\n if(this.fallbackTimer){ this.passPrimaryHealthCheck() }\n let {topic, event, payload, ref, join_ref} = msg\n if(ref && ref === this.pendingHeartbeatRef){\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n // removing a channel or callback while dispatching replaces the list instead of\n // changing it, so we iterate over the lists from before to not skip any of them\n const channels = this.channels\n for(let i = 0; i < channels.length; i++){\n const channel = channels[i]\n if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n channel.trigger(event, payload, ref, join_ref)\n }\n\n const callbacks = this.stateChangeCallbacks.message\n for(let i = 0; i < callbacks.length; i++){\n let [, callback] = callbacks[i]\n callback(msg)\n }\n\n const ping = ref && connection.pings.get(ref)\n if(ping){\n connection.pings.delete(ref)\n ping()\n }\n })\n }\n\n leaveOpenTopic(topic){\n let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n if(dupChannel){\n if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n dupChannel.leave()\n }\n }\n}\n"], + "mappings": "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", "names": ["closure", "fallbackTransportName"] } diff --git a/priv/static/phoenix.js b/priv/static/phoenix.js index 9313341602..aeadef9d29 100644 --- a/priv/static/phoenix.js +++ b/priv/static/phoenix.js @@ -1617,11 +1617,38 @@ var Phoenix = (() => { if (this.hasLogger()) this.log("transport", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`); this.closeWasClean = false; this.establishedConnections++; + this.pingBeforeBufferedJoin(); this.flushSendBuffer(); this.reconnectTimer.reset(); this.resetHeartbeat(); this.stateChangeCallbacks.open.forEach(([, callback]) => callback()); } + /** + * @private + * + * Sends the fallback health check before flushSendBuffer. No-op unless a + * fallback attempt is waiting on the primary transport. + */ + pingBeforeBufferedJoin() { + if (!this.fallbackTimer || this.transport === LongPoll) { + return; + } + this.ping(() => this.passPrimaryHealthCheck()); + } + /** + * @private + * + * The primary transport answered. Later frames on this connection also call + * this, because a frame is the proof the ping was waiting for. + */ + passPrimaryHealthCheck() { + if (this.transport === LongPoll) { + return; + } + this.primaryPassedHealthCheck = true; + clearTimeout(this.fallbackTimer); + this.fallbackTimer = null; + } /** * @private */ @@ -1900,6 +1927,9 @@ var Phoenix = (() => { if (connection !== this.connection || wasOpen && !connection.isOpen()) { return; } + if (this.fallbackTimer) { + this.passPrimaryHealthCheck(); + } let { topic, event, payload, ref, join_ref } = msg; if (ref && ref === this.pendingHeartbeatRef) { this.clearHeartbeats(); diff --git a/priv/static/phoenix.min.js b/priv/static/phoenix.min.js index 4b47a26ed9..fedbe09f37 100644 --- a/priv/static/phoenix.min.js +++ b/priv/static/phoenix.min.js @@ -1,2 +1,2 @@ -var Phoenix=(()=>{var H=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var W=(h,e)=>{for(var t in e)H(h,t,{get:e[t],enumerable:!0})},z=(h,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of F(e))!I.call(h,s)&&s!==t&&H(h,s,{get:()=>e[s],enumerable:!(i=D(e,s))||i.enumerable});return h};var J=h=>z(H({},"__esModule",{value:!0}),h);var G={};W(G,{Channel:()=>j,LongPoll:()=>S,Presence:()=>w,Serializer:()=>L,Socket:()=>x});var y=h=>typeof h=="function"?h:function(){return h};var X=typeof self!="undefined"?self:null,C=typeof window!="undefined"?window:null,E=X||C||globalThis,B="2.0.0",p={connecting:0,open:1,closing:2,closed:3},P=100,$=1e4,M=1e3,m={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},T={close:"phx_close",error:"phx_error",join:"phx_join",reply:"phx_reply",leave:"phx_leave"},A={longpoll:"longpoll",websocket:"websocket"},U={complete:4},O="base64url.bearer.phx.";var v=class{constructor(e,t,i,s){this.channel=e,this.event=t,this.payload=i||function(){return{}},this.receivedResp=null,this.timeout=s,this.timeoutTimer=null,this.recHooks=[],this.sent=!1}resend(e){this.timeout=e,this.reset(),this.send()}send(){this.hasReceived("timeout")||(this.startTimeout(),this.sent=!0,this.channel.socket.push({topic:this.channel.topic,event:this.event,payload:this.payload(),ref:this.ref,join_ref:this.channel.joinRef()},this.channel))}receive(e,t){return this.hasReceived(e)&&t(this.receivedResp.response),this.recHooks.push({status:e,callback:t}),this}reset(){this.cancelRefEvent(),this.ref=null,this.refEvent=null,this.receivedResp=null,this.sent=!1}matchReceive({status:e,response:t,_ref:i}){this.recHooks.filter(s=>s.status===e).forEach(s=>s.callback(t))}cancelRefEvent(){this.refEvent&&this.channel.off(this.refEvent)}cancelTimeout(){clearTimeout(this.timeoutTimer),this.timeoutTimer=null}startTimeout(){this.timeoutTimer&&this.cancelTimeout(),this.cancelRefEvent(),this.ref=this.channel.socket.makeRef(),this.refEvent=this.channel.replyEventName(this.ref),this.channel.on(this.refEvent,e=>{this.cancelRefEvent(),this.cancelTimeout(),this.receivedResp=e,this.matchReceive(e)}),this.timeoutTimer=setTimeout(()=>{this.trigger("timeout",{})},this.timeout)}hasReceived(e){return this.receivedResp&&this.receivedResp.status===e}trigger(e,t){this.channel.trigger(this.refEvent,{status:e,response:t})}};var k=class{constructor(e,t){this.callback=e,this.timerCalc=t,this.timer=null,this.tries=0}reset(){this.tries=0,clearTimeout(this.timer)}scheduleTimeout(){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.tries=this.tries+1,this.callback()},this.timerCalc(this.tries+1))}};var j=class{constructor(e,t,i){this.state=m.closed,this.topic=e,this.params=y(t||{}),this.socket=i,this.bindings=[],this.bindingRef=0,this.timeout=this.socket.timeout,this.joinedOnce=!1,this.joinPush=new v(this,T.join,this.params,this.timeout),this.pushBuffer=[],this.stateChangeRefs=[],this.rejoinTimer=new k(()=>{this.socket.isConnected()&&this.rejoin()},this.socket.rejoinAfterMs),this.stateChangeRefs.push(this.socket.onError(()=>this.rejoinTimer.reset())),this.stateChangeRefs.push(this.socket.onOpen(()=>{this.rejoinTimer.reset(),this.isErrored()&&this.rejoin()})),this.joinPush.receive("ok",()=>{this.state=m.joined,this.rejoinTimer.reset(),this.pushBuffer.forEach(s=>s.send()),this.pushBuffer=[]}),this.joinPush.receive("error",()=>{this.state=m.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.onClose(()=>{this.rejoinTimer.reset(),this.socket.hasLogger()&&this.socket.log("channel",`close ${this.topic} ${this.joinRef()}`),this.state=m.closed,this.socket.remove(this)}),this.onError(s=>{this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,s),this.isJoining()&&this.joinPush.reset(),this.state=m.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.joinPush.receive("timeout",()=>{this.socket.hasLogger()&&this.socket.log("channel",`timeout ${this.topic} (${this.joinRef()})`,this.joinPush.timeout),new v(this,T.leave,y({}),this.timeout).send(),this.state=m.errored,this.joinPush.reset(),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.on(T.reply,(s,n)=>{this.trigger(this.replyEventName(n),s)})}join(e=this.timeout){if(this.joinedOnce)throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance");return this.timeout=e,this.joinedOnce=!0,this.rejoin(),this.joinPush}onClose(e){this.on(T.close,e)}onError(e){return this.on(T.error,t=>e(t))}on(e,t){let i=this.bindingRef++;return this.bindings.push({event:e,ref:i,callback:t}),i}off(e,t){this.bindings=this.bindings.filter(i=>!(i.event===e&&(typeof t=="undefined"||t===i.ref)))}canPush(){return this.socket.isConnected()&&this.isJoined()}push(e,t,i=this.timeout){if(t=t||{},!this.joinedOnce)throw new Error(`tried to push '${e}' to '${this.topic}' before joining. Use channel.join() before pushing events`);let s=new v(this,e,function(){return t},i);return this.canPush()?s.send():(s.startTimeout(),this.pushBuffer.push(s)),s}leave(e=this.timeout){this.rejoinTimer.reset(),this.joinPush.cancelTimeout(),this.state=m.leaving;let t=()=>{this.socket.hasLogger()&&this.socket.log("channel",`leave ${this.topic}`),this.trigger(T.close,"leave")},i=new v(this,T.leave,y({}),e);return i.receive("ok",()=>t()).receive("timeout",()=>t()),i.send(),this.canPush()||i.trigger("ok",{}),i}onMessage(e,t,i){return t}isMember(e,t,i,s){return this.topic!==e?!1:s&&s!==this.joinRef()?(this.socket.hasLogger()&&this.socket.log("channel","dropping outdated message",{topic:e,event:t,payload:i,joinRef:s}),!1):!0}joinRef(){return this.joinPush.ref}rejoin(e=this.timeout){this.isLeaving()||(this.socket.leaveOpenTopic(this.topic),this.state=m.joining,this.joinPush.resend(e))}trigger(e,t,i,s){let n=this.onMessage(e,t,i,s);if(t&&!n)throw new Error("channel onMessage callbacks must return the payload, modified or unmodified");let o=this.bindings.filter(r=>r.event===e);for(let r=0;rl.abort(),n),a.signal=l.signal),E.fetch(t,a).then(f=>f.text()).then(f=>this.parseJSON(f)).then(f=>{c&&clearTimeout(c),r&&r(f)}).catch(f=>{c&&clearTimeout(c),f.name==="AbortError"&&o?o():r&&r(null)}),l}static xdomainRequest(e,t,i,s,n,o,r){return e.timeout=n,e.open(t,i),e.onload=()=>{let a=this.parseJSON(e.responseText);r&&r(a)},o&&(e.ontimeout=o),e.onprogress=()=>{},e.send(s),e}static xhrRequest(e,t,i,s,n,o,r,a){e.open(t,i,!0),e.timeout=o;for(let[l,c]of Object.entries(s))e.setRequestHeader(l,c);return e.onerror=()=>a&&a(null),e.onreadystatechange=()=>{if(e.readyState===U.complete&&a){let l=this.parseJSON(e.responseText);a(l)}},r&&(e.ontimeout=r),e.send(n),e}static parseJSON(e){if(!e||e==="")return null;try{return JSON.parse(e)}catch(t){return console&&console.log("failed to parse JSON response",e),null}}static serialize(e,t){let i=[];for(var s in e){if(!Object.prototype.hasOwnProperty.call(e,s))continue;let n=t?`${t}[${s}]`:s,o=e[s];typeof o=="object"?i.push(this.serialize(o,n)):i.push(encodeURIComponent(n)+"="+encodeURIComponent(o))}return i.join("&")}static appendParams(e,t){if(Object.keys(t).length===0)return e;let i=e.match(/\?/)?"&":"?";return`${e}${i}${this.serialize(t)}`}};var q=h=>{let e="",t=new Uint8Array(h),i=t.byteLength;for(let s=0;sthis.poll(),0)}normalizeEndpoint(e){return e.replace("ws://","http://").replace("wss://","https://").replace(new RegExp("(.*)/"+A.websocket),"$1/"+A.longpoll)}endpointURL(){return this.pollEndpoint}closeAndRetry(e,t,i,s){this.isActive()&&(this.stop(p.connecting),this.onerror(e),this.isActive()&&this.emitClose(t,i,s))}ontimeout(){this.closeAndRetry("timeout",1005,"timeout",!1)}isActive(){return this.readyState===p.open||this.readyState===p.connecting}poll(){if(!this.isActive())return;let e={Accept:"application/json"};this.authToken&&(e["X-Phoenix-AuthToken"]=this.authToken),this.ajax("GET",e,null,()=>this.ontimeout(),t=>{if(t){var{status:i,token:s,messages:n}=t;if(i===410&&this.token!==null){this.closeAndRetry(410,3410,"session_gone",!1);return}this.token=s}else i=0;switch(i){case 200:n.forEach(o=>{let r=setTimeout(()=>{this.messageTimers.delete(r),this.onmessage({data:o})},0);this.messageTimers.add(r)}),this.poll();break;case 204:this.poll();break;case 410:this.readyState=p.open,this.onopen({}),this.poll();break;case 403:this.onerror(403),this.close(1008,"forbidden",!1);break;case 0:case 500:this.closeAndRetry(500,1011,"internal server error",!1);break;default:throw new Error(`unhandled poll status ${i}`)}})}send(e){typeof e!="string"&&(e=q(e)),this.currentBatch?this.currentBatch.push(e):this.awaitingBatchAck?this.batchBuffer.push(e):(this.currentBatch=[e],this.currentBatchTimer=setTimeout(()=>{this.batchSend(this.currentBatch),this.currentBatch=null},0))}batchSend(e,t=0){this.awaitingBatchAck=!0;let i=t+P,s=e.slice(t,i);this.ajax("POST",{"Content-Type":"application/x-ndjson"},s.join(` -`),()=>this.ontimeout(),n=>{!n||n.status!==200?this.closeAndRetry(n&&n.status,1011,"internal server error",!1):i0?(this.batchSend(this.batchBuffer),this.batchBuffer=[]):this.awaitingBatchAck=!1})}close(e,t,i){this.stop(p.closed),this.emitClose(e,t,i)}stop(e){this.readyState=e;let t=this.reqs;this.reqs=new Set;for(let{req:i}of t)i&&i.abort();for(let i of this.messageTimers)clearTimeout(i);this.messageTimers.clear(),this.batchBuffer=[],this.awaitingBatchAck=!1,clearTimeout(this.currentBatchTimer),this.currentBatchTimer=null,this.currentBatch=null}emitClose(e=1005,t,i=!0){let s={code:e,reason:t,wasClean:i};typeof CloseEvent!="undefined"?this.onclose(new CloseEvent("close",s)):this.onclose(s)}ajax(e,t,i,s,n){let o={req:null};this.reqs.add(o);let r=()=>{this.reqs.delete(o)&&s()};this.token!==null&&(t=Object.assign({},t,{"X-Phoenix-Longpoll-Token":this.token})),o.req=R.request(e,this.endpointURL(),t,i,this.timeout,r,a=>{this.reqs.delete(o)&&n(a)})}};var w=class h{constructor(e,t={}){let i=t.events||{state:"presence_state",diff:"presence_diff"};this.state=Object.create(null),this.pendingDiffs=[],this.channel=e,this.joinRef=null,this.caller={onJoin:function(){},onLeave:function(){},onSync:function(){}},this.channel.on(i.state,s=>{let{onJoin:n,onLeave:o,onSync:r}=this.caller;this.joinRef=this.channel.joinRef(),this.state=h.syncState(this.state,s,n,o),this.pendingDiffs.forEach(a=>{this.state=h.syncDiff(this.state,a,n,o)}),this.pendingDiffs=[],r()}),this.channel.on(i.diff,s=>{let{onJoin:n,onLeave:o,onSync:r}=this.caller;this.inPendingSyncState()?this.pendingDiffs.push(s):(this.state=h.syncDiff(this.state,s,n,o),r())})}onJoin(e){this.caller.onJoin=e}onLeave(e){this.caller.onLeave=e}onSync(e){this.caller.onSync=e}list(e){return h.list(this.state,e)}inPendingSyncState(){return!this.joinRef||this.joinRef!==this.channel.joinRef()}static syncState(e,t,i,s){let n=this.toNullProtoObj(this.clone(e));t=this.toNullProtoObj(t);let o=Object.create(null),r=Object.create(null);return this.map(n,(a,l)=>{t[a]||(r[a]=l)}),this.map(t,(a,l)=>{let c=n[a];if(c){let f=l.metas.map(u=>u.phx_ref),g=c.metas.map(u=>u.phx_ref),d=l.metas.filter(u=>g.indexOf(u.phx_ref)<0),b=c.metas.filter(u=>f.indexOf(u.phx_ref)<0);d.length>0&&(o[a]=l,o[a].metas=d),b.length>0&&(r[a]=this.clone(c),r[a].metas=b)}else o[a]=l}),this.syncDiff(n,{joins:o,leaves:r},i,s)}static syncDiff(e,t,i,s){e=this.toNullProtoObj(e);let{joins:n,leaves:o}=this.clone(t);return i||(i=function(){}),s||(s=function(){}),this.map(n,(r,a)=>{let l=e[r];if(e[r]=this.clone(a),l){let c=e[r].metas.map(g=>g.phx_ref),f=l.metas.filter(g=>c.indexOf(g.phx_ref)<0);e[r].metas.unshift(...f)}i(r,l,a)}),this.map(o,(r,a)=>{let l=e[r];if(!l)return;let c=a.metas.map(f=>f.phx_ref);l.metas=l.metas.filter(f=>c.indexOf(f.phx_ref)<0),s(r,l,a),l.metas.length===0&&delete e[r]}),e}static list(e,t){return t||(t=function(i,s){return s}),this.map(e,(i,s)=>t(i,s))}static map(e,t){return Object.getOwnPropertyNames(e).map(i=>t(i,e[i]))}static toNullProtoObj(e){if(Object.getPrototypeOf(e)===null)return e;let t=Object.create(null);return Object.getOwnPropertyNames(e).forEach(i=>{t[i]=e[i]}),t}static clone(e){return JSON.parse(JSON.stringify(e))}};var L={HEADER_LENGTH:1,META_LENGTH:4,KINDS:{push:0,reply:1,broadcast:2},encode(h,e){if(h.payload.constructor===ArrayBuffer)return e(this.binaryEncode(h));{let t=[h.join_ref,h.ref,h.topic,h.event,h.payload];return e(JSON.stringify(t))}},decode(h,e){if(h.constructor===ArrayBuffer)return e(this.binaryDecode(h));{let[t,i,s,n,o]=JSON.parse(h);return e({join_ref:t,ref:i,topic:s,event:n,payload:o})}},binaryEncode(h){let{join_ref:e,ref:t,event:i,topic:s,payload:n}=h,o=new TextEncoder,r=o.encode(e),a=o.encode(t),l=o.encode(s),c=o.encode(i);this.assertFieldSize(r.byteLength,"join_ref"),this.assertFieldSize(a.byteLength,"ref"),this.assertFieldSize(l.byteLength,"topic"),this.assertFieldSize(c.byteLength,"event");let f=this.META_LENGTH+r.byteLength+a.byteLength+l.byteLength+c.byteLength,g=new ArrayBuffer(this.HEADER_LENGTH+f),d=new Uint8Array(g),b=new DataView(g),u=0;b.setUint8(u++,this.KINDS.push),b.setUint8(u++,r.byteLength),b.setUint8(u++,a.byteLength),b.setUint8(u++,l.byteLength),b.setUint8(u++,c.byteLength),d.set(r,u),u+=r.byteLength,d.set(a,u),u+=a.byteLength,d.set(l,u),u+=l.byteLength,d.set(c,u),u+=c.byteLength;var N=new Uint8Array(g.byteLength+n.byteLength);return N.set(d,0),N.set(new Uint8Array(n),g.byteLength),N.buffer},assertFieldSize(h,e){if(h>255)throw new Error(`unable to convert ${e} to binary: must be less than or equal to 255 bytes, but is ${h} bytes`)},binaryDecode(h){let e=new DataView(h),t=e.getUint8(0),i=new TextDecoder;switch(t){case this.KINDS.push:return this.decodePush(h,e,i);case this.KINDS.reply:return this.decodeReply(h,e,i);case this.KINDS.broadcast:return this.decodeBroadcast(h,e,i)}},decodePush(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=e.getUint8(3),o=this.HEADER_LENGTH+this.META_LENGTH-1,r=t.decode(h.slice(o,o+i));o=o+i;let a=t.decode(h.slice(o,o+s));o=o+s;let l=t.decode(h.slice(o,o+n));o=o+n;let c=h.slice(o,h.byteLength);return{join_ref:r,ref:null,topic:a,event:l,payload:c}},decodeReply(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=e.getUint8(3),o=e.getUint8(4),r=this.HEADER_LENGTH+this.META_LENGTH,a=t.decode(h.slice(r,r+i));r=r+i;let l=t.decode(h.slice(r,r+s));r=r+s;let c=t.decode(h.slice(r,r+n));r=r+n;let f=t.decode(h.slice(r,r+o));r=r+o;let g=h.slice(r,h.byteLength),d={status:f,response:g};return{join_ref:a,ref:l,topic:c,event:T.reply,payload:d}},decodeBroadcast(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=this.HEADER_LENGTH+2,o=t.decode(h.slice(n,n+i));n=n+i;let r=t.decode(h.slice(n,n+s));n=n+s;let a=h.slice(n,h.byteLength);return{join_ref:null,ref:null,topic:o,event:r,payload:a}}};var _=class{constructor(e,t){this.socket=e,this.transport=t,this.joins=new WeakMap,this.pings=new Map,this.closing=!1,this.ended=!1,this.channelsErrored=!1,this.closeHandled=!1,t.onopen=()=>{this.isCurrent()&&e.onConnOpen()},t.onerror=i=>{this.isCurrent()&&e.onConnError(i)},t.onmessage=i=>{this.isCurrent()&&e.onConnMessage(i)},t.onclose=i=>{this.isCurrent()&&!this.closeHandled&&(this.closeHandled=!0,e.onConnClose(i))}}isCurrent(){return this.socket.connection===this}isOpen(){return this.transport.readyState===p.open}isConnecting(){return this.transport.readyState===p.connecting}carried(e,t=e.joinRef()){return this.joins.has(e)&&this.joins.get(e)===t}close(e,t){this.closing=!0,this.transport.onerror=function(){},e?this.transport.close(e,t||""):this.transport.close()}};var x=class{constructor(e,t={}){this.stateChangeCallbacks={open:[],close:[],error:[],message:[]},this.channels=[],this.sendBuffer=[],this.ref=0,this.fallbackRef=null,this.fallbackErrorRef=null,this.timeout=t.timeout||$,this.transport=t.transport||E.WebSocket||S,this.primaryPassedHealthCheck=!1,this.longPollFallbackMs=t.longPollFallbackMs,this.fallbackTimer=null,this.sessionStore=t.sessionStorage||E&&E.sessionStorage,this.establishedConnections=0,this.defaultEncoder=L.encode.bind(L),this.defaultDecoder=L.decode.bind(L),this.closeWasClean=!0,this.binaryType=t.binaryType||"arraybuffer",this.connection=null,this.connectClock=1,this.visibilityConnection=null,this.transport!==S?(this.encode=t.encode||this.defaultEncoder,this.decode=t.decode||this.defaultDecoder):(this.encode=this.defaultEncoder,this.decode=this.defaultDecoder);let i=null;C&&C.addEventListener&&(C.addEventListener("pagehide",s=>{this.connection&&!this.connection.ended&&(this.disconnect(),i=this.connectClock)}),C.addEventListener("pageshow",s=>{i===this.connectClock&&(i=null,this.connect())}),C.addEventListener("visibilitychange",()=>{this.handleVisibilityChange()}),C.document&&C.document.addEventListener("resume",()=>{this.handleVisibilityChange()})),this.heartbeatIntervalMs=t.heartbeatIntervalMs||3e4,this.rejoinAfterMs=s=>t.rejoinAfterMs?t.rejoinAfterMs(s):[1e3,2e3,5e3][s-1]||1e4,this.reconnectAfterMs=s=>t.reconnectAfterMs?t.reconnectAfterMs(s):[10,50,100,150,200,250,500,1e3,2e3][s-1]||5e3,this.reconnectWhenHidden=t.reconnectWhenHidden||!1,this.logger=t.logger||null,!this.logger&&t.debug&&(this.logger=(s,n,o)=>{console.log(`${s}: ${n}`,o)}),this.longpollerTimeout=t.longpollerTimeout||2e4,this.params=y(t.params||{}),this.endPoint=`${e}/${A.websocket}`,this.vsn=t.vsn||B,this.heartbeatTimeoutTimer=null,this.heartbeatTimer=null,this.pendingHeartbeatRef=null,this.reconnectTimer=new k(()=>{if(this.pageHidden&&!this.reconnectWhenHidden){this.log("Not reconnecting as page is hidden!"),this.teardown();return}this.teardownAndReconnect(()=>this.connect())},this.reconnectAfterMs),this.authToken=t.authToken&&y(t.authToken)}get conn(){return this.connection&&this.connection.transport}get pageHidden(){return C&&C.document?C.document.visibilityState==="hidden":!1}handleVisibilityChange(){if(this.pageHidden){this.visibilityConnection=null;return}let e=this.connection;e&&e===this.visibilityConnection&&e.isConnecting()||!this.isConnected()&&!this.closeWasClean&&(this.reconnectTimer.reset(),this.teardownAndReconnect(()=>{this.connect(),this.visibilityConnection=this.connection}))}getLongPollTransport(){return S}replaceTransport(e){let t=++this.connectClock;this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.transport=e;let i=this.connection;if(i){let s=i.isOpen();this.connection=null,i.ended=!0,i.close(),this.clearHeartbeats(),s&&this.triggerChanError("connection_closed",i)}return t}protocol(){return location.protocol.match(/^https/)?"wss":"ws"}endPointURL(){let e=R.appendParams(R.appendParams(this.endPoint,this.params()),{vsn:this.vsn});return e.charAt(0)!=="/"?e:e.charAt(1)==="/"?`${this.protocol()}:${e}`:`${this.protocol()}://${location.host}${e}`}disconnect(e,t,i){this.connectClock++,this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.connection&&(this.connection.ended=!0),this.teardown(e,t,i)}connect(e){e&&(console&&console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor"),this.params=y(e)),!(this.connection&&!this.connection.ended)&&(this.longPollFallbackMs&&this.transport!==S?this.connectWithFallback(S,this.longPollFallbackMs):this.transportConnect())}log(e,t,i){this.logger&&this.logger(e,t,i)}hasLogger(){return this.logger!==null}onOpen(e){let t=this.makeRef();return this.stateChangeCallbacks.open.push([t,e]),t}onClose(e){let t=this.makeRef();return this.stateChangeCallbacks.close.push([t,e]),t}onError(e){let t=this.makeRef();return this.stateChangeCallbacks.error.push([t,e]),t}onMessage(e){let t=this.makeRef();return this.stateChangeCallbacks.message.push([t,e]),t}ping(e){if(!this.isConnected())return!1;let t=this.makeRef(),i=Date.now();return this.connection.pings.set(t,()=>e(Date.now()-i)),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:t}),!0}transportName(e){switch(e){case S:return"LongPoll";default:return e.name}}transportConnect(){let e=this.connection;this.connectClock++,this.closeWasClean=!1,clearTimeout(this.reconnectTimer.timer);let t;this.authToken&&(t=["phoenix",`${O}${btoa(this.authToken()).replace(/=/g,"")}`]);let i=new this.transport(this.endPointURL(),t);i.binaryType=this.binaryType,i.timeout=this.longpollerTimeout,this.connection=new _(this,i),e&&e.isOpen()&&this.triggerChanError("connection_closed",e)}getSession(e){return this.sessionStore&&this.sessionStore.getItem(e)}storeSession(e,t){this.sessionStore&&this.sessionStore.setItem(e,t)}connectWithFallback(e,t=2500){this.cancelFallback();let i=!1,s=!0,n,o,r=()=>o===this.connectClock&&!this.closeWasClean,a=this.transportName(e),l=c=>{this.log("transport",`falling back to ${a}...`,c),this.off([n]),s=!1,this.replaceTransport(e)===this.connectClock&&this.transportConnect()};if(this.getSession(`phx:fallback:${a}`))return l("memorized");this.fallbackTimer=setTimeout(l,t),n=this.fallbackErrorRef=this.onError(c=>{r()&&(this.log("transport","error",c),s&&!i&&(clearTimeout(this.fallbackTimer),l(c)))}),this.fallbackRef=this.onOpen(()=>{if(!s){if(this.transport!==e||this.closeWasClean)return;let c=this.transportName(e);return this.primaryPassedHealthCheck||this.storeSession(`phx:fallback:${c}`,"true"),this.log("transport",`established ${c} fallback`)}r()&&(i=!0,clearTimeout(this.fallbackTimer),this.fallbackTimer=setTimeout(l,t),this.ping(c=>{this.log("transport","connected to primary after",c),this.primaryPassedHealthCheck=!0,clearTimeout(this.fallbackTimer)}))}),this.transportConnect(),o=this.connectClock}cancelFallback(){clearTimeout(this.fallbackTimer),this.off([this.fallbackRef,this.fallbackErrorRef])}clearHeartbeats(){clearTimeout(this.heartbeatTimer),clearTimeout(this.heartbeatTimeoutTimer),this.pendingHeartbeatRef=null}onConnOpen(){this.hasLogger()&&this.log("transport",`${this.transportName(this.transport)} connected to ${this.endPointURL()}`),this.closeWasClean=!1,this.establishedConnections++,this.flushSendBuffer(),this.reconnectTimer.reset(),this.resetHeartbeat(),this.stateChangeCallbacks.open.forEach(([,e])=>e())}heartbeatTimeout(){if(this.pendingHeartbeatRef){let e=this.connection;if(this.pendingHeartbeatRef=null,this.hasLogger()&&this.log("transport","heartbeat timeout. Attempting to re-establish connection"),this.closeWasClean=!1,e&&e.close(M,"heartbeat timeout"),!this.stillUses(e)||(this.triggerChanError("heartbeat_timeout"),!this.stillUses(e)))return;this.teardownAndReconnect(()=>this.reconnectTimer.scheduleTimeout())}}resetHeartbeat(){this.conn&&this.conn.skipHeartbeat||(this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs))}teardownAndReconnect(e){let t=this.connection,i=this.connectClock;this.teardown(()=>{i!==this.connectClock||t&&t.ended||e()})}stillUses(e){return this.connection===e&&!(e&&e.ended)}teardown(e,t,i){let s=this.connection;if(!s)return e&&e();this.clearHeartbeats(),this.waitForBufferDone(s.transport,()=>{if(!s.closing){let n=!s.isConnecting();s.close(t,i),n&&this.triggerChanError("connection_closed",s)}this.waitForSocketClosed(s.transport,()=>{this.connection===s&&(this.connection=null),e&&e()})})}waitForBufferDone(e,t,i=1){if(i===5||!e.bufferedAmount||e.readyState!==p.open){t();return}setTimeout(()=>{this.waitForBufferDone(e,t,i+1)},150*i)}waitForSocketClosed(e,t,i=1){if(i===5||e.readyState===p.closed){t();return}setTimeout(()=>{this.waitForSocketClosed(e,t,i+1)},150*i)}onConnClose(e){let t=this.connection;this.hasLogger()&&this.log("transport","close",e),this.clearHeartbeats();let i=!!(t&&t.closing);this.triggerChanError("connection_closed",t),this.stillUses(t)&&!i&&!this.closeWasClean&&this.reconnectTimer.scheduleTimeout(),this.stateChangeCallbacks.close.forEach(([,s])=>s(e))}onConnError(e){let t=this.connection;this.hasLogger()&&this.log("transport","error",e);let i=this.transport,s=this.establishedConnections;this.stateChangeCallbacks.error.forEach(([,n])=>{n(e,i,s)}),this.triggerChanError("connection_error",t)}triggerChanError(e,t=this.connection){let i=!1;t&&!t.isOpen()&&(i=t.channelsErrored,t.channelsErrored=!0),this.channels.map(n=>[n,n.joinRef()]).forEach(([n,o])=>{n.joinRef()===o&&(t&&(i||!t.isCurrent())&&!t.carried(n,o)||n.isErrored()||n.isLeaving()||n.isClosed()||n.trigger(T.error,{source:"transport",reason:e}))})}connectionState(){switch(this.conn&&this.conn.readyState){case p.connecting:return"connecting";case p.open:return"open";case p.closing:return"closing";default:return"closed"}}isConnected(){return this.connectionState()==="open"}remove(e){this.off(e.stateChangeRefs),this.channels=this.channels.filter(t=>t!==e)}off(e){for(let t in this.stateChangeCallbacks)this.stateChangeCallbacks[t]=this.stateChangeCallbacks[t].filter(([i])=>e.indexOf(i)===-1)}channel(e,t={}){let i=new j(e,t,this);return this.channels.push(i),i}push(e,t){if(this.hasLogger()){let{topic:i,event:s,payload:n,ref:o,join_ref:r}=e;this.log("push",`${i} ${s} (${r}, ${o})`,n)}this.isConnected()?this.send(e,t):this.sendBuffer.push(()=>{this.isOutdated(e,t)||this.send(e,t)})}send(e,t){let i=this.connection;t&&e.event===T.join&&i.joins.set(t,e.join_ref),this.encode(e,s=>{!i.isCurrent()||!i.isOpen()||e.event!==T.leave&&this.isOutdated(e,t)||i.transport.send(s)})}isOutdated(e,t){return!!t&&(e.join_ref!==t.joinRef()||t.isClosed())}makeRef(){let e=this.ref+1;return e===this.ref?this.ref=0:this.ref=e,this.ref.toString()}sendHeartbeat(){this.pendingHeartbeatRef&&!this.isConnected()||(this.pendingHeartbeatRef=this.makeRef(),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:this.pendingHeartbeatRef}),this.heartbeatTimeoutTimer=setTimeout(()=>this.heartbeatTimeout(),this.heartbeatIntervalMs))}flushSendBuffer(){this.isConnected()&&this.sendBuffer.length>0&&(this.sendBuffer.forEach(e=>e()),this.sendBuffer=[])}onConnMessage(e){let t=this.connection,i=t&&t.isOpen();this.decode(e.data,s=>{if(t!==this.connection||i&&!t.isOpen())return;let{topic:n,event:o,payload:r,ref:a,join_ref:l}=s;a&&a===this.pendingHeartbeatRef&&(this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs)),this.hasLogger()&&this.log("receive",`${r.status||""} ${n} ${o} ${a&&"("+a+")"||""}`,r);let c=this.channels;for(let d=0;di.topic===e&&(i.isJoined()||i.isJoining()));t&&(this.hasLogger()&&this.log("transport",`leaving duplicate topic "${e}"`),t.leave())}};return J(G);})(); +var Phoenix=(()=>{var N=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var W=(h,e)=>{for(var t in e)N(h,t,{get:e[t],enumerable:!0})},z=(h,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of F(e))!I.call(h,s)&&s!==t&&N(h,s,{get:()=>e[s],enumerable:!(i=D(e,s))||i.enumerable});return h};var J=h=>z(N({},"__esModule",{value:!0}),h);var G={};W(G,{Channel:()=>j,LongPoll:()=>b,Presence:()=>w,Serializer:()=>L,Socket:()=>H});var S=h=>typeof h=="function"?h:function(){return h};var X=typeof self!="undefined"?self:null,E=typeof window!="undefined"?window:null,y=X||E||globalThis,B="2.0.0",p={connecting:0,open:1,closing:2,closed:3},P=100,$=1e4,M=1e3,g={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},T={close:"phx_close",error:"phx_error",join:"phx_join",reply:"phx_reply",leave:"phx_leave"},A={longpoll:"longpoll",websocket:"websocket"},U={complete:4},O="base64url.bearer.phx.";var v=class{constructor(e,t,i,s){this.channel=e,this.event=t,this.payload=i||function(){return{}},this.receivedResp=null,this.timeout=s,this.timeoutTimer=null,this.recHooks=[],this.sent=!1}resend(e){this.timeout=e,this.reset(),this.send()}send(){this.hasReceived("timeout")||(this.startTimeout(),this.sent=!0,this.channel.socket.push({topic:this.channel.topic,event:this.event,payload:this.payload(),ref:this.ref,join_ref:this.channel.joinRef()},this.channel))}receive(e,t){return this.hasReceived(e)&&t(this.receivedResp.response),this.recHooks.push({status:e,callback:t}),this}reset(){this.cancelRefEvent(),this.ref=null,this.refEvent=null,this.receivedResp=null,this.sent=!1}matchReceive({status:e,response:t,_ref:i}){this.recHooks.filter(s=>s.status===e).forEach(s=>s.callback(t))}cancelRefEvent(){this.refEvent&&this.channel.off(this.refEvent)}cancelTimeout(){clearTimeout(this.timeoutTimer),this.timeoutTimer=null}startTimeout(){this.timeoutTimer&&this.cancelTimeout(),this.cancelRefEvent(),this.ref=this.channel.socket.makeRef(),this.refEvent=this.channel.replyEventName(this.ref),this.channel.on(this.refEvent,e=>{this.cancelRefEvent(),this.cancelTimeout(),this.receivedResp=e,this.matchReceive(e)}),this.timeoutTimer=setTimeout(()=>{this.trigger("timeout",{})},this.timeout)}hasReceived(e){return this.receivedResp&&this.receivedResp.status===e}trigger(e,t){this.channel.trigger(this.refEvent,{status:e,response:t})}};var R=class{constructor(e,t){this.callback=e,this.timerCalc=t,this.timer=null,this.tries=0}reset(){this.tries=0,clearTimeout(this.timer)}scheduleTimeout(){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.tries=this.tries+1,this.callback()},this.timerCalc(this.tries+1))}};var j=class{constructor(e,t,i){this.state=g.closed,this.topic=e,this.params=S(t||{}),this.socket=i,this.bindings=[],this.bindingRef=0,this.timeout=this.socket.timeout,this.joinedOnce=!1,this.joinPush=new v(this,T.join,this.params,this.timeout),this.pushBuffer=[],this.stateChangeRefs=[],this.rejoinTimer=new R(()=>{this.socket.isConnected()&&this.rejoin()},this.socket.rejoinAfterMs),this.stateChangeRefs.push(this.socket.onError(()=>this.rejoinTimer.reset())),this.stateChangeRefs.push(this.socket.onOpen(()=>{this.rejoinTimer.reset(),this.isErrored()&&this.rejoin()})),this.joinPush.receive("ok",()=>{this.state=g.joined,this.rejoinTimer.reset(),this.pushBuffer.forEach(s=>s.send()),this.pushBuffer=[]}),this.joinPush.receive("error",()=>{this.state=g.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.onClose(()=>{this.rejoinTimer.reset(),this.socket.hasLogger()&&this.socket.log("channel",`close ${this.topic} ${this.joinRef()}`),this.state=g.closed,this.socket.remove(this)}),this.onError(s=>{this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,s),this.isJoining()&&this.joinPush.reset(),this.state=g.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.joinPush.receive("timeout",()=>{this.socket.hasLogger()&&this.socket.log("channel",`timeout ${this.topic} (${this.joinRef()})`,this.joinPush.timeout),new v(this,T.leave,S({}),this.timeout).send(),this.state=g.errored,this.joinPush.reset(),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.on(T.reply,(s,n)=>{this.trigger(this.replyEventName(n),s)})}join(e=this.timeout){if(this.joinedOnce)throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance");return this.timeout=e,this.joinedOnce=!0,this.rejoin(),this.joinPush}onClose(e){this.on(T.close,e)}onError(e){return this.on(T.error,t=>e(t))}on(e,t){let i=this.bindingRef++;return this.bindings.push({event:e,ref:i,callback:t}),i}off(e,t){this.bindings=this.bindings.filter(i=>!(i.event===e&&(typeof t=="undefined"||t===i.ref)))}canPush(){return this.socket.isConnected()&&this.isJoined()}push(e,t,i=this.timeout){if(t=t||{},!this.joinedOnce)throw new Error(`tried to push '${e}' to '${this.topic}' before joining. Use channel.join() before pushing events`);let s=new v(this,e,function(){return t},i);return this.canPush()?s.send():(s.startTimeout(),this.pushBuffer.push(s)),s}leave(e=this.timeout){this.rejoinTimer.reset(),this.joinPush.cancelTimeout(),this.state=g.leaving;let t=()=>{this.socket.hasLogger()&&this.socket.log("channel",`leave ${this.topic}`),this.trigger(T.close,"leave")},i=new v(this,T.leave,S({}),e);return i.receive("ok",()=>t()).receive("timeout",()=>t()),i.send(),this.canPush()||i.trigger("ok",{}),i}onMessage(e,t,i){return t}isMember(e,t,i,s){return this.topic!==e?!1:s&&s!==this.joinRef()?(this.socket.hasLogger()&&this.socket.log("channel","dropping outdated message",{topic:e,event:t,payload:i,joinRef:s}),!1):!0}joinRef(){return this.joinPush.ref}rejoin(e=this.timeout){this.isLeaving()||(this.socket.leaveOpenTopic(this.topic),this.state=g.joining,this.joinPush.resend(e))}trigger(e,t,i,s){let n=this.onMessage(e,t,i,s);if(t&&!n)throw new Error("channel onMessage callbacks must return the payload, modified or unmodified");let o=this.bindings.filter(r=>r.event===e);for(let r=0;rl.abort(),n),a.signal=l.signal),y.fetch(t,a).then(f=>f.text()).then(f=>this.parseJSON(f)).then(f=>{c&&clearTimeout(c),r&&r(f)}).catch(f=>{c&&clearTimeout(c),f.name==="AbortError"&&o?o():r&&r(null)}),l}static xdomainRequest(e,t,i,s,n,o,r){return e.timeout=n,e.open(t,i),e.onload=()=>{let a=this.parseJSON(e.responseText);r&&r(a)},o&&(e.ontimeout=o),e.onprogress=()=>{},e.send(s),e}static xhrRequest(e,t,i,s,n,o,r,a){e.open(t,i,!0),e.timeout=o;for(let[l,c]of Object.entries(s))e.setRequestHeader(l,c);return e.onerror=()=>a&&a(null),e.onreadystatechange=()=>{if(e.readyState===U.complete&&a){let l=this.parseJSON(e.responseText);a(l)}},r&&(e.ontimeout=r),e.send(n),e}static parseJSON(e){if(!e||e==="")return null;try{return JSON.parse(e)}catch(t){return console&&console.log("failed to parse JSON response",e),null}}static serialize(e,t){let i=[];for(var s in e){if(!Object.prototype.hasOwnProperty.call(e,s))continue;let n=t?`${t}[${s}]`:s,o=e[s];typeof o=="object"?i.push(this.serialize(o,n)):i.push(encodeURIComponent(n)+"="+encodeURIComponent(o))}return i.join("&")}static appendParams(e,t){if(Object.keys(t).length===0)return e;let i=e.match(/\?/)?"&":"?";return`${e}${i}${this.serialize(t)}`}};var q=h=>{let e="",t=new Uint8Array(h),i=t.byteLength;for(let s=0;sthis.poll(),0)}normalizeEndpoint(e){return e.replace("ws://","http://").replace("wss://","https://").replace(new RegExp("(.*)/"+A.websocket),"$1/"+A.longpoll)}endpointURL(){return this.pollEndpoint}closeAndRetry(e,t,i,s){this.isActive()&&(this.stop(p.connecting),this.onerror(e),this.isActive()&&this.emitClose(t,i,s))}ontimeout(){this.closeAndRetry("timeout",1005,"timeout",!1)}isActive(){return this.readyState===p.open||this.readyState===p.connecting}poll(){if(!this.isActive())return;let e={Accept:"application/json"};this.authToken&&(e["X-Phoenix-AuthToken"]=this.authToken),this.ajax("GET",e,null,()=>this.ontimeout(),t=>{if(t){var{status:i,token:s,messages:n}=t;if(i===410&&this.token!==null){this.closeAndRetry(410,3410,"session_gone",!1);return}this.token=s}else i=0;switch(i){case 200:n.forEach(o=>{let r=setTimeout(()=>{this.messageTimers.delete(r),this.onmessage({data:o})},0);this.messageTimers.add(r)}),this.poll();break;case 204:this.poll();break;case 410:this.readyState=p.open,this.onopen({}),this.poll();break;case 403:this.onerror(403),this.close(1008,"forbidden",!1);break;case 0:case 500:this.closeAndRetry(500,1011,"internal server error",!1);break;default:throw new Error(`unhandled poll status ${i}`)}})}send(e){typeof e!="string"&&(e=q(e)),this.currentBatch?this.currentBatch.push(e):this.awaitingBatchAck?this.batchBuffer.push(e):(this.currentBatch=[e],this.currentBatchTimer=setTimeout(()=>{this.batchSend(this.currentBatch),this.currentBatch=null},0))}batchSend(e,t=0){this.awaitingBatchAck=!0;let i=t+P,s=e.slice(t,i);this.ajax("POST",{"Content-Type":"application/x-ndjson"},s.join(` +`),()=>this.ontimeout(),n=>{!n||n.status!==200?this.closeAndRetry(n&&n.status,1011,"internal server error",!1):i0?(this.batchSend(this.batchBuffer),this.batchBuffer=[]):this.awaitingBatchAck=!1})}close(e,t,i){this.stop(p.closed),this.emitClose(e,t,i)}stop(e){this.readyState=e;let t=this.reqs;this.reqs=new Set;for(let{req:i}of t)i&&i.abort();for(let i of this.messageTimers)clearTimeout(i);this.messageTimers.clear(),this.batchBuffer=[],this.awaitingBatchAck=!1,clearTimeout(this.currentBatchTimer),this.currentBatchTimer=null,this.currentBatch=null}emitClose(e=1005,t,i=!0){let s={code:e,reason:t,wasClean:i};typeof CloseEvent!="undefined"?this.onclose(new CloseEvent("close",s)):this.onclose(s)}ajax(e,t,i,s,n){let o={req:null};this.reqs.add(o);let r=()=>{this.reqs.delete(o)&&s()};this.token!==null&&(t=Object.assign({},t,{"X-Phoenix-Longpoll-Token":this.token})),o.req=k.request(e,this.endpointURL(),t,i,this.timeout,r,a=>{this.reqs.delete(o)&&n(a)})}};var w=class h{constructor(e,t={}){let i=t.events||{state:"presence_state",diff:"presence_diff"};this.state=Object.create(null),this.pendingDiffs=[],this.channel=e,this.joinRef=null,this.caller={onJoin:function(){},onLeave:function(){},onSync:function(){}},this.channel.on(i.state,s=>{let{onJoin:n,onLeave:o,onSync:r}=this.caller;this.joinRef=this.channel.joinRef(),this.state=h.syncState(this.state,s,n,o),this.pendingDiffs.forEach(a=>{this.state=h.syncDiff(this.state,a,n,o)}),this.pendingDiffs=[],r()}),this.channel.on(i.diff,s=>{let{onJoin:n,onLeave:o,onSync:r}=this.caller;this.inPendingSyncState()?this.pendingDiffs.push(s):(this.state=h.syncDiff(this.state,s,n,o),r())})}onJoin(e){this.caller.onJoin=e}onLeave(e){this.caller.onLeave=e}onSync(e){this.caller.onSync=e}list(e){return h.list(this.state,e)}inPendingSyncState(){return!this.joinRef||this.joinRef!==this.channel.joinRef()}static syncState(e,t,i,s){let n=this.toNullProtoObj(this.clone(e));t=this.toNullProtoObj(t);let o=Object.create(null),r=Object.create(null);return this.map(n,(a,l)=>{t[a]||(r[a]=l)}),this.map(t,(a,l)=>{let c=n[a];if(c){let f=l.metas.map(u=>u.phx_ref),m=c.metas.map(u=>u.phx_ref),d=l.metas.filter(u=>m.indexOf(u.phx_ref)<0),C=c.metas.filter(u=>f.indexOf(u.phx_ref)<0);d.length>0&&(o[a]=l,o[a].metas=d),C.length>0&&(r[a]=this.clone(c),r[a].metas=C)}else o[a]=l}),this.syncDiff(n,{joins:o,leaves:r},i,s)}static syncDiff(e,t,i,s){e=this.toNullProtoObj(e);let{joins:n,leaves:o}=this.clone(t);return i||(i=function(){}),s||(s=function(){}),this.map(n,(r,a)=>{let l=e[r];if(e[r]=this.clone(a),l){let c=e[r].metas.map(m=>m.phx_ref),f=l.metas.filter(m=>c.indexOf(m.phx_ref)<0);e[r].metas.unshift(...f)}i(r,l,a)}),this.map(o,(r,a)=>{let l=e[r];if(!l)return;let c=a.metas.map(f=>f.phx_ref);l.metas=l.metas.filter(f=>c.indexOf(f.phx_ref)<0),s(r,l,a),l.metas.length===0&&delete e[r]}),e}static list(e,t){return t||(t=function(i,s){return s}),this.map(e,(i,s)=>t(i,s))}static map(e,t){return Object.getOwnPropertyNames(e).map(i=>t(i,e[i]))}static toNullProtoObj(e){if(Object.getPrototypeOf(e)===null)return e;let t=Object.create(null);return Object.getOwnPropertyNames(e).forEach(i=>{t[i]=e[i]}),t}static clone(e){return JSON.parse(JSON.stringify(e))}};var L={HEADER_LENGTH:1,META_LENGTH:4,KINDS:{push:0,reply:1,broadcast:2},encode(h,e){if(h.payload.constructor===ArrayBuffer)return e(this.binaryEncode(h));{let t=[h.join_ref,h.ref,h.topic,h.event,h.payload];return e(JSON.stringify(t))}},decode(h,e){if(h.constructor===ArrayBuffer)return e(this.binaryDecode(h));{let[t,i,s,n,o]=JSON.parse(h);return e({join_ref:t,ref:i,topic:s,event:n,payload:o})}},binaryEncode(h){let{join_ref:e,ref:t,event:i,topic:s,payload:n}=h,o=new TextEncoder,r=o.encode(e),a=o.encode(t),l=o.encode(s),c=o.encode(i);this.assertFieldSize(r.byteLength,"join_ref"),this.assertFieldSize(a.byteLength,"ref"),this.assertFieldSize(l.byteLength,"topic"),this.assertFieldSize(c.byteLength,"event");let f=this.META_LENGTH+r.byteLength+a.byteLength+l.byteLength+c.byteLength,m=new ArrayBuffer(this.HEADER_LENGTH+f),d=new Uint8Array(m),C=new DataView(m),u=0;C.setUint8(u++,this.KINDS.push),C.setUint8(u++,r.byteLength),C.setUint8(u++,a.byteLength),C.setUint8(u++,l.byteLength),C.setUint8(u++,c.byteLength),d.set(r,u),u+=r.byteLength,d.set(a,u),u+=a.byteLength,d.set(l,u),u+=l.byteLength,d.set(c,u),u+=c.byteLength;var x=new Uint8Array(m.byteLength+n.byteLength);return x.set(d,0),x.set(new Uint8Array(n),m.byteLength),x.buffer},assertFieldSize(h,e){if(h>255)throw new Error(`unable to convert ${e} to binary: must be less than or equal to 255 bytes, but is ${h} bytes`)},binaryDecode(h){let e=new DataView(h),t=e.getUint8(0),i=new TextDecoder;switch(t){case this.KINDS.push:return this.decodePush(h,e,i);case this.KINDS.reply:return this.decodeReply(h,e,i);case this.KINDS.broadcast:return this.decodeBroadcast(h,e,i)}},decodePush(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=e.getUint8(3),o=this.HEADER_LENGTH+this.META_LENGTH-1,r=t.decode(h.slice(o,o+i));o=o+i;let a=t.decode(h.slice(o,o+s));o=o+s;let l=t.decode(h.slice(o,o+n));o=o+n;let c=h.slice(o,h.byteLength);return{join_ref:r,ref:null,topic:a,event:l,payload:c}},decodeReply(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=e.getUint8(3),o=e.getUint8(4),r=this.HEADER_LENGTH+this.META_LENGTH,a=t.decode(h.slice(r,r+i));r=r+i;let l=t.decode(h.slice(r,r+s));r=r+s;let c=t.decode(h.slice(r,r+n));r=r+n;let f=t.decode(h.slice(r,r+o));r=r+o;let m=h.slice(r,h.byteLength),d={status:f,response:m};return{join_ref:a,ref:l,topic:c,event:T.reply,payload:d}},decodeBroadcast(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=this.HEADER_LENGTH+2,o=t.decode(h.slice(n,n+i));n=n+i;let r=t.decode(h.slice(n,n+s));n=n+s;let a=h.slice(n,h.byteLength);return{join_ref:null,ref:null,topic:o,event:r,payload:a}}};var _=class{constructor(e,t){this.socket=e,this.transport=t,this.joins=new WeakMap,this.pings=new Map,this.closing=!1,this.ended=!1,this.channelsErrored=!1,this.closeHandled=!1,t.onopen=()=>{this.isCurrent()&&e.onConnOpen()},t.onerror=i=>{this.isCurrent()&&e.onConnError(i)},t.onmessage=i=>{this.isCurrent()&&e.onConnMessage(i)},t.onclose=i=>{this.isCurrent()&&!this.closeHandled&&(this.closeHandled=!0,e.onConnClose(i))}}isCurrent(){return this.socket.connection===this}isOpen(){return this.transport.readyState===p.open}isConnecting(){return this.transport.readyState===p.connecting}carried(e,t=e.joinRef()){return this.joins.has(e)&&this.joins.get(e)===t}close(e,t){this.closing=!0,this.transport.onerror=function(){},e?this.transport.close(e,t||""):this.transport.close()}};var H=class{constructor(e,t={}){this.stateChangeCallbacks={open:[],close:[],error:[],message:[]},this.channels=[],this.sendBuffer=[],this.ref=0,this.fallbackRef=null,this.fallbackErrorRef=null,this.timeout=t.timeout||$,this.transport=t.transport||y.WebSocket||b,this.primaryPassedHealthCheck=!1,this.longPollFallbackMs=t.longPollFallbackMs,this.fallbackTimer=null,this.sessionStore=t.sessionStorage||y&&y.sessionStorage,this.establishedConnections=0,this.defaultEncoder=L.encode.bind(L),this.defaultDecoder=L.decode.bind(L),this.closeWasClean=!0,this.binaryType=t.binaryType||"arraybuffer",this.connection=null,this.connectClock=1,this.visibilityConnection=null,this.transport!==b?(this.encode=t.encode||this.defaultEncoder,this.decode=t.decode||this.defaultDecoder):(this.encode=this.defaultEncoder,this.decode=this.defaultDecoder);let i=null;E&&E.addEventListener&&(E.addEventListener("pagehide",s=>{this.connection&&!this.connection.ended&&(this.disconnect(),i=this.connectClock)}),E.addEventListener("pageshow",s=>{i===this.connectClock&&(i=null,this.connect())}),E.addEventListener("visibilitychange",()=>{this.handleVisibilityChange()}),E.document&&E.document.addEventListener("resume",()=>{this.handleVisibilityChange()})),this.heartbeatIntervalMs=t.heartbeatIntervalMs||3e4,this.rejoinAfterMs=s=>t.rejoinAfterMs?t.rejoinAfterMs(s):[1e3,2e3,5e3][s-1]||1e4,this.reconnectAfterMs=s=>t.reconnectAfterMs?t.reconnectAfterMs(s):[10,50,100,150,200,250,500,1e3,2e3][s-1]||5e3,this.reconnectWhenHidden=t.reconnectWhenHidden||!1,this.logger=t.logger||null,!this.logger&&t.debug&&(this.logger=(s,n,o)=>{console.log(`${s}: ${n}`,o)}),this.longpollerTimeout=t.longpollerTimeout||2e4,this.params=S(t.params||{}),this.endPoint=`${e}/${A.websocket}`,this.vsn=t.vsn||B,this.heartbeatTimeoutTimer=null,this.heartbeatTimer=null,this.pendingHeartbeatRef=null,this.reconnectTimer=new R(()=>{if(this.pageHidden&&!this.reconnectWhenHidden){this.log("Not reconnecting as page is hidden!"),this.teardown();return}this.teardownAndReconnect(()=>this.connect())},this.reconnectAfterMs),this.authToken=t.authToken&&S(t.authToken)}get conn(){return this.connection&&this.connection.transport}get pageHidden(){return E&&E.document?E.document.visibilityState==="hidden":!1}handleVisibilityChange(){if(this.pageHidden){this.visibilityConnection=null;return}let e=this.connection;e&&e===this.visibilityConnection&&e.isConnecting()||!this.isConnected()&&!this.closeWasClean&&(this.reconnectTimer.reset(),this.teardownAndReconnect(()=>{this.connect(),this.visibilityConnection=this.connection}))}getLongPollTransport(){return b}replaceTransport(e){let t=++this.connectClock;this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.transport=e;let i=this.connection;if(i){let s=i.isOpen();this.connection=null,i.ended=!0,i.close(),this.clearHeartbeats(),s&&this.triggerChanError("connection_closed",i)}return t}protocol(){return location.protocol.match(/^https/)?"wss":"ws"}endPointURL(){let e=k.appendParams(k.appendParams(this.endPoint,this.params()),{vsn:this.vsn});return e.charAt(0)!=="/"?e:e.charAt(1)==="/"?`${this.protocol()}:${e}`:`${this.protocol()}://${location.host}${e}`}disconnect(e,t,i){this.connectClock++,this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.connection&&(this.connection.ended=!0),this.teardown(e,t,i)}connect(e){e&&(console&&console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor"),this.params=S(e)),!(this.connection&&!this.connection.ended)&&(this.longPollFallbackMs&&this.transport!==b?this.connectWithFallback(b,this.longPollFallbackMs):this.transportConnect())}log(e,t,i){this.logger&&this.logger(e,t,i)}hasLogger(){return this.logger!==null}onOpen(e){let t=this.makeRef();return this.stateChangeCallbacks.open.push([t,e]),t}onClose(e){let t=this.makeRef();return this.stateChangeCallbacks.close.push([t,e]),t}onError(e){let t=this.makeRef();return this.stateChangeCallbacks.error.push([t,e]),t}onMessage(e){let t=this.makeRef();return this.stateChangeCallbacks.message.push([t,e]),t}ping(e){if(!this.isConnected())return!1;let t=this.makeRef(),i=Date.now();return this.connection.pings.set(t,()=>e(Date.now()-i)),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:t}),!0}transportName(e){switch(e){case b:return"LongPoll";default:return e.name}}transportConnect(){let e=this.connection;this.connectClock++,this.closeWasClean=!1,clearTimeout(this.reconnectTimer.timer);let t;this.authToken&&(t=["phoenix",`${O}${btoa(this.authToken()).replace(/=/g,"")}`]);let i=new this.transport(this.endPointURL(),t);i.binaryType=this.binaryType,i.timeout=this.longpollerTimeout,this.connection=new _(this,i),e&&e.isOpen()&&this.triggerChanError("connection_closed",e)}getSession(e){return this.sessionStore&&this.sessionStore.getItem(e)}storeSession(e,t){this.sessionStore&&this.sessionStore.setItem(e,t)}connectWithFallback(e,t=2500){this.cancelFallback();let i=!1,s=!0,n,o,r=()=>o===this.connectClock&&!this.closeWasClean,a=this.transportName(e),l=c=>{this.log("transport",`falling back to ${a}...`,c),this.off([n]),s=!1,this.replaceTransport(e)===this.connectClock&&this.transportConnect()};if(this.getSession(`phx:fallback:${a}`))return l("memorized");this.fallbackTimer=setTimeout(l,t),n=this.fallbackErrorRef=this.onError(c=>{r()&&(this.log("transport","error",c),s&&!i&&(clearTimeout(this.fallbackTimer),l(c)))}),this.fallbackRef=this.onOpen(()=>{if(!s){if(this.transport!==e||this.closeWasClean)return;let c=this.transportName(e);return this.primaryPassedHealthCheck||this.storeSession(`phx:fallback:${c}`,"true"),this.log("transport",`established ${c} fallback`)}r()&&(i=!0,clearTimeout(this.fallbackTimer),this.fallbackTimer=setTimeout(l,t),this.ping(c=>{this.log("transport","connected to primary after",c),this.primaryPassedHealthCheck=!0,clearTimeout(this.fallbackTimer)}))}),this.transportConnect(),o=this.connectClock}cancelFallback(){clearTimeout(this.fallbackTimer),this.off([this.fallbackRef,this.fallbackErrorRef])}clearHeartbeats(){clearTimeout(this.heartbeatTimer),clearTimeout(this.heartbeatTimeoutTimer),this.pendingHeartbeatRef=null}onConnOpen(){this.hasLogger()&&this.log("transport",`${this.transportName(this.transport)} connected to ${this.endPointURL()}`),this.closeWasClean=!1,this.establishedConnections++,this.pingBeforeBufferedJoin(),this.flushSendBuffer(),this.reconnectTimer.reset(),this.resetHeartbeat(),this.stateChangeCallbacks.open.forEach(([,e])=>e())}pingBeforeBufferedJoin(){!this.fallbackTimer||this.transport===b||this.ping(()=>this.passPrimaryHealthCheck())}passPrimaryHealthCheck(){this.transport!==b&&(this.primaryPassedHealthCheck=!0,clearTimeout(this.fallbackTimer),this.fallbackTimer=null)}heartbeatTimeout(){if(this.pendingHeartbeatRef){let e=this.connection;if(this.pendingHeartbeatRef=null,this.hasLogger()&&this.log("transport","heartbeat timeout. Attempting to re-establish connection"),this.closeWasClean=!1,e&&e.close(M,"heartbeat timeout"),!this.stillUses(e)||(this.triggerChanError("heartbeat_timeout"),!this.stillUses(e)))return;this.teardownAndReconnect(()=>this.reconnectTimer.scheduleTimeout())}}resetHeartbeat(){this.conn&&this.conn.skipHeartbeat||(this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs))}teardownAndReconnect(e){let t=this.connection,i=this.connectClock;this.teardown(()=>{i!==this.connectClock||t&&t.ended||e()})}stillUses(e){return this.connection===e&&!(e&&e.ended)}teardown(e,t,i){let s=this.connection;if(!s)return e&&e();this.clearHeartbeats(),this.waitForBufferDone(s.transport,()=>{if(!s.closing){let n=!s.isConnecting();s.close(t,i),n&&this.triggerChanError("connection_closed",s)}this.waitForSocketClosed(s.transport,()=>{this.connection===s&&(this.connection=null),e&&e()})})}waitForBufferDone(e,t,i=1){if(i===5||!e.bufferedAmount||e.readyState!==p.open){t();return}setTimeout(()=>{this.waitForBufferDone(e,t,i+1)},150*i)}waitForSocketClosed(e,t,i=1){if(i===5||e.readyState===p.closed){t();return}setTimeout(()=>{this.waitForSocketClosed(e,t,i+1)},150*i)}onConnClose(e){let t=this.connection;this.hasLogger()&&this.log("transport","close",e),this.clearHeartbeats();let i=!!(t&&t.closing);this.triggerChanError("connection_closed",t),this.stillUses(t)&&!i&&!this.closeWasClean&&this.reconnectTimer.scheduleTimeout(),this.stateChangeCallbacks.close.forEach(([,s])=>s(e))}onConnError(e){let t=this.connection;this.hasLogger()&&this.log("transport","error",e);let i=this.transport,s=this.establishedConnections;this.stateChangeCallbacks.error.forEach(([,n])=>{n(e,i,s)}),this.triggerChanError("connection_error",t)}triggerChanError(e,t=this.connection){let i=!1;t&&!t.isOpen()&&(i=t.channelsErrored,t.channelsErrored=!0),this.channels.map(n=>[n,n.joinRef()]).forEach(([n,o])=>{n.joinRef()===o&&(t&&(i||!t.isCurrent())&&!t.carried(n,o)||n.isErrored()||n.isLeaving()||n.isClosed()||n.trigger(T.error,{source:"transport",reason:e}))})}connectionState(){switch(this.conn&&this.conn.readyState){case p.connecting:return"connecting";case p.open:return"open";case p.closing:return"closing";default:return"closed"}}isConnected(){return this.connectionState()==="open"}remove(e){this.off(e.stateChangeRefs),this.channels=this.channels.filter(t=>t!==e)}off(e){for(let t in this.stateChangeCallbacks)this.stateChangeCallbacks[t]=this.stateChangeCallbacks[t].filter(([i])=>e.indexOf(i)===-1)}channel(e,t={}){let i=new j(e,t,this);return this.channels.push(i),i}push(e,t){if(this.hasLogger()){let{topic:i,event:s,payload:n,ref:o,join_ref:r}=e;this.log("push",`${i} ${s} (${r}, ${o})`,n)}this.isConnected()?this.send(e,t):this.sendBuffer.push(()=>{this.isOutdated(e,t)||this.send(e,t)})}send(e,t){let i=this.connection;t&&e.event===T.join&&i.joins.set(t,e.join_ref),this.encode(e,s=>{!i.isCurrent()||!i.isOpen()||e.event!==T.leave&&this.isOutdated(e,t)||i.transport.send(s)})}isOutdated(e,t){return!!t&&(e.join_ref!==t.joinRef()||t.isClosed())}makeRef(){let e=this.ref+1;return e===this.ref?this.ref=0:this.ref=e,this.ref.toString()}sendHeartbeat(){this.pendingHeartbeatRef&&!this.isConnected()||(this.pendingHeartbeatRef=this.makeRef(),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:this.pendingHeartbeatRef}),this.heartbeatTimeoutTimer=setTimeout(()=>this.heartbeatTimeout(),this.heartbeatIntervalMs))}flushSendBuffer(){this.isConnected()&&this.sendBuffer.length>0&&(this.sendBuffer.forEach(e=>e()),this.sendBuffer=[])}onConnMessage(e){let t=this.connection,i=t&&t.isOpen();this.decode(e.data,s=>{if(t!==this.connection||i&&!t.isOpen())return;this.fallbackTimer&&this.passPrimaryHealthCheck();let{topic:n,event:o,payload:r,ref:a,join_ref:l}=s;a&&a===this.pendingHeartbeatRef&&(this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs)),this.hasLogger()&&this.log("receive",`${r.status||""} ${n} ${o} ${a&&"("+a+")"||""}`,r);let c=this.channels;for(let d=0;di.topic===e&&(i.isJoined()||i.isJoining()));t&&(this.hasLogger()&&this.log("transport",`leaving duplicate topic "${e}"`),t.leave())}};return J(G);})(); diff --git a/priv/static/phoenix.mjs b/priv/static/phoenix.mjs index afa887c42a..a44c2239f3 100644 --- a/priv/static/phoenix.mjs +++ b/priv/static/phoenix.mjs @@ -1588,11 +1588,38 @@ var Socket = class { if (this.hasLogger()) this.log("transport", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`); this.closeWasClean = false; this.establishedConnections++; + this.pingBeforeBufferedJoin(); this.flushSendBuffer(); this.reconnectTimer.reset(); this.resetHeartbeat(); this.stateChangeCallbacks.open.forEach(([, callback]) => callback()); } + /** + * @private + * + * Sends the fallback health check before flushSendBuffer. No-op unless a + * fallback attempt is waiting on the primary transport. + */ + pingBeforeBufferedJoin() { + if (!this.fallbackTimer || this.transport === LongPoll) { + return; + } + this.ping(() => this.passPrimaryHealthCheck()); + } + /** + * @private + * + * The primary transport answered. Later frames on this connection also call + * this, because a frame is the proof the ping was waiting for. + */ + passPrimaryHealthCheck() { + if (this.transport === LongPoll) { + return; + } + this.primaryPassedHealthCheck = true; + clearTimeout(this.fallbackTimer); + this.fallbackTimer = null; + } /** * @private */ @@ -1871,6 +1898,9 @@ var Socket = class { if (connection !== this.connection || wasOpen && !connection.isOpen()) { return; } + if (this.fallbackTimer) { + this.passPrimaryHealthCheck(); + } let { topic, event, payload, ref, join_ref } = msg; if (ref && ref === this.pendingHeartbeatRef) { this.clearHeartbeats(); diff --git a/priv/static/phoenix.mjs.map b/priv/static/phoenix.mjs.map index f376195875..0520384272 100644 --- a/priv/static/phoenix.mjs.map +++ b/priv/static/phoenix.mjs.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../assets/js/phoenix/utils.js", "../../assets/js/phoenix/constants.js", "../../assets/js/phoenix/push.js", "../../assets/js/phoenix/timer.js", "../../assets/js/phoenix/channel.js", "../../assets/js/phoenix/ajax.js", "../../assets/js/phoenix/longpoll.js", "../../assets/js/phoenix/presence.js", "../../assets/js/phoenix/serializer.js", "../../assets/js/phoenix/connection.js", "../../assets/js/phoenix/socket.js"], - "sourcesContent": ["// wraps value in closure or returns closure\nexport let closure = (value) => {\n if(typeof value === \"function\"){\n return value\n } else {\n let closure = function (){ return value }\n return closure\n }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || globalThis\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const MAX_LONGPOLL_BATCH_SIZE = 100;\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n closed: \"closed\",\n errored: \"errored\",\n joined: \"joined\",\n joining: \"joining\",\n leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n close: \"phx_close\",\n error: \"phx_error\",\n join: \"phx_join\",\n reply: \"phx_reply\",\n leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n longpoll: \"longpoll\",\n websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n complete: 4\n}\nexport const AUTH_TOKEN_PREFIX = \"base64url.bearer.phx.\"\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n constructor(channel, event, payload, timeout){\n this.channel = channel\n this.event = event\n this.payload = payload || function (){ return {} }\n this.receivedResp = null\n this.timeout = timeout\n this.timeoutTimer = null\n this.recHooks = []\n this.sent = false\n }\n\n /**\n *\n * @param {number} timeout\n */\n resend(timeout){\n this.timeout = timeout\n this.reset()\n this.send()\n }\n\n /**\n *\n */\n send(){\n if(this.hasReceived(\"timeout\")){ return }\n this.startTimeout()\n this.sent = true\n this.channel.socket.push({\n topic: this.channel.topic,\n event: this.event,\n payload: this.payload(),\n ref: this.ref,\n join_ref: this.channel.joinRef()\n }, this.channel)\n }\n\n /**\n *\n * @param {*} status\n * @param {*} callback\n */\n receive(status, callback){\n if(this.hasReceived(status)){\n callback(this.receivedResp.response)\n }\n\n this.recHooks.push({status, callback})\n return this\n }\n\n /**\n * @private\n */\n reset(){\n this.cancelRefEvent()\n this.ref = null\n this.refEvent = null\n this.receivedResp = null\n this.sent = false\n }\n\n /**\n * @private\n */\n matchReceive({status, response, _ref}){\n this.recHooks.filter(h => h.status === status)\n .forEach(h => h.callback(response))\n }\n\n /**\n * @private\n */\n cancelRefEvent(){\n if(!this.refEvent){ return }\n this.channel.off(this.refEvent)\n }\n\n /**\n * @private\n */\n cancelTimeout(){\n clearTimeout(this.timeoutTimer)\n this.timeoutTimer = null\n }\n\n /**\n * @private\n */\n startTimeout(){\n if(this.timeoutTimer){ this.cancelTimeout() }\n this.cancelRefEvent()\n this.ref = this.channel.socket.makeRef()\n this.refEvent = this.channel.replyEventName(this.ref)\n\n this.channel.on(this.refEvent, payload => {\n this.cancelRefEvent()\n this.cancelTimeout()\n this.receivedResp = payload\n this.matchReceive(payload)\n })\n\n this.timeoutTimer = setTimeout(() => {\n this.trigger(\"timeout\", {})\n }, this.timeout)\n }\n\n /**\n * @private\n */\n hasReceived(status){\n return this.receivedResp && this.receivedResp.status === status\n }\n\n /**\n * @private\n */\n trigger(status, response){\n this.channel.trigger(this.refEvent, {status, response})\n }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n * return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n constructor(callback, timerCalc){\n this.callback = callback\n this.timerCalc = timerCalc\n this.timer = null\n this.tries = 0\n }\n\n reset(){\n this.tries = 0\n clearTimeout(this.timer)\n }\n\n /**\n * Cancels any previous scheduleTimeout and schedules callback\n */\n scheduleTimeout(){\n clearTimeout(this.timer)\n\n this.timer = setTimeout(() => {\n this.tries = this.tries + 1\n this.callback()\n }, this.timerCalc(this.tries + 1))\n }\n}\n", "import {closure} from \"./utils\"\nimport {\n CHANNEL_EVENTS,\n CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n constructor(topic, params, socket){\n this.state = CHANNEL_STATES.closed\n this.topic = topic\n this.params = closure(params || {})\n this.socket = socket\n this.bindings = []\n this.bindingRef = 0\n this.timeout = this.socket.timeout\n this.joinedOnce = false\n this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n this.pushBuffer = []\n this.stateChangeRefs = []\n\n this.rejoinTimer = new Timer(() => {\n if(this.socket.isConnected()){ this.rejoin() }\n }, this.socket.rejoinAfterMs)\n this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n this.stateChangeRefs.push(this.socket.onOpen(() => {\n this.rejoinTimer.reset()\n if(this.isErrored()){ this.rejoin() }\n })\n )\n this.joinPush.receive(\"ok\", () => {\n this.state = CHANNEL_STATES.joined\n this.rejoinTimer.reset()\n this.pushBuffer.forEach(pushEvent => pushEvent.send())\n this.pushBuffer = []\n })\n this.joinPush.receive(\"error\", () => {\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.onClose(() => {\n this.rejoinTimer.reset()\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n this.state = CHANNEL_STATES.closed\n this.socket.remove(this)\n })\n this.onError(reason => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n if(this.isJoining()){ this.joinPush.reset() }\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.joinPush.receive(\"timeout\", () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n leavePush.send()\n this.state = CHANNEL_STATES.errored\n this.joinPush.reset()\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n this.trigger(this.replyEventName(ref), payload)\n })\n }\n\n /**\n * Join the channel\n * @param {integer} timeout\n * @returns {Push}\n */\n join(timeout = this.timeout){\n if(this.joinedOnce){\n throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n } else {\n this.timeout = timeout\n this.joinedOnce = true\n this.rejoin()\n return this.joinPush\n }\n }\n\n /**\n * Hook into channel close\n * @param {Function} callback\n */\n onClose(callback){\n this.on(CHANNEL_EVENTS.close, callback)\n }\n\n /**\n * Hook into channel errors\n * @param {Function} callback\n */\n onError(callback){\n return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n }\n\n /**\n * Subscribes on channel events\n *\n * Subscription returns a ref counter, which can be used later to\n * unsubscribe the exact event listener\n *\n * @example\n * const ref1 = channel.on(\"event\", do_stuff)\n * const ref2 = channel.on(\"event\", do_other_stuff)\n * channel.off(\"event\", ref1)\n * // Since unsubscription, do_stuff won't fire,\n * // while do_other_stuff will keep firing on the \"event\"\n *\n * @param {string} event\n * @param {Function} callback\n * @returns {integer} ref\n */\n on(event, callback){\n let ref = this.bindingRef++\n this.bindings.push({event, ref, callback})\n return ref\n }\n\n /**\n * Unsubscribes off of channel events\n *\n * Use the ref returned from a channel.on() to unsubscribe one\n * handler, or pass nothing for the ref to unsubscribe all\n * handlers for the given event.\n *\n * @example\n * // Unsubscribe the do_stuff handler\n * const ref1 = channel.on(\"event\", do_stuff)\n * channel.off(\"event\", ref1)\n *\n * // Unsubscribe all handlers from event\n * channel.off(\"event\")\n *\n * @param {string} event\n * @param {integer} ref\n */\n off(event, ref){\n this.bindings = this.bindings.filter((bind) => {\n return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n })\n }\n\n /**\n * @private\n */\n canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n /**\n * Sends a message `event` to phoenix with the payload `payload`.\n * Phoenix receives this in the `handle_in(event, payload, socket)`\n * function. if phoenix replies or it times out (default 10000ms),\n * then optionally the reply can be received.\n *\n * @example\n * channel.push(\"event\")\n * .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n * .receive(\"error\", err => console.log(\"phoenix errored\", err))\n * .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n * @param {string} event\n * @param {Object} payload\n * @param {number} [timeout]\n * @returns {Push}\n */\n push(event, payload, timeout = this.timeout){\n payload = payload || {}\n if(!this.joinedOnce){\n throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n }\n let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n if(this.canPush()){\n pushEvent.send()\n } else {\n pushEvent.startTimeout()\n this.pushBuffer.push(pushEvent)\n }\n\n return pushEvent\n }\n\n /** Leaves the channel\n *\n * Unsubscribes from server events, and\n * instructs channel to terminate on server\n *\n * Triggers onClose() hooks\n *\n * To receive leave acknowledgements, use the `receive`\n * hook to bind to the server ack, ie:\n *\n * @example\n * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n *\n * @param {integer} timeout\n * @returns {Push}\n */\n leave(timeout = this.timeout){\n this.rejoinTimer.reset()\n this.joinPush.cancelTimeout()\n\n this.state = CHANNEL_STATES.leaving\n let onClose = () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n this.trigger(CHANNEL_EVENTS.close, \"leave\")\n }\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n leavePush.receive(\"ok\", () => onClose())\n .receive(\"timeout\", () => onClose())\n leavePush.send()\n if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n return leavePush\n }\n\n /**\n * Overridable message hook\n *\n * Receives all events for specialized message handling\n * before dispatching to the channel callbacks.\n *\n * Must return the payload, modified or unmodified\n * @param {string} event\n * @param {Object} payload\n * @param {integer} ref\n * @returns {Object}\n */\n onMessage(_event, payload, _ref){ return payload }\n\n /**\n * @private\n */\n isMember(topic, event, payload, joinRef){\n if(this.topic !== topic){ return false }\n\n if(joinRef && joinRef !== this.joinRef()){\n if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n return false\n } else {\n return true\n }\n }\n\n /**\n * @private\n */\n joinRef(){ return this.joinPush.ref }\n\n /**\n * @private\n */\n rejoin(timeout = this.timeout){\n if(this.isLeaving()){ return }\n this.socket.leaveOpenTopic(this.topic)\n this.state = CHANNEL_STATES.joining\n this.joinPush.resend(timeout)\n }\n\n /**\n * @private\n */\n trigger(event, payload, ref, joinRef){\n let handledPayload = this.onMessage(event, payload, ref, joinRef)\n if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n for(let i = 0; i < eventBindings.length; i++){\n let bind = eventBindings[i]\n bind.callback(handledPayload, ref, joinRef || this.joinRef())\n }\n }\n\n /**\n * @private\n */\n replyEventName(ref){ return `chan_reply_${ref}` }\n\n /**\n * @private\n */\n isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n /**\n * @private\n */\n isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n /**\n * @private\n */\n isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n /**\n * @private\n */\n isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n /**\n * @private\n */\n isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n global,\n XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n static request(method, endPoint, headers, body, timeout, ontimeout, callback){\n if(global.XDomainRequest){\n let req = new global.XDomainRequest() // IE8, IE9\n return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n } else if(global.XMLHttpRequest){\n let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)\n } else if(global.fetch && global.AbortController){\n // Fetch with AbortController for modern browsers\n return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)\n } else {\n throw new Error(\"No suitable XMLHttpRequest implementation found\")\n }\n }\n\n static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){\n let options = {\n method,\n headers,\n body,\n }\n let controller = null\n let timeoutId = null\n if(timeout){\n controller = new AbortController()\n timeoutId = setTimeout(() => controller.abort(), timeout)\n options.signal = controller.signal\n }\n global.fetch(endPoint, options)\n .then(response => response.text())\n .then(data => this.parseJSON(data))\n .then(data => {\n if(timeoutId){ clearTimeout(timeoutId) }\n callback && callback(data)\n })\n .catch(err => {\n if(timeoutId){ clearTimeout(timeoutId) }\n if(err.name === \"AbortError\" && ontimeout){\n ontimeout()\n } else {\n callback && callback(null)\n }\n })\n return controller\n }\n\n static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n req.timeout = timeout\n req.open(method, endPoint)\n req.onload = () => {\n let response = this.parseJSON(req.responseText)\n callback && callback(response)\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n // Work around bug in IE9 that requires an attached onprogress handler\n req.onprogress = () => { }\n\n req.send(body)\n return req\n }\n\n static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){\n req.open(method, endPoint, true)\n req.timeout = timeout\n for(let [key, value] of Object.entries(headers)){\n req.setRequestHeader(key, value)\n }\n req.onerror = () => callback && callback(null)\n req.onreadystatechange = () => {\n if(req.readyState === XHR_STATES.complete && callback){\n let response = this.parseJSON(req.responseText)\n callback(response)\n }\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n req.send(body)\n return req\n }\n\n static parseJSON(resp){\n if(!resp || resp === \"\"){ return null }\n\n try {\n return JSON.parse(resp)\n } catch {\n console && console.log(\"failed to parse JSON response\", resp)\n return null\n }\n }\n\n static serialize(obj, parentKey){\n let queryStr = []\n for(var key in obj){\n if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n let paramKey = parentKey ? `${parentKey}[${key}]` : key\n let paramVal = obj[key]\n if(typeof paramVal === \"object\"){\n queryStr.push(this.serialize(paramVal, paramKey))\n } else {\n queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n }\n }\n return queryStr.join(\"&\")\n }\n\n static appendParams(url, params){\n if(Object.keys(params).length === 0){ return url }\n\n let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n return `${url}${prefix}${this.serialize(params)}`\n }\n}\n", "import {\n SOCKET_STATES,\n TRANSPORTS,\n AUTH_TOKEN_PREFIX,\n MAX_LONGPOLL_BATCH_SIZE\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBufferToBase64 = (buffer) => {\n let binary = \"\"\n let bytes = new Uint8Array(buffer)\n let len = bytes.byteLength\n for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }\n return btoa(binary)\n}\n\nexport default class LongPoll {\n\n constructor(endPoint, protocols){\n // we only support subprotocols for authToken\n // [\"phoenix\", \"base64url.bearer.phx.BASE64_ENCODED_TOKEN\"]\n if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){\n this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))\n }\n this.endPoint = null\n this.token = null\n this.skipHeartbeat = true\n // the pending requests and queued messages, which are cancelled when we stop\n this.reqs = new Set()\n this.messageTimers = new Set()\n this.awaitingBatchAck = false\n this.currentBatch = null\n this.currentBatchTimer = null\n this.batchBuffer = []\n this.onopen = function (){ } // noop\n this.onerror = function (){ } // noop\n this.onmessage = function (){ } // noop\n this.onclose = function (){ } // noop\n this.pollEndpoint = this.normalizeEndpoint(endPoint)\n this.readyState = SOCKET_STATES.connecting\n // we must wait for the caller to finish setting up our callbacks and timeout properties,\n // and it might close us before that, e.g. when disconnecting right after connecting\n setTimeout(() => this.poll(), 0)\n }\n\n normalizeEndpoint(endPoint){\n return (endPoint\n .replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")\n .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n }\n\n endpointURL(){\n return this.pollEndpoint\n }\n\n closeAndRetry(error, code, reason, wasClean){\n if(!this.isActive()){ return }\n // like a failed WebSocket, we are no longer open when we emit the error and then the close.\n // We are connecting again before their callbacks run, so that they can still close us for\n // good, e.g. when they disconnect the socket\n this.stop(SOCKET_STATES.connecting)\n this.onerror(error)\n if(this.isActive()){ this.emitClose(code, reason, wasClean) }\n }\n\n ontimeout(){\n this.closeAndRetry(\"timeout\", 1005, \"timeout\", false)\n }\n\n isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }\n\n poll(){\n // onopen can close us before the next poll, just as the caller can close us\n // before the constructor's deferred first poll\n if(!this.isActive()){ return }\n const headers = {\"Accept\": \"application/json\"}\n if(this.authToken){\n headers[\"X-Phoenix-AuthToken\"] = this.authToken\n }\n this.ajax(\"GET\", headers, null, () => this.ontimeout(), resp => {\n if(resp){\n var {status, token, messages} = resp\n if(status === 410 && this.token !== null){\n // In case we already have a token, this means that our existing session\n // is gone. We fail so that the client rejoins its channels.\n this.closeAndRetry(410, 3410, \"session_gone\", false)\n return\n }\n this.token = token\n } else {\n status = 0\n }\n\n switch(status){\n case 200:\n messages.forEach(msg => {\n // Tasks are what things like event handlers, setTimeout callbacks,\n // promise resolves and more are run within.\n // In modern browsers, there are two different kinds of tasks,\n // microtasks and macrotasks.\n // Microtasks are mainly used for Promises, while macrotasks are\n // used for everything else.\n // Microtasks always have priority over macrotasks. If the JS engine\n // is looking for a task to run, it will always try to empty the\n // microtask queue before attempting to run anything from the\n // macrotask queue.\n //\n // For the WebSocket transport, messages always arrive in their own\n // event. This means that if any promises are resolved from within,\n // their callbacks will always finish execution by the time the\n // next message event handler is run.\n //\n // In order to emulate this behaviour, we need to make sure each\n // onmessage handler is run within its own macrotask.\n //\n // We might stop before that, e.g. when a POST times out or a message\n // handler disconnects, which cancels the remaining messages.\n const timer = setTimeout(() => {\n this.messageTimers.delete(timer)\n this.onmessage({data: msg})\n }, 0)\n this.messageTimers.add(timer)\n })\n this.poll()\n break\n case 204:\n this.poll()\n break\n case 410:\n this.readyState = SOCKET_STATES.open\n this.onopen({})\n this.poll()\n break\n case 403:\n this.onerror(403)\n this.close(1008, \"forbidden\", false)\n break\n case 0:\n case 500:\n this.closeAndRetry(500, 1011, \"internal server error\", false)\n break\n default: throw new Error(`unhandled poll status ${status}`)\n }\n })\n }\n\n // we collect all pushes within the current event loop by\n // setTimeout 0, which optimizes back-to-back procedural\n // pushes against an empty buffer\n\n send(body){\n if(typeof(body) !== \"string\"){ body = arrayBufferToBase64(body) }\n if(this.currentBatch){\n this.currentBatch.push(body)\n } else if(this.awaitingBatchAck){\n this.batchBuffer.push(body)\n } else {\n this.currentBatch = [body]\n this.currentBatchTimer = setTimeout(() => {\n this.batchSend(this.currentBatch)\n this.currentBatch = null\n }, 0)\n }\n }\n\n batchSend(messages, offset = 0){\n this.awaitingBatchAck = true\n const next = offset + MAX_LONGPOLL_BATCH_SIZE\n const batch = messages.slice(offset, next)\n this.ajax(\"POST\", {\"Content-Type\": \"application/x-ndjson\"}, batch.join(\"\\n\"), () => this.ontimeout(), resp => {\n if(!resp || resp.status !== 200){\n // this calls stop which already clears awaitingBatchAck\n this.closeAndRetry(resp && resp.status, 1011, \"internal server error\", false)\n } else if(next < messages.length){\n this.batchSend(messages, next)\n } else if(this.batchBuffer.length > 0){\n this.batchSend(this.batchBuffer)\n this.batchBuffer = []\n } else {\n this.awaitingBatchAck = false\n }\n })\n }\n\n close(code, reason, wasClean){\n this.stop(SOCKET_STATES.closed)\n this.emitClose(code, reason, wasClean)\n }\n\n /**\n * Cancels the pending requests, queued messages and outgoing batches.\n */\n stop(readyState){\n this.readyState = readyState\n // the requests are no longer ours before we abort them, as aborting can invoke their\n // callbacks, synchronously for XHR and asynchronously for fetch\n const reqs = this.reqs\n this.reqs = new Set()\n for(let {req} of reqs){ req && req.abort() }\n for(let timer of this.messageTimers){ clearTimeout(timer) }\n this.messageTimers.clear()\n this.batchBuffer = []\n this.awaitingBatchAck = false\n clearTimeout(this.currentBatchTimer)\n this.currentBatchTimer = null\n this.currentBatch = null\n }\n\n /**\n * Emits the close event. Like a WebSocket closed without a code, it reports 1005 (no status)\n * unless one is given, as 1000 would look like the server closing the connection normally.\n */\n emitClose(code = 1005, reason, wasClean = true){\n let opts = {code, reason, wasClean}\n if(typeof(CloseEvent) !== \"undefined\"){\n this.onclose(new CloseEvent(\"close\", opts))\n } else {\n this.onclose(opts)\n }\n }\n\n ajax(method, headers, body, onCallerTimeout, callback){\n // the request is ours until it completes or we stop, and the callbacks of a request\n // that is not ours anymore are ignored. It is ours before it starts, as a request can\n // complete while it starts.\n const request = {req: null}\n this.reqs.add(request)\n let ontimeout = () => {\n if(this.reqs.delete(request)){ onCallerTimeout() }\n }\n if(this.token !== null){\n headers = Object.assign({}, headers, {\"X-Phoenix-Longpoll-Token\": this.token})\n }\n request.req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {\n if(this.reqs.delete(request)){ callback(resp) }\n })\n }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n * for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n constructor(channel, opts = {}){\n let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n this.state = Object.create(null)\n this.pendingDiffs = []\n this.channel = channel\n this.joinRef = null\n this.caller = {\n onJoin: function (){ },\n onLeave: function (){ },\n onSync: function (){ }\n }\n\n this.channel.on(events.state, newState => {\n let {onJoin, onLeave, onSync} = this.caller\n\n this.joinRef = this.channel.joinRef()\n this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n this.pendingDiffs.forEach(diff => {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n })\n this.pendingDiffs = []\n onSync()\n })\n\n this.channel.on(events.diff, diff => {\n let {onJoin, onLeave, onSync} = this.caller\n\n if(this.inPendingSyncState()){\n this.pendingDiffs.push(diff)\n } else {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n onSync()\n }\n })\n }\n\n onJoin(callback){ this.caller.onJoin = callback }\n\n onLeave(callback){ this.caller.onLeave = callback }\n\n onSync(callback){ this.caller.onSync = callback }\n\n list(by){ return Presence.list(this.state, by) }\n\n inPendingSyncState(){\n return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n }\n\n // lower-level public static API\n\n /**\n * Used to sync the list of presences on the server\n * with the client's state. An optional `onJoin` and `onLeave` callback can\n * be provided to react to changes in the client's local presences across\n * disconnects and reconnects with the server.\n *\n * @returns {Presence}\n */\n static syncState(currentState, newState, onJoin, onLeave){\n let state = this.toNullProtoObj(this.clone(currentState))\n newState = this.toNullProtoObj(newState)\n let joins = Object.create(null)\n let leaves = Object.create(null)\n\n this.map(state, (key, presence) => {\n if(!newState[key]){\n leaves[key] = presence\n }\n })\n this.map(newState, (key, newPresence) => {\n let currentPresence = state[key]\n if(currentPresence){\n let newRefs = newPresence.metas.map(m => m.phx_ref)\n let curRefs = currentPresence.metas.map(m => m.phx_ref)\n let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n if(joinedMetas.length > 0){\n joins[key] = newPresence\n joins[key].metas = joinedMetas\n }\n if(leftMetas.length > 0){\n leaves[key] = this.clone(currentPresence)\n leaves[key].metas = leftMetas\n }\n } else {\n joins[key] = newPresence\n }\n })\n return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n }\n\n /**\n *\n * Used to sync a diff of presence join and leave\n * events from the server, as they happen. Like `syncState`, `syncDiff`\n * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n * joining or leaving from a device.\n *\n * @returns {Presence}\n */\n static syncDiff(state, diff, onJoin, onLeave){\n state = this.toNullProtoObj(state)\n let {joins, leaves} = this.clone(diff)\n if(!onJoin){ onJoin = function (){ } }\n if(!onLeave){ onLeave = function (){ } }\n\n this.map(joins, (key, newPresence) => {\n let currentPresence = state[key]\n state[key] = this.clone(newPresence)\n if(currentPresence){\n let joinedRefs = state[key].metas.map(m => m.phx_ref)\n let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n state[key].metas.unshift(...curMetas)\n }\n onJoin(key, currentPresence, newPresence)\n })\n this.map(leaves, (key, leftPresence) => {\n let currentPresence = state[key]\n if(!currentPresence){ return }\n let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n currentPresence.metas = currentPresence.metas.filter(p => {\n return refsToRemove.indexOf(p.phx_ref) < 0\n })\n onLeave(key, currentPresence, leftPresence)\n if(currentPresence.metas.length === 0){\n delete state[key]\n }\n })\n return state\n }\n\n /**\n * Returns the array of presences, with selected metadata.\n *\n * @param {Object} presences\n * @param {Function} chooser\n *\n * @returns {Presence}\n */\n static list(presences, chooser){\n if(!chooser){ chooser = function (key, pres){ return pres } }\n\n return this.map(presences, (key, presence) => {\n return chooser(key, presence)\n })\n }\n\n // private\n\n static map(obj, func){\n return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n }\n\n // Presence keys are chosen on the server and may collide with\n // Object.prototype properties (\"__proto__\", \"constructor\", ...), so any\n // object indexed by presence key must not have a prototype chain\n //\n // TODO: replace the null-prototype objects with Maps in Phoenix 2.0\n // (breaking change for the lower-level static API)\n static toNullProtoObj(obj){\n if(Object.getPrototypeOf(obj) === null){ return obj }\n let cleaned = Object.create(null)\n Object.getOwnPropertyNames(obj).forEach(key => {\n cleaned[key] = obj[key]\n })\n return cleaned\n }\n\n static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n HEADER_LENGTH: 1,\n META_LENGTH: 4,\n KINDS: {push: 0, reply: 1, broadcast: 2},\n\n encode(msg, callback){\n if(msg.payload.constructor === ArrayBuffer){\n return callback(this.binaryEncode(msg))\n } else {\n let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n return callback(JSON.stringify(payload))\n }\n },\n\n decode(rawPayload, callback){\n if(rawPayload.constructor === ArrayBuffer){\n return callback(this.binaryDecode(rawPayload))\n } else {\n let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n return callback({join_ref, ref, topic, event, payload})\n }\n },\n\n // private\n\n binaryEncode(message){\n let {join_ref, ref, event, topic, payload} = message\n let encoder = new TextEncoder()\n let joinRefBytes = encoder.encode(join_ref)\n let refBytes = encoder.encode(ref)\n let topicBytes = encoder.encode(topic)\n let eventBytes = encoder.encode(event)\n\n this.assertFieldSize(joinRefBytes.byteLength, \"join_ref\")\n this.assertFieldSize(refBytes.byteLength, \"ref\")\n this.assertFieldSize(topicBytes.byteLength, \"topic\")\n this.assertFieldSize(eventBytes.byteLength, \"event\")\n\n let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength\n let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n let headerBytes = new Uint8Array(header)\n let view = new DataView(header)\n let offset = 0\n\n view.setUint8(offset++, this.KINDS.push) // kind\n view.setUint8(offset++, joinRefBytes.byteLength)\n view.setUint8(offset++, refBytes.byteLength)\n view.setUint8(offset++, topicBytes.byteLength)\n view.setUint8(offset++, eventBytes.byteLength)\n headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength\n headerBytes.set(refBytes, offset); offset += refBytes.byteLength\n headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength\n headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength\n\n var combined = new Uint8Array(header.byteLength + payload.byteLength)\n combined.set(headerBytes, 0)\n combined.set(new Uint8Array(payload), header.byteLength)\n\n return combined.buffer\n },\n\n assertFieldSize(size, name){\n if(size > 255){\n throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)\n }\n },\n\n binaryDecode(buffer){\n let view = new DataView(buffer)\n let kind = view.getUint8(0)\n let decoder = new TextDecoder()\n switch(kind){\n case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n }\n },\n\n decodePush(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let topicSize = view.getUint8(2)\n let eventSize = view.getUint8(3)\n let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n },\n\n decodeReply(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let refSize = view.getUint8(2)\n let topicSize = view.getUint8(3)\n let eventSize = view.getUint8(4)\n let offset = this.HEADER_LENGTH + this.META_LENGTH\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n offset = offset + refSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n let payload = {status: event, response: data}\n return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n },\n\n decodeBroadcast(buffer, view, decoder){\n let topicSize = view.getUint8(1)\n let eventSize = view.getUint8(2)\n let offset = this.HEADER_LENGTH + 2\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n\n return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n }\n}\n", "import {SOCKET_STATES} from \"./constants\"\n\n/**\n * @private\n *\n * A connection of the socket over a single transport instance.\n *\n * It owns everything that belongs to this connection, so that nothing about it can\n * affect a later connection: the transport events are only handled while it is the\n * socket's current connection, and its state is gone together with it.\n */\nexport default class Connection {\n /**\n * @param {Socket} socket\n * @param {Object} transport - The transport instance, for example a WebSocket\n */\n constructor(socket, transport){\n this.socket = socket\n this.transport = transport\n // the join ref of each channel whose join was sent over this connection, weakly held\n // as a connection can outlive many channels, e.g. when navigating between LiveViews\n this.joins = new WeakMap()\n // the callbacks of the pings sent over this connection by ref, as their replies can only\n // arrive over it, so they are gone together with it instead of accumulating on the socket\n this.pings = new Map()\n // set when we close the connection, as opposed to the server or the network\n this.closing = false\n // set when the socket does not want this connection anymore, e.g. on disconnect,\n // so that connecting again creates a new one instead of waiting for it\n this.ended = false\n // set once the channels were errored after the connection stopped being open\n this.channelsErrored = false\n // set once the close event was handled, as LongPoll can emit it more than once\n this.closeHandled = false\n transport.onopen = () => { if(this.isCurrent()){ socket.onConnOpen() } }\n transport.onerror = error => { if(this.isCurrent()){ socket.onConnError(error) } }\n transport.onmessage = event => { if(this.isCurrent()){ socket.onConnMessage(event) } }\n transport.onclose = event => {\n if(this.isCurrent() && !this.closeHandled){\n this.closeHandled = true\n socket.onConnClose(event)\n }\n }\n }\n\n /**\n * Whether this is the connection the socket currently uses. A replaced connection\n * can still emit events while it closes, but they no longer concern the socket.\n */\n isCurrent(){ return this.socket.connection === this }\n\n isOpen(){ return this.transport.readyState === SOCKET_STATES.open }\n\n isConnecting(){ return this.transport.readyState === SOCKET_STATES.connecting }\n\n /**\n * Whether the channel's current join was sent over this connection.\n */\n carried(channel, joinRef = channel.joinRef()){\n return this.joins.has(channel) && this.joins.get(channel) === joinRef\n }\n\n /**\n * Closes the transport. Its close event then only confirms our close.\n */\n close(code, reason){\n this.closing = true\n // closing a transport that is still connecting makes it emit an error, which is\n // caused by closing it and must not be handled as a transport error\n this.transport.onerror = function (){ } // noop\n if(code){ this.transport.close(code, reason || \"\") } else { this.transport.close() }\n }\n}\n", "import {\n global,\n phxWindow,\n CHANNEL_EVENTS,\n DEFAULT_TIMEOUT,\n DEFAULT_VSN,\n SOCKET_STATES,\n TRANSPORTS,\n WS_CLOSE_NORMAL,\n AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport {\n closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\nimport Connection from \"./connection\"\n\n/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n * `\"wss://example.com\"`\n * `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.\n * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.\n *\n * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport\n * before falling back to the LongPoll transport. Disabled by default.\n *\n * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.\n *\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the\n * socket reconnect interval, in milliseconds.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n * return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {boolean} [opts.reconnectWhenHidden] - When true, the socket also reconnects while the\n * page is hidden, for example in a background tab or on a sleeping phone.\n *\n * Defaults to false. The socket then waits until the page is visible again before it reconnects.\n * This avoids reconnect attempts that stall on mobile devices and delay the connection after the\n * device wakes up.\n *\n * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n * return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n * console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {(Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {(string|function)} [opts.authToken] - the optional authentication token to be exposed on the server\n * under the `:auth_token` connect_info key. Can be a string or a function that returns a string.\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n *\n * @param {Object} [opts.sessionStorage] - An optional Storage compatible object\n * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is\n * useful when Phoenix won't have access to `sessionStorage`. For example, This could\n * happen if a site loads a cross-domain channel in an iframe. Example usage:\n *\n * class InMemoryStorage {\n * constructor() { this.storage = {} }\n * getItem(keyName) { return this.storage[keyName] || null }\n * removeItem(keyName) { delete this.storage[keyName] }\n * setItem(keyName, keyValue) { this.storage[keyName] = keyValue }\n * }\n *\n*/\nexport default class Socket {\n constructor(endPoint, opts = {}){\n this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n this.channels = []\n this.sendBuffer = []\n this.ref = 0\n this.fallbackRef = null\n this.fallbackErrorRef = null\n this.timeout = opts.timeout || DEFAULT_TIMEOUT\n this.transport = opts.transport || global.WebSocket || LongPoll\n this.primaryPassedHealthCheck = false\n this.longPollFallbackMs = opts.longPollFallbackMs\n this.fallbackTimer = null\n this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)\n this.establishedConnections = 0\n this.defaultEncoder = Serializer.encode.bind(Serializer)\n this.defaultDecoder = Serializer.decode.bind(Serializer)\n // We start with closeWasClean true to avoid the visibility change\n // logic from connecting if the socket was never connected in the first place.\n // transportConnect sets it to false on open.\n this.closeWasClean = true\n this.binaryType = opts.binaryType || \"arraybuffer\"\n // the connection over the current transport instance, see Connection\n this.connection = null\n // changes whenever we connect, disconnect or replace the transport, for the code that needs\n // to know whether that happened while there was no connection, see replaceTransport\n this.connectClock = 1\n // the connection created when the page was shown, see handleVisibilityChange\n this.visibilityConnection = null\n if(this.transport !== LongPoll){\n this.encode = opts.encode || this.defaultEncoder\n this.decode = opts.decode || this.defaultDecoder\n } else {\n this.encode = this.defaultEncoder\n this.decode = this.defaultDecoder\n }\n let awaitingConnectionOnPageShow = null\n if(phxWindow && phxWindow.addEventListener){\n phxWindow.addEventListener(\"pagehide\", _e => {\n // an ended connection only finishes closing, e.g. after disconnect\n if(this.connection && !this.connection.ended){\n this.disconnect()\n awaitingConnectionOnPageShow = this.connectClock\n }\n })\n phxWindow.addEventListener(\"pageshow\", _e => {\n if(awaitingConnectionOnPageShow === this.connectClock){\n awaitingConnectionOnPageShow = null\n this.connect()\n }\n })\n phxWindow.addEventListener(\"visibilitychange\", () => {\n this.handleVisibilityChange()\n })\n // Chrome 149 started to not reliably fire visibilitychange after resume,\n // see https://issues.chromium.org/issues/547062449.\n phxWindow.document && phxWindow.document.addEventListener(\"resume\", () => {\n this.handleVisibilityChange()\n })\n }\n this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n this.rejoinAfterMs = (tries) => {\n if(opts.rejoinAfterMs){\n return opts.rejoinAfterMs(tries)\n } else {\n return [1000, 2000, 5000][tries - 1] || 10000\n }\n }\n this.reconnectAfterMs = (tries) => {\n if(opts.reconnectAfterMs){\n return opts.reconnectAfterMs(tries)\n } else {\n return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n }\n }\n this.reconnectWhenHidden = opts.reconnectWhenHidden || false\n this.logger = opts.logger || null\n if(!this.logger && opts.debug){\n this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }\n }\n this.longpollerTimeout = opts.longpollerTimeout || 20000\n this.params = closure(opts.params || {})\n this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n this.vsn = opts.vsn || DEFAULT_VSN\n this.heartbeatTimeoutTimer = null\n this.heartbeatTimer = null\n this.pendingHeartbeatRef = null\n this.reconnectTimer = new Timer(() => {\n // handleVisibilityChange reconnects once the page is visible again\n if(this.pageHidden && !this.reconnectWhenHidden){\n this.log(\"Not reconnecting as page is hidden!\")\n this.teardown()\n return\n }\n this.teardownAndReconnect(() => this.connect())\n }, this.reconnectAfterMs)\n this.authToken = opts.authToken && closure(opts.authToken)\n }\n\n /**\n * The transport instance of the current connection, for example a WebSocket\n */\n get conn(){ return this.connection && this.connection.transport }\n\n /**\n * @internal\n */\n get pageHidden(){\n return phxWindow && phxWindow.document ? phxWindow.document.visibilityState === \"hidden\" : false\n }\n\n /**\n * @internal\n */\n handleVisibilityChange(){\n if(this.pageHidden){\n this.visibilityConnection = null\n return\n }\n // resume and visibilitychange can both fire when the page is shown again, so we keep\n // the connection created for the first one while it is still connecting\n const connection = this.connection\n if(connection && connection === this.visibilityConnection && connection.isConnecting()){ return }\n // reconnect immediately\n if(!this.isConnected() && !this.closeWasClean){\n // a reconnect that was scheduled before would tear down the connection we create here\n this.reconnectTimer.reset()\n this.teardownAndReconnect(() => {\n this.connect()\n this.visibilityConnection = this.connection\n })\n }\n }\n\n /**\n * Returns the LongPoll transport reference\n */\n getLongPollTransport(){ return LongPoll }\n\n /**\n * Disconnects and replaces the active transport\n *\n * @param {Function} newTransport - The new transport class to instantiate\n *\n */\n replaceTransport(newTransport){\n const connectClock = ++this.connectClock\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // replaced first, as channel error callbacks below can already connect again\n this.transport = newTransport\n const connection = this.connection\n if(connection){\n const wasOpen = connection.isOpen()\n // the old transport closes asynchronously, but its events no longer concern us\n this.connection = null\n connection.ended = true\n connection.close()\n this.clearHeartbeats()\n // any in-flight pushes on the old connection are lost, so channels need to rejoin\n if(wasOpen){ this.triggerChanError(\"connection_closed\", connection) }\n }\n return connectClock\n }\n\n /**\n * Returns the socket protocol\n *\n * @returns {string}\n */\n protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n /**\n * The fully qualified socket url\n *\n * @returns {string}\n */\n endPointURL(){\n let uri = Ajax.appendParams(\n Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n if(uri.charAt(0) !== \"/\"){ return uri }\n if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n return `${this.protocol()}://${location.host}${uri}`\n }\n\n /**\n * Disconnects the socket\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n *\n * @param {Function} callback - Optional callback which is called after socket is disconnected.\n * @param {integer} code - A status code for disconnection (Optional).\n * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n */\n disconnect(callback, code, reason){\n this.connectClock++\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // connecting again creates a new connection right away instead of waiting for this one to close\n if(this.connection){ this.connection.ended = true }\n this.teardown(callback, code, reason)\n }\n\n /**\n *\n * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n *\n * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n */\n connect(params){\n if(params){\n console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n this.params = closure(params)\n }\n // the current connection is kept, including while it reconnects, until the socket is done with it\n if(this.connection && !this.connection.ended){ return }\n if(this.longPollFallbackMs && this.transport !== LongPoll){\n this.connectWithFallback(LongPoll, this.longPollFallbackMs)\n } else {\n this.transportConnect()\n }\n }\n\n /**\n * Logs the message. Override `this.logger` for specialized logging. noops by default\n * @param {string} kind\n * @param {string} msg\n * @param {Object} data\n */\n log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }\n\n /**\n * Returns true if a logger has been set on this socket.\n */\n hasLogger(){ return this.logger !== null }\n\n /**\n * Registers callbacks for connection open events\n *\n * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n *\n * @param {Function} callback\n */\n onOpen(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.open.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection close events\n * @param {Function} callback\n */\n onClose(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.close.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection error events\n *\n * @example socket.onError(function(error){ alert(\"An error occurred\") })\n *\n * @param {Function} callback\n */\n onError(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.error.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection message events\n * @param {Function} callback\n */\n onMessage(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.message.push([ref, callback])\n return ref\n }\n\n /**\n * Pings the server and invokes the callback with the RTT in milliseconds\n * @param {Function} callback\n *\n * Returns true if the ping was pushed or false if unable to be pushed.\n */\n ping(callback){\n if(!this.isConnected()){ return false }\n let ref = this.makeRef()\n let startTime = Date.now()\n this.connection.pings.set(ref, () => callback(Date.now() - startTime))\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: ref})\n return true\n }\n\n /**\n * @private\n *\n * @param {Function}\n */\n transportName(transport){\n // JavaScript minification, enabled by default in production in Phoenix\n // projects, renames symbols to reduce code size.\n // See https://esbuild.github.io/api/#keep-names.\n // This helper ensures we return the correct name for the LongPoll transport\n // even after minification. The other common transport is WebSocket, which\n // is native to browsers and does not need special handling.\n switch(transport){\n case LongPoll: return \"LongPoll\"\n default: return transport.name\n }\n }\n\n /**\n * @private\n */\n transportConnect(){\n const previous = this.connection\n this.connectClock++\n this.closeWasClean = false\n // a reconnect scheduled before would tear down the new connection, but we keep\n // its tries, which only reset once a connection opens\n clearTimeout(this.reconnectTimer.timer)\n let protocols = undefined\n // Sec-WebSocket-Protocol based token\n // (longpoll uses Authorization header instead)\n if(this.authToken){\n protocols = [\"phoenix\", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken()).replace(/=/g, \"\")}`]\n }\n const transport = new this.transport(this.endPointURL(), protocols)\n transport.binaryType = this.binaryType\n transport.timeout = this.longpollerTimeout\n this.connection = new Connection(this, transport)\n // connect can replace an ended connection that is still open while its buffer drains, but the\n // channels joined over it need to rejoin on the new connection instead of waiting for it to close\n if(previous && previous.isOpen()){ this.triggerChanError(\"connection_closed\", previous) }\n }\n\n getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }\n\n storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }\n\n connectWithFallback(fallbackTransport, fallbackThreshold = 2500){\n // the callbacks of a previous attempt must not act on this one, e.g. an attempt\n // that never opened would otherwise fall back again on the next error\n this.cancelFallback()\n let established = false\n let primaryTransport = true\n let errorRef\n let connectClock\n const isCurrent = () => connectClock === this.connectClock && !this.closeWasClean\n let fallbackTransportName = this.transportName(fallbackTransport)\n // the fallback timer is cleared whenever the attempt ends, and the callbacks below\n // only fall back while the attempt is current\n let fallback = (reason) => {\n this.log(\"transport\", `falling back to ${fallbackTransportName}...`, reason)\n // the open callback stays registered to remember the fallback once it connects\n this.off([errorRef])\n primaryTransport = false\n const replaceClock = this.replaceTransport(fallbackTransport)\n if(replaceClock !== this.connectClock){ return }\n this.transportConnect()\n }\n if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback(\"memorized\") }\n\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n\n errorRef = this.fallbackErrorRef = this.onError(reason => {\n if(!isCurrent()){ return }\n this.log(\"transport\", \"error\", reason)\n if(primaryTransport && !established){\n clearTimeout(this.fallbackTimer)\n fallback(reason)\n }\n })\n this.fallbackRef = this.onOpen(() => {\n if(!primaryTransport){\n // reconnects keep using the fallback transport, so a later successful connection\n // must still be remembered, unless an earlier callback disconnected it\n if(this.transport !== fallbackTransport || this.closeWasClean){ return }\n let fallbackTransportName = this.transportName(fallbackTransport)\n // only memorize LP if we never connected to primary\n if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, \"true\") }\n return this.log(\"transport\", `established ${fallbackTransportName} fallback`)\n }\n if(!isCurrent()){ return }\n established = true\n // if we've established primary, give the fallback a new period to attempt ping\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n this.ping(rtt => {\n this.log(\"transport\", \"connected to primary after\", rtt)\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n })\n })\n this.transportConnect()\n connectClock = this.connectClock\n }\n\n /**\n * @private\n *\n * Stops the fallback of the current connection attempt, see connectWithFallback\n */\n cancelFallback(){\n clearTimeout(this.fallbackTimer)\n this.off([this.fallbackRef, this.fallbackErrorRef])\n }\n\n clearHeartbeats(){\n clearTimeout(this.heartbeatTimer)\n clearTimeout(this.heartbeatTimeoutTimer)\n // a late reply to the pending heartbeat must not start the heartbeats again\n this.pendingHeartbeatRef = null\n }\n\n onConnOpen(){\n if(this.hasLogger()) this.log(\"transport\", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)\n this.closeWasClean = false\n this.establishedConnections++\n this.flushSendBuffer()\n this.reconnectTimer.reset()\n this.resetHeartbeat()\n this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n }\n\n /**\n * @private\n */\n\n heartbeatTimeout(){\n if(this.pendingHeartbeatRef){\n const connection = this.connection\n this.pendingHeartbeatRef = null\n if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n this.closeWasClean = false\n // the connection is presumed dead, so we close it right away instead of waiting for its\n // buffer to drain, which also ensures that it is no longer considered connected when the\n // channels error (LiveView would report a server error otherwise)\n if(connection){ connection.close(WS_CLOSE_NORMAL, \"heartbeat timeout\") }\n // some transports emit their close event synchronously, and callbacks of it as well as\n // channel error callbacks can disconnect or connect, which takes precedence over reconnecting\n if(!this.stillUses(connection)){ return }\n this.triggerChanError(\"heartbeat_timeout\")\n if(!this.stillUses(connection)){ return }\n this.teardownAndReconnect(() => this.reconnectTimer.scheduleTimeout())\n }\n }\n\n resetHeartbeat(){\n if(this.conn && this.conn.skipHeartbeat){ return }\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n /**\n * @private\n *\n * Tears down the current connection to reconnect afterwards, unless the socket was\n * disconnected or connected again in the meantime, which takes precedence.\n */\n teardownAndReconnect(reconnect){\n const connection = this.connection\n const connectClock = this.connectClock\n this.teardown(() => {\n // the socket was disconnected or connected again in the meantime, which the connection does not\n // tell once an overlapping teardown gave up on it\n if(connectClock !== this.connectClock || (connection && connection.ended)){ return }\n reconnect()\n })\n }\n\n /**\n * @private\n *\n * Whether the socket still uses the connection and did not give up on it. User callbacks\n * can disconnect or connect, so code continuing after them checks this first.\n */\n stillUses(connection){\n return this.connection === connection && !(connection && connection.ended)\n }\n\n teardown(callback, code, reason){\n // If someone calls connect before we finish tearing down,\n // we create a new connection, but we still want to finish tearing down the old one.\n const connection = this.connection\n if(!connection){\n return callback && callback()\n }\n // onConnClose is not called when the close event arrives late, so we stop the\n // heartbeats here, as a heartbeat timeout would otherwise reconnect the socket\n this.clearHeartbeats()\n\n this.waitForBufferDone(connection.transport, () => {\n // a connection we already closed, e.g. on a heartbeat timeout or in an\n // overlapping teardown, also errored its channels already\n if(!connection.closing){\n const wasOpened = !connection.isConnecting()\n connection.close(code, reason)\n // channels joined over this connection need to rejoin on the next one, but we cannot rely\n // on onConnClose for that, as the close event may arrive after we stopped waiting for it\n // or after a new connection already replaced this one\n if(wasOpened){ this.triggerChanError(\"connection_closed\", connection) }\n }\n\n this.waitForSocketClosed(connection.transport, () => {\n if(this.connection === connection){ this.connection = null }\n callback && callback()\n })\n })\n }\n\n waitForBufferDone(conn, callback, tries = 1){\n // there is no point in waiting for a conn that is already closing\n if(tries === 5 || !conn.bufferedAmount || conn.readyState !== SOCKET_STATES.open){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForBufferDone(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n waitForSocketClosed(conn, callback, tries = 1){\n if(tries === 5 || conn.readyState === SOCKET_STATES.closed){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForSocketClosed(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n onConnClose(event){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n this.clearHeartbeats()\n // we only close a connection to disconnect or to reconnect, which the code closing it takes\n // care of, so its close event only confirms our close. Any other close is reconnected, whatever\n // its code, as the server, proxies and load balancers also close with 1000 (normal closure)\n const closedByUs = !!(connection && connection.closing)\n // channel callbacks may disconnect or create a new connection, so finish cleaning up\n // this connection first and do not schedule a reconnect for its replacement\n this.triggerChanError(\"connection_closed\", connection)\n if(this.stillUses(connection) && !closedByUs && !this.closeWasClean){\n this.reconnectTimer.scheduleTimeout()\n }\n this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n }\n\n /**\n * @private\n */\n onConnError(error){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"error\", error)\n let transportBefore = this.transport\n let establishedBefore = this.establishedConnections\n this.stateChangeCallbacks.error.forEach(([, callback]) => {\n callback(error, transportBefore, establishedBefore)\n })\n // callbacks can replace the connection, in which case only its own joins are errored\n this.triggerChanError(\"connection_error\", connection)\n }\n\n /**\n * @private\n *\n * Errors the channels, as they need to rejoin once the given connection stopped being open.\n */\n triggerChanError(reason, connection = this.connection){\n // several places notice that a connection stopped being open (onConnError, onConnClose,\n // teardown and heartbeatTimeout). After the first, only channels whose join it carried\n // are errored, as error callbacks may have joined channels that join over another connection.\n let erroredBefore = false\n if(connection && !connection.isOpen()){\n erroredBefore = connection.channelsErrored\n connection.channelsErrored = true\n }\n const joins = this.channels.map(channel => [channel, channel.joinRef()])\n joins.forEach(([channel, joinRef]) => {\n // an earlier error callback can rejoin a later channel, so check its join\n // again immediately before dispatching rather than filtering the list first\n if(channel.joinRef() !== joinRef){ return }\n // the joins of a replaced connection are not the only ones anymore\n if(connection && (erroredBefore || !connection.isCurrent()) && !connection.carried(channel, joinRef)){ return }\n if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n channel.trigger(CHANNEL_EVENTS.error, {source: \"transport\", reason})\n }\n })\n }\n\n /**\n * @returns {string}\n */\n connectionState(){\n switch(this.conn && this.conn.readyState){\n case SOCKET_STATES.connecting: return \"connecting\"\n case SOCKET_STATES.open: return \"open\"\n case SOCKET_STATES.closing: return \"closing\"\n default: return \"closed\"\n }\n }\n\n /**\n * @returns {boolean}\n */\n isConnected(){ return this.connectionState() === \"open\" }\n\n /**\n * @private\n *\n * @param {Channel}\n */\n remove(channel){\n this.off(channel.stateChangeRefs)\n this.channels = this.channels.filter(c => c !== channel)\n }\n\n /**\n * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n *\n * @param {refs} - list of refs returned by calls to\n * `onOpen`, `onClose`, `onError,` and `onMessage`\n */\n off(refs){\n for(let key in this.stateChangeCallbacks){\n this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n return refs.indexOf(ref) === -1\n })\n }\n }\n\n /**\n * Initiates a new channel for the given topic\n *\n * @param {string} topic\n * @param {Object} chanParams - Parameters for the channel\n * @returns {Channel}\n */\n channel(topic, chanParams = {}){\n let chan = new Channel(topic, chanParams, this)\n this.channels.push(chan)\n return chan\n }\n\n /**\n * @param {Object} data\n * @param {Channel} [channel] - The channel the push belongs to, if any\n */\n push(data, channel){\n if(this.hasLogger()){\n let {topic, event, payload, ref, join_ref} = data\n this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n }\n\n if(this.isConnected()){\n this.send(data, channel)\n } else {\n this.sendBuffer.push(() => {\n // the channel may have errored or left while the push was buffered, e.g. when the\n // connection failed before opening, in which case its rejoin supersedes the buffered join\n if(!this.isOutdated(data, channel)){ this.send(data, channel) }\n })\n }\n }\n\n /**\n * @private\n */\n send(data, channel){\n const connection = this.connection\n // the join counts as carried by this connection while it is still encoded, so that the\n // channel rejoins when the connection is torn down before the encoding finished\n if(channel && data.event === CHANNEL_EVENTS.join){ connection.joins.set(channel, data.join_ref) }\n this.encode(data, result => {\n // let encodes finish while this connection drains, but never send on a\n // replacement or after the original connection has started closing\n if(!connection.isCurrent() || !connection.isOpen()){ return }\n // a leave still needs to reach the server after its channel was removed locally\n if(data.event !== CHANNEL_EVENTS.leave && this.isOutdated(data, channel)){ return }\n connection.transport.send(result)\n })\n }\n\n /**\n * @private\n *\n * Whether the push was made for an earlier join of its channel, or the channel left since.\n */\n isOutdated(data, channel){\n return !!channel && (data.join_ref !== channel.joinRef() || channel.isClosed())\n }\n\n /**\n * Return the next message ref, accounting for overflows\n * @returns {string}\n */\n makeRef(){\n let newRef = this.ref + 1\n if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n return this.ref.toString()\n }\n\n sendHeartbeat(){\n if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n this.pendingHeartbeatRef = this.makeRef()\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n }\n\n flushSendBuffer(){\n if(this.isConnected() && this.sendBuffer.length > 0){\n this.sendBuffer.forEach(callback => callback())\n this.sendBuffer = []\n }\n }\n\n onConnMessage(rawMessage){\n const connection = this.connection\n const wasOpen = connection && connection.isOpen()\n this.decode(rawMessage.data, msg => {\n // the transport event can be current even though its asynchronous decode is not, as the\n // connection may have been replaced or closed in the meantime (browsers also drop messages\n // that arrive once a WebSocket is closing)\n if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return }\n let {topic, event, payload, ref, join_ref} = msg\n if(ref && ref === this.pendingHeartbeatRef){\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n // removing a channel or callback while dispatching replaces the list instead of\n // changing it, so we iterate over the lists from before to not skip any of them\n const channels = this.channels\n for(let i = 0; i < channels.length; i++){\n const channel = channels[i]\n if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n channel.trigger(event, payload, ref, join_ref)\n }\n\n const callbacks = this.stateChangeCallbacks.message\n for(let i = 0; i < callbacks.length; i++){\n let [, callback] = callbacks[i]\n callback(msg)\n }\n\n const ping = ref && connection.pings.get(ref)\n if(ping){\n connection.pings.delete(ref)\n ping()\n }\n })\n }\n\n leaveOpenTopic(topic){\n let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n if(dupChannel){\n if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n dupChannel.leave()\n }\n }\n}\n"], - "mappings": "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", + "sourcesContent": ["// wraps value in closure or returns closure\nexport let closure = (value) => {\n if(typeof value === \"function\"){\n return value\n } else {\n let closure = function (){ return value }\n return closure\n }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || globalThis\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const MAX_LONGPOLL_BATCH_SIZE = 100;\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n closed: \"closed\",\n errored: \"errored\",\n joined: \"joined\",\n joining: \"joining\",\n leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n close: \"phx_close\",\n error: \"phx_error\",\n join: \"phx_join\",\n reply: \"phx_reply\",\n leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n longpoll: \"longpoll\",\n websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n complete: 4\n}\nexport const AUTH_TOKEN_PREFIX = \"base64url.bearer.phx.\"\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n constructor(channel, event, payload, timeout){\n this.channel = channel\n this.event = event\n this.payload = payload || function (){ return {} }\n this.receivedResp = null\n this.timeout = timeout\n this.timeoutTimer = null\n this.recHooks = []\n this.sent = false\n }\n\n /**\n *\n * @param {number} timeout\n */\n resend(timeout){\n this.timeout = timeout\n this.reset()\n this.send()\n }\n\n /**\n *\n */\n send(){\n if(this.hasReceived(\"timeout\")){ return }\n this.startTimeout()\n this.sent = true\n this.channel.socket.push({\n topic: this.channel.topic,\n event: this.event,\n payload: this.payload(),\n ref: this.ref,\n join_ref: this.channel.joinRef()\n }, this.channel)\n }\n\n /**\n *\n * @param {*} status\n * @param {*} callback\n */\n receive(status, callback){\n if(this.hasReceived(status)){\n callback(this.receivedResp.response)\n }\n\n this.recHooks.push({status, callback})\n return this\n }\n\n /**\n * @private\n */\n reset(){\n this.cancelRefEvent()\n this.ref = null\n this.refEvent = null\n this.receivedResp = null\n this.sent = false\n }\n\n /**\n * @private\n */\n matchReceive({status, response, _ref}){\n this.recHooks.filter(h => h.status === status)\n .forEach(h => h.callback(response))\n }\n\n /**\n * @private\n */\n cancelRefEvent(){\n if(!this.refEvent){ return }\n this.channel.off(this.refEvent)\n }\n\n /**\n * @private\n */\n cancelTimeout(){\n clearTimeout(this.timeoutTimer)\n this.timeoutTimer = null\n }\n\n /**\n * @private\n */\n startTimeout(){\n if(this.timeoutTimer){ this.cancelTimeout() }\n this.cancelRefEvent()\n this.ref = this.channel.socket.makeRef()\n this.refEvent = this.channel.replyEventName(this.ref)\n\n this.channel.on(this.refEvent, payload => {\n this.cancelRefEvent()\n this.cancelTimeout()\n this.receivedResp = payload\n this.matchReceive(payload)\n })\n\n this.timeoutTimer = setTimeout(() => {\n this.trigger(\"timeout\", {})\n }, this.timeout)\n }\n\n /**\n * @private\n */\n hasReceived(status){\n return this.receivedResp && this.receivedResp.status === status\n }\n\n /**\n * @private\n */\n trigger(status, response){\n this.channel.trigger(this.refEvent, {status, response})\n }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n * return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n constructor(callback, timerCalc){\n this.callback = callback\n this.timerCalc = timerCalc\n this.timer = null\n this.tries = 0\n }\n\n reset(){\n this.tries = 0\n clearTimeout(this.timer)\n }\n\n /**\n * Cancels any previous scheduleTimeout and schedules callback\n */\n scheduleTimeout(){\n clearTimeout(this.timer)\n\n this.timer = setTimeout(() => {\n this.tries = this.tries + 1\n this.callback()\n }, this.timerCalc(this.tries + 1))\n }\n}\n", "import {closure} from \"./utils\"\nimport {\n CHANNEL_EVENTS,\n CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n constructor(topic, params, socket){\n this.state = CHANNEL_STATES.closed\n this.topic = topic\n this.params = closure(params || {})\n this.socket = socket\n this.bindings = []\n this.bindingRef = 0\n this.timeout = this.socket.timeout\n this.joinedOnce = false\n this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n this.pushBuffer = []\n this.stateChangeRefs = []\n\n this.rejoinTimer = new Timer(() => {\n if(this.socket.isConnected()){ this.rejoin() }\n }, this.socket.rejoinAfterMs)\n this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n this.stateChangeRefs.push(this.socket.onOpen(() => {\n this.rejoinTimer.reset()\n if(this.isErrored()){ this.rejoin() }\n })\n )\n this.joinPush.receive(\"ok\", () => {\n this.state = CHANNEL_STATES.joined\n this.rejoinTimer.reset()\n this.pushBuffer.forEach(pushEvent => pushEvent.send())\n this.pushBuffer = []\n })\n this.joinPush.receive(\"error\", () => {\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.onClose(() => {\n this.rejoinTimer.reset()\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n this.state = CHANNEL_STATES.closed\n this.socket.remove(this)\n })\n this.onError(reason => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n if(this.isJoining()){ this.joinPush.reset() }\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.joinPush.receive(\"timeout\", () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n leavePush.send()\n this.state = CHANNEL_STATES.errored\n this.joinPush.reset()\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n this.trigger(this.replyEventName(ref), payload)\n })\n }\n\n /**\n * Join the channel\n * @param {integer} timeout\n * @returns {Push}\n */\n join(timeout = this.timeout){\n if(this.joinedOnce){\n throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n } else {\n this.timeout = timeout\n this.joinedOnce = true\n this.rejoin()\n return this.joinPush\n }\n }\n\n /**\n * Hook into channel close\n * @param {Function} callback\n */\n onClose(callback){\n this.on(CHANNEL_EVENTS.close, callback)\n }\n\n /**\n * Hook into channel errors\n * @param {Function} callback\n */\n onError(callback){\n return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n }\n\n /**\n * Subscribes on channel events\n *\n * Subscription returns a ref counter, which can be used later to\n * unsubscribe the exact event listener\n *\n * @example\n * const ref1 = channel.on(\"event\", do_stuff)\n * const ref2 = channel.on(\"event\", do_other_stuff)\n * channel.off(\"event\", ref1)\n * // Since unsubscription, do_stuff won't fire,\n * // while do_other_stuff will keep firing on the \"event\"\n *\n * @param {string} event\n * @param {Function} callback\n * @returns {integer} ref\n */\n on(event, callback){\n let ref = this.bindingRef++\n this.bindings.push({event, ref, callback})\n return ref\n }\n\n /**\n * Unsubscribes off of channel events\n *\n * Use the ref returned from a channel.on() to unsubscribe one\n * handler, or pass nothing for the ref to unsubscribe all\n * handlers for the given event.\n *\n * @example\n * // Unsubscribe the do_stuff handler\n * const ref1 = channel.on(\"event\", do_stuff)\n * channel.off(\"event\", ref1)\n *\n * // Unsubscribe all handlers from event\n * channel.off(\"event\")\n *\n * @param {string} event\n * @param {integer} ref\n */\n off(event, ref){\n this.bindings = this.bindings.filter((bind) => {\n return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n })\n }\n\n /**\n * @private\n */\n canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n /**\n * Sends a message `event` to phoenix with the payload `payload`.\n * Phoenix receives this in the `handle_in(event, payload, socket)`\n * function. if phoenix replies or it times out (default 10000ms),\n * then optionally the reply can be received.\n *\n * @example\n * channel.push(\"event\")\n * .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n * .receive(\"error\", err => console.log(\"phoenix errored\", err))\n * .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n * @param {string} event\n * @param {Object} payload\n * @param {number} [timeout]\n * @returns {Push}\n */\n push(event, payload, timeout = this.timeout){\n payload = payload || {}\n if(!this.joinedOnce){\n throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n }\n let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n if(this.canPush()){\n pushEvent.send()\n } else {\n pushEvent.startTimeout()\n this.pushBuffer.push(pushEvent)\n }\n\n return pushEvent\n }\n\n /** Leaves the channel\n *\n * Unsubscribes from server events, and\n * instructs channel to terminate on server\n *\n * Triggers onClose() hooks\n *\n * To receive leave acknowledgements, use the `receive`\n * hook to bind to the server ack, ie:\n *\n * @example\n * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n *\n * @param {integer} timeout\n * @returns {Push}\n */\n leave(timeout = this.timeout){\n this.rejoinTimer.reset()\n this.joinPush.cancelTimeout()\n\n this.state = CHANNEL_STATES.leaving\n let onClose = () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n this.trigger(CHANNEL_EVENTS.close, \"leave\")\n }\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n leavePush.receive(\"ok\", () => onClose())\n .receive(\"timeout\", () => onClose())\n leavePush.send()\n if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n return leavePush\n }\n\n /**\n * Overridable message hook\n *\n * Receives all events for specialized message handling\n * before dispatching to the channel callbacks.\n *\n * Must return the payload, modified or unmodified\n * @param {string} event\n * @param {Object} payload\n * @param {integer} ref\n * @returns {Object}\n */\n onMessage(_event, payload, _ref){ return payload }\n\n /**\n * @private\n */\n isMember(topic, event, payload, joinRef){\n if(this.topic !== topic){ return false }\n\n if(joinRef && joinRef !== this.joinRef()){\n if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n return false\n } else {\n return true\n }\n }\n\n /**\n * @private\n */\n joinRef(){ return this.joinPush.ref }\n\n /**\n * @private\n */\n rejoin(timeout = this.timeout){\n if(this.isLeaving()){ return }\n this.socket.leaveOpenTopic(this.topic)\n this.state = CHANNEL_STATES.joining\n this.joinPush.resend(timeout)\n }\n\n /**\n * @private\n */\n trigger(event, payload, ref, joinRef){\n let handledPayload = this.onMessage(event, payload, ref, joinRef)\n if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n for(let i = 0; i < eventBindings.length; i++){\n let bind = eventBindings[i]\n bind.callback(handledPayload, ref, joinRef || this.joinRef())\n }\n }\n\n /**\n * @private\n */\n replyEventName(ref){ return `chan_reply_${ref}` }\n\n /**\n * @private\n */\n isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n /**\n * @private\n */\n isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n /**\n * @private\n */\n isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n /**\n * @private\n */\n isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n /**\n * @private\n */\n isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n global,\n XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n static request(method, endPoint, headers, body, timeout, ontimeout, callback){\n if(global.XDomainRequest){\n let req = new global.XDomainRequest() // IE8, IE9\n return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n } else if(global.XMLHttpRequest){\n let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)\n } else if(global.fetch && global.AbortController){\n // Fetch with AbortController for modern browsers\n return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)\n } else {\n throw new Error(\"No suitable XMLHttpRequest implementation found\")\n }\n }\n\n static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){\n let options = {\n method,\n headers,\n body,\n }\n let controller = null\n let timeoutId = null\n if(timeout){\n controller = new AbortController()\n timeoutId = setTimeout(() => controller.abort(), timeout)\n options.signal = controller.signal\n }\n global.fetch(endPoint, options)\n .then(response => response.text())\n .then(data => this.parseJSON(data))\n .then(data => {\n if(timeoutId){ clearTimeout(timeoutId) }\n callback && callback(data)\n })\n .catch(err => {\n if(timeoutId){ clearTimeout(timeoutId) }\n if(err.name === \"AbortError\" && ontimeout){\n ontimeout()\n } else {\n callback && callback(null)\n }\n })\n return controller\n }\n\n static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n req.timeout = timeout\n req.open(method, endPoint)\n req.onload = () => {\n let response = this.parseJSON(req.responseText)\n callback && callback(response)\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n // Work around bug in IE9 that requires an attached onprogress handler\n req.onprogress = () => { }\n\n req.send(body)\n return req\n }\n\n static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){\n req.open(method, endPoint, true)\n req.timeout = timeout\n for(let [key, value] of Object.entries(headers)){\n req.setRequestHeader(key, value)\n }\n req.onerror = () => callback && callback(null)\n req.onreadystatechange = () => {\n if(req.readyState === XHR_STATES.complete && callback){\n let response = this.parseJSON(req.responseText)\n callback(response)\n }\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n req.send(body)\n return req\n }\n\n static parseJSON(resp){\n if(!resp || resp === \"\"){ return null }\n\n try {\n return JSON.parse(resp)\n } catch {\n console && console.log(\"failed to parse JSON response\", resp)\n return null\n }\n }\n\n static serialize(obj, parentKey){\n let queryStr = []\n for(var key in obj){\n if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n let paramKey = parentKey ? `${parentKey}[${key}]` : key\n let paramVal = obj[key]\n if(typeof paramVal === \"object\"){\n queryStr.push(this.serialize(paramVal, paramKey))\n } else {\n queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n }\n }\n return queryStr.join(\"&\")\n }\n\n static appendParams(url, params){\n if(Object.keys(params).length === 0){ return url }\n\n let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n return `${url}${prefix}${this.serialize(params)}`\n }\n}\n", "import {\n SOCKET_STATES,\n TRANSPORTS,\n AUTH_TOKEN_PREFIX,\n MAX_LONGPOLL_BATCH_SIZE\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBufferToBase64 = (buffer) => {\n let binary = \"\"\n let bytes = new Uint8Array(buffer)\n let len = bytes.byteLength\n for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }\n return btoa(binary)\n}\n\nexport default class LongPoll {\n\n constructor(endPoint, protocols){\n // we only support subprotocols for authToken\n // [\"phoenix\", \"base64url.bearer.phx.BASE64_ENCODED_TOKEN\"]\n if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){\n this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))\n }\n this.endPoint = null\n this.token = null\n this.skipHeartbeat = true\n // the pending requests and queued messages, which are cancelled when we stop\n this.reqs = new Set()\n this.messageTimers = new Set()\n this.awaitingBatchAck = false\n this.currentBatch = null\n this.currentBatchTimer = null\n this.batchBuffer = []\n this.onopen = function (){ } // noop\n this.onerror = function (){ } // noop\n this.onmessage = function (){ } // noop\n this.onclose = function (){ } // noop\n this.pollEndpoint = this.normalizeEndpoint(endPoint)\n this.readyState = SOCKET_STATES.connecting\n // we must wait for the caller to finish setting up our callbacks and timeout properties,\n // and it might close us before that, e.g. when disconnecting right after connecting\n setTimeout(() => this.poll(), 0)\n }\n\n normalizeEndpoint(endPoint){\n return (endPoint\n .replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")\n .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n }\n\n endpointURL(){\n return this.pollEndpoint\n }\n\n closeAndRetry(error, code, reason, wasClean){\n if(!this.isActive()){ return }\n // like a failed WebSocket, we are no longer open when we emit the error and then the close.\n // We are connecting again before their callbacks run, so that they can still close us for\n // good, e.g. when they disconnect the socket\n this.stop(SOCKET_STATES.connecting)\n this.onerror(error)\n if(this.isActive()){ this.emitClose(code, reason, wasClean) }\n }\n\n ontimeout(){\n this.closeAndRetry(\"timeout\", 1005, \"timeout\", false)\n }\n\n isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }\n\n poll(){\n // onopen can close us before the next poll, just as the caller can close us\n // before the constructor's deferred first poll\n if(!this.isActive()){ return }\n const headers = {\"Accept\": \"application/json\"}\n if(this.authToken){\n headers[\"X-Phoenix-AuthToken\"] = this.authToken\n }\n this.ajax(\"GET\", headers, null, () => this.ontimeout(), resp => {\n if(resp){\n var {status, token, messages} = resp\n if(status === 410 && this.token !== null){\n // In case we already have a token, this means that our existing session\n // is gone. We fail so that the client rejoins its channels.\n this.closeAndRetry(410, 3410, \"session_gone\", false)\n return\n }\n this.token = token\n } else {\n status = 0\n }\n\n switch(status){\n case 200:\n messages.forEach(msg => {\n // Tasks are what things like event handlers, setTimeout callbacks,\n // promise resolves and more are run within.\n // In modern browsers, there are two different kinds of tasks,\n // microtasks and macrotasks.\n // Microtasks are mainly used for Promises, while macrotasks are\n // used for everything else.\n // Microtasks always have priority over macrotasks. If the JS engine\n // is looking for a task to run, it will always try to empty the\n // microtask queue before attempting to run anything from the\n // macrotask queue.\n //\n // For the WebSocket transport, messages always arrive in their own\n // event. This means that if any promises are resolved from within,\n // their callbacks will always finish execution by the time the\n // next message event handler is run.\n //\n // In order to emulate this behaviour, we need to make sure each\n // onmessage handler is run within its own macrotask.\n //\n // We might stop before that, e.g. when a POST times out or a message\n // handler disconnects, which cancels the remaining messages.\n const timer = setTimeout(() => {\n this.messageTimers.delete(timer)\n this.onmessage({data: msg})\n }, 0)\n this.messageTimers.add(timer)\n })\n this.poll()\n break\n case 204:\n this.poll()\n break\n case 410:\n this.readyState = SOCKET_STATES.open\n this.onopen({})\n this.poll()\n break\n case 403:\n this.onerror(403)\n this.close(1008, \"forbidden\", false)\n break\n case 0:\n case 500:\n this.closeAndRetry(500, 1011, \"internal server error\", false)\n break\n default: throw new Error(`unhandled poll status ${status}`)\n }\n })\n }\n\n // we collect all pushes within the current event loop by\n // setTimeout 0, which optimizes back-to-back procedural\n // pushes against an empty buffer\n\n send(body){\n if(typeof(body) !== \"string\"){ body = arrayBufferToBase64(body) }\n if(this.currentBatch){\n this.currentBatch.push(body)\n } else if(this.awaitingBatchAck){\n this.batchBuffer.push(body)\n } else {\n this.currentBatch = [body]\n this.currentBatchTimer = setTimeout(() => {\n this.batchSend(this.currentBatch)\n this.currentBatch = null\n }, 0)\n }\n }\n\n batchSend(messages, offset = 0){\n this.awaitingBatchAck = true\n const next = offset + MAX_LONGPOLL_BATCH_SIZE\n const batch = messages.slice(offset, next)\n this.ajax(\"POST\", {\"Content-Type\": \"application/x-ndjson\"}, batch.join(\"\\n\"), () => this.ontimeout(), resp => {\n if(!resp || resp.status !== 200){\n // this calls stop which already clears awaitingBatchAck\n this.closeAndRetry(resp && resp.status, 1011, \"internal server error\", false)\n } else if(next < messages.length){\n this.batchSend(messages, next)\n } else if(this.batchBuffer.length > 0){\n this.batchSend(this.batchBuffer)\n this.batchBuffer = []\n } else {\n this.awaitingBatchAck = false\n }\n })\n }\n\n close(code, reason, wasClean){\n this.stop(SOCKET_STATES.closed)\n this.emitClose(code, reason, wasClean)\n }\n\n /**\n * Cancels the pending requests, queued messages and outgoing batches.\n */\n stop(readyState){\n this.readyState = readyState\n // the requests are no longer ours before we abort them, as aborting can invoke their\n // callbacks, synchronously for XHR and asynchronously for fetch\n const reqs = this.reqs\n this.reqs = new Set()\n for(let {req} of reqs){ req && req.abort() }\n for(let timer of this.messageTimers){ clearTimeout(timer) }\n this.messageTimers.clear()\n this.batchBuffer = []\n this.awaitingBatchAck = false\n clearTimeout(this.currentBatchTimer)\n this.currentBatchTimer = null\n this.currentBatch = null\n }\n\n /**\n * Emits the close event. Like a WebSocket closed without a code, it reports 1005 (no status)\n * unless one is given, as 1000 would look like the server closing the connection normally.\n */\n emitClose(code = 1005, reason, wasClean = true){\n let opts = {code, reason, wasClean}\n if(typeof(CloseEvent) !== \"undefined\"){\n this.onclose(new CloseEvent(\"close\", opts))\n } else {\n this.onclose(opts)\n }\n }\n\n ajax(method, headers, body, onCallerTimeout, callback){\n // the request is ours until it completes or we stop, and the callbacks of a request\n // that is not ours anymore are ignored. It is ours before it starts, as a request can\n // complete while it starts.\n const request = {req: null}\n this.reqs.add(request)\n let ontimeout = () => {\n if(this.reqs.delete(request)){ onCallerTimeout() }\n }\n if(this.token !== null){\n headers = Object.assign({}, headers, {\"X-Phoenix-Longpoll-Token\": this.token})\n }\n request.req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {\n if(this.reqs.delete(request)){ callback(resp) }\n })\n }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n * for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n constructor(channel, opts = {}){\n let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n this.state = Object.create(null)\n this.pendingDiffs = []\n this.channel = channel\n this.joinRef = null\n this.caller = {\n onJoin: function (){ },\n onLeave: function (){ },\n onSync: function (){ }\n }\n\n this.channel.on(events.state, newState => {\n let {onJoin, onLeave, onSync} = this.caller\n\n this.joinRef = this.channel.joinRef()\n this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n this.pendingDiffs.forEach(diff => {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n })\n this.pendingDiffs = []\n onSync()\n })\n\n this.channel.on(events.diff, diff => {\n let {onJoin, onLeave, onSync} = this.caller\n\n if(this.inPendingSyncState()){\n this.pendingDiffs.push(diff)\n } else {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n onSync()\n }\n })\n }\n\n onJoin(callback){ this.caller.onJoin = callback }\n\n onLeave(callback){ this.caller.onLeave = callback }\n\n onSync(callback){ this.caller.onSync = callback }\n\n list(by){ return Presence.list(this.state, by) }\n\n inPendingSyncState(){\n return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n }\n\n // lower-level public static API\n\n /**\n * Used to sync the list of presences on the server\n * with the client's state. An optional `onJoin` and `onLeave` callback can\n * be provided to react to changes in the client's local presences across\n * disconnects and reconnects with the server.\n *\n * @returns {Presence}\n */\n static syncState(currentState, newState, onJoin, onLeave){\n let state = this.toNullProtoObj(this.clone(currentState))\n newState = this.toNullProtoObj(newState)\n let joins = Object.create(null)\n let leaves = Object.create(null)\n\n this.map(state, (key, presence) => {\n if(!newState[key]){\n leaves[key] = presence\n }\n })\n this.map(newState, (key, newPresence) => {\n let currentPresence = state[key]\n if(currentPresence){\n let newRefs = newPresence.metas.map(m => m.phx_ref)\n let curRefs = currentPresence.metas.map(m => m.phx_ref)\n let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n if(joinedMetas.length > 0){\n joins[key] = newPresence\n joins[key].metas = joinedMetas\n }\n if(leftMetas.length > 0){\n leaves[key] = this.clone(currentPresence)\n leaves[key].metas = leftMetas\n }\n } else {\n joins[key] = newPresence\n }\n })\n return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n }\n\n /**\n *\n * Used to sync a diff of presence join and leave\n * events from the server, as they happen. Like `syncState`, `syncDiff`\n * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n * joining or leaving from a device.\n *\n * @returns {Presence}\n */\n static syncDiff(state, diff, onJoin, onLeave){\n state = this.toNullProtoObj(state)\n let {joins, leaves} = this.clone(diff)\n if(!onJoin){ onJoin = function (){ } }\n if(!onLeave){ onLeave = function (){ } }\n\n this.map(joins, (key, newPresence) => {\n let currentPresence = state[key]\n state[key] = this.clone(newPresence)\n if(currentPresence){\n let joinedRefs = state[key].metas.map(m => m.phx_ref)\n let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n state[key].metas.unshift(...curMetas)\n }\n onJoin(key, currentPresence, newPresence)\n })\n this.map(leaves, (key, leftPresence) => {\n let currentPresence = state[key]\n if(!currentPresence){ return }\n let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n currentPresence.metas = currentPresence.metas.filter(p => {\n return refsToRemove.indexOf(p.phx_ref) < 0\n })\n onLeave(key, currentPresence, leftPresence)\n if(currentPresence.metas.length === 0){\n delete state[key]\n }\n })\n return state\n }\n\n /**\n * Returns the array of presences, with selected metadata.\n *\n * @param {Object} presences\n * @param {Function} chooser\n *\n * @returns {Presence}\n */\n static list(presences, chooser){\n if(!chooser){ chooser = function (key, pres){ return pres } }\n\n return this.map(presences, (key, presence) => {\n return chooser(key, presence)\n })\n }\n\n // private\n\n static map(obj, func){\n return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n }\n\n // Presence keys are chosen on the server and may collide with\n // Object.prototype properties (\"__proto__\", \"constructor\", ...), so any\n // object indexed by presence key must not have a prototype chain\n //\n // TODO: replace the null-prototype objects with Maps in Phoenix 2.0\n // (breaking change for the lower-level static API)\n static toNullProtoObj(obj){\n if(Object.getPrototypeOf(obj) === null){ return obj }\n let cleaned = Object.create(null)\n Object.getOwnPropertyNames(obj).forEach(key => {\n cleaned[key] = obj[key]\n })\n return cleaned\n }\n\n static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n HEADER_LENGTH: 1,\n META_LENGTH: 4,\n KINDS: {push: 0, reply: 1, broadcast: 2},\n\n encode(msg, callback){\n if(msg.payload.constructor === ArrayBuffer){\n return callback(this.binaryEncode(msg))\n } else {\n let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n return callback(JSON.stringify(payload))\n }\n },\n\n decode(rawPayload, callback){\n if(rawPayload.constructor === ArrayBuffer){\n return callback(this.binaryDecode(rawPayload))\n } else {\n let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n return callback({join_ref, ref, topic, event, payload})\n }\n },\n\n // private\n\n binaryEncode(message){\n let {join_ref, ref, event, topic, payload} = message\n let encoder = new TextEncoder()\n let joinRefBytes = encoder.encode(join_ref)\n let refBytes = encoder.encode(ref)\n let topicBytes = encoder.encode(topic)\n let eventBytes = encoder.encode(event)\n\n this.assertFieldSize(joinRefBytes.byteLength, \"join_ref\")\n this.assertFieldSize(refBytes.byteLength, \"ref\")\n this.assertFieldSize(topicBytes.byteLength, \"topic\")\n this.assertFieldSize(eventBytes.byteLength, \"event\")\n\n let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength\n let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n let headerBytes = new Uint8Array(header)\n let view = new DataView(header)\n let offset = 0\n\n view.setUint8(offset++, this.KINDS.push) // kind\n view.setUint8(offset++, joinRefBytes.byteLength)\n view.setUint8(offset++, refBytes.byteLength)\n view.setUint8(offset++, topicBytes.byteLength)\n view.setUint8(offset++, eventBytes.byteLength)\n headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength\n headerBytes.set(refBytes, offset); offset += refBytes.byteLength\n headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength\n headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength\n\n var combined = new Uint8Array(header.byteLength + payload.byteLength)\n combined.set(headerBytes, 0)\n combined.set(new Uint8Array(payload), header.byteLength)\n\n return combined.buffer\n },\n\n assertFieldSize(size, name){\n if(size > 255){\n throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)\n }\n },\n\n binaryDecode(buffer){\n let view = new DataView(buffer)\n let kind = view.getUint8(0)\n let decoder = new TextDecoder()\n switch(kind){\n case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n }\n },\n\n decodePush(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let topicSize = view.getUint8(2)\n let eventSize = view.getUint8(3)\n let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n },\n\n decodeReply(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let refSize = view.getUint8(2)\n let topicSize = view.getUint8(3)\n let eventSize = view.getUint8(4)\n let offset = this.HEADER_LENGTH + this.META_LENGTH\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n offset = offset + refSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n let payload = {status: event, response: data}\n return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n },\n\n decodeBroadcast(buffer, view, decoder){\n let topicSize = view.getUint8(1)\n let eventSize = view.getUint8(2)\n let offset = this.HEADER_LENGTH + 2\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n\n return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n }\n}\n", "import {SOCKET_STATES} from \"./constants\"\n\n/**\n * @private\n *\n * A connection of the socket over a single transport instance.\n *\n * It owns everything that belongs to this connection, so that nothing about it can\n * affect a later connection: the transport events are only handled while it is the\n * socket's current connection, and its state is gone together with it.\n */\nexport default class Connection {\n /**\n * @param {Socket} socket\n * @param {Object} transport - The transport instance, for example a WebSocket\n */\n constructor(socket, transport){\n this.socket = socket\n this.transport = transport\n // the join ref of each channel whose join was sent over this connection, weakly held\n // as a connection can outlive many channels, e.g. when navigating between LiveViews\n this.joins = new WeakMap()\n // the callbacks of the pings sent over this connection by ref, as their replies can only\n // arrive over it, so they are gone together with it instead of accumulating on the socket\n this.pings = new Map()\n // set when we close the connection, as opposed to the server or the network\n this.closing = false\n // set when the socket does not want this connection anymore, e.g. on disconnect,\n // so that connecting again creates a new one instead of waiting for it\n this.ended = false\n // set once the channels were errored after the connection stopped being open\n this.channelsErrored = false\n // set once the close event was handled, as LongPoll can emit it more than once\n this.closeHandled = false\n transport.onopen = () => { if(this.isCurrent()){ socket.onConnOpen() } }\n transport.onerror = error => { if(this.isCurrent()){ socket.onConnError(error) } }\n transport.onmessage = event => { if(this.isCurrent()){ socket.onConnMessage(event) } }\n transport.onclose = event => {\n if(this.isCurrent() && !this.closeHandled){\n this.closeHandled = true\n socket.onConnClose(event)\n }\n }\n }\n\n /**\n * Whether this is the connection the socket currently uses. A replaced connection\n * can still emit events while it closes, but they no longer concern the socket.\n */\n isCurrent(){ return this.socket.connection === this }\n\n isOpen(){ return this.transport.readyState === SOCKET_STATES.open }\n\n isConnecting(){ return this.transport.readyState === SOCKET_STATES.connecting }\n\n /**\n * Whether the channel's current join was sent over this connection.\n */\n carried(channel, joinRef = channel.joinRef()){\n return this.joins.has(channel) && this.joins.get(channel) === joinRef\n }\n\n /**\n * Closes the transport. Its close event then only confirms our close.\n */\n close(code, reason){\n this.closing = true\n // closing a transport that is still connecting makes it emit an error, which is\n // caused by closing it and must not be handled as a transport error\n this.transport.onerror = function (){ } // noop\n if(code){ this.transport.close(code, reason || \"\") } else { this.transport.close() }\n }\n}\n", "import {\n global,\n phxWindow,\n CHANNEL_EVENTS,\n DEFAULT_TIMEOUT,\n DEFAULT_VSN,\n SOCKET_STATES,\n TRANSPORTS,\n WS_CLOSE_NORMAL,\n AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport {\n closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\nimport Connection from \"./connection\"\n\n/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n * `\"wss://example.com\"`\n * `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.\n * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.\n *\n * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport\n * before falling back to the LongPoll transport. Disabled by default.\n *\n * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.\n *\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the\n * socket reconnect interval, in milliseconds.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n * return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {boolean} [opts.reconnectWhenHidden] - When true, the socket also reconnects while the\n * page is hidden, for example in a background tab or on a sleeping phone.\n *\n * Defaults to false. The socket then waits until the page is visible again before it reconnects.\n * This avoids reconnect attempts that stall on mobile devices and delay the connection after the\n * device wakes up.\n *\n * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n * return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n * console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {(Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {(string|function)} [opts.authToken] - the optional authentication token to be exposed on the server\n * under the `:auth_token` connect_info key. Can be a string or a function that returns a string.\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n *\n * @param {Object} [opts.sessionStorage] - An optional Storage compatible object\n * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is\n * useful when Phoenix won't have access to `sessionStorage`. For example, This could\n * happen if a site loads a cross-domain channel in an iframe. Example usage:\n *\n * class InMemoryStorage {\n * constructor() { this.storage = {} }\n * getItem(keyName) { return this.storage[keyName] || null }\n * removeItem(keyName) { delete this.storage[keyName] }\n * setItem(keyName, keyValue) { this.storage[keyName] = keyValue }\n * }\n *\n*/\nexport default class Socket {\n constructor(endPoint, opts = {}){\n this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n this.channels = []\n this.sendBuffer = []\n this.ref = 0\n this.fallbackRef = null\n this.fallbackErrorRef = null\n this.timeout = opts.timeout || DEFAULT_TIMEOUT\n this.transport = opts.transport || global.WebSocket || LongPoll\n this.primaryPassedHealthCheck = false\n this.longPollFallbackMs = opts.longPollFallbackMs\n this.fallbackTimer = null\n this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)\n this.establishedConnections = 0\n this.defaultEncoder = Serializer.encode.bind(Serializer)\n this.defaultDecoder = Serializer.decode.bind(Serializer)\n // We start with closeWasClean true to avoid the visibility change\n // logic from connecting if the socket was never connected in the first place.\n // transportConnect sets it to false on open.\n this.closeWasClean = true\n this.binaryType = opts.binaryType || \"arraybuffer\"\n // the connection over the current transport instance, see Connection\n this.connection = null\n // changes whenever we connect, disconnect or replace the transport, for the code that needs\n // to know whether that happened while there was no connection, see replaceTransport\n this.connectClock = 1\n // the connection created when the page was shown, see handleVisibilityChange\n this.visibilityConnection = null\n if(this.transport !== LongPoll){\n this.encode = opts.encode || this.defaultEncoder\n this.decode = opts.decode || this.defaultDecoder\n } else {\n this.encode = this.defaultEncoder\n this.decode = this.defaultDecoder\n }\n let awaitingConnectionOnPageShow = null\n if(phxWindow && phxWindow.addEventListener){\n phxWindow.addEventListener(\"pagehide\", _e => {\n // an ended connection only finishes closing, e.g. after disconnect\n if(this.connection && !this.connection.ended){\n this.disconnect()\n awaitingConnectionOnPageShow = this.connectClock\n }\n })\n phxWindow.addEventListener(\"pageshow\", _e => {\n if(awaitingConnectionOnPageShow === this.connectClock){\n awaitingConnectionOnPageShow = null\n this.connect()\n }\n })\n phxWindow.addEventListener(\"visibilitychange\", () => {\n this.handleVisibilityChange()\n })\n // Chrome 149 started to not reliably fire visibilitychange after resume,\n // see https://issues.chromium.org/issues/547062449.\n phxWindow.document && phxWindow.document.addEventListener(\"resume\", () => {\n this.handleVisibilityChange()\n })\n }\n this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n this.rejoinAfterMs = (tries) => {\n if(opts.rejoinAfterMs){\n return opts.rejoinAfterMs(tries)\n } else {\n return [1000, 2000, 5000][tries - 1] || 10000\n }\n }\n this.reconnectAfterMs = (tries) => {\n if(opts.reconnectAfterMs){\n return opts.reconnectAfterMs(tries)\n } else {\n return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n }\n }\n this.reconnectWhenHidden = opts.reconnectWhenHidden || false\n this.logger = opts.logger || null\n if(!this.logger && opts.debug){\n this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }\n }\n this.longpollerTimeout = opts.longpollerTimeout || 20000\n this.params = closure(opts.params || {})\n this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n this.vsn = opts.vsn || DEFAULT_VSN\n this.heartbeatTimeoutTimer = null\n this.heartbeatTimer = null\n this.pendingHeartbeatRef = null\n this.reconnectTimer = new Timer(() => {\n // handleVisibilityChange reconnects once the page is visible again\n if(this.pageHidden && !this.reconnectWhenHidden){\n this.log(\"Not reconnecting as page is hidden!\")\n this.teardown()\n return\n }\n this.teardownAndReconnect(() => this.connect())\n }, this.reconnectAfterMs)\n this.authToken = opts.authToken && closure(opts.authToken)\n }\n\n /**\n * The transport instance of the current connection, for example a WebSocket\n */\n get conn(){ return this.connection && this.connection.transport }\n\n /**\n * @internal\n */\n get pageHidden(){\n return phxWindow && phxWindow.document ? phxWindow.document.visibilityState === \"hidden\" : false\n }\n\n /**\n * @internal\n */\n handleVisibilityChange(){\n if(this.pageHidden){\n this.visibilityConnection = null\n return\n }\n // resume and visibilitychange can both fire when the page is shown again, so we keep\n // the connection created for the first one while it is still connecting\n const connection = this.connection\n if(connection && connection === this.visibilityConnection && connection.isConnecting()){ return }\n // reconnect immediately\n if(!this.isConnected() && !this.closeWasClean){\n // a reconnect that was scheduled before would tear down the connection we create here\n this.reconnectTimer.reset()\n this.teardownAndReconnect(() => {\n this.connect()\n this.visibilityConnection = this.connection\n })\n }\n }\n\n /**\n * Returns the LongPoll transport reference\n */\n getLongPollTransport(){ return LongPoll }\n\n /**\n * Disconnects and replaces the active transport\n *\n * @param {Function} newTransport - The new transport class to instantiate\n *\n */\n replaceTransport(newTransport){\n const connectClock = ++this.connectClock\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // replaced first, as channel error callbacks below can already connect again\n this.transport = newTransport\n const connection = this.connection\n if(connection){\n const wasOpen = connection.isOpen()\n // the old transport closes asynchronously, but its events no longer concern us\n this.connection = null\n connection.ended = true\n connection.close()\n this.clearHeartbeats()\n // any in-flight pushes on the old connection are lost, so channels need to rejoin\n if(wasOpen){ this.triggerChanError(\"connection_closed\", connection) }\n }\n return connectClock\n }\n\n /**\n * Returns the socket protocol\n *\n * @returns {string}\n */\n protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n /**\n * The fully qualified socket url\n *\n * @returns {string}\n */\n endPointURL(){\n let uri = Ajax.appendParams(\n Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n if(uri.charAt(0) !== \"/\"){ return uri }\n if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n return `${this.protocol()}://${location.host}${uri}`\n }\n\n /**\n * Disconnects the socket\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n *\n * @param {Function} callback - Optional callback which is called after socket is disconnected.\n * @param {integer} code - A status code for disconnection (Optional).\n * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n */\n disconnect(callback, code, reason){\n this.connectClock++\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // connecting again creates a new connection right away instead of waiting for this one to close\n if(this.connection){ this.connection.ended = true }\n this.teardown(callback, code, reason)\n }\n\n /**\n *\n * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n *\n * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n */\n connect(params){\n if(params){\n console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n this.params = closure(params)\n }\n // the current connection is kept, including while it reconnects, until the socket is done with it\n if(this.connection && !this.connection.ended){ return }\n if(this.longPollFallbackMs && this.transport !== LongPoll){\n this.connectWithFallback(LongPoll, this.longPollFallbackMs)\n } else {\n this.transportConnect()\n }\n }\n\n /**\n * Logs the message. Override `this.logger` for specialized logging. noops by default\n * @param {string} kind\n * @param {string} msg\n * @param {Object} data\n */\n log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }\n\n /**\n * Returns true if a logger has been set on this socket.\n */\n hasLogger(){ return this.logger !== null }\n\n /**\n * Registers callbacks for connection open events\n *\n * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n *\n * @param {Function} callback\n */\n onOpen(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.open.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection close events\n * @param {Function} callback\n */\n onClose(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.close.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection error events\n *\n * @example socket.onError(function(error){ alert(\"An error occurred\") })\n *\n * @param {Function} callback\n */\n onError(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.error.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection message events\n * @param {Function} callback\n */\n onMessage(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.message.push([ref, callback])\n return ref\n }\n\n /**\n * Pings the server and invokes the callback with the RTT in milliseconds\n * @param {Function} callback\n *\n * Returns true if the ping was pushed or false if unable to be pushed.\n */\n ping(callback){\n if(!this.isConnected()){ return false }\n let ref = this.makeRef()\n let startTime = Date.now()\n this.connection.pings.set(ref, () => callback(Date.now() - startTime))\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: ref})\n return true\n }\n\n /**\n * @private\n *\n * @param {Function}\n */\n transportName(transport){\n // JavaScript minification, enabled by default in production in Phoenix\n // projects, renames symbols to reduce code size.\n // See https://esbuild.github.io/api/#keep-names.\n // This helper ensures we return the correct name for the LongPoll transport\n // even after minification. The other common transport is WebSocket, which\n // is native to browsers and does not need special handling.\n switch(transport){\n case LongPoll: return \"LongPoll\"\n default: return transport.name\n }\n }\n\n /**\n * @private\n */\n transportConnect(){\n const previous = this.connection\n this.connectClock++\n this.closeWasClean = false\n // a reconnect scheduled before would tear down the new connection, but we keep\n // its tries, which only reset once a connection opens\n clearTimeout(this.reconnectTimer.timer)\n let protocols = undefined\n // Sec-WebSocket-Protocol based token\n // (longpoll uses Authorization header instead)\n if(this.authToken){\n protocols = [\"phoenix\", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken()).replace(/=/g, \"\")}`]\n }\n const transport = new this.transport(this.endPointURL(), protocols)\n transport.binaryType = this.binaryType\n transport.timeout = this.longpollerTimeout\n this.connection = new Connection(this, transport)\n // connect can replace an ended connection that is still open while its buffer drains, but the\n // channels joined over it need to rejoin on the new connection instead of waiting for it to close\n if(previous && previous.isOpen()){ this.triggerChanError(\"connection_closed\", previous) }\n }\n\n getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }\n\n storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }\n\n connectWithFallback(fallbackTransport, fallbackThreshold = 2500){\n // the callbacks of a previous attempt must not act on this one, e.g. an attempt\n // that never opened would otherwise fall back again on the next error\n this.cancelFallback()\n let established = false\n let primaryTransport = true\n let errorRef\n let connectClock\n const isCurrent = () => connectClock === this.connectClock && !this.closeWasClean\n let fallbackTransportName = this.transportName(fallbackTransport)\n // the fallback timer is cleared whenever the attempt ends, and the callbacks below\n // only fall back while the attempt is current\n let fallback = (reason) => {\n this.log(\"transport\", `falling back to ${fallbackTransportName}...`, reason)\n // the open callback stays registered to remember the fallback once it connects\n this.off([errorRef])\n primaryTransport = false\n const replaceClock = this.replaceTransport(fallbackTransport)\n if(replaceClock !== this.connectClock){ return }\n this.transportConnect()\n }\n if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback(\"memorized\") }\n\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n\n errorRef = this.fallbackErrorRef = this.onError(reason => {\n if(!isCurrent()){ return }\n this.log(\"transport\", \"error\", reason)\n if(primaryTransport && !established){\n clearTimeout(this.fallbackTimer)\n fallback(reason)\n }\n })\n this.fallbackRef = this.onOpen(() => {\n if(!primaryTransport){\n // reconnects keep using the fallback transport, so a later successful connection\n // must still be remembered, unless an earlier callback disconnected it\n if(this.transport !== fallbackTransport || this.closeWasClean){ return }\n let fallbackTransportName = this.transportName(fallbackTransport)\n // only memorize LP if we never connected to primary\n if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, \"true\") }\n return this.log(\"transport\", `established ${fallbackTransportName} fallback`)\n }\n if(!isCurrent()){ return }\n established = true\n // if we've established primary, give the fallback a new period to attempt ping\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n this.ping(rtt => {\n this.log(\"transport\", \"connected to primary after\", rtt)\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n })\n })\n this.transportConnect()\n connectClock = this.connectClock\n }\n\n /**\n * @private\n *\n * Stops the fallback of the current connection attempt, see connectWithFallback\n */\n cancelFallback(){\n clearTimeout(this.fallbackTimer)\n this.off([this.fallbackRef, this.fallbackErrorRef])\n }\n\n clearHeartbeats(){\n clearTimeout(this.heartbeatTimer)\n clearTimeout(this.heartbeatTimeoutTimer)\n // a late reply to the pending heartbeat must not start the heartbeats again\n this.pendingHeartbeatRef = null\n }\n\n onConnOpen(){\n if(this.hasLogger()) this.log(\"transport\", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)\n this.closeWasClean = false\n this.establishedConnections++\n // the health-check ping has to leave before the buffered join. The transport\n // process blocks in Channel.Server.join until the join returns, so a ping\n // sent after the join is not answered while the mount runs, and the fallback\n // timer then drops a socket that is fine.\n this.pingBeforeBufferedJoin()\n this.flushSendBuffer()\n this.reconnectTimer.reset()\n this.resetHeartbeat()\n this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n }\n\n /**\n * @private\n *\n * Sends the fallback health check before flushSendBuffer. No-op unless a\n * fallback attempt is waiting on the primary transport.\n */\n pingBeforeBufferedJoin(){\n if(!this.fallbackTimer || this.transport === LongPoll){ return }\n this.ping(() => this.passPrimaryHealthCheck())\n }\n\n /**\n * @private\n *\n * The primary transport answered. Later frames on this connection also call\n * this, because a frame is the proof the ping was waiting for.\n */\n passPrimaryHealthCheck(){\n if(this.transport === LongPoll){ return }\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = null\n }\n\n /**\n * @private\n */\n\n heartbeatTimeout(){\n if(this.pendingHeartbeatRef){\n const connection = this.connection\n this.pendingHeartbeatRef = null\n if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n this.closeWasClean = false\n // the connection is presumed dead, so we close it right away instead of waiting for its\n // buffer to drain, which also ensures that it is no longer considered connected when the\n // channels error (LiveView would report a server error otherwise)\n if(connection){ connection.close(WS_CLOSE_NORMAL, \"heartbeat timeout\") }\n // some transports emit their close event synchronously, and callbacks of it as well as\n // channel error callbacks can disconnect or connect, which takes precedence over reconnecting\n if(!this.stillUses(connection)){ return }\n this.triggerChanError(\"heartbeat_timeout\")\n if(!this.stillUses(connection)){ return }\n this.teardownAndReconnect(() => this.reconnectTimer.scheduleTimeout())\n }\n }\n\n resetHeartbeat(){\n if(this.conn && this.conn.skipHeartbeat){ return }\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n /**\n * @private\n *\n * Tears down the current connection to reconnect afterwards, unless the socket was\n * disconnected or connected again in the meantime, which takes precedence.\n */\n teardownAndReconnect(reconnect){\n const connection = this.connection\n const connectClock = this.connectClock\n this.teardown(() => {\n // the socket was disconnected or connected again in the meantime, which the connection does not\n // tell once an overlapping teardown gave up on it\n if(connectClock !== this.connectClock || (connection && connection.ended)){ return }\n reconnect()\n })\n }\n\n /**\n * @private\n *\n * Whether the socket still uses the connection and did not give up on it. User callbacks\n * can disconnect or connect, so code continuing after them checks this first.\n */\n stillUses(connection){\n return this.connection === connection && !(connection && connection.ended)\n }\n\n teardown(callback, code, reason){\n // If someone calls connect before we finish tearing down,\n // we create a new connection, but we still want to finish tearing down the old one.\n const connection = this.connection\n if(!connection){\n return callback && callback()\n }\n // onConnClose is not called when the close event arrives late, so we stop the\n // heartbeats here, as a heartbeat timeout would otherwise reconnect the socket\n this.clearHeartbeats()\n\n this.waitForBufferDone(connection.transport, () => {\n // a connection we already closed, e.g. on a heartbeat timeout or in an\n // overlapping teardown, also errored its channels already\n if(!connection.closing){\n const wasOpened = !connection.isConnecting()\n connection.close(code, reason)\n // channels joined over this connection need to rejoin on the next one, but we cannot rely\n // on onConnClose for that, as the close event may arrive after we stopped waiting for it\n // or after a new connection already replaced this one\n if(wasOpened){ this.triggerChanError(\"connection_closed\", connection) }\n }\n\n this.waitForSocketClosed(connection.transport, () => {\n if(this.connection === connection){ this.connection = null }\n callback && callback()\n })\n })\n }\n\n waitForBufferDone(conn, callback, tries = 1){\n // there is no point in waiting for a conn that is already closing\n if(tries === 5 || !conn.bufferedAmount || conn.readyState !== SOCKET_STATES.open){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForBufferDone(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n waitForSocketClosed(conn, callback, tries = 1){\n if(tries === 5 || conn.readyState === SOCKET_STATES.closed){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForSocketClosed(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n onConnClose(event){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n this.clearHeartbeats()\n // we only close a connection to disconnect or to reconnect, which the code closing it takes\n // care of, so its close event only confirms our close. Any other close is reconnected, whatever\n // its code, as the server, proxies and load balancers also close with 1000 (normal closure)\n const closedByUs = !!(connection && connection.closing)\n // channel callbacks may disconnect or create a new connection, so finish cleaning up\n // this connection first and do not schedule a reconnect for its replacement\n this.triggerChanError(\"connection_closed\", connection)\n if(this.stillUses(connection) && !closedByUs && !this.closeWasClean){\n this.reconnectTimer.scheduleTimeout()\n }\n this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n }\n\n /**\n * @private\n */\n onConnError(error){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"error\", error)\n let transportBefore = this.transport\n let establishedBefore = this.establishedConnections\n this.stateChangeCallbacks.error.forEach(([, callback]) => {\n callback(error, transportBefore, establishedBefore)\n })\n // callbacks can replace the connection, in which case only its own joins are errored\n this.triggerChanError(\"connection_error\", connection)\n }\n\n /**\n * @private\n *\n * Errors the channels, as they need to rejoin once the given connection stopped being open.\n */\n triggerChanError(reason, connection = this.connection){\n // several places notice that a connection stopped being open (onConnError, onConnClose,\n // teardown and heartbeatTimeout). After the first, only channels whose join it carried\n // are errored, as error callbacks may have joined channels that join over another connection.\n let erroredBefore = false\n if(connection && !connection.isOpen()){\n erroredBefore = connection.channelsErrored\n connection.channelsErrored = true\n }\n const joins = this.channels.map(channel => [channel, channel.joinRef()])\n joins.forEach(([channel, joinRef]) => {\n // an earlier error callback can rejoin a later channel, so check its join\n // again immediately before dispatching rather than filtering the list first\n if(channel.joinRef() !== joinRef){ return }\n // the joins of a replaced connection are not the only ones anymore\n if(connection && (erroredBefore || !connection.isCurrent()) && !connection.carried(channel, joinRef)){ return }\n if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n channel.trigger(CHANNEL_EVENTS.error, {source: \"transport\", reason})\n }\n })\n }\n\n /**\n * @returns {string}\n */\n connectionState(){\n switch(this.conn && this.conn.readyState){\n case SOCKET_STATES.connecting: return \"connecting\"\n case SOCKET_STATES.open: return \"open\"\n case SOCKET_STATES.closing: return \"closing\"\n default: return \"closed\"\n }\n }\n\n /**\n * @returns {boolean}\n */\n isConnected(){ return this.connectionState() === \"open\" }\n\n /**\n * @private\n *\n * @param {Channel}\n */\n remove(channel){\n this.off(channel.stateChangeRefs)\n this.channels = this.channels.filter(c => c !== channel)\n }\n\n /**\n * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n *\n * @param {refs} - list of refs returned by calls to\n * `onOpen`, `onClose`, `onError,` and `onMessage`\n */\n off(refs){\n for(let key in this.stateChangeCallbacks){\n this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n return refs.indexOf(ref) === -1\n })\n }\n }\n\n /**\n * Initiates a new channel for the given topic\n *\n * @param {string} topic\n * @param {Object} chanParams - Parameters for the channel\n * @returns {Channel}\n */\n channel(topic, chanParams = {}){\n let chan = new Channel(topic, chanParams, this)\n this.channels.push(chan)\n return chan\n }\n\n /**\n * @param {Object} data\n * @param {Channel} [channel] - The channel the push belongs to, if any\n */\n push(data, channel){\n if(this.hasLogger()){\n let {topic, event, payload, ref, join_ref} = data\n this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n }\n\n if(this.isConnected()){\n this.send(data, channel)\n } else {\n this.sendBuffer.push(() => {\n // the channel may have errored or left while the push was buffered, e.g. when the\n // connection failed before opening, in which case its rejoin supersedes the buffered join\n if(!this.isOutdated(data, channel)){ this.send(data, channel) }\n })\n }\n }\n\n /**\n * @private\n */\n send(data, channel){\n const connection = this.connection\n // the join counts as carried by this connection while it is still encoded, so that the\n // channel rejoins when the connection is torn down before the encoding finished\n if(channel && data.event === CHANNEL_EVENTS.join){ connection.joins.set(channel, data.join_ref) }\n this.encode(data, result => {\n // let encodes finish while this connection drains, but never send on a\n // replacement or after the original connection has started closing\n if(!connection.isCurrent() || !connection.isOpen()){ return }\n // a leave still needs to reach the server after its channel was removed locally\n if(data.event !== CHANNEL_EVENTS.leave && this.isOutdated(data, channel)){ return }\n connection.transport.send(result)\n })\n }\n\n /**\n * @private\n *\n * Whether the push was made for an earlier join of its channel, or the channel left since.\n */\n isOutdated(data, channel){\n return !!channel && (data.join_ref !== channel.joinRef() || channel.isClosed())\n }\n\n /**\n * Return the next message ref, accounting for overflows\n * @returns {string}\n */\n makeRef(){\n let newRef = this.ref + 1\n if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n return this.ref.toString()\n }\n\n sendHeartbeat(){\n if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n this.pendingHeartbeatRef = this.makeRef()\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n }\n\n flushSendBuffer(){\n if(this.isConnected() && this.sendBuffer.length > 0){\n this.sendBuffer.forEach(callback => callback())\n this.sendBuffer = []\n }\n }\n\n onConnMessage(rawMessage){\n const connection = this.connection\n const wasOpen = connection && connection.isOpen()\n this.decode(rawMessage.data, msg => {\n // the transport event can be current even though its asynchronous decode is not, as the\n // connection may have been replaced or closed in the meantime (browsers also drop messages\n // that arrive once a WebSocket is closing)\n if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return }\n // a frame on this connection is the health check. The ping reply is one\n // such frame, and so is anything the server already sent.\n if(this.fallbackTimer){ this.passPrimaryHealthCheck() }\n let {topic, event, payload, ref, join_ref} = msg\n if(ref && ref === this.pendingHeartbeatRef){\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n // removing a channel or callback while dispatching replaces the list instead of\n // changing it, so we iterate over the lists from before to not skip any of them\n const channels = this.channels\n for(let i = 0; i < channels.length; i++){\n const channel = channels[i]\n if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n channel.trigger(event, payload, ref, join_ref)\n }\n\n const callbacks = this.stateChangeCallbacks.message\n for(let i = 0; i < callbacks.length; i++){\n let [, callback] = callbacks[i]\n callback(msg)\n }\n\n const ping = ref && connection.pings.get(ref)\n if(ping){\n connection.pings.delete(ref)\n ping()\n }\n })\n }\n\n leaveOpenTopic(topic){\n let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n if(dupChannel){\n if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n dupChannel.leave()\n }\n }\n}\n"], + "mappings": "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", "names": ["closure", "fallbackTransportName"] } From 76dcb65056878ba1097fa64d1fee558653a56cd1 Mon Sep 17 00:00:00 2001 From: oliviasculley Date: Fri, 6 Nov 2026 15:17:33 +0000 Subject: [PATCH 2/2] Send the fallback health check only once. The open callback ping repeats the one that already goes out before the join. That ping still logs the round trip. The built files in priv/static stay out of this change. --- assets/js/phoenix/socket.js | 12 +++++------- assets/test/socket_test.js | 2 +- priv/static/phoenix.cjs.js | 30 ------------------------------ priv/static/phoenix.cjs.js.map | 4 ++-- priv/static/phoenix.js | 30 ------------------------------ priv/static/phoenix.min.js | 4 ++-- priv/static/phoenix.mjs | 30 ------------------------------ priv/static/phoenix.mjs.map | 4 ++-- 8 files changed, 12 insertions(+), 104 deletions(-) diff --git a/assets/js/phoenix/socket.js b/assets/js/phoenix/socket.js index d064d6be51..36926ce5ee 100644 --- a/assets/js/phoenix/socket.js +++ b/assets/js/phoenix/socket.js @@ -511,14 +511,9 @@ export default class Socket { } if(!isCurrent()){ return } established = true - // if we've established primary, give the fallback a new period to attempt ping + // the health check already went out. This period is how long a frame has to arrive. clearTimeout(this.fallbackTimer) this.fallbackTimer = setTimeout(fallback, fallbackThreshold) - this.ping(rtt => { - this.log("transport", "connected to primary after", rtt) - this.primaryPassedHealthCheck = true - clearTimeout(this.fallbackTimer) - }) }) this.transportConnect() connectClock = this.connectClock @@ -564,7 +559,10 @@ export default class Socket { */ pingBeforeBufferedJoin(){ if(!this.fallbackTimer || this.transport === LongPoll){ return } - this.ping(() => this.passPrimaryHealthCheck()) + this.ping(rtt => { + this.log("transport", "connected to primary after", rtt) + this.passPrimaryHealthCheck() + }) } /** diff --git a/assets/test/socket_test.js b/assets/test/socket_test.js index a74afcba4c..68a42116ce 100644 --- a/assets/test/socket_test.js +++ b/assets/test/socket_test.js @@ -203,7 +203,7 @@ describe("with transports", function (){ socket.connect() connections[0].open() - expect(connections[0].sent.map(([, , , event]) => event).slice(0, 2)).toEqual(["heartbeat", "phx_join"]) + expect(connections[0].sent.map(([, , , event]) => event)).toEqual(["heartbeat", "phx_join"]) }) it("does not fall back when the health check is answered before the join reply", function (){ diff --git a/priv/static/phoenix.cjs.js b/priv/static/phoenix.cjs.js index 1096fe6b08..09f856431d 100644 --- a/priv/static/phoenix.cjs.js +++ b/priv/static/phoenix.cjs.js @@ -1617,38 +1617,11 @@ var Socket = class { if (this.hasLogger()) this.log("transport", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`); this.closeWasClean = false; this.establishedConnections++; - this.pingBeforeBufferedJoin(); this.flushSendBuffer(); this.reconnectTimer.reset(); this.resetHeartbeat(); this.stateChangeCallbacks.open.forEach(([, callback]) => callback()); } - /** - * @private - * - * Sends the fallback health check before flushSendBuffer. No-op unless a - * fallback attempt is waiting on the primary transport. - */ - pingBeforeBufferedJoin() { - if (!this.fallbackTimer || this.transport === LongPoll) { - return; - } - this.ping(() => this.passPrimaryHealthCheck()); - } - /** - * @private - * - * The primary transport answered. Later frames on this connection also call - * this, because a frame is the proof the ping was waiting for. - */ - passPrimaryHealthCheck() { - if (this.transport === LongPoll) { - return; - } - this.primaryPassedHealthCheck = true; - clearTimeout(this.fallbackTimer); - this.fallbackTimer = null; - } /** * @private */ @@ -1927,9 +1900,6 @@ var Socket = class { if (connection !== this.connection || wasOpen && !connection.isOpen()) { return; } - if (this.fallbackTimer) { - this.passPrimaryHealthCheck(); - } let { topic, event, payload, ref, join_ref } = msg; if (ref && ref === this.pendingHeartbeatRef) { this.clearHeartbeats(); diff --git a/priv/static/phoenix.cjs.js.map b/priv/static/phoenix.cjs.js.map index d34a086e71..f013604845 100644 --- a/priv/static/phoenix.cjs.js.map +++ b/priv/static/phoenix.cjs.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../assets/js/phoenix/index.js", "../../assets/js/phoenix/utils.js", "../../assets/js/phoenix/constants.js", "../../assets/js/phoenix/push.js", "../../assets/js/phoenix/timer.js", "../../assets/js/phoenix/channel.js", "../../assets/js/phoenix/ajax.js", "../../assets/js/phoenix/longpoll.js", "../../assets/js/phoenix/presence.js", "../../assets/js/phoenix/serializer.js", "../../assets/js/phoenix/connection.js", "../../assets/js/phoenix/socket.js"], - "sourcesContent": ["/**\n * Phoenix Channels JavaScript client\n *\n * ## Socket Connection\n *\n * A single connection is established to the server and\n * channels are multiplexed over the connection.\n * Connect to the server using the `Socket` class:\n *\n * ```javascript\n * let socket = new Socket(\"/socket\", {params: {userToken: \"123\"}})\n * socket.connect()\n * ```\n *\n * The `Socket` constructor takes the mount point of the socket,\n * the authentication params, as well as options that can be found in\n * the Socket docs, such as configuring the `LongPoll` transport, and\n * heartbeat.\n *\n * ## Channels\n *\n * Channels are isolated, concurrent processes on the server that\n * subscribe to topics and broker events between the client and server.\n * To join a channel, you must provide the topic, and channel params for\n * authorization. Here's an example chat room example where `\"new_msg\"`\n * events are listened for, messages are pushed to the server, and\n * the channel is joined with ok/error/timeout matches:\n *\n * ```\n * let channel = socket.channel(\"room:123\", {token: roomToken})\n * channel.on(\"new_msg\", msg => console.log(\"Got message\", msg) )\n * $input.onEnter( e => {\n * channel.push(\"new_msg\", {body: e.target.val}, 10000)\n * .receive(\"ok\", (msg) => console.log(\"created message\", msg) )\n * .receive(\"error\", (reasons) => console.log(\"create failed\", reasons) )\n * .receive(\"timeout\", () => console.log(\"Networking issue...\") )\n * })\n *\n * channel.join()\n * .receive(\"ok\", ({messages}) => console.log(\"catching up\", messages) )\n * .receive(\"error\", ({reason}) => console.log(\"failed join\", reason) )\n * .receive(\"timeout\", () => console.log(\"Networking issue. Still waiting...\"))\n *```\n *\n * ## Joining\n *\n * Creating a channel with `socket.channel(topic, params)`, binds the params to\n * `channel.params`, which are sent up on `channel.join()`.\n * Subsequent rejoins will send up the modified params for\n * updating authorization params, or passing up last_message_id information.\n * Successful joins receive an \"ok\" status, while unsuccessful joins\n * receive \"error\".\n *\n * With the default serializers and WebSocket transport, JSON text frames are\n * used for pushing a JSON object literal. If an `ArrayBuffer` instance is provided,\n * binary encoding will be used and the message will be sent with the binary\n * opcode.\n *\n * *Note*: binary messages are only supported on the WebSocket transport.\n *\n * ## Duplicate Join Subscriptions\n *\n * While the client may join any number of topics on any number of channels,\n * the client may only hold a single subscription for each unique topic at any\n * given time. When attempting to create a duplicate subscription,\n * the server will close the existing channel, log a warning, and\n * spawn a new channel for the topic. The client will have their\n * `channel.onClose` callbacks fired for the existing channel, and the new\n * channel join will have its receive hooks processed as normal.\n *\n * ## Pushing Messages\n *\n * From the previous example, we can see that pushing messages to the server\n * can be done with `channel.push(eventName, payload)` and we can optionally\n * receive responses from the push. Additionally, we can use\n * `receive(\"timeout\", callback)` to abort waiting for our other `receive` hooks\n * and take action after some period of waiting. The default timeout is 10000ms.\n *\n *\n * ## Socket Hooks\n *\n * Lifecycle events of the multiplexed connection can be hooked into via\n * `socket.onError()` and `socket.onClose()` events, ie:\n *\n * ```\n * socket.onError( () => console.log(\"there was an error with the connection!\") )\n * socket.onClose( () => console.log(\"the connection dropped\") )\n * ```\n *\n *\n * ## Channel Hooks\n *\n * For each joined channel, you can bind to `onError` and `onClose` events\n * to monitor the channel lifecycle, ie:\n *\n * ```\n * channel.onError( () => console.log(\"there was an error!\") )\n * channel.onClose( () => console.log(\"the channel has gone away gracefully\") )\n * ```\n *\n * ### onError hooks\n *\n * `onError` hooks are invoked if the socket connection drops, or the channel\n * crashes on the server. In either case, a channel rejoin is attempted\n * automatically in an exponential backoff manner.\n *\n * ### onClose hooks\n *\n * `onClose` hooks are invoked only in two cases. 1) the channel explicitly\n * closed on the server, or 2). The client explicitly closed, by calling\n * `channel.leave()`\n *\n *\n * ## Presence\n *\n * The `Presence` object provides features for syncing presence information\n * from the server with the client and handling presences joining and leaving.\n *\n * ### Syncing state from the server\n *\n * To sync presence state from the server, first instantiate an object and\n * pass your channel in to track lifecycle events:\n *\n * ```\n * let channel = socket.channel(\"some:topic\")\n * let presence = new Presence(channel)\n * ```\n *\n * Next, use the `presence.onSync` callback to react to state changes\n * from the server. For example, to render the list of users every time\n * the list changes, you could write:\n *\n * ```\n * presence.onSync(() => {\n * myRenderUsersFunction(presence.list())\n * })\n * ```\n *\n * ### Listing Presences\n *\n * `presence.list` is used to return a list of presence information\n * based on the local state of metadata. By default, all presence\n * metadata is returned, but a `listBy` function can be supplied to\n * allow the client to select which metadata to use for a given presence.\n * For example, you may have a user online from different devices with\n * a metadata status of \"online\", but they have set themselves to \"away\"\n * on another device. In this case, the app may choose to use the \"away\"\n * status for what appears on the UI. The example below defines a `listBy`\n * function which prioritizes the first metadata which was registered for\n * each user. This could be the first tab they opened, or the first device\n * they came online from:\n *\n * ```\n * let listBy = (id, {metas: [first, ...rest]}) => {\n * first.count = rest.length + 1 // count of this user's presences\n * first.id = id\n * return first\n * }\n * let onlineUsers = presence.list(listBy)\n * ```\n *\n * ### Handling individual presence join and leave events\n *\n * The `presence.onJoin` and `presence.onLeave` callbacks can be used to\n * react to individual presences joining and leaving the app. For example:\n *\n * ```\n * let presence = new Presence(channel)\n *\n * // detect if user has joined for the 1st time or from another tab/device\n * presence.onJoin((id, current, newPres) => {\n * if(!current){\n * console.log(\"user has entered for the first time\", newPres)\n * } else {\n * console.log(\"user additional presence\", newPres)\n * }\n * })\n *\n * // detect if user has left from all tabs/devices, or is still present\n * presence.onLeave((id, current, leftPres) => {\n * if(current.metas.length === 0){\n * console.log(\"user has left from all devices\", leftPres)\n * } else {\n * console.log(\"user left from a device\", leftPres)\n * }\n * })\n * // receive presence data from server\n * presence.onSync(() => {\n * displayUsers(presence.list())\n * })\n * ```\n * @module phoenix\n */\n\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Presence from \"./presence\"\nimport Serializer from \"./serializer\"\nimport Socket from \"./socket\"\n\nexport {\n Channel,\n LongPoll,\n Presence,\n Serializer,\n Socket\n}\n", "// wraps value in closure or returns closure\nexport let closure = (value) => {\n if(typeof value === \"function\"){\n return value\n } else {\n let closure = function (){ return value }\n return closure\n }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || globalThis\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const MAX_LONGPOLL_BATCH_SIZE = 100;\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n closed: \"closed\",\n errored: \"errored\",\n joined: \"joined\",\n joining: \"joining\",\n leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n close: \"phx_close\",\n error: \"phx_error\",\n join: \"phx_join\",\n reply: \"phx_reply\",\n leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n longpoll: \"longpoll\",\n websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n complete: 4\n}\nexport const AUTH_TOKEN_PREFIX = \"base64url.bearer.phx.\"\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n constructor(channel, event, payload, timeout){\n this.channel = channel\n this.event = event\n this.payload = payload || function (){ return {} }\n this.receivedResp = null\n this.timeout = timeout\n this.timeoutTimer = null\n this.recHooks = []\n this.sent = false\n }\n\n /**\n *\n * @param {number} timeout\n */\n resend(timeout){\n this.timeout = timeout\n this.reset()\n this.send()\n }\n\n /**\n *\n */\n send(){\n if(this.hasReceived(\"timeout\")){ return }\n this.startTimeout()\n this.sent = true\n this.channel.socket.push({\n topic: this.channel.topic,\n event: this.event,\n payload: this.payload(),\n ref: this.ref,\n join_ref: this.channel.joinRef()\n }, this.channel)\n }\n\n /**\n *\n * @param {*} status\n * @param {*} callback\n */\n receive(status, callback){\n if(this.hasReceived(status)){\n callback(this.receivedResp.response)\n }\n\n this.recHooks.push({status, callback})\n return this\n }\n\n /**\n * @private\n */\n reset(){\n this.cancelRefEvent()\n this.ref = null\n this.refEvent = null\n this.receivedResp = null\n this.sent = false\n }\n\n /**\n * @private\n */\n matchReceive({status, response, _ref}){\n this.recHooks.filter(h => h.status === status)\n .forEach(h => h.callback(response))\n }\n\n /**\n * @private\n */\n cancelRefEvent(){\n if(!this.refEvent){ return }\n this.channel.off(this.refEvent)\n }\n\n /**\n * @private\n */\n cancelTimeout(){\n clearTimeout(this.timeoutTimer)\n this.timeoutTimer = null\n }\n\n /**\n * @private\n */\n startTimeout(){\n if(this.timeoutTimer){ this.cancelTimeout() }\n this.cancelRefEvent()\n this.ref = this.channel.socket.makeRef()\n this.refEvent = this.channel.replyEventName(this.ref)\n\n this.channel.on(this.refEvent, payload => {\n this.cancelRefEvent()\n this.cancelTimeout()\n this.receivedResp = payload\n this.matchReceive(payload)\n })\n\n this.timeoutTimer = setTimeout(() => {\n this.trigger(\"timeout\", {})\n }, this.timeout)\n }\n\n /**\n * @private\n */\n hasReceived(status){\n return this.receivedResp && this.receivedResp.status === status\n }\n\n /**\n * @private\n */\n trigger(status, response){\n this.channel.trigger(this.refEvent, {status, response})\n }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n * return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n constructor(callback, timerCalc){\n this.callback = callback\n this.timerCalc = timerCalc\n this.timer = null\n this.tries = 0\n }\n\n reset(){\n this.tries = 0\n clearTimeout(this.timer)\n }\n\n /**\n * Cancels any previous scheduleTimeout and schedules callback\n */\n scheduleTimeout(){\n clearTimeout(this.timer)\n\n this.timer = setTimeout(() => {\n this.tries = this.tries + 1\n this.callback()\n }, this.timerCalc(this.tries + 1))\n }\n}\n", "import {closure} from \"./utils\"\nimport {\n CHANNEL_EVENTS,\n CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n constructor(topic, params, socket){\n this.state = CHANNEL_STATES.closed\n this.topic = topic\n this.params = closure(params || {})\n this.socket = socket\n this.bindings = []\n this.bindingRef = 0\n this.timeout = this.socket.timeout\n this.joinedOnce = false\n this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n this.pushBuffer = []\n this.stateChangeRefs = []\n\n this.rejoinTimer = new Timer(() => {\n if(this.socket.isConnected()){ this.rejoin() }\n }, this.socket.rejoinAfterMs)\n this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n this.stateChangeRefs.push(this.socket.onOpen(() => {\n this.rejoinTimer.reset()\n if(this.isErrored()){ this.rejoin() }\n })\n )\n this.joinPush.receive(\"ok\", () => {\n this.state = CHANNEL_STATES.joined\n this.rejoinTimer.reset()\n this.pushBuffer.forEach(pushEvent => pushEvent.send())\n this.pushBuffer = []\n })\n this.joinPush.receive(\"error\", () => {\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.onClose(() => {\n this.rejoinTimer.reset()\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n this.state = CHANNEL_STATES.closed\n this.socket.remove(this)\n })\n this.onError(reason => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n if(this.isJoining()){ this.joinPush.reset() }\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.joinPush.receive(\"timeout\", () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n leavePush.send()\n this.state = CHANNEL_STATES.errored\n this.joinPush.reset()\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n this.trigger(this.replyEventName(ref), payload)\n })\n }\n\n /**\n * Join the channel\n * @param {integer} timeout\n * @returns {Push}\n */\n join(timeout = this.timeout){\n if(this.joinedOnce){\n throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n } else {\n this.timeout = timeout\n this.joinedOnce = true\n this.rejoin()\n return this.joinPush\n }\n }\n\n /**\n * Hook into channel close\n * @param {Function} callback\n */\n onClose(callback){\n this.on(CHANNEL_EVENTS.close, callback)\n }\n\n /**\n * Hook into channel errors\n * @param {Function} callback\n */\n onError(callback){\n return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n }\n\n /**\n * Subscribes on channel events\n *\n * Subscription returns a ref counter, which can be used later to\n * unsubscribe the exact event listener\n *\n * @example\n * const ref1 = channel.on(\"event\", do_stuff)\n * const ref2 = channel.on(\"event\", do_other_stuff)\n * channel.off(\"event\", ref1)\n * // Since unsubscription, do_stuff won't fire,\n * // while do_other_stuff will keep firing on the \"event\"\n *\n * @param {string} event\n * @param {Function} callback\n * @returns {integer} ref\n */\n on(event, callback){\n let ref = this.bindingRef++\n this.bindings.push({event, ref, callback})\n return ref\n }\n\n /**\n * Unsubscribes off of channel events\n *\n * Use the ref returned from a channel.on() to unsubscribe one\n * handler, or pass nothing for the ref to unsubscribe all\n * handlers for the given event.\n *\n * @example\n * // Unsubscribe the do_stuff handler\n * const ref1 = channel.on(\"event\", do_stuff)\n * channel.off(\"event\", ref1)\n *\n * // Unsubscribe all handlers from event\n * channel.off(\"event\")\n *\n * @param {string} event\n * @param {integer} ref\n */\n off(event, ref){\n this.bindings = this.bindings.filter((bind) => {\n return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n })\n }\n\n /**\n * @private\n */\n canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n /**\n * Sends a message `event` to phoenix with the payload `payload`.\n * Phoenix receives this in the `handle_in(event, payload, socket)`\n * function. if phoenix replies or it times out (default 10000ms),\n * then optionally the reply can be received.\n *\n * @example\n * channel.push(\"event\")\n * .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n * .receive(\"error\", err => console.log(\"phoenix errored\", err))\n * .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n * @param {string} event\n * @param {Object} payload\n * @param {number} [timeout]\n * @returns {Push}\n */\n push(event, payload, timeout = this.timeout){\n payload = payload || {}\n if(!this.joinedOnce){\n throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n }\n let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n if(this.canPush()){\n pushEvent.send()\n } else {\n pushEvent.startTimeout()\n this.pushBuffer.push(pushEvent)\n }\n\n return pushEvent\n }\n\n /** Leaves the channel\n *\n * Unsubscribes from server events, and\n * instructs channel to terminate on server\n *\n * Triggers onClose() hooks\n *\n * To receive leave acknowledgements, use the `receive`\n * hook to bind to the server ack, ie:\n *\n * @example\n * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n *\n * @param {integer} timeout\n * @returns {Push}\n */\n leave(timeout = this.timeout){\n this.rejoinTimer.reset()\n this.joinPush.cancelTimeout()\n\n this.state = CHANNEL_STATES.leaving\n let onClose = () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n this.trigger(CHANNEL_EVENTS.close, \"leave\")\n }\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n leavePush.receive(\"ok\", () => onClose())\n .receive(\"timeout\", () => onClose())\n leavePush.send()\n if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n return leavePush\n }\n\n /**\n * Overridable message hook\n *\n * Receives all events for specialized message handling\n * before dispatching to the channel callbacks.\n *\n * Must return the payload, modified or unmodified\n * @param {string} event\n * @param {Object} payload\n * @param {integer} ref\n * @returns {Object}\n */\n onMessage(_event, payload, _ref){ return payload }\n\n /**\n * @private\n */\n isMember(topic, event, payload, joinRef){\n if(this.topic !== topic){ return false }\n\n if(joinRef && joinRef !== this.joinRef()){\n if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n return false\n } else {\n return true\n }\n }\n\n /**\n * @private\n */\n joinRef(){ return this.joinPush.ref }\n\n /**\n * @private\n */\n rejoin(timeout = this.timeout){\n if(this.isLeaving()){ return }\n this.socket.leaveOpenTopic(this.topic)\n this.state = CHANNEL_STATES.joining\n this.joinPush.resend(timeout)\n }\n\n /**\n * @private\n */\n trigger(event, payload, ref, joinRef){\n let handledPayload = this.onMessage(event, payload, ref, joinRef)\n if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n for(let i = 0; i < eventBindings.length; i++){\n let bind = eventBindings[i]\n bind.callback(handledPayload, ref, joinRef || this.joinRef())\n }\n }\n\n /**\n * @private\n */\n replyEventName(ref){ return `chan_reply_${ref}` }\n\n /**\n * @private\n */\n isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n /**\n * @private\n */\n isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n /**\n * @private\n */\n isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n /**\n * @private\n */\n isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n /**\n * @private\n */\n isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n global,\n XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n static request(method, endPoint, headers, body, timeout, ontimeout, callback){\n if(global.XDomainRequest){\n let req = new global.XDomainRequest() // IE8, IE9\n return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n } else if(global.XMLHttpRequest){\n let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)\n } else if(global.fetch && global.AbortController){\n // Fetch with AbortController for modern browsers\n return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)\n } else {\n throw new Error(\"No suitable XMLHttpRequest implementation found\")\n }\n }\n\n static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){\n let options = {\n method,\n headers,\n body,\n }\n let controller = null\n let timeoutId = null\n if(timeout){\n controller = new AbortController()\n timeoutId = setTimeout(() => controller.abort(), timeout)\n options.signal = controller.signal\n }\n global.fetch(endPoint, options)\n .then(response => response.text())\n .then(data => this.parseJSON(data))\n .then(data => {\n if(timeoutId){ clearTimeout(timeoutId) }\n callback && callback(data)\n })\n .catch(err => {\n if(timeoutId){ clearTimeout(timeoutId) }\n if(err.name === \"AbortError\" && ontimeout){\n ontimeout()\n } else {\n callback && callback(null)\n }\n })\n return controller\n }\n\n static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n req.timeout = timeout\n req.open(method, endPoint)\n req.onload = () => {\n let response = this.parseJSON(req.responseText)\n callback && callback(response)\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n // Work around bug in IE9 that requires an attached onprogress handler\n req.onprogress = () => { }\n\n req.send(body)\n return req\n }\n\n static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){\n req.open(method, endPoint, true)\n req.timeout = timeout\n for(let [key, value] of Object.entries(headers)){\n req.setRequestHeader(key, value)\n }\n req.onerror = () => callback && callback(null)\n req.onreadystatechange = () => {\n if(req.readyState === XHR_STATES.complete && callback){\n let response = this.parseJSON(req.responseText)\n callback(response)\n }\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n req.send(body)\n return req\n }\n\n static parseJSON(resp){\n if(!resp || resp === \"\"){ return null }\n\n try {\n return JSON.parse(resp)\n } catch {\n console && console.log(\"failed to parse JSON response\", resp)\n return null\n }\n }\n\n static serialize(obj, parentKey){\n let queryStr = []\n for(var key in obj){\n if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n let paramKey = parentKey ? `${parentKey}[${key}]` : key\n let paramVal = obj[key]\n if(typeof paramVal === \"object\"){\n queryStr.push(this.serialize(paramVal, paramKey))\n } else {\n queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n }\n }\n return queryStr.join(\"&\")\n }\n\n static appendParams(url, params){\n if(Object.keys(params).length === 0){ return url }\n\n let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n return `${url}${prefix}${this.serialize(params)}`\n }\n}\n", "import {\n SOCKET_STATES,\n TRANSPORTS,\n AUTH_TOKEN_PREFIX,\n MAX_LONGPOLL_BATCH_SIZE\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBufferToBase64 = (buffer) => {\n let binary = \"\"\n let bytes = new Uint8Array(buffer)\n let len = bytes.byteLength\n for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }\n return btoa(binary)\n}\n\nexport default class LongPoll {\n\n constructor(endPoint, protocols){\n // we only support subprotocols for authToken\n // [\"phoenix\", \"base64url.bearer.phx.BASE64_ENCODED_TOKEN\"]\n if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){\n this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))\n }\n this.endPoint = null\n this.token = null\n this.skipHeartbeat = true\n // the pending requests and queued messages, which are cancelled when we stop\n this.reqs = new Set()\n this.messageTimers = new Set()\n this.awaitingBatchAck = false\n this.currentBatch = null\n this.currentBatchTimer = null\n this.batchBuffer = []\n this.onopen = function (){ } // noop\n this.onerror = function (){ } // noop\n this.onmessage = function (){ } // noop\n this.onclose = function (){ } // noop\n this.pollEndpoint = this.normalizeEndpoint(endPoint)\n this.readyState = SOCKET_STATES.connecting\n // we must wait for the caller to finish setting up our callbacks and timeout properties,\n // and it might close us before that, e.g. when disconnecting right after connecting\n setTimeout(() => this.poll(), 0)\n }\n\n normalizeEndpoint(endPoint){\n return (endPoint\n .replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")\n .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n }\n\n endpointURL(){\n return this.pollEndpoint\n }\n\n closeAndRetry(error, code, reason, wasClean){\n if(!this.isActive()){ return }\n // like a failed WebSocket, we are no longer open when we emit the error and then the close.\n // We are connecting again before their callbacks run, so that they can still close us for\n // good, e.g. when they disconnect the socket\n this.stop(SOCKET_STATES.connecting)\n this.onerror(error)\n if(this.isActive()){ this.emitClose(code, reason, wasClean) }\n }\n\n ontimeout(){\n this.closeAndRetry(\"timeout\", 1005, \"timeout\", false)\n }\n\n isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }\n\n poll(){\n // onopen can close us before the next poll, just as the caller can close us\n // before the constructor's deferred first poll\n if(!this.isActive()){ return }\n const headers = {\"Accept\": \"application/json\"}\n if(this.authToken){\n headers[\"X-Phoenix-AuthToken\"] = this.authToken\n }\n this.ajax(\"GET\", headers, null, () => this.ontimeout(), resp => {\n if(resp){\n var {status, token, messages} = resp\n if(status === 410 && this.token !== null){\n // In case we already have a token, this means that our existing session\n // is gone. We fail so that the client rejoins its channels.\n this.closeAndRetry(410, 3410, \"session_gone\", false)\n return\n }\n this.token = token\n } else {\n status = 0\n }\n\n switch(status){\n case 200:\n messages.forEach(msg => {\n // Tasks are what things like event handlers, setTimeout callbacks,\n // promise resolves and more are run within.\n // In modern browsers, there are two different kinds of tasks,\n // microtasks and macrotasks.\n // Microtasks are mainly used for Promises, while macrotasks are\n // used for everything else.\n // Microtasks always have priority over macrotasks. If the JS engine\n // is looking for a task to run, it will always try to empty the\n // microtask queue before attempting to run anything from the\n // macrotask queue.\n //\n // For the WebSocket transport, messages always arrive in their own\n // event. This means that if any promises are resolved from within,\n // their callbacks will always finish execution by the time the\n // next message event handler is run.\n //\n // In order to emulate this behaviour, we need to make sure each\n // onmessage handler is run within its own macrotask.\n //\n // We might stop before that, e.g. when a POST times out or a message\n // handler disconnects, which cancels the remaining messages.\n const timer = setTimeout(() => {\n this.messageTimers.delete(timer)\n this.onmessage({data: msg})\n }, 0)\n this.messageTimers.add(timer)\n })\n this.poll()\n break\n case 204:\n this.poll()\n break\n case 410:\n this.readyState = SOCKET_STATES.open\n this.onopen({})\n this.poll()\n break\n case 403:\n this.onerror(403)\n this.close(1008, \"forbidden\", false)\n break\n case 0:\n case 500:\n this.closeAndRetry(500, 1011, \"internal server error\", false)\n break\n default: throw new Error(`unhandled poll status ${status}`)\n }\n })\n }\n\n // we collect all pushes within the current event loop by\n // setTimeout 0, which optimizes back-to-back procedural\n // pushes against an empty buffer\n\n send(body){\n if(typeof(body) !== \"string\"){ body = arrayBufferToBase64(body) }\n if(this.currentBatch){\n this.currentBatch.push(body)\n } else if(this.awaitingBatchAck){\n this.batchBuffer.push(body)\n } else {\n this.currentBatch = [body]\n this.currentBatchTimer = setTimeout(() => {\n this.batchSend(this.currentBatch)\n this.currentBatch = null\n }, 0)\n }\n }\n\n batchSend(messages, offset = 0){\n this.awaitingBatchAck = true\n const next = offset + MAX_LONGPOLL_BATCH_SIZE\n const batch = messages.slice(offset, next)\n this.ajax(\"POST\", {\"Content-Type\": \"application/x-ndjson\"}, batch.join(\"\\n\"), () => this.ontimeout(), resp => {\n if(!resp || resp.status !== 200){\n // this calls stop which already clears awaitingBatchAck\n this.closeAndRetry(resp && resp.status, 1011, \"internal server error\", false)\n } else if(next < messages.length){\n this.batchSend(messages, next)\n } else if(this.batchBuffer.length > 0){\n this.batchSend(this.batchBuffer)\n this.batchBuffer = []\n } else {\n this.awaitingBatchAck = false\n }\n })\n }\n\n close(code, reason, wasClean){\n this.stop(SOCKET_STATES.closed)\n this.emitClose(code, reason, wasClean)\n }\n\n /**\n * Cancels the pending requests, queued messages and outgoing batches.\n */\n stop(readyState){\n this.readyState = readyState\n // the requests are no longer ours before we abort them, as aborting can invoke their\n // callbacks, synchronously for XHR and asynchronously for fetch\n const reqs = this.reqs\n this.reqs = new Set()\n for(let {req} of reqs){ req && req.abort() }\n for(let timer of this.messageTimers){ clearTimeout(timer) }\n this.messageTimers.clear()\n this.batchBuffer = []\n this.awaitingBatchAck = false\n clearTimeout(this.currentBatchTimer)\n this.currentBatchTimer = null\n this.currentBatch = null\n }\n\n /**\n * Emits the close event. Like a WebSocket closed without a code, it reports 1005 (no status)\n * unless one is given, as 1000 would look like the server closing the connection normally.\n */\n emitClose(code = 1005, reason, wasClean = true){\n let opts = {code, reason, wasClean}\n if(typeof(CloseEvent) !== \"undefined\"){\n this.onclose(new CloseEvent(\"close\", opts))\n } else {\n this.onclose(opts)\n }\n }\n\n ajax(method, headers, body, onCallerTimeout, callback){\n // the request is ours until it completes or we stop, and the callbacks of a request\n // that is not ours anymore are ignored. It is ours before it starts, as a request can\n // complete while it starts.\n const request = {req: null}\n this.reqs.add(request)\n let ontimeout = () => {\n if(this.reqs.delete(request)){ onCallerTimeout() }\n }\n if(this.token !== null){\n headers = Object.assign({}, headers, {\"X-Phoenix-Longpoll-Token\": this.token})\n }\n request.req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {\n if(this.reqs.delete(request)){ callback(resp) }\n })\n }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n * for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n constructor(channel, opts = {}){\n let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n this.state = Object.create(null)\n this.pendingDiffs = []\n this.channel = channel\n this.joinRef = null\n this.caller = {\n onJoin: function (){ },\n onLeave: function (){ },\n onSync: function (){ }\n }\n\n this.channel.on(events.state, newState => {\n let {onJoin, onLeave, onSync} = this.caller\n\n this.joinRef = this.channel.joinRef()\n this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n this.pendingDiffs.forEach(diff => {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n })\n this.pendingDiffs = []\n onSync()\n })\n\n this.channel.on(events.diff, diff => {\n let {onJoin, onLeave, onSync} = this.caller\n\n if(this.inPendingSyncState()){\n this.pendingDiffs.push(diff)\n } else {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n onSync()\n }\n })\n }\n\n onJoin(callback){ this.caller.onJoin = callback }\n\n onLeave(callback){ this.caller.onLeave = callback }\n\n onSync(callback){ this.caller.onSync = callback }\n\n list(by){ return Presence.list(this.state, by) }\n\n inPendingSyncState(){\n return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n }\n\n // lower-level public static API\n\n /**\n * Used to sync the list of presences on the server\n * with the client's state. An optional `onJoin` and `onLeave` callback can\n * be provided to react to changes in the client's local presences across\n * disconnects and reconnects with the server.\n *\n * @returns {Presence}\n */\n static syncState(currentState, newState, onJoin, onLeave){\n let state = this.toNullProtoObj(this.clone(currentState))\n newState = this.toNullProtoObj(newState)\n let joins = Object.create(null)\n let leaves = Object.create(null)\n\n this.map(state, (key, presence) => {\n if(!newState[key]){\n leaves[key] = presence\n }\n })\n this.map(newState, (key, newPresence) => {\n let currentPresence = state[key]\n if(currentPresence){\n let newRefs = newPresence.metas.map(m => m.phx_ref)\n let curRefs = currentPresence.metas.map(m => m.phx_ref)\n let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n if(joinedMetas.length > 0){\n joins[key] = newPresence\n joins[key].metas = joinedMetas\n }\n if(leftMetas.length > 0){\n leaves[key] = this.clone(currentPresence)\n leaves[key].metas = leftMetas\n }\n } else {\n joins[key] = newPresence\n }\n })\n return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n }\n\n /**\n *\n * Used to sync a diff of presence join and leave\n * events from the server, as they happen. Like `syncState`, `syncDiff`\n * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n * joining or leaving from a device.\n *\n * @returns {Presence}\n */\n static syncDiff(state, diff, onJoin, onLeave){\n state = this.toNullProtoObj(state)\n let {joins, leaves} = this.clone(diff)\n if(!onJoin){ onJoin = function (){ } }\n if(!onLeave){ onLeave = function (){ } }\n\n this.map(joins, (key, newPresence) => {\n let currentPresence = state[key]\n state[key] = this.clone(newPresence)\n if(currentPresence){\n let joinedRefs = state[key].metas.map(m => m.phx_ref)\n let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n state[key].metas.unshift(...curMetas)\n }\n onJoin(key, currentPresence, newPresence)\n })\n this.map(leaves, (key, leftPresence) => {\n let currentPresence = state[key]\n if(!currentPresence){ return }\n let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n currentPresence.metas = currentPresence.metas.filter(p => {\n return refsToRemove.indexOf(p.phx_ref) < 0\n })\n onLeave(key, currentPresence, leftPresence)\n if(currentPresence.metas.length === 0){\n delete state[key]\n }\n })\n return state\n }\n\n /**\n * Returns the array of presences, with selected metadata.\n *\n * @param {Object} presences\n * @param {Function} chooser\n *\n * @returns {Presence}\n */\n static list(presences, chooser){\n if(!chooser){ chooser = function (key, pres){ return pres } }\n\n return this.map(presences, (key, presence) => {\n return chooser(key, presence)\n })\n }\n\n // private\n\n static map(obj, func){\n return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n }\n\n // Presence keys are chosen on the server and may collide with\n // Object.prototype properties (\"__proto__\", \"constructor\", ...), so any\n // object indexed by presence key must not have a prototype chain\n //\n // TODO: replace the null-prototype objects with Maps in Phoenix 2.0\n // (breaking change for the lower-level static API)\n static toNullProtoObj(obj){\n if(Object.getPrototypeOf(obj) === null){ return obj }\n let cleaned = Object.create(null)\n Object.getOwnPropertyNames(obj).forEach(key => {\n cleaned[key] = obj[key]\n })\n return cleaned\n }\n\n static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n HEADER_LENGTH: 1,\n META_LENGTH: 4,\n KINDS: {push: 0, reply: 1, broadcast: 2},\n\n encode(msg, callback){\n if(msg.payload.constructor === ArrayBuffer){\n return callback(this.binaryEncode(msg))\n } else {\n let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n return callback(JSON.stringify(payload))\n }\n },\n\n decode(rawPayload, callback){\n if(rawPayload.constructor === ArrayBuffer){\n return callback(this.binaryDecode(rawPayload))\n } else {\n let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n return callback({join_ref, ref, topic, event, payload})\n }\n },\n\n // private\n\n binaryEncode(message){\n let {join_ref, ref, event, topic, payload} = message\n let encoder = new TextEncoder()\n let joinRefBytes = encoder.encode(join_ref)\n let refBytes = encoder.encode(ref)\n let topicBytes = encoder.encode(topic)\n let eventBytes = encoder.encode(event)\n\n this.assertFieldSize(joinRefBytes.byteLength, \"join_ref\")\n this.assertFieldSize(refBytes.byteLength, \"ref\")\n this.assertFieldSize(topicBytes.byteLength, \"topic\")\n this.assertFieldSize(eventBytes.byteLength, \"event\")\n\n let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength\n let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n let headerBytes = new Uint8Array(header)\n let view = new DataView(header)\n let offset = 0\n\n view.setUint8(offset++, this.KINDS.push) // kind\n view.setUint8(offset++, joinRefBytes.byteLength)\n view.setUint8(offset++, refBytes.byteLength)\n view.setUint8(offset++, topicBytes.byteLength)\n view.setUint8(offset++, eventBytes.byteLength)\n headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength\n headerBytes.set(refBytes, offset); offset += refBytes.byteLength\n headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength\n headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength\n\n var combined = new Uint8Array(header.byteLength + payload.byteLength)\n combined.set(headerBytes, 0)\n combined.set(new Uint8Array(payload), header.byteLength)\n\n return combined.buffer\n },\n\n assertFieldSize(size, name){\n if(size > 255){\n throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)\n }\n },\n\n binaryDecode(buffer){\n let view = new DataView(buffer)\n let kind = view.getUint8(0)\n let decoder = new TextDecoder()\n switch(kind){\n case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n }\n },\n\n decodePush(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let topicSize = view.getUint8(2)\n let eventSize = view.getUint8(3)\n let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n },\n\n decodeReply(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let refSize = view.getUint8(2)\n let topicSize = view.getUint8(3)\n let eventSize = view.getUint8(4)\n let offset = this.HEADER_LENGTH + this.META_LENGTH\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n offset = offset + refSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n let payload = {status: event, response: data}\n return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n },\n\n decodeBroadcast(buffer, view, decoder){\n let topicSize = view.getUint8(1)\n let eventSize = view.getUint8(2)\n let offset = this.HEADER_LENGTH + 2\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n\n return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n }\n}\n", "import {SOCKET_STATES} from \"./constants\"\n\n/**\n * @private\n *\n * A connection of the socket over a single transport instance.\n *\n * It owns everything that belongs to this connection, so that nothing about it can\n * affect a later connection: the transport events are only handled while it is the\n * socket's current connection, and its state is gone together with it.\n */\nexport default class Connection {\n /**\n * @param {Socket} socket\n * @param {Object} transport - The transport instance, for example a WebSocket\n */\n constructor(socket, transport){\n this.socket = socket\n this.transport = transport\n // the join ref of each channel whose join was sent over this connection, weakly held\n // as a connection can outlive many channels, e.g. when navigating between LiveViews\n this.joins = new WeakMap()\n // the callbacks of the pings sent over this connection by ref, as their replies can only\n // arrive over it, so they are gone together with it instead of accumulating on the socket\n this.pings = new Map()\n // set when we close the connection, as opposed to the server or the network\n this.closing = false\n // set when the socket does not want this connection anymore, e.g. on disconnect,\n // so that connecting again creates a new one instead of waiting for it\n this.ended = false\n // set once the channels were errored after the connection stopped being open\n this.channelsErrored = false\n // set once the close event was handled, as LongPoll can emit it more than once\n this.closeHandled = false\n transport.onopen = () => { if(this.isCurrent()){ socket.onConnOpen() } }\n transport.onerror = error => { if(this.isCurrent()){ socket.onConnError(error) } }\n transport.onmessage = event => { if(this.isCurrent()){ socket.onConnMessage(event) } }\n transport.onclose = event => {\n if(this.isCurrent() && !this.closeHandled){\n this.closeHandled = true\n socket.onConnClose(event)\n }\n }\n }\n\n /**\n * Whether this is the connection the socket currently uses. A replaced connection\n * can still emit events while it closes, but they no longer concern the socket.\n */\n isCurrent(){ return this.socket.connection === this }\n\n isOpen(){ return this.transport.readyState === SOCKET_STATES.open }\n\n isConnecting(){ return this.transport.readyState === SOCKET_STATES.connecting }\n\n /**\n * Whether the channel's current join was sent over this connection.\n */\n carried(channel, joinRef = channel.joinRef()){\n return this.joins.has(channel) && this.joins.get(channel) === joinRef\n }\n\n /**\n * Closes the transport. Its close event then only confirms our close.\n */\n close(code, reason){\n this.closing = true\n // closing a transport that is still connecting makes it emit an error, which is\n // caused by closing it and must not be handled as a transport error\n this.transport.onerror = function (){ } // noop\n if(code){ this.transport.close(code, reason || \"\") } else { this.transport.close() }\n }\n}\n", "import {\n global,\n phxWindow,\n CHANNEL_EVENTS,\n DEFAULT_TIMEOUT,\n DEFAULT_VSN,\n SOCKET_STATES,\n TRANSPORTS,\n WS_CLOSE_NORMAL,\n AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport {\n closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\nimport Connection from \"./connection\"\n\n/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n * `\"wss://example.com\"`\n * `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.\n * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.\n *\n * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport\n * before falling back to the LongPoll transport. Disabled by default.\n *\n * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.\n *\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the\n * socket reconnect interval, in milliseconds.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n * return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {boolean} [opts.reconnectWhenHidden] - When true, the socket also reconnects while the\n * page is hidden, for example in a background tab or on a sleeping phone.\n *\n * Defaults to false. The socket then waits until the page is visible again before it reconnects.\n * This avoids reconnect attempts that stall on mobile devices and delay the connection after the\n * device wakes up.\n *\n * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n * return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n * console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {(Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {(string|function)} [opts.authToken] - the optional authentication token to be exposed on the server\n * under the `:auth_token` connect_info key. Can be a string or a function that returns a string.\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n *\n * @param {Object} [opts.sessionStorage] - An optional Storage compatible object\n * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is\n * useful when Phoenix won't have access to `sessionStorage`. For example, This could\n * happen if a site loads a cross-domain channel in an iframe. Example usage:\n *\n * class InMemoryStorage {\n * constructor() { this.storage = {} }\n * getItem(keyName) { return this.storage[keyName] || null }\n * removeItem(keyName) { delete this.storage[keyName] }\n * setItem(keyName, keyValue) { this.storage[keyName] = keyValue }\n * }\n *\n*/\nexport default class Socket {\n constructor(endPoint, opts = {}){\n this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n this.channels = []\n this.sendBuffer = []\n this.ref = 0\n this.fallbackRef = null\n this.fallbackErrorRef = null\n this.timeout = opts.timeout || DEFAULT_TIMEOUT\n this.transport = opts.transport || global.WebSocket || LongPoll\n this.primaryPassedHealthCheck = false\n this.longPollFallbackMs = opts.longPollFallbackMs\n this.fallbackTimer = null\n this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)\n this.establishedConnections = 0\n this.defaultEncoder = Serializer.encode.bind(Serializer)\n this.defaultDecoder = Serializer.decode.bind(Serializer)\n // We start with closeWasClean true to avoid the visibility change\n // logic from connecting if the socket was never connected in the first place.\n // transportConnect sets it to false on open.\n this.closeWasClean = true\n this.binaryType = opts.binaryType || \"arraybuffer\"\n // the connection over the current transport instance, see Connection\n this.connection = null\n // changes whenever we connect, disconnect or replace the transport, for the code that needs\n // to know whether that happened while there was no connection, see replaceTransport\n this.connectClock = 1\n // the connection created when the page was shown, see handleVisibilityChange\n this.visibilityConnection = null\n if(this.transport !== LongPoll){\n this.encode = opts.encode || this.defaultEncoder\n this.decode = opts.decode || this.defaultDecoder\n } else {\n this.encode = this.defaultEncoder\n this.decode = this.defaultDecoder\n }\n let awaitingConnectionOnPageShow = null\n if(phxWindow && phxWindow.addEventListener){\n phxWindow.addEventListener(\"pagehide\", _e => {\n // an ended connection only finishes closing, e.g. after disconnect\n if(this.connection && !this.connection.ended){\n this.disconnect()\n awaitingConnectionOnPageShow = this.connectClock\n }\n })\n phxWindow.addEventListener(\"pageshow\", _e => {\n if(awaitingConnectionOnPageShow === this.connectClock){\n awaitingConnectionOnPageShow = null\n this.connect()\n }\n })\n phxWindow.addEventListener(\"visibilitychange\", () => {\n this.handleVisibilityChange()\n })\n // Chrome 149 started to not reliably fire visibilitychange after resume,\n // see https://issues.chromium.org/issues/547062449.\n phxWindow.document && phxWindow.document.addEventListener(\"resume\", () => {\n this.handleVisibilityChange()\n })\n }\n this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n this.rejoinAfterMs = (tries) => {\n if(opts.rejoinAfterMs){\n return opts.rejoinAfterMs(tries)\n } else {\n return [1000, 2000, 5000][tries - 1] || 10000\n }\n }\n this.reconnectAfterMs = (tries) => {\n if(opts.reconnectAfterMs){\n return opts.reconnectAfterMs(tries)\n } else {\n return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n }\n }\n this.reconnectWhenHidden = opts.reconnectWhenHidden || false\n this.logger = opts.logger || null\n if(!this.logger && opts.debug){\n this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }\n }\n this.longpollerTimeout = opts.longpollerTimeout || 20000\n this.params = closure(opts.params || {})\n this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n this.vsn = opts.vsn || DEFAULT_VSN\n this.heartbeatTimeoutTimer = null\n this.heartbeatTimer = null\n this.pendingHeartbeatRef = null\n this.reconnectTimer = new Timer(() => {\n // handleVisibilityChange reconnects once the page is visible again\n if(this.pageHidden && !this.reconnectWhenHidden){\n this.log(\"Not reconnecting as page is hidden!\")\n this.teardown()\n return\n }\n this.teardownAndReconnect(() => this.connect())\n }, this.reconnectAfterMs)\n this.authToken = opts.authToken && closure(opts.authToken)\n }\n\n /**\n * The transport instance of the current connection, for example a WebSocket\n */\n get conn(){ return this.connection && this.connection.transport }\n\n /**\n * @internal\n */\n get pageHidden(){\n return phxWindow && phxWindow.document ? phxWindow.document.visibilityState === \"hidden\" : false\n }\n\n /**\n * @internal\n */\n handleVisibilityChange(){\n if(this.pageHidden){\n this.visibilityConnection = null\n return\n }\n // resume and visibilitychange can both fire when the page is shown again, so we keep\n // the connection created for the first one while it is still connecting\n const connection = this.connection\n if(connection && connection === this.visibilityConnection && connection.isConnecting()){ return }\n // reconnect immediately\n if(!this.isConnected() && !this.closeWasClean){\n // a reconnect that was scheduled before would tear down the connection we create here\n this.reconnectTimer.reset()\n this.teardownAndReconnect(() => {\n this.connect()\n this.visibilityConnection = this.connection\n })\n }\n }\n\n /**\n * Returns the LongPoll transport reference\n */\n getLongPollTransport(){ return LongPoll }\n\n /**\n * Disconnects and replaces the active transport\n *\n * @param {Function} newTransport - The new transport class to instantiate\n *\n */\n replaceTransport(newTransport){\n const connectClock = ++this.connectClock\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // replaced first, as channel error callbacks below can already connect again\n this.transport = newTransport\n const connection = this.connection\n if(connection){\n const wasOpen = connection.isOpen()\n // the old transport closes asynchronously, but its events no longer concern us\n this.connection = null\n connection.ended = true\n connection.close()\n this.clearHeartbeats()\n // any in-flight pushes on the old connection are lost, so channels need to rejoin\n if(wasOpen){ this.triggerChanError(\"connection_closed\", connection) }\n }\n return connectClock\n }\n\n /**\n * Returns the socket protocol\n *\n * @returns {string}\n */\n protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n /**\n * The fully qualified socket url\n *\n * @returns {string}\n */\n endPointURL(){\n let uri = Ajax.appendParams(\n Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n if(uri.charAt(0) !== \"/\"){ return uri }\n if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n return `${this.protocol()}://${location.host}${uri}`\n }\n\n /**\n * Disconnects the socket\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n *\n * @param {Function} callback - Optional callback which is called after socket is disconnected.\n * @param {integer} code - A status code for disconnection (Optional).\n * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n */\n disconnect(callback, code, reason){\n this.connectClock++\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // connecting again creates a new connection right away instead of waiting for this one to close\n if(this.connection){ this.connection.ended = true }\n this.teardown(callback, code, reason)\n }\n\n /**\n *\n * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n *\n * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n */\n connect(params){\n if(params){\n console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n this.params = closure(params)\n }\n // the current connection is kept, including while it reconnects, until the socket is done with it\n if(this.connection && !this.connection.ended){ return }\n if(this.longPollFallbackMs && this.transport !== LongPoll){\n this.connectWithFallback(LongPoll, this.longPollFallbackMs)\n } else {\n this.transportConnect()\n }\n }\n\n /**\n * Logs the message. Override `this.logger` for specialized logging. noops by default\n * @param {string} kind\n * @param {string} msg\n * @param {Object} data\n */\n log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }\n\n /**\n * Returns true if a logger has been set on this socket.\n */\n hasLogger(){ return this.logger !== null }\n\n /**\n * Registers callbacks for connection open events\n *\n * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n *\n * @param {Function} callback\n */\n onOpen(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.open.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection close events\n * @param {Function} callback\n */\n onClose(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.close.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection error events\n *\n * @example socket.onError(function(error){ alert(\"An error occurred\") })\n *\n * @param {Function} callback\n */\n onError(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.error.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection message events\n * @param {Function} callback\n */\n onMessage(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.message.push([ref, callback])\n return ref\n }\n\n /**\n * Pings the server and invokes the callback with the RTT in milliseconds\n * @param {Function} callback\n *\n * Returns true if the ping was pushed or false if unable to be pushed.\n */\n ping(callback){\n if(!this.isConnected()){ return false }\n let ref = this.makeRef()\n let startTime = Date.now()\n this.connection.pings.set(ref, () => callback(Date.now() - startTime))\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: ref})\n return true\n }\n\n /**\n * @private\n *\n * @param {Function}\n */\n transportName(transport){\n // JavaScript minification, enabled by default in production in Phoenix\n // projects, renames symbols to reduce code size.\n // See https://esbuild.github.io/api/#keep-names.\n // This helper ensures we return the correct name for the LongPoll transport\n // even after minification. The other common transport is WebSocket, which\n // is native to browsers and does not need special handling.\n switch(transport){\n case LongPoll: return \"LongPoll\"\n default: return transport.name\n }\n }\n\n /**\n * @private\n */\n transportConnect(){\n const previous = this.connection\n this.connectClock++\n this.closeWasClean = false\n // a reconnect scheduled before would tear down the new connection, but we keep\n // its tries, which only reset once a connection opens\n clearTimeout(this.reconnectTimer.timer)\n let protocols = undefined\n // Sec-WebSocket-Protocol based token\n // (longpoll uses Authorization header instead)\n if(this.authToken){\n protocols = [\"phoenix\", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken()).replace(/=/g, \"\")}`]\n }\n const transport = new this.transport(this.endPointURL(), protocols)\n transport.binaryType = this.binaryType\n transport.timeout = this.longpollerTimeout\n this.connection = new Connection(this, transport)\n // connect can replace an ended connection that is still open while its buffer drains, but the\n // channels joined over it need to rejoin on the new connection instead of waiting for it to close\n if(previous && previous.isOpen()){ this.triggerChanError(\"connection_closed\", previous) }\n }\n\n getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }\n\n storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }\n\n connectWithFallback(fallbackTransport, fallbackThreshold = 2500){\n // the callbacks of a previous attempt must not act on this one, e.g. an attempt\n // that never opened would otherwise fall back again on the next error\n this.cancelFallback()\n let established = false\n let primaryTransport = true\n let errorRef\n let connectClock\n const isCurrent = () => connectClock === this.connectClock && !this.closeWasClean\n let fallbackTransportName = this.transportName(fallbackTransport)\n // the fallback timer is cleared whenever the attempt ends, and the callbacks below\n // only fall back while the attempt is current\n let fallback = (reason) => {\n this.log(\"transport\", `falling back to ${fallbackTransportName}...`, reason)\n // the open callback stays registered to remember the fallback once it connects\n this.off([errorRef])\n primaryTransport = false\n const replaceClock = this.replaceTransport(fallbackTransport)\n if(replaceClock !== this.connectClock){ return }\n this.transportConnect()\n }\n if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback(\"memorized\") }\n\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n\n errorRef = this.fallbackErrorRef = this.onError(reason => {\n if(!isCurrent()){ return }\n this.log(\"transport\", \"error\", reason)\n if(primaryTransport && !established){\n clearTimeout(this.fallbackTimer)\n fallback(reason)\n }\n })\n this.fallbackRef = this.onOpen(() => {\n if(!primaryTransport){\n // reconnects keep using the fallback transport, so a later successful connection\n // must still be remembered, unless an earlier callback disconnected it\n if(this.transport !== fallbackTransport || this.closeWasClean){ return }\n let fallbackTransportName = this.transportName(fallbackTransport)\n // only memorize LP if we never connected to primary\n if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, \"true\") }\n return this.log(\"transport\", `established ${fallbackTransportName} fallback`)\n }\n if(!isCurrent()){ return }\n established = true\n // if we've established primary, give the fallback a new period to attempt ping\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n this.ping(rtt => {\n this.log(\"transport\", \"connected to primary after\", rtt)\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n })\n })\n this.transportConnect()\n connectClock = this.connectClock\n }\n\n /**\n * @private\n *\n * Stops the fallback of the current connection attempt, see connectWithFallback\n */\n cancelFallback(){\n clearTimeout(this.fallbackTimer)\n this.off([this.fallbackRef, this.fallbackErrorRef])\n }\n\n clearHeartbeats(){\n clearTimeout(this.heartbeatTimer)\n clearTimeout(this.heartbeatTimeoutTimer)\n // a late reply to the pending heartbeat must not start the heartbeats again\n this.pendingHeartbeatRef = null\n }\n\n onConnOpen(){\n if(this.hasLogger()) this.log(\"transport\", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)\n this.closeWasClean = false\n this.establishedConnections++\n // the health-check ping has to leave before the buffered join. The transport\n // process blocks in Channel.Server.join until the join returns, so a ping\n // sent after the join is not answered while the mount runs, and the fallback\n // timer then drops a socket that is fine.\n this.pingBeforeBufferedJoin()\n this.flushSendBuffer()\n this.reconnectTimer.reset()\n this.resetHeartbeat()\n this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n }\n\n /**\n * @private\n *\n * Sends the fallback health check before flushSendBuffer. No-op unless a\n * fallback attempt is waiting on the primary transport.\n */\n pingBeforeBufferedJoin(){\n if(!this.fallbackTimer || this.transport === LongPoll){ return }\n this.ping(() => this.passPrimaryHealthCheck())\n }\n\n /**\n * @private\n *\n * The primary transport answered. Later frames on this connection also call\n * this, because a frame is the proof the ping was waiting for.\n */\n passPrimaryHealthCheck(){\n if(this.transport === LongPoll){ return }\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = null\n }\n\n /**\n * @private\n */\n\n heartbeatTimeout(){\n if(this.pendingHeartbeatRef){\n const connection = this.connection\n this.pendingHeartbeatRef = null\n if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n this.closeWasClean = false\n // the connection is presumed dead, so we close it right away instead of waiting for its\n // buffer to drain, which also ensures that it is no longer considered connected when the\n // channels error (LiveView would report a server error otherwise)\n if(connection){ connection.close(WS_CLOSE_NORMAL, \"heartbeat timeout\") }\n // some transports emit their close event synchronously, and callbacks of it as well as\n // channel error callbacks can disconnect or connect, which takes precedence over reconnecting\n if(!this.stillUses(connection)){ return }\n this.triggerChanError(\"heartbeat_timeout\")\n if(!this.stillUses(connection)){ return }\n this.teardownAndReconnect(() => this.reconnectTimer.scheduleTimeout())\n }\n }\n\n resetHeartbeat(){\n if(this.conn && this.conn.skipHeartbeat){ return }\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n /**\n * @private\n *\n * Tears down the current connection to reconnect afterwards, unless the socket was\n * disconnected or connected again in the meantime, which takes precedence.\n */\n teardownAndReconnect(reconnect){\n const connection = this.connection\n const connectClock = this.connectClock\n this.teardown(() => {\n // the socket was disconnected or connected again in the meantime, which the connection does not\n // tell once an overlapping teardown gave up on it\n if(connectClock !== this.connectClock || (connection && connection.ended)){ return }\n reconnect()\n })\n }\n\n /**\n * @private\n *\n * Whether the socket still uses the connection and did not give up on it. User callbacks\n * can disconnect or connect, so code continuing after them checks this first.\n */\n stillUses(connection){\n return this.connection === connection && !(connection && connection.ended)\n }\n\n teardown(callback, code, reason){\n // If someone calls connect before we finish tearing down,\n // we create a new connection, but we still want to finish tearing down the old one.\n const connection = this.connection\n if(!connection){\n return callback && callback()\n }\n // onConnClose is not called when the close event arrives late, so we stop the\n // heartbeats here, as a heartbeat timeout would otherwise reconnect the socket\n this.clearHeartbeats()\n\n this.waitForBufferDone(connection.transport, () => {\n // a connection we already closed, e.g. on a heartbeat timeout or in an\n // overlapping teardown, also errored its channels already\n if(!connection.closing){\n const wasOpened = !connection.isConnecting()\n connection.close(code, reason)\n // channels joined over this connection need to rejoin on the next one, but we cannot rely\n // on onConnClose for that, as the close event may arrive after we stopped waiting for it\n // or after a new connection already replaced this one\n if(wasOpened){ this.triggerChanError(\"connection_closed\", connection) }\n }\n\n this.waitForSocketClosed(connection.transport, () => {\n if(this.connection === connection){ this.connection = null }\n callback && callback()\n })\n })\n }\n\n waitForBufferDone(conn, callback, tries = 1){\n // there is no point in waiting for a conn that is already closing\n if(tries === 5 || !conn.bufferedAmount || conn.readyState !== SOCKET_STATES.open){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForBufferDone(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n waitForSocketClosed(conn, callback, tries = 1){\n if(tries === 5 || conn.readyState === SOCKET_STATES.closed){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForSocketClosed(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n onConnClose(event){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n this.clearHeartbeats()\n // we only close a connection to disconnect or to reconnect, which the code closing it takes\n // care of, so its close event only confirms our close. Any other close is reconnected, whatever\n // its code, as the server, proxies and load balancers also close with 1000 (normal closure)\n const closedByUs = !!(connection && connection.closing)\n // channel callbacks may disconnect or create a new connection, so finish cleaning up\n // this connection first and do not schedule a reconnect for its replacement\n this.triggerChanError(\"connection_closed\", connection)\n if(this.stillUses(connection) && !closedByUs && !this.closeWasClean){\n this.reconnectTimer.scheduleTimeout()\n }\n this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n }\n\n /**\n * @private\n */\n onConnError(error){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"error\", error)\n let transportBefore = this.transport\n let establishedBefore = this.establishedConnections\n this.stateChangeCallbacks.error.forEach(([, callback]) => {\n callback(error, transportBefore, establishedBefore)\n })\n // callbacks can replace the connection, in which case only its own joins are errored\n this.triggerChanError(\"connection_error\", connection)\n }\n\n /**\n * @private\n *\n * Errors the channels, as they need to rejoin once the given connection stopped being open.\n */\n triggerChanError(reason, connection = this.connection){\n // several places notice that a connection stopped being open (onConnError, onConnClose,\n // teardown and heartbeatTimeout). After the first, only channels whose join it carried\n // are errored, as error callbacks may have joined channels that join over another connection.\n let erroredBefore = false\n if(connection && !connection.isOpen()){\n erroredBefore = connection.channelsErrored\n connection.channelsErrored = true\n }\n const joins = this.channels.map(channel => [channel, channel.joinRef()])\n joins.forEach(([channel, joinRef]) => {\n // an earlier error callback can rejoin a later channel, so check its join\n // again immediately before dispatching rather than filtering the list first\n if(channel.joinRef() !== joinRef){ return }\n // the joins of a replaced connection are not the only ones anymore\n if(connection && (erroredBefore || !connection.isCurrent()) && !connection.carried(channel, joinRef)){ return }\n if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n channel.trigger(CHANNEL_EVENTS.error, {source: \"transport\", reason})\n }\n })\n }\n\n /**\n * @returns {string}\n */\n connectionState(){\n switch(this.conn && this.conn.readyState){\n case SOCKET_STATES.connecting: return \"connecting\"\n case SOCKET_STATES.open: return \"open\"\n case SOCKET_STATES.closing: return \"closing\"\n default: return \"closed\"\n }\n }\n\n /**\n * @returns {boolean}\n */\n isConnected(){ return this.connectionState() === \"open\" }\n\n /**\n * @private\n *\n * @param {Channel}\n */\n remove(channel){\n this.off(channel.stateChangeRefs)\n this.channels = this.channels.filter(c => c !== channel)\n }\n\n /**\n * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n *\n * @param {refs} - list of refs returned by calls to\n * `onOpen`, `onClose`, `onError,` and `onMessage`\n */\n off(refs){\n for(let key in this.stateChangeCallbacks){\n this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n return refs.indexOf(ref) === -1\n })\n }\n }\n\n /**\n * Initiates a new channel for the given topic\n *\n * @param {string} topic\n * @param {Object} chanParams - Parameters for the channel\n * @returns {Channel}\n */\n channel(topic, chanParams = {}){\n let chan = new Channel(topic, chanParams, this)\n this.channels.push(chan)\n return chan\n }\n\n /**\n * @param {Object} data\n * @param {Channel} [channel] - The channel the push belongs to, if any\n */\n push(data, channel){\n if(this.hasLogger()){\n let {topic, event, payload, ref, join_ref} = data\n this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n }\n\n if(this.isConnected()){\n this.send(data, channel)\n } else {\n this.sendBuffer.push(() => {\n // the channel may have errored or left while the push was buffered, e.g. when the\n // connection failed before opening, in which case its rejoin supersedes the buffered join\n if(!this.isOutdated(data, channel)){ this.send(data, channel) }\n })\n }\n }\n\n /**\n * @private\n */\n send(data, channel){\n const connection = this.connection\n // the join counts as carried by this connection while it is still encoded, so that the\n // channel rejoins when the connection is torn down before the encoding finished\n if(channel && data.event === CHANNEL_EVENTS.join){ connection.joins.set(channel, data.join_ref) }\n this.encode(data, result => {\n // let encodes finish while this connection drains, but never send on a\n // replacement or after the original connection has started closing\n if(!connection.isCurrent() || !connection.isOpen()){ return }\n // a leave still needs to reach the server after its channel was removed locally\n if(data.event !== CHANNEL_EVENTS.leave && this.isOutdated(data, channel)){ return }\n connection.transport.send(result)\n })\n }\n\n /**\n * @private\n *\n * Whether the push was made for an earlier join of its channel, or the channel left since.\n */\n isOutdated(data, channel){\n return !!channel && (data.join_ref !== channel.joinRef() || channel.isClosed())\n }\n\n /**\n * Return the next message ref, accounting for overflows\n * @returns {string}\n */\n makeRef(){\n let newRef = this.ref + 1\n if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n return this.ref.toString()\n }\n\n sendHeartbeat(){\n if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n this.pendingHeartbeatRef = this.makeRef()\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n }\n\n flushSendBuffer(){\n if(this.isConnected() && this.sendBuffer.length > 0){\n this.sendBuffer.forEach(callback => callback())\n this.sendBuffer = []\n }\n }\n\n onConnMessage(rawMessage){\n const connection = this.connection\n const wasOpen = connection && connection.isOpen()\n this.decode(rawMessage.data, msg => {\n // the transport event can be current even though its asynchronous decode is not, as the\n // connection may have been replaced or closed in the meantime (browsers also drop messages\n // that arrive once a WebSocket is closing)\n if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return }\n // a frame on this connection is the health check. The ping reply is one\n // such frame, and so is anything the server already sent.\n if(this.fallbackTimer){ this.passPrimaryHealthCheck() }\n let {topic, event, payload, ref, join_ref} = msg\n if(ref && ref === this.pendingHeartbeatRef){\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n // removing a channel or callback while dispatching replaces the list instead of\n // changing it, so we iterate over the lists from before to not skip any of them\n const channels = this.channels\n for(let i = 0; i < channels.length; i++){\n const channel = channels[i]\n if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n channel.trigger(event, payload, ref, join_ref)\n }\n\n const callbacks = this.stateChangeCallbacks.message\n for(let i = 0; i < callbacks.length; i++){\n let [, callback] = callbacks[i]\n callback(msg)\n }\n\n const ping = ref && connection.pings.get(ref)\n if(ping){\n connection.pings.delete(ref)\n ping()\n }\n })\n }\n\n leaveOpenTopic(topic){\n let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n if(dupChannel){\n if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n dupChannel.leave()\n }\n }\n}\n"], - "mappings": "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", + "sourcesContent": ["/**\n * Phoenix Channels JavaScript client\n *\n * ## Socket Connection\n *\n * A single connection is established to the server and\n * channels are multiplexed over the connection.\n * Connect to the server using the `Socket` class:\n *\n * ```javascript\n * let socket = new Socket(\"/socket\", {params: {userToken: \"123\"}})\n * socket.connect()\n * ```\n *\n * The `Socket` constructor takes the mount point of the socket,\n * the authentication params, as well as options that can be found in\n * the Socket docs, such as configuring the `LongPoll` transport, and\n * heartbeat.\n *\n * ## Channels\n *\n * Channels are isolated, concurrent processes on the server that\n * subscribe to topics and broker events between the client and server.\n * To join a channel, you must provide the topic, and channel params for\n * authorization. Here's an example chat room example where `\"new_msg\"`\n * events are listened for, messages are pushed to the server, and\n * the channel is joined with ok/error/timeout matches:\n *\n * ```\n * let channel = socket.channel(\"room:123\", {token: roomToken})\n * channel.on(\"new_msg\", msg => console.log(\"Got message\", msg) )\n * $input.onEnter( e => {\n * channel.push(\"new_msg\", {body: e.target.val}, 10000)\n * .receive(\"ok\", (msg) => console.log(\"created message\", msg) )\n * .receive(\"error\", (reasons) => console.log(\"create failed\", reasons) )\n * .receive(\"timeout\", () => console.log(\"Networking issue...\") )\n * })\n *\n * channel.join()\n * .receive(\"ok\", ({messages}) => console.log(\"catching up\", messages) )\n * .receive(\"error\", ({reason}) => console.log(\"failed join\", reason) )\n * .receive(\"timeout\", () => console.log(\"Networking issue. Still waiting...\"))\n *```\n *\n * ## Joining\n *\n * Creating a channel with `socket.channel(topic, params)`, binds the params to\n * `channel.params`, which are sent up on `channel.join()`.\n * Subsequent rejoins will send up the modified params for\n * updating authorization params, or passing up last_message_id information.\n * Successful joins receive an \"ok\" status, while unsuccessful joins\n * receive \"error\".\n *\n * With the default serializers and WebSocket transport, JSON text frames are\n * used for pushing a JSON object literal. If an `ArrayBuffer` instance is provided,\n * binary encoding will be used and the message will be sent with the binary\n * opcode.\n *\n * *Note*: binary messages are only supported on the WebSocket transport.\n *\n * ## Duplicate Join Subscriptions\n *\n * While the client may join any number of topics on any number of channels,\n * the client may only hold a single subscription for each unique topic at any\n * given time. When attempting to create a duplicate subscription,\n * the server will close the existing channel, log a warning, and\n * spawn a new channel for the topic. The client will have their\n * `channel.onClose` callbacks fired for the existing channel, and the new\n * channel join will have its receive hooks processed as normal.\n *\n * ## Pushing Messages\n *\n * From the previous example, we can see that pushing messages to the server\n * can be done with `channel.push(eventName, payload)` and we can optionally\n * receive responses from the push. Additionally, we can use\n * `receive(\"timeout\", callback)` to abort waiting for our other `receive` hooks\n * and take action after some period of waiting. The default timeout is 10000ms.\n *\n *\n * ## Socket Hooks\n *\n * Lifecycle events of the multiplexed connection can be hooked into via\n * `socket.onError()` and `socket.onClose()` events, ie:\n *\n * ```\n * socket.onError( () => console.log(\"there was an error with the connection!\") )\n * socket.onClose( () => console.log(\"the connection dropped\") )\n * ```\n *\n *\n * ## Channel Hooks\n *\n * For each joined channel, you can bind to `onError` and `onClose` events\n * to monitor the channel lifecycle, ie:\n *\n * ```\n * channel.onError( () => console.log(\"there was an error!\") )\n * channel.onClose( () => console.log(\"the channel has gone away gracefully\") )\n * ```\n *\n * ### onError hooks\n *\n * `onError` hooks are invoked if the socket connection drops, or the channel\n * crashes on the server. In either case, a channel rejoin is attempted\n * automatically in an exponential backoff manner.\n *\n * ### onClose hooks\n *\n * `onClose` hooks are invoked only in two cases. 1) the channel explicitly\n * closed on the server, or 2). The client explicitly closed, by calling\n * `channel.leave()`\n *\n *\n * ## Presence\n *\n * The `Presence` object provides features for syncing presence information\n * from the server with the client and handling presences joining and leaving.\n *\n * ### Syncing state from the server\n *\n * To sync presence state from the server, first instantiate an object and\n * pass your channel in to track lifecycle events:\n *\n * ```\n * let channel = socket.channel(\"some:topic\")\n * let presence = new Presence(channel)\n * ```\n *\n * Next, use the `presence.onSync` callback to react to state changes\n * from the server. For example, to render the list of users every time\n * the list changes, you could write:\n *\n * ```\n * presence.onSync(() => {\n * myRenderUsersFunction(presence.list())\n * })\n * ```\n *\n * ### Listing Presences\n *\n * `presence.list` is used to return a list of presence information\n * based on the local state of metadata. By default, all presence\n * metadata is returned, but a `listBy` function can be supplied to\n * allow the client to select which metadata to use for a given presence.\n * For example, you may have a user online from different devices with\n * a metadata status of \"online\", but they have set themselves to \"away\"\n * on another device. In this case, the app may choose to use the \"away\"\n * status for what appears on the UI. The example below defines a `listBy`\n * function which prioritizes the first metadata which was registered for\n * each user. This could be the first tab they opened, or the first device\n * they came online from:\n *\n * ```\n * let listBy = (id, {metas: [first, ...rest]}) => {\n * first.count = rest.length + 1 // count of this user's presences\n * first.id = id\n * return first\n * }\n * let onlineUsers = presence.list(listBy)\n * ```\n *\n * ### Handling individual presence join and leave events\n *\n * The `presence.onJoin` and `presence.onLeave` callbacks can be used to\n * react to individual presences joining and leaving the app. For example:\n *\n * ```\n * let presence = new Presence(channel)\n *\n * // detect if user has joined for the 1st time or from another tab/device\n * presence.onJoin((id, current, newPres) => {\n * if(!current){\n * console.log(\"user has entered for the first time\", newPres)\n * } else {\n * console.log(\"user additional presence\", newPres)\n * }\n * })\n *\n * // detect if user has left from all tabs/devices, or is still present\n * presence.onLeave((id, current, leftPres) => {\n * if(current.metas.length === 0){\n * console.log(\"user has left from all devices\", leftPres)\n * } else {\n * console.log(\"user left from a device\", leftPres)\n * }\n * })\n * // receive presence data from server\n * presence.onSync(() => {\n * displayUsers(presence.list())\n * })\n * ```\n * @module phoenix\n */\n\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Presence from \"./presence\"\nimport Serializer from \"./serializer\"\nimport Socket from \"./socket\"\n\nexport {\n Channel,\n LongPoll,\n Presence,\n Serializer,\n Socket\n}\n", "// wraps value in closure or returns closure\nexport let closure = (value) => {\n if(typeof value === \"function\"){\n return value\n } else {\n let closure = function (){ return value }\n return closure\n }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || globalThis\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const MAX_LONGPOLL_BATCH_SIZE = 100;\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n closed: \"closed\",\n errored: \"errored\",\n joined: \"joined\",\n joining: \"joining\",\n leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n close: \"phx_close\",\n error: \"phx_error\",\n join: \"phx_join\",\n reply: \"phx_reply\",\n leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n longpoll: \"longpoll\",\n websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n complete: 4\n}\nexport const AUTH_TOKEN_PREFIX = \"base64url.bearer.phx.\"\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n constructor(channel, event, payload, timeout){\n this.channel = channel\n this.event = event\n this.payload = payload || function (){ return {} }\n this.receivedResp = null\n this.timeout = timeout\n this.timeoutTimer = null\n this.recHooks = []\n this.sent = false\n }\n\n /**\n *\n * @param {number} timeout\n */\n resend(timeout){\n this.timeout = timeout\n this.reset()\n this.send()\n }\n\n /**\n *\n */\n send(){\n if(this.hasReceived(\"timeout\")){ return }\n this.startTimeout()\n this.sent = true\n this.channel.socket.push({\n topic: this.channel.topic,\n event: this.event,\n payload: this.payload(),\n ref: this.ref,\n join_ref: this.channel.joinRef()\n }, this.channel)\n }\n\n /**\n *\n * @param {*} status\n * @param {*} callback\n */\n receive(status, callback){\n if(this.hasReceived(status)){\n callback(this.receivedResp.response)\n }\n\n this.recHooks.push({status, callback})\n return this\n }\n\n /**\n * @private\n */\n reset(){\n this.cancelRefEvent()\n this.ref = null\n this.refEvent = null\n this.receivedResp = null\n this.sent = false\n }\n\n /**\n * @private\n */\n matchReceive({status, response, _ref}){\n this.recHooks.filter(h => h.status === status)\n .forEach(h => h.callback(response))\n }\n\n /**\n * @private\n */\n cancelRefEvent(){\n if(!this.refEvent){ return }\n this.channel.off(this.refEvent)\n }\n\n /**\n * @private\n */\n cancelTimeout(){\n clearTimeout(this.timeoutTimer)\n this.timeoutTimer = null\n }\n\n /**\n * @private\n */\n startTimeout(){\n if(this.timeoutTimer){ this.cancelTimeout() }\n this.cancelRefEvent()\n this.ref = this.channel.socket.makeRef()\n this.refEvent = this.channel.replyEventName(this.ref)\n\n this.channel.on(this.refEvent, payload => {\n this.cancelRefEvent()\n this.cancelTimeout()\n this.receivedResp = payload\n this.matchReceive(payload)\n })\n\n this.timeoutTimer = setTimeout(() => {\n this.trigger(\"timeout\", {})\n }, this.timeout)\n }\n\n /**\n * @private\n */\n hasReceived(status){\n return this.receivedResp && this.receivedResp.status === status\n }\n\n /**\n * @private\n */\n trigger(status, response){\n this.channel.trigger(this.refEvent, {status, response})\n }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n * return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n constructor(callback, timerCalc){\n this.callback = callback\n this.timerCalc = timerCalc\n this.timer = null\n this.tries = 0\n }\n\n reset(){\n this.tries = 0\n clearTimeout(this.timer)\n }\n\n /**\n * Cancels any previous scheduleTimeout and schedules callback\n */\n scheduleTimeout(){\n clearTimeout(this.timer)\n\n this.timer = setTimeout(() => {\n this.tries = this.tries + 1\n this.callback()\n }, this.timerCalc(this.tries + 1))\n }\n}\n", "import {closure} from \"./utils\"\nimport {\n CHANNEL_EVENTS,\n CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n constructor(topic, params, socket){\n this.state = CHANNEL_STATES.closed\n this.topic = topic\n this.params = closure(params || {})\n this.socket = socket\n this.bindings = []\n this.bindingRef = 0\n this.timeout = this.socket.timeout\n this.joinedOnce = false\n this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n this.pushBuffer = []\n this.stateChangeRefs = []\n\n this.rejoinTimer = new Timer(() => {\n if(this.socket.isConnected()){ this.rejoin() }\n }, this.socket.rejoinAfterMs)\n this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n this.stateChangeRefs.push(this.socket.onOpen(() => {\n this.rejoinTimer.reset()\n if(this.isErrored()){ this.rejoin() }\n })\n )\n this.joinPush.receive(\"ok\", () => {\n this.state = CHANNEL_STATES.joined\n this.rejoinTimer.reset()\n this.pushBuffer.forEach(pushEvent => pushEvent.send())\n this.pushBuffer = []\n })\n this.joinPush.receive(\"error\", () => {\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.onClose(() => {\n this.rejoinTimer.reset()\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n this.state = CHANNEL_STATES.closed\n this.socket.remove(this)\n })\n this.onError(reason => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n if(this.isJoining()){ this.joinPush.reset() }\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.joinPush.receive(\"timeout\", () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n leavePush.send()\n this.state = CHANNEL_STATES.errored\n this.joinPush.reset()\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n this.trigger(this.replyEventName(ref), payload)\n })\n }\n\n /**\n * Join the channel\n * @param {integer} timeout\n * @returns {Push}\n */\n join(timeout = this.timeout){\n if(this.joinedOnce){\n throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n } else {\n this.timeout = timeout\n this.joinedOnce = true\n this.rejoin()\n return this.joinPush\n }\n }\n\n /**\n * Hook into channel close\n * @param {Function} callback\n */\n onClose(callback){\n this.on(CHANNEL_EVENTS.close, callback)\n }\n\n /**\n * Hook into channel errors\n * @param {Function} callback\n */\n onError(callback){\n return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n }\n\n /**\n * Subscribes on channel events\n *\n * Subscription returns a ref counter, which can be used later to\n * unsubscribe the exact event listener\n *\n * @example\n * const ref1 = channel.on(\"event\", do_stuff)\n * const ref2 = channel.on(\"event\", do_other_stuff)\n * channel.off(\"event\", ref1)\n * // Since unsubscription, do_stuff won't fire,\n * // while do_other_stuff will keep firing on the \"event\"\n *\n * @param {string} event\n * @param {Function} callback\n * @returns {integer} ref\n */\n on(event, callback){\n let ref = this.bindingRef++\n this.bindings.push({event, ref, callback})\n return ref\n }\n\n /**\n * Unsubscribes off of channel events\n *\n * Use the ref returned from a channel.on() to unsubscribe one\n * handler, or pass nothing for the ref to unsubscribe all\n * handlers for the given event.\n *\n * @example\n * // Unsubscribe the do_stuff handler\n * const ref1 = channel.on(\"event\", do_stuff)\n * channel.off(\"event\", ref1)\n *\n * // Unsubscribe all handlers from event\n * channel.off(\"event\")\n *\n * @param {string} event\n * @param {integer} ref\n */\n off(event, ref){\n this.bindings = this.bindings.filter((bind) => {\n return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n })\n }\n\n /**\n * @private\n */\n canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n /**\n * Sends a message `event` to phoenix with the payload `payload`.\n * Phoenix receives this in the `handle_in(event, payload, socket)`\n * function. if phoenix replies or it times out (default 10000ms),\n * then optionally the reply can be received.\n *\n * @example\n * channel.push(\"event\")\n * .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n * .receive(\"error\", err => console.log(\"phoenix errored\", err))\n * .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n * @param {string} event\n * @param {Object} payload\n * @param {number} [timeout]\n * @returns {Push}\n */\n push(event, payload, timeout = this.timeout){\n payload = payload || {}\n if(!this.joinedOnce){\n throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n }\n let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n if(this.canPush()){\n pushEvent.send()\n } else {\n pushEvent.startTimeout()\n this.pushBuffer.push(pushEvent)\n }\n\n return pushEvent\n }\n\n /** Leaves the channel\n *\n * Unsubscribes from server events, and\n * instructs channel to terminate on server\n *\n * Triggers onClose() hooks\n *\n * To receive leave acknowledgements, use the `receive`\n * hook to bind to the server ack, ie:\n *\n * @example\n * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n *\n * @param {integer} timeout\n * @returns {Push}\n */\n leave(timeout = this.timeout){\n this.rejoinTimer.reset()\n this.joinPush.cancelTimeout()\n\n this.state = CHANNEL_STATES.leaving\n let onClose = () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n this.trigger(CHANNEL_EVENTS.close, \"leave\")\n }\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n leavePush.receive(\"ok\", () => onClose())\n .receive(\"timeout\", () => onClose())\n leavePush.send()\n if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n return leavePush\n }\n\n /**\n * Overridable message hook\n *\n * Receives all events for specialized message handling\n * before dispatching to the channel callbacks.\n *\n * Must return the payload, modified or unmodified\n * @param {string} event\n * @param {Object} payload\n * @param {integer} ref\n * @returns {Object}\n */\n onMessage(_event, payload, _ref){ return payload }\n\n /**\n * @private\n */\n isMember(topic, event, payload, joinRef){\n if(this.topic !== topic){ return false }\n\n if(joinRef && joinRef !== this.joinRef()){\n if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n return false\n } else {\n return true\n }\n }\n\n /**\n * @private\n */\n joinRef(){ return this.joinPush.ref }\n\n /**\n * @private\n */\n rejoin(timeout = this.timeout){\n if(this.isLeaving()){ return }\n this.socket.leaveOpenTopic(this.topic)\n this.state = CHANNEL_STATES.joining\n this.joinPush.resend(timeout)\n }\n\n /**\n * @private\n */\n trigger(event, payload, ref, joinRef){\n let handledPayload = this.onMessage(event, payload, ref, joinRef)\n if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n for(let i = 0; i < eventBindings.length; i++){\n let bind = eventBindings[i]\n bind.callback(handledPayload, ref, joinRef || this.joinRef())\n }\n }\n\n /**\n * @private\n */\n replyEventName(ref){ return `chan_reply_${ref}` }\n\n /**\n * @private\n */\n isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n /**\n * @private\n */\n isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n /**\n * @private\n */\n isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n /**\n * @private\n */\n isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n /**\n * @private\n */\n isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n global,\n XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n static request(method, endPoint, headers, body, timeout, ontimeout, callback){\n if(global.XDomainRequest){\n let req = new global.XDomainRequest() // IE8, IE9\n return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n } else if(global.XMLHttpRequest){\n let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)\n } else if(global.fetch && global.AbortController){\n // Fetch with AbortController for modern browsers\n return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)\n } else {\n throw new Error(\"No suitable XMLHttpRequest implementation found\")\n }\n }\n\n static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){\n let options = {\n method,\n headers,\n body,\n }\n let controller = null\n let timeoutId = null\n if(timeout){\n controller = new AbortController()\n timeoutId = setTimeout(() => controller.abort(), timeout)\n options.signal = controller.signal\n }\n global.fetch(endPoint, options)\n .then(response => response.text())\n .then(data => this.parseJSON(data))\n .then(data => {\n if(timeoutId){ clearTimeout(timeoutId) }\n callback && callback(data)\n })\n .catch(err => {\n if(timeoutId){ clearTimeout(timeoutId) }\n if(err.name === \"AbortError\" && ontimeout){\n ontimeout()\n } else {\n callback && callback(null)\n }\n })\n return controller\n }\n\n static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n req.timeout = timeout\n req.open(method, endPoint)\n req.onload = () => {\n let response = this.parseJSON(req.responseText)\n callback && callback(response)\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n // Work around bug in IE9 that requires an attached onprogress handler\n req.onprogress = () => { }\n\n req.send(body)\n return req\n }\n\n static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){\n req.open(method, endPoint, true)\n req.timeout = timeout\n for(let [key, value] of Object.entries(headers)){\n req.setRequestHeader(key, value)\n }\n req.onerror = () => callback && callback(null)\n req.onreadystatechange = () => {\n if(req.readyState === XHR_STATES.complete && callback){\n let response = this.parseJSON(req.responseText)\n callback(response)\n }\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n req.send(body)\n return req\n }\n\n static parseJSON(resp){\n if(!resp || resp === \"\"){ return null }\n\n try {\n return JSON.parse(resp)\n } catch {\n console && console.log(\"failed to parse JSON response\", resp)\n return null\n }\n }\n\n static serialize(obj, parentKey){\n let queryStr = []\n for(var key in obj){\n if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n let paramKey = parentKey ? `${parentKey}[${key}]` : key\n let paramVal = obj[key]\n if(typeof paramVal === \"object\"){\n queryStr.push(this.serialize(paramVal, paramKey))\n } else {\n queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n }\n }\n return queryStr.join(\"&\")\n }\n\n static appendParams(url, params){\n if(Object.keys(params).length === 0){ return url }\n\n let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n return `${url}${prefix}${this.serialize(params)}`\n }\n}\n", "import {\n SOCKET_STATES,\n TRANSPORTS,\n AUTH_TOKEN_PREFIX,\n MAX_LONGPOLL_BATCH_SIZE\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBufferToBase64 = (buffer) => {\n let binary = \"\"\n let bytes = new Uint8Array(buffer)\n let len = bytes.byteLength\n for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }\n return btoa(binary)\n}\n\nexport default class LongPoll {\n\n constructor(endPoint, protocols){\n // we only support subprotocols for authToken\n // [\"phoenix\", \"base64url.bearer.phx.BASE64_ENCODED_TOKEN\"]\n if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){\n this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))\n }\n this.endPoint = null\n this.token = null\n this.skipHeartbeat = true\n // the pending requests and queued messages, which are cancelled when we stop\n this.reqs = new Set()\n this.messageTimers = new Set()\n this.awaitingBatchAck = false\n this.currentBatch = null\n this.currentBatchTimer = null\n this.batchBuffer = []\n this.onopen = function (){ } // noop\n this.onerror = function (){ } // noop\n this.onmessage = function (){ } // noop\n this.onclose = function (){ } // noop\n this.pollEndpoint = this.normalizeEndpoint(endPoint)\n this.readyState = SOCKET_STATES.connecting\n // we must wait for the caller to finish setting up our callbacks and timeout properties,\n // and it might close us before that, e.g. when disconnecting right after connecting\n setTimeout(() => this.poll(), 0)\n }\n\n normalizeEndpoint(endPoint){\n return (endPoint\n .replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")\n .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n }\n\n endpointURL(){\n return this.pollEndpoint\n }\n\n closeAndRetry(error, code, reason, wasClean){\n if(!this.isActive()){ return }\n // like a failed WebSocket, we are no longer open when we emit the error and then the close.\n // We are connecting again before their callbacks run, so that they can still close us for\n // good, e.g. when they disconnect the socket\n this.stop(SOCKET_STATES.connecting)\n this.onerror(error)\n if(this.isActive()){ this.emitClose(code, reason, wasClean) }\n }\n\n ontimeout(){\n this.closeAndRetry(\"timeout\", 1005, \"timeout\", false)\n }\n\n isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }\n\n poll(){\n // onopen can close us before the next poll, just as the caller can close us\n // before the constructor's deferred first poll\n if(!this.isActive()){ return }\n const headers = {\"Accept\": \"application/json\"}\n if(this.authToken){\n headers[\"X-Phoenix-AuthToken\"] = this.authToken\n }\n this.ajax(\"GET\", headers, null, () => this.ontimeout(), resp => {\n if(resp){\n var {status, token, messages} = resp\n if(status === 410 && this.token !== null){\n // In case we already have a token, this means that our existing session\n // is gone. We fail so that the client rejoins its channels.\n this.closeAndRetry(410, 3410, \"session_gone\", false)\n return\n }\n this.token = token\n } else {\n status = 0\n }\n\n switch(status){\n case 200:\n messages.forEach(msg => {\n // Tasks are what things like event handlers, setTimeout callbacks,\n // promise resolves and more are run within.\n // In modern browsers, there are two different kinds of tasks,\n // microtasks and macrotasks.\n // Microtasks are mainly used for Promises, while macrotasks are\n // used for everything else.\n // Microtasks always have priority over macrotasks. If the JS engine\n // is looking for a task to run, it will always try to empty the\n // microtask queue before attempting to run anything from the\n // macrotask queue.\n //\n // For the WebSocket transport, messages always arrive in their own\n // event. This means that if any promises are resolved from within,\n // their callbacks will always finish execution by the time the\n // next message event handler is run.\n //\n // In order to emulate this behaviour, we need to make sure each\n // onmessage handler is run within its own macrotask.\n //\n // We might stop before that, e.g. when a POST times out or a message\n // handler disconnects, which cancels the remaining messages.\n const timer = setTimeout(() => {\n this.messageTimers.delete(timer)\n this.onmessage({data: msg})\n }, 0)\n this.messageTimers.add(timer)\n })\n this.poll()\n break\n case 204:\n this.poll()\n break\n case 410:\n this.readyState = SOCKET_STATES.open\n this.onopen({})\n this.poll()\n break\n case 403:\n this.onerror(403)\n this.close(1008, \"forbidden\", false)\n break\n case 0:\n case 500:\n this.closeAndRetry(500, 1011, \"internal server error\", false)\n break\n default: throw new Error(`unhandled poll status ${status}`)\n }\n })\n }\n\n // we collect all pushes within the current event loop by\n // setTimeout 0, which optimizes back-to-back procedural\n // pushes against an empty buffer\n\n send(body){\n if(typeof(body) !== \"string\"){ body = arrayBufferToBase64(body) }\n if(this.currentBatch){\n this.currentBatch.push(body)\n } else if(this.awaitingBatchAck){\n this.batchBuffer.push(body)\n } else {\n this.currentBatch = [body]\n this.currentBatchTimer = setTimeout(() => {\n this.batchSend(this.currentBatch)\n this.currentBatch = null\n }, 0)\n }\n }\n\n batchSend(messages, offset = 0){\n this.awaitingBatchAck = true\n const next = offset + MAX_LONGPOLL_BATCH_SIZE\n const batch = messages.slice(offset, next)\n this.ajax(\"POST\", {\"Content-Type\": \"application/x-ndjson\"}, batch.join(\"\\n\"), () => this.ontimeout(), resp => {\n if(!resp || resp.status !== 200){\n // this calls stop which already clears awaitingBatchAck\n this.closeAndRetry(resp && resp.status, 1011, \"internal server error\", false)\n } else if(next < messages.length){\n this.batchSend(messages, next)\n } else if(this.batchBuffer.length > 0){\n this.batchSend(this.batchBuffer)\n this.batchBuffer = []\n } else {\n this.awaitingBatchAck = false\n }\n })\n }\n\n close(code, reason, wasClean){\n this.stop(SOCKET_STATES.closed)\n this.emitClose(code, reason, wasClean)\n }\n\n /**\n * Cancels the pending requests, queued messages and outgoing batches.\n */\n stop(readyState){\n this.readyState = readyState\n // the requests are no longer ours before we abort them, as aborting can invoke their\n // callbacks, synchronously for XHR and asynchronously for fetch\n const reqs = this.reqs\n this.reqs = new Set()\n for(let {req} of reqs){ req && req.abort() }\n for(let timer of this.messageTimers){ clearTimeout(timer) }\n this.messageTimers.clear()\n this.batchBuffer = []\n this.awaitingBatchAck = false\n clearTimeout(this.currentBatchTimer)\n this.currentBatchTimer = null\n this.currentBatch = null\n }\n\n /**\n * Emits the close event. Like a WebSocket closed without a code, it reports 1005 (no status)\n * unless one is given, as 1000 would look like the server closing the connection normally.\n */\n emitClose(code = 1005, reason, wasClean = true){\n let opts = {code, reason, wasClean}\n if(typeof(CloseEvent) !== \"undefined\"){\n this.onclose(new CloseEvent(\"close\", opts))\n } else {\n this.onclose(opts)\n }\n }\n\n ajax(method, headers, body, onCallerTimeout, callback){\n // the request is ours until it completes or we stop, and the callbacks of a request\n // that is not ours anymore are ignored. It is ours before it starts, as a request can\n // complete while it starts.\n const request = {req: null}\n this.reqs.add(request)\n let ontimeout = () => {\n if(this.reqs.delete(request)){ onCallerTimeout() }\n }\n if(this.token !== null){\n headers = Object.assign({}, headers, {\"X-Phoenix-Longpoll-Token\": this.token})\n }\n request.req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {\n if(this.reqs.delete(request)){ callback(resp) }\n })\n }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n * for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n constructor(channel, opts = {}){\n let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n this.state = Object.create(null)\n this.pendingDiffs = []\n this.channel = channel\n this.joinRef = null\n this.caller = {\n onJoin: function (){ },\n onLeave: function (){ },\n onSync: function (){ }\n }\n\n this.channel.on(events.state, newState => {\n let {onJoin, onLeave, onSync} = this.caller\n\n this.joinRef = this.channel.joinRef()\n this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n this.pendingDiffs.forEach(diff => {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n })\n this.pendingDiffs = []\n onSync()\n })\n\n this.channel.on(events.diff, diff => {\n let {onJoin, onLeave, onSync} = this.caller\n\n if(this.inPendingSyncState()){\n this.pendingDiffs.push(diff)\n } else {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n onSync()\n }\n })\n }\n\n onJoin(callback){ this.caller.onJoin = callback }\n\n onLeave(callback){ this.caller.onLeave = callback }\n\n onSync(callback){ this.caller.onSync = callback }\n\n list(by){ return Presence.list(this.state, by) }\n\n inPendingSyncState(){\n return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n }\n\n // lower-level public static API\n\n /**\n * Used to sync the list of presences on the server\n * with the client's state. An optional `onJoin` and `onLeave` callback can\n * be provided to react to changes in the client's local presences across\n * disconnects and reconnects with the server.\n *\n * @returns {Presence}\n */\n static syncState(currentState, newState, onJoin, onLeave){\n let state = this.toNullProtoObj(this.clone(currentState))\n newState = this.toNullProtoObj(newState)\n let joins = Object.create(null)\n let leaves = Object.create(null)\n\n this.map(state, (key, presence) => {\n if(!newState[key]){\n leaves[key] = presence\n }\n })\n this.map(newState, (key, newPresence) => {\n let currentPresence = state[key]\n if(currentPresence){\n let newRefs = newPresence.metas.map(m => m.phx_ref)\n let curRefs = currentPresence.metas.map(m => m.phx_ref)\n let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n if(joinedMetas.length > 0){\n joins[key] = newPresence\n joins[key].metas = joinedMetas\n }\n if(leftMetas.length > 0){\n leaves[key] = this.clone(currentPresence)\n leaves[key].metas = leftMetas\n }\n } else {\n joins[key] = newPresence\n }\n })\n return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n }\n\n /**\n *\n * Used to sync a diff of presence join and leave\n * events from the server, as they happen. Like `syncState`, `syncDiff`\n * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n * joining or leaving from a device.\n *\n * @returns {Presence}\n */\n static syncDiff(state, diff, onJoin, onLeave){\n state = this.toNullProtoObj(state)\n let {joins, leaves} = this.clone(diff)\n if(!onJoin){ onJoin = function (){ } }\n if(!onLeave){ onLeave = function (){ } }\n\n this.map(joins, (key, newPresence) => {\n let currentPresence = state[key]\n state[key] = this.clone(newPresence)\n if(currentPresence){\n let joinedRefs = state[key].metas.map(m => m.phx_ref)\n let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n state[key].metas.unshift(...curMetas)\n }\n onJoin(key, currentPresence, newPresence)\n })\n this.map(leaves, (key, leftPresence) => {\n let currentPresence = state[key]\n if(!currentPresence){ return }\n let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n currentPresence.metas = currentPresence.metas.filter(p => {\n return refsToRemove.indexOf(p.phx_ref) < 0\n })\n onLeave(key, currentPresence, leftPresence)\n if(currentPresence.metas.length === 0){\n delete state[key]\n }\n })\n return state\n }\n\n /**\n * Returns the array of presences, with selected metadata.\n *\n * @param {Object} presences\n * @param {Function} chooser\n *\n * @returns {Presence}\n */\n static list(presences, chooser){\n if(!chooser){ chooser = function (key, pres){ return pres } }\n\n return this.map(presences, (key, presence) => {\n return chooser(key, presence)\n })\n }\n\n // private\n\n static map(obj, func){\n return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n }\n\n // Presence keys are chosen on the server and may collide with\n // Object.prototype properties (\"__proto__\", \"constructor\", ...), so any\n // object indexed by presence key must not have a prototype chain\n //\n // TODO: replace the null-prototype objects with Maps in Phoenix 2.0\n // (breaking change for the lower-level static API)\n static toNullProtoObj(obj){\n if(Object.getPrototypeOf(obj) === null){ return obj }\n let cleaned = Object.create(null)\n Object.getOwnPropertyNames(obj).forEach(key => {\n cleaned[key] = obj[key]\n })\n return cleaned\n }\n\n static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n HEADER_LENGTH: 1,\n META_LENGTH: 4,\n KINDS: {push: 0, reply: 1, broadcast: 2},\n\n encode(msg, callback){\n if(msg.payload.constructor === ArrayBuffer){\n return callback(this.binaryEncode(msg))\n } else {\n let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n return callback(JSON.stringify(payload))\n }\n },\n\n decode(rawPayload, callback){\n if(rawPayload.constructor === ArrayBuffer){\n return callback(this.binaryDecode(rawPayload))\n } else {\n let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n return callback({join_ref, ref, topic, event, payload})\n }\n },\n\n // private\n\n binaryEncode(message){\n let {join_ref, ref, event, topic, payload} = message\n let encoder = new TextEncoder()\n let joinRefBytes = encoder.encode(join_ref)\n let refBytes = encoder.encode(ref)\n let topicBytes = encoder.encode(topic)\n let eventBytes = encoder.encode(event)\n\n this.assertFieldSize(joinRefBytes.byteLength, \"join_ref\")\n this.assertFieldSize(refBytes.byteLength, \"ref\")\n this.assertFieldSize(topicBytes.byteLength, \"topic\")\n this.assertFieldSize(eventBytes.byteLength, \"event\")\n\n let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength\n let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n let headerBytes = new Uint8Array(header)\n let view = new DataView(header)\n let offset = 0\n\n view.setUint8(offset++, this.KINDS.push) // kind\n view.setUint8(offset++, joinRefBytes.byteLength)\n view.setUint8(offset++, refBytes.byteLength)\n view.setUint8(offset++, topicBytes.byteLength)\n view.setUint8(offset++, eventBytes.byteLength)\n headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength\n headerBytes.set(refBytes, offset); offset += refBytes.byteLength\n headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength\n headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength\n\n var combined = new Uint8Array(header.byteLength + payload.byteLength)\n combined.set(headerBytes, 0)\n combined.set(new Uint8Array(payload), header.byteLength)\n\n return combined.buffer\n },\n\n assertFieldSize(size, name){\n if(size > 255){\n throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)\n }\n },\n\n binaryDecode(buffer){\n let view = new DataView(buffer)\n let kind = view.getUint8(0)\n let decoder = new TextDecoder()\n switch(kind){\n case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n }\n },\n\n decodePush(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let topicSize = view.getUint8(2)\n let eventSize = view.getUint8(3)\n let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n },\n\n decodeReply(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let refSize = view.getUint8(2)\n let topicSize = view.getUint8(3)\n let eventSize = view.getUint8(4)\n let offset = this.HEADER_LENGTH + this.META_LENGTH\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n offset = offset + refSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n let payload = {status: event, response: data}\n return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n },\n\n decodeBroadcast(buffer, view, decoder){\n let topicSize = view.getUint8(1)\n let eventSize = view.getUint8(2)\n let offset = this.HEADER_LENGTH + 2\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n\n return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n }\n}\n", "import {SOCKET_STATES} from \"./constants\"\n\n/**\n * @private\n *\n * A connection of the socket over a single transport instance.\n *\n * It owns everything that belongs to this connection, so that nothing about it can\n * affect a later connection: the transport events are only handled while it is the\n * socket's current connection, and its state is gone together with it.\n */\nexport default class Connection {\n /**\n * @param {Socket} socket\n * @param {Object} transport - The transport instance, for example a WebSocket\n */\n constructor(socket, transport){\n this.socket = socket\n this.transport = transport\n // the join ref of each channel whose join was sent over this connection, weakly held\n // as a connection can outlive many channels, e.g. when navigating between LiveViews\n this.joins = new WeakMap()\n // the callbacks of the pings sent over this connection by ref, as their replies can only\n // arrive over it, so they are gone together with it instead of accumulating on the socket\n this.pings = new Map()\n // set when we close the connection, as opposed to the server or the network\n this.closing = false\n // set when the socket does not want this connection anymore, e.g. on disconnect,\n // so that connecting again creates a new one instead of waiting for it\n this.ended = false\n // set once the channels were errored after the connection stopped being open\n this.channelsErrored = false\n // set once the close event was handled, as LongPoll can emit it more than once\n this.closeHandled = false\n transport.onopen = () => { if(this.isCurrent()){ socket.onConnOpen() } }\n transport.onerror = error => { if(this.isCurrent()){ socket.onConnError(error) } }\n transport.onmessage = event => { if(this.isCurrent()){ socket.onConnMessage(event) } }\n transport.onclose = event => {\n if(this.isCurrent() && !this.closeHandled){\n this.closeHandled = true\n socket.onConnClose(event)\n }\n }\n }\n\n /**\n * Whether this is the connection the socket currently uses. A replaced connection\n * can still emit events while it closes, but they no longer concern the socket.\n */\n isCurrent(){ return this.socket.connection === this }\n\n isOpen(){ return this.transport.readyState === SOCKET_STATES.open }\n\n isConnecting(){ return this.transport.readyState === SOCKET_STATES.connecting }\n\n /**\n * Whether the channel's current join was sent over this connection.\n */\n carried(channel, joinRef = channel.joinRef()){\n return this.joins.has(channel) && this.joins.get(channel) === joinRef\n }\n\n /**\n * Closes the transport. Its close event then only confirms our close.\n */\n close(code, reason){\n this.closing = true\n // closing a transport that is still connecting makes it emit an error, which is\n // caused by closing it and must not be handled as a transport error\n this.transport.onerror = function (){ } // noop\n if(code){ this.transport.close(code, reason || \"\") } else { this.transport.close() }\n }\n}\n", "import {\n global,\n phxWindow,\n CHANNEL_EVENTS,\n DEFAULT_TIMEOUT,\n DEFAULT_VSN,\n SOCKET_STATES,\n TRANSPORTS,\n WS_CLOSE_NORMAL,\n AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport {\n closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\nimport Connection from \"./connection\"\n\n/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n * `\"wss://example.com\"`\n * `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.\n * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.\n *\n * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport\n * before falling back to the LongPoll transport. Disabled by default.\n *\n * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.\n *\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the\n * socket reconnect interval, in milliseconds.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n * return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {boolean} [opts.reconnectWhenHidden] - When true, the socket also reconnects while the\n * page is hidden, for example in a background tab or on a sleeping phone.\n *\n * Defaults to false. The socket then waits until the page is visible again before it reconnects.\n * This avoids reconnect attempts that stall on mobile devices and delay the connection after the\n * device wakes up.\n *\n * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n * return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n * console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {(Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {(string|function)} [opts.authToken] - the optional authentication token to be exposed on the server\n * under the `:auth_token` connect_info key. Can be a string or a function that returns a string.\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n *\n * @param {Object} [opts.sessionStorage] - An optional Storage compatible object\n * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is\n * useful when Phoenix won't have access to `sessionStorage`. For example, This could\n * happen if a site loads a cross-domain channel in an iframe. Example usage:\n *\n * class InMemoryStorage {\n * constructor() { this.storage = {} }\n * getItem(keyName) { return this.storage[keyName] || null }\n * removeItem(keyName) { delete this.storage[keyName] }\n * setItem(keyName, keyValue) { this.storage[keyName] = keyValue }\n * }\n *\n*/\nexport default class Socket {\n constructor(endPoint, opts = {}){\n this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n this.channels = []\n this.sendBuffer = []\n this.ref = 0\n this.fallbackRef = null\n this.fallbackErrorRef = null\n this.timeout = opts.timeout || DEFAULT_TIMEOUT\n this.transport = opts.transport || global.WebSocket || LongPoll\n this.primaryPassedHealthCheck = false\n this.longPollFallbackMs = opts.longPollFallbackMs\n this.fallbackTimer = null\n this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)\n this.establishedConnections = 0\n this.defaultEncoder = Serializer.encode.bind(Serializer)\n this.defaultDecoder = Serializer.decode.bind(Serializer)\n // We start with closeWasClean true to avoid the visibility change\n // logic from connecting if the socket was never connected in the first place.\n // transportConnect sets it to false on open.\n this.closeWasClean = true\n this.binaryType = opts.binaryType || \"arraybuffer\"\n // the connection over the current transport instance, see Connection\n this.connection = null\n // changes whenever we connect, disconnect or replace the transport, for the code that needs\n // to know whether that happened while there was no connection, see replaceTransport\n this.connectClock = 1\n // the connection created when the page was shown, see handleVisibilityChange\n this.visibilityConnection = null\n if(this.transport !== LongPoll){\n this.encode = opts.encode || this.defaultEncoder\n this.decode = opts.decode || this.defaultDecoder\n } else {\n this.encode = this.defaultEncoder\n this.decode = this.defaultDecoder\n }\n let awaitingConnectionOnPageShow = null\n if(phxWindow && phxWindow.addEventListener){\n phxWindow.addEventListener(\"pagehide\", _e => {\n // an ended connection only finishes closing, e.g. after disconnect\n if(this.connection && !this.connection.ended){\n this.disconnect()\n awaitingConnectionOnPageShow = this.connectClock\n }\n })\n phxWindow.addEventListener(\"pageshow\", _e => {\n if(awaitingConnectionOnPageShow === this.connectClock){\n awaitingConnectionOnPageShow = null\n this.connect()\n }\n })\n phxWindow.addEventListener(\"visibilitychange\", () => {\n this.handleVisibilityChange()\n })\n // Chrome 149 started to not reliably fire visibilitychange after resume,\n // see https://issues.chromium.org/issues/547062449.\n phxWindow.document && phxWindow.document.addEventListener(\"resume\", () => {\n this.handleVisibilityChange()\n })\n }\n this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n this.rejoinAfterMs = (tries) => {\n if(opts.rejoinAfterMs){\n return opts.rejoinAfterMs(tries)\n } else {\n return [1000, 2000, 5000][tries - 1] || 10000\n }\n }\n this.reconnectAfterMs = (tries) => {\n if(opts.reconnectAfterMs){\n return opts.reconnectAfterMs(tries)\n } else {\n return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n }\n }\n this.reconnectWhenHidden = opts.reconnectWhenHidden || false\n this.logger = opts.logger || null\n if(!this.logger && opts.debug){\n this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }\n }\n this.longpollerTimeout = opts.longpollerTimeout || 20000\n this.params = closure(opts.params || {})\n this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n this.vsn = opts.vsn || DEFAULT_VSN\n this.heartbeatTimeoutTimer = null\n this.heartbeatTimer = null\n this.pendingHeartbeatRef = null\n this.reconnectTimer = new Timer(() => {\n // handleVisibilityChange reconnects once the page is visible again\n if(this.pageHidden && !this.reconnectWhenHidden){\n this.log(\"Not reconnecting as page is hidden!\")\n this.teardown()\n return\n }\n this.teardownAndReconnect(() => this.connect())\n }, this.reconnectAfterMs)\n this.authToken = opts.authToken && closure(opts.authToken)\n }\n\n /**\n * The transport instance of the current connection, for example a WebSocket\n */\n get conn(){ return this.connection && this.connection.transport }\n\n /**\n * @internal\n */\n get pageHidden(){\n return phxWindow && phxWindow.document ? phxWindow.document.visibilityState === \"hidden\" : false\n }\n\n /**\n * @internal\n */\n handleVisibilityChange(){\n if(this.pageHidden){\n this.visibilityConnection = null\n return\n }\n // resume and visibilitychange can both fire when the page is shown again, so we keep\n // the connection created for the first one while it is still connecting\n const connection = this.connection\n if(connection && connection === this.visibilityConnection && connection.isConnecting()){ return }\n // reconnect immediately\n if(!this.isConnected() && !this.closeWasClean){\n // a reconnect that was scheduled before would tear down the connection we create here\n this.reconnectTimer.reset()\n this.teardownAndReconnect(() => {\n this.connect()\n this.visibilityConnection = this.connection\n })\n }\n }\n\n /**\n * Returns the LongPoll transport reference\n */\n getLongPollTransport(){ return LongPoll }\n\n /**\n * Disconnects and replaces the active transport\n *\n * @param {Function} newTransport - The new transport class to instantiate\n *\n */\n replaceTransport(newTransport){\n const connectClock = ++this.connectClock\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // replaced first, as channel error callbacks below can already connect again\n this.transport = newTransport\n const connection = this.connection\n if(connection){\n const wasOpen = connection.isOpen()\n // the old transport closes asynchronously, but its events no longer concern us\n this.connection = null\n connection.ended = true\n connection.close()\n this.clearHeartbeats()\n // any in-flight pushes on the old connection are lost, so channels need to rejoin\n if(wasOpen){ this.triggerChanError(\"connection_closed\", connection) }\n }\n return connectClock\n }\n\n /**\n * Returns the socket protocol\n *\n * @returns {string}\n */\n protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n /**\n * The fully qualified socket url\n *\n * @returns {string}\n */\n endPointURL(){\n let uri = Ajax.appendParams(\n Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n if(uri.charAt(0) !== \"/\"){ return uri }\n if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n return `${this.protocol()}://${location.host}${uri}`\n }\n\n /**\n * Disconnects the socket\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n *\n * @param {Function} callback - Optional callback which is called after socket is disconnected.\n * @param {integer} code - A status code for disconnection (Optional).\n * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n */\n disconnect(callback, code, reason){\n this.connectClock++\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // connecting again creates a new connection right away instead of waiting for this one to close\n if(this.connection){ this.connection.ended = true }\n this.teardown(callback, code, reason)\n }\n\n /**\n *\n * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n *\n * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n */\n connect(params){\n if(params){\n console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n this.params = closure(params)\n }\n // the current connection is kept, including while it reconnects, until the socket is done with it\n if(this.connection && !this.connection.ended){ return }\n if(this.longPollFallbackMs && this.transport !== LongPoll){\n this.connectWithFallback(LongPoll, this.longPollFallbackMs)\n } else {\n this.transportConnect()\n }\n }\n\n /**\n * Logs the message. Override `this.logger` for specialized logging. noops by default\n * @param {string} kind\n * @param {string} msg\n * @param {Object} data\n */\n log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }\n\n /**\n * Returns true if a logger has been set on this socket.\n */\n hasLogger(){ return this.logger !== null }\n\n /**\n * Registers callbacks for connection open events\n *\n * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n *\n * @param {Function} callback\n */\n onOpen(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.open.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection close events\n * @param {Function} callback\n */\n onClose(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.close.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection error events\n *\n * @example socket.onError(function(error){ alert(\"An error occurred\") })\n *\n * @param {Function} callback\n */\n onError(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.error.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection message events\n * @param {Function} callback\n */\n onMessage(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.message.push([ref, callback])\n return ref\n }\n\n /**\n * Pings the server and invokes the callback with the RTT in milliseconds\n * @param {Function} callback\n *\n * Returns true if the ping was pushed or false if unable to be pushed.\n */\n ping(callback){\n if(!this.isConnected()){ return false }\n let ref = this.makeRef()\n let startTime = Date.now()\n this.connection.pings.set(ref, () => callback(Date.now() - startTime))\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: ref})\n return true\n }\n\n /**\n * @private\n *\n * @param {Function}\n */\n transportName(transport){\n // JavaScript minification, enabled by default in production in Phoenix\n // projects, renames symbols to reduce code size.\n // See https://esbuild.github.io/api/#keep-names.\n // This helper ensures we return the correct name for the LongPoll transport\n // even after minification. The other common transport is WebSocket, which\n // is native to browsers and does not need special handling.\n switch(transport){\n case LongPoll: return \"LongPoll\"\n default: return transport.name\n }\n }\n\n /**\n * @private\n */\n transportConnect(){\n const previous = this.connection\n this.connectClock++\n this.closeWasClean = false\n // a reconnect scheduled before would tear down the new connection, but we keep\n // its tries, which only reset once a connection opens\n clearTimeout(this.reconnectTimer.timer)\n let protocols = undefined\n // Sec-WebSocket-Protocol based token\n // (longpoll uses Authorization header instead)\n if(this.authToken){\n protocols = [\"phoenix\", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken()).replace(/=/g, \"\")}`]\n }\n const transport = new this.transport(this.endPointURL(), protocols)\n transport.binaryType = this.binaryType\n transport.timeout = this.longpollerTimeout\n this.connection = new Connection(this, transport)\n // connect can replace an ended connection that is still open while its buffer drains, but the\n // channels joined over it need to rejoin on the new connection instead of waiting for it to close\n if(previous && previous.isOpen()){ this.triggerChanError(\"connection_closed\", previous) }\n }\n\n getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }\n\n storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }\n\n connectWithFallback(fallbackTransport, fallbackThreshold = 2500){\n // the callbacks of a previous attempt must not act on this one, e.g. an attempt\n // that never opened would otherwise fall back again on the next error\n this.cancelFallback()\n let established = false\n let primaryTransport = true\n let errorRef\n let connectClock\n const isCurrent = () => connectClock === this.connectClock && !this.closeWasClean\n let fallbackTransportName = this.transportName(fallbackTransport)\n // the fallback timer is cleared whenever the attempt ends, and the callbacks below\n // only fall back while the attempt is current\n let fallback = (reason) => {\n this.log(\"transport\", `falling back to ${fallbackTransportName}...`, reason)\n // the open callback stays registered to remember the fallback once it connects\n this.off([errorRef])\n primaryTransport = false\n const replaceClock = this.replaceTransport(fallbackTransport)\n if(replaceClock !== this.connectClock){ return }\n this.transportConnect()\n }\n if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback(\"memorized\") }\n\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n\n errorRef = this.fallbackErrorRef = this.onError(reason => {\n if(!isCurrent()){ return }\n this.log(\"transport\", \"error\", reason)\n if(primaryTransport && !established){\n clearTimeout(this.fallbackTimer)\n fallback(reason)\n }\n })\n this.fallbackRef = this.onOpen(() => {\n if(!primaryTransport){\n // reconnects keep using the fallback transport, so a later successful connection\n // must still be remembered, unless an earlier callback disconnected it\n if(this.transport !== fallbackTransport || this.closeWasClean){ return }\n let fallbackTransportName = this.transportName(fallbackTransport)\n // only memorize LP if we never connected to primary\n if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, \"true\") }\n return this.log(\"transport\", `established ${fallbackTransportName} fallback`)\n }\n if(!isCurrent()){ return }\n established = true\n // if we've established primary, give the fallback a new period to attempt ping\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n this.ping(rtt => {\n this.log(\"transport\", \"connected to primary after\", rtt)\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n })\n })\n this.transportConnect()\n connectClock = this.connectClock\n }\n\n /**\n * @private\n *\n * Stops the fallback of the current connection attempt, see connectWithFallback\n */\n cancelFallback(){\n clearTimeout(this.fallbackTimer)\n this.off([this.fallbackRef, this.fallbackErrorRef])\n }\n\n clearHeartbeats(){\n clearTimeout(this.heartbeatTimer)\n clearTimeout(this.heartbeatTimeoutTimer)\n // a late reply to the pending heartbeat must not start the heartbeats again\n this.pendingHeartbeatRef = null\n }\n\n onConnOpen(){\n if(this.hasLogger()) this.log(\"transport\", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)\n this.closeWasClean = false\n this.establishedConnections++\n this.flushSendBuffer()\n this.reconnectTimer.reset()\n this.resetHeartbeat()\n this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n }\n\n /**\n * @private\n */\n\n heartbeatTimeout(){\n if(this.pendingHeartbeatRef){\n const connection = this.connection\n this.pendingHeartbeatRef = null\n if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n this.closeWasClean = false\n // the connection is presumed dead, so we close it right away instead of waiting for its\n // buffer to drain, which also ensures that it is no longer considered connected when the\n // channels error (LiveView would report a server error otherwise)\n if(connection){ connection.close(WS_CLOSE_NORMAL, \"heartbeat timeout\") }\n // some transports emit their close event synchronously, and callbacks of it as well as\n // channel error callbacks can disconnect or connect, which takes precedence over reconnecting\n if(!this.stillUses(connection)){ return }\n this.triggerChanError(\"heartbeat_timeout\")\n if(!this.stillUses(connection)){ return }\n this.teardownAndReconnect(() => this.reconnectTimer.scheduleTimeout())\n }\n }\n\n resetHeartbeat(){\n if(this.conn && this.conn.skipHeartbeat){ return }\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n /**\n * @private\n *\n * Tears down the current connection to reconnect afterwards, unless the socket was\n * disconnected or connected again in the meantime, which takes precedence.\n */\n teardownAndReconnect(reconnect){\n const connection = this.connection\n const connectClock = this.connectClock\n this.teardown(() => {\n // the socket was disconnected or connected again in the meantime, which the connection does not\n // tell once an overlapping teardown gave up on it\n if(connectClock !== this.connectClock || (connection && connection.ended)){ return }\n reconnect()\n })\n }\n\n /**\n * @private\n *\n * Whether the socket still uses the connection and did not give up on it. User callbacks\n * can disconnect or connect, so code continuing after them checks this first.\n */\n stillUses(connection){\n return this.connection === connection && !(connection && connection.ended)\n }\n\n teardown(callback, code, reason){\n // If someone calls connect before we finish tearing down,\n // we create a new connection, but we still want to finish tearing down the old one.\n const connection = this.connection\n if(!connection){\n return callback && callback()\n }\n // onConnClose is not called when the close event arrives late, so we stop the\n // heartbeats here, as a heartbeat timeout would otherwise reconnect the socket\n this.clearHeartbeats()\n\n this.waitForBufferDone(connection.transport, () => {\n // a connection we already closed, e.g. on a heartbeat timeout or in an\n // overlapping teardown, also errored its channels already\n if(!connection.closing){\n const wasOpened = !connection.isConnecting()\n connection.close(code, reason)\n // channels joined over this connection need to rejoin on the next one, but we cannot rely\n // on onConnClose for that, as the close event may arrive after we stopped waiting for it\n // or after a new connection already replaced this one\n if(wasOpened){ this.triggerChanError(\"connection_closed\", connection) }\n }\n\n this.waitForSocketClosed(connection.transport, () => {\n if(this.connection === connection){ this.connection = null }\n callback && callback()\n })\n })\n }\n\n waitForBufferDone(conn, callback, tries = 1){\n // there is no point in waiting for a conn that is already closing\n if(tries === 5 || !conn.bufferedAmount || conn.readyState !== SOCKET_STATES.open){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForBufferDone(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n waitForSocketClosed(conn, callback, tries = 1){\n if(tries === 5 || conn.readyState === SOCKET_STATES.closed){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForSocketClosed(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n onConnClose(event){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n this.clearHeartbeats()\n // we only close a connection to disconnect or to reconnect, which the code closing it takes\n // care of, so its close event only confirms our close. Any other close is reconnected, whatever\n // its code, as the server, proxies and load balancers also close with 1000 (normal closure)\n const closedByUs = !!(connection && connection.closing)\n // channel callbacks may disconnect or create a new connection, so finish cleaning up\n // this connection first and do not schedule a reconnect for its replacement\n this.triggerChanError(\"connection_closed\", connection)\n if(this.stillUses(connection) && !closedByUs && !this.closeWasClean){\n this.reconnectTimer.scheduleTimeout()\n }\n this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n }\n\n /**\n * @private\n */\n onConnError(error){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"error\", error)\n let transportBefore = this.transport\n let establishedBefore = this.establishedConnections\n this.stateChangeCallbacks.error.forEach(([, callback]) => {\n callback(error, transportBefore, establishedBefore)\n })\n // callbacks can replace the connection, in which case only its own joins are errored\n this.triggerChanError(\"connection_error\", connection)\n }\n\n /**\n * @private\n *\n * Errors the channels, as they need to rejoin once the given connection stopped being open.\n */\n triggerChanError(reason, connection = this.connection){\n // several places notice that a connection stopped being open (onConnError, onConnClose,\n // teardown and heartbeatTimeout). After the first, only channels whose join it carried\n // are errored, as error callbacks may have joined channels that join over another connection.\n let erroredBefore = false\n if(connection && !connection.isOpen()){\n erroredBefore = connection.channelsErrored\n connection.channelsErrored = true\n }\n const joins = this.channels.map(channel => [channel, channel.joinRef()])\n joins.forEach(([channel, joinRef]) => {\n // an earlier error callback can rejoin a later channel, so check its join\n // again immediately before dispatching rather than filtering the list first\n if(channel.joinRef() !== joinRef){ return }\n // the joins of a replaced connection are not the only ones anymore\n if(connection && (erroredBefore || !connection.isCurrent()) && !connection.carried(channel, joinRef)){ return }\n if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n channel.trigger(CHANNEL_EVENTS.error, {source: \"transport\", reason})\n }\n })\n }\n\n /**\n * @returns {string}\n */\n connectionState(){\n switch(this.conn && this.conn.readyState){\n case SOCKET_STATES.connecting: return \"connecting\"\n case SOCKET_STATES.open: return \"open\"\n case SOCKET_STATES.closing: return \"closing\"\n default: return \"closed\"\n }\n }\n\n /**\n * @returns {boolean}\n */\n isConnected(){ return this.connectionState() === \"open\" }\n\n /**\n * @private\n *\n * @param {Channel}\n */\n remove(channel){\n this.off(channel.stateChangeRefs)\n this.channels = this.channels.filter(c => c !== channel)\n }\n\n /**\n * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n *\n * @param {refs} - list of refs returned by calls to\n * `onOpen`, `onClose`, `onError,` and `onMessage`\n */\n off(refs){\n for(let key in this.stateChangeCallbacks){\n this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n return refs.indexOf(ref) === -1\n })\n }\n }\n\n /**\n * Initiates a new channel for the given topic\n *\n * @param {string} topic\n * @param {Object} chanParams - Parameters for the channel\n * @returns {Channel}\n */\n channel(topic, chanParams = {}){\n let chan = new Channel(topic, chanParams, this)\n this.channels.push(chan)\n return chan\n }\n\n /**\n * @param {Object} data\n * @param {Channel} [channel] - The channel the push belongs to, if any\n */\n push(data, channel){\n if(this.hasLogger()){\n let {topic, event, payload, ref, join_ref} = data\n this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n }\n\n if(this.isConnected()){\n this.send(data, channel)\n } else {\n this.sendBuffer.push(() => {\n // the channel may have errored or left while the push was buffered, e.g. when the\n // connection failed before opening, in which case its rejoin supersedes the buffered join\n if(!this.isOutdated(data, channel)){ this.send(data, channel) }\n })\n }\n }\n\n /**\n * @private\n */\n send(data, channel){\n const connection = this.connection\n // the join counts as carried by this connection while it is still encoded, so that the\n // channel rejoins when the connection is torn down before the encoding finished\n if(channel && data.event === CHANNEL_EVENTS.join){ connection.joins.set(channel, data.join_ref) }\n this.encode(data, result => {\n // let encodes finish while this connection drains, but never send on a\n // replacement or after the original connection has started closing\n if(!connection.isCurrent() || !connection.isOpen()){ return }\n // a leave still needs to reach the server after its channel was removed locally\n if(data.event !== CHANNEL_EVENTS.leave && this.isOutdated(data, channel)){ return }\n connection.transport.send(result)\n })\n }\n\n /**\n * @private\n *\n * Whether the push was made for an earlier join of its channel, or the channel left since.\n */\n isOutdated(data, channel){\n return !!channel && (data.join_ref !== channel.joinRef() || channel.isClosed())\n }\n\n /**\n * Return the next message ref, accounting for overflows\n * @returns {string}\n */\n makeRef(){\n let newRef = this.ref + 1\n if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n return this.ref.toString()\n }\n\n sendHeartbeat(){\n if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n this.pendingHeartbeatRef = this.makeRef()\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n }\n\n flushSendBuffer(){\n if(this.isConnected() && this.sendBuffer.length > 0){\n this.sendBuffer.forEach(callback => callback())\n this.sendBuffer = []\n }\n }\n\n onConnMessage(rawMessage){\n const connection = this.connection\n const wasOpen = connection && connection.isOpen()\n this.decode(rawMessage.data, msg => {\n // the transport event can be current even though its asynchronous decode is not, as the\n // connection may have been replaced or closed in the meantime (browsers also drop messages\n // that arrive once a WebSocket is closing)\n if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return }\n let {topic, event, payload, ref, join_ref} = msg\n if(ref && ref === this.pendingHeartbeatRef){\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n // removing a channel or callback while dispatching replaces the list instead of\n // changing it, so we iterate over the lists from before to not skip any of them\n const channels = this.channels\n for(let i = 0; i < channels.length; i++){\n const channel = channels[i]\n if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n channel.trigger(event, payload, ref, join_ref)\n }\n\n const callbacks = this.stateChangeCallbacks.message\n for(let i = 0; i < callbacks.length; i++){\n let [, callback] = callbacks[i]\n callback(msg)\n }\n\n const ping = ref && connection.pings.get(ref)\n if(ping){\n connection.pings.delete(ref)\n ping()\n }\n })\n }\n\n leaveOpenTopic(topic){\n let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n if(dupChannel){\n if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n dupChannel.leave()\n }\n }\n}\n"], + "mappings": "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", "names": ["closure", "fallbackTransportName"] } diff --git a/priv/static/phoenix.js b/priv/static/phoenix.js index aeadef9d29..9313341602 100644 --- a/priv/static/phoenix.js +++ b/priv/static/phoenix.js @@ -1617,38 +1617,11 @@ var Phoenix = (() => { if (this.hasLogger()) this.log("transport", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`); this.closeWasClean = false; this.establishedConnections++; - this.pingBeforeBufferedJoin(); this.flushSendBuffer(); this.reconnectTimer.reset(); this.resetHeartbeat(); this.stateChangeCallbacks.open.forEach(([, callback]) => callback()); } - /** - * @private - * - * Sends the fallback health check before flushSendBuffer. No-op unless a - * fallback attempt is waiting on the primary transport. - */ - pingBeforeBufferedJoin() { - if (!this.fallbackTimer || this.transport === LongPoll) { - return; - } - this.ping(() => this.passPrimaryHealthCheck()); - } - /** - * @private - * - * The primary transport answered. Later frames on this connection also call - * this, because a frame is the proof the ping was waiting for. - */ - passPrimaryHealthCheck() { - if (this.transport === LongPoll) { - return; - } - this.primaryPassedHealthCheck = true; - clearTimeout(this.fallbackTimer); - this.fallbackTimer = null; - } /** * @private */ @@ -1927,9 +1900,6 @@ var Phoenix = (() => { if (connection !== this.connection || wasOpen && !connection.isOpen()) { return; } - if (this.fallbackTimer) { - this.passPrimaryHealthCheck(); - } let { topic, event, payload, ref, join_ref } = msg; if (ref && ref === this.pendingHeartbeatRef) { this.clearHeartbeats(); diff --git a/priv/static/phoenix.min.js b/priv/static/phoenix.min.js index fedbe09f37..4b47a26ed9 100644 --- a/priv/static/phoenix.min.js +++ b/priv/static/phoenix.min.js @@ -1,2 +1,2 @@ -var Phoenix=(()=>{var N=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var W=(h,e)=>{for(var t in e)N(h,t,{get:e[t],enumerable:!0})},z=(h,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of F(e))!I.call(h,s)&&s!==t&&N(h,s,{get:()=>e[s],enumerable:!(i=D(e,s))||i.enumerable});return h};var J=h=>z(N({},"__esModule",{value:!0}),h);var G={};W(G,{Channel:()=>j,LongPoll:()=>b,Presence:()=>w,Serializer:()=>L,Socket:()=>H});var S=h=>typeof h=="function"?h:function(){return h};var X=typeof self!="undefined"?self:null,E=typeof window!="undefined"?window:null,y=X||E||globalThis,B="2.0.0",p={connecting:0,open:1,closing:2,closed:3},P=100,$=1e4,M=1e3,g={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},T={close:"phx_close",error:"phx_error",join:"phx_join",reply:"phx_reply",leave:"phx_leave"},A={longpoll:"longpoll",websocket:"websocket"},U={complete:4},O="base64url.bearer.phx.";var v=class{constructor(e,t,i,s){this.channel=e,this.event=t,this.payload=i||function(){return{}},this.receivedResp=null,this.timeout=s,this.timeoutTimer=null,this.recHooks=[],this.sent=!1}resend(e){this.timeout=e,this.reset(),this.send()}send(){this.hasReceived("timeout")||(this.startTimeout(),this.sent=!0,this.channel.socket.push({topic:this.channel.topic,event:this.event,payload:this.payload(),ref:this.ref,join_ref:this.channel.joinRef()},this.channel))}receive(e,t){return this.hasReceived(e)&&t(this.receivedResp.response),this.recHooks.push({status:e,callback:t}),this}reset(){this.cancelRefEvent(),this.ref=null,this.refEvent=null,this.receivedResp=null,this.sent=!1}matchReceive({status:e,response:t,_ref:i}){this.recHooks.filter(s=>s.status===e).forEach(s=>s.callback(t))}cancelRefEvent(){this.refEvent&&this.channel.off(this.refEvent)}cancelTimeout(){clearTimeout(this.timeoutTimer),this.timeoutTimer=null}startTimeout(){this.timeoutTimer&&this.cancelTimeout(),this.cancelRefEvent(),this.ref=this.channel.socket.makeRef(),this.refEvent=this.channel.replyEventName(this.ref),this.channel.on(this.refEvent,e=>{this.cancelRefEvent(),this.cancelTimeout(),this.receivedResp=e,this.matchReceive(e)}),this.timeoutTimer=setTimeout(()=>{this.trigger("timeout",{})},this.timeout)}hasReceived(e){return this.receivedResp&&this.receivedResp.status===e}trigger(e,t){this.channel.trigger(this.refEvent,{status:e,response:t})}};var R=class{constructor(e,t){this.callback=e,this.timerCalc=t,this.timer=null,this.tries=0}reset(){this.tries=0,clearTimeout(this.timer)}scheduleTimeout(){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.tries=this.tries+1,this.callback()},this.timerCalc(this.tries+1))}};var j=class{constructor(e,t,i){this.state=g.closed,this.topic=e,this.params=S(t||{}),this.socket=i,this.bindings=[],this.bindingRef=0,this.timeout=this.socket.timeout,this.joinedOnce=!1,this.joinPush=new v(this,T.join,this.params,this.timeout),this.pushBuffer=[],this.stateChangeRefs=[],this.rejoinTimer=new R(()=>{this.socket.isConnected()&&this.rejoin()},this.socket.rejoinAfterMs),this.stateChangeRefs.push(this.socket.onError(()=>this.rejoinTimer.reset())),this.stateChangeRefs.push(this.socket.onOpen(()=>{this.rejoinTimer.reset(),this.isErrored()&&this.rejoin()})),this.joinPush.receive("ok",()=>{this.state=g.joined,this.rejoinTimer.reset(),this.pushBuffer.forEach(s=>s.send()),this.pushBuffer=[]}),this.joinPush.receive("error",()=>{this.state=g.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.onClose(()=>{this.rejoinTimer.reset(),this.socket.hasLogger()&&this.socket.log("channel",`close ${this.topic} ${this.joinRef()}`),this.state=g.closed,this.socket.remove(this)}),this.onError(s=>{this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,s),this.isJoining()&&this.joinPush.reset(),this.state=g.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.joinPush.receive("timeout",()=>{this.socket.hasLogger()&&this.socket.log("channel",`timeout ${this.topic} (${this.joinRef()})`,this.joinPush.timeout),new v(this,T.leave,S({}),this.timeout).send(),this.state=g.errored,this.joinPush.reset(),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.on(T.reply,(s,n)=>{this.trigger(this.replyEventName(n),s)})}join(e=this.timeout){if(this.joinedOnce)throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance");return this.timeout=e,this.joinedOnce=!0,this.rejoin(),this.joinPush}onClose(e){this.on(T.close,e)}onError(e){return this.on(T.error,t=>e(t))}on(e,t){let i=this.bindingRef++;return this.bindings.push({event:e,ref:i,callback:t}),i}off(e,t){this.bindings=this.bindings.filter(i=>!(i.event===e&&(typeof t=="undefined"||t===i.ref)))}canPush(){return this.socket.isConnected()&&this.isJoined()}push(e,t,i=this.timeout){if(t=t||{},!this.joinedOnce)throw new Error(`tried to push '${e}' to '${this.topic}' before joining. Use channel.join() before pushing events`);let s=new v(this,e,function(){return t},i);return this.canPush()?s.send():(s.startTimeout(),this.pushBuffer.push(s)),s}leave(e=this.timeout){this.rejoinTimer.reset(),this.joinPush.cancelTimeout(),this.state=g.leaving;let t=()=>{this.socket.hasLogger()&&this.socket.log("channel",`leave ${this.topic}`),this.trigger(T.close,"leave")},i=new v(this,T.leave,S({}),e);return i.receive("ok",()=>t()).receive("timeout",()=>t()),i.send(),this.canPush()||i.trigger("ok",{}),i}onMessage(e,t,i){return t}isMember(e,t,i,s){return this.topic!==e?!1:s&&s!==this.joinRef()?(this.socket.hasLogger()&&this.socket.log("channel","dropping outdated message",{topic:e,event:t,payload:i,joinRef:s}),!1):!0}joinRef(){return this.joinPush.ref}rejoin(e=this.timeout){this.isLeaving()||(this.socket.leaveOpenTopic(this.topic),this.state=g.joining,this.joinPush.resend(e))}trigger(e,t,i,s){let n=this.onMessage(e,t,i,s);if(t&&!n)throw new Error("channel onMessage callbacks must return the payload, modified or unmodified");let o=this.bindings.filter(r=>r.event===e);for(let r=0;rl.abort(),n),a.signal=l.signal),y.fetch(t,a).then(f=>f.text()).then(f=>this.parseJSON(f)).then(f=>{c&&clearTimeout(c),r&&r(f)}).catch(f=>{c&&clearTimeout(c),f.name==="AbortError"&&o?o():r&&r(null)}),l}static xdomainRequest(e,t,i,s,n,o,r){return e.timeout=n,e.open(t,i),e.onload=()=>{let a=this.parseJSON(e.responseText);r&&r(a)},o&&(e.ontimeout=o),e.onprogress=()=>{},e.send(s),e}static xhrRequest(e,t,i,s,n,o,r,a){e.open(t,i,!0),e.timeout=o;for(let[l,c]of Object.entries(s))e.setRequestHeader(l,c);return e.onerror=()=>a&&a(null),e.onreadystatechange=()=>{if(e.readyState===U.complete&&a){let l=this.parseJSON(e.responseText);a(l)}},r&&(e.ontimeout=r),e.send(n),e}static parseJSON(e){if(!e||e==="")return null;try{return JSON.parse(e)}catch(t){return console&&console.log("failed to parse JSON response",e),null}}static serialize(e,t){let i=[];for(var s in e){if(!Object.prototype.hasOwnProperty.call(e,s))continue;let n=t?`${t}[${s}]`:s,o=e[s];typeof o=="object"?i.push(this.serialize(o,n)):i.push(encodeURIComponent(n)+"="+encodeURIComponent(o))}return i.join("&")}static appendParams(e,t){if(Object.keys(t).length===0)return e;let i=e.match(/\?/)?"&":"?";return`${e}${i}${this.serialize(t)}`}};var q=h=>{let e="",t=new Uint8Array(h),i=t.byteLength;for(let s=0;sthis.poll(),0)}normalizeEndpoint(e){return e.replace("ws://","http://").replace("wss://","https://").replace(new RegExp("(.*)/"+A.websocket),"$1/"+A.longpoll)}endpointURL(){return this.pollEndpoint}closeAndRetry(e,t,i,s){this.isActive()&&(this.stop(p.connecting),this.onerror(e),this.isActive()&&this.emitClose(t,i,s))}ontimeout(){this.closeAndRetry("timeout",1005,"timeout",!1)}isActive(){return this.readyState===p.open||this.readyState===p.connecting}poll(){if(!this.isActive())return;let e={Accept:"application/json"};this.authToken&&(e["X-Phoenix-AuthToken"]=this.authToken),this.ajax("GET",e,null,()=>this.ontimeout(),t=>{if(t){var{status:i,token:s,messages:n}=t;if(i===410&&this.token!==null){this.closeAndRetry(410,3410,"session_gone",!1);return}this.token=s}else i=0;switch(i){case 200:n.forEach(o=>{let r=setTimeout(()=>{this.messageTimers.delete(r),this.onmessage({data:o})},0);this.messageTimers.add(r)}),this.poll();break;case 204:this.poll();break;case 410:this.readyState=p.open,this.onopen({}),this.poll();break;case 403:this.onerror(403),this.close(1008,"forbidden",!1);break;case 0:case 500:this.closeAndRetry(500,1011,"internal server error",!1);break;default:throw new Error(`unhandled poll status ${i}`)}})}send(e){typeof e!="string"&&(e=q(e)),this.currentBatch?this.currentBatch.push(e):this.awaitingBatchAck?this.batchBuffer.push(e):(this.currentBatch=[e],this.currentBatchTimer=setTimeout(()=>{this.batchSend(this.currentBatch),this.currentBatch=null},0))}batchSend(e,t=0){this.awaitingBatchAck=!0;let i=t+P,s=e.slice(t,i);this.ajax("POST",{"Content-Type":"application/x-ndjson"},s.join(` -`),()=>this.ontimeout(),n=>{!n||n.status!==200?this.closeAndRetry(n&&n.status,1011,"internal server error",!1):i0?(this.batchSend(this.batchBuffer),this.batchBuffer=[]):this.awaitingBatchAck=!1})}close(e,t,i){this.stop(p.closed),this.emitClose(e,t,i)}stop(e){this.readyState=e;let t=this.reqs;this.reqs=new Set;for(let{req:i}of t)i&&i.abort();for(let i of this.messageTimers)clearTimeout(i);this.messageTimers.clear(),this.batchBuffer=[],this.awaitingBatchAck=!1,clearTimeout(this.currentBatchTimer),this.currentBatchTimer=null,this.currentBatch=null}emitClose(e=1005,t,i=!0){let s={code:e,reason:t,wasClean:i};typeof CloseEvent!="undefined"?this.onclose(new CloseEvent("close",s)):this.onclose(s)}ajax(e,t,i,s,n){let o={req:null};this.reqs.add(o);let r=()=>{this.reqs.delete(o)&&s()};this.token!==null&&(t=Object.assign({},t,{"X-Phoenix-Longpoll-Token":this.token})),o.req=k.request(e,this.endpointURL(),t,i,this.timeout,r,a=>{this.reqs.delete(o)&&n(a)})}};var w=class h{constructor(e,t={}){let i=t.events||{state:"presence_state",diff:"presence_diff"};this.state=Object.create(null),this.pendingDiffs=[],this.channel=e,this.joinRef=null,this.caller={onJoin:function(){},onLeave:function(){},onSync:function(){}},this.channel.on(i.state,s=>{let{onJoin:n,onLeave:o,onSync:r}=this.caller;this.joinRef=this.channel.joinRef(),this.state=h.syncState(this.state,s,n,o),this.pendingDiffs.forEach(a=>{this.state=h.syncDiff(this.state,a,n,o)}),this.pendingDiffs=[],r()}),this.channel.on(i.diff,s=>{let{onJoin:n,onLeave:o,onSync:r}=this.caller;this.inPendingSyncState()?this.pendingDiffs.push(s):(this.state=h.syncDiff(this.state,s,n,o),r())})}onJoin(e){this.caller.onJoin=e}onLeave(e){this.caller.onLeave=e}onSync(e){this.caller.onSync=e}list(e){return h.list(this.state,e)}inPendingSyncState(){return!this.joinRef||this.joinRef!==this.channel.joinRef()}static syncState(e,t,i,s){let n=this.toNullProtoObj(this.clone(e));t=this.toNullProtoObj(t);let o=Object.create(null),r=Object.create(null);return this.map(n,(a,l)=>{t[a]||(r[a]=l)}),this.map(t,(a,l)=>{let c=n[a];if(c){let f=l.metas.map(u=>u.phx_ref),m=c.metas.map(u=>u.phx_ref),d=l.metas.filter(u=>m.indexOf(u.phx_ref)<0),C=c.metas.filter(u=>f.indexOf(u.phx_ref)<0);d.length>0&&(o[a]=l,o[a].metas=d),C.length>0&&(r[a]=this.clone(c),r[a].metas=C)}else o[a]=l}),this.syncDiff(n,{joins:o,leaves:r},i,s)}static syncDiff(e,t,i,s){e=this.toNullProtoObj(e);let{joins:n,leaves:o}=this.clone(t);return i||(i=function(){}),s||(s=function(){}),this.map(n,(r,a)=>{let l=e[r];if(e[r]=this.clone(a),l){let c=e[r].metas.map(m=>m.phx_ref),f=l.metas.filter(m=>c.indexOf(m.phx_ref)<0);e[r].metas.unshift(...f)}i(r,l,a)}),this.map(o,(r,a)=>{let l=e[r];if(!l)return;let c=a.metas.map(f=>f.phx_ref);l.metas=l.metas.filter(f=>c.indexOf(f.phx_ref)<0),s(r,l,a),l.metas.length===0&&delete e[r]}),e}static list(e,t){return t||(t=function(i,s){return s}),this.map(e,(i,s)=>t(i,s))}static map(e,t){return Object.getOwnPropertyNames(e).map(i=>t(i,e[i]))}static toNullProtoObj(e){if(Object.getPrototypeOf(e)===null)return e;let t=Object.create(null);return Object.getOwnPropertyNames(e).forEach(i=>{t[i]=e[i]}),t}static clone(e){return JSON.parse(JSON.stringify(e))}};var L={HEADER_LENGTH:1,META_LENGTH:4,KINDS:{push:0,reply:1,broadcast:2},encode(h,e){if(h.payload.constructor===ArrayBuffer)return e(this.binaryEncode(h));{let t=[h.join_ref,h.ref,h.topic,h.event,h.payload];return e(JSON.stringify(t))}},decode(h,e){if(h.constructor===ArrayBuffer)return e(this.binaryDecode(h));{let[t,i,s,n,o]=JSON.parse(h);return e({join_ref:t,ref:i,topic:s,event:n,payload:o})}},binaryEncode(h){let{join_ref:e,ref:t,event:i,topic:s,payload:n}=h,o=new TextEncoder,r=o.encode(e),a=o.encode(t),l=o.encode(s),c=o.encode(i);this.assertFieldSize(r.byteLength,"join_ref"),this.assertFieldSize(a.byteLength,"ref"),this.assertFieldSize(l.byteLength,"topic"),this.assertFieldSize(c.byteLength,"event");let f=this.META_LENGTH+r.byteLength+a.byteLength+l.byteLength+c.byteLength,m=new ArrayBuffer(this.HEADER_LENGTH+f),d=new Uint8Array(m),C=new DataView(m),u=0;C.setUint8(u++,this.KINDS.push),C.setUint8(u++,r.byteLength),C.setUint8(u++,a.byteLength),C.setUint8(u++,l.byteLength),C.setUint8(u++,c.byteLength),d.set(r,u),u+=r.byteLength,d.set(a,u),u+=a.byteLength,d.set(l,u),u+=l.byteLength,d.set(c,u),u+=c.byteLength;var x=new Uint8Array(m.byteLength+n.byteLength);return x.set(d,0),x.set(new Uint8Array(n),m.byteLength),x.buffer},assertFieldSize(h,e){if(h>255)throw new Error(`unable to convert ${e} to binary: must be less than or equal to 255 bytes, but is ${h} bytes`)},binaryDecode(h){let e=new DataView(h),t=e.getUint8(0),i=new TextDecoder;switch(t){case this.KINDS.push:return this.decodePush(h,e,i);case this.KINDS.reply:return this.decodeReply(h,e,i);case this.KINDS.broadcast:return this.decodeBroadcast(h,e,i)}},decodePush(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=e.getUint8(3),o=this.HEADER_LENGTH+this.META_LENGTH-1,r=t.decode(h.slice(o,o+i));o=o+i;let a=t.decode(h.slice(o,o+s));o=o+s;let l=t.decode(h.slice(o,o+n));o=o+n;let c=h.slice(o,h.byteLength);return{join_ref:r,ref:null,topic:a,event:l,payload:c}},decodeReply(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=e.getUint8(3),o=e.getUint8(4),r=this.HEADER_LENGTH+this.META_LENGTH,a=t.decode(h.slice(r,r+i));r=r+i;let l=t.decode(h.slice(r,r+s));r=r+s;let c=t.decode(h.slice(r,r+n));r=r+n;let f=t.decode(h.slice(r,r+o));r=r+o;let m=h.slice(r,h.byteLength),d={status:f,response:m};return{join_ref:a,ref:l,topic:c,event:T.reply,payload:d}},decodeBroadcast(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=this.HEADER_LENGTH+2,o=t.decode(h.slice(n,n+i));n=n+i;let r=t.decode(h.slice(n,n+s));n=n+s;let a=h.slice(n,h.byteLength);return{join_ref:null,ref:null,topic:o,event:r,payload:a}}};var _=class{constructor(e,t){this.socket=e,this.transport=t,this.joins=new WeakMap,this.pings=new Map,this.closing=!1,this.ended=!1,this.channelsErrored=!1,this.closeHandled=!1,t.onopen=()=>{this.isCurrent()&&e.onConnOpen()},t.onerror=i=>{this.isCurrent()&&e.onConnError(i)},t.onmessage=i=>{this.isCurrent()&&e.onConnMessage(i)},t.onclose=i=>{this.isCurrent()&&!this.closeHandled&&(this.closeHandled=!0,e.onConnClose(i))}}isCurrent(){return this.socket.connection===this}isOpen(){return this.transport.readyState===p.open}isConnecting(){return this.transport.readyState===p.connecting}carried(e,t=e.joinRef()){return this.joins.has(e)&&this.joins.get(e)===t}close(e,t){this.closing=!0,this.transport.onerror=function(){},e?this.transport.close(e,t||""):this.transport.close()}};var H=class{constructor(e,t={}){this.stateChangeCallbacks={open:[],close:[],error:[],message:[]},this.channels=[],this.sendBuffer=[],this.ref=0,this.fallbackRef=null,this.fallbackErrorRef=null,this.timeout=t.timeout||$,this.transport=t.transport||y.WebSocket||b,this.primaryPassedHealthCheck=!1,this.longPollFallbackMs=t.longPollFallbackMs,this.fallbackTimer=null,this.sessionStore=t.sessionStorage||y&&y.sessionStorage,this.establishedConnections=0,this.defaultEncoder=L.encode.bind(L),this.defaultDecoder=L.decode.bind(L),this.closeWasClean=!0,this.binaryType=t.binaryType||"arraybuffer",this.connection=null,this.connectClock=1,this.visibilityConnection=null,this.transport!==b?(this.encode=t.encode||this.defaultEncoder,this.decode=t.decode||this.defaultDecoder):(this.encode=this.defaultEncoder,this.decode=this.defaultDecoder);let i=null;E&&E.addEventListener&&(E.addEventListener("pagehide",s=>{this.connection&&!this.connection.ended&&(this.disconnect(),i=this.connectClock)}),E.addEventListener("pageshow",s=>{i===this.connectClock&&(i=null,this.connect())}),E.addEventListener("visibilitychange",()=>{this.handleVisibilityChange()}),E.document&&E.document.addEventListener("resume",()=>{this.handleVisibilityChange()})),this.heartbeatIntervalMs=t.heartbeatIntervalMs||3e4,this.rejoinAfterMs=s=>t.rejoinAfterMs?t.rejoinAfterMs(s):[1e3,2e3,5e3][s-1]||1e4,this.reconnectAfterMs=s=>t.reconnectAfterMs?t.reconnectAfterMs(s):[10,50,100,150,200,250,500,1e3,2e3][s-1]||5e3,this.reconnectWhenHidden=t.reconnectWhenHidden||!1,this.logger=t.logger||null,!this.logger&&t.debug&&(this.logger=(s,n,o)=>{console.log(`${s}: ${n}`,o)}),this.longpollerTimeout=t.longpollerTimeout||2e4,this.params=S(t.params||{}),this.endPoint=`${e}/${A.websocket}`,this.vsn=t.vsn||B,this.heartbeatTimeoutTimer=null,this.heartbeatTimer=null,this.pendingHeartbeatRef=null,this.reconnectTimer=new R(()=>{if(this.pageHidden&&!this.reconnectWhenHidden){this.log("Not reconnecting as page is hidden!"),this.teardown();return}this.teardownAndReconnect(()=>this.connect())},this.reconnectAfterMs),this.authToken=t.authToken&&S(t.authToken)}get conn(){return this.connection&&this.connection.transport}get pageHidden(){return E&&E.document?E.document.visibilityState==="hidden":!1}handleVisibilityChange(){if(this.pageHidden){this.visibilityConnection=null;return}let e=this.connection;e&&e===this.visibilityConnection&&e.isConnecting()||!this.isConnected()&&!this.closeWasClean&&(this.reconnectTimer.reset(),this.teardownAndReconnect(()=>{this.connect(),this.visibilityConnection=this.connection}))}getLongPollTransport(){return b}replaceTransport(e){let t=++this.connectClock;this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.transport=e;let i=this.connection;if(i){let s=i.isOpen();this.connection=null,i.ended=!0,i.close(),this.clearHeartbeats(),s&&this.triggerChanError("connection_closed",i)}return t}protocol(){return location.protocol.match(/^https/)?"wss":"ws"}endPointURL(){let e=k.appendParams(k.appendParams(this.endPoint,this.params()),{vsn:this.vsn});return e.charAt(0)!=="/"?e:e.charAt(1)==="/"?`${this.protocol()}:${e}`:`${this.protocol()}://${location.host}${e}`}disconnect(e,t,i){this.connectClock++,this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.connection&&(this.connection.ended=!0),this.teardown(e,t,i)}connect(e){e&&(console&&console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor"),this.params=S(e)),!(this.connection&&!this.connection.ended)&&(this.longPollFallbackMs&&this.transport!==b?this.connectWithFallback(b,this.longPollFallbackMs):this.transportConnect())}log(e,t,i){this.logger&&this.logger(e,t,i)}hasLogger(){return this.logger!==null}onOpen(e){let t=this.makeRef();return this.stateChangeCallbacks.open.push([t,e]),t}onClose(e){let t=this.makeRef();return this.stateChangeCallbacks.close.push([t,e]),t}onError(e){let t=this.makeRef();return this.stateChangeCallbacks.error.push([t,e]),t}onMessage(e){let t=this.makeRef();return this.stateChangeCallbacks.message.push([t,e]),t}ping(e){if(!this.isConnected())return!1;let t=this.makeRef(),i=Date.now();return this.connection.pings.set(t,()=>e(Date.now()-i)),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:t}),!0}transportName(e){switch(e){case b:return"LongPoll";default:return e.name}}transportConnect(){let e=this.connection;this.connectClock++,this.closeWasClean=!1,clearTimeout(this.reconnectTimer.timer);let t;this.authToken&&(t=["phoenix",`${O}${btoa(this.authToken()).replace(/=/g,"")}`]);let i=new this.transport(this.endPointURL(),t);i.binaryType=this.binaryType,i.timeout=this.longpollerTimeout,this.connection=new _(this,i),e&&e.isOpen()&&this.triggerChanError("connection_closed",e)}getSession(e){return this.sessionStore&&this.sessionStore.getItem(e)}storeSession(e,t){this.sessionStore&&this.sessionStore.setItem(e,t)}connectWithFallback(e,t=2500){this.cancelFallback();let i=!1,s=!0,n,o,r=()=>o===this.connectClock&&!this.closeWasClean,a=this.transportName(e),l=c=>{this.log("transport",`falling back to ${a}...`,c),this.off([n]),s=!1,this.replaceTransport(e)===this.connectClock&&this.transportConnect()};if(this.getSession(`phx:fallback:${a}`))return l("memorized");this.fallbackTimer=setTimeout(l,t),n=this.fallbackErrorRef=this.onError(c=>{r()&&(this.log("transport","error",c),s&&!i&&(clearTimeout(this.fallbackTimer),l(c)))}),this.fallbackRef=this.onOpen(()=>{if(!s){if(this.transport!==e||this.closeWasClean)return;let c=this.transportName(e);return this.primaryPassedHealthCheck||this.storeSession(`phx:fallback:${c}`,"true"),this.log("transport",`established ${c} fallback`)}r()&&(i=!0,clearTimeout(this.fallbackTimer),this.fallbackTimer=setTimeout(l,t),this.ping(c=>{this.log("transport","connected to primary after",c),this.primaryPassedHealthCheck=!0,clearTimeout(this.fallbackTimer)}))}),this.transportConnect(),o=this.connectClock}cancelFallback(){clearTimeout(this.fallbackTimer),this.off([this.fallbackRef,this.fallbackErrorRef])}clearHeartbeats(){clearTimeout(this.heartbeatTimer),clearTimeout(this.heartbeatTimeoutTimer),this.pendingHeartbeatRef=null}onConnOpen(){this.hasLogger()&&this.log("transport",`${this.transportName(this.transport)} connected to ${this.endPointURL()}`),this.closeWasClean=!1,this.establishedConnections++,this.pingBeforeBufferedJoin(),this.flushSendBuffer(),this.reconnectTimer.reset(),this.resetHeartbeat(),this.stateChangeCallbacks.open.forEach(([,e])=>e())}pingBeforeBufferedJoin(){!this.fallbackTimer||this.transport===b||this.ping(()=>this.passPrimaryHealthCheck())}passPrimaryHealthCheck(){this.transport!==b&&(this.primaryPassedHealthCheck=!0,clearTimeout(this.fallbackTimer),this.fallbackTimer=null)}heartbeatTimeout(){if(this.pendingHeartbeatRef){let e=this.connection;if(this.pendingHeartbeatRef=null,this.hasLogger()&&this.log("transport","heartbeat timeout. Attempting to re-establish connection"),this.closeWasClean=!1,e&&e.close(M,"heartbeat timeout"),!this.stillUses(e)||(this.triggerChanError("heartbeat_timeout"),!this.stillUses(e)))return;this.teardownAndReconnect(()=>this.reconnectTimer.scheduleTimeout())}}resetHeartbeat(){this.conn&&this.conn.skipHeartbeat||(this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs))}teardownAndReconnect(e){let t=this.connection,i=this.connectClock;this.teardown(()=>{i!==this.connectClock||t&&t.ended||e()})}stillUses(e){return this.connection===e&&!(e&&e.ended)}teardown(e,t,i){let s=this.connection;if(!s)return e&&e();this.clearHeartbeats(),this.waitForBufferDone(s.transport,()=>{if(!s.closing){let n=!s.isConnecting();s.close(t,i),n&&this.triggerChanError("connection_closed",s)}this.waitForSocketClosed(s.transport,()=>{this.connection===s&&(this.connection=null),e&&e()})})}waitForBufferDone(e,t,i=1){if(i===5||!e.bufferedAmount||e.readyState!==p.open){t();return}setTimeout(()=>{this.waitForBufferDone(e,t,i+1)},150*i)}waitForSocketClosed(e,t,i=1){if(i===5||e.readyState===p.closed){t();return}setTimeout(()=>{this.waitForSocketClosed(e,t,i+1)},150*i)}onConnClose(e){let t=this.connection;this.hasLogger()&&this.log("transport","close",e),this.clearHeartbeats();let i=!!(t&&t.closing);this.triggerChanError("connection_closed",t),this.stillUses(t)&&!i&&!this.closeWasClean&&this.reconnectTimer.scheduleTimeout(),this.stateChangeCallbacks.close.forEach(([,s])=>s(e))}onConnError(e){let t=this.connection;this.hasLogger()&&this.log("transport","error",e);let i=this.transport,s=this.establishedConnections;this.stateChangeCallbacks.error.forEach(([,n])=>{n(e,i,s)}),this.triggerChanError("connection_error",t)}triggerChanError(e,t=this.connection){let i=!1;t&&!t.isOpen()&&(i=t.channelsErrored,t.channelsErrored=!0),this.channels.map(n=>[n,n.joinRef()]).forEach(([n,o])=>{n.joinRef()===o&&(t&&(i||!t.isCurrent())&&!t.carried(n,o)||n.isErrored()||n.isLeaving()||n.isClosed()||n.trigger(T.error,{source:"transport",reason:e}))})}connectionState(){switch(this.conn&&this.conn.readyState){case p.connecting:return"connecting";case p.open:return"open";case p.closing:return"closing";default:return"closed"}}isConnected(){return this.connectionState()==="open"}remove(e){this.off(e.stateChangeRefs),this.channels=this.channels.filter(t=>t!==e)}off(e){for(let t in this.stateChangeCallbacks)this.stateChangeCallbacks[t]=this.stateChangeCallbacks[t].filter(([i])=>e.indexOf(i)===-1)}channel(e,t={}){let i=new j(e,t,this);return this.channels.push(i),i}push(e,t){if(this.hasLogger()){let{topic:i,event:s,payload:n,ref:o,join_ref:r}=e;this.log("push",`${i} ${s} (${r}, ${o})`,n)}this.isConnected()?this.send(e,t):this.sendBuffer.push(()=>{this.isOutdated(e,t)||this.send(e,t)})}send(e,t){let i=this.connection;t&&e.event===T.join&&i.joins.set(t,e.join_ref),this.encode(e,s=>{!i.isCurrent()||!i.isOpen()||e.event!==T.leave&&this.isOutdated(e,t)||i.transport.send(s)})}isOutdated(e,t){return!!t&&(e.join_ref!==t.joinRef()||t.isClosed())}makeRef(){let e=this.ref+1;return e===this.ref?this.ref=0:this.ref=e,this.ref.toString()}sendHeartbeat(){this.pendingHeartbeatRef&&!this.isConnected()||(this.pendingHeartbeatRef=this.makeRef(),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:this.pendingHeartbeatRef}),this.heartbeatTimeoutTimer=setTimeout(()=>this.heartbeatTimeout(),this.heartbeatIntervalMs))}flushSendBuffer(){this.isConnected()&&this.sendBuffer.length>0&&(this.sendBuffer.forEach(e=>e()),this.sendBuffer=[])}onConnMessage(e){let t=this.connection,i=t&&t.isOpen();this.decode(e.data,s=>{if(t!==this.connection||i&&!t.isOpen())return;this.fallbackTimer&&this.passPrimaryHealthCheck();let{topic:n,event:o,payload:r,ref:a,join_ref:l}=s;a&&a===this.pendingHeartbeatRef&&(this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs)),this.hasLogger()&&this.log("receive",`${r.status||""} ${n} ${o} ${a&&"("+a+")"||""}`,r);let c=this.channels;for(let d=0;di.topic===e&&(i.isJoined()||i.isJoining()));t&&(this.hasLogger()&&this.log("transport",`leaving duplicate topic "${e}"`),t.leave())}};return J(G);})(); +var Phoenix=(()=>{var H=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var I=Object.prototype.hasOwnProperty;var W=(h,e)=>{for(var t in e)H(h,t,{get:e[t],enumerable:!0})},z=(h,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of F(e))!I.call(h,s)&&s!==t&&H(h,s,{get:()=>e[s],enumerable:!(i=D(e,s))||i.enumerable});return h};var J=h=>z(H({},"__esModule",{value:!0}),h);var G={};W(G,{Channel:()=>j,LongPoll:()=>S,Presence:()=>w,Serializer:()=>L,Socket:()=>x});var y=h=>typeof h=="function"?h:function(){return h};var X=typeof self!="undefined"?self:null,C=typeof window!="undefined"?window:null,E=X||C||globalThis,B="2.0.0",p={connecting:0,open:1,closing:2,closed:3},P=100,$=1e4,M=1e3,m={closed:"closed",errored:"errored",joined:"joined",joining:"joining",leaving:"leaving"},T={close:"phx_close",error:"phx_error",join:"phx_join",reply:"phx_reply",leave:"phx_leave"},A={longpoll:"longpoll",websocket:"websocket"},U={complete:4},O="base64url.bearer.phx.";var v=class{constructor(e,t,i,s){this.channel=e,this.event=t,this.payload=i||function(){return{}},this.receivedResp=null,this.timeout=s,this.timeoutTimer=null,this.recHooks=[],this.sent=!1}resend(e){this.timeout=e,this.reset(),this.send()}send(){this.hasReceived("timeout")||(this.startTimeout(),this.sent=!0,this.channel.socket.push({topic:this.channel.topic,event:this.event,payload:this.payload(),ref:this.ref,join_ref:this.channel.joinRef()},this.channel))}receive(e,t){return this.hasReceived(e)&&t(this.receivedResp.response),this.recHooks.push({status:e,callback:t}),this}reset(){this.cancelRefEvent(),this.ref=null,this.refEvent=null,this.receivedResp=null,this.sent=!1}matchReceive({status:e,response:t,_ref:i}){this.recHooks.filter(s=>s.status===e).forEach(s=>s.callback(t))}cancelRefEvent(){this.refEvent&&this.channel.off(this.refEvent)}cancelTimeout(){clearTimeout(this.timeoutTimer),this.timeoutTimer=null}startTimeout(){this.timeoutTimer&&this.cancelTimeout(),this.cancelRefEvent(),this.ref=this.channel.socket.makeRef(),this.refEvent=this.channel.replyEventName(this.ref),this.channel.on(this.refEvent,e=>{this.cancelRefEvent(),this.cancelTimeout(),this.receivedResp=e,this.matchReceive(e)}),this.timeoutTimer=setTimeout(()=>{this.trigger("timeout",{})},this.timeout)}hasReceived(e){return this.receivedResp&&this.receivedResp.status===e}trigger(e,t){this.channel.trigger(this.refEvent,{status:e,response:t})}};var k=class{constructor(e,t){this.callback=e,this.timerCalc=t,this.timer=null,this.tries=0}reset(){this.tries=0,clearTimeout(this.timer)}scheduleTimeout(){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.tries=this.tries+1,this.callback()},this.timerCalc(this.tries+1))}};var j=class{constructor(e,t,i){this.state=m.closed,this.topic=e,this.params=y(t||{}),this.socket=i,this.bindings=[],this.bindingRef=0,this.timeout=this.socket.timeout,this.joinedOnce=!1,this.joinPush=new v(this,T.join,this.params,this.timeout),this.pushBuffer=[],this.stateChangeRefs=[],this.rejoinTimer=new k(()=>{this.socket.isConnected()&&this.rejoin()},this.socket.rejoinAfterMs),this.stateChangeRefs.push(this.socket.onError(()=>this.rejoinTimer.reset())),this.stateChangeRefs.push(this.socket.onOpen(()=>{this.rejoinTimer.reset(),this.isErrored()&&this.rejoin()})),this.joinPush.receive("ok",()=>{this.state=m.joined,this.rejoinTimer.reset(),this.pushBuffer.forEach(s=>s.send()),this.pushBuffer=[]}),this.joinPush.receive("error",()=>{this.state=m.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.onClose(()=>{this.rejoinTimer.reset(),this.socket.hasLogger()&&this.socket.log("channel",`close ${this.topic} ${this.joinRef()}`),this.state=m.closed,this.socket.remove(this)}),this.onError(s=>{this.socket.hasLogger()&&this.socket.log("channel",`error ${this.topic}`,s),this.isJoining()&&this.joinPush.reset(),this.state=m.errored,this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.joinPush.receive("timeout",()=>{this.socket.hasLogger()&&this.socket.log("channel",`timeout ${this.topic} (${this.joinRef()})`,this.joinPush.timeout),new v(this,T.leave,y({}),this.timeout).send(),this.state=m.errored,this.joinPush.reset(),this.socket.isConnected()&&this.rejoinTimer.scheduleTimeout()}),this.on(T.reply,(s,n)=>{this.trigger(this.replyEventName(n),s)})}join(e=this.timeout){if(this.joinedOnce)throw new Error("tried to join multiple times. 'join' can only be called a single time per channel instance");return this.timeout=e,this.joinedOnce=!0,this.rejoin(),this.joinPush}onClose(e){this.on(T.close,e)}onError(e){return this.on(T.error,t=>e(t))}on(e,t){let i=this.bindingRef++;return this.bindings.push({event:e,ref:i,callback:t}),i}off(e,t){this.bindings=this.bindings.filter(i=>!(i.event===e&&(typeof t=="undefined"||t===i.ref)))}canPush(){return this.socket.isConnected()&&this.isJoined()}push(e,t,i=this.timeout){if(t=t||{},!this.joinedOnce)throw new Error(`tried to push '${e}' to '${this.topic}' before joining. Use channel.join() before pushing events`);let s=new v(this,e,function(){return t},i);return this.canPush()?s.send():(s.startTimeout(),this.pushBuffer.push(s)),s}leave(e=this.timeout){this.rejoinTimer.reset(),this.joinPush.cancelTimeout(),this.state=m.leaving;let t=()=>{this.socket.hasLogger()&&this.socket.log("channel",`leave ${this.topic}`),this.trigger(T.close,"leave")},i=new v(this,T.leave,y({}),e);return i.receive("ok",()=>t()).receive("timeout",()=>t()),i.send(),this.canPush()||i.trigger("ok",{}),i}onMessage(e,t,i){return t}isMember(e,t,i,s){return this.topic!==e?!1:s&&s!==this.joinRef()?(this.socket.hasLogger()&&this.socket.log("channel","dropping outdated message",{topic:e,event:t,payload:i,joinRef:s}),!1):!0}joinRef(){return this.joinPush.ref}rejoin(e=this.timeout){this.isLeaving()||(this.socket.leaveOpenTopic(this.topic),this.state=m.joining,this.joinPush.resend(e))}trigger(e,t,i,s){let n=this.onMessage(e,t,i,s);if(t&&!n)throw new Error("channel onMessage callbacks must return the payload, modified or unmodified");let o=this.bindings.filter(r=>r.event===e);for(let r=0;rl.abort(),n),a.signal=l.signal),E.fetch(t,a).then(f=>f.text()).then(f=>this.parseJSON(f)).then(f=>{c&&clearTimeout(c),r&&r(f)}).catch(f=>{c&&clearTimeout(c),f.name==="AbortError"&&o?o():r&&r(null)}),l}static xdomainRequest(e,t,i,s,n,o,r){return e.timeout=n,e.open(t,i),e.onload=()=>{let a=this.parseJSON(e.responseText);r&&r(a)},o&&(e.ontimeout=o),e.onprogress=()=>{},e.send(s),e}static xhrRequest(e,t,i,s,n,o,r,a){e.open(t,i,!0),e.timeout=o;for(let[l,c]of Object.entries(s))e.setRequestHeader(l,c);return e.onerror=()=>a&&a(null),e.onreadystatechange=()=>{if(e.readyState===U.complete&&a){let l=this.parseJSON(e.responseText);a(l)}},r&&(e.ontimeout=r),e.send(n),e}static parseJSON(e){if(!e||e==="")return null;try{return JSON.parse(e)}catch(t){return console&&console.log("failed to parse JSON response",e),null}}static serialize(e,t){let i=[];for(var s in e){if(!Object.prototype.hasOwnProperty.call(e,s))continue;let n=t?`${t}[${s}]`:s,o=e[s];typeof o=="object"?i.push(this.serialize(o,n)):i.push(encodeURIComponent(n)+"="+encodeURIComponent(o))}return i.join("&")}static appendParams(e,t){if(Object.keys(t).length===0)return e;let i=e.match(/\?/)?"&":"?";return`${e}${i}${this.serialize(t)}`}};var q=h=>{let e="",t=new Uint8Array(h),i=t.byteLength;for(let s=0;sthis.poll(),0)}normalizeEndpoint(e){return e.replace("ws://","http://").replace("wss://","https://").replace(new RegExp("(.*)/"+A.websocket),"$1/"+A.longpoll)}endpointURL(){return this.pollEndpoint}closeAndRetry(e,t,i,s){this.isActive()&&(this.stop(p.connecting),this.onerror(e),this.isActive()&&this.emitClose(t,i,s))}ontimeout(){this.closeAndRetry("timeout",1005,"timeout",!1)}isActive(){return this.readyState===p.open||this.readyState===p.connecting}poll(){if(!this.isActive())return;let e={Accept:"application/json"};this.authToken&&(e["X-Phoenix-AuthToken"]=this.authToken),this.ajax("GET",e,null,()=>this.ontimeout(),t=>{if(t){var{status:i,token:s,messages:n}=t;if(i===410&&this.token!==null){this.closeAndRetry(410,3410,"session_gone",!1);return}this.token=s}else i=0;switch(i){case 200:n.forEach(o=>{let r=setTimeout(()=>{this.messageTimers.delete(r),this.onmessage({data:o})},0);this.messageTimers.add(r)}),this.poll();break;case 204:this.poll();break;case 410:this.readyState=p.open,this.onopen({}),this.poll();break;case 403:this.onerror(403),this.close(1008,"forbidden",!1);break;case 0:case 500:this.closeAndRetry(500,1011,"internal server error",!1);break;default:throw new Error(`unhandled poll status ${i}`)}})}send(e){typeof e!="string"&&(e=q(e)),this.currentBatch?this.currentBatch.push(e):this.awaitingBatchAck?this.batchBuffer.push(e):(this.currentBatch=[e],this.currentBatchTimer=setTimeout(()=>{this.batchSend(this.currentBatch),this.currentBatch=null},0))}batchSend(e,t=0){this.awaitingBatchAck=!0;let i=t+P,s=e.slice(t,i);this.ajax("POST",{"Content-Type":"application/x-ndjson"},s.join(` +`),()=>this.ontimeout(),n=>{!n||n.status!==200?this.closeAndRetry(n&&n.status,1011,"internal server error",!1):i0?(this.batchSend(this.batchBuffer),this.batchBuffer=[]):this.awaitingBatchAck=!1})}close(e,t,i){this.stop(p.closed),this.emitClose(e,t,i)}stop(e){this.readyState=e;let t=this.reqs;this.reqs=new Set;for(let{req:i}of t)i&&i.abort();for(let i of this.messageTimers)clearTimeout(i);this.messageTimers.clear(),this.batchBuffer=[],this.awaitingBatchAck=!1,clearTimeout(this.currentBatchTimer),this.currentBatchTimer=null,this.currentBatch=null}emitClose(e=1005,t,i=!0){let s={code:e,reason:t,wasClean:i};typeof CloseEvent!="undefined"?this.onclose(new CloseEvent("close",s)):this.onclose(s)}ajax(e,t,i,s,n){let o={req:null};this.reqs.add(o);let r=()=>{this.reqs.delete(o)&&s()};this.token!==null&&(t=Object.assign({},t,{"X-Phoenix-Longpoll-Token":this.token})),o.req=R.request(e,this.endpointURL(),t,i,this.timeout,r,a=>{this.reqs.delete(o)&&n(a)})}};var w=class h{constructor(e,t={}){let i=t.events||{state:"presence_state",diff:"presence_diff"};this.state=Object.create(null),this.pendingDiffs=[],this.channel=e,this.joinRef=null,this.caller={onJoin:function(){},onLeave:function(){},onSync:function(){}},this.channel.on(i.state,s=>{let{onJoin:n,onLeave:o,onSync:r}=this.caller;this.joinRef=this.channel.joinRef(),this.state=h.syncState(this.state,s,n,o),this.pendingDiffs.forEach(a=>{this.state=h.syncDiff(this.state,a,n,o)}),this.pendingDiffs=[],r()}),this.channel.on(i.diff,s=>{let{onJoin:n,onLeave:o,onSync:r}=this.caller;this.inPendingSyncState()?this.pendingDiffs.push(s):(this.state=h.syncDiff(this.state,s,n,o),r())})}onJoin(e){this.caller.onJoin=e}onLeave(e){this.caller.onLeave=e}onSync(e){this.caller.onSync=e}list(e){return h.list(this.state,e)}inPendingSyncState(){return!this.joinRef||this.joinRef!==this.channel.joinRef()}static syncState(e,t,i,s){let n=this.toNullProtoObj(this.clone(e));t=this.toNullProtoObj(t);let o=Object.create(null),r=Object.create(null);return this.map(n,(a,l)=>{t[a]||(r[a]=l)}),this.map(t,(a,l)=>{let c=n[a];if(c){let f=l.metas.map(u=>u.phx_ref),g=c.metas.map(u=>u.phx_ref),d=l.metas.filter(u=>g.indexOf(u.phx_ref)<0),b=c.metas.filter(u=>f.indexOf(u.phx_ref)<0);d.length>0&&(o[a]=l,o[a].metas=d),b.length>0&&(r[a]=this.clone(c),r[a].metas=b)}else o[a]=l}),this.syncDiff(n,{joins:o,leaves:r},i,s)}static syncDiff(e,t,i,s){e=this.toNullProtoObj(e);let{joins:n,leaves:o}=this.clone(t);return i||(i=function(){}),s||(s=function(){}),this.map(n,(r,a)=>{let l=e[r];if(e[r]=this.clone(a),l){let c=e[r].metas.map(g=>g.phx_ref),f=l.metas.filter(g=>c.indexOf(g.phx_ref)<0);e[r].metas.unshift(...f)}i(r,l,a)}),this.map(o,(r,a)=>{let l=e[r];if(!l)return;let c=a.metas.map(f=>f.phx_ref);l.metas=l.metas.filter(f=>c.indexOf(f.phx_ref)<0),s(r,l,a),l.metas.length===0&&delete e[r]}),e}static list(e,t){return t||(t=function(i,s){return s}),this.map(e,(i,s)=>t(i,s))}static map(e,t){return Object.getOwnPropertyNames(e).map(i=>t(i,e[i]))}static toNullProtoObj(e){if(Object.getPrototypeOf(e)===null)return e;let t=Object.create(null);return Object.getOwnPropertyNames(e).forEach(i=>{t[i]=e[i]}),t}static clone(e){return JSON.parse(JSON.stringify(e))}};var L={HEADER_LENGTH:1,META_LENGTH:4,KINDS:{push:0,reply:1,broadcast:2},encode(h,e){if(h.payload.constructor===ArrayBuffer)return e(this.binaryEncode(h));{let t=[h.join_ref,h.ref,h.topic,h.event,h.payload];return e(JSON.stringify(t))}},decode(h,e){if(h.constructor===ArrayBuffer)return e(this.binaryDecode(h));{let[t,i,s,n,o]=JSON.parse(h);return e({join_ref:t,ref:i,topic:s,event:n,payload:o})}},binaryEncode(h){let{join_ref:e,ref:t,event:i,topic:s,payload:n}=h,o=new TextEncoder,r=o.encode(e),a=o.encode(t),l=o.encode(s),c=o.encode(i);this.assertFieldSize(r.byteLength,"join_ref"),this.assertFieldSize(a.byteLength,"ref"),this.assertFieldSize(l.byteLength,"topic"),this.assertFieldSize(c.byteLength,"event");let f=this.META_LENGTH+r.byteLength+a.byteLength+l.byteLength+c.byteLength,g=new ArrayBuffer(this.HEADER_LENGTH+f),d=new Uint8Array(g),b=new DataView(g),u=0;b.setUint8(u++,this.KINDS.push),b.setUint8(u++,r.byteLength),b.setUint8(u++,a.byteLength),b.setUint8(u++,l.byteLength),b.setUint8(u++,c.byteLength),d.set(r,u),u+=r.byteLength,d.set(a,u),u+=a.byteLength,d.set(l,u),u+=l.byteLength,d.set(c,u),u+=c.byteLength;var N=new Uint8Array(g.byteLength+n.byteLength);return N.set(d,0),N.set(new Uint8Array(n),g.byteLength),N.buffer},assertFieldSize(h,e){if(h>255)throw new Error(`unable to convert ${e} to binary: must be less than or equal to 255 bytes, but is ${h} bytes`)},binaryDecode(h){let e=new DataView(h),t=e.getUint8(0),i=new TextDecoder;switch(t){case this.KINDS.push:return this.decodePush(h,e,i);case this.KINDS.reply:return this.decodeReply(h,e,i);case this.KINDS.broadcast:return this.decodeBroadcast(h,e,i)}},decodePush(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=e.getUint8(3),o=this.HEADER_LENGTH+this.META_LENGTH-1,r=t.decode(h.slice(o,o+i));o=o+i;let a=t.decode(h.slice(o,o+s));o=o+s;let l=t.decode(h.slice(o,o+n));o=o+n;let c=h.slice(o,h.byteLength);return{join_ref:r,ref:null,topic:a,event:l,payload:c}},decodeReply(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=e.getUint8(3),o=e.getUint8(4),r=this.HEADER_LENGTH+this.META_LENGTH,a=t.decode(h.slice(r,r+i));r=r+i;let l=t.decode(h.slice(r,r+s));r=r+s;let c=t.decode(h.slice(r,r+n));r=r+n;let f=t.decode(h.slice(r,r+o));r=r+o;let g=h.slice(r,h.byteLength),d={status:f,response:g};return{join_ref:a,ref:l,topic:c,event:T.reply,payload:d}},decodeBroadcast(h,e,t){let i=e.getUint8(1),s=e.getUint8(2),n=this.HEADER_LENGTH+2,o=t.decode(h.slice(n,n+i));n=n+i;let r=t.decode(h.slice(n,n+s));n=n+s;let a=h.slice(n,h.byteLength);return{join_ref:null,ref:null,topic:o,event:r,payload:a}}};var _=class{constructor(e,t){this.socket=e,this.transport=t,this.joins=new WeakMap,this.pings=new Map,this.closing=!1,this.ended=!1,this.channelsErrored=!1,this.closeHandled=!1,t.onopen=()=>{this.isCurrent()&&e.onConnOpen()},t.onerror=i=>{this.isCurrent()&&e.onConnError(i)},t.onmessage=i=>{this.isCurrent()&&e.onConnMessage(i)},t.onclose=i=>{this.isCurrent()&&!this.closeHandled&&(this.closeHandled=!0,e.onConnClose(i))}}isCurrent(){return this.socket.connection===this}isOpen(){return this.transport.readyState===p.open}isConnecting(){return this.transport.readyState===p.connecting}carried(e,t=e.joinRef()){return this.joins.has(e)&&this.joins.get(e)===t}close(e,t){this.closing=!0,this.transport.onerror=function(){},e?this.transport.close(e,t||""):this.transport.close()}};var x=class{constructor(e,t={}){this.stateChangeCallbacks={open:[],close:[],error:[],message:[]},this.channels=[],this.sendBuffer=[],this.ref=0,this.fallbackRef=null,this.fallbackErrorRef=null,this.timeout=t.timeout||$,this.transport=t.transport||E.WebSocket||S,this.primaryPassedHealthCheck=!1,this.longPollFallbackMs=t.longPollFallbackMs,this.fallbackTimer=null,this.sessionStore=t.sessionStorage||E&&E.sessionStorage,this.establishedConnections=0,this.defaultEncoder=L.encode.bind(L),this.defaultDecoder=L.decode.bind(L),this.closeWasClean=!0,this.binaryType=t.binaryType||"arraybuffer",this.connection=null,this.connectClock=1,this.visibilityConnection=null,this.transport!==S?(this.encode=t.encode||this.defaultEncoder,this.decode=t.decode||this.defaultDecoder):(this.encode=this.defaultEncoder,this.decode=this.defaultDecoder);let i=null;C&&C.addEventListener&&(C.addEventListener("pagehide",s=>{this.connection&&!this.connection.ended&&(this.disconnect(),i=this.connectClock)}),C.addEventListener("pageshow",s=>{i===this.connectClock&&(i=null,this.connect())}),C.addEventListener("visibilitychange",()=>{this.handleVisibilityChange()}),C.document&&C.document.addEventListener("resume",()=>{this.handleVisibilityChange()})),this.heartbeatIntervalMs=t.heartbeatIntervalMs||3e4,this.rejoinAfterMs=s=>t.rejoinAfterMs?t.rejoinAfterMs(s):[1e3,2e3,5e3][s-1]||1e4,this.reconnectAfterMs=s=>t.reconnectAfterMs?t.reconnectAfterMs(s):[10,50,100,150,200,250,500,1e3,2e3][s-1]||5e3,this.reconnectWhenHidden=t.reconnectWhenHidden||!1,this.logger=t.logger||null,!this.logger&&t.debug&&(this.logger=(s,n,o)=>{console.log(`${s}: ${n}`,o)}),this.longpollerTimeout=t.longpollerTimeout||2e4,this.params=y(t.params||{}),this.endPoint=`${e}/${A.websocket}`,this.vsn=t.vsn||B,this.heartbeatTimeoutTimer=null,this.heartbeatTimer=null,this.pendingHeartbeatRef=null,this.reconnectTimer=new k(()=>{if(this.pageHidden&&!this.reconnectWhenHidden){this.log("Not reconnecting as page is hidden!"),this.teardown();return}this.teardownAndReconnect(()=>this.connect())},this.reconnectAfterMs),this.authToken=t.authToken&&y(t.authToken)}get conn(){return this.connection&&this.connection.transport}get pageHidden(){return C&&C.document?C.document.visibilityState==="hidden":!1}handleVisibilityChange(){if(this.pageHidden){this.visibilityConnection=null;return}let e=this.connection;e&&e===this.visibilityConnection&&e.isConnecting()||!this.isConnected()&&!this.closeWasClean&&(this.reconnectTimer.reset(),this.teardownAndReconnect(()=>{this.connect(),this.visibilityConnection=this.connection}))}getLongPollTransport(){return S}replaceTransport(e){let t=++this.connectClock;this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.transport=e;let i=this.connection;if(i){let s=i.isOpen();this.connection=null,i.ended=!0,i.close(),this.clearHeartbeats(),s&&this.triggerChanError("connection_closed",i)}return t}protocol(){return location.protocol.match(/^https/)?"wss":"ws"}endPointURL(){let e=R.appendParams(R.appendParams(this.endPoint,this.params()),{vsn:this.vsn});return e.charAt(0)!=="/"?e:e.charAt(1)==="/"?`${this.protocol()}:${e}`:`${this.protocol()}://${location.host}${e}`}disconnect(e,t,i){this.connectClock++,this.closeWasClean=!0,clearTimeout(this.fallbackTimer),this.reconnectTimer.reset(),this.connection&&(this.connection.ended=!0),this.teardown(e,t,i)}connect(e){e&&(console&&console.log("passing params to connect is deprecated. Instead pass :params to the Socket constructor"),this.params=y(e)),!(this.connection&&!this.connection.ended)&&(this.longPollFallbackMs&&this.transport!==S?this.connectWithFallback(S,this.longPollFallbackMs):this.transportConnect())}log(e,t,i){this.logger&&this.logger(e,t,i)}hasLogger(){return this.logger!==null}onOpen(e){let t=this.makeRef();return this.stateChangeCallbacks.open.push([t,e]),t}onClose(e){let t=this.makeRef();return this.stateChangeCallbacks.close.push([t,e]),t}onError(e){let t=this.makeRef();return this.stateChangeCallbacks.error.push([t,e]),t}onMessage(e){let t=this.makeRef();return this.stateChangeCallbacks.message.push([t,e]),t}ping(e){if(!this.isConnected())return!1;let t=this.makeRef(),i=Date.now();return this.connection.pings.set(t,()=>e(Date.now()-i)),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:t}),!0}transportName(e){switch(e){case S:return"LongPoll";default:return e.name}}transportConnect(){let e=this.connection;this.connectClock++,this.closeWasClean=!1,clearTimeout(this.reconnectTimer.timer);let t;this.authToken&&(t=["phoenix",`${O}${btoa(this.authToken()).replace(/=/g,"")}`]);let i=new this.transport(this.endPointURL(),t);i.binaryType=this.binaryType,i.timeout=this.longpollerTimeout,this.connection=new _(this,i),e&&e.isOpen()&&this.triggerChanError("connection_closed",e)}getSession(e){return this.sessionStore&&this.sessionStore.getItem(e)}storeSession(e,t){this.sessionStore&&this.sessionStore.setItem(e,t)}connectWithFallback(e,t=2500){this.cancelFallback();let i=!1,s=!0,n,o,r=()=>o===this.connectClock&&!this.closeWasClean,a=this.transportName(e),l=c=>{this.log("transport",`falling back to ${a}...`,c),this.off([n]),s=!1,this.replaceTransport(e)===this.connectClock&&this.transportConnect()};if(this.getSession(`phx:fallback:${a}`))return l("memorized");this.fallbackTimer=setTimeout(l,t),n=this.fallbackErrorRef=this.onError(c=>{r()&&(this.log("transport","error",c),s&&!i&&(clearTimeout(this.fallbackTimer),l(c)))}),this.fallbackRef=this.onOpen(()=>{if(!s){if(this.transport!==e||this.closeWasClean)return;let c=this.transportName(e);return this.primaryPassedHealthCheck||this.storeSession(`phx:fallback:${c}`,"true"),this.log("transport",`established ${c} fallback`)}r()&&(i=!0,clearTimeout(this.fallbackTimer),this.fallbackTimer=setTimeout(l,t),this.ping(c=>{this.log("transport","connected to primary after",c),this.primaryPassedHealthCheck=!0,clearTimeout(this.fallbackTimer)}))}),this.transportConnect(),o=this.connectClock}cancelFallback(){clearTimeout(this.fallbackTimer),this.off([this.fallbackRef,this.fallbackErrorRef])}clearHeartbeats(){clearTimeout(this.heartbeatTimer),clearTimeout(this.heartbeatTimeoutTimer),this.pendingHeartbeatRef=null}onConnOpen(){this.hasLogger()&&this.log("transport",`${this.transportName(this.transport)} connected to ${this.endPointURL()}`),this.closeWasClean=!1,this.establishedConnections++,this.flushSendBuffer(),this.reconnectTimer.reset(),this.resetHeartbeat(),this.stateChangeCallbacks.open.forEach(([,e])=>e())}heartbeatTimeout(){if(this.pendingHeartbeatRef){let e=this.connection;if(this.pendingHeartbeatRef=null,this.hasLogger()&&this.log("transport","heartbeat timeout. Attempting to re-establish connection"),this.closeWasClean=!1,e&&e.close(M,"heartbeat timeout"),!this.stillUses(e)||(this.triggerChanError("heartbeat_timeout"),!this.stillUses(e)))return;this.teardownAndReconnect(()=>this.reconnectTimer.scheduleTimeout())}}resetHeartbeat(){this.conn&&this.conn.skipHeartbeat||(this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs))}teardownAndReconnect(e){let t=this.connection,i=this.connectClock;this.teardown(()=>{i!==this.connectClock||t&&t.ended||e()})}stillUses(e){return this.connection===e&&!(e&&e.ended)}teardown(e,t,i){let s=this.connection;if(!s)return e&&e();this.clearHeartbeats(),this.waitForBufferDone(s.transport,()=>{if(!s.closing){let n=!s.isConnecting();s.close(t,i),n&&this.triggerChanError("connection_closed",s)}this.waitForSocketClosed(s.transport,()=>{this.connection===s&&(this.connection=null),e&&e()})})}waitForBufferDone(e,t,i=1){if(i===5||!e.bufferedAmount||e.readyState!==p.open){t();return}setTimeout(()=>{this.waitForBufferDone(e,t,i+1)},150*i)}waitForSocketClosed(e,t,i=1){if(i===5||e.readyState===p.closed){t();return}setTimeout(()=>{this.waitForSocketClosed(e,t,i+1)},150*i)}onConnClose(e){let t=this.connection;this.hasLogger()&&this.log("transport","close",e),this.clearHeartbeats();let i=!!(t&&t.closing);this.triggerChanError("connection_closed",t),this.stillUses(t)&&!i&&!this.closeWasClean&&this.reconnectTimer.scheduleTimeout(),this.stateChangeCallbacks.close.forEach(([,s])=>s(e))}onConnError(e){let t=this.connection;this.hasLogger()&&this.log("transport","error",e);let i=this.transport,s=this.establishedConnections;this.stateChangeCallbacks.error.forEach(([,n])=>{n(e,i,s)}),this.triggerChanError("connection_error",t)}triggerChanError(e,t=this.connection){let i=!1;t&&!t.isOpen()&&(i=t.channelsErrored,t.channelsErrored=!0),this.channels.map(n=>[n,n.joinRef()]).forEach(([n,o])=>{n.joinRef()===o&&(t&&(i||!t.isCurrent())&&!t.carried(n,o)||n.isErrored()||n.isLeaving()||n.isClosed()||n.trigger(T.error,{source:"transport",reason:e}))})}connectionState(){switch(this.conn&&this.conn.readyState){case p.connecting:return"connecting";case p.open:return"open";case p.closing:return"closing";default:return"closed"}}isConnected(){return this.connectionState()==="open"}remove(e){this.off(e.stateChangeRefs),this.channels=this.channels.filter(t=>t!==e)}off(e){for(let t in this.stateChangeCallbacks)this.stateChangeCallbacks[t]=this.stateChangeCallbacks[t].filter(([i])=>e.indexOf(i)===-1)}channel(e,t={}){let i=new j(e,t,this);return this.channels.push(i),i}push(e,t){if(this.hasLogger()){let{topic:i,event:s,payload:n,ref:o,join_ref:r}=e;this.log("push",`${i} ${s} (${r}, ${o})`,n)}this.isConnected()?this.send(e,t):this.sendBuffer.push(()=>{this.isOutdated(e,t)||this.send(e,t)})}send(e,t){let i=this.connection;t&&e.event===T.join&&i.joins.set(t,e.join_ref),this.encode(e,s=>{!i.isCurrent()||!i.isOpen()||e.event!==T.leave&&this.isOutdated(e,t)||i.transport.send(s)})}isOutdated(e,t){return!!t&&(e.join_ref!==t.joinRef()||t.isClosed())}makeRef(){let e=this.ref+1;return e===this.ref?this.ref=0:this.ref=e,this.ref.toString()}sendHeartbeat(){this.pendingHeartbeatRef&&!this.isConnected()||(this.pendingHeartbeatRef=this.makeRef(),this.push({topic:"phoenix",event:"heartbeat",payload:{},ref:this.pendingHeartbeatRef}),this.heartbeatTimeoutTimer=setTimeout(()=>this.heartbeatTimeout(),this.heartbeatIntervalMs))}flushSendBuffer(){this.isConnected()&&this.sendBuffer.length>0&&(this.sendBuffer.forEach(e=>e()),this.sendBuffer=[])}onConnMessage(e){let t=this.connection,i=t&&t.isOpen();this.decode(e.data,s=>{if(t!==this.connection||i&&!t.isOpen())return;let{topic:n,event:o,payload:r,ref:a,join_ref:l}=s;a&&a===this.pendingHeartbeatRef&&(this.clearHeartbeats(),this.heartbeatTimer=setTimeout(()=>this.sendHeartbeat(),this.heartbeatIntervalMs)),this.hasLogger()&&this.log("receive",`${r.status||""} ${n} ${o} ${a&&"("+a+")"||""}`,r);let c=this.channels;for(let d=0;di.topic===e&&(i.isJoined()||i.isJoining()));t&&(this.hasLogger()&&this.log("transport",`leaving duplicate topic "${e}"`),t.leave())}};return J(G);})(); diff --git a/priv/static/phoenix.mjs b/priv/static/phoenix.mjs index a44c2239f3..afa887c42a 100644 --- a/priv/static/phoenix.mjs +++ b/priv/static/phoenix.mjs @@ -1588,38 +1588,11 @@ var Socket = class { if (this.hasLogger()) this.log("transport", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`); this.closeWasClean = false; this.establishedConnections++; - this.pingBeforeBufferedJoin(); this.flushSendBuffer(); this.reconnectTimer.reset(); this.resetHeartbeat(); this.stateChangeCallbacks.open.forEach(([, callback]) => callback()); } - /** - * @private - * - * Sends the fallback health check before flushSendBuffer. No-op unless a - * fallback attempt is waiting on the primary transport. - */ - pingBeforeBufferedJoin() { - if (!this.fallbackTimer || this.transport === LongPoll) { - return; - } - this.ping(() => this.passPrimaryHealthCheck()); - } - /** - * @private - * - * The primary transport answered. Later frames on this connection also call - * this, because a frame is the proof the ping was waiting for. - */ - passPrimaryHealthCheck() { - if (this.transport === LongPoll) { - return; - } - this.primaryPassedHealthCheck = true; - clearTimeout(this.fallbackTimer); - this.fallbackTimer = null; - } /** * @private */ @@ -1898,9 +1871,6 @@ var Socket = class { if (connection !== this.connection || wasOpen && !connection.isOpen()) { return; } - if (this.fallbackTimer) { - this.passPrimaryHealthCheck(); - } let { topic, event, payload, ref, join_ref } = msg; if (ref && ref === this.pendingHeartbeatRef) { this.clearHeartbeats(); diff --git a/priv/static/phoenix.mjs.map b/priv/static/phoenix.mjs.map index 0520384272..f376195875 100644 --- a/priv/static/phoenix.mjs.map +++ b/priv/static/phoenix.mjs.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../assets/js/phoenix/utils.js", "../../assets/js/phoenix/constants.js", "../../assets/js/phoenix/push.js", "../../assets/js/phoenix/timer.js", "../../assets/js/phoenix/channel.js", "../../assets/js/phoenix/ajax.js", "../../assets/js/phoenix/longpoll.js", "../../assets/js/phoenix/presence.js", "../../assets/js/phoenix/serializer.js", "../../assets/js/phoenix/connection.js", "../../assets/js/phoenix/socket.js"], - "sourcesContent": ["// wraps value in closure or returns closure\nexport let closure = (value) => {\n if(typeof value === \"function\"){\n return value\n } else {\n let closure = function (){ return value }\n return closure\n }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || globalThis\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const MAX_LONGPOLL_BATCH_SIZE = 100;\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n closed: \"closed\",\n errored: \"errored\",\n joined: \"joined\",\n joining: \"joining\",\n leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n close: \"phx_close\",\n error: \"phx_error\",\n join: \"phx_join\",\n reply: \"phx_reply\",\n leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n longpoll: \"longpoll\",\n websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n complete: 4\n}\nexport const AUTH_TOKEN_PREFIX = \"base64url.bearer.phx.\"\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n constructor(channel, event, payload, timeout){\n this.channel = channel\n this.event = event\n this.payload = payload || function (){ return {} }\n this.receivedResp = null\n this.timeout = timeout\n this.timeoutTimer = null\n this.recHooks = []\n this.sent = false\n }\n\n /**\n *\n * @param {number} timeout\n */\n resend(timeout){\n this.timeout = timeout\n this.reset()\n this.send()\n }\n\n /**\n *\n */\n send(){\n if(this.hasReceived(\"timeout\")){ return }\n this.startTimeout()\n this.sent = true\n this.channel.socket.push({\n topic: this.channel.topic,\n event: this.event,\n payload: this.payload(),\n ref: this.ref,\n join_ref: this.channel.joinRef()\n }, this.channel)\n }\n\n /**\n *\n * @param {*} status\n * @param {*} callback\n */\n receive(status, callback){\n if(this.hasReceived(status)){\n callback(this.receivedResp.response)\n }\n\n this.recHooks.push({status, callback})\n return this\n }\n\n /**\n * @private\n */\n reset(){\n this.cancelRefEvent()\n this.ref = null\n this.refEvent = null\n this.receivedResp = null\n this.sent = false\n }\n\n /**\n * @private\n */\n matchReceive({status, response, _ref}){\n this.recHooks.filter(h => h.status === status)\n .forEach(h => h.callback(response))\n }\n\n /**\n * @private\n */\n cancelRefEvent(){\n if(!this.refEvent){ return }\n this.channel.off(this.refEvent)\n }\n\n /**\n * @private\n */\n cancelTimeout(){\n clearTimeout(this.timeoutTimer)\n this.timeoutTimer = null\n }\n\n /**\n * @private\n */\n startTimeout(){\n if(this.timeoutTimer){ this.cancelTimeout() }\n this.cancelRefEvent()\n this.ref = this.channel.socket.makeRef()\n this.refEvent = this.channel.replyEventName(this.ref)\n\n this.channel.on(this.refEvent, payload => {\n this.cancelRefEvent()\n this.cancelTimeout()\n this.receivedResp = payload\n this.matchReceive(payload)\n })\n\n this.timeoutTimer = setTimeout(() => {\n this.trigger(\"timeout\", {})\n }, this.timeout)\n }\n\n /**\n * @private\n */\n hasReceived(status){\n return this.receivedResp && this.receivedResp.status === status\n }\n\n /**\n * @private\n */\n trigger(status, response){\n this.channel.trigger(this.refEvent, {status, response})\n }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n * return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n constructor(callback, timerCalc){\n this.callback = callback\n this.timerCalc = timerCalc\n this.timer = null\n this.tries = 0\n }\n\n reset(){\n this.tries = 0\n clearTimeout(this.timer)\n }\n\n /**\n * Cancels any previous scheduleTimeout and schedules callback\n */\n scheduleTimeout(){\n clearTimeout(this.timer)\n\n this.timer = setTimeout(() => {\n this.tries = this.tries + 1\n this.callback()\n }, this.timerCalc(this.tries + 1))\n }\n}\n", "import {closure} from \"./utils\"\nimport {\n CHANNEL_EVENTS,\n CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n constructor(topic, params, socket){\n this.state = CHANNEL_STATES.closed\n this.topic = topic\n this.params = closure(params || {})\n this.socket = socket\n this.bindings = []\n this.bindingRef = 0\n this.timeout = this.socket.timeout\n this.joinedOnce = false\n this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n this.pushBuffer = []\n this.stateChangeRefs = []\n\n this.rejoinTimer = new Timer(() => {\n if(this.socket.isConnected()){ this.rejoin() }\n }, this.socket.rejoinAfterMs)\n this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n this.stateChangeRefs.push(this.socket.onOpen(() => {\n this.rejoinTimer.reset()\n if(this.isErrored()){ this.rejoin() }\n })\n )\n this.joinPush.receive(\"ok\", () => {\n this.state = CHANNEL_STATES.joined\n this.rejoinTimer.reset()\n this.pushBuffer.forEach(pushEvent => pushEvent.send())\n this.pushBuffer = []\n })\n this.joinPush.receive(\"error\", () => {\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.onClose(() => {\n this.rejoinTimer.reset()\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n this.state = CHANNEL_STATES.closed\n this.socket.remove(this)\n })\n this.onError(reason => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n if(this.isJoining()){ this.joinPush.reset() }\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.joinPush.receive(\"timeout\", () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n leavePush.send()\n this.state = CHANNEL_STATES.errored\n this.joinPush.reset()\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n this.trigger(this.replyEventName(ref), payload)\n })\n }\n\n /**\n * Join the channel\n * @param {integer} timeout\n * @returns {Push}\n */\n join(timeout = this.timeout){\n if(this.joinedOnce){\n throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n } else {\n this.timeout = timeout\n this.joinedOnce = true\n this.rejoin()\n return this.joinPush\n }\n }\n\n /**\n * Hook into channel close\n * @param {Function} callback\n */\n onClose(callback){\n this.on(CHANNEL_EVENTS.close, callback)\n }\n\n /**\n * Hook into channel errors\n * @param {Function} callback\n */\n onError(callback){\n return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n }\n\n /**\n * Subscribes on channel events\n *\n * Subscription returns a ref counter, which can be used later to\n * unsubscribe the exact event listener\n *\n * @example\n * const ref1 = channel.on(\"event\", do_stuff)\n * const ref2 = channel.on(\"event\", do_other_stuff)\n * channel.off(\"event\", ref1)\n * // Since unsubscription, do_stuff won't fire,\n * // while do_other_stuff will keep firing on the \"event\"\n *\n * @param {string} event\n * @param {Function} callback\n * @returns {integer} ref\n */\n on(event, callback){\n let ref = this.bindingRef++\n this.bindings.push({event, ref, callback})\n return ref\n }\n\n /**\n * Unsubscribes off of channel events\n *\n * Use the ref returned from a channel.on() to unsubscribe one\n * handler, or pass nothing for the ref to unsubscribe all\n * handlers for the given event.\n *\n * @example\n * // Unsubscribe the do_stuff handler\n * const ref1 = channel.on(\"event\", do_stuff)\n * channel.off(\"event\", ref1)\n *\n * // Unsubscribe all handlers from event\n * channel.off(\"event\")\n *\n * @param {string} event\n * @param {integer} ref\n */\n off(event, ref){\n this.bindings = this.bindings.filter((bind) => {\n return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n })\n }\n\n /**\n * @private\n */\n canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n /**\n * Sends a message `event` to phoenix with the payload `payload`.\n * Phoenix receives this in the `handle_in(event, payload, socket)`\n * function. if phoenix replies or it times out (default 10000ms),\n * then optionally the reply can be received.\n *\n * @example\n * channel.push(\"event\")\n * .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n * .receive(\"error\", err => console.log(\"phoenix errored\", err))\n * .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n * @param {string} event\n * @param {Object} payload\n * @param {number} [timeout]\n * @returns {Push}\n */\n push(event, payload, timeout = this.timeout){\n payload = payload || {}\n if(!this.joinedOnce){\n throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n }\n let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n if(this.canPush()){\n pushEvent.send()\n } else {\n pushEvent.startTimeout()\n this.pushBuffer.push(pushEvent)\n }\n\n return pushEvent\n }\n\n /** Leaves the channel\n *\n * Unsubscribes from server events, and\n * instructs channel to terminate on server\n *\n * Triggers onClose() hooks\n *\n * To receive leave acknowledgements, use the `receive`\n * hook to bind to the server ack, ie:\n *\n * @example\n * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n *\n * @param {integer} timeout\n * @returns {Push}\n */\n leave(timeout = this.timeout){\n this.rejoinTimer.reset()\n this.joinPush.cancelTimeout()\n\n this.state = CHANNEL_STATES.leaving\n let onClose = () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n this.trigger(CHANNEL_EVENTS.close, \"leave\")\n }\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n leavePush.receive(\"ok\", () => onClose())\n .receive(\"timeout\", () => onClose())\n leavePush.send()\n if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n return leavePush\n }\n\n /**\n * Overridable message hook\n *\n * Receives all events for specialized message handling\n * before dispatching to the channel callbacks.\n *\n * Must return the payload, modified or unmodified\n * @param {string} event\n * @param {Object} payload\n * @param {integer} ref\n * @returns {Object}\n */\n onMessage(_event, payload, _ref){ return payload }\n\n /**\n * @private\n */\n isMember(topic, event, payload, joinRef){\n if(this.topic !== topic){ return false }\n\n if(joinRef && joinRef !== this.joinRef()){\n if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n return false\n } else {\n return true\n }\n }\n\n /**\n * @private\n */\n joinRef(){ return this.joinPush.ref }\n\n /**\n * @private\n */\n rejoin(timeout = this.timeout){\n if(this.isLeaving()){ return }\n this.socket.leaveOpenTopic(this.topic)\n this.state = CHANNEL_STATES.joining\n this.joinPush.resend(timeout)\n }\n\n /**\n * @private\n */\n trigger(event, payload, ref, joinRef){\n let handledPayload = this.onMessage(event, payload, ref, joinRef)\n if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n for(let i = 0; i < eventBindings.length; i++){\n let bind = eventBindings[i]\n bind.callback(handledPayload, ref, joinRef || this.joinRef())\n }\n }\n\n /**\n * @private\n */\n replyEventName(ref){ return `chan_reply_${ref}` }\n\n /**\n * @private\n */\n isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n /**\n * @private\n */\n isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n /**\n * @private\n */\n isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n /**\n * @private\n */\n isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n /**\n * @private\n */\n isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n global,\n XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n static request(method, endPoint, headers, body, timeout, ontimeout, callback){\n if(global.XDomainRequest){\n let req = new global.XDomainRequest() // IE8, IE9\n return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n } else if(global.XMLHttpRequest){\n let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)\n } else if(global.fetch && global.AbortController){\n // Fetch with AbortController for modern browsers\n return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)\n } else {\n throw new Error(\"No suitable XMLHttpRequest implementation found\")\n }\n }\n\n static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){\n let options = {\n method,\n headers,\n body,\n }\n let controller = null\n let timeoutId = null\n if(timeout){\n controller = new AbortController()\n timeoutId = setTimeout(() => controller.abort(), timeout)\n options.signal = controller.signal\n }\n global.fetch(endPoint, options)\n .then(response => response.text())\n .then(data => this.parseJSON(data))\n .then(data => {\n if(timeoutId){ clearTimeout(timeoutId) }\n callback && callback(data)\n })\n .catch(err => {\n if(timeoutId){ clearTimeout(timeoutId) }\n if(err.name === \"AbortError\" && ontimeout){\n ontimeout()\n } else {\n callback && callback(null)\n }\n })\n return controller\n }\n\n static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n req.timeout = timeout\n req.open(method, endPoint)\n req.onload = () => {\n let response = this.parseJSON(req.responseText)\n callback && callback(response)\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n // Work around bug in IE9 that requires an attached onprogress handler\n req.onprogress = () => { }\n\n req.send(body)\n return req\n }\n\n static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){\n req.open(method, endPoint, true)\n req.timeout = timeout\n for(let [key, value] of Object.entries(headers)){\n req.setRequestHeader(key, value)\n }\n req.onerror = () => callback && callback(null)\n req.onreadystatechange = () => {\n if(req.readyState === XHR_STATES.complete && callback){\n let response = this.parseJSON(req.responseText)\n callback(response)\n }\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n req.send(body)\n return req\n }\n\n static parseJSON(resp){\n if(!resp || resp === \"\"){ return null }\n\n try {\n return JSON.parse(resp)\n } catch {\n console && console.log(\"failed to parse JSON response\", resp)\n return null\n }\n }\n\n static serialize(obj, parentKey){\n let queryStr = []\n for(var key in obj){\n if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n let paramKey = parentKey ? `${parentKey}[${key}]` : key\n let paramVal = obj[key]\n if(typeof paramVal === \"object\"){\n queryStr.push(this.serialize(paramVal, paramKey))\n } else {\n queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n }\n }\n return queryStr.join(\"&\")\n }\n\n static appendParams(url, params){\n if(Object.keys(params).length === 0){ return url }\n\n let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n return `${url}${prefix}${this.serialize(params)}`\n }\n}\n", "import {\n SOCKET_STATES,\n TRANSPORTS,\n AUTH_TOKEN_PREFIX,\n MAX_LONGPOLL_BATCH_SIZE\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBufferToBase64 = (buffer) => {\n let binary = \"\"\n let bytes = new Uint8Array(buffer)\n let len = bytes.byteLength\n for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }\n return btoa(binary)\n}\n\nexport default class LongPoll {\n\n constructor(endPoint, protocols){\n // we only support subprotocols for authToken\n // [\"phoenix\", \"base64url.bearer.phx.BASE64_ENCODED_TOKEN\"]\n if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){\n this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))\n }\n this.endPoint = null\n this.token = null\n this.skipHeartbeat = true\n // the pending requests and queued messages, which are cancelled when we stop\n this.reqs = new Set()\n this.messageTimers = new Set()\n this.awaitingBatchAck = false\n this.currentBatch = null\n this.currentBatchTimer = null\n this.batchBuffer = []\n this.onopen = function (){ } // noop\n this.onerror = function (){ } // noop\n this.onmessage = function (){ } // noop\n this.onclose = function (){ } // noop\n this.pollEndpoint = this.normalizeEndpoint(endPoint)\n this.readyState = SOCKET_STATES.connecting\n // we must wait for the caller to finish setting up our callbacks and timeout properties,\n // and it might close us before that, e.g. when disconnecting right after connecting\n setTimeout(() => this.poll(), 0)\n }\n\n normalizeEndpoint(endPoint){\n return (endPoint\n .replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")\n .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n }\n\n endpointURL(){\n return this.pollEndpoint\n }\n\n closeAndRetry(error, code, reason, wasClean){\n if(!this.isActive()){ return }\n // like a failed WebSocket, we are no longer open when we emit the error and then the close.\n // We are connecting again before their callbacks run, so that they can still close us for\n // good, e.g. when they disconnect the socket\n this.stop(SOCKET_STATES.connecting)\n this.onerror(error)\n if(this.isActive()){ this.emitClose(code, reason, wasClean) }\n }\n\n ontimeout(){\n this.closeAndRetry(\"timeout\", 1005, \"timeout\", false)\n }\n\n isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }\n\n poll(){\n // onopen can close us before the next poll, just as the caller can close us\n // before the constructor's deferred first poll\n if(!this.isActive()){ return }\n const headers = {\"Accept\": \"application/json\"}\n if(this.authToken){\n headers[\"X-Phoenix-AuthToken\"] = this.authToken\n }\n this.ajax(\"GET\", headers, null, () => this.ontimeout(), resp => {\n if(resp){\n var {status, token, messages} = resp\n if(status === 410 && this.token !== null){\n // In case we already have a token, this means that our existing session\n // is gone. We fail so that the client rejoins its channels.\n this.closeAndRetry(410, 3410, \"session_gone\", false)\n return\n }\n this.token = token\n } else {\n status = 0\n }\n\n switch(status){\n case 200:\n messages.forEach(msg => {\n // Tasks are what things like event handlers, setTimeout callbacks,\n // promise resolves and more are run within.\n // In modern browsers, there are two different kinds of tasks,\n // microtasks and macrotasks.\n // Microtasks are mainly used for Promises, while macrotasks are\n // used for everything else.\n // Microtasks always have priority over macrotasks. If the JS engine\n // is looking for a task to run, it will always try to empty the\n // microtask queue before attempting to run anything from the\n // macrotask queue.\n //\n // For the WebSocket transport, messages always arrive in their own\n // event. This means that if any promises are resolved from within,\n // their callbacks will always finish execution by the time the\n // next message event handler is run.\n //\n // In order to emulate this behaviour, we need to make sure each\n // onmessage handler is run within its own macrotask.\n //\n // We might stop before that, e.g. when a POST times out or a message\n // handler disconnects, which cancels the remaining messages.\n const timer = setTimeout(() => {\n this.messageTimers.delete(timer)\n this.onmessage({data: msg})\n }, 0)\n this.messageTimers.add(timer)\n })\n this.poll()\n break\n case 204:\n this.poll()\n break\n case 410:\n this.readyState = SOCKET_STATES.open\n this.onopen({})\n this.poll()\n break\n case 403:\n this.onerror(403)\n this.close(1008, \"forbidden\", false)\n break\n case 0:\n case 500:\n this.closeAndRetry(500, 1011, \"internal server error\", false)\n break\n default: throw new Error(`unhandled poll status ${status}`)\n }\n })\n }\n\n // we collect all pushes within the current event loop by\n // setTimeout 0, which optimizes back-to-back procedural\n // pushes against an empty buffer\n\n send(body){\n if(typeof(body) !== \"string\"){ body = arrayBufferToBase64(body) }\n if(this.currentBatch){\n this.currentBatch.push(body)\n } else if(this.awaitingBatchAck){\n this.batchBuffer.push(body)\n } else {\n this.currentBatch = [body]\n this.currentBatchTimer = setTimeout(() => {\n this.batchSend(this.currentBatch)\n this.currentBatch = null\n }, 0)\n }\n }\n\n batchSend(messages, offset = 0){\n this.awaitingBatchAck = true\n const next = offset + MAX_LONGPOLL_BATCH_SIZE\n const batch = messages.slice(offset, next)\n this.ajax(\"POST\", {\"Content-Type\": \"application/x-ndjson\"}, batch.join(\"\\n\"), () => this.ontimeout(), resp => {\n if(!resp || resp.status !== 200){\n // this calls stop which already clears awaitingBatchAck\n this.closeAndRetry(resp && resp.status, 1011, \"internal server error\", false)\n } else if(next < messages.length){\n this.batchSend(messages, next)\n } else if(this.batchBuffer.length > 0){\n this.batchSend(this.batchBuffer)\n this.batchBuffer = []\n } else {\n this.awaitingBatchAck = false\n }\n })\n }\n\n close(code, reason, wasClean){\n this.stop(SOCKET_STATES.closed)\n this.emitClose(code, reason, wasClean)\n }\n\n /**\n * Cancels the pending requests, queued messages and outgoing batches.\n */\n stop(readyState){\n this.readyState = readyState\n // the requests are no longer ours before we abort them, as aborting can invoke their\n // callbacks, synchronously for XHR and asynchronously for fetch\n const reqs = this.reqs\n this.reqs = new Set()\n for(let {req} of reqs){ req && req.abort() }\n for(let timer of this.messageTimers){ clearTimeout(timer) }\n this.messageTimers.clear()\n this.batchBuffer = []\n this.awaitingBatchAck = false\n clearTimeout(this.currentBatchTimer)\n this.currentBatchTimer = null\n this.currentBatch = null\n }\n\n /**\n * Emits the close event. Like a WebSocket closed without a code, it reports 1005 (no status)\n * unless one is given, as 1000 would look like the server closing the connection normally.\n */\n emitClose(code = 1005, reason, wasClean = true){\n let opts = {code, reason, wasClean}\n if(typeof(CloseEvent) !== \"undefined\"){\n this.onclose(new CloseEvent(\"close\", opts))\n } else {\n this.onclose(opts)\n }\n }\n\n ajax(method, headers, body, onCallerTimeout, callback){\n // the request is ours until it completes or we stop, and the callbacks of a request\n // that is not ours anymore are ignored. It is ours before it starts, as a request can\n // complete while it starts.\n const request = {req: null}\n this.reqs.add(request)\n let ontimeout = () => {\n if(this.reqs.delete(request)){ onCallerTimeout() }\n }\n if(this.token !== null){\n headers = Object.assign({}, headers, {\"X-Phoenix-Longpoll-Token\": this.token})\n }\n request.req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {\n if(this.reqs.delete(request)){ callback(resp) }\n })\n }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n * for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n constructor(channel, opts = {}){\n let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n this.state = Object.create(null)\n this.pendingDiffs = []\n this.channel = channel\n this.joinRef = null\n this.caller = {\n onJoin: function (){ },\n onLeave: function (){ },\n onSync: function (){ }\n }\n\n this.channel.on(events.state, newState => {\n let {onJoin, onLeave, onSync} = this.caller\n\n this.joinRef = this.channel.joinRef()\n this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n this.pendingDiffs.forEach(diff => {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n })\n this.pendingDiffs = []\n onSync()\n })\n\n this.channel.on(events.diff, diff => {\n let {onJoin, onLeave, onSync} = this.caller\n\n if(this.inPendingSyncState()){\n this.pendingDiffs.push(diff)\n } else {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n onSync()\n }\n })\n }\n\n onJoin(callback){ this.caller.onJoin = callback }\n\n onLeave(callback){ this.caller.onLeave = callback }\n\n onSync(callback){ this.caller.onSync = callback }\n\n list(by){ return Presence.list(this.state, by) }\n\n inPendingSyncState(){\n return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n }\n\n // lower-level public static API\n\n /**\n * Used to sync the list of presences on the server\n * with the client's state. An optional `onJoin` and `onLeave` callback can\n * be provided to react to changes in the client's local presences across\n * disconnects and reconnects with the server.\n *\n * @returns {Presence}\n */\n static syncState(currentState, newState, onJoin, onLeave){\n let state = this.toNullProtoObj(this.clone(currentState))\n newState = this.toNullProtoObj(newState)\n let joins = Object.create(null)\n let leaves = Object.create(null)\n\n this.map(state, (key, presence) => {\n if(!newState[key]){\n leaves[key] = presence\n }\n })\n this.map(newState, (key, newPresence) => {\n let currentPresence = state[key]\n if(currentPresence){\n let newRefs = newPresence.metas.map(m => m.phx_ref)\n let curRefs = currentPresence.metas.map(m => m.phx_ref)\n let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n if(joinedMetas.length > 0){\n joins[key] = newPresence\n joins[key].metas = joinedMetas\n }\n if(leftMetas.length > 0){\n leaves[key] = this.clone(currentPresence)\n leaves[key].metas = leftMetas\n }\n } else {\n joins[key] = newPresence\n }\n })\n return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n }\n\n /**\n *\n * Used to sync a diff of presence join and leave\n * events from the server, as they happen. Like `syncState`, `syncDiff`\n * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n * joining or leaving from a device.\n *\n * @returns {Presence}\n */\n static syncDiff(state, diff, onJoin, onLeave){\n state = this.toNullProtoObj(state)\n let {joins, leaves} = this.clone(diff)\n if(!onJoin){ onJoin = function (){ } }\n if(!onLeave){ onLeave = function (){ } }\n\n this.map(joins, (key, newPresence) => {\n let currentPresence = state[key]\n state[key] = this.clone(newPresence)\n if(currentPresence){\n let joinedRefs = state[key].metas.map(m => m.phx_ref)\n let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n state[key].metas.unshift(...curMetas)\n }\n onJoin(key, currentPresence, newPresence)\n })\n this.map(leaves, (key, leftPresence) => {\n let currentPresence = state[key]\n if(!currentPresence){ return }\n let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n currentPresence.metas = currentPresence.metas.filter(p => {\n return refsToRemove.indexOf(p.phx_ref) < 0\n })\n onLeave(key, currentPresence, leftPresence)\n if(currentPresence.metas.length === 0){\n delete state[key]\n }\n })\n return state\n }\n\n /**\n * Returns the array of presences, with selected metadata.\n *\n * @param {Object} presences\n * @param {Function} chooser\n *\n * @returns {Presence}\n */\n static list(presences, chooser){\n if(!chooser){ chooser = function (key, pres){ return pres } }\n\n return this.map(presences, (key, presence) => {\n return chooser(key, presence)\n })\n }\n\n // private\n\n static map(obj, func){\n return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n }\n\n // Presence keys are chosen on the server and may collide with\n // Object.prototype properties (\"__proto__\", \"constructor\", ...), so any\n // object indexed by presence key must not have a prototype chain\n //\n // TODO: replace the null-prototype objects with Maps in Phoenix 2.0\n // (breaking change for the lower-level static API)\n static toNullProtoObj(obj){\n if(Object.getPrototypeOf(obj) === null){ return obj }\n let cleaned = Object.create(null)\n Object.getOwnPropertyNames(obj).forEach(key => {\n cleaned[key] = obj[key]\n })\n return cleaned\n }\n\n static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n HEADER_LENGTH: 1,\n META_LENGTH: 4,\n KINDS: {push: 0, reply: 1, broadcast: 2},\n\n encode(msg, callback){\n if(msg.payload.constructor === ArrayBuffer){\n return callback(this.binaryEncode(msg))\n } else {\n let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n return callback(JSON.stringify(payload))\n }\n },\n\n decode(rawPayload, callback){\n if(rawPayload.constructor === ArrayBuffer){\n return callback(this.binaryDecode(rawPayload))\n } else {\n let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n return callback({join_ref, ref, topic, event, payload})\n }\n },\n\n // private\n\n binaryEncode(message){\n let {join_ref, ref, event, topic, payload} = message\n let encoder = new TextEncoder()\n let joinRefBytes = encoder.encode(join_ref)\n let refBytes = encoder.encode(ref)\n let topicBytes = encoder.encode(topic)\n let eventBytes = encoder.encode(event)\n\n this.assertFieldSize(joinRefBytes.byteLength, \"join_ref\")\n this.assertFieldSize(refBytes.byteLength, \"ref\")\n this.assertFieldSize(topicBytes.byteLength, \"topic\")\n this.assertFieldSize(eventBytes.byteLength, \"event\")\n\n let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength\n let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n let headerBytes = new Uint8Array(header)\n let view = new DataView(header)\n let offset = 0\n\n view.setUint8(offset++, this.KINDS.push) // kind\n view.setUint8(offset++, joinRefBytes.byteLength)\n view.setUint8(offset++, refBytes.byteLength)\n view.setUint8(offset++, topicBytes.byteLength)\n view.setUint8(offset++, eventBytes.byteLength)\n headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength\n headerBytes.set(refBytes, offset); offset += refBytes.byteLength\n headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength\n headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength\n\n var combined = new Uint8Array(header.byteLength + payload.byteLength)\n combined.set(headerBytes, 0)\n combined.set(new Uint8Array(payload), header.byteLength)\n\n return combined.buffer\n },\n\n assertFieldSize(size, name){\n if(size > 255){\n throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)\n }\n },\n\n binaryDecode(buffer){\n let view = new DataView(buffer)\n let kind = view.getUint8(0)\n let decoder = new TextDecoder()\n switch(kind){\n case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n }\n },\n\n decodePush(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let topicSize = view.getUint8(2)\n let eventSize = view.getUint8(3)\n let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n },\n\n decodeReply(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let refSize = view.getUint8(2)\n let topicSize = view.getUint8(3)\n let eventSize = view.getUint8(4)\n let offset = this.HEADER_LENGTH + this.META_LENGTH\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n offset = offset + refSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n let payload = {status: event, response: data}\n return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n },\n\n decodeBroadcast(buffer, view, decoder){\n let topicSize = view.getUint8(1)\n let eventSize = view.getUint8(2)\n let offset = this.HEADER_LENGTH + 2\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n\n return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n }\n}\n", "import {SOCKET_STATES} from \"./constants\"\n\n/**\n * @private\n *\n * A connection of the socket over a single transport instance.\n *\n * It owns everything that belongs to this connection, so that nothing about it can\n * affect a later connection: the transport events are only handled while it is the\n * socket's current connection, and its state is gone together with it.\n */\nexport default class Connection {\n /**\n * @param {Socket} socket\n * @param {Object} transport - The transport instance, for example a WebSocket\n */\n constructor(socket, transport){\n this.socket = socket\n this.transport = transport\n // the join ref of each channel whose join was sent over this connection, weakly held\n // as a connection can outlive many channels, e.g. when navigating between LiveViews\n this.joins = new WeakMap()\n // the callbacks of the pings sent over this connection by ref, as their replies can only\n // arrive over it, so they are gone together with it instead of accumulating on the socket\n this.pings = new Map()\n // set when we close the connection, as opposed to the server or the network\n this.closing = false\n // set when the socket does not want this connection anymore, e.g. on disconnect,\n // so that connecting again creates a new one instead of waiting for it\n this.ended = false\n // set once the channels were errored after the connection stopped being open\n this.channelsErrored = false\n // set once the close event was handled, as LongPoll can emit it more than once\n this.closeHandled = false\n transport.onopen = () => { if(this.isCurrent()){ socket.onConnOpen() } }\n transport.onerror = error => { if(this.isCurrent()){ socket.onConnError(error) } }\n transport.onmessage = event => { if(this.isCurrent()){ socket.onConnMessage(event) } }\n transport.onclose = event => {\n if(this.isCurrent() && !this.closeHandled){\n this.closeHandled = true\n socket.onConnClose(event)\n }\n }\n }\n\n /**\n * Whether this is the connection the socket currently uses. A replaced connection\n * can still emit events while it closes, but they no longer concern the socket.\n */\n isCurrent(){ return this.socket.connection === this }\n\n isOpen(){ return this.transport.readyState === SOCKET_STATES.open }\n\n isConnecting(){ return this.transport.readyState === SOCKET_STATES.connecting }\n\n /**\n * Whether the channel's current join was sent over this connection.\n */\n carried(channel, joinRef = channel.joinRef()){\n return this.joins.has(channel) && this.joins.get(channel) === joinRef\n }\n\n /**\n * Closes the transport. Its close event then only confirms our close.\n */\n close(code, reason){\n this.closing = true\n // closing a transport that is still connecting makes it emit an error, which is\n // caused by closing it and must not be handled as a transport error\n this.transport.onerror = function (){ } // noop\n if(code){ this.transport.close(code, reason || \"\") } else { this.transport.close() }\n }\n}\n", "import {\n global,\n phxWindow,\n CHANNEL_EVENTS,\n DEFAULT_TIMEOUT,\n DEFAULT_VSN,\n SOCKET_STATES,\n TRANSPORTS,\n WS_CLOSE_NORMAL,\n AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport {\n closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\nimport Connection from \"./connection\"\n\n/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n * `\"wss://example.com\"`\n * `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.\n * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.\n *\n * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport\n * before falling back to the LongPoll transport. Disabled by default.\n *\n * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.\n *\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the\n * socket reconnect interval, in milliseconds.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n * return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {boolean} [opts.reconnectWhenHidden] - When true, the socket also reconnects while the\n * page is hidden, for example in a background tab or on a sleeping phone.\n *\n * Defaults to false. The socket then waits until the page is visible again before it reconnects.\n * This avoids reconnect attempts that stall on mobile devices and delay the connection after the\n * device wakes up.\n *\n * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n * return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n * console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {(Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {(string|function)} [opts.authToken] - the optional authentication token to be exposed on the server\n * under the `:auth_token` connect_info key. Can be a string or a function that returns a string.\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n *\n * @param {Object} [opts.sessionStorage] - An optional Storage compatible object\n * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is\n * useful when Phoenix won't have access to `sessionStorage`. For example, This could\n * happen if a site loads a cross-domain channel in an iframe. Example usage:\n *\n * class InMemoryStorage {\n * constructor() { this.storage = {} }\n * getItem(keyName) { return this.storage[keyName] || null }\n * removeItem(keyName) { delete this.storage[keyName] }\n * setItem(keyName, keyValue) { this.storage[keyName] = keyValue }\n * }\n *\n*/\nexport default class Socket {\n constructor(endPoint, opts = {}){\n this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n this.channels = []\n this.sendBuffer = []\n this.ref = 0\n this.fallbackRef = null\n this.fallbackErrorRef = null\n this.timeout = opts.timeout || DEFAULT_TIMEOUT\n this.transport = opts.transport || global.WebSocket || LongPoll\n this.primaryPassedHealthCheck = false\n this.longPollFallbackMs = opts.longPollFallbackMs\n this.fallbackTimer = null\n this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)\n this.establishedConnections = 0\n this.defaultEncoder = Serializer.encode.bind(Serializer)\n this.defaultDecoder = Serializer.decode.bind(Serializer)\n // We start with closeWasClean true to avoid the visibility change\n // logic from connecting if the socket was never connected in the first place.\n // transportConnect sets it to false on open.\n this.closeWasClean = true\n this.binaryType = opts.binaryType || \"arraybuffer\"\n // the connection over the current transport instance, see Connection\n this.connection = null\n // changes whenever we connect, disconnect or replace the transport, for the code that needs\n // to know whether that happened while there was no connection, see replaceTransport\n this.connectClock = 1\n // the connection created when the page was shown, see handleVisibilityChange\n this.visibilityConnection = null\n if(this.transport !== LongPoll){\n this.encode = opts.encode || this.defaultEncoder\n this.decode = opts.decode || this.defaultDecoder\n } else {\n this.encode = this.defaultEncoder\n this.decode = this.defaultDecoder\n }\n let awaitingConnectionOnPageShow = null\n if(phxWindow && phxWindow.addEventListener){\n phxWindow.addEventListener(\"pagehide\", _e => {\n // an ended connection only finishes closing, e.g. after disconnect\n if(this.connection && !this.connection.ended){\n this.disconnect()\n awaitingConnectionOnPageShow = this.connectClock\n }\n })\n phxWindow.addEventListener(\"pageshow\", _e => {\n if(awaitingConnectionOnPageShow === this.connectClock){\n awaitingConnectionOnPageShow = null\n this.connect()\n }\n })\n phxWindow.addEventListener(\"visibilitychange\", () => {\n this.handleVisibilityChange()\n })\n // Chrome 149 started to not reliably fire visibilitychange after resume,\n // see https://issues.chromium.org/issues/547062449.\n phxWindow.document && phxWindow.document.addEventListener(\"resume\", () => {\n this.handleVisibilityChange()\n })\n }\n this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n this.rejoinAfterMs = (tries) => {\n if(opts.rejoinAfterMs){\n return opts.rejoinAfterMs(tries)\n } else {\n return [1000, 2000, 5000][tries - 1] || 10000\n }\n }\n this.reconnectAfterMs = (tries) => {\n if(opts.reconnectAfterMs){\n return opts.reconnectAfterMs(tries)\n } else {\n return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n }\n }\n this.reconnectWhenHidden = opts.reconnectWhenHidden || false\n this.logger = opts.logger || null\n if(!this.logger && opts.debug){\n this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }\n }\n this.longpollerTimeout = opts.longpollerTimeout || 20000\n this.params = closure(opts.params || {})\n this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n this.vsn = opts.vsn || DEFAULT_VSN\n this.heartbeatTimeoutTimer = null\n this.heartbeatTimer = null\n this.pendingHeartbeatRef = null\n this.reconnectTimer = new Timer(() => {\n // handleVisibilityChange reconnects once the page is visible again\n if(this.pageHidden && !this.reconnectWhenHidden){\n this.log(\"Not reconnecting as page is hidden!\")\n this.teardown()\n return\n }\n this.teardownAndReconnect(() => this.connect())\n }, this.reconnectAfterMs)\n this.authToken = opts.authToken && closure(opts.authToken)\n }\n\n /**\n * The transport instance of the current connection, for example a WebSocket\n */\n get conn(){ return this.connection && this.connection.transport }\n\n /**\n * @internal\n */\n get pageHidden(){\n return phxWindow && phxWindow.document ? phxWindow.document.visibilityState === \"hidden\" : false\n }\n\n /**\n * @internal\n */\n handleVisibilityChange(){\n if(this.pageHidden){\n this.visibilityConnection = null\n return\n }\n // resume and visibilitychange can both fire when the page is shown again, so we keep\n // the connection created for the first one while it is still connecting\n const connection = this.connection\n if(connection && connection === this.visibilityConnection && connection.isConnecting()){ return }\n // reconnect immediately\n if(!this.isConnected() && !this.closeWasClean){\n // a reconnect that was scheduled before would tear down the connection we create here\n this.reconnectTimer.reset()\n this.teardownAndReconnect(() => {\n this.connect()\n this.visibilityConnection = this.connection\n })\n }\n }\n\n /**\n * Returns the LongPoll transport reference\n */\n getLongPollTransport(){ return LongPoll }\n\n /**\n * Disconnects and replaces the active transport\n *\n * @param {Function} newTransport - The new transport class to instantiate\n *\n */\n replaceTransport(newTransport){\n const connectClock = ++this.connectClock\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // replaced first, as channel error callbacks below can already connect again\n this.transport = newTransport\n const connection = this.connection\n if(connection){\n const wasOpen = connection.isOpen()\n // the old transport closes asynchronously, but its events no longer concern us\n this.connection = null\n connection.ended = true\n connection.close()\n this.clearHeartbeats()\n // any in-flight pushes on the old connection are lost, so channels need to rejoin\n if(wasOpen){ this.triggerChanError(\"connection_closed\", connection) }\n }\n return connectClock\n }\n\n /**\n * Returns the socket protocol\n *\n * @returns {string}\n */\n protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n /**\n * The fully qualified socket url\n *\n * @returns {string}\n */\n endPointURL(){\n let uri = Ajax.appendParams(\n Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n if(uri.charAt(0) !== \"/\"){ return uri }\n if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n return `${this.protocol()}://${location.host}${uri}`\n }\n\n /**\n * Disconnects the socket\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n *\n * @param {Function} callback - Optional callback which is called after socket is disconnected.\n * @param {integer} code - A status code for disconnection (Optional).\n * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n */\n disconnect(callback, code, reason){\n this.connectClock++\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // connecting again creates a new connection right away instead of waiting for this one to close\n if(this.connection){ this.connection.ended = true }\n this.teardown(callback, code, reason)\n }\n\n /**\n *\n * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n *\n * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n */\n connect(params){\n if(params){\n console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n this.params = closure(params)\n }\n // the current connection is kept, including while it reconnects, until the socket is done with it\n if(this.connection && !this.connection.ended){ return }\n if(this.longPollFallbackMs && this.transport !== LongPoll){\n this.connectWithFallback(LongPoll, this.longPollFallbackMs)\n } else {\n this.transportConnect()\n }\n }\n\n /**\n * Logs the message. Override `this.logger` for specialized logging. noops by default\n * @param {string} kind\n * @param {string} msg\n * @param {Object} data\n */\n log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }\n\n /**\n * Returns true if a logger has been set on this socket.\n */\n hasLogger(){ return this.logger !== null }\n\n /**\n * Registers callbacks for connection open events\n *\n * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n *\n * @param {Function} callback\n */\n onOpen(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.open.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection close events\n * @param {Function} callback\n */\n onClose(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.close.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection error events\n *\n * @example socket.onError(function(error){ alert(\"An error occurred\") })\n *\n * @param {Function} callback\n */\n onError(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.error.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection message events\n * @param {Function} callback\n */\n onMessage(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.message.push([ref, callback])\n return ref\n }\n\n /**\n * Pings the server and invokes the callback with the RTT in milliseconds\n * @param {Function} callback\n *\n * Returns true if the ping was pushed or false if unable to be pushed.\n */\n ping(callback){\n if(!this.isConnected()){ return false }\n let ref = this.makeRef()\n let startTime = Date.now()\n this.connection.pings.set(ref, () => callback(Date.now() - startTime))\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: ref})\n return true\n }\n\n /**\n * @private\n *\n * @param {Function}\n */\n transportName(transport){\n // JavaScript minification, enabled by default in production in Phoenix\n // projects, renames symbols to reduce code size.\n // See https://esbuild.github.io/api/#keep-names.\n // This helper ensures we return the correct name for the LongPoll transport\n // even after minification. The other common transport is WebSocket, which\n // is native to browsers and does not need special handling.\n switch(transport){\n case LongPoll: return \"LongPoll\"\n default: return transport.name\n }\n }\n\n /**\n * @private\n */\n transportConnect(){\n const previous = this.connection\n this.connectClock++\n this.closeWasClean = false\n // a reconnect scheduled before would tear down the new connection, but we keep\n // its tries, which only reset once a connection opens\n clearTimeout(this.reconnectTimer.timer)\n let protocols = undefined\n // Sec-WebSocket-Protocol based token\n // (longpoll uses Authorization header instead)\n if(this.authToken){\n protocols = [\"phoenix\", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken()).replace(/=/g, \"\")}`]\n }\n const transport = new this.transport(this.endPointURL(), protocols)\n transport.binaryType = this.binaryType\n transport.timeout = this.longpollerTimeout\n this.connection = new Connection(this, transport)\n // connect can replace an ended connection that is still open while its buffer drains, but the\n // channels joined over it need to rejoin on the new connection instead of waiting for it to close\n if(previous && previous.isOpen()){ this.triggerChanError(\"connection_closed\", previous) }\n }\n\n getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }\n\n storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }\n\n connectWithFallback(fallbackTransport, fallbackThreshold = 2500){\n // the callbacks of a previous attempt must not act on this one, e.g. an attempt\n // that never opened would otherwise fall back again on the next error\n this.cancelFallback()\n let established = false\n let primaryTransport = true\n let errorRef\n let connectClock\n const isCurrent = () => connectClock === this.connectClock && !this.closeWasClean\n let fallbackTransportName = this.transportName(fallbackTransport)\n // the fallback timer is cleared whenever the attempt ends, and the callbacks below\n // only fall back while the attempt is current\n let fallback = (reason) => {\n this.log(\"transport\", `falling back to ${fallbackTransportName}...`, reason)\n // the open callback stays registered to remember the fallback once it connects\n this.off([errorRef])\n primaryTransport = false\n const replaceClock = this.replaceTransport(fallbackTransport)\n if(replaceClock !== this.connectClock){ return }\n this.transportConnect()\n }\n if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback(\"memorized\") }\n\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n\n errorRef = this.fallbackErrorRef = this.onError(reason => {\n if(!isCurrent()){ return }\n this.log(\"transport\", \"error\", reason)\n if(primaryTransport && !established){\n clearTimeout(this.fallbackTimer)\n fallback(reason)\n }\n })\n this.fallbackRef = this.onOpen(() => {\n if(!primaryTransport){\n // reconnects keep using the fallback transport, so a later successful connection\n // must still be remembered, unless an earlier callback disconnected it\n if(this.transport !== fallbackTransport || this.closeWasClean){ return }\n let fallbackTransportName = this.transportName(fallbackTransport)\n // only memorize LP if we never connected to primary\n if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, \"true\") }\n return this.log(\"transport\", `established ${fallbackTransportName} fallback`)\n }\n if(!isCurrent()){ return }\n established = true\n // if we've established primary, give the fallback a new period to attempt ping\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n this.ping(rtt => {\n this.log(\"transport\", \"connected to primary after\", rtt)\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n })\n })\n this.transportConnect()\n connectClock = this.connectClock\n }\n\n /**\n * @private\n *\n * Stops the fallback of the current connection attempt, see connectWithFallback\n */\n cancelFallback(){\n clearTimeout(this.fallbackTimer)\n this.off([this.fallbackRef, this.fallbackErrorRef])\n }\n\n clearHeartbeats(){\n clearTimeout(this.heartbeatTimer)\n clearTimeout(this.heartbeatTimeoutTimer)\n // a late reply to the pending heartbeat must not start the heartbeats again\n this.pendingHeartbeatRef = null\n }\n\n onConnOpen(){\n if(this.hasLogger()) this.log(\"transport\", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)\n this.closeWasClean = false\n this.establishedConnections++\n // the health-check ping has to leave before the buffered join. The transport\n // process blocks in Channel.Server.join until the join returns, so a ping\n // sent after the join is not answered while the mount runs, and the fallback\n // timer then drops a socket that is fine.\n this.pingBeforeBufferedJoin()\n this.flushSendBuffer()\n this.reconnectTimer.reset()\n this.resetHeartbeat()\n this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n }\n\n /**\n * @private\n *\n * Sends the fallback health check before flushSendBuffer. No-op unless a\n * fallback attempt is waiting on the primary transport.\n */\n pingBeforeBufferedJoin(){\n if(!this.fallbackTimer || this.transport === LongPoll){ return }\n this.ping(() => this.passPrimaryHealthCheck())\n }\n\n /**\n * @private\n *\n * The primary transport answered. Later frames on this connection also call\n * this, because a frame is the proof the ping was waiting for.\n */\n passPrimaryHealthCheck(){\n if(this.transport === LongPoll){ return }\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = null\n }\n\n /**\n * @private\n */\n\n heartbeatTimeout(){\n if(this.pendingHeartbeatRef){\n const connection = this.connection\n this.pendingHeartbeatRef = null\n if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n this.closeWasClean = false\n // the connection is presumed dead, so we close it right away instead of waiting for its\n // buffer to drain, which also ensures that it is no longer considered connected when the\n // channels error (LiveView would report a server error otherwise)\n if(connection){ connection.close(WS_CLOSE_NORMAL, \"heartbeat timeout\") }\n // some transports emit their close event synchronously, and callbacks of it as well as\n // channel error callbacks can disconnect or connect, which takes precedence over reconnecting\n if(!this.stillUses(connection)){ return }\n this.triggerChanError(\"heartbeat_timeout\")\n if(!this.stillUses(connection)){ return }\n this.teardownAndReconnect(() => this.reconnectTimer.scheduleTimeout())\n }\n }\n\n resetHeartbeat(){\n if(this.conn && this.conn.skipHeartbeat){ return }\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n /**\n * @private\n *\n * Tears down the current connection to reconnect afterwards, unless the socket was\n * disconnected or connected again in the meantime, which takes precedence.\n */\n teardownAndReconnect(reconnect){\n const connection = this.connection\n const connectClock = this.connectClock\n this.teardown(() => {\n // the socket was disconnected or connected again in the meantime, which the connection does not\n // tell once an overlapping teardown gave up on it\n if(connectClock !== this.connectClock || (connection && connection.ended)){ return }\n reconnect()\n })\n }\n\n /**\n * @private\n *\n * Whether the socket still uses the connection and did not give up on it. User callbacks\n * can disconnect or connect, so code continuing after them checks this first.\n */\n stillUses(connection){\n return this.connection === connection && !(connection && connection.ended)\n }\n\n teardown(callback, code, reason){\n // If someone calls connect before we finish tearing down,\n // we create a new connection, but we still want to finish tearing down the old one.\n const connection = this.connection\n if(!connection){\n return callback && callback()\n }\n // onConnClose is not called when the close event arrives late, so we stop the\n // heartbeats here, as a heartbeat timeout would otherwise reconnect the socket\n this.clearHeartbeats()\n\n this.waitForBufferDone(connection.transport, () => {\n // a connection we already closed, e.g. on a heartbeat timeout or in an\n // overlapping teardown, also errored its channels already\n if(!connection.closing){\n const wasOpened = !connection.isConnecting()\n connection.close(code, reason)\n // channels joined over this connection need to rejoin on the next one, but we cannot rely\n // on onConnClose for that, as the close event may arrive after we stopped waiting for it\n // or after a new connection already replaced this one\n if(wasOpened){ this.triggerChanError(\"connection_closed\", connection) }\n }\n\n this.waitForSocketClosed(connection.transport, () => {\n if(this.connection === connection){ this.connection = null }\n callback && callback()\n })\n })\n }\n\n waitForBufferDone(conn, callback, tries = 1){\n // there is no point in waiting for a conn that is already closing\n if(tries === 5 || !conn.bufferedAmount || conn.readyState !== SOCKET_STATES.open){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForBufferDone(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n waitForSocketClosed(conn, callback, tries = 1){\n if(tries === 5 || conn.readyState === SOCKET_STATES.closed){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForSocketClosed(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n onConnClose(event){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n this.clearHeartbeats()\n // we only close a connection to disconnect or to reconnect, which the code closing it takes\n // care of, so its close event only confirms our close. Any other close is reconnected, whatever\n // its code, as the server, proxies and load balancers also close with 1000 (normal closure)\n const closedByUs = !!(connection && connection.closing)\n // channel callbacks may disconnect or create a new connection, so finish cleaning up\n // this connection first and do not schedule a reconnect for its replacement\n this.triggerChanError(\"connection_closed\", connection)\n if(this.stillUses(connection) && !closedByUs && !this.closeWasClean){\n this.reconnectTimer.scheduleTimeout()\n }\n this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n }\n\n /**\n * @private\n */\n onConnError(error){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"error\", error)\n let transportBefore = this.transport\n let establishedBefore = this.establishedConnections\n this.stateChangeCallbacks.error.forEach(([, callback]) => {\n callback(error, transportBefore, establishedBefore)\n })\n // callbacks can replace the connection, in which case only its own joins are errored\n this.triggerChanError(\"connection_error\", connection)\n }\n\n /**\n * @private\n *\n * Errors the channels, as they need to rejoin once the given connection stopped being open.\n */\n triggerChanError(reason, connection = this.connection){\n // several places notice that a connection stopped being open (onConnError, onConnClose,\n // teardown and heartbeatTimeout). After the first, only channels whose join it carried\n // are errored, as error callbacks may have joined channels that join over another connection.\n let erroredBefore = false\n if(connection && !connection.isOpen()){\n erroredBefore = connection.channelsErrored\n connection.channelsErrored = true\n }\n const joins = this.channels.map(channel => [channel, channel.joinRef()])\n joins.forEach(([channel, joinRef]) => {\n // an earlier error callback can rejoin a later channel, so check its join\n // again immediately before dispatching rather than filtering the list first\n if(channel.joinRef() !== joinRef){ return }\n // the joins of a replaced connection are not the only ones anymore\n if(connection && (erroredBefore || !connection.isCurrent()) && !connection.carried(channel, joinRef)){ return }\n if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n channel.trigger(CHANNEL_EVENTS.error, {source: \"transport\", reason})\n }\n })\n }\n\n /**\n * @returns {string}\n */\n connectionState(){\n switch(this.conn && this.conn.readyState){\n case SOCKET_STATES.connecting: return \"connecting\"\n case SOCKET_STATES.open: return \"open\"\n case SOCKET_STATES.closing: return \"closing\"\n default: return \"closed\"\n }\n }\n\n /**\n * @returns {boolean}\n */\n isConnected(){ return this.connectionState() === \"open\" }\n\n /**\n * @private\n *\n * @param {Channel}\n */\n remove(channel){\n this.off(channel.stateChangeRefs)\n this.channels = this.channels.filter(c => c !== channel)\n }\n\n /**\n * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n *\n * @param {refs} - list of refs returned by calls to\n * `onOpen`, `onClose`, `onError,` and `onMessage`\n */\n off(refs){\n for(let key in this.stateChangeCallbacks){\n this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n return refs.indexOf(ref) === -1\n })\n }\n }\n\n /**\n * Initiates a new channel for the given topic\n *\n * @param {string} topic\n * @param {Object} chanParams - Parameters for the channel\n * @returns {Channel}\n */\n channel(topic, chanParams = {}){\n let chan = new Channel(topic, chanParams, this)\n this.channels.push(chan)\n return chan\n }\n\n /**\n * @param {Object} data\n * @param {Channel} [channel] - The channel the push belongs to, if any\n */\n push(data, channel){\n if(this.hasLogger()){\n let {topic, event, payload, ref, join_ref} = data\n this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n }\n\n if(this.isConnected()){\n this.send(data, channel)\n } else {\n this.sendBuffer.push(() => {\n // the channel may have errored or left while the push was buffered, e.g. when the\n // connection failed before opening, in which case its rejoin supersedes the buffered join\n if(!this.isOutdated(data, channel)){ this.send(data, channel) }\n })\n }\n }\n\n /**\n * @private\n */\n send(data, channel){\n const connection = this.connection\n // the join counts as carried by this connection while it is still encoded, so that the\n // channel rejoins when the connection is torn down before the encoding finished\n if(channel && data.event === CHANNEL_EVENTS.join){ connection.joins.set(channel, data.join_ref) }\n this.encode(data, result => {\n // let encodes finish while this connection drains, but never send on a\n // replacement or after the original connection has started closing\n if(!connection.isCurrent() || !connection.isOpen()){ return }\n // a leave still needs to reach the server after its channel was removed locally\n if(data.event !== CHANNEL_EVENTS.leave && this.isOutdated(data, channel)){ return }\n connection.transport.send(result)\n })\n }\n\n /**\n * @private\n *\n * Whether the push was made for an earlier join of its channel, or the channel left since.\n */\n isOutdated(data, channel){\n return !!channel && (data.join_ref !== channel.joinRef() || channel.isClosed())\n }\n\n /**\n * Return the next message ref, accounting for overflows\n * @returns {string}\n */\n makeRef(){\n let newRef = this.ref + 1\n if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n return this.ref.toString()\n }\n\n sendHeartbeat(){\n if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n this.pendingHeartbeatRef = this.makeRef()\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n }\n\n flushSendBuffer(){\n if(this.isConnected() && this.sendBuffer.length > 0){\n this.sendBuffer.forEach(callback => callback())\n this.sendBuffer = []\n }\n }\n\n onConnMessage(rawMessage){\n const connection = this.connection\n const wasOpen = connection && connection.isOpen()\n this.decode(rawMessage.data, msg => {\n // the transport event can be current even though its asynchronous decode is not, as the\n // connection may have been replaced or closed in the meantime (browsers also drop messages\n // that arrive once a WebSocket is closing)\n if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return }\n // a frame on this connection is the health check. The ping reply is one\n // such frame, and so is anything the server already sent.\n if(this.fallbackTimer){ this.passPrimaryHealthCheck() }\n let {topic, event, payload, ref, join_ref} = msg\n if(ref && ref === this.pendingHeartbeatRef){\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n // removing a channel or callback while dispatching replaces the list instead of\n // changing it, so we iterate over the lists from before to not skip any of them\n const channels = this.channels\n for(let i = 0; i < channels.length; i++){\n const channel = channels[i]\n if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n channel.trigger(event, payload, ref, join_ref)\n }\n\n const callbacks = this.stateChangeCallbacks.message\n for(let i = 0; i < callbacks.length; i++){\n let [, callback] = callbacks[i]\n callback(msg)\n }\n\n const ping = ref && connection.pings.get(ref)\n if(ping){\n connection.pings.delete(ref)\n ping()\n }\n })\n }\n\n leaveOpenTopic(topic){\n let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n if(dupChannel){\n if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n dupChannel.leave()\n }\n }\n}\n"], - "mappings": "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", + "sourcesContent": ["// wraps value in closure or returns closure\nexport let closure = (value) => {\n if(typeof value === \"function\"){\n return value\n } else {\n let closure = function (){ return value }\n return closure\n }\n}\n", "export const globalSelf = typeof self !== \"undefined\" ? self : null\nexport const phxWindow = typeof window !== \"undefined\" ? window : null\nexport const global = globalSelf || phxWindow || globalThis\nexport const DEFAULT_VSN = \"2.0.0\"\nexport const SOCKET_STATES = {connecting: 0, open: 1, closing: 2, closed: 3}\nexport const MAX_LONGPOLL_BATCH_SIZE = 100;\nexport const DEFAULT_TIMEOUT = 10000\nexport const WS_CLOSE_NORMAL = 1000\nexport const CHANNEL_STATES = {\n closed: \"closed\",\n errored: \"errored\",\n joined: \"joined\",\n joining: \"joining\",\n leaving: \"leaving\",\n}\nexport const CHANNEL_EVENTS = {\n close: \"phx_close\",\n error: \"phx_error\",\n join: \"phx_join\",\n reply: \"phx_reply\",\n leave: \"phx_leave\"\n}\n\nexport const TRANSPORTS = {\n longpoll: \"longpoll\",\n websocket: \"websocket\"\n}\nexport const XHR_STATES = {\n complete: 4\n}\nexport const AUTH_TOKEN_PREFIX = \"base64url.bearer.phx.\"\n", "/**\n * Initializes the Push\n * @param {Channel} channel - The Channel\n * @param {string} event - The event, for example `\"phx_join\"`\n * @param {Object} payload - The payload, for example `{user_id: 123}`\n * @param {number} timeout - The push timeout in milliseconds\n */\nexport default class Push {\n constructor(channel, event, payload, timeout){\n this.channel = channel\n this.event = event\n this.payload = payload || function (){ return {} }\n this.receivedResp = null\n this.timeout = timeout\n this.timeoutTimer = null\n this.recHooks = []\n this.sent = false\n }\n\n /**\n *\n * @param {number} timeout\n */\n resend(timeout){\n this.timeout = timeout\n this.reset()\n this.send()\n }\n\n /**\n *\n */\n send(){\n if(this.hasReceived(\"timeout\")){ return }\n this.startTimeout()\n this.sent = true\n this.channel.socket.push({\n topic: this.channel.topic,\n event: this.event,\n payload: this.payload(),\n ref: this.ref,\n join_ref: this.channel.joinRef()\n }, this.channel)\n }\n\n /**\n *\n * @param {*} status\n * @param {*} callback\n */\n receive(status, callback){\n if(this.hasReceived(status)){\n callback(this.receivedResp.response)\n }\n\n this.recHooks.push({status, callback})\n return this\n }\n\n /**\n * @private\n */\n reset(){\n this.cancelRefEvent()\n this.ref = null\n this.refEvent = null\n this.receivedResp = null\n this.sent = false\n }\n\n /**\n * @private\n */\n matchReceive({status, response, _ref}){\n this.recHooks.filter(h => h.status === status)\n .forEach(h => h.callback(response))\n }\n\n /**\n * @private\n */\n cancelRefEvent(){\n if(!this.refEvent){ return }\n this.channel.off(this.refEvent)\n }\n\n /**\n * @private\n */\n cancelTimeout(){\n clearTimeout(this.timeoutTimer)\n this.timeoutTimer = null\n }\n\n /**\n * @private\n */\n startTimeout(){\n if(this.timeoutTimer){ this.cancelTimeout() }\n this.cancelRefEvent()\n this.ref = this.channel.socket.makeRef()\n this.refEvent = this.channel.replyEventName(this.ref)\n\n this.channel.on(this.refEvent, payload => {\n this.cancelRefEvent()\n this.cancelTimeout()\n this.receivedResp = payload\n this.matchReceive(payload)\n })\n\n this.timeoutTimer = setTimeout(() => {\n this.trigger(\"timeout\", {})\n }, this.timeout)\n }\n\n /**\n * @private\n */\n hasReceived(status){\n return this.receivedResp && this.receivedResp.status === status\n }\n\n /**\n * @private\n */\n trigger(status, response){\n this.channel.trigger(this.refEvent, {status, response})\n }\n}\n", "/**\n *\n * Creates a timer that accepts a `timerCalc` function to perform\n * calculated timeout retries, such as exponential backoff.\n *\n * @example\n * let reconnectTimer = new Timer(() => this.connect(), function(tries){\n * return [1000, 5000, 10000][tries - 1] || 10000\n * })\n * reconnectTimer.scheduleTimeout() // fires after 1000\n * reconnectTimer.scheduleTimeout() // fires after 5000\n * reconnectTimer.reset()\n * reconnectTimer.scheduleTimeout() // fires after 1000\n *\n * @param {Function} callback\n * @param {Function} timerCalc\n */\nexport default class Timer {\n constructor(callback, timerCalc){\n this.callback = callback\n this.timerCalc = timerCalc\n this.timer = null\n this.tries = 0\n }\n\n reset(){\n this.tries = 0\n clearTimeout(this.timer)\n }\n\n /**\n * Cancels any previous scheduleTimeout and schedules callback\n */\n scheduleTimeout(){\n clearTimeout(this.timer)\n\n this.timer = setTimeout(() => {\n this.tries = this.tries + 1\n this.callback()\n }, this.timerCalc(this.tries + 1))\n }\n}\n", "import {closure} from \"./utils\"\nimport {\n CHANNEL_EVENTS,\n CHANNEL_STATES,\n} from \"./constants\"\n\nimport Push from \"./push\"\nimport Timer from \"./timer\"\n\n/**\n *\n * @param {string} topic\n * @param {(Object|function)} params\n * @param {Socket} socket\n */\nexport default class Channel {\n constructor(topic, params, socket){\n this.state = CHANNEL_STATES.closed\n this.topic = topic\n this.params = closure(params || {})\n this.socket = socket\n this.bindings = []\n this.bindingRef = 0\n this.timeout = this.socket.timeout\n this.joinedOnce = false\n this.joinPush = new Push(this, CHANNEL_EVENTS.join, this.params, this.timeout)\n this.pushBuffer = []\n this.stateChangeRefs = []\n\n this.rejoinTimer = new Timer(() => {\n if(this.socket.isConnected()){ this.rejoin() }\n }, this.socket.rejoinAfterMs)\n this.stateChangeRefs.push(this.socket.onError(() => this.rejoinTimer.reset()))\n this.stateChangeRefs.push(this.socket.onOpen(() => {\n this.rejoinTimer.reset()\n if(this.isErrored()){ this.rejoin() }\n })\n )\n this.joinPush.receive(\"ok\", () => {\n this.state = CHANNEL_STATES.joined\n this.rejoinTimer.reset()\n this.pushBuffer.forEach(pushEvent => pushEvent.send())\n this.pushBuffer = []\n })\n this.joinPush.receive(\"error\", () => {\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.onClose(() => {\n this.rejoinTimer.reset()\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `close ${this.topic} ${this.joinRef()}`)\n this.state = CHANNEL_STATES.closed\n this.socket.remove(this)\n })\n this.onError(reason => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `error ${this.topic}`, reason)\n if(this.isJoining()){ this.joinPush.reset() }\n this.state = CHANNEL_STATES.errored\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.joinPush.receive(\"timeout\", () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `timeout ${this.topic} (${this.joinRef()})`, this.joinPush.timeout)\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), this.timeout)\n leavePush.send()\n this.state = CHANNEL_STATES.errored\n this.joinPush.reset()\n if(this.socket.isConnected()){ this.rejoinTimer.scheduleTimeout() }\n })\n this.on(CHANNEL_EVENTS.reply, (payload, ref) => {\n this.trigger(this.replyEventName(ref), payload)\n })\n }\n\n /**\n * Join the channel\n * @param {integer} timeout\n * @returns {Push}\n */\n join(timeout = this.timeout){\n if(this.joinedOnce){\n throw new Error(\"tried to join multiple times. 'join' can only be called a single time per channel instance\")\n } else {\n this.timeout = timeout\n this.joinedOnce = true\n this.rejoin()\n return this.joinPush\n }\n }\n\n /**\n * Hook into channel close\n * @param {Function} callback\n */\n onClose(callback){\n this.on(CHANNEL_EVENTS.close, callback)\n }\n\n /**\n * Hook into channel errors\n * @param {Function} callback\n */\n onError(callback){\n return this.on(CHANNEL_EVENTS.error, reason => callback(reason))\n }\n\n /**\n * Subscribes on channel events\n *\n * Subscription returns a ref counter, which can be used later to\n * unsubscribe the exact event listener\n *\n * @example\n * const ref1 = channel.on(\"event\", do_stuff)\n * const ref2 = channel.on(\"event\", do_other_stuff)\n * channel.off(\"event\", ref1)\n * // Since unsubscription, do_stuff won't fire,\n * // while do_other_stuff will keep firing on the \"event\"\n *\n * @param {string} event\n * @param {Function} callback\n * @returns {integer} ref\n */\n on(event, callback){\n let ref = this.bindingRef++\n this.bindings.push({event, ref, callback})\n return ref\n }\n\n /**\n * Unsubscribes off of channel events\n *\n * Use the ref returned from a channel.on() to unsubscribe one\n * handler, or pass nothing for the ref to unsubscribe all\n * handlers for the given event.\n *\n * @example\n * // Unsubscribe the do_stuff handler\n * const ref1 = channel.on(\"event\", do_stuff)\n * channel.off(\"event\", ref1)\n *\n * // Unsubscribe all handlers from event\n * channel.off(\"event\")\n *\n * @param {string} event\n * @param {integer} ref\n */\n off(event, ref){\n this.bindings = this.bindings.filter((bind) => {\n return !(bind.event === event && (typeof ref === \"undefined\" || ref === bind.ref))\n })\n }\n\n /**\n * @private\n */\n canPush(){ return this.socket.isConnected() && this.isJoined() }\n\n /**\n * Sends a message `event` to phoenix with the payload `payload`.\n * Phoenix receives this in the `handle_in(event, payload, socket)`\n * function. if phoenix replies or it times out (default 10000ms),\n * then optionally the reply can be received.\n *\n * @example\n * channel.push(\"event\")\n * .receive(\"ok\", payload => console.log(\"phoenix replied:\", payload))\n * .receive(\"error\", err => console.log(\"phoenix errored\", err))\n * .receive(\"timeout\", () => console.log(\"timed out pushing\"))\n * @param {string} event\n * @param {Object} payload\n * @param {number} [timeout]\n * @returns {Push}\n */\n push(event, payload, timeout = this.timeout){\n payload = payload || {}\n if(!this.joinedOnce){\n throw new Error(`tried to push '${event}' to '${this.topic}' before joining. Use channel.join() before pushing events`)\n }\n let pushEvent = new Push(this, event, function (){ return payload }, timeout)\n if(this.canPush()){\n pushEvent.send()\n } else {\n pushEvent.startTimeout()\n this.pushBuffer.push(pushEvent)\n }\n\n return pushEvent\n }\n\n /** Leaves the channel\n *\n * Unsubscribes from server events, and\n * instructs channel to terminate on server\n *\n * Triggers onClose() hooks\n *\n * To receive leave acknowledgements, use the `receive`\n * hook to bind to the server ack, ie:\n *\n * @example\n * channel.leave().receive(\"ok\", () => alert(\"left!\") )\n *\n * @param {integer} timeout\n * @returns {Push}\n */\n leave(timeout = this.timeout){\n this.rejoinTimer.reset()\n this.joinPush.cancelTimeout()\n\n this.state = CHANNEL_STATES.leaving\n let onClose = () => {\n if(this.socket.hasLogger()) this.socket.log(\"channel\", `leave ${this.topic}`)\n this.trigger(CHANNEL_EVENTS.close, \"leave\")\n }\n let leavePush = new Push(this, CHANNEL_EVENTS.leave, closure({}), timeout)\n leavePush.receive(\"ok\", () => onClose())\n .receive(\"timeout\", () => onClose())\n leavePush.send()\n if(!this.canPush()){ leavePush.trigger(\"ok\", {}) }\n\n return leavePush\n }\n\n /**\n * Overridable message hook\n *\n * Receives all events for specialized message handling\n * before dispatching to the channel callbacks.\n *\n * Must return the payload, modified or unmodified\n * @param {string} event\n * @param {Object} payload\n * @param {integer} ref\n * @returns {Object}\n */\n onMessage(_event, payload, _ref){ return payload }\n\n /**\n * @private\n */\n isMember(topic, event, payload, joinRef){\n if(this.topic !== topic){ return false }\n\n if(joinRef && joinRef !== this.joinRef()){\n if(this.socket.hasLogger()) this.socket.log(\"channel\", \"dropping outdated message\", {topic, event, payload, joinRef})\n return false\n } else {\n return true\n }\n }\n\n /**\n * @private\n */\n joinRef(){ return this.joinPush.ref }\n\n /**\n * @private\n */\n rejoin(timeout = this.timeout){\n if(this.isLeaving()){ return }\n this.socket.leaveOpenTopic(this.topic)\n this.state = CHANNEL_STATES.joining\n this.joinPush.resend(timeout)\n }\n\n /**\n * @private\n */\n trigger(event, payload, ref, joinRef){\n let handledPayload = this.onMessage(event, payload, ref, joinRef)\n if(payload && !handledPayload){ throw new Error(\"channel onMessage callbacks must return the payload, modified or unmodified\") }\n\n let eventBindings = this.bindings.filter(bind => bind.event === event)\n\n for(let i = 0; i < eventBindings.length; i++){\n let bind = eventBindings[i]\n bind.callback(handledPayload, ref, joinRef || this.joinRef())\n }\n }\n\n /**\n * @private\n */\n replyEventName(ref){ return `chan_reply_${ref}` }\n\n /**\n * @private\n */\n isClosed(){ return this.state === CHANNEL_STATES.closed }\n\n /**\n * @private\n */\n isErrored(){ return this.state === CHANNEL_STATES.errored }\n\n /**\n * @private\n */\n isJoined(){ return this.state === CHANNEL_STATES.joined }\n\n /**\n * @private\n */\n isJoining(){ return this.state === CHANNEL_STATES.joining }\n\n /**\n * @private\n */\n isLeaving(){ return this.state === CHANNEL_STATES.leaving }\n}\n", "import {\n global,\n XHR_STATES\n} from \"./constants\"\n\nexport default class Ajax {\n\n static request(method, endPoint, headers, body, timeout, ontimeout, callback){\n if(global.XDomainRequest){\n let req = new global.XDomainRequest() // IE8, IE9\n return this.xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback)\n } else if(global.XMLHttpRequest){\n let req = new global.XMLHttpRequest() // IE7+, Firefox, Chrome, Opera, Safari\n return this.xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback)\n } else if(global.fetch && global.AbortController){\n // Fetch with AbortController for modern browsers\n return this.fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback)\n } else {\n throw new Error(\"No suitable XMLHttpRequest implementation found\")\n }\n }\n\n static fetchRequest(method, endPoint, headers, body, timeout, ontimeout, callback){\n let options = {\n method,\n headers,\n body,\n }\n let controller = null\n let timeoutId = null\n if(timeout){\n controller = new AbortController()\n timeoutId = setTimeout(() => controller.abort(), timeout)\n options.signal = controller.signal\n }\n global.fetch(endPoint, options)\n .then(response => response.text())\n .then(data => this.parseJSON(data))\n .then(data => {\n if(timeoutId){ clearTimeout(timeoutId) }\n callback && callback(data)\n })\n .catch(err => {\n if(timeoutId){ clearTimeout(timeoutId) }\n if(err.name === \"AbortError\" && ontimeout){\n ontimeout()\n } else {\n callback && callback(null)\n }\n })\n return controller\n }\n\n static xdomainRequest(req, method, endPoint, body, timeout, ontimeout, callback){\n req.timeout = timeout\n req.open(method, endPoint)\n req.onload = () => {\n let response = this.parseJSON(req.responseText)\n callback && callback(response)\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n // Work around bug in IE9 that requires an attached onprogress handler\n req.onprogress = () => { }\n\n req.send(body)\n return req\n }\n\n static xhrRequest(req, method, endPoint, headers, body, timeout, ontimeout, callback){\n req.open(method, endPoint, true)\n req.timeout = timeout\n for(let [key, value] of Object.entries(headers)){\n req.setRequestHeader(key, value)\n }\n req.onerror = () => callback && callback(null)\n req.onreadystatechange = () => {\n if(req.readyState === XHR_STATES.complete && callback){\n let response = this.parseJSON(req.responseText)\n callback(response)\n }\n }\n if(ontimeout){ req.ontimeout = ontimeout }\n\n req.send(body)\n return req\n }\n\n static parseJSON(resp){\n if(!resp || resp === \"\"){ return null }\n\n try {\n return JSON.parse(resp)\n } catch {\n console && console.log(\"failed to parse JSON response\", resp)\n return null\n }\n }\n\n static serialize(obj, parentKey){\n let queryStr = []\n for(var key in obj){\n if(!Object.prototype.hasOwnProperty.call(obj, key)){ continue }\n let paramKey = parentKey ? `${parentKey}[${key}]` : key\n let paramVal = obj[key]\n if(typeof paramVal === \"object\"){\n queryStr.push(this.serialize(paramVal, paramKey))\n } else {\n queryStr.push(encodeURIComponent(paramKey) + \"=\" + encodeURIComponent(paramVal))\n }\n }\n return queryStr.join(\"&\")\n }\n\n static appendParams(url, params){\n if(Object.keys(params).length === 0){ return url }\n\n let prefix = url.match(/\\?/) ? \"&\" : \"?\"\n return `${url}${prefix}${this.serialize(params)}`\n }\n}\n", "import {\n SOCKET_STATES,\n TRANSPORTS,\n AUTH_TOKEN_PREFIX,\n MAX_LONGPOLL_BATCH_SIZE\n} from \"./constants\"\n\nimport Ajax from \"./ajax\"\n\nlet arrayBufferToBase64 = (buffer) => {\n let binary = \"\"\n let bytes = new Uint8Array(buffer)\n let len = bytes.byteLength\n for(let i = 0; i < len; i++){ binary += String.fromCharCode(bytes[i]) }\n return btoa(binary)\n}\n\nexport default class LongPoll {\n\n constructor(endPoint, protocols){\n // we only support subprotocols for authToken\n // [\"phoenix\", \"base64url.bearer.phx.BASE64_ENCODED_TOKEN\"]\n if(protocols && protocols.length === 2 && protocols[1].startsWith(AUTH_TOKEN_PREFIX)){\n this.authToken = atob(protocols[1].slice(AUTH_TOKEN_PREFIX.length))\n }\n this.endPoint = null\n this.token = null\n this.skipHeartbeat = true\n // the pending requests and queued messages, which are cancelled when we stop\n this.reqs = new Set()\n this.messageTimers = new Set()\n this.awaitingBatchAck = false\n this.currentBatch = null\n this.currentBatchTimer = null\n this.batchBuffer = []\n this.onopen = function (){ } // noop\n this.onerror = function (){ } // noop\n this.onmessage = function (){ } // noop\n this.onclose = function (){ } // noop\n this.pollEndpoint = this.normalizeEndpoint(endPoint)\n this.readyState = SOCKET_STATES.connecting\n // we must wait for the caller to finish setting up our callbacks and timeout properties,\n // and it might close us before that, e.g. when disconnecting right after connecting\n setTimeout(() => this.poll(), 0)\n }\n\n normalizeEndpoint(endPoint){\n return (endPoint\n .replace(\"ws://\", \"http://\")\n .replace(\"wss://\", \"https://\")\n .replace(new RegExp(\"(.*)\\/\" + TRANSPORTS.websocket), \"$1/\" + TRANSPORTS.longpoll))\n }\n\n endpointURL(){\n return this.pollEndpoint\n }\n\n closeAndRetry(error, code, reason, wasClean){\n if(!this.isActive()){ return }\n // like a failed WebSocket, we are no longer open when we emit the error and then the close.\n // We are connecting again before their callbacks run, so that they can still close us for\n // good, e.g. when they disconnect the socket\n this.stop(SOCKET_STATES.connecting)\n this.onerror(error)\n if(this.isActive()){ this.emitClose(code, reason, wasClean) }\n }\n\n ontimeout(){\n this.closeAndRetry(\"timeout\", 1005, \"timeout\", false)\n }\n\n isActive(){ return this.readyState === SOCKET_STATES.open || this.readyState === SOCKET_STATES.connecting }\n\n poll(){\n // onopen can close us before the next poll, just as the caller can close us\n // before the constructor's deferred first poll\n if(!this.isActive()){ return }\n const headers = {\"Accept\": \"application/json\"}\n if(this.authToken){\n headers[\"X-Phoenix-AuthToken\"] = this.authToken\n }\n this.ajax(\"GET\", headers, null, () => this.ontimeout(), resp => {\n if(resp){\n var {status, token, messages} = resp\n if(status === 410 && this.token !== null){\n // In case we already have a token, this means that our existing session\n // is gone. We fail so that the client rejoins its channels.\n this.closeAndRetry(410, 3410, \"session_gone\", false)\n return\n }\n this.token = token\n } else {\n status = 0\n }\n\n switch(status){\n case 200:\n messages.forEach(msg => {\n // Tasks are what things like event handlers, setTimeout callbacks,\n // promise resolves and more are run within.\n // In modern browsers, there are two different kinds of tasks,\n // microtasks and macrotasks.\n // Microtasks are mainly used for Promises, while macrotasks are\n // used for everything else.\n // Microtasks always have priority over macrotasks. If the JS engine\n // is looking for a task to run, it will always try to empty the\n // microtask queue before attempting to run anything from the\n // macrotask queue.\n //\n // For the WebSocket transport, messages always arrive in their own\n // event. This means that if any promises are resolved from within,\n // their callbacks will always finish execution by the time the\n // next message event handler is run.\n //\n // In order to emulate this behaviour, we need to make sure each\n // onmessage handler is run within its own macrotask.\n //\n // We might stop before that, e.g. when a POST times out or a message\n // handler disconnects, which cancels the remaining messages.\n const timer = setTimeout(() => {\n this.messageTimers.delete(timer)\n this.onmessage({data: msg})\n }, 0)\n this.messageTimers.add(timer)\n })\n this.poll()\n break\n case 204:\n this.poll()\n break\n case 410:\n this.readyState = SOCKET_STATES.open\n this.onopen({})\n this.poll()\n break\n case 403:\n this.onerror(403)\n this.close(1008, \"forbidden\", false)\n break\n case 0:\n case 500:\n this.closeAndRetry(500, 1011, \"internal server error\", false)\n break\n default: throw new Error(`unhandled poll status ${status}`)\n }\n })\n }\n\n // we collect all pushes within the current event loop by\n // setTimeout 0, which optimizes back-to-back procedural\n // pushes against an empty buffer\n\n send(body){\n if(typeof(body) !== \"string\"){ body = arrayBufferToBase64(body) }\n if(this.currentBatch){\n this.currentBatch.push(body)\n } else if(this.awaitingBatchAck){\n this.batchBuffer.push(body)\n } else {\n this.currentBatch = [body]\n this.currentBatchTimer = setTimeout(() => {\n this.batchSend(this.currentBatch)\n this.currentBatch = null\n }, 0)\n }\n }\n\n batchSend(messages, offset = 0){\n this.awaitingBatchAck = true\n const next = offset + MAX_LONGPOLL_BATCH_SIZE\n const batch = messages.slice(offset, next)\n this.ajax(\"POST\", {\"Content-Type\": \"application/x-ndjson\"}, batch.join(\"\\n\"), () => this.ontimeout(), resp => {\n if(!resp || resp.status !== 200){\n // this calls stop which already clears awaitingBatchAck\n this.closeAndRetry(resp && resp.status, 1011, \"internal server error\", false)\n } else if(next < messages.length){\n this.batchSend(messages, next)\n } else if(this.batchBuffer.length > 0){\n this.batchSend(this.batchBuffer)\n this.batchBuffer = []\n } else {\n this.awaitingBatchAck = false\n }\n })\n }\n\n close(code, reason, wasClean){\n this.stop(SOCKET_STATES.closed)\n this.emitClose(code, reason, wasClean)\n }\n\n /**\n * Cancels the pending requests, queued messages and outgoing batches.\n */\n stop(readyState){\n this.readyState = readyState\n // the requests are no longer ours before we abort them, as aborting can invoke their\n // callbacks, synchronously for XHR and asynchronously for fetch\n const reqs = this.reqs\n this.reqs = new Set()\n for(let {req} of reqs){ req && req.abort() }\n for(let timer of this.messageTimers){ clearTimeout(timer) }\n this.messageTimers.clear()\n this.batchBuffer = []\n this.awaitingBatchAck = false\n clearTimeout(this.currentBatchTimer)\n this.currentBatchTimer = null\n this.currentBatch = null\n }\n\n /**\n * Emits the close event. Like a WebSocket closed without a code, it reports 1005 (no status)\n * unless one is given, as 1000 would look like the server closing the connection normally.\n */\n emitClose(code = 1005, reason, wasClean = true){\n let opts = {code, reason, wasClean}\n if(typeof(CloseEvent) !== \"undefined\"){\n this.onclose(new CloseEvent(\"close\", opts))\n } else {\n this.onclose(opts)\n }\n }\n\n ajax(method, headers, body, onCallerTimeout, callback){\n // the request is ours until it completes or we stop, and the callbacks of a request\n // that is not ours anymore are ignored. It is ours before it starts, as a request can\n // complete while it starts.\n const request = {req: null}\n this.reqs.add(request)\n let ontimeout = () => {\n if(this.reqs.delete(request)){ onCallerTimeout() }\n }\n if(this.token !== null){\n headers = Object.assign({}, headers, {\"X-Phoenix-Longpoll-Token\": this.token})\n }\n request.req = Ajax.request(method, this.endpointURL(), headers, body, this.timeout, ontimeout, resp => {\n if(this.reqs.delete(request)){ callback(resp) }\n })\n }\n}\n", "/**\n * Initializes the Presence\n * @param {Channel} channel - The Channel\n * @param {Object} opts - The options,\n * for example `{events: {state: \"state\", diff: \"diff\"}}`\n */\nexport default class Presence {\n\n constructor(channel, opts = {}){\n let events = opts.events || {state: \"presence_state\", diff: \"presence_diff\"}\n this.state = Object.create(null)\n this.pendingDiffs = []\n this.channel = channel\n this.joinRef = null\n this.caller = {\n onJoin: function (){ },\n onLeave: function (){ },\n onSync: function (){ }\n }\n\n this.channel.on(events.state, newState => {\n let {onJoin, onLeave, onSync} = this.caller\n\n this.joinRef = this.channel.joinRef()\n this.state = Presence.syncState(this.state, newState, onJoin, onLeave)\n\n this.pendingDiffs.forEach(diff => {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n })\n this.pendingDiffs = []\n onSync()\n })\n\n this.channel.on(events.diff, diff => {\n let {onJoin, onLeave, onSync} = this.caller\n\n if(this.inPendingSyncState()){\n this.pendingDiffs.push(diff)\n } else {\n this.state = Presence.syncDiff(this.state, diff, onJoin, onLeave)\n onSync()\n }\n })\n }\n\n onJoin(callback){ this.caller.onJoin = callback }\n\n onLeave(callback){ this.caller.onLeave = callback }\n\n onSync(callback){ this.caller.onSync = callback }\n\n list(by){ return Presence.list(this.state, by) }\n\n inPendingSyncState(){\n return !this.joinRef || (this.joinRef !== this.channel.joinRef())\n }\n\n // lower-level public static API\n\n /**\n * Used to sync the list of presences on the server\n * with the client's state. An optional `onJoin` and `onLeave` callback can\n * be provided to react to changes in the client's local presences across\n * disconnects and reconnects with the server.\n *\n * @returns {Presence}\n */\n static syncState(currentState, newState, onJoin, onLeave){\n let state = this.toNullProtoObj(this.clone(currentState))\n newState = this.toNullProtoObj(newState)\n let joins = Object.create(null)\n let leaves = Object.create(null)\n\n this.map(state, (key, presence) => {\n if(!newState[key]){\n leaves[key] = presence\n }\n })\n this.map(newState, (key, newPresence) => {\n let currentPresence = state[key]\n if(currentPresence){\n let newRefs = newPresence.metas.map(m => m.phx_ref)\n let curRefs = currentPresence.metas.map(m => m.phx_ref)\n let joinedMetas = newPresence.metas.filter(m => curRefs.indexOf(m.phx_ref) < 0)\n let leftMetas = currentPresence.metas.filter(m => newRefs.indexOf(m.phx_ref) < 0)\n if(joinedMetas.length > 0){\n joins[key] = newPresence\n joins[key].metas = joinedMetas\n }\n if(leftMetas.length > 0){\n leaves[key] = this.clone(currentPresence)\n leaves[key].metas = leftMetas\n }\n } else {\n joins[key] = newPresence\n }\n })\n return this.syncDiff(state, {joins: joins, leaves: leaves}, onJoin, onLeave)\n }\n\n /**\n *\n * Used to sync a diff of presence join and leave\n * events from the server, as they happen. Like `syncState`, `syncDiff`\n * accepts optional `onJoin` and `onLeave` callbacks to react to a user\n * joining or leaving from a device.\n *\n * @returns {Presence}\n */\n static syncDiff(state, diff, onJoin, onLeave){\n state = this.toNullProtoObj(state)\n let {joins, leaves} = this.clone(diff)\n if(!onJoin){ onJoin = function (){ } }\n if(!onLeave){ onLeave = function (){ } }\n\n this.map(joins, (key, newPresence) => {\n let currentPresence = state[key]\n state[key] = this.clone(newPresence)\n if(currentPresence){\n let joinedRefs = state[key].metas.map(m => m.phx_ref)\n let curMetas = currentPresence.metas.filter(m => joinedRefs.indexOf(m.phx_ref) < 0)\n state[key].metas.unshift(...curMetas)\n }\n onJoin(key, currentPresence, newPresence)\n })\n this.map(leaves, (key, leftPresence) => {\n let currentPresence = state[key]\n if(!currentPresence){ return }\n let refsToRemove = leftPresence.metas.map(m => m.phx_ref)\n currentPresence.metas = currentPresence.metas.filter(p => {\n return refsToRemove.indexOf(p.phx_ref) < 0\n })\n onLeave(key, currentPresence, leftPresence)\n if(currentPresence.metas.length === 0){\n delete state[key]\n }\n })\n return state\n }\n\n /**\n * Returns the array of presences, with selected metadata.\n *\n * @param {Object} presences\n * @param {Function} chooser\n *\n * @returns {Presence}\n */\n static list(presences, chooser){\n if(!chooser){ chooser = function (key, pres){ return pres } }\n\n return this.map(presences, (key, presence) => {\n return chooser(key, presence)\n })\n }\n\n // private\n\n static map(obj, func){\n return Object.getOwnPropertyNames(obj).map(key => func(key, obj[key]))\n }\n\n // Presence keys are chosen on the server and may collide with\n // Object.prototype properties (\"__proto__\", \"constructor\", ...), so any\n // object indexed by presence key must not have a prototype chain\n //\n // TODO: replace the null-prototype objects with Maps in Phoenix 2.0\n // (breaking change for the lower-level static API)\n static toNullProtoObj(obj){\n if(Object.getPrototypeOf(obj) === null){ return obj }\n let cleaned = Object.create(null)\n Object.getOwnPropertyNames(obj).forEach(key => {\n cleaned[key] = obj[key]\n })\n return cleaned\n }\n\n static clone(obj){ return JSON.parse(JSON.stringify(obj)) }\n}\n", "/* The default serializer for encoding and decoding messages */\nimport {\n CHANNEL_EVENTS\n} from \"./constants\"\n\nexport default {\n HEADER_LENGTH: 1,\n META_LENGTH: 4,\n KINDS: {push: 0, reply: 1, broadcast: 2},\n\n encode(msg, callback){\n if(msg.payload.constructor === ArrayBuffer){\n return callback(this.binaryEncode(msg))\n } else {\n let payload = [msg.join_ref, msg.ref, msg.topic, msg.event, msg.payload]\n return callback(JSON.stringify(payload))\n }\n },\n\n decode(rawPayload, callback){\n if(rawPayload.constructor === ArrayBuffer){\n return callback(this.binaryDecode(rawPayload))\n } else {\n let [join_ref, ref, topic, event, payload] = JSON.parse(rawPayload)\n return callback({join_ref, ref, topic, event, payload})\n }\n },\n\n // private\n\n binaryEncode(message){\n let {join_ref, ref, event, topic, payload} = message\n let encoder = new TextEncoder()\n let joinRefBytes = encoder.encode(join_ref)\n let refBytes = encoder.encode(ref)\n let topicBytes = encoder.encode(topic)\n let eventBytes = encoder.encode(event)\n\n this.assertFieldSize(joinRefBytes.byteLength, \"join_ref\")\n this.assertFieldSize(refBytes.byteLength, \"ref\")\n this.assertFieldSize(topicBytes.byteLength, \"topic\")\n this.assertFieldSize(eventBytes.byteLength, \"event\")\n\n let metaLength = this.META_LENGTH + joinRefBytes.byteLength + refBytes.byteLength + topicBytes.byteLength + eventBytes.byteLength\n let header = new ArrayBuffer(this.HEADER_LENGTH + metaLength)\n let headerBytes = new Uint8Array(header)\n let view = new DataView(header)\n let offset = 0\n\n view.setUint8(offset++, this.KINDS.push) // kind\n view.setUint8(offset++, joinRefBytes.byteLength)\n view.setUint8(offset++, refBytes.byteLength)\n view.setUint8(offset++, topicBytes.byteLength)\n view.setUint8(offset++, eventBytes.byteLength)\n headerBytes.set(joinRefBytes, offset); offset += joinRefBytes.byteLength\n headerBytes.set(refBytes, offset); offset += refBytes.byteLength\n headerBytes.set(topicBytes, offset); offset += topicBytes.byteLength\n headerBytes.set(eventBytes, offset); offset += eventBytes.byteLength\n\n var combined = new Uint8Array(header.byteLength + payload.byteLength)\n combined.set(headerBytes, 0)\n combined.set(new Uint8Array(payload), header.byteLength)\n\n return combined.buffer\n },\n\n assertFieldSize(size, name){\n if(size > 255){\n throw new Error(`unable to convert ${name} to binary: must be less than or equal to 255 bytes, but is ${size} bytes`)\n }\n },\n\n binaryDecode(buffer){\n let view = new DataView(buffer)\n let kind = view.getUint8(0)\n let decoder = new TextDecoder()\n switch(kind){\n case this.KINDS.push: return this.decodePush(buffer, view, decoder)\n case this.KINDS.reply: return this.decodeReply(buffer, view, decoder)\n case this.KINDS.broadcast: return this.decodeBroadcast(buffer, view, decoder)\n }\n },\n\n decodePush(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let topicSize = view.getUint8(2)\n let eventSize = view.getUint8(3)\n let offset = this.HEADER_LENGTH + this.META_LENGTH - 1 // pushes have no ref\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n return {join_ref: joinRef, ref: null, topic: topic, event: event, payload: data}\n },\n\n decodeReply(buffer, view, decoder){\n let joinRefSize = view.getUint8(1)\n let refSize = view.getUint8(2)\n let topicSize = view.getUint8(3)\n let eventSize = view.getUint8(4)\n let offset = this.HEADER_LENGTH + this.META_LENGTH\n let joinRef = decoder.decode(buffer.slice(offset, offset + joinRefSize))\n offset = offset + joinRefSize\n let ref = decoder.decode(buffer.slice(offset, offset + refSize))\n offset = offset + refSize\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n let payload = {status: event, response: data}\n return {join_ref: joinRef, ref: ref, topic: topic, event: CHANNEL_EVENTS.reply, payload: payload}\n },\n\n decodeBroadcast(buffer, view, decoder){\n let topicSize = view.getUint8(1)\n let eventSize = view.getUint8(2)\n let offset = this.HEADER_LENGTH + 2\n let topic = decoder.decode(buffer.slice(offset, offset + topicSize))\n offset = offset + topicSize\n let event = decoder.decode(buffer.slice(offset, offset + eventSize))\n offset = offset + eventSize\n let data = buffer.slice(offset, buffer.byteLength)\n\n return {join_ref: null, ref: null, topic: topic, event: event, payload: data}\n }\n}\n", "import {SOCKET_STATES} from \"./constants\"\n\n/**\n * @private\n *\n * A connection of the socket over a single transport instance.\n *\n * It owns everything that belongs to this connection, so that nothing about it can\n * affect a later connection: the transport events are only handled while it is the\n * socket's current connection, and its state is gone together with it.\n */\nexport default class Connection {\n /**\n * @param {Socket} socket\n * @param {Object} transport - The transport instance, for example a WebSocket\n */\n constructor(socket, transport){\n this.socket = socket\n this.transport = transport\n // the join ref of each channel whose join was sent over this connection, weakly held\n // as a connection can outlive many channels, e.g. when navigating between LiveViews\n this.joins = new WeakMap()\n // the callbacks of the pings sent over this connection by ref, as their replies can only\n // arrive over it, so they are gone together with it instead of accumulating on the socket\n this.pings = new Map()\n // set when we close the connection, as opposed to the server or the network\n this.closing = false\n // set when the socket does not want this connection anymore, e.g. on disconnect,\n // so that connecting again creates a new one instead of waiting for it\n this.ended = false\n // set once the channels were errored after the connection stopped being open\n this.channelsErrored = false\n // set once the close event was handled, as LongPoll can emit it more than once\n this.closeHandled = false\n transport.onopen = () => { if(this.isCurrent()){ socket.onConnOpen() } }\n transport.onerror = error => { if(this.isCurrent()){ socket.onConnError(error) } }\n transport.onmessage = event => { if(this.isCurrent()){ socket.onConnMessage(event) } }\n transport.onclose = event => {\n if(this.isCurrent() && !this.closeHandled){\n this.closeHandled = true\n socket.onConnClose(event)\n }\n }\n }\n\n /**\n * Whether this is the connection the socket currently uses. A replaced connection\n * can still emit events while it closes, but they no longer concern the socket.\n */\n isCurrent(){ return this.socket.connection === this }\n\n isOpen(){ return this.transport.readyState === SOCKET_STATES.open }\n\n isConnecting(){ return this.transport.readyState === SOCKET_STATES.connecting }\n\n /**\n * Whether the channel's current join was sent over this connection.\n */\n carried(channel, joinRef = channel.joinRef()){\n return this.joins.has(channel) && this.joins.get(channel) === joinRef\n }\n\n /**\n * Closes the transport. Its close event then only confirms our close.\n */\n close(code, reason){\n this.closing = true\n // closing a transport that is still connecting makes it emit an error, which is\n // caused by closing it and must not be handled as a transport error\n this.transport.onerror = function (){ } // noop\n if(code){ this.transport.close(code, reason || \"\") } else { this.transport.close() }\n }\n}\n", "import {\n global,\n phxWindow,\n CHANNEL_EVENTS,\n DEFAULT_TIMEOUT,\n DEFAULT_VSN,\n SOCKET_STATES,\n TRANSPORTS,\n WS_CLOSE_NORMAL,\n AUTH_TOKEN_PREFIX\n} from \"./constants\"\n\nimport {\n closure\n} from \"./utils\"\n\nimport Ajax from \"./ajax\"\nimport Channel from \"./channel\"\nimport LongPoll from \"./longpoll\"\nimport Serializer from \"./serializer\"\nimport Timer from \"./timer\"\nimport Connection from \"./connection\"\n\n/** Initializes the Socket *\n *\n * For IE8 support use an ES5-shim (https://github.com/es-shims/es5-shim)\n *\n * @param {string} endPoint - The string WebSocket endpoint, ie, `\"ws://example.com/socket\"`,\n * `\"wss://example.com\"`\n * `\"/socket\"` (inherited host & protocol)\n * @param {Object} [opts] - Optional configuration\n * @param {Function} [opts.transport] - The Websocket Transport, for example WebSocket or Phoenix.LongPoll.\n *\n * Defaults to WebSocket with automatic LongPoll fallback if WebSocket is not defined.\n * To fallback to LongPoll when WebSocket attempts fail, use `longPollFallbackMs: 2500`.\n *\n * @param {number} [opts.longPollFallbackMs] - The millisecond time to attempt the primary transport\n * before falling back to the LongPoll transport. Disabled by default.\n *\n * @param {boolean} [opts.debug] - When true, enables debug logging. Default false.\n *\n * @param {Function} [opts.encode] - The function to encode outgoing messages.\n *\n * Defaults to JSON encoder.\n *\n * @param {Function} [opts.decode] - The function to decode incoming messages.\n *\n * Defaults to JSON:\n *\n * ```javascript\n * (payload, callback) => callback(JSON.parse(payload))\n * ```\n *\n * @param {number} [opts.timeout] - The default timeout in milliseconds to trigger push timeouts.\n *\n * Defaults `DEFAULT_TIMEOUT`\n * @param {number} [opts.heartbeatIntervalMs] - The millisec interval to send a heartbeat message\n * @param {Function} [opts.reconnectAfterMs] - The optional function that returns the\n * socket reconnect interval, in milliseconds.\n *\n * Defaults to stepped backoff of:\n *\n * ```javascript\n * function(tries){\n * return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n * }\n * ````\n *\n * @param {boolean} [opts.reconnectWhenHidden] - When true, the socket also reconnects while the\n * page is hidden, for example in a background tab or on a sleeping phone.\n *\n * Defaults to false. The socket then waits until the page is visible again before it reconnects.\n * This avoids reconnect attempts that stall on mobile devices and delay the connection after the\n * device wakes up.\n *\n * @param {Function} [opts.rejoinAfterMs] - The optional function that returns the millisec\n * rejoin interval for individual channels.\n *\n * ```javascript\n * function(tries){\n * return [1000, 2000, 5000][tries - 1] || 10000\n * }\n * ````\n *\n * @param {Function} [opts.logger] - The optional function for specialized logging, ie:\n *\n * ```javascript\n * function(kind, msg, data) {\n * console.log(`${kind}: ${msg}`, data)\n * }\n * ```\n *\n * @param {number} [opts.longpollerTimeout] - The maximum timeout of a long poll AJAX request.\n *\n * Defaults to 20s (double the server long poll timer).\n *\n * @param {(Object|function)} [opts.params] - The optional params to pass when connecting\n * @param {(string|function)} [opts.authToken] - the optional authentication token to be exposed on the server\n * under the `:auth_token` connect_info key. Can be a string or a function that returns a string.\n * @param {string} [opts.binaryType] - The binary type to use for binary WebSocket frames.\n *\n * Defaults to \"arraybuffer\"\n *\n * @param {vsn} [opts.vsn] - The serializer's protocol version to send on connect.\n *\n * Defaults to DEFAULT_VSN.\n *\n * @param {Object} [opts.sessionStorage] - An optional Storage compatible object\n * Phoenix uses sessionStorage for longpoll fallback history. Overriding the store is\n * useful when Phoenix won't have access to `sessionStorage`. For example, This could\n * happen if a site loads a cross-domain channel in an iframe. Example usage:\n *\n * class InMemoryStorage {\n * constructor() { this.storage = {} }\n * getItem(keyName) { return this.storage[keyName] || null }\n * removeItem(keyName) { delete this.storage[keyName] }\n * setItem(keyName, keyValue) { this.storage[keyName] = keyValue }\n * }\n *\n*/\nexport default class Socket {\n constructor(endPoint, opts = {}){\n this.stateChangeCallbacks = {open: [], close: [], error: [], message: []}\n this.channels = []\n this.sendBuffer = []\n this.ref = 0\n this.fallbackRef = null\n this.fallbackErrorRef = null\n this.timeout = opts.timeout || DEFAULT_TIMEOUT\n this.transport = opts.transport || global.WebSocket || LongPoll\n this.primaryPassedHealthCheck = false\n this.longPollFallbackMs = opts.longPollFallbackMs\n this.fallbackTimer = null\n this.sessionStore = opts.sessionStorage || (global && global.sessionStorage)\n this.establishedConnections = 0\n this.defaultEncoder = Serializer.encode.bind(Serializer)\n this.defaultDecoder = Serializer.decode.bind(Serializer)\n // We start with closeWasClean true to avoid the visibility change\n // logic from connecting if the socket was never connected in the first place.\n // transportConnect sets it to false on open.\n this.closeWasClean = true\n this.binaryType = opts.binaryType || \"arraybuffer\"\n // the connection over the current transport instance, see Connection\n this.connection = null\n // changes whenever we connect, disconnect or replace the transport, for the code that needs\n // to know whether that happened while there was no connection, see replaceTransport\n this.connectClock = 1\n // the connection created when the page was shown, see handleVisibilityChange\n this.visibilityConnection = null\n if(this.transport !== LongPoll){\n this.encode = opts.encode || this.defaultEncoder\n this.decode = opts.decode || this.defaultDecoder\n } else {\n this.encode = this.defaultEncoder\n this.decode = this.defaultDecoder\n }\n let awaitingConnectionOnPageShow = null\n if(phxWindow && phxWindow.addEventListener){\n phxWindow.addEventListener(\"pagehide\", _e => {\n // an ended connection only finishes closing, e.g. after disconnect\n if(this.connection && !this.connection.ended){\n this.disconnect()\n awaitingConnectionOnPageShow = this.connectClock\n }\n })\n phxWindow.addEventListener(\"pageshow\", _e => {\n if(awaitingConnectionOnPageShow === this.connectClock){\n awaitingConnectionOnPageShow = null\n this.connect()\n }\n })\n phxWindow.addEventListener(\"visibilitychange\", () => {\n this.handleVisibilityChange()\n })\n // Chrome 149 started to not reliably fire visibilitychange after resume,\n // see https://issues.chromium.org/issues/547062449.\n phxWindow.document && phxWindow.document.addEventListener(\"resume\", () => {\n this.handleVisibilityChange()\n })\n }\n this.heartbeatIntervalMs = opts.heartbeatIntervalMs || 30000\n this.rejoinAfterMs = (tries) => {\n if(opts.rejoinAfterMs){\n return opts.rejoinAfterMs(tries)\n } else {\n return [1000, 2000, 5000][tries - 1] || 10000\n }\n }\n this.reconnectAfterMs = (tries) => {\n if(opts.reconnectAfterMs){\n return opts.reconnectAfterMs(tries)\n } else {\n return [10, 50, 100, 150, 200, 250, 500, 1000, 2000][tries - 1] || 5000\n }\n }\n this.reconnectWhenHidden = opts.reconnectWhenHidden || false\n this.logger = opts.logger || null\n if(!this.logger && opts.debug){\n this.logger = (kind, msg, data) => { console.log(`${kind}: ${msg}`, data) }\n }\n this.longpollerTimeout = opts.longpollerTimeout || 20000\n this.params = closure(opts.params || {})\n this.endPoint = `${endPoint}/${TRANSPORTS.websocket}`\n this.vsn = opts.vsn || DEFAULT_VSN\n this.heartbeatTimeoutTimer = null\n this.heartbeatTimer = null\n this.pendingHeartbeatRef = null\n this.reconnectTimer = new Timer(() => {\n // handleVisibilityChange reconnects once the page is visible again\n if(this.pageHidden && !this.reconnectWhenHidden){\n this.log(\"Not reconnecting as page is hidden!\")\n this.teardown()\n return\n }\n this.teardownAndReconnect(() => this.connect())\n }, this.reconnectAfterMs)\n this.authToken = opts.authToken && closure(opts.authToken)\n }\n\n /**\n * The transport instance of the current connection, for example a WebSocket\n */\n get conn(){ return this.connection && this.connection.transport }\n\n /**\n * @internal\n */\n get pageHidden(){\n return phxWindow && phxWindow.document ? phxWindow.document.visibilityState === \"hidden\" : false\n }\n\n /**\n * @internal\n */\n handleVisibilityChange(){\n if(this.pageHidden){\n this.visibilityConnection = null\n return\n }\n // resume and visibilitychange can both fire when the page is shown again, so we keep\n // the connection created for the first one while it is still connecting\n const connection = this.connection\n if(connection && connection === this.visibilityConnection && connection.isConnecting()){ return }\n // reconnect immediately\n if(!this.isConnected() && !this.closeWasClean){\n // a reconnect that was scheduled before would tear down the connection we create here\n this.reconnectTimer.reset()\n this.teardownAndReconnect(() => {\n this.connect()\n this.visibilityConnection = this.connection\n })\n }\n }\n\n /**\n * Returns the LongPoll transport reference\n */\n getLongPollTransport(){ return LongPoll }\n\n /**\n * Disconnects and replaces the active transport\n *\n * @param {Function} newTransport - The new transport class to instantiate\n *\n */\n replaceTransport(newTransport){\n const connectClock = ++this.connectClock\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // replaced first, as channel error callbacks below can already connect again\n this.transport = newTransport\n const connection = this.connection\n if(connection){\n const wasOpen = connection.isOpen()\n // the old transport closes asynchronously, but its events no longer concern us\n this.connection = null\n connection.ended = true\n connection.close()\n this.clearHeartbeats()\n // any in-flight pushes on the old connection are lost, so channels need to rejoin\n if(wasOpen){ this.triggerChanError(\"connection_closed\", connection) }\n }\n return connectClock\n }\n\n /**\n * Returns the socket protocol\n *\n * @returns {string}\n */\n protocol(){ return location.protocol.match(/^https/) ? \"wss\" : \"ws\" }\n\n /**\n * The fully qualified socket url\n *\n * @returns {string}\n */\n endPointURL(){\n let uri = Ajax.appendParams(\n Ajax.appendParams(this.endPoint, this.params()), {vsn: this.vsn})\n if(uri.charAt(0) !== \"/\"){ return uri }\n if(uri.charAt(1) === \"/\"){ return `${this.protocol()}:${uri}` }\n\n return `${this.protocol()}://${location.host}${uri}`\n }\n\n /**\n * Disconnects the socket\n *\n * See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent#Status_codes for valid status codes.\n *\n * @param {Function} callback - Optional callback which is called after socket is disconnected.\n * @param {integer} code - A status code for disconnection (Optional).\n * @param {string} reason - A textual description of the reason to disconnect. (Optional)\n */\n disconnect(callback, code, reason){\n this.connectClock++\n this.closeWasClean = true\n clearTimeout(this.fallbackTimer)\n this.reconnectTimer.reset()\n // connecting again creates a new connection right away instead of waiting for this one to close\n if(this.connection){ this.connection.ended = true }\n this.teardown(callback, code, reason)\n }\n\n /**\n *\n * @param {Object} params - The params to send when connecting, for example `{user_id: userToken}`\n *\n * Passing params to connect is deprecated; pass them in the Socket constructor instead:\n * `new Socket(\"/socket\", {params: {user_id: userToken}})`.\n */\n connect(params){\n if(params){\n console && console.log(\"passing params to connect is deprecated. Instead pass :params to the Socket constructor\")\n this.params = closure(params)\n }\n // the current connection is kept, including while it reconnects, until the socket is done with it\n if(this.connection && !this.connection.ended){ return }\n if(this.longPollFallbackMs && this.transport !== LongPoll){\n this.connectWithFallback(LongPoll, this.longPollFallbackMs)\n } else {\n this.transportConnect()\n }\n }\n\n /**\n * Logs the message. Override `this.logger` for specialized logging. noops by default\n * @param {string} kind\n * @param {string} msg\n * @param {Object} data\n */\n log(kind, msg, data){ this.logger && this.logger(kind, msg, data) }\n\n /**\n * Returns true if a logger has been set on this socket.\n */\n hasLogger(){ return this.logger !== null }\n\n /**\n * Registers callbacks for connection open events\n *\n * @example socket.onOpen(function(){ console.info(\"the socket was opened\") })\n *\n * @param {Function} callback\n */\n onOpen(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.open.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection close events\n * @param {Function} callback\n */\n onClose(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.close.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection error events\n *\n * @example socket.onError(function(error){ alert(\"An error occurred\") })\n *\n * @param {Function} callback\n */\n onError(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.error.push([ref, callback])\n return ref\n }\n\n /**\n * Registers callbacks for connection message events\n * @param {Function} callback\n */\n onMessage(callback){\n let ref = this.makeRef()\n this.stateChangeCallbacks.message.push([ref, callback])\n return ref\n }\n\n /**\n * Pings the server and invokes the callback with the RTT in milliseconds\n * @param {Function} callback\n *\n * Returns true if the ping was pushed or false if unable to be pushed.\n */\n ping(callback){\n if(!this.isConnected()){ return false }\n let ref = this.makeRef()\n let startTime = Date.now()\n this.connection.pings.set(ref, () => callback(Date.now() - startTime))\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: ref})\n return true\n }\n\n /**\n * @private\n *\n * @param {Function}\n */\n transportName(transport){\n // JavaScript minification, enabled by default in production in Phoenix\n // projects, renames symbols to reduce code size.\n // See https://esbuild.github.io/api/#keep-names.\n // This helper ensures we return the correct name for the LongPoll transport\n // even after minification. The other common transport is WebSocket, which\n // is native to browsers and does not need special handling.\n switch(transport){\n case LongPoll: return \"LongPoll\"\n default: return transport.name\n }\n }\n\n /**\n * @private\n */\n transportConnect(){\n const previous = this.connection\n this.connectClock++\n this.closeWasClean = false\n // a reconnect scheduled before would tear down the new connection, but we keep\n // its tries, which only reset once a connection opens\n clearTimeout(this.reconnectTimer.timer)\n let protocols = undefined\n // Sec-WebSocket-Protocol based token\n // (longpoll uses Authorization header instead)\n if(this.authToken){\n protocols = [\"phoenix\", `${AUTH_TOKEN_PREFIX}${btoa(this.authToken()).replace(/=/g, \"\")}`]\n }\n const transport = new this.transport(this.endPointURL(), protocols)\n transport.binaryType = this.binaryType\n transport.timeout = this.longpollerTimeout\n this.connection = new Connection(this, transport)\n // connect can replace an ended connection that is still open while its buffer drains, but the\n // channels joined over it need to rejoin on the new connection instead of waiting for it to close\n if(previous && previous.isOpen()){ this.triggerChanError(\"connection_closed\", previous) }\n }\n\n getSession(key){ return this.sessionStore && this.sessionStore.getItem(key) }\n\n storeSession(key, val){ this.sessionStore && this.sessionStore.setItem(key, val) }\n\n connectWithFallback(fallbackTransport, fallbackThreshold = 2500){\n // the callbacks of a previous attempt must not act on this one, e.g. an attempt\n // that never opened would otherwise fall back again on the next error\n this.cancelFallback()\n let established = false\n let primaryTransport = true\n let errorRef\n let connectClock\n const isCurrent = () => connectClock === this.connectClock && !this.closeWasClean\n let fallbackTransportName = this.transportName(fallbackTransport)\n // the fallback timer is cleared whenever the attempt ends, and the callbacks below\n // only fall back while the attempt is current\n let fallback = (reason) => {\n this.log(\"transport\", `falling back to ${fallbackTransportName}...`, reason)\n // the open callback stays registered to remember the fallback once it connects\n this.off([errorRef])\n primaryTransport = false\n const replaceClock = this.replaceTransport(fallbackTransport)\n if(replaceClock !== this.connectClock){ return }\n this.transportConnect()\n }\n if(this.getSession(`phx:fallback:${fallbackTransportName}`)){ return fallback(\"memorized\") }\n\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n\n errorRef = this.fallbackErrorRef = this.onError(reason => {\n if(!isCurrent()){ return }\n this.log(\"transport\", \"error\", reason)\n if(primaryTransport && !established){\n clearTimeout(this.fallbackTimer)\n fallback(reason)\n }\n })\n this.fallbackRef = this.onOpen(() => {\n if(!primaryTransport){\n // reconnects keep using the fallback transport, so a later successful connection\n // must still be remembered, unless an earlier callback disconnected it\n if(this.transport !== fallbackTransport || this.closeWasClean){ return }\n let fallbackTransportName = this.transportName(fallbackTransport)\n // only memorize LP if we never connected to primary\n if(!this.primaryPassedHealthCheck){ this.storeSession(`phx:fallback:${fallbackTransportName}`, \"true\") }\n return this.log(\"transport\", `established ${fallbackTransportName} fallback`)\n }\n if(!isCurrent()){ return }\n established = true\n // if we've established primary, give the fallback a new period to attempt ping\n clearTimeout(this.fallbackTimer)\n this.fallbackTimer = setTimeout(fallback, fallbackThreshold)\n this.ping(rtt => {\n this.log(\"transport\", \"connected to primary after\", rtt)\n this.primaryPassedHealthCheck = true\n clearTimeout(this.fallbackTimer)\n })\n })\n this.transportConnect()\n connectClock = this.connectClock\n }\n\n /**\n * @private\n *\n * Stops the fallback of the current connection attempt, see connectWithFallback\n */\n cancelFallback(){\n clearTimeout(this.fallbackTimer)\n this.off([this.fallbackRef, this.fallbackErrorRef])\n }\n\n clearHeartbeats(){\n clearTimeout(this.heartbeatTimer)\n clearTimeout(this.heartbeatTimeoutTimer)\n // a late reply to the pending heartbeat must not start the heartbeats again\n this.pendingHeartbeatRef = null\n }\n\n onConnOpen(){\n if(this.hasLogger()) this.log(\"transport\", `${this.transportName(this.transport)} connected to ${this.endPointURL()}`)\n this.closeWasClean = false\n this.establishedConnections++\n this.flushSendBuffer()\n this.reconnectTimer.reset()\n this.resetHeartbeat()\n this.stateChangeCallbacks.open.forEach(([, callback]) => callback())\n }\n\n /**\n * @private\n */\n\n heartbeatTimeout(){\n if(this.pendingHeartbeatRef){\n const connection = this.connection\n this.pendingHeartbeatRef = null\n if(this.hasLogger()){ this.log(\"transport\", \"heartbeat timeout. Attempting to re-establish connection\") }\n this.closeWasClean = false\n // the connection is presumed dead, so we close it right away instead of waiting for its\n // buffer to drain, which also ensures that it is no longer considered connected when the\n // channels error (LiveView would report a server error otherwise)\n if(connection){ connection.close(WS_CLOSE_NORMAL, \"heartbeat timeout\") }\n // some transports emit their close event synchronously, and callbacks of it as well as\n // channel error callbacks can disconnect or connect, which takes precedence over reconnecting\n if(!this.stillUses(connection)){ return }\n this.triggerChanError(\"heartbeat_timeout\")\n if(!this.stillUses(connection)){ return }\n this.teardownAndReconnect(() => this.reconnectTimer.scheduleTimeout())\n }\n }\n\n resetHeartbeat(){\n if(this.conn && this.conn.skipHeartbeat){ return }\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n /**\n * @private\n *\n * Tears down the current connection to reconnect afterwards, unless the socket was\n * disconnected or connected again in the meantime, which takes precedence.\n */\n teardownAndReconnect(reconnect){\n const connection = this.connection\n const connectClock = this.connectClock\n this.teardown(() => {\n // the socket was disconnected or connected again in the meantime, which the connection does not\n // tell once an overlapping teardown gave up on it\n if(connectClock !== this.connectClock || (connection && connection.ended)){ return }\n reconnect()\n })\n }\n\n /**\n * @private\n *\n * Whether the socket still uses the connection and did not give up on it. User callbacks\n * can disconnect or connect, so code continuing after them checks this first.\n */\n stillUses(connection){\n return this.connection === connection && !(connection && connection.ended)\n }\n\n teardown(callback, code, reason){\n // If someone calls connect before we finish tearing down,\n // we create a new connection, but we still want to finish tearing down the old one.\n const connection = this.connection\n if(!connection){\n return callback && callback()\n }\n // onConnClose is not called when the close event arrives late, so we stop the\n // heartbeats here, as a heartbeat timeout would otherwise reconnect the socket\n this.clearHeartbeats()\n\n this.waitForBufferDone(connection.transport, () => {\n // a connection we already closed, e.g. on a heartbeat timeout or in an\n // overlapping teardown, also errored its channels already\n if(!connection.closing){\n const wasOpened = !connection.isConnecting()\n connection.close(code, reason)\n // channels joined over this connection need to rejoin on the next one, but we cannot rely\n // on onConnClose for that, as the close event may arrive after we stopped waiting for it\n // or after a new connection already replaced this one\n if(wasOpened){ this.triggerChanError(\"connection_closed\", connection) }\n }\n\n this.waitForSocketClosed(connection.transport, () => {\n if(this.connection === connection){ this.connection = null }\n callback && callback()\n })\n })\n }\n\n waitForBufferDone(conn, callback, tries = 1){\n // there is no point in waiting for a conn that is already closing\n if(tries === 5 || !conn.bufferedAmount || conn.readyState !== SOCKET_STATES.open){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForBufferDone(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n waitForSocketClosed(conn, callback, tries = 1){\n if(tries === 5 || conn.readyState === SOCKET_STATES.closed){\n callback()\n return\n }\n\n setTimeout(() => {\n this.waitForSocketClosed(conn, callback, tries + 1)\n }, 150 * tries)\n }\n\n onConnClose(event){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"close\", event)\n this.clearHeartbeats()\n // we only close a connection to disconnect or to reconnect, which the code closing it takes\n // care of, so its close event only confirms our close. Any other close is reconnected, whatever\n // its code, as the server, proxies and load balancers also close with 1000 (normal closure)\n const closedByUs = !!(connection && connection.closing)\n // channel callbacks may disconnect or create a new connection, so finish cleaning up\n // this connection first and do not schedule a reconnect for its replacement\n this.triggerChanError(\"connection_closed\", connection)\n if(this.stillUses(connection) && !closedByUs && !this.closeWasClean){\n this.reconnectTimer.scheduleTimeout()\n }\n this.stateChangeCallbacks.close.forEach(([, callback]) => callback(event))\n }\n\n /**\n * @private\n */\n onConnError(error){\n const connection = this.connection\n if(this.hasLogger()) this.log(\"transport\", \"error\", error)\n let transportBefore = this.transport\n let establishedBefore = this.establishedConnections\n this.stateChangeCallbacks.error.forEach(([, callback]) => {\n callback(error, transportBefore, establishedBefore)\n })\n // callbacks can replace the connection, in which case only its own joins are errored\n this.triggerChanError(\"connection_error\", connection)\n }\n\n /**\n * @private\n *\n * Errors the channels, as they need to rejoin once the given connection stopped being open.\n */\n triggerChanError(reason, connection = this.connection){\n // several places notice that a connection stopped being open (onConnError, onConnClose,\n // teardown and heartbeatTimeout). After the first, only channels whose join it carried\n // are errored, as error callbacks may have joined channels that join over another connection.\n let erroredBefore = false\n if(connection && !connection.isOpen()){\n erroredBefore = connection.channelsErrored\n connection.channelsErrored = true\n }\n const joins = this.channels.map(channel => [channel, channel.joinRef()])\n joins.forEach(([channel, joinRef]) => {\n // an earlier error callback can rejoin a later channel, so check its join\n // again immediately before dispatching rather than filtering the list first\n if(channel.joinRef() !== joinRef){ return }\n // the joins of a replaced connection are not the only ones anymore\n if(connection && (erroredBefore || !connection.isCurrent()) && !connection.carried(channel, joinRef)){ return }\n if(!(channel.isErrored() || channel.isLeaving() || channel.isClosed())){\n channel.trigger(CHANNEL_EVENTS.error, {source: \"transport\", reason})\n }\n })\n }\n\n /**\n * @returns {string}\n */\n connectionState(){\n switch(this.conn && this.conn.readyState){\n case SOCKET_STATES.connecting: return \"connecting\"\n case SOCKET_STATES.open: return \"open\"\n case SOCKET_STATES.closing: return \"closing\"\n default: return \"closed\"\n }\n }\n\n /**\n * @returns {boolean}\n */\n isConnected(){ return this.connectionState() === \"open\" }\n\n /**\n * @private\n *\n * @param {Channel}\n */\n remove(channel){\n this.off(channel.stateChangeRefs)\n this.channels = this.channels.filter(c => c !== channel)\n }\n\n /**\n * Removes `onOpen`, `onClose`, `onError,` and `onMessage` registrations.\n *\n * @param {refs} - list of refs returned by calls to\n * `onOpen`, `onClose`, `onError,` and `onMessage`\n */\n off(refs){\n for(let key in this.stateChangeCallbacks){\n this.stateChangeCallbacks[key] = this.stateChangeCallbacks[key].filter(([ref]) => {\n return refs.indexOf(ref) === -1\n })\n }\n }\n\n /**\n * Initiates a new channel for the given topic\n *\n * @param {string} topic\n * @param {Object} chanParams - Parameters for the channel\n * @returns {Channel}\n */\n channel(topic, chanParams = {}){\n let chan = new Channel(topic, chanParams, this)\n this.channels.push(chan)\n return chan\n }\n\n /**\n * @param {Object} data\n * @param {Channel} [channel] - The channel the push belongs to, if any\n */\n push(data, channel){\n if(this.hasLogger()){\n let {topic, event, payload, ref, join_ref} = data\n this.log(\"push\", `${topic} ${event} (${join_ref}, ${ref})`, payload)\n }\n\n if(this.isConnected()){\n this.send(data, channel)\n } else {\n this.sendBuffer.push(() => {\n // the channel may have errored or left while the push was buffered, e.g. when the\n // connection failed before opening, in which case its rejoin supersedes the buffered join\n if(!this.isOutdated(data, channel)){ this.send(data, channel) }\n })\n }\n }\n\n /**\n * @private\n */\n send(data, channel){\n const connection = this.connection\n // the join counts as carried by this connection while it is still encoded, so that the\n // channel rejoins when the connection is torn down before the encoding finished\n if(channel && data.event === CHANNEL_EVENTS.join){ connection.joins.set(channel, data.join_ref) }\n this.encode(data, result => {\n // let encodes finish while this connection drains, but never send on a\n // replacement or after the original connection has started closing\n if(!connection.isCurrent() || !connection.isOpen()){ return }\n // a leave still needs to reach the server after its channel was removed locally\n if(data.event !== CHANNEL_EVENTS.leave && this.isOutdated(data, channel)){ return }\n connection.transport.send(result)\n })\n }\n\n /**\n * @private\n *\n * Whether the push was made for an earlier join of its channel, or the channel left since.\n */\n isOutdated(data, channel){\n return !!channel && (data.join_ref !== channel.joinRef() || channel.isClosed())\n }\n\n /**\n * Return the next message ref, accounting for overflows\n * @returns {string}\n */\n makeRef(){\n let newRef = this.ref + 1\n if(newRef === this.ref){ this.ref = 0 } else { this.ref = newRef }\n\n return this.ref.toString()\n }\n\n sendHeartbeat(){\n if(this.pendingHeartbeatRef && !this.isConnected()){ return }\n this.pendingHeartbeatRef = this.makeRef()\n this.push({topic: \"phoenix\", event: \"heartbeat\", payload: {}, ref: this.pendingHeartbeatRef})\n this.heartbeatTimeoutTimer = setTimeout(() => this.heartbeatTimeout(), this.heartbeatIntervalMs)\n }\n\n flushSendBuffer(){\n if(this.isConnected() && this.sendBuffer.length > 0){\n this.sendBuffer.forEach(callback => callback())\n this.sendBuffer = []\n }\n }\n\n onConnMessage(rawMessage){\n const connection = this.connection\n const wasOpen = connection && connection.isOpen()\n this.decode(rawMessage.data, msg => {\n // the transport event can be current even though its asynchronous decode is not, as the\n // connection may have been replaced or closed in the meantime (browsers also drop messages\n // that arrive once a WebSocket is closing)\n if(connection !== this.connection || (wasOpen && !connection.isOpen())){ return }\n let {topic, event, payload, ref, join_ref} = msg\n if(ref && ref === this.pendingHeartbeatRef){\n this.clearHeartbeats()\n this.heartbeatTimer = setTimeout(() => this.sendHeartbeat(), this.heartbeatIntervalMs)\n }\n\n if(this.hasLogger()) this.log(\"receive\", `${payload.status || \"\"} ${topic} ${event} ${ref && \"(\" + ref + \")\" || \"\"}`, payload)\n\n // removing a channel or callback while dispatching replaces the list instead of\n // changing it, so we iterate over the lists from before to not skip any of them\n const channels = this.channels\n for(let i = 0; i < channels.length; i++){\n const channel = channels[i]\n if(!channel.isMember(topic, event, payload, join_ref)){ continue }\n channel.trigger(event, payload, ref, join_ref)\n }\n\n const callbacks = this.stateChangeCallbacks.message\n for(let i = 0; i < callbacks.length; i++){\n let [, callback] = callbacks[i]\n callback(msg)\n }\n\n const ping = ref && connection.pings.get(ref)\n if(ping){\n connection.pings.delete(ref)\n ping()\n }\n })\n }\n\n leaveOpenTopic(topic){\n let dupChannel = this.channels.find(c => c.topic === topic && (c.isJoined() || c.isJoining()))\n if(dupChannel){\n if(this.hasLogger()) this.log(\"transport\", `leaving duplicate topic \"${topic}\"`)\n dupChannel.leave()\n }\n }\n}\n"], + "mappings": ";AACO,IAAI,UAAU,CAAC,UAAU;AAC9B,MAAG,OAAO,UAAU,YAAW;AAC7B,WAAO;AAAA,EACT,OAAO;AACL,QAAIA,WAAU,WAAW;AAAE,aAAO;AAAA,IAAM;AACxC,WAAOA;AAAA,EACT;AACF;;;ACRO,IAAM,aAAa,OAAO,SAAS,cAAc,OAAO;AACxD,IAAM,YAAY,OAAO,WAAW,cAAc,SAAS;AAC3D,IAAM,SAAS,cAAc,aAAa;AAC1C,IAAM,cAAc;AACpB,IAAM,gBAAgB,EAAC,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,EAAC;AACpE,IAAM,0BAA0B;AAChC,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AACxB,IAAM,iBAAiB;AAAA,EAC5B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,SAAS;AACX;AACO,IAAM,iBAAiB;AAAA,EAC5B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,MAAM;AAAA,EACN,OAAO;AAAA,EACP,OAAO;AACT;AAEO,IAAM,aAAa;AAAA,EACxB,UAAU;AAAA,EACV,WAAW;AACb;AACO,IAAM,aAAa;AAAA,EACxB,UAAU;AACZ;AACO,IAAM,oBAAoB;;;ACvBjC,IAAqB,OAArB,MAA0B;AAAA,EACxB,YAAY,SAAS,OAAO,SAAS,SAAQ;AAC3C,SAAK,UAAU;AACf,SAAK,QAAQ;AACb,SAAK,UAAU,WAAW,WAAW;AAAE,aAAO,CAAC;AAAA,IAAE;AACjD,SAAK,eAAe;AACpB,SAAK,UAAU;AACf,SAAK,eAAe;AACpB,SAAK,WAAW,CAAC;AACjB,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,SAAQ;AACb,SAAK,UAAU;AACf,SAAK,MAAM;AACX,SAAK,KAAK;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA,EAKA,OAAM;AACJ,QAAG,KAAK,YAAY,SAAS,GAAE;AAAE;AAAA,IAAO;AACxC,SAAK,aAAa;AAClB,SAAK,OAAO;AACZ,SAAK,QAAQ,OAAO,KAAK;AAAA,MACvB,OAAO,KAAK,QAAQ;AAAA,MACpB,OAAO,KAAK;AAAA,MACZ,SAAS,KAAK,QAAQ;AAAA,MACtB,KAAK,KAAK;AAAA,MACV,UAAU,KAAK,QAAQ,QAAQ;AAAA,IACjC,GAAG,KAAK,OAAO;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QAAQ,QAAQ,UAAS;AACvB,QAAG,KAAK,YAAY,MAAM,GAAE;AAC1B,eAAS,KAAK,aAAa,QAAQ;AAAA,IACrC;AAEA,SAAK,SAAS,KAAK,EAAC,QAAQ,SAAQ,CAAC;AACrC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAO;AACL,SAAK,eAAe;AACpB,SAAK,MAAM;AACX,SAAK,WAAW;AAChB,SAAK,eAAe;AACpB,SAAK,OAAO;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,EAAC,QAAQ,UAAU,KAAI,GAAE;AACpC,SAAK,SAAS,OAAO,OAAK,EAAE,WAAW,MAAM,EAC1C,QAAQ,OAAK,EAAE,SAAS,QAAQ,CAAC;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAgB;AACd,QAAG,CAAC,KAAK,UAAS;AAAE;AAAA,IAAO;AAC3B,SAAK,QAAQ,IAAI,KAAK,QAAQ;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAe;AACb,iBAAa,KAAK,YAAY;AAC9B,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,eAAc;AACZ,QAAG,KAAK,cAAa;AAAE,WAAK,cAAc;AAAA,IAAE;AAC5C,SAAK,eAAe;AACpB,SAAK,MAAM,KAAK,QAAQ,OAAO,QAAQ;AACvC,SAAK,WAAW,KAAK,QAAQ,eAAe,KAAK,GAAG;AAEpD,SAAK,QAAQ,GAAG,KAAK,UAAU,aAAW;AACxC,WAAK,eAAe;AACpB,WAAK,cAAc;AACnB,WAAK,eAAe;AACpB,WAAK,aAAa,OAAO;AAAA,IAC3B,CAAC;AAED,SAAK,eAAe,WAAW,MAAM;AACnC,WAAK,QAAQ,WAAW,CAAC,CAAC;AAAA,IAC5B,GAAG,KAAK,OAAO;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,QAAO;AACjB,WAAO,KAAK,gBAAgB,KAAK,aAAa,WAAW;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,QAAQ,UAAS;AACvB,SAAK,QAAQ,QAAQ,KAAK,UAAU,EAAC,QAAQ,SAAQ,CAAC;AAAA,EACxD;AACF;;;AC/GA,IAAqB,QAArB,MAA2B;AAAA,EACzB,YAAY,UAAU,WAAU;AAC9B,SAAK,WAAW;AAChB,SAAK,YAAY;AACjB,SAAK,QAAQ;AACb,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,QAAO;AACL,SAAK,QAAQ;AACb,iBAAa,KAAK,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAiB;AACf,iBAAa,KAAK,KAAK;AAEvB,SAAK,QAAQ,WAAW,MAAM;AAC5B,WAAK,QAAQ,KAAK,QAAQ;AAC1B,WAAK,SAAS;AAAA,IAChB,GAAG,KAAK,UAAU,KAAK,QAAQ,CAAC,CAAC;AAAA,EACnC;AACF;;;AC1BA,IAAqB,UAArB,MAA6B;AAAA,EAC3B,YAAY,OAAO,QAAQ,QAAO;AAChC,SAAK,QAAQ,eAAe;AAC5B,SAAK,QAAQ;AACb,SAAK,SAAS,QAAQ,UAAU,CAAC,CAAC;AAClC,SAAK,SAAS;AACd,SAAK,WAAW,CAAC;AACjB,SAAK,aAAa;AAClB,SAAK,UAAU,KAAK,OAAO;AAC3B,SAAK,aAAa;AAClB,SAAK,WAAW,IAAI,KAAK,MAAM,eAAe,MAAM,KAAK,QAAQ,KAAK,OAAO;AAC7E,SAAK,aAAa,CAAC;AACnB,SAAK,kBAAkB,CAAC;AAExB,SAAK,cAAc,IAAI,MAAM,MAAM;AACjC,UAAG,KAAK,OAAO,YAAY,GAAE;AAAE,aAAK,OAAO;AAAA,MAAE;AAAA,IAC/C,GAAG,KAAK,OAAO,aAAa;AAC5B,SAAK,gBAAgB,KAAK,KAAK,OAAO,QAAQ,MAAM,KAAK,YAAY,MAAM,CAAC,CAAC;AAC7E,SAAK,gBAAgB;AAAA,MAAK,KAAK,OAAO,OAAO,MAAM;AACjD,aAAK,YAAY,MAAM;AACvB,YAAG,KAAK,UAAU,GAAE;AAAE,eAAK,OAAO;AAAA,QAAE;AAAA,MACtC,CAAC;AAAA,IACD;AACA,SAAK,SAAS,QAAQ,MAAM,MAAM;AAChC,WAAK,QAAQ,eAAe;AAC5B,WAAK,YAAY,MAAM;AACvB,WAAK,WAAW,QAAQ,eAAa,UAAU,KAAK,CAAC;AACrD,WAAK,aAAa,CAAC;AAAA,IACrB,CAAC;AACD,SAAK,SAAS,QAAQ,SAAS,MAAM;AACnC,WAAK,QAAQ,eAAe;AAC5B,UAAG,KAAK,OAAO,YAAY,GAAE;AAAE,aAAK,YAAY,gBAAgB;AAAA,MAAE;AAAA,IACpE,CAAC;AACD,SAAK,QAAQ,MAAM;AACjB,WAAK,YAAY,MAAM;AACvB,UAAG,KAAK,OAAO,UAAU,EAAG,MAAK,OAAO,IAAI,WAAW,SAAS,KAAK,KAAK,IAAI,KAAK,QAAQ,CAAC,EAAE;AAC9F,WAAK,QAAQ,eAAe;AAC5B,WAAK,OAAO,OAAO,IAAI;AAAA,IACzB,CAAC;AACD,SAAK,QAAQ,YAAU;AACrB,UAAG,KAAK,OAAO,UAAU,EAAG,MAAK,OAAO,IAAI,WAAW,SAAS,KAAK,KAAK,IAAI,MAAM;AACpF,UAAG,KAAK,UAAU,GAAE;AAAE,aAAK,SAAS,MAAM;AAAA,MAAE;AAC5C,WAAK,QAAQ,eAAe;AAC5B,UAAG,KAAK,OAAO,YAAY,GAAE;AAAE,aAAK,YAAY,gBAAgB;AAAA,MAAE;AAAA,IACpE,CAAC;AACD,SAAK,SAAS,QAAQ,WAAW,MAAM;AACrC,UAAG,KAAK,OAAO,UAAU,EAAG,MAAK,OAAO,IAAI,WAAW,WAAW,KAAK,KAAK,KAAK,KAAK,QAAQ,CAAC,KAAK,KAAK,SAAS,OAAO;AACzH,UAAI,YAAY,IAAI,KAAK,MAAM,eAAe,OAAO,QAAQ,CAAC,CAAC,GAAG,KAAK,OAAO;AAC9E,gBAAU,KAAK;AACf,WAAK,QAAQ,eAAe;AAC5B,WAAK,SAAS,MAAM;AACpB,UAAG,KAAK,OAAO,YAAY,GAAE;AAAE,aAAK,YAAY,gBAAgB;AAAA,MAAE;AAAA,IACpE,CAAC;AACD,SAAK,GAAG,eAAe,OAAO,CAAC,SAAS,QAAQ;AAC9C,WAAK,QAAQ,KAAK,eAAe,GAAG,GAAG,OAAO;AAAA,IAChD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KAAK,UAAU,KAAK,SAAQ;AAC1B,QAAG,KAAK,YAAW;AACjB,YAAM,IAAI,MAAM,4FAA4F;AAAA,IAC9G,OAAO;AACL,WAAK,UAAU;AACf,WAAK,aAAa;AAClB,WAAK,OAAO;AACZ,aAAO,KAAK;AAAA,IACd;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,UAAS;AACf,SAAK,GAAG,eAAe,OAAO,QAAQ;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,UAAS;AACf,WAAO,KAAK,GAAG,eAAe,OAAO,YAAU,SAAS,MAAM,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,GAAG,OAAO,UAAS;AACjB,QAAI,MAAM,KAAK;AACf,SAAK,SAAS,KAAK,EAAC,OAAO,KAAK,SAAQ,CAAC;AACzC,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBA,IAAI,OAAO,KAAI;AACb,SAAK,WAAW,KAAK,SAAS,OAAO,CAAC,SAAS;AAC7C,aAAO,EAAE,KAAK,UAAU,UAAU,OAAO,QAAQ,eAAe,QAAQ,KAAK;AAAA,IAC/E,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,UAAS;AAAE,WAAO,KAAK,OAAO,YAAY,KAAK,KAAK,SAAS;AAAA,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkB/D,KAAK,OAAO,SAAS,UAAU,KAAK,SAAQ;AAC1C,cAAU,WAAW,CAAC;AACtB,QAAG,CAAC,KAAK,YAAW;AAClB,YAAM,IAAI,MAAM,kBAAkB,KAAK,SAAS,KAAK,KAAK,4DAA4D;AAAA,IACxH;AACA,QAAI,YAAY,IAAI,KAAK,MAAM,OAAO,WAAW;AAAE,aAAO;AAAA,IAAQ,GAAG,OAAO;AAC5E,QAAG,KAAK,QAAQ,GAAE;AAChB,gBAAU,KAAK;AAAA,IACjB,OAAO;AACL,gBAAU,aAAa;AACvB,WAAK,WAAW,KAAK,SAAS;AAAA,IAChC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,MAAM,UAAU,KAAK,SAAQ;AAC3B,SAAK,YAAY,MAAM;AACvB,SAAK,SAAS,cAAc;AAE5B,SAAK,QAAQ,eAAe;AAC5B,QAAI,UAAU,MAAM;AAClB,UAAG,KAAK,OAAO,UAAU,EAAG,MAAK,OAAO,IAAI,WAAW,SAAS,KAAK,KAAK,EAAE;AAC5E,WAAK,QAAQ,eAAe,OAAO,OAAO;AAAA,IAC5C;AACA,QAAI,YAAY,IAAI,KAAK,MAAM,eAAe,OAAO,QAAQ,CAAC,CAAC,GAAG,OAAO;AACzE,cAAU,QAAQ,MAAM,MAAM,QAAQ,CAAC,EACpC,QAAQ,WAAW,MAAM,QAAQ,CAAC;AACrC,cAAU,KAAK;AACf,QAAG,CAAC,KAAK,QAAQ,GAAE;AAAE,gBAAU,QAAQ,MAAM,CAAC,CAAC;AAAA,IAAE;AAEjD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,UAAU,QAAQ,SAAS,MAAK;AAAE,WAAO;AAAA,EAAQ;AAAA;AAAA;AAAA;AAAA,EAKjD,SAAS,OAAO,OAAO,SAAS,SAAQ;AACtC,QAAG,KAAK,UAAU,OAAM;AAAE,aAAO;AAAA,IAAM;AAEvC,QAAG,WAAW,YAAY,KAAK,QAAQ,GAAE;AACvC,UAAG,KAAK,OAAO,UAAU,EAAG,MAAK,OAAO,IAAI,WAAW,6BAA6B,EAAC,OAAO,OAAO,SAAS,QAAO,CAAC;AACpH,aAAO;AAAA,IACT,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,UAAS;AAAE,WAAO,KAAK,SAAS;AAAA,EAAI;AAAA;AAAA;AAAA;AAAA,EAKpC,OAAO,UAAU,KAAK,SAAQ;AAC5B,QAAG,KAAK,UAAU,GAAE;AAAE;AAAA,IAAO;AAC7B,SAAK,OAAO,eAAe,KAAK,KAAK;AACrC,SAAK,QAAQ,eAAe;AAC5B,SAAK,SAAS,OAAO,OAAO;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,OAAO,SAAS,KAAK,SAAQ;AACnC,QAAI,iBAAiB,KAAK,UAAU,OAAO,SAAS,KAAK,OAAO;AAChE,QAAG,WAAW,CAAC,gBAAe;AAAE,YAAM,IAAI,MAAM,6EAA6E;AAAA,IAAE;AAE/H,QAAI,gBAAgB,KAAK,SAAS,OAAO,UAAQ,KAAK,UAAU,KAAK;AAErE,aAAQ,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAI;AAC3C,UAAI,OAAO,cAAc,CAAC;AAC1B,WAAK,SAAS,gBAAgB,KAAK,WAAW,KAAK,QAAQ,CAAC;AAAA,IAC9D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,eAAe,KAAI;AAAE,WAAO,cAAc,GAAG;AAAA,EAAG;AAAA;AAAA;AAAA;AAAA,EAKhD,WAAU;AAAE,WAAO,KAAK,UAAU,eAAe;AAAA,EAAO;AAAA;AAAA;AAAA;AAAA,EAKxD,YAAW;AAAE,WAAO,KAAK,UAAU,eAAe;AAAA,EAAQ;AAAA;AAAA;AAAA;AAAA,EAK1D,WAAU;AAAE,WAAO,KAAK,UAAU,eAAe;AAAA,EAAO;AAAA;AAAA;AAAA;AAAA,EAKxD,YAAW;AAAE,WAAO,KAAK,UAAU,eAAe;AAAA,EAAQ;AAAA;AAAA;AAAA;AAAA,EAK1D,YAAW;AAAE,WAAO,KAAK,UAAU,eAAe;AAAA,EAAQ;AAC5D;;;ACjTA,IAAqB,OAArB,MAA0B;AAAA,EAExB,OAAO,QAAQ,QAAQ,UAAU,SAAS,MAAM,SAAS,WAAW,UAAS;AAC3E,QAAG,OAAO,gBAAe;AACvB,UAAI,MAAM,IAAI,OAAO,eAAe;AACpC,aAAO,KAAK,eAAe,KAAK,QAAQ,UAAU,MAAM,SAAS,WAAW,QAAQ;AAAA,IACtF,WAAU,OAAO,gBAAe;AAC9B,UAAI,MAAM,IAAI,OAAO,eAAe;AACpC,aAAO,KAAK,WAAW,KAAK,QAAQ,UAAU,SAAS,MAAM,SAAS,WAAW,QAAQ;AAAA,IAC3F,WAAU,OAAO,SAAS,OAAO,iBAAgB;AAE/C,aAAO,KAAK,aAAa,QAAQ,UAAU,SAAS,MAAM,SAAS,WAAW,QAAQ;AAAA,IACxF,OAAO;AACL,YAAM,IAAI,MAAM,iDAAiD;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,OAAO,aAAa,QAAQ,UAAU,SAAS,MAAM,SAAS,WAAW,UAAS;AAChF,QAAI,UAAU;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,QAAI,aAAa;AACjB,QAAI,YAAY;AAChB,QAAG,SAAQ;AACT,mBAAa,IAAI,gBAAgB;AACjC,kBAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AACxD,cAAQ,SAAS,WAAW;AAAA,IAC9B;AACA,WAAO,MAAM,UAAU,OAAO,EAC3B,KAAK,cAAY,SAAS,KAAK,CAAC,EAChC,KAAK,UAAQ,KAAK,UAAU,IAAI,CAAC,EACjC,KAAK,UAAQ;AACZ,UAAG,WAAU;AAAE,qBAAa,SAAS;AAAA,MAAE;AACvC,kBAAY,SAAS,IAAI;AAAA,IAC3B,CAAC,EACA,MAAM,SAAO;AACZ,UAAG,WAAU;AAAE,qBAAa,SAAS;AAAA,MAAE;AACvC,UAAG,IAAI,SAAS,gBAAgB,WAAU;AACxC,kBAAU;AAAA,MACZ,OAAO;AACL,oBAAY,SAAS,IAAI;AAAA,MAC3B;AAAA,IACF,CAAC;AACH,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,eAAe,KAAK,QAAQ,UAAU,MAAM,SAAS,WAAW,UAAS;AAC9E,QAAI,UAAU;AACd,QAAI,KAAK,QAAQ,QAAQ;AACzB,QAAI,SAAS,MAAM;AACjB,UAAI,WAAW,KAAK,UAAU,IAAI,YAAY;AAC9C,kBAAY,SAAS,QAAQ;AAAA,IAC/B;AACA,QAAG,WAAU;AAAE,UAAI,YAAY;AAAA,IAAU;AAGzC,QAAI,aAAa,MAAM;AAAA,IAAE;AAEzB,QAAI,KAAK,IAAI;AACb,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,WAAW,KAAK,QAAQ,UAAU,SAAS,MAAM,SAAS,WAAW,UAAS;AACnF,QAAI,KAAK,QAAQ,UAAU,IAAI;AAC/B,QAAI,UAAU;AACd,aAAQ,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAE;AAC9C,UAAI,iBAAiB,KAAK,KAAK;AAAA,IACjC;AACA,QAAI,UAAU,MAAM,YAAY,SAAS,IAAI;AAC7C,QAAI,qBAAqB,MAAM;AAC7B,UAAG,IAAI,eAAe,WAAW,YAAY,UAAS;AACpD,YAAI,WAAW,KAAK,UAAU,IAAI,YAAY;AAC9C,iBAAS,QAAQ;AAAA,MACnB;AAAA,IACF;AACA,QAAG,WAAU;AAAE,UAAI,YAAY;AAAA,IAAU;AAEzC,QAAI,KAAK,IAAI;AACb,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,UAAU,MAAK;AACpB,QAAG,CAAC,QAAQ,SAAS,IAAG;AAAE,aAAO;AAAA,IAAK;AAEtC,QAAI;AACF,aAAO,KAAK,MAAM,IAAI;AAAA,IACxB,QAAQ;AACN,iBAAW,QAAQ,IAAI,iCAAiC,IAAI;AAC5D,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,OAAO,UAAU,KAAK,WAAU;AAC9B,QAAI,WAAW,CAAC;AAChB,aAAQ,OAAO,KAAI;AACjB,UAAG,CAAC,OAAO,UAAU,eAAe,KAAK,KAAK,GAAG,GAAE;AAAE;AAAA,MAAS;AAC9D,UAAI,WAAW,YAAY,GAAG,SAAS,IAAI,GAAG,MAAM;AACpD,UAAI,WAAW,IAAI,GAAG;AACtB,UAAG,OAAO,aAAa,UAAS;AAC9B,iBAAS,KAAK,KAAK,UAAU,UAAU,QAAQ,CAAC;AAAA,MAClD,OAAO;AACL,iBAAS,KAAK,mBAAmB,QAAQ,IAAI,MAAM,mBAAmB,QAAQ,CAAC;AAAA,MACjF;AAAA,IACF;AACA,WAAO,SAAS,KAAK,GAAG;AAAA,EAC1B;AAAA,EAEA,OAAO,aAAa,KAAK,QAAO;AAC9B,QAAG,OAAO,KAAK,MAAM,EAAE,WAAW,GAAE;AAAE,aAAO;AAAA,IAAI;AAEjD,QAAI,SAAS,IAAI,MAAM,IAAI,IAAI,MAAM;AACrC,WAAO,GAAG,GAAG,GAAG,MAAM,GAAG,KAAK,UAAU,MAAM,CAAC;AAAA,EACjD;AACF;;;AC/GA,IAAI,sBAAsB,CAAC,WAAW;AACpC,MAAI,SAAS;AACb,MAAI,QAAQ,IAAI,WAAW,MAAM;AACjC,MAAI,MAAM,MAAM;AAChB,WAAQ,IAAI,GAAG,IAAI,KAAK,KAAI;AAAE,cAAU,OAAO,aAAa,MAAM,CAAC,CAAC;AAAA,EAAE;AACtE,SAAO,KAAK,MAAM;AACpB;AAEA,IAAqB,WAArB,MAA8B;AAAA,EAE5B,YAAY,UAAU,WAAU;AAG9B,QAAG,aAAa,UAAU,WAAW,KAAK,UAAU,CAAC,EAAE,WAAW,iBAAiB,GAAE;AACnF,WAAK,YAAY,KAAK,UAAU,CAAC,EAAE,MAAM,kBAAkB,MAAM,CAAC;AAAA,IACpE;AACA,SAAK,WAAW;AAChB,SAAK,QAAQ;AACb,SAAK,gBAAgB;AAErB,SAAK,OAAO,oBAAI,IAAI;AACpB,SAAK,gBAAgB,oBAAI,IAAI;AAC7B,SAAK,mBAAmB;AACxB,SAAK,eAAe;AACpB,SAAK,oBAAoB;AACzB,SAAK,cAAc,CAAC;AACpB,SAAK,SAAS,WAAW;AAAA,IAAE;AAC3B,SAAK,UAAU,WAAW;AAAA,IAAE;AAC5B,SAAK,YAAY,WAAW;AAAA,IAAE;AAC9B,SAAK,UAAU,WAAW;AAAA,IAAE;AAC5B,SAAK,eAAe,KAAK,kBAAkB,QAAQ;AACnD,SAAK,aAAa,cAAc;AAGhC,eAAW,MAAM,KAAK,KAAK,GAAG,CAAC;AAAA,EACjC;AAAA,EAEA,kBAAkB,UAAS;AACzB,WAAQ,SACL,QAAQ,SAAS,SAAS,EAC1B,QAAQ,UAAU,UAAU,EAC5B,QAAQ,IAAI,OAAO,UAAW,WAAW,SAAS,GAAG,QAAQ,WAAW,QAAQ;AAAA,EACrF;AAAA,EAEA,cAAa;AACX,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,cAAc,OAAO,MAAM,QAAQ,UAAS;AAC1C,QAAG,CAAC,KAAK,SAAS,GAAE;AAAE;AAAA,IAAO;AAI7B,SAAK,KAAK,cAAc,UAAU;AAClC,SAAK,QAAQ,KAAK;AAClB,QAAG,KAAK,SAAS,GAAE;AAAE,WAAK,UAAU,MAAM,QAAQ,QAAQ;AAAA,IAAE;AAAA,EAC9D;AAAA,EAEA,YAAW;AACT,SAAK,cAAc,WAAW,MAAM,WAAW,KAAK;AAAA,EACtD;AAAA,EAEA,WAAU;AAAE,WAAO,KAAK,eAAe,cAAc,QAAQ,KAAK,eAAe,cAAc;AAAA,EAAW;AAAA,EAE1G,OAAM;AAGJ,QAAG,CAAC,KAAK,SAAS,GAAE;AAAE;AAAA,IAAO;AAC7B,UAAM,UAAU,EAAC,UAAU,mBAAkB;AAC7C,QAAG,KAAK,WAAU;AAChB,cAAQ,qBAAqB,IAAI,KAAK;AAAA,IACxC;AACA,SAAK,KAAK,OAAO,SAAS,MAAM,MAAM,KAAK,UAAU,GAAG,UAAQ;AAC9D,UAAG,MAAK;AACN,YAAI,EAAC,QAAQ,OAAO,SAAQ,IAAI;AAChC,YAAG,WAAW,OAAO,KAAK,UAAU,MAAK;AAGvC,eAAK,cAAc,KAAK,MAAM,gBAAgB,KAAK;AACnD;AAAA,QACF;AACA,aAAK,QAAQ;AAAA,MACf,OAAO;AACL,iBAAS;AAAA,MACX;AAEA,cAAO,QAAO;AAAA,QACZ,KAAK;AACH,mBAAS,QAAQ,SAAO;AAsBtB,kBAAM,QAAQ,WAAW,MAAM;AAC7B,mBAAK,cAAc,OAAO,KAAK;AAC/B,mBAAK,UAAU,EAAC,MAAM,IAAG,CAAC;AAAA,YAC5B,GAAG,CAAC;AACJ,iBAAK,cAAc,IAAI,KAAK;AAAA,UAC9B,CAAC;AACD,eAAK,KAAK;AACV;AAAA,QACF,KAAK;AACH,eAAK,KAAK;AACV;AAAA,QACF,KAAK;AACH,eAAK,aAAa,cAAc;AAChC,eAAK,OAAO,CAAC,CAAC;AACd,eAAK,KAAK;AACV;AAAA,QACF,KAAK;AACH,eAAK,QAAQ,GAAG;AAChB,eAAK,MAAM,MAAM,aAAa,KAAK;AACnC;AAAA,QACF,KAAK;AAAA,QACL,KAAK;AACH,eAAK,cAAc,KAAK,MAAM,yBAAyB,KAAK;AAC5D;AAAA,QACF;AAAS,gBAAM,IAAI,MAAM,yBAAyB,MAAM,EAAE;AAAA,MAC5D;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,MAAK;AACR,QAAG,OAAO,SAAU,UAAS;AAAE,aAAO,oBAAoB,IAAI;AAAA,IAAE;AAChE,QAAG,KAAK,cAAa;AACnB,WAAK,aAAa,KAAK,IAAI;AAAA,IAC7B,WAAU,KAAK,kBAAiB;AAC9B,WAAK,YAAY,KAAK,IAAI;AAAA,IAC5B,OAAO;AACL,WAAK,eAAe,CAAC,IAAI;AACzB,WAAK,oBAAoB,WAAW,MAAM;AACxC,aAAK,UAAU,KAAK,YAAY;AAChC,aAAK,eAAe;AAAA,MACtB,GAAG,CAAC;AAAA,IACN;AAAA,EACF;AAAA,EAEA,UAAU,UAAU,SAAS,GAAE;AAC7B,SAAK,mBAAmB;AACxB,UAAM,OAAO,SAAS;AACtB,UAAM,QAAQ,SAAS,MAAM,QAAQ,IAAI;AACzC,SAAK,KAAK,QAAQ,EAAC,gBAAgB,uBAAsB,GAAG,MAAM,KAAK,IAAI,GAAG,MAAM,KAAK,UAAU,GAAG,UAAQ;AAC5G,UAAG,CAAC,QAAQ,KAAK,WAAW,KAAI;AAE9B,aAAK,cAAc,QAAQ,KAAK,QAAQ,MAAM,yBAAyB,KAAK;AAAA,MAC9E,WAAU,OAAO,SAAS,QAAO;AAC/B,aAAK,UAAU,UAAU,IAAI;AAAA,MAC/B,WAAU,KAAK,YAAY,SAAS,GAAE;AACpC,aAAK,UAAU,KAAK,WAAW;AAC/B,aAAK,cAAc,CAAC;AAAA,MACtB,OAAO;AACL,aAAK,mBAAmB;AAAA,MAC1B;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAM,QAAQ,UAAS;AAC3B,SAAK,KAAK,cAAc,MAAM;AAC9B,SAAK,UAAU,MAAM,QAAQ,QAAQ;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,YAAW;AACd,SAAK,aAAa;AAGlB,UAAM,OAAO,KAAK;AAClB,SAAK,OAAO,oBAAI,IAAI;AACpB,aAAQ,EAAC,IAAG,KAAK,MAAK;AAAE,aAAO,IAAI,MAAM;AAAA,IAAE;AAC3C,aAAQ,SAAS,KAAK,eAAc;AAAE,mBAAa,KAAK;AAAA,IAAE;AAC1D,SAAK,cAAc,MAAM;AACzB,SAAK,cAAc,CAAC;AACpB,SAAK,mBAAmB;AACxB,iBAAa,KAAK,iBAAiB;AACnC,SAAK,oBAAoB;AACzB,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,OAAO,MAAM,QAAQ,WAAW,MAAK;AAC7C,QAAI,OAAO,EAAC,MAAM,QAAQ,SAAQ;AAClC,QAAG,OAAO,eAAgB,aAAY;AACpC,WAAK,QAAQ,IAAI,WAAW,SAAS,IAAI,CAAC;AAAA,IAC5C,OAAO;AACL,WAAK,QAAQ,IAAI;AAAA,IACnB;AAAA,EACF;AAAA,EAEA,KAAK,QAAQ,SAAS,MAAM,iBAAiB,UAAS;AAIpD,UAAM,UAAU,EAAC,KAAK,KAAI;AAC1B,SAAK,KAAK,IAAI,OAAO;AACrB,QAAI,YAAY,MAAM;AACpB,UAAG,KAAK,KAAK,OAAO,OAAO,GAAE;AAAE,wBAAgB;AAAA,MAAE;AAAA,IACnD;AACA,QAAG,KAAK,UAAU,MAAK;AACrB,gBAAU,OAAO,OAAO,CAAC,GAAG,SAAS,EAAC,4BAA4B,KAAK,MAAK,CAAC;AAAA,IAC/E;AACA,YAAQ,MAAM,KAAK,QAAQ,QAAQ,KAAK,YAAY,GAAG,SAAS,MAAM,KAAK,SAAS,WAAW,UAAQ;AACrG,UAAG,KAAK,KAAK,OAAO,OAAO,GAAE;AAAE,iBAAS,IAAI;AAAA,MAAE;AAAA,IAChD,CAAC;AAAA,EACH;AACF;;;ACzOA,IAAqB,WAArB,MAAqB,UAAS;AAAA,EAE5B,YAAY,SAAS,OAAO,CAAC,GAAE;AAC7B,QAAI,SAAS,KAAK,UAAU,EAAC,OAAO,kBAAkB,MAAM,gBAAe;AAC3E,SAAK,QAAQ,uBAAO,OAAO,IAAI;AAC/B,SAAK,eAAe,CAAC;AACrB,SAAK,UAAU;AACf,SAAK,UAAU;AACf,SAAK,SAAS;AAAA,MACZ,QAAQ,WAAW;AAAA,MAAE;AAAA,MACrB,SAAS,WAAW;AAAA,MAAE;AAAA,MACtB,QAAQ,WAAW;AAAA,MAAE;AAAA,IACvB;AAEA,SAAK,QAAQ,GAAG,OAAO,OAAO,cAAY;AACxC,UAAI,EAAC,QAAQ,SAAS,OAAM,IAAI,KAAK;AAErC,WAAK,UAAU,KAAK,QAAQ,QAAQ;AACpC,WAAK,QAAQ,UAAS,UAAU,KAAK,OAAO,UAAU,QAAQ,OAAO;AAErE,WAAK,aAAa,QAAQ,UAAQ;AAChC,aAAK,QAAQ,UAAS,SAAS,KAAK,OAAO,MAAM,QAAQ,OAAO;AAAA,MAClE,CAAC;AACD,WAAK,eAAe,CAAC;AACrB,aAAO;AAAA,IACT,CAAC;AAED,SAAK,QAAQ,GAAG,OAAO,MAAM,UAAQ;AACnC,UAAI,EAAC,QAAQ,SAAS,OAAM,IAAI,KAAK;AAErC,UAAG,KAAK,mBAAmB,GAAE;AAC3B,aAAK,aAAa,KAAK,IAAI;AAAA,MAC7B,OAAO;AACL,aAAK,QAAQ,UAAS,SAAS,KAAK,OAAO,MAAM,QAAQ,OAAO;AAChE,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,UAAS;AAAE,SAAK,OAAO,SAAS;AAAA,EAAS;AAAA,EAEhD,QAAQ,UAAS;AAAE,SAAK,OAAO,UAAU;AAAA,EAAS;AAAA,EAElD,OAAO,UAAS;AAAE,SAAK,OAAO,SAAS;AAAA,EAAS;AAAA,EAEhD,KAAK,IAAG;AAAE,WAAO,UAAS,KAAK,KAAK,OAAO,EAAE;AAAA,EAAE;AAAA,EAE/C,qBAAoB;AAClB,WAAO,CAAC,KAAK,WAAY,KAAK,YAAY,KAAK,QAAQ,QAAQ;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,OAAO,UAAU,cAAc,UAAU,QAAQ,SAAQ;AACvD,QAAI,QAAQ,KAAK,eAAe,KAAK,MAAM,YAAY,CAAC;AACxD,eAAW,KAAK,eAAe,QAAQ;AACvC,QAAI,QAAQ,uBAAO,OAAO,IAAI;AAC9B,QAAI,SAAS,uBAAO,OAAO,IAAI;AAE/B,SAAK,IAAI,OAAO,CAAC,KAAK,aAAa;AACjC,UAAG,CAAC,SAAS,GAAG,GAAE;AAChB,eAAO,GAAG,IAAI;AAAA,MAChB;AAAA,IACF,CAAC;AACD,SAAK,IAAI,UAAU,CAAC,KAAK,gBAAgB;AACvC,UAAI,kBAAkB,MAAM,GAAG;AAC/B,UAAG,iBAAgB;AACjB,YAAI,UAAU,YAAY,MAAM,IAAI,OAAK,EAAE,OAAO;AAClD,YAAI,UAAU,gBAAgB,MAAM,IAAI,OAAK,EAAE,OAAO;AACtD,YAAI,cAAc,YAAY,MAAM,OAAO,OAAK,QAAQ,QAAQ,EAAE,OAAO,IAAI,CAAC;AAC9E,YAAI,YAAY,gBAAgB,MAAM,OAAO,OAAK,QAAQ,QAAQ,EAAE,OAAO,IAAI,CAAC;AAChF,YAAG,YAAY,SAAS,GAAE;AACxB,gBAAM,GAAG,IAAI;AACb,gBAAM,GAAG,EAAE,QAAQ;AAAA,QACrB;AACA,YAAG,UAAU,SAAS,GAAE;AACtB,iBAAO,GAAG,IAAI,KAAK,MAAM,eAAe;AACxC,iBAAO,GAAG,EAAE,QAAQ;AAAA,QACtB;AAAA,MACF,OAAO;AACL,cAAM,GAAG,IAAI;AAAA,MACf;AAAA,IACF,CAAC;AACD,WAAO,KAAK,SAAS,OAAO,EAAC,OAAc,OAAc,GAAG,QAAQ,OAAO;AAAA,EAC7E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,OAAO,SAAS,OAAO,MAAM,QAAQ,SAAQ;AAC3C,YAAQ,KAAK,eAAe,KAAK;AACjC,QAAI,EAAC,OAAO,OAAM,IAAI,KAAK,MAAM,IAAI;AACrC,QAAG,CAAC,QAAO;AAAE,eAAS,WAAW;AAAA,MAAE;AAAA,IAAE;AACrC,QAAG,CAAC,SAAQ;AAAE,gBAAU,WAAW;AAAA,MAAE;AAAA,IAAE;AAEvC,SAAK,IAAI,OAAO,CAAC,KAAK,gBAAgB;AACpC,UAAI,kBAAkB,MAAM,GAAG;AAC/B,YAAM,GAAG,IAAI,KAAK,MAAM,WAAW;AACnC,UAAG,iBAAgB;AACjB,YAAI,aAAa,MAAM,GAAG,EAAE,MAAM,IAAI,OAAK,EAAE,OAAO;AACpD,YAAI,WAAW,gBAAgB,MAAM,OAAO,OAAK,WAAW,QAAQ,EAAE,OAAO,IAAI,CAAC;AAClF,cAAM,GAAG,EAAE,MAAM,QAAQ,GAAG,QAAQ;AAAA,MACtC;AACA,aAAO,KAAK,iBAAiB,WAAW;AAAA,IAC1C,CAAC;AACD,SAAK,IAAI,QAAQ,CAAC,KAAK,iBAAiB;AACtC,UAAI,kBAAkB,MAAM,GAAG;AAC/B,UAAG,CAAC,iBAAgB;AAAE;AAAA,MAAO;AAC7B,UAAI,eAAe,aAAa,MAAM,IAAI,OAAK,EAAE,OAAO;AACxD,sBAAgB,QAAQ,gBAAgB,MAAM,OAAO,OAAK;AACxD,eAAO,aAAa,QAAQ,EAAE,OAAO,IAAI;AAAA,MAC3C,CAAC;AACD,cAAQ,KAAK,iBAAiB,YAAY;AAC1C,UAAG,gBAAgB,MAAM,WAAW,GAAE;AACpC,eAAO,MAAM,GAAG;AAAA,MAClB;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAAO,KAAK,WAAW,SAAQ;AAC7B,QAAG,CAAC,SAAQ;AAAE,gBAAU,SAAU,KAAK,MAAK;AAAE,eAAO;AAAA,MAAK;AAAA,IAAE;AAE5D,WAAO,KAAK,IAAI,WAAW,CAAC,KAAK,aAAa;AAC5C,aAAO,QAAQ,KAAK,QAAQ;AAAA,IAC9B,CAAC;AAAA,EACH;AAAA;AAAA,EAIA,OAAO,IAAI,KAAK,MAAK;AACnB,WAAO,OAAO,oBAAoB,GAAG,EAAE,IAAI,SAAO,KAAK,KAAK,IAAI,GAAG,CAAC,CAAC;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,eAAe,KAAI;AACxB,QAAG,OAAO,eAAe,GAAG,MAAM,MAAK;AAAE,aAAO;AAAA,IAAI;AACpD,QAAI,UAAU,uBAAO,OAAO,IAAI;AAChC,WAAO,oBAAoB,GAAG,EAAE,QAAQ,SAAO;AAC7C,cAAQ,GAAG,IAAI,IAAI,GAAG;AAAA,IACxB,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,MAAM,KAAI;AAAE,WAAO,KAAK,MAAM,KAAK,UAAU,GAAG,CAAC;AAAA,EAAE;AAC5D;;;AC7KA,IAAO,qBAAQ;AAAA,EACb,eAAe;AAAA,EACf,aAAa;AAAA,EACb,OAAO,EAAC,MAAM,GAAG,OAAO,GAAG,WAAW,EAAC;AAAA,EAEvC,OAAO,KAAK,UAAS;AACnB,QAAG,IAAI,QAAQ,gBAAgB,aAAY;AACzC,aAAO,SAAS,KAAK,aAAa,GAAG,CAAC;AAAA,IACxC,OAAO;AACL,UAAI,UAAU,CAAC,IAAI,UAAU,IAAI,KAAK,IAAI,OAAO,IAAI,OAAO,IAAI,OAAO;AACvE,aAAO,SAAS,KAAK,UAAU,OAAO,CAAC;AAAA,IACzC;AAAA,EACF;AAAA,EAEA,OAAO,YAAY,UAAS;AAC1B,QAAG,WAAW,gBAAgB,aAAY;AACxC,aAAO,SAAS,KAAK,aAAa,UAAU,CAAC;AAAA,IAC/C,OAAO;AACL,UAAI,CAAC,UAAU,KAAK,OAAO,OAAO,OAAO,IAAI,KAAK,MAAM,UAAU;AAClE,aAAO,SAAS,EAAC,UAAU,KAAK,OAAO,OAAO,QAAO,CAAC;AAAA,IACxD;AAAA,EACF;AAAA;AAAA,EAIA,aAAa,SAAQ;AACnB,QAAI,EAAC,UAAU,KAAK,OAAO,OAAO,QAAO,IAAI;AAC7C,QAAI,UAAU,IAAI,YAAY;AAC9B,QAAI,eAAe,QAAQ,OAAO,QAAQ;AAC1C,QAAI,WAAW,QAAQ,OAAO,GAAG;AACjC,QAAI,aAAa,QAAQ,OAAO,KAAK;AACrC,QAAI,aAAa,QAAQ,OAAO,KAAK;AAErC,SAAK,gBAAgB,aAAa,YAAY,UAAU;AACxD,SAAK,gBAAgB,SAAS,YAAY,KAAK;AAC/C,SAAK,gBAAgB,WAAW,YAAY,OAAO;AACnD,SAAK,gBAAgB,WAAW,YAAY,OAAO;AAEnD,QAAI,aAAa,KAAK,cAAc,aAAa,aAAa,SAAS,aAAa,WAAW,aAAa,WAAW;AACvH,QAAI,SAAS,IAAI,YAAY,KAAK,gBAAgB,UAAU;AAC5D,QAAI,cAAc,IAAI,WAAW,MAAM;AACvC,QAAI,OAAO,IAAI,SAAS,MAAM;AAC9B,QAAI,SAAS;AAEb,SAAK,SAAS,UAAU,KAAK,MAAM,IAAI;AACvC,SAAK,SAAS,UAAU,aAAa,UAAU;AAC/C,SAAK,SAAS,UAAU,SAAS,UAAU;AAC3C,SAAK,SAAS,UAAU,WAAW,UAAU;AAC7C,SAAK,SAAS,UAAU,WAAW,UAAU;AAC7C,gBAAY,IAAI,cAAc,MAAM;AAAG,cAAU,aAAa;AAC9D,gBAAY,IAAI,UAAU,MAAM;AAAG,cAAU,SAAS;AACtD,gBAAY,IAAI,YAAY,MAAM;AAAG,cAAU,WAAW;AAC1D,gBAAY,IAAI,YAAY,MAAM;AAAG,cAAU,WAAW;AAE1D,QAAI,WAAW,IAAI,WAAW,OAAO,aAAa,QAAQ,UAAU;AACpE,aAAS,IAAI,aAAa,CAAC;AAC3B,aAAS,IAAI,IAAI,WAAW,OAAO,GAAG,OAAO,UAAU;AAEvD,WAAO,SAAS;AAAA,EAClB;AAAA,EAEA,gBAAgB,MAAM,MAAK;AACzB,QAAG,OAAO,KAAI;AACZ,YAAM,IAAI,MAAM,qBAAqB,IAAI,+DAA+D,IAAI,QAAQ;AAAA,IACtH;AAAA,EACF;AAAA,EAEA,aAAa,QAAO;AAClB,QAAI,OAAO,IAAI,SAAS,MAAM;AAC9B,QAAI,OAAO,KAAK,SAAS,CAAC;AAC1B,QAAI,UAAU,IAAI,YAAY;AAC9B,YAAO,MAAK;AAAA,MACV,KAAK,KAAK,MAAM;AAAM,eAAO,KAAK,WAAW,QAAQ,MAAM,OAAO;AAAA,MAClE,KAAK,KAAK,MAAM;AAAO,eAAO,KAAK,YAAY,QAAQ,MAAM,OAAO;AAAA,MACpE,KAAK,KAAK,MAAM;AAAW,eAAO,KAAK,gBAAgB,QAAQ,MAAM,OAAO;AAAA,IAC9E;AAAA,EACF;AAAA,EAEA,WAAW,QAAQ,MAAM,SAAQ;AAC/B,QAAI,cAAc,KAAK,SAAS,CAAC;AACjC,QAAI,YAAY,KAAK,SAAS,CAAC;AAC/B,QAAI,YAAY,KAAK,SAAS,CAAC;AAC/B,QAAI,SAAS,KAAK,gBAAgB,KAAK,cAAc;AACrD,QAAI,UAAU,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,WAAW,CAAC;AACvE,aAAS,SAAS;AAClB,QAAI,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,SAAS,CAAC;AACnE,aAAS,SAAS;AAClB,QAAI,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,SAAS,CAAC;AACnE,aAAS,SAAS;AAClB,QAAI,OAAO,OAAO,MAAM,QAAQ,OAAO,UAAU;AACjD,WAAO,EAAC,UAAU,SAAS,KAAK,MAAM,OAAc,OAAc,SAAS,KAAI;AAAA,EACjF;AAAA,EAEA,YAAY,QAAQ,MAAM,SAAQ;AAChC,QAAI,cAAc,KAAK,SAAS,CAAC;AACjC,QAAI,UAAU,KAAK,SAAS,CAAC;AAC7B,QAAI,YAAY,KAAK,SAAS,CAAC;AAC/B,QAAI,YAAY,KAAK,SAAS,CAAC;AAC/B,QAAI,SAAS,KAAK,gBAAgB,KAAK;AACvC,QAAI,UAAU,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,WAAW,CAAC;AACvE,aAAS,SAAS;AAClB,QAAI,MAAM,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,OAAO,CAAC;AAC/D,aAAS,SAAS;AAClB,QAAI,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,SAAS,CAAC;AACnE,aAAS,SAAS;AAClB,QAAI,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,SAAS,CAAC;AACnE,aAAS,SAAS;AAClB,QAAI,OAAO,OAAO,MAAM,QAAQ,OAAO,UAAU;AACjD,QAAI,UAAU,EAAC,QAAQ,OAAO,UAAU,KAAI;AAC5C,WAAO,EAAC,UAAU,SAAS,KAAU,OAAc,OAAO,eAAe,OAAO,QAAgB;AAAA,EAClG;AAAA,EAEA,gBAAgB,QAAQ,MAAM,SAAQ;AACpC,QAAI,YAAY,KAAK,SAAS,CAAC;AAC/B,QAAI,YAAY,KAAK,SAAS,CAAC;AAC/B,QAAI,SAAS,KAAK,gBAAgB;AAClC,QAAI,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,SAAS,CAAC;AACnE,aAAS,SAAS;AAClB,QAAI,QAAQ,QAAQ,OAAO,OAAO,MAAM,QAAQ,SAAS,SAAS,CAAC;AACnE,aAAS,SAAS;AAClB,QAAI,OAAO,OAAO,MAAM,QAAQ,OAAO,UAAU;AAEjD,WAAO,EAAC,UAAU,MAAM,KAAK,MAAM,OAAc,OAAc,SAAS,KAAI;AAAA,EAC9E;AACF;;;ACtHA,IAAqB,aAArB,MAAgC;AAAA;AAAA;AAAA;AAAA;AAAA,EAK9B,YAAY,QAAQ,WAAU;AAC5B,SAAK,SAAS;AACd,SAAK,YAAY;AAGjB,SAAK,QAAQ,oBAAI,QAAQ;AAGzB,SAAK,QAAQ,oBAAI,IAAI;AAErB,SAAK,UAAU;AAGf,SAAK,QAAQ;AAEb,SAAK,kBAAkB;AAEvB,SAAK,eAAe;AACpB,cAAU,SAAS,MAAM;AAAE,UAAG,KAAK,UAAU,GAAE;AAAE,eAAO,WAAW;AAAA,MAAE;AAAA,IAAE;AACvE,cAAU,UAAU,WAAS;AAAE,UAAG,KAAK,UAAU,GAAE;AAAE,eAAO,YAAY,KAAK;AAAA,MAAE;AAAA,IAAE;AACjF,cAAU,YAAY,WAAS;AAAE,UAAG,KAAK,UAAU,GAAE;AAAE,eAAO,cAAc,KAAK;AAAA,MAAE;AAAA,IAAE;AACrF,cAAU,UAAU,WAAS;AAC3B,UAAG,KAAK,UAAU,KAAK,CAAC,KAAK,cAAa;AACxC,aAAK,eAAe;AACpB,eAAO,YAAY,KAAK;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAW;AAAE,WAAO,KAAK,OAAO,eAAe;AAAA,EAAK;AAAA,EAEpD,SAAQ;AAAE,WAAO,KAAK,UAAU,eAAe,cAAc;AAAA,EAAK;AAAA,EAElE,eAAc;AAAE,WAAO,KAAK,UAAU,eAAe,cAAc;AAAA,EAAW;AAAA;AAAA;AAAA;AAAA,EAK9E,QAAQ,SAAS,UAAU,QAAQ,QAAQ,GAAE;AAC3C,WAAO,KAAK,MAAM,IAAI,OAAO,KAAK,KAAK,MAAM,IAAI,OAAO,MAAM;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,MAAM,QAAO;AACjB,SAAK,UAAU;AAGf,SAAK,UAAU,UAAU,WAAW;AAAA,IAAE;AACtC,QAAG,MAAK;AAAE,WAAK,UAAU,MAAM,MAAM,UAAU,EAAE;AAAA,IAAE,OAAO;AAAE,WAAK,UAAU,MAAM;AAAA,IAAE;AAAA,EACrF;AACF;;;ACgDA,IAAqB,SAArB,MAA4B;AAAA,EAC1B,YAAY,UAAU,OAAO,CAAC,GAAE;AAC9B,SAAK,uBAAuB,EAAC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,GAAG,SAAS,CAAC,EAAC;AACxE,SAAK,WAAW,CAAC;AACjB,SAAK,aAAa,CAAC;AACnB,SAAK,MAAM;AACX,SAAK,cAAc;AACnB,SAAK,mBAAmB;AACxB,SAAK,UAAU,KAAK,WAAW;AAC/B,SAAK,YAAY,KAAK,aAAa,OAAO,aAAa;AACvD,SAAK,2BAA2B;AAChC,SAAK,qBAAqB,KAAK;AAC/B,SAAK,gBAAgB;AACrB,SAAK,eAAe,KAAK,kBAAmB,UAAU,OAAO;AAC7D,SAAK,yBAAyB;AAC9B,SAAK,iBAAiB,mBAAW,OAAO,KAAK,kBAAU;AACvD,SAAK,iBAAiB,mBAAW,OAAO,KAAK,kBAAU;AAIvD,SAAK,gBAAgB;AACrB,SAAK,aAAa,KAAK,cAAc;AAErC,SAAK,aAAa;AAGlB,SAAK,eAAe;AAEpB,SAAK,uBAAuB;AAC5B,QAAG,KAAK,cAAc,UAAS;AAC7B,WAAK,SAAS,KAAK,UAAU,KAAK;AAClC,WAAK,SAAS,KAAK,UAAU,KAAK;AAAA,IACpC,OAAO;AACL,WAAK,SAAS,KAAK;AACnB,WAAK,SAAS,KAAK;AAAA,IACrB;AACA,QAAI,+BAA+B;AACnC,QAAG,aAAa,UAAU,kBAAiB;AACzC,gBAAU,iBAAiB,YAAY,QAAM;AAE3C,YAAG,KAAK,cAAc,CAAC,KAAK,WAAW,OAAM;AAC3C,eAAK,WAAW;AAChB,yCAA+B,KAAK;AAAA,QACtC;AAAA,MACF,CAAC;AACD,gBAAU,iBAAiB,YAAY,QAAM;AAC3C,YAAG,iCAAiC,KAAK,cAAa;AACpD,yCAA+B;AAC/B,eAAK,QAAQ;AAAA,QACf;AAAA,MACF,CAAC;AACD,gBAAU,iBAAiB,oBAAoB,MAAM;AACnD,aAAK,uBAAuB;AAAA,MAC9B,CAAC;AAGD,gBAAU,YAAY,UAAU,SAAS,iBAAiB,UAAU,MAAM;AACxE,aAAK,uBAAuB;AAAA,MAC9B,CAAC;AAAA,IACH;AACA,SAAK,sBAAsB,KAAK,uBAAuB;AACvD,SAAK,gBAAgB,CAAC,UAAU;AAC9B,UAAG,KAAK,eAAc;AACpB,eAAO,KAAK,cAAc,KAAK;AAAA,MACjC,OAAO;AACL,eAAO,CAAC,KAAM,KAAM,GAAI,EAAE,QAAQ,CAAC,KAAK;AAAA,MAC1C;AAAA,IACF;AACA,SAAK,mBAAmB,CAAC,UAAU;AACjC,UAAG,KAAK,kBAAiB;AACvB,eAAO,KAAK,iBAAiB,KAAK;AAAA,MACpC,OAAO;AACL,eAAO,CAAC,IAAI,IAAI,KAAK,KAAK,KAAK,KAAK,KAAK,KAAM,GAAI,EAAE,QAAQ,CAAC,KAAK;AAAA,MACrE;AAAA,IACF;AACA,SAAK,sBAAsB,KAAK,uBAAuB;AACvD,SAAK,SAAS,KAAK,UAAU;AAC7B,QAAG,CAAC,KAAK,UAAU,KAAK,OAAM;AAC5B,WAAK,SAAS,CAAC,MAAM,KAAK,SAAS;AAAE,gBAAQ,IAAI,GAAG,IAAI,KAAK,GAAG,IAAI,IAAI;AAAA,MAAE;AAAA,IAC5E;AACA,SAAK,oBAAoB,KAAK,qBAAqB;AACnD,SAAK,SAAS,QAAQ,KAAK,UAAU,CAAC,CAAC;AACvC,SAAK,WAAW,GAAG,QAAQ,IAAI,WAAW,SAAS;AACnD,SAAK,MAAM,KAAK,OAAO;AACvB,SAAK,wBAAwB;AAC7B,SAAK,iBAAiB;AACtB,SAAK,sBAAsB;AAC3B,SAAK,iBAAiB,IAAI,MAAM,MAAM;AAEpC,UAAG,KAAK,cAAc,CAAC,KAAK,qBAAoB;AAC9C,aAAK,IAAI,qCAAqC;AAC9C,aAAK,SAAS;AACd;AAAA,MACF;AACA,WAAK,qBAAqB,MAAM,KAAK,QAAQ,CAAC;AAAA,IAChD,GAAG,KAAK,gBAAgB;AACxB,SAAK,YAAY,KAAK,aAAa,QAAQ,KAAK,SAAS;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKA,IAAI,OAAM;AAAE,WAAO,KAAK,cAAc,KAAK,WAAW;AAAA,EAAU;AAAA;AAAA;AAAA;AAAA,EAKhE,IAAI,aAAY;AACd,WAAO,aAAa,UAAU,WAAW,UAAU,SAAS,oBAAoB,WAAW;AAAA,EAC7F;AAAA;AAAA;AAAA;AAAA,EAKA,yBAAwB;AACtB,QAAG,KAAK,YAAW;AACjB,WAAK,uBAAuB;AAC5B;AAAA,IACF;AAGA,UAAM,aAAa,KAAK;AACxB,QAAG,cAAc,eAAe,KAAK,wBAAwB,WAAW,aAAa,GAAE;AAAE;AAAA,IAAO;AAEhG,QAAG,CAAC,KAAK,YAAY,KAAK,CAAC,KAAK,eAAc;AAE5C,WAAK,eAAe,MAAM;AAC1B,WAAK,qBAAqB,MAAM;AAC9B,aAAK,QAAQ;AACb,aAAK,uBAAuB,KAAK;AAAA,MACnC,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAsB;AAAE,WAAO;AAAA,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQxC,iBAAiB,cAAa;AAC5B,UAAM,eAAe,EAAE,KAAK;AAC5B,SAAK,gBAAgB;AACrB,iBAAa,KAAK,aAAa;AAC/B,SAAK,eAAe,MAAM;AAE1B,SAAK,YAAY;AACjB,UAAM,aAAa,KAAK;AACxB,QAAG,YAAW;AACZ,YAAM,UAAU,WAAW,OAAO;AAElC,WAAK,aAAa;AAClB,iBAAW,QAAQ;AACnB,iBAAW,MAAM;AACjB,WAAK,gBAAgB;AAErB,UAAG,SAAQ;AAAE,aAAK,iBAAiB,qBAAqB,UAAU;AAAA,MAAE;AAAA,IACtE;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAU;AAAE,WAAO,SAAS,SAAS,MAAM,QAAQ,IAAI,QAAQ;AAAA,EAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpE,cAAa;AACX,QAAI,MAAM,KAAK;AAAA,MACb,KAAK,aAAa,KAAK,UAAU,KAAK,OAAO,CAAC;AAAA,MAAG,EAAC,KAAK,KAAK,IAAG;AAAA,IAAC;AAClE,QAAG,IAAI,OAAO,CAAC,MAAM,KAAI;AAAE,aAAO;AAAA,IAAI;AACtC,QAAG,IAAI,OAAO,CAAC,MAAM,KAAI;AAAE,aAAO,GAAG,KAAK,SAAS,CAAC,IAAI,GAAG;AAAA,IAAG;AAE9D,WAAO,GAAG,KAAK,SAAS,CAAC,MAAM,SAAS,IAAI,GAAG,GAAG;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,WAAW,UAAU,MAAM,QAAO;AAChC,SAAK;AACL,SAAK,gBAAgB;AACrB,iBAAa,KAAK,aAAa;AAC/B,SAAK,eAAe,MAAM;AAE1B,QAAG,KAAK,YAAW;AAAE,WAAK,WAAW,QAAQ;AAAA,IAAK;AAClD,SAAK,SAAS,UAAU,MAAM,MAAM;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,QAAQ,QAAO;AACb,QAAG,QAAO;AACR,iBAAW,QAAQ,IAAI,yFAAyF;AAChH,WAAK,SAAS,QAAQ,MAAM;AAAA,IAC9B;AAEA,QAAG,KAAK,cAAc,CAAC,KAAK,WAAW,OAAM;AAAE;AAAA,IAAO;AACtD,QAAG,KAAK,sBAAsB,KAAK,cAAc,UAAS;AACxD,WAAK,oBAAoB,UAAU,KAAK,kBAAkB;AAAA,IAC5D,OAAO;AACL,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,MAAM,KAAK,MAAK;AAAE,SAAK,UAAU,KAAK,OAAO,MAAM,KAAK,IAAI;AAAA,EAAE;AAAA;AAAA;AAAA;AAAA,EAKlE,YAAW;AAAE,WAAO,KAAK,WAAW;AAAA,EAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASzC,OAAO,UAAS;AACd,QAAI,MAAM,KAAK,QAAQ;AACvB,SAAK,qBAAqB,KAAK,KAAK,CAAC,KAAK,QAAQ,CAAC;AACnD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,UAAS;AACf,QAAI,MAAM,KAAK,QAAQ;AACvB,SAAK,qBAAqB,MAAM,KAAK,CAAC,KAAK,QAAQ,CAAC;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,QAAQ,UAAS;AACf,QAAI,MAAM,KAAK,QAAQ;AACvB,SAAK,qBAAqB,MAAM,KAAK,CAAC,KAAK,QAAQ,CAAC;AACpD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,UAAS;AACjB,QAAI,MAAM,KAAK,QAAQ;AACvB,SAAK,qBAAqB,QAAQ,KAAK,CAAC,KAAK,QAAQ,CAAC;AACtD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,KAAK,UAAS;AACZ,QAAG,CAAC,KAAK,YAAY,GAAE;AAAE,aAAO;AAAA,IAAM;AACtC,QAAI,MAAM,KAAK,QAAQ;AACvB,QAAI,YAAY,KAAK,IAAI;AACzB,SAAK,WAAW,MAAM,IAAI,KAAK,MAAM,SAAS,KAAK,IAAI,IAAI,SAAS,CAAC;AACrE,SAAK,KAAK,EAAC,OAAO,WAAW,OAAO,aAAa,SAAS,CAAC,GAAG,IAAQ,CAAC;AACvE,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cAAc,WAAU;AAOtB,YAAO,WAAU;AAAA,MACf,KAAK;AAAU,eAAO;AAAA,MACtB;AAAS,eAAO,UAAU;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAkB;AAChB,UAAM,WAAW,KAAK;AACtB,SAAK;AACL,SAAK,gBAAgB;AAGrB,iBAAa,KAAK,eAAe,KAAK;AACtC,QAAI,YAAY;AAGhB,QAAG,KAAK,WAAU;AAChB,kBAAY,CAAC,WAAW,GAAG,iBAAiB,GAAG,KAAK,KAAK,UAAU,CAAC,EAAE,QAAQ,MAAM,EAAE,CAAC,EAAE;AAAA,IAC3F;AACA,UAAM,YAAY,IAAI,KAAK,UAAU,KAAK,YAAY,GAAG,SAAS;AAClE,cAAU,aAAa,KAAK;AAC5B,cAAU,UAAU,KAAK;AACzB,SAAK,aAAa,IAAI,WAAW,MAAM,SAAS;AAGhD,QAAG,YAAY,SAAS,OAAO,GAAE;AAAE,WAAK,iBAAiB,qBAAqB,QAAQ;AAAA,IAAE;AAAA,EAC1F;AAAA,EAEA,WAAW,KAAI;AAAE,WAAO,KAAK,gBAAgB,KAAK,aAAa,QAAQ,GAAG;AAAA,EAAE;AAAA,EAE5E,aAAa,KAAK,KAAI;AAAE,SAAK,gBAAgB,KAAK,aAAa,QAAQ,KAAK,GAAG;AAAA,EAAE;AAAA,EAEjF,oBAAoB,mBAAmB,oBAAoB,MAAK;AAG9D,SAAK,eAAe;AACpB,QAAI,cAAc;AAClB,QAAI,mBAAmB;AACvB,QAAI;AACJ,QAAI;AACJ,UAAM,YAAY,MAAM,iBAAiB,KAAK,gBAAgB,CAAC,KAAK;AACpE,QAAI,wBAAwB,KAAK,cAAc,iBAAiB;AAGhE,QAAI,WAAW,CAAC,WAAW;AACzB,WAAK,IAAI,aAAa,mBAAmB,qBAAqB,OAAO,MAAM;AAE3E,WAAK,IAAI,CAAC,QAAQ,CAAC;AACnB,yBAAmB;AACnB,YAAM,eAAe,KAAK,iBAAiB,iBAAiB;AAC5D,UAAG,iBAAiB,KAAK,cAAa;AAAE;AAAA,MAAO;AAC/C,WAAK,iBAAiB;AAAA,IACxB;AACA,QAAG,KAAK,WAAW,gBAAgB,qBAAqB,EAAE,GAAE;AAAE,aAAO,SAAS,WAAW;AAAA,IAAE;AAE3F,SAAK,gBAAgB,WAAW,UAAU,iBAAiB;AAE3D,eAAW,KAAK,mBAAmB,KAAK,QAAQ,YAAU;AACxD,UAAG,CAAC,UAAU,GAAE;AAAE;AAAA,MAAO;AACzB,WAAK,IAAI,aAAa,SAAS,MAAM;AACrC,UAAG,oBAAoB,CAAC,aAAY;AAClC,qBAAa,KAAK,aAAa;AAC/B,iBAAS,MAAM;AAAA,MACjB;AAAA,IACF,CAAC;AACD,SAAK,cAAc,KAAK,OAAO,MAAM;AACnC,UAAG,CAAC,kBAAiB;AAGnB,YAAG,KAAK,cAAc,qBAAqB,KAAK,eAAc;AAAE;AAAA,QAAO;AACvE,YAAIC,yBAAwB,KAAK,cAAc,iBAAiB;AAEhE,YAAG,CAAC,KAAK,0BAAyB;AAAE,eAAK,aAAa,gBAAgBA,sBAAqB,IAAI,MAAM;AAAA,QAAE;AACvG,eAAO,KAAK,IAAI,aAAa,eAAeA,sBAAqB,WAAW;AAAA,MAC9E;AACA,UAAG,CAAC,UAAU,GAAE;AAAE;AAAA,MAAO;AACzB,oBAAc;AAEd,mBAAa,KAAK,aAAa;AAC/B,WAAK,gBAAgB,WAAW,UAAU,iBAAiB;AAC3D,WAAK,KAAK,SAAO;AACf,aAAK,IAAI,aAAa,8BAA8B,GAAG;AACvD,aAAK,2BAA2B;AAChC,qBAAa,KAAK,aAAa;AAAA,MACjC,CAAC;AAAA,IACH,CAAC;AACD,SAAK,iBAAiB;AACtB,mBAAe,KAAK;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAgB;AACd,iBAAa,KAAK,aAAa;AAC/B,SAAK,IAAI,CAAC,KAAK,aAAa,KAAK,gBAAgB,CAAC;AAAA,EACpD;AAAA,EAEA,kBAAiB;AACf,iBAAa,KAAK,cAAc;AAChC,iBAAa,KAAK,qBAAqB;AAEvC,SAAK,sBAAsB;AAAA,EAC7B;AAAA,EAEA,aAAY;AACV,QAAG,KAAK,UAAU,EAAG,MAAK,IAAI,aAAa,GAAG,KAAK,cAAc,KAAK,SAAS,CAAC,iBAAiB,KAAK,YAAY,CAAC,EAAE;AACrH,SAAK,gBAAgB;AACrB,SAAK;AACL,SAAK,gBAAgB;AACrB,SAAK,eAAe,MAAM;AAC1B,SAAK,eAAe;AACpB,SAAK,qBAAqB,KAAK,QAAQ,CAAC,CAAC,EAAE,QAAQ,MAAM,SAAS,CAAC;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAkB;AAChB,QAAG,KAAK,qBAAoB;AAC1B,YAAM,aAAa,KAAK;AACxB,WAAK,sBAAsB;AAC3B,UAAG,KAAK,UAAU,GAAE;AAAE,aAAK,IAAI,aAAa,0DAA0D;AAAA,MAAE;AACxG,WAAK,gBAAgB;AAIrB,UAAG,YAAW;AAAE,mBAAW,MAAM,iBAAiB,mBAAmB;AAAA,MAAE;AAGvE,UAAG,CAAC,KAAK,UAAU,UAAU,GAAE;AAAE;AAAA,MAAO;AACxC,WAAK,iBAAiB,mBAAmB;AACzC,UAAG,CAAC,KAAK,UAAU,UAAU,GAAE;AAAE;AAAA,MAAO;AACxC,WAAK,qBAAqB,MAAM,KAAK,eAAe,gBAAgB,CAAC;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,iBAAgB;AACd,QAAG,KAAK,QAAQ,KAAK,KAAK,eAAc;AAAE;AAAA,IAAO;AACjD,SAAK,gBAAgB;AACrB,SAAK,iBAAiB,WAAW,MAAM,KAAK,cAAc,GAAG,KAAK,mBAAmB;AAAA,EACvF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,qBAAqB,WAAU;AAC7B,UAAM,aAAa,KAAK;AACxB,UAAM,eAAe,KAAK;AAC1B,SAAK,SAAS,MAAM;AAGlB,UAAG,iBAAiB,KAAK,gBAAiB,cAAc,WAAW,OAAO;AAAE;AAAA,MAAO;AACnF,gBAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAU,YAAW;AACnB,WAAO,KAAK,eAAe,cAAc,EAAE,cAAc,WAAW;AAAA,EACtE;AAAA,EAEA,SAAS,UAAU,MAAM,QAAO;AAG9B,UAAM,aAAa,KAAK;AACxB,QAAG,CAAC,YAAW;AACb,aAAO,YAAY,SAAS;AAAA,IAC9B;AAGA,SAAK,gBAAgB;AAErB,SAAK,kBAAkB,WAAW,WAAW,MAAM;AAGjD,UAAG,CAAC,WAAW,SAAQ;AACrB,cAAM,YAAY,CAAC,WAAW,aAAa;AAC3C,mBAAW,MAAM,MAAM,MAAM;AAI7B,YAAG,WAAU;AAAE,eAAK,iBAAiB,qBAAqB,UAAU;AAAA,QAAE;AAAA,MACxE;AAEA,WAAK,oBAAoB,WAAW,WAAW,MAAM;AACnD,YAAG,KAAK,eAAe,YAAW;AAAE,eAAK,aAAa;AAAA,QAAK;AAC3D,oBAAY,SAAS;AAAA,MACvB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,kBAAkB,MAAM,UAAU,QAAQ,GAAE;AAE1C,QAAG,UAAU,KAAK,CAAC,KAAK,kBAAkB,KAAK,eAAe,cAAc,MAAK;AAC/E,eAAS;AACT;AAAA,IACF;AAEA,eAAW,MAAM;AACf,WAAK,kBAAkB,MAAM,UAAU,QAAQ,CAAC;AAAA,IAClD,GAAG,MAAM,KAAK;AAAA,EAChB;AAAA,EAEA,oBAAoB,MAAM,UAAU,QAAQ,GAAE;AAC5C,QAAG,UAAU,KAAK,KAAK,eAAe,cAAc,QAAO;AACzD,eAAS;AACT;AAAA,IACF;AAEA,eAAW,MAAM;AACf,WAAK,oBAAoB,MAAM,UAAU,QAAQ,CAAC;AAAA,IACpD,GAAG,MAAM,KAAK;AAAA,EAChB;AAAA,EAEA,YAAY,OAAM;AAChB,UAAM,aAAa,KAAK;AACxB,QAAG,KAAK,UAAU,EAAG,MAAK,IAAI,aAAa,SAAS,KAAK;AACzD,SAAK,gBAAgB;AAIrB,UAAM,aAAa,CAAC,EAAE,cAAc,WAAW;AAG/C,SAAK,iBAAiB,qBAAqB,UAAU;AACrD,QAAG,KAAK,UAAU,UAAU,KAAK,CAAC,cAAc,CAAC,KAAK,eAAc;AAClE,WAAK,eAAe,gBAAgB;AAAA,IACtC;AACA,SAAK,qBAAqB,MAAM,QAAQ,CAAC,CAAC,EAAE,QAAQ,MAAM,SAAS,KAAK,CAAC;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,OAAM;AAChB,UAAM,aAAa,KAAK;AACxB,QAAG,KAAK,UAAU,EAAG,MAAK,IAAI,aAAa,SAAS,KAAK;AACzD,QAAI,kBAAkB,KAAK;AAC3B,QAAI,oBAAoB,KAAK;AAC7B,SAAK,qBAAqB,MAAM,QAAQ,CAAC,CAAC,EAAE,QAAQ,MAAM;AACxD,eAAS,OAAO,iBAAiB,iBAAiB;AAAA,IACpD,CAAC;AAED,SAAK,iBAAiB,oBAAoB,UAAU;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAiB,QAAQ,aAAa,KAAK,YAAW;AAIpD,QAAI,gBAAgB;AACpB,QAAG,cAAc,CAAC,WAAW,OAAO,GAAE;AACpC,sBAAgB,WAAW;AAC3B,iBAAW,kBAAkB;AAAA,IAC/B;AACA,UAAM,QAAQ,KAAK,SAAS,IAAI,aAAW,CAAC,SAAS,QAAQ,QAAQ,CAAC,CAAC;AACvE,UAAM,QAAQ,CAAC,CAAC,SAAS,OAAO,MAAM;AAGpC,UAAG,QAAQ,QAAQ,MAAM,SAAQ;AAAE;AAAA,MAAO;AAE1C,UAAG,eAAe,iBAAiB,CAAC,WAAW,UAAU,MAAM,CAAC,WAAW,QAAQ,SAAS,OAAO,GAAE;AAAE;AAAA,MAAO;AAC9G,UAAG,EAAE,QAAQ,UAAU,KAAK,QAAQ,UAAU,KAAK,QAAQ,SAAS,IAAG;AACrE,gBAAQ,QAAQ,eAAe,OAAO,EAAC,QAAQ,aAAa,OAAM,CAAC;AAAA,MACrE;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,kBAAiB;AACf,YAAO,KAAK,QAAQ,KAAK,KAAK,YAAW;AAAA,MACvC,KAAK,cAAc;AAAY,eAAO;AAAA,MACtC,KAAK,cAAc;AAAM,eAAO;AAAA,MAChC,KAAK,cAAc;AAAS,eAAO;AAAA,MACnC;AAAS,eAAO;AAAA,IAClB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAa;AAAE,WAAO,KAAK,gBAAgB,MAAM;AAAA,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOxD,OAAO,SAAQ;AACb,SAAK,IAAI,QAAQ,eAAe;AAChC,SAAK,WAAW,KAAK,SAAS,OAAO,OAAK,MAAM,OAAO;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,IAAI,MAAK;AACP,aAAQ,OAAO,KAAK,sBAAqB;AACvC,WAAK,qBAAqB,GAAG,IAAI,KAAK,qBAAqB,GAAG,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM;AAChF,eAAO,KAAK,QAAQ,GAAG,MAAM;AAAA,MAC/B,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,QAAQ,OAAO,aAAa,CAAC,GAAE;AAC7B,QAAI,OAAO,IAAI,QAAQ,OAAO,YAAY,IAAI;AAC9C,SAAK,SAAS,KAAK,IAAI;AACvB,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAK,MAAM,SAAQ;AACjB,QAAG,KAAK,UAAU,GAAE;AAClB,UAAI,EAAC,OAAO,OAAO,SAAS,KAAK,SAAQ,IAAI;AAC7C,WAAK,IAAI,QAAQ,GAAG,KAAK,IAAI,KAAK,KAAK,QAAQ,KAAK,GAAG,KAAK,OAAO;AAAA,IACrE;AAEA,QAAG,KAAK,YAAY,GAAE;AACpB,WAAK,KAAK,MAAM,OAAO;AAAA,IACzB,OAAO;AACL,WAAK,WAAW,KAAK,MAAM;AAGzB,YAAG,CAAC,KAAK,WAAW,MAAM,OAAO,GAAE;AAAE,eAAK,KAAK,MAAM,OAAO;AAAA,QAAE;AAAA,MAChE,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,KAAK,MAAM,SAAQ;AACjB,UAAM,aAAa,KAAK;AAGxB,QAAG,WAAW,KAAK,UAAU,eAAe,MAAK;AAAE,iBAAW,MAAM,IAAI,SAAS,KAAK,QAAQ;AAAA,IAAE;AAChG,SAAK,OAAO,MAAM,YAAU;AAG1B,UAAG,CAAC,WAAW,UAAU,KAAK,CAAC,WAAW,OAAO,GAAE;AAAE;AAAA,MAAO;AAE5D,UAAG,KAAK,UAAU,eAAe,SAAS,KAAK,WAAW,MAAM,OAAO,GAAE;AAAE;AAAA,MAAO;AAClF,iBAAW,UAAU,KAAK,MAAM;AAAA,IAClC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,WAAW,MAAM,SAAQ;AACvB,WAAO,CAAC,CAAC,YAAY,KAAK,aAAa,QAAQ,QAAQ,KAAK,QAAQ,SAAS;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAS;AACP,QAAI,SAAS,KAAK,MAAM;AACxB,QAAG,WAAW,KAAK,KAAI;AAAE,WAAK,MAAM;AAAA,IAAE,OAAO;AAAE,WAAK,MAAM;AAAA,IAAO;AAEjE,WAAO,KAAK,IAAI,SAAS;AAAA,EAC3B;AAAA,EAEA,gBAAe;AACb,QAAG,KAAK,uBAAuB,CAAC,KAAK,YAAY,GAAE;AAAE;AAAA,IAAO;AAC5D,SAAK,sBAAsB,KAAK,QAAQ;AACxC,SAAK,KAAK,EAAC,OAAO,WAAW,OAAO,aAAa,SAAS,CAAC,GAAG,KAAK,KAAK,oBAAmB,CAAC;AAC5F,SAAK,wBAAwB,WAAW,MAAM,KAAK,iBAAiB,GAAG,KAAK,mBAAmB;AAAA,EACjG;AAAA,EAEA,kBAAiB;AACf,QAAG,KAAK,YAAY,KAAK,KAAK,WAAW,SAAS,GAAE;AAClD,WAAK,WAAW,QAAQ,cAAY,SAAS,CAAC;AAC9C,WAAK,aAAa,CAAC;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,cAAc,YAAW;AACvB,UAAM,aAAa,KAAK;AACxB,UAAM,UAAU,cAAc,WAAW,OAAO;AAChD,SAAK,OAAO,WAAW,MAAM,SAAO;AAIlC,UAAG,eAAe,KAAK,cAAe,WAAW,CAAC,WAAW,OAAO,GAAG;AAAE;AAAA,MAAO;AAChF,UAAI,EAAC,OAAO,OAAO,SAAS,KAAK,SAAQ,IAAI;AAC7C,UAAG,OAAO,QAAQ,KAAK,qBAAoB;AACzC,aAAK,gBAAgB;AACrB,aAAK,iBAAiB,WAAW,MAAM,KAAK,cAAc,GAAG,KAAK,mBAAmB;AAAA,MACvF;AAEA,UAAG,KAAK,UAAU,EAAG,MAAK,IAAI,WAAW,GAAG,QAAQ,UAAU,EAAE,IAAI,KAAK,IAAI,KAAK,IAAI,OAAO,MAAM,MAAM,OAAO,EAAE,IAAI,OAAO;AAI7H,YAAM,WAAW,KAAK;AACtB,eAAQ,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAI;AACtC,cAAM,UAAU,SAAS,CAAC;AAC1B,YAAG,CAAC,QAAQ,SAAS,OAAO,OAAO,SAAS,QAAQ,GAAE;AAAE;AAAA,QAAS;AACjE,gBAAQ,QAAQ,OAAO,SAAS,KAAK,QAAQ;AAAA,MAC/C;AAEA,YAAM,YAAY,KAAK,qBAAqB;AAC5C,eAAQ,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAI;AACvC,YAAI,CAAC,EAAE,QAAQ,IAAI,UAAU,CAAC;AAC9B,iBAAS,GAAG;AAAA,MACd;AAEA,YAAM,OAAO,OAAO,WAAW,MAAM,IAAI,GAAG;AAC5C,UAAG,MAAK;AACN,mBAAW,MAAM,OAAO,GAAG;AAC3B,aAAK;AAAA,MACP;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,eAAe,OAAM;AACnB,QAAI,aAAa,KAAK,SAAS,KAAK,OAAK,EAAE,UAAU,UAAU,EAAE,SAAS,KAAK,EAAE,UAAU,EAAE;AAC7F,QAAG,YAAW;AACZ,UAAG,KAAK,UAAU,EAAG,MAAK,IAAI,aAAa,4BAA4B,KAAK,GAAG;AAC/E,iBAAW,MAAM;AAAA,IACnB;AAAA,EACF;AACF;", "names": ["closure", "fallbackTransportName"] }