durablews 1.0.1 → 2.0.0-alpha.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/backoff.ts","../src/codec.ts","../src/fsm.ts","../src/heartbeat.ts","../src/helpers/event-bus.ts","../src/pipeline.ts","../src/queue.ts","../src/schema.ts","../src/client.ts","../src/helpers/binding.ts","../src/react.ts"],"names":["promise","useState","useSyncExternalStore","useEffect"],"mappings":"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","file":"react.cjs","sourcesContent":["import type { ReconnectOptions } from \"@/types\";\n\n/** `ReconnectOptions` with every field filled in. */\nexport type ResolvedReconnect = Required<ReconnectOptions>;\n\n/**\n * The durable-by-default reconnection policy: full-jitter exponential backoff,\n * never give up. See the RFC's M3 decisions for the rationale behind each value.\n */\nexport const RECONNECT_DEFAULTS: ResolvedReconnect = {\n baseDelay: 500,\n factor: 2,\n maxDelay: 30_000,\n jitter: true,\n maxRetries: Number.POSITIVE_INFINITY,\n shouldReconnect: () => true\n};\n\n/**\n * Resolve the user's `reconnect` config: `false` disables reconnection\n * entirely (`null`), anything else is merged over the defaults.\n */\nexport function resolveReconnect(\n option: false | ReconnectOptions | undefined\n): ResolvedReconnect | null {\n if (option === false) {\n return null;\n }\n return { ...RECONNECT_DEFAULTS, ...option };\n}\n\n/**\n * Compute the backoff delay for a retry.\n *\n * The exponential delay is `baseDelay × factorᵃᵗᵗᵉᵐᵖᵗ`, capped at `maxDelay`.\n * With jitter (the default), the actual delay is drawn uniformly from\n * `[0, exponential]` — \"full jitter\", which spreads a fleet of simultaneously\n * dropped clients across the whole window instead of letting them retry in\n * synchronized waves.\n *\n * @param attempt - 0-based: the first retry is attempt `0`.\n * @param random - injectable for deterministic tests; defaults to `Math.random`.\n */\nexport function computeDelay(\n attempt: number,\n options: ResolvedReconnect,\n random: () => number = Math.random\n): number {\n const exponential = Math.min(\n options.maxDelay,\n options.baseDelay * options.factor ** attempt\n );\n return options.jitter ? random() * exponential : exponential;\n}\n","import type { Codec } from \"@/types\";\n\n/**\n * Parse a string as JSON, falling back to the raw string when it isn't valid\n * JSON. Lets text protocols that mix JSON and plain strings work unchanged.\n */\nfunction safeJSONParse(data: string): unknown {\n try {\n return JSON.parse(data);\n } catch {\n return data;\n }\n}\n\n/**\n * Whether a value is already a WebSocket-sendable binary frame and should be\n * passed through rather than JSON-encoded (`ArrayBuffer`, any typed array or\n * `DataView`, or a `Blob`).\n *\n * The predicate claims `BufferSource` (what `WebSocket.send` accepts), which\n * asserts the view is not backed by a `SharedArrayBuffer` — `send` rejects\n * those at runtime, so they were never sendable anyway.\n */\nfunction isBinary(data: unknown): data is BufferSource | Blob {\n return (\n data instanceof ArrayBuffer ||\n ArrayBuffer.isView(data) ||\n (typeof Blob !== \"undefined\" && data instanceof Blob)\n );\n}\n\n/**\n * The default codec: JSON over text frames, binary passed through.\n *\n * - `encode` — strings and binary frames (`ArrayBuffer`/typed arrays/`Blob`)\n * are sent verbatim; everything else is `JSON.stringify`d.\n * - `decode` — text frames are JSON-parsed (falling back to the raw string);\n * binary frames are passed through untouched.\n */\nexport const jsonCodec: Codec = {\n encode(data: unknown) {\n if (typeof data === \"string\" || isBinary(data)) {\n return data;\n }\n return JSON.stringify(data);\n },\n decode(data: unknown) {\n return typeof data === \"string\" ? safeJSONParse(data) : data;\n }\n};\n","import type { ConnectionEvent, ConnectionState } from \"@/types\";\n\n/**\n * The connection lifecycle as an explicit finite state machine.\n *\n * A connection has a small, fixed set of legal states and transitions. Modelling\n * it as a table — rather than a free-form reducer — means an illegal transition\n * (e.g. a `CLOSED` event arriving while already `closed`) is *representable as the\n * absence of an entry* and can be rejected, instead of silently doing nothing.\n * That silent no-op is exactly what allowed the original close/error bug.\n *\n * `ERROR` is deliberately absent from the table: a transport error does not by\n * itself change the connection state (the browser/runtime fires `error` and then\n * `close`), so it is handled out-of-band by the client (record + emit) and the\n * following `CLOSED` event performs the actual transition.\n */\nconst TRANSITIONS: Record<\n ConnectionState,\n Partial<Record<ConnectionEvent, ConnectionState>>\n> = {\n idle: { CONNECT: \"connecting\" },\n connecting: {\n OPEN: \"open\",\n CLOSE_REQUESTED: \"closing\",\n CLOSED: \"closed\",\n RETRY: \"reconnecting\"\n },\n open: {\n CLOSE_REQUESTED: \"closing\",\n CLOSED: \"closed\",\n RETRY: \"reconnecting\"\n },\n closing: { CLOSED: \"closed\" },\n // `reconnecting` = waiting out the backoff delay; no socket exists, so\n // CLOSE_REQUESTED goes straight to `closed` (there is nothing to wait for)\n // and OPEN/CLOSED cannot legally occur.\n reconnecting: {\n CONNECT: \"connecting\",\n CLOSE_REQUESTED: \"closed\"\n },\n closed: { CONNECT: \"connecting\" }\n};\n\n/**\n * Resolve the next state for an `(state, event)` pair.\n *\n * @returns the destination state, or `null` if the transition is illegal.\n */\nexport function nextState(\n state: ConnectionState,\n event: ConnectionEvent\n): ConnectionState | null {\n return TRANSITIONS[state][event] ?? null;\n}\n\n/**\n * Whether `(state, event)` is a legal transition.\n */\nexport function canTransition(\n state: ConnectionState,\n event: ConnectionEvent\n): boolean {\n return nextState(state, event) !== null;\n}\n","import type { HeartbeatOptions } from \"@/types\";\n\n/** `HeartbeatOptions` with every field filled in. */\nexport interface ResolvedHeartbeat {\n readonly interval: number;\n readonly message: unknown;\n readonly timeout: number;\n}\n\n/**\n * The close code used when the heartbeat declares the link dead (app-reserved\n * 4xxx range, mnemonic for HTTP 408 Request Timeout). Useful in a custom\n * `shouldReconnect` to distinguish heartbeat closes from server closes.\n */\nexport const HEARTBEAT_TIMEOUT_CODE = 4408;\n\n/**\n * Resolve the user's `heartbeat` config. Heartbeat is **opt-in**: absence means\n * off (`null`) — a naive \"no traffic → reconnect\" would kill legitimately\n * quiet-but-healthy connections, and any ping depends on app-level semantics\n * the library can't assume (see the RFC's M3 decisions).\n */\nexport function resolveHeartbeat(\n option: HeartbeatOptions | undefined\n): ResolvedHeartbeat | null {\n if (option === undefined) {\n return null;\n }\n return {\n interval: option.interval,\n message: option.message ?? \"ping\",\n timeout: option.timeout ?? option.interval\n };\n}\n","import type { EventBus } from \"@/types\";\n\n/**\n * Creates an event bus for managing event subscriptions and emissions.\n * Provides a simple pub/sub pattern with type-safe event handling.\n *\n * @returns EventBus instance with on, off, emit, and once methods\n *\n * @example\n * ```typescript\n * const eventBus = defineEventBus();\n *\n * // Subscribe to events\n * eventBus.on('user-login', (user) => {\n * console.log('User logged in:', user);\n * });\n *\n * // Emit events\n * eventBus.emit('user-login', { id: 1, name: 'John' });\n *\n * // One-time subscription\n * eventBus.once('app-ready', () => {\n * console.log('App is ready!');\n * });\n * ```\n */\nexport function defineEventBus(): EventBus {\n const listeners = new Map<string, Array<(payload: unknown) => void>>();\n\n /**\n * Subscribes to an event with a handler function.\n *\n * @template T - The type of the event payload\n * @param eventName - The name of the event to listen for\n * @param handler - Function to call when the event is emitted\n */\n function on<T = unknown>(\n eventName: string,\n handler: (payload: T) => void\n ): void {\n const handlers = listeners.get(eventName) ?? [];\n handlers.push(handler as (payload: unknown) => void);\n listeners.set(eventName, handlers);\n }\n\n /**\n * Unsubscribes a handler from an event.\n *\n * @template T - The type of the event payload\n * @param eventName - The name of the event to stop listening for\n * @param handler - The handler function to remove\n */\n function off<T = unknown>(\n eventName: string,\n handler: (payload: T) => void\n ) {\n const handlers = listeners.get(eventName);\n if (!handlers) {\n return;\n }\n listeners.set(\n eventName,\n handlers.filter((h) => h !== handler)\n );\n }\n\n /**\n * Subscribes to an event with a handler that will only be called once.\n * The handler is automatically removed after the first emission.\n *\n * @template T - The type of the event payload\n * @param eventName - The name of the event to listen for\n * @param handler - Function to call when the event is emitted (only once)\n */\n function once<T = unknown>(\n eventName: string,\n handler: (payload: T) => void\n ): void {\n const onceHandler = (payload: T) => {\n // Remove the handler after first invocation\n off(eventName, onceHandler as (payload: unknown) => void);\n // Call the original handler\n handler(payload);\n };\n\n // Add the wrapper handler\n on(eventName, onceHandler);\n }\n\n /**\n * Emits an event to all subscribed handlers.\n *\n * @template T - The type of the event payload\n * @param eventName - The name of the event to emit\n * @param payload - The data to pass to event handlers\n */\n function emit<T = unknown>(eventName: string, payload?: T) {\n const handlers = listeners.get(eventName);\n handlers?.forEach((fn) => {\n fn(payload);\n });\n }\n\n return { on, off, emit, once };\n}\n","/**\n * The functional shape shared by inbound and outbound middleware: receive a\n * context and a `next`, optionally async. The context type is the only thing\n * that differs per direction.\n */\ntype PipelineMiddleware<TContext> = (\n ctx: TContext,\n next: () => void | Promise<void>\n) => void | Promise<void>;\n\n/**\n * Runs a message through a middleware chain (onion model).\n *\n * Each middleware receives the shared context and a `next` it may call to pass\n * control onward. When the chain runs to completion, `terminal` is invoked —\n * for inbound messages that is where the client emits `message`; for outbound,\n * where it encodes and writes to the socket. A middleware that returns without\n * calling `next()` short-circuits the chain (and `terminal`).\n *\n * Mirrors the guard from the old store pipeline: calling `next()` more than once\n * in a single middleware is a bug and throws.\n *\n * @returns `void`, or a `Promise` if any middleware in the chain is async.\n */\nexport function runPipeline<TContext>(\n middlewares: readonly PipelineMiddleware<TContext>[],\n ctx: TContext,\n terminal: () => void\n): void | Promise<void> {\n let invoked = -1;\n\n function dispatch(i: number): void | Promise<void> {\n if (i <= invoked) {\n throw new Error(\"next() called multiple times\");\n }\n invoked = i;\n\n const middleware = middlewares[i];\n if (!middleware) {\n return terminal();\n }\n return middleware(ctx, () => dispatch(i + 1));\n }\n\n return dispatch(0);\n}\n","import type { QueueOptions } from \"@/types\";\n\n/** `QueueOptions` with every field filled in. */\nexport type ResolvedQueue = Required<QueueOptions>;\n\n/**\n * The default outbound-queue policy: bounded at 256 messages, drop-oldest.\n * Never silently unbounded (a memory leak), never silently lossy (every drop\n * emits a `drop` event). See the RFC's M3 decisions.\n */\nexport const QUEUE_DEFAULTS: ResolvedQueue = {\n maxSize: 256\n};\n\n/**\n * Resolve the user's `queue` config: `false` disables queueing entirely\n * (`null`), anything else is merged over the defaults.\n */\nexport function resolveQueue(\n option: false | QueueOptions | undefined\n): ResolvedQueue | null {\n if (option === false) {\n return null;\n }\n return { ...QUEUE_DEFAULTS, ...option };\n}\n","/**\n * The Standard Schema v1 interface (https://standardschema.dev), vendored as\n * types-only per the spec's guidance — implementing libraries (zod, valibot,\n * arktype, …) conform to this shape, so DurableWS can accept any of them\n * without depending on any of them. Keeps core zero-dependency.\n */\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n readonly \"~standard\": StandardSchemaV1.Props<Input, Output>;\n}\n\nexport declare namespace StandardSchemaV1 {\n export interface Props<Input = unknown, Output = Input> {\n readonly version: 1;\n readonly vendor: string;\n readonly validate: (\n value: unknown\n ) => Result<Output> | Promise<Result<Output>>;\n readonly types?: Types<Input, Output> | undefined;\n }\n\n export type Result<Output> = SuccessResult<Output> | FailureResult;\n\n export interface SuccessResult<Output> {\n readonly value: Output;\n readonly issues?: undefined;\n }\n\n export interface FailureResult {\n readonly issues: ReadonlyArray<Issue>;\n }\n\n export interface Issue {\n readonly message: string;\n readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;\n }\n\n export interface PathSegment {\n readonly key: PropertyKey;\n }\n\n export interface Types<Input = unknown, Output = Input> {\n readonly input: Input;\n readonly output: Output;\n }\n\n export type InferInput<Schema extends StandardSchemaV1> = NonNullable<\n Schema[\"~standard\"][\"types\"]\n >[\"input\"];\n\n export type InferOutput<Schema extends StandardSchemaV1> = NonNullable<\n Schema[\"~standard\"][\"types\"]\n >[\"output\"];\n}\n\n/**\n * Thrown (as an `error` event payload) when an inbound message fails the\n * configured schema. The message is **not** emitted — handlers only ever see\n * data that passed validation.\n */\nexport class SchemaValidationError extends Error {\n readonly issues: ReadonlyArray<StandardSchemaV1.Issue>;\n\n constructor(issues: ReadonlyArray<StandardSchemaV1.Issue>) {\n super(\n `Inbound message failed schema validation: ${issues\n .map((issue) => issue.message)\n .join(\"; \")}`\n );\n this.name = \"SchemaValidationError\";\n this.issues = issues;\n }\n}\n","import { computeDelay, resolveReconnect } from \"@/backoff\";\nimport { jsonCodec } from \"@/codec\";\nimport { nextState } from \"@/fsm\";\nimport { HEARTBEAT_TIMEOUT_CODE, resolveHeartbeat } from \"@/heartbeat\";\nimport { defineEventBus } from \"@/helpers/event-bus\";\nimport { runPipeline } from \"@/pipeline\";\nimport { resolveQueue } from \"@/queue\";\nimport { SchemaValidationError, type StandardSchemaV1 } from \"@/schema\";\nimport type {\n ClientEventMap,\n ClientState,\n ConnectionEvent,\n ConnectionState,\n DirectionalMiddleware,\n Middleware,\n OutboundMiddleware,\n WebSocketClient,\n WebSocketClientConfig\n} from \"@/types\";\n\n/**\n * Creates a WebSocket client driven by an explicit connection FSM.\n *\n * Durable by default: an unexpected disconnect schedules a reconnect with\n * full-jitter exponential backoff (see `backoff.ts`); pass `reconnect: false`\n * to opt out.\n *\n * @param config - Connection configuration (at minimum, a `url`).\n *\n * @example\n * ```typescript\n * const ws = client({ url: \"wss://example.com/socket\" });\n *\n * ws.on(\"message\", (data) => console.log(\"received:\", data));\n * ws.on(\"reconnecting\", ({ attempt, delay }) => {\n * console.log(`retry #${attempt} in ${Math.round(delay)}ms`);\n * });\n *\n * await ws.connect();\n * ws.send({ type: \"hello\", message: \"world\" });\n *\n * ws.close();\n * ```\n */\nexport function client(config: WebSocketClientConfig): WebSocketClient {\n const bus = defineEventBus();\n const codec = config.codec ?? jsonCodec;\n const reconnect = resolveReconnect(config.reconnect);\n const queue = resolveQueue(config.queue);\n const heartbeat = resolveHeartbeat(config.heartbeat);\n const middlewares: Middleware[] = [];\n const outboundMiddlewares: OutboundMiddleware[] = [];\n\n // Outbound messages awaiting an open socket, in send() order. Original\n // (un-encoded) values: encoding happens at flush, so a drop event can hand\n // the user back exactly what they passed to send().\n const queued: unknown[] = [];\n\n let socket: WebSocket | null = null;\n let state: ConnectionState = \"idle\";\n let lastError: Event | Error | null = null;\n\n // Heartbeat bookkeeping: when the last inbound frame (of any kind)\n // arrived, the ping interval, and the per-ping liveness deadline.\n let lastInboundAt = 0;\n let heartbeatTimer: ReturnType<typeof setInterval> | null = null;\n let heartbeatDeadline: ReturnType<typeof setTimeout> | null = null;\n\n // The serialized outbound path (in play only when outbound middleware is\n // registered). `outboundTail` is the tail of the in-flight chain: while\n // set, every new message chains behind it, so socket writes keep send()\n // order even when a middleware awaits. `requeueIndex` keeps messages that\n // were mid-pipeline when the connection dropped ahead of newer queued\n // sends; it resets on every socket close.\n let outboundTail: Promise<void> | null = null;\n let requeueIndex = 0;\n\n // True between a user close() and the next connect(); suppresses\n // reconnection so \"the user hung up\" is never retried.\n let closeRequested = false;\n // Retries used in the current disconnection episode (see ClientState).\n let retryAttempt = 0;\n let retryTimer: ReturnType<typeof setTimeout> | null = null;\n\n // subscribe() listeners plus the cached getState() snapshot. The cache\n // makes snapshots referentially stable between changes — required by\n // React's useSyncExternalStore (a fresh object per getSnapshot() call\n // would loop the renderer).\n const subscribers = new Set<() => void>();\n let snapshot: ClientState | null = null;\n\n // The in-flight connect() promise and its settlers. A single promise is\n // shared across concurrent/repeat connect() calls so the method is\n // idempotent, and it survives failed attempts while reconnection is\n // active — it settles only on first open or terminal failure.\n let pending: {\n readonly promise: Promise<void>;\n readonly resolve: () => void;\n readonly reject: (reason: Error) => void;\n } | null = null;\n\n /**\n * Applies an FSM event. Legal transitions update the state and emit\n * `statechange`; illegal ones are ignored (the table is the guard).\n *\n * @returns the new state if a transition occurred, otherwise `null`.\n */\n /**\n * Invalidates the cached snapshot and fires subscribe() listeners. Called\n * after every mutation of the observable state, before the corresponding\n * bus event, so any observer reading getState() sees fresh data.\n */\n function notify() {\n snapshot = null;\n for (const listener of [...subscribers]) {\n listener();\n }\n }\n\n function transition(event: ConnectionEvent): ConnectionState | null {\n const next = nextState(state, event);\n if (next === null || next === state) {\n return null;\n }\n\n const previous = state;\n state = next;\n notify();\n bus.emit(\"statechange\", { previous, current: next });\n return next;\n }\n\n function settleConnected() {\n pending?.resolve();\n pending = null;\n }\n\n function settleFailed(reason: Error) {\n pending?.reject(reason);\n pending = null;\n }\n\n function clearRetryTimer() {\n if (retryTimer !== null) {\n clearTimeout(retryTimer);\n retryTimer = null;\n }\n }\n\n function stopHeartbeat() {\n if (heartbeatTimer !== null) {\n clearInterval(heartbeatTimer);\n heartbeatTimer = null;\n }\n if (heartbeatDeadline !== null) {\n clearTimeout(heartbeatDeadline);\n heartbeatDeadline = null;\n }\n }\n\n /**\n * While open, sends the heartbeat message every `interval` ms and arms a\n * liveness deadline after each ping. Any inbound frame before the deadline\n * proves the link; none means it's dead — force-close with code 4408,\n * which flows into the normal reconnect machinery (the close is not\n * user-initiated, so it is retryable).\n */\n function startHeartbeat() {\n if (heartbeat === null) {\n return;\n }\n stopHeartbeat();\n heartbeatTimer = setInterval(() => {\n if (state !== \"open\" || !socket) {\n return;\n }\n const pingSentAt = Date.now();\n try {\n socket.send(codec.encode(heartbeat.message));\n } catch (error) {\n bus.emit(\"error\", toError(error));\n }\n if (heartbeatDeadline !== null) {\n clearTimeout(heartbeatDeadline);\n }\n heartbeatDeadline = setTimeout(() => {\n heartbeatDeadline = null;\n if (state === \"open\" && socket && lastInboundAt < pingSentAt) {\n const failure = new Error(\n `Heartbeat timeout: no inbound traffic within ${heartbeat.timeout}ms of a ping`\n );\n lastError = failure;\n notify();\n bus.emit(\"error\", failure);\n socket.close(HEARTBEAT_TIMEOUT_CODE, \"heartbeat timeout\");\n }\n }, heartbeat.timeout);\n }, heartbeat.interval);\n }\n\n /**\n * Final stage of the outbound pipeline: encode the (possibly transformed)\n * message and write it to the socket. The connection may have changed\n * while middleware awaited, so re-check here: if a retry is underway, the\n * *original* value is re-queued ahead of newer sends (middleware re-runs\n * at the next transmission, keeping e.g. tokens fresh); on a dead-end\n * state it surfaces as a `drop` — never silently lost.\n */\n function wire(transformed: unknown, original: unknown) {\n if (socket && state === \"open\") {\n socket.send(codec.encode(transformed));\n return;\n }\n if (\n queue !== null &&\n (state === \"connecting\" || state === \"reconnecting\")\n ) {\n if (queued.length >= queue.maxSize) {\n bus.emit(\"drop\", {\n data: queued.shift(),\n reason: \"overflow\"\n });\n if (requeueIndex > 0) {\n requeueIndex -= 1;\n }\n }\n queued.splice(requeueIndex, 0, original);\n requeueIndex += 1;\n notify();\n return;\n }\n bus.emit(\"drop\", { data: original, reason: \"close\" });\n }\n\n /**\n * Runs one message through the outbound middleware chain. Errors are\n * per-message: a throw/rejection surfaces as `error` and only this\n * message is skipped.\n */\n function runOutbound(data: unknown): void | Promise<void> {\n const ctx = { data, client: api };\n try {\n const result = runPipeline(outboundMiddlewares, ctx, () => {\n wire(ctx.data, data);\n });\n if (result instanceof Promise) {\n return result.catch((error: unknown) => {\n bus.emit(\"error\", toError(error));\n });\n }\n } catch (error) {\n bus.emit(\"error\", toError(error));\n }\n }\n\n /**\n * Sends a message through the outbound middleware pipeline, preserving\n * send() order: while any message is in flight (async middleware), newer\n * messages chain behind it. With no middleware in flight and an all-sync\n * chain, the path stays fully synchronous.\n */\n function transmit(data: unknown) {\n if (outboundTail === null) {\n const result = runOutbound(data);\n if (result instanceof Promise) {\n setOutboundTail(result);\n }\n return;\n }\n setOutboundTail(outboundTail.then(() => runOutbound(data)));\n }\n\n function setOutboundTail(run: Promise<void>) {\n const tail = run.then(() => {\n // Chain drained: restore the synchronous fast path.\n if (outboundTail === tail) {\n outboundTail = null;\n }\n });\n outboundTail = tail;\n }\n\n /**\n * Sends every queued message in order. Called once the socket opens,\n * *before* the `open` event, so the backlog (sent earlier in time)\n * precedes anything an `open` handler sends — under async outbound\n * middleware \"precedes\" means pipeline order; socket writes follow it.\n * A per-message encode/send failure surfaces as an `error` event and\n * flushing continues.\n */\n function flushQueue() {\n const hadQueued = queued.length > 0;\n requeueIndex = 0;\n while (queued.length > 0 && socket && state === \"open\") {\n const data = queued.shift();\n if (outboundMiddlewares.length === 0) {\n try {\n socket.send(codec.encode(data));\n } catch (error) {\n bus.emit(\"error\", toError(error));\n }\n } else {\n transmit(data);\n }\n }\n if (hadQueued) {\n notify();\n }\n }\n\n /**\n * Empties the queue as `drop` events — these messages will never be sent\n * (user `close()` or terminal failure). Never silently lossy.\n */\n function dropQueued() {\n const hadQueued = queued.length > 0;\n while (queued.length > 0) {\n bus.emit(\"drop\", { data: queued.shift(), reason: \"close\" });\n }\n if (hadQueued) {\n notify();\n }\n }\n\n /**\n * Whether an unexpected close should be retried: reconnection enabled, not\n * a user `close()`, retries left, and no `shouldReconnect` veto.\n */\n function isRetryable(event: CloseEvent): boolean {\n return (\n reconnect !== null &&\n !closeRequested &&\n retryAttempt < reconnect.maxRetries &&\n reconnect.shouldReconnect(event)\n );\n }\n\n function openSocket() {\n if (lastError !== null) {\n lastError = null;\n notify();\n }\n socket = config.protocols\n ? new WebSocket(config.url, config.protocols)\n : new WebSocket(config.url);\n if (config.binaryType) {\n socket.binaryType = config.binaryType;\n }\n\n socket.onopen = () => {\n retryAttempt = 0;\n transition(\"OPEN\");\n flushQueue();\n startHeartbeat();\n bus.emit(\"open\");\n settleConnected();\n };\n\n socket.onmessage = (event: MessageEvent) => {\n lastInboundAt = Date.now();\n deliver(event.data);\n };\n\n socket.onerror = (event: Event) => {\n lastError = event;\n notify();\n bus.emit(\"error\", event);\n };\n\n socket.onclose = (event: CloseEvent) => {\n const wasConnecting = state === \"connecting\";\n socket = null;\n stopHeartbeat();\n // A new disconnected period begins: in-flight outbound messages\n // re-queue from the front (see wire()).\n requeueIndex = 0;\n\n if (reconnect !== null && isRetryable(event)) {\n // Event order is deliberate: statechange (state already moved\n // to `reconnecting`), then the close that caused it, then the\n // retry announcement with attempt + delay.\n retryAttempt += 1;\n const delay = computeDelay(retryAttempt - 1, reconnect);\n transition(\"RETRY\");\n bus.emit(\"close\", event);\n bus.emit(\"reconnecting\", { attempt: retryAttempt, delay });\n retryTimer = setTimeout(() => {\n retryTimer = null;\n if (transition(\"CONNECT\") !== null) {\n openSocket();\n }\n }, delay);\n return; // pending connect() survives the retry\n }\n\n transition(\"CLOSED\");\n dropQueued();\n bus.emit(\"close\", event);\n // Terminal for any in-flight connect(): closed before first open\n // with no (further) retries coming.\n if (wasConnecting || pending) {\n settleFailed(\n new Error(\n retryAttempt > 0\n ? `WebSocket reconnect gave up after ${retryAttempt} attempt(s) (code ${event.code})`\n : `WebSocket closed before opening (code ${event.code}${\n event.reason ? `: ${event.reason}` : \"\"\n })`\n )\n );\n }\n };\n }\n\n /**\n * Runs a decoded — and, if a schema is configured, validated — inbound\n * message through the middleware chain. If the chain completes, the\n * message is emitted as `message`; a middleware that throws or rejects\n * surfaces as an `error`. Validation precedes middleware, so middleware\n * only ever sees trusted data; an invalid message surfaces as an `error`\n * (`SchemaValidationError`) and never reaches middleware or `message`.\n */\n function deliver(raw: unknown) {\n const decoded = codec.decode(raw);\n if (config.schema === undefined) {\n dispatchMessage(decoded);\n return;\n }\n let result: ReturnType<StandardSchemaV1[\"~standard\"][\"validate\"]>;\n try {\n result = config.schema[\"~standard\"].validate(decoded);\n } catch (error) {\n bus.emit(\"error\", toError(error));\n return;\n }\n if (result instanceof Promise) {\n result.then(handleValidated, (error: unknown) => {\n bus.emit(\"error\", toError(error));\n });\n return;\n }\n handleValidated(result);\n }\n\n function handleValidated(result: StandardSchemaV1.Result<unknown>) {\n if (result.issues) {\n bus.emit(\"error\", new SchemaValidationError(result.issues));\n return;\n }\n dispatchMessage(result.value);\n }\n\n function dispatchMessage(data: unknown) {\n const ctx = { data, client: api };\n const emit = () => {\n bus.emit(\"message\", ctx.data);\n };\n try {\n const result = runPipeline(middlewares, ctx, emit);\n if (result instanceof Promise) {\n result.catch((error: unknown) => {\n bus.emit(\"error\", toError(error));\n });\n }\n } catch (error) {\n bus.emit(\"error\", toError(error));\n }\n }\n\n /** Lazily create (or reuse) the shared connect() promise. */\n function ensurePending(): Promise<void> {\n if (pending) {\n return pending.promise;\n }\n let resolve!: () => void;\n let reject!: (reason: Error) => void;\n const promise = new Promise<void>((res, rej) => {\n resolve = res;\n reject = rej;\n });\n pending = { promise, resolve, reject };\n return promise;\n }\n\n const api: WebSocketClient = {\n get state() {\n return state;\n },\n\n connect() {\n if (state === \"open\") {\n return Promise.resolve();\n }\n if (state === \"connecting\") {\n return ensurePending();\n }\n if (state === \"closing\") {\n return Promise.reject(\n new Error(\n \"Cannot connect() while the connection is closing\"\n )\n );\n }\n if (state === \"reconnecting\") {\n // Skip the rest of the backoff wait and attempt now.\n clearRetryTimer();\n const promise = ensurePending();\n transition(\"CONNECT\");\n openSocket();\n return promise;\n }\n\n // idle or closed → start a fresh episode.\n closeRequested = false;\n retryAttempt = 0;\n if (transition(\"CONNECT\") === null) {\n return Promise.reject(\n new Error(`Cannot connect() from state \"${state}\"`)\n );\n }\n\n const promise = ensurePending();\n try {\n openSocket();\n } catch (error) {\n transition(\"CLOSED\");\n socket = null;\n settleFailed(toError(error));\n }\n\n return promise;\n },\n\n send(data: unknown) {\n if (socket && state === \"open\") {\n if (outboundMiddlewares.length === 0) {\n // No outbound middleware: today's direct path, including\n // synchronous encode errors thrown to the caller.\n socket.send(codec.encode(data));\n } else {\n transmit(data);\n }\n return;\n }\n // An open is coming (or being retried): queue, bounded drop-oldest.\n if (\n queue !== null &&\n (state === \"connecting\" || state === \"reconnecting\")\n ) {\n if (queued.length >= queue.maxSize) {\n bus.emit(\"drop\", {\n data: queued.shift(),\n reason: \"overflow\"\n });\n }\n queued.push(data);\n notify();\n return;\n }\n throw new Error(\n `Cannot send: connection is not open (state: \"${state}\")`\n );\n },\n\n close(code?: number, reason?: string) {\n closeRequested = true;\n clearRetryTimer();\n stopHeartbeat();\n if (retryAttempt !== 0) {\n retryAttempt = 0;\n notify();\n }\n // The user hung up: queued messages will never send. Surface them\n // now (deterministically), not when the socket finishes closing.\n dropQueued();\n\n if (state === \"reconnecting\") {\n // No socket exists while waiting out the backoff: go straight\n // to closed and terminate any in-flight connect().\n transition(\"CLOSE_REQUESTED\");\n settleFailed(\n new Error(\"close() called before the connection opened\")\n );\n return;\n }\n if (!socket) {\n return;\n }\n transition(\"CLOSE_REQUESTED\");\n socket.close(code, reason);\n },\n\n on<K extends keyof ClientEventMap>(\n event: K,\n handler: (payload: ClientEventMap[K]) => void\n ) {\n bus.on(event, handler);\n return () => bus.off(event, handler);\n },\n\n use(middleware: Middleware | DirectionalMiddleware) {\n if (typeof middleware === \"function\") {\n middlewares.push(middleware);\n return api;\n }\n if (middleware.inbound) {\n middlewares.push(middleware.inbound);\n }\n if (middleware.outbound) {\n outboundMiddlewares.push(middleware.outbound);\n }\n return api;\n },\n\n getState(): ClientState {\n if (snapshot === null) {\n snapshot = Object.freeze({\n state,\n lastError,\n retryAttempt,\n queueLength: queued.length\n });\n }\n return snapshot;\n },\n\n subscribe(listener: () => void) {\n subscribers.add(listener);\n return () => {\n subscribers.delete(listener);\n };\n }\n };\n\n return api;\n}\n\n/** Normalize an unknown thrown value into an `Error`. */\nfunction toError(value: unknown): Error {\n return value instanceof Error ? value : new Error(String(value));\n}\n","import { client } from \"@/client\";\nimport type { WebSocketClient, WebSocketClientConfig } from \"@/types\";\n\n/**\n * What a framework binding accepts: either a config (the binding creates and\n * **owns** the client — connecting it and closing it with the component) or an\n * existing client (the binding only **observes** it — sharing one connection\n * across many components without any binding taking over its lifecycle).\n */\nexport type UseWebSocketSource<TIn = unknown, TOut = unknown> =\n | WebSocketClientConfig\n | WebSocketClient<TIn, TOut>;\n\nexport interface ResolvedSource<TIn = unknown, TOut = unknown> {\n readonly client: WebSocketClient<TIn, TOut>;\n /** Whether the binding created the client and therefore owns its lifecycle. */\n readonly owned: boolean;\n}\n\n/**\n * Resolves a binding source into a client plus ownership. A config produces a\n * fresh, owned client; a passed-in client is borrowed as-is.\n */\nexport function resolveSource<TIn = unknown, TOut = unknown>(\n source: UseWebSocketSource<TIn, TOut>\n): ResolvedSource<TIn, TOut> {\n if (isClient(source)) {\n return { client: source, owned: false };\n }\n return {\n client: client(source) as WebSocketClient<TIn, TOut>,\n owned: true\n };\n}\n\nfunction isClient<TIn, TOut>(\n source: UseWebSocketSource<TIn, TOut>\n): source is WebSocketClient<TIn, TOut> {\n return typeof (source as WebSocketClient).subscribe === \"function\";\n}\n\n/**\n * Whether a standard `WebSocket` global exists. Bindings skip auto-connect in\n * its absence (e.g. SSR), so creating a component server-side never throws —\n * the client connects when setup runs again in the browser.\n */\nexport function canConnect(): boolean {\n return typeof WebSocket !== \"undefined\";\n}\n","/**\n * React bindings for durablews — `import { useWebSocket } from \"durablews/react\"`.\n *\n * Requires React 18+ (an optional peer dependency: installing durablews\n * without React never warns; this module only loads when imported).\n */\nimport { useEffect, useState, useSyncExternalStore } from \"react\";\nimport { resolveSource, type UseWebSocketSource } from \"@/helpers/binding\";\nimport type { StandardSchemaV1 } from \"@/schema\";\nimport type {\n ConnectionState,\n WebSocketClient,\n WebSocketClientConfig\n} from \"@/types\";\n\n/**\n * What {@link useWebSocket} returns: the client itself plus its observable\n * state as plain render-ready values.\n */\nexport interface UseWebSocketReturn<TIn = unknown, TOut = unknown> {\n /** The underlying client, for everything beyond the hook's surface. */\n readonly client: WebSocketClient<TIn, TOut>;\n /** The connection state. */\n readonly state: ConnectionState;\n /** The most recent failure, if any. */\n readonly lastError: Event | Error | null;\n /** Retries used in the current disconnection episode. */\n readonly retryAttempt: number;\n /** Messages currently waiting in the outbound queue. */\n readonly queueLength: number;\n /**\n * The most recent inbound message (decoded and, if a schema is configured,\n * validated). `undefined` until the first message arrives. Only the latest\n * message is retained — handle the `message` event on {@link client} to\n * process every message.\n */\n readonly lastMessage: TIn | undefined;\n /** Sends data (queueing while disconnected, per the client's config). */\n readonly send: (data: TOut) => void;\n /** Opens the connection. See `WebSocketClient.connect`. */\n readonly connect: () => Promise<void>;\n /** Closes the connection. */\n readonly close: (code?: number, reason?: string) => void;\n}\n\n/**\n * A hook exposing a durablews client as React state, built on\n * `useSyncExternalStore` (the client's `subscribe`/`getState` pair is exactly\n * that contract, with referentially stable snapshots).\n *\n * Pass a **config** and the hook owns the client: it connects in an effect\n * (SSR-safe — effects don't run on the server) and closes the connection on\n * unmount. The config is captured on first render; changing it later does not\n * recreate the client. Pass an **existing client** (e.g. an app-wide singleton\n * shared by many components) and the hook only observes it — it never connects\n * or closes a client it was handed.\n *\n * @example\n * ```tsx\n * import { useWebSocket } from \"durablews/react\";\n *\n * function Status() {\n * const { state, lastMessage, send } = useWebSocket({\n * url: \"wss://example.com/socket\"\n * });\n * return (\n * <button onClick={() => send({ type: \"hello\" })}>\n * {state} — last: {JSON.stringify(lastMessage)}\n * </button>\n * );\n * }\n * ```\n */\nexport function useWebSocket<TSchema extends StandardSchemaV1>(\n source: WebSocketClientConfig & { readonly schema: TSchema }\n): UseWebSocketReturn<StandardSchemaV1.InferOutput<TSchema>>;\nexport function useWebSocket<TIn = unknown, TOut = unknown>(\n source: UseWebSocketSource<TIn, TOut>\n): UseWebSocketReturn<TIn, TOut>;\nexport function useWebSocket(source: UseWebSocketSource): UseWebSocketReturn {\n // Resolve exactly once per hook instance (state initializers run only on\n // first render), so re-renders never spawn extra clients.\n const [{ client: ws, owned }] = useState(() => resolveSource(source));\n\n const snapshot = useSyncExternalStore(\n ws.subscribe,\n ws.getState,\n ws.getState\n );\n\n const [lastMessage, setLastMessage] = useState<unknown>(undefined);\n useEffect(\n // The updater form guards against function-valued messages, which\n // setState would otherwise invoke instead of store.\n () => ws.on(\"message\", (msg) => setLastMessage(() => msg)),\n [ws]\n );\n\n useEffect(() => {\n if (!owned) {\n return;\n }\n // Terminal failures surface via the `error`/`close` events; swallow\n // the rejection so fire-and-forget auto-connect never goes unhandled.\n ws.connect().catch(() => {});\n // Strict Mode runs this effect twice: close() then connect() again is\n // a legal closed → fresh-episode sequence, so the double-run is safe.\n return () => ws.close();\n }, [ws, owned]);\n\n return {\n client: ws,\n state: snapshot.state,\n lastError: snapshot.lastError,\n retryAttempt: snapshot.retryAttempt,\n queueLength: snapshot.queueLength,\n lastMessage,\n send: ws.send,\n connect: ws.connect,\n close: ws.close\n };\n}\n\nexport type { UseWebSocketSource } from \"@/helpers/binding\";\n"]}
@@ -0,0 +1,66 @@
1
+ import { U as UseWebSocketSource } from './binding-bZNBe2Bz.cjs';
2
+ import { W as WebSocketClient, d as ConnectionState, S as StandardSchemaV1, a as WebSocketClientConfig } from './types-BDvztGcG.cjs';
3
+
4
+ /**
5
+ * What {@link useWebSocket} returns: the client itself plus its observable
6
+ * state as plain render-ready values.
7
+ */
8
+ interface UseWebSocketReturn<TIn = unknown, TOut = unknown> {
9
+ /** The underlying client, for everything beyond the hook's surface. */
10
+ readonly client: WebSocketClient<TIn, TOut>;
11
+ /** The connection state. */
12
+ readonly state: ConnectionState;
13
+ /** The most recent failure, if any. */
14
+ readonly lastError: Event | Error | null;
15
+ /** Retries used in the current disconnection episode. */
16
+ readonly retryAttempt: number;
17
+ /** Messages currently waiting in the outbound queue. */
18
+ readonly queueLength: number;
19
+ /**
20
+ * The most recent inbound message (decoded and, if a schema is configured,
21
+ * validated). `undefined` until the first message arrives. Only the latest
22
+ * message is retained — handle the `message` event on {@link client} to
23
+ * process every message.
24
+ */
25
+ readonly lastMessage: TIn | undefined;
26
+ /** Sends data (queueing while disconnected, per the client's config). */
27
+ readonly send: (data: TOut) => void;
28
+ /** Opens the connection. See `WebSocketClient.connect`. */
29
+ readonly connect: () => Promise<void>;
30
+ /** Closes the connection. */
31
+ readonly close: (code?: number, reason?: string) => void;
32
+ }
33
+ /**
34
+ * A hook exposing a durablews client as React state, built on
35
+ * `useSyncExternalStore` (the client's `subscribe`/`getState` pair is exactly
36
+ * that contract, with referentially stable snapshots).
37
+ *
38
+ * Pass a **config** and the hook owns the client: it connects in an effect
39
+ * (SSR-safe — effects don't run on the server) and closes the connection on
40
+ * unmount. The config is captured on first render; changing it later does not
41
+ * recreate the client. Pass an **existing client** (e.g. an app-wide singleton
42
+ * shared by many components) and the hook only observes it — it never connects
43
+ * or closes a client it was handed.
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * import { useWebSocket } from "durablews/react";
48
+ *
49
+ * function Status() {
50
+ * const { state, lastMessage, send } = useWebSocket({
51
+ * url: "wss://example.com/socket"
52
+ * });
53
+ * return (
54
+ * <button onClick={() => send({ type: "hello" })}>
55
+ * {state} — last: {JSON.stringify(lastMessage)}
56
+ * </button>
57
+ * );
58
+ * }
59
+ * ```
60
+ */
61
+ declare function useWebSocket<TSchema extends StandardSchemaV1>(source: WebSocketClientConfig & {
62
+ readonly schema: TSchema;
63
+ }): UseWebSocketReturn<StandardSchemaV1.InferOutput<TSchema>>;
64
+ declare function useWebSocket<TIn = unknown, TOut = unknown>(source: UseWebSocketSource<TIn, TOut>): UseWebSocketReturn<TIn, TOut>;
65
+
66
+ export { type UseWebSocketReturn, UseWebSocketSource, useWebSocket };
@@ -0,0 +1,66 @@
1
+ import { U as UseWebSocketSource } from './binding-gyPQiK9i.js';
2
+ import { W as WebSocketClient, d as ConnectionState, S as StandardSchemaV1, a as WebSocketClientConfig } from './types-BDvztGcG.js';
3
+
4
+ /**
5
+ * What {@link useWebSocket} returns: the client itself plus its observable
6
+ * state as plain render-ready values.
7
+ */
8
+ interface UseWebSocketReturn<TIn = unknown, TOut = unknown> {
9
+ /** The underlying client, for everything beyond the hook's surface. */
10
+ readonly client: WebSocketClient<TIn, TOut>;
11
+ /** The connection state. */
12
+ readonly state: ConnectionState;
13
+ /** The most recent failure, if any. */
14
+ readonly lastError: Event | Error | null;
15
+ /** Retries used in the current disconnection episode. */
16
+ readonly retryAttempt: number;
17
+ /** Messages currently waiting in the outbound queue. */
18
+ readonly queueLength: number;
19
+ /**
20
+ * The most recent inbound message (decoded and, if a schema is configured,
21
+ * validated). `undefined` until the first message arrives. Only the latest
22
+ * message is retained — handle the `message` event on {@link client} to
23
+ * process every message.
24
+ */
25
+ readonly lastMessage: TIn | undefined;
26
+ /** Sends data (queueing while disconnected, per the client's config). */
27
+ readonly send: (data: TOut) => void;
28
+ /** Opens the connection. See `WebSocketClient.connect`. */
29
+ readonly connect: () => Promise<void>;
30
+ /** Closes the connection. */
31
+ readonly close: (code?: number, reason?: string) => void;
32
+ }
33
+ /**
34
+ * A hook exposing a durablews client as React state, built on
35
+ * `useSyncExternalStore` (the client's `subscribe`/`getState` pair is exactly
36
+ * that contract, with referentially stable snapshots).
37
+ *
38
+ * Pass a **config** and the hook owns the client: it connects in an effect
39
+ * (SSR-safe — effects don't run on the server) and closes the connection on
40
+ * unmount. The config is captured on first render; changing it later does not
41
+ * recreate the client. Pass an **existing client** (e.g. an app-wide singleton
42
+ * shared by many components) and the hook only observes it — it never connects
43
+ * or closes a client it was handed.
44
+ *
45
+ * @example
46
+ * ```tsx
47
+ * import { useWebSocket } from "durablews/react";
48
+ *
49
+ * function Status() {
50
+ * const { state, lastMessage, send } = useWebSocket({
51
+ * url: "wss://example.com/socket"
52
+ * });
53
+ * return (
54
+ * <button onClick={() => send({ type: "hello" })}>
55
+ * {state} — last: {JSON.stringify(lastMessage)}
56
+ * </button>
57
+ * );
58
+ * }
59
+ * ```
60
+ */
61
+ declare function useWebSocket<TSchema extends StandardSchemaV1>(source: WebSocketClientConfig & {
62
+ readonly schema: TSchema;
63
+ }): UseWebSocketReturn<StandardSchemaV1.InferOutput<TSchema>>;
64
+ declare function useWebSocket<TIn = unknown, TOut = unknown>(source: UseWebSocketSource<TIn, TOut>): UseWebSocketReturn<TIn, TOut>;
65
+
66
+ export { type UseWebSocketReturn, UseWebSocketSource, useWebSocket };
package/dist/react.js ADDED
@@ -0,0 +1,42 @@
1
+ import { resolveSource } from './chunk-GIDTCSPD.js';
2
+ import './chunk-MG4TK53H.js';
3
+ import { useState, useSyncExternalStore, useEffect } from 'react';
4
+
5
+ function useWebSocket(source) {
6
+ const [{ client: ws, owned }] = useState(() => resolveSource(source));
7
+ const snapshot = useSyncExternalStore(
8
+ ws.subscribe,
9
+ ws.getState,
10
+ ws.getState
11
+ );
12
+ const [lastMessage, setLastMessage] = useState(void 0);
13
+ useEffect(
14
+ // The updater form guards against function-valued messages, which
15
+ // setState would otherwise invoke instead of store.
16
+ () => ws.on("message", (msg) => setLastMessage(() => msg)),
17
+ [ws]
18
+ );
19
+ useEffect(() => {
20
+ if (!owned) {
21
+ return;
22
+ }
23
+ ws.connect().catch(() => {
24
+ });
25
+ return () => ws.close();
26
+ }, [ws, owned]);
27
+ return {
28
+ client: ws,
29
+ state: snapshot.state,
30
+ lastError: snapshot.lastError,
31
+ retryAttempt: snapshot.retryAttempt,
32
+ queueLength: snapshot.queueLength,
33
+ lastMessage,
34
+ send: ws.send,
35
+ connect: ws.connect,
36
+ close: ws.close
37
+ };
38
+ }
39
+
40
+ export { useWebSocket };
41
+ //# sourceMappingURL=react.js.map
42
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react.ts"],"names":[],"mappings":";;;;AA+EO,SAAS,aAAa,MAAA,EAAgD;AAGzE,EAAA,MAAM,CAAC,EAAE,MAAA,EAAQ,EAAA,EAAI,KAAA,EAAO,CAAA,GAAI,QAAA,CAAS,MAAM,aAAA,CAAc,MAAM,CAAC,CAAA;AAEpE,EAAA,MAAM,QAAA,GAAW,oBAAA;AAAA,IACb,EAAA,CAAG,SAAA;AAAA,IACH,EAAA,CAAG,QAAA;AAAA,IACH,EAAA,CAAG;AAAA,GACP;AAEA,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAkB,MAAS,CAAA;AACjE,EAAA,SAAA;AAAA;AAAA;AAAA,IAGI,MAAM,GAAG,EAAA,CAAG,SAAA,EAAW,CAAC,GAAA,KAAQ,cAAA,CAAe,MAAM,GAAG,CAAC,CAAA;AAAA,IACzD,CAAC,EAAE;AAAA,GACP;AAEA,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,IAAI,CAAC,KAAA,EAAO;AACR,MAAA;AAAA,IACJ;AAGA,IAAA,EAAA,CAAG,OAAA,EAAQ,CAAE,KAAA,CAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAG3B,IAAA,OAAO,MAAM,GAAG,KAAA,EAAM;AAAA,EAC1B,CAAA,EAAG,CAAC,EAAA,EAAI,KAAK,CAAC,CAAA;AAEd,EAAA,OAAO;AAAA,IACH,MAAA,EAAQ,EAAA;AAAA,IACR,OAAO,QAAA,CAAS,KAAA;AAAA,IAChB,WAAW,QAAA,CAAS,SAAA;AAAA,IACpB,cAAc,QAAA,CAAS,YAAA;AAAA,IACvB,aAAa,QAAA,CAAS,WAAA;AAAA,IACtB,WAAA;AAAA,IACA,MAAM,EAAA,CAAG,IAAA;AAAA,IACT,SAAS,EAAA,CAAG,OAAA;AAAA,IACZ,OAAO,EAAA,CAAG;AAAA,GACd;AACJ","file":"react.js","sourcesContent":["/**\n * React bindings for durablews — `import { useWebSocket } from \"durablews/react\"`.\n *\n * Requires React 18+ (an optional peer dependency: installing durablews\n * without React never warns; this module only loads when imported).\n */\nimport { useEffect, useState, useSyncExternalStore } from \"react\";\nimport { resolveSource, type UseWebSocketSource } from \"@/helpers/binding\";\nimport type { StandardSchemaV1 } from \"@/schema\";\nimport type {\n ConnectionState,\n WebSocketClient,\n WebSocketClientConfig\n} from \"@/types\";\n\n/**\n * What {@link useWebSocket} returns: the client itself plus its observable\n * state as plain render-ready values.\n */\nexport interface UseWebSocketReturn<TIn = unknown, TOut = unknown> {\n /** The underlying client, for everything beyond the hook's surface. */\n readonly client: WebSocketClient<TIn, TOut>;\n /** The connection state. */\n readonly state: ConnectionState;\n /** The most recent failure, if any. */\n readonly lastError: Event | Error | null;\n /** Retries used in the current disconnection episode. */\n readonly retryAttempt: number;\n /** Messages currently waiting in the outbound queue. */\n readonly queueLength: number;\n /**\n * The most recent inbound message (decoded and, if a schema is configured,\n * validated). `undefined` until the first message arrives. Only the latest\n * message is retained — handle the `message` event on {@link client} to\n * process every message.\n */\n readonly lastMessage: TIn | undefined;\n /** Sends data (queueing while disconnected, per the client's config). */\n readonly send: (data: TOut) => void;\n /** Opens the connection. See `WebSocketClient.connect`. */\n readonly connect: () => Promise<void>;\n /** Closes the connection. */\n readonly close: (code?: number, reason?: string) => void;\n}\n\n/**\n * A hook exposing a durablews client as React state, built on\n * `useSyncExternalStore` (the client's `subscribe`/`getState` pair is exactly\n * that contract, with referentially stable snapshots).\n *\n * Pass a **config** and the hook owns the client: it connects in an effect\n * (SSR-safe — effects don't run on the server) and closes the connection on\n * unmount. The config is captured on first render; changing it later does not\n * recreate the client. Pass an **existing client** (e.g. an app-wide singleton\n * shared by many components) and the hook only observes it — it never connects\n * or closes a client it was handed.\n *\n * @example\n * ```tsx\n * import { useWebSocket } from \"durablews/react\";\n *\n * function Status() {\n * const { state, lastMessage, send } = useWebSocket({\n * url: \"wss://example.com/socket\"\n * });\n * return (\n * <button onClick={() => send({ type: \"hello\" })}>\n * {state} — last: {JSON.stringify(lastMessage)}\n * </button>\n * );\n * }\n * ```\n */\nexport function useWebSocket<TSchema extends StandardSchemaV1>(\n source: WebSocketClientConfig & { readonly schema: TSchema }\n): UseWebSocketReturn<StandardSchemaV1.InferOutput<TSchema>>;\nexport function useWebSocket<TIn = unknown, TOut = unknown>(\n source: UseWebSocketSource<TIn, TOut>\n): UseWebSocketReturn<TIn, TOut>;\nexport function useWebSocket(source: UseWebSocketSource): UseWebSocketReturn {\n // Resolve exactly once per hook instance (state initializers run only on\n // first render), so re-renders never spawn extra clients.\n const [{ client: ws, owned }] = useState(() => resolveSource(source));\n\n const snapshot = useSyncExternalStore(\n ws.subscribe,\n ws.getState,\n ws.getState\n );\n\n const [lastMessage, setLastMessage] = useState<unknown>(undefined);\n useEffect(\n // The updater form guards against function-valued messages, which\n // setState would otherwise invoke instead of store.\n () => ws.on(\"message\", (msg) => setLastMessage(() => msg)),\n [ws]\n );\n\n useEffect(() => {\n if (!owned) {\n return;\n }\n // Terminal failures surface via the `error`/`close` events; swallow\n // the rejection so fire-and-forget auto-connect never goes unhandled.\n ws.connect().catch(() => {});\n // Strict Mode runs this effect twice: close() then connect() again is\n // a legal closed → fresh-episode sequence, so the double-run is safe.\n return () => ws.close();\n }, [ws, owned]);\n\n return {\n client: ws,\n state: snapshot.state,\n lastError: snapshot.lastError,\n retryAttempt: snapshot.retryAttempt,\n queueLength: snapshot.queueLength,\n lastMessage,\n send: ws.send,\n connect: ws.connect,\n close: ws.close\n };\n}\n\nexport type { UseWebSocketSource } from \"@/helpers/binding\";\n"]}