@@ -28,6 +28,8 @@ import {
2828} from "./render.ts" ;
2929import { renderToString } from "preact-render-to-string" ;
3030
31+ const ENCODER = new TextEncoder ( ) ;
32+
3133export interface Island {
3234 file : string ;
3335 name : string ;
@@ -258,11 +260,7 @@ export class Context<State> {
258260 appVNode = appChild ?? h ( Fragment , null ) ;
259261 }
260262
261- const headers = init . headers !== undefined
262- ? init . headers instanceof Headers
263- ? init . headers
264- : new Headers ( init . headers )
265- : new Headers ( ) ;
263+ const headers = getHeadersFromInit ( init ) ;
266264
267265 headers . set ( "Content-Type" , "text/html; charset=utf-8" ) ;
268266 const responseInit : ResponseInit = {
@@ -376,4 +374,102 @@ export class Context<State> {
376374 } ) ;
377375 return new Response ( html , responseInit ) ;
378376 }
377+
378+ /**
379+ * Respond with text. Sets `Content-Type: text/plain`.
380+ * ```tsx
381+ * app.use(ctx => ctx.text("Hello World!"));
382+ * ```
383+ */
384+ text ( content : string , init ?: ResponseInit ) : Response {
385+ return new Response ( content , init ) ;
386+ }
387+
388+ /**
389+ * Respond with html string. Sets `Content-Type: text/html`.
390+ * ```tsx
391+ * app.get("/", ctx => ctx.html("<h1>foo</h1>"));
392+ * ```
393+ */
394+ html ( content : string , init ?: ResponseInit ) : Response {
395+ const headers = getHeadersFromInit ( init ) ;
396+ headers . set ( "Content-Type" , "text/html; charset=utf-8" ) ;
397+
398+ return new Response ( content , { ...init , headers } ) ;
399+ }
400+
401+ /**
402+ * Respond with json string, same as `Response.json()`. Sets
403+ * `Content-Type: application/json`.
404+ * ```tsx
405+ * app.get("/", ctx => ctx.json({ foo: 123 }));
406+ * ```
407+ */
408+ // deno-lint-ignore no-explicit-any
409+ json ( content : any , init ?: ResponseInit ) : Response {
410+ return Response . json ( content , init ) ;
411+ }
412+
413+ /**
414+ * Helper to stream a sync or async iterable and encode text
415+ * automatically.
416+ *
417+ * ```tsx
418+ * function* gen() {
419+ * yield "foo";
420+ * yield "bar";
421+ * }
422+ *
423+ * app.use(ctx => ctx.stream(gen()))
424+ * ```
425+ *
426+ * Or pass in the function directly:
427+ *
428+ * ```tsx
429+ * app.use(ctx => {
430+ * return ctx.stream(function* gen() {
431+ * yield "foo";
432+ * yield "bar";
433+ * });
434+ * );
435+ * ```
436+ */
437+ stream < U extends string | Uint8Array > (
438+ stream :
439+ | Iterable < U >
440+ | AsyncIterable < U >
441+ | ( ( ) => Iterable < U > | AsyncIterable < U > ) ,
442+ init ?: ResponseInit ,
443+ ) : Response {
444+ const raw = typeof stream === "function" ? stream ( ) : stream ;
445+
446+ const body = ReadableStream . from ( raw )
447+ . pipeThrough (
448+ new TransformStream ( {
449+ transform ( chunk , controller ) {
450+ if ( chunk instanceof Uint8Array ) {
451+ // deno-lint-ignore no-explicit-any
452+ controller . enqueue ( chunk as any ) ;
453+ } else if ( chunk === undefined ) {
454+ controller . enqueue ( undefined ) ;
455+ } else {
456+ const raw = ENCODER . encode ( String ( chunk ) ) ;
457+ controller . enqueue ( raw ) ;
458+ }
459+ } ,
460+ } ) ,
461+ ) ;
462+
463+ return new Response ( body , init ) ;
464+ }
465+ }
466+
467+ function getHeadersFromInit ( init ?: ResponseInit ) {
468+ if ( init === undefined ) {
469+ return new Headers ( ) ;
470+ }
471+
472+ return init . headers !== undefined
473+ ? init . headers instanceof Headers ? init . headers : new Headers ( init . headers )
474+ : new Headers ( ) ;
379475}
0 commit comments