Complete API documentation for reactiveSWR.
- Main Entry Point (
reactive-swr) - Server Entry Point (
reactive-swr/server) - Testing Entry Point (
reactive-swr/testing) - Types
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
SWRConfigprovider - Only one
SSEProvidershould 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 (whenmethod,body, orheadersare set), and custom (viatransportfactory) - When
schemais provided in config, event mappings are derived automatically
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.
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
): voidParameters:
| 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
Returns the current SSE connection status.
function useSSEStatus(): SSEStatusReturns: 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.
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
transformfunction uses a ref pattern so changing its reference does not cause reconnection - When
bodyorheadersare provided without an explicitmethod, a fetch-based transport is used automatically
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)Create a streaming SSE wire-protocol parser. Useful for building custom transports that consume raw SSE text.
function createSSEParser(callbacks: SSEParserCallbacks): SSEParserCallbacks:
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 reuseNotes:
- Handles
\r\n,\r, and\nline endings - Strips BOM at the start of the stream
- Comment lines (starting with
:) are silently ignored - Multi-line
datafields are joined with\n
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 }
): ChannelParameters:
| 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 allconnect()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\ncomment when a client connects- Throws
Error('Cannot connect: channel is closed')orError('Cannot respond: channel is closed')if called afterclose()
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'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
emitcallback - 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
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
): SSEAdapterParameters:
| 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 effectstop()disables emission but does not remove the registered middleware (Prisma$use()does not support removal)
Create an adapter that watches a MongoDB collection via Change Streams and emits SSE events for document changes.
function createMongoAdapter(
collection: MongoCollection,
mapping: MongoAdapterMapping
): SSEAdapterParameters:
| 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
invalidateevents by reopening the stream (up to 5 reconnection attempts) - Payload is
fullDocumentfor insert/update/replace,documentKeyfor delete start()is async -- it opens the Change Stream and begins iteration
Create an adapter that listens on PostgreSQL LISTEN/NOTIFY channels and emits SSE events when NOTIFY messages arrive.
function createPgAdapter(
client: PgClient,
mapping: PgAdapterMapping
): SSEAdapterParameters:
| 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 issuesLISTENqueries for each mapped channelstop()is async -- it issuesUNLISTENqueries 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()andclient.removeListener()for cleanup - Be aware of the PostgreSQL NOTIFY payload limit of 8000 bytes
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
): SSEAdapterParameters:
| 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 anyon/off-compatible interface - The first argument of the emitter event is passed as the payload to
emit() stop()callsoff()for each registered handler
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): MockSSEControlsReturns:
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 forEventSource,fetch, andRequest - Multiple URLs can be registered before
restore()is called mockSSE.restore()must be called after each test to prevent cross-test pollution- The mock
Requestconstructor handles relative URLs for registered mock URLs by prefixinghttp://localhost - After
restore()is called,sendEvent,sendRaw, andsendSSEbecome no-ops
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'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
}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. |
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],
},
},
}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)
The parsed representation of an SSE event after the parseEvent function processes it.
interface ParsedEvent {
type: string
payload: unknown
}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)
}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:
addEventListenerandremoveEventListenerare for named SSE data events only (e.g.,"user.updated"), not generic DOM events like"open"or"error"readyStatefollows theEventSourceconstants:0(CONNECTING),1(OPEN),2(CLOSED)
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>
}Props for the SSEProvider component.
interface SSEProviderProps {
config: SSEConfig
children?: ReactNode
}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>)
>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
}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.
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
}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. |