rpc4next 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/cli/cache.d.ts +4 -0
- package/dist/cli/cache.js +24 -0
- package/dist/cli/cli.d.ts +2 -0
- package/dist/cli/cli.js +81 -0
- package/dist/cli/constants.d.ts +12 -0
- package/dist/cli/constants.js +20 -0
- package/dist/cli/debounce.d.ts +1 -0
- package/dist/cli/debounce.js +14 -0
- package/dist/cli/generate-path-structure.d.ts +7 -0
- package/dist/cli/generate-path-structure.js +37 -0
- package/dist/cli/path-utils.d.ts +1 -0
- package/dist/cli/path-utils.js +19 -0
- package/dist/cli/route-scanner.d.ts +21 -0
- package/dist/cli/route-scanner.js +167 -0
- package/dist/cli/scan-utils.d.ts +20 -0
- package/dist/cli/scan-utils.js +52 -0
- package/dist/cli/type-utils.d.ts +6 -0
- package/dist/cli/type-utils.js +26 -0
- package/dist/cli/types.d.ts +2 -0
- package/dist/cli/types.js +2 -0
- package/dist/helper/client/http-method.d.ts +2 -0
- package/dist/helper/client/http-method.js +68 -0
- package/dist/helper/client/index.d.ts +2 -0
- package/dist/helper/client/index.js +6 -0
- package/dist/helper/client/match.d.ts +1 -0
- package/dist/helper/client/match.js +33 -0
- package/dist/helper/client/rpc.d.ts +55 -0
- package/dist/helper/client/rpc.js +100 -0
- package/dist/helper/client/types.d.ts +119 -0
- package/dist/helper/client/types.js +2 -0
- package/dist/helper/client/url.d.ts +8 -0
- package/dist/helper/client/url.js +57 -0
- package/dist/helper/client/utils.d.ts +5 -0
- package/dist/helper/client/utils.js +31 -0
- package/dist/helper/server/create-handler.d.ts +2 -0
- package/dist/helper/server/create-handler.js +10 -0
- package/dist/helper/server/create-route-context.d.ts +5 -0
- package/dist/helper/server/create-route-context.js +27 -0
- package/dist/helper/server/index.d.ts +2 -0
- package/dist/helper/server/index.js +5 -0
- package/dist/helper/server/route-handler-factory.d.ts +53 -0
- package/dist/helper/server/route-handler-factory.js +67 -0
- package/dist/helper/server/search-params-to-object.d.ts +1 -0
- package/dist/helper/server/search-params-to-object.js +18 -0
- package/dist/helper/server/types.d.ts +250 -0
- package/dist/helper/server/types.js +4 -0
- package/dist/helper/server/validators/zod/index.d.ts +1 -0
- package/dist/helper/server/validators/zod/index.js +5 -0
- package/dist/helper/server/validators/zod/zod-validator.d.ts +6 -0
- package/dist/helper/server/validators/zod/zod-validator.js +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/lib/constants.d.ts +5 -0
- package/dist/lib/constants.js +9 -0
- package/dist/lib/types.d.ts +1 -0
- package/dist/lib/types.js +2 -0
- package/package.json +81 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Query, Params, RouteBindings, MethodRouteDefinition, RequiredRouteResponse, ErrorHandler } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* A factory function that creates route handlers for various HTTP methods (GET, POST, etc.).
|
|
4
|
+
*
|
|
5
|
+
* Optionally accepts a global error handler that will be used if any route handler throws.
|
|
6
|
+
*
|
|
7
|
+
* Example usage:
|
|
8
|
+
* ```ts
|
|
9
|
+
* const createRouteHandler = routeHandlerFactory((err, rc) => rc.text("error", {status : 400}));
|
|
10
|
+
* export const { POST } = createRouteHandler<{ params: ..., query: ... }>().post((rc) => rc.json({success: true}));
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* @template TOnErrorResponse Type of the response returned by the error handler (optional)
|
|
14
|
+
* @param onError Optional global error handler. If not provided, errors will be re-thrown.
|
|
15
|
+
* @returns An object with methods (`get`, `post`, `put`, etc.) to define route handlers for each HTTP method
|
|
16
|
+
*/
|
|
17
|
+
export declare const routeHandlerFactory: <TOnErrorResponse extends RequiredRouteResponse = never>(onError?: ErrorHandler<TOnErrorResponse>) => <TBindings extends RouteBindings>() => {
|
|
18
|
+
get: MethodRouteDefinition<"GET", TBindings, TOnErrorResponse, TBindings extends {
|
|
19
|
+
params: infer TValue;
|
|
20
|
+
} ? Awaited<TValue> : Params, TBindings extends {
|
|
21
|
+
query: infer TValue_1;
|
|
22
|
+
} ? TValue_1 : Query>;
|
|
23
|
+
post: MethodRouteDefinition<"POST", TBindings, TOnErrorResponse, TBindings extends {
|
|
24
|
+
params: infer TValue;
|
|
25
|
+
} ? Awaited<TValue> : Params, TBindings extends {
|
|
26
|
+
query: infer TValue_1;
|
|
27
|
+
} ? TValue_1 : Query>;
|
|
28
|
+
put: MethodRouteDefinition<"PUT", TBindings, TOnErrorResponse, TBindings extends {
|
|
29
|
+
params: infer TValue;
|
|
30
|
+
} ? Awaited<TValue> : Params, TBindings extends {
|
|
31
|
+
query: infer TValue_1;
|
|
32
|
+
} ? TValue_1 : Query>;
|
|
33
|
+
delete: MethodRouteDefinition<"DELETE", TBindings, TOnErrorResponse, TBindings extends {
|
|
34
|
+
params: infer TValue;
|
|
35
|
+
} ? Awaited<TValue> : Params, TBindings extends {
|
|
36
|
+
query: infer TValue_1;
|
|
37
|
+
} ? TValue_1 : Query>;
|
|
38
|
+
patch: MethodRouteDefinition<"PATCH", TBindings, TOnErrorResponse, TBindings extends {
|
|
39
|
+
params: infer TValue;
|
|
40
|
+
} ? Awaited<TValue> : Params, TBindings extends {
|
|
41
|
+
query: infer TValue_1;
|
|
42
|
+
} ? TValue_1 : Query>;
|
|
43
|
+
head: MethodRouteDefinition<"HEAD", TBindings, TOnErrorResponse, TBindings extends {
|
|
44
|
+
params: infer TValue;
|
|
45
|
+
} ? Awaited<TValue> : Params, TBindings extends {
|
|
46
|
+
query: infer TValue_1;
|
|
47
|
+
} ? TValue_1 : Query>;
|
|
48
|
+
options: MethodRouteDefinition<"OPTIONS", TBindings, TOnErrorResponse, TBindings extends {
|
|
49
|
+
params: infer TValue;
|
|
50
|
+
} ? Awaited<TValue> : Params, TBindings extends {
|
|
51
|
+
query: infer TValue_1;
|
|
52
|
+
} ? TValue_1 : Query>;
|
|
53
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.routeHandlerFactory = void 0;
|
|
13
|
+
const create_route_context_1 = require("./create-route-context");
|
|
14
|
+
const composeHandlersWithError = (handlers, onError) => {
|
|
15
|
+
return (req, segmentData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
const routeContext = (0, create_route_context_1.createRouteContext)(req, segmentData);
|
|
17
|
+
try {
|
|
18
|
+
for (const handler of handlers) {
|
|
19
|
+
const result = yield handler(routeContext);
|
|
20
|
+
if (result instanceof Response) {
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new Error("No handler returned a response");
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return yield onError(error, routeContext);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A factory function that creates route handlers for various HTTP methods (GET, POST, etc.).
|
|
33
|
+
*
|
|
34
|
+
* Optionally accepts a global error handler that will be used if any route handler throws.
|
|
35
|
+
*
|
|
36
|
+
* Example usage:
|
|
37
|
+
* ```ts
|
|
38
|
+
* const createRouteHandler = routeHandlerFactory((err, rc) => rc.text("error", {status : 400}));
|
|
39
|
+
* export const { POST } = createRouteHandler<{ params: ..., query: ... }>().post((rc) => rc.json({success: true}));
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @template TOnErrorResponse Type of the response returned by the error handler (optional)
|
|
43
|
+
* @param onError Optional global error handler. If not provided, errors will be re-thrown.
|
|
44
|
+
* @returns An object with methods (`get`, `post`, `put`, etc.) to define route handlers for each HTTP method
|
|
45
|
+
*/
|
|
46
|
+
const routeHandlerFactory = (onError) => () => {
|
|
47
|
+
const defineRouteForMethod = (method) => {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
+
return ((...handlers) => {
|
|
50
|
+
const resolvedOnError = onError !== null && onError !== void 0 ? onError : ((error, _) => {
|
|
51
|
+
throw error;
|
|
52
|
+
});
|
|
53
|
+
const routeHandler = composeHandlersWithError(handlers, resolvedOnError);
|
|
54
|
+
return { [method]: routeHandler };
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
get: defineRouteForMethod("GET"),
|
|
59
|
+
post: defineRouteForMethod("POST"),
|
|
60
|
+
put: defineRouteForMethod("PUT"),
|
|
61
|
+
delete: defineRouteForMethod("DELETE"),
|
|
62
|
+
patch: defineRouteForMethod("PATCH"),
|
|
63
|
+
head: defineRouteForMethod("HEAD"),
|
|
64
|
+
options: defineRouteForMethod("OPTIONS"),
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
exports.routeHandlerFactory = routeHandlerFactory;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const searchParamsToObject: <T extends Record<string, string | string[] | undefined>>(searchParams: URLSearchParams) => T;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.searchParamsToObject = void 0;
|
|
4
|
+
const searchParamsToObject = (searchParams) => {
|
|
5
|
+
const params = {};
|
|
6
|
+
searchParams.forEach((value, key) => {
|
|
7
|
+
if (key in params) {
|
|
8
|
+
params[key] = Array.isArray(params[key])
|
|
9
|
+
? [...params[key], value]
|
|
10
|
+
: [params[key], value];
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
params[key] = value;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
return params;
|
|
17
|
+
};
|
|
18
|
+
exports.searchParamsToObject = searchParamsToObject;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { HttpMethod } from "../../lib/types";
|
|
2
|
+
import type { NextResponse, NextRequest } from "next/server";
|
|
3
|
+
type KnownContentType = "application/json" | "text/html" | "text/plain" | "application/javascript" | "text/css" | "image/png" | "image/jpeg" | "image/svg+xml" | "application/pdf" | "application/octet-stream" | "multipart/form-data" | "application/x-www-form-urlencoded";
|
|
4
|
+
/**
|
|
5
|
+
* A content type that can be either one of the predefined `KnownContentType` values,
|
|
6
|
+
* or any other custom string.
|
|
7
|
+
*/
|
|
8
|
+
export type ContentType = KnownContentType | (string & {});
|
|
9
|
+
/**
|
|
10
|
+
* Informational responses (100–199)
|
|
11
|
+
*/
|
|
12
|
+
type InformationalHttpStatusCode = 100 | 101 | 102 | 103;
|
|
13
|
+
/**
|
|
14
|
+
* Successful responses (200–299)
|
|
15
|
+
*/
|
|
16
|
+
type SuccessfulHttpStatusCode = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226;
|
|
17
|
+
/**
|
|
18
|
+
* Redirection messages (300–399)
|
|
19
|
+
*/
|
|
20
|
+
export type RedirectionHttpStatusCode = 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308;
|
|
21
|
+
/**
|
|
22
|
+
* Client error responses (400–499)
|
|
23
|
+
*/
|
|
24
|
+
type ClientErrorHttpStatusCode = 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 421 | 422 | 423 | 424 | 425 | 426 | 428 | 429 | 431 | 451;
|
|
25
|
+
/**
|
|
26
|
+
* Server error responses (500–599)
|
|
27
|
+
*/
|
|
28
|
+
type ServerErrorHttpStatusCode = 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 510 | 511;
|
|
29
|
+
/**
|
|
30
|
+
* Http status code (100~599)
|
|
31
|
+
*/
|
|
32
|
+
export type HttpStatusCode = InformationalHttpStatusCode | SuccessfulHttpStatusCode | RedirectionHttpStatusCode | ClientErrorHttpStatusCode | ServerErrorHttpStatusCode;
|
|
33
|
+
/**
|
|
34
|
+
* Represents the result of an HTTP response status check.
|
|
35
|
+
*
|
|
36
|
+
* If the status code is in the range of successful HTTP status codes (e.g., 200–299),
|
|
37
|
+
* `ok` is `true` and `status` is set to the given successful status code.
|
|
38
|
+
*
|
|
39
|
+
* Otherwise, `ok` is `false` and `status` is set to a non-successful status code
|
|
40
|
+
* (i.e., `T` excluding successful status codes).
|
|
41
|
+
*
|
|
42
|
+
* @template T - An HTTP status code to classify.
|
|
43
|
+
*/
|
|
44
|
+
type HttpStatus<T extends HttpStatusCode> = T extends SuccessfulHttpStatusCode ? {
|
|
45
|
+
ok: true;
|
|
46
|
+
status: T;
|
|
47
|
+
} : {
|
|
48
|
+
ok: false;
|
|
49
|
+
status: Exclude<T, SuccessfulHttpStatusCode>;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Represents HTTP response headers with optional fields, parameterized by the content type.
|
|
53
|
+
* This type includes common headers used for caching, content description, CORS, authentication, security, cookies, redirects, connection, and server information.
|
|
54
|
+
*
|
|
55
|
+
* @template TContentType - The specific content type for the `Content-Type` header.
|
|
56
|
+
*/
|
|
57
|
+
type HttpResponseHeaders<TContentType extends ContentType> = Partial<{
|
|
58
|
+
"Cache-Control": string;
|
|
59
|
+
Expires: string;
|
|
60
|
+
ETag: string;
|
|
61
|
+
"Last-Modified": string;
|
|
62
|
+
"Content-Type": TContentType;
|
|
63
|
+
"Content-Length": string;
|
|
64
|
+
"Content-Encoding": string;
|
|
65
|
+
"Content-Language": string;
|
|
66
|
+
"Content-Location": string;
|
|
67
|
+
"Content-Disposition": string;
|
|
68
|
+
"Access-Control-Allow-Origin": string;
|
|
69
|
+
"Access-Control-Allow-Credentials": string;
|
|
70
|
+
"Access-Control-Allow-Headers": string;
|
|
71
|
+
"Access-Control-Allow-Methods": string;
|
|
72
|
+
"Access-Control-Expose-Headers": string;
|
|
73
|
+
"WWW-Authenticate": string;
|
|
74
|
+
Authorization: string;
|
|
75
|
+
"Strict-Transport-Security": string;
|
|
76
|
+
"Content-Security-Policy": string;
|
|
77
|
+
"X-Content-Type-Options": string;
|
|
78
|
+
"X-Frame-Options": string;
|
|
79
|
+
"X-XSS-Protection": string;
|
|
80
|
+
"Referrer-Policy": string;
|
|
81
|
+
"Permissions-Policy": string;
|
|
82
|
+
"Cross-Origin-Opener-Policy": string;
|
|
83
|
+
"Cross-Origin-Embedder-Policy": string;
|
|
84
|
+
"Cross-Origin-Resource-Policy": string;
|
|
85
|
+
"Set-Cookie": string;
|
|
86
|
+
Location: string;
|
|
87
|
+
Connection: string;
|
|
88
|
+
"Keep-Alive": string;
|
|
89
|
+
"Transfer-Encoding": string;
|
|
90
|
+
Upgrade: string;
|
|
91
|
+
Vary: string;
|
|
92
|
+
Date: string;
|
|
93
|
+
Server: string;
|
|
94
|
+
"X-Powered-By": string;
|
|
95
|
+
}>;
|
|
96
|
+
/**
|
|
97
|
+
* Extension of the standard `ResponseInit` interface with strongly typed status and headers.
|
|
98
|
+
*
|
|
99
|
+
* @template TStatus - The HTTP status code.
|
|
100
|
+
* @template TContentType - The content type of the response.
|
|
101
|
+
*/
|
|
102
|
+
export interface TypedResponseInit<TStatus extends HttpStatusCode, TContentType extends ContentType> extends ResponseInit {
|
|
103
|
+
headers?: HttpResponseHeaders<TContentType> | HeadersInit;
|
|
104
|
+
status?: TStatus;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* A strongly typed wrapper around the standard Next.js `NextResponse` object,
|
|
108
|
+
* with additional type information for status code, content type, and response body.
|
|
109
|
+
*
|
|
110
|
+
* @template TData - The type of the response body (e.g., a JSON object or string).
|
|
111
|
+
* @template TStatus - The HTTP status code type of the response.
|
|
112
|
+
* @template TContentType - The content type of the response (e.g., "application/json" or "text/plain").
|
|
113
|
+
*/
|
|
114
|
+
export interface TypedNextResponse<TData = unknown, TStatus extends HttpStatusCode = HttpStatusCode, TContentType extends ContentType = ContentType> extends NextResponse {
|
|
115
|
+
/**
|
|
116
|
+
* Returns the parsed response body as JSON, if the content type is "application/json".
|
|
117
|
+
* Otherwise, returns a `Promise<never>`.
|
|
118
|
+
*/
|
|
119
|
+
json: TContentType extends "application/json" ? () => Promise<TData> : () => Promise<never>;
|
|
120
|
+
/**
|
|
121
|
+
* Returns the response body as plain text, if the content type is "text/plain".
|
|
122
|
+
* If the expected type `T` is not a string, returns `Promise<never>`.
|
|
123
|
+
* Otherwise, returns the raw string body.
|
|
124
|
+
*/
|
|
125
|
+
text: TContentType extends "text/plain" ? TData extends string ? () => Promise<TData> : () => Promise<never> : () => Promise<string>;
|
|
126
|
+
/**
|
|
127
|
+
* Indicates whether the HTTP status code represents a successful response.
|
|
128
|
+
*/
|
|
129
|
+
readonly ok: HttpStatus<TStatus>["ok"];
|
|
130
|
+
/**
|
|
131
|
+
* The HTTP status code of the response, typed based on the given `TStatus`.
|
|
132
|
+
*/
|
|
133
|
+
readonly status: HttpStatus<TStatus>["status"];
|
|
134
|
+
}
|
|
135
|
+
export type Params = Record<string, string | string[]>;
|
|
136
|
+
export type Query = Record<string, string | string[]>;
|
|
137
|
+
/**
|
|
138
|
+
* A typed wrapper around Next.js request/response utilities for API route handling.
|
|
139
|
+
*
|
|
140
|
+
* ## Features:
|
|
141
|
+
* - Enhanced `req` with helpers to parse query parameters, route params, and perform schema validation.
|
|
142
|
+
* - Typed response helpers (`json`, `text`, `body`) that return custom `TypedNextResponse` objects.
|
|
143
|
+
*
|
|
144
|
+
* @template TParams - Shape of dynamic route parameters.
|
|
145
|
+
* @template TQuery - Shape of parsed URL query parameters.
|
|
146
|
+
* @template TValidationSchema - Validation schema used to validate body/query/params.
|
|
147
|
+
*/
|
|
148
|
+
export interface RouteContext<TParams = Params, TQuery = Query, TValidationSchema extends ValidationSchema = ValidationSchema> {
|
|
149
|
+
/**
|
|
150
|
+
* The original `NextRequest` object, extended with helper methods
|
|
151
|
+
* for parsing parameters, query, and managing validation.
|
|
152
|
+
*/
|
|
153
|
+
req: NextRequest & {
|
|
154
|
+
/**
|
|
155
|
+
* Parses and returns typed query parameters from `req.nextUrl.searchParams`.
|
|
156
|
+
*
|
|
157
|
+
* @returns Query parameters
|
|
158
|
+
*/
|
|
159
|
+
query: () => TQuery;
|
|
160
|
+
/**
|
|
161
|
+
* Resolves and returns dynamic route parameters.
|
|
162
|
+
* Typically sourced from the Next.js segment config.
|
|
163
|
+
*
|
|
164
|
+
* @returns Route parameters
|
|
165
|
+
*/
|
|
166
|
+
params: () => Promise<TParams>;
|
|
167
|
+
/**
|
|
168
|
+
* Retrieves validated data for a specific request part (e.g., `body`, `query`, `params`)
|
|
169
|
+
* that has been previously stored via `addValidatedData`.
|
|
170
|
+
*
|
|
171
|
+
* @param target - The part of the request to validate.
|
|
172
|
+
* @returns The validation result of the target.
|
|
173
|
+
*/
|
|
174
|
+
valid: <TValidationTarget extends ValidationTarget>(target: TValidationTarget) => ValidationOutputFor<TValidationTarget, TValidationSchema>;
|
|
175
|
+
/**
|
|
176
|
+
* Stores validated data for a specific part of the request.
|
|
177
|
+
* This data can be retrieved later using `valid(...)`.
|
|
178
|
+
*
|
|
179
|
+
* @param target - The request part to associate the value with.
|
|
180
|
+
* @param value - The validated data.
|
|
181
|
+
*/
|
|
182
|
+
addValidatedData: (target: ValidationTarget, value: object) => void;
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* Creates a typed response with an optional status.
|
|
186
|
+
* Internally wraps `new NextResponse(...)`.
|
|
187
|
+
*
|
|
188
|
+
* @param data - The response body.
|
|
189
|
+
* @param init - Optional response init.
|
|
190
|
+
* @returns A typed response.
|
|
191
|
+
*/
|
|
192
|
+
body: <TData extends BodyInit | null, TContentType extends ContentType, TStatus extends HttpStatusCode = 200>(data: TData, init?: TypedResponseInit<TStatus, TContentType>) => TypedNextResponse<TData, TStatus, TContentType>;
|
|
193
|
+
/**
|
|
194
|
+
* Creates a typed JSON response using `NextResponse.json(...)`.
|
|
195
|
+
*
|
|
196
|
+
* @param data - The response body as JSON.
|
|
197
|
+
* @param init - Optional response init.
|
|
198
|
+
* @returns A JSON response.
|
|
199
|
+
*/
|
|
200
|
+
json: <TData, TStatus extends HttpStatusCode = 200>(data: TData, init?: TypedResponseInit<TStatus, "application/json">) => TypedNextResponse<TData, TStatus, "application/json">;
|
|
201
|
+
/**
|
|
202
|
+
* Creates a plain text response with `Content-Type: text/plain`.
|
|
203
|
+
* Internally uses `new NextResponse(...)` with headers.
|
|
204
|
+
*
|
|
205
|
+
* @param data - The response body as plain text.
|
|
206
|
+
* @param init - Optional response init.
|
|
207
|
+
* @returns A plain text response.
|
|
208
|
+
*/
|
|
209
|
+
text: <TData extends string, TStatus extends HttpStatusCode = 200>(data: TData, init?: TypedResponseInit<TStatus, "text/plain">) => TypedNextResponse<TData, TStatus, "text/plain">;
|
|
210
|
+
/**
|
|
211
|
+
* Issues a redirect response.
|
|
212
|
+
* Internally wraps `NextResponse.redirect(...)`.
|
|
213
|
+
*
|
|
214
|
+
* @param url - The URL to redirect to.
|
|
215
|
+
* @param init - Optional redirect status code (default: 302).
|
|
216
|
+
* @returns A redirect response.
|
|
217
|
+
*/
|
|
218
|
+
redirect: <TStatus extends RedirectionHttpStatusCode = 302>(url: string, init?: TStatus | TypedResponseInit<TStatus, "">) => TypedNextResponse<undefined, TStatus, "">;
|
|
219
|
+
}
|
|
220
|
+
export type RouteResponse = TypedNextResponse | Promise<TypedNextResponse | void>;
|
|
221
|
+
export type RequiredRouteResponse = TypedNextResponse | Promise<TypedNextResponse>;
|
|
222
|
+
export interface RouteBindings {
|
|
223
|
+
params?: Params | Promise<Params>;
|
|
224
|
+
query?: Query;
|
|
225
|
+
}
|
|
226
|
+
export type ValidationTarget = "params" | "query";
|
|
227
|
+
export interface ValidationSchema {
|
|
228
|
+
input: {};
|
|
229
|
+
output: {};
|
|
230
|
+
}
|
|
231
|
+
type ValidationFor<TDirection extends keyof ValidationSchema, TTarget extends ValidationTarget, TSchema extends ValidationSchema> = TTarget extends keyof TSchema[TDirection] ? TSchema[TDirection][TTarget] : never;
|
|
232
|
+
type ValidationInputFor<TTarget extends ValidationTarget, TSchema extends ValidationSchema> = ValidationFor<"input", TTarget, TSchema>;
|
|
233
|
+
type ValidationOutputFor<TTarget extends ValidationTarget, TSchema extends ValidationSchema> = ValidationFor<"output", TTarget, TSchema>;
|
|
234
|
+
export type ConditionalValidationInput<TTarget extends ValidationTarget, TExpected extends ValidationTarget, TSchema extends ValidationSchema, TFallback> = TTarget extends TExpected ? ValidationInputFor<TTarget, TSchema> : TFallback;
|
|
235
|
+
export type Handler<TParams = Params, TQuery = Query, TValidationSchema extends ValidationSchema = ValidationSchema, TRouteResponse extends RouteResponse = RouteResponse> = (routeContext: RouteContext<TParams, TQuery, TValidationSchema>) => TRouteResponse;
|
|
236
|
+
export type ErrorHandler<TRouteResponse extends RequiredRouteResponse, TParams = Params, TQuery = Query, TValidationSchema extends ValidationSchema = ValidationSchema> = (error: unknown, routeContext: RouteContext<TParams, TQuery, TValidationSchema>) => TRouteResponse;
|
|
237
|
+
type RouteHandler<TParams extends RouteBindings["params"], TRouteResponse extends RouteResponse> = (req: NextRequest, segmentData: {
|
|
238
|
+
params: Promise<TParams>;
|
|
239
|
+
}) => Promise<Exclude<Awaited<TRouteResponse>, void | undefined>>;
|
|
240
|
+
type HttpMethodMapping<THttpMethod extends HttpMethod, TParams extends RouteBindings["params"], TRouteResponse extends RouteResponse> = Record<THttpMethod, RouteHandler<TParams, TRouteResponse>>;
|
|
241
|
+
export interface MethodRouteDefinition<THttpMethod extends HttpMethod, TBindings extends RouteBindings, TOnErrorResponse extends RequiredRouteResponse, TParams extends TBindings["params"] = TBindings extends {
|
|
242
|
+
params: infer TValue;
|
|
243
|
+
} ? Awaited<TValue> : Params, TQuery extends TBindings["query"] = TBindings extends {
|
|
244
|
+
query: infer TValue;
|
|
245
|
+
} ? TValue : Query> {
|
|
246
|
+
<TV1 extends ValidationSchema = ValidationSchema, TR1 extends RequiredRouteResponse = RequiredRouteResponse>(handler: Handler<TParams, TQuery, TV1, TR1>): HttpMethodMapping<THttpMethod, TParams, TR1 | TOnErrorResponse>;
|
|
247
|
+
<TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TR1 extends RouteResponse = RouteResponse, TR2 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<TParams, TQuery, TV1, TR1>, handler2: Handler<TParams, TQuery, TV2, TR2>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TOnErrorResponse>;
|
|
248
|
+
<TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TV3 extends ValidationSchema = TV1 & TV2, TR1 extends RouteResponse = RouteResponse, TR2 extends RouteResponse = RouteResponse, TR3 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<TParams, TQuery, TV1, TR1>, handler2: Handler<TParams, TQuery, TV2, TR2>, handler3: Handler<TParams, TQuery, TV3, TR3>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TR3 | TOnErrorResponse>;
|
|
249
|
+
}
|
|
250
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { zodValidator } from "./zod-validator";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.zodValidator = void 0;
|
|
4
|
+
var zod_validator_1 = require("./zod-validator");
|
|
5
|
+
Object.defineProperty(exports, "zodValidator", { enumerable: true, get: function () { return zod_validator_1.zodValidator; } });
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { RouteContext, Params, Query, TypedNextResponse, ValidationSchema, ConditionalValidationInput, ValidationTarget } from "../../types";
|
|
2
|
+
import type { z, ZodSchema } from "zod";
|
|
3
|
+
export declare const zodValidator: <TValidationTarget extends ValidationTarget, TSchema extends ZodSchema<any>, TParams extends ConditionalValidationInput<TValidationTarget, "params", TValidationSchema, Params> & Params, TQuery extends ConditionalValidationInput<TValidationTarget, "query", TValidationSchema, Query> & Query, TInput = z.input<TSchema>, TOutput = z.output<TSchema>, TValidationSchema extends ValidationSchema = {
|
|
4
|
+
input: Record<TValidationTarget, TInput>;
|
|
5
|
+
output: Record<TValidationTarget, TOutput>;
|
|
6
|
+
}, THookReturn extends TypedNextResponse | void = TypedNextResponse<z.SafeParseError<TInput>, 400, "application/json"> | void>(target: TValidationTarget, schema: TSchema, hook?: (result: z.SafeParseReturnType<TInput, TOutput>, routeContext: RouteContext<TParams, TQuery, TValidationSchema>) => THookReturn) => import("../../types").Handler<TParams, TQuery, TValidationSchema, Promise<Exclude<THookReturn, void> | undefined>>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Inspired by Hono (https://github.com/honojs/middleware/tree/main/packages/zod-validator)
|
|
3
|
+
// Some parts of this code are based on or adapted from the Hono project
|
|
4
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
5
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
6
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
7
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
8
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
9
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
10
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.zodValidator = void 0;
|
|
15
|
+
const create_handler_1 = require("../../create-handler");
|
|
16
|
+
const zodValidator = (target, schema, hook) => {
|
|
17
|
+
const resolvedHook = hook !== null && hook !== void 0 ? hook : ((result, rc) => {
|
|
18
|
+
if (!result.success) {
|
|
19
|
+
return rc.json(result, { status: 400 });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return (0, create_handler_1.createHandler)()((rc) => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
+
const value = yield (() => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
|
+
if (target === "params") {
|
|
25
|
+
return yield rc.req.params();
|
|
26
|
+
}
|
|
27
|
+
if (target === "query") {
|
|
28
|
+
return rc.req.query();
|
|
29
|
+
}
|
|
30
|
+
}))();
|
|
31
|
+
const result = yield schema.safeParseAsync(value);
|
|
32
|
+
const hookResult = resolvedHook(result, rc);
|
|
33
|
+
if (hookResult instanceof Response) {
|
|
34
|
+
// If it's of type Response, it won't be void, so we're excluding void here
|
|
35
|
+
return hookResult;
|
|
36
|
+
}
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
throw new Error("If you provide a custom hook, you must explicitly return a response when validation fails.");
|
|
39
|
+
}
|
|
40
|
+
// If validation succeeds, register it as validatedData
|
|
41
|
+
rc.req.addValidatedData(target, result.data);
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
exports.zodValidator = zodValidator;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./helper/server"), exports);
|
|
18
|
+
__exportStar(require("./helper/client"), exports);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const OPTIONAL_CATCH_ALL_PREFIX = "_____";
|
|
2
|
+
export declare const CATCH_ALL_PREFIX = "___";
|
|
3
|
+
export declare const DYNAMIC_PREFIX = "_";
|
|
4
|
+
export declare const HTTP_METHODS_EXCLUDE_OPTIONS: ("GET" | "HEAD" | "POST" | "PUT" | "DELETE" | "PATCH")[];
|
|
5
|
+
export declare const HTTP_METHOD_FUNC_KEYS: ("$delete" | "$get" | "$head" | "$post" | "$put" | "$patch")[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HTTP_METHOD_FUNC_KEYS = exports.HTTP_METHODS_EXCLUDE_OPTIONS = exports.DYNAMIC_PREFIX = exports.CATCH_ALL_PREFIX = exports.OPTIONAL_CATCH_ALL_PREFIX = void 0;
|
|
4
|
+
const http_1 = require("next/dist/server/web/http");
|
|
5
|
+
exports.OPTIONAL_CATCH_ALL_PREFIX = "_____";
|
|
6
|
+
exports.CATCH_ALL_PREFIX = "___";
|
|
7
|
+
exports.DYNAMIC_PREFIX = "_";
|
|
8
|
+
exports.HTTP_METHODS_EXCLUDE_OPTIONS = http_1.HTTP_METHODS.filter((method) => method !== "OPTIONS");
|
|
9
|
+
exports.HTTP_METHOD_FUNC_KEYS = exports.HTTP_METHODS_EXCLUDE_OPTIONS.map((method) => `$${method.toLowerCase()}`);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { HTTP_METHOD as HttpMethod } from "next/dist/server/web/http";
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "rpc4next",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Inspired by Hono RPC and Pathpida, rpc4next brings a lightweight and intuitive RPC solution to Next.js, making server-client communication seamless",
|
|
5
|
+
"author": "watanabe-1",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/watanabe-1/rpc4next"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/watanabe-1/rpc4next#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/watanabe-1/rpc4next/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"next.js",
|
|
17
|
+
"rpc",
|
|
18
|
+
"typescript",
|
|
19
|
+
"cli"
|
|
20
|
+
],
|
|
21
|
+
"module": "dist/index.js",
|
|
22
|
+
"types": "dist/index.d.ts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"bin": {
|
|
27
|
+
"rpc4next": "dist/cli/cli.js"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
"./client": {
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/helper/client/index.d.ts",
|
|
33
|
+
"default": "./dist/helper/client/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"./server": {
|
|
37
|
+
"import": {
|
|
38
|
+
"types": "./dist/helper/server/index.d.ts",
|
|
39
|
+
"default": "./dist/helper/server/index.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"./server/validators/zod": {
|
|
43
|
+
"import": {
|
|
44
|
+
"types": "./dist/helper/server/validators/zod/index.d.ts",
|
|
45
|
+
"default": "./dist/helper/server/validators/zod/index.js"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "npm run clean && tsc -p tsconfig.build.json",
|
|
51
|
+
"clean": "node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest --watch",
|
|
54
|
+
"lint": "eslint \"**/*.ts\"",
|
|
55
|
+
"lint:fix": "eslint \"**/*.ts\" --fix"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"chokidar": "^4.0.3",
|
|
59
|
+
"commander": "^13.1.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "^22.13.14",
|
|
63
|
+
"@vitest/eslint-plugin": "^1.1.38",
|
|
64
|
+
"eslint": "^9.23.0",
|
|
65
|
+
"eslint-config-prettier": "^10.1.1",
|
|
66
|
+
"eslint-plugin-import": "^2.31.0",
|
|
67
|
+
"eslint-plugin-unused-imports": "^4.1.4",
|
|
68
|
+
"mock-fs": "^5.5.0",
|
|
69
|
+
"msw": "^2.7.3",
|
|
70
|
+
"next": "15.2.4",
|
|
71
|
+
"prettier": "^3.5.3",
|
|
72
|
+
"ts-node": "^10.9.2",
|
|
73
|
+
"typescript": "^5.8.2",
|
|
74
|
+
"typescript-eslint": "^8.28.0",
|
|
75
|
+
"vitest": "^3.0.9",
|
|
76
|
+
"zod": "^3.24.2"
|
|
77
|
+
},
|
|
78
|
+
"peerDependencies": {
|
|
79
|
+
"next": "^14.0.0 || ^15.0.0"
|
|
80
|
+
}
|
|
81
|
+
}
|