@shadow-js/realtime 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,23 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.3
4
+
5
+ ### Fixed
6
+
7
+ - Idempotency keys belong to their publisher: the hub stores `key/u:<user>` (a signed-in user), `key/a` (anonymous connections) or `key/s` (`hub.publish`), so another user or the server reusing a key is never a duplicate.
8
+ - `maxPresenceMembers` holds under concurrent joins: joins to one channel are applied one at a time per hub.
9
+ - A `revoke` issued while a subscribe is awaiting its async rule refuses that subscribe (and counts it).
10
+ - After `subbed gap:true` the client resumes from the gap's head, so a gap with nothing left to replay is reported once, not on every reconnect.
11
+
3
12
  ## 1.0.2
4
13
 
5
14
  ### Fixed
6
15
 
7
- - - Client: adding the same handler to a channel twice no longer leaves a subscription hold that nothing can release.
8
- - - Client: a POST that never answers (server-sent events transport) is abandoned after the acknowledgment timeout, so the publishes queued behind it are sent.
9
- - - Server: a stream declaration that names its own `t` or `n` is always read as a subscription, never as a publish or request.
10
- - - Server: a WebSocket that closes while `authenticate` is still running no longer leaves a connection in the hub.
11
- - - Server: presence changes posted over HTTP spend from the same rate limit as publishes, and a POST body is read only up to the size limit.
12
- - - Server: a member whose presence entry expired is written back with its last state instead of `null`; a transport whose `send` throws is closed.
13
- - - Memory adapter: idempotency keys are capped per channel (65,536), and expired ones are swept when the map doubles.
14
- - - Redis adapter: a channel whose keys were lost and whose numbering restarted still delivers its messages (the hub tells subscribers `reset`).
15
- - - Client: the event stream parser holds at most 1 MiB for an unfinished line and event; a longer one closes the stream and reconnects. A tab that becomes visible while the client waits out a reconnect backoff reconnects at once.
16
- - - Server: `sub` and `unsub` frames spend from a per-connection bucket (the publish rate, a burst that covers all of `maxChannelsPerConnection`), so subscription churn is limited like publishing.
17
- - - Server: a frame over `maxMessageBytes` is refused when it arrives instead of after the frames queued before it; it counts as an invalid frame.
18
- - - Server: a presence state posted over HTTP also updates the connection's own copy (on this server directly, on another through the presence event), so a write-back after an expiry carries the newest version and state.
19
- - - Cloudflare adapter: a listener whose queue overflows is reported (`onError`), and a presence listener is resynced to the real member set (leaves, joins, changes) instead of staying wrong.
20
- - - Errors while dropping a connection and listener failures in the memory adapter go to `onError` (memory adapter: new `onError` option, default `console.error`) instead of being swallowed.
16
+ - Client: adding the same handler to a channel twice no longer leaves a subscription hold that nothing can release.
17
+ - Client: a POST that never answers (server-sent events transport) is abandoned after the acknowledgment timeout, so the publishes queued behind it are sent.
18
+ - Server: a stream declaration that names its own `t` or `n` is always read as a subscription, never as a publish or request.
19
+ - Server: a WebSocket that closes while `authenticate` is still running no longer leaves a connection in the hub.
20
+ - Server: presence changes posted over HTTP spend from the same rate limit as publishes, and a POST body is read only up to the size limit.
21
+ - Server: a member whose presence entry expired is written back with its last state instead of `null`; a transport whose `send` throws is closed.
22
+ - Memory adapter: idempotency keys are capped per channel (65,536), and expired ones are swept when the map doubles.
23
+ - Redis adapter: a channel whose keys were lost and whose numbering restarted still delivers its messages (the hub tells subscribers `reset`).
24
+ - Client: the event stream parser holds at most 1 MiB for an unfinished line and event; a longer one closes the stream and reconnects. A tab that becomes visible while the client waits out a reconnect backoff reconnects at once.
25
+ - Server: `sub` and `unsub` frames spend from a per-connection bucket (the publish rate, a burst that covers all of `maxChannelsPerConnection`), so subscription churn is limited like publishing.
26
+ - Server: a frame over `maxMessageBytes` is refused when it arrives instead of after the frames queued before it; it counts as an invalid frame.
27
+ - Server: a presence state posted over HTTP also updates the connection's own copy (on this server directly, on another through the presence event), so a write-back after an expiry carries the newest version and state.
28
+ - Cloudflare adapter: a listener whose queue overflows is reported (`onError`), and a presence listener is resynced to the real member set (leaves, joins, changes) instead of staying wrong.
29
+ - Errors while dropping a connection and listener failures in the memory adapter go to `onError` (memory adapter: new `onError` option, default `console.error`) instead of being swallowed.
21
30
  - The realtime client takes `reconnect.maxReconnectAttempts` (default unlimited). After that many failed reconnects in a row (the count restarts when a connection stays open `stableMs`) the client is `closed`, pending calls reject, and `onError` receives a `closed` error.
22
31
 
23
32
  ## 1.0.1
@@ -211,7 +211,7 @@ function createRealtime(options) {
211
211
  const subscription = (chan, n) => {
212
212
  const join = joins(chan);
213
213
  const watch = watches(chan);
214
- const after = chan.sy ? chan.last : chan.options.replay === true ? 0 : void 0;
214
+ const after = chan.sy ? Math.max(chan.last, chan.gh) : chan.options.replay === true ? 0 : void 0;
215
215
  chan.w = {
216
216
  n,
217
217
  join,
@@ -469,6 +469,7 @@ function createRealtime(options) {
469
469
  chan.sy = true;
470
470
  chan.fails = 0;
471
471
  setChanStatus(chan, "live");
472
+ chan.gh = gap ? frame.head : 0;
472
473
  if (gap) emit(chan.gl, {
473
474
  after: w.after ?? 0,
474
475
  head: frame.head,
@@ -700,6 +701,7 @@ function createRealtime(options) {
700
701
  refs: 0,
701
702
  hd: 0,
702
703
  last: 0,
704
+ gh: 0,
703
705
  sy: false,
704
706
  state: channelOptions.presence,
705
707
  sv: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","names":[],"sources":["../../../src/client/core.ts"],"sourcesContent":["import { type ErrorCode, RealtimeError } from \"../errors\";\nimport {\n byteLength,\n isChannelName,\n isEventName,\n isIdempotencyKey,\n type Json,\n type Member,\n type ServerFrame,\n serverFrameOf,\n} from \"../protocol\";\nimport { applyPresence } from \"./presence\";\nimport { type Link, type LinkEvents, openStream } from \"./sse\";\nimport type {\n AnyMessage,\n Channel,\n ChannelDef,\n ChannelOptions,\n ChannelSchema,\n ChannelStatus,\n GapInfo,\n MessageMeta,\n PublishAck,\n Realtime,\n RealtimeOptions,\n Status,\n} from \"./types\";\nimport { backoff, clientId, resolveUrl, sendUrl } from \"./util\";\nimport { openSocket } from \"./ws\";\n\n/** Close codes that mean the client did something the server will not accept again. */\nconst FATAL = new Set([1002, 1008, 1009]);\n/** Refusals that may heal by asking again. */\nconst TRANSIENT = new Set<ErrorCode>([\"unavailable\", \"rate_limited\", \"timeout\"]);\n/** HTTP sl of a stream request that are worth retrying among the 4xx. */\nconst RETRYABLE_STATUS = new Set([408, 425, 429]);\nconst MAX_FRAMES_PER_POST = 64;\nconst MAX_POST_BYTES = 256_000;\nconst MAX_STREAM_CHANNELS = 64;\nconst DEFAULT_REQUEST_MS = 10_000;\n\ntype Handler = (data: Json, meta: MessageMeta) => void;\ntype Timer = ReturnType<typeof setTimeout>;\n\ninterface Wire {\n /** The number the subscription went out with; the server answers with it. */\n readonly n: number;\n readonly join: boolean;\n readonly watch: boolean;\n /** The cursor the subscription asked to resume from. */\n readonly after: number | undefined;\n /** The presence state version the server hd for this connection. */\n ver: number;\n /** False until the server can accept presence changes for this connection (over SSE, until the member list arrived). */\n ready: boolean;\n}\n\ninterface Chan {\n readonly name: string;\n readonly options: ChannelOptions;\n readonly ev: Map<string, Set<Handler>>;\n readonly any: Set<(message: AnyMessage<ChannelDef>) => void>;\n readonly gl: Set<(info: GapInfo) => void>;\n readonly sl: Set<(status: ChannelStatus) => void>;\n readonly presence: Set<(members: readonly Member[]) => void>;\n readonly members: Map<string, Member>;\n list?: readonly Member[] | undefined;\n status: ChannelStatus;\n /** Listeners registered; the channel subscribes while this is above zero. */\n refs: number;\n /** Requests in flight; a stateful connection answers a request only on a subscribed channel. */\n hd: number;\n last: number;\n /** True once a `subbed` arrived since the channel last went idle. */\n sy: boolean;\n state: Json | undefined;\n sv: number;\n w?: Wire | undefined;\n retry?: Timer | undefined;\n fails: number;\n closed: boolean;\n hn?: Channel<ChannelDef> | undefined;\n}\n\ninterface Op {\n readonly kind: \"pub\" | \"req\" | \"pres\";\n readonly chan: Chan;\n readonly event: string;\n /** The JSON text of the data (or of the presence state). */\n readonly data: string;\n readonly key?: string | undefined;\n readonly ack: boolean;\n readonly ms: number;\n readonly order: number;\n n: number;\n timer?: Timer | undefined;\n settle?: ((error: RealtimeError | undefined, value?: unknown) => void) | undefined;\n}\n\n/**\n * Creates the realtime client: one transport (WebSocket, falling back to server-sent ev), reconnects with backoff, a\n * bounded offline queue, resume from the last sequence number and typed channels. It connects when the first channel\n * listens or the first message is sent, never earlier.\n *\n * @param options The stream URL and the policy.\n * @returns The client.\n * @example\n * import { createRealtime } from \"@shadow-js/realtime/client\";\n *\n * type Schema = { [name: `chat:${string}`]: { ev: { message: { text: string } } } };\n * const rt = createRealtime<Schema>({ url: \"/rt\" });\n * const lobby = rt.channel(\"chat:lobby\");\n * const stop = lobby.on(\"message\", (data) => console.log(data.text));\n * await lobby.publish(\"message\", { text: \"hello\" });\n * stop();\n * rt.close();\n * @since 1.0.0\n */\nexport function createRealtime<Schema extends ChannelSchema = ChannelSchema>(\n options: RealtimeOptions,\n): Realtime<Schema> {\n const reconnect = options.reconnect ?? {};\n const minDelay = reconnect.minDelayMs ?? 250;\n const maxDelay = reconnect.maxDelayMs ?? 30_000;\n const stableMs = reconnect.stableMs ?? 5000;\n const random = reconnect.random ?? Math.random;\n const maxQueued = options.maxQueued ?? 100;\n const queueTtl = options.queueTtlMs ?? 30_000;\n const ackTtl = options.ackTimeoutMs ?? 10_000;\n const auto = (options.transport ?? \"auto\") === \"auto\";\n const id = clientId();\n const chans = new Map<string, Chan>();\n const dirty = new Set<Chan>();\n const queue: Op[] = [];\n const inflight = new Map<number, Op>();\n const statusListeners = new Set<(status: Status) => void>();\n\n let status: Status = \"connecting\";\n let mode: \"ws\" | \"sse\" = options.transport === \"sse\" ? \"sse\" : \"ws\";\n let finished = false;\n let scheduled = false;\n let link: Link | undefined;\n let gen = 0;\n let ready = false;\n let connId: string | undefined;\n let maxBytes = 65_536;\n let streamSig = \"\";\n let byeCode: number | undefined;\n let wsFails = 0;\n let attempt = 0;\n let counter = 0;\n let keys = 0;\n let orders = 0;\n /** Heartbeat intervals since the last thing was received. */\n let silent = 0;\n let connectTimer: Timer | undefined;\n let reconnectTimer: Timer | undefined;\n let stableTimer: Timer | undefined;\n let beatTimer: ReturnType<typeof setInterval> | undefined;\n let posting = false;\n /** The POST in flight, which closing the client abandons. */\n let batch: AbortController | undefined;\n let postTimer: Timer | undefined;\n let postFails = 0;\n let listening = false;\n\n const report = (error: unknown): void => {\n try {\n if (options.onError !== undefined) options.onError(error);\n else console.error(\"[realtime]\", error);\n } catch {\n // A failing reporter must not break the client.\n }\n };\n const guard = (run: () => void): void => {\n try {\n run();\n } catch (error) {\n report(error);\n }\n };\n const doFetch: typeof fetch = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);\n const getHeaders = (): HeadersInit | undefined | Promise<HeadersInit | undefined> =>\n typeof options.headers === \"function\" ? options.headers() : options.headers;\n\n const emit = <T>(listeners: Iterable<(value: T) => void>, value: T): void => {\n for (const listener of [...listeners]) guard(() => listener(value));\n };\n const setStatus = (next: Status): void => {\n if (next === status) return;\n status = next;\n if (options.onStatus !== undefined) guard(() => options.onStatus?.(next));\n emit(statusListeners, next);\n };\n const setChanStatus = (chan: Chan, next: ChannelStatus): void => {\n if (chan.status === next) return;\n chan.status = next;\n emit(chan.sl, next);\n };\n\n // --- operations: publishes and requests, queued while there is no connection --------------------------------------\n\n /** Settles an operation once; an unacknowledged publish that fails is reported instead. */\n const finish = (op: Op, error: RealtimeError | undefined, value?: unknown): void => {\n clearTimeout(op.timer);\n const settle = op.settle;\n op.settle = undefined;\n if (settle !== undefined) settle(error, value);\n else if (error !== undefined && op.kind === \"pub\") report(error);\n };\n const arm = (op: Op, ms: number): void => {\n clearTimeout(op.timer);\n op.timer = setTimeout(() => {\n const at = queue.indexOf(op);\n if (at >= 0) queue.splice(at, 1);\n if (inflight.get(op.n) === op) inflight.delete(op.n);\n finish(op, new RealtimeError(\"timeout\"));\n }, ms);\n };\n const take = (n: number): Op | undefined => {\n const op = inflight.get(n);\n if (op !== undefined) inflight.delete(n);\n return op;\n };\n\n const enqueue = (op: Op): RealtimeError | undefined => {\n if (finished) return new RealtimeError(\"closed\");\n const flowing = ready && mode === \"ws\";\n if (op.kind !== \"pres\" && !flowing && queue.length >= maxQueued)\n return new RealtimeError(\"queue_full\");\n if (op.kind === \"pres\") {\n const old = queue.findIndex((item) => item.kind === \"pres\" && item.chan === op.chan);\n if (old >= 0) queue.splice(old, 1);\n } else arm(op, queueTtl);\n queue.push(op);\n if (ready) flush();\n else schedule();\n return undefined;\n };\n\n const newOp = (\n kind: Op[\"kind\"],\n chan: Chan,\n event: string,\n data: string,\n extra: Partial<Pick<Op, \"key\" | \"ack\" | \"ms\" | \"settle\">>,\n ): Op => ({ kind, chan, event, data, ack: true, ms: 0, order: ++orders, n: 0, ...extra });\n\n /** Whether the server answers an operation (a request, or a publish that asks for an acknowledgment). */\n const answered = (op: Op): boolean => op.kind === \"req\" || (op.kind === \"pub\" && op.ack);\n /** How long an operation that went out waits for its answer: a request's own time and a second, or the acknowledgment time. */\n const waitOf = (op: Op): number => (op.kind === \"req\" ? op.ms + 1000 : ackTtl);\n\n /** The frame text of an operation, numbered for an answer. Undefined (and the operation failed) when it is too large. */\n const encode = (op: Op): string | undefined => {\n const c = JSON.stringify(op.chan.name);\n const wantsAnswer = answered(op);\n op.n = wantsAnswer ? ++counter : 0;\n const text =\n op.kind === \"pres\"\n ? `{\"t\":\"pres\",\"c\":${c},\"s\":${op.data}}`\n : op.kind === \"req\"\n ? `{\"t\":\"req\",\"n\":${op.n},\"c\":${c},\"e\":\"${op.event}\",\"d\":${op.data},\"ms\":${op.ms}}`\n : `{\"t\":\"pub\",\"c\":${c},\"e\":\"${op.event}\",\"d\":${op.data}${wantsAnswer ? `,\"n\":${op.n}` : \"\"},\"k\":${JSON.stringify(op.key)}}`;\n if (byteLength(text) > maxBytes) {\n finish(op, new RealtimeError(\"too_large\"));\n return undefined;\n }\n return text;\n };\n\n /** Records that an operation went out: it now waits for an answer, or is done. */\n const sent = (op: Op): void => {\n if (op.kind === \"pres\") return;\n if (answered(op)) {\n inflight.set(op.n, op);\n arm(op, waitOf(op));\n } else clearTimeout(op.timer);\n };\n\n /** Puts operations that went out on a connection that died back at the head of the queue, in their original order. */\n const requeue = (ops: Op[]): void => {\n ops.sort((a, b) => a.order - b.order);\n const again: Op[] = [];\n for (const op of ops) {\n if (op.kind === \"req\") finish(op, new RealtimeError(\"closed\"));\n else {\n if (op.kind === \"pub\") arm(op, queueTtl);\n again.push(op);\n }\n }\n queue.unshift(...again);\n };\n\n // --- channels -----------------------------------------------------------------------------------------------------\n\n const wants = (chan: Chan): boolean =>\n !chan.closed &&\n (chan.refs > 0 ||\n chan.state !== undefined ||\n chan.options.watch === true ||\n (chan.hd > 0 && mode === \"ws\"));\n const joins = (chan: Chan): boolean => chan.state !== undefined;\n const watches = (chan: Chan): boolean =>\n !joins(chan) && (chan.presence.size > 0 || chan.options.watch === true);\n\n const touch = (chan: Chan): void => {\n if (chan.status === \"idle\" && wants(chan)) setChanStatus(chan, \"subscribing\");\n dirty.add(chan);\n schedule();\n };\n\n /** The fields a subscription goes out with, over a WebSocket as a frame and over SSE as a query entry. */\n const subscription = (chan: Chan, n: number): Record<string, unknown> => {\n const join = joins(chan);\n const watch = watches(chan);\n const after = chan.sy ? chan.last : chan.options.replay === true ? 0 : undefined;\n const w: Wire = { n, join, watch, after, ver: chan.sv, ready: !join };\n chan.w = w;\n setChanStatus(chan, chan.sy ? \"resuming\" : \"subscribing\");\n return {\n c: chan.name,\n ...(after === undefined ? {} : { after }),\n ...(chan.options.echo === false ? { echo: false } : {}),\n ...(join ? { presence: chan.state } : {}),\n ...(watch ? { watch: true } : {}),\n };\n };\n\n const sendPresence = (chan: Chan): void => {\n const w = chan.w;\n if (w === undefined || chan.state === undefined) return;\n w.ver = chan.sv;\n const op = newOp(\"pres\", chan, \"\", JSON.stringify(chan.state), {});\n if (mode === \"ws\") {\n const text = encode(op);\n if (text !== undefined) link?.send(text);\n } else enqueue(op);\n };\n\n const goIdle = (chan: Chan): void => {\n chan.sy = false;\n chan.last = 0;\n chan.w = undefined;\n clearTimeout(chan.retry);\n chan.retry = undefined;\n chan.fails = 0;\n if (chan.members.size > 0) {\n chan.members.clear();\n chan.list = undefined;\n }\n setChanStatus(chan, chan.closed ? \"closed\" : \"idle\");\n };\n\n /** Brings the w in line with what a channel wants (WebSocket: subscribe, unsubscribe, presence). */\n const unsub = (chan: Chan): void => {\n link?.send(`{\"t\":\"unsub\",\"c\":${JSON.stringify(chan.name)}}`);\n };\n\n const syncChan = (chan: Chan): void => {\n const want = wants(chan);\n const w = chan.w;\n if (mode === \"ws\" && ready) {\n if (w !== undefined && (!want || w.join !== joins(chan) || w.watch !== watches(chan))) {\n unsub(chan);\n chan.w = undefined;\n }\n if (want && chan.w === undefined && chan.retry === undefined) {\n const n = ++counter;\n link?.send(JSON.stringify({ t: \"sub\", n, ...subscription(chan, n) }));\n }\n }\n if (!want) {\n if (chan.status !== \"idle\" && chan.status !== \"closed\") goIdle(chan);\n return;\n }\n const current = chan.w;\n if (current?.join && current.ready && current.ver !== chan.sv) sendPresence(chan);\n };\n\n const syncAll = (): void => {\n const batch = [...dirty];\n dirty.clear();\n for (const chan of batch) syncChan(chan);\n };\n\n const closeChan = (chan: Chan, error?: RealtimeError): void => {\n if (chan.closed) return;\n chan.closed = true;\n if (chans.get(chan.name) === chan) chans.delete(chan.name);\n if (chan.w !== undefined && mode === \"ws\" && ready) unsub(chan);\n goIdle(chan);\n chan.ev.clear();\n chan.any.clear();\n chan.gl.clear();\n chan.presence.clear();\n chan.sl.clear();\n chan.refs = 0;\n dirty.delete(chan);\n if (error !== undefined) report(error);\n schedule();\n };\n\n /** Subscribes again after a refusal or a removal the server announced; `now` skips the delay. */\n const resubscribe = (chan: Chan, now: boolean): void => {\n chan.w = undefined;\n setChanStatus(chan, chan.sy ? \"resuming\" : \"subscribing\");\n if (now) {\n touch(chan);\n return;\n }\n chan.retry = setTimeout(\n () => {\n chan.retry = undefined;\n touch(chan);\n },\n backoff(chan.fails, minDelay, maxDelay, random),\n );\n chan.fails += 1;\n };\n\n // --- connection ---------------------------------------------------------------------------------------------------\n\n /** What the open stream must be reopened for: the channels it carries and how they join. */\n const signature = (): string => {\n let sig = \"\";\n for (const chan of chans.values())\n if (wants(chan))\n sig += `${chan.name}|${chan.options.echo === false ? 0 : 1}${joins(chan) ? 1 : 0}${watches(chan) ? 1 : 0};`;\n return sig;\n };\n\n const connect = (): void => {\n clearTimeout(reconnectTimer);\n reconnectTimer = undefined;\n if (finished || link !== undefined) return;\n gen += 1;\n const g = gen;\n ready = false;\n silent = 0;\n const ev: LinkEvents = {\n rx: () => {\n if (g === gen) silent = 0;\n },\n frame: (frame) => {\n if (g === gen) guard(() => onFrame(frame, false));\n },\n closed: (info) => {\n if (g === gen) linkClosed(info);\n },\n };\n try {\n if (!listening && typeof addEventListener === \"function\") {\n listening = true;\n addEventListener(\"online\", onOnline);\n if (typeof document !== \"undefined\")\n document.addEventListener(\"visibilitychange\", onVisible);\n }\n if (mode === \"sse\") {\n const url = resolveUrl(options.url, false);\n // The subscriptions of the stream go in the query: the entries of the channels it carries.\n const entries: unknown[] = [];\n for (const chan of [...chans.values()]) {\n if (!wants(chan)) continue;\n if (entries.length >= MAX_STREAM_CHANNELS) {\n closeChan(chan, new RealtimeError(\"limit\"));\n continue;\n }\n entries.push(subscription(chan, entries.length + 1));\n }\n streamSig = signature();\n url.searchParams.set(\"s\", JSON.stringify(entries));\n link = openStream(url.toString(), doFetch, getHeaders, ev);\n } else\n link = openSocket(\n resolveUrl(options.url, true).toString(),\n options.WebSocket ?? globalThis.WebSocket,\n ev,\n );\n } catch (error) {\n gen += 1;\n shutdown(new RealtimeError(\"closed\", __DEV__ ? \"cannot open\" : undefined, { cause: error }));\n return;\n }\n connectTimer = setTimeout(() => {\n if (g === gen) linkClosed({});\n }, ackTtl);\n };\n\n const onOnline = (): void => {\n if (reconnectTimer !== undefined) connect();\n };\n\n /** A tab that was hidden keeps its timers throttled or frozen: when it shows again, a connection waiting out a backoff reconnects now. */\n const onVisible = (): void => {\n if (document.visibilityState === \"visible\" && reconnectTimer !== undefined) connect();\n };\n\n /** Releases the current transport and everything tied to it. */\n const drop = (): void => {\n gen += 1;\n const old = link;\n link = undefined;\n ready = false;\n connId = undefined;\n clearTimeout(connectTimer);\n clearTimeout(stableTimer);\n clearInterval(beatTimer);\n beatTimer = undefined;\n old?.close();\n };\n\n const linkClosed = (info: { readonly code?: number; readonly status?: number }): void => {\n const wasReady = ready;\n const code = byeCode ?? info.code;\n byeCode = undefined;\n drop();\n if (finished) return;\n if (mode === \"ws\" && inflight.size > 0) {\n const lost = [...inflight.values()];\n inflight.clear();\n requeue(lost);\n }\n for (const chan of chans.values()) {\n chan.w = undefined;\n clearTimeout(chan.retry);\n chan.retry = undefined;\n if (wants(chan)) setChanStatus(chan, chan.sy ? \"resuming\" : \"subscribing\");\n }\n const refused =\n info.status !== undefined &&\n info.status >= 400 &&\n info.status < 500 &&\n !RETRYABLE_STATUS.has(info.status);\n if (((code !== undefined && FATAL.has(code)) || refused) && reconnect.retryFatal !== true) {\n const why = __DEV__\n ? code !== undefined\n ? `the server closed the connection with ${code}`\n : `the server answered ${info.status}`\n : undefined;\n shutdown(new RealtimeError(\"closed\", why));\n return;\n }\n if (attempt >= (reconnect.maxReconnectAttempts ?? Infinity)) {\n shutdown(\n __DEV__\n ? new RealtimeError(\n \"closed\",\n `gave up after ${reconnect.maxReconnectAttempts} reconnect attempts`,\n )\n : new RealtimeError(\"closed\"),\n );\n return;\n }\n let delay = backoff(attempt, minDelay, maxDelay, random);\n attempt += 1;\n if (wasReady) wsFails = 0;\n else if (auto && mode === \"ws\") {\n wsFails += 1;\n if (wsFails >= 2) {\n mode = \"sse\";\n delay = 0;\n }\n }\n setStatus(\"reconnecting\");\n reconnectTimer = setTimeout(connect, delay);\n };\n\n /** Heartbeat: every interval sends a ping; two intervals with nothing received declare the connection dead. */\n const beat = (): void => {\n // The interval that saw the last frame is the first of the three beats counted before the connection is dead.\n silent += 1;\n if (silent > 2) {\n linkClosed({});\n return;\n }\n if (mode === \"ws\") link?.send('{\"t\":\"ping\"}');\n };\n\n // --- frames from the server ---------------------------------------------------------------------------------------\n\n const onFrame = (frame: ServerFrame, viaPost: boolean): void => {\n switch (frame.t) {\n case \"ready\":\n if (ready) return;\n ready = true;\n connId = frame.id;\n maxBytes = frame.max;\n wsFails = 0;\n silent = 0;\n clearTimeout(connectTimer);\n if (frame.hb > 0) beatTimer = setInterval(beat, frame.hb);\n stableTimer = setTimeout(() => {\n attempt = 0;\n }, stableMs);\n setStatus(\"open\");\n if (mode === \"ws\") for (const chan of chans.values()) dirty.add(chan);\n flush();\n return;\n case \"bye\":\n byeCode = frame.code;\n return;\n case \"subbed\": {\n const chan = chans.get(frame.c);\n const w = chan?.w;\n if (chan === undefined || w === undefined || w.n !== frame.n) return;\n if (frame.reset || w.after === undefined) chan.last = frame.head;\n const gap =\n frame.reset === true ||\n (frame.gap === true && w.after !== undefined && !(w.after === 0 && !chan.sy));\n chan.sy = true;\n chan.fails = 0;\n setChanStatus(chan, \"live\");\n if (gap)\n emit(chan.gl, { after: w.after ?? 0, head: frame.head, reset: frame.reset === true });\n return;\n }\n case \"unsubbed\": {\n const chan = chans.get(frame.c);\n if (chan === undefined) return;\n if (frame.why === \"revoked\") {\n // The hub dropped the subscription: closing must not send an unsub it would answer with not_subscribed.\n chan.w = undefined;\n closeChan(chan, new RealtimeError(\"forbidden\", __DEV__ ? \"revoked\" : undefined));\n } else resubscribe(chan, frame.why === \"gap\" || frame.why === \"reset\");\n return;\n }\n case \"msg\": {\n const chan = chans.get(frame.c);\n if (chan === undefined || frame.s <= chan.last) return;\n chan.last = frame.s;\n const meta: MessageMeta = {\n seq: frame.s,\n replayed: frame.r === 1,\n ...(frame.p === undefined ? {} : { publisher: frame.p }),\n ...(frame.u === undefined ? {} : { user: frame.u }),\n };\n for (const handler of [...(chan.ev.get(frame.e) ?? [])])\n guard(() => handler(frame.d, meta));\n for (const handler of [...chan.any])\n guard(() => handler({ event: frame.e, data: frame.d, meta } as AnyMessage<ChannelDef>));\n return;\n }\n case \"ack\": {\n const op = take(frame.n);\n if (op !== undefined)\n finish(op, undefined, { seq: frame.s, duplicate: frame.dup === true });\n return;\n }\n case \"rep\": {\n const op = take(frame.n);\n if (op !== undefined) finish(op, undefined, frame.d);\n return;\n }\n case \"err\": {\n const error = new RealtimeError(frame.code, frame.msg);\n const chan = frame.c === undefined ? undefined : chans.get(frame.c);\n if (frame.n !== undefined) {\n if (!viaPost && chan?.w?.n === frame.n) {\n if (TRANSIENT.has(frame.code)) {\n resubscribe(chan, false);\n report(error);\n } else {\n chan.w = undefined;\n closeChan(chan, error);\n }\n return;\n }\n // Over SSE a number on the stream names a subscription; only a POST answer names an operation.\n const op = viaPost || mode === \"ws\" ? take(frame.n) : undefined;\n if (op !== undefined) {\n finish(op, error);\n return;\n }\n }\n report(error);\n return;\n }\n case \"pres\": {\n const chan = chans.get(frame.c);\n if (chan === undefined) return;\n if (applyPresence(chan.members, frame.op, frame.m)) {\n chan.list = [...chan.members.values()];\n emit(chan.presence, chan.list);\n }\n if (frame.op === \"sync\" && chan.w !== undefined) {\n chan.w.ready = true;\n if (chan.w.join && chan.w.ver !== chan.sv) sendPresence(chan);\n }\n return;\n }\n case \"pong\":\n return;\n }\n };\n\n // --- sending ------------------------------------------------------------------------------------------------------\n\n const flush = (): void => {\n if (!ready || finished) return;\n syncAll();\n if (mode === \"sse\") {\n postNext();\n return;\n }\n for (let op = queue[0]; op !== undefined && ready; op = queue[0]) {\n const text = encode(op);\n if (text !== undefined) {\n if (link?.send(text) !== true) return;\n sent(op);\n }\n queue.shift();\n }\n };\n\n const postNext = (): void => {\n if (posting || postTimer !== undefined || connId === undefined) return;\n const ops: Op[] = [];\n const texts: string[] = [];\n let bytes = 0;\n while (queue.length > 0 && ops.length < MAX_FRAMES_PER_POST && bytes < MAX_POST_BYTES) {\n const op = queue.shift();\n if (op === undefined) break;\n const text = encode(op);\n if (text === undefined) continue;\n ops.push(op);\n texts.push(text);\n bytes += text.length;\n }\n if (ops.length === 0) return;\n posting = true;\n postBatch(ops, `{\"cid\":${JSON.stringify(connId)},\"frames\":[${texts.join(\",\")}]}`).then(\n undefined,\n report,\n );\n };\n\n const postBatch = async (ops: Op[], body: string): Promise<void> => {\n for (const op of ops) sent(op);\n let again = false;\n // A request that never answers would hold `posting` for as long as the platform waits (minutes) and nothing behind it\n // could be sent: it is abandoned when the longest wait of its operations is over, and its operations are retried.\n const mine = new AbortController();\n batch = mine;\n const patience = setTimeout(() => mine.abort(), Math.max(...ops.map(waitOf)));\n try {\n const headers = new Headers(await getHeaders());\n headers.set(\"content-type\", \"application/json\");\n const url =\n options.postUrl === undefined\n ? sendUrl(resolveUrl(options.url, false))\n : resolveUrl(options.postUrl, false);\n const response = await doFetch(url, { method: \"POST\", headers, body, signal: mine.signal });\n if (response.ok) {\n const parsed = (await response.json()) as { frames?: unknown };\n if (Array.isArray(parsed.frames))\n for (const item of parsed.frames as unknown[]) {\n const frame = serverFrameOf(item);\n if (frame !== undefined) guard(() => onFrame(frame, true));\n }\n postFails = 0;\n } else if (response.status >= 500 || RETRYABLE_STATUS.has(response.status)) again = true;\n else {\n const refusal = (await response.json().catch(() => undefined)) as\n | { error?: { code?: ErrorCode; message?: string } }\n | undefined;\n const code: ErrorCode = refusal?.error?.code ?? \"unavailable\";\n for (const op of ops) {\n if (inflight.get(op.n) === op) inflight.delete(op.n);\n finish(op, new RealtimeError(code, refusal?.error?.message));\n }\n }\n } catch {\n if (finished) return;\n again = true;\n } finally {\n clearTimeout(patience);\n }\n posting = false;\n const unanswered = ops.filter((op) => answered(op) && inflight.get(op.n) === op);\n for (const op of unanswered) inflight.delete(op.n);\n if (again) {\n for (const op of unanswered)\n if (op.kind === \"req\") finish(op, new RealtimeError(\"unavailable\"));\n requeue(\n ops.filter(\n (op) => op.kind === \"pres\" || (op.kind === \"pub\" && (!op.ack || unanswered.includes(op))),\n ),\n );\n postFails += 1;\n postTimer = setTimeout(\n () => {\n postTimer = undefined;\n flush();\n },\n backoff(postFails - 1, minDelay, maxDelay, random),\n );\n } else for (const op of unanswered) finish(op, new RealtimeError(\"unavailable\"));\n flush();\n };\n\n // --- lifecycle ----------------------------------------------------------------------------------------------------\n\n const run = (): void => {\n scheduled = false;\n if (finished) return;\n syncAll();\n if (link === undefined) {\n if (reconnectTimer === undefined && (queue.length > 0 || [...chans.values()].some(wants)))\n connect();\n return;\n }\n if (\n mode === \"sse\" &&\n (signature() !== streamSig ||\n [...chans.values()].some((c) => wants(c) && c.w === undefined && c.retry === undefined))\n ) {\n drop();\n for (const chan of chans.values()) chan.w = undefined;\n connect();\n } else flush();\n };\n\n function schedule(): void {\n if (scheduled || finished) return;\n scheduled = true;\n queueMicrotask(run);\n }\n\n function shutdown(error?: RealtimeError): void {\n if (finished) return;\n finished = true;\n clearTimeout(reconnectTimer);\n clearTimeout(postTimer);\n reconnectTimer = undefined;\n postTimer = undefined;\n drop();\n batch?.abort();\n if (listening) {\n if (typeof removeEventListener === \"function\") removeEventListener(\"online\", onOnline);\n if (typeof document !== \"undefined\")\n document.removeEventListener(\"visibilitychange\", onVisible);\n listening = false;\n }\n const closed = error ?? new RealtimeError(\"closed\");\n const pending = [...queue.splice(0), ...inflight.values()];\n inflight.clear();\n for (const op of pending) {\n if (op.settle === undefined) clearTimeout(op.timer);\n else finish(op, closed);\n }\n for (const chan of [...chans.values()]) closeChan(chan);\n chans.clear();\n dirty.clear();\n setStatus(\"closed\");\n statusListeners.clear();\n if (error !== undefined) report(error);\n }\n\n // --- the public handles -------------------------------------------------------------------------------------------\n\n const makeChannel = (name: string, channelOptions: ChannelOptions): Channel<ChannelDef> => {\n const chan: Chan = {\n name,\n options: channelOptions,\n ev: new Map(),\n any: new Set(),\n gl: new Set(),\n sl: new Set(),\n presence: new Set(),\n members: new Map(),\n status: \"idle\",\n refs: 0,\n hd: 0,\n last: 0,\n sy: false,\n state: channelOptions.presence,\n sv: 0,\n fails: 0,\n closed: false,\n };\n chans.set(name, chan);\n if (wants(chan)) touch(chan);\n\n /** Registers a listener; unless `passive`, it keeps the channel subscribed until removed. */\n const listen = <T>(\n set: Set<T>,\n item: T,\n passive?: boolean,\n after?: () => void,\n ): (() => void) => {\n if (chan.closed) return () => undefined;\n // A set holds a function once, like `addEventListener`: only the registration that added it counts a hold and may\n // remove it, so adding the same function twice never leaves a hold that nothing can release.\n let owns = !set.has(item);\n set.add(item);\n if (!passive && owns) {\n chan.refs += 1;\n touch(chan);\n }\n return () => {\n if (!owns) return;\n owns = false;\n // The channel may have been closed (its sets cleared and its holds reset) since this was registered.\n if (!set.delete(item) || passive) return;\n chan.refs -= 1;\n after?.();\n touch(chan);\n };\n };\n\n /** Validates and queues a publish or a request; the promise settles with its answer. */\n const submit = <T>(\n kind: \"pub\" | \"req\",\n event: string,\n data: unknown,\n key: string | undefined,\n ack: boolean,\n ms: number,\n ): Promise<T> => {\n let text = \"null\";\n let refusal: RealtimeError | undefined;\n if (chan.closed) refusal = new RealtimeError(\"closed\");\n else if (!isEventName(event) || (key !== undefined && !isIdempotencyKey(key)))\n refusal = new RealtimeError(\"bad_frame\");\n else\n try {\n text = JSON.stringify(data ?? null) ?? \"null\";\n } catch (cause) {\n refusal = new RealtimeError(\"bad_frame\", __DEV__ ? \"not JSON\" : undefined, { cause });\n }\n if (refusal !== undefined) return Promise.reject(refusal);\n return new Promise<T>((resolve, reject) => {\n const request = kind === \"req\";\n const op = newOp(kind, chan, event, text, {\n key: request ? undefined : (key ?? `${id}:${++keys}`),\n ack,\n ms,\n settle: ack\n ? (error, value) => {\n if (request) {\n chan.hd -= 1;\n touch(chan);\n }\n if (error) reject(error);\n else resolve(value as T);\n }\n : undefined,\n });\n if (request) {\n chan.hd += 1;\n touch(chan);\n }\n const refused = enqueue(op);\n if (refused !== undefined) {\n op.settle = undefined;\n if (request) chan.hd -= 1;\n reject(refused);\n } else if (!ack) resolve({ seq: 0, duplicate: false } as T);\n });\n };\n\n const hn: Channel<ChannelDef> = {\n name,\n get status() {\n return chan.status;\n },\n get lastSeq() {\n return chan.last;\n },\n on(event, handler) {\n let set = chan.ev.get(event);\n if (set === undefined) {\n set = new Set();\n chan.ev.set(event, set);\n }\n const handlers = set;\n return listen(handlers, handler as Handler, false, () => {\n if (handlers.size === 0 && chan.ev.get(event) === handlers) chan.ev.delete(event);\n });\n },\n onAny: (handler) => listen(chan.any, handler as (message: AnyMessage<ChannelDef>) => void),\n onGap: (handler) => listen(chan.gl, handler),\n onStatus: (handler) => listen(chan.sl, handler, true),\n publish: (event, data, publishOptions = {}) =>\n submit<PublishAck>(\"pub\", event, data, publishOptions.key, publishOptions.ack !== false, 0),\n request: (event, data, requestOptions = {}) =>\n submit(\"req\", event, data, undefined, true, requestOptions.timeoutMs ?? DEFAULT_REQUEST_MS),\n presence: {\n members: () => {\n chan.list ??= [...chan.members.values()];\n return chan.list as never;\n },\n set(state) {\n if (chan.closed) return;\n chan.state = state as Json;\n chan.sv += 1;\n touch(chan);\n },\n leave() {\n if (chan.closed || chan.state === undefined) return;\n chan.state = undefined;\n chan.sv += 1;\n touch(chan);\n },\n on: (callback) => listen(chan.presence, callback as (members: readonly Member[]) => void),\n },\n close: () => closeChan(chan),\n };\n chan.hn = hn;\n return hn;\n };\n\n return {\n channel(name, channelOptions = {}) {\n if (finished) throw new RealtimeError(\"closed\");\n if (!isChannelName(name)) throw new RealtimeError(\"bad_channel\", name);\n const live = chans.get(name);\n return (live?.hn ?? makeChannel(name, channelOptions)) as never;\n },\n get status() {\n return status;\n },\n get connectionId() {\n return connId;\n },\n onStatus(handler) {\n if (!finished) statusListeners.add(handler);\n return () => {\n statusListeners.delete(handler);\n };\n },\n close: () => shutdown(),\n };\n}\n"],"mappings":";;;;;;;;AA+BA,MAAM,wBAAQ,IAAI,IAAI;CAAC;CAAM;CAAM;AAAI,CAAC;;AAExC,MAAM,4BAAY,IAAI,IAAe;CAAC;CAAe;CAAgB;AAAS,CAAC;;AAE/E,MAAM,mCAAmB,IAAI,IAAI;CAAC;CAAK;CAAK;AAAG,CAAC;AAChD,MAAM,sBAAsB;AAC5B,MAAM,iBAAiB;AACvB,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;AA+E3B,SAAgB,eACd,SACkB;CAClB,MAAM,YAAY,QAAQ,aAAa,CAAC;CACxC,MAAM,WAAW,UAAU,cAAc;CACzC,MAAM,WAAW,UAAU,cAAc;CACzC,MAAM,WAAW,UAAU,YAAY;CACvC,MAAM,SAAS,UAAU,UAAU,KAAK;CACxC,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,WAAW,QAAQ,cAAc;CACvC,MAAM,SAAS,QAAQ,gBAAgB;CACvC,MAAM,QAAQ,QAAQ,aAAa,YAAY;CAC/C,MAAM,KAAK,SAAS;CACpB,MAAM,wBAAQ,IAAI,IAAkB;CACpC,MAAM,wBAAQ,IAAI,IAAU;CAC5B,MAAM,QAAc,CAAC;CACrB,MAAM,2BAAW,IAAI,IAAgB;CACrC,MAAM,kCAAkB,IAAI,IAA8B;CAE1D,IAAI,SAAiB;CACrB,IAAI,OAAqB,QAAQ,cAAc,QAAQ,QAAQ;CAC/D,IAAI,WAAW;CACf,IAAI,YAAY;CAChB,IAAI;CACJ,IAAI,MAAM;CACV,IAAI,QAAQ;CACZ,IAAI;CACJ,IAAI,WAAW;CACf,IAAI,YAAY;CAChB,IAAI;CACJ,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,UAAU;CACd,IAAI,OAAO;CACX,IAAI,SAAS;;CAEb,IAAI,SAAS;CACb,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,IAAI,UAAU;;CAEd,IAAI;CACJ,IAAI;CACJ,IAAI,YAAY;CAChB,IAAI,YAAY;CAEhB,MAAM,UAAU,UAAyB;EACvC,IAAI;GACF,IAAI,QAAQ,YAAY,KAAA,GAAW,QAAQ,QAAQ,KAAK;QACnD,QAAQ,MAAM,cAAc,KAAK;EACxC,QAAQ,CAER;CACF;CACA,MAAM,SAAS,QAA0B;EACvC,IAAI;GACF,IAAI;EACN,SAAS,OAAO;GACd,OAAO,KAAK;EACd;CACF;CACA,MAAM,WAAyB,OAAO,UAAU,QAAQ,SAAS,WAAW,MAAA,CAAO,OAAO,IAAI;CAC9F,MAAM,mBACJ,OAAO,QAAQ,YAAY,aAAa,QAAQ,QAAQ,IAAI,QAAQ;CAEtE,MAAM,QAAW,WAAyC,UAAmB;EAC3E,KAAK,MAAM,YAAY,CAAC,GAAG,SAAS,GAAG,YAAY,SAAS,KAAK,CAAC;CACpE;CACA,MAAM,aAAa,SAAuB;EACxC,IAAI,SAAS,QAAQ;EACrB,SAAS;EACT,IAAI,QAAQ,aAAa,KAAA,GAAW,YAAY,QAAQ,WAAW,IAAI,CAAC;EACxE,KAAK,iBAAiB,IAAI;CAC5B;CACA,MAAM,iBAAiB,MAAY,SAA8B;EAC/D,IAAI,KAAK,WAAW,MAAM;EAC1B,KAAK,SAAS;EACd,KAAK,KAAK,IAAI,IAAI;CACpB;;CAKA,MAAM,UAAU,IAAQ,OAAkC,UAA0B;EAClF,aAAa,GAAG,KAAK;EACrB,MAAM,SAAS,GAAG;EAClB,GAAG,SAAS,KAAA;EACZ,IAAI,WAAW,KAAA,GAAW,OAAO,OAAO,KAAK;OACxC,IAAI,UAAU,KAAA,KAAa,GAAG,SAAS,OAAO,OAAO,KAAK;CACjE;CACA,MAAM,OAAO,IAAQ,OAAqB;EACxC,aAAa,GAAG,KAAK;EACrB,GAAG,QAAQ,iBAAiB;GAC1B,MAAM,KAAK,MAAM,QAAQ,EAAE;GAC3B,IAAI,MAAM,GAAG,MAAM,OAAO,IAAI,CAAC;GAC/B,IAAI,SAAS,IAAI,GAAG,CAAC,MAAM,IAAI,SAAS,OAAO,GAAG,CAAC;GACnD,OAAO,IAAI,IAAI,cAAc,SAAS,CAAC;EACzC,GAAG,EAAE;CACP;CACA,MAAM,QAAQ,MAA8B;EAC1C,MAAM,KAAK,SAAS,IAAI,CAAC;EACzB,IAAI,OAAO,KAAA,GAAW,SAAS,OAAO,CAAC;EACvC,OAAO;CACT;CAEA,MAAM,WAAW,OAAsC;EACrD,IAAI,UAAU,OAAO,IAAI,cAAc,QAAQ;EAC/C,MAAM,UAAU,SAAS,SAAS;EAClC,IAAI,GAAG,SAAS,UAAU,CAAC,WAAW,MAAM,UAAU,WACpD,OAAO,IAAI,cAAc,YAAY;EACvC,IAAI,GAAG,SAAS,QAAQ;GACtB,MAAM,MAAM,MAAM,WAAW,SAAS,KAAK,SAAS,UAAU,KAAK,SAAS,GAAG,IAAI;GACnF,IAAI,OAAO,GAAG,MAAM,OAAO,KAAK,CAAC;EACnC,OAAO,IAAI,IAAI,QAAQ;EACvB,MAAM,KAAK,EAAE;EACb,IAAI,OAAO,MAAM;OACZ,SAAS;CAEhB;CAEA,MAAM,SACJ,MACA,MACA,OACA,MACA,WACQ;EAAE;EAAM;EAAM;EAAO;EAAM,KAAK;EAAM,IAAI;EAAG,OAAO,EAAE;EAAQ,GAAG;EAAG,GAAG;CAAM;;CAGvF,MAAM,YAAY,OAAoB,GAAG,SAAS,SAAU,GAAG,SAAS,SAAS,GAAG;;CAEpF,MAAM,UAAU,OAAoB,GAAG,SAAS,QAAQ,GAAG,KAAK,MAAO;;CAGvE,MAAM,UAAU,OAA+B;EAC7C,MAAM,IAAI,KAAK,UAAU,GAAG,KAAK,IAAI;EACrC,MAAM,cAAc,SAAS,EAAE;EAC/B,GAAG,IAAI,cAAc,EAAE,UAAU;EACjC,MAAM,OACJ,GAAG,SAAS,SACR,mBAAmB,EAAE,OAAO,GAAG,KAAK,KACpC,GAAG,SAAS,QACV,kBAAkB,GAAG,EAAE,OAAO,EAAE,QAAQ,GAAG,MAAM,QAAQ,GAAG,KAAK,QAAQ,GAAG,GAAG,KAC/E,kBAAkB,EAAE,QAAQ,GAAG,MAAM,QAAQ,GAAG,OAAO,cAAc,QAAQ,GAAG,MAAM,GAAG,OAAO,KAAK,UAAU,GAAG,GAAG,EAAE;EAC/H,IAAI,WAAW,IAAI,IAAI,UAAU;GAC/B,OAAO,IAAI,IAAI,cAAc,WAAW,CAAC;GACzC;EACF;EACA,OAAO;CACT;;CAGA,MAAM,QAAQ,OAAiB;EAC7B,IAAI,GAAG,SAAS,QAAQ;EACxB,IAAI,SAAS,EAAE,GAAG;GAChB,SAAS,IAAI,GAAG,GAAG,EAAE;GACrB,IAAI,IAAI,OAAO,EAAE,CAAC;EACpB,OAAO,aAAa,GAAG,KAAK;CAC9B;;CAGA,MAAM,WAAW,QAAoB;EACnC,IAAI,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;EACpC,MAAM,QAAc,CAAC;EACrB,KAAK,MAAM,MAAM,KACf,IAAI,GAAG,SAAS,OAAO,OAAO,IAAI,IAAI,cAAc,QAAQ,CAAC;OACxD;GACH,IAAI,GAAG,SAAS,OAAO,IAAI,IAAI,QAAQ;GACvC,MAAM,KAAK,EAAE;EACf;EAEF,MAAM,QAAQ,GAAG,KAAK;CACxB;CAIA,MAAM,SAAS,SACb,CAAC,KAAK,WACL,KAAK,OAAO,KACX,KAAK,UAAU,KAAA,KACf,KAAK,QAAQ,UAAU,QACtB,KAAK,KAAK,KAAK,SAAS;CAC7B,MAAM,SAAS,SAAwB,KAAK,UAAU,KAAA;CACtD,MAAM,WAAW,SACf,CAAC,MAAM,IAAI,MAAM,KAAK,SAAS,OAAO,KAAK,KAAK,QAAQ,UAAU;CAEpE,MAAM,SAAS,SAAqB;EAClC,IAAI,KAAK,WAAW,UAAU,MAAM,IAAI,GAAG,cAAc,MAAM,aAAa;EAC5E,MAAM,IAAI,IAAI;EACd,SAAS;CACX;;CAGA,MAAM,gBAAgB,MAAY,MAAuC;EACvE,MAAM,OAAO,MAAM,IAAI;EACvB,MAAM,QAAQ,QAAQ,IAAI;EAC1B,MAAM,QAAQ,KAAK,KAAK,KAAK,OAAO,KAAK,QAAQ,WAAW,OAAO,IAAI,KAAA;EAEvE,KAAK,IAAI;GADS;GAAG;GAAM;GAAO;GAAO,KAAK,KAAK;GAAI,OAAO,CAAC;EACtD;EACT,cAAc,MAAM,KAAK,KAAK,aAAa,aAAa;EACxD,OAAO;GACL,GAAG,KAAK;GACR,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;GACvC,GAAI,KAAK,QAAQ,SAAS,QAAQ,EAAE,MAAM,MAAM,IAAI,CAAC;GACrD,GAAI,OAAO,EAAE,UAAU,KAAK,MAAM,IAAI,CAAC;GACvC,GAAI,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;EACjC;CACF;CAEA,MAAM,gBAAgB,SAAqB;EACzC,MAAM,IAAI,KAAK;EACf,IAAI,MAAM,KAAA,KAAa,KAAK,UAAU,KAAA,GAAW;EACjD,EAAE,MAAM,KAAK;EACb,MAAM,KAAK,MAAM,QAAQ,MAAM,IAAI,KAAK,UAAU,KAAK,KAAK,GAAG,CAAC,CAAC;EACjE,IAAI,SAAS,MAAM;GACjB,MAAM,OAAO,OAAO,EAAE;GACtB,IAAI,SAAS,KAAA,GAAW,MAAM,KAAK,IAAI;EACzC,OAAO,QAAQ,EAAE;CACnB;CAEA,MAAM,UAAU,SAAqB;EACnC,KAAK,KAAK;EACV,KAAK,OAAO;EACZ,KAAK,IAAI,KAAA;EACT,aAAa,KAAK,KAAK;EACvB,KAAK,QAAQ,KAAA;EACb,KAAK,QAAQ;EACb,IAAI,KAAK,QAAQ,OAAO,GAAG;GACzB,KAAK,QAAQ,MAAM;GACnB,KAAK,OAAO,KAAA;EACd;EACA,cAAc,MAAM,KAAK,SAAS,WAAW,MAAM;CACrD;;CAGA,MAAM,SAAS,SAAqB;EAClC,MAAM,KAAK,oBAAoB,KAAK,UAAU,KAAK,IAAI,EAAE,EAAE;CAC7D;CAEA,MAAM,YAAY,SAAqB;EACrC,MAAM,OAAO,MAAM,IAAI;EACvB,MAAM,IAAI,KAAK;EACf,IAAI,SAAS,QAAQ,OAAO;GAC1B,IAAI,MAAM,KAAA,MAAc,CAAC,QAAQ,EAAE,SAAS,MAAM,IAAI,KAAK,EAAE,UAAU,QAAQ,IAAI,IAAI;IACrF,MAAM,IAAI;IACV,KAAK,IAAI,KAAA;GACX;GACA,IAAI,QAAQ,KAAK,MAAM,KAAA,KAAa,KAAK,UAAU,KAAA,GAAW;IAC5D,MAAM,IAAI,EAAE;IACZ,MAAM,KAAK,KAAK,UAAU;KAAE,GAAG;KAAO;KAAG,GAAG,aAAa,MAAM,CAAC;IAAE,CAAC,CAAC;GACtE;EACF;EACA,IAAI,CAAC,MAAM;GACT,IAAI,KAAK,WAAW,UAAU,KAAK,WAAW,UAAU,OAAO,IAAI;GACnE;EACF;EACA,MAAM,UAAU,KAAK;EACrB,IAAI,SAAS,QAAQ,QAAQ,SAAS,QAAQ,QAAQ,KAAK,IAAI,aAAa,IAAI;CAClF;CAEA,MAAM,gBAAsB;EAC1B,MAAM,QAAQ,CAAC,GAAG,KAAK;EACvB,MAAM,MAAM;EACZ,KAAK,MAAM,QAAQ,OAAO,SAAS,IAAI;CACzC;CAEA,MAAM,aAAa,MAAY,UAAgC;EAC7D,IAAI,KAAK,QAAQ;EACjB,KAAK,SAAS;EACd,IAAI,MAAM,IAAI,KAAK,IAAI,MAAM,MAAM,MAAM,OAAO,KAAK,IAAI;EACzD,IAAI,KAAK,MAAM,KAAA,KAAa,SAAS,QAAQ,OAAO,MAAM,IAAI;EAC9D,OAAO,IAAI;EACX,KAAK,GAAG,MAAM;EACd,KAAK,IAAI,MAAM;EACf,KAAK,GAAG,MAAM;EACd,KAAK,SAAS,MAAM;EACpB,KAAK,GAAG,MAAM;EACd,KAAK,OAAO;EACZ,MAAM,OAAO,IAAI;EACjB,IAAI,UAAU,KAAA,GAAW,OAAO,KAAK;EACrC,SAAS;CACX;;CAGA,MAAM,eAAe,MAAY,QAAuB;EACtD,KAAK,IAAI,KAAA;EACT,cAAc,MAAM,KAAK,KAAK,aAAa,aAAa;EACxD,IAAI,KAAK;GACP,MAAM,IAAI;GACV;EACF;EACA,KAAK,QAAQ,iBACL;GACJ,KAAK,QAAQ,KAAA;GACb,MAAM,IAAI;EACZ,GACA,QAAQ,KAAK,OAAO,UAAU,UAAU,MAAM,CAChD;EACA,KAAK,SAAS;CAChB;;CAKA,MAAM,kBAA0B;EAC9B,IAAI,MAAM;EACV,KAAK,MAAM,QAAQ,MAAM,OAAO,GAC9B,IAAI,MAAM,IAAI,GACZ,OAAO,GAAG,KAAK,KAAK,GAAG,KAAK,QAAQ,SAAS,QAAQ,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,IAAI,QAAQ,IAAI,IAAI,IAAI,EAAE;EAC7G,OAAO;CACT;CAEA,MAAM,gBAAsB;EAC1B,aAAa,cAAc;EAC3B,iBAAiB,KAAA;EACjB,IAAI,YAAY,SAAS,KAAA,GAAW;EACpC,OAAO;EACP,MAAM,IAAI;EACV,QAAQ;EACR,SAAS;EACT,MAAM,KAAiB;GACrB,UAAU;IACR,IAAI,MAAM,KAAK,SAAS;GAC1B;GACA,QAAQ,UAAU;IAChB,IAAI,MAAM,KAAK,YAAY,QAAQ,OAAO,KAAK,CAAC;GAClD;GACA,SAAS,SAAS;IAChB,IAAI,MAAM,KAAK,WAAW,IAAI;GAChC;EACF;EACA,IAAI;GACF,IAAI,CAAC,aAAa,OAAO,qBAAqB,YAAY;IACxD,YAAY;IACZ,iBAAiB,UAAU,QAAQ;IACnC,IAAI,OAAO,aAAa,aACtB,SAAS,iBAAiB,oBAAoB,SAAS;GAC3D;GACA,IAAI,SAAS,OAAO;IAClB,MAAM,MAAM,WAAW,QAAQ,KAAK,KAAK;IAEzC,MAAM,UAAqB,CAAC;IAC5B,KAAK,MAAM,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG;KACtC,IAAI,CAAC,MAAM,IAAI,GAAG;KAClB,IAAI,QAAQ,UAAU,qBAAqB;MACzC,UAAU,MAAM,IAAI,cAAc,OAAO,CAAC;MAC1C;KACF;KACA,QAAQ,KAAK,aAAa,MAAM,QAAQ,SAAS,CAAC,CAAC;IACrD;IACA,YAAY,UAAU;IACtB,IAAI,aAAa,IAAI,KAAK,KAAK,UAAU,OAAO,CAAC;IACjD,OAAO,WAAW,IAAI,SAAS,GAAG,SAAS,YAAY,EAAE;GAC3D,OACE,OAAO,WACL,WAAW,QAAQ,KAAK,IAAI,CAAC,CAAC,SAAS,GACvC,QAAQ,aAAa,WAAW,WAChC,EACF;EACJ,SAAS,OAAO;GACd,OAAO;GACP,SAAS,IAAI,cAAc,UAAoB,eAA2B,EAAE,OAAO,MAAM,CAAC,CAAC;GAC3F;EACF;EACA,eAAe,iBAAiB;GAC9B,IAAI,MAAM,KAAK,WAAW,CAAC,CAAC;EAC9B,GAAG,MAAM;CACX;CAEA,MAAM,iBAAuB;EAC3B,IAAI,mBAAmB,KAAA,GAAW,QAAQ;CAC5C;;CAGA,MAAM,kBAAwB;EAC5B,IAAI,SAAS,oBAAoB,aAAa,mBAAmB,KAAA,GAAW,QAAQ;CACtF;;CAGA,MAAM,aAAmB;EACvB,OAAO;EACP,MAAM,MAAM;EACZ,OAAO,KAAA;EACP,QAAQ;EACR,SAAS,KAAA;EACT,aAAa,YAAY;EACzB,aAAa,WAAW;EACxB,cAAc,SAAS;EACvB,YAAY,KAAA;EACZ,KAAK,MAAM;CACb;CAEA,MAAM,cAAc,SAAqE;EACvF,MAAM,WAAW;EACjB,MAAM,OAAO,WAAW,KAAK;EAC7B,UAAU,KAAA;EACV,KAAK;EACL,IAAI,UAAU;EACd,IAAI,SAAS,QAAQ,SAAS,OAAO,GAAG;GACtC,MAAM,OAAO,CAAC,GAAG,SAAS,OAAO,CAAC;GAClC,SAAS,MAAM;GACf,QAAQ,IAAI;EACd;EACA,KAAK,MAAM,QAAQ,MAAM,OAAO,GAAG;GACjC,KAAK,IAAI,KAAA;GACT,aAAa,KAAK,KAAK;GACvB,KAAK,QAAQ,KAAA;GACb,IAAI,MAAM,IAAI,GAAG,cAAc,MAAM,KAAK,KAAK,aAAa,aAAa;EAC3E;EACA,MAAM,UACJ,KAAK,WAAW,KAAA,KAChB,KAAK,UAAU,OACf,KAAK,SAAS,OACd,CAAC,iBAAiB,IAAI,KAAK,MAAM;EACnC,KAAM,SAAS,KAAA,KAAa,MAAM,IAAI,IAAI,KAAM,YAAY,UAAU,eAAe,MAAM;GACzF,MAAM,MACF,SAAS,KAAA,IACP,yCAAyC,SACzC,uBAAuB,KAAK;GAElC,SAAS,IAAI,cAAc,UAAU,GAAG,CAAC;GACzC;EACF;EACA,IAAI,YAAY,UAAU,wBAAwB,WAAW;GAC3D,SAEM,IAAI,cACF,UACA,iBAAiB,UAAU,qBAAqB,oBAClD,CAEN;GACA;EACF;EACA,IAAI,QAAQ,QAAQ,SAAS,UAAU,UAAU,MAAM;EACvD,WAAW;EACX,IAAI,UAAU,UAAU;OACnB,IAAI,QAAQ,SAAS,MAAM;GAC9B,WAAW;GACX,IAAI,WAAW,GAAG;IAChB,OAAO;IACP,QAAQ;GACV;EACF;EACA,UAAU,cAAc;EACxB,iBAAiB,WAAW,SAAS,KAAK;CAC5C;;CAGA,MAAM,aAAmB;EAEvB,UAAU;EACV,IAAI,SAAS,GAAG;GACd,WAAW,CAAC,CAAC;GACb;EACF;EACA,IAAI,SAAS,MAAM,MAAM,KAAK,kBAAc;CAC9C;CAIA,MAAM,WAAW,OAAoB,YAA2B;EAC9D,QAAQ,MAAM,GAAd;GACE,KAAK;IACH,IAAI,OAAO;IACX,QAAQ;IACR,SAAS,MAAM;IACf,WAAW,MAAM;IACjB,UAAU;IACV,SAAS;IACT,aAAa,YAAY;IACzB,IAAI,MAAM,KAAK,GAAG,YAAY,YAAY,MAAM,MAAM,EAAE;IACxD,cAAc,iBAAiB;KAC7B,UAAU;IACZ,GAAG,QAAQ;IACX,UAAU,MAAM;IAChB,IAAI,SAAS,MAAM,KAAK,MAAM,QAAQ,MAAM,OAAO,GAAG,MAAM,IAAI,IAAI;IACpE,MAAM;IACN;GACF,KAAK;IACH,UAAU,MAAM;IAChB;GACF,KAAK,UAAU;IACb,MAAM,OAAO,MAAM,IAAI,MAAM,CAAC;IAC9B,MAAM,IAAI,MAAM;IAChB,IAAI,SAAS,KAAA,KAAa,MAAM,KAAA,KAAa,EAAE,MAAM,MAAM,GAAG;IAC9D,IAAI,MAAM,SAAS,EAAE,UAAU,KAAA,GAAW,KAAK,OAAO,MAAM;IAC5D,MAAM,MACJ,MAAM,UAAU,QACf,MAAM,QAAQ,QAAQ,EAAE,UAAU,KAAA,KAAa,EAAE,EAAE,UAAU,KAAK,CAAC,KAAK;IAC3E,KAAK,KAAK;IACV,KAAK,QAAQ;IACb,cAAc,MAAM,MAAM;IAC1B,IAAI,KACF,KAAK,KAAK,IAAI;KAAE,OAAO,EAAE,SAAS;KAAG,MAAM,MAAM;KAAM,OAAO,MAAM,UAAU;IAAK,CAAC;IACtF;GACF;GACA,KAAK,YAAY;IACf,MAAM,OAAO,MAAM,IAAI,MAAM,CAAC;IAC9B,IAAI,SAAS,KAAA,GAAW;IACxB,IAAI,MAAM,QAAQ,WAAW;KAE3B,KAAK,IAAI,KAAA;KACT,UAAU,MAAM,IAAI,cAAc,aAAuB,SAAqB,CAAC;IACjF,OAAO,YAAY,MAAM,MAAM,QAAQ,SAAS,MAAM,QAAQ,OAAO;IACrE;GACF;GACA,KAAK,OAAO;IACV,MAAM,OAAO,MAAM,IAAI,MAAM,CAAC;IAC9B,IAAI,SAAS,KAAA,KAAa,MAAM,KAAK,KAAK,MAAM;IAChD,KAAK,OAAO,MAAM;IAClB,MAAM,OAAoB;KACxB,KAAK,MAAM;KACX,UAAU,MAAM,MAAM;KACtB,GAAI,MAAM,MAAM,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,MAAM,EAAE;KACtD,GAAI,MAAM,MAAM,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,MAAM,EAAE;IACnD;IACA,KAAK,MAAM,WAAW,CAAC,GAAI,KAAK,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,CAAE,GACpD,YAAY,QAAQ,MAAM,GAAG,IAAI,CAAC;IACpC,KAAK,MAAM,WAAW,CAAC,GAAG,KAAK,GAAG,GAChC,YAAY,QAAQ;KAAE,OAAO,MAAM;KAAG,MAAM,MAAM;KAAG;IAAK,CAA2B,CAAC;IACxF;GACF;GACA,KAAK,OAAO;IACV,MAAM,KAAK,KAAK,MAAM,CAAC;IACvB,IAAI,OAAO,KAAA,GACT,OAAO,IAAI,KAAA,GAAW;KAAE,KAAK,MAAM;KAAG,WAAW,MAAM,QAAQ;IAAK,CAAC;IACvE;GACF;GACA,KAAK,OAAO;IACV,MAAM,KAAK,KAAK,MAAM,CAAC;IACvB,IAAI,OAAO,KAAA,GAAW,OAAO,IAAI,KAAA,GAAW,MAAM,CAAC;IACnD;GACF;GACA,KAAK,OAAO;IACV,MAAM,QAAQ,IAAI,cAAc,MAAM,MAAM,MAAM,GAAG;IACrD,MAAM,OAAO,MAAM,MAAM,KAAA,IAAY,KAAA,IAAY,MAAM,IAAI,MAAM,CAAC;IAClE,IAAI,MAAM,MAAM,KAAA,GAAW;KACzB,IAAI,CAAC,WAAW,MAAM,GAAG,MAAM,MAAM,GAAG;MACtC,IAAI,UAAU,IAAI,MAAM,IAAI,GAAG;OAC7B,YAAY,MAAM,KAAK;OACvB,OAAO,KAAK;MACd,OAAO;OACL,KAAK,IAAI,KAAA;OACT,UAAU,MAAM,KAAK;MACvB;MACA;KACF;KAEA,MAAM,KAAK,WAAW,SAAS,OAAO,KAAK,MAAM,CAAC,IAAI,KAAA;KACtD,IAAI,OAAO,KAAA,GAAW;MACpB,OAAO,IAAI,KAAK;MAChB;KACF;IACF;IACA,OAAO,KAAK;IACZ;GACF;GACA,KAAK,QAAQ;IACX,MAAM,OAAO,MAAM,IAAI,MAAM,CAAC;IAC9B,IAAI,SAAS,KAAA,GAAW;IACxB,IAAI,cAAc,KAAK,SAAS,MAAM,IAAI,MAAM,CAAC,GAAG;KAClD,KAAK,OAAO,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC;KACrC,KAAK,KAAK,UAAU,KAAK,IAAI;IAC/B;IACA,IAAI,MAAM,OAAO,UAAU,KAAK,MAAM,KAAA,GAAW;KAC/C,KAAK,EAAE,QAAQ;KACf,IAAI,KAAK,EAAE,QAAQ,KAAK,EAAE,QAAQ,KAAK,IAAI,aAAa,IAAI;IAC9D;IACA;GACF;GACA,KAAK,QACH;EACJ;CACF;CAIA,MAAM,cAAoB;EACxB,IAAI,CAAC,SAAS,UAAU;EACxB,QAAQ;EACR,IAAI,SAAS,OAAO;GAClB,SAAS;GACT;EACF;EACA,KAAK,IAAI,KAAK,MAAM,IAAI,OAAO,KAAA,KAAa,OAAO,KAAK,MAAM,IAAI;GAChE,MAAM,OAAO,OAAO,EAAE;GACtB,IAAI,SAAS,KAAA,GAAW;IACtB,IAAI,MAAM,KAAK,IAAI,MAAM,MAAM;IAC/B,KAAK,EAAE;GACT;GACA,MAAM,MAAM;EACd;CACF;CAEA,MAAM,iBAAuB;EAC3B,IAAI,WAAW,cAAc,KAAA,KAAa,WAAW,KAAA,GAAW;EAChE,MAAM,MAAY,CAAC;EACnB,MAAM,QAAkB,CAAC;EACzB,IAAI,QAAQ;EACZ,OAAO,MAAM,SAAS,KAAK,IAAI,SAAS,uBAAuB,QAAQ,gBAAgB;GACrF,MAAM,KAAK,MAAM,MAAM;GACvB,IAAI,OAAO,KAAA,GAAW;GACtB,MAAM,OAAO,OAAO,EAAE;GACtB,IAAI,SAAS,KAAA,GAAW;GACxB,IAAI,KAAK,EAAE;GACX,MAAM,KAAK,IAAI;GACf,SAAS,KAAK;EAChB;EACA,IAAI,IAAI,WAAW,GAAG;EACtB,UAAU;EACV,UAAU,KAAK,UAAU,KAAK,UAAU,MAAM,EAAE,aAAa,MAAM,KAAK,GAAG,EAAE,GAAG,CAAC,CAAC,KAChF,KAAA,GACA,MACF;CACF;CAEA,MAAM,YAAY,OAAO,KAAW,SAAgC;EAClE,KAAK,MAAM,MAAM,KAAK,KAAK,EAAE;EAC7B,IAAI,QAAQ;EAGZ,MAAM,OAAO,IAAI,gBAAgB;EACjC,QAAQ;EACR,MAAM,WAAW,iBAAiB,KAAK,MAAM,GAAG,KAAK,IAAI,GAAG,IAAI,IAAI,MAAM,CAAC,CAAC;EAC5E,IAAI;GACF,MAAM,UAAU,IAAI,QAAQ,MAAM,WAAW,CAAC;GAC9C,QAAQ,IAAI,gBAAgB,kBAAkB;GAC9C,MAAM,MACJ,QAAQ,YAAY,KAAA,IAChB,QAAQ,WAAW,QAAQ,KAAK,KAAK,CAAC,IACtC,WAAW,QAAQ,SAAS,KAAK;GACvC,MAAM,WAAW,MAAM,QAAQ,KAAK;IAAE,QAAQ;IAAQ;IAAS;IAAM,QAAQ,KAAK;GAAO,CAAC;GAC1F,IAAI,SAAS,IAAI;IACf,MAAM,SAAU,MAAM,SAAS,KAAK;IACpC,IAAI,MAAM,QAAQ,OAAO,MAAM,GAC7B,KAAK,MAAM,QAAQ,OAAO,QAAqB;KAC7C,MAAM,QAAQ,cAAc,IAAI;KAChC,IAAI,UAAU,KAAA,GAAW,YAAY,QAAQ,OAAO,IAAI,CAAC;IAC3D;IACF,YAAY;GACd,OAAO,IAAI,SAAS,UAAU,OAAO,iBAAiB,IAAI,SAAS,MAAM,GAAG,QAAQ;QAC/E;IACH,MAAM,UAAW,MAAM,SAAS,KAAK,CAAC,CAAC,YAAY,KAAA,CAAS;IAG5D,MAAM,OAAkB,SAAS,OAAO,QAAQ;IAChD,KAAK,MAAM,MAAM,KAAK;KACpB,IAAI,SAAS,IAAI,GAAG,CAAC,MAAM,IAAI,SAAS,OAAO,GAAG,CAAC;KACnD,OAAO,IAAI,IAAI,cAAc,MAAM,SAAS,OAAO,OAAO,CAAC;IAC7D;GACF;EACF,QAAQ;GACN,IAAI,UAAU;GACd,QAAQ;EACV,UAAU;GACR,aAAa,QAAQ;EACvB;EACA,UAAU;EACV,MAAM,aAAa,IAAI,QAAQ,OAAO,SAAS,EAAE,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,EAAE;EAC/E,KAAK,MAAM,MAAM,YAAY,SAAS,OAAO,GAAG,CAAC;EACjD,IAAI,OAAO;GACT,KAAK,MAAM,MAAM,YACf,IAAI,GAAG,SAAS,OAAO,OAAO,IAAI,IAAI,cAAc,aAAa,CAAC;GACpE,QACE,IAAI,QACD,OAAO,GAAG,SAAS,UAAW,GAAG,SAAS,UAAU,CAAC,GAAG,OAAO,WAAW,SAAS,EAAE,EACxF,CACF;GACA,aAAa;GACb,YAAY,iBACJ;IACJ,YAAY,KAAA;IACZ,MAAM;GACR,GACA,QAAQ,YAAY,GAAG,UAAU,UAAU,MAAM,CACnD;EACF,OAAO,KAAK,MAAM,MAAM,YAAY,OAAO,IAAI,IAAI,cAAc,aAAa,CAAC;EAC/E,MAAM;CACR;CAIA,MAAM,YAAkB;EACtB,YAAY;EACZ,IAAI,UAAU;EACd,QAAQ;EACR,IAAI,SAAS,KAAA,GAAW;GACtB,IAAI,mBAAmB,KAAA,MAAc,MAAM,SAAS,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,KAAK,IACrF,QAAQ;GACV;EACF;EACA,IACE,SAAS,UACR,UAAU,MAAM,aACf,CAAC,GAAG,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,MAAM,MAAM,CAAC,KAAK,EAAE,MAAM,KAAA,KAAa,EAAE,UAAU,KAAA,CAAS,IACxF;GACA,KAAK;GACL,KAAK,MAAM,QAAQ,MAAM,OAAO,GAAG,KAAK,IAAI,KAAA;GAC5C,QAAQ;EACV,OAAO,MAAM;CACf;CAEA,SAAS,WAAiB;EACxB,IAAI,aAAa,UAAU;EAC3B,YAAY;EACZ,eAAe,GAAG;CACpB;CAEA,SAAS,SAAS,OAA6B;EAC7C,IAAI,UAAU;EACd,WAAW;EACX,aAAa,cAAc;EAC3B,aAAa,SAAS;EACtB,iBAAiB,KAAA;EACjB,YAAY,KAAA;EACZ,KAAK;EACL,OAAO,MAAM;EACb,IAAI,WAAW;GACb,IAAI,OAAO,wBAAwB,YAAY,oBAAoB,UAAU,QAAQ;GACrF,IAAI,OAAO,aAAa,aACtB,SAAS,oBAAoB,oBAAoB,SAAS;GAC5D,YAAY;EACd;EACA,MAAM,SAAS,SAAS,IAAI,cAAc,QAAQ;EAClD,MAAM,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,GAAG,SAAS,OAAO,CAAC;EACzD,SAAS,MAAM;EACf,KAAK,MAAM,MAAM,SACf,IAAI,GAAG,WAAW,KAAA,GAAW,aAAa,GAAG,KAAK;OAC7C,OAAO,IAAI,MAAM;EAExB,KAAK,MAAM,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,UAAU,IAAI;EACtD,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,UAAU,QAAQ;EAClB,gBAAgB,MAAM;EACtB,IAAI,UAAU,KAAA,GAAW,OAAO,KAAK;CACvC;CAIA,MAAM,eAAe,MAAc,mBAAwD;EACzF,MAAM,OAAa;GACjB;GACA,SAAS;GACT,oBAAI,IAAI,IAAI;GACZ,qBAAK,IAAI,IAAI;GACb,oBAAI,IAAI,IAAI;GACZ,oBAAI,IAAI,IAAI;GACZ,0BAAU,IAAI,IAAI;GAClB,yBAAS,IAAI,IAAI;GACjB,QAAQ;GACR,MAAM;GACN,IAAI;GACJ,MAAM;GACN,IAAI;GACJ,OAAO,eAAe;GACtB,IAAI;GACJ,OAAO;GACP,QAAQ;EACV;EACA,MAAM,IAAI,MAAM,IAAI;EACpB,IAAI,MAAM,IAAI,GAAG,MAAM,IAAI;;EAG3B,MAAM,UACJ,KACA,MACA,SACA,UACiB;GACjB,IAAI,KAAK,QAAQ,aAAa,KAAA;GAG9B,IAAI,OAAO,CAAC,IAAI,IAAI,IAAI;GACxB,IAAI,IAAI,IAAI;GACZ,IAAI,CAAC,WAAW,MAAM;IACpB,KAAK,QAAQ;IACb,MAAM,IAAI;GACZ;GACA,aAAa;IACX,IAAI,CAAC,MAAM;IACX,OAAO;IAEP,IAAI,CAAC,IAAI,OAAO,IAAI,KAAK,SAAS;IAClC,KAAK,QAAQ;IACb,QAAQ;IACR,MAAM,IAAI;GACZ;EACF;;EAGA,MAAM,UACJ,MACA,OACA,MACA,KACA,KACA,OACe;GACf,IAAI,OAAO;GACX,IAAI;GACJ,IAAI,KAAK,QAAQ,UAAU,IAAI,cAAc,QAAQ;QAChD,IAAI,CAAC,YAAY,KAAK,KAAM,QAAQ,KAAA,KAAa,CAAC,iBAAiB,GAAG,GACzE,UAAU,IAAI,cAAc,WAAW;QAEvC,IAAI;IACF,OAAO,KAAK,UAAU,QAAQ,IAAI,KAAK;GACzC,SAAS,OAAO;IACd,UAAU,IAAI,cAAc,aAAuB,YAAwB,EAAE,MAAM,CAAC;GACtF;GACF,IAAI,YAAY,KAAA,GAAW,OAAO,QAAQ,OAAO,OAAO;GACxD,OAAO,IAAI,SAAY,SAAS,WAAW;IACzC,MAAM,UAAU,SAAS;IACzB,MAAM,KAAK,MAAM,MAAM,MAAM,OAAO,MAAM;KACxC,KAAK,UAAU,KAAA,IAAa,OAAO,GAAG,GAAG,GAAG,EAAE;KAC9C;KACA;KACA,QAAQ,OACH,OAAO,UAAU;MAChB,IAAI,SAAS;OACX,KAAK,MAAM;OACX,MAAM,IAAI;MACZ;MACA,IAAI,OAAO,OAAO,KAAK;WAClB,QAAQ,KAAU;KACzB,IACA,KAAA;IACN,CAAC;IACD,IAAI,SAAS;KACX,KAAK,MAAM;KACX,MAAM,IAAI;IACZ;IACA,MAAM,UAAU,QAAQ,EAAE;IAC1B,IAAI,YAAY,KAAA,GAAW;KACzB,GAAG,SAAS,KAAA;KACZ,IAAI,SAAS,KAAK,MAAM;KACxB,OAAO,OAAO;IAChB,OAAO,IAAI,CAAC,KAAK,QAAQ;KAAE,KAAK;KAAG,WAAW;IAAM,CAAM;GAC5D,CAAC;EACH;EAEA,MAAM,KAA0B;GAC9B;GACA,IAAI,SAAS;IACX,OAAO,KAAK;GACd;GACA,IAAI,UAAU;IACZ,OAAO,KAAK;GACd;GACA,GAAG,OAAO,SAAS;IACjB,IAAI,MAAM,KAAK,GAAG,IAAI,KAAK;IAC3B,IAAI,QAAQ,KAAA,GAAW;KACrB,sBAAM,IAAI,IAAI;KACd,KAAK,GAAG,IAAI,OAAO,GAAG;IACxB;IACA,MAAM,WAAW;IACjB,OAAO,OAAO,UAAU,SAAoB,aAAa;KACvD,IAAI,SAAS,SAAS,KAAK,KAAK,GAAG,IAAI,KAAK,MAAM,UAAU,KAAK,GAAG,OAAO,KAAK;IAClF,CAAC;GACH;GACA,QAAQ,YAAY,OAAO,KAAK,KAAK,OAAoD;GACzF,QAAQ,YAAY,OAAO,KAAK,IAAI,OAAO;GAC3C,WAAW,YAAY,OAAO,KAAK,IAAI,SAAS,IAAI;GACpD,UAAU,OAAO,MAAM,iBAAiB,CAAC,MACvC,OAAmB,OAAO,OAAO,MAAM,eAAe,KAAK,eAAe,QAAQ,OAAO,CAAC;GAC5F,UAAU,OAAO,MAAM,iBAAiB,CAAC,MACvC,OAAO,OAAO,OAAO,MAAM,KAAA,GAAW,MAAM,eAAe,aAAa,kBAAkB;GAC5F,UAAU;IACR,eAAe;KACb,KAAK,SAAS,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC;KACvC,OAAO,KAAK;IACd;IACA,IAAI,OAAO;KACT,IAAI,KAAK,QAAQ;KACjB,KAAK,QAAQ;KACb,KAAK,MAAM;KACX,MAAM,IAAI;IACZ;IACA,QAAQ;KACN,IAAI,KAAK,UAAU,KAAK,UAAU,KAAA,GAAW;KAC7C,KAAK,QAAQ,KAAA;KACb,KAAK,MAAM;KACX,MAAM,IAAI;IACZ;IACA,KAAK,aAAa,OAAO,KAAK,UAAU,QAAgD;GAC1F;GACA,aAAa,UAAU,IAAI;EAC7B;EACA,KAAK,KAAK;EACV,OAAO;CACT;CAEA,OAAO;EACL,QAAQ,MAAM,iBAAiB,CAAC,GAAG;GACjC,IAAI,UAAU,MAAM,IAAI,cAAc,QAAQ;GAC9C,IAAI,CAAC,cAAc,IAAI,GAAG,MAAM,IAAI,cAAc,eAAe,IAAI;GAErE,OADa,MAAM,IAAI,IACZ,CAAC,EAAE,MAAM,YAAY,MAAM,cAAc;EACtD;EACA,IAAI,SAAS;GACX,OAAO;EACT;EACA,IAAI,eAAe;GACjB,OAAO;EACT;EACA,SAAS,SAAS;GAChB,IAAI,CAAC,UAAU,gBAAgB,IAAI,OAAO;GAC1C,aAAa;IACX,gBAAgB,OAAO,OAAO;GAChC;EACF;EACA,aAAa,SAAS;CACxB;AACF"}
1
+ {"version":3,"file":"core.js","names":[],"sources":["../../../src/client/core.ts"],"sourcesContent":["import { type ErrorCode, RealtimeError } from \"../errors\";\nimport {\n byteLength,\n isChannelName,\n isEventName,\n isIdempotencyKey,\n type Json,\n type Member,\n type ServerFrame,\n serverFrameOf,\n} from \"../protocol\";\nimport { applyPresence } from \"./presence\";\nimport { type Link, type LinkEvents, openStream } from \"./sse\";\nimport type {\n AnyMessage,\n Channel,\n ChannelDef,\n ChannelOptions,\n ChannelSchema,\n ChannelStatus,\n GapInfo,\n MessageMeta,\n PublishAck,\n Realtime,\n RealtimeOptions,\n Status,\n} from \"./types\";\nimport { backoff, clientId, resolveUrl, sendUrl } from \"./util\";\nimport { openSocket } from \"./ws\";\n\n/** Close codes that mean the client did something the server will not accept again. */\nconst FATAL = new Set([1002, 1008, 1009]);\n/** Refusals that may heal by asking again. */\nconst TRANSIENT = new Set<ErrorCode>([\"unavailable\", \"rate_limited\", \"timeout\"]);\n/** HTTP sl of a stream request that are worth retrying among the 4xx. */\nconst RETRYABLE_STATUS = new Set([408, 425, 429]);\nconst MAX_FRAMES_PER_POST = 64;\nconst MAX_POST_BYTES = 256_000;\nconst MAX_STREAM_CHANNELS = 64;\nconst DEFAULT_REQUEST_MS = 10_000;\n\ntype Handler = (data: Json, meta: MessageMeta) => void;\ntype Timer = ReturnType<typeof setTimeout>;\n\ninterface Wire {\n /** The number the subscription went out with; the server answers with it. */\n readonly n: number;\n readonly join: boolean;\n readonly watch: boolean;\n /** The cursor the subscription asked to resume from. */\n readonly after: number | undefined;\n /** The presence state version the server hd for this connection. */\n ver: number;\n /** False until the server can accept presence changes for this connection (over SSE, until the member list arrived). */\n ready: boolean;\n}\n\ninterface Chan {\n readonly name: string;\n readonly options: ChannelOptions;\n readonly ev: Map<string, Set<Handler>>;\n readonly any: Set<(message: AnyMessage<ChannelDef>) => void>;\n readonly gl: Set<(info: GapInfo) => void>;\n readonly sl: Set<(status: ChannelStatus) => void>;\n readonly presence: Set<(members: readonly Member[]) => void>;\n readonly members: Map<string, Member>;\n list?: readonly Member[] | undefined;\n status: ChannelStatus;\n /** Listeners registered; the channel subscribes while this is above zero. */\n refs: number;\n /** Requests in flight; a stateful connection answers a request only on a subscribed channel. */\n hd: number;\n last: number;\n /** The head of the last gap reported: the next resume starts there, so a gap with nothing left to replay is not reported again. */\n gh: number;\n /** True once a `subbed` arrived since the channel last went idle. */\n sy: boolean;\n state: Json | undefined;\n sv: number;\n w?: Wire | undefined;\n retry?: Timer | undefined;\n fails: number;\n closed: boolean;\n hn?: Channel<ChannelDef> | undefined;\n}\n\ninterface Op {\n readonly kind: \"pub\" | \"req\" | \"pres\";\n readonly chan: Chan;\n readonly event: string;\n /** The JSON text of the data (or of the presence state). */\n readonly data: string;\n readonly key?: string | undefined;\n readonly ack: boolean;\n readonly ms: number;\n readonly order: number;\n n: number;\n timer?: Timer | undefined;\n settle?: ((error: RealtimeError | undefined, value?: unknown) => void) | undefined;\n}\n\n/**\n * Creates the realtime client: one transport (WebSocket, falling back to server-sent ev), reconnects with backoff, a\n * bounded offline queue, resume from the last sequence number and typed channels. It connects when the first channel\n * listens or the first message is sent, never earlier.\n *\n * @param options The stream URL and the policy.\n * @returns The client.\n * @example\n * import { createRealtime } from \"@shadow-js/realtime/client\";\n *\n * type Schema = { [name: `chat:${string}`]: { ev: { message: { text: string } } } };\n * const rt = createRealtime<Schema>({ url: \"/rt\" });\n * const lobby = rt.channel(\"chat:lobby\");\n * const stop = lobby.on(\"message\", (data) => console.log(data.text));\n * await lobby.publish(\"message\", { text: \"hello\" });\n * stop();\n * rt.close();\n * @since 1.0.0\n */\nexport function createRealtime<Schema extends ChannelSchema = ChannelSchema>(\n options: RealtimeOptions,\n): Realtime<Schema> {\n const reconnect = options.reconnect ?? {};\n const minDelay = reconnect.minDelayMs ?? 250;\n const maxDelay = reconnect.maxDelayMs ?? 30_000;\n const stableMs = reconnect.stableMs ?? 5000;\n const random = reconnect.random ?? Math.random;\n const maxQueued = options.maxQueued ?? 100;\n const queueTtl = options.queueTtlMs ?? 30_000;\n const ackTtl = options.ackTimeoutMs ?? 10_000;\n const auto = (options.transport ?? \"auto\") === \"auto\";\n const id = clientId();\n const chans = new Map<string, Chan>();\n const dirty = new Set<Chan>();\n const queue: Op[] = [];\n const inflight = new Map<number, Op>();\n const statusListeners = new Set<(status: Status) => void>();\n\n let status: Status = \"connecting\";\n let mode: \"ws\" | \"sse\" = options.transport === \"sse\" ? \"sse\" : \"ws\";\n let finished = false;\n let scheduled = false;\n let link: Link | undefined;\n let gen = 0;\n let ready = false;\n let connId: string | undefined;\n let maxBytes = 65_536;\n let streamSig = \"\";\n let byeCode: number | undefined;\n let wsFails = 0;\n let attempt = 0;\n let counter = 0;\n let keys = 0;\n let orders = 0;\n /** Heartbeat intervals since the last thing was received. */\n let silent = 0;\n let connectTimer: Timer | undefined;\n let reconnectTimer: Timer | undefined;\n let stableTimer: Timer | undefined;\n let beatTimer: ReturnType<typeof setInterval> | undefined;\n let posting = false;\n /** The POST in flight, which closing the client abandons. */\n let batch: AbortController | undefined;\n let postTimer: Timer | undefined;\n let postFails = 0;\n let listening = false;\n\n const report = (error: unknown): void => {\n try {\n if (options.onError !== undefined) options.onError(error);\n else console.error(\"[realtime]\", error);\n } catch {\n // A failing reporter must not break the client.\n }\n };\n const guard = (run: () => void): void => {\n try {\n run();\n } catch (error) {\n report(error);\n }\n };\n const doFetch: typeof fetch = (input, init) => (options.fetch ?? globalThis.fetch)(input, init);\n const getHeaders = (): HeadersInit | undefined | Promise<HeadersInit | undefined> =>\n typeof options.headers === \"function\" ? options.headers() : options.headers;\n\n const emit = <T>(listeners: Iterable<(value: T) => void>, value: T): void => {\n for (const listener of [...listeners]) guard(() => listener(value));\n };\n const setStatus = (next: Status): void => {\n if (next === status) return;\n status = next;\n if (options.onStatus !== undefined) guard(() => options.onStatus?.(next));\n emit(statusListeners, next);\n };\n const setChanStatus = (chan: Chan, next: ChannelStatus): void => {\n if (chan.status === next) return;\n chan.status = next;\n emit(chan.sl, next);\n };\n\n // --- operations: publishes and requests, queued while there is no connection --------------------------------------\n\n /** Settles an operation once; an unacknowledged publish that fails is reported instead. */\n const finish = (op: Op, error: RealtimeError | undefined, value?: unknown): void => {\n clearTimeout(op.timer);\n const settle = op.settle;\n op.settle = undefined;\n if (settle !== undefined) settle(error, value);\n else if (error !== undefined && op.kind === \"pub\") report(error);\n };\n const arm = (op: Op, ms: number): void => {\n clearTimeout(op.timer);\n op.timer = setTimeout(() => {\n const at = queue.indexOf(op);\n if (at >= 0) queue.splice(at, 1);\n if (inflight.get(op.n) === op) inflight.delete(op.n);\n finish(op, new RealtimeError(\"timeout\"));\n }, ms);\n };\n const take = (n: number): Op | undefined => {\n const op = inflight.get(n);\n if (op !== undefined) inflight.delete(n);\n return op;\n };\n\n const enqueue = (op: Op): RealtimeError | undefined => {\n if (finished) return new RealtimeError(\"closed\");\n const flowing = ready && mode === \"ws\";\n if (op.kind !== \"pres\" && !flowing && queue.length >= maxQueued)\n return new RealtimeError(\"queue_full\");\n if (op.kind === \"pres\") {\n const old = queue.findIndex((item) => item.kind === \"pres\" && item.chan === op.chan);\n if (old >= 0) queue.splice(old, 1);\n } else arm(op, queueTtl);\n queue.push(op);\n if (ready) flush();\n else schedule();\n return undefined;\n };\n\n const newOp = (\n kind: Op[\"kind\"],\n chan: Chan,\n event: string,\n data: string,\n extra: Partial<Pick<Op, \"key\" | \"ack\" | \"ms\" | \"settle\">>,\n ): Op => ({ kind, chan, event, data, ack: true, ms: 0, order: ++orders, n: 0, ...extra });\n\n /** Whether the server answers an operation (a request, or a publish that asks for an acknowledgment). */\n const answered = (op: Op): boolean => op.kind === \"req\" || (op.kind === \"pub\" && op.ack);\n /** How long an operation that went out waits for its answer: a request's own time and a second, or the acknowledgment time. */\n const waitOf = (op: Op): number => (op.kind === \"req\" ? op.ms + 1000 : ackTtl);\n\n /** The frame text of an operation, numbered for an answer. Undefined (and the operation failed) when it is too large. */\n const encode = (op: Op): string | undefined => {\n const c = JSON.stringify(op.chan.name);\n const wantsAnswer = answered(op);\n op.n = wantsAnswer ? ++counter : 0;\n const text =\n op.kind === \"pres\"\n ? `{\"t\":\"pres\",\"c\":${c},\"s\":${op.data}}`\n : op.kind === \"req\"\n ? `{\"t\":\"req\",\"n\":${op.n},\"c\":${c},\"e\":\"${op.event}\",\"d\":${op.data},\"ms\":${op.ms}}`\n : `{\"t\":\"pub\",\"c\":${c},\"e\":\"${op.event}\",\"d\":${op.data}${wantsAnswer ? `,\"n\":${op.n}` : \"\"},\"k\":${JSON.stringify(op.key)}}`;\n if (byteLength(text) > maxBytes) {\n finish(op, new RealtimeError(\"too_large\"));\n return undefined;\n }\n return text;\n };\n\n /** Records that an operation went out: it now waits for an answer, or is done. */\n const sent = (op: Op): void => {\n if (op.kind === \"pres\") return;\n if (answered(op)) {\n inflight.set(op.n, op);\n arm(op, waitOf(op));\n } else clearTimeout(op.timer);\n };\n\n /** Puts operations that went out on a connection that died back at the head of the queue, in their original order. */\n const requeue = (ops: Op[]): void => {\n ops.sort((a, b) => a.order - b.order);\n const again: Op[] = [];\n for (const op of ops) {\n if (op.kind === \"req\") finish(op, new RealtimeError(\"closed\"));\n else {\n if (op.kind === \"pub\") arm(op, queueTtl);\n again.push(op);\n }\n }\n queue.unshift(...again);\n };\n\n // --- channels -----------------------------------------------------------------------------------------------------\n\n const wants = (chan: Chan): boolean =>\n !chan.closed &&\n (chan.refs > 0 ||\n chan.state !== undefined ||\n chan.options.watch === true ||\n (chan.hd > 0 && mode === \"ws\"));\n const joins = (chan: Chan): boolean => chan.state !== undefined;\n const watches = (chan: Chan): boolean =>\n !joins(chan) && (chan.presence.size > 0 || chan.options.watch === true);\n\n const touch = (chan: Chan): void => {\n if (chan.status === \"idle\" && wants(chan)) setChanStatus(chan, \"subscribing\");\n dirty.add(chan);\n schedule();\n };\n\n /** The fields a subscription goes out with, over a WebSocket as a frame and over SSE as a query entry. */\n const subscription = (chan: Chan, n: number): Record<string, unknown> => {\n const join = joins(chan);\n const watch = watches(chan);\n const after = chan.sy\n ? Math.max(chan.last, chan.gh)\n : chan.options.replay === true\n ? 0\n : undefined;\n const w: Wire = { n, join, watch, after, ver: chan.sv, ready: !join };\n chan.w = w;\n setChanStatus(chan, chan.sy ? \"resuming\" : \"subscribing\");\n return {\n c: chan.name,\n ...(after === undefined ? {} : { after }),\n ...(chan.options.echo === false ? { echo: false } : {}),\n ...(join ? { presence: chan.state } : {}),\n ...(watch ? { watch: true } : {}),\n };\n };\n\n const sendPresence = (chan: Chan): void => {\n const w = chan.w;\n if (w === undefined || chan.state === undefined) return;\n w.ver = chan.sv;\n const op = newOp(\"pres\", chan, \"\", JSON.stringify(chan.state), {});\n if (mode === \"ws\") {\n const text = encode(op);\n if (text !== undefined) link?.send(text);\n } else enqueue(op);\n };\n\n const goIdle = (chan: Chan): void => {\n chan.sy = false;\n chan.last = 0;\n chan.w = undefined;\n clearTimeout(chan.retry);\n chan.retry = undefined;\n chan.fails = 0;\n if (chan.members.size > 0) {\n chan.members.clear();\n chan.list = undefined;\n }\n setChanStatus(chan, chan.closed ? \"closed\" : \"idle\");\n };\n\n /** Brings the w in line with what a channel wants (WebSocket: subscribe, unsubscribe, presence). */\n const unsub = (chan: Chan): void => {\n link?.send(`{\"t\":\"unsub\",\"c\":${JSON.stringify(chan.name)}}`);\n };\n\n const syncChan = (chan: Chan): void => {\n const want = wants(chan);\n const w = chan.w;\n if (mode === \"ws\" && ready) {\n if (w !== undefined && (!want || w.join !== joins(chan) || w.watch !== watches(chan))) {\n unsub(chan);\n chan.w = undefined;\n }\n if (want && chan.w === undefined && chan.retry === undefined) {\n const n = ++counter;\n link?.send(JSON.stringify({ t: \"sub\", n, ...subscription(chan, n) }));\n }\n }\n if (!want) {\n if (chan.status !== \"idle\" && chan.status !== \"closed\") goIdle(chan);\n return;\n }\n const current = chan.w;\n if (current?.join && current.ready && current.ver !== chan.sv) sendPresence(chan);\n };\n\n const syncAll = (): void => {\n const batch = [...dirty];\n dirty.clear();\n for (const chan of batch) syncChan(chan);\n };\n\n const closeChan = (chan: Chan, error?: RealtimeError): void => {\n if (chan.closed) return;\n chan.closed = true;\n if (chans.get(chan.name) === chan) chans.delete(chan.name);\n if (chan.w !== undefined && mode === \"ws\" && ready) unsub(chan);\n goIdle(chan);\n chan.ev.clear();\n chan.any.clear();\n chan.gl.clear();\n chan.presence.clear();\n chan.sl.clear();\n chan.refs = 0;\n dirty.delete(chan);\n if (error !== undefined) report(error);\n schedule();\n };\n\n /** Subscribes again after a refusal or a removal the server announced; `now` skips the delay. */\n const resubscribe = (chan: Chan, now: boolean): void => {\n chan.w = undefined;\n setChanStatus(chan, chan.sy ? \"resuming\" : \"subscribing\");\n if (now) {\n touch(chan);\n return;\n }\n chan.retry = setTimeout(\n () => {\n chan.retry = undefined;\n touch(chan);\n },\n backoff(chan.fails, minDelay, maxDelay, random),\n );\n chan.fails += 1;\n };\n\n // --- connection ---------------------------------------------------------------------------------------------------\n\n /** What the open stream must be reopened for: the channels it carries and how they join. */\n const signature = (): string => {\n let sig = \"\";\n for (const chan of chans.values())\n if (wants(chan))\n sig += `${chan.name}|${chan.options.echo === false ? 0 : 1}${joins(chan) ? 1 : 0}${watches(chan) ? 1 : 0};`;\n return sig;\n };\n\n const connect = (): void => {\n clearTimeout(reconnectTimer);\n reconnectTimer = undefined;\n if (finished || link !== undefined) return;\n gen += 1;\n const g = gen;\n ready = false;\n silent = 0;\n const ev: LinkEvents = {\n rx: () => {\n if (g === gen) silent = 0;\n },\n frame: (frame) => {\n if (g === gen) guard(() => onFrame(frame, false));\n },\n closed: (info) => {\n if (g === gen) linkClosed(info);\n },\n };\n try {\n if (!listening && typeof addEventListener === \"function\") {\n listening = true;\n addEventListener(\"online\", onOnline);\n if (typeof document !== \"undefined\")\n document.addEventListener(\"visibilitychange\", onVisible);\n }\n if (mode === \"sse\") {\n const url = resolveUrl(options.url, false);\n // The subscriptions of the stream go in the query: the entries of the channels it carries.\n const entries: unknown[] = [];\n for (const chan of [...chans.values()]) {\n if (!wants(chan)) continue;\n if (entries.length >= MAX_STREAM_CHANNELS) {\n closeChan(chan, new RealtimeError(\"limit\"));\n continue;\n }\n entries.push(subscription(chan, entries.length + 1));\n }\n streamSig = signature();\n url.searchParams.set(\"s\", JSON.stringify(entries));\n link = openStream(url.toString(), doFetch, getHeaders, ev);\n } else\n link = openSocket(\n resolveUrl(options.url, true).toString(),\n options.WebSocket ?? globalThis.WebSocket,\n ev,\n );\n } catch (error) {\n gen += 1;\n shutdown(new RealtimeError(\"closed\", __DEV__ ? \"cannot open\" : undefined, { cause: error }));\n return;\n }\n connectTimer = setTimeout(() => {\n if (g === gen) linkClosed({});\n }, ackTtl);\n };\n\n const onOnline = (): void => {\n if (reconnectTimer !== undefined) connect();\n };\n\n /** A tab that was hidden keeps its timers throttled or frozen: when it shows again, a connection waiting out a backoff reconnects now. */\n const onVisible = (): void => {\n if (document.visibilityState === \"visible\" && reconnectTimer !== undefined) connect();\n };\n\n /** Releases the current transport and everything tied to it. */\n const drop = (): void => {\n gen += 1;\n const old = link;\n link = undefined;\n ready = false;\n connId = undefined;\n clearTimeout(connectTimer);\n clearTimeout(stableTimer);\n clearInterval(beatTimer);\n beatTimer = undefined;\n old?.close();\n };\n\n const linkClosed = (info: { readonly code?: number; readonly status?: number }): void => {\n const wasReady = ready;\n const code = byeCode ?? info.code;\n byeCode = undefined;\n drop();\n if (finished) return;\n if (mode === \"ws\" && inflight.size > 0) {\n const lost = [...inflight.values()];\n inflight.clear();\n requeue(lost);\n }\n for (const chan of chans.values()) {\n chan.w = undefined;\n clearTimeout(chan.retry);\n chan.retry = undefined;\n if (wants(chan)) setChanStatus(chan, chan.sy ? \"resuming\" : \"subscribing\");\n }\n const refused =\n info.status !== undefined &&\n info.status >= 400 &&\n info.status < 500 &&\n !RETRYABLE_STATUS.has(info.status);\n if (((code !== undefined && FATAL.has(code)) || refused) && reconnect.retryFatal !== true) {\n const why = __DEV__\n ? code !== undefined\n ? `the server closed the connection with ${code}`\n : `the server answered ${info.status}`\n : undefined;\n shutdown(new RealtimeError(\"closed\", why));\n return;\n }\n if (attempt >= (reconnect.maxReconnectAttempts ?? Infinity)) {\n shutdown(\n __DEV__\n ? new RealtimeError(\n \"closed\",\n `gave up after ${reconnect.maxReconnectAttempts} reconnect attempts`,\n )\n : new RealtimeError(\"closed\"),\n );\n return;\n }\n let delay = backoff(attempt, minDelay, maxDelay, random);\n attempt += 1;\n if (wasReady) wsFails = 0;\n else if (auto && mode === \"ws\") {\n wsFails += 1;\n if (wsFails >= 2) {\n mode = \"sse\";\n delay = 0;\n }\n }\n setStatus(\"reconnecting\");\n reconnectTimer = setTimeout(connect, delay);\n };\n\n /** Heartbeat: every interval sends a ping; two intervals with nothing received declare the connection dead. */\n const beat = (): void => {\n // The interval that saw the last frame is the first of the three beats counted before the connection is dead.\n silent += 1;\n if (silent > 2) {\n linkClosed({});\n return;\n }\n if (mode === \"ws\") link?.send('{\"t\":\"ping\"}');\n };\n\n // --- frames from the server ---------------------------------------------------------------------------------------\n\n const onFrame = (frame: ServerFrame, viaPost: boolean): void => {\n switch (frame.t) {\n case \"ready\":\n if (ready) return;\n ready = true;\n connId = frame.id;\n maxBytes = frame.max;\n wsFails = 0;\n silent = 0;\n clearTimeout(connectTimer);\n if (frame.hb > 0) beatTimer = setInterval(beat, frame.hb);\n stableTimer = setTimeout(() => {\n attempt = 0;\n }, stableMs);\n setStatus(\"open\");\n if (mode === \"ws\") for (const chan of chans.values()) dirty.add(chan);\n flush();\n return;\n case \"bye\":\n byeCode = frame.code;\n return;\n case \"subbed\": {\n const chan = chans.get(frame.c);\n const w = chan?.w;\n if (chan === undefined || w === undefined || w.n !== frame.n) return;\n if (frame.reset || w.after === undefined) chan.last = frame.head;\n const gap =\n frame.reset === true ||\n (frame.gap === true && w.after !== undefined && !(w.after === 0 && !chan.sy));\n chan.sy = true;\n chan.fails = 0;\n setChanStatus(chan, \"live\");\n // Read only while the channel is subscribed (see `subscription`), so no reset is needed when it goes idle.\n chan.gh = gap ? frame.head : 0;\n if (gap)\n emit(chan.gl, { after: w.after ?? 0, head: frame.head, reset: frame.reset === true });\n return;\n }\n case \"unsubbed\": {\n const chan = chans.get(frame.c);\n if (chan === undefined) return;\n if (frame.why === \"revoked\") {\n // The hub dropped the subscription: closing must not send an unsub it would answer with not_subscribed.\n chan.w = undefined;\n closeChan(chan, new RealtimeError(\"forbidden\", __DEV__ ? \"revoked\" : undefined));\n } else resubscribe(chan, frame.why === \"gap\" || frame.why === \"reset\");\n return;\n }\n case \"msg\": {\n const chan = chans.get(frame.c);\n if (chan === undefined || frame.s <= chan.last) return;\n chan.last = frame.s;\n const meta: MessageMeta = {\n seq: frame.s,\n replayed: frame.r === 1,\n ...(frame.p === undefined ? {} : { publisher: frame.p }),\n ...(frame.u === undefined ? {} : { user: frame.u }),\n };\n for (const handler of [...(chan.ev.get(frame.e) ?? [])])\n guard(() => handler(frame.d, meta));\n for (const handler of [...chan.any])\n guard(() => handler({ event: frame.e, data: frame.d, meta } as AnyMessage<ChannelDef>));\n return;\n }\n case \"ack\": {\n const op = take(frame.n);\n if (op !== undefined)\n finish(op, undefined, { seq: frame.s, duplicate: frame.dup === true });\n return;\n }\n case \"rep\": {\n const op = take(frame.n);\n if (op !== undefined) finish(op, undefined, frame.d);\n return;\n }\n case \"err\": {\n const error = new RealtimeError(frame.code, frame.msg);\n const chan = frame.c === undefined ? undefined : chans.get(frame.c);\n if (frame.n !== undefined) {\n if (!viaPost && chan?.w?.n === frame.n) {\n if (TRANSIENT.has(frame.code)) {\n resubscribe(chan, false);\n report(error);\n } else {\n chan.w = undefined;\n closeChan(chan, error);\n }\n return;\n }\n // Over SSE a number on the stream names a subscription; only a POST answer names an operation.\n const op = viaPost || mode === \"ws\" ? take(frame.n) : undefined;\n if (op !== undefined) {\n finish(op, error);\n return;\n }\n }\n report(error);\n return;\n }\n case \"pres\": {\n const chan = chans.get(frame.c);\n if (chan === undefined) return;\n if (applyPresence(chan.members, frame.op, frame.m)) {\n chan.list = [...chan.members.values()];\n emit(chan.presence, chan.list);\n }\n if (frame.op === \"sync\" && chan.w !== undefined) {\n chan.w.ready = true;\n if (chan.w.join && chan.w.ver !== chan.sv) sendPresence(chan);\n }\n return;\n }\n case \"pong\":\n return;\n }\n };\n\n // --- sending ------------------------------------------------------------------------------------------------------\n\n const flush = (): void => {\n if (!ready || finished) return;\n syncAll();\n if (mode === \"sse\") {\n postNext();\n return;\n }\n for (let op = queue[0]; op !== undefined && ready; op = queue[0]) {\n const text = encode(op);\n if (text !== undefined) {\n if (link?.send(text) !== true) return;\n sent(op);\n }\n queue.shift();\n }\n };\n\n const postNext = (): void => {\n if (posting || postTimer !== undefined || connId === undefined) return;\n const ops: Op[] = [];\n const texts: string[] = [];\n let bytes = 0;\n while (queue.length > 0 && ops.length < MAX_FRAMES_PER_POST && bytes < MAX_POST_BYTES) {\n const op = queue.shift();\n if (op === undefined) break;\n const text = encode(op);\n if (text === undefined) continue;\n ops.push(op);\n texts.push(text);\n bytes += text.length;\n }\n if (ops.length === 0) return;\n posting = true;\n postBatch(ops, `{\"cid\":${JSON.stringify(connId)},\"frames\":[${texts.join(\",\")}]}`).then(\n undefined,\n report,\n );\n };\n\n const postBatch = async (ops: Op[], body: string): Promise<void> => {\n for (const op of ops) sent(op);\n let again = false;\n // A request that never answers would hold `posting` for as long as the platform waits (minutes) and nothing behind it\n // could be sent: it is abandoned when the longest wait of its operations is over, and its operations are retried.\n const mine = new AbortController();\n batch = mine;\n const patience = setTimeout(() => mine.abort(), Math.max(...ops.map(waitOf)));\n try {\n const headers = new Headers(await getHeaders());\n headers.set(\"content-type\", \"application/json\");\n const url =\n options.postUrl === undefined\n ? sendUrl(resolveUrl(options.url, false))\n : resolveUrl(options.postUrl, false);\n const response = await doFetch(url, { method: \"POST\", headers, body, signal: mine.signal });\n if (response.ok) {\n const parsed = (await response.json()) as { frames?: unknown };\n if (Array.isArray(parsed.frames))\n for (const item of parsed.frames as unknown[]) {\n const frame = serverFrameOf(item);\n if (frame !== undefined) guard(() => onFrame(frame, true));\n }\n postFails = 0;\n } else if (response.status >= 500 || RETRYABLE_STATUS.has(response.status)) again = true;\n else {\n const refusal = (await response.json().catch(() => undefined)) as\n | { error?: { code?: ErrorCode; message?: string } }\n | undefined;\n const code: ErrorCode = refusal?.error?.code ?? \"unavailable\";\n for (const op of ops) {\n if (inflight.get(op.n) === op) inflight.delete(op.n);\n finish(op, new RealtimeError(code, refusal?.error?.message));\n }\n }\n } catch {\n if (finished) return;\n again = true;\n } finally {\n clearTimeout(patience);\n }\n posting = false;\n const unanswered = ops.filter((op) => answered(op) && inflight.get(op.n) === op);\n for (const op of unanswered) inflight.delete(op.n);\n if (again) {\n for (const op of unanswered)\n if (op.kind === \"req\") finish(op, new RealtimeError(\"unavailable\"));\n requeue(\n ops.filter(\n (op) => op.kind === \"pres\" || (op.kind === \"pub\" && (!op.ack || unanswered.includes(op))),\n ),\n );\n postFails += 1;\n postTimer = setTimeout(\n () => {\n postTimer = undefined;\n flush();\n },\n backoff(postFails - 1, minDelay, maxDelay, random),\n );\n } else for (const op of unanswered) finish(op, new RealtimeError(\"unavailable\"));\n flush();\n };\n\n // --- lifecycle ----------------------------------------------------------------------------------------------------\n\n const run = (): void => {\n scheduled = false;\n if (finished) return;\n syncAll();\n if (link === undefined) {\n if (reconnectTimer === undefined && (queue.length > 0 || [...chans.values()].some(wants)))\n connect();\n return;\n }\n if (\n mode === \"sse\" &&\n (signature() !== streamSig ||\n [...chans.values()].some((c) => wants(c) && c.w === undefined && c.retry === undefined))\n ) {\n drop();\n for (const chan of chans.values()) chan.w = undefined;\n connect();\n } else flush();\n };\n\n function schedule(): void {\n if (scheduled || finished) return;\n scheduled = true;\n queueMicrotask(run);\n }\n\n function shutdown(error?: RealtimeError): void {\n if (finished) return;\n finished = true;\n clearTimeout(reconnectTimer);\n clearTimeout(postTimer);\n reconnectTimer = undefined;\n postTimer = undefined;\n drop();\n batch?.abort();\n if (listening) {\n if (typeof removeEventListener === \"function\") removeEventListener(\"online\", onOnline);\n if (typeof document !== \"undefined\")\n document.removeEventListener(\"visibilitychange\", onVisible);\n listening = false;\n }\n const closed = error ?? new RealtimeError(\"closed\");\n const pending = [...queue.splice(0), ...inflight.values()];\n inflight.clear();\n for (const op of pending) {\n if (op.settle === undefined) clearTimeout(op.timer);\n else finish(op, closed);\n }\n for (const chan of [...chans.values()]) closeChan(chan);\n chans.clear();\n dirty.clear();\n setStatus(\"closed\");\n statusListeners.clear();\n if (error !== undefined) report(error);\n }\n\n // --- the public handles -------------------------------------------------------------------------------------------\n\n const makeChannel = (name: string, channelOptions: ChannelOptions): Channel<ChannelDef> => {\n const chan: Chan = {\n name,\n options: channelOptions,\n ev: new Map(),\n any: new Set(),\n gl: new Set(),\n sl: new Set(),\n presence: new Set(),\n members: new Map(),\n status: \"idle\",\n refs: 0,\n hd: 0,\n last: 0,\n gh: 0,\n sy: false,\n state: channelOptions.presence,\n sv: 0,\n fails: 0,\n closed: false,\n };\n chans.set(name, chan);\n if (wants(chan)) touch(chan);\n\n /** Registers a listener; unless `passive`, it keeps the channel subscribed until removed. */\n const listen = <T>(\n set: Set<T>,\n item: T,\n passive?: boolean,\n after?: () => void,\n ): (() => void) => {\n if (chan.closed) return () => undefined;\n // A set holds a function once, like `addEventListener`: only the registration that added it counts a hold and may\n // remove it, so adding the same function twice never leaves a hold that nothing can release.\n let owns = !set.has(item);\n set.add(item);\n if (!passive && owns) {\n chan.refs += 1;\n touch(chan);\n }\n return () => {\n if (!owns) return;\n owns = false;\n // The channel may have been closed (its sets cleared and its holds reset) since this was registered.\n if (!set.delete(item) || passive) return;\n chan.refs -= 1;\n after?.();\n touch(chan);\n };\n };\n\n /** Validates and queues a publish or a request; the promise settles with its answer. */\n const submit = <T>(\n kind: \"pub\" | \"req\",\n event: string,\n data: unknown,\n key: string | undefined,\n ack: boolean,\n ms: number,\n ): Promise<T> => {\n let text = \"null\";\n let refusal: RealtimeError | undefined;\n if (chan.closed) refusal = new RealtimeError(\"closed\");\n else if (!isEventName(event) || (key !== undefined && !isIdempotencyKey(key)))\n refusal = new RealtimeError(\"bad_frame\");\n else\n try {\n text = JSON.stringify(data ?? null) ?? \"null\";\n } catch (cause) {\n refusal = new RealtimeError(\"bad_frame\", __DEV__ ? \"not JSON\" : undefined, { cause });\n }\n if (refusal !== undefined) return Promise.reject(refusal);\n return new Promise<T>((resolve, reject) => {\n const request = kind === \"req\";\n const op = newOp(kind, chan, event, text, {\n key: request ? undefined : (key ?? `${id}:${++keys}`),\n ack,\n ms,\n settle: ack\n ? (error, value) => {\n if (request) {\n chan.hd -= 1;\n touch(chan);\n }\n if (error) reject(error);\n else resolve(value as T);\n }\n : undefined,\n });\n if (request) {\n chan.hd += 1;\n touch(chan);\n }\n const refused = enqueue(op);\n if (refused !== undefined) {\n op.settle = undefined;\n if (request) chan.hd -= 1;\n reject(refused);\n } else if (!ack) resolve({ seq: 0, duplicate: false } as T);\n });\n };\n\n const hn: Channel<ChannelDef> = {\n name,\n get status() {\n return chan.status;\n },\n get lastSeq() {\n return chan.last;\n },\n on(event, handler) {\n let set = chan.ev.get(event);\n if (set === undefined) {\n set = new Set();\n chan.ev.set(event, set);\n }\n const handlers = set;\n return listen(handlers, handler as Handler, false, () => {\n if (handlers.size === 0 && chan.ev.get(event) === handlers) chan.ev.delete(event);\n });\n },\n onAny: (handler) => listen(chan.any, handler as (message: AnyMessage<ChannelDef>) => void),\n onGap: (handler) => listen(chan.gl, handler),\n onStatus: (handler) => listen(chan.sl, handler, true),\n publish: (event, data, publishOptions = {}) =>\n submit<PublishAck>(\"pub\", event, data, publishOptions.key, publishOptions.ack !== false, 0),\n request: (event, data, requestOptions = {}) =>\n submit(\"req\", event, data, undefined, true, requestOptions.timeoutMs ?? DEFAULT_REQUEST_MS),\n presence: {\n members: () => {\n chan.list ??= [...chan.members.values()];\n return chan.list as never;\n },\n set(state) {\n if (chan.closed) return;\n chan.state = state as Json;\n chan.sv += 1;\n touch(chan);\n },\n leave() {\n if (chan.closed || chan.state === undefined) return;\n chan.state = undefined;\n chan.sv += 1;\n touch(chan);\n },\n on: (callback) => listen(chan.presence, callback as (members: readonly Member[]) => void),\n },\n close: () => closeChan(chan),\n };\n chan.hn = hn;\n return hn;\n };\n\n return {\n channel(name, channelOptions = {}) {\n if (finished) throw new RealtimeError(\"closed\");\n if (!isChannelName(name)) throw new RealtimeError(\"bad_channel\", name);\n const live = chans.get(name);\n return (live?.hn ?? makeChannel(name, channelOptions)) as never;\n },\n get status() {\n return status;\n },\n get connectionId() {\n return connId;\n },\n onStatus(handler) {\n if (!finished) statusListeners.add(handler);\n return () => {\n statusListeners.delete(handler);\n };\n },\n close: () => shutdown(),\n };\n}\n"],"mappings":"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"}
@@ -87,7 +87,7 @@ export interface HubCore {
87
87
  publish(channel: string, event: string, data: unknown, options?: {
88
88
  readonly key?: string;
89
89
  }): Promise<PublishResult>;
90
- /** Removes matching subscriptions; each subscriber gets `unsubbed why:"revoked"`. Returns how many were removed. */
90
+ /** Removes matching subscriptions (a subscribe still awaiting its rule is refused); each subscriber gets `unsubbed why:"revoked"`. Returns how many were removed. */
91
91
  revoke(target: RevokeTarget): number;
92
92
  /** Counters. */
93
93
  stats(): HubStats;
@@ -50,6 +50,10 @@ function createHubCore(options = {}) {
50
50
  const rules = [];
51
51
  const conns = /* @__PURE__ */ new Map();
52
52
  const chans = /* @__PURE__ */ new Map();
53
+ /** Subscribes whose rules are being awaited: a revoke issued meanwhile marks them, so the grant cannot outlive it. */
54
+ const judging = /* @__PURE__ */ new Set();
55
+ /** The tail of each channel's presence joins that are queued or running; an entry leaves when its chain is idle. */
56
+ const joinChains = /* @__PURE__ */ new Map();
53
57
  let heartbeat;
54
58
  let closing = false;
55
59
  let beating = false;
@@ -330,9 +334,19 @@ function createHubCore(options = {}) {
330
334
  if (conn.subs.has(name)) return fail(conn, n, "conflict", name, "already subscribed");
331
335
  if (conn.subs.size >= limits.maxChannelsPerConnection) return fail(conn, n, "limit", name, "too many channels on this connection");
332
336
  const found = match(name);
333
- if (found === void 0 || !await allowed(found.rule, "subscribe", name, found.params, conn.identity)) return fail(conn, n, identityDenied(conn), name);
337
+ if (found === void 0) return fail(conn, n, identityDenied(conn), name);
334
338
  const wantsPresence = frame.hasPresence || frame.watch === true;
335
- if (wantsPresence && !await allowed(found.rule, "presence", name, found.params, conn.identity)) return fail(conn, n, identityDenied(conn), name);
339
+ const pending = {
340
+ conn,
341
+ channel: name,
342
+ revoked: false
343
+ };
344
+ judging.add(pending);
345
+ try {
346
+ if (!await allowed(found.rule, "subscribe", name, found.params, conn.identity) || wantsPresence && !await allowed(found.rule, "presence", name, found.params, conn.identity) || pending.revoked) return fail(conn, n, identityDenied(conn), name);
347
+ } finally {
348
+ judging.delete(pending);
349
+ }
336
350
  if (frame.hasPresence && byteLength(JSON.stringify(frame.presence ?? null)) > limits.maxStateBytes) return fail(conn, n, "too_large", name, "the presence state is too large");
337
351
  if (conn.closed) return;
338
352
  const chan = channelState(name);
@@ -407,19 +421,35 @@ function createHubCore(options = {}) {
407
421
  }
408
422
  }
409
423
  }
410
- async function joinPresence(sub, state) {
424
+ /** Joins one at a time per channel: the capacity check reads the set, so two joins in flight would both see room. */
425
+ function joinPresence(sub, state) {
426
+ const name = sub.channel.name;
427
+ const run = (joinChains.get(name) ?? Promise.resolve()).then(() => joinOne(sub, state));
428
+ const settle = () => {
429
+ if (joinChains.get(name) === tail) joinChains.delete(name);
430
+ };
431
+ const tail = run.then(settle, settle);
432
+ joinChains.set(name, tail);
433
+ return run;
434
+ }
435
+ async function joinOne(sub, state) {
436
+ if (!sub.active) return;
411
437
  if ((await adapter.presenceList(sub.channel.name)).length >= limits.maxPresenceMembers) throw new RealtimeError("limit", "the presence set is full");
412
438
  sub.version = 1;
413
439
  sub.state = state;
414
440
  sub.joined = true;
415
441
  startHeartbeat();
416
- const member = {
442
+ await adapter.presenceSet(sub.channel.name, memberOf(sub), limits.presenceTtlMs);
443
+ }
444
+ /** The presence member a joined subscription stands for. */
445
+ function memberOf(sub) {
446
+ const { user } = sub.conn.identity;
447
+ return {
417
448
  id: sub.conn.id,
418
- ...sub.conn.identity.user === void 0 ? {} : { u: sub.conn.identity.user },
419
- s: state,
420
- v: 1
449
+ ...user === void 0 ? {} : { u: user },
450
+ s: sub.state,
451
+ v: sub.version
421
452
  };
422
- await adapter.presenceSet(sub.channel.name, member, limits.presenceTtlMs);
423
453
  }
424
454
  async function syncPresence(sub) {
425
455
  const members = await adapter.presenceList(sub.channel.name);
@@ -446,14 +476,8 @@ function createHubCore(options = {}) {
446
476
  if (!conn.publishBucket.take(now())) return limit(conn, void 0, frame.c);
447
477
  sub.version += 1;
448
478
  sub.state = frame.s;
449
- const member = {
450
- id: conn.id,
451
- ...conn.identity.user === void 0 ? {} : { u: conn.identity.user },
452
- s: frame.s,
453
- v: sub.version
454
- };
455
479
  try {
456
- await adapter.presenceSet(frame.c, member, limits.presenceTtlMs);
480
+ await adapter.presenceSet(frame.c, memberOf(sub), limits.presenceTtlMs);
457
481
  } catch (error) {
458
482
  report(error, {
459
483
  where: "presenceSet",
@@ -483,7 +507,7 @@ function createHubCore(options = {}) {
483
507
  history: found.rule.history ?? limits.history,
484
508
  historyTtlMs: limits.historyTtlMs,
485
509
  idempotencyTtlMs: limits.idempotencyTtlMs,
486
- ...frame.k === void 0 ? {} : { key: frame.k }
510
+ ...frame.k === void 0 ? {} : { key: `${frame.k}/${conn.identity.user === void 0 ? "a" : `u:${conn.identity.user}`}` }
487
511
  });
488
512
  counters.published += result.duplicate ? 0 : 1;
489
513
  if (n !== void 0) sendFrame(conn, {
@@ -680,12 +704,7 @@ function createHubCore(options = {}) {
680
704
  for (const ref of missing) {
681
705
  const sub = conns.get(ref.id)?.subs.get(ref.channel);
682
706
  if (sub === void 0 || !sub.joined) continue;
683
- await adapter.presenceSet(ref.channel, {
684
- id: ref.id,
685
- ...sub.conn.identity.user === void 0 ? {} : { u: sub.conn.identity.user },
686
- s: sub.state,
687
- v: sub.version
688
- }, limits.presenceTtlMs);
707
+ await adapter.presenceSet(ref.channel, memberOf(sub), limits.presenceTtlMs);
689
708
  }
690
709
  for (const channel of watched) await adapter.presenceReap(channel);
691
710
  } catch (error) {
@@ -785,7 +804,7 @@ function createHubCore(options = {}) {
785
804
  history: found?.rule.history ?? limits.history,
786
805
  historyTtlMs: limits.historyTtlMs,
787
806
  idempotencyTtlMs: limits.idempotencyTtlMs,
788
- ...publishOptions.key === void 0 ? {} : { key: publishOptions.key }
807
+ ...publishOptions.key === void 0 ? {} : { key: `${publishOptions.key}/s` }
789
808
  });
790
809
  counters.published += result.duplicate ? 0 : 1;
791
810
  return result;
@@ -812,15 +831,17 @@ function createHubCore(options = {}) {
812
831
  },
813
832
  revoke(target) {
814
833
  if (target.user === void 0 && target.connection === void 0 && target.channel === void 0) throw new TypeError("revoke needs a user, a connection or a channel.");
834
+ const hit = (conn, channel) => (target.connection === void 0 || conn.id === target.connection) && (target.user === void 0 || conn.identity.user === target.user) && (target.channel === void 0 || channel === target.channel);
815
835
  let count = 0;
816
- for (const conn of [...conns.values()]) {
817
- if (target.connection !== void 0 && conn.id !== target.connection) continue;
818
- if (target.user !== void 0 && conn.identity.user !== target.user) continue;
819
- for (const sub of [...conn.subs.values()]) {
820
- if (target.channel !== void 0 && sub.channel.name !== target.channel) continue;
821
- removeSub(sub, "revoked");
822
- count += 1;
823
- }
836
+ for (const item of judging) {
837
+ if (item.revoked || !hit(item.conn, item.channel)) continue;
838
+ item.revoked = true;
839
+ count += 1;
840
+ }
841
+ for (const conn of [...conns.values()]) for (const sub of [...conn.subs.values()]) {
842
+ if (!hit(conn, sub.channel.name)) continue;
843
+ removeSub(sub, "revoked");
844
+ count += 1;
824
845
  }
825
846
  return count;
826
847
  },