rpc4next 0.3.9 → 0.4.9
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/dist/index.js +1 -0
- package/dist/rpc/client/client-utils.d.ts +6 -0
- package/dist/rpc/client/client-utils.js +1 -1
- package/dist/rpc/client/http-method.d.ts +17 -1
- package/dist/rpc/client/http-method.js +1 -1
- package/dist/rpc/client/index.d.ts +1 -1
- package/dist/rpc/client/match.d.ts +12 -1
- package/dist/rpc/client/match.js +1 -1
- package/dist/rpc/client/rpc.js +1 -1
- package/dist/rpc/client/types.d.ts +37 -24
- package/dist/rpc/client/url.d.ts +118 -0
- package/dist/rpc/lib/content-type-types.d.ts +1 -1
- package/dist/rpc/lib/headers.d.ts +36 -0
- package/dist/rpc/lib/headers.js +1 -0
- package/dist/rpc/lib/http-request-headers-types.d.ts +11 -6
- package/dist/rpc/lib/http-response-headers-types.d.ts +28 -5
- package/dist/rpc/lib/search-params.d.ts +35 -0
- package/dist/rpc/lib/types.d.ts +1 -2
- package/dist/rpc/server/handler.d.ts +1 -1
- package/dist/rpc/server/route-context.js +1 -1
- package/dist/rpc/server/route-handler-factory.js +1 -1
- package/dist/rpc/server/route-types.d.ts +1 -1
- package/dist/rpc/server/types.d.ts +14 -9
- package/dist/rpc/server/validators/validator.d.ts +1 -1
- package/dist/rpc/server/validators/validator.js +1 -1
- package/dist/rpc/server/validators/zod/zod-validator.d.ts +2 -2
- package/package.json +44 -36
- package/LICENSE +0 -21
- package/README.md +0 -164
- package/dist/rpc/cli/index.js +0 -7
- package/dist/rpc/lib/constants.d.ts +0 -6
- package/dist/rpc/lib/constants.js +0 -1
- package/dist/rpc/server/server-utils.d.ts +0 -1
- /package/dist/rpc/{server/server-utils.js → lib/search-params.js} +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalizes various `HeadersInit` formats into a plain object
|
|
3
|
+
* with lower-cased header names.
|
|
4
|
+
*
|
|
5
|
+
* This utility converts `Headers`, an array of `[key, value]` tuples,
|
|
6
|
+
* or a plain object into a `Record<string, string>`.
|
|
7
|
+
*
|
|
8
|
+
* All header names are converted to lowercase to ensure
|
|
9
|
+
* case-insensitive consistency when accessing header values.
|
|
10
|
+
*
|
|
11
|
+
* @param headers - A `HeadersInit` value (`Headers`, `[string, string][]`, or `Record<string, string>`).
|
|
12
|
+
* If `undefined`, an empty object is returned.
|
|
13
|
+
*
|
|
14
|
+
* @returns A plain object whose keys are lower-cased header names
|
|
15
|
+
* and whose values are header strings.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const headers = new Headers({
|
|
20
|
+
* "Content-Type": "application/json",
|
|
21
|
+
* "Authorization": "Bearer token",
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* const normalized = normalizeHeaders(headers);
|
|
25
|
+
* // {
|
|
26
|
+
* // "content-type": "application/json",
|
|
27
|
+
* // "authorization": "Bearer token"
|
|
28
|
+
* // }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* - If duplicate header names exist (ignoring case),
|
|
33
|
+
* the last encountered value will overwrite previous ones.
|
|
34
|
+
* - This function does not merge multiple values for the same header.
|
|
35
|
+
*/
|
|
36
|
+
export declare const normalizeHeaders: (headers?: HeadersInit) => Record<string, string>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const n=t=>{const r={};return t?t instanceof Headers?(t.forEach((o,e)=>{r[e.toLowerCase()]=o}),r):Array.isArray(t)?(t.forEach(([o,e])=>{r[o.toLowerCase()]=e}),r):(Object.entries(t).forEach(([o,e])=>{r[o.toLowerCase()]=e}),r):r};export{n as normalizeHeaders};
|
|
@@ -6,6 +6,7 @@ import type { ContentType } from "../server";
|
|
|
6
6
|
export type HttpRequestHeaders = Partial<{
|
|
7
7
|
Accept: string;
|
|
8
8
|
"Accept-Charset": string;
|
|
9
|
+
"Accept-Datetime": string;
|
|
9
10
|
"Accept-Encoding": string;
|
|
10
11
|
"Accept-Language": string;
|
|
11
12
|
Authorization: string;
|
|
@@ -26,24 +27,28 @@ export type HttpRequestHeaders = Partial<{
|
|
|
26
27
|
"If-Unmodified-Since": string;
|
|
27
28
|
"Max-Forwards": string;
|
|
28
29
|
Origin: string;
|
|
29
|
-
Pragma: string;
|
|
30
30
|
Range: string;
|
|
31
31
|
Referer: string;
|
|
32
32
|
TE: string;
|
|
33
33
|
Trailer: string;
|
|
34
34
|
"Transfer-Encoding": string;
|
|
35
35
|
Upgrade: string;
|
|
36
|
+
"Upgrade-Insecure-Requests": string;
|
|
36
37
|
"User-Agent": string;
|
|
37
38
|
Via: string;
|
|
38
|
-
Warning: string;
|
|
39
39
|
"Access-Control-Request-Method": string;
|
|
40
40
|
"Access-Control-Request-Headers": string;
|
|
41
|
-
DNT: string;
|
|
42
41
|
"Sec-Fetch-Dest": string;
|
|
43
42
|
"Sec-Fetch-Mode": string;
|
|
44
43
|
"Sec-Fetch-Site": string;
|
|
45
44
|
"Sec-Fetch-User": string;
|
|
46
|
-
"Sec-
|
|
47
|
-
"
|
|
48
|
-
|
|
45
|
+
"Sec-Purpose": string;
|
|
46
|
+
"Device-Memory": string;
|
|
47
|
+
Priority: string;
|
|
48
|
+
"Origin-Agent-Cluster": string;
|
|
49
|
+
"Service-Worker": string;
|
|
50
|
+
"Service-Worker-Allowed": string;
|
|
51
|
+
"Service-Worker-Navigation-Preload": string;
|
|
52
|
+
"Set-Login": string;
|
|
53
|
+
SourceMap: string;
|
|
49
54
|
}>;
|
|
@@ -10,26 +10,29 @@ export type HttpResponseHeaders<TContentType extends ContentType> = Partial<{
|
|
|
10
10
|
Expires: string;
|
|
11
11
|
ETag: string;
|
|
12
12
|
"Last-Modified": string;
|
|
13
|
+
Age: string;
|
|
13
14
|
"Content-Type": TContentType;
|
|
14
15
|
"Content-Length": string;
|
|
15
16
|
"Content-Encoding": string;
|
|
16
17
|
"Content-Language": string;
|
|
17
18
|
"Content-Location": string;
|
|
18
19
|
"Content-Disposition": string;
|
|
20
|
+
"Content-Range": string;
|
|
21
|
+
"Content-Digest": string;
|
|
22
|
+
"Content-Security-Policy": string;
|
|
23
|
+
"Content-Security-Policy-Report-Only": string;
|
|
19
24
|
"Access-Control-Allow-Origin": string;
|
|
20
25
|
"Access-Control-Allow-Credentials": string;
|
|
21
26
|
"Access-Control-Allow-Headers": string;
|
|
22
27
|
"Access-Control-Allow-Methods": string;
|
|
23
28
|
"Access-Control-Expose-Headers": string;
|
|
29
|
+
"Access-Control-Max-Age": string;
|
|
24
30
|
"WWW-Authenticate": string;
|
|
25
|
-
|
|
31
|
+
"Proxy-Authenticate": string;
|
|
26
32
|
"Strict-Transport-Security": string;
|
|
27
|
-
"Content-Security-Policy": string;
|
|
28
33
|
"X-Content-Type-Options": string;
|
|
29
34
|
"X-Frame-Options": string;
|
|
30
|
-
"X-XSS-Protection": string;
|
|
31
35
|
"Referrer-Policy": string;
|
|
32
|
-
"Permissions-Policy": string;
|
|
33
36
|
"Cross-Origin-Opener-Policy": string;
|
|
34
37
|
"Cross-Origin-Embedder-Policy": string;
|
|
35
38
|
"Cross-Origin-Resource-Policy": string;
|
|
@@ -40,7 +43,27 @@ export type HttpResponseHeaders<TContentType extends ContentType> = Partial<{
|
|
|
40
43
|
"Transfer-Encoding": string;
|
|
41
44
|
Upgrade: string;
|
|
42
45
|
Vary: string;
|
|
46
|
+
Trailer: string;
|
|
47
|
+
"Upgrade-Insecure-Requests": string;
|
|
43
48
|
Date: string;
|
|
44
49
|
Server: string;
|
|
45
|
-
"
|
|
50
|
+
"Accept-CH": string;
|
|
51
|
+
"Accept-Patch": string;
|
|
52
|
+
"Accept-Post": string;
|
|
53
|
+
"Accept-Ranges": string;
|
|
54
|
+
Allow: string;
|
|
55
|
+
"Alt-Svc": string;
|
|
56
|
+
"Alt-Used": string;
|
|
57
|
+
"Clear-Site-Data": string;
|
|
58
|
+
Link: string;
|
|
59
|
+
"Origin-Agent-Cluster": string;
|
|
60
|
+
"Preference-Applied": string;
|
|
61
|
+
Priority: string;
|
|
62
|
+
"Reporting-Endpoints": string;
|
|
63
|
+
"Retry-After": string;
|
|
64
|
+
"Server-Timing": string;
|
|
65
|
+
SourceMap: string;
|
|
66
|
+
"Timing-Allow-Origin": string;
|
|
67
|
+
"Want-Content-Digest": string;
|
|
68
|
+
"Want-Repr-Digest": string;
|
|
46
69
|
}>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a `URLSearchParams` instance into a plain object.
|
|
3
|
+
*
|
|
4
|
+
* This utility aggregates query parameters into an object where:
|
|
5
|
+
* - The first occurrence of a key becomes a `string`
|
|
6
|
+
* - If the same key appears multiple times, the value becomes a `string[]`
|
|
7
|
+
*
|
|
8
|
+
* The returned object is cast to the generic type `T`. This is a **type-level**
|
|
9
|
+
* convenience: the runtime output only contains `string` or `string[]` values
|
|
10
|
+
* (no `undefined` keys are produced).
|
|
11
|
+
*
|
|
12
|
+
* @typeParam T - The desired return shape. Must be a record of `string` keys
|
|
13
|
+
* with values `string | string[] | undefined`. Note: `undefined` is allowed in
|
|
14
|
+
* the type to support downstream optional fields, but is not produced at runtime.
|
|
15
|
+
*
|
|
16
|
+
* @param searchParams - The `URLSearchParams` to convert.
|
|
17
|
+
* @returns A plain object containing the query parameters.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const sp = new URLSearchParams("q=chatgpt&tag=a&tag=b");
|
|
22
|
+
* const obj = searchParamsToObject(sp);
|
|
23
|
+
* // => { q: "chatgpt", tag: ["a", "b"] }
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* type Params = { q?: string; tag?: string[] };
|
|
29
|
+
* const sp = new URLSearchParams("q=hello&tag=a&tag=b");
|
|
30
|
+
* const obj = searchParamsToObject<Params>(sp);
|
|
31
|
+
* // obj.q is typed as string | undefined
|
|
32
|
+
* // obj.tag is typed as string[] | undefined
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare const searchParamsToObject: <T extends Record<string, string | string[] | undefined>>(searchParams: URLSearchParams) => T;
|
package/dist/rpc/lib/types.d.ts
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export type HttpMethod = (typeof HTTP_METHODS)[number];
|
|
1
|
+
export type { HttpMethod } from "rpc4next-shared";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ValidationSchema, RouteResponse, Handler } from "./route-types";
|
|
2
2
|
import type { Params, Query } from "./types";
|
|
3
|
-
import type { HttpMethod } from "
|
|
3
|
+
import type { HttpMethod } from "rpc4next-shared";
|
|
4
4
|
export declare const createHandler: <THttpMethod extends HttpMethod, TParams extends Params, TQuery extends Query, TValidationSchema extends ValidationSchema>() => <TRouteResponse extends RouteResponse>(handler: Handler<THttpMethod, TParams, TQuery, TValidationSchema, TRouteResponse>) => Handler<THttpMethod, TParams, TQuery, TValidationSchema, TRouteResponse>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NextResponse as
|
|
1
|
+
import{NextResponse as n}from"next/server";import{normalizeHeaders as T}from"../lib/headers";import{searchParamsToObject as i}from"../lib/search-params";const p=t=>typeof t=="number"&&!Number.isNaN(t),o=t=>{if(!t)return t;if(p(t))return{status:t};const r=t.headers??T(t.headersInit),{headers:d,headersInit:a,...e}=t;return{...e,headers:r}},S=(t,r)=>{const d={};return{req:Object.assign(t,{query:()=>i(t.nextUrl.searchParams),params:()=>r.params,valid:a=>d[a],addValidatedData:(a,e)=>{d[a]=e}}),body:(a,e)=>new n(a,o(e)),json:(a,e)=>n.json(a,o(e)),text:(a,e)=>{const s=o(e);return new n(a,{...s,headers:{...s?.headers,"Content-Type":"text/plain"}})},redirect:(a,e)=>{const s=p(e)?e:o(e);return n.redirect(a,s)}}};export{S as createRouteContext};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
/*!
|
|
2
2
|
* Inspired by Hono (https://github.com/honojs/hono),
|
|
3
3
|
* particularly its routing design and handler interface.
|
|
4
|
-
*/import{createRouteContext as d}from"./route-context";const i=(o,e)=>async(n,s)=>{const a=d(n,s);try{for(const
|
|
4
|
+
*/import{createRouteContext as d}from"./route-context";const i=(o,e)=>async(n,s)=>{const a=d(n,s);try{for(const r of o){const t=await r(a);if(t instanceof Response)return t}throw new Error("No handler returned a response")}catch(r){return await e(r,a)}},T=o=>()=>{const e=n=>((...s)=>{const r=i(s,o??((t,p)=>{throw t}));return{[n]:r}});return{get:e("GET"),post:e("POST"),put:e("PUT"),delete:e("DELETE"),patch:e("PATCH"),head:e("HEAD"),options:e("OPTIONS")}};export{T as routeHandlerFactory};
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* Hono is licensed under the MIT License.
|
|
8
8
|
*/
|
|
9
9
|
import type { TypedNextResponse, Query, RouteContext, Params } from "./types";
|
|
10
|
-
import type { HttpMethod } from "../lib/types";
|
|
11
10
|
import type { NextRequest } from "next/server";
|
|
11
|
+
import type { HttpMethod } from "rpc4next-shared";
|
|
12
12
|
export type RouteResponse = TypedNextResponse | Promise<TypedNextResponse | void>;
|
|
13
13
|
export type RequiredRouteResponse = TypedNextResponse | Promise<TypedNextResponse>;
|
|
14
14
|
export interface RouteBindings {
|
|
@@ -2,8 +2,8 @@ import type { ValidationSchema } from "./route-types";
|
|
|
2
2
|
import type { ContentType } from "../lib/content-type-types";
|
|
3
3
|
import type { HttpResponseHeaders } from "../lib/http-response-headers-types";
|
|
4
4
|
import type { HttpStatusCode, RedirectionHttpStatusCode, SuccessfulHttpStatusCode } from "../lib/http-status-code-types";
|
|
5
|
-
import type { HttpMethod } from "../lib/types";
|
|
6
5
|
import type { NextResponse, NextRequest } from "next/server";
|
|
6
|
+
import type { HttpMethod } from "rpc4next-shared";
|
|
7
7
|
/**
|
|
8
8
|
* Represents the result of an HTTP response status check.
|
|
9
9
|
*
|
|
@@ -28,10 +28,15 @@ type HttpStatus<T extends HttpStatusCode> = T extends SuccessfulHttpStatusCode ?
|
|
|
28
28
|
* @template TStatus - The HTTP status code.
|
|
29
29
|
* @template TContentType - The content type of the response.
|
|
30
30
|
*/
|
|
31
|
-
export
|
|
32
|
-
headers?: HttpResponseHeaders<TContentType>
|
|
31
|
+
export type TypedResponseInit<TStatus extends HttpStatusCode, TContentType extends ContentType> = ({
|
|
32
|
+
headers?: HttpResponseHeaders<TContentType> & Record<string, string>;
|
|
33
|
+
headersInit?: never;
|
|
34
|
+
} & Omit<ResponseInit, "headers">) | (({
|
|
35
|
+
headers?: never;
|
|
36
|
+
headersInit?: HeadersInit;
|
|
37
|
+
} & Omit<ResponseInit, "headers">) & {
|
|
33
38
|
status?: TStatus;
|
|
34
|
-
}
|
|
39
|
+
});
|
|
35
40
|
/**
|
|
36
41
|
* A strongly typed wrapper around the standard Next.js `NextResponse` object,
|
|
37
42
|
* with additional type information for status code, content type, and response body.
|
|
@@ -118,7 +123,7 @@ export interface RouteContext<TParams = Params, TQuery = Query, TValidationSchem
|
|
|
118
123
|
* @param init - Optional response init.
|
|
119
124
|
* @returns A typed response.
|
|
120
125
|
*/
|
|
121
|
-
body: <TData extends BodyInit | null, TContentType extends ContentType, TStatus extends HttpStatusCode = 200>(data: TData, init?: TypedResponseInit<TStatus, TContentType>) => TypedNextResponse<TData, TStatus, TContentType>;
|
|
126
|
+
body: <TData extends BodyInit | null, TContentType extends ContentType, TStatus extends HttpStatusCode = 200>(data: TData, init?: TStatus | TypedResponseInit<TStatus, TContentType>) => TypedNextResponse<TData, TStatus, TContentType>;
|
|
122
127
|
/**
|
|
123
128
|
* Creates a typed JSON response using `NextResponse.json(...)`.
|
|
124
129
|
*
|
|
@@ -126,7 +131,7 @@ export interface RouteContext<TParams = Params, TQuery = Query, TValidationSchem
|
|
|
126
131
|
* @param init - Optional response init.
|
|
127
132
|
* @returns A JSON response.
|
|
128
133
|
*/
|
|
129
|
-
json: <TData, TStatus extends HttpStatusCode = 200>(data: TData, init?: TypedResponseInit<TStatus, "application/json">) => TypedNextResponse<TData, TStatus, "application/json">;
|
|
134
|
+
json: <TData, TStatus extends HttpStatusCode = 200>(data: TData, init?: TStatus | TypedResponseInit<TStatus, "application/json">) => TypedNextResponse<TData, TStatus, "application/json">;
|
|
130
135
|
/**
|
|
131
136
|
* Creates a plain text response with `Content-Type: text/plain`.
|
|
132
137
|
* Internally uses `new NextResponse(...)` with headers.
|
|
@@ -135,16 +140,16 @@ export interface RouteContext<TParams = Params, TQuery = Query, TValidationSchem
|
|
|
135
140
|
* @param init - Optional response init.
|
|
136
141
|
* @returns A plain text response.
|
|
137
142
|
*/
|
|
138
|
-
text: <TData extends string, TStatus extends HttpStatusCode = 200>(data: TData, init?: TypedResponseInit<TStatus, "text/plain">) => TypedNextResponse<TData, TStatus, "text/plain">;
|
|
143
|
+
text: <TData extends string, TStatus extends HttpStatusCode = 200>(data: TData, init?: TStatus | TypedResponseInit<TStatus, "text/plain">) => TypedNextResponse<TData, TStatus, "text/plain">;
|
|
139
144
|
/**
|
|
140
145
|
* Issues a redirect response.
|
|
141
146
|
* Internally wraps `NextResponse.redirect(...)`.
|
|
142
147
|
*
|
|
143
148
|
* @param url - The URL to redirect to.
|
|
144
|
-
* @param init - Optional redirect status code (default:
|
|
149
|
+
* @param init - Optional redirect status code (default: 307).
|
|
145
150
|
* @returns A redirect response.
|
|
146
151
|
*/
|
|
147
|
-
redirect: <TStatus extends RedirectionHttpStatusCode =
|
|
152
|
+
redirect: <TStatus extends RedirectionHttpStatusCode = 307>(url: string, init?: TStatus | TypedResponseInit<TStatus, "">) => TypedNextResponse<undefined, TStatus, "">;
|
|
148
153
|
}
|
|
149
154
|
declare const __validatedBrand: unique symbol;
|
|
150
155
|
export type ValidatedData = {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Original copyright belongs to Yusuke Wada and the Hono project contributors.
|
|
7
7
|
* Hono is licensed under the MIT License.
|
|
8
8
|
*/
|
|
9
|
-
import type { HttpMethod } from "../../lib/types";
|
|
10
9
|
import type { ValidationSchema } from "../route-types";
|
|
11
10
|
import type { Params, Query, RouteContext, TypedNextResponse, ValidatedData, ValidationTarget } from "../types";
|
|
11
|
+
import type { HttpMethod } from "rpc4next-shared";
|
|
12
12
|
export declare const validator: <THttpMethod extends HttpMethod, TValidationTarget extends ValidationTarget<THttpMethod>, TParams extends Params, TQuery extends Query, TValidationSchema extends ValidationSchema>() => <TTypedNextResponse extends TypedNextResponse>(target: TValidationTarget, validateHandler: (value: object, routeContext: RouteContext<TParams, TQuery, TValidationSchema>) => Promise<ValidatedData | TTypedNextResponse>) => import("../route-types").Handler<THttpMethod, TParams, TQuery, TValidationSchema, Promise<TTypedNextResponse | undefined>>;
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
* This code has been adapted and modified for this project.
|
|
6
6
|
* Original copyright belongs to Yusuke Wada and the Hono project contributors.
|
|
7
7
|
* Hono is licensed under the MIT License.
|
|
8
|
-
*/import{createHandler as
|
|
8
|
+
*/import{createHandler as n}from"../handler";import{getCookiesObject as i,getHeadersObject as s}from"./validator-utils";const T=()=>(e,r)=>n()(async t=>{const o=await(async()=>{if(e==="params")return await t.req.params();if(e==="query")return t.req.query();if(e==="json")return t.req.json();if(e==="headers")return await s();if(e==="cookies")return await i();throw new Error(`Unexpected target: ${e}`)})(),a=await r(o,t);if(a instanceof Response)return a;t.req.addValidatedData(e,a)});export{T as validator};
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
* Original copyright belongs to Yusuke Wada and the Hono project contributors.
|
|
7
7
|
* Hono is licensed under the MIT License.
|
|
8
8
|
*/
|
|
9
|
-
import type { HttpMethod } from "../../../lib/types";
|
|
10
9
|
import type { ValidationSchema } from "../../route-types";
|
|
11
10
|
import type { RouteContext, Params, Query, TypedNextResponse, ConditionalValidationInput, ValidationTarget } from "../../types";
|
|
11
|
+
import type { HttpMethod } from "rpc4next-shared";
|
|
12
12
|
import type { z, ZodSchema } from "zod";
|
|
13
13
|
export declare const zValidator: <THttpMethod extends HttpMethod, TValidationTarget extends ValidationTarget<THttpMethod>, 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 = {
|
|
14
14
|
input: Record<TValidationTarget, TInput>;
|
|
15
15
|
output: Record<TValidationTarget, TOutput>;
|
|
16
|
-
}, THookReturn extends TypedNextResponse | void = TypedNextResponse<z.
|
|
16
|
+
}, THookReturn extends TypedNextResponse | void = TypedNextResponse<z.ZodSafeParseError<TInput>, 400, "application/json"> | void>(target: TValidationTarget, schema: TSchema, hook?: (result: z.ZodSafeParseResult<TOutput>, routeContext: RouteContext<TParams, TQuery, TValidationSchema>) => THookReturn) => import("../../route-types").Handler<THttpMethod, TParams, TQuery, TValidationSchema, Promise<Exclude<THookReturn, void> | undefined>>;
|
package/package.json
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rpc4next",
|
|
3
|
-
"version": "0.3.9",
|
|
4
3
|
"description": "Inspired by Hono RPC and Pathpida, rpc4next brings a lightweight and intuitive RPC solution to Next.js, making server-client communication seamless",
|
|
5
4
|
"author": "watanabe-1",
|
|
6
5
|
"license": "MIT",
|
|
7
6
|
"repository": {
|
|
8
7
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/watanabe-1/rpc4next"
|
|
8
|
+
"url": "git+https://github.com/watanabe-1/rpc4next.git",
|
|
9
|
+
"directory": "packages/rpc4next"
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://github.com/watanabe-1/rpc4next#readme",
|
|
12
12
|
"bugs": {
|
|
13
13
|
"url": "https://github.com/watanabe-1/rpc4next/issues"
|
|
14
14
|
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
15
18
|
"keywords": [
|
|
16
19
|
"next.js",
|
|
17
20
|
"rpc",
|
|
18
|
-
"typescript"
|
|
19
|
-
"cli"
|
|
21
|
+
"typescript"
|
|
20
22
|
],
|
|
21
23
|
"type": "module",
|
|
22
24
|
"module": "dist/index.js",
|
|
@@ -24,10 +26,17 @@
|
|
|
24
26
|
"files": [
|
|
25
27
|
"dist"
|
|
26
28
|
],
|
|
27
|
-
"bin": {
|
|
28
|
-
"rpc4next": "dist/rpc/cli/index.js"
|
|
29
|
-
},
|
|
30
29
|
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"default": "./dist/index.js"
|
|
34
|
+
},
|
|
35
|
+
"default": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"default": "./dist/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
31
40
|
"./client": {
|
|
32
41
|
"import": {
|
|
33
42
|
"types": "./dist/rpc/client/index.d.ts",
|
|
@@ -57,46 +66,45 @@
|
|
|
57
66
|
"types": "./dist/rpc/server/validators/zod/index.d.ts",
|
|
58
67
|
"default": "./dist/rpc/server/validators/zod/index.js"
|
|
59
68
|
}
|
|
69
|
+
},
|
|
70
|
+
"./lib/constants": {
|
|
71
|
+
"import": {
|
|
72
|
+
"types": "./dist/rpc/lib/constants.d.ts",
|
|
73
|
+
"default": "./dist/rpc/lib/constants.js"
|
|
74
|
+
},
|
|
75
|
+
"default": {
|
|
76
|
+
"types": "./dist/rpc/lib/constants.d.ts",
|
|
77
|
+
"default": "./dist/rpc/lib/constants.js"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"./lib/types": {
|
|
81
|
+
"import": {
|
|
82
|
+
"types": "./dist/rpc/lib/types.d.ts",
|
|
83
|
+
"default": "./dist/rpc/lib/types.js"
|
|
84
|
+
},
|
|
85
|
+
"default": {
|
|
86
|
+
"types": "./dist/rpc/lib/types.d.ts",
|
|
87
|
+
"default": "./dist/rpc/lib/types.js"
|
|
88
|
+
}
|
|
60
89
|
}
|
|
61
90
|
},
|
|
62
91
|
"scripts": {
|
|
63
92
|
"build": "bun run clean && bun build.ts && bun run build:types",
|
|
64
|
-
"build:types": "tsc -
|
|
65
|
-
"clean": "bun -e \"require('fs').rmSync('dist',
|
|
93
|
+
"build:types": "tsc -b --force tsconfig.build-types.json",
|
|
94
|
+
"clean": "bun -e \"const fs=require('fs'); fs.rmSync('dist',{recursive:true,force:true}); fs.rmSync('tsconfig.build-types.tsbuildinfo',{force:true}); fs.rmSync('tsconfig.tsbuildinfo',{force:true});\"",
|
|
66
95
|
"test": "vitest run",
|
|
67
96
|
"test:coverage": "vitest run --coverage.enabled true",
|
|
68
97
|
"test:ui": "vitest --ui --coverage.enabled true",
|
|
69
98
|
"test:watch": "vitest --watch",
|
|
70
|
-
"lint": "eslint \"
|
|
71
|
-
"lint:fix": "eslint \"
|
|
72
|
-
"typecheck": "tsc --noEmit"
|
|
99
|
+
"lint": "eslint \"src/**/*.ts\"",
|
|
100
|
+
"lint:fix": "eslint \"src/**/*.ts\" --fix",
|
|
101
|
+
"typecheck": "tsc -b tsconfig.build.json --noEmit"
|
|
73
102
|
},
|
|
74
103
|
"dependencies": {
|
|
75
|
-
"
|
|
76
|
-
"chokidar": "^4.0.3",
|
|
77
|
-
"commander": "^13.1.0"
|
|
78
|
-
},
|
|
79
|
-
"devDependencies": {
|
|
80
|
-
"@types/node": "^22.15.2",
|
|
81
|
-
"@vitest/coverage-v8": "^3.1.2",
|
|
82
|
-
"@vitest/eslint-plugin": "^1.1.43",
|
|
83
|
-
"@vitest/ui": "^3.1.2",
|
|
84
|
-
"esbuild": "^0.25.3",
|
|
85
|
-
"eslint": "^9.25.1",
|
|
86
|
-
"eslint-config-prettier": "^10.1.2",
|
|
87
|
-
"eslint-plugin-import": "^2.31.0",
|
|
88
|
-
"eslint-plugin-unused-imports": "^4.1.4",
|
|
89
|
-
"mock-fs": "^5.5.0",
|
|
90
|
-
"msw": "^2.7.5",
|
|
91
|
-
"next": "15.3.1",
|
|
92
|
-
"prettier": "^3.5.3",
|
|
93
|
-
"ts-node": "^10.9.2",
|
|
94
|
-
"typescript": "^5.8.3",
|
|
95
|
-
"typescript-eslint": "^8.31.0",
|
|
96
|
-
"vitest": "^3.1.2",
|
|
97
|
-
"zod": "^3.24.3"
|
|
104
|
+
"rpc4next-shared": "workspace:*"
|
|
98
105
|
},
|
|
99
106
|
"peerDependencies": {
|
|
100
107
|
"next": "^14.0.0 || ^15.0.0"
|
|
101
|
-
}
|
|
108
|
+
},
|
|
109
|
+
"version": "0.4.9"
|
|
102
110
|
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 watanabe-1
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.md
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# rpc4next
|
|
2
|
-
|
|
3
|
-
Lightweight, type-safe RPC system for Next.js App Router projects.
|
|
4
|
-
|
|
5
|
-
Inspired by Hono RPC and Pathpida, **rpc4next** automatically generates a type-safe client for your existing `route.ts` **and** `page.tsx` files, enabling seamless server-client communication with full type inference.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## ✨ Features
|
|
10
|
-
|
|
11
|
-
- ✅ ルート、パラメータ、クエリパラメータ、 リクエストボディ、レスポンスの型安全なクライアント生成
|
|
12
|
-
- ✅ 既存の `app/**/route.ts` および `app/**/page.tsx` を活用するため、新たなハンドラファイルの作成は不要
|
|
13
|
-
- ✅ 最小限のセットアップで、カスタムサーバー不要
|
|
14
|
-
- ✅ 動的ルート(`[id]`、`[...slug]` など)に対応
|
|
15
|
-
- ✅ CLI による自動クライアント用型定義生成
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## 🚀 Getting Started
|
|
20
|
-
|
|
21
|
-
### 1. Install rpc4next
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npm install rpc4next
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### 2. Define API Routes in Next.js
|
|
28
|
-
|
|
29
|
-
Next.js プロジェクト内の既存の `app/**/route.ts` と `app/**/page.tsx` ファイルをそのまま利用できます。
|
|
30
|
-
さらに、クエリパラメータ(searchParams)の型安全性を有効にするには、対象のファイル内で `Query` または `OptionalQuery` 型を定義し、`export` してください。
|
|
31
|
-
|
|
32
|
-
```ts
|
|
33
|
-
// app/api/user/[id]/route.ts
|
|
34
|
-
import { NextRequest, NextResponse } from "next/server";
|
|
35
|
-
|
|
36
|
-
// searchParams用の型定義
|
|
37
|
-
export type Query = {
|
|
38
|
-
q: string; // 必須
|
|
39
|
-
page?: number; // 任意
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export async function GET(
|
|
43
|
-
req: NextRequest,
|
|
44
|
-
segmentData: { params: Promise<{ id: string }> }
|
|
45
|
-
) {
|
|
46
|
-
const { id } = await segmentData.params;
|
|
47
|
-
const q = req.nextUrl.searchParams.get("q");
|
|
48
|
-
return NextResponse.json({ id, q });
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
🚩 Query or OptionalQuery 型を export することで、searchParams の型も自動的にクライアントに反映されます。
|
|
53
|
-
|
|
54
|
-
- **RPCとしてresponseの戻り値の推論が機能するのは、対象となる `route.ts` の HTTPメソッドハンドラ内で`NextResponse.json()` をしている関数のみになります**
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
### 3. Generate Type Definitions with CLI
|
|
59
|
-
|
|
60
|
-
CLI を利用して、Next.js のルート構造から型安全な RPC クライアントの定義を自動生成します。
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
npx rpc4next <baseDir> <outputPath>
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
- `<baseDir>`: Next.js の Appルータが配置されたベースディレクトリ
|
|
67
|
-
- `<outputPath>`: 生成された型定義ファイルの出力先
|
|
68
|
-
|
|
69
|
-
#### オプション
|
|
70
|
-
|
|
71
|
-
- **ウォッチモード**
|
|
72
|
-
ファイル変更を検知して自動的に再生成する場合は `--watch` or `-w` オプションを付けます。
|
|
73
|
-
|
|
74
|
-
```bash
|
|
75
|
-
npx rpc4next <baseDir> <outputPath> --watch
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
- **パラメータ型ファイルの生成**
|
|
79
|
-
各ルートに対して個別のパラメータ型定義ファイルを生成する場合は、`--params-file` or `-p` オプションにファイル名を指定します。
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
npx rpc4next <baseDir> <outputPath> --generate-params-types <paramsFileName>
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
---
|
|
86
|
-
|
|
87
|
-
### 4. Create Your RPC Client
|
|
88
|
-
|
|
89
|
-
生成された型定義ファイルを基に、RPC クライアントを作成します。
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
// lib/rpcClient.ts
|
|
93
|
-
import { createClient } from "rpc4next/client";
|
|
94
|
-
import type { PathStructure } from "あなたが生成した型定義ファイル";
|
|
95
|
-
|
|
96
|
-
export const rpc = createClient<PathStructure>();
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
### 5. Use It in Your Components
|
|
102
|
-
|
|
103
|
-
コンポーネント内で生成された RPC クライアントを使用します。
|
|
104
|
-
|
|
105
|
-
```tsx
|
|
106
|
-
// app/page.tsx
|
|
107
|
-
import { rpc } from "@/lib/rpcClient";
|
|
108
|
-
|
|
109
|
-
export default async function Page() {
|
|
110
|
-
const res = await rpc.api.user._id("123").$get({
|
|
111
|
-
query: { q: "hello", page: 1 },
|
|
112
|
-
});
|
|
113
|
-
const json = await res.json();
|
|
114
|
-
return <div>{json.q}</div>;
|
|
115
|
-
}
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
- エディタの補完機能により、利用可能なエンドポイントが自動的に表示されます。
|
|
119
|
-
- リクエストの構造(params, query)はサーバーコードから推論され、レスポンスも型安全に扱えます。
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## ✅ さらに型安全にしたい場合 `honolike` + `createRouteHandler` による Next.js の型安全強化
|
|
124
|
-
|
|
125
|
-
さらに `honolike` をベースとした `createRouteHandler()` を組み合わせることで、
|
|
126
|
-
|
|
127
|
-
### 📌 主なメリット
|
|
128
|
-
|
|
129
|
-
1. **レスポンス型安全**
|
|
130
|
-
|
|
131
|
-
- ステータス、Content-Type、Body がすべて型で保証される
|
|
132
|
-
- クライアントは受け取るレスポンス型を完全に推論可能
|
|
133
|
-
|
|
134
|
-
2. **クライアント側補完強化**
|
|
135
|
-
|
|
136
|
-
- `status`, `content-type`, `json()`, `text()` などが適切に補完される
|
|
137
|
-
|
|
138
|
-
3. **サーバー側 params / query も型安全**
|
|
139
|
-
- `routeHandlerFactory()` を使えば、`params`, `query` も型推論可能
|
|
140
|
-
|
|
141
|
-
---
|
|
142
|
-
|
|
143
|
-
### ✅ 基本的な使い方
|
|
144
|
-
|
|
145
|
-
```ts
|
|
146
|
-
const createRouteHandler = routeHandlerFactory((err, rc) =>
|
|
147
|
-
rc.text("error", { status: 400 })
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const { POST } = createRouteHandler().post(async (rc) => rc.text("plain text"));
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
これだけで、POST リクエストの返り値が、responseの内容(json,textなど)、status,contenttypeが型付けされるようになります。
|
|
154
|
-
|
|
155
|
-
## 🚧 Requirements
|
|
156
|
-
|
|
157
|
-
- Next.js 14+ (App Router 使用)
|
|
158
|
-
- Node.js 18+
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## 💼 License
|
|
163
|
-
|
|
164
|
-
MIT
|