rpc4next 0.3.7 → 0.3.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.
Files changed (41) hide show
  1. package/dist/rpc/cli/index.js +1 -1
  2. package/dist/rpc/client/http-method.js +1 -1
  3. package/dist/rpc/client/index.d.ts +2 -1
  4. package/dist/rpc/client/index.js +1 -1
  5. package/dist/rpc/client/match.js +1 -1
  6. package/dist/rpc/client/rpc-client.d.ts +32 -0
  7. package/dist/rpc/client/rpc-client.js +1 -0
  8. package/dist/rpc/client/rpc-helper.d.ts +23 -0
  9. package/dist/rpc/client/rpc-helper.js +1 -0
  10. package/dist/rpc/client/rpc.d.ts +2 -55
  11. package/dist/rpc/client/rpc.js +1 -1
  12. package/dist/rpc/client/types.d.ts +6 -0
  13. package/dist/rpc/server/handler.d.ts +4 -0
  14. package/dist/rpc/server/handler.js +1 -0
  15. package/dist/rpc/server/route-context.js +1 -0
  16. package/dist/rpc/server/route-handler-factory.js +1 -1
  17. package/dist/rpc/server/route-types.d.ts +7 -7
  18. package/dist/rpc/server/types.d.ts +9 -3
  19. package/dist/rpc/server/validators/validator.d.ts +12 -0
  20. package/dist/rpc/server/validators/validator.js +8 -0
  21. package/dist/rpc/server/validators/zod/index.d.ts +1 -1
  22. package/dist/rpc/server/validators/zod/index.js +1 -1
  23. package/dist/rpc/server/validators/zod/zod-validator.d.ts +3 -2
  24. package/dist/rpc/server/validators/zod/zod-validator.js +1 -1
  25. package/package.json +11 -10
  26. package/dist/rpc/client/types.js +0 -0
  27. package/dist/rpc/lib/content-type-types.js +0 -0
  28. package/dist/rpc/lib/http-request-headers-types.js +0 -0
  29. package/dist/rpc/lib/http-response-headers-types.js +0 -0
  30. package/dist/rpc/lib/http-status-code-types.js +0 -0
  31. package/dist/rpc/lib/types.js +0 -0
  32. package/dist/rpc/server/create-handler.d.ts +0 -3
  33. package/dist/rpc/server/create-handler.js +0 -1
  34. package/dist/rpc/server/create-route-context.js +0 -1
  35. package/dist/rpc/server/route-types.js +0 -8
  36. package/dist/rpc/server/types.js +0 -0
  37. /package/dist/rpc/client/{utils.d.ts → client-utils.d.ts} +0 -0
  38. /package/dist/rpc/client/{utils.js → client-utils.js} +0 -0
  39. /package/dist/rpc/server/{create-route-context.d.ts → route-context.d.ts} +0 -0
  40. /package/dist/rpc/server/{search-params-to-object.d.ts → server-utils.d.ts} +0 -0
  41. /package/dist/rpc/server/{search-params-to-object.js → server-utils.js} +0 -0
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
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);
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 g=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(g,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 ht=new Set(R),gt=t=>{if(g.has(t))return g.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 g.set(t,!1),!1;if(e.isFile()&&ht.has(o))return g.set(t,!0),!0;if(e.isDirectory()&&gt(n))return g.set(t,!0),!0}return g.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 gt(P)}return ht.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("]]"),h=l.startsWith("[...")&&l.endsWith("]"),d=l.startsWith("[")&&l.endsWith("]"),{paramName:At,keyName:Rt}=wt(l,{isDynamic:d,isCatchAll:h,isOptionalCatchAll:f}),Ot=d||h||f?[...c,{paramName:At,routeType:{isDynamic:d,isCatchAll:h,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:h,type:d}=u;p.push({statement:f,path:h}),a.push(d)}}),a.push(j),c.length>0){let T=c.map(({paramName:f,routeType:h})=>{let d=h.isCatchAll?"string[]":h.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 +1 @@
1
- import{createUrl as q}from"./url";import{deepMerge as T}from"./utils";const l=t=>{const e={};return t?t instanceof Headers?(t.forEach((n,r)=>{e[r.toLowerCase()]=n}),e):Array.isArray(t)?(t.forEach(([n,r])=>{e[n.toLowerCase()]=r}),e):(Object.entries(t).forEach(([n,r])=>{e[n.toLowerCase()]=r}),e):e},F=(t,e,n,r,c)=>async(s,p)=>{let d,a;s?.body?.json&&(a="application/json",d=JSON.stringify(s.body.json));const g=s?.requestHeaders?.headers,f=s?.requestHeaders?.cookies,m=q([...e],n,r)(s?.url),h=t.replace(/^\$/,"").toUpperCase(),H=p?.fetch??c.fetch??fetch,u=c.init??{},y=p?.init??{},O=l(u.headers),C=l(g??y.headers),o={...O,...C};a&&(o["content-type"]=a),f&&(o.cookie=Object.entries(f).map(([j,R])=>`${j}=${R}`).join("; "));const{headers:k,...b}=u,{headers:w,...I}=y,i=T(b,I);return i.method=h,Object.keys(o).length>0&&(i.headers=o),d&&(i.body=d),await H(m.path,i)};export{F as httpMethod};
1
+ import{deepMerge as q}from"./client-utils";import{createUrl as T}from"./url";const l=t=>{const e={};return t?t instanceof Headers?(t.forEach((n,r)=>{e[r.toLowerCase()]=n}),e):Array.isArray(t)?(t.forEach(([n,r])=>{e[n.toLowerCase()]=r}),e):(Object.entries(t).forEach(([n,r])=>{e[n.toLowerCase()]=r}),e):e},F=(t,e,n,r,c)=>async(s,p)=>{let d,a;s?.body?.json&&(a="application/json",d=JSON.stringify(s.body.json));const g=s?.requestHeaders?.headers,f=s?.requestHeaders?.cookies,m=T([...e],n,r)(s?.url),h=t.replace(/^\$/,"").toUpperCase(),H=p?.fetch??c.fetch??fetch,u=c.init??{},y=p?.init??{},O=l(u.headers),C=l(g??y.headers),o={...O,...C};a&&(o["content-type"]=a),f&&(o.cookie=Object.entries(f).map(([j,R])=>`${j}=${R}`).join("; "));const{headers:k,...b}=u,{headers:w,...I}=y,i=q(b,I);return i.method=h,Object.keys(o).length>0&&(i.headers=o),d&&(i.body=d),await H(m.path,i)};export{F as httpMethod};
@@ -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};
@@ -1 +1 @@
1
- import{replaceDynamicSegments as d}from"./url";import{isCatchAllOrOptional as u}from"./utils";const f=(r,o)=>a=>{const c=`/${r.slice(1).join("/")}`,i=d(c,{optionalCatchAll:"(?:/(.*))?",catchAll:"/([^/]+(?:/[^/]+)*)",dynamic:"/([^/]+)"}),n=new RegExp(`^${i}/?$`).exec(a);return n?o.reduce((l,t,s)=>{const m=t.replace(/^_+/,""),e=n[s+1],p=u(t)?e===void 0||e===""?void 0:e.split("/").filter(Boolean).map(decodeURIComponent):decodeURIComponent(e);return{...l,[m]:p}},{}):null};export{f as matchPath};
1
+ import{isCatchAllOrOptional as d}from"./client-utils";import{replaceDynamicSegments as u}from"./url";const f=(r,o)=>a=>{const c=`/${r.slice(1).join("/")}`,i=u(c,{optionalCatchAll:"(?:/(.*))?",catchAll:"/([^/]+(?:/[^/]+)*)",dynamic:"/([^/]+)"}),n=new RegExp(`^${i}/?$`).exec(a);return n?o.reduce((l,t,s)=>{const m=t.replace(/^_+/,""),e=n[s+1],p=d(t)?e===void 0||e===""?void 0:e.split("/").filter(Boolean).map(decodeURIComponent):decodeURIComponent(e);return{...l,[m]:p}},{}):null};export{f as matchPath};
@@ -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{isHttpMethod as n}from"./client-utils";import{httpMethod as p}from"./http-method";import{makeCreateRpc as m}from"./rpc";import{createUrl as c}from"./url";const l=m((t,{paths:r,params:o,dynamicKeys:e,options:i})=>{if(t==="$url")return c([...r],o,e);if(n(t))return p(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"./client-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 {};
@@ -0,0 +1,4 @@
1
+ import type { ValidationSchema, RouteResponse, Handler } from "./route-types";
2
+ import type { Params, Query } from "./types";
3
+ import type { HttpMethod } from "../lib/types";
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>;
@@ -0,0 +1 @@
1
+ const t=()=>e=>e;export{t as createHandler};
@@ -0,0 +1 @@
1
+ import{NextResponse as a}from"next/server";import{searchParamsToObject as p}from"./server-utils";const d=(s,o)=>{const n={};return{req:Object.assign(s,{query:()=>p(s.nextUrl.searchParams),params:()=>o.params,valid:t=>n[t],addValidatedData:(t,e)=>{n[t]=e}}),body:(t,e)=>new a(t,e),json:(t,e)=>a.json(t,e),text:(t,e)=>new a(t,{...e,headers:{"Content-Type":"text/plain",...e?.headers}}),redirect:(t,e)=>a.redirect(t,e)}};export{d 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"./create-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};
4
+ */import{createRouteContext as d}from"./route-context";const i=(o,e)=>async(n,s)=>{const a=d(n,s);try{for(const t of o){const r=await t(a);if(r instanceof Response)return r}throw new Error("No handler returned a response")}catch(t){return await e(t,a)}},T=o=>()=>{const e=n=>(...s)=>{const t=i(s,o??((r,p)=>{throw r}));return{[n]:t}};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};
@@ -19,7 +19,7 @@ export interface ValidationSchema {
19
19
  input: {};
20
20
  output: {};
21
21
  }
22
- export type Handler<TParams = Params, TQuery = Query, TValidationSchema extends ValidationSchema = ValidationSchema, TRouteResponse extends RouteResponse = RouteResponse> = (routeContext: RouteContext<TParams, TQuery, TValidationSchema>) => TRouteResponse;
22
+ export type Handler<_THttpMethod extends HttpMethod, TParams = Params, TQuery = Query, TValidationSchema extends ValidationSchema = ValidationSchema, TRouteResponse extends RouteResponse = RouteResponse> = (routeContext: RouteContext<TParams, TQuery, TValidationSchema>) => TRouteResponse;
23
23
  export type ErrorHandler<TRouteResponse extends RequiredRouteResponse, TParams = Params, TQuery = Query, TValidationSchema extends ValidationSchema = ValidationSchema> = (error: unknown, routeContext: RouteContext<TParams, TQuery, TValidationSchema>) => TRouteResponse;
24
24
  export type RouteHandlerResponse<TRouteResponse extends RouteResponse, _TValidationSchema extends ValidationSchema> = Promise<Exclude<Awaited<TRouteResponse>, void | undefined>>;
25
25
  export type RouteHandler<TParams extends RouteBindings["params"], TRouteResponse extends RouteResponse, TValidationSchema extends ValidationSchema> = (req: NextRequest, segmentData: {
@@ -31,11 +31,11 @@ export interface MethodRouteDefinition<THttpMethod extends HttpMethod, TBindings
31
31
  } ? Awaited<TValue> : Params, TQuery extends TBindings["query"] = TBindings extends {
32
32
  query: infer TValue;
33
33
  } ? TValue : Query> {
34
- <TV1 extends ValidationSchema = ValidationSchema, TR1 extends RequiredRouteResponse = RequiredRouteResponse>(handler: Handler<TParams, TQuery, TV1, TR1>): HttpMethodMapping<THttpMethod, TParams, TR1 | TOnErrorResponse, TV1>;
35
- <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, TV2>;
36
- <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, TV3>;
37
- <TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TV3 extends ValidationSchema = TV1 & TV2, TV4 extends ValidationSchema = TV1 & TV2 & TV3, TR1 extends RouteResponse = RouteResponse, TR2 extends RouteResponse = RouteResponse, TR3 extends RouteResponse = RouteResponse, TR4 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<TParams, TQuery, TV1, TR1>, handler2: Handler<TParams, TQuery, TV2, TR2>, handler3: Handler<TParams, TQuery, TV3, TR3>, handler4: Handler<TParams, TQuery, TV4, TR4>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TR3 | TR4 | TOnErrorResponse, TV4>;
38
- <TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TV3 extends ValidationSchema = TV1 & TV2, TV4 extends ValidationSchema = TV1 & TV2 & TV3, TV5 extends ValidationSchema = TV1 & TV2 & TV3 & TV4, TR1 extends RouteResponse = RouteResponse, TR2 extends RouteResponse = RouteResponse, TR3 extends RouteResponse = RouteResponse, TR4 extends RouteResponse = RouteResponse, TR5 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<TParams, TQuery, TV1, TR1>, handler2: Handler<TParams, TQuery, TV2, TR2>, handler3: Handler<TParams, TQuery, TV3, TR3>, handler4: Handler<TParams, TQuery, TV4, TR4>, handler5: Handler<TParams, TQuery, TV5, TR5>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TR3 | TR4 | TR5 | TOnErrorResponse, TV5>;
39
- <TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TV3 extends ValidationSchema = TV1 & TV2, TV4 extends ValidationSchema = TV1 & TV2 & TV3, TV5 extends ValidationSchema = TV1 & TV2 & TV3 & TV4, TV6 extends ValidationSchema = TV1 & TV2 & TV3 & TV4 & TV5, TR1 extends RouteResponse = RouteResponse, TR2 extends RouteResponse = RouteResponse, TR3 extends RouteResponse = RouteResponse, TR4 extends RouteResponse = RouteResponse, TR5 extends RouteResponse = RouteResponse, TR6 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<TParams, TQuery, TV1, TR1>, handler2: Handler<TParams, TQuery, TV2, TR2>, handler3: Handler<TParams, TQuery, TV3, TR3>, handler4: Handler<TParams, TQuery, TV4, TR4>, handler5: Handler<TParams, TQuery, TV5, TR5>, handler6: Handler<TParams, TQuery, TV6, TR6>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TR3 | TR4 | TR5 | TR6 | TOnErrorResponse, TV6>;
34
+ <TV1 extends ValidationSchema = ValidationSchema, TR1 extends RequiredRouteResponse = RequiredRouteResponse>(handler: Handler<THttpMethod, TParams, TQuery, TV1, TR1>): HttpMethodMapping<THttpMethod, TParams, TR1 | TOnErrorResponse, TV1>;
35
+ <TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TR1 extends RouteResponse = RouteResponse, TR2 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<THttpMethod, TParams, TQuery, TV1, TR1>, handler2: Handler<THttpMethod, TParams, TQuery, TV2, TR2>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TOnErrorResponse, TV2>;
36
+ <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<THttpMethod, TParams, TQuery, TV1, TR1>, handler2: Handler<THttpMethod, TParams, TQuery, TV2, TR2>, handler3: Handler<THttpMethod, TParams, TQuery, TV3, TR3>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TR3 | TOnErrorResponse, TV3>;
37
+ <TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TV3 extends ValidationSchema = TV1 & TV2, TV4 extends ValidationSchema = TV1 & TV2 & TV3, TR1 extends RouteResponse = RouteResponse, TR2 extends RouteResponse = RouteResponse, TR3 extends RouteResponse = RouteResponse, TR4 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<THttpMethod, TParams, TQuery, TV1, TR1>, handler2: Handler<THttpMethod, TParams, TQuery, TV2, TR2>, handler3: Handler<THttpMethod, TParams, TQuery, TV3, TR3>, handler4: Handler<THttpMethod, TParams, TQuery, TV4, TR4>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TR3 | TR4 | TOnErrorResponse, TV4>;
38
+ <TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TV3 extends ValidationSchema = TV1 & TV2, TV4 extends ValidationSchema = TV1 & TV2 & TV3, TV5 extends ValidationSchema = TV1 & TV2 & TV3 & TV4, TR1 extends RouteResponse = RouteResponse, TR2 extends RouteResponse = RouteResponse, TR3 extends RouteResponse = RouteResponse, TR4 extends RouteResponse = RouteResponse, TR5 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<THttpMethod, TParams, TQuery, TV1, TR1>, handler2: Handler<THttpMethod, TParams, TQuery, TV2, TR2>, handler3: Handler<THttpMethod, TParams, TQuery, TV3, TR3>, handler4: Handler<THttpMethod, TParams, TQuery, TV4, TR4>, handler5: Handler<THttpMethod, TParams, TQuery, TV5, TR5>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TR3 | TR4 | TR5 | TOnErrorResponse, TV5>;
39
+ <TV1 extends ValidationSchema = ValidationSchema, TV2 extends ValidationSchema = TV1, TV3 extends ValidationSchema = TV1 & TV2, TV4 extends ValidationSchema = TV1 & TV2 & TV3, TV5 extends ValidationSchema = TV1 & TV2 & TV3 & TV4, TV6 extends ValidationSchema = TV1 & TV2 & TV3 & TV4 & TV5, TR1 extends RouteResponse = RouteResponse, TR2 extends RouteResponse = RouteResponse, TR3 extends RouteResponse = RouteResponse, TR4 extends RouteResponse = RouteResponse, TR5 extends RouteResponse = RouteResponse, TR6 extends RequiredRouteResponse = RequiredRouteResponse>(handler1: Handler<THttpMethod, TParams, TQuery, TV1, TR1>, handler2: Handler<THttpMethod, TParams, TQuery, TV2, TR2>, handler3: Handler<THttpMethod, TParams, TQuery, TV3, TR3>, handler4: Handler<THttpMethod, TParams, TQuery, TV4, TR4>, handler5: Handler<THttpMethod, TParams, TQuery, TV5, TR5>, handler6: Handler<THttpMethod, TParams, TQuery, TV6, TR6>): HttpMethodMapping<THttpMethod, TParams, TR1 | TR2 | TR3 | TR4 | TR5 | TR6 | TOnErrorResponse, TV6>;
40
40
  }
41
41
  export {};
@@ -2,6 +2,7 @@ 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";
5
6
  import type { NextResponse, NextRequest } from "next/server";
6
7
  /**
7
8
  * Represents the result of an HTTP response status check.
@@ -99,7 +100,7 @@ export interface RouteContext<TParams = Params, TQuery = Query, TValidationSchem
99
100
  * @param target - The part of the request to validate.
100
101
  * @returns The validation result of the target.
101
102
  */
102
- valid: <TValidationTarget extends ValidationTarget>(target: TValidationTarget) => ValidationOutputFor<TValidationTarget, TValidationSchema>;
103
+ valid: <TValidationTarget extends ValidationTarget>(target: Extract<TValidationTarget, keyof TValidationSchema["output"]>) => ValidationOutputFor<TValidationTarget, TValidationSchema>;
103
104
  /**
104
105
  * Stores validated data for a specific part of the request.
105
106
  * This data can be retrieved later using `valid(...)`.
@@ -107,7 +108,7 @@ export interface RouteContext<TParams = Params, TQuery = Query, TValidationSchem
107
108
  * @param target - The request part to associate the value with.
108
109
  * @param value - The validated data.
109
110
  */
110
- addValidatedData: (target: ValidationTarget, value: object) => void;
111
+ addValidatedData: (target: ValidationTarget, value: ValidatedData) => void;
111
112
  };
112
113
  /**
113
114
  * Creates a typed response with an optional status.
@@ -145,7 +146,12 @@ export interface RouteContext<TParams = Params, TQuery = Query, TValidationSchem
145
146
  */
146
147
  redirect: <TStatus extends RedirectionHttpStatusCode = 302>(url: string, init?: TStatus | TypedResponseInit<TStatus, "">) => TypedNextResponse<undefined, TStatus, "">;
147
148
  }
148
- export type ValidationTarget = "params" | "query" | "json" | "headers" | "cookies";
149
+ declare const __validatedBrand: unique symbol;
150
+ export type ValidatedData = {
151
+ [__validatedBrand]: true;
152
+ };
153
+ type ValidationTargetKey = "params" | "query" | "json" | "headers" | "cookies";
154
+ export type ValidationTarget<THttpMethod extends HttpMethod = HttpMethod> = THttpMethod extends "GET" | "HEAD" ? Exclude<ValidationTargetKey, "json"> : ValidationTargetKey;
149
155
  type ValidationFor<TDirection extends keyof ValidationSchema, TTarget extends ValidationTarget, TSchema extends ValidationSchema> = TTarget extends keyof TSchema[TDirection] ? TSchema[TDirection][TTarget] : never;
150
156
  export type ValidationInputFor<TTarget extends ValidationTarget, TSchema extends ValidationSchema> = ValidationFor<"input", TTarget, TSchema>;
151
157
  type ValidationOutputFor<TTarget extends ValidationTarget, TSchema extends ValidationSchema> = ValidationFor<"output", TTarget, TSchema>;
@@ -0,0 +1,12 @@
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
+ */
9
+ import type { HttpMethod } from "../../lib/types";
10
+ import type { ValidationSchema } from "../route-types";
11
+ import type { Params, Query, RouteContext, TypedNextResponse, ValidatedData, ValidationTarget } from "../types";
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>>;
@@ -0,0 +1,8 @@
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
+ */import{createHandler as i}from"../handler";import{getCookiesObject as n,getHeadersObject as s}from"./validator-utils";const T=()=>(e,r)=>i()(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 n();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};
@@ -1 +1 @@
1
- export { zodValidator } from "./zod-validator";
1
+ export { zValidator } from "./zod-validator";
@@ -1 +1 @@
1
- import{zodValidator as a}from"./zod-validator";export{a as zodValidator};
1
+ import{zValidator as a}from"./zod-validator";export{a as zValidator};
@@ -6,10 +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";
9
10
  import type { ValidationSchema } from "../../route-types";
10
11
  import type { RouteContext, Params, Query, TypedNextResponse, ConditionalValidationInput, ValidationTarget } from "../../types";
11
12
  import type { z, ZodSchema } from "zod";
12
- 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 = {
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 = {
13
14
  input: Record<TValidationTarget, TInput>;
14
15
  output: Record<TValidationTarget, TOutput>;
15
- }, 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("../../route-types").Handler<TParams, TQuery, TValidationSchema, Promise<Exclude<THookReturn, void> | undefined>>;
16
+ }, 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("../../route-types").Handler<THttpMethod, TParams, TQuery, TValidationSchema, Promise<Exclude<THookReturn, void> | 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 u}from"../../create-handler";import{getCookiesObject as d,getHeadersObject as T}from"../validator-utils";const m=(a,i,r)=>{const s=r??((e,t)=>{if(!e.success)return t.json(e,{status:400})});return u()(async e=>{const t=await(async()=>{if(a==="params")return await e.req.params();if(a==="query")return e.req.query();if(a==="json")return e.req.json();if(a==="headers")return await T();if(a==="cookies")return await d()})(),o=await i.safeParseAsync(t),n=s(o,e);if(n instanceof Response)return n;if(!o.success)throw new Error("If you provide a custom hook, you must explicitly return a response when validation fails.");e.req.addValidatedData(a,o.data)})};export{m as zodValidator};
8
+ */import{validator as s}from"../validator";const T=(n,i,r)=>{const d=r??((t,a)=>{if(!t.success)return a.json(t,{status:400})});return s()(n,async(t,a)=>{const e=await i.safeParseAsync(t),o=d(e,a);if(o instanceof Response)return o;if(!e.success)throw new Error("If you provide a custom hook, you must explicitly return a response when validation fails.");return e.data})};export{T as zValidator};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rpc4next",
3
- "version": "0.3.7",
3
+ "version": "0.3.9",
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",
@@ -68,7 +68,8 @@
68
68
  "test:ui": "vitest --ui --coverage.enabled true",
69
69
  "test:watch": "vitest --watch",
70
70
  "lint": "eslint \"**/*.ts\"",
71
- "lint:fix": "eslint \"**/*.ts\" --fix"
71
+ "lint:fix": "eslint \"**/*.ts\" --fix",
72
+ "typecheck": "tsc --noEmit"
72
73
  },
73
74
  "dependencies": {
74
75
  "chalk": "^5.4.1",
@@ -76,23 +77,23 @@
76
77
  "commander": "^13.1.0"
77
78
  },
78
79
  "devDependencies": {
79
- "@types/node": "^22.14.1",
80
- "@vitest/coverage-v8": "^3.1.1",
80
+ "@types/node": "^22.15.2",
81
+ "@vitest/coverage-v8": "^3.1.2",
81
82
  "@vitest/eslint-plugin": "^1.1.43",
82
- "@vitest/ui": "^3.1.1",
83
- "esbuild": "^0.25.2",
84
- "eslint": "^9.24.0",
83
+ "@vitest/ui": "^3.1.2",
84
+ "esbuild": "^0.25.3",
85
+ "eslint": "^9.25.1",
85
86
  "eslint-config-prettier": "^10.1.2",
86
87
  "eslint-plugin-import": "^2.31.0",
87
88
  "eslint-plugin-unused-imports": "^4.1.4",
88
89
  "mock-fs": "^5.5.0",
89
- "msw": "^2.7.4",
90
+ "msw": "^2.7.5",
90
91
  "next": "15.3.1",
91
92
  "prettier": "^3.5.3",
92
93
  "ts-node": "^10.9.2",
93
94
  "typescript": "^5.8.3",
94
- "typescript-eslint": "^8.30.1",
95
- "vitest": "^3.1.1",
95
+ "typescript-eslint": "^8.31.0",
96
+ "vitest": "^3.1.2",
96
97
  "zod": "^3.24.3"
97
98
  },
98
99
  "peerDependencies": {
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,3 +0,0 @@
1
- import type { ValidationSchema, RouteResponse, Handler } from "./route-types";
2
- import type { Params, Query } from "./types";
3
- export declare const createHandler: <TParams extends Params, TQuery extends Query, TValidationSchema extends ValidationSchema>() => <TRouteResponse extends RouteResponse>(handler: Handler<TParams, TQuery, TValidationSchema, TRouteResponse>) => Handler<TParams, TQuery, TValidationSchema, TRouteResponse>;
@@ -1 +0,0 @@
1
- const a=()=>e=>e;export{a as createHandler};
@@ -1 +0,0 @@
1
- import{NextResponse as a}from"next/server";import{searchParamsToObject as p}from"./search-params-to-object";const d=(s,o)=>{const n={};return{req:Object.assign(s,{query:()=>p(s.nextUrl.searchParams),params:()=>o.params,valid:t=>n[t],addValidatedData:(t,e)=>{n[t]=e}}),body:(t,e)=>new a(t,e),json:(t,e)=>a.json(t,e),text:(t,e)=>new a(t,{...e,headers:{"Content-Type":"text/plain",...e?.headers}}),redirect:(t,e)=>a.redirect(t,e)}};export{d as createRouteContext};
@@ -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
File without changes