rpc4next 0.3.6 → 0.3.8

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.
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import{Command as Wt}from"commander";import $t from"path";var R=["page.tsx","route.ts"],w=0,$=1,Y=1,M=20,W="\u2192";import U from"path";var et=(t,r)=>{let e=P(U.relative(U.dirname(t),r)).replace(/\.tsx?$/,"");return e.startsWith("../")||(e="./"+e),e},P=t=>t.replace(/\\/g,"/"),C=t=>U.relative(process.cwd(),t);import _t from"fs";import Yt from"path";var rt=["Query","OptionalQuery"],L=" ",g=`
3
- `,S=";",H=";",j="Endpoint",X="QueryKey",K="OptionalQueryKey",Q="ParamsKey",ot=[j,K,Q,X],nt="rpc4next/client";import ht from"fs";import b from"path";import G from"path";var T=new Map,N=new Map,st=(t,r)=>{let e=G.resolve(r);[...t.keys()].forEach(o=>{let n=G.resolve(o);(n===e||e.startsWith(n+G.sep))&&t.delete(o)})},it=t=>{st(T,t)},at=t=>{st(N,t)};import vt from"fs";import bt from"crypto";var pt=(t,r)=>{let e=bt.createHash("md5").update(`${t}::${r}`).digest("hex").slice(0,16);return`${r}_${e}`};var I=(t,r)=>!t||!r?"":`Record<${t}, ${r}>`,O=t=>t.length===0||t.some(({name:r,type:e})=>!r||!e)?"":`{ ${t.map(({name:r,type:e})=>`"${r}": ${e}`).join(`${H} `)}${t.length>1?H:""} }`,D=(t,r,e)=>!t||!r?"":e?`import type { ${t} as ${e} } from "${r}"${S}`:`import type { ${t} } from "${r}"${S}`;var ct=(t,r,e,o)=>{let n=vt.readFileSync(r,"utf8"),i=e(n);if(!i)return;let s=et(t,r),p=pt(s,i);return{importName:p,importPath:s,importStatement:D(i,s,p),type:o(i,p)}},mt=(t,r)=>ct(t,r,e=>rt.find(o=>new RegExp(`export (interface ${o} ?{|type ${o} ?=)`).test(e)),(e,o)=>e==="Query"?I(X,o):I(K,o)),lt=(t,r,e)=>ct(t,r,o=>[e].find(n=>new RegExp(`export (async )?(function ${n} ?\\(|const ${n} ?=|\\{[^}]*\\b${n}\\b[^}]*\\} ?=|const \\{[^}]*\\b${n}\\b[^}]*\\} ?=|\\{[^}]*\\b${n}\\b[^}]*\\} from)`).test(o)),(o,n)=>O([{name:`$${o.toLowerCase()}`,type:`typeof ${n}`}]));import{HTTP_METHODS as Ft}from"next/dist/server/web/http";var ut="_____",ft="___",gt="_",k=Ft.filter(t=>t!=="OPTIONS"),ne=k.map(t=>`$${t.toLowerCase()}`);var Et=new Set(R),Tt=t=>{if(T.has(t))return T.get(t);let r=ht.readdirSync(t,{withFileTypes:!0});for(let e of r){let{name:o}=e,n=b.join(t,o);if(o==="node_modules"||o.startsWith("_")||o.startsWith("(.)")||o.startsWith("(..)")||o.startsWith("(...)"))return T.set(t,!1),!1;if(e.isFile()&&Et.has(o))return T.set(t,!0),!0;if(e.isDirectory()&&Tt(n))return T.set(t,!0),!0}return T.set(t,!1),!1},wt=(t,{isDynamic:r,isCatchAll:e,isOptionalCatchAll:o})=>{let n=t;return r&&(n=n.replace(/^\[+|\]+$/g,"")),(e||o)&&(n=n.replace(/^\.{3}/,"")),{paramName:n,keyName:`${o?ut:e?ft:r?gt:""}${n}`}},V=(t,r,e="",o=[])=>{if(N.has(r))return N.get(r);let n=e,i=e+L,s=[],p=[],a=[],m=[],c=[...o],x=ht.readdirSync(r,{withFileTypes:!0}).filter(y=>{if(y.isDirectory()){let _=b.join(r,y.name);return Tt(_)}return Et.has(y.name)}).sort();for(let y of x){let _=P(b.join(r,y.name));if(y.isDirectory()){let l=y.name,h=l.startsWith("(")&&l.endsWith(")"),u=l.startsWith("@"),f=l.startsWith("[[...")&&l.endsWith("]]"),E=l.startsWith("[...")&&l.endsWith("]"),d=l.startsWith("[")&&l.endsWith("]"),{paramName:At,keyName:Rt}=wt(l,{isDynamic:d,isCatchAll:E,isOptionalCatchAll:f}),Lt=d||E||f?[...c,{paramName:At,routeType:{isDynamic:d,isCatchAll:E,isOptionalCatchAll:f,isGroup:h,isParallel:u}}]:c,J=h||u,{pathStructure:Z,imports:Ot,paramsTypes:Dt}=V(t,_,J?n:i,Lt);if(p.push(...Ot),m.push(...Dt),J){let tt=Z.match(/^\s*\{([\s\S]*)\}\s*$/);tt&&s.push(`${i}${tt[1].trim()}`)}else s.push(`${i}"${Rt}": ${Z}`)}else{let l=mt(t,_);if(l){let{importStatement:h,importPath:u,type:f}=l;p.push({statement:h,path:u}),a.push(f)}if(k.forEach(h=>{let u=lt(t,_,h);if(u){let{importStatement:f,importPath:E,type:d}=u;p.push({statement:f,path:E}),a.push(d)}}),a.push(j),c.length>0){let h=c.map(({paramName:f,routeType:E})=>{let d=E.isCatchAll?"string[]":E.isOptionalCatchAll?"string[] | undefined":"string";return{name:f,type:d}}),u=O(h);m.push({paramsType:u,dirPath:b.dirname(_)}),a.push(I(Q,u))}}}let A=a.join(" & "),F=s.length>0?`{${g}${s.join(`,${g}`)}${g}${n}}`:"",z={pathStructure:A&&F?`${A} & ${F}`:A||F,imports:p,paramsTypes:m};return N.set(r,z),z};var yt=(t,r)=>{let{pathStructure:e,imports:o,paramsTypes:n}=V(t,r),i=`export type PathStructure = ${e}${S}`,s=o.length?`${o.sort((c,x)=>c.path.localeCompare(x.path,void 0,{numeric:!0})).map(c=>c.statement).join(g)}`:"",p=ot.filter(c=>e.includes(c)),a=D(p.join(" ,"),nt),m=n.map(({paramsType:c,dirPath:x})=>({paramsType:`export type Params = ${c}${S}`,dirPath:x}));return{pathStructure:`${a}${g}${s}${g}${g}${i}`,paramsTypes:m}};import v from"chalk";var q=(t,r,e="\u2192",o=24)=>t.padEnd(o)+` ${e} ${r}`,B=(t=0)=>L.repeat(t),dt=()=>({info:(t,r={})=>{let{indentLevel:e=0,event:o}=r,n=o?`${v.cyan(`[${o}]`)} `:"";console.log(`${B(e)}${n}${t}`)},success:(t,r={})=>{let{indentLevel:e=0}=r;console.log(`${B(e)}${v.green("\u2713")} ${t}`)},error:(t,r={})=>{let{indentLevel:e=0}=r;console.error(`${B(e)}${v.red("\u2717")} ${v.red(t)}`)}});var Pt=({baseDir:t,outputPath:r,paramsFileName:e,logger:o})=>{o.info("Generating types...",{event:"generate"});let{pathStructure:n,paramsTypes:i}=yt(r,t);_t.writeFileSync(r,n),o.success(q("Path structure type",C(r),W,M),{indentLevel:Y}),e&&(i.forEach(({paramsType:s,dirPath:p})=>{let a=Yt.join(p,e);_t.writeFileSync(a,s)}),o.success(q("Params types",e,W,M),{indentLevel:Y}))};import Mt from"chokidar";var St=(t,r)=>{let e=null,o=!1,n=null,i=async(...s)=>{o=!0;try{await t(...s)}finally{if(o=!1,n){let p=n;n=null,i(...p)}}};return(...s)=>{e&&clearTimeout(e),e=setTimeout(()=>{if(o){n=s;return}i(...s)},r)}};var xt=(t,r,e)=>{e.info(`${C(t)}`,{event:"watch"});let o=a=>R.some(m=>a.endsWith(m)),n=new Set,i=St(()=>{n.forEach(a=>{it(a),at(a)}),n.clear(),r()},300),s=Mt.watch(t,{ignoreInitial:!0,ignored:(a,m)=>!!m?.isFile()&&!o(a)});s.on("ready",()=>{i(),s.on("all",(a,m)=>{if(o(m)){let c=C(m);e.info(c,{event:a}),n.add(m),i()}})}),s.on("error",a=>{a instanceof Error?e.error(`Watcher error: ${a.message}`):e.error(`Unknown watcher error: ${String(a)}`)});let p=()=>{s.close().then(()=>{e.info("Watcher closed.",{event:"watch"})}).catch(a=>{e.error(`Failed to close watcher: ${a.message}`)})};process.on("SIGINT",p),process.on("SIGTERM",p)};var Ct=(t,r,e,o)=>{try{return Pt({baseDir:t,outputPath:r,paramsFileName:e,logger:o}),w}catch(n){return n instanceof Error?o.error(`Failed to generate: ${n.message}`):o.error(`Unknown error occurred during generate: ${String(n)}`),$}},Nt=(t,r,e,o)=>{let n=P($t.resolve(t)),i=P($t.resolve(r)),s=typeof e.paramsFile=="string"?e.paramsFile:null;return e.paramsFile!==void 0&&!s?(o.error("Error: --params-file requires a filename."),$):e.watch?(xt(n,()=>{Ct(n,i,s,o)},o),w):Ct(n,i,s,o)};var It=(t,r=dt())=>{let e=new Wt;e.description("Generate RPC client type definitions based on the Next.js path structure.").argument("<baseDir>","Base directory containing Next.js paths for type generation").argument("<outputPath>","Output path for the generated type definitions").option("-w, --watch","Watch mode: regenerate on file changes").option("-p, --params-file [filename]","Generate params types file with specified filename").action(async(o,n,i)=>{try{let s=await Nt(o,n,i,r);i.watch||process.exit(s)}catch(s){r.error(`Unexpected error occurred:${s instanceof Error?s.message:String(s)}`),process.exit($)}}),e.parse(t)};It(process.argv);
2
+ import{Command as Wt}from"commander";import $t from"path";var R=["page.tsx","route.ts"],w=0,$=1,Y=1,M=20,W="\u2192";import U from"path";var et=(t,r)=>{let e=_(U.relative(U.dirname(t),r)).replace(/\.tsx?$/,"");return e.startsWith("../")||(e="./"+e),e},_=t=>t.replace(/\\/g,"/"),C=t=>U.relative(process.cwd(),t);import Pt from"fs";import Yt from"path";var rt=["Query","OptionalQuery"],O=" ",E=`
3
+ `,S=";",H=";",j="Endpoint",X="QueryKey",K="OptionalQueryKey",Q="ParamsKey",ot=[j,K,Q,X],nt="rpc4next/client";import Tt from"fs";import b from"path";import G from"path";var h=new Map,N=new Map,st=(t,r)=>{let e=G.resolve(r);[...t.keys()].forEach(o=>{let n=G.resolve(o);(n===e||e.startsWith(n+G.sep))&&t.delete(o)})},it=t=>{st(h,t)},at=t=>{st(N,t)};import vt from"fs";import bt from"crypto";var pt=(t,r)=>{let e=bt.createHash("md5").update(`${t}::${r}`).digest("hex").slice(0,16);return`${r}_${e}`};var I=(t,r)=>!t||!r?"":`Record<${t}, ${r}>`,L=t=>t.length===0||t.some(({name:r,type:e})=>!r||!e)?"":`{ ${t.map(({name:r,type:e})=>`"${r}": ${e}`).join(`${H} `)}${t.length>1?H:""} }`,D=(t,r,e)=>!t||!r?"":e?`import type { ${t} as ${e} } from "${r}"${S}`:`import type { ${t} } from "${r}"${S}`;var ct=(t,r,e,o)=>{let n=vt.readFileSync(r,"utf8"),i=e(n);if(!i)return;let s=et(t,r),p=pt(s,i);return{importName:p,importPath:s,importStatement:D(i,s,p),type:o(i,p)}},mt=(t,r)=>ct(t,r,e=>rt.find(o=>new RegExp(`export (interface ${o} ?{|type ${o} ?=)`).test(e)),(e,o)=>e==="Query"?I(X,o):I(K,o)),lt=(t,r,e)=>ct(t,r,o=>[e].find(n=>new RegExp(`export (async )?(function ${n} ?\\(|const ${n} ?=|\\{[^}]*\\b${n}\\b[^}]*\\} ?=|const \\{[^}]*\\b${n}\\b[^}]*\\} ?=|\\{[^}]*\\b${n}\\b[^}]*\\} from)`).test(o)),(o,n)=>L([{name:`$${o.toLowerCase()}`,type:`typeof ${n}`}]));var Ft=["GET","HEAD","OPTIONS","POST","PUT","DELETE","PATCH"],ut="_____",ft="___",Et="_",k=Ft.filter(t=>t!=="OPTIONS"),oe=k.map(t=>`$${t.toLowerCase()}`);var gt=new Set(R),ht=t=>{if(h.has(t))return h.get(t);let r=Tt.readdirSync(t,{withFileTypes:!0});for(let e of r){let{name:o}=e,n=b.join(t,o);if(o==="node_modules"||o.startsWith("_")||o.startsWith("(.)")||o.startsWith("(..)")||o.startsWith("(...)"))return h.set(t,!1),!1;if(e.isFile()&&gt.has(o))return h.set(t,!0),!0;if(e.isDirectory()&&ht(n))return h.set(t,!0),!0}return h.set(t,!1),!1},wt=(t,{isDynamic:r,isCatchAll:e,isOptionalCatchAll:o})=>{let n=t;return r&&(n=n.replace(/^\[+|\]+$/g,"")),(e||o)&&(n=n.replace(/^\.{3}/,"")),{paramName:n,keyName:`${o?ut:e?ft:r?Et:""}${n}`}},V=(t,r,e="",o=[])=>{if(N.has(r))return N.get(r);let n=e,i=e+O,s=[],p=[],a=[],m=[],c=[...o],x=Tt.readdirSync(r,{withFileTypes:!0}).filter(y=>{if(y.isDirectory()){let P=b.join(r,y.name);return ht(P)}return gt.has(y.name)}).sort();for(let y of x){let P=_(b.join(r,y.name));if(y.isDirectory()){let l=y.name,T=l.startsWith("(")&&l.endsWith(")"),u=l.startsWith("@"),f=l.startsWith("[[...")&&l.endsWith("]]"),g=l.startsWith("[...")&&l.endsWith("]"),d=l.startsWith("[")&&l.endsWith("]"),{paramName:At,keyName:Rt}=wt(l,{isDynamic:d,isCatchAll:g,isOptionalCatchAll:f}),Ot=d||g||f?[...c,{paramName:At,routeType:{isDynamic:d,isCatchAll:g,isOptionalCatchAll:f,isGroup:T,isParallel:u}}]:c,J=T||u,{pathStructure:Z,imports:Lt,paramsTypes:Dt}=V(t,P,J?n:i,Ot);if(p.push(...Lt),m.push(...Dt),J){let tt=Z.match(/^\s*\{([\s\S]*)\}\s*$/);tt&&s.push(`${i}${tt[1].trim()}`)}else s.push(`${i}"${Rt}": ${Z}`)}else{let l=mt(t,P);if(l){let{importStatement:T,importPath:u,type:f}=l;p.push({statement:T,path:u}),a.push(f)}if(k.forEach(T=>{let u=lt(t,P,T);if(u){let{importStatement:f,importPath:g,type:d}=u;p.push({statement:f,path:g}),a.push(d)}}),a.push(j),c.length>0){let T=c.map(({paramName:f,routeType:g})=>{let d=g.isCatchAll?"string[]":g.isOptionalCatchAll?"string[] | undefined":"string";return{name:f,type:d}}),u=L(T);m.push({paramsType:u,dirPath:b.dirname(P)}),a.push(I(Q,u))}}}let A=a.join(" & "),F=s.length>0?`{${E}${s.join(`,${E}`)}${E}${n}}`:"",z={pathStructure:A&&F?`${A} & ${F}`:A||F,imports:p,paramsTypes:m};return N.set(r,z),z};var yt=(t,r)=>{let{pathStructure:e,imports:o,paramsTypes:n}=V(t,r),i=`export type PathStructure = ${e}${S}`,s=o.length?`${o.sort((c,x)=>c.path.localeCompare(x.path,void 0,{numeric:!0})).map(c=>c.statement).join(E)}`:"",p=ot.filter(c=>e.includes(c)),a=D(p.join(" ,"),nt),m=n.map(({paramsType:c,dirPath:x})=>({paramsType:`export type Params = ${c}${S}`,dirPath:x}));return{pathStructure:`${a}${E}${s}${E}${E}${i}`,paramsTypes:m}};import v from"chalk";var q=(t,r,e="\u2192",o=24)=>t.padEnd(o)+` ${e} ${r}`,B=(t=0)=>O.repeat(t),dt=()=>({info:(t,r={})=>{let{indentLevel:e=0,event:o}=r,n=o?`${v.cyan(`[${o}]`)} `:"";console.log(`${B(e)}${n}${t}`)},success:(t,r={})=>{let{indentLevel:e=0}=r;console.log(`${B(e)}${v.green("\u2713")} ${t}`)},error:(t,r={})=>{let{indentLevel:e=0}=r;console.error(`${B(e)}${v.red("\u2717")} ${v.red(t)}`)}});var _t=({baseDir:t,outputPath:r,paramsFileName:e,logger:o})=>{o.info("Generating types...",{event:"generate"});let{pathStructure:n,paramsTypes:i}=yt(r,t);Pt.writeFileSync(r,n),o.success(q("Path structure type",C(r),W,M),{indentLevel:Y}),e&&(i.forEach(({paramsType:s,dirPath:p})=>{let a=Yt.join(p,e);Pt.writeFileSync(a,s)}),o.success(q("Params types",e,W,M),{indentLevel:Y}))};import Mt from"chokidar";var St=(t,r)=>{let e=null,o=!1,n=null,i=async(...s)=>{o=!0;try{await t(...s)}finally{if(o=!1,n){let p=n;n=null,i(...p)}}};return(...s)=>{e&&clearTimeout(e),e=setTimeout(()=>{if(o){n=s;return}i(...s)},r)}};var xt=(t,r,e)=>{e.info(`${C(t)}`,{event:"watch"});let o=a=>R.some(m=>a.endsWith(m)),n=new Set,i=St(()=>{n.forEach(a=>{it(a),at(a)}),n.clear(),r()},300),s=Mt.watch(t,{ignoreInitial:!0,ignored:(a,m)=>!!m?.isFile()&&!o(a)});s.on("ready",()=>{i(),s.on("all",(a,m)=>{if(o(m)){let c=C(m);e.info(c,{event:a}),n.add(m),i()}})}),s.on("error",a=>{a instanceof Error?e.error(`Watcher error: ${a.message}`):e.error(`Unknown watcher error: ${String(a)}`)});let p=()=>{s.close().then(()=>{e.info("Watcher closed.",{event:"watch"})}).catch(a=>{e.error(`Failed to close watcher: ${a.message}`)})};process.on("SIGINT",p),process.on("SIGTERM",p)};var Ct=(t,r,e,o)=>{try{return _t({baseDir:t,outputPath:r,paramsFileName:e,logger:o}),w}catch(n){return n instanceof Error?o.error(`Failed to generate: ${n.message}`):o.error(`Unknown error occurred during generate: ${String(n)}`),$}},Nt=(t,r,e,o)=>{let n=_($t.resolve(t)),i=_($t.resolve(r)),s=typeof e.paramsFile=="string"?e.paramsFile:null;return e.paramsFile!==void 0&&!s?(o.error("Error: --params-file requires a filename."),$):e.watch?(xt(n,()=>{Ct(n,i,s,o)},o),w):Ct(n,i,s,o)};var It=(t,r=dt())=>{let e=new Wt;e.description("Generate RPC client type definitions based on the Next.js path structure.").argument("<baseDir>","Base directory containing Next.js paths for type generation").argument("<outputPath>","Output path for the generated type definitions").option("-w, --watch","Watch mode: regenerate on file changes").option("-p, --params-file [filename]","Generate params types file with specified filename").action(async(o,n,i)=>{try{let s=await Nt(o,n,i,r);i.watch||process.exit(s)}catch(s){r.error(`Unexpected error occurred:${s instanceof Error?s.message:String(s)}`),process.exit($)}}),e.parse(t)};It(process.argv);
4
4
  /*!
5
5
  * Inspired by pathpida (https://github.com/aspida/pathpida),
6
6
  * especially the design and UX of its CLI.
@@ -1,2 +1,3 @@
1
- export { createRpcClient, createRpcHelper } from "./rpc";
1
+ export { createRpcClient } from "./rpc-client";
2
+ export { createRpcHelper } from "./rpc-helper";
2
3
  export type { Endpoint, ParamsKey, QueryKey, OptionalQueryKey } from "./types";
@@ -1 +1 @@
1
- import{createRpcClient as p,createRpcHelper as t}from"./rpc";export{p as createRpcClient,t as createRpcHelper};
1
+ import{createRpcClient as p}from"./rpc-client";import{createRpcHelper as o}from"./rpc-helper";export{p as createRpcClient,o as createRpcHelper};
@@ -0,0 +1,32 @@
1
+ import type { ClientOptions, DynamicPathProxyAsFunction } from "./types";
2
+ /**
3
+ * Creates an RPC client proxy for making HTTP requests with a strongly typed API.
4
+ *
5
+ * @template T - The type defining the RPC endpoint structure.
6
+ * @param baseUrl - The base URL for the RPC client. This URL will be used as the root for all generated endpoint URLs.
7
+ * @param options - (Optional) Client options to customize the behavior of the RPC client. These options may include a custom fetch function and default request initialization options.
8
+ * @returns An object that enables you to dynamically build endpoint URLs and execute HTTP requests (such as GET, POST, DELETE, etc.) with full type support.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { createRpcClient } from "./rpc";
13
+ * import type { PathStructure } from "./types";
14
+ *
15
+ * // Create an RPC client with a base URL
16
+ * const client = createRpcClient<PathStructure>("http://localhost:3000");
17
+ *
18
+ * // Generate a URL for a dynamic endpoint
19
+ * const urlResult = client.fuga._foo("test").$url();
20
+ * console.log(urlResult.path); // "http://localhost:3000/fuga/test"
21
+ * console.log(urlResult.relativePath); // "/fuga/test"
22
+ * console.log(urlResult.pathname); // "/fuga/[foo]"
23
+ * console.log(urlResult.params); // { foo: "test" }
24
+ *
25
+ * // Execute a GET request on an endpoint
26
+ * const response = await client.api.hoge._foo("test").$get();
27
+ * console.log(await response.json()); // Expected response: { method: "get" }
28
+ * ```
29
+ *
30
+ * The above example demonstrates how to generate URLs with dynamic segments and how to execute HTTP requests.
31
+ */
32
+ export declare const createRpcClient: <T extends object>(baseUrl: string, options?: ClientOptions) => DynamicPathProxyAsFunction<T>;
@@ -0,0 +1 @@
1
+ import{httpMethod as n}from"./http-method";import{makeCreateRpc as p}from"./rpc";import{createUrl as m}from"./url";import{isHttpMethod as c}from"./utils";const l=p((t,{paths:r,params:o,dynamicKeys:e,options:i})=>{if(t==="$url")return m([...r],o,e);if(c(t))return n(t,[...r],o,e,i)});export{l as createRpcClient};
@@ -0,0 +1,23 @@
1
+ import type { DynamicPathProxyAsProperty } from "./types";
2
+ /**
3
+ * Creates an RPC helper proxy for dynamic path matching based on a given endpoint structure.
4
+ *
5
+ * @template T - The type defining the RPC endpoint structure.
6
+ *
7
+ * @returns An object that provides dynamic RPC helper methods for the defined endpoints.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * // Create the RPC helper
12
+ * const rpcHelper = createRpcHelper<PathStructure>();
13
+ *
14
+ * // Use the $match method to extract dynamic parameters from a URL path
15
+ * const match = rpcHelper.fuga._foo.$match("/fuga/test");
16
+ * // Expected output: { foo: "test" }
17
+ *
18
+ * // If the path does not match, it returns null
19
+ * const noMatch = rpcHelper.fuga._foo.$match("/hoge/test");
20
+ * // Expected output: null
21
+ * ```
22
+ */
23
+ export declare const createRpcHelper: <T extends object>() => DynamicPathProxyAsProperty<T>;
@@ -0,0 +1 @@
1
+ import{matchPath as o}from"./match";import{makeCreateRpc as a}from"./rpc";const c=a((r,{paths:t,dynamicKeys:e})=>{if(r==="$match")return o([...t],e)});export{c as createRpcHelper};
@@ -3,58 +3,5 @@
3
3
  * and pathpida (https://github.com/aspida/pathpida)
4
4
  * particularly their routing structures and developer experience.
5
5
  */
6
- import type { FuncParams, DynamicPathProxyAsFunction, ClientOptions, DynamicPathProxyAsProperty } from "./types";
7
- export declare const createRpcProxy: <T extends object>(options: ClientOptions, paths?: string[], params?: FuncParams, dynamicKeys?: string[]) => T;
8
- /**
9
- * Creates an RPC client proxy for making HTTP requests with a strongly typed API.
10
- *
11
- * @template T - The type defining the RPC endpoint structure.
12
- * @param baseUrl - The base URL for the RPC client. This URL will be used as the root for all generated endpoint URLs.
13
- * @param options - (Optional) Client options to customize the behavior of the RPC client. These options may include a custom fetch function and default request initialization options.
14
- * @returns An object that enables you to dynamically build endpoint URLs and execute HTTP requests (such as GET, POST, DELETE, etc.) with full type support.
15
- *
16
- * @example
17
- * ```ts
18
- * import { createRpcClient } from "./rpc";
19
- * import type { PathStructure } from "./types";
20
- *
21
- * // Create an RPC client with a base URL
22
- * const client = createRpcClient<PathStructure>("http://localhost:3000");
23
- *
24
- * // Generate a URL for a dynamic endpoint
25
- * const urlResult = client.fuga._foo("test").$url();
26
- * console.log(urlResult.path); // "http://localhost:3000/fuga/test"
27
- * console.log(urlResult.relativePath); // "/fuga/test"
28
- * console.log(urlResult.pathname); // "/fuga/[foo]"
29
- * console.log(urlResult.params); // { foo: "test" }
30
- *
31
- * // Execute a GET request on an endpoint
32
- * const response = await client.api.hoge._foo("test").$get();
33
- * console.log(await response.json()); // Expected response: { method: "get" }
34
- * ```
35
- *
36
- * The above example demonstrates how to generate URLs with dynamic segments and how to execute HTTP requests.
37
- */
38
- export declare const createRpcClient: <T extends object>(baseUrl: string, options?: ClientOptions) => DynamicPathProxyAsFunction<T>;
39
- /**
40
- * Creates an RPC helper proxy for dynamic path matching based on a given endpoint structure.
41
- *
42
- * @template T - The type defining the RPC endpoint structure.
43
- *
44
- * @returns An object that provides dynamic RPC helper methods for the defined endpoints.
45
- *
46
- * @example
47
- * ```ts
48
- * // Create the RPC helper
49
- * const rpcHelper = createRpcHelper<PathStructure>();
50
- *
51
- * // Use the $match method to extract dynamic parameters from a URL path
52
- * const match = rpcHelper.fuga._foo.$match("/fuga/test");
53
- * // Expected output: { foo: "test" }
54
- *
55
- * // If the path does not match, it returns null
56
- * const noMatch = rpcHelper.fuga._foo.$match("/hoge/test");
57
- * // Expected output: null
58
- * ```
59
- */
60
- export declare const createRpcHelper: <T extends object>() => DynamicPathProxyAsProperty<T>;
6
+ import type { ClientOptions, RpcHandler } from "./types";
7
+ export declare const makeCreateRpc: (handler: RpcHandler) => <T extends object>(base?: string, options?: ClientOptions) => T;
@@ -2,4 +2,4 @@
2
2
  * Inspired by the design of Hono (https://github.com/honojs/hono)
3
3
  * and pathpida (https://github.com/aspida/pathpida)
4
4
  * particularly their routing structures and developer experience.
5
- */import{httpMethod as m}from"./http-method";import{matchPath as f}from"./match";import{createUrl as p}from"./url";import{isDynamic as u,isHttpMethod as P}from"./utils";const i=(e,t=[],o={},n=[])=>new Proxy(c=>{const r=t.at(-1),a=n.at(-1);if(c===void 0)throw new Error(`An argument is required when calling the function for paramKey: ${a}`);if(r&&a&&u(r))return i(e,[...t],{...o,[a]:c},n);throw new Error(`paramKey: ${r} is not a dynamic parameter and cannot be called as a function`)},{get:(c,r)=>r==="$url"?p([...t],o,n):r==="$match"?f([...t],n):P(r)?m(r,[...t],o,n,{...e}):u(r)?i(e,[...t,r],o,[...n,r]):i(e,[...t,r],o,n)}),w=(e,t={})=>i(t,[e]),T=()=>i({},["/"]);export{w as createRpcClient,T as createRpcHelper,i as createRpcProxy};
5
+ */import{isDynamic as f}from"./utils";const s=(n,e,t,c,o)=>new Proxy(u=>{const r=t.at(-1),i=o.at(-1);if(u===void 0)throw new Error(`Missing value for dynamic parameter: ${i}`);if(r&&i&&f(r))return s(n,e,[...t],{...c,[i]:u},o);throw new Error(`${r} is not dynamic`)},{get(u,r){const i=n(r,{paths:t,params:c,dynamicKeys:o,options:e});return i!==void 0?i:f(r)?s(n,e,[...t,r],c,[...o,r]):s(n,e,[...t,r],c,o)}}),l=n=>(e="/",t={})=>s(n,t,[e],{},[]);export{l as makeCreateRpc};
@@ -100,4 +100,10 @@ export type DynamicPathProxyAsFunction<T> = Omit<(T extends Endpoint ? PathProxy
100
100
  export type DynamicPathProxyAsProperty<T> = Omit<(T extends Endpoint ? PathProxyAsProperty<T> : unknown) & {
101
101
  [K in keyof T]: K extends unknown ? DynamicPathProxyAsProperty<T[K]> : DynamicPathProxyAsProperty<T[K]>;
102
102
  }, QueryKey | OptionalQueryKey | ParamsKey>;
103
+ export type RpcHandler = (key: string, context: {
104
+ paths: string[];
105
+ params: FuncParams;
106
+ dynamicKeys: string[];
107
+ options: ClientOptions;
108
+ }) => unknown | undefined;
103
109
  export {};
@@ -1,5 +1,6 @@
1
+ export declare const HTTP_METHODS: readonly ["GET", "HEAD", "OPTIONS", "POST", "PUT", "DELETE", "PATCH"];
1
2
  export declare const OPTIONAL_CATCH_ALL_PREFIX = "_____";
2
3
  export declare const CATCH_ALL_PREFIX = "___";
3
4
  export declare const DYNAMIC_PREFIX = "_";
4
5
  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")[];
6
+ export declare const HTTP_METHOD_FUNC_KEYS: ("$get" | "$head" | "$post" | "$put" | "$delete" | "$patch")[];
@@ -1 +1 @@
1
- import{HTTP_METHODS as o}from"next/dist/server/web/http";const e="_____",r="___",E="_",t=o.filter(_=>_!=="OPTIONS"),O=t.map(_=>`$${_.toLowerCase()}`);export{r as CATCH_ALL_PREFIX,E as DYNAMIC_PREFIX,t as HTTP_METHODS_EXCLUDE_OPTIONS,O as HTTP_METHOD_FUNC_KEYS,e as OPTIONAL_CATCH_ALL_PREFIX};
1
+ const T=["GET","HEAD","OPTIONS","POST","PUT","DELETE","PATCH"],t="_____",E="___",e="_",o=T.filter(_=>_!=="OPTIONS"),P=o.map(_=>`$${_.toLowerCase()}`);export{E as CATCH_ALL_PREFIX,e as DYNAMIC_PREFIX,T as HTTP_METHODS,o as HTTP_METHODS_EXCLUDE_OPTIONS,P as HTTP_METHOD_FUNC_KEYS,t as OPTIONAL_CATCH_ALL_PREFIX};
@@ -1 +1,2 @@
1
- export type { HTTP_METHOD as HttpMethod } from "next/dist/server/web/http";
1
+ import type { HTTP_METHODS } from "./constants";
2
+ export type HttpMethod = (typeof HTTP_METHODS)[number];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rpc4next",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
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
5
  "author": "watanabe-1",
6
6
  "license": "MIT",
@@ -77,22 +77,22 @@
77
77
  },
78
78
  "devDependencies": {
79
79
  "@types/node": "^22.14.1",
80
- "@vitest/coverage-v8": "^3.1.1",
80
+ "@vitest/coverage-v8": "^3.1.2",
81
81
  "@vitest/eslint-plugin": "^1.1.43",
82
- "@vitest/ui": "^3.1.1",
83
- "esbuild": "^0.25.2",
84
- "eslint": "^9.24.0",
82
+ "@vitest/ui": "^3.1.2",
83
+ "esbuild": "^0.25.3",
84
+ "eslint": "^9.25.1",
85
85
  "eslint-config-prettier": "^10.1.2",
86
86
  "eslint-plugin-import": "^2.31.0",
87
87
  "eslint-plugin-unused-imports": "^4.1.4",
88
88
  "mock-fs": "^5.5.0",
89
- "msw": "^2.7.4",
89
+ "msw": "^2.7.5",
90
90
  "next": "15.3.1",
91
91
  "prettier": "^3.5.3",
92
92
  "ts-node": "^10.9.2",
93
93
  "typescript": "^5.8.3",
94
- "typescript-eslint": "^8.30.1",
95
- "vitest": "^3.1.1",
94
+ "typescript-eslint": "^8.31.0",
95
+ "vitest": "^3.1.2",
96
96
  "zod": "^3.24.3"
97
97
  },
98
98
  "peerDependencies": {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,8 +0,0 @@
1
- /*!
2
- * Portions of this code are based on the Hono project (https://github.com/honojs/hono),
3
- * originally created by Yusuke Wada (https://github.com/yusukebe) and developed with
4
- * contributions from the Hono community.
5
- * This code has been adapted and modified for this project.
6
- * Original copyright belongs to Yusuke Wada and the Hono project contributors.
7
- * Hono is licensed under the MIT License.
8
- */
File without changes