@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 +23 -14
- package/dist/dev/client/core.js +3 -1
- package/dist/dev/client/core.js.map +1 -1
- package/dist/dev/server/hub.d.ts +1 -1
- package/dist/dev/server/hub.js +52 -31
- package/dist/dev/server/hub.js.map +1 -1
- package/dist/prod/client/core.js +1 -1
- package/dist/prod/client/core.js.map +1 -1
- package/dist/prod/server/hub.js +1 -1
- package/dist/prod/server/hub.js.map +1 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.js","names":["FATAL","TRANSIENT","RETRYABLE_STATUS","createRealtime","reconnect","options","minDelay","maxDelay","stableMs","random","maxQueued","queueTtl","ackTtl","auto","id","clientId","chans","dirty","queue","inflight","statusListeners","status","mode","finished","scheduled","link","gen","ready","connId","maxBytes","streamSig","byeCode","wsFails","attempt","counter","keys","orders","silent","connectTimer","reconnectTimer","stableTimer","beatTimer","posting","batch","postTimer","postFails","listening","report","error","guard","run","doFetch","input","init","getHeaders","emit","listener","listeners","value","setStatus","next","setChanStatus","chan","finish","op","settle","arm","at","RealtimeError","ms","take","n","enqueue","flowing","old","item","flush","schedule","newOp","extra","answered","waitOf","encode","c","wantsAnswer","text","byteLength","sent","requeue","ops","a","b","again","wants","joins","watches","touch","subscription","join","watch","after","sendPresence","w","goIdle","unsub","syncChan","want","current","syncAll","closeChan","resubscribe","now","backoff","signature","sig","connect","g","ev","onFrame","frame","linkClosed","info","onOnline","onVisible","url","resolveUrl","entries","openStream","openSocket","shutdown","drop","wasReady","code","lost","refused","delay","beat","gap","meta","handler","viaPost","applyPresence","postNext","texts","bytes","postBatch","mine","patience","headers","sendUrl","response","parsed","serverFrameOf","refusal","unanswered","closed","pending","makeChannel","channelOptions","name","listen","owns","set","passive","submit","isEventName","event","key","isIdempotencyKey","data","cause","request","kind","ack","reject","resolve","hn","handlers","publishOptions","requestOptions","state","callback","isChannelName"],"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":"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"}
|
|
1
|
+
{"version":3,"file":"core.js","names":["FATAL","TRANSIENT","RETRYABLE_STATUS","createRealtime","reconnect","options","minDelay","maxDelay","stableMs","random","maxQueued","queueTtl","ackTtl","auto","id","clientId","chans","dirty","queue","inflight","statusListeners","status","mode","finished","scheduled","link","gen","ready","connId","maxBytes","streamSig","byeCode","wsFails","attempt","counter","keys","orders","silent","connectTimer","reconnectTimer","stableTimer","beatTimer","posting","batch","postTimer","postFails","listening","report","error","guard","run","doFetch","input","init","getHeaders","emit","listener","listeners","value","setStatus","next","setChanStatus","chan","finish","op","settle","arm","at","RealtimeError","ms","take","n","enqueue","flowing","old","item","flush","schedule","newOp","extra","answered","waitOf","encode","c","wantsAnswer","text","byteLength","sent","requeue","ops","a","b","again","wants","joins","watches","touch","subscription","join","watch","after","sendPresence","w","goIdle","unsub","syncChan","want","current","syncAll","closeChan","resubscribe","now","backoff","signature","sig","connect","g","ev","onFrame","frame","linkClosed","info","onOnline","onVisible","url","resolveUrl","entries","openStream","openSocket","shutdown","drop","wasReady","code","lost","refused","delay","beat","gap","meta","handler","viaPost","applyPresence","postNext","texts","bytes","postBatch","mine","patience","headers","sendUrl","response","parsed","serverFrameOf","refusal","unanswered","closed","pending","makeChannel","channelOptions","name","listen","owns","set","passive","submit","isEventName","event","key","isIdempotencyKey","data","cause","request","kind","ack","reject","resolve","hn","handlers","publishOptions","requestOptions","state","callback","isChannelName"],"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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"}
|
package/dist/prod/server/hub.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{RealtimeError as e}from"../errors.js";import{byteLength as t,decodeClientFrame as n,isChannelName as r,isEventName as i,isIdempotencyKey as a}from"../protocol.js";import{TokenBucket as o,WindowCounter as s,resolveLimits as c}from"./limits.js";import{memoryAdapter as l}from"../memory.js";import{compileRule as u,matchRule as d}from"./rules.js";function f(){let e=crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));return btoa(String.fromCharCode(...e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function p(e){return JSON.stringify(e)}function m(e,t,n){return`{"t":"msg","c":${JSON.stringify(e)},"s":${t.seq},"e":${JSON.stringify(t.event)},"d":${t.data}`+(t.publisher===void 0?``:`,"p":${JSON.stringify(t.publisher)}`)+(t.user===void 0?``:`,"u":${JSON.stringify(t.user)}`)+(n?`,"r":1}`:`}`)}function h(h={}){let g=c(h.limits),_=h.now??Date.now,v=h.adapter===void 0,y=h.adapter??l({now:_,onError:(e,t)=>D(e,{where:`memory.${t.where}`,channel:t.channel})}),b=[],x=/* @__PURE__ */ new Map,S=/* @__PURE__ */ new Map,C,w=!1,T=!1,E={published:0,delivered:0,refused:0,slowConsumers:0},D=(e,t)=>{if(h.onError!==void 0)try{h.onError(e,t)}catch{}else console.error(`[realtime] ${t.where}`,e)};function O(e){for(let t of b){let n=d(t,e);if(n!==void 0)return{rule:t.rule,params:n}}}async function k(t,n,r,i,a){let o=t[n];if(o===void 0||o===!1)return!1;if(o===!0)return!0;try{return await o({channel:r,params:i,identity:a,op:n})===!0}catch(t){throw D(t,{where:`rule.${n}`,channel:r}),new e(`unavailable`,void 0)}}function A(e,t){if(!e.closed){try{if((e.transport.buffered?.()??0)>g.maxBufferedBytes){E.slowConsumers+=1,P(e,1013,``);return}if(e.transport.send(t))return}catch(t){D(t,{where:`transport.send`,connection:e.id}),j(e,`transport.close`,()=>e.transport.close(1011,``))}Z(e)}}function j(e,t,n){try{n()}catch(n){D(n,{where:t,connection:e.id})}}function M(e,t){A(e,p(t))}function N(e,t,n,r,i){E.refused+=1,M(e,{t:`err`,...t===void 0?{}:{n:t},...r===void 0?{}:{c:r},code:n,...i===void 0||i===``?{}:{msg:i}})}function P(e,t,n){e.closed||(j(e,`transport.send`,()=>e.transport.send(p({t:`bye`,code:t,why:n}))),j(e,`transport.close`,()=>e.transport.close(t,n)),Z(e))}function F(e){let t=S.get(e);return(t===void 0||t.closing)&&(t={name:e,subs:/* @__PURE__ */ new Set,bucket:new o(g.channelPublishesPerSecond,g.channelPublishesPerSecond,_()),adapterSub:void 0,presenceSub:void 0,closing:!1,frame:void 0},S.set(e,t)),t}async function I(e,t,n){if(e[t]===void 0){let r=n();e[t]=r,r.catch(()=>{e[t]=void 0})}await e[t]}function L(e,t){for(let n of e.subs)n.active&&(n.pumping?n.buffer.length>=1e4?H(n,`gap`):n.buffer.push(t):t.seq===n.lastSeq+1?R(n,t,!1):t.seq>n.lastSeq+1?(n.buffer.push(t),z(n,void 0)):t.at>n.lastAt&&H(n,`reset`))}function R(e,t,n){if(e.lastSeq=t.seq,e.lastAt=Math.max(e.lastAt,t.at),!e.echo&&t.publisher===e.conn.id)return;if(E.delivered+=1,n){A(e.conn,m(e.channel.name,t,!0));return}let r=e.channel.frame,i=r!==void 0&&r.seq===t.seq?r.text:m(e.channel.name,t,!1);(r===void 0||r.seq!==t.seq)&&(e.channel.frame={seq:t.seq,text:i}),A(e.conn,i)}async function z(e,t){if(!(e.pumping&&t===void 0)){e.pumping=!0;try{for(;e.active;){e.buffer=e.buffer.filter(t=>t.seq>e.lastSeq);let n=e.buffer[0];if(n!==void 0&&n.seq===e.lastSeq+1){R(e,n,!1),e.buffer.shift();continue}let r=n===void 0?t??0:n.seq-1;if(e.lastSeq>=r)break;let i=await y.history(e.channel.name,e.lastSeq,Math.min(500,r-e.lastSeq));if(!e.active)return;let a=i.messages[0];if(a===void 0||a.seq!==e.lastSeq+1){if(!e.gapOk){H(e,`gap`);return}e.lastSeq=(a?.seq??r+1)-1}else for(let t of i.messages){if(t.seq>r)break;R(e,t,!0)}}}catch(t){D(t,{where:`pump`,channel:e.channel.name,connection:e.conn.id}),e.active&&H(e,`unavailable`)}finally{e.pumping=!1}e.active&&e.buffer.length>0&&z(e,void 0)}}function B(e,t){for(let n of e.subs)n.joined&&t.op!==`leave`&&t.member.id===n.conn.id&&t.member.v>n.version&&(n.version=t.member.v,n.state=t.member.s),n.active&&(n.joined||n.watch)&&(n.presenceQueue===void 0?V(n,t):n.presenceQueue.push(t))}function V(e,t){M(e.conn,{t:`pres`,c:t.channel,op:t.op,m:t.member})}function H(e,t){e.active&&(e.active=!1,e.conn.subs.delete(e.channel.name),e.channel.subs.delete(e),t!==void 0&&!e.conn.closed&&M(e.conn,{t:`unsubbed`,c:e.channel.name,...t===``?{}:{why:t}}),W(e))}let U=/* @__PURE__ */ new Set;function W(e){let t=G(e);return U.add(t),t.finally(()=>U.delete(t)),t}async function G(e){let t=e.channel;try{await e.joining?.catch(()=>void 0),e.joined&&(e.joined=!1,await y.presenceRemove(t.name,e.conn.id))}catch(n){D(n,{where:`presenceRemove`,channel:t.name,connection:e.conn.id})}if(t.subs.size>0||t.closing)return;t.closing=!0,S.get(t.name)===t&&S.delete(t.name);let n=[t.adapterSub,t.presenceSub].filter(e=>e!==void 0);await Promise.all(n.map(async e=>{try{await(await e)()}catch(e){D(e,{where:`unsubscribe`,channel:t.name})}}))}function K(e){return e.identity.user===void 0?`unauthorized`:`forbidden`}async function q(n,r){let{c:i,n:a}=r;if(n.subs.has(i))return N(n,a,`conflict`,i,void 0);if(n.subs.size>=g.maxChannelsPerConnection)return N(n,a,`limit`,i,void 0);let o=O(i);if(o===void 0||!await k(o.rule,`subscribe`,i,o.params,n.identity))return N(n,a,K(n),i);let s=r.hasPresence||r.watch===!0;if(s&&!await k(o.rule,`presence`,i,o.params,n.identity))return N(n,a,K(n),i);if(r.hasPresence&&t(JSON.stringify(r.presence??null))>g.maxStateBytes)return N(n,a,`too_large`,i,void 0);if(n.closed)return;let c=F(i);if(c.subs.size>=g.maxSubscribersPerChannel)return N(n,a,`limit`,i,void 0);let l={conn:n,channel:c,rule:o.rule,params:o.params,echo:r.echo!==!1,watch:r.watch===!0,joined:!1,version:0,state:null,lastSeq:0,lastAt:_(),pumping:!0,gapOk:!1,buffer:[],presenceQueue:s?[]:void 0,joining:void 0,active:!0};n.subs.set(i,l),c.subs.add(l);try{await I(c,`adapterSub`,()=>y.subscribe(c.name,e=>L(c,e))),s&&(await I(c,`presenceSub`,()=>y.presenceSubscribe(c.name,e=>B(c,e))),Q());let e=await y.history(i,2**53-1,0);if(!l.active)return;let t=e.head,o=r.after,u=!1,d=!1;if(o===void 0?l.lastSeq=t:o>t?(d=!0,l.lastSeq=t):(l.lastSeq=o,u=e.oldest===0?t>o:e.oldest>o+1,l.gapOk=u),M(n,{t:`subbed`,n:a,c:i,head:t,...u?{gap:!0}:{},...d?{reset:!0}:{}}),r.hasPresence&&(l.joining=ee(l,r.presence??null),await l.joining),s&&await te(l),!l.active)return;await z(l,t),l.gapOk=!1}catch(t){l.active&&(H(l),D(t,{where:`subscribe`,channel:i,connection:n.id}),N(n,a,t instanceof e?t.code:`unavailable`,i))}}async function ee(t,n){if((await y.presenceList(t.channel.name)).length>=g.maxPresenceMembers)throw new e(`limit`,void 0);t.version=1,t.state=n,t.joined=!0,Q();let r={id:t.conn.id,...t.conn.identity.user===void 0?{}:{u:t.conn.identity.user},s:n,v:1};await y.presenceSet(t.channel.name,r,g.presenceTtlMs)}async function te(e){let t=await y.presenceList(e.channel.name);if(!e.active)return;M(e.conn,{t:`pres`,c:e.channel.name,op:`sync`,m:t});let n=e.presenceQueue??[];e.presenceQueue=void 0;let r=new Map(t.map(e=>[e.id,e.v]));for(let t of n){let n=r.get(t.member.id);(t.op===`leave`?n===void 0:n!==void 0&&n>=t.member.v)||V(e,t)}}async function ne(e,n){let r=e.subs.get(n.c);if(r===void 0||!r.joined)return N(e,void 0,`not_subscribed`,n.c);if(t(JSON.stringify(n.s))>g.maxStateBytes)return N(e,void 0,`too_large`,n.c,void 0);if(!e.publishBucket.take(_()))return J(e,void 0,n.c);r.version+=1,r.state=n.s;let i={id:e.id,...e.identity.user===void 0?{}:{u:e.identity.user},s:n.s,v:r.version};try{await y.presenceSet(n.c,i,g.presenceTtlMs)}catch(t){D(t,{where:`presenceSet`,channel:n.c,connection:e.id}),N(e,void 0,`unavailable`,n.c)}}function J(e,t,n){N(e,t,`rate_limited`,n),e.limited.hit(_())&&P(e,1008,``)}async function re(e,t){let{c:n,n:r}=t,i=O(n);if(i===void 0||!await k(i.rule,`publish`,n,i.params,e.identity))return N(e,r,K(e),n);if(t.d.length>g.maxMessageBytes)return N(e,r,`too_large`,n,void 0);if(!e.publishBucket.take(_())||S.get(n)?.bucket.take(_())===!1)return J(e,r,n);try{let a=await y.publish(n,{event:t.e,data:t.d,publisher:e.id,...e.identity.user===void 0?{}:{user:e.identity.user}},{history:i.rule.history??g.history,historyTtlMs:g.historyTtlMs,idempotencyTtlMs:g.idempotencyTtlMs,...t.k===void 0?{}:{key:t.k}});E.published+=+!a.duplicate,r!==void 0&&M(e,{t:`ack`,n:r,s:a.seq,...a.duplicate?{dup:!0}:{}})}catch(t){D(t,{where:`publish`,channel:n,connection:e.id}),N(e,r,`unavailable`,n)}}async function ie(n,r){let{c:i,n:a}=r,o=n.subs.get(i);if(o===void 0&&n.stateless){let e=O(i);e!==void 0&&await k(e.rule,`subscribe`,i,e.params,n.identity)&&(o=e)}if(o===void 0)return N(n,a,K(n)===`unauthorized`&&n.stateless?`unauthorized`:`not_subscribed`,i);let s=o,c=s.rule.handlers?.[r.e];if(c===void 0)return N(n,a,`no_handler`,i);if(n.pending.size>=g.maxPendingRequests)return N(n,a,`limit`,i,void 0);if(!n.publishBucket.take(_()))return J(n,a,i);let l=new AbortController;n.pending.add(l);let u=Math.min(r.ms??g.requestTimeoutMs,g.requestTimeoutMs),d=setTimeout(()=>l.abort(new e(`timeout`)),u);(async()=>{try{let e=Promise.resolve(c({channel:i,params:s.params,identity:n.identity,event:r.e,data:r.d,signal:l.signal})),o=new Promise((e,t)=>{l.signal.addEventListener(`abort`,()=>t(l.signal.reason),{once:!0})}),u=await Promise.race([e,o])??null,d=JSON.stringify(u);if(d===void 0||t(d)>g.maxMessageBytes)return N(n,a,`too_large`,i,void 0);A(n,`{"t":"rep","n":${a},"d":${d}}`)}catch(t){t instanceof e?N(n,a,t.code,i,t.code===`timeout`||t.code===`closed`?void 0:t.message):(D(t,{where:`request`,channel:i,connection:n.id}),N(n,a,`handler_error`,i))}finally{clearTimeout(d),n.pending.delete(l),X(n)}})()}function Y(e,t,n,r){N(e,t,n,void 0,r),e.invalid.hit(_())&&P(e,1008,``)}async function ae(t,r){let i=n(r,g.maxMessageBytes);if(!i.ok){Y(t,i.n,i.code,i.why);return}let a=i.frame,o=`n`in a?a.n:void 0;if((a.t===`sub`||a.t===`unsub`)&&!t.controlBucket.take(_())&&a.t===`sub`)return J(t,o,a.c);try{switch(a.t){case`hello`:return;case`ping`:M(t,{t:`pong`,...a.n===void 0?{}:{n:a.n}});return;case`sub`:return await q(t,a);case`unsub`:{let e=t.subs.get(a.c);if(e===void 0)return N(t,a.n,`not_subscribed`,a.c);H(e);return}case`pub`:return await re(t,a);case`req`:return await ie(t,a);case`pres`:return await ne(t,a)}}catch(n){let r=`c`in a?a.c:void 0;n instanceof e?N(t,o,n.code,r):(D(n,{where:`frame`,connection:t.id,...r===void 0?{}:{channel:r}}),N(t,o,`unavailable`,r))}}async function oe(t){if(!t.running){t.running=!0;try{for(let n=t.queue.shift();n!==void 0&&!t.closed;n=t.queue.shift())try{await ae(t,n)}catch(n){n instanceof e?N(t,void 0,n.code):(D(n,{where:`frame`,connection:t.id}),N(t,void 0,`unavailable`))}}finally{t.running=!1,X(t)}}}function X(e){if(!(e.running||e.queue.length>0||e.pending.size>0))for(let t of e.waiters.splice(0))t()}async function Z(t){if(t.closed)return t.done;t.closed=!0,x.get(t.id)===t&&x.delete(t.id);for(let n of t.pending)n.abort(new e(`closed`));t.pending.clear(),t.queue.length=0;for(let e of t.waiters.splice(0))e();let n=[...t.subs.values()];for(let e of n)e.active=!1,e.channel.subs.delete(e);t.subs.clear(),await Promise.allSettled(n.map(e=>W(e))),t.release?.()}function Q(){if(C!==void 0||w)return;let e=setInterval(()=>void se(),g.presenceHeartbeatMs);e.unref?.(),C=e}async function se(){if(!T){T=!0;try{await ce()}finally{T=!1}}}async function ce(){let e=[],t=/* @__PURE__ */ new Set;for(let n of x.values())for(let r of n.subs.values())r.joined&&e.push({channel:r.channel.name,id:n.id}),(r.joined||r.watch)&&t.add(r.channel.name);if(e.length===0&&t.size===0)clearInterval(C),C=void 0;else try{let n=await y.presenceTouch(e,g.presenceTtlMs);for(let e of n){let t=x.get(e.id)?.subs.get(e.channel);t!==void 0&&t.joined&&await y.presenceSet(e.channel,{id:e.id,...t.conn.identity.user===void 0?{}:{u:t.conn.identity.user},s:t.state,v:t.version},g.presenceTtlMs)}for(let e of t)await y.presenceReap(e)}catch(e){D(e,{where:`presenceHeartbeat`})}}for(let[e,t]of Object.entries(h.channels??{}))b.push(u(e,t));let $={limits:g,adapter:y,report:D,match:O,allowed:k,channel(e,t){return b.push(u(e,t)),$},connect(n,r={},i={}){if(w)throw new e(`closed`,void 0);let a=()=>void 0,c=new Promise(e=>{a=e}),l=i.actingFor===void 0?void 0:x.get(i.actingFor),u=l!==void 0&&r.user!==void 0&&l.identity.user===r.user,d={id:u?i.actingFor:f(),identity:r,transport:n,subs:/* @__PURE__ */ new Map,queue:[],publishBucket:i.publishBucket??new o(g.publishesPerSecond,g.publishBurst,_()),controlBucket:new o(g.publishesPerSecond,Math.max(g.publishBurst,g.maxChannelsPerConnection),_()),stateless:i.stateless===!0,waiters:[],invalid:new s(10,6e4),limited:new s(50,1e4),pending:/* @__PURE__ */ new Set,running:!1,closed:!1,release:a,done:c};return u||x.set(d.id,d),M(d,{t:`ready`,v:1,id:d.id,hb:g.heartbeatMs,max:g.maxMessageBytes}),{id:d.id,identity:r,done:c,receive(e){d.closed||(t(e)>g.maxMessageBytes?Y(d,void 0,`too_large`):d.queue.length>=g.maxQueuedFrames?P(d,1013,``):(d.queue.push(e),oe(d)))},closed(){Z(d)},settled(){return new Promise(e=>{d.waiters.push(e),X(d)})}}},async publish(n,o,s,c={}){if(!r(n))throw new e(`bad_channel`,void 0);if(!i(o)||c.key!==void 0&&!a(c.key))throw new e(`bad_frame`,void 0);let l;try{l=JSON.stringify(s===void 0?null:s)}catch(e){throw TypeError(`REALTIME_DATA`,{cause:e})}if(l===void 0)throw TypeError(`REALTIME_DATA`);if(t(l)>g.maxMessageBytes)throw new e(`too_large`,void 0);let u=O(n),d=await y.publish(n,{event:o,data:l},{history:u?.rule.history??g.history,historyTtlMs:g.historyTtlMs,idempotencyTtlMs:g.idempotencyTtlMs,...c.key===void 0?{}:{key:c.key}});return E.published+=+!d.duplicate,d},async setMemberState(n,r,i,a){if(n.user===void 0)throw new e(`unauthorized`,void 0);let o=O(r);if(o===void 0||!await k(o.rule,`presence`,r,o.params,n))throw new e(`forbidden`);if(t(JSON.stringify(a))>g.maxStateBytes)throw new e(`too_large`,void 0);let s=(await y.presenceList(r)).find(e=>e.id===i);if(s===void 0||s.u!==n.user)throw new e(`forbidden`,void 0);let c=x.get(i)?.subs.get(r),l=Math.max(s.v,c?.version??0)+1;c?.joined===!0&&(c.version=l,c.state=a),await y.presenceSet(r,{id:s.id,u:s.u,s:a,v:l},g.presenceTtlMs)},revoke(e){if(e.user===void 0&&e.connection===void 0&&e.channel===void 0)throw TypeError(`REALTIME_REVOKE`);let t=0;for(let n of[...x.values()])if((e.connection===void 0||n.id===e.connection)&&(e.user===void 0||n.identity.user===e.user))for(let r of[...n.subs.values()])(e.channel===void 0||r.channel.name===e.channel)&&(H(r,`revoked`),t+=1);return t},stats(){let e=0;for(let t of x.values())e+=t.subs.size;return{connections:x.size,channels:S.size,subscriptions:e,...E}},async close(){if(w)return;w=!0,C!==void 0&&clearInterval(C),C=void 0;let e=[...x.values()];for(let t of e)P(t,1001,``);await Promise.all(e.map(e=>e.done)),await Promise.allSettled([...U]),v&&await y.close()}};return $}export{h as createHubCore};
|
|
1
|
+
import{RealtimeError as e}from"../errors.js";import{byteLength as t,decodeClientFrame as n,isChannelName as r,isEventName as i,isIdempotencyKey as a}from"../protocol.js";import{TokenBucket as o,WindowCounter as s,resolveLimits as c}from"./limits.js";import{memoryAdapter as l}from"../memory.js";import{compileRule as u,matchRule as d}from"./rules.js";function ee(){let e=crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16));return btoa(String.fromCharCode(...e)).replace(/\+/g,`-`).replace(/\//g,`_`).replace(/=+$/,``)}function f(e){return JSON.stringify(e)}function p(e,t,n){return`{"t":"msg","c":${JSON.stringify(e)},"s":${t.seq},"e":${JSON.stringify(t.event)},"d":${t.data}`+(t.publisher===void 0?``:`,"p":${JSON.stringify(t.publisher)}`)+(t.user===void 0?``:`,"u":${JSON.stringify(t.user)}`)+(n?`,"r":1}`:`}`)}function m(m={}){let h=c(m.limits),g=m.now??Date.now,te=m.adapter===void 0,_=m.adapter??l({now:g,onError:(e,t)=>D(e,{where:`memory.${t.where}`,channel:t.channel})}),v=[],y=/* @__PURE__ */ new Map,b=/* @__PURE__ */ new Map,x=/* @__PURE__ */ new Set,S=/* @__PURE__ */ new Map,C,w=!1,T=!1,E={published:0,delivered:0,refused:0,slowConsumers:0},D=(e,t)=>{if(m.onError!==void 0)try{m.onError(e,t)}catch{}else console.error(`[realtime] ${t.where}`,e)};function O(e){for(let t of v){let n=d(t,e);if(n!==void 0)return{rule:t.rule,params:n}}}async function k(t,n,r,i,a){let o=t[n];if(o===void 0||o===!1)return!1;if(o===!0)return!0;try{return await o({channel:r,params:i,identity:a,op:n})===!0}catch(t){throw D(t,{where:`rule.${n}`,channel:r}),new e(`unavailable`,void 0)}}function A(e,t){if(!e.closed){try{if((e.transport.buffered?.()??0)>h.maxBufferedBytes){E.slowConsumers+=1,P(e,1013,``);return}if(e.transport.send(t))return}catch(t){D(t,{where:`transport.send`,connection:e.id}),j(e,`transport.close`,()=>e.transport.close(1011,``))}Z(e)}}function j(e,t,n){try{n()}catch(n){D(n,{where:t,connection:e.id})}}function M(e,t){A(e,f(t))}function N(e,t,n,r,i){E.refused+=1,M(e,{t:`err`,...t===void 0?{}:{n:t},...r===void 0?{}:{c:r},code:n,...i===void 0||i===``?{}:{msg:i}})}function P(e,t,n){e.closed||(j(e,`transport.send`,()=>e.transport.send(f({t:`bye`,code:t,why:n}))),j(e,`transport.close`,()=>e.transport.close(t,n)),Z(e))}function F(e){let t=b.get(e);return(t===void 0||t.closing)&&(t={name:e,subs:/* @__PURE__ */ new Set,bucket:new o(h.channelPublishesPerSecond,h.channelPublishesPerSecond,g()),adapterSub:void 0,presenceSub:void 0,closing:!1,frame:void 0},b.set(e,t)),t}async function I(e,t,n){if(e[t]===void 0){let r=n();e[t]=r,r.catch(()=>{e[t]=void 0})}await e[t]}function ne(e,t){for(let n of e.subs)n.active&&(n.pumping?n.buffer.length>=1e4?B(n,`gap`):n.buffer.push(t):t.seq===n.lastSeq+1?L(n,t,!1):t.seq>n.lastSeq+1?(n.buffer.push(t),R(n,void 0)):t.at>n.lastAt&&B(n,`reset`))}function L(e,t,n){if(e.lastSeq=t.seq,e.lastAt=Math.max(e.lastAt,t.at),!e.echo&&t.publisher===e.conn.id)return;if(E.delivered+=1,n){A(e.conn,p(e.channel.name,t,!0));return}let r=e.channel.frame,i=r!==void 0&&r.seq===t.seq?r.text:p(e.channel.name,t,!1);(r===void 0||r.seq!==t.seq)&&(e.channel.frame={seq:t.seq,text:i}),A(e.conn,i)}async function R(e,t){if(!(e.pumping&&t===void 0)){e.pumping=!0;try{for(;e.active;){e.buffer=e.buffer.filter(t=>t.seq>e.lastSeq);let n=e.buffer[0];if(n!==void 0&&n.seq===e.lastSeq+1){L(e,n,!1),e.buffer.shift();continue}let r=n===void 0?t??0:n.seq-1;if(e.lastSeq>=r)break;let i=await _.history(e.channel.name,e.lastSeq,Math.min(500,r-e.lastSeq));if(!e.active)return;let a=i.messages[0];if(a===void 0||a.seq!==e.lastSeq+1){if(!e.gapOk){B(e,`gap`);return}e.lastSeq=(a?.seq??r+1)-1}else for(let t of i.messages){if(t.seq>r)break;L(e,t,!0)}}}catch(t){D(t,{where:`pump`,channel:e.channel.name,connection:e.conn.id}),e.active&&B(e,`unavailable`)}finally{e.pumping=!1}e.active&&e.buffer.length>0&&R(e,void 0)}}function re(e,t){for(let n of e.subs)n.joined&&t.op!==`leave`&&t.member.id===n.conn.id&&t.member.v>n.version&&(n.version=t.member.v,n.state=t.member.s),n.active&&(n.joined||n.watch)&&(n.presenceQueue===void 0?z(n,t):n.presenceQueue.push(t))}function z(e,t){M(e.conn,{t:`pres`,c:t.channel,op:t.op,m:t.member})}function B(e,t){e.active&&(e.active=!1,e.conn.subs.delete(e.channel.name),e.channel.subs.delete(e),t!==void 0&&!e.conn.closed&&M(e.conn,{t:`unsubbed`,c:e.channel.name,...t===``?{}:{why:t}}),H(e))}let V=/* @__PURE__ */ new Set;function H(e){let t=ie(e);return V.add(t),t.finally(()=>V.delete(t)),t}async function ie(e){let t=e.channel;try{await e.joining?.catch(()=>void 0),e.joined&&(e.joined=!1,await _.presenceRemove(t.name,e.conn.id))}catch(n){D(n,{where:`presenceRemove`,channel:t.name,connection:e.conn.id})}if(t.subs.size>0||t.closing)return;t.closing=!0,b.get(t.name)===t&&b.delete(t.name);let n=[t.adapterSub,t.presenceSub].filter(e=>e!==void 0);await Promise.all(n.map(async e=>{try{await(await e)()}catch(e){D(e,{where:`unsubscribe`,channel:t.name})}}))}function U(e){return e.identity.user===void 0?`unauthorized`:`forbidden`}async function ae(n,r){let{c:i,n:a}=r;if(n.subs.has(i))return N(n,a,`conflict`,i,void 0);if(n.subs.size>=h.maxChannelsPerConnection)return N(n,a,`limit`,i,void 0);let o=O(i);if(o===void 0)return N(n,a,U(n),i);let s=r.hasPresence||r.watch===!0,c={conn:n,channel:i,revoked:!1};x.add(c);try{if(!await k(o.rule,`subscribe`,i,o.params,n.identity)||s&&!await k(o.rule,`presence`,i,o.params,n.identity)||c.revoked)return N(n,a,U(n),i)}finally{x.delete(c)}if(r.hasPresence&&t(JSON.stringify(r.presence??null))>h.maxStateBytes)return N(n,a,`too_large`,i,void 0);if(n.closed)return;let l=F(i);if(l.subs.size>=h.maxSubscribersPerChannel)return N(n,a,`limit`,i,void 0);let u={conn:n,channel:l,rule:o.rule,params:o.params,echo:r.echo!==!1,watch:r.watch===!0,joined:!1,version:0,state:null,lastSeq:0,lastAt:g(),pumping:!0,gapOk:!1,buffer:[],presenceQueue:s?[]:void 0,joining:void 0,active:!0};n.subs.set(i,u),l.subs.add(u);try{await I(l,`adapterSub`,()=>_.subscribe(l.name,e=>ne(l,e))),s&&(await I(l,`presenceSub`,()=>_.presenceSubscribe(l.name,e=>re(l,e))),Q());let e=await _.history(i,2**53-1,0);if(!u.active)return;let t=e.head,o=r.after,c=!1,d=!1;if(o===void 0?u.lastSeq=t:o>t?(d=!0,u.lastSeq=t):(u.lastSeq=o,c=e.oldest===0?t>o:e.oldest>o+1,u.gapOk=c),M(n,{t:`subbed`,n:a,c:i,head:t,...c?{gap:!0}:{},...d?{reset:!0}:{}}),r.hasPresence&&(u.joining=oe(u,r.presence??null),await u.joining),s&&await K(u),!u.active)return;await R(u,t),u.gapOk=!1}catch(t){u.active&&(B(u),D(t,{where:`subscribe`,channel:i,connection:n.id}),N(n,a,t instanceof e?t.code:`unavailable`,i))}}function oe(e,t){let n=e.channel.name,r=(S.get(n)??Promise.resolve()).then(()=>W(e,t)),i=()=>{S.get(n)===a&&S.delete(n)},a=r.then(i,i);return S.set(n,a),r}async function W(t,n){if(t.active){if((await _.presenceList(t.channel.name)).length>=h.maxPresenceMembers)throw new e(`limit`,void 0);t.version=1,t.state=n,t.joined=!0,Q(),await _.presenceSet(t.channel.name,G(t),h.presenceTtlMs)}}function G(e){let{user:t}=e.conn.identity;return{id:e.conn.id,...t===void 0?{}:{u:t},s:e.state,v:e.version}}async function K(e){let t=await _.presenceList(e.channel.name);if(!e.active)return;M(e.conn,{t:`pres`,c:e.channel.name,op:`sync`,m:t});let n=e.presenceQueue??[];e.presenceQueue=void 0;let r=new Map(t.map(e=>[e.id,e.v]));for(let t of n){let n=r.get(t.member.id);(t.op===`leave`?n===void 0:n!==void 0&&n>=t.member.v)||z(e,t)}}async function se(e,n){let r=e.subs.get(n.c);if(r===void 0||!r.joined)return N(e,void 0,`not_subscribed`,n.c);if(t(JSON.stringify(n.s))>h.maxStateBytes)return N(e,void 0,`too_large`,n.c,void 0);if(!e.publishBucket.take(g()))return q(e,void 0,n.c);r.version+=1,r.state=n.s;try{await _.presenceSet(n.c,G(r),h.presenceTtlMs)}catch(t){D(t,{where:`presenceSet`,channel:n.c,connection:e.id}),N(e,void 0,`unavailable`,n.c)}}function q(e,t,n){N(e,t,`rate_limited`,n),e.limited.hit(g())&&P(e,1008,``)}async function J(e,t){let{c:n,n:r}=t,i=O(n);if(i===void 0||!await k(i.rule,`publish`,n,i.params,e.identity))return N(e,r,U(e),n);if(t.d.length>h.maxMessageBytes)return N(e,r,`too_large`,n,void 0);if(!e.publishBucket.take(g())||b.get(n)?.bucket.take(g())===!1)return q(e,r,n);try{let a=await _.publish(n,{event:t.e,data:t.d,publisher:e.id,...e.identity.user===void 0?{}:{user:e.identity.user}},{history:i.rule.history??h.history,historyTtlMs:h.historyTtlMs,idempotencyTtlMs:h.idempotencyTtlMs,...t.k===void 0?{}:{key:`${t.k}/${e.identity.user===void 0?`a`:`u:${e.identity.user}`}`}});E.published+=+!a.duplicate,r!==void 0&&M(e,{t:`ack`,n:r,s:a.seq,...a.duplicate?{dup:!0}:{}})}catch(t){D(t,{where:`publish`,channel:n,connection:e.id}),N(e,r,`unavailable`,n)}}async function ce(n,r){let{c:i,n:a}=r,o=n.subs.get(i);if(o===void 0&&n.stateless){let e=O(i);e!==void 0&&await k(e.rule,`subscribe`,i,e.params,n.identity)&&(o=e)}if(o===void 0)return N(n,a,U(n)===`unauthorized`&&n.stateless?`unauthorized`:`not_subscribed`,i);let s=o,c=s.rule.handlers?.[r.e];if(c===void 0)return N(n,a,`no_handler`,i);if(n.pending.size>=h.maxPendingRequests)return N(n,a,`limit`,i,void 0);if(!n.publishBucket.take(g()))return q(n,a,i);let l=new AbortController;n.pending.add(l);let u=Math.min(r.ms??h.requestTimeoutMs,h.requestTimeoutMs),d=setTimeout(()=>l.abort(new e(`timeout`)),u);(async()=>{try{let e=Promise.resolve(c({channel:i,params:s.params,identity:n.identity,event:r.e,data:r.d,signal:l.signal})),o=new Promise((e,t)=>{l.signal.addEventListener(`abort`,()=>t(l.signal.reason),{once:!0})}),u=await Promise.race([e,o])??null,d=JSON.stringify(u);if(d===void 0||t(d)>h.maxMessageBytes)return N(n,a,`too_large`,i,void 0);A(n,`{"t":"rep","n":${a},"d":${d}}`)}catch(t){t instanceof e?N(n,a,t.code,i,t.code===`timeout`||t.code===`closed`?void 0:t.message):(D(t,{where:`request`,channel:i,connection:n.id}),N(n,a,`handler_error`,i))}finally{clearTimeout(d),n.pending.delete(l),X(n)}})()}function Y(e,t,n,r){N(e,t,n,void 0,r),e.invalid.hit(g())&&P(e,1008,``)}async function le(t,r){let i=n(r,h.maxMessageBytes);if(!i.ok){Y(t,i.n,i.code,i.why);return}let a=i.frame,o=`n`in a?a.n:void 0;if((a.t===`sub`||a.t===`unsub`)&&!t.controlBucket.take(g())&&a.t===`sub`)return q(t,o,a.c);try{switch(a.t){case`hello`:return;case`ping`:M(t,{t:`pong`,...a.n===void 0?{}:{n:a.n}});return;case`sub`:return await ae(t,a);case`unsub`:{let e=t.subs.get(a.c);if(e===void 0)return N(t,a.n,`not_subscribed`,a.c);B(e);return}case`pub`:return await J(t,a);case`req`:return await ce(t,a);case`pres`:return await se(t,a)}}catch(n){let r=`c`in a?a.c:void 0;n instanceof e?N(t,o,n.code,r):(D(n,{where:`frame`,connection:t.id,...r===void 0?{}:{channel:r}}),N(t,o,`unavailable`,r))}}async function ue(t){if(!t.running){t.running=!0;try{for(let n=t.queue.shift();n!==void 0&&!t.closed;n=t.queue.shift())try{await le(t,n)}catch(n){n instanceof e?N(t,void 0,n.code):(D(n,{where:`frame`,connection:t.id}),N(t,void 0,`unavailable`))}}finally{t.running=!1,X(t)}}}function X(e){if(!(e.running||e.queue.length>0||e.pending.size>0))for(let t of e.waiters.splice(0))t()}async function Z(t){if(t.closed)return t.done;t.closed=!0,y.get(t.id)===t&&y.delete(t.id);for(let n of t.pending)n.abort(new e(`closed`));t.pending.clear(),t.queue.length=0;for(let e of t.waiters.splice(0))e();let n=[...t.subs.values()];for(let e of n)e.active=!1,e.channel.subs.delete(e);t.subs.clear(),await Promise.allSettled(n.map(e=>H(e))),t.release?.()}function Q(){if(C!==void 0||w)return;let e=setInterval(()=>void de(),h.presenceHeartbeatMs);e.unref?.(),C=e}async function de(){if(!T){T=!0;try{await fe()}finally{T=!1}}}async function fe(){let e=[],t=/* @__PURE__ */ new Set;for(let n of y.values())for(let r of n.subs.values())r.joined&&e.push({channel:r.channel.name,id:n.id}),(r.joined||r.watch)&&t.add(r.channel.name);if(e.length===0&&t.size===0)clearInterval(C),C=void 0;else try{let n=await _.presenceTouch(e,h.presenceTtlMs);for(let e of n){let t=y.get(e.id)?.subs.get(e.channel);t!==void 0&&t.joined&&await _.presenceSet(e.channel,G(t),h.presenceTtlMs)}for(let e of t)await _.presenceReap(e)}catch(e){D(e,{where:`presenceHeartbeat`})}}for(let[e,t]of Object.entries(m.channels??{}))v.push(u(e,t));let $={limits:h,adapter:_,report:D,match:O,allowed:k,channel(e,t){return v.push(u(e,t)),$},connect(n,r={},i={}){if(w)throw new e(`closed`,void 0);let a=()=>void 0,c=new Promise(e=>{a=e}),l=i.actingFor===void 0?void 0:y.get(i.actingFor),u=l!==void 0&&r.user!==void 0&&l.identity.user===r.user,d={id:u?i.actingFor:ee(),identity:r,transport:n,subs:/* @__PURE__ */ new Map,queue:[],publishBucket:i.publishBucket??new o(h.publishesPerSecond,h.publishBurst,g()),controlBucket:new o(h.publishesPerSecond,Math.max(h.publishBurst,h.maxChannelsPerConnection),g()),stateless:i.stateless===!0,waiters:[],invalid:new s(10,6e4),limited:new s(50,1e4),pending:/* @__PURE__ */ new Set,running:!1,closed:!1,release:a,done:c};return u||y.set(d.id,d),M(d,{t:`ready`,v:1,id:d.id,hb:h.heartbeatMs,max:h.maxMessageBytes}),{id:d.id,identity:r,done:c,receive(e){d.closed||(t(e)>h.maxMessageBytes?Y(d,void 0,`too_large`):d.queue.length>=h.maxQueuedFrames?P(d,1013,``):(d.queue.push(e),ue(d)))},closed(){Z(d)},settled(){return new Promise(e=>{d.waiters.push(e),X(d)})}}},async publish(n,o,s,c={}){if(!r(n))throw new e(`bad_channel`,void 0);if(!i(o)||c.key!==void 0&&!a(c.key))throw new e(`bad_frame`,void 0);let l;try{l=JSON.stringify(s===void 0?null:s)}catch(e){throw TypeError(`REALTIME_DATA`,{cause:e})}if(l===void 0)throw TypeError(`REALTIME_DATA`);if(t(l)>h.maxMessageBytes)throw new e(`too_large`,void 0);let u=O(n),d=await _.publish(n,{event:o,data:l},{history:u?.rule.history??h.history,historyTtlMs:h.historyTtlMs,idempotencyTtlMs:h.idempotencyTtlMs,...c.key===void 0?{}:{key:`${c.key}/s`}});return E.published+=+!d.duplicate,d},async setMemberState(n,r,i,a){if(n.user===void 0)throw new e(`unauthorized`,void 0);let o=O(r);if(o===void 0||!await k(o.rule,`presence`,r,o.params,n))throw new e(`forbidden`);if(t(JSON.stringify(a))>h.maxStateBytes)throw new e(`too_large`,void 0);let s=(await _.presenceList(r)).find(e=>e.id===i);if(s===void 0||s.u!==n.user)throw new e(`forbidden`,void 0);let c=y.get(i)?.subs.get(r),l=Math.max(s.v,c?.version??0)+1;c?.joined===!0&&(c.version=l,c.state=a),await _.presenceSet(r,{id:s.id,u:s.u,s:a,v:l},h.presenceTtlMs)},revoke(e){if(e.user===void 0&&e.connection===void 0&&e.channel===void 0)throw TypeError(`REALTIME_REVOKE`);let t=(t,n)=>(e.connection===void 0||t.id===e.connection)&&(e.user===void 0||t.identity.user===e.user)&&(e.channel===void 0||n===e.channel),n=0;for(let e of x)!e.revoked&&t(e.conn,e.channel)&&(e.revoked=!0,n+=1);for(let e of[...y.values()])for(let r of[...e.subs.values()])t(e,r.channel.name)&&(B(r,`revoked`),n+=1);return n},stats(){let e=0;for(let t of y.values())e+=t.subs.size;return{connections:y.size,channels:b.size,subscriptions:e,...E}},async close(){if(w)return;w=!0,C!==void 0&&clearInterval(C),C=void 0;let e=[...y.values()];for(let t of e)P(t,1001,``);await Promise.all(e.map(e=>e.done)),await Promise.allSettled([...V]),te&&await _.close()}};return $}export{m as createHubCore};
|
|
2
2
|
//# sourceMappingURL=hub.js.map
|