Skip to content

Commit

Permalink
feat: merge return type with context and preserve body/query
Browse files Browse the repository at this point in the history
  • Loading branch information
danielroe committed Aug 12, 2023
1 parent 5ec877c commit 6f2ea16
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 14 deletions.
9 changes: 6 additions & 3 deletions src/event/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ export interface NodeEventContext {
export class H3Event<
// eslint-disable-next-line @typescript-eslint/no-unused-vars
_RequestT extends EventHandlerRequest = EventHandlerRequest,
Context = unknown,
> implements Pick<FetchEvent, "respondWith">
{
"__is_event__" = true;

// Context
node: NodeEventContext;
context: H3EventContext = {};
context = {} as unknown extends Context
? H3EventContext
: H3EventContext & Context;

// Request
_request: Request | undefined;
Expand Down Expand Up @@ -56,7 +59,7 @@ export class H3Event<

get url() {
if (!this._url) {
this._url = getRequestURL(this);
this._url = getRequestURL(this as H3Event<EventHandlerRequest, unknown>);
}
return this._url;
}
Expand Down Expand Up @@ -132,7 +135,7 @@ export class H3Event<

respondWith(response: Response | PromiseLike<Response>): Promise<void> {
return Promise.resolve(response).then((_response) =>
sendWebResponse(this, _response),
sendWebResponse(this as H3Event<EventHandlerRequest, unknown>, _response),

Check warning on line 138 in src/event/event.ts

View check run for this annotation

Codecov / codecov/patch

src/event/event.ts#L138

Added line #L138 was not covered by tests
);
}

Expand Down
24 changes: 21 additions & 3 deletions src/event/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { ParsedBodySymbol, ParsedQuerySymbol } from "../utils/symbols";
import type {
EventHandler,
LazyEventHandler,
Expand Down Expand Up @@ -63,12 +64,15 @@ export function defineEventHandler<
}
// Object Syntax
const _handler: EventHandler<Request, any> = (event) => {
return _callHandler(event, handler);
return _callHandler(event, handler as EventHandlerObject);
};
return Object.assign(_handler, { __is_handler__: true });
}

async function _callHandler(event: H3Event, handler: EventHandlerObject) {
async function _callHandler(
event: H3Event<any, any>,
handler: EventHandlerObject,
) {
if (handler.before) {
for (const hook of handler.before) {
await hook(event);
Expand All @@ -78,7 +82,21 @@ async function _callHandler(event: H3Event, handler: EventHandlerObject) {
}
}
if (handler.validate) {
await validateEvent(event, handler.validate);
const res = (await validateEvent(event, handler.validate)) as Record<
string,
any
>;
if (res && typeof res === "object") {
for (const property in res) {
if (property === "body") {
(event.node.req as any)[ParsedBodySymbol] = res.body;

Check warning on line 92 in src/event/utils.ts

View check run for this annotation

Codecov / codecov/patch

src/event/utils.ts#L92

Added line #L92 was not covered by tests
} else if ("query" in res) {
(event.node.req as any)[ParsedQuerySymbol] = res.query;

Check warning on line 94 in src/event/utils.ts

View check run for this annotation

Codecov / codecov/patch

src/event/utils.ts#L94

Added line #L94 was not covered by tests
} else {
event.context[property] = res[property];
}
}
}
}
const body = await handler.handler(event);
const response = { body };
Expand Down
22 changes: 19 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,13 @@ export interface EventHandlerRequest {

export type InferEventInput<
Key extends keyof EventHandlerRequest,
Event extends H3Event,
Event extends H3Event<any, any>,
T,
> = void extends T ? (Event extends H3Event<infer E> ? E[Key] : never) : T;
> = unknown extends T
? Event extends H3Event<infer E, any>
? E[Key]
: never
: T;

export interface EventHandler<
Request extends EventHandlerRequest = EventHandlerRequest,
Expand All @@ -83,6 +87,18 @@ export type EventValidatedRequest<
? Awaited<ReturnType<ValidateFunction>>
: EventHandlerRequest;

export type Simplify<TType> = TType extends any[] | Date
? TType
: { [K in keyof TType]: TType[K] };

export type EventFromValidatedRequest<Request extends EventHandlerRequest> =
keyof Request extends "body" | "query"
? H3Event<Request>
: H3Event<
Simplify<Pick<Request, keyof Request & ("body" | "query")>>,
Simplify<Omit<Request, "body" | "query">>
>;

export type EventHandlerObject<
Request extends EventHandlerRequest = EventHandlerRequest,
Response extends EventHandlerResponse = EventHandlerResponse,
Expand All @@ -92,7 +108,7 @@ export type EventHandlerObject<
EventValidatedRequest<_ValidateFunction> = EventValidatedRequest<_ValidateFunction>,
> = {
validate?: _ValidateFunction;
handler: EventHandler<_ValidatedRequest, Response>;
handler: (event: EventFromValidatedRequest<_ValidatedRequest>) => Response;
before?: ((event: H3Event<EventHandlerRequest>) => void | Promise<void>)[];
after?: ((
event: H3Event<_ValidatedRequest>,
Expand Down
5 changes: 2 additions & 3 deletions src/utils/body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import { createError } from "../error";
import { parse as parseMultipartData } from "./internal/multipart";
import { assertMethod, getRequestHeader } from "./request";
import { ValidateFunction, validateData } from "./internal/validate";
import { ParsedBodySymbol, RawBodySymbol } from "./symbols";

export type { MultiPartData } from "./internal/multipart";

const RawBodySymbol = Symbol.for("h3RawBody");
const ParsedBodySymbol = Symbol.for("h3ParsedBody");
type InternalRequest<T = any> = IncomingMessage & {
[RawBodySymbol]?: Promise<Buffer | undefined>;
[ParsedBodySymbol]?: T;
Expand Down Expand Up @@ -94,7 +93,7 @@ export function readRawBody<E extends Encoding = "utf8">(

export async function readBody<
T,
Event extends H3Event = H3Event,
Event extends H3Event<any, any> = H3Event<any, any>,
_T = InferEventInput<"body", Event, T>,
>(event: Event, options: { strict?: boolean } = {}): Promise<_T> {
const request = event.node.req as InternalRequest<T>;
Expand Down
4 changes: 4 additions & 0 deletions src/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ import { createError } from "../error";
import type { HTTPMethod, InferEventInput, RequestHeaders } from "../types";
import type { H3Event } from "../event";
import { validateData, ValidateFunction } from "./internal/validate";
import { ParsedQuerySymbol } from "./symbols";

export function getQuery<
T,
Event extends H3Event = H3Event,
_T = Exclude<InferEventInput<"query", Event, T>, undefined>,
>(event: Event): _T {
if (ParsedQuerySymbol in event) {
return event[ParsedQuerySymbol] as _T;
}

Check warning on line 15 in src/utils/request.ts

View check run for this annotation

Codecov / codecov/patch

src/utils/request.ts#L14-L15

Added lines #L14 - L15 were not covered by tests
return _getQuery(event.path || "") as _T;
}

Expand Down
3 changes: 3 additions & 0 deletions src/utils/symbols.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const RawBodySymbol = Symbol.for("h3RawBody");
export const ParsedBodySymbol = Symbol.for("h3ParsedBody");
export const ParsedQuerySymbol = Symbol.for("h3ParsedQuery");
9 changes: 7 additions & 2 deletions test/types.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,20 @@ describe("types", () => {
async validate(event) {
await Promise.resolve();
expectTypeOf(event).toEqualTypeOf<H3Event>();
return {} as { body: { id: string } };
return {} as { body: { id: string }; other: true };
},
async handler(event) {
expectTypeOf(event).toEqualTypeOf<
H3Event<{ body: { id: string } }>
H3Event<{ body: { id: string } }, { other: true }>
>();

expectTypeOf(event.context.other).toEqualTypeOf<true>();
// TODO: should be unknown in future version of h3
expectTypeOf(event.context.body).toBeAny();

const body = await readBody(event);
expectTypeOf(body).toEqualTypeOf<{ id: string }>();
expectTypeOf(await readBody<string>(event)).toEqualTypeOf<string>();

return { foo: "bar" };
},
Expand Down

0 comments on commit 6f2ea16

Please sign in to comment.