@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":"hub.js","names":[],"sources":["../../../src/server/hub.ts"],"sourcesContent":["import type { Adapter, PublishResult, Unsubscribe } from \"../adapter\";\nimport { RealtimeError } from \"../errors\";\nimport { memoryAdapter } from \"../memory\";\nimport {\n byteLength,\n type ClientFrame,\n decodeClientFrame,\n isChannelName,\n isEventName,\n isIdempotencyKey,\n type Json,\n type Member,\n PROTOCOL_VERSION,\n type PresenceEvent,\n type StoredMessage,\n} from \"../protocol\";\nimport { type HubLimits, resolveLimits, TokenBucket, WindowCounter } from \"./limits\";\nimport {\n type ChannelRule,\n type CompiledRule,\n compileRule,\n type Identity,\n matchRule,\n type RuleContext,\n} from \"./rules\";\n\n/**\n * What a hub needs from a connection's transport. A WebSocket, an SSE stream and an in-memory test client all fit.\n * @since 1.0.0\n */\nexport interface Transport {\n /** Sends one frame (text). False when the transport is closed. */\n send(frame: string): boolean;\n /** Closes the transport with a WebSocket close code and a reason of at most 123 bytes. */\n close(code: number, reason: string): void;\n /** Bytes accepted by `send` that the peer has not yet taken. When present, a connection over the limit is closed. */\n buffered?(): number;\n}\n\n/** Where an error reported to `onError` came from. @since 1.0.0 */\nexport interface ErrorContext {\n readonly where: string;\n readonly channel?: string;\n readonly connection?: string;\n}\n\n/** Options of {@link createHub}. @since 1.0.0 */\nexport interface HubOptions {\n /** Storage and fan-out. Default: a fresh `memoryAdapter()`, closed with the hub. */\n readonly adapter?: Adapter;\n /** Channel rules by pattern, in order. The first matching pattern wins. */\n readonly channels?: Readonly<Record<string, ChannelRule>>;\n /** Limits (see {@link HubLimits}). */\n readonly limits?: Partial<HubLimits>;\n /** Receives failures the hub handled (an adapter outage, a rule predicate that threw). Default: `console.error`. */\n readonly onError?: (error: unknown, context: ErrorContext) => void;\n /** The clock, in epoch milliseconds. Default `Date.now`. */\n readonly now?: () => number;\n}\n\n/** Counters of a running hub. @since 1.0.0 */\nexport interface HubStats {\n readonly connections: number;\n readonly channels: number;\n readonly subscriptions: number;\n readonly published: number;\n readonly delivered: number;\n readonly refused: number;\n readonly slowConsumers: number;\n}\n\n/** One connection, as the transport glue drives it. @since 1.0.0 */\nexport interface HubConnection {\n readonly id: string;\n readonly identity: Identity;\n /** Hands the hub one text frame from the peer. Frames are handled in order. */\n receive(text: string): void;\n /** Tells the hub the transport closed. Safe to call twice. */\n closed(): void;\n /** Resolves once every frame received so far was handled and every request answered. */\n settled(): Promise<void>;\n /** Resolves when the hub finished releasing what the connection held. */\n readonly done: Promise<void>;\n}\n\n/** How a connection differs from a long-lived one. @since 1.0.0 */\nexport interface ConnectOptions {\n /**\n * A connection that exists for one HTTP request: a request is answered for any identity the channel's `subscribe` rule\n * accepts (there is no subscription to look up), and nothing is kept after it closes.\n */\n readonly stateless?: boolean;\n /** Replaces the connection's own publish bucket, so a caller can share one across requests. */\n readonly publishBucket?: TokenBucket;\n /**\n * The id of a long-lived connection of the same user this request acts for (SSE clients post under their stream's id, so\n * `echo:false` and `publisher` work). Honored only when that connection is open on this server and has the same user.\n */\n readonly actingFor?: string;\n}\n\n/** Who to remove subscriptions of. At least one field. @since 1.0.0 */\nexport interface RevokeTarget {\n readonly user?: string;\n readonly connection?: string;\n readonly channel?: string;\n}\n\n/** The server object. @since 1.0.0 */\nexport interface HubCore {\n /** Adds a channel rule after the ones already registered. */\n channel(pattern: string, rule: ChannelRule): HubCore;\n /** Starts handling a connection. Sends `ready`. */\n connect(transport: Transport, identity?: Identity, options?: ConnectOptions): HubConnection;\n /** Publishes from the server (no rule applies). */\n publish(\n channel: string,\n event: string,\n data: unknown,\n options?: { readonly key?: string },\n ): Promise<PublishResult>;\n /** Removes matching subscriptions (a subscribe still awaiting its rule is refused); each subscriber gets `unsubbed why:\"revoked\"`. Returns how many were removed. */\n revoke(target: RevokeTarget): number;\n /** Counters. */\n stats(): HubStats;\n /** Closes every connection with 1001, stops timers, removes presence, settles requests. */\n close(): Promise<void>;\n /** @internal Changes the state of a presence member from outside its connection (SSE clients). */\n setMemberState(\n identity: Identity,\n channel: string,\n connectionId: string,\n state: Json,\n ): Promise<void>;\n /** @internal The resolved limits. */\n readonly limits: HubLimits;\n /** @internal The adapter. */\n readonly adapter: Adapter;\n /** @internal Reports an error. */\n report(error: unknown, context: ErrorContext): void;\n /** @internal Matches a channel against the rules. */\n match(\n channel: string,\n ): { readonly rule: ChannelRule; readonly params: Record<string, string> } | undefined;\n /** @internal Evaluates a rule permission for an identity. */\n allowed(\n rule: ChannelRule,\n op: RuleContext[\"op\"],\n channel: string,\n params: Record<string, string>,\n identity: Identity,\n ): Promise<boolean>;\n}\n\ninterface SubState {\n readonly conn: ConnState;\n readonly channel: ChanState;\n readonly rule: ChannelRule;\n readonly params: Record<string, string>;\n readonly echo: boolean;\n readonly watch: boolean;\n joined: boolean;\n version: number;\n /** The state this connection last published to the presence set, to write it back if the entry expired. */\n state: Json;\n lastSeq: number;\n /** The `at` of the last message delivered: a message with an old seq but a newer `at` means the numbering restarted. */\n lastAt: number;\n /** While true, live messages wait in `buffer` and `pump` delivers in order. */\n pumping: boolean;\n gapOk: boolean;\n buffer: StoredMessage[];\n presenceQueue: PresenceEvent[] | undefined;\n /** The join in progress, so that releasing the subscription waits for it and never leaves a member behind. */\n joining: Promise<void> | undefined;\n active: boolean;\n}\n\ninterface ChanState {\n readonly name: string;\n readonly subs: Set<SubState>;\n readonly bucket: TokenBucket;\n adapterSub: Promise<Unsubscribe> | undefined;\n presenceSub: Promise<Unsubscribe> | undefined;\n closing: boolean;\n /** Cache of the encoded frame of the message being fanned out. */\n frame: { readonly seq: number; readonly text: string } | undefined;\n}\n\ninterface ConnState {\n readonly id: string;\n readonly identity: Identity;\n readonly transport: Transport;\n readonly subs: Map<string, SubState>;\n readonly queue: string[];\n readonly publishBucket: TokenBucket;\n /** Spent by `sub` and `unsub` frames: subscription churn costs the adapter (and Redis) work like a publish does. */\n readonly controlBucket: TokenBucket;\n readonly invalid: WindowCounter;\n readonly limited: WindowCounter;\n readonly pending: Set<AbortController>;\n readonly stateless: boolean;\n readonly waiters: (() => void)[];\n running: boolean;\n closed: boolean;\n release: (() => void) | undefined;\n readonly done: Promise<void>;\n}\n\nconst REPLAY_PAGE = 500;\nconst MAX_PUMP_BUFFER = 10_000;\nconst ID_BYTES = 16;\n\n/** A random id: 128 bits, base64url. */\nfunction randomId(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(ID_BYTES));\n return btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction encodeFrame(frame: Record<string, unknown>): string {\n return JSON.stringify(frame);\n}\n\nfunction messageFrame(channel: string, message: StoredMessage, replayed: boolean): string {\n return (\n `{\"t\":\"msg\",\"c\":${JSON.stringify(channel)},\"s\":${message.seq},\"e\":${JSON.stringify(message.event)},\"d\":${message.data}` +\n (message.publisher === undefined ? \"\" : `,\"p\":${JSON.stringify(message.publisher)}`) +\n (message.user === undefined ? \"\" : `,\"u\":${JSON.stringify(message.user)}`) +\n (replayed ? ',\"r\":1}' : \"}\")\n );\n}\n\n/**\n * Creates the hub: the server side of `@shadow-js/realtime`. It owns connections and channel rules, enforces limits and talks to\n * one adapter. Wire it to a transport with `hub.websocket()`, `hub.sse()` and `hub.post()` (Dusk routes) or with `hub.connect`.\n *\n * @param options The adapter, the channel rules and the limits.\n * @returns The hub.\n * @throws RangeError for a limit that is not valid; TypeError for an invalid channel pattern.\n * @example\n * import { createHub } from \"@shadow-js/realtime/server\";\n *\n * const hub = createHub({\n * channels: { \"chat:{room}\": { subscribe: true, publish: ({ identity }) => identity.user !== undefined } },\n * });\n * const sent: string[] = [];\n * const connection = hub.connect({ send: (frame) => (sent.push(frame), true), close: () => {} });\n * connection.receive('{\"t\":\"sub\",\"n\":1,\"c\":\"chat:lobby\"}');\n * @since 1.0.0\n */\nexport function createHubCore(options: HubOptions = {}): HubCore {\n const limits = resolveLimits(options.limits);\n const now = options.now ?? Date.now;\n const ownsAdapter = options.adapter === undefined;\n const adapter =\n options.adapter ??\n memoryAdapter({\n now,\n onError: (error, context) =>\n report(error, { where: `memory.${context.where}`, channel: context.channel }),\n });\n const rules: CompiledRule[] = [];\n const conns = new Map<string, ConnState>();\n const chans = new Map<string, ChanState>();\n /** Subscribes whose rules are being awaited: a revoke issued meanwhile marks them, so the grant cannot outlive it. */\n const judging = new Set<{\n readonly conn: ConnState;\n readonly channel: string;\n revoked: boolean;\n }>();\n /** The tail of each channel's presence joins that are queued or running; an entry leaves when its chain is idle. */\n const joinChains = new Map<string, Promise<void>>();\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n let closing = false;\n let beating = false;\n const counters = { published: 0, delivered: 0, refused: 0, slowConsumers: 0 };\n\n const report = (error: unknown, context: ErrorContext): void => {\n if (options.onError !== undefined) {\n try {\n options.onError(error, context);\n } catch {\n // A failing reporter must not break the hub.\n }\n } else {\n console.error(`[realtime] ${context.where}`, error);\n }\n };\n\n function match(\n channel: string,\n ): { rule: ChannelRule; params: Record<string, string> } | undefined {\n for (const compiled of rules) {\n const params = matchRule(compiled, channel);\n if (params !== undefined) return { rule: compiled.rule, params };\n }\n return undefined;\n }\n\n async function allowed(\n rule: ChannelRule,\n op: RuleContext[\"op\"],\n channel: string,\n params: Record<string, string>,\n identity: Identity,\n ): Promise<boolean> {\n const permission = rule[op];\n if (permission === undefined || permission === false) return false;\n if (permission === true) return true;\n try {\n return (await permission({ channel, params, identity, op })) === true;\n } catch (error) {\n report(error, { where: `rule.${op}`, channel });\n throw new RealtimeError(\n \"unavailable\",\n __DEV__ ? \"the channel rule could not be evaluated\" : undefined,\n );\n }\n }\n\n function send(conn: ConnState, text: string): void {\n if (conn.closed) return;\n try {\n const buffered = conn.transport.buffered?.() ?? 0;\n if (buffered > limits.maxBufferedBytes) {\n counters.slowConsumers += 1;\n drop(conn, 1013, __DEV__ ? \"slow consumer\" : \"\");\n return;\n }\n if (conn.transport.send(text)) return;\n } catch (error) {\n // A transport that throws is a broken socket: this connection goes, the others are not affected (guarantee G13). The\n // transport is closed too, or its peer would keep a socket the hub no longer answers on until its own heartbeat gave up.\n report(error, { where: \"transport.send\", connection: conn.id });\n onTransport(conn, \"transport.close\", () =>\n conn.transport.close(1011, __DEV__ ? \"write failed\" : \"\"),\n );\n }\n void closeConnection(conn);\n }\n\n /** Runs a call on the transport; a transport that is already gone throws, which is only reported. */\n function onTransport(conn: ConnState, where: string, call: () => unknown): void {\n try {\n call();\n } catch (error) {\n report(error, { where, connection: conn.id });\n }\n }\n\n function sendFrame(conn: ConnState, frame: Record<string, unknown>): void {\n send(conn, encodeFrame(frame));\n }\n\n function fail(\n conn: ConnState,\n n: number | undefined,\n code: RealtimeError[\"code\"],\n channel?: string,\n message?: string,\n ): void {\n counters.refused += 1;\n sendFrame(conn, {\n t: \"err\",\n ...(n === undefined ? {} : { n }),\n ...(channel === undefined ? {} : { c: channel }),\n code,\n ...(message === undefined || message === \"\" ? {} : { msg: message }),\n });\n }\n\n /** Sends a final frame, closes the transport and releases the connection. */\n function drop(conn: ConnState, code: number, reason: string): void {\n if (conn.closed) return;\n // The connection is released whatever the transport does, so its failures are only reported.\n onTransport(conn, \"transport.send\", () =>\n conn.transport.send(encodeFrame({ t: \"bye\", code, why: reason })),\n );\n onTransport(conn, \"transport.close\", () => conn.transport.close(code, reason));\n void closeConnection(conn);\n }\n\n // --- channels ------------------------------------------------------------------------------------------------------\n\n function channelState(name: string): ChanState {\n let chan = chans.get(name);\n if (chan === undefined || chan.closing) {\n chan = {\n name,\n subs: new Set(),\n bucket: new TokenBucket(\n limits.channelPublishesPerSecond,\n limits.channelPublishesPerSecond,\n now(),\n ),\n adapterSub: undefined,\n presenceSub: undefined,\n closing: false,\n frame: undefined,\n };\n chans.set(name, chan);\n }\n return chan;\n }\n\n /** Opens one of the channel's two adapter feeds once; a failed attempt is forgotten so the next caller retries. */\n async function ensureFeed(\n chan: ChanState,\n key: \"adapterSub\" | \"presenceSub\",\n open: () => Promise<Unsubscribe>,\n ): Promise<void> {\n if (chan[key] === undefined) {\n const opened = open();\n chan[key] = opened;\n opened.catch(() => {\n chan[key] = undefined;\n });\n }\n await chan[key];\n }\n\n function onMessage(chan: ChanState, message: StoredMessage): void {\n for (const sub of chan.subs) {\n if (!sub.active) continue;\n if (sub.pumping) {\n // A consumer so far behind that it cannot catch up is resubscribed from its last seq instead of holding memory.\n if (sub.buffer.length >= MAX_PUMP_BUFFER) removeSub(sub, \"gap\");\n else sub.buffer.push(message);\n continue;\n }\n if (message.seq === sub.lastSeq + 1) deliver(sub, message, false);\n else if (message.seq > sub.lastSeq + 1) {\n sub.buffer.push(message);\n void pump(sub, undefined);\n } else if (message.at > sub.lastAt) removeSub(sub, \"reset\");\n // Any other seq at or below the cursor was already delivered: dropped (guarantee G2).\n }\n }\n\n function deliver(sub: SubState, message: StoredMessage, replayed: boolean): void {\n sub.lastSeq = message.seq;\n sub.lastAt = Math.max(sub.lastAt, message.at);\n if (!sub.echo && message.publisher === sub.conn.id) return;\n counters.delivered += 1;\n if (replayed) {\n send(sub.conn, messageFrame(sub.channel.name, message, true));\n return;\n }\n const cached = sub.channel.frame;\n const text =\n cached !== undefined && cached.seq === message.seq\n ? cached.text\n : messageFrame(sub.channel.name, message, false);\n if (cached === undefined || cached.seq !== message.seq)\n sub.channel.frame = { seq: message.seq, text };\n send(sub.conn, text);\n }\n\n /**\n * Delivers, in order and without a gap, the messages after `sub.lastSeq` up to `upTo` and whatever is buffered. Fetches the\n * missing ones from history. When history no longer has them: a subscription that reported a gap skips ahead; any other one\n * is removed with `unsubbed why:\"gap\"`.\n */\n async function pump(sub: SubState, upTo: number | undefined): Promise<void> {\n if (sub.pumping && upTo === undefined) return;\n sub.pumping = true;\n try {\n while (sub.active) {\n sub.buffer = sub.buffer.filter((message) => message.seq > sub.lastSeq);\n const next = sub.buffer[0];\n if (next !== undefined && next.seq === sub.lastSeq + 1) {\n deliver(sub, next, false);\n sub.buffer.shift();\n continue;\n }\n const target = next === undefined ? (upTo ?? 0) : next.seq - 1;\n if (sub.lastSeq >= target) break;\n const page = await adapter.history(\n sub.channel.name,\n sub.lastSeq,\n Math.min(REPLAY_PAGE, target - sub.lastSeq),\n );\n if (!sub.active) return;\n const first = page.messages[0];\n if (first === undefined || first.seq !== sub.lastSeq + 1) {\n if (!sub.gapOk) {\n removeSub(sub, \"gap\");\n return;\n }\n // The gap was reported at subscribe time: continue from what is retained.\n sub.lastSeq = (first?.seq ?? target + 1) - 1;\n continue;\n }\n for (const message of page.messages) {\n if (message.seq > target) break;\n deliver(sub, message, true);\n }\n }\n } catch (error) {\n report(error, { where: \"pump\", channel: sub.channel.name, connection: sub.conn.id });\n if (sub.active) removeSub(sub, \"unavailable\");\n } finally {\n sub.pumping = false;\n }\n // A message that arrived while the last history page was awaited is in the buffer; one more pass delivers it.\n if (sub.active && sub.buffer.length > 0) void pump(sub, undefined);\n }\n\n function onPresence(chan: ChanState, event: PresenceEvent): void {\n for (const sub of chan.subs) {\n // A change to this connection's own member made elsewhere (an HTTP post to another server) is what a write-back after an\n // expiry has to carry, so the connection's copy follows it.\n if (\n sub.joined &&\n event.op !== \"leave\" &&\n event.member.id === sub.conn.id &&\n event.member.v > sub.version\n ) {\n sub.version = event.member.v;\n sub.state = event.member.s;\n }\n if (!sub.active || (!sub.joined && !sub.watch)) continue;\n if (sub.presenceQueue !== undefined) sub.presenceQueue.push(event);\n else sendPresence(sub, event);\n }\n }\n\n function sendPresence(sub: SubState, event: PresenceEvent): void {\n sendFrame(sub.conn, { t: \"pres\", c: event.channel, op: event.op, m: event.member });\n }\n\n /** Removes a subscription from its connection and channel; tells the peer when `why` is given. */\n function removeSub(sub: SubState, why?: string): void {\n if (!sub.active) return;\n sub.active = false;\n sub.conn.subs.delete(sub.channel.name);\n sub.channel.subs.delete(sub);\n if (why !== undefined && !sub.conn.closed)\n sendFrame(sub.conn, { t: \"unsubbed\", c: sub.channel.name, ...(why === \"\" ? {} : { why }) });\n void releaseSub(sub);\n }\n\n /** The releases still running, so that `close` finishes them before it closes the adapter. */\n const releasing = new Set<Promise<void>>();\n\n function releaseSub(sub: SubState): Promise<void> {\n const running = release(sub);\n releasing.add(running);\n void running.finally(() => releasing.delete(running));\n return running;\n }\n\n async function release(sub: SubState): Promise<void> {\n const chan = sub.channel;\n try {\n // A join that is still being written must finish first, or its member would outlive the connection.\n await sub.joining?.catch(() => undefined);\n if (sub.joined) {\n sub.joined = false;\n await adapter.presenceRemove(chan.name, sub.conn.id);\n }\n } catch (error) {\n report(error, { where: \"presenceRemove\", channel: chan.name, connection: sub.conn.id });\n }\n if (chan.subs.size > 0 || chan.closing) return;\n chan.closing = true;\n if (chans.get(chan.name) === chan) chans.delete(chan.name);\n const stops = [chan.adapterSub, chan.presenceSub].filter(\n (item): item is Promise<Unsubscribe> => item !== undefined,\n );\n await Promise.all(\n stops.map(async (stop) => {\n try {\n await (await stop)();\n } catch (error) {\n report(error, { where: \"unsubscribe\", channel: chan.name });\n }\n }),\n );\n }\n\n // --- frames --------------------------------------------------------------------------------------------------------\n\n function identityDenied(conn: ConnState): RealtimeError[\"code\"] {\n return conn.identity.user === undefined ? \"unauthorized\" : \"forbidden\";\n }\n\n async function subscribe(\n conn: ConnState,\n frame: Extract<ClientFrame, { t: \"sub\" }>,\n ): Promise<void> {\n const { c: name, n } = frame;\n if (conn.subs.has(name))\n return fail(conn, n, \"conflict\", name, __DEV__ ? \"already subscribed\" : undefined);\n if (conn.subs.size >= limits.maxChannelsPerConnection)\n return fail(\n conn,\n n,\n \"limit\",\n name,\n __DEV__ ? \"too many channels on this connection\" : undefined,\n );\n const found = match(name);\n if (found === undefined) return fail(conn, n, identityDenied(conn), name);\n const wantsPresence = frame.hasPresence || frame.watch === true;\n const pending = { conn, channel: name, revoked: false };\n judging.add(pending);\n try {\n if (\n !(await allowed(found.rule, \"subscribe\", name, found.params, conn.identity)) ||\n (wantsPresence &&\n !(await allowed(found.rule, \"presence\", name, found.params, conn.identity))) ||\n pending.revoked\n )\n return fail(conn, n, identityDenied(conn), name);\n } finally {\n judging.delete(pending);\n }\n if (\n frame.hasPresence &&\n byteLength(JSON.stringify(frame.presence ?? null)) > limits.maxStateBytes\n )\n return fail(\n conn,\n n,\n \"too_large\",\n name,\n __DEV__ ? \"the presence state is too large\" : undefined,\n );\n if (conn.closed) return;\n const chan = channelState(name);\n if (chan.subs.size >= limits.maxSubscribersPerChannel)\n return fail(conn, n, \"limit\", name, __DEV__ ? \"the channel is full\" : undefined);\n const sub: SubState = {\n conn,\n channel: chan,\n rule: found.rule,\n params: found.params,\n echo: frame.echo !== false,\n watch: frame.watch === true,\n joined: false,\n version: 0,\n state: null,\n lastSeq: 0,\n lastAt: now(),\n pumping: true,\n gapOk: false,\n buffer: [],\n presenceQueue: wantsPresence ? [] : undefined,\n joining: undefined,\n active: true,\n };\n conn.subs.set(name, sub);\n chan.subs.add(sub);\n try {\n await ensureFeed(chan, \"adapterSub\", () =>\n adapter.subscribe(chan.name, (message) => onMessage(chan, message)),\n );\n if (wantsPresence) {\n await ensureFeed(chan, \"presenceSub\", () =>\n adapter.presenceSubscribe(chan.name, (event) => onPresence(chan, event)),\n );\n startHeartbeat();\n }\n const probe = await adapter.history(name, Number.MAX_SAFE_INTEGER, 0);\n if (!sub.active) return;\n const head = probe.head;\n const after = frame.after;\n let gap = false;\n let reset = false;\n if (after === undefined) {\n sub.lastSeq = head;\n } else if (after > head) {\n reset = true;\n sub.lastSeq = head;\n } else {\n sub.lastSeq = after;\n gap = probe.oldest === 0 ? head > after : probe.oldest > after + 1;\n sub.gapOk = gap;\n }\n sendFrame(conn, {\n t: \"subbed\",\n n,\n c: name,\n head,\n ...(gap ? { gap: true } : {}),\n ...(reset ? { reset: true } : {}),\n });\n if (frame.hasPresence) {\n sub.joining = joinPresence(sub, frame.presence ?? null);\n await sub.joining;\n }\n if (wantsPresence) await syncPresence(sub);\n if (!sub.active) return;\n await pump(sub, head);\n sub.gapOk = false;\n } catch (error) {\n if (sub.active) {\n removeSub(sub);\n report(error, { where: \"subscribe\", channel: name, connection: conn.id });\n fail(conn, n, error instanceof RealtimeError ? error.code : \"unavailable\", name);\n }\n }\n }\n\n /** Joins one at a time per channel: the capacity check reads the set, so two joins in flight would both see room. */\n function joinPresence(sub: SubState, state: Json): Promise<void> {\n const name = sub.channel.name;\n const run = (joinChains.get(name) ?? Promise.resolve()).then(() => joinOne(sub, state));\n const settle = (): void => {\n if (joinChains.get(name) === tail) joinChains.delete(name);\n };\n const tail: Promise<void> = run.then(settle, settle);\n joinChains.set(name, tail);\n return run;\n }\n\n async function joinOne(sub: SubState, state: Json): Promise<void> {\n if (!sub.active) return;\n const existing = await adapter.presenceList(sub.channel.name);\n if (existing.length >= limits.maxPresenceMembers)\n throw new RealtimeError(\"limit\", __DEV__ ? \"the presence set is full\" : undefined);\n sub.version = 1;\n sub.state = state;\n sub.joined = true;\n startHeartbeat();\n await adapter.presenceSet(sub.channel.name, memberOf(sub), limits.presenceTtlMs);\n }\n\n /** The presence member a joined subscription stands for. */\n function memberOf(sub: SubState): Member {\n const { user } = sub.conn.identity;\n return {\n id: sub.conn.id,\n ...(user === undefined ? {} : { u: user }),\n s: sub.state,\n v: sub.version,\n };\n }\n\n async function syncPresence(sub: SubState): Promise<void> {\n const members = await adapter.presenceList(sub.channel.name);\n if (!sub.active) return;\n sendFrame(sub.conn, { t: \"pres\", c: sub.channel.name, op: \"sync\", m: members });\n const queued = sub.presenceQueue ?? [];\n sub.presenceQueue = undefined;\n const known = new Map(members.map((member) => [member.id, member.v]));\n for (const event of queued) {\n // What the snapshot already shows is not announced again.\n const version = known.get(event.member.id);\n if (\n event.op === \"leave\"\n ? version === undefined\n : version !== undefined && version >= event.member.v\n )\n continue;\n sendPresence(sub, event);\n }\n }\n\n async function setPresence(\n conn: ConnState,\n frame: Extract<ClientFrame, { t: \"pres\" }>,\n ): Promise<void> {\n const sub = conn.subs.get(frame.c);\n if (sub === undefined || !sub.joined) return fail(conn, undefined, \"not_subscribed\", frame.c);\n if (byteLength(JSON.stringify(frame.s)) > limits.maxStateBytes)\n return fail(\n conn,\n undefined,\n \"too_large\",\n frame.c,\n __DEV__ ? \"the presence state is too large\" : undefined,\n );\n if (!conn.publishBucket.take(now())) return limit(conn, undefined, frame.c);\n sub.version += 1;\n sub.state = frame.s;\n try {\n await adapter.presenceSet(frame.c, memberOf(sub), limits.presenceTtlMs);\n } catch (error) {\n report(error, { where: \"presenceSet\", channel: frame.c, connection: conn.id });\n fail(conn, undefined, \"unavailable\", frame.c);\n }\n }\n\n function limit(conn: ConnState, n: number | undefined, channel: string): void {\n fail(conn, n, \"rate_limited\", channel);\n if (conn.limited.hit(now())) drop(conn, 1008, __DEV__ ? \"rate limit exceeded\" : \"\");\n }\n\n async function publishFrame(\n conn: ConnState,\n frame: Extract<ClientFrame, { t: \"pub\" }>,\n ): Promise<void> {\n const { c: name, n } = frame;\n const found = match(name);\n if (\n found === undefined ||\n !(await allowed(found.rule, \"publish\", name, found.params, conn.identity))\n )\n return fail(conn, n, identityDenied(conn), name);\n if (frame.d.length > limits.maxMessageBytes)\n return fail(conn, n, \"too_large\", name, __DEV__ ? \"the message is too large\" : undefined);\n // A channel with no subscriber on this server has no bucket: only the connection's own rate applies to it.\n if (!conn.publishBucket.take(now()) || chans.get(name)?.bucket.take(now()) === false)\n return limit(conn, n, name);\n try {\n const result = await adapter.publish(\n name,\n {\n event: frame.e,\n data: frame.d,\n publisher: conn.id,\n ...(conn.identity.user === undefined ? {} : { user: conn.identity.user }),\n },\n {\n history: found.rule.history ?? limits.history,\n historyTtlMs: limits.historyTtlMs,\n idempotencyTtlMs: limits.idempotencyTtlMs,\n // An idempotency key is only unique per publisher: the adapters hold one key table per channel, so without a\n // scope a client could mark another user's (or the server's) publish as a duplicate by reusing its key. A\n // signed-in user keeps the scope across connections; anonymous connections share one. Keys never contain\n // \"/\", so `key/scope` cannot be formed two ways.\n ...(frame.k === undefined\n ? {}\n : {\n key: `${frame.k}/${conn.identity.user === undefined ? \"a\" : `u:${conn.identity.user}`}`,\n }),\n },\n );\n counters.published += result.duplicate ? 0 : 1;\n if (n !== undefined)\n sendFrame(conn, { t: \"ack\", n, s: result.seq, ...(result.duplicate ? { dup: true } : {}) });\n } catch (error) {\n report(error, { where: \"publish\", channel: name, connection: conn.id });\n fail(conn, n, \"unavailable\", name);\n }\n }\n\n async function request(\n conn: ConnState,\n frame: Extract<ClientFrame, { t: \"req\" }>,\n ): Promise<void> {\n const { c: name, n } = frame;\n let target: { rule: ChannelRule; params: Record<string, string> } | undefined =\n conn.subs.get(name);\n if (target === undefined && conn.stateless) {\n const found = match(name);\n if (\n found !== undefined &&\n (await allowed(found.rule, \"subscribe\", name, found.params, conn.identity))\n )\n target = found;\n }\n if (target === undefined)\n return fail(\n conn,\n n,\n identityDenied(conn) === \"unauthorized\" && conn.stateless\n ? \"unauthorized\"\n : \"not_subscribed\",\n name,\n );\n const sub = target;\n const handler = sub.rule.handlers?.[frame.e];\n if (handler === undefined) return fail(conn, n, \"no_handler\", name);\n if (conn.pending.size >= limits.maxPendingRequests)\n return fail(conn, n, \"limit\", name, __DEV__ ? \"too many requests in flight\" : undefined);\n if (!conn.publishBucket.take(now())) return limit(conn, n, name);\n const controller = new AbortController();\n conn.pending.add(controller);\n const timeout = Math.min(frame.ms ?? limits.requestTimeoutMs, limits.requestTimeoutMs);\n const timer = setTimeout(() => controller.abort(new RealtimeError(\"timeout\")), timeout);\n void (async () => {\n try {\n const run = Promise.resolve(\n handler({\n channel: name,\n params: sub.params,\n identity: conn.identity,\n event: frame.e,\n data: frame.d,\n signal: controller.signal,\n }),\n );\n const aborted = new Promise<never>((_resolve, reject) => {\n controller.signal.addEventListener(\"abort\", () => reject(controller.signal.reason), {\n once: true,\n });\n });\n const reply = (await Promise.race([run, aborted])) ?? null;\n const text = JSON.stringify(reply);\n if (text === undefined || byteLength(text) > limits.maxMessageBytes)\n return fail(conn, n, \"too_large\", name, __DEV__ ? \"the reply is too large\" : undefined);\n send(conn, `{\"t\":\"rep\",\"n\":${n},\"d\":${text}}`);\n } catch (error) {\n if (error instanceof RealtimeError)\n fail(\n conn,\n n,\n error.code,\n name,\n error.code === \"timeout\" || error.code === \"closed\" ? undefined : error.message,\n );\n else {\n report(error, { where: \"request\", channel: name, connection: conn.id });\n fail(conn, n, \"handler_error\", name);\n }\n } finally {\n clearTimeout(timer);\n conn.pending.delete(controller);\n settle(conn);\n }\n })();\n }\n\n /** Answers a frame that cannot be handled; a peer that keeps sending them is dropped. */\n function refuseFrame(\n conn: ConnState,\n n: number | undefined,\n code: RealtimeError[\"code\"],\n why?: string,\n ): void {\n fail(conn, n, code, undefined, why);\n if (conn.invalid.hit(now())) drop(conn, 1008, __DEV__ ? \"too many invalid frames\" : \"\");\n }\n\n async function handle(conn: ConnState, text: string): Promise<void> {\n const decoded = decodeClientFrame(text, limits.maxMessageBytes);\n if (!decoded.ok) {\n refuseFrame(conn, decoded.n, decoded.code, decoded.why);\n return;\n }\n const frame = decoded.frame;\n const n = \"n\" in frame ? frame.n : undefined;\n // An `unsub` spends a token but is never refused: the client cannot tell a refused one (the answer names no frame), and would\n // believe it left a channel the hub still holds, so its next `sub` of that channel would be a conflict.\n if (\n (frame.t === \"sub\" || frame.t === \"unsub\") &&\n !conn.controlBucket.take(now()) &&\n frame.t === \"sub\"\n )\n return limit(conn, n, frame.c);\n try {\n switch (frame.t) {\n case \"hello\":\n return;\n case \"ping\":\n sendFrame(conn, { t: \"pong\", ...(frame.n === undefined ? {} : { n: frame.n }) });\n return;\n case \"sub\":\n return await subscribe(conn, frame);\n case \"unsub\": {\n const sub = conn.subs.get(frame.c);\n if (sub === undefined) return fail(conn, frame.n, \"not_subscribed\", frame.c);\n removeSub(sub);\n return;\n }\n case \"pub\":\n return await publishFrame(conn, frame);\n case \"req\":\n return await request(conn, frame);\n case \"pres\":\n return await setPresence(conn, frame);\n }\n } catch (error) {\n const channel = \"c\" in frame ? frame.c : undefined;\n if (error instanceof RealtimeError) fail(conn, n, error.code, channel);\n else {\n report(error, {\n where: \"frame\",\n connection: conn.id,\n ...(channel === undefined ? {} : { channel }),\n });\n fail(conn, n, \"unavailable\", channel);\n }\n }\n }\n\n async function run(conn: ConnState): Promise<void> {\n if (conn.running) return;\n conn.running = true;\n try {\n for (let text = conn.queue.shift(); text !== undefined; text = conn.queue.shift()) {\n if (conn.closed) break;\n try {\n await handle(conn, text);\n } catch (error) {\n if (error instanceof RealtimeError) fail(conn, undefined, error.code);\n else {\n report(error, { where: \"frame\", connection: conn.id });\n fail(conn, undefined, \"unavailable\");\n }\n }\n }\n } finally {\n conn.running = false;\n settle(conn);\n }\n }\n\n /** Wakes the callers of `settled` when the connection has nothing left to do. */\n function settle(conn: ConnState): void {\n if (conn.running || conn.queue.length > 0 || conn.pending.size > 0) return;\n for (const wake of conn.waiters.splice(0)) wake();\n }\n\n async function closeConnection(conn: ConnState): Promise<void> {\n if (conn.closed) return conn.done;\n conn.closed = true;\n if (conns.get(conn.id) === conn) conns.delete(conn.id);\n for (const controller of conn.pending) controller.abort(new RealtimeError(\"closed\"));\n conn.pending.clear();\n conn.queue.length = 0;\n for (const wake of conn.waiters.splice(0)) wake();\n const subs = [...conn.subs.values()];\n for (const sub of subs) {\n sub.active = false;\n sub.channel.subs.delete(sub);\n }\n conn.subs.clear();\n await Promise.allSettled(subs.map((sub) => releaseSub(sub)));\n conn.release?.();\n }\n\n // --- presence heartbeat --------------------------------------------------------------------------------------------\n\n function startHeartbeat(): void {\n if (heartbeat !== undefined || closing) return;\n const timer = setInterval(() => void beat(), limits.presenceHeartbeatMs);\n // Node's timer can be unref'd so an idle hub does not keep the process alive; other runtimes return a number.\n (timer as unknown as { unref?: () => void }).unref?.();\n heartbeat = timer;\n }\n\n async function beat(): Promise<void> {\n if (beating) return;\n beating = true;\n try {\n await beatOnce();\n } finally {\n beating = false;\n }\n }\n\n async function beatOnce(): Promise<void> {\n const refs: { channel: string; id: string }[] = [];\n const watched = new Set<string>();\n for (const conn of conns.values())\n for (const sub of conn.subs.values()) {\n if (sub.joined) refs.push({ channel: sub.channel.name, id: conn.id });\n if (sub.joined || sub.watch) watched.add(sub.channel.name);\n }\n if (refs.length === 0 && watched.size === 0) {\n clearInterval(heartbeat);\n heartbeat = undefined;\n return;\n }\n try {\n const missing = await adapter.presenceTouch(refs, limits.presenceTtlMs);\n for (const ref of missing) {\n const sub = conns.get(ref.id)?.subs.get(ref.channel);\n if (sub === undefined || !sub.joined) continue;\n await adapter.presenceSet(ref.channel, memberOf(sub), limits.presenceTtlMs);\n }\n for (const channel of watched) await adapter.presenceReap(channel);\n } catch (error) {\n report(error, { where: \"presenceHeartbeat\" });\n }\n }\n\n // --- the hub -------------------------------------------------------------------------------------------------------\n\n for (const [pattern, rule] of Object.entries(options.channels ?? {}))\n rules.push(compileRule(pattern, rule));\n\n const core: HubCore = {\n limits,\n adapter,\n report,\n match,\n allowed,\n\n channel(pattern, rule) {\n rules.push(compileRule(pattern, rule));\n return core;\n },\n\n connect(transport, identity = {}, connectOptions = {}) {\n if (closing) throw new RealtimeError(\"closed\", __DEV__ ? \"the hub is closed\" : undefined);\n let release: () => void = () => undefined;\n const done = new Promise<void>((resolve) => {\n release = resolve;\n });\n const owner =\n connectOptions.actingFor === undefined ? undefined : conns.get(connectOptions.actingFor);\n const acting =\n owner !== undefined && identity.user !== undefined && owner.identity.user === identity.user;\n const conn: ConnState = {\n id: acting ? (connectOptions.actingFor as string) : randomId(),\n identity,\n transport,\n subs: new Map(),\n queue: [],\n publishBucket:\n connectOptions.publishBucket ??\n new TokenBucket(limits.publishesPerSecond, limits.publishBurst, now()),\n // The refill is the publish rate; the burst always covers a connection declaring all its channels at once.\n controlBucket: new TokenBucket(\n limits.publishesPerSecond,\n Math.max(limits.publishBurst, limits.maxChannelsPerConnection),\n now(),\n ),\n stateless: connectOptions.stateless === true,\n waiters: [],\n invalid: new WindowCounter(10, 60_000),\n limited: new WindowCounter(50, 10_000),\n pending: new Set(),\n running: false,\n closed: false,\n release,\n done,\n };\n // An acting connection shares its owner's id: it must not replace the owner in the table.\n if (!acting) conns.set(conn.id, conn);\n sendFrame(conn, {\n t: \"ready\",\n v: PROTOCOL_VERSION,\n id: conn.id,\n hb: limits.heartbeatMs,\n max: limits.maxMessageBytes,\n });\n return {\n id: conn.id,\n identity,\n done,\n receive(text) {\n if (conn.closed) return;\n // Judged before it takes a place in the queue: a frame over the limit can never be handled, so it must not be able to\n // fill the queue (or be held until its turn) however many arrive.\n if (byteLength(text) > limits.maxMessageBytes) {\n refuseFrame(conn, undefined, \"too_large\");\n return;\n }\n if (conn.queue.length >= limits.maxQueuedFrames) {\n drop(conn, 1013, __DEV__ ? \"too many frames waiting\" : \"\");\n return;\n }\n conn.queue.push(text);\n void run(conn);\n },\n closed() {\n void closeConnection(conn);\n },\n settled() {\n return new Promise<void>((resolve) => {\n conn.waiters.push(resolve);\n settle(conn);\n });\n },\n };\n },\n\n async publish(channel, event, data, publishOptions = {}) {\n if (!isChannelName(channel))\n throw new RealtimeError(\n \"bad_channel\",\n __DEV__ ? `\"${channel}\" is not a valid channel name` : undefined,\n );\n if (!isEventName(event))\n throw new RealtimeError(\n \"bad_frame\",\n __DEV__ ? `\"${event}\" is not a valid event name` : undefined,\n );\n if (publishOptions.key !== undefined && !isIdempotencyKey(publishOptions.key))\n throw new RealtimeError(\n \"bad_frame\",\n __DEV__ ? \"the idempotency key is not valid\" : undefined,\n );\n let text: string | undefined;\n try {\n text = JSON.stringify(data === undefined ? null : data);\n } catch (error) {\n throw new TypeError(__DEV__ ? \"the data is not JSON\" : \"REALTIME_DATA\", {\n cause: error,\n });\n }\n if (text === undefined)\n throw new TypeError(__DEV__ ? \"the data is not JSON\" : \"REALTIME_DATA\");\n if (byteLength(text) > limits.maxMessageBytes)\n throw new RealtimeError(\"too_large\", __DEV__ ? \"the message is too large\" : undefined);\n const found = match(channel);\n const result = await adapter.publish(\n channel,\n { event, data: text },\n {\n history: found?.rule.history ?? limits.history,\n historyTtlMs: limits.historyTtlMs,\n idempotencyTtlMs: limits.idempotencyTtlMs,\n ...(publishOptions.key === undefined ? {} : { key: `${publishOptions.key}/s` }),\n },\n );\n counters.published += result.duplicate ? 0 : 1;\n return result;\n },\n\n async setMemberState(identity, channel, connectionId, state) {\n if (identity.user === undefined)\n throw new RealtimeError(\n \"unauthorized\",\n __DEV__ ? \"presence changes over HTTP need a signed-in user\" : undefined,\n );\n const found = match(channel);\n if (\n found === undefined ||\n !(await allowed(found.rule, \"presence\", channel, found.params, identity))\n )\n throw new RealtimeError(\"forbidden\");\n if (byteLength(JSON.stringify(state)) > limits.maxStateBytes)\n throw new RealtimeError(\n \"too_large\",\n __DEV__ ? \"the presence state is too large\" : undefined,\n );\n const members = await adapter.presenceList(channel);\n const member = members.find((item) => item.id === connectionId);\n if (member === undefined || member.u !== identity.user)\n throw new RealtimeError(\"forbidden\", __DEV__ ? \"that connection is not yours\" : undefined);\n // The connection may live on this server: its own copy of the member is what the heartbeat writes back after an expiry, so\n // it moves with the stored one (the same for a connection on another server, through the presence event below).\n const local = conns.get(connectionId)?.subs.get(channel);\n const version = Math.max(member.v, local?.version ?? 0) + 1;\n if (local?.joined === true) {\n local.version = version;\n local.state = state;\n }\n await adapter.presenceSet(\n channel,\n { id: member.id, u: member.u, s: state, v: version },\n limits.presenceTtlMs,\n );\n },\n\n revoke(target) {\n if (\n target.user === undefined &&\n target.connection === undefined &&\n target.channel === undefined\n )\n throw new TypeError(\n __DEV__ ? \"revoke needs a user, a connection or a channel.\" : \"REALTIME_REVOKE\",\n );\n const hit = (conn: ConnState, channel: string): boolean =>\n (target.connection === undefined || conn.id === target.connection) &&\n (target.user === undefined || conn.identity.user === target.user) &&\n (target.channel === undefined || channel === target.channel);\n let count = 0;\n for (const item of judging) {\n if (item.revoked || !hit(item.conn, item.channel)) continue;\n item.revoked = true;\n count += 1;\n }\n for (const conn of [...conns.values()]) {\n for (const sub of [...conn.subs.values()]) {\n if (!hit(conn, sub.channel.name)) continue;\n removeSub(sub, \"revoked\");\n count += 1;\n }\n }\n return count;\n },\n\n stats() {\n let subscriptions = 0;\n for (const conn of conns.values()) subscriptions += conn.subs.size;\n return {\n connections: conns.size,\n channels: chans.size,\n subscriptions,\n ...counters,\n };\n },\n\n async close() {\n if (closing) return;\n closing = true;\n if (heartbeat !== undefined) clearInterval(heartbeat);\n heartbeat = undefined;\n const all = [...conns.values()];\n for (const conn of all) drop(conn, 1001, __DEV__ ? \"going away\" : \"\");\n await Promise.all(all.map((conn) => conn.done));\n await Promise.allSettled([...releasing]);\n if (ownsAdapter) await adapter.close();\n },\n };\n return core;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"hub.js","names":[],"sources":["../../../src/server/hub.ts"],"sourcesContent":["import type { Adapter, PublishResult, Unsubscribe } from \"../adapter\";\nimport { RealtimeError } from \"../errors\";\nimport { memoryAdapter } from \"../memory\";\nimport {\n byteLength,\n type ClientFrame,\n decodeClientFrame,\n isChannelName,\n isEventName,\n isIdempotencyKey,\n type Json,\n type Member,\n PROTOCOL_VERSION,\n type PresenceEvent,\n type StoredMessage,\n} from \"../protocol\";\nimport { type HubLimits, resolveLimits, TokenBucket, WindowCounter } from \"./limits\";\nimport {\n type ChannelRule,\n type CompiledRule,\n compileRule,\n type Identity,\n matchRule,\n type RuleContext,\n} from \"./rules\";\n\n/**\n * What a hub needs from a connection's transport. A WebSocket, an SSE stream and an in-memory test client all fit.\n * @since 1.0.0\n */\nexport interface Transport {\n /** Sends one frame (text). False when the transport is closed. */\n send(frame: string): boolean;\n /** Closes the transport with a WebSocket close code and a reason of at most 123 bytes. */\n close(code: number, reason: string): void;\n /** Bytes accepted by `send` that the peer has not yet taken. When present, a connection over the limit is closed. */\n buffered?(): number;\n}\n\n/** Where an error reported to `onError` came from. @since 1.0.0 */\nexport interface ErrorContext {\n readonly where: string;\n readonly channel?: string;\n readonly connection?: string;\n}\n\n/** Options of {@link createHub}. @since 1.0.0 */\nexport interface HubOptions {\n /** Storage and fan-out. Default: a fresh `memoryAdapter()`, closed with the hub. */\n readonly adapter?: Adapter;\n /** Channel rules by pattern, in order. The first matching pattern wins. */\n readonly channels?: Readonly<Record<string, ChannelRule>>;\n /** Limits (see {@link HubLimits}). */\n readonly limits?: Partial<HubLimits>;\n /** Receives failures the hub handled (an adapter outage, a rule predicate that threw). Default: `console.error`. */\n readonly onError?: (error: unknown, context: ErrorContext) => void;\n /** The clock, in epoch milliseconds. Default `Date.now`. */\n readonly now?: () => number;\n}\n\n/** Counters of a running hub. @since 1.0.0 */\nexport interface HubStats {\n readonly connections: number;\n readonly channels: number;\n readonly subscriptions: number;\n readonly published: number;\n readonly delivered: number;\n readonly refused: number;\n readonly slowConsumers: number;\n}\n\n/** One connection, as the transport glue drives it. @since 1.0.0 */\nexport interface HubConnection {\n readonly id: string;\n readonly identity: Identity;\n /** Hands the hub one text frame from the peer. Frames are handled in order. */\n receive(text: string): void;\n /** Tells the hub the transport closed. Safe to call twice. */\n closed(): void;\n /** Resolves once every frame received so far was handled and every request answered. */\n settled(): Promise<void>;\n /** Resolves when the hub finished releasing what the connection held. */\n readonly done: Promise<void>;\n}\n\n/** How a connection differs from a long-lived one. @since 1.0.0 */\nexport interface ConnectOptions {\n /**\n * A connection that exists for one HTTP request: a request is answered for any identity the channel's `subscribe` rule\n * accepts (there is no subscription to look up), and nothing is kept after it closes.\n */\n readonly stateless?: boolean;\n /** Replaces the connection's own publish bucket, so a caller can share one across requests. */\n readonly publishBucket?: TokenBucket;\n /**\n * The id of a long-lived connection of the same user this request acts for (SSE clients post under their stream's id, so\n * `echo:false` and `publisher` work). Honored only when that connection is open on this server and has the same user.\n */\n readonly actingFor?: string;\n}\n\n/** Who to remove subscriptions of. At least one field. @since 1.0.0 */\nexport interface RevokeTarget {\n readonly user?: string;\n readonly connection?: string;\n readonly channel?: string;\n}\n\n/** The server object. @since 1.0.0 */\nexport interface HubCore {\n /** Adds a channel rule after the ones already registered. */\n channel(pattern: string, rule: ChannelRule): HubCore;\n /** Starts handling a connection. Sends `ready`. */\n connect(transport: Transport, identity?: Identity, options?: ConnectOptions): HubConnection;\n /** Publishes from the server (no rule applies). */\n publish(\n channel: string,\n event: string,\n data: unknown,\n options?: { readonly key?: string },\n ): Promise<PublishResult>;\n /** Removes matching subscriptions (a subscribe still awaiting its rule is refused); each subscriber gets `unsubbed why:\"revoked\"`. Returns how many were removed. */\n revoke(target: RevokeTarget): number;\n /** Counters. */\n stats(): HubStats;\n /** Closes every connection with 1001, stops timers, removes presence, settles requests. */\n close(): Promise<void>;\n /** @internal Changes the state of a presence member from outside its connection (SSE clients). */\n setMemberState(\n identity: Identity,\n channel: string,\n connectionId: string,\n state: Json,\n ): Promise<void>;\n /** @internal The resolved limits. */\n readonly limits: HubLimits;\n /** @internal The adapter. */\n readonly adapter: Adapter;\n /** @internal Reports an error. */\n report(error: unknown, context: ErrorContext): void;\n /** @internal Matches a channel against the rules. */\n match(\n channel: string,\n ): { readonly rule: ChannelRule; readonly params: Record<string, string> } | undefined;\n /** @internal Evaluates a rule permission for an identity. */\n allowed(\n rule: ChannelRule,\n op: RuleContext[\"op\"],\n channel: string,\n params: Record<string, string>,\n identity: Identity,\n ): Promise<boolean>;\n}\n\ninterface SubState {\n readonly conn: ConnState;\n readonly channel: ChanState;\n readonly rule: ChannelRule;\n readonly params: Record<string, string>;\n readonly echo: boolean;\n readonly watch: boolean;\n joined?: boolean;\n version: number;\n /** The state this connection last published to the presence set, to write it back if the entry expired. */\n state: Json;\n seq: number;\n /** The `at` of the last message delivered: a message with an old seq but a newer `at` means the numbering restarted. */\n at: number;\n /** While true, live messages wait in `buffer` and `pump` delivers in order. */\n pumping: boolean;\n gapOk?: boolean;\n buffer: StoredMessage[];\n queued: PresenceEvent[] | undefined;\n /** The join in progress, so that releasing the subscription waits for it and never leaves a member behind. */\n joining?: Promise<void> | undefined;\n active: boolean;\n}\n\ninterface ChanState {\n readonly name: string;\n readonly subs: Set<SubState>;\n readonly bucket: TokenBucket;\n feed?: Promise<Unsubscribe> | undefined;\n pfeed?: Promise<Unsubscribe> | undefined;\n closing?: boolean;\n /** Cache of the encoded frame of the message being fanned out. */\n frame?: { readonly seq: number; readonly text: string } | undefined;\n}\n\ninterface ConnState {\n readonly id: string;\n readonly identity: Identity;\n readonly transport: Transport;\n readonly subs: Map<string, SubState>;\n readonly queue: string[];\n readonly publishBucket: TokenBucket;\n /** Spent by `sub` and `unsub` frames: subscription churn costs the adapter (and Redis) work like a publish does. */\n readonly ctl: TokenBucket;\n readonly invalid: WindowCounter;\n readonly limited: WindowCounter;\n readonly pending: Set<AbortController>;\n readonly stateless: boolean;\n readonly waiters: (() => void)[];\n running?: boolean;\n closed?: boolean;\n release: (() => void) | undefined;\n readonly done: Promise<void>;\n}\n\nconst REPLAY_PAGE = 500;\nconst MAX_PUMP_BUFFER = 10_000;\nconst ID_BYTES = 16;\n\n/** A random id: 128 bits, base64url. */\nfunction randomId(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(ID_BYTES));\n return btoa(String.fromCharCode(...bytes))\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/, \"\");\n}\n\nfunction encodeFrame(frame: Record<string, unknown>): string {\n return JSON.stringify(frame);\n}\n\nfunction messageFrame(channel: string, message: StoredMessage, replayed: boolean): string {\n return (\n `{\"t\":\"msg\",\"c\":${JSON.stringify(channel)},\"s\":${message.seq},\"e\":${JSON.stringify(message.event)},\"d\":${message.data}` +\n (message.publisher === undefined ? \"\" : `,\"p\":${JSON.stringify(message.publisher)}`) +\n (message.user === undefined ? \"\" : `,\"u\":${JSON.stringify(message.user)}`) +\n (replayed ? ',\"r\":1}' : \"}\")\n );\n}\n\n/**\n * Creates the hub: the server side of `@shadow-js/realtime`. It owns connections and channel rules, enforces limits and talks to\n * one adapter. Wire it to a transport with `hub.websocket()`, `hub.sse()` and `hub.post()` (Dusk routes) or with `hub.connect`.\n *\n * @param options The adapter, the channel rules and the limits.\n * @returns The hub.\n * @throws RangeError for a limit that is not valid; TypeError for an invalid channel pattern.\n * @example\n * import { createHub } from \"@shadow-js/realtime/server\";\n *\n * const hub = createHub({\n * channels: { \"chat:{room}\": { subscribe: true, publish: ({ identity }) => identity.user !== undefined } },\n * });\n * const sent: string[] = [];\n * const connection = hub.connect({ send: (frame) => (sent.push(frame), true), close: () => {} });\n * connection.receive('{\"t\":\"sub\",\"n\":1,\"c\":\"chat:lobby\"}');\n * @since 1.0.0\n */\nexport function createHubCore(options: HubOptions = {}): HubCore {\n const limits = resolveLimits(options.limits);\n const now = options.now ?? Date.now;\n const ownsAdapter = options.adapter === undefined;\n const adapter =\n options.adapter ??\n memoryAdapter({\n now,\n onError: (error, context) =>\n report(error, { where: `memory.${context.where}`, channel: context.channel }),\n });\n const rules: CompiledRule[] = [];\n const conns = new Map<string, ConnState>();\n const chans = new Map<string, ChanState>();\n /** Subscribes whose rules are being awaited: a revoke issued meanwhile takes them out, so the grant cannot outlive it. */\n const judging = new Set<{ readonly conn: ConnState; readonly channel: string }>();\n /** The tail of each channel's presence joins that are queued or running; an entry leaves when its chain is idle. */\n const joinChains = new Map<string, Promise<void>>();\n let heartbeat: ReturnType<typeof setInterval> | undefined;\n let closing = false;\n let beating = false;\n const counters = { published: 0, delivered: 0, refused: 0, slowConsumers: 0 };\n\n const report = (error: unknown, context: ErrorContext): void => {\n if (options.onError !== undefined) {\n try {\n options.onError(error, context);\n } catch {\n // A failing reporter must not break the hub.\n }\n } else {\n console.error(`[realtime] ${context.where}`, error);\n }\n };\n\n function match(\n channel: string,\n ): { rule: ChannelRule; params: Record<string, string> } | undefined {\n for (const compiled of rules) {\n const params = matchRule(compiled, channel);\n if (params !== undefined) return { rule: compiled.rule, params };\n }\n return undefined;\n }\n\n async function allowed(\n rule: ChannelRule,\n op: RuleContext[\"op\"],\n channel: string,\n params: Record<string, string>,\n identity: Identity,\n ): Promise<boolean> {\n const permission = rule[op];\n if (permission === undefined || permission === false) return false;\n if (permission === true) return true;\n try {\n return (await permission({ channel, params, identity, op })) === true;\n } catch (error) {\n report(error, { where: `rule.${op}`, channel });\n throw new RealtimeError(\n \"unavailable\",\n ...(__DEV__ ? [\"the channel rule could not be evaluated\"] : []),\n );\n }\n }\n\n function send(conn: ConnState, text: string): void {\n if (conn.closed) return;\n try {\n const buffered = conn.transport.buffered?.() ?? 0;\n if (buffered > limits.maxBufferedBytes) {\n counters.slowConsumers += 1;\n drop(conn, 1013, __DEV__ ? \"slow consumer\" : \"\");\n return;\n }\n if (conn.transport.send(text)) return;\n } catch (error) {\n // A transport that throws is a broken socket: this connection goes, the others are not affected (guarantee G13). The\n // transport is closed too, or its peer would keep a socket the hub no longer answers on until its own heartbeat gave up.\n report(error, { where: \"transport.send\", connection: conn.id });\n onTransport(conn, \"transport.close\", () =>\n conn.transport.close(1011, __DEV__ ? \"write failed\" : \"\"),\n );\n }\n void closeConnection(conn);\n }\n\n /** Runs a call on the transport; a transport that is already gone throws, which is only reported. */\n function onTransport(conn: ConnState, where: string, call: () => unknown): void {\n try {\n call();\n } catch (error) {\n report(error, { where, connection: conn.id });\n }\n }\n\n function sendFrame(conn: ConnState, frame: Record<string, unknown>): void {\n send(conn, encodeFrame(frame));\n }\n\n // Development texts are passed as `...(__DEV__ ? [\"text\"] : [])`: production passes no argument at all, where\n // `__DEV__ ? \"text\" : undefined` would leave an `undefined` argument in every call.\n function fail(\n conn: ConnState,\n n: number | undefined,\n code: RealtimeError[\"code\"],\n channel?: string,\n message?: string,\n ): void {\n counters.refused += 1;\n sendFrame(conn, {\n t: \"err\",\n ...(n === undefined ? {} : { n }),\n ...(channel === undefined ? {} : { c: channel }),\n code,\n ...(message === undefined || message === \"\" ? {} : { msg: message }),\n });\n }\n\n /** Sends a final frame, closes the transport and releases the connection. */\n function drop(conn: ConnState, code: number, reason: string): void {\n if (conn.closed) return;\n // The connection is released whatever the transport does, so its failures are only reported.\n onTransport(conn, \"transport.send\", () =>\n conn.transport.send(encodeFrame({ t: \"bye\", code, why: reason })),\n );\n onTransport(conn, \"transport.close\", () => conn.transport.close(code, reason));\n void closeConnection(conn);\n }\n\n // --- channels ------------------------------------------------------------------------------------------------------\n\n function channelState(name: string): ChanState {\n let chan = chans.get(name);\n if (chan === undefined || chan.closing) {\n chan = {\n name,\n subs: new Set(),\n bucket: new TokenBucket(\n limits.channelPublishesPerSecond,\n limits.channelPublishesPerSecond,\n now(),\n ),\n };\n chans.set(name, chan);\n }\n return chan;\n }\n\n /** Opens one of the channel's two adapter feeds once; a failed attempt is forgotten so the next caller retries. */\n async function ensureFeed(\n chan: ChanState,\n key: \"feed\" | \"pfeed\",\n open: () => Promise<Unsubscribe>,\n ): Promise<void> {\n if (chan[key] === undefined) {\n const opened = open();\n chan[key] = opened;\n opened.catch(() => {\n chan[key] = undefined;\n });\n }\n await chan[key];\n }\n\n function onMessage(chan: ChanState, message: StoredMessage): void {\n for (const sub of chan.subs) {\n if (!sub.active) continue;\n if (sub.pumping) {\n // A consumer so far behind that it cannot catch up is resubscribed from its last seq instead of holding memory.\n if (sub.buffer.length >= MAX_PUMP_BUFFER) removeSub(sub, \"gap\");\n else sub.buffer.push(message);\n continue;\n }\n if (message.seq === sub.seq + 1) deliver(sub, message, false);\n else if (message.seq > sub.seq + 1) {\n sub.buffer.push(message);\n void pump(sub, undefined);\n } else if (message.at > sub.at) removeSub(sub, \"reset\");\n // Any other seq at or below the cursor was already delivered: dropped (guarantee G2).\n }\n }\n\n function deliver(sub: SubState, message: StoredMessage, replayed: boolean): void {\n sub.seq = message.seq;\n sub.at = Math.max(sub.at, message.at);\n if (!sub.echo && message.publisher === sub.conn.id) return;\n counters.delivered += 1;\n if (replayed) {\n send(sub.conn, messageFrame(sub.channel.name, message, true));\n return;\n }\n let cached = sub.channel.frame;\n if (cached === undefined || cached.seq !== message.seq)\n cached = sub.channel.frame = {\n seq: message.seq,\n text: messageFrame(sub.channel.name, message, false),\n };\n send(sub.conn, cached.text);\n }\n\n /**\n * Delivers, in order and without a gap, the messages after `sub.seq` up to `upTo` and whatever is buffered. Fetches the\n * missing ones from history. When history no longer has them: a subscription that reported a gap skips ahead; any other one\n * is removed with `unsubbed why:\"gap\"`.\n */\n async function pump(sub: SubState, upTo: number | undefined): Promise<void> {\n if (sub.pumping && upTo === undefined) return;\n sub.pumping = true;\n try {\n while (sub.active) {\n sub.buffer = sub.buffer.filter((message) => message.seq > sub.seq);\n const next = sub.buffer[0];\n if (next !== undefined && next.seq === sub.seq + 1) {\n deliver(sub, next, false);\n sub.buffer.shift();\n continue;\n }\n const target = next === undefined ? (upTo ?? 0) : next.seq - 1;\n if (sub.seq >= target) break;\n const page = await adapter.history(\n sub.channel.name,\n sub.seq,\n Math.min(REPLAY_PAGE, target - sub.seq),\n );\n if (!sub.active) return;\n const first = page.messages[0];\n if (first === undefined || first.seq !== sub.seq + 1) {\n if (!sub.gapOk) {\n removeSub(sub, \"gap\");\n return;\n }\n // The gap was reported at subscribe time: continue from what is retained.\n sub.seq = (first?.seq ?? target + 1) - 1;\n continue;\n }\n for (const message of page.messages) {\n if (message.seq > target) break;\n deliver(sub, message, true);\n }\n }\n } catch (error) {\n report(error, { where: \"pump\", channel: sub.channel.name, connection: sub.conn.id });\n if (sub.active) removeSub(sub, \"unavailable\");\n } finally {\n sub.pumping = false;\n }\n // A message that arrived while the last history page was awaited is in the buffer; one more pass delivers it.\n if (sub.active && sub.buffer.length > 0) void pump(sub, undefined);\n }\n\n function onPresence(chan: ChanState, event: PresenceEvent): void {\n for (const sub of chan.subs) {\n // A change to this connection's own member made elsewhere (an HTTP post to another server) is what a write-back after an\n // expiry has to carry, so the connection's copy follows it.\n if (\n sub.joined &&\n event.op !== \"leave\" &&\n event.member.id === sub.conn.id &&\n event.member.v > sub.version\n ) {\n sub.version = event.member.v;\n sub.state = event.member.s;\n }\n if (!sub.active || (!sub.joined && !sub.watch)) continue;\n if (sub.queued !== undefined) sub.queued.push(event);\n else sendPresence(sub, event);\n }\n }\n\n function sendPresence(sub: SubState, event: PresenceEvent): void {\n sendFrame(sub.conn, { t: \"pres\", c: event.channel, op: event.op, m: event.member });\n }\n\n /** Removes a subscription from its connection and channel; tells the peer when `why` is given. */\n function removeSub(sub: SubState, why?: string): void {\n if (!sub.active) return;\n sub.active = false;\n sub.conn.subs.delete(sub.channel.name);\n sub.channel.subs.delete(sub);\n if (why !== undefined && !sub.conn.closed)\n sendFrame(sub.conn, { t: \"unsubbed\", c: sub.channel.name, ...(why === \"\" ? {} : { why }) });\n void releaseSub(sub);\n }\n\n /** The releases still running, so that `close` finishes them before it closes the adapter. */\n const releasing = new Set<Promise<void>>();\n\n function releaseSub(sub: SubState): Promise<void> {\n const running = release(sub);\n releasing.add(running);\n void running.finally(() => releasing.delete(running));\n return running;\n }\n\n async function release(sub: SubState): Promise<void> {\n const chan = sub.channel;\n try {\n // A join that is still being written must finish first, or its member would outlive the connection.\n await sub.joining?.catch(() => undefined);\n if (sub.joined) {\n sub.joined = false;\n await adapter.presenceRemove(chan.name, sub.conn.id);\n }\n } catch (error) {\n report(error, { where: \"presenceRemove\", channel: chan.name, connection: sub.conn.id });\n }\n if (chan.subs.size > 0 || chan.closing) return;\n chan.closing = true;\n if (chans.get(chan.name) === chan) chans.delete(chan.name);\n const stops = [chan.feed, chan.pfeed].filter(\n (item): item is Promise<Unsubscribe> => item !== undefined,\n );\n await Promise.all(\n stops.map(async (stop) => {\n try {\n await (await stop)();\n } catch (error) {\n report(error, { where: \"unsubscribe\", channel: chan.name });\n }\n }),\n );\n }\n\n // --- frames --------------------------------------------------------------------------------------------------------\n\n function identityDenied(conn: ConnState): RealtimeError[\"code\"] {\n return conn.identity.user === undefined ? \"unauthorized\" : \"forbidden\";\n }\n\n async function subscribe(\n conn: ConnState,\n frame: Extract<ClientFrame, { t: \"sub\" }>,\n ): Promise<void> {\n const { c: name, n } = frame;\n if (conn.subs.has(name))\n return fail(conn, n, \"conflict\", name, ...(__DEV__ ? [\"already subscribed\"] : []));\n if (conn.subs.size >= limits.maxChannelsPerConnection)\n return fail(\n conn,\n n,\n \"limit\",\n name,\n ...(__DEV__ ? [\"too many channels on this connection\"] : []),\n );\n const found = match(name);\n if (found === undefined) return fail(conn, n, identityDenied(conn), name);\n const wantsPresence = frame.hasPresence || frame.watch === true;\n const pending = { conn, channel: name };\n judging.add(pending);\n try {\n if (\n !(await allowed(found.rule, \"subscribe\", name, found.params, conn.identity)) ||\n (wantsPresence &&\n !(await allowed(found.rule, \"presence\", name, found.params, conn.identity))) ||\n !judging.has(pending)\n )\n return fail(conn, n, identityDenied(conn), name);\n } finally {\n judging.delete(pending);\n }\n if (\n frame.hasPresence &&\n byteLength(JSON.stringify(frame.presence ?? null)) > limits.maxStateBytes\n )\n return fail(\n conn,\n n,\n \"too_large\",\n name,\n ...(__DEV__ ? [\"the presence state is too large\"] : []),\n );\n if (conn.closed) return;\n const chan = channelState(name);\n if (chan.subs.size >= limits.maxSubscribersPerChannel)\n return fail(conn, n, \"limit\", name, ...(__DEV__ ? [\"the channel is full\"] : []));\n const sub: SubState = {\n conn,\n channel: chan,\n ...found,\n echo: frame.echo !== false,\n watch: frame.watch === true,\n version: 0,\n state: null,\n seq: 0,\n at: now(),\n pumping: true,\n buffer: [],\n queued: wantsPresence ? [] : undefined,\n active: true,\n };\n conn.subs.set(name, sub);\n chan.subs.add(sub);\n try {\n await ensureFeed(chan, \"feed\", () =>\n adapter.subscribe(chan.name, (message) => onMessage(chan, message)),\n );\n if (wantsPresence) {\n await ensureFeed(chan, \"pfeed\", () =>\n adapter.presenceSubscribe(chan.name, (event) => onPresence(chan, event)),\n );\n startHeartbeat();\n }\n const probe = await adapter.history(name, Number.MAX_SAFE_INTEGER, 0);\n if (!sub.active) return;\n const head = probe.head;\n const after = frame.after;\n let gap = false;\n let reset = false;\n if (after === undefined) {\n sub.seq = head;\n } else if (after > head) {\n reset = true;\n sub.seq = head;\n } else {\n sub.seq = after;\n gap = probe.oldest === 0 ? head > after : probe.oldest > after + 1;\n sub.gapOk = gap;\n }\n sendFrame(conn, {\n t: \"subbed\",\n n,\n c: name,\n head,\n ...(gap ? { gap: true } : {}),\n ...(reset ? { reset: true } : {}),\n });\n if (frame.hasPresence) {\n sub.joining = joinPresence(sub, frame.presence ?? null);\n await sub.joining;\n }\n if (wantsPresence) await syncPresence(sub);\n if (!sub.active) return;\n await pump(sub, head);\n sub.gapOk = false;\n } catch (error) {\n if (sub.active) {\n removeSub(sub);\n report(error, { where: \"subscribe\", channel: name, connection: conn.id });\n fail(conn, n, error instanceof RealtimeError ? error.code : \"unavailable\", name);\n }\n }\n }\n\n /** Joins one at a time per channel: the capacity check reads the set, so two joins in flight would both see room. */\n function joinPresence(sub: SubState, state: Json): Promise<void> {\n const name = sub.channel.name;\n const run = (joinChains.get(name) ?? Promise.resolve()).then(async () => {\n if (!sub.active) return;\n if ((await adapter.presenceList(name)).length >= limits.maxPresenceMembers)\n throw new RealtimeError(\"limit\", ...(__DEV__ ? [\"the presence set is full\"] : []));\n sub.version = 1;\n sub.state = state;\n sub.joined = true;\n startHeartbeat();\n await adapter.presenceSet(name, memberOf(sub), limits.presenceTtlMs);\n });\n const settle = (): void => {\n if (joinChains.get(name) === tail) joinChains.delete(name);\n };\n const tail: Promise<void> = run.then(settle, settle);\n joinChains.set(name, tail);\n return run;\n }\n\n /** The presence member a joined subscription stands for. */\n function memberOf(sub: SubState): Member {\n const { user } = sub.conn.identity;\n return {\n id: sub.conn.id,\n ...(user === undefined ? {} : { u: user }),\n s: sub.state,\n v: sub.version,\n };\n }\n\n async function syncPresence(sub: SubState): Promise<void> {\n const members = await adapter.presenceList(sub.channel.name);\n if (!sub.active) return;\n sendFrame(sub.conn, { t: \"pres\", c: sub.channel.name, op: \"sync\", m: members });\n const queued = sub.queued ?? [];\n sub.queued = undefined;\n const known = new Map(members.map((member) => [member.id, member.v]));\n for (const event of queued) {\n // What the snapshot already shows is not announced again.\n const version = known.get(event.member.id);\n if (\n event.op === \"leave\"\n ? version === undefined\n : version !== undefined && version >= event.member.v\n )\n continue;\n sendPresence(sub, event);\n }\n }\n\n async function setPresence(\n conn: ConnState,\n frame: Extract<ClientFrame, { t: \"pres\" }>,\n ): Promise<void> {\n const sub = conn.subs.get(frame.c);\n if (sub === undefined || !sub.joined) return fail(conn, undefined, \"not_subscribed\", frame.c);\n if (byteLength(JSON.stringify(frame.s)) > limits.maxStateBytes)\n return fail(\n conn,\n undefined,\n \"too_large\",\n frame.c,\n ...(__DEV__ ? [\"the presence state is too large\"] : []),\n );\n if (!conn.publishBucket.take(now())) return limit(conn, undefined, frame.c);\n sub.version += 1;\n sub.state = frame.s;\n try {\n await adapter.presenceSet(frame.c, memberOf(sub), limits.presenceTtlMs);\n } catch (error) {\n report(error, { where: \"presenceSet\", channel: frame.c, connection: conn.id });\n fail(conn, undefined, \"unavailable\", frame.c);\n }\n }\n\n function limit(conn: ConnState, n: number | undefined, channel: string): void {\n fail(conn, n, \"rate_limited\", channel);\n if (conn.limited.hit(now())) drop(conn, 1008, __DEV__ ? \"rate limit exceeded\" : \"\");\n }\n\n async function publishFrame(\n conn: ConnState,\n frame: Extract<ClientFrame, { t: \"pub\" }>,\n ): Promise<void> {\n const { c: name, n } = frame;\n const found = match(name);\n if (\n found === undefined ||\n !(await allowed(found.rule, \"publish\", name, found.params, conn.identity))\n )\n return fail(conn, n, identityDenied(conn), name);\n if (frame.d.length > limits.maxMessageBytes)\n return fail(conn, n, \"too_large\", name, ...(__DEV__ ? [\"the message is too large\"] : []));\n // A channel with no subscriber on this server has no bucket: only the connection's own rate applies to it.\n if (!conn.publishBucket.take(now()) || chans.get(name)?.bucket.take(now()) === false)\n return limit(conn, n, name);\n try {\n const result = await adapter.publish(\n name,\n {\n event: frame.e,\n data: frame.d,\n publisher: conn.id,\n ...(conn.identity.user === undefined ? {} : { user: conn.identity.user }),\n },\n {\n history: found.rule.history ?? limits.history,\n historyTtlMs: limits.historyTtlMs,\n idempotencyTtlMs: limits.idempotencyTtlMs,\n // An idempotency key is only unique per publisher: the adapters hold one key table per channel, so without a\n // scope a client could mark another user's (or the server's) publish as a duplicate by reusing its key. A\n // signed-in user keeps the scope across connections; anonymous connections share one. Keys never contain\n // \"/\", so `key/scope` cannot be formed two ways.\n ...(frame.k === undefined\n ? {}\n : {\n key: `${frame.k}/${conn.identity.user === undefined ? \"a\" : `u:${conn.identity.user}`}`,\n }),\n },\n );\n counters.published += result.duplicate ? 0 : 1;\n if (n !== undefined)\n sendFrame(conn, { t: \"ack\", n, s: result.seq, ...(result.duplicate ? { dup: true } : {}) });\n } catch (error) {\n report(error, { where: \"publish\", channel: name, connection: conn.id });\n fail(conn, n, \"unavailable\", name);\n }\n }\n\n async function request(\n conn: ConnState,\n frame: Extract<ClientFrame, { t: \"req\" }>,\n ): Promise<void> {\n const { c: name, n } = frame;\n let target: { rule: ChannelRule; params: Record<string, string> } | undefined =\n conn.subs.get(name);\n if (target === undefined && conn.stateless) {\n const found = match(name);\n if (\n found !== undefined &&\n (await allowed(found.rule, \"subscribe\", name, found.params, conn.identity))\n )\n target = found;\n }\n if (target === undefined)\n return fail(\n conn,\n n,\n identityDenied(conn) === \"unauthorized\" && conn.stateless\n ? \"unauthorized\"\n : \"not_subscribed\",\n name,\n );\n const sub = target;\n const handler = sub.rule.handlers?.[frame.e];\n if (handler === undefined) return fail(conn, n, \"no_handler\", name);\n if (conn.pending.size >= limits.maxPendingRequests)\n return fail(conn, n, \"limit\", name, ...(__DEV__ ? [\"too many requests in flight\"] : []));\n if (!conn.publishBucket.take(now())) return limit(conn, n, name);\n const controller = new AbortController();\n conn.pending.add(controller);\n const timeout = Math.min(frame.ms ?? limits.requestTimeoutMs, limits.requestTimeoutMs);\n const timer = setTimeout(() => controller.abort(new RealtimeError(\"timeout\")), timeout);\n void (async () => {\n try {\n const run = Promise.resolve(\n handler({\n channel: name,\n params: sub.params,\n identity: conn.identity,\n event: frame.e,\n data: frame.d,\n signal: controller.signal,\n }),\n );\n const aborted = new Promise<never>((_resolve, reject) => {\n controller.signal.addEventListener(\"abort\", () => reject(controller.signal.reason), {\n once: true,\n });\n });\n const reply = (await Promise.race([run, aborted])) ?? null;\n const text = JSON.stringify(reply);\n if (text === undefined || byteLength(text) > limits.maxMessageBytes)\n return fail(conn, n, \"too_large\", name, ...(__DEV__ ? [\"the reply is too large\"] : []));\n send(conn, `{\"t\":\"rep\",\"n\":${n},\"d\":${text}}`);\n } catch (error) {\n if (error instanceof RealtimeError)\n fail(\n conn,\n n,\n error.code,\n name,\n error.code === \"timeout\" || error.code === \"closed\" ? undefined : error.message,\n );\n else {\n report(error, { where: \"request\", channel: name, connection: conn.id });\n fail(conn, n, \"handler_error\", name);\n }\n } finally {\n clearTimeout(timer);\n conn.pending.delete(controller);\n settle(conn);\n }\n })();\n }\n\n /** Answers a frame that cannot be handled; a peer that keeps sending them is dropped. */\n function refuseFrame(\n conn: ConnState,\n n: number | undefined,\n code: RealtimeError[\"code\"],\n why?: string,\n ): void {\n fail(conn, n, code, undefined, why);\n if (conn.invalid.hit(now())) drop(conn, 1008, __DEV__ ? \"too many invalid frames\" : \"\");\n }\n\n async function handle(conn: ConnState, text: string): Promise<void> {\n const decoded = decodeClientFrame(text, limits.maxMessageBytes);\n if (!decoded.ok) {\n refuseFrame(conn, decoded.n, decoded.code, decoded.why);\n return;\n }\n const frame = decoded.frame;\n const n = \"n\" in frame ? frame.n : undefined;\n // An `unsub` spends a token but is never refused: the client cannot tell a refused one (the answer names no frame), and would\n // believe it left a channel the hub still holds, so its next `sub` of that channel would be a conflict.\n if ((frame.t === \"sub\" || frame.t === \"unsub\") && !conn.ctl.take(now()) && frame.t === \"sub\")\n return limit(conn, n, frame.c);\n try {\n switch (frame.t) {\n case \"hello\":\n return;\n case \"ping\":\n sendFrame(conn, { t: \"pong\", ...(frame.n === undefined ? {} : { n: frame.n }) });\n return;\n case \"sub\":\n return await subscribe(conn, frame);\n case \"unsub\": {\n const sub = conn.subs.get(frame.c);\n if (sub === undefined) return fail(conn, frame.n, \"not_subscribed\", frame.c);\n removeSub(sub);\n return;\n }\n case \"pub\":\n return await publishFrame(conn, frame);\n case \"req\":\n return await request(conn, frame);\n case \"pres\":\n return await setPresence(conn, frame);\n }\n } catch (error) {\n const channel = \"c\" in frame ? frame.c : undefined;\n if (error instanceof RealtimeError) fail(conn, n, error.code, channel);\n else {\n report(error, {\n where: \"frame\",\n connection: conn.id,\n ...(channel === undefined ? {} : { channel }),\n });\n fail(conn, n, \"unavailable\", channel);\n }\n }\n }\n\n async function run(conn: ConnState): Promise<void> {\n if (conn.running) return;\n conn.running = true;\n try {\n for (let text = conn.queue.shift(); text !== undefined; text = conn.queue.shift()) {\n if (conn.closed) break;\n try {\n await handle(conn, text);\n } catch (error) {\n if (error instanceof RealtimeError) fail(conn, undefined, error.code);\n else {\n report(error, { where: \"frame\", connection: conn.id });\n fail(conn, undefined, \"unavailable\");\n }\n }\n }\n } finally {\n conn.running = false;\n settle(conn);\n }\n }\n\n /** Wakes the callers of `settled` when the connection has nothing left to do. */\n function settle(conn: ConnState): void {\n if (conn.running || conn.queue.length > 0 || conn.pending.size > 0) return;\n for (const wake of conn.waiters.splice(0)) wake();\n }\n\n async function closeConnection(conn: ConnState): Promise<void> {\n if (conn.closed) return conn.done;\n conn.closed = true;\n if (conns.get(conn.id) === conn) conns.delete(conn.id);\n for (const controller of conn.pending) controller.abort(new RealtimeError(\"closed\"));\n conn.pending.clear();\n conn.queue.length = 0;\n for (const wake of conn.waiters.splice(0)) wake();\n const subs = [...conn.subs.values()];\n for (const sub of subs) {\n sub.active = false;\n sub.channel.subs.delete(sub);\n }\n conn.subs.clear();\n await Promise.allSettled(subs.map((sub) => releaseSub(sub)));\n conn.release?.();\n }\n\n // --- presence heartbeat --------------------------------------------------------------------------------------------\n\n function startHeartbeat(): void {\n if (heartbeat !== undefined || closing) return;\n const timer = setInterval(() => void beat(), limits.presenceHeartbeatMs);\n // Node's timer can be unref'd so an idle hub does not keep the process alive; other runtimes return a number.\n (timer as unknown as { unref?: () => void }).unref?.();\n heartbeat = timer;\n }\n\n async function beat(): Promise<void> {\n if (beating) return;\n beating = true;\n try {\n const refs: { channel: string; id: string }[] = [];\n const watched = new Set<string>();\n for (const conn of conns.values())\n for (const sub of conn.subs.values()) {\n if (sub.joined) refs.push({ channel: sub.channel.name, id: conn.id });\n if (sub.joined || sub.watch) watched.add(sub.channel.name);\n }\n if (refs.length === 0 && watched.size === 0) {\n clearInterval(heartbeat);\n heartbeat = undefined;\n return;\n }\n try {\n const missing = await adapter.presenceTouch(refs, limits.presenceTtlMs);\n for (const ref of missing) {\n const sub = conns.get(ref.id)?.subs.get(ref.channel);\n if (sub === undefined || !sub.joined) continue;\n await adapter.presenceSet(ref.channel, memberOf(sub), limits.presenceTtlMs);\n }\n for (const channel of watched) await adapter.presenceReap(channel);\n } catch (error) {\n report(error, { where: \"presenceHeartbeat\" });\n }\n } finally {\n beating = false;\n }\n }\n\n // --- the hub -------------------------------------------------------------------------------------------------------\n\n for (const [pattern, rule] of Object.entries(options.channels ?? {}))\n rules.push(compileRule(pattern, rule));\n\n const core: HubCore = {\n limits,\n adapter,\n report,\n match,\n allowed,\n\n channel(pattern, rule) {\n rules.push(compileRule(pattern, rule));\n return core;\n },\n\n connect(transport, identity = {}, connectOptions = {}) {\n if (closing) throw new RealtimeError(\"closed\", ...(__DEV__ ? [\"the hub is closed\"] : []));\n let release: (() => void) | undefined;\n const done = new Promise<void>((resolve) => {\n release = resolve;\n });\n const owner =\n connectOptions.actingFor === undefined ? undefined : conns.get(connectOptions.actingFor);\n const acting =\n owner !== undefined && identity.user !== undefined && owner.identity.user === identity.user;\n const conn: ConnState = {\n id: acting ? (connectOptions.actingFor as string) : randomId(),\n identity,\n transport,\n subs: new Map(),\n queue: [],\n publishBucket:\n connectOptions.publishBucket ??\n new TokenBucket(limits.publishesPerSecond, limits.publishBurst, now()),\n // The refill is the publish rate; the burst always covers a connection declaring all its channels at once.\n ctl: new TokenBucket(\n limits.publishesPerSecond,\n Math.max(limits.publishBurst, limits.maxChannelsPerConnection),\n now(),\n ),\n stateless: connectOptions.stateless === true,\n waiters: [],\n invalid: new WindowCounter(10, 60_000),\n limited: new WindowCounter(50, 10_000),\n pending: new Set(),\n release,\n done,\n };\n // An acting connection shares its owner's id: it must not replace the owner in the table.\n if (!acting) conns.set(conn.id, conn);\n sendFrame(conn, {\n t: \"ready\",\n v: PROTOCOL_VERSION,\n id: conn.id,\n hb: limits.heartbeatMs,\n max: limits.maxMessageBytes,\n });\n return {\n id: conn.id,\n identity,\n done,\n receive(text) {\n if (conn.closed) return;\n // Judged before it takes a place in the queue: a frame over the limit can never be handled, so it must not be able to\n // fill the queue (or be held until its turn) however many arrive.\n if (byteLength(text) > limits.maxMessageBytes) {\n refuseFrame(conn, undefined, \"too_large\");\n return;\n }\n if (conn.queue.length >= limits.maxQueuedFrames) {\n drop(conn, 1013, __DEV__ ? \"too many frames waiting\" : \"\");\n return;\n }\n conn.queue.push(text);\n void run(conn);\n },\n closed() {\n void closeConnection(conn);\n },\n settled() {\n return new Promise<void>((resolve) => {\n conn.waiters.push(resolve);\n settle(conn);\n });\n },\n };\n },\n\n async publish(channel, event, data, publishOptions = {}) {\n if (!isChannelName(channel))\n throw new RealtimeError(\n \"bad_channel\",\n ...(__DEV__ ? [`\"${channel}\" is not a valid channel name`] : []),\n );\n if (!isEventName(event))\n throw new RealtimeError(\n \"bad_frame\",\n ...(__DEV__ ? [`\"${event}\" is not a valid event name`] : []),\n );\n if (publishOptions.key !== undefined && !isIdempotencyKey(publishOptions.key))\n throw new RealtimeError(\n \"bad_frame\",\n ...(__DEV__ ? [\"the idempotency key is not valid\"] : []),\n );\n let text: string | undefined;\n try {\n text = JSON.stringify(data === undefined ? null : data);\n } catch (error) {\n throw new TypeError(__DEV__ ? \"the data is not JSON\" : \"REALTIME_DATA\", {\n cause: error,\n });\n }\n if (text === undefined)\n throw new TypeError(__DEV__ ? \"the data is not JSON\" : \"REALTIME_DATA\");\n if (byteLength(text) > limits.maxMessageBytes)\n throw new RealtimeError(\"too_large\", ...(__DEV__ ? [\"the message is too large\"] : []));\n const found = match(channel);\n const result = await adapter.publish(\n channel,\n { event, data: text },\n {\n history: found?.rule.history ?? limits.history,\n historyTtlMs: limits.historyTtlMs,\n idempotencyTtlMs: limits.idempotencyTtlMs,\n ...(publishOptions.key === undefined ? {} : { key: `${publishOptions.key}/s` }),\n },\n );\n counters.published += result.duplicate ? 0 : 1;\n return result;\n },\n\n async setMemberState(identity, channel, connectionId, state) {\n if (identity.user === undefined)\n throw new RealtimeError(\n \"unauthorized\",\n ...(__DEV__ ? [\"presence changes over HTTP need a signed-in user\"] : []),\n );\n const found = match(channel);\n if (\n found === undefined ||\n !(await allowed(found.rule, \"presence\", channel, found.params, identity))\n )\n throw new RealtimeError(\"forbidden\");\n if (byteLength(JSON.stringify(state)) > limits.maxStateBytes)\n throw new RealtimeError(\n \"too_large\",\n ...(__DEV__ ? [\"the presence state is too large\"] : []),\n );\n const members = await adapter.presenceList(channel);\n const member = members.find((item) => item.id === connectionId);\n if (member === undefined || member.u !== identity.user)\n throw new RealtimeError(\"forbidden\", ...(__DEV__ ? [\"that connection is not yours\"] : []));\n // The connection may live on this server: its own copy of the member is what the heartbeat writes back after an expiry, so\n // it moves with the stored one (the same for a connection on another server, through the presence event below).\n const local = conns.get(connectionId)?.subs.get(channel);\n const version = Math.max(member.v, local?.version ?? 0) + 1;\n if (local?.joined === true) {\n local.version = version;\n local.state = state;\n }\n await adapter.presenceSet(\n channel,\n { id: member.id, u: member.u, s: state, v: version },\n limits.presenceTtlMs,\n );\n },\n\n revoke(target) {\n if (\n target.user === undefined &&\n target.connection === undefined &&\n target.channel === undefined\n )\n throw new TypeError(\n __DEV__ ? \"revoke needs a user, a connection or a channel.\" : \"REALTIME_REVOKE\",\n );\n const hit = (conn: ConnState, channel: string): boolean =>\n (target.connection === undefined || conn.id === target.connection) &&\n (target.user === undefined || conn.identity.user === target.user) &&\n (target.channel === undefined || channel === target.channel);\n let count = 0;\n for (const item of judging) {\n if (hit(item.conn, item.channel)) {\n judging.delete(item);\n count += 1;\n }\n }\n for (const conn of [...conns.values()]) {\n for (const sub of [...conn.subs.values()]) {\n if (!hit(conn, sub.channel.name)) continue;\n removeSub(sub, \"revoked\");\n count += 1;\n }\n }\n return count;\n },\n\n stats() {\n let subscriptions = 0;\n for (const conn of conns.values()) subscriptions += conn.subs.size;\n return {\n connections: conns.size,\n channels: chans.size,\n subscriptions,\n ...counters,\n };\n },\n\n async close() {\n if (closing) return;\n closing = true;\n if (heartbeat !== undefined) clearInterval(heartbeat);\n heartbeat = undefined;\n const all = [...conns.values()];\n for (const conn of all) drop(conn, 1001, __DEV__ ? \"going away\" : \"\");\n await Promise.all(all.map((conn) => conn.done));\n await Promise.allSettled([...releasing]);\n if (ownsAdapter) await adapter.close();\n },\n };\n return core;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import{RealtimeError as e}from"../errors.js";import{byteLength as t,isChannelName as n,isEventName as r,isIdempotencyKey as i,serverFrameOf as a}from"../protocol.js";import{applyPresence as o}from"./presence.js";import{openStream as s}from"./sse.js";import{backoff as c,clientId as ee,resolveUrl as l,sendUrl as u}from"./util.js";import{openSocket as te}from"./ws.js";const ne=/* @__PURE__ */ new Set([1002,1008,1009]),re=/* @__PURE__ */ new Set([`unavailable`,`rate_limited`,`timeout`]),ie=/* @__PURE__ */ new Set([408,425,429]);function d(d){let f=d.reconnect??{},p=f.minDelayMs??250,m=f.maxDelayMs??3e4,ae=f.stableMs??5e3,h=f.random??Math.random,oe=d.maxQueued??100,se=d.queueTtlMs??3e4,ce=d.ackTimeoutMs??1e4,le=(d.transport??`auto`)===`auto`,ue=ee(),g=/* @__PURE__ */ new Map,_=/* @__PURE__ */ new Set,v=[],y=/* @__PURE__ */ new Map,b=/* @__PURE__ */ new Set,x=`connecting`,S=d.transport===`sse`?`sse`:`ws`,C=!1,w=!1,T,E=0,D=!1,O,de=65536,fe=``,k,A=0,j=0,pe=0,me=0,he=0,M=0,N,P,ge,F,I=!1,_e,L,R=0,z=!1,B=e=>{try{d.onError===void 0?console.error(`[realtime]`,e):d.onError(e)}catch{}},V=e=>{try{e()}catch(e){B(e)}},ve=(e,t)=>(d.fetch??globalThis.fetch)(e,t),ye=()=>typeof d.headers==`function`?d.headers():d.headers,H=(e,t)=>{for(let n of[...e])V(()=>n(t))},U=e=>{e!==x&&(x=e,d.onStatus!==void 0&&V(()=>d.onStatus?.(e)),H(b,e))},W=(e,t)=>{e.status!==t&&(e.status=t,H(e.sl,t))},G=(e,t,n)=>{clearTimeout(e.timer);let r=e.settle;e.settle=void 0,r===void 0?t!==void 0&&e.kind===`pub`&&B(t):r(t,n)},K=(t,n)=>{clearTimeout(t.timer),t.timer=setTimeout(()=>{let n=v.indexOf(t);n>=0&&v.splice(n,1),y.get(t.n)===t&&y.delete(t.n),G(t,new e(`timeout`))},n)},be=e=>{let t=y.get(e);return t!==void 0&&y.delete(e),t},xe=t=>{if(C)return new e(`closed`);let n=D&&S===`ws`;if(t.kind!==`pres`&&!n&&v.length>=oe)return new e(`queue_full`);if(t.kind===`pres`){let e=v.findIndex(e=>e.kind===`pres`&&e.chan===t.chan);e>=0&&v.splice(e,1)}else K(t,se);v.push(t),D?Q():Ke()},Se=(e,t,n,r,i)=>({kind:e,chan:t,event:n,data:r,ack:!0,ms:0,order:++he,n:0,...i}),Ce=e=>e.kind===`req`||e.kind===`pub`&&e.ack,we=e=>e.kind===`req`?e.ms+1e3:ce,Te=n=>{let r=JSON.stringify(n.chan.name),i=Ce(n);n.n=i?++pe:0;let a=n.kind===`pres`?`{"t":"pres","c":${r},"s":${n.data}}`:n.kind===`req`?`{"t":"req","n":${n.n},"c":${r},"e":"${n.event}","d":${n.data},"ms":${n.ms}}`:`{"t":"pub","c":${r},"e":"${n.event}","d":${n.data}${i?`,"n":${n.n}`:``},"k":${JSON.stringify(n.key)}}`;if(t(a)>de)G(n,new e(`too_large`));else return a},Ee=e=>{e.kind!==`pres`&&(Ce(e)?(y.set(e.n,e),K(e,we(e))):clearTimeout(e.timer))},De=t=>{t.sort((e,t)=>e.order-t.order);let n=[];for(let r of t)r.kind===`req`?G(r,new e(`closed`)):(r.kind===`pub`&&K(r,se),n.push(r));v.unshift(...n)},q=e=>!e.closed&&(e.refs>0||e.state!==void 0||e.options.watch===!0||e.hd>0&&S===`ws`),J=e=>e.state!==void 0,Oe=e=>!J(e)&&(e.presence.size>0||e.options.watch===!0),Y=e=>{e.status===`idle`&&q(e)&&W(e,`subscribing`),_.add(e),Ke()},ke=(e,t)=>{let n=J(e),r=Oe(e),i=e.sy?Math.max(e.last,e.gh):e.options.replay===!0?0:void 0;return e.w={n:t,join:n,watch:r,after:i,ver:e.sv,ready:!n},W(e,e.sy?`resuming`:`subscribing`),{c:e.name,...i===void 0?{}:{after:i},...e.options.echo===!1?{echo:!1}:{},...n?{presence:e.state}:{},...r?{watch:!0}:{}}},Ae=e=>{let t=e.w;if(t===void 0||e.state===void 0)return;t.ver=e.sv;let n=Se(`pres`,e,``,JSON.stringify(e.state),{});if(S===`ws`){let e=Te(n);e!==void 0&&T?.send(e)}else xe(n)},je=e=>{e.sy=!1,e.last=0,e.w=void 0,clearTimeout(e.retry),e.retry=void 0,e.fails=0,e.members.size>0&&(e.members.clear(),e.list=void 0),W(e,e.closed?`closed`:`idle`)},Me=e=>{T?.send(`{"t":"unsub","c":${JSON.stringify(e.name)}}`)},Ne=e=>{let t=q(e),n=e.w;if(S===`ws`&&D&&(n===void 0||t&&n.join===J(e)&&n.watch===Oe(e)||(Me(e),e.w=void 0),t&&e.w===void 0&&e.retry===void 0)){let t=++pe;T?.send(JSON.stringify({t:`sub`,n:t,...ke(e,t)}))}if(!t){e.status!==`idle`&&e.status!==`closed`&&je(e);return}let r=e.w;r?.join&&r.ready&&r.ver!==e.sv&&Ae(e)},Pe=()=>{let e=[..._];_.clear();for(let t of e)Ne(t)},X=(e,t)=>{e.closed||(e.closed=!0,g.get(e.name)===e&&g.delete(e.name),e.w!==void 0&&S===`ws`&&D&&Me(e),je(e),e.ev.clear(),e.any.clear(),e.gl.clear(),e.presence.clear(),e.sl.clear(),e.refs=0,_.delete(e),t!==void 0&&B(t),Ke())},Fe=(e,t)=>{e.w=void 0,W(e,e.sy?`resuming`:`subscribing`),t?Y(e):(e.retry=setTimeout(()=>{e.retry=void 0,Y(e)},c(e.fails,p,m,h)),e.fails+=1)},Ie=()=>{let e=``;for(let t of g.values())q(t)&&(e+=`${t.name}|${t.options.echo===!1?0:1}${+!!J(t)}${+!!Oe(t)};`);return e},Z=()=>{if(clearTimeout(P),P=void 0,C||T!==void 0)return;E+=1;let t=E;D=!1,M=0;let n={rx:()=>{t===E&&(M=0)},frame:e=>{t===E&&V(()=>He(e,!1))},closed:e=>{t===E&&Be(e)}};try{if(!z&&typeof addEventListener==`function`&&(z=!0,addEventListener(`online`,Le),typeof document<`u`&&document.addEventListener(`visibilitychange`,Re)),S===`sse`){let t=l(d.url,!1),r=[];for(let t of[...g.values()])q(t)&&(r.length>=64?X(t,new e(`limit`)):r.push(ke(t,r.length+1)));fe=Ie(),t.searchParams.set(`s`,JSON.stringify(r)),T=s(t.toString(),ve,ye,n)}else T=te(l(d.url,!0).toString(),d.WebSocket??globalThis.WebSocket,n)}catch(t){E+=1,$(new e(`closed`,void 0,{cause:t}));return}N=setTimeout(()=>{t===E&&Be({})},ce)},Le=()=>{P!==void 0&&Z()},Re=()=>{document.visibilityState===`visible`&&P!==void 0&&Z()},ze=()=>{E+=1;let e=T;T=void 0,D=!1,O=void 0,clearTimeout(N),clearTimeout(ge),clearInterval(F),F=void 0,e?.close()},Be=t=>{let n=D,r=k??t.code;if(k=void 0,ze(),C)return;if(S===`ws`&&y.size>0){let e=[...y.values()];y.clear(),De(e)}for(let e of g.values())e.w=void 0,clearTimeout(e.retry),e.retry=void 0,q(e)&&W(e,e.sy?`resuming`:`subscribing`);let i=t.status!==void 0&&t.status>=400&&t.status<500&&!ie.has(t.status);if((r!==void 0&&ne.has(r)||i)&&f.retryFatal!==!0){$(new e(`closed`,void 0));return}if(j>=(f.maxReconnectAttempts??1/0)){$(new e(`closed`));return}let a=c(j,p,m,h);j+=1,n?A=0:le&&S===`ws`&&(A+=1,A>=2&&(S=`sse`,a=0)),U(`reconnecting`),P=setTimeout(Z,a)},Ve=()=>{M+=1,M>2?Be({}):S===`ws`&&T?.send(`{"t":"ping"}`)},He=(t,n)=>{switch(t.t){case`ready`:if(D)return;if(D=!0,O=t.id,de=t.max,A=0,M=0,clearTimeout(N),t.hb>0&&(F=setInterval(Ve,t.hb)),ge=setTimeout(()=>{j=0},ae),U(`open`),S===`ws`)for(let e of g.values())_.add(e);Q();return;case`bye`:k=t.code;return;case`subbed`:{let e=g.get(t.c),n=e?.w;if(e===void 0||n===void 0||n.n!==t.n)return;(t.reset||n.after===void 0)&&(e.last=t.head);let r=t.reset===!0||t.gap===!0&&n.after!==void 0&&!(n.after===0&&!e.sy);e.sy=!0,e.fails=0,W(e,`live`),e.gh=r?t.head:0,r&&H(e.gl,{after:n.after??0,head:t.head,reset:t.reset===!0});return}case`unsubbed`:{let n=g.get(t.c);if(n===void 0)return;t.why===`revoked`?(n.w=void 0,X(n,new e(`forbidden`,void 0))):Fe(n,t.why===`gap`||t.why===`reset`);return}case`msg`:{let e=g.get(t.c);if(e===void 0||t.s<=e.last)return;e.last=t.s;let n={seq:t.s,replayed:t.r===1,...t.p===void 0?{}:{publisher:t.p},...t.u===void 0?{}:{user:t.u}};for(let r of[...e.ev.get(t.e)??[]])V(()=>r(t.d,n));for(let r of[...e.any])V(()=>r({event:t.e,data:t.d,meta:n}));return}case`ack`:{let e=be(t.n);e!==void 0&&G(e,void 0,{seq:t.s,duplicate:t.dup===!0});return}case`rep`:{let e=be(t.n);e!==void 0&&G(e,void 0,t.d);return}case`err`:{let r=new e(t.code,t.msg),i=t.c===void 0?void 0:g.get(t.c);if(t.n!==void 0){if(!n&&i?.w?.n===t.n){re.has(t.code)?(Fe(i,!1),B(r)):(i.w=void 0,X(i,r));return}let e=n||S===`ws`?be(t.n):void 0;if(e!==void 0){G(e,r);return}}B(r);return}case`pres`:{let e=g.get(t.c);if(e===void 0)return;o(e.members,t.op,t.m)&&(e.list=[...e.members.values()],H(e.presence,e.list)),t.op===`sync`&&e.w!==void 0&&(e.w.ready=!0,e.w.join&&e.w.ver!==e.sv&&Ae(e));return}case`pong`:return}},Q=()=>{if(D&&!C){if(Pe(),S===`sse`)Ue();else for(let e=v[0];e!==void 0&&D;e=v[0]){let t=Te(e);if(t!==void 0){if(T?.send(t)!==!0)return;Ee(e)}v.shift()}}},Ue=()=>{if(I||L!==void 0||O===void 0)return;let e=[],t=[],n=0;for(;v.length>0&&e.length<64&&n<256e3;){let r=v.shift();if(r===void 0)break;let i=Te(r);i!==void 0&&(e.push(r),t.push(i),n+=i.length)}e.length!==0&&(I=!0,We(e,`{"cid":${JSON.stringify(O)},"frames":[${t.join(`,`)}]}`).then(void 0,B))},We=async(t,n)=>{for(let e of t)Ee(e);let r=!1,i=new AbortController;_e=i;let o=setTimeout(()=>i.abort(),Math.max(...t.map(we)));try{let o=new Headers(await ye());o.set(`content-type`,`application/json`);let s=d.postUrl===void 0?u(l(d.url,!1)):l(d.postUrl,!1),c=await ve(s,{method:`POST`,headers:o,body:n,signal:i.signal});if(c.ok){let e=await c.json();if(Array.isArray(e.frames))for(let t of e.frames){let e=a(t);e!==void 0&&V(()=>He(e,!0))}R=0}else if(c.status>=500||ie.has(c.status))r=!0;else{let n=await c.json().catch(()=>void 0),r=n?.error?.code??`unavailable`;for(let i of t)y.get(i.n)===i&&y.delete(i.n),G(i,new e(r,n?.error?.message))}}catch{if(C)return;r=!0}finally{clearTimeout(o)}I=!1;let s=t.filter(e=>Ce(e)&&y.get(e.n)===e);for(let e of s)y.delete(e.n);if(r){for(let t of s)t.kind===`req`&&G(t,new e(`unavailable`));De(t.filter(e=>e.kind===`pres`||e.kind===`pub`&&(!e.ack||s.includes(e)))),R+=1,L=setTimeout(()=>{L=void 0,Q()},c(R-1,p,m,h))}else for(let t of s)G(t,new e(`unavailable`));Q()},Ge=()=>{if(w=!1,!C){if(Pe(),T===void 0)P===void 0&&(v.length>0||[...g.values()].some(q))&&Z();else if(S===`sse`&&(Ie()!==fe||[...g.values()].some(e=>q(e)&&e.w===void 0&&e.retry===void 0))){ze();for(let e of g.values())e.w=void 0;Z()}else Q()}};function Ke(){w||C||(w=!0,queueMicrotask(Ge))}function $(t){if(C)return;C=!0,clearTimeout(P),clearTimeout(L),P=void 0,L=void 0,ze(),_e?.abort(),z&&=(typeof removeEventListener==`function`&&removeEventListener(`online`,Le),typeof document<`u`&&document.removeEventListener(`visibilitychange`,Re),!1);let n=t??new e(`closed`),r=[...v.splice(0),...y.values()];y.clear();for(let e of r)e.settle===void 0?clearTimeout(e.timer):G(e,n);for(let e of[...g.values()])X(e);g.clear(),_.clear(),U(`closed`),b.clear(),t!==void 0&&B(t)}let qe=(t,n)=>{let a={name:t,options:n,ev:/* @__PURE__ */ new Map,any:/* @__PURE__ */ new Set,gl:/* @__PURE__ */ new Set,sl:/* @__PURE__ */ new Set,presence:/* @__PURE__ */ new Set,members:/* @__PURE__ */ new Map,status:`idle`,refs:0,hd:0,last:0,gh:0,sy:!1,state:n.presence,sv:0,fails:0,closed:!1};g.set(t,a),q(a)&&Y(a);let o=(e,t,n,r)=>{if(a.closed)return()=>void 0;let i=!e.has(t);return e.add(t),!n&&i&&(a.refs+=1,Y(a)),()=>{i&&(i=!1,e.delete(t)&&!n&&(--a.refs,r?.(),Y(a)))}},s=(t,n,o,s,c,ee)=>{let l=`null`,u;if(a.closed)u=new e(`closed`);else if(!r(n)||s!==void 0&&!i(s))u=new e(`bad_frame`);else try{l=JSON.stringify(o??null)??`null`}catch(t){u=new e(`bad_frame`,void 0,{cause:t})}return u===void 0?new Promise((e,r)=>{let i=t===`req`,o=Se(t,a,n,l,{key:i?void 0:s??`${ue}:${++me}`,ack:c,ms:ee,settle:c?(t,n)=>{i&&(--a.hd,Y(a)),t?r(t):e(n)}:void 0});i&&(a.hd+=1,Y(a));let u=xe(o);u===void 0?c||e({seq:0,duplicate:!1}):(o.settle=void 0,i&&--a.hd,r(u))}):Promise.reject(u)},c={name:t,get status(){return a.status},get lastSeq(){return a.last},on(e,t){let n=a.ev.get(e);n===void 0&&(n=/* @__PURE__ */ new Set,a.ev.set(e,n));let r=n;return o(r,t,!1,()=>{r.size===0&&a.ev.get(e)===r&&a.ev.delete(e)})},onAny:e=>o(a.any,e),onGap:e=>o(a.gl,e),onStatus:e=>o(a.sl,e,!0),publish:(e,t,n={})=>s(`pub`,e,t,n.key,n.ack!==!1,0),request:(e,t,n={})=>s(`req`,e,t,void 0,!0,n.timeoutMs??1e4),presence:{members:()=>(a.list??=[...a.members.values()],a.list),set(e){a.closed||(a.state=e,a.sv+=1,Y(a))},leave(){a.closed||a.state===void 0||(a.state=void 0,a.sv+=1,Y(a))},on:e=>o(a.presence,e)},close:()=>X(a)};return a.hn=c,c};return{channel(t,r={}){if(C)throw new e(`closed`);if(!n(t))throw new e(`bad_channel`,t);return g.get(t)?.hn??qe(t,r)},get status(){return x},get connectionId(){return O},onStatus(e){return C||b.add(e),()=>{b.delete(e)}},close:()=>$()}}export{d as createRealtime};
1
+ import{RealtimeError as e}from"../errors.js";import{byteLength as t,isChannelName as n,isEventName as r,isIdempotencyKey as i,serverFrameOf as a}from"../protocol.js";import{applyPresence as o}from"./presence.js";import{openStream as s}from"./sse.js";import{backoff as c,resolveUrl as l}from"./util.js";import{openSocket as u}from"./ws.js";const d=/* @__PURE__ */ new Set([1002,1008,1009]),ee=/* @__PURE__ */ new Set([`unavailable`,`rate_limited`,`timeout`]),te=/* @__PURE__ */ new Set([408,425,429]);function f(f){let p=f.reconnect??{},m=p.minDelayMs??250,h=p.maxDelayMs??3e4,ne=p.stableMs??5e3,g=p.random??Math.random,re=f.maxQueued??100,ie=f.queueTtlMs??3e4,ae=f.ackTimeoutMs??1e4,oe=(f.transport??`auto`)===`auto`,se=Array.from(globalThis.crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(6)),e=>e.toString(36).padStart(2,`0`)).join(``),_=/* @__PURE__ */ new Map,v=/* @__PURE__ */ new Set,y=[],b=/* @__PURE__ */ new Map,x=/* @__PURE__ */ new Set,S=`connecting`,C=f.transport===`sse`?`sse`:`ws`,w=!1,T=!1,E,D=0,O=!1,k,ce=65536,le=``,ue,A=0,j=0,de=0,fe=0,pe=0,M=0,N,P,me,F,I=!1,he,L,R=0,z=!1,B=e=>{try{f.onError===void 0?console.error(`[realtime]`,e):f.onError(e)}catch{}},V=e=>{try{e()}catch(e){B(e)}},ge=(e,t)=>(f.fetch??globalThis.fetch)(e,t),_e=()=>typeof f.headers==`function`?f.headers():f.headers,H=(e,t)=>{for(let n of[...e])V(()=>n(t))},U=e=>{e!==S&&(S=e,f.onStatus!==void 0&&V(()=>f.onStatus?.(e)),H(x,e))},W=(e,t)=>{e.status!==t&&(e.status=t,H(e.sl,t))},G=(e,t,n)=>{clearTimeout(e.timer);let r=e.settle;e.settle=void 0,r===void 0?t!==void 0&&e.kind===`pub`&&B(t):r(t,n)},ve=(t,n)=>{clearTimeout(t.timer),t.timer=setTimeout(()=>{let n=y.indexOf(t);n>=0&&y.splice(n,1),b.get(t.n)===t&&b.delete(t.n),G(t,new e(`timeout`))},n)},ye=e=>{let t=b.get(e);return t!==void 0&&b.delete(e),t},be=t=>{if(w)return new e(`closed`);let n=O&&C===`ws`;if(t.kind!==`pres`&&!n&&y.length>=re)return new e(`queue_full`);if(t.kind===`pres`){let e=y.findIndex(e=>e.kind===`pres`&&e.chan===t.chan);e>=0&&y.splice(e,1)}else ve(t,ie);y.push(t),O?Z():Q()},xe=(e,t,n,r,i)=>({kind:e,chan:t,event:n,data:r,ack:!0,ms:0,order:++pe,n:0,...i}),Se=e=>e.kind===`req`||e.kind===`pub`&&e.ack,Ce=e=>e.kind===`req`?e.ms+1e3:ae,we=n=>{let r=JSON.stringify(n.chan.name),i=Se(n);n.n=i?++de:0;let a=n.kind===`pres`?`{"t":"pres","c":${r},"s":${n.data}}`:n.kind===`req`?`{"t":"req","n":${n.n},"c":${r},"e":"${n.event}","d":${n.data},"ms":${n.ms}}`:`{"t":"pub","c":${r},"e":"${n.event}","d":${n.data}${i?`,"n":${n.n}`:``},"k":${JSON.stringify(n.key)}}`;if(t(a)>ce)G(n,new e(`too_large`));else return a},Te=e=>{e.kind!==`pres`&&(Se(e)?(b.set(e.n,e),ve(e,Ce(e))):clearTimeout(e.timer))},Ee=t=>{t.sort((e,t)=>e.order-t.order);let n=[];for(let r of t)r.kind===`req`?G(r,new e(`closed`)):(r.kind===`pub`&&ve(r,ie),n.push(r));y.unshift(...n)},K=e=>!e.closed&&(e.refs>0||e.state!==void 0||e.options.watch===!0||e.hd>0&&C===`ws`),q=e=>e.state!==void 0,De=e=>!q(e)&&(e.presence.size>0||e.options.watch===!0),J=e=>{e.status===`idle`&&K(e)&&W(e,`subscribing`),v.add(e),Q()},Oe=(e,t)=>{let n=q(e),r=De(e),i=e.sy?Math.max(e.last,e.gh):e.options.replay===!0?0:void 0;return e.w={n:t,join:n,watch:r,after:i,ver:e.sv,ready:!n},W(e,e.sy?`resuming`:`subscribing`),{c:e.name,...i===void 0?{}:{after:i},...e.options.echo===!1?{echo:!1}:{},...n?{presence:e.state}:{},...r?{watch:!0}:{}}},ke=e=>{let t=e.w;if(t===void 0||e.state===void 0)return;t.ver=e.sv;let n=xe(`pres`,e,``,JSON.stringify(e.state),{});if(C===`ws`){let e=we(n);e!==void 0&&E?.send(e)}else be(n)},Ae=e=>{e.sy=!1,e.last=0,e.w=void 0,clearTimeout(e.retry),e.retry=void 0,e.fails=0,e.members.size>0&&(e.members.clear(),e.list=void 0),W(e,e.closed?`closed`:`idle`)},je=e=>{E?.send(`{"t":"unsub","c":${JSON.stringify(e.name)}}`)},Me=e=>{let t=K(e),n=e.w;if(C===`ws`&&O&&(n===void 0||t&&n.join===q(e)&&n.watch===De(e)||(je(e),e.w=void 0),t&&e.w===void 0&&e.retry===void 0)){let t=++de;E?.send(JSON.stringify({t:`sub`,n:t,...Oe(e,t)}))}if(!t){e.status!==`idle`&&e.status!==`closed`&&Ae(e);return}let r=e.w;r?.join&&r.ready&&r.ver!==e.sv&&ke(e)},Ne=()=>{let e=[...v];v.clear();for(let t of e)Me(t)},Y=(e,t)=>{e.closed||(e.closed=!0,_.get(e.name)===e&&_.delete(e.name),e.w!==void 0&&C===`ws`&&O&&je(e),Ae(e),e.ev.clear(),e.any.clear(),e.gl.clear(),e.presence.clear(),e.sl.clear(),e.refs=0,v.delete(e),t!==void 0&&B(t),Q())},Pe=(e,t)=>{e.w=void 0,W(e,e.sy?`resuming`:`subscribing`),t?J(e):(e.retry=setTimeout(()=>{e.retry=void 0,J(e)},c(e.fails,m,h,g)),e.fails+=1)},Fe=()=>{let e=``;for(let t of _.values())K(t)&&(e+=`${t.name}|${t.options.echo===!1?0:1}${+!!q(t)}${+!!De(t)};`);return e},X=()=>{if(clearTimeout(P),P=void 0,w||E!==void 0)return;D+=1;let t=D;O=!1,M=0;let n={rx:()=>{t===D&&(M=0)},frame:e=>{t===D&&V(()=>Ve(e,!1))},closed:e=>{t===D&&ze(e)}};try{if(!z&&typeof addEventListener==`function`&&(z=!0,addEventListener(`online`,Ie),typeof document<`u`&&document.addEventListener(`visibilitychange`,Le)),C===`sse`){let t=l(f.url,!1),r=[];for(let t of[..._.values()])K(t)&&(r.length>=64?Y(t,new e(`limit`)):r.push(Oe(t,r.length+1)));le=Fe(),t.searchParams.set(`s`,JSON.stringify(r)),E=s(t.toString(),ge,_e,n)}else E=u(l(f.url,!0).toString(),f.WebSocket??globalThis.WebSocket,n)}catch(t){D+=1,$(new e(`closed`,void 0,{cause:t}));return}N=setTimeout(()=>{t===D&&ze({})},ae)},Ie=()=>{P!==void 0&&X()},Le=()=>{document.visibilityState===`visible`&&P!==void 0&&X()},Re=()=>{D+=1;let e=E;E=void 0,O=!1,k=void 0,clearTimeout(N),clearTimeout(me),clearInterval(F),F=void 0,e?.close()},ze=t=>{let n=O,r=ue??t.code;if(ue=void 0,Re(),w)return;if(C===`ws`&&b.size>0){let e=[...b.values()];b.clear(),Ee(e)}for(let e of _.values())e.w=void 0,clearTimeout(e.retry),e.retry=void 0,K(e)&&W(e,e.sy?`resuming`:`subscribing`);let i=t.status!==void 0&&t.status>=400&&t.status<500&&!te.has(t.status);if((r!==void 0&&d.has(r)||i)&&p.retryFatal!==!0){$(new e(`closed`,void 0));return}if(j>=(p.maxReconnectAttempts??1/0)){$(new e(`closed`));return}let a=c(j,m,h,g);j+=1,n?A=0:oe&&C===`ws`&&(A+=1,A>=2&&(C=`sse`,a=0)),U(`reconnecting`),P=setTimeout(X,a)},Be=()=>{M+=1,M>2?ze({}):C===`ws`&&E?.send(`{"t":"ping"}`)},Ve=(t,n)=>{switch(t.t){case`ready`:if(O)return;if(O=!0,k=t.id,ce=t.max,A=0,M=0,clearTimeout(N),t.hb>0&&(F=setInterval(Be,t.hb)),me=setTimeout(()=>{j=0},ne),U(`open`),C===`ws`)for(let e of _.values())v.add(e);Z();return;case`bye`:ue=t.code;return;case`subbed`:{let e=_.get(t.c),n=e?.w;if(e===void 0||n===void 0||n.n!==t.n)return;(t.reset||n.after===void 0)&&(e.last=t.head);let r=t.reset===!0||t.gap===!0&&n.after!==void 0&&!(n.after===0&&!e.sy);e.sy=!0,e.fails=0,W(e,`live`),e.gh=r?t.head:0,r&&H(e.gl,{after:n.after??0,head:t.head,reset:t.reset===!0});return}case`unsubbed`:{let n=_.get(t.c);if(n===void 0)return;t.why===`revoked`?(n.w=void 0,Y(n,new e(`forbidden`,void 0))):Pe(n,t.why===`gap`||t.why===`reset`);return}case`msg`:{let e=_.get(t.c);if(e===void 0||t.s<=e.last)return;e.last=t.s;let n={seq:t.s,replayed:t.r===1,...t.p===void 0?{}:{publisher:t.p},...t.u===void 0?{}:{user:t.u}};for(let r of[...e.ev.get(t.e)??[]])V(()=>r(t.d,n));for(let r of[...e.any])V(()=>r({event:t.e,data:t.d,meta:n}));return}case`ack`:{let e=ye(t.n);e!==void 0&&G(e,void 0,{seq:t.s,duplicate:t.dup===!0});return}case`rep`:{let e=ye(t.n);e!==void 0&&G(e,void 0,t.d);return}case`err`:{let r=new e(t.code,t.msg),i=t.c===void 0?void 0:_.get(t.c);if(t.n!==void 0){if(!n&&i?.w?.n===t.n){ee.has(t.code)?(Pe(i,!1),B(r)):(i.w=void 0,Y(i,r));return}let e=n||C===`ws`?ye(t.n):void 0;if(e!==void 0){G(e,r);return}}B(r);return}case`pres`:{let e=_.get(t.c);if(e===void 0)return;o(e.members,t.op,t.m)&&(e.list=[...e.members.values()],H(e.presence,e.list)),t.op===`sync`&&e.w!==void 0&&(e.w.ready=!0,e.w.join&&e.w.ver!==e.sv&&ke(e));return}}},Z=()=>{if(O&&!w){if(Ne(),C===`sse`)He();else for(let e=y[0];e!==void 0&&O;e=y[0]){let t=we(e);if(t!==void 0){if(E?.send(t)!==!0)return;Te(e)}y.shift()}}},He=()=>{if(I||L!==void 0||k===void 0)return;let e=[],t=[],n=0;for(;y.length>0&&e.length<64&&n<256e3;){let r=y.shift();if(r===void 0)break;let i=we(r);i!==void 0&&(e.push(r),t.push(i),n+=i.length)}e.length!==0&&(I=!0,Ue(e,`{"cid":${JSON.stringify(k)},"frames":[${t.join(`,`)}]}`).then(void 0,B))},Ue=async(t,n)=>{for(let e of t)Te(e);let r=!1,i=new AbortController;he=i;let o=setTimeout(()=>i.abort(),Math.max(...t.map(Ce)));try{let o=new Headers(await _e());o.set(`content-type`,`application/json`);let s=l(f.postUrl??f.url,!1);f.postUrl===void 0&&(s.pathname=`${s.pathname.replace(/\/$/,``)}/send`);let c=await ge(s,{method:`POST`,headers:o,body:n,signal:i.signal});if(c.ok){let e=await c.json();if(Array.isArray(e.frames))for(let t of e.frames){let e=a(t);e!==void 0&&V(()=>Ve(e,!0))}R=0}else if(c.status>=500||te.has(c.status))r=!0;else{let n=await c.json().catch(()=>void 0),r=n?.error?.code??`unavailable`;for(let i of t)b.get(i.n)===i&&b.delete(i.n),G(i,new e(r,n?.error?.message))}}catch{if(w)return;r=!0}finally{clearTimeout(o)}I=!1;let s=t.filter(e=>Se(e)&&b.get(e.n)===e);for(let e of s)b.delete(e.n);if(r){for(let t of s)t.kind===`req`&&G(t,new e(`unavailable`));Ee(t.filter(e=>e.kind===`pres`||e.kind===`pub`&&(!e.ack||s.includes(e)))),R+=1,L=setTimeout(()=>{L=void 0,Z()},c(R-1,m,h,g))}else for(let t of s)G(t,new e(`unavailable`));Z()},We=()=>{if(T=!1,!w){if(Ne(),E===void 0)P===void 0&&(y.length>0||[..._.values()].some(K))&&X();else if(C===`sse`&&(Fe()!==le||[..._.values()].some(e=>K(e)&&e.w===void 0&&e.retry===void 0))){Re();for(let e of _.values())e.w=void 0;X()}else Z()}};function Q(){T||w||(T=!0,queueMicrotask(We))}function $(t){if(w)return;w=!0,clearTimeout(P),clearTimeout(L),P=void 0,L=void 0,Re(),he?.abort(),z&&=(typeof removeEventListener==`function`&&removeEventListener(`online`,Ie),typeof document<`u`&&document.removeEventListener(`visibilitychange`,Le),!1);let n=t??new e(`closed`),r=[...y.splice(0),...b.values()];b.clear();for(let e of r)e.settle===void 0?clearTimeout(e.timer):G(e,n);for(let e of[..._.values()])Y(e);_.clear(),v.clear(),U(`closed`),x.clear(),t!==void 0&&B(t)}let Ge=(t,n)=>{let a={name:t,options:n,ev:/* @__PURE__ */ new Map,any:/* @__PURE__ */ new Set,gl:/* @__PURE__ */ new Set,sl:/* @__PURE__ */ new Set,presence:/* @__PURE__ */ new Set,members:/* @__PURE__ */ new Map,status:`idle`,refs:0,hd:0,last:0,gh:0,sy:!1,state:n.presence,sv:0,fails:0,closed:!1};_.set(t,a),K(a)&&J(a);let o=(e,t,n,r)=>{if(a.closed)return()=>void 0;let i=!e.has(t);return e.add(t),!n&&i&&(a.refs+=1,J(a)),()=>{i&&(i=!1,e.delete(t)&&!n&&(--a.refs,r?.(),J(a)))}},s=(t,n,o,s,c,l)=>{let u=`null`,d;if(a.closed)d=new e(`closed`);else if(!r(n)||s!==void 0&&!i(s))d=new e(`bad_frame`);else try{u=JSON.stringify(o??null)??`null`}catch(t){d=new e(`bad_frame`,void 0,{cause:t})}return d===void 0?new Promise((e,r)=>{let i=t===`req`,o=xe(t,a,n,u,{key:i?void 0:s??`${se}:${++fe}`,ack:c,ms:l,settle:c?(t,n)=>{i&&(--a.hd,J(a)),t?r(t):e(n)}:void 0});i&&(a.hd+=1,J(a));let d=be(o);d===void 0?c||e({seq:0,duplicate:!1}):(o.settle=void 0,i&&--a.hd,r(d))}):Promise.reject(d)},c={name:t,get status(){return a.status},get lastSeq(){return a.last},on(e,t){let n=a.ev.get(e);n===void 0&&(n=/* @__PURE__ */ new Set,a.ev.set(e,n));let r=n;return o(r,t,!1,()=>{r.size===0&&a.ev.get(e)===r&&a.ev.delete(e)})},onAny:e=>o(a.any,e),onGap:e=>o(a.gl,e),onStatus:e=>o(a.sl,e,!0),publish:(e,t,n={})=>s(`pub`,e,t,n.key,n.ack!==!1,0),request:(e,t,n={})=>s(`req`,e,t,void 0,!0,n.timeoutMs??1e4),presence:{members:()=>(a.list??=[...a.members.values()],a.list),set(e){a.closed||(a.state=e,a.sv+=1,J(a))},leave(){a.closed||a.state===void 0||(a.state=void 0,a.sv+=1,J(a))},on:e=>o(a.presence,e)},close:()=>Y(a)};return a.hn=c,c};return{channel(t,r={}){if(w)throw new e(`closed`);if(!n(t))throw new e(`bad_channel`,t);return _.get(t)?.hn??Ge(t,r)},get status(){return S},get connectionId(){return k},onStatus(e){return w||x.add(e),()=>{x.delete(e)}},close:()=>$()}}export{f as createRealtime};
2
2
  //# sourceMappingURL=core.js.map