Skip to content

Commit

Permalink
feat: support EventMap type argument
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Oct 1, 2024
1 parent 1226612 commit 4c49a14
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 17 deletions.
49 changes: 34 additions & 15 deletions src/browser/sse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@ import {
} from '~/core/handlers/HttpHandler'
import type { Path, PathParams } from '~/core/utils/matching/matchRequestUrl'

export type ServerSentEventResolverExtras<Params extends PathParams> =
HttpRequestResolverExtras<Params> & {
client: ServerSentEventClient
server: ServerSentEventServer
}
export type ServerSentEventResolverExtras<
EventMap extends Record<string, unknown>,
Params extends PathParams,
> = HttpRequestResolverExtras<Params> & {
client: ServerSentEventClient<EventMap>
server: ServerSentEventServer
}

export type ServerSentEventResolver<Params extends PathParams> =
ResponseResolver<ServerSentEventResolverExtras<Params>, any, any>
export type ServerSentEventResolver<
EventMap extends Record<string, unknown>,
Params extends PathParams,
> = ResponseResolver<ServerSentEventResolverExtras<EventMap, Params>, any, any>

export type ServerSentEventRequestHandler = <
EventMap extends Record<string, unknown>,
Params extends PathParams<keyof Params> = PathParams,
RequestPath extends Path = Path,
>(
path: RequestPath,
resolver: ServerSentEventResolver<Params>,
resolver: ServerSentEventResolver<EventMap, Params>,
) => HttpHandler

/**
Expand All @@ -38,8 +43,10 @@ export const sse: ServerSentEventRequestHandler = (path, resolver) => {
return new ServerSentEventHandler(path, resolver)
}

class ServerSentEventHandler extends HttpHandler {
constructor(path: Path, resolver: ServerSentEventResolver<any>) {
class ServerSentEventHandler<
EventMap extends Record<string, unknown>,
> extends HttpHandler {
constructor(path: Path, resolver: ServerSentEventResolver<EventMap, any>) {
invariant(
typeof EventSource !== 'undefined',
'Failed to construct a Server-Sent Event handler for path "%s": your environment does not support the EventSource API',
Expand All @@ -49,7 +56,7 @@ class ServerSentEventHandler extends HttpHandler {
super('GET', path, (info) => {
const stream = new ReadableStream({
start(controller) {
const client = new ServerSentEventClient({
const client = new ServerSentEventClient<EventMap>({
controller,
})
const server = new ServerSentEventServer({
Expand Down Expand Up @@ -92,7 +99,7 @@ class ServerSentEventHandler extends HttpHandler {

const kSend = Symbol('kSend')

class ServerSentEventClient {
class ServerSentEventClient<EventMap extends Record<string, unknown>> {
private encoder: TextEncoder
protected controller: ReadableStreamDefaultController

Expand All @@ -104,7 +111,19 @@ class ServerSentEventClient {
/**
* Sends the given payload to the underlying `EventSource`.
*/
public send(payload: { id?: string; event?: string; data: unknown }): void {
public send<EventType extends keyof EventMap & string>(
payload:
| {
id?: string
event: EventType
data: EventMap[EventType]
}
| {
id?: string
event?: never
data?: EventMap['message']
},
): void {
this[kSend]({
id: payload.id,
event: payload.event,
Expand Down Expand Up @@ -142,9 +161,9 @@ class ServerSentEventClient {
this.controller.error()
}

private [kSend](payload: {
private [kSend]<EventType extends keyof EventMap & string>(payload: {
id?: string
event?: string
event?: EventType
data: string
}): void {
const frames: Array<string> = []
Expand Down
File renamed without changes.
39 changes: 39 additions & 0 deletions test/typings/sse.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { it } from 'vitest'
import { sse } from 'msw/browser'

it('supports custom event map type argument', () => {
sse<{ myevent: string }>('/stream', ({ client }) => {
client.send({
event: 'myevent',
data: 'hello',
})

client.send({
// @ts-expect-error Unknown event type "unknown".
event: 'unknown',
data: 'hello',
})
})
})

it('supports event map type argument for unnamed events', () => {
sse<{ message: number; custom: 'goodbye' }>('/stream', ({ client }) => {
client.send({
data: 123,
})
client.send({
// @ts-expect-error Unexpected data type for "message" event.
data: 'goodbye',
})

client.send({
event: 'custom',
data: 'goodbye',
})
client.send({
event: 'custom',
// @ts-expect-error Unexpected data type for "custom" event.
data: 'invalid',
})
})
})
4 changes: 2 additions & 2 deletions test/typings/vitest.config.mts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as path from 'node:path'
import * as fs from 'fs'
import { defineConfig } from 'vitest/config'
import tsPackageJson from 'typescript/package.json' assert { type: 'json' }
import { invariant } from 'outvariant'
import * as fs from 'fs'
import tsPackageJson from 'typescript/package.json' assert { type: 'json' }

const LIB_DIR = path.resolve(__dirname, '../../lib')

Expand Down

0 comments on commit 4c49a14

Please sign in to comment.