Skip to content

Instantly share code, notes, and snippets.

@cowboyd
Created October 2, 2026 17:16
Show Gist options
  • Select an option

  • Save cowboyd/6dc41dc2ac69dcca33f8ee5ed0cba48f to your computer and use it in GitHub Desktop.

Select an option

Save cowboyd/6dc41dc2ac69dcca33f8ee5ed0cba48f to your computer and use it in GitHub Desktop.
one-off revoution
import {
action,
createContext,
ensure,
once,
type Operation,
race,
resource,
suspend,
until,
useScope,
withResolvers,
} from "effection";
import { serveDir, type ServeDirOptions } from "@std/http";
import { toHtml } from "hast-util-to-html";
import { match, type MatchResult, type ParamData } from "path-to-regexp";
import type * as hast from "hast";
export type { JSXChild, JSXElement } from "revolution/jsx-runtime";
import type { JSXElement } from "revolution/jsx-runtime";
export interface Handler<Input, Output> {
(request: Input): Operation<Output>;
}
export interface Middleware<Input, Output> {
(request: Input, next: Handler<Input, Output>): Operation<Output>;
}
export interface RevolutionPlugin {
html?: HTMLMiddleware | HTMLMiddleware[];
http?: HTTPMiddleware | HTTPMiddleware[];
}
export type HASTHtmlNode = hast.Element & {
type: "element";
tagName: "html";
};
export type HTMLMiddleware = Middleware<Request, HASTHtmlNode>;
export type HTTPMiddleware = Middleware<Request, Response>;
export type AppMiddleware = Middleware<Request, JSXElement> | HTTPMiddleware;
export interface RevolutionOptions {
app?: AppMiddleware[];
plugins?: RevolutionPlugin[];
}
export interface ServerInfo {
hostname: string;
port: number;
}
export interface Revolution {
start(options?: { port?: number }): Operation<ServerInfo>;
}
const OptionsContext = createContext<RevolutionOptions>("revolution.options");
const ParamsContext = createContext<MatchResult<ParamData>["params"]>(
"revolution.params",
);
const ResponseContext = createContext<(response: Response) => void>(
"revolution.http-response",
);
export function* useRevolutionOptions(): Operation<RevolutionOptions> {
return yield* OptionsContext.expect();
}
export function* useParams<T extends object>(): Operation<T> {
return (yield* ParamsContext.expect()) as T;
}
export function route<T>(
path: string,
...middlewares: Middleware<Request, T>[]
): Middleware<Request, T> {
let handler = concat(...middlewares);
return function* (request, next) {
let result = match(path)(new URL(request.url).pathname);
if (result) {
// path-to-regexp uses a null-prototype object, which cannot be rendered
// in Effection's diagnostic description for Context.set().
yield* ParamsContext.set({ ...result.params });
return yield* handler(request, next);
} else {
return yield* next(request);
}
};
}
export function* respondNotFound(): Operation<never> {
let respond = yield* ResponseContext.expect();
respond(new Response("Not Found", { status: 404, statusText: "Not Found" }));
yield* suspend();
throw new Error("unreachable");
}
export function serveDirMiddleware(
options?: ServeDirOptions,
): HTTPMiddleware {
return function* (request) {
return yield* until(serveDir(request, options));
};
}
export function createRevolution(options: RevolutionOptions = {}): Revolution {
let handler = createApp(options);
return {
*start({ port } = {}) {
return yield* useServer({ handler, port });
},
};
}
function createApp({
app: middlewares = [],
plugins = [],
}: RevolutionOptions): Handler<Request, Response> {
let html = concat(...plugins.flatMap((plugin) => [plugin.html ?? []].flat()));
let http = concat(...plugins.flatMap((plugin) => [plugin.http ?? []].flat()));
let app = concat(...middlewares.map<HTTPMiddleware>((middleware) => {
return function* (request, next) {
let invoke = middleware as Middleware<Request, Response | JSXElement>;
let result = yield* invoke(request, next);
if (result instanceof Response) {
return result;
} else {
assertIsHTMLNode(result);
let element = yield* html(request, function* () {
return result;
});
return serializeHtml(element);
}
};
}));
let provideOptions: HTTPMiddleware = function* (request, next) {
yield* OptionsContext.set({ app: middlewares, plugins });
return yield* next(request);
};
return dispatch(
concat(provideOptions, httpResponsesMiddleware(), http, app),
respondNotFound,
);
}
function httpResponsesMiddleware(): HTTPMiddleware {
return function* (request, next) {
try {
let response = withResolvers<Response>();
yield* ResponseContext.set(response.resolve);
return yield* race([response.operation, next(request)]);
} catch (error) {
return new Response((error as Error).stack, {
status: 500,
statusText: "Internal Server Error",
});
}
};
}
function useServer(
options: { port?: number; handler: Handler<Request, Response> },
): Operation<ServerInfo> {
return resource(function* (provide) {
let scope = yield* useScope();
let server: Deno.HttpServer | undefined;
yield* ensure(function* () {
if (server) {
yield* until(server.shutdown());
}
});
let handler = (request: Request) =>
scope.run(function* () {
let response = yield* race([
aborted(request),
options.handler(request),
]);
let driver = getDriver(response);
if (driver) {
scope.run(function* () {
if (driver.params) {
yield* ParamsContext.set(driver.params);
}
yield* race([aborted(request), driver.operation()]);
});
}
return response;
});
let info = yield* action<ServerInfo>((resolve) => {
server = Deno.serve({ handler, port: options.port, onListen: resolve });
return () => {};
});
yield* provide(info);
});
}
function* aborted(request: Request): Operation<Response> {
if (!request.signal.aborted) {
yield* once(request.signal, "abort");
}
return new Response("aborted");
}
interface Driver {
operation(): Operation<void>;
params?: MatchResult<ParamData>["params"];
}
const DRIVER = Symbol.for("revolution.driver");
function getDriver(response: Response): Driver | undefined {
return (response as Response & { [DRIVER]?: Driver })[DRIVER];
}
function concat<Input, Output>(
...middlewares: Middleware<Input, Output>[]
): Middleware<Input, Output> {
return middlewares.reduceRight<Middleware<Input, Output>>(
(rest, middleware) => (request, next) =>
middleware(request, (value) => rest(value, next)),
(request, next) => next(request),
);
}
function dispatch<Input, Output>(
middleware: Middleware<Input, Output>,
terminus: Handler<Input, Output>,
): Handler<Input, Output> {
return (request) => middleware(request, terminus);
}
function assertIsHTMLNode(value: JSXElement): asserts value is HASTHtmlNode {
if (
value.type !== "element" || value.tagName.toLocaleLowerCase() !== "html"
) {
throw new Error("expected an <html> element");
}
}
function serializeHtml(node: HASTHtmlNode): Response {
return new Response(toHtml([{ type: "doctype" }, node]), {
headers: { "Content-Type": "text/html; charset=utf-8;" },
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment