@fedify/h3
Version:
Integrate Fedify with h3
83 lines (79 loc) • 3.02 kB
text/typescript
import type { Federation } from "@fedify/fedify";
import {
type EventHandler,
type EventHandlerRequest,
type EventHandlerResponse,
type H3Error,
type H3Event,
defineEventHandler,
toWebRequest,
} from "h3";
/**
* A factory function that creates the context data that will be passed to the
* `Federation` instance.
* @typeParam TContextData The type of the context data that will be passed to
* the `Federation` instance.
* @param event The event that triggered the handler.
* @param request The request that triggered the handler.
* @returns The context data that will be passed to the `Federation` instance.
* This can be a promise that resolves to the context data.
*/
export type ContextDataFactory<TContextData> = (
event: H3Event<EventHandlerRequest>,
request: Request,
) => Promise<TContextData> | TContextData;
/**
* Integrates a `Federation` instance with an H3 handler.
* @param federation
* @param contextDataFactory
* @returns
*/
export function integrateFederation<TContextData>(
federation: Federation<TContextData>,
contextDataFactory: ContextDataFactory<TContextData>,
): EventHandler<EventHandlerRequest, EventHandlerResponse> {
return defineEventHandler({
async handler(event) {
const request = toWebRequest(event);
const response = await federation.fetch(request, {
contextData: await contextDataFactory(event, request),
});
// If the response is 404 Not Found, then we delegate the handling to
// the next handler in the chain. This is because the handler might
// have an endpoint that Fedify does not have, and we want to give the
// handler a chance to respond to the request:
if (response.status === 404) return;
// See also onBeforeResponse() above:
if (response.status === 406) {
event.context["__fedify_response__"] = response;
return;
}
await event.respondWith(response);
},
});
}
/**
* An error handler that responds with a 406 Not Acceptable if Fedify
* responded with a 406 Not Acceptable and the actual handler responded with
* a 404 Not Found.
* @param error The error that occurred.
* @param event The event that triggered the handler.
*/
export async function onError(
error: H3Error<unknown>,
event: H3Event<EventHandlerResponse>,
): Promise<void> {
// If Fedify responded with a 406 Not Acceptable and later on the actual
// handler responded with a 404 Not Found, then we consider it failed
// to negotiate a response. For example, if Fedify has an endpoint /foo
// that supports only application/activity+json and the handler has no
// endpoint /foo at all, then when a client requests /foo with
// Accept: text/html, it should respond with 406 Not Acceptable:
if (
"__fedify_response__" in event.context &&
event.context["__fedify_response__"].status === 406 &&
error.statusCode === 404
) {
await event.respondWith(event.context["__fedify_response__"]);
}
}