Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context onto Request #15

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/universal-handler.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 0 additions & 11 deletions examples/tool/context.d.ts

This file was deleted.

3 changes: 3 additions & 0 deletions examples/tool/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@
"prepack": "pnpm build",
"test:typecheck": "tsc -p tsconfig.json --noEmit"
},
"dependencies": {
"@universal-middleware/core": "^"
},
"devDependencies": {
"@hono/node-server": "^1.12.0",
"@swc/core": "^1.6.13",
Expand Down
5 changes: 3 additions & 2 deletions examples/tool/src/handlers/handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Get, UniversalHandler } from "universal-middleware";
import { getContext } from "@universal-middleware/core";

const handler: Get<[], UniversalHandler> = () => (_request, ctx) => {
return new Response("context: " + JSON.stringify(ctx), {
const handler: Get<[], UniversalHandler> = () => (request) => {
return new Response("context: " + JSON.stringify(getContext(request)), {
status: 200,
});
};
Expand Down
11 changes: 7 additions & 4 deletions examples/tool/src/middlewares/context.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { Get, UniversalMiddleware } from "universal-middleware";
import { setContext } from "@universal-middleware/core";

const contextMiddleware: Get<[string], UniversalMiddleware> =
(value) => (_request, ctx) => {
ctx.something = value;
};
const contextMiddleware: Get<
[string],
UniversalMiddleware<{ something: string }>
> = (value) => (request) => {
setContext(request, "something", value);
};

export default contextMiddleware;
10 changes: 7 additions & 3 deletions examples/tool/src/middlewares/headers.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import type { Get, UniversalMiddleware } from "universal-middleware";
import { getContext } from "@universal-middleware/core";

const headersMiddleware: Get<[], UniversalMiddleware> =
() => (_request, ctx) => {
const headersMiddleware: Get<[], UniversalMiddleware<{ something: string }>> =
() => (request) => {
return (response) => {
response.headers.set("X-Custom-Header", ctx.something ?? "NONE");
response.headers.set(
"X-Custom-Header",
getContext(request).something ?? "NONE",
);

return response;
};
Expand Down
1 change: 1 addition & 0 deletions packages/adapter-express/deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"imports": {
"@universal-middleware/tests": "../tests/dist",
"@universal-middleware/core": "../core/dist",
"mri": "npm:mri",
"zx": "npm:zx",
"wait-port": "npm:wait-port"
Expand Down
59 changes: 33 additions & 26 deletions packages/adapter-express/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import {
type NodeRequestAdapterOptions,
} from "./request.js";
import { sendResponse, wrapResponse } from "./response.js";
import type {
Awaitable,
Get,
UniversalHandler,
UniversalMiddleware,
import {
type Awaitable,
type Get,
getContext as getContextCore,
type UniversalHandler,
type UniversalMiddleware,
type UniversalRequest,
} from "@universal-middleware/core";

export const contextSymbol = Symbol("unContext");
export const requestSymbol = Symbol("unRequest");
export const pendingMiddlewaresSymbol = Symbol("unPendingMiddlewares");
export const wrappedResponseSymbol = Symbol("unWrappedResponse");
Expand Down Expand Up @@ -42,13 +43,13 @@ export interface PossiblyEncryptedSocket extends Socket {
* `IncomingMessage` possibly augmented by Express-specific
* `ip` and `protocol` properties.
*/
export interface DecoratedRequest extends Omit<IncomingMessage, "socket"> {
export interface DecoratedRequest<T extends object>
extends Omit<IncomingMessage, "socket"> {
ip?: string;
protocol?: string;
socket?: PossiblyEncryptedSocket;
rawBody?: Buffer | null;
[contextSymbol]?: Universal.Context;
[requestSymbol]?: Request;
[requestSymbol]?: UniversalRequest<T>;
}

export interface DecoratedServerResponse extends ServerResponse {
Expand All @@ -57,13 +58,13 @@ export interface DecoratedServerResponse extends ServerResponse {
}

/** Connect/Express style request listener/middleware */
export type NodeMiddleware = (
req: DecoratedRequest,
export type NodeMiddleware<T extends object> = (
req: DecoratedRequest<T>,
res: DecoratedServerResponse,
next?: (err?: unknown) => void,
) => void;

export type NodeHandler = NodeMiddleware;
export type NodeHandler<T extends object> = NodeMiddleware<T>;

/** Adapter options */
export interface NodeAdapterHandlerOptions extends NodeRequestAdapterOptions {}
Expand All @@ -74,20 +75,20 @@ export interface NodeAdapterMiddlewareOptions
* Creates a request handler to be passed to http.createServer() or used as a
* middleware in Connect-style frameworks like Express.
*/
export function createHandler<T extends unknown[]>(
handlerFactory: Get<T, UniversalHandler>,
export function createHandler<T extends unknown[], C extends object>(
handlerFactory: Get<T, UniversalHandler<C>>,
options: NodeAdapterHandlerOptions = {},
): Get<T, NodeMiddleware> {
): Get<T, NodeMiddleware<C>> {
const requestAdapter = createRequestAdapter(options);

return (...args) => {
const handler = handlerFactory(...args);

return async (req, res, next) => {
try {
req[contextSymbol] ??= {};
initContext(req);
const request = requestAdapter(req);
const response = await handler(request, req[contextSymbol]);
const response = await handler(request);

await sendResponse(response, res);
} catch (error) {
Expand All @@ -112,20 +113,20 @@ export function createHandler<T extends unknown[]>(
/**
* Creates a middleware to be passed to Connect-style frameworks like Express
*/
export function createMiddleware<T extends unknown[]>(
middlewareFactory: Get<T, UniversalMiddleware>,
export function createMiddleware<T extends unknown[], C extends object>(
middlewareFactory: Get<T, UniversalMiddleware<C>>,
options: NodeAdapterMiddlewareOptions = {},
): Get<T, NodeMiddleware> {
): Get<T, NodeMiddleware<C>> {
const requestAdapter = createRequestAdapter(options);

return (...args) => {
const middleware = middlewareFactory(...args);

return async (req, res, next) => {
try {
req[contextSymbol] ??= {};
initContext(req);
const request = requestAdapter(req);
const response = await middleware(request, req[contextSymbol]);
const response = await middleware(request);

if (!response) {
return next?.();
Expand Down Expand Up @@ -162,8 +163,14 @@ export function createMiddleware<T extends unknown[]>(
};
}

export function getContext(
req: DecoratedRequest,
): Universal.Context | undefined {
return req[contextSymbol];
export function initContext<T extends object>(req: DecoratedRequest<T>): void {
if (req[requestSymbol]) {
getContextCore(req[requestSymbol]);
}
}

export function getContext<T extends object>(
req: DecoratedRequest<T>,
): T | undefined {
return req[requestSymbol] ? getContextCore(req[requestSymbol]) : undefined;
}
8 changes: 5 additions & 3 deletions packages/adapter-express/src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export interface NodeRequestAdapterOptions {
}

/** Create a function that converts a Node HTTP request into a fetch API `Request` object */
export function createRequestAdapter(
export function createRequestAdapter<T extends object>(
options: NodeRequestAdapterOptions = {},
): (req: DecoratedRequest) => Request {
): (req: DecoratedRequest<T>) => Request {
const { origin = env.ORIGIN, trustProxy = env.TRUST_PROXY === "1" } = options;

// eslint-disable-next-line prefer-const
Expand Down Expand Up @@ -100,7 +100,9 @@ export function createRequestAdapter(
};
}

function convertBody(req: DecoratedRequest): BodyInit | null | undefined {
function convertBody<T extends object>(
req: DecoratedRequest<T>,
): BodyInit | null | undefined {
if (req.method === "GET" || req.method === "HEAD") {
return;
}
Expand Down
1 change: 1 addition & 0 deletions packages/adapter-hattip/deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"imports": {
"@universal-middleware/tests": "../tests/dist",
"@universal-middleware/core": "../core/dist",
"mri": "npm:mri",
"zx": "npm:zx",
"wait-port": "npm:wait-port"
Expand Down
34 changes: 10 additions & 24 deletions packages/adapter-hattip/src/common.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
import type { AdapterRequestContext, HattipHandler } from "@hattip/core";
import type { RequestHandler } from "@hattip/compose";
import type {
Get,
UniversalHandler,
UniversalMiddleware,
import {
type Get,
getContext as getContextCore,
type UniversalHandler,
type UniversalMiddleware,
} from "@universal-middleware/core";

export const contextSymbol = Symbol("unContext");

declare module "@hattip/core" {
interface AdapterRequestContext {
[contextSymbol]?: Universal.Context;
}
}

export type { HattipHandler };
export type HattipMiddleware = RequestHandler;

Expand All @@ -27,8 +20,7 @@ export function createHandler<T extends unknown[]>(
const handler = handlerFactory(...args);

return (context) => {
context[contextSymbol] ??= {};
return handler(context.request, context[contextSymbol]);
return handler(context.request);
};
};
}
Expand All @@ -43,11 +35,7 @@ export function createMiddleware<T extends unknown[]>(
const middleware = middlewareFactory(...args);

return async (context) => {
context[contextSymbol] ??= {};
const response = await middleware(
context.request,
context[contextSymbol],
);
const response = await middleware(context.request);

if (typeof response === "function") {
const res = await context.next();
Expand All @@ -59,10 +47,8 @@ export function createMiddleware<T extends unknown[]>(
};
}

type X = typeof createMiddleware;

export function getContext(
export function getContext<T extends object>(
context: AdapterRequestContext,
): Universal.Context | undefined {
return context[contextSymbol];
): T | undefined {
return getContextCore<T>(context.request);
}
1 change: 1 addition & 0 deletions packages/adapter-hono/deno.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"imports": {
"@universal-middleware/tests": "../tests/dist",
"@universal-middleware/core": "../core/dist",
"mri": "npm:mri",
"zx": "npm:zx",
"wait-port": "npm:wait-port"
Expand Down
5 changes: 3 additions & 2 deletions packages/adapter-hono/deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading