Skip to content

Latest commit

 

History

History
1210 lines (907 loc) · 35 KB

File metadata and controls

1210 lines (907 loc) · 35 KB

API Reference

Complete API documentation for reactiveSWR.

Table of Contents


Main Entry Point (reactive-swr)

SSEProvider

Provider component that establishes and manages the SSE connection. Handles event dispatching, SWR cache mutations, reconnection with exponential backoff, and visibility-based reconnection.

import { SSEProvider } from 'reactive-swr'

function App() {
  return (
    <SWRConfig value={{ fetcher }}>
      <SSEProvider config={sseConfig}>
        <Dashboard />
      </SSEProvider>
    </SWRConfig>
  )
}

Props:

Prop Type Description
config SSEConfig Configuration for the SSE connection and event handling
children ReactNode Child components

Behavior:

  • Must be wrapped in SWR's SWRConfig provider
  • Only one SSEProvider should be active per SSE endpoint
  • Automatically reconnects on tab visibility change when the connection is lost
  • Supports three transport modes: native EventSource (default), fetch-based (when method, body, or headers are set), and custom (via transport factory)
  • When schema is provided in config, event mappings are derived automatically

useSSEContext

Low-level hook that returns the SSE context value. Most consumers should use useSSEStatus or useSSEEvent instead.

import { useSSEContext } from 'reactive-swr'

function MyComponent() {
  const { status, subscribe, config } = useSSEContext()
}

Returns: { status: SSEStatus, subscribe: (eventType, handler) => unsubscribe, config: SSEConfig }

Throws: Error if used outside an SSEProvider.


useSSEEvent

Subscribe to raw SSE events of a specific type. Allows components to react to events imperatively, outside the declarative events config.

function useSSEEvent<T = unknown>(
  eventType: string,
  handler: (payload: T) => void
): void

Parameters:

Parameter Type Description
eventType string The event type to subscribe to
handler (payload: T) => void Callback invoked when a matching event is received

Usage:

import { useSSEEvent } from 'reactive-swr'

function NotificationToast() {
  useSSEEvent<{ title: string; body: string }>('notification', (payload) => {
    showToast(payload.title, payload.body)
  })

  return null
}

Notes:

  • Handler is called for all events of the given type, regardless of config.events
  • Uses the "latest ref" pattern so handler identity changes do not cause resubscription
  • Multiple components can subscribe to the same event type independently
  • Must be used within an SSEProvider

useSSEStatus

Returns the current SSE connection status.

function useSSEStatus(): SSEStatus

Returns: An SSEStatus object.

Usage:

import { useSSEStatus } from 'reactive-swr'

function ConnectionBanner() {
  const { connected, connecting, error, reconnectAttempt } = useSSEStatus()

  if (error) return <div>Error: {error.message}</div>
  if (connecting) return <div>Reconnecting (attempt {reconnectAttempt})...</div>
  if (connected) return <div>Connected</div>
  return <div>Disconnected</div>
}

Throws: Error if used outside an SSEProvider.


useSSEStream

Create an independent SSE connection for a dedicated stream. Does not require SSEProvider.

function useSSEStream<T = unknown>(
  url: string,
  options?: UseSSEStreamOptions<T>
): UseSSEStreamResult<T>

Parameters:

Parameter Type Description
url string The SSE endpoint URL
options UseSSEStreamOptions<T> Optional configuration

Usage:

import { useSSEStream } from 'reactive-swr'

function StockTicker({ symbol }: { symbol: string }) {
  const { data, error } = useSSEStream<{ price: number }>(
    `/api/stocks/${symbol}/stream`,
    { transform: (raw) => raw as { price: number } }
  )

  if (error) return <span>--</span>
  if (!data) return <span>Loading...</span>
  return <span>${data.price.toFixed(2)}</span>
}

POST request example:

const { data } = useSSEStream<Result>('/api/query', {
  method: 'POST',
  body: { query: 'SELECT * FROM users' },
  headers: { Authorization: `Bearer ${token}` },
  transform: (raw) => raw as Result,
})

Custom transport example:

const { data } = useSSEStream<Result>('/api/stream', {
  transport: (url) => myCustomTransport(url),
})

Notes:

  • Connections are shared across components using the same URL and options
  • Connection closes automatically when all subscribers unmount
  • URL or transport option changes close the old connection and open a new one
  • The transform function uses a ref pattern so changing its reference does not cause reconnection
  • When body or headers are provided without an explicit method, a fetch-based transport is used automatically

defineSchema

Define a shared, frozen schema object consumed by both createChannel() (server) and SSEProvider (client). Provides TypeScript inference for event types and payloads.

function defineSchema<T extends SchemaDefinition>(
  definition: T
): SchemaResult<T>

Usage:

import { defineSchema } from 'reactive-swr'

const schema = defineSchema({
  'user.updated': {
    key: '/api/users',
    update: 'set',
  },
  'order.placed': {
    key: (p: { id: string }) => `/api/orders/${p.id}`,
  },
  'stats.changed': {
    key: ['/api/stats/daily', '/api/stats/weekly'],
    update: 'refetch',
  },
})

The returned schema object is frozen with Object.freeze(). Each entry defaults update to 'set' when not specified.

With resources:

const schema = defineSchema({
  resources: {
    orders: {
      created: { key: '/api/orders', update: 'refetch' },
      updated: { key: (p: { id: string }) => `/api/orders/${p.id}`, update: 'set' },
      deleted: { key: '/api/orders', update: 'refetch' },
    },
  },
  // Explicit events alongside resources
  'notification.sent': { key: '/api/notifications' },
})

Each resource key (e.g., orders) expands into orders.created, orders.updated, and orders.deleted events. Per-operation definitions are optional -- omitted operations get a default event with the resource name as the key and 'set' as the update strategy. Explicit event definitions always take precedence over generated resource events.

Using with SSEProvider:

<SSEProvider config={{ url: '/api/events', schema }}>
  <App />
</SSEProvider>

Using with createChannel:

const channel = createChannel(schema)

createSSEParser

Create a streaming SSE wire-protocol parser. Useful for building custom transports that consume raw SSE text.

function createSSEParser(callbacks: SSEParserCallbacks): SSEParser

Callbacks:

interface SSEParserCallbacks {
  onEvent: (event: SSEEvent) => void
  onRetry?: (ms: number) => void
}

Returns:

interface SSEParser {
  feed(chunk: string): void
  reset(): void
}

SSEEvent:

interface SSEEvent {
  data: string
  event: string  // defaults to "message" for unnamed events
  id: string
  retry?: number
}

Usage:

import { createSSEParser } from 'reactive-swr'

const parser = createSSEParser({
  onEvent(event) {
    console.log(event.event, event.data)
  },
  onRetry(ms) {
    console.log('Server requested retry interval:', ms)
  },
})

// Feed raw SSE text chunks as they arrive
parser.feed('event: user.updated\n')
parser.feed('data: {"id":42}\n\n')
// onEvent fires with { event: "user.updated", data: '{"id":42}', id: "" }

parser.reset() // Clear internal state for reuse

Notes:

  • Handles \r\n, \r, and \n line endings
  • Strips BOM at the start of the stream
  • Comment lines (starting with :) are silently ignored
  • Multi-line data fields are joined with \n

Server Entry Point (reactive-swr/server)

createChannel

Create a server-side SSE channel that broadcasts typed events to connected clients. Supports both Web standard (Fetch API / edge runtimes) and Node.js HTTP environments.

import { createChannel } from 'reactive-swr/server'

function createChannel(
  schema: Record<string, any>,
  options?: { heartbeatInterval?: number }
): Channel

Parameters:

Parameter Type Default Description
schema schema object (required) Schema from defineSchema(). Used for TypeScript type inference only; not inspected at runtime.
options.heartbeatInterval number 30000 Milliseconds between heartbeat comments sent to keep connections alive

Channel methods:

Method Description
connect(request: Request): Response Web standard. Returns a streaming Response for the client.
connect(req, res): void Node.js. Writes SSE headers and streams to the ServerResponse.
respond(request: Request): { response, emitter } Web standard. Returns a Response and a scoped ScopedEmitter for request-scoped streaming.
respond(req, res): ScopedEmitter Node.js. Writes SSE headers and returns a scoped ScopedEmitter.
emit(type, payload): void Broadcast an event to all clients connected via connect().
watch(adapter): cleanup Connect an SSEAdapter to the channel. Returns a cleanup function (or Promise<cleanup> if the adapter's start() is async).
close(): void Close all connections, stop all watched adapters, and stop the heartbeat timer.
isClosed(): boolean Returns true if the channel has been closed.

ScopedEmitter:

interface ScopedEmitter {
  emit(type: string, payload: unknown): void
  close(): void
}

A scoped emitter writes only to the single client it was created for (via respond()), unlike channel.emit() which broadcasts to all connect() clients.

Web standard usage (e.g., Next.js Route Handlers, Cloudflare Workers):

import { createChannel } from 'reactive-swr/server'

const channel = createChannel(schema)

// Broadcast endpoint — long-lived connection
export function GET(request: Request) {
  return channel.connect(request)
}

// Request-scoped endpoint — stream results then close
export async function POST(request: Request) {
  const body = await request.json()
  const { response, emitter } = channel.respond(request)

  emitter.emit('result', { rows: await queryDB(body.query) })
  emitter.close()

  return response
}

Node.js usage:

import http from 'node:http'
import { createChannel } from 'reactive-swr/server'

const channel = createChannel(schema)

http.createServer((req, res) => {
  if (req.url === '/events') {
    // Broadcast — long-lived connection
    channel.connect(req, res)
  } else if (req.url === '/query') {
    // Request-scoped
    const emitter = channel.respond(req, res)
    emitter.emit('result', { rows: queryDB() })
    emitter.close()
  }
})

// Broadcast to all connected clients from anywhere
channel.emit('user.updated', { id: 42, name: 'Alice' })

// Graceful shutdown
channel.close()

Notes:

  • Heartbeat comments (: heartbeat\n\n) keep connections alive and are sent to all connect() clients at the configured interval
  • The heartbeat timer starts when the first client connects and stops when the last client disconnects
  • Dead clients (closed streams, ended responses) are automatically pruned during broadcast and heartbeat cycles
  • connect() sends an initial : connected\n\n comment when a client connects
  • Throws Error('Cannot connect: channel is closed') or Error('Cannot respond: channel is closed') if called after close()

Adapters

Database and event source adapters that bridge change notifications to channel.emit(). Each adapter implements the SSEAdapter interface and can be connected to a channel via channel.watch().

All adapters are tree-shakeable and can be imported individually or from the barrel export:

// Individual imports (recommended for tree-shaking)
import { createPrismaAdapter } from 'reactive-swr/server/adapters/prisma'
import { createMongoAdapter } from 'reactive-swr/server/adapters/mongodb'
import { createPgAdapter } from 'reactive-swr/server/adapters/pg'
import { createEmitterAdapter } from 'reactive-swr/server/adapters/emitter'

// Barrel import
import {
  createPrismaAdapter,
  createMongoAdapter,
  createPgAdapter,
  createEmitterAdapter,
  type SSEAdapter,
  type AdapterMapping,
} from 'reactive-swr/server'

SSEAdapter Interface

The standard contract all adapters implement. Exported from reactive-swr/server for third-party adapter authors.

interface SSEAdapter {
  start(emit: (eventType: string, payload: unknown) => void): void | Promise<void>
  stop(): void | Promise<void>
}
Method Description
start(emit) Begin watching for changes. Call emit(eventType, payload) when a change occurs. May be sync or async.
stop() Stop watching and clean up resources. May be sync or async.

Notes:

  • Adapters are stateless with respect to the channel -- the channel provides the emit callback
  • Each adapter is responsible for its own reconnection logic (e.g., MongoDB resume tokens)
  • emit() errors thrown inside the adapter are caught and do not propagate

createPrismaAdapter

Create an adapter that intercepts Prisma create, update, and delete operations via $use() middleware and emits SSE events after each operation completes.

function createPrismaAdapter(
  prisma: PrismaClient,
  mapping: PrismaAdapterMapping
): SSEAdapter

Parameters:

Parameter Type Description
prisma PrismaClient A Prisma client instance with $use() support
mapping PrismaAdapterMapping Maps Prisma model names to event types per operation

PrismaAdapterMapping:

type PrismaAdapterMapping = {
  [modelName: string]: {
    created?: string
    updated?: string
    deleted?: string
  }
}

Usage:

import { createPrismaAdapter } from 'reactive-swr/server/adapters/prisma'

const adapter = createPrismaAdapter(prisma, {
  Order: {
    created: 'orders.created',
    updated: 'orders.updated',
    deleted: 'orders.deleted',
  },
})

const cleanup = channel.watch(adapter)

Supported Prisma actions: create, createMany, update, updateMany, delete, deleteMany.

Notes:

  • Does not import @prisma/client -- accepts the client instance as a parameter
  • Events are emitted after the operation completes (post-middleware), not before
  • The adapter's middleware should be registered last to run after other middleware
  • start() is idempotent -- calling it multiple times has no effect
  • stop() disables emission but does not remove the registered middleware (Prisma $use() does not support removal)

createMongoAdapter

Create an adapter that watches a MongoDB collection via Change Streams and emits SSE events for document changes.

function createMongoAdapter(
  collection: MongoCollection,
  mapping: MongoAdapterMapping
): SSEAdapter

Parameters:

Parameter Type Description
collection MongoCollection A MongoDB collection with watch() support
mapping MongoAdapterMapping Maps Change Stream operation types to event types

MongoAdapterMapping:

type MongoAdapterMapping = {
  [operationType: string]: string  // e.g., 'insert' -> 'orders.created'
}

Usage:

import { createMongoAdapter } from 'reactive-swr/server/adapters/mongodb'

const adapter = createMongoAdapter(db.collection('orders'), {
  insert: 'orders.created',
  update: 'orders.updated',
  replace: 'orders.updated',
  delete: 'orders.deleted',
})

const cleanup = await channel.watch(adapter)

Notes:

  • Does not import mongodb -- accepts the collection instance as a parameter
  • Persists resume tokens so reconnections pick up where they left off
  • Handles invalidate events by reopening the stream (up to 5 reconnection attempts)
  • Payload is fullDocument for insert/update/replace, documentKey for delete
  • start() is async -- it opens the Change Stream and begins iteration

createPgAdapter

Create an adapter that listens on PostgreSQL LISTEN/NOTIFY channels and emits SSE events when NOTIFY messages arrive.

function createPgAdapter(
  client: PgClient,
  mapping: PgAdapterMapping
): SSEAdapter

Parameters:

Parameter Type Description
client PgClient A pg client instance with query() and on() methods
mapping PgAdapterMapping Maps PostgreSQL channel names to event types

PgAdapterMapping:

type PgAdapterMapping = {
  [channelName: string]: string  // e.g., 'order_changes' -> 'orders.updated'
}

Usage:

import { createPgAdapter } from 'reactive-swr/server/adapters/pg'

const adapter = createPgAdapter(pgClient, {
  order_changes: 'orders.updated',
  user_changes: 'users.updated',
})

const cleanup = await channel.watch(adapter)

Notes:

  • Does not import pg -- accepts the client instance as a parameter
  • start() is async -- it issues LISTEN queries for each mapped channel
  • stop() is async -- it issues UNLISTEN queries and removes the notification listener
  • NOTIFY payloads are parsed as JSON; malformed payloads emit undefined
  • SQL identifiers are properly quoted to prevent injection
  • Supports both client.off() and client.removeListener() for cleanup
  • Be aware of the PostgreSQL NOTIFY payload limit of 8000 bytes

createEmitterAdapter

Create an adapter that bridges any object with on(event, listener) and off(event, listener) methods to SSE events.

function createEmitterAdapter(
  emitter: OnOffEmitter,
  mapping: EmitterAdapterMapping
): SSEAdapter

Parameters:

Parameter Type Description
emitter OnOffEmitter Any object with on(event, listener) and off(event, listener) methods
mapping EmitterAdapterMapping Maps emitter event names to schema event types

EmitterAdapterMapping:

type EmitterAdapterMapping = {
  [emitterEvent: string]: string  // e.g., 'order:changed' -> 'orders.updated'
}

Usage:

import { createEmitterAdapter } from 'reactive-swr/server/adapters/emitter'

const adapter = createEmitterAdapter(myEventBus, {
  'order:changed': 'orders.updated',
  'user:changed': 'users.updated',
})

const cleanup = channel.watch(adapter)

Notes:

  • Does not require Node.js EventEmitter -- works with any on/off-compatible interface
  • The first argument of the emitter event is passed as the payload to emit()
  • stop() calls off() for each registered handler

Testing Entry Point (reactive-swr/testing)

mockSSE

Create a mock SSE connection for testing. Intercepts both the global EventSource constructor and fetch so that connections to registered URLs return controllable mocks.

import { mockSSE } from 'reactive-swr/testing'

function mockSSE(url: string): MockSSEControls

Returns:

interface MockSSEControls {
  sendEvent(event: SSEEventData): void
  sendRaw(text: string): void
  sendSSE(data: unknown): void
  close(): void
  getConnection(): MockEventSource | undefined
}

interface SSEEventData {
  type: string
  payload: unknown
}

Methods:

Method Description
sendEvent({ type, payload }) Dispatch a typed event to both the MockEventSource (as a MessageEvent) and any fetch-based streams (as formatted SSE wire text).
sendRaw(text) Send raw text directly to fetch-based streams. Does not affect EventSource listeners.
sendSSE(data) Send data as data: <json>\n\n format to fetch-based streams. Does not affect EventSource listeners.
close() Simulate a connection close. Fires the error handler on the MockEventSource and closes fetch streams.
getConnection() Get the MockEventSource instance for the registered URL, if one has been created.

Static Methods:

Method Description
mockSSE.restore() Restore the original EventSource, fetch, and Request globals. Closes all mock instances and streams. Call this in afterEach.

Usage:

import { mockSSE } from 'reactive-swr/testing'
import { render, screen, waitFor } from '@testing-library/react'

describe('OrderStatus', () => {
  afterEach(() => {
    mockSSE.restore()
  })

  it('updates when order event received', async () => {
    const mock = mockSSE('/api/events')

    render(
      <SWRConfig value={{ provider: () => new Map() }}>
        <SSEProvider config={{ url: '/api/events', schema }}>
          <OrderStatus orderId="123" />
        </SSEProvider>
      </SWRConfig>
    )

    mock.sendEvent({
      type: 'order.updated',
      payload: { id: '123', status: 'shipped' },
    })

    await waitFor(() => {
      expect(screen.getByText('shipped')).toBeInTheDocument()
    })
  })
})

Testing fetch-based transports:

it('works with POST SSE streams', async () => {
  const mock = mockSSE('/api/query')

  render(<StreamingResults url="/api/query" body={{ q: 'test' }} />)

  // sendEvent dispatches to both EventSource and fetch listeners
  mock.sendEvent({ type: 'row', payload: { id: 1 } })

  // sendRaw sends raw SSE wire text to fetch streams only
  mock.sendRaw('event: row\ndata: {"id":2}\n\n')

  // sendSSE sends unnamed data messages to fetch streams only
  mock.sendSSE({ id: 3 })
})

Notes:

  • The first call to mockSSE(url) installs global overrides for EventSource, fetch, and Request
  • Multiple URLs can be registered before restore() is called
  • mockSSE.restore() must be called after each test to prevent cross-test pollution
  • The mock Request constructor handles relative URLs for registered mock URLs by prefixing http://localhost
  • After restore() is called, sendEvent, sendRaw, and sendSSE become no-ops

Types

All types are exported from the main entry point:

import type {
  EventMapping,
  ParsedEvent,
  ReconnectConfig,
  ResourceDefinition,
  ResourceOperationDefinition,
  SchemaDefinition,
  SchemaEventDefinition,
  SchemaResult,
  SSEConfig,
  SSEProviderProps,
  SSERequestOptions,
  SSEStatus,
  SSETransport,
  UpdateStrategy,
  UseSSEStreamOptions,
  UseSSEStreamResult,
} from 'reactive-swr'

Server types:

import type { SSEAdapter, AdapterMapping } from 'reactive-swr/server'

Testing types:

import type { MockSSEControls, SSEEventData } from 'reactive-swr/testing'

SSEConfig

Configuration for the SSE connection and event handling. This is a discriminated union with three variants:

type SSEConfig =
  | SSEConfigWithSchema   // has `schema`, no `events`
  | SSEConfigWithEvents   // has `events`, no `schema`
  | SSEConfigWithNeither  // neither (for manual event handling via useSSEEvent)

Provide either events (manual mapping) or schema (auto-derived from defineSchema output), but not both. Providing both is a TypeScript compile error. At runtime, if both are somehow provided, schema takes precedence and a warning is logged when debug: true.

Shared properties (all variants):

Property Type Required Description
url string Yes The SSE endpoint URL
parseEvent (event: MessageEvent) => ParsedEvent No Custom event parser. Default expects { type, payload } JSON for unnamed events. For named events, data is parsed as JSON for the payload.
reconnect ReconnectConfig No Reconnection settings
debug boolean No Enable console.debug logging for event processing
onConnect () => void No Called when the connection opens
onDisconnect () => void No Called when the connection closes
onError (error: Event) => void No Called on connection error
onEventError (event: ParsedEvent, error: unknown) => void No Called when event processing fails
method string No HTTP method. Triggers fetch-based transport when set.
body BodyInit | Record<string, unknown> No Request body. Triggers fetch-based transport when set.
headers Record<string, string> No Additional request headers. Triggers fetch-based transport when set.
transport (url: string) => SSETransport No Custom transport factory. Takes precedence over method/body/headers.

Schema variant:

interface SSEConfigWithSchema extends SSEConfigBase {
  schema: Record<string, any>  // output of defineSchema()
  events?: never
}

Events variant:

interface SSEConfigWithEvents extends SSEConfigBase {
  events: Record<string, EventMapping>
  schema?: never
}

EventMapping

Defines how to handle a specific event type, including which SWR cache keys to update and how.

interface EventMapping<TPayload = any, TData = any> {
  key: string | string[] | ((payload: TPayload) => string | string[])
  update?: UpdateStrategy<TPayload, TData>
  filter?: (payload: TPayload) => boolean
  transform?: (payload: TPayload) => TPayload
}
Property Type Required Description
key string | string[] | ((payload) => string | string[]) Yes SWR cache key(s) to update. Can be static, an array, or a function of the payload.
update UpdateStrategy No How to update the cache. Defaults to 'set'.
filter (payload) => boolean No Return false to skip processing this event. Applied to the raw payload before transform.
transform (payload) => payload No Transform payload before cache update and key resolution.

UpdateStrategy

Controls how the SWR cache is updated when an event is received.

type UpdateStrategy<TPayload, TData> =
  | 'set'
  | 'refetch'
  | ((current: TData | undefined, payload: TPayload) => TData)
Strategy Description
'set' Replace cache value with the payload directly (no network request)
'refetch' Trigger SWR revalidation (ignores payload, fetches fresh data from the server)
function Custom merge: receives current cache value and payload, returns new value

Example with custom merge:

const config: SSEConfig = {
  url: '/api/events',
  events: {
    'item.added': {
      key: '/api/items',
      update: (current: Item[] | undefined, newItem: Item) =>
        current ? [...current, newItem] : [newItem],
    },
  },
}

ReconnectConfig

Configuration for automatic reconnection with exponential backoff.

interface ReconnectConfig {
  enabled?: boolean
  initialDelay?: number
  maxDelay?: number
  backoffMultiplier?: number
  maxAttempts?: number
}
Property Type Default Description
enabled boolean true Enable automatic reconnection
initialDelay number 1000 Initial delay before first reconnect (ms)
maxDelay number 30000 Maximum delay between attempts (ms)
backoffMultiplier number 2 Exponential backoff multiplier
maxAttempts number Infinity Maximum reconnection attempts

Backoff formula: min(initialDelay * (backoffMultiplier ^ attemptNumber), maxDelay)


ParsedEvent

The parsed representation of an SSE event after the parseEvent function processes it.

interface ParsedEvent {
  type: string
  payload: unknown
}

SSEStatus

Connection status returned by useSSEStatus().

interface SSEStatus {
  connected: boolean       // True when the connection is open
  connecting: boolean      // True during initial connection or reconnection
  error: Error | null      // Last connection error, if any
  reconnectAttempt: number // Current reconnection attempt (0 when connected)
}

SSETransport

Abstraction over the native EventSource API to support custom transports (e.g., fetch-based SSE for POST requests).

interface SSETransport {
  onmessage: ((event: MessageEvent) => void) | null
  onerror: ((event: Event) => void) | null
  onopen: ((event: Event) => void) | null
  close: () => void
  readyState: number
  addEventListener: (type: string, listener: (event: MessageEvent) => void) => void
  removeEventListener: (type: string, listener: (event: MessageEvent) => void) => void
}

Notes:

  • addEventListener and removeEventListener are for named SSE data events only (e.g., "user.updated"), not generic DOM events like "open" or "error"
  • readyState follows the EventSource constants: 0 (CONNECTING), 1 (OPEN), 2 (CLOSED)

SSERequestOptions

Request options for SSE connections that require custom HTTP methods, request bodies, or additional headers.

interface SSERequestOptions {
  method?: string
  body?: BodyInit | Record<string, unknown>
  headers?: Record<string, string>
}

SSEProviderProps

Props for the SSEProvider component.

interface SSEProviderProps {
  config: SSEConfig
  children?: ReactNode
}

SchemaDefinition and SchemaResult

Types for the defineSchema() function.

// Input shape accepted by defineSchema()
// Accepts explicit event definitions and an optional `resources` key
type SchemaDefinition = {
  resources?: Record<string, ResourceDefinition>
} & Record<string, SchemaEventDefinition | Record<string, ResourceDefinition> | undefined>

// A single event definition entry within a schema
interface SchemaEventDefinition<TPayload = any, TData = any> {
  key: string | string[] | ((payload: TPayload) => string | string[])
  update?: UpdateStrategy<TPayload, TData>
  filter?: (payload: TPayload) => boolean
  transform?: (payload: TPayload) => TPayload
}

// Frozen schema object returned by defineSchema()
// Includes both explicit event definitions and resource-expanded events
type SchemaResult<T extends Record<string, any>> = Readonly<
  {
    [K in keyof T as T[K] extends SchemaEventDefinition ? K : never]:
      T[K] extends SchemaEventDefinition
        ? Required<Pick<T[K], 'key'>> &
            Omit<T[K], 'key'> & { update: NonNullable<T[K]['update']> | 'set' }
        : never
  } & (T extends { resources: infer R extends Record<string, ResourceDefinition> }
    ? ResourceSchemaEntries<R>  // Adds .created/.updated/.deleted keys
    : Record<never, never>)
>

ResourceDefinition and ResourceOperationDefinition

Types for the resources field in defineSchema().

// Definition for a resource -- each operation is optional
interface ResourceDefinition {
  created?: ResourceOperationDefinition
  updated?: ResourceOperationDefinition
  deleted?: ResourceOperationDefinition
}

// Definition for a single resource operation
interface ResourceOperationDefinition<TPayload = any, TData = any> {
  key?: string | string[] | ((payload: TPayload) => string | string[])
  update?: UpdateStrategy<TPayload, TData>
  filter?: (payload: TPayload) => boolean
  transform?: (payload: TPayload) => TPayload
}

AdapterMapping

Type-safe mapping from source event names to schema event type keys. Constrains mapped values to keys that exist in the schema (excluding internal keys like resources).

type AdapterMapping<S extends Record<string, any>> = {
  [sourceEvent: string]: EventKeysOf<S>
}

Exported from reactive-swr/server for use by third-party adapter authors who want type-safe mappings against a schema.


SSEEvent and SSEParser

Types for the createSSEParser() function.

interface SSEEvent {
  data: string    // The event data (multi-line data fields joined with \n)
  event: string   // The event type (defaults to "message" for unnamed events)
  id: string      // The last event ID
  retry?: number  // Server-requested retry interval in ms
}

interface SSEParser {
  feed(chunk: string): void  // Feed a raw text chunk to the parser
  reset(): void              // Reset internal state for reuse
}

interface SSEParserCallbacks {
  onEvent: (event: SSEEvent) => void
  onRetry?: (ms: number) => void
}

UseSSEStreamOptions and UseSSEStreamResult

Types for the useSSEStream() hook.

interface UseSSEStreamOptions<T> {
  transform?: (data: unknown) => T
  method?: string
  body?: BodyInit | Record<string, unknown>
  headers?: Record<string, string>
  transport?: (url: string) => SSETransport
}

interface UseSSEStreamResult<T> {
  data: T | undefined       // Latest received data
  error: Error | undefined  // Connection or parse error
}
Option Type Description
transform (data: unknown) => T Transform raw parsed JSON before storing. Uses a ref pattern; changing the function reference does not cause reconnection.
method string HTTP method for the request. When body is provided without method, defaults to POST.
body BodyInit | Record<string, unknown> Request body. Triggers use of fetch-based transport instead of EventSource.
headers Record<string, string> Additional request headers. Triggers use of fetch-based transport instead of EventSource.
transport (url: string) => SSETransport Custom transport factory. Takes precedence over method/body/headers.