@shadow-js/realtime 1.0.3 → 1.0.4

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.
@@ -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 /** 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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"}
1
+ {"version":3,"file":"core.js","names":["FATAL","TRANSIENT","RETRYABLE_STATUS","createRealtime","reconnect","options","minDelay","maxDelay","stableMs","random","maxQueued","queueTtl","ackTtl","auto","id","byte","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","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, resolveUrl } 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 // A random identifier of 12 base-36 characters (six bytes of two characters each), used to build idempotency keys.\n const id = Array.from(globalThis.crypto.getRandomValues(new Uint8Array(6)), (byte) =>\n byte.toString(36).padStart(2, \"0\"),\n ).join(\"\");\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 }\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 = resolveUrl(options.postUrl ?? options.url, false);\n // The default POST URL is the stream path plus `/send`, keeping the query.\n if (options.postUrl === undefined) url.pathname = `${url.pathname.replace(/\\/$/, \"\")}/send`;\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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"}
@@ -1,2 +1,2 @@
1
- function e(e,t,n,r){return r()*Math.min(n,t*2**Math.min(e,30))}function t(){let e=/* @__PURE__ */ new Uint8Array(6);globalThis.crypto.getRandomValues(e);let t=``;for(let n of e)t+=n.toString(36).padStart(2,`0`);return t}function n(e,t){let n=globalThis.location?.href,r=new URL(e,n!==void 0&&URL.canParse(e,n)?n:`http://localhost/`);return t&&(r.protocol=r.protocol===`https:`?`wss:`:`ws:`),r}function r(e){let t=new URL(e);return t.pathname=`${t.pathname.replace(/\/$/,``)}/send`,t}export{e as backoff,t as clientId,n as resolveUrl,r as sendUrl};
1
+ function e(e,t,n,r){return r()*Math.min(n,t*2**Math.min(e,30))}function t(e,t){let n=globalThis.location?.href,r=new URL(e,n!==void 0&&URL.canParse(e,n)?n:`http://localhost/`);return t&&(r.protocol=r.protocol===`https:`?`wss:`:`ws:`),r}export{e as backoff,t as resolveUrl};
2
2
  //# sourceMappingURL=util.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","names":["backoff","random","max","min","attempt","clientId","bytes","id","byte","resolveUrl","href","resolved","url","ws","sendUrl","next"],"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,SAAgBA,EAAQ,EAAiB,EAAa,EAAa,EAA8B,CAC/F,OAAOC,EAAO,EAAI,KAAK,IAAIC,EAAKC,EAAM,GAAK,KAAK,IAAIC,EAAS,EAAE,CAAC,CAClE,CAGA,SAAgBC,GAAmB,CAEjC,IAAMC,kBAAQ,IAAI,WAAW,CAAC,EAC9B,WAAW,OAAO,gBAAgBA,CAAK,EACvC,IAAIC,EAAK,GACT,IAAK,IAAMC,KAAQF,EAAO,GAAME,EAAK,SAAS,EAAE,CAAC,CAAC,SAAS,EAAG,GAAG,EACjE,OAAOD,CACT,CAGA,SAAgBE,EAAW,EAAa,EAAkB,CACxD,IAAMC,EAAO,WAAW,UAAU,KAE5BC,EAAW,IAAI,IAAIC,EADZF,IAAS,IAAA,IAAa,IAAI,SAASE,EAAKF,CAAI,EAAIA,EAAO,mBAClC,EAElC,OADIG,IAAI,EAAS,SAAWF,EAAS,WAAa,SAAW,OAAS,OAC/DA,CACT,CAGA,SAAgBG,EAAQ,EAAe,CACrC,IAAMC,EAAO,IAAI,IAAIH,CAAG,EAExB,MADA,GAAK,SAAW,GAAGG,EAAK,SAAS,QAAQ,MAAO,EAAE,EAAE,OAC7CA,CACT,C"}
1
+ {"version":3,"file":"util.js","names":["backoff","random","max","min","attempt","resolveUrl","href","resolved","url","ws"],"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/** 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"],"mappings":"AACA,SAAgBA,EAAQ,EAAiB,EAAa,EAAa,EAA8B,CAC/F,OAAOC,EAAO,EAAI,KAAK,IAAIC,EAAKC,EAAM,GAAK,KAAK,IAAIC,EAAS,EAAE,CAAC,CAClE,CAGA,SAAgBC,EAAW,EAAa,EAAkB,CACxD,IAAMC,EAAO,WAAW,UAAU,KAE5BC,EAAW,IAAI,IAAIC,EADZF,IAAS,IAAA,IAAa,IAAI,SAASE,EAAKF,CAAI,EAAIA,EAAO,mBAClC,EAElC,OADIG,IAAI,EAAS,SAAWF,EAAS,WAAa,SAAW,OAAS,OAC/DA,CACT,C"}
@@ -1,2 +1,2 @@
1
- const e=1,t=/^[A-Za-z0-9_.:/@-]{1,128}$/,n=/^[A-Za-z0-9_.:@-]{1,64}$/,r=new TextEncoder;function i(e){return typeof e==`string`&&t.test(e)}function a(e){return typeof e==`string`&&n.test(e)}function o(e){return typeof e==`string`&&n.test(e)}function s(e){if(e.length*3<=2147483647){let t=!0;for(let n=0;n<e.length;n+=1)if(e.charCodeAt(n)>127){t=!1;break}if(t)return e.length}return r.encode(e).length}function c(e){return typeof e==`number`&&Number.isSafeInteger(e)&&e>=0}function l(e,t){return{ok:!1,code:`bad_frame`,why:e,...c(t)?{n:t}:{}}}function u(e,t){if(s(e)>t)return{ok:!1,code:`too_large`,why:`REALTIME_FRAME_11`};let n;try{n=JSON.parse(e)}catch{return l(`REALTIME_FRAME_5`)}if(typeof n!=`object`||!n||Array.isArray(n))return l(`REALTIME_FRAME_10`);let r=n,u=r.t,d=r.n;if(d!==void 0&&!c(d))return l(`REALTIME_FRAME_8`);let f=r.c,p=()=>i(f)?void 0:{...l(`REALTIME_FRAME_2`,d),code:`bad_channel`};switch(u){case`hello`:{let e=r.v;return typeof e!=`number`||!Number.isInteger(e)||e<1?l(`REALTIME_FRAME_13`):{ok:!0,frame:{t:`hello`,v:e}}}case`ping`:return{ok:!0,frame:d===void 0?{t:`ping`}:{t:`ping`,n:d}};case`sub`:{let e=p();if(e!==void 0)return e;if(d===void 0)return l(`REALTIME_FRAME_9`);let t=r.after;if(t!==void 0&&!c(t))return l(`REALTIME_FRAME_1`,d);let n=r.echo;if(n!==void 0&&typeof n!=`boolean`)return l(`REALTIME_FRAME_3`,d);let i=r.watch;if(i!==void 0&&typeof i!=`boolean`)return l(`REALTIME_FRAME_14`,d);let a=`presence`in r;return{ok:!0,frame:{t:`sub`,n:d,c:f,hasPresence:a,...t===void 0?{}:{after:t},...n===void 0?{}:{echo:n},...i===void 0?{}:{watch:i},...a?{presence:r.presence}:{}}}}case`unsub`:{let e=p();return e===void 0?{ok:!0,frame:d===void 0?{t:`unsub`,c:f}:{t:`unsub`,n:d,c:f}}:e}case`pub`:{let e=p();if(e!==void 0)return e;let t=r.e;if(!a(t))return l(`REALTIME_FRAME_4`,d);let n=r.k;return n!==void 0&&!o(n)?l(`REALTIME_FRAME_6`,d):{ok:!0,frame:{t:`pub`,c:f,e:t,d:JSON.stringify(r.d??null),...d===void 0?{}:{n:d},...n===void 0?{}:{k:n}}}}case`req`:{let e=p();if(e!==void 0)return e;if(d===void 0)return l(`REALTIME_FRAME_9`);let t=r.e;if(!a(t))return l(`REALTIME_FRAME_4`,d);let n=r.ms;return n!==void 0&&!(typeof n==`number`&&Number.isFinite(n)&&n>0)?l(`REALTIME_FRAME_7`,d):{ok:!0,frame:{t:`req`,n:d,c:f,e:t,d:r.d??null,...n===void 0?{}:{ms:n}}}}case`pres`:{let e=p();return e===void 0?{ok:!0,frame:{t:`pres`,c:f,s:r.s??null}}:e}default:return l(`REALTIME_FRAME_12`,d)}}function d(e){try{return f(JSON.parse(e))}catch{return}}function f(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e,n=e=>{let n=t[e];return typeof n==`string`?n:void 0},r=e=>{let n=t[e];return c(n)?n:void 0};switch(t.t){case`ready`:{let e=r(`v`),t=n(`id`);return e===void 0||t===void 0?void 0:{t:`ready`,v:e,id:t,hb:r(`hb`)??3e4,max:r(`max`)??65536}}case`subbed`:{let e=r(`n`),i=n(`c`),a=r(`head`);return e===void 0||i===void 0||a===void 0?void 0:{t:`subbed`,n:e,c:i,head:a,...t.gap===!0?{gap:!0}:{},...t.reset===!0?{reset:!0}:{}}}case`unsubbed`:{let e=n(`c`);if(e===void 0)return;let t=n(`why`);return t===void 0?{t:`unsubbed`,c:e}:{t:`unsubbed`,c:e,why:t}}case`msg`:{let e=n(`c`),i=r(`s`),a=n(`e`);if(e===void 0||i===void 0||a===void 0||i<1)return;let o=n(`p`),s=n(`u`);return{t:`msg`,c:e,s:i,e:a,d:t.d,...o===void 0?{}:{p:o},...s===void 0?{}:{u:s},...t.r===1?{r:1}:{}}}case`ack`:{let e=r(`n`),n=r(`s`);return e===void 0||n===void 0?void 0:{t:`ack`,n:e,s:n,...t.dup===!0?{dup:!0}:{}}}case`rep`:{let e=r(`n`);return e===void 0?void 0:{t:`rep`,n:e,d:t.d}}case`err`:{let e=n(`code`);if(e===void 0)return;let t=r(`n`),i=n(`c`),a=n(`msg`);return{t:`err`,code:e,...t===void 0?{}:{n:t},...i===void 0?{}:{c:i},...a===void 0?{}:{msg:a}}}case`pres`:{let e=n(`c`),r=t.op,i=t.m;if(e===void 0||r!==`sync`&&r!==`join`&&r!==`set`&&r!==`leave`)return;let a=e=>{if(typeof e!=`object`||!e)return;let t=e;if(typeof t.id==`string`&&c(t.v))return{id:t.id,v:t.v,s:t.s??null,...typeof t.u==`string`?{u:t.u}:{}}};if(r===`sync`){if(!Array.isArray(i))return;let t=i.map(a);return t.some(e=>e===void 0)?void 0:{t:`pres`,c:e,op:r,m:t}}let o=a(i);return o===void 0?void 0:{t:`pres`,c:e,op:r,m:o}}case`pong`:{let e=r(`n`);return e===void 0?{t:`pong`}:{t:`pong`,n:e}}case`bye`:{let e=r(`code`);return e===void 0?void 0:{t:`bye`,code:e,why:n(`why`)??``}}default:return}}export{e as PROTOCOL_VERSION,s as byteLength,u as decodeClientFrame,d as decodeServerFrame,i as isChannelName,a as isEventName,o as isIdempotencyKey,f as serverFrameOf};
1
+ const e=1,t=/^[A-Za-z0-9_.:/@-]{1,128}$/,n=/^[A-Za-z0-9_.:@-]{1,64}$/,r=new TextEncoder;function i(e){return typeof e==`string`&&t.test(e)}function a(e){return typeof e==`string`&&n.test(e)}const o=a;function s(e){for(let t=0;t<e.length;t+=1)if(e.charCodeAt(t)>127)return r.encode(e).length;return e.length}function c(e){return Number.isSafeInteger(e)&&e>=0}function l(e,t){return{ok:!1,code:`bad_frame`,why:e,...c(t)?{n:t}:{}}}function u(e,t){if(s(e)>t)return{ok:!1,code:`too_large`,why:`REALTIME_FRAME_11`};let n;try{n=JSON.parse(e)}catch{return l(`REALTIME_FRAME_5`)}if(typeof n!=`object`||!n||Array.isArray(n))return l(`REALTIME_FRAME_10`);let r=n,u=r.t,d=r.n;if(d!==void 0&&!c(d))return l(`REALTIME_FRAME_8`);let f=r.c,p=()=>i(f)?void 0:{...l(`REALTIME_FRAME_2`,d),code:`bad_channel`};switch(u){case`hello`:{let e=r.v;return typeof e!=`number`||!Number.isInteger(e)||e<1?l(`REALTIME_FRAME_13`):{ok:!0,frame:{t:`hello`,v:e}}}case`ping`:return{ok:!0,frame:d===void 0?{t:`ping`}:{t:`ping`,n:d}};case`sub`:{let e=p();if(e!==void 0)return e;if(d===void 0)return l(`REALTIME_FRAME_9`);let t=r.after;if(t!==void 0&&!c(t))return l(`REALTIME_FRAME_1`,d);let n=r.echo;if(n!==void 0&&typeof n!=`boolean`)return l(`REALTIME_FRAME_3`,d);let i=r.watch;if(i!==void 0&&typeof i!=`boolean`)return l(`REALTIME_FRAME_14`,d);let a=`presence`in r;return{ok:!0,frame:{t:`sub`,n:d,c:f,hasPresence:a,...t===void 0?{}:{after:t},...n===void 0?{}:{echo:n},...i===void 0?{}:{watch:i},...a?{presence:r.presence}:{}}}}case`unsub`:{let e=p();return e===void 0?{ok:!0,frame:d===void 0?{t:`unsub`,c:f}:{t:`unsub`,n:d,c:f}}:e}case`pub`:{let e=p();if(e!==void 0)return e;let t=r.e;if(!a(t))return l(`REALTIME_FRAME_4`,d);let n=r.k;return n!==void 0&&!o(n)?l(`REALTIME_FRAME_6`,d):{ok:!0,frame:{t:`pub`,c:f,e:t,d:JSON.stringify(r.d??null),...d===void 0?{}:{n:d},...n===void 0?{}:{k:n}}}}case`req`:{let e=p();if(e!==void 0)return e;if(d===void 0)return l(`REALTIME_FRAME_9`);let t=r.e;if(!a(t))return l(`REALTIME_FRAME_4`,d);let n=r.ms;return n!==void 0&&!(typeof n==`number`&&Number.isFinite(n)&&n>0)?l(`REALTIME_FRAME_7`,d):{ok:!0,frame:{t:`req`,n:d,c:f,e:t,d:r.d??null,...n===void 0?{}:{ms:n}}}}case`pres`:{let e=p();return e===void 0?{ok:!0,frame:{t:`pres`,c:f,s:r.s??null}}:e}default:return l(`REALTIME_FRAME_12`,d)}}function d(e){try{return f(JSON.parse(e))}catch{return}}function f(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=e,n=e=>{let n=t[e];return typeof n==`string`?n:void 0},r=e=>{let n=t[e];return c(n)?n:void 0};switch(t.t){case`ready`:{let e=r(`v`),t=n(`id`);return e===void 0||t===void 0?void 0:{t:`ready`,v:e,id:t,hb:r(`hb`)??3e4,max:r(`max`)??65536}}case`subbed`:{let e=r(`n`),i=n(`c`),a=r(`head`);return e===void 0||i===void 0||a===void 0?void 0:{t:`subbed`,n:e,c:i,head:a,...t.gap===!0?{gap:!0}:{},...t.reset===!0?{reset:!0}:{}}}case`unsubbed`:{let e=n(`c`);if(e===void 0)return;let t=n(`why`);return t===void 0?{t:`unsubbed`,c:e}:{t:`unsubbed`,c:e,why:t}}case`msg`:{let e=n(`c`),i=r(`s`),a=n(`e`);if(e===void 0||i===void 0||a===void 0||i<1)return;let o=n(`p`),s=n(`u`);return{t:`msg`,c:e,s:i,e:a,d:t.d,...o===void 0?{}:{p:o},...s===void 0?{}:{u:s},...t.r===1?{r:1}:{}}}case`ack`:{let e=r(`n`),n=r(`s`);return e===void 0||n===void 0?void 0:{t:`ack`,n:e,s:n,...t.dup===!0?{dup:!0}:{}}}case`rep`:{let e=r(`n`);return e===void 0?void 0:{t:`rep`,n:e,d:t.d}}case`err`:{let e=n(`code`);if(e===void 0)return;let t=r(`n`),i=n(`c`),a=n(`msg`);return{t:`err`,code:e,...t===void 0?{}:{n:t},...i===void 0?{}:{c:i},...a===void 0?{}:{msg:a}}}case`pres`:{let e=n(`c`),r=t.op,i=t.m;if(e===void 0||r!==`sync`&&r!==`join`&&r!==`set`&&r!==`leave`)return;let a=e=>{if(typeof e!=`object`||!e)return;let t=e;if(typeof t.id==`string`&&c(t.v))return{id:t.id,v:t.v,s:t.s??null,...typeof t.u==`string`?{u:t.u}:{}}};if(r===`sync`){if(!Array.isArray(i))return;let t=i.map(a);return t.some(e=>e===void 0)?void 0:{t:`pres`,c:e,op:r,m:t}}let o=a(i);return o===void 0?void 0:{t:`pres`,c:e,op:r,m:o}}case`pong`:{let e=r(`n`);return e===void 0?{t:`pong`}:{t:`pong`,n:e}}case`bye`:{let e=r(`code`);return e===void 0?void 0:{t:`bye`,code:e,why:n(`why`)??``}}default:return}}export{e as PROTOCOL_VERSION,s as byteLength,u as decodeClientFrame,d as decodeServerFrame,i as isChannelName,a as isEventName,o as isIdempotencyKey,f as serverFrameOf};
2
2
  //# sourceMappingURL=protocol.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","names":["PROTOCOL_VERSION","CHANNEL","EVENT","encoder","isChannelName","value","isEventName","isIdempotencyKey","byteLength","text","ascii","at","isCount","bad","n","decodeClientFrame","maxBytes","raw","type","\"t\"","\"n\"","c","\"c\"","needChannel","v","\"v\"","refusal","after","\"after\"","echo","\"echo\"","watch","\"watch\"","hasPresence","\"presence\"","e","\"e\"","k","\"k\"","\"d\"","ms","\"ms\"","\"s\"","decodeServerFrame","serverFrameOf","str","item","key","num","id","head","\"gap\"","\"reset\"","why","s","p","u","\"r\"","\"dup\"","code","msg","op","\"op\"","m","\"m\"","member","entry","\"id\"","\"u\"","list","one"],"sources":["../../src/protocol.ts"],"sourcesContent":["import type { ErrorCode } from \"./errors\";\n\n/**\n * The wire protocol version this package speaks (https://realtime.shadowjs.net/guides/realtime-protocol/).\n * @example\n * import { PROTOCOL_VERSION } from \"@shadow-js/realtime\";\n *\n * const hello = JSON.stringify({ t: \"hello\", v: PROTOCOL_VERSION });\n * @since 1.0.0\n */\nexport const PROTOCOL_VERSION: 1 = 1;\n\n/**\n * Any JSON value.\n * @since 1.0.0\n */\nexport type Json =\n | null\n | boolean\n | number\n | string\n | readonly Json[]\n | { readonly [key: string]: Json };\n\n/**\n * One member of a channel's presence set: the connection that joined, its user, its state, and the version of that state.\n * @since 1.0.0\n */\nexport interface Member<State = Json> {\n /** The connection id. */\n readonly id: string;\n /** The user of the connection's identity, when it has one. */\n readonly u?: string;\n /** The member's state. */\n readonly s: State;\n /** A counter that rises with every state change of this member, from 1. */\n readonly v: number;\n}\n\n/** A message as stored by an adapter: the data is its JSON text. @since 1.0.0 */\nexport interface StoredMessage {\n readonly seq: number;\n readonly event: string;\n /** The JSON text of the data. */\n readonly data: string;\n /** The publishing connection id, when a connection published it. */\n readonly publisher?: string;\n /** The publishing user, when it has one. */\n readonly user?: string;\n /** When the adapter accepted it, in epoch milliseconds. */\n readonly at: number;\n}\n\n/** What a publisher supplies; the adapter adds `seq` and `at`. @since 1.0.0 */\nexport type StoredEntry = Omit<StoredMessage, \"seq\" | \"at\">;\n\n/** A change of a presence set, as adapters deliver it. @since 1.0.0 */\nexport interface PresenceEvent {\n readonly channel: string;\n readonly op: \"join\" | \"set\" | \"leave\";\n readonly member: Member;\n}\n\n/** Frames a client sends. @since 1.0.0 */\nexport type ClientFrame =\n | { readonly t: \"hello\"; readonly v: number }\n | {\n readonly t: \"sub\";\n readonly n: number;\n readonly c: string;\n readonly after?: number;\n readonly echo?: boolean;\n readonly presence?: Json;\n readonly hasPresence: boolean;\n readonly watch?: boolean;\n }\n | { readonly t: \"unsub\"; readonly n?: number; readonly c: string }\n | {\n readonly t: \"pub\";\n readonly c: string;\n readonly e: string;\n readonly d: string;\n readonly n?: number;\n readonly k?: string;\n }\n | {\n readonly t: \"req\";\n readonly n: number;\n readonly c: string;\n readonly e: string;\n readonly d: Json;\n readonly ms?: number;\n }\n | { readonly t: \"pres\"; readonly c: string; readonly s: Json }\n | { readonly t: \"ping\"; readonly n?: number };\n\n/** What decoding a client frame yields. @since 1.0.0 */\nexport type DecodedClientFrame =\n | { readonly ok: true; readonly frame: ClientFrame }\n | { readonly ok: false; readonly code: ErrorCode; readonly n?: number; readonly why: string };\n\nconst CHANNEL = /^[A-Za-z0-9_.:/@-]{1,128}$/;\n// Event names and idempotency keys share one shape.\nconst EVENT = /^[A-Za-z0-9_.:@-]{1,64}$/;\nconst encoder = new TextEncoder();\n\n/**\n * Whether `value` is a valid channel name: 1..128 characters of `A-Za-z0-9_.:/@-`, not starting with the reserved `$`.\n * @param value The candidate.\n * @returns True for a valid name.\n * @example\n * import { isChannelName } from \"@shadow-js/realtime\";\n *\n * isChannelName(\"chat:lobby\"); // true\n * isChannelName(\"chat lobby\"); // false\n * @since 1.0.0\n */\nexport function isChannelName(value: unknown): value is string {\n return typeof value === \"string\" && CHANNEL.test(value);\n}\n\n/**\n * Whether `value` is a valid event name: 1..64 characters of `A-Za-z0-9_.:@-`.\n * @param value The candidate.\n * @returns True for a valid name.\n * @example\n * import { isEventName } from \"@shadow-js/realtime\";\n *\n * isEventName(\"message\"); // true\n * isEventName(\"a/b\"); // false\n * @since 1.0.0\n */\nexport function isEventName(value: unknown): value is string {\n return typeof value === \"string\" && EVENT.test(value);\n}\n\n/** Whether `value` is a valid idempotency key. @internal */\nexport function isIdempotencyKey(value: unknown): value is string {\n return typeof value === \"string\" && EVENT.test(value);\n}\n\n/** The number of bytes of `text` in UTF-8, without encoding the common cases. @internal */\nexport function byteLength(text: string): number {\n // Every UTF-16 unit is at most 3 bytes in UTF-8, and at least 1.\n if (text.length * 3 <= 0x7fffffff) {\n let ascii = true;\n for (let at = 0; at < text.length; at += 1) {\n if (text.charCodeAt(at) > 0x7f) {\n ascii = false;\n break;\n }\n }\n if (ascii) return text.length;\n }\n return encoder.encode(text).length;\n}\n\nfunction isCount(value: unknown): value is number {\n return typeof value === \"number\" && Number.isSafeInteger(value) && value >= 0;\n}\n\ntype Refusal = Extract<DecodedClientFrame, { ok: false }>;\n\nfunction bad(why: string, n?: unknown): Refusal {\n return { ok: false, code: \"bad_frame\", why, ...(isCount(n) ? { n } : {}) };\n}\n\n/**\n * Decodes and validates one client frame (server side). Never throws: any text yields a frame or a refusal.\n *\n * @param text The frame text.\n * @param maxBytes The largest accepted frame, in UTF-8 bytes.\n * @returns The frame, or the code and reason to refuse it with.\n * @example\n * import { decodeClientFrame } from \"@shadow-js/realtime\";\n *\n * const result = decodeClientFrame('{\"t\":\"ping\"}', 65_536);\n * result.ok; // true\n * @since 1.0.0\n */\nexport function decodeClientFrame(text: string, maxBytes: number): DecodedClientFrame {\n if (byteLength(text) > maxBytes)\n return {\n ok: false,\n code: \"too_large\",\n why: __DEV__ ? \"the frame is larger than the limit\" : \"REALTIME_FRAME_11\",\n };\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return bad(__DEV__ ? \"the frame is not JSON\" : \"REALTIME_FRAME_5\");\n }\n if (value === null || typeof value !== \"object\" || Array.isArray(value))\n return bad(__DEV__ ? \"the frame is not an object\" : \"REALTIME_FRAME_10\");\n const raw = value as Record<string, unknown>;\n const type = raw[\"t\"];\n const n = raw[\"n\"];\n if (n !== undefined && !isCount(n))\n return bad(__DEV__ ? \"n must be a non-negative integer\" : \"REALTIME_FRAME_8\");\n const c = raw[\"c\"];\n const needChannel = (): Refusal | undefined =>\n isChannelName(c)\n ? undefined\n : {\n ...bad(__DEV__ ? \"c is not a valid channel name\" : \"REALTIME_FRAME_2\", n),\n code: \"bad_channel\",\n };\n switch (type) {\n case \"hello\": {\n const v = raw[\"v\"];\n if (typeof v !== \"number\" || !Number.isInteger(v) || v < 1)\n return bad(__DEV__ ? \"v must be a version\" : \"REALTIME_FRAME_13\");\n return { ok: true, frame: { t: \"hello\", v } };\n }\n case \"ping\":\n return { ok: true, frame: n === undefined ? { t: \"ping\" } : { t: \"ping\", n } };\n case \"sub\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n if (n === undefined) return bad(__DEV__ ? \"sub needs n\" : \"REALTIME_FRAME_9\");\n const after = raw[\"after\"];\n if (after !== undefined && !isCount(after))\n return bad(__DEV__ ? \"after must be a non-negative integer\" : \"REALTIME_FRAME_1\", n);\n const echo = raw[\"echo\"];\n if (echo !== undefined && typeof echo !== \"boolean\")\n return bad(__DEV__ ? \"echo must be a boolean\" : \"REALTIME_FRAME_3\", n);\n const watch = raw[\"watch\"];\n if (watch !== undefined && typeof watch !== \"boolean\")\n return bad(__DEV__ ? \"watch must be a boolean\" : \"REALTIME_FRAME_14\", n);\n const hasPresence = \"presence\" in raw;\n return {\n ok: true,\n frame: {\n t: \"sub\",\n n,\n c: c as string,\n hasPresence,\n ...(after === undefined ? {} : { after }),\n ...(echo === undefined ? {} : { echo }),\n ...(watch === undefined ? {} : { watch }),\n ...(hasPresence ? { presence: raw[\"presence\"] as Json } : {}),\n },\n };\n }\n case \"unsub\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n return {\n ok: true,\n frame: n === undefined ? { t: \"unsub\", c: c as string } : { t: \"unsub\", n, c: c as string },\n };\n }\n case \"pub\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n const e = raw[\"e\"];\n if (!isEventName(e))\n return bad(__DEV__ ? \"e is not a valid event name\" : \"REALTIME_FRAME_4\", n);\n const k = raw[\"k\"];\n if (k !== undefined && !isIdempotencyKey(k))\n return bad(__DEV__ ? \"k is not a valid key\" : \"REALTIME_FRAME_6\", n);\n const data = JSON.stringify(raw[\"d\"] ?? null);\n return {\n ok: true,\n frame: {\n t: \"pub\",\n c: c as string,\n e,\n d: data,\n ...(n === undefined ? {} : { n }),\n ...(k === undefined ? {} : { k }),\n },\n };\n }\n case \"req\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n if (n === undefined) return bad(__DEV__ ? \"req needs n\" : \"REALTIME_FRAME_9\");\n const e = raw[\"e\"];\n if (!isEventName(e))\n return bad(__DEV__ ? \"e is not a valid event name\" : \"REALTIME_FRAME_4\", n);\n const ms = raw[\"ms\"];\n if (ms !== undefined && !(typeof ms === \"number\" && Number.isFinite(ms) && ms > 0))\n return bad(__DEV__ ? \"ms must be a positive number\" : \"REALTIME_FRAME_7\", n);\n return {\n ok: true,\n frame: {\n t: \"req\",\n n,\n c: c as string,\n e,\n d: (raw[\"d\"] ?? null) as Json,\n ...(ms === undefined ? {} : { ms }),\n },\n };\n }\n case \"pres\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n return { ok: true, frame: { t: \"pres\", c: c as string, s: (raw[\"s\"] ?? null) as Json } };\n }\n default:\n return bad(__DEV__ ? \"unknown frame type\" : \"REALTIME_FRAME_12\", n);\n }\n}\n\n/** Frames a server sends, as a client reads them. @since 1.0.0 */\nexport type ServerFrame =\n | {\n readonly t: \"ready\";\n readonly v: number;\n readonly id: string;\n readonly hb: number;\n readonly max: number;\n }\n | {\n readonly t: \"subbed\";\n readonly n: number;\n readonly c: string;\n readonly head: number;\n readonly gap?: boolean;\n readonly reset?: boolean;\n }\n | { readonly t: \"unsubbed\"; readonly c: string; readonly why?: string }\n | {\n readonly t: \"msg\";\n readonly c: string;\n readonly s: number;\n readonly e: string;\n readonly d: Json;\n readonly p?: string;\n readonly u?: string;\n readonly r?: 1;\n }\n | { readonly t: \"ack\"; readonly n: number; readonly s: number; readonly dup?: boolean }\n | { readonly t: \"rep\"; readonly n: number; readonly d: Json }\n | {\n readonly t: \"err\";\n readonly n?: number;\n readonly c?: string;\n readonly code: ErrorCode;\n readonly msg?: string;\n }\n | {\n readonly t: \"pres\";\n readonly c: string;\n readonly op: \"sync\" | \"join\" | \"set\" | \"leave\";\n readonly m: Member | readonly Member[];\n }\n | { readonly t: \"pong\"; readonly n?: number }\n | { readonly t: \"bye\"; readonly code: number; readonly why: string };\n\n/**\n * Decodes one server frame (client side). Returns `undefined` for anything that is not a known, well-formed frame, so a\n * client ignores what a newer server may send and never acts on a malformed frame.\n *\n * @param text The frame text.\n * @returns The frame, or undefined.\n * @example\n * import { decodeServerFrame } from \"@shadow-js/realtime\";\n *\n * decodeServerFrame('{\"t\":\"pong\"}'); // { t: \"pong\" }\n * decodeServerFrame(\"not json\"); // undefined\n * @since 1.0.0\n */\nexport function decodeServerFrame(text: string): ServerFrame | undefined {\n try {\n return serverFrameOf(JSON.parse(text));\n } catch {\n return undefined;\n }\n}\n\n/**\n * Decodes a server frame that is already parsed (the frames of an HTTP answer arrive inside one JSON document), with the\n * rules of {@link decodeServerFrame}.\n * @internal\n */\nexport function serverFrameOf(value: unknown): ServerFrame | undefined {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const raw = value as Record<string, unknown>;\n const str = (key: string): string | undefined => {\n const item = raw[key];\n return typeof item === \"string\" ? item : undefined;\n };\n const num = (key: string): number | undefined => {\n const item = raw[key];\n return isCount(item) ? item : undefined;\n };\n switch (raw[\"t\"]) {\n case \"ready\": {\n const v = num(\"v\");\n const id = str(\"id\");\n if (v === undefined || id === undefined) return undefined;\n return { t: \"ready\", v, id, hb: num(\"hb\") ?? 30_000, max: num(\"max\") ?? 65_536 };\n }\n case \"subbed\": {\n const n = num(\"n\");\n const c = str(\"c\");\n const head = num(\"head\");\n if (n === undefined || c === undefined || head === undefined) return undefined;\n return {\n t: \"subbed\",\n n,\n c,\n head,\n ...(raw[\"gap\"] === true ? { gap: true } : {}),\n ...(raw[\"reset\"] === true ? { reset: true } : {}),\n };\n }\n case \"unsubbed\": {\n const c = str(\"c\");\n if (c === undefined) return undefined;\n const why = str(\"why\");\n return why === undefined ? { t: \"unsubbed\", c } : { t: \"unsubbed\", c, why };\n }\n case \"msg\": {\n const c = str(\"c\");\n const s = num(\"s\");\n const e = str(\"e\");\n if (c === undefined || s === undefined || e === undefined || s < 1) return undefined;\n const p = str(\"p\");\n const u = str(\"u\");\n return {\n t: \"msg\",\n c,\n s,\n e,\n d: raw[\"d\"] as Json,\n ...(p === undefined ? {} : { p }),\n ...(u === undefined ? {} : { u }),\n ...(raw[\"r\"] === 1 ? { r: 1 as const } : {}),\n };\n }\n case \"ack\": {\n const n = num(\"n\");\n const s = num(\"s\");\n if (n === undefined || s === undefined) return undefined;\n return { t: \"ack\", n, s, ...(raw[\"dup\"] === true ? { dup: true } : {}) };\n }\n case \"rep\": {\n const n = num(\"n\");\n if (n === undefined) return undefined;\n return { t: \"rep\", n, d: raw[\"d\"] as Json };\n }\n case \"err\": {\n const code = str(\"code\");\n if (code === undefined) return undefined;\n const n = num(\"n\");\n const c = str(\"c\");\n const msg = str(\"msg\");\n return {\n t: \"err\",\n code: code as ErrorCode,\n ...(n === undefined ? {} : { n }),\n ...(c === undefined ? {} : { c }),\n ...(msg === undefined ? {} : { msg }),\n };\n }\n case \"pres\": {\n const c = str(\"c\");\n const op = raw[\"op\"];\n const m = raw[\"m\"];\n if (c === undefined || (op !== \"sync\" && op !== \"join\" && op !== \"set\" && op !== \"leave\"))\n return undefined;\n const member = (item: unknown): Member | undefined => {\n if (item === null || typeof item !== \"object\") return undefined;\n const entry = item as Record<string, unknown>;\n if (typeof entry[\"id\"] !== \"string\" || !isCount(entry[\"v\"])) return undefined;\n return {\n id: entry[\"id\"],\n v: entry[\"v\"],\n s: (entry[\"s\"] ?? null) as Json,\n ...(typeof entry[\"u\"] === \"string\" ? { u: entry[\"u\"] } : {}),\n };\n };\n if (op === \"sync\") {\n if (!Array.isArray(m)) return undefined;\n const list = m.map(member);\n if (list.some((item) => item === undefined)) return undefined;\n return { t: \"pres\", c, op, m: list as Member[] };\n }\n const one = member(m);\n return one === undefined ? undefined : { t: \"pres\", c, op, m: one };\n }\n case \"pong\": {\n const n = num(\"n\");\n return n === undefined ? { t: \"pong\" } : { t: \"pong\", n };\n }\n case \"bye\": {\n const code = num(\"code\");\n return code === undefined ? undefined : { t: \"bye\", code, why: str(\"why\") ?? \"\" };\n }\n default:\n return undefined;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"protocol.js","names":["PROTOCOL_VERSION","CHANNEL","EVENT","encoder","isChannelName","value","isEventName","isIdempotencyKey","byteLength","at","text","isCount","bad","n","decodeClientFrame","maxBytes","raw","type","\"t\"","\"n\"","c","\"c\"","needChannel","v","\"v\"","refusal","after","\"after\"","echo","\"echo\"","watch","\"watch\"","hasPresence","\"presence\"","e","\"e\"","k","\"k\"","\"d\"","ms","\"ms\"","\"s\"","decodeServerFrame","serverFrameOf","str","item","key","num","id","head","\"gap\"","\"reset\"","why","s","p","u","\"r\"","\"dup\"","code","msg","op","\"op\"","m","\"m\"","member","entry","\"id\"","\"u\"","list","one"],"sources":["../../src/protocol.ts"],"sourcesContent":["import type { ErrorCode } from \"./errors\";\n\n/**\n * The wire protocol version this package speaks (https://realtime.shadowjs.net/guides/realtime-protocol/).\n * @example\n * import { PROTOCOL_VERSION } from \"@shadow-js/realtime\";\n *\n * const hello = JSON.stringify({ t: \"hello\", v: PROTOCOL_VERSION });\n * @since 1.0.0\n */\nexport const PROTOCOL_VERSION: 1 = 1;\n\n/**\n * Any JSON value.\n * @since 1.0.0\n */\nexport type Json =\n | null\n | boolean\n | number\n | string\n | readonly Json[]\n | { readonly [key: string]: Json };\n\n/**\n * One member of a channel's presence set: the connection that joined, its user, its state, and the version of that state.\n * @since 1.0.0\n */\nexport interface Member<State = Json> {\n /** The connection id. */\n readonly id: string;\n /** The user of the connection's identity, when it has one. */\n readonly u?: string;\n /** The member's state. */\n readonly s: State;\n /** A counter that rises with every state change of this member, from 1. */\n readonly v: number;\n}\n\n/** A message as stored by an adapter: the data is its JSON text. @since 1.0.0 */\nexport interface StoredMessage {\n readonly seq: number;\n readonly event: string;\n /** The JSON text of the data. */\n readonly data: string;\n /** The publishing connection id, when a connection published it. */\n readonly publisher?: string;\n /** The publishing user, when it has one. */\n readonly user?: string;\n /** When the adapter accepted it, in epoch milliseconds. */\n readonly at: number;\n}\n\n/** What a publisher supplies; the adapter adds `seq` and `at`. @since 1.0.0 */\nexport type StoredEntry = Omit<StoredMessage, \"seq\" | \"at\">;\n\n/** A change of a presence set, as adapters deliver it. @since 1.0.0 */\nexport interface PresenceEvent {\n readonly channel: string;\n readonly op: \"join\" | \"set\" | \"leave\";\n readonly member: Member;\n}\n\n/** Frames a client sends. @since 1.0.0 */\nexport type ClientFrame =\n | { readonly t: \"hello\"; readonly v: number }\n | {\n readonly t: \"sub\";\n readonly n: number;\n readonly c: string;\n readonly after?: number;\n readonly echo?: boolean;\n readonly presence?: Json;\n readonly hasPresence: boolean;\n readonly watch?: boolean;\n }\n | { readonly t: \"unsub\"; readonly n?: number; readonly c: string }\n | {\n readonly t: \"pub\";\n readonly c: string;\n readonly e: string;\n readonly d: string;\n readonly n?: number;\n readonly k?: string;\n }\n | {\n readonly t: \"req\";\n readonly n: number;\n readonly c: string;\n readonly e: string;\n readonly d: Json;\n readonly ms?: number;\n }\n | { readonly t: \"pres\"; readonly c: string; readonly s: Json }\n | { readonly t: \"ping\"; readonly n?: number };\n\n/** What decoding a client frame yields. @since 1.0.0 */\nexport type DecodedClientFrame =\n | { readonly ok: true; readonly frame: ClientFrame }\n | { readonly ok: false; readonly code: ErrorCode; readonly n?: number; readonly why: string };\n\nconst CHANNEL = /^[A-Za-z0-9_.:/@-]{1,128}$/;\n// Event names and idempotency keys share one shape.\nconst EVENT = /^[A-Za-z0-9_.:@-]{1,64}$/;\nconst encoder = new TextEncoder();\n\n/**\n * Whether `value` is a valid channel name: 1..128 characters of `A-Za-z0-9_.:/@-`, not starting with the reserved `$`.\n * @param value The candidate.\n * @returns True for a valid name.\n * @example\n * import { isChannelName } from \"@shadow-js/realtime\";\n *\n * isChannelName(\"chat:lobby\"); // true\n * isChannelName(\"chat lobby\"); // false\n * @since 1.0.0\n */\nexport function isChannelName(value: unknown): value is string {\n return typeof value === \"string\" && CHANNEL.test(value);\n}\n\n/**\n * Whether `value` is a valid event name: 1..64 characters of `A-Za-z0-9_.:@-`.\n * @param value The candidate.\n * @returns True for a valid name.\n * @example\n * import { isEventName } from \"@shadow-js/realtime\";\n *\n * isEventName(\"message\"); // true\n * isEventName(\"a/b\"); // false\n * @since 1.0.0\n */\nexport function isEventName(value: unknown): value is string {\n return typeof value === \"string\" && EVENT.test(value);\n}\n\n/** Whether `value` is a valid idempotency key (the shape of an event name). @internal */\nexport const isIdempotencyKey: (value: unknown) => value is string = isEventName;\n\n/** The number of bytes of `text` in UTF-8, without encoding the common (all ASCII) case. @internal */\nexport function byteLength(text: string): number {\n for (let at = 0; at < text.length; at += 1) {\n if (text.charCodeAt(at) > 0x7f) return encoder.encode(text).length;\n }\n return text.length;\n}\n\nfunction isCount(value: unknown): value is number {\n // `isSafeInteger` is false for anything that is not a number.\n return Number.isSafeInteger(value) && (value as number) >= 0;\n}\n\ntype Refusal = Extract<DecodedClientFrame, { ok: false }>;\n\nfunction bad(why: string, n?: unknown): Refusal {\n return { ok: false, code: \"bad_frame\", why, ...(isCount(n) ? { n } : {}) };\n}\n\n/**\n * Decodes and validates one client frame (server side). Never throws: any text yields a frame or a refusal.\n *\n * @param text The frame text.\n * @param maxBytes The largest accepted frame, in UTF-8 bytes.\n * @returns The frame, or the code and reason to refuse it with.\n * @example\n * import { decodeClientFrame } from \"@shadow-js/realtime\";\n *\n * const result = decodeClientFrame('{\"t\":\"ping\"}', 65_536);\n * result.ok; // true\n * @since 1.0.0\n */\nexport function decodeClientFrame(text: string, maxBytes: number): DecodedClientFrame {\n if (byteLength(text) > maxBytes)\n return {\n ok: false,\n code: \"too_large\",\n why: __DEV__ ? \"the frame is larger than the limit\" : \"REALTIME_FRAME_11\",\n };\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return bad(__DEV__ ? \"the frame is not JSON\" : \"REALTIME_FRAME_5\");\n }\n if (value === null || typeof value !== \"object\" || Array.isArray(value))\n return bad(__DEV__ ? \"the frame is not an object\" : \"REALTIME_FRAME_10\");\n const raw = value as Record<string, unknown>;\n const type = raw[\"t\"];\n const n = raw[\"n\"];\n if (n !== undefined && !isCount(n))\n return bad(__DEV__ ? \"n must be a non-negative integer\" : \"REALTIME_FRAME_8\");\n const c = raw[\"c\"];\n const needChannel = (): Refusal | undefined =>\n isChannelName(c)\n ? undefined\n : {\n ...bad(__DEV__ ? \"c is not a valid channel name\" : \"REALTIME_FRAME_2\", n),\n code: \"bad_channel\",\n };\n switch (type) {\n case \"hello\": {\n const v = raw[\"v\"];\n if (typeof v !== \"number\" || !Number.isInteger(v) || v < 1)\n return bad(__DEV__ ? \"v must be a version\" : \"REALTIME_FRAME_13\");\n return { ok: true, frame: { t: \"hello\", v } };\n }\n case \"ping\":\n return { ok: true, frame: n === undefined ? { t: \"ping\" } : { t: \"ping\", n } };\n case \"sub\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n if (n === undefined) return bad(__DEV__ ? \"sub needs n\" : \"REALTIME_FRAME_9\");\n const after = raw[\"after\"];\n if (after !== undefined && !isCount(after))\n return bad(__DEV__ ? \"after must be a non-negative integer\" : \"REALTIME_FRAME_1\", n);\n const echo = raw[\"echo\"];\n if (echo !== undefined && typeof echo !== \"boolean\")\n return bad(__DEV__ ? \"echo must be a boolean\" : \"REALTIME_FRAME_3\", n);\n const watch = raw[\"watch\"];\n if (watch !== undefined && typeof watch !== \"boolean\")\n return bad(__DEV__ ? \"watch must be a boolean\" : \"REALTIME_FRAME_14\", n);\n const hasPresence = \"presence\" in raw;\n return {\n ok: true,\n frame: {\n t: \"sub\",\n n,\n c: c as string,\n hasPresence,\n ...(after === undefined ? {} : { after }),\n ...(echo === undefined ? {} : { echo }),\n ...(watch === undefined ? {} : { watch }),\n ...(hasPresence ? { presence: raw[\"presence\"] as Json } : {}),\n },\n };\n }\n case \"unsub\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n return {\n ok: true,\n frame: n === undefined ? { t: \"unsub\", c: c as string } : { t: \"unsub\", n, c: c as string },\n };\n }\n case \"pub\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n const e = raw[\"e\"];\n if (!isEventName(e))\n return bad(__DEV__ ? \"e is not a valid event name\" : \"REALTIME_FRAME_4\", n);\n const k = raw[\"k\"];\n if (k !== undefined && !isIdempotencyKey(k))\n return bad(__DEV__ ? \"k is not a valid key\" : \"REALTIME_FRAME_6\", n);\n const data = JSON.stringify(raw[\"d\"] ?? null);\n return {\n ok: true,\n frame: {\n t: \"pub\",\n c: c as string,\n e,\n d: data,\n ...(n === undefined ? {} : { n }),\n ...(k === undefined ? {} : { k }),\n },\n };\n }\n case \"req\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n if (n === undefined) return bad(__DEV__ ? \"req needs n\" : \"REALTIME_FRAME_9\");\n const e = raw[\"e\"];\n if (!isEventName(e))\n return bad(__DEV__ ? \"e is not a valid event name\" : \"REALTIME_FRAME_4\", n);\n const ms = raw[\"ms\"];\n if (ms !== undefined && !(typeof ms === \"number\" && Number.isFinite(ms) && ms > 0))\n return bad(__DEV__ ? \"ms must be a positive number\" : \"REALTIME_FRAME_7\", n);\n return {\n ok: true,\n frame: {\n t: \"req\",\n n,\n c: c as string,\n e,\n d: (raw[\"d\"] ?? null) as Json,\n ...(ms === undefined ? {} : { ms }),\n },\n };\n }\n case \"pres\": {\n const refusal = needChannel();\n if (refusal !== undefined) return refusal;\n return { ok: true, frame: { t: \"pres\", c: c as string, s: (raw[\"s\"] ?? null) as Json } };\n }\n default:\n return bad(__DEV__ ? \"unknown frame type\" : \"REALTIME_FRAME_12\", n);\n }\n}\n\n/** Frames a server sends, as a client reads them. @since 1.0.0 */\nexport type ServerFrame =\n | {\n readonly t: \"ready\";\n readonly v: number;\n readonly id: string;\n readonly hb: number;\n readonly max: number;\n }\n | {\n readonly t: \"subbed\";\n readonly n: number;\n readonly c: string;\n readonly head: number;\n readonly gap?: boolean;\n readonly reset?: boolean;\n }\n | { readonly t: \"unsubbed\"; readonly c: string; readonly why?: string }\n | {\n readonly t: \"msg\";\n readonly c: string;\n readonly s: number;\n readonly e: string;\n readonly d: Json;\n readonly p?: string;\n readonly u?: string;\n readonly r?: 1;\n }\n | { readonly t: \"ack\"; readonly n: number; readonly s: number; readonly dup?: boolean }\n | { readonly t: \"rep\"; readonly n: number; readonly d: Json }\n | {\n readonly t: \"err\";\n readonly n?: number;\n readonly c?: string;\n readonly code: ErrorCode;\n readonly msg?: string;\n }\n | {\n readonly t: \"pres\";\n readonly c: string;\n readonly op: \"sync\" | \"join\" | \"set\" | \"leave\";\n readonly m: Member | readonly Member[];\n }\n | { readonly t: \"pong\"; readonly n?: number }\n | { readonly t: \"bye\"; readonly code: number; readonly why: string };\n\n/**\n * Decodes one server frame (client side). Returns `undefined` for anything that is not a known, well-formed frame, so a\n * client ignores what a newer server may send and never acts on a malformed frame.\n *\n * @param text The frame text.\n * @returns The frame, or undefined.\n * @example\n * import { decodeServerFrame } from \"@shadow-js/realtime\";\n *\n * decodeServerFrame('{\"t\":\"pong\"}'); // { t: \"pong\" }\n * decodeServerFrame(\"not json\"); // undefined\n * @since 1.0.0\n */\nexport function decodeServerFrame(text: string): ServerFrame | undefined {\n try {\n return serverFrameOf(JSON.parse(text));\n } catch {\n return undefined;\n }\n}\n\n/**\n * Decodes a server frame that is already parsed (the frames of an HTTP answer arrive inside one JSON document), with the\n * rules of {@link decodeServerFrame}.\n * @internal\n */\nexport function serverFrameOf(value: unknown): ServerFrame | undefined {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) return undefined;\n const raw = value as Record<string, unknown>;\n const str = (key: string): string | undefined => {\n const item = raw[key];\n return typeof item === \"string\" ? item : undefined;\n };\n const num = (key: string): number | undefined => {\n const item = raw[key];\n return isCount(item) ? item : undefined;\n };\n switch (raw[\"t\"]) {\n case \"ready\": {\n const v = num(\"v\");\n const id = str(\"id\");\n if (v === undefined || id === undefined) return undefined;\n return { t: \"ready\", v, id, hb: num(\"hb\") ?? 30_000, max: num(\"max\") ?? 65_536 };\n }\n case \"subbed\": {\n const n = num(\"n\");\n const c = str(\"c\");\n const head = num(\"head\");\n if (n === undefined || c === undefined || head === undefined) return undefined;\n return {\n t: \"subbed\",\n n,\n c,\n head,\n ...(raw[\"gap\"] === true ? { gap: true } : {}),\n ...(raw[\"reset\"] === true ? { reset: true } : {}),\n };\n }\n case \"unsubbed\": {\n const c = str(\"c\");\n if (c === undefined) return undefined;\n const why = str(\"why\");\n return why === undefined ? { t: \"unsubbed\", c } : { t: \"unsubbed\", c, why };\n }\n case \"msg\": {\n const c = str(\"c\");\n const s = num(\"s\");\n const e = str(\"e\");\n if (c === undefined || s === undefined || e === undefined || s < 1) return undefined;\n const p = str(\"p\");\n const u = str(\"u\");\n return {\n t: \"msg\",\n c,\n s,\n e,\n d: raw[\"d\"] as Json,\n ...(p === undefined ? {} : { p }),\n ...(u === undefined ? {} : { u }),\n ...(raw[\"r\"] === 1 ? { r: 1 as const } : {}),\n };\n }\n case \"ack\": {\n const n = num(\"n\");\n const s = num(\"s\");\n if (n === undefined || s === undefined) return undefined;\n return { t: \"ack\", n, s, ...(raw[\"dup\"] === true ? { dup: true } : {}) };\n }\n case \"rep\": {\n const n = num(\"n\");\n if (n === undefined) return undefined;\n return { t: \"rep\", n, d: raw[\"d\"] as Json };\n }\n case \"err\": {\n const code = str(\"code\");\n if (code === undefined) return undefined;\n const n = num(\"n\");\n const c = str(\"c\");\n const msg = str(\"msg\");\n return {\n t: \"err\",\n code: code as ErrorCode,\n ...(n === undefined ? {} : { n }),\n ...(c === undefined ? {} : { c }),\n ...(msg === undefined ? {} : { msg }),\n };\n }\n case \"pres\": {\n const c = str(\"c\");\n const op = raw[\"op\"];\n const m = raw[\"m\"];\n if (c === undefined || (op !== \"sync\" && op !== \"join\" && op !== \"set\" && op !== \"leave\"))\n return undefined;\n const member = (item: unknown): Member | undefined => {\n if (item === null || typeof item !== \"object\") return undefined;\n const entry = item as Record<string, unknown>;\n if (typeof entry[\"id\"] !== \"string\" || !isCount(entry[\"v\"])) return undefined;\n return {\n id: entry[\"id\"],\n v: entry[\"v\"],\n s: (entry[\"s\"] ?? null) as Json,\n ...(typeof entry[\"u\"] === \"string\" ? { u: entry[\"u\"] } : {}),\n };\n };\n if (op === \"sync\") {\n if (!Array.isArray(m)) return undefined;\n const list = m.map(member);\n if (list.some((item) => item === undefined)) return undefined;\n return { t: \"pres\", c, op, m: list as Member[] };\n }\n const one = member(m);\n return one === undefined ? undefined : { t: \"pres\", c, op, m: one };\n }\n case \"pong\": {\n const n = num(\"n\");\n return n === undefined ? { t: \"pong\" } : { t: \"pong\", n };\n }\n case \"bye\": {\n const code = num(\"code\");\n return code === undefined ? undefined : { t: \"bye\", code, why: str(\"why\") ?? \"\" };\n }\n default:\n return undefined;\n }\n}\n"],"mappings":"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"}
@@ -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 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};
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`)}}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())},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 L(e,t){for(let n of e.subs)n.active&&(n.pumping?n.buffer.length>=1e4?V(n,`gap`):n.buffer.push(t):t.seq===n.seq+1?R(n,t,!1):t.seq>n.seq+1?(n.buffer.push(t),z(n,void 0)):t.at>n.at&&V(n,`reset`))}function R(e,t,n){if(e.seq=t.seq,e.at=Math.max(e.at,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;(r===void 0||r.seq!==t.seq)&&(r=e.channel.frame={seq:t.seq,text:p(e.channel.name,t,!1)}),A(e.conn,r.text)}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.seq);let n=e.buffer[0];if(n!==void 0&&n.seq===e.seq+1){R(e,n,!1),e.buffer.shift();continue}let r=n===void 0?t??0:n.seq-1;if(e.seq>=r)break;let i=await _.history(e.channel.name,e.seq,Math.min(500,r-e.seq));if(!e.active)return;let a=i.messages[0];if(a===void 0||a.seq!==e.seq+1){if(!e.gapOk){V(e,`gap`);return}e.seq=(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&&V(e,`unavailable`)}finally{e.pumping=!1}e.active&&e.buffer.length>0&&z(e,void 0)}}function ne(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.queued===void 0?B(n,t):n.queued.push(t))}function B(e,t){M(e.conn,{t:`pres`,c:t.channel,op:t.op,m:t.member})}function V(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}}),U(e))}let H=/* @__PURE__ */ new Set;function U(e){let t=W(e);return H.add(t),t.finally(()=>H.delete(t)),t}async function W(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.feed,t.pfeed].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 G(e){return e.identity.user===void 0?`unauthorized`:`forbidden`}async function K(n,r){let{c:i,n:a}=r;if(n.subs.has(i))return N(n,a,`conflict`,i);if(n.subs.size>=h.maxChannelsPerConnection)return N(n,a,`limit`,i);let o=O(i);if(o===void 0)return N(n,a,G(n),i);let s=r.hasPresence||r.watch===!0,c={conn:n,channel:i};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)||!x.has(c))return N(n,a,G(n),i)}finally{x.delete(c)}if(r.hasPresence&&t(JSON.stringify(r.presence??null))>h.maxStateBytes)return N(n,a,`too_large`,i);if(n.closed)return;let l=F(i);if(l.subs.size>=h.maxSubscribersPerChannel)return N(n,a,`limit`,i);let u={conn:n,channel:l,...o,echo:r.echo!==!1,watch:r.watch===!0,version:0,state:null,seq:0,at:g(),pumping:!0,buffer:[],queued:s?[]:void 0,active:!0};n.subs.set(i,u),l.subs.add(u);try{await I(l,`feed`,()=>_.subscribe(l.name,e=>L(l,e))),s&&(await I(l,`pfeed`,()=>_.presenceSubscribe(l.name,e=>ne(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.seq=t:o>t?(d=!0,u.seq=t):(u.seq=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=re(u,r.presence??null),await u.joining),s&&await ie(u),!u.active)return;await z(u,t),u.gapOk=!1}catch(t){u.active&&(V(u),D(t,{where:`subscribe`,channel:i,connection:n.id}),N(n,a,t instanceof e?t.code:`unavailable`,i))}}function re(t,n){let r=t.channel.name,i=(S.get(r)??Promise.resolve()).then(async()=>{if(t.active){if((await _.presenceList(r)).length>=h.maxPresenceMembers)throw new e(`limit`);t.version=1,t.state=n,t.joined=!0,Q(),await _.presenceSet(r,q(t),h.presenceTtlMs)}}),a=()=>{S.get(r)===o&&S.delete(r)},o=i.then(a,a);return S.set(r,o),i}function q(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 ie(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.queued??[];e.queued=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)||B(e,t)}}async function ae(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);if(!e.publishBucket.take(g()))return J(e,void 0,n.c);r.version+=1,r.state=n.s;try{await _.presenceSet(n.c,q(r),h.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(g())&&P(e,1008,``)}async function oe(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,G(e),n);if(t.d.length>h.maxMessageBytes)return N(e,r,`too_large`,n);if(!e.publishBucket.take(g())||b.get(n)?.bucket.take(g())===!1)return J(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 se(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,G(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);if(!n.publishBucket.take(g()))return J(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);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 ce(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.ctl.take(g())&&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 K(t,a);case`unsub`:{let e=t.subs.get(a.c);if(e===void 0)return N(t,a.n,`not_subscribed`,a.c);V(e);return}case`pub`:return await oe(t,a);case`req`:return await se(t,a);case`pres`:return await ae(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 le(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 ce(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=>U(e))),t.release?.()}function Q(){if(C!==void 0||w)return;let e=setInterval(()=>void ue(),h.presenceHeartbeatMs);e.unref?.(),C=e}async function ue(){if(!T){T=!0;try{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;return}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,q(t),h.presenceTtlMs)}for(let e of t)await _.presenceReap(e)}catch(e){D(e,{where:`presenceHeartbeat`})}}finally{T=!1}}}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`);let a,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()),ctl: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,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),le(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`);if(!i(o)||c.key!==void 0&&!a(c.key))throw new e(`bad_frame`);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`);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`);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`);let s=(await _.presenceList(r)).find(e=>e.id===i);if(s===void 0||s.u!==n.user)throw new e(`forbidden`);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)t(e.conn,e.channel)&&(x.delete(e),n+=1);for(let e of[...y.values()])for(let r of[...e.subs.values()])t(e,r.channel.name)&&(V(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([...H]),te&&await _.close()}};return $}export{m as createHubCore};
2
2
  //# sourceMappingURL=hub.js.map