@shadow-js/realtime 1.0.1 → 1.0.2

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +35 -0
  3. package/dist/dev/client/core.js +104 -106
  4. package/dist/dev/client/core.js.map +1 -1
  5. package/dist/dev/client/sse.js +33 -24
  6. package/dist/dev/client/sse.js.map +1 -1
  7. package/dist/dev/client/types.d.ts +6 -0
  8. package/dist/dev/client/util.js +2 -2
  9. package/dist/dev/client/util.js.map +1 -1
  10. package/dist/dev/cloudflare/link.js +69 -8
  11. package/dist/dev/cloudflare/link.js.map +1 -1
  12. package/dist/dev/memory.d.ts +11 -0
  13. package/dist/dev/memory.js +38 -10
  14. package/dist/dev/memory.js.map +1 -1
  15. package/dist/dev/protocol.js +13 -12
  16. package/dist/dev/protocol.js.map +1 -1
  17. package/dist/dev/redis/topics.d.ts +6 -1
  18. package/dist/dev/redis/topics.js +4 -1
  19. package/dist/dev/redis/topics.js.map +1 -1
  20. package/dist/dev/server/http.js +37 -24
  21. package/dist/dev/server/http.js.map +1 -1
  22. package/dist/dev/server/hub.js +66 -39
  23. package/dist/dev/server/hub.js.map +1 -1
  24. package/dist/dev/server/index.d.ts +2 -2
  25. package/dist/dev/server/index.js.map +1 -1
  26. package/dist/dev/server/limits.d.ts +4 -1
  27. package/dist/dev/server/limits.js.map +1 -1
  28. package/dist/dev/server/rules.js.map +1 -1
  29. package/dist/prod/client/core.js +1 -1
  30. package/dist/prod/client/core.js.map +1 -1
  31. package/dist/prod/client/sse.js +1 -4
  32. package/dist/prod/client/sse.js.map +1 -1
  33. package/dist/prod/client/util.js +1 -1
  34. package/dist/prod/client/util.js.map +1 -1
  35. package/dist/prod/cloudflare/link.js +1 -1
  36. package/dist/prod/cloudflare/link.js.map +1 -1
  37. package/dist/prod/memory.js +1 -1
  38. package/dist/prod/memory.js.map +1 -1
  39. package/dist/prod/protocol.js +1 -1
  40. package/dist/prod/protocol.js.map +1 -1
  41. package/dist/prod/redis/topics.js +1 -1
  42. package/dist/prod/redis/topics.js.map +1 -1
  43. package/dist/prod/server/http.js +1 -1
  44. package/dist/prod/server/http.js.map +1 -1
  45. package/dist/prod/server/hub.js +1 -1
  46. package/dist/prod/server/hub.js.map +1 -1
  47. package/dist/prod/server/index.js.map +1 -1
  48. package/dist/prod/server/limits.js +1 -1
  49. package/dist/prod/server/limits.js.map +1 -1
  50. package/dist/prod/server/rules.js +1 -1
  51. package/dist/prod/server/rules.js.map +1 -1
  52. package/package.json +6 -5
@@ -1 +1 @@
1
- {"version":3,"file":"core.js","names":[],"sources":["../../../src/client/core.ts"],"sourcesContent":["import { type ErrorCode, RealtimeError } from \"../errors\";\nimport {\n byteLength,\n decodeServerFrame,\n isChannelName,\n isEventName,\n isIdempotencyKey,\n type Json,\n type Member,\n type ServerFrame,\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 life = new AbortController();\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 let received = false;\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 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 fail = (op: Op, error: RealtimeError): void => finish(op, error);\n const succeed = (op: Op, value: unknown): void => finish(op, undefined, value);\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 fail(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 => ({\n kind,\n chan,\n event,\n data,\n key: extra.key,\n ack: extra.ack ?? true,\n ms: extra.ms ?? 0,\n order: ++orders,\n n: 0,\n timer: undefined,\n settle: extra.settle,\n });\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 = op.kind === \"req\" || (op.kind === \"pub\" && op.ack);\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 fail(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 (op.kind === \"req\" || op.ack) {\n inflight.set(op.n, op);\n arm(op, op.kind === \"req\" ? op.ms + 1000 : ackTtl);\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\") fail(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): { fields: 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 fields: {\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\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 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 link?.send(`{\"t\":\"unsub\",\"c\":${JSON.stringify(chan.name)}}`);\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).fields }));\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)\n link?.send(`{\"t\":\"unsub\",\"c\":${JSON.stringify(chan.name)}}`);\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 /** Declares the subscriptions of an SSE stream; returns the query entries. */\n const declare = (): unknown[] => {\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).fields);\n }\n streamSig = signature();\n return entries;\n };\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 received = false;\n silent = 0;\n const ev: LinkEvents = {\n rx: () => {\n if (g === gen) received = true;\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 }\n if (mode === \"sse\") {\n const url = resolveUrl(options.url, false);\n url.searchParams.set(\"s\", JSON.stringify(declare()));\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\", \"cannot open\", { 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 /** 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 =\n code !== undefined\n ? `the server closed the connection with ${code}`\n : `the server answered ${info.status}`;\n shutdown(new RealtimeError(\"closed\", why));\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 if (received) {\n received = false;\n silent = 0;\n } else {\n silent += 1;\n if (silent >= 2) {\n linkClosed({});\n return;\n }\n }\n if (mode === \"ws\") link?.send('{\"t\":\"ping\"}');\n };\n\n const reopen = (): void => {\n drop();\n for (const chan of chans.values()) chan.w = undefined;\n connect();\n };\n\n const onReady = (frame: Extract<ServerFrame, { t: \"ready\" }>): void => {\n if (ready) return;\n ready = true;\n connId = frame.id;\n maxBytes = frame.max;\n wsFails = 0;\n received = true;\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 };\n\n // --- frames from the server ---------------------------------------------------------------------------------------\n\n const notifyPresence = (chan: Chan): void => {\n const members = [...chan.members.values()];\n chan.list = members;\n emit(chan.presence, members);\n };\n\n const onFrame = (frame: ServerFrame, viaPost: boolean): void => {\n switch (frame.t) {\n case \"ready\":\n onReady(frame);\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 info = { after: w.after ?? 0, head: frame.head };\n const gap = frame.reset\n ? 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) emit(chan.gl, { ...info, 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\", \"revoked\"));\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) succeed(op, { seq: frame.s, duplicate: frame.dup === true });\n return;\n }\n case \"rep\": {\n const op = take(frame.n);\n if (op !== undefined) succeed(op, 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 fail(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)) notifyPresence(chan);\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 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: life.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 = decodeServerFrame(JSON.stringify(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 fail(op, new RealtimeError(code, refusal?.error?.message));\n }\n }\n } catch {\n if (life.signal.aborted) return;\n again = true;\n }\n posting = false;\n const unanswered = ops.filter(\n (op) => (op.kind === \"req\" || (op.kind === \"pub\" && op.ack)) && inflight.get(op.n) === op,\n );\n for (const op of unanswered) inflight.delete(op.n);\n if (again) {\n for (const op of unanswered)\n if (op.kind === \"req\") fail(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) fail(op, new RealtimeError(\"unavailable\"));\n flush();\n };\n\n // --- lifecycle ----------------------------------------------------------------------------------------------------\n\n const needed = (): boolean => queue.length > 0 || [...chans.values()].some(wants);\n\n const run = (): void => {\n scheduled = false;\n if (finished) return;\n syncAll();\n if (link === undefined) {\n if (reconnectTimer === undefined && needed()) 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 reopen();\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 life.abort();\n if (listening && typeof removeEventListener === \"function\")\n removeEventListener(\"online\", onOnline);\n listening = false;\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 fail(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 list: undefined,\n status: \"idle\",\n refs: 0,\n hd: 0,\n last: 0,\n sy: false,\n state: channelOptions.presence,\n sv: 0,\n w: undefined,\n retry: undefined,\n fails: 0,\n closed: false,\n hn: undefined,\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 set.add(item);\n if (!passive) {\n chan.refs += 1;\n touch(chan);\n }\n return () => {\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\", \"not JSON\", { 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":[],"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,35 +1,39 @@
1
+ import { RealtimeError } from "../errors.js";
1
2
  import { decodeServerFrame } from "../protocol.js";
2
3
  // src/client/sse.ts
3
- /** Splits an event stream into `rt` events; keeps a partial line (and a trailing CR) for the next chunk. @internal */
4
- function sseParser(onData) {
4
+ /**
5
+ * Splits an event stream into `rt` events; keeps a partial line (and a trailing CR) for the next chunk.
6
+ *
7
+ * What the parser holds is bounded: the unfinished line plus the `data` lines of the event being read may not pass `maxBuffer`
8
+ * code units (default 1 MiB, far above the largest frame a hub sends with its default limits; the cap is a function so a
9
+ * hub configured with larger frames can raise it once its `ready` frame has said so). A server that streams an endless line, or endless
10
+ * `data:` lines with no blank line, would otherwise grow the client's memory without limit; the feed function throws a
11
+ * `RealtimeError` with code `too_large` instead, and the transport closes and reconnects.
12
+ * @internal
13
+ */
14
+ function sseParser(onData, maxBuffer = () => 1048576) {
5
15
  let buffer = "";
6
16
  let name = "";
7
- let data = [];
17
+ let data;
8
18
  return (chunk) => {
9
- buffer += chunk;
10
- let start = 0;
11
- for (let at = 0; at < buffer.length; at += 1) {
12
- const char = buffer[at];
13
- if (char !== "\n" && char !== "\r") continue;
14
- if (char === "\r" && at === buffer.length - 1) break;
15
- const line = buffer.slice(start, at);
16
- if (char === "\r" && buffer[at + 1] === "\n") at += 1;
17
- start = at + 1;
19
+ const lines = (buffer + chunk).split(/\r\n|\n|\r(?!$)/);
20
+ buffer = lines.pop() ?? "";
21
+ for (const line of lines) {
18
22
  if (line === "") {
19
- if (name === "rt" && data.length > 0) onData(data.join("\n"));
23
+ if (name === "rt" && data !== void 0) onData(data);
20
24
  name = "";
21
- data = [];
25
+ data = void 0;
22
26
  continue;
23
27
  }
24
- if (line[0] === ":") continue;
25
- const colon = line.indexOf(":");
26
- const key = colon < 0 ? line : line.slice(0, colon);
27
- let value = colon < 0 ? "" : line.slice(colon + 1);
28
- if (value[0] === " ") value = value.slice(1);
28
+ const [, key, value] = /^([^:]*):? ?(.*)/s.exec(line);
29
29
  if (key === "event") name = value;
30
- else if (key === "data") data.push(value);
30
+ else if (key === "data") data = data === void 0 ? value : `${data}\n${value}`;
31
+ }
32
+ if (buffer.length + (data?.length ?? 0) > maxBuffer()) {
33
+ buffer = "";
34
+ data = void 0;
35
+ throw new RealtimeError("too_large", "the event stream sent more than the client buffers");
31
36
  }
32
- buffer = buffer.slice(start);
33
37
  };
34
38
  }
35
39
  /** Reads a server-sent events stream with `fetch`, so headers work. Frames are events named `rt`. @internal */
@@ -49,10 +53,13 @@ function openStream(url, send, getHeaders, events) {
49
53
  }
50
54
  const reader = response.body.getReader();
51
55
  const decoder = new TextDecoder();
56
+ let cap = 1048576;
52
57
  const feed = sseParser((data) => {
53
58
  const frame = decodeServerFrame(data);
54
- if (frame !== void 0) events.frame(frame);
55
- });
59
+ if (frame === void 0) return;
60
+ if (frame.t === "ready") cap = Math.min(frame.max * 2 + 1048576, 16777216);
61
+ events.frame(frame);
62
+ }, () => cap);
56
63
  for (;;) {
57
64
  const { done, value } = await reader.read();
58
65
  if (done) break;
@@ -62,7 +69,9 @@ function openStream(url, send, getHeaders, events) {
62
69
  events.closed({});
63
70
  };
64
71
  const end = () => {
65
- if (!control.signal.aborted) events.closed({});
72
+ if (control.signal.aborted) return;
73
+ control.abort();
74
+ events.closed({});
66
75
  };
67
76
  run().then(void 0, end);
68
77
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"sse.js","names":[],"sources":["../../../src/client/sse.ts"],"sourcesContent":["import { decodeServerFrame, type ServerFrame } from \"../protocol\";\n\n/** What a transport reports to the client. @internal */\nexport interface LinkEvents {\n /** Any bytes arrived (a comment counts: it proves the stream is alive). */\n rx(): void;\n frame(frame: ServerFrame): void;\n /** The transport ended. `code` is a WebSocket close code, `status` an HTTP status. */\n closed(info: { readonly code?: number; readonly status?: number }): void;\n}\n\n/** One open transport. @internal */\nexport interface Link {\n send(text: string): boolean;\n close(): void;\n}\n\n/** Splits an event stream into `rt` events; keeps a partial line (and a trailing CR) for the next chunk. @internal */\nexport function sseParser(onData: (data: string) => void): (chunk: string) => void {\n let buffer = \"\";\n let name = \"\";\n let data: string[] = [];\n return (chunk) => {\n buffer += chunk;\n let start = 0;\n for (let at = 0; at < buffer.length; at += 1) {\n const char = buffer[at];\n if (char !== \"\\n\" && char !== \"\\r\") continue;\n if (char === \"\\r\" && at === buffer.length - 1) break;\n const line = buffer.slice(start, at);\n if (char === \"\\r\" && buffer[at + 1] === \"\\n\") at += 1;\n start = at + 1;\n if (line === \"\") {\n if (name === \"rt\" && data.length > 0) onData(data.join(\"\\n\"));\n name = \"\";\n data = [];\n continue;\n }\n if (line[0] === \":\") continue;\n const colon = line.indexOf(\":\");\n const key = colon < 0 ? line : line.slice(0, colon);\n let value = colon < 0 ? \"\" : line.slice(colon + 1);\n if (value[0] === \" \") value = value.slice(1);\n if (key === \"event\") name = value;\n else if (key === \"data\") data.push(value);\n }\n buffer = buffer.slice(start);\n };\n}\n\n/** Reads a server-sent events stream with `fetch`, so headers work. Frames are events named `rt`. @internal */\nexport function openStream(\n url: string,\n send: typeof fetch,\n getHeaders: () => HeadersInit | undefined | Promise<HeadersInit | undefined>,\n events: LinkEvents,\n): Link {\n const control = new AbortController();\n const run = async (): Promise<void> => {\n const headers = new Headers(await getHeaders());\n headers.set(\"accept\", \"text/event-stream\");\n const response = await send(url, { headers, signal: control.signal, cache: \"no-store\" });\n if (!response.ok || response.body === null) {\n void response.body?.cancel().catch(() => undefined);\n return events.closed({ status: response.status });\n }\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n const feed = sseParser((data) => {\n const frame = decodeServerFrame(data);\n if (frame !== undefined) events.frame(frame);\n });\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n events.rx();\n feed(decoder.decode(value, { stream: true }));\n }\n events.closed({});\n };\n const end = (): void => {\n if (!control.signal.aborted) events.closed({});\n };\n run().then(undefined, end);\n return { send: () => false, close: () => control.abort() };\n}\n"],"mappings":";;;AAkBA,SAAgB,UAAU,QAAyD;CACjF,IAAI,SAAS;CACb,IAAI,OAAO;CACX,IAAI,OAAiB,CAAC;CACtB,QAAQ,UAAU;EAChB,UAAU;EACV,IAAI,QAAQ;EACZ,KAAK,IAAI,KAAK,GAAG,KAAK,OAAO,QAAQ,MAAM,GAAG;GAC5C,MAAM,OAAO,OAAO;GACpB,IAAI,SAAS,QAAQ,SAAS,MAAM;GACpC,IAAI,SAAS,QAAQ,OAAO,OAAO,SAAS,GAAG;GAC/C,MAAM,OAAO,OAAO,MAAM,OAAO,EAAE;GACnC,IAAI,SAAS,QAAQ,OAAO,KAAK,OAAO,MAAM,MAAM;GACpD,QAAQ,KAAK;GACb,IAAI,SAAS,IAAI;IACf,IAAI,SAAS,QAAQ,KAAK,SAAS,GAAG,OAAO,KAAK,KAAK,IAAI,CAAC;IAC5D,OAAO;IACP,OAAO,CAAC;IACR;GACF;GACA,IAAI,KAAK,OAAO,KAAK;GACrB,MAAM,QAAQ,KAAK,QAAQ,GAAG;GAC9B,MAAM,MAAM,QAAQ,IAAI,OAAO,KAAK,MAAM,GAAG,KAAK;GAClD,IAAI,QAAQ,QAAQ,IAAI,KAAK,KAAK,MAAM,QAAQ,CAAC;GACjD,IAAI,MAAM,OAAO,KAAK,QAAQ,MAAM,MAAM,CAAC;GAC3C,IAAI,QAAQ,SAAS,OAAO;QACvB,IAAI,QAAQ,QAAQ,KAAK,KAAK,KAAK;EAC1C;EACA,SAAS,OAAO,MAAM,KAAK;CAC7B;AACF;;AAGA,SAAgB,WACd,KACA,MACA,YACA,QACM;CACN,MAAM,UAAU,IAAI,gBAAgB;CACpC,MAAM,MAAM,YAA2B;EACrC,MAAM,UAAU,IAAI,QAAQ,MAAM,WAAW,CAAC;EAC9C,QAAQ,IAAI,UAAU,mBAAmB;EACzC,MAAM,WAAW,MAAM,KAAK,KAAK;GAAE;GAAS,QAAQ,QAAQ;GAAQ,OAAO;EAAW,CAAC;EACvF,IAAI,CAAC,SAAS,MAAM,SAAS,SAAS,MAAM;GAC1C,SAAc,MAAM,OAAO,CAAC,CAAC,YAAY,KAAA,CAAS;GAClD,OAAO,OAAO,OAAO,EAAE,QAAQ,SAAS,OAAO,CAAC;EAClD;EACA,MAAM,SAAS,SAAS,KAAK,UAAU;EACvC,MAAM,UAAU,IAAI,YAAY;EAChC,MAAM,OAAO,WAAW,SAAS;GAC/B,MAAM,QAAQ,kBAAkB,IAAI;GACpC,IAAI,UAAU,KAAA,GAAW,OAAO,MAAM,KAAK;EAC7C,CAAC;EACD,SAAS;GACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;GACV,OAAO,GAAG;GACV,KAAK,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC;EAC9C;EACA,OAAO,OAAO,CAAC,CAAC;CAClB;CACA,MAAM,YAAkB;EACtB,IAAI,CAAC,QAAQ,OAAO,SAAS,OAAO,OAAO,CAAC,CAAC;CAC/C;CACA,IAAI,CAAC,CAAC,KAAK,KAAA,GAAW,GAAG;CACzB,OAAO;EAAE,YAAY;EAAO,aAAa,QAAQ,MAAM;CAAE;AAC3D"}
1
+ {"version":3,"file":"sse.js","names":[],"sources":["../../../src/client/sse.ts"],"sourcesContent":["import { RealtimeError } from \"../errors\";\nimport { decodeServerFrame, type ServerFrame } from \"../protocol\";\n\n/** What a transport reports to the client. @internal */\nexport interface LinkEvents {\n /** Any bytes arrived (a comment counts: it proves the stream is alive). */\n rx(): void;\n frame(frame: ServerFrame): void;\n /** The transport ended. `code` is a WebSocket close code, `status` an HTTP status. */\n closed(info: { readonly code?: number; readonly status?: number }): void;\n}\n\n/** One open transport. @internal */\nexport interface Link {\n send(text: string): boolean;\n close(): void;\n}\n\n/**\n * Splits an event stream into `rt` events; keeps a partial line (and a trailing CR) for the next chunk.\n *\n * What the parser holds is bounded: the unfinished line plus the `data` lines of the event being read may not pass `maxBuffer`\n * code units (default 1 MiB, far above the largest frame a hub sends with its default limits; the cap is a function so a\n * hub configured with larger frames can raise it once its `ready` frame has said so). A server that streams an endless line, or endless\n * `data:` lines with no blank line, would otherwise grow the client's memory without limit; the feed function throws a\n * `RealtimeError` with code `too_large` instead, and the transport closes and reconnects.\n * @internal\n */\nexport function sseParser(\n onData: (data: string) => void,\n maxBuffer: () => number = () => 1_048_576,\n): (chunk: string) => void {\n let buffer = \"\";\n let name = \"\";\n // The `data` lines of the event being read, joined by newlines; undefined until the event has one.\n let data: string | undefined;\n return (chunk) => {\n // A line ends at LF, CRLF or CR; a CR at the very end waits for the next chunk, which may start with the LF.\n const lines = (buffer + chunk).split(/\\r\\n|\\n|\\r(?!$)/);\n buffer = lines.pop() ?? \"\";\n for (const line of lines) {\n if (line === \"\") {\n if (name === \"rt\" && data !== undefined) onData(data);\n name = \"\";\n data = undefined;\n continue;\n }\n // `field`, `field:value` or `field: value` (one space after the colon is dropped). A comment line has an empty\n // field name, which matches neither branch. The pattern always matches (every group may be empty), and its `s`\n // flag keeps U+2028 in a value: only CR and LF end a line.\n const [, key, value] = /^([^:]*):? ?(.*)/s.exec(line) as unknown as [string, string, string];\n if (key === \"event\") name = value;\n else if (key === \"data\") data = data === undefined ? value : `${data}\\n${value}`;\n }\n if (buffer.length + (data?.length ?? 0) > maxBuffer()) {\n buffer = \"\";\n data = undefined;\n throw new RealtimeError(\n \"too_large\",\n __DEV__ ? \"the event stream sent more than the client buffers\" : undefined,\n );\n }\n };\n}\n\n/** Reads a server-sent events stream with `fetch`, so headers work. Frames are events named `rt`. @internal */\nexport function openStream(\n url: string,\n send: typeof fetch,\n getHeaders: () => HeadersInit | undefined | Promise<HeadersInit | undefined>,\n events: LinkEvents,\n): Link {\n const control = new AbortController();\n const run = async (): Promise<void> => {\n const headers = new Headers(await getHeaders());\n headers.set(\"accept\", \"text/event-stream\");\n const response = await send(url, { headers, signal: control.signal, cache: \"no-store\" });\n if (!response.ok || response.body === null) {\n void response.body?.cancel().catch(() => undefined);\n return events.closed({ status: response.status });\n }\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n // A hub may be configured with frames larger than the default cap: its `ready` frame names the largest it sends.\n let cap = 1_048_576;\n const feed = sseParser(\n (data) => {\n const frame = decodeServerFrame(data);\n if (frame === undefined) return;\n if (frame.t === \"ready\") cap = Math.min(frame.max * 2 + 1_048_576, 16_777_216);\n events.frame(frame);\n },\n () => cap,\n );\n for (;;) {\n const { done, value } = await reader.read();\n if (done) break;\n events.rx();\n // An oversized stream throws here, which stops the reading; `end` aborts the request and reports the close.\n feed(decoder.decode(value, { stream: true }));\n }\n events.closed({});\n };\n const end = (): void => {\n if (control.signal.aborted) return;\n control.abort();\n events.closed({});\n };\n run().then(undefined, end);\n return { send: () => false, close: () => control.abort() };\n}\n"],"mappings":";;;;;;;;;;;;;AA4BA,SAAgB,UACd,QACA,kBAAgC,SACP;CACzB,IAAI,SAAS;CACb,IAAI,OAAO;CAEX,IAAI;CACJ,QAAQ,UAAU;EAEhB,MAAM,SAAS,SAAS,MAAA,CAAO,MAAM,iBAAiB;EACtD,SAAS,MAAM,IAAI,KAAK;EACxB,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,SAAS,IAAI;IACf,IAAI,SAAS,QAAQ,SAAS,KAAA,GAAW,OAAO,IAAI;IACpD,OAAO;IACP,OAAO,KAAA;IACP;GACF;GAIA,MAAM,GAAG,KAAK,SAAS,oBAAoB,KAAK,IAAI;GACpD,IAAI,QAAQ,SAAS,OAAO;QACvB,IAAI,QAAQ,QAAQ,OAAO,SAAS,KAAA,IAAY,QAAQ,GAAG,KAAK,IAAI;EAC3E;EACA,IAAI,OAAO,UAAU,MAAM,UAAU,KAAK,UAAU,GAAG;GACrD,SAAS;GACT,OAAO,KAAA;GACP,MAAM,IAAI,cACR,aACU,oDACZ;EACF;CACF;AACF;;AAGA,SAAgB,WACd,KACA,MACA,YACA,QACM;CACN,MAAM,UAAU,IAAI,gBAAgB;CACpC,MAAM,MAAM,YAA2B;EACrC,MAAM,UAAU,IAAI,QAAQ,MAAM,WAAW,CAAC;EAC9C,QAAQ,IAAI,UAAU,mBAAmB;EACzC,MAAM,WAAW,MAAM,KAAK,KAAK;GAAE;GAAS,QAAQ,QAAQ;GAAQ,OAAO;EAAW,CAAC;EACvF,IAAI,CAAC,SAAS,MAAM,SAAS,SAAS,MAAM;GAC1C,SAAc,MAAM,OAAO,CAAC,CAAC,YAAY,KAAA,CAAS;GAClD,OAAO,OAAO,OAAO,EAAE,QAAQ,SAAS,OAAO,CAAC;EAClD;EACA,MAAM,SAAS,SAAS,KAAK,UAAU;EACvC,MAAM,UAAU,IAAI,YAAY;EAEhC,IAAI,MAAM;EACV,MAAM,OAAO,WACV,SAAS;GACR,MAAM,QAAQ,kBAAkB,IAAI;GACpC,IAAI,UAAU,KAAA,GAAW;GACzB,IAAI,MAAM,MAAM,SAAS,MAAM,KAAK,IAAI,MAAM,MAAM,IAAI,SAAW,QAAU;GAC7E,OAAO,MAAM,KAAK;EACpB,SACM,GACR;EACA,SAAS;GACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;GACV,OAAO,GAAG;GAEV,KAAK,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC;EAC9C;EACA,OAAO,OAAO,CAAC,CAAC;CAClB;CACA,MAAM,YAAkB;EACtB,IAAI,QAAQ,OAAO,SAAS;EAC5B,QAAQ,MAAM;EACd,OAAO,OAAO,CAAC,CAAC;CAClB;CACA,IAAI,CAAC,CAAC,KAAK,KAAA,GAAW,GAAG;CACzB,OAAO;EAAE,YAAY;EAAO,aAAa,QAAQ,MAAM;CAAE;AAC3D"}
@@ -213,6 +213,12 @@ export interface RealtimeOptions {
213
213
  readonly minDelayMs?: number;
214
214
  readonly maxDelayMs?: number;
215
215
  readonly stableMs?: number;
216
+ /**
217
+ * The most reconnects tried in a row after a connection is lost. The count restarts when a connection stays open
218
+ * `stableMs`. After the last one fails the client is closed for good: `status` is `"closed"`, pending calls reject
219
+ * and `onError` receives a `closed` error. Default: unlimited.
220
+ */
221
+ readonly maxReconnectAttempts?: number;
216
222
  /** Retry after a close the server marked as a violation (1002, 1008, 1009). Default false. */
217
223
  readonly retryFatal?: boolean;
218
224
  /** The random source for jitter, in [0, 1). Default `Math.random`. */
@@ -5,11 +5,11 @@ function backoff(attempt, min, max, random) {
5
5
  }
6
6
  /** A random identifier of 12 base-36 characters, used to build idempotency keys. @internal */
7
7
  function clientId() {
8
- const bytes = /* @__PURE__ */ new Uint8Array(8);
8
+ const bytes = /* @__PURE__ */ new Uint8Array(6);
9
9
  globalThis.crypto.getRandomValues(bytes);
10
10
  let id = "";
11
11
  for (const byte of bytes) id += byte.toString(36).padStart(2, "0");
12
- return id.slice(0, 12);
12
+ return id;
13
13
  }
14
14
  /** Resolves a stream or POST path against the page; `ws` swaps `http(s):` for `ws(s):`. @internal */
15
15
  function resolveUrl(url, ws) {
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","names":[],"sources":["../../../src/client/util.ts"],"sourcesContent":["/** The delay before reconnect attempt `attempt` (0-based): full jitter below an exponential cap. @internal */\nexport function backoff(attempt: number, min: number, max: number, random: () => number): number {\n return random() * Math.min(max, min * 2 ** Math.min(attempt, 30));\n}\n\n/** A random identifier of 12 base-36 characters, used to build idempotency keys. @internal */\nexport function clientId(): string {\n const bytes = new Uint8Array(8);\n globalThis.crypto.getRandomValues(bytes);\n let id = \"\";\n for (const byte of bytes) id += byte.toString(36).padStart(2, \"0\");\n return id.slice(0, 12);\n}\n\n/** Resolves a stream or POST path against the page; `ws` swaps `http(s):` for `ws(s):`. @internal */\nexport function resolveUrl(url: string, ws: boolean): URL {\n const href = globalThis.location?.href;\n const base = href !== undefined && URL.canParse(url, href) ? href : \"http://localhost/\";\n const resolved = new URL(url, base);\n if (ws) resolved.protocol = resolved.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return resolved;\n}\n\n/** The default POST URL: the stream path plus `/send`, keeping the query. @internal */\nexport function sendUrl(url: URL): URL {\n const next = new URL(url);\n next.pathname = `${next.pathname.replace(/\\/$/, \"\")}/send`;\n return next;\n}\n"],"mappings":";;AACA,SAAgB,QAAQ,SAAiB,KAAa,KAAa,QAA8B;CAC/F,OAAO,OAAO,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK,IAAI,SAAS,EAAE,CAAC;AAClE;;AAGA,SAAgB,WAAmB;CACjC,MAAM,wBAAQ,IAAI,WAAW,CAAC;CAC9B,WAAW,OAAO,gBAAgB,KAAK;CACvC,IAAI,KAAK;CACT,KAAK,MAAM,QAAQ,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CACjE,OAAO,GAAG,MAAM,GAAG,EAAE;AACvB;;AAGA,SAAgB,WAAW,KAAa,IAAkB;CACxD,MAAM,OAAO,WAAW,UAAU;CAElC,MAAM,WAAW,IAAI,IAAI,KADZ,SAAS,KAAA,KAAa,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,mBAClC;CAClC,IAAI,IAAI,SAAS,WAAW,SAAS,aAAa,WAAW,SAAS;CACtE,OAAO;AACT;;AAGA,SAAgB,QAAQ,KAAe;CACrC,MAAM,OAAO,IAAI,IAAI,GAAG;CACxB,KAAK,WAAW,GAAG,KAAK,SAAS,QAAQ,OAAO,EAAE,EAAE;CACpD,OAAO;AACT"}
1
+ {"version":3,"file":"util.js","names":[],"sources":["../../../src/client/util.ts"],"sourcesContent":["/** The delay before reconnect attempt `attempt` (0-based): full jitter below an exponential cap. @internal */\nexport function backoff(attempt: number, min: number, max: number, random: () => number): number {\n return random() * Math.min(max, min * 2 ** Math.min(attempt, 30));\n}\n\n/** A random identifier of 12 base-36 characters, used to build idempotency keys. @internal */\nexport function clientId(): string {\n // Six bytes of two base-36 characters each make the twelve.\n const bytes = new Uint8Array(6);\n globalThis.crypto.getRandomValues(bytes);\n let id = \"\";\n for (const byte of bytes) id += byte.toString(36).padStart(2, \"0\");\n return id;\n}\n\n/** Resolves a stream or POST path against the page; `ws` swaps `http(s):` for `ws(s):`. @internal */\nexport function resolveUrl(url: string, ws: boolean): URL {\n const href = globalThis.location?.href;\n const base = href !== undefined && URL.canParse(url, href) ? href : \"http://localhost/\";\n const resolved = new URL(url, base);\n if (ws) resolved.protocol = resolved.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n return resolved;\n}\n\n/** The default POST URL: the stream path plus `/send`, keeping the query. @internal */\nexport function sendUrl(url: URL): URL {\n const next = new URL(url);\n next.pathname = `${next.pathname.replace(/\\/$/, \"\")}/send`;\n return next;\n}\n"],"mappings":";;AACA,SAAgB,QAAQ,SAAiB,KAAa,KAAa,QAA8B;CAC/F,OAAO,OAAO,IAAI,KAAK,IAAI,KAAK,MAAM,KAAK,KAAK,IAAI,SAAS,EAAE,CAAC;AAClE;;AAGA,SAAgB,WAAmB;CAEjC,MAAM,wBAAQ,IAAI,WAAW,CAAC;CAC9B,WAAW,OAAO,gBAAgB,KAAK;CACvC,IAAI,KAAK;CACT,KAAK,MAAM,QAAQ,OAAO,MAAM,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CACjE,OAAO;AACT;;AAGA,SAAgB,WAAW,KAAa,IAAkB;CACxD,MAAM,OAAO,WAAW,UAAU;CAElC,MAAM,WAAW,IAAI,IAAI,KADZ,SAAS,KAAA,KAAa,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,mBAClC;CAClC,IAAI,IAAI,SAAS,WAAW,SAAS,aAAa,WAAW,SAAS;CACtE,OAAO;AACT;;AAGA,SAAgB,QAAQ,KAAe;CACrC,MAAM,OAAO,IAAI,IAAI,GAAG;CACxB,KAAK,WAAW,GAAG,KAAK,SAAS,QAAQ,OAAO,EAAE,EAAE;CACpD,OAAO;AACT"}
@@ -28,13 +28,24 @@ function deferred() {
28
28
  var Pump = class {
29
29
  #queue = [];
30
30
  #max;
31
+ #onDrop;
31
32
  #wake;
32
33
  #stopped = false;
33
- constructor(max) {
34
+ /**
35
+ * @param max The most items waiting for the loop.
36
+ * @param onDrop Called for every item refused because the queue is full, so the owner can report it and repair what the
37
+ * listener missed. It runs inside `push`, so it must not call back into this pump.
38
+ */
39
+ constructor(max, onDrop) {
34
40
  this.#max = max;
41
+ this.#onDrop = onDrop;
35
42
  }
36
43
  push(item) {
37
- if (this.#stopped || this.#queue.length >= this.#max) return;
44
+ if (this.#stopped) return;
45
+ if (this.#queue.length >= this.#max) {
46
+ this.#onDrop();
47
+ return;
48
+ }
38
49
  this.#queue.push(item);
39
50
  const wake = this.#wake;
40
51
  this.#wake = void 0;
@@ -47,12 +58,14 @@ var Pump = class {
47
58
  this.#wake = void 0;
48
59
  wake?.();
49
60
  }
50
- async run(deliver, report, every) {
61
+ async run(deliver, report, every, idle) {
51
62
  const cancel = every?.();
52
63
  try {
53
64
  while (!this.#stopped) {
54
65
  const item = this.#queue.shift();
55
66
  if (item === void 0) {
67
+ idle?.();
68
+ if (this.#queue.length > 0) continue;
56
69
  await new Promise((resolve) => {
57
70
  this.#wake = resolve;
58
71
  });
@@ -105,11 +118,14 @@ var ChannelLink = class {
105
118
  addMessages(listener) {
106
119
  const sub = {
107
120
  listener,
108
- pump: new Pump(this.#host.maxQueued),
109
- last: this.#state === "open" ? this.#last : -1
121
+ pump: new Pump(this.#host.maxQueued, () => this.#dropped(sub)),
122
+ last: this.#state === "open" ? this.#last : -1,
123
+ overflowed: false
110
124
  };
111
125
  this.#messages.add(sub);
112
- sub.pump.run(listener, (error) => this.#host.report(error, "listener", this.channel), this.#nudger());
126
+ sub.pump.run(listener, (error) => this.#host.report(error, "listener", this.channel), this.#nudger(), () => {
127
+ sub.overflowed = false;
128
+ });
113
129
  this.#start();
114
130
  return sub;
115
131
  }
@@ -117,13 +133,58 @@ var ChannelLink = class {
117
133
  addPresence(listener) {
118
134
  const sub = {
119
135
  listener,
120
- pump: new Pump(this.#host.maxQueued)
136
+ pump: new Pump(this.#host.maxQueued, () => this.#dropped(sub)),
137
+ view: /* @__PURE__ */ new Map(),
138
+ stale: false
121
139
  };
122
140
  this.#presence.add(sub);
123
- sub.pump.run(listener, (error) => this.#host.report(error, "listener", this.channel), this.#nudger());
141
+ sub.pump.run((event) => {
142
+ if (event.op === "leave") sub.view.delete(event.member.id);
143
+ else sub.view.set(event.member.id, event.member);
144
+ listener(event);
145
+ }, (error) => this.#host.report(error, "listener", this.channel), this.#nudger(), () => this.#resync(sub));
124
146
  this.#start();
125
147
  return sub;
126
148
  }
149
+ /** A listener's queue refused an item: reported once per overflow, and a presence listener is marked for a resync. */
150
+ #dropped(sub) {
151
+ if ("view" in sub) {
152
+ if (sub.stale) return;
153
+ sub.stale = true;
154
+ } else {
155
+ if (sub.overflowed) return;
156
+ sub.overflowed = true;
157
+ }
158
+ this.#host.report(new RealtimeError("limit", "a listener fell behind: its queue is full and events were dropped"), "queue", this.channel);
159
+ }
160
+ /**
161
+ * Called when a presence listener has caught up: after a drop it is sent the difference between what it was given and the
162
+ * member set the link holds (leaves, joins, changes), so one overflow cannot leave its picture of the room wrong for good.
163
+ * When the difference is larger than the queue, the rest follows at the next catch-up.
164
+ */
165
+ #resync(sub) {
166
+ if (!sub.stale) return;
167
+ sub.stale = false;
168
+ const current = this.#members ?? /* @__PURE__ */ new Map();
169
+ for (const [id, member] of sub.view) if (!current.has(id)) sub.pump.push({
170
+ channel: this.channel,
171
+ op: "leave",
172
+ member
173
+ });
174
+ for (const [id, member] of current) {
175
+ const seen = sub.view.get(id);
176
+ if (seen === void 0) sub.pump.push({
177
+ channel: this.channel,
178
+ op: "join",
179
+ member
180
+ });
181
+ else if (member.v > seen.v) sub.pump.push({
182
+ channel: this.channel,
183
+ op: "set",
184
+ member
185
+ });
186
+ }
187
+ }
127
188
  /** Removes a listener; the last one closes the link. */
128
189
  remove(sub) {
129
190
  sub.pump.stop();