Created
March 10, 2025 05:40
-
-
Save nmabhinandan/4a8283cd1076983ffb5e9de66129d019 to your computer and use it in GitHub Desktop.
Datastar SDK implementation for Hono
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ServerSentEventGenerator as AbstractSSEGenerator } from '@starfederation/datastar-sdk/src/abstractServerSentEventGenerator' | |
import type { DatastarEventOptions, EventType, MergeFragmentsOptions } from '@starfederation/datastar-sdk/src/types' | |
import type { Context } from 'hono' | |
import type { JSX } from 'hono/jsx/jsx-runtime' | |
import type { SSEStreamingApi } from 'hono/streaming' | |
import { streamSSE } from 'hono/streaming' | |
import type { Jsonifiable } from 'type-fest' | |
export function isRecord(obj: unknown): obj is Record<string, Jsonifiable> { | |
return typeof obj === 'object' && obj !== null | |
} | |
export class ServerSentEventGenerator extends AbstractSSEGenerator { | |
private c: Context | |
private stream: SSEStreamingApi | undefined | |
private constructor(c: Context) { | |
super() | |
this.c = c | |
} | |
private setStream(stream: SSEStreamingApi) { | |
this.stream = stream | |
} | |
static async readSignals(c: Context): Promise<Record<string, Jsonifiable>> { | |
try { | |
if (c.req.method === 'GET') { | |
const params = c.req.param() | |
if (params['datastar'] !== undefined) { | |
const signals = JSON.parse(params['datastar']) | |
if (isRecord(signals)) { | |
return signals | |
} else { | |
//throw | |
} | |
} else { | |
//throw | |
} | |
} | |
const signals = await c.req.json() | |
if (isRecord(signals)) { | |
return signals | |
} else { | |
//throw | |
} | |
} catch (err: unknown) { | |
if (isRecord(err) && 'message' in err && typeof err.message === 'string') { | |
//throw | |
} | |
} | |
//throw | |
} | |
static async stream( | |
c: Context, | |
onStart: (stream: ServerSentEventGenerator) => Promise<void>, | |
options?: Partial<{ | |
onError?: (e: Error, stream: ServerSentEventGenerator) => Promise<void> | |
}>, | |
): Promise<Response> { | |
const generator = new ServerSentEventGenerator(c) | |
const streamFn = async (stream: SSEStreamingApi) => { | |
generator.setStream(stream) | |
await onStart(generator) | |
await stream.close() | |
} | |
const streamErrFn = async (e: Error, stream: SSEStreamingApi) => { | |
generator.setStream(stream) | |
if (options?.onError !== undefined) { | |
await options.onError(e, generator) | |
} else { | |
//throw | |
} | |
} | |
try { | |
return streamSSE(c, streamFn, streamErrFn) | |
} catch (err: unknown) { | |
if (isRecord(err) && 'message' in err && typeof err.message === 'string') { | |
//throw | |
} | |
} | |
//throw | |
} | |
protected override send( | |
event: EventType, | |
dataLines: string[], | |
options: DatastarEventOptions, | |
): string[] { | |
const eventLines = super.send(event, dataLines, options) | |
if (this.stream !== undefined) { | |
eventLines.forEach((line) => { | |
this.stream?.write(line) | |
}) | |
} else { | |
//throw | |
} | |
return eventLines | |
} | |
public mergeJsx( | |
data: JSX.Element, | |
options?: MergeFragmentsOptions, | |
): ReturnType<typeof this.send> { | |
return this.mergeFragments(data.toString(), options) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment