workers/app.ts
Rama main · réplica de lectura del repositorio original.
// Copyright (c) 2026 Doforu
// Copyright (c) 2026 Cloudflare, Inc.
// Licensed under the Apache 2.0 license found in the LICENSE-APACHE file or at:
// https://opensource.org/licenses/Apache-2.0
// Modifications Copyright (c) 2026 Doforu, distributed under the AGPL-3.0-only (see LICENSE).
import { Hono } from "hono";
import {
createContext,
createRequestHandler,
RouterContextProvider,
} from "react-router";
import { app as apiApp, receiveEmail } from "./index";
import type { Env } from "./types";
/**
* Context key for the Cloudflare bindings, exposed to React Router loaders
* and middleware via `context.get(cloudflareContext)`.
*
* With `future.v8_middleware` enabled, the request context is a
* `RouterContextProvider` (not a plain object), and `createRequestHandler`
* rejects any other shape — so the bindings now travel as a typed context
* entry instead of a plain `AppLoadContext` object.
*/
export const cloudflareContext = createContext<{
env: Env;
ctx: ExecutionContext;
}>();
declare module "react-router" {
export interface AppLoadContext {
cloudflare: {
env: Env;
ctx: ExecutionContext;
};
}
}
const requestHandler = createRequestHandler(
() => import("virtual:react-router/server-build"),
import.meta.env.MODE,
);
// Main app that wraps the API and adds React Router fallback
const app = new Hono<{ Bindings: Env }>();
// Mount the API routes
app.route("/", apiApp);
// React Router catch-all: serves the SPA for all non-API routes
app.all("*", (c) => {
// With middleware enabled the load context must be a `RouterContextProvider`.
// Route middleware (e.g. i18n) extends this same object for the request.
const context = new RouterContextProvider();
context.set(cloudflareContext, {
env: c.env,
ctx: c.executionCtx as ExecutionContext,
});
return requestHandler(c.req.raw, context);
});
// Export the Hono app as the default export with an email handler
export default {
fetch: app.fetch,
async email(
event: { raw: ReadableStream; rawSize: number },
env: Env,
ctx: ExecutionContext,
) {
try {
await receiveEmail(event, env, ctx);
} catch (e) {
console.error("Failed to process incoming email:", (e as Error).message, (e as Error).stack);
// Re-throw so Cloudflare's email routing can retry delivery or bounce the message.
// Swallowing the error would silently drop the email.
throw e;
}
},
};