@lowerdeck/hono 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+
2
+ $ microbundle
3
+ No name was provided for external module '@lowerdeck/forwarded-for' in output.globals – guessing 'forwardedFor'
4
+ No name was provided for external module '@lowerdeck/error' in output.globals – guessing 'error'
5
+ No name was provided for external module 'hono/cors' in output.globals – guessing 'cors'
6
+ Build "@lowerdeck/hono" to dist:
7
+ 821 B: index.cjs.gz
8
+ 707 B: index.cjs.br
9
+ 720 B: index.modern.js.gz
10
+ 626 B: index.modern.js.br
11
+ 805 B: index.module.js.gz
12
+ 705 B: index.module.js.br
13
+ 911 B: index.umd.js.gz
14
+ 782 B: index.umd.js.br
@@ -0,0 +1,11 @@
1
+
2
+ $ vitest run --passWithNoTests
3
+ [?25l
4
+  RUN  v3.2.4 /Users/tobias/code/metorial/metorial-enterprise/oss/src/packages/server/hono
5
+
6
+ No test files found, exiting with code 0
7
+
8
+ include: **/*.{test,spec}.?(c|m)[jt]s?(x)
9
+ exclude: **/node_modules/**, **/dist/**, **/cypress/**, **/.{idea,git,cache,output,temp}/**, **/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*
10
+
11
+ [?25h
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # `@lowerdeck/hono`
2
+
3
+ Create Hono web applications with Metorial defaults. Includes error handling for ServiceError, 404 handling, and CORS configuration.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @lowerdeck/hono
9
+ yarn add @lowerdeck/hono
10
+ bun add @lowerdeck/hono
11
+ pnpm add @lowerdeck/hono
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```typescript
17
+ import { createHono, cors } from '@lowerdeck/hono';
18
+
19
+ // Create Hono app with defaults
20
+ const app = createHono('/api');
21
+
22
+ // Routes
23
+ app.get('/users/:id', async (c) => {
24
+ const id = c.req.param('id');
25
+ const user = await getUser(id);
26
+ return c.json(user);
27
+ });
28
+
29
+ // ServiceError handling is automatic
30
+ import { createError } from '@lowerdeck/error';
31
+
32
+ const UserNotFound = createError({
33
+ statusCode: 404,
34
+ errorCode: 'USER_NOT_FOUND',
35
+ message: 'User not found'
36
+ });
37
+
38
+ app.get('/users/:id', async (c) => {
39
+ const user = await getUser(id);
40
+ if (!user) {
41
+ throw UserNotFound(); // Automatically handled
42
+ }
43
+ return c.json(user);
44
+ });
45
+
46
+ // Custom CORS configuration
47
+ app.use('/api/*', cors({
48
+ origin: 'https://example.com',
49
+ credentials: true
50
+ }));
51
+
52
+ export default app;
53
+ ```
54
+
55
+ ### Features
56
+
57
+ - Automatic ServiceError handling with proper HTTP status codes
58
+ - 404 handling for undefined routes
59
+ - X-Powered-By header set to Metorial
60
+ - Optional base path prefix
61
+ - CORS middleware included
62
+
63
+ ## License
64
+
65
+ This project is licensed under the Apache License 2.0.
66
+
67
+ <div align="center">
68
+ <sub>Built with ❤️ by <a href="https://metorial.com">Metorial</a></sub>
69
+ </div>
@@ -0,0 +1,6 @@
1
+ import { Context } from 'hono';
2
+ export declare let useRequestContext: (c: Context) => {
3
+ ua: string | undefined;
4
+ ip: string;
5
+ };
6
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,eAAO,IAAI,iBAAiB,GAAI,GAAG,OAAO;;;CAWzC,CAAC"}
package/dist/hono.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Context, Env, Hono } from 'hono';
2
+ import { cors } from 'hono/cors';
3
+ export { cors, type Context };
4
+ export declare let createHono: <E extends Env>(basePath?: string) => Hono<E, import("hono/types").BlankSchema, "/">;
5
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../src/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;AAE9B,eAAO,IAAI,UAAU,GAAI,CAAC,SAAS,GAAG,EAAE,WAAW,MAAM,mDAyBxD,CAAC"}
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ var r=require("@lowerdeck/forwarded-for"),e=require("@lowerdeck/error"),o=require("hono"),n=require("hono/cors");Object.defineProperty(exports,"cors",{enumerable:!0,get:function(){return n.cors}}),exports.createHono=function(r){var n=new o.Hono;return r&&(n=n.basePath(r)),n.use(function(r,e){try{return Promise.resolve(e()).then(function(){r.res.headers.set("X-Powered-By","Metorial")})}catch(r){return Promise.reject(r)}}),n.notFound(function(r){return r.json(e.notFoundError("endpoint",null).toResponse(),404)}),n.onError(function(r,o){return e.isServiceError(r)?o.json(r.toResponse(),r.data.status):(console.error(r),o.json(e.internalServerError().toResponse(),500))}),n},exports.useRequestContext=function(e){var o,n,t,s;return{ua:e.req.header("user-agent"),ip:null!=(o=r.parseForwardedFor(null!=(n=null!=(t=null!=(s=e.req.header("metorial-connecting-ip"))?s:e.req.header("cf-connecting-ip"))?t:e.req.header("x-forwarded-for"))?n:e.req.header("x-real-ip")))?o:"0.0.0.0"}},exports.useValidatedBody=function(r,o){try{var n,t=function(r){var t=o.validate(n);if(!t.success)throw new e.ServiceError(e.validationError({entity:"body",errors:t.errors}));return t.value},s=null!=(a=r.req.header("Content-Type"))&&a.includes("application/x-www-form-urlencoded")?Promise.resolve(r.req.text()).then(function(r){n=Object.fromEntries(new URLSearchParams(r))}):function(r,e){try{var o=r()}catch(r){return e()}return o&&o.then?o.then(void 0,e):o}(function(){return Promise.resolve(r.req.json()).then(function(r){n=r})},function(){throw new e.ServiceError(e.badRequestError({message:"Invalid JSON body"}))});return Promise.resolve(s&&s.then?s.then(t):t())}catch(r){return Promise.reject(r)}var a},exports.useValidatedQuery=function(r,o){try{var n;try{n=r.req.query()}catch(r){throw new e.ServiceError(e.badRequestError({message:"Invalid JSON body"}))}var t=o.validate(n);if(!t.success)throw new e.ServiceError(e.validationError({entity:"query",errors:t.errors}));return Promise.resolve(t.value)}catch(r){return Promise.reject(r)}};
2
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/hono.ts","../src/context.ts","../src/validation.ts"],"sourcesContent":["import { internalServerError, isServiceError, notFoundError } from '@lowerdeck/error';\nimport { Context, Env, Hono } from 'hono';\nimport { cors } from 'hono/cors';\n\nexport { cors, type Context };\n\nexport let createHono = <E extends Env>(basePath?: string) => {\n let app = new Hono<E>();\n if (basePath) app = app.basePath(basePath);\n\n app.use(async (c, next) => {\n await next();\n\n c.res.headers.set('X-Powered-By', 'Metorial');\n });\n\n app.notFound(c => {\n return c.json(notFoundError('endpoint', null).toResponse(), 404);\n });\n\n app.onError((e, c) => {\n if (isServiceError(e)) {\n return c.json(e.toResponse(), e.data.status);\n }\n\n console.error(e);\n\n return c.json(internalServerError().toResponse(), 500);\n });\n\n return app;\n};\n","import { parseForwardedFor } from '@lowerdeck/forwarded-for';\nimport { Context } from 'hono';\n\nexport let useRequestContext = (c: Context) => {\n let ua = c.req.header('user-agent');\n let ip =\n parseForwardedFor(\n c.req.header('metorial-connecting-ip') ??\n c.req.header('cf-connecting-ip') ??\n c.req.header('x-forwarded-for') ??\n c.req.header('x-real-ip')\n ) ?? '0.0.0.0';\n\n return { ua, ip };\n};\n","import { badRequestError, ServiceError, validationError } from '@lowerdeck/error';\nimport { ValidationType } from '@lowerdeck/validation';\nimport { Context } from 'hono';\n\nexport let useValidatedBody = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {\n let body: any;\n\n if (c.req.header('Content-Type')?.includes('application/x-www-form-urlencoded')) {\n body = Object.fromEntries(new URLSearchParams(await c.req.text()));\n } else {\n try {\n body = await c.req.json();\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid JSON body'\n })\n );\n }\n }\n\n let val = v.validate(body);\n if (!val.success) {\n throw new ServiceError(\n validationError({\n entity: 'body',\n errors: val.errors\n })\n );\n }\n\n return val.value;\n};\n\nexport let useValidatedQuery = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {\n let body: any;\n\n try {\n body = c.req.query();\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid JSON body'\n })\n );\n }\n\n let val = v.validate(body);\n if (!val.success) {\n throw new ServiceError(\n validationError({\n entity: 'query',\n errors: val.errors\n })\n );\n }\n\n return val.value;\n};\n"],"names":["basePath","app","Hono","use","c","next","Promise","resolve","then","res","headers","set","e","reject","notFound","json","notFoundError","toResponse","onError","isServiceError","data","status","console","error","internalServerError","_parseForwardedFor","_ref","_ref2","_c$req$header","ua","req","header","ip","parseForwardedFor","v","_temp2","body","_result","val","validate","success","ServiceError","validationError","entity","errors","value","_temp","includes","text","_c$req$text","Object","fromEntries","URLSearchParams","_catch","_c$req$json","badRequestError","message","query"],"mappings":"wNAMwB,SAAgBA,GACtC,IAAIC,EAAM,IAAIC,EAAAA,KAuBd,OAtBIF,IAAUC,EAAMA,EAAID,SAASA,IAEjCC,EAAIE,IAAWC,SAAAA,EAAGC,GAAI,WAAIC,QAAAC,QAClBF,KAAMG,KAEZJ,WAAAA,EAAEK,IAAIC,QAAQC,IAAI,eAAgB,WAAY,EAChD,CAAC,MAAAC,UAAAN,QAAAO,OAAAD,EAAA,CAAA,GAEDX,EAAIa,SAAS,SAAAV,GACX,OAAOA,EAAEW,KAAKC,EAAAA,cAAc,WAAY,MAAMC,aAAc,IAC9D,GAEAhB,EAAIiB,QAAQ,SAACN,EAAGR,GACd,OAAIe,iBAAeP,GACVR,EAAEW,KAAKH,EAAEK,aAAcL,EAAEQ,KAAKC,SAGvCC,QAAQC,MAAMX,GAEPR,EAAEW,KAAKS,wBAAsBP,aAAc,KACpD,GAEOhB,CACT,4BC5B+B,SAACG,GAAcqB,IAAAA,EAAAC,EAAAC,EAAAC,EAU5C,MAAO,CAAEC,GATAzB,EAAE0B,IAAIC,OAAO,cASTC,GAFV,OANGP,EACJQ,EAAAA,kBAGmC,OAHlBP,EAEmB,OAFnBC,EACuBC,OADvBA,EACfxB,EAAE0B,IAAIC,OAAO,2BAAyBH,EACpCxB,EAAE0B,IAAIC,OAAO,qBAAmBJ,EAChCvB,EAAE0B,IAAIC,OAAO,oBAAkBL,EAC/BtB,EAAE0B,IAAIC,OAAO,eAChBN,EAAI,UAGT,2BCVW,SAA6BrB,EAAY8B,OAAoCC,IAClFC,EADkFD,WAAAE,GAiBtF,IAAIC,EAAMJ,EAAEK,SAASH,GACrB,IAAKE,EAAIE,QACP,MAAU,IAAAC,eACRC,EAAAA,gBAAgB,CACdC,OAAQ,OACRC,OAAQN,EAAIM,UAKlB,OAAON,EAAIO,KAAM,EA1BHC,EAEkB,OAFlBlB,EAEVxB,EAAE0B,IAAIC,OAAO,kBAAbH,EAA8BmB,SAAS,qCAAoCzC,QAAAC,QACzBH,EAAE0B,IAAIkB,QAAMxC,cAAAyC,GAAhEb,EAAOc,OAAOC,YAAY,IAAIC,gBAAeH,GAAsB,uFAAAI,CAE/D,WAAA,OAAA/C,QAAAC,QACWH,EAAE0B,IAAIf,QAAMP,KAAA,SAAA8C,GAAzBlB,EAAIkB,CAAsB,EAC5B,EAAC,WACC,MAAM,IAAIb,EAAYA,aACpBc,kBAAgB,CACdC,QAAS,sBAGf,GAAC,OAAAlD,QAAAC,QAAAuC,GAAAA,EAAAtC,KAAAsC,EAAAtC,KAAA2B,GAAAA,IAcL,CAAC,MAAAvB,UAAAN,QAAAO,OAAAD,GA3BegB,IAAAA,6BA6BY,SAAaxB,EAAY8B,OACnD,IAAIE,EAEJ,IACEA,EAAOhC,EAAE0B,IAAI2B,OACf,CAAE,MAAO7C,GACP,UAAU6B,EAAYA,aACpBc,kBAAgB,CACdC,QAAS,sBAGf,CAEA,IAAIlB,EAAMJ,EAAEK,SAASH,GACrB,IAAKE,EAAIE,QACP,MAAM,IAAIC,EAAAA,aACRC,EAAeA,gBAAC,CACdC,OAAQ,QACRC,OAAQN,EAAIM,UAKlB,OAAAtC,QAAAC,QAAO+B,EAAIO,MACb,CAAC,MAAAjC,GAAA,OAAAN,QAAAO,OAAAD,EAAA,CAAA"}
@@ -0,0 +1,4 @@
1
+ export * from './context';
2
+ export * from './hono';
3
+ export * from './validation';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC"}
@@ -0,0 +1,2 @@
1
+ import{parseForwardedFor as e}from"@lowerdeck/forwarded-for";import{notFoundError as r,isServiceError as o,internalServerError as t,ServiceError as n,badRequestError as a,validationError as s}from"@lowerdeck/error";import{Hono as l}from"hono";export{cors}from"hono/cors";let i=r=>{var o,t,n,a;return{ua:r.req.header("user-agent"),ip:null!=(o=e(null!=(t=null!=(n=null!=(a=r.req.header("metorial-connecting-ip"))?a:r.req.header("cf-connecting-ip"))?n:r.req.header("x-forwarded-for"))?t:r.req.header("x-real-ip")))?o:"0.0.0.0"}},d=e=>{let n=new l;return e&&(n=n.basePath(e)),n.use(async(e,r)=>{await r(),e.res.headers.set("X-Powered-By","Metorial")}),n.notFound(e=>e.json(r("endpoint",null).toResponse(),404)),n.onError((e,r)=>o(e)?r.json(e.toResponse(),e.data.status):(console.error(e),r.json(t().toResponse(),500))),n},c=async(e,r)=>{var o;let t;if(null!=(o=e.req.header("Content-Type"))&&o.includes("application/x-www-form-urlencoded"))t=Object.fromEntries(new URLSearchParams(await e.req.text()));else try{t=await e.req.json()}catch(e){throw new n(a({message:"Invalid JSON body"}))}let l=r.validate(t);if(!l.success)throw new n(s({entity:"body",errors:l.errors}));return l.value},u=async(e,r)=>{let o;try{o=e.req.query()}catch(e){throw new n(a({message:"Invalid JSON body"}))}let t=r.validate(o);if(!t.success)throw new n(s({entity:"query",errors:t.errors}));return t.value};export{d as createHono,i as useRequestContext,c as useValidatedBody,u as useValidatedQuery};
2
+ //# sourceMappingURL=index.modern.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.modern.js","sources":["../src/context.ts","../src/hono.ts","../src/validation.ts"],"sourcesContent":["import { parseForwardedFor } from '@lowerdeck/forwarded-for';\nimport { Context } from 'hono';\n\nexport let useRequestContext = (c: Context) => {\n let ua = c.req.header('user-agent');\n let ip =\n parseForwardedFor(\n c.req.header('metorial-connecting-ip') ??\n c.req.header('cf-connecting-ip') ??\n c.req.header('x-forwarded-for') ??\n c.req.header('x-real-ip')\n ) ?? '0.0.0.0';\n\n return { ua, ip };\n};\n","import { internalServerError, isServiceError, notFoundError } from '@lowerdeck/error';\nimport { Context, Env, Hono } from 'hono';\nimport { cors } from 'hono/cors';\n\nexport { cors, type Context };\n\nexport let createHono = <E extends Env>(basePath?: string) => {\n let app = new Hono<E>();\n if (basePath) app = app.basePath(basePath);\n\n app.use(async (c, next) => {\n await next();\n\n c.res.headers.set('X-Powered-By', 'Metorial');\n });\n\n app.notFound(c => {\n return c.json(notFoundError('endpoint', null).toResponse(), 404);\n });\n\n app.onError((e, c) => {\n if (isServiceError(e)) {\n return c.json(e.toResponse(), e.data.status);\n }\n\n console.error(e);\n\n return c.json(internalServerError().toResponse(), 500);\n });\n\n return app;\n};\n","import { badRequestError, ServiceError, validationError } from '@lowerdeck/error';\nimport { ValidationType } from '@lowerdeck/validation';\nimport { Context } from 'hono';\n\nexport let useValidatedBody = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {\n let body: any;\n\n if (c.req.header('Content-Type')?.includes('application/x-www-form-urlencoded')) {\n body = Object.fromEntries(new URLSearchParams(await c.req.text()));\n } else {\n try {\n body = await c.req.json();\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid JSON body'\n })\n );\n }\n }\n\n let val = v.validate(body);\n if (!val.success) {\n throw new ServiceError(\n validationError({\n entity: 'body',\n errors: val.errors\n })\n );\n }\n\n return val.value;\n};\n\nexport let useValidatedQuery = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {\n let body: any;\n\n try {\n body = c.req.query();\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid JSON body'\n })\n );\n }\n\n let val = v.validate(body);\n if (!val.success) {\n throw new ServiceError(\n validationError({\n entity: 'query',\n errors: val.errors\n })\n );\n }\n\n return val.value;\n};\n"],"names":["useRequestContext","c","_parseForwardedFor","_ref","_ref2","_c$req$header","ua","req","header","ip","parseForwardedFor","createHono","basePath","app","Hono","use","async","next","res","headers","set","notFound","json","notFoundError","toResponse","onError","e","isServiceError","data","status","console","error","internalServerError","useValidatedBody","v","body","includes","Object","fromEntries","URLSearchParams","text","ServiceError","badRequestError","message","val","validate","success","validationError","entity","errors","value","useValidatedQuery","query"],"mappings":"+QAGW,IAAAA,EAAqBC,QAAcC,EAAAC,EAAAC,EAAAC,EAU5C,MAAO,CAAEC,GATAL,EAAEM,IAAIC,OAAO,cASTC,UARPP,EACJQ,EAGmCP,OAHlBA,SAAAC,EACuB,OADvBC,EACfJ,EAAEM,IAAIC,OAAO,2BAAyBH,EACpCJ,EAAEM,IAAIC,OAAO,qBAAmBJ,EAChCH,EAAEM,IAAIC,OAAO,oBAAkBL,EAC/BF,EAAEM,IAAIC,OAAO,eAChBN,EAAI,YCLES,EAA6BC,IACtC,IAAIC,EAAM,IAAIC,EAuBd,OAtBIF,IAAUC,EAAMA,EAAID,SAASA,IAEjCC,EAAIE,IAAIC,MAAOf,EAAGgB,WACVA,IAENhB,EAAEiB,IAAIC,QAAQC,IAAI,eAAgB,cAGpCP,EAAIQ,SAASpB,GACJA,EAAEqB,KAAKC,EAAc,WAAY,MAAMC,aAAc,MAG9DX,EAAIY,QAAQ,CAACC,EAAGzB,IACV0B,EAAeD,GACVzB,EAAEqB,KAAKI,EAAEF,aAAcE,EAAEE,KAAKC,SAGvCC,QAAQC,MAAML,GAEPzB,EAAEqB,KAAKU,IAAsBR,aAAc,OAG7CX,GC1BEoB,EAAmBjB,MAAUf,EAAYiC,KAAoC,IAAA7B,EACtF,IAAI8B,EAEJ,GAAI9B,OAAJA,EAAIJ,EAAEM,IAAIC,OAAO,kBAAbH,EAA8B+B,SAAS,qCACzCD,EAAOE,OAAOC,YAAY,IAAIC,sBAAsBtC,EAAEM,IAAIiC,cAE1D,IACEL,QAAalC,EAAEM,IAAIe,MACrB,CAAE,MAAOI,GACP,UAAUe,EACRC,EAAgB,CACdC,QAAS,sBAGf,CAGF,IAAIC,EAAMV,EAAEW,SAASV,GACrB,IAAKS,EAAIE,QACP,MAAU,IAAAL,EACRM,EAAgB,CACdC,OAAQ,OACRC,OAAQL,EAAIK,UAKlB,OAAOL,EAAIM,OAGFC,EAAoBnC,MAAUf,EAAYiC,KACnD,IAAIC,EAEJ,IACEA,EAAOlC,EAAEM,IAAI6C,OACf,CAAE,MAAO1B,GACP,MAAM,IAAIe,EACRC,EAAgB,CACdC,QAAS,sBAGf,CAEA,IAAIC,EAAMV,EAAEW,SAASV,GACrB,IAAKS,EAAIE,QACP,MAAM,IAAIL,EACRM,EAAgB,CACdC,OAAQ,QACRC,OAAQL,EAAIK,UAKlB,OAAOL,EAAIM"}
@@ -0,0 +1,2 @@
1
+ import{parseForwardedFor as e}from"@lowerdeck/forwarded-for";import{notFoundError as r,isServiceError as n,internalServerError as o,ServiceError as t,validationError as s,badRequestError as a}from"@lowerdeck/error";import{Hono as i}from"hono";export{cors}from"hono/cors";var u=function(r){var n,o,t,s;return{ua:r.req.header("user-agent"),ip:null!=(n=e(null!=(o=null!=(t=null!=(s=r.req.header("metorial-connecting-ip"))?s:r.req.header("cf-connecting-ip"))?t:r.req.header("x-forwarded-for"))?o:r.req.header("x-real-ip")))?n:"0.0.0.0"}},c=function(e){var t=new i;return e&&(t=t.basePath(e)),t.use(function(e,r){try{return Promise.resolve(r()).then(function(){e.res.headers.set("X-Powered-By","Metorial")})}catch(e){return Promise.reject(e)}}),t.notFound(function(e){return e.json(r("endpoint",null).toResponse(),404)}),t.onError(function(e,r){return n(e)?r.json(e.toResponse(),e.data.status):(console.error(e),r.json(o().toResponse(),500))}),t},l=function(e,r){try{var n,o=function(e){var o=r.validate(n);if(!o.success)throw new t(s({entity:"body",errors:o.errors}));return o.value},i=null!=(u=e.req.header("Content-Type"))&&u.includes("application/x-www-form-urlencoded")?Promise.resolve(e.req.text()).then(function(e){n=Object.fromEntries(new URLSearchParams(e))}):function(e,r){try{var n=e()}catch(e){return r()}return n&&n.then?n.then(void 0,r):n}(function(){return Promise.resolve(e.req.json()).then(function(e){n=e})},function(){throw new t(a({message:"Invalid JSON body"}))});return Promise.resolve(i&&i.then?i.then(o):o())}catch(e){return Promise.reject(e)}var u},d=function(e,r){try{var n;try{n=e.req.query()}catch(e){throw new t(a({message:"Invalid JSON body"}))}var o=r.validate(n);if(!o.success)throw new t(s({entity:"query",errors:o.errors}));return Promise.resolve(o.value)}catch(e){return Promise.reject(e)}};export{c as createHono,u as useRequestContext,l as useValidatedBody,d as useValidatedQuery};
2
+ //# sourceMappingURL=index.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.module.js","sources":["../src/context.ts","../src/hono.ts","../src/validation.ts"],"sourcesContent":["import { parseForwardedFor } from '@lowerdeck/forwarded-for';\nimport { Context } from 'hono';\n\nexport let useRequestContext = (c: Context) => {\n let ua = c.req.header('user-agent');\n let ip =\n parseForwardedFor(\n c.req.header('metorial-connecting-ip') ??\n c.req.header('cf-connecting-ip') ??\n c.req.header('x-forwarded-for') ??\n c.req.header('x-real-ip')\n ) ?? '0.0.0.0';\n\n return { ua, ip };\n};\n","import { internalServerError, isServiceError, notFoundError } from '@lowerdeck/error';\nimport { Context, Env, Hono } from 'hono';\nimport { cors } from 'hono/cors';\n\nexport { cors, type Context };\n\nexport let createHono = <E extends Env>(basePath?: string) => {\n let app = new Hono<E>();\n if (basePath) app = app.basePath(basePath);\n\n app.use(async (c, next) => {\n await next();\n\n c.res.headers.set('X-Powered-By', 'Metorial');\n });\n\n app.notFound(c => {\n return c.json(notFoundError('endpoint', null).toResponse(), 404);\n });\n\n app.onError((e, c) => {\n if (isServiceError(e)) {\n return c.json(e.toResponse(), e.data.status);\n }\n\n console.error(e);\n\n return c.json(internalServerError().toResponse(), 500);\n });\n\n return app;\n};\n","import { badRequestError, ServiceError, validationError } from '@lowerdeck/error';\nimport { ValidationType } from '@lowerdeck/validation';\nimport { Context } from 'hono';\n\nexport let useValidatedBody = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {\n let body: any;\n\n if (c.req.header('Content-Type')?.includes('application/x-www-form-urlencoded')) {\n body = Object.fromEntries(new URLSearchParams(await c.req.text()));\n } else {\n try {\n body = await c.req.json();\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid JSON body'\n })\n );\n }\n }\n\n let val = v.validate(body);\n if (!val.success) {\n throw new ServiceError(\n validationError({\n entity: 'body',\n errors: val.errors\n })\n );\n }\n\n return val.value;\n};\n\nexport let useValidatedQuery = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {\n let body: any;\n\n try {\n body = c.req.query();\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid JSON body'\n })\n );\n }\n\n let val = v.validate(body);\n if (!val.success) {\n throw new ServiceError(\n validationError({\n entity: 'query',\n errors: val.errors\n })\n );\n }\n\n return val.value;\n};\n"],"names":["useRequestContext","c","_parseForwardedFor","_ref","_ref2","_c$req$header","ua","req","header","ip","parseForwardedFor","createHono","basePath","app","Hono","use","next","Promise","resolve","then","res","headers","set","e","reject","notFound","json","notFoundError","toResponse","onError","isServiceError","data","status","console","error","internalServerError","useValidatedBody","v","_temp2","body","_result","val","validate","success","ServiceError","validationError","entity","errors","value","_temp","includes","text","_c$req$text","Object","fromEntries","URLSearchParams","_catch","_c$req$json","badRequestError","message","useValidatedQuery","query"],"mappings":"+QAGW,IAAAA,EAAoB,SAACC,GAAcC,IAAAA,EAAAC,EAAAC,EAAAC,EAU5C,MAAO,CAAEC,GATAL,EAAEM,IAAIC,OAAO,cASTC,GAFV,OANGP,EACJQ,EAGmC,OAHlBP,EAEmB,OAFnBC,EACuBC,OADvBA,EACfJ,EAAEM,IAAIC,OAAO,2BAAyBH,EACpCJ,EAAEM,IAAIC,OAAO,qBAAmBJ,EAChCH,EAAEM,IAAIC,OAAO,oBAAkBL,EAC/BF,EAAEM,IAAIC,OAAO,eAChBN,EAAI,UAGT,ECRWS,EAAa,SAAgBC,GACtC,IAAIC,EAAM,IAAIC,EAuBd,OAtBIF,IAAUC,EAAMA,EAAID,SAASA,IAEjCC,EAAIE,IAAWd,SAAAA,EAAGe,GAAI,WAAIC,QAAAC,QAClBF,KAAMG,KAEZlB,WAAAA,EAAEmB,IAAIC,QAAQC,IAAI,eAAgB,WAAY,EAChD,CAAC,MAAAC,UAAAN,QAAAO,OAAAD,EAAA,CAAA,GAEDV,EAAIY,SAAS,SAAAxB,GACX,OAAOA,EAAEyB,KAAKC,EAAc,WAAY,MAAMC,aAAc,IAC9D,GAEAf,EAAIgB,QAAQ,SAACN,EAAGtB,GACd,OAAI6B,EAAeP,GACVtB,EAAEyB,KAAKH,EAAEK,aAAcL,EAAEQ,KAAKC,SAGvCC,QAAQC,MAAMX,GAEPtB,EAAEyB,KAAKS,IAAsBP,aAAc,KACpD,GAEOf,CACT,EC3BWuB,EAAA,SAA6BnC,EAAYoC,OAAoCC,IAClFC,EADkFD,WAAAE,GAiBtF,IAAIC,EAAMJ,EAAEK,SAASH,GACrB,IAAKE,EAAIE,QACP,MAAU,IAAAC,EACRC,EAAgB,CACdC,OAAQ,OACRC,OAAQN,EAAIM,UAKlB,OAAON,EAAIO,KAAM,EA1BHC,EAEkB,OAFlB5C,EAEVJ,EAAEM,IAAIC,OAAO,kBAAbH,EAA8B6C,SAAS,qCAAoCjC,QAAAC,QACzBjB,EAAEM,IAAI4C,QAAMhC,cAAAiC,GAAhEb,EAAOc,OAAOC,YAAY,IAAIC,gBAAeH,GAAsB,uFAAAI,CAE/D,WAAA,OAAAvC,QAAAC,QACWjB,EAAEM,IAAImB,QAAMP,KAAA,SAAAsC,GAAzBlB,EAAIkB,CAAsB,EAC5B,EAAC,WACC,MAAM,IAAIb,EACRc,EAAgB,CACdC,QAAS,sBAGf,GAAC,OAAA1C,QAAAC,QAAA+B,GAAAA,EAAA9B,KAAA8B,EAAA9B,KAAAmB,GAAAA,IAcL,CAAC,MAAAf,UAAAN,QAAAO,OAAAD,GA3BelB,IAAAA,GA6BLuD,EAAiB,SAAa3D,EAAYoC,OACnD,IAAIE,EAEJ,IACEA,EAAOtC,EAAEM,IAAIsD,OACf,CAAE,MAAOtC,GACP,UAAUqB,EACRc,EAAgB,CACdC,QAAS,sBAGf,CAEA,IAAIlB,EAAMJ,EAAEK,SAASH,GACrB,IAAKE,EAAIE,QACP,MAAM,IAAIC,EACRC,EAAgB,CACdC,OAAQ,QACRC,OAAQN,EAAIM,UAKlB,OAAA9B,QAAAC,QAAOuB,EAAIO,MACb,CAAC,MAAAzB,GAAA,OAAAN,QAAAO,OAAAD,EAAA,CAAA"}
@@ -0,0 +1,2 @@
1
+ !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("@lowerdeck/forwarded-for"),require("@lowerdeck/error"),require("hono"),require("hono/cors")):"function"==typeof define&&define.amd?define(["exports","@lowerdeck/forwarded-for","@lowerdeck/error","hono","hono/cors"],r):r((e||self).hono={},e.forwardedFor,e.error,e.hono,e.cors)}(this,function(e,r,o,n,t){Object.defineProperty(e,"cors",{enumerable:!0,get:function(){return t.cors}}),e.createHono=function(e){var r=new n.Hono;return e&&(r=r.basePath(e)),r.use(function(e,r){try{return Promise.resolve(r()).then(function(){e.res.headers.set("X-Powered-By","Metorial")})}catch(e){return Promise.reject(e)}}),r.notFound(function(e){return e.json(o.notFoundError("endpoint",null).toResponse(),404)}),r.onError(function(e,r){return o.isServiceError(e)?r.json(e.toResponse(),e.data.status):(console.error(e),r.json(o.internalServerError().toResponse(),500))}),r},e.useRequestContext=function(e){var o,n,t,i;return{ua:e.req.header("user-agent"),ip:null!=(o=r.parseForwardedFor(null!=(n=null!=(t=null!=(i=e.req.header("metorial-connecting-ip"))?i:e.req.header("cf-connecting-ip"))?t:e.req.header("x-forwarded-for"))?n:e.req.header("x-real-ip")))?o:"0.0.0.0"}},e.useValidatedBody=function(e,r){try{var n,t=function(e){var t=r.validate(n);if(!t.success)throw new o.ServiceError(o.validationError({entity:"body",errors:t.errors}));return t.value},i=null!=(s=e.req.header("Content-Type"))&&s.includes("application/x-www-form-urlencoded")?Promise.resolve(e.req.text()).then(function(e){n=Object.fromEntries(new URLSearchParams(e))}):function(e,r){try{var o=e()}catch(e){return r()}return o&&o.then?o.then(void 0,r):o}(function(){return Promise.resolve(e.req.json()).then(function(e){n=e})},function(){throw new o.ServiceError(o.badRequestError({message:"Invalid JSON body"}))});return Promise.resolve(i&&i.then?i.then(t):t())}catch(e){return Promise.reject(e)}var s},e.useValidatedQuery=function(e,r){try{var n;try{n=e.req.query()}catch(e){throw new o.ServiceError(o.badRequestError({message:"Invalid JSON body"}))}var t=r.validate(n);if(!t.success)throw new o.ServiceError(o.validationError({entity:"query",errors:t.errors}));return Promise.resolve(t.value)}catch(e){return Promise.reject(e)}}});
2
+ //# sourceMappingURL=index.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.umd.js","sources":["../src/hono.ts","../src/context.ts","../src/validation.ts"],"sourcesContent":["import { internalServerError, isServiceError, notFoundError } from '@lowerdeck/error';\nimport { Context, Env, Hono } from 'hono';\nimport { cors } from 'hono/cors';\n\nexport { cors, type Context };\n\nexport let createHono = <E extends Env>(basePath?: string) => {\n let app = new Hono<E>();\n if (basePath) app = app.basePath(basePath);\n\n app.use(async (c, next) => {\n await next();\n\n c.res.headers.set('X-Powered-By', 'Metorial');\n });\n\n app.notFound(c => {\n return c.json(notFoundError('endpoint', null).toResponse(), 404);\n });\n\n app.onError((e, c) => {\n if (isServiceError(e)) {\n return c.json(e.toResponse(), e.data.status);\n }\n\n console.error(e);\n\n return c.json(internalServerError().toResponse(), 500);\n });\n\n return app;\n};\n","import { parseForwardedFor } from '@lowerdeck/forwarded-for';\nimport { Context } from 'hono';\n\nexport let useRequestContext = (c: Context) => {\n let ua = c.req.header('user-agent');\n let ip =\n parseForwardedFor(\n c.req.header('metorial-connecting-ip') ??\n c.req.header('cf-connecting-ip') ??\n c.req.header('x-forwarded-for') ??\n c.req.header('x-real-ip')\n ) ?? '0.0.0.0';\n\n return { ua, ip };\n};\n","import { badRequestError, ServiceError, validationError } from '@lowerdeck/error';\nimport { ValidationType } from '@lowerdeck/validation';\nimport { Context } from 'hono';\n\nexport let useValidatedBody = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {\n let body: any;\n\n if (c.req.header('Content-Type')?.includes('application/x-www-form-urlencoded')) {\n body = Object.fromEntries(new URLSearchParams(await c.req.text()));\n } else {\n try {\n body = await c.req.json();\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid JSON body'\n })\n );\n }\n }\n\n let val = v.validate(body);\n if (!val.success) {\n throw new ServiceError(\n validationError({\n entity: 'body',\n errors: val.errors\n })\n );\n }\n\n return val.value;\n};\n\nexport let useValidatedQuery = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {\n let body: any;\n\n try {\n body = c.req.query();\n } catch (e) {\n throw new ServiceError(\n badRequestError({\n message: 'Invalid JSON body'\n })\n );\n }\n\n let val = v.validate(body);\n if (!val.success) {\n throw new ServiceError(\n validationError({\n entity: 'query',\n errors: val.errors\n })\n );\n }\n\n return val.value;\n};\n"],"names":["basePath","app","Hono","use","c","next","Promise","resolve","then","res","headers","set","e","reject","notFound","json","notFoundError","toResponse","onError","isServiceError","data","status","console","error","internalServerError","_parseForwardedFor","_ref","_ref2","_c$req$header","ua","req","header","ip","parseForwardedFor","v","_temp2","body","_result","val","validate","success","ServiceError","validationError","entity","errors","value","_temp","includes","text","_c$req$text","Object","fromEntries","URLSearchParams","_catch","_c$req$json","badRequestError","message","query"],"mappings":"4gBAMwB,SAAgBA,GACtC,IAAIC,EAAM,IAAIC,EAAAA,KAuBd,OAtBIF,IAAUC,EAAMA,EAAID,SAASA,IAEjCC,EAAIE,IAAWC,SAAAA,EAAGC,GAAI,WAAIC,QAAAC,QAClBF,KAAMG,KAEZJ,WAAAA,EAAEK,IAAIC,QAAQC,IAAI,eAAgB,WAAY,EAChD,CAAC,MAAAC,UAAAN,QAAAO,OAAAD,EAAA,CAAA,GAEDX,EAAIa,SAAS,SAAAV,GACX,OAAOA,EAAEW,KAAKC,EAAAA,cAAc,WAAY,MAAMC,aAAc,IAC9D,GAEAhB,EAAIiB,QAAQ,SAACN,EAAGR,GACd,OAAIe,iBAAeP,GACVR,EAAEW,KAAKH,EAAEK,aAAcL,EAAEQ,KAAKC,SAGvCC,QAAQC,MAAMX,GAEPR,EAAEW,KAAKS,wBAAsBP,aAAc,KACpD,GAEOhB,CACT,sBC5B+B,SAACG,GAAcqB,IAAAA,EAAAC,EAAAC,EAAAC,EAU5C,MAAO,CAAEC,GATAzB,EAAE0B,IAAIC,OAAO,cASTC,GAFV,OANGP,EACJQ,EAAAA,kBAGmC,OAHlBP,EAEmB,OAFnBC,EACuBC,OADvBA,EACfxB,EAAE0B,IAAIC,OAAO,2BAAyBH,EACpCxB,EAAE0B,IAAIC,OAAO,qBAAmBJ,EAChCvB,EAAE0B,IAAIC,OAAO,oBAAkBL,EAC/BtB,EAAE0B,IAAIC,OAAO,eAChBN,EAAI,UAGT,qBCVW,SAA6BrB,EAAY8B,OAAoCC,IAClFC,EADkFD,WAAAE,GAiBtF,IAAIC,EAAMJ,EAAEK,SAASH,GACrB,IAAKE,EAAIE,QACP,MAAU,IAAAC,eACRC,EAAAA,gBAAgB,CACdC,OAAQ,OACRC,OAAQN,EAAIM,UAKlB,OAAON,EAAIO,KAAM,EA1BHC,EAEkB,OAFlBlB,EAEVxB,EAAE0B,IAAIC,OAAO,kBAAbH,EAA8BmB,SAAS,qCAAoCzC,QAAAC,QACzBH,EAAE0B,IAAIkB,QAAMxC,cAAAyC,GAAhEb,EAAOc,OAAOC,YAAY,IAAIC,gBAAeH,GAAsB,uFAAAI,CAE/D,WAAA,OAAA/C,QAAAC,QACWH,EAAE0B,IAAIf,QAAMP,KAAA,SAAA8C,GAAzBlB,EAAIkB,CAAsB,EAC5B,EAAC,WACC,MAAM,IAAIb,EAAYA,aACpBc,kBAAgB,CACdC,QAAS,sBAGf,GAAC,OAAAlD,QAAAC,QAAAuC,GAAAA,EAAAtC,KAAAsC,EAAAtC,KAAA2B,GAAAA,IAcL,CAAC,MAAAvB,UAAAN,QAAAO,OAAAD,GA3BegB,IAAAA,uBA6BY,SAAaxB,EAAY8B,OACnD,IAAIE,EAEJ,IACEA,EAAOhC,EAAE0B,IAAI2B,OACf,CAAE,MAAO7C,GACP,UAAU6B,EAAYA,aACpBc,kBAAgB,CACdC,QAAS,sBAGf,CAEA,IAAIlB,EAAMJ,EAAEK,SAASH,GACrB,IAAKE,EAAIE,QACP,MAAM,IAAIC,EAAAA,aACRC,EAAeA,gBAAC,CACdC,OAAQ,QACRC,OAAQN,EAAIM,UAKlB,OAAAtC,QAAAC,QAAO+B,EAAIO,MACb,CAAC,MAAAjC,GAAA,OAAAN,QAAAO,OAAAD,EAAA,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { ValidationType } from '@lowerdeck/validation';
2
+ import { Context } from 'hono';
3
+ export declare let useValidatedBody: <T>(c: Context, v: ValidationType<T>) => Promise<T>;
4
+ export declare let useValidatedQuery: <T>(c: Context, v: ValidationType<T>) => Promise<T>;
5
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,eAAO,IAAI,gBAAgB,GAAU,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CA4BnF,CAAC;AAEF,eAAO,IAAI,iBAAiB,GAAU,CAAC,EAAE,GAAG,OAAO,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAwBpF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@lowerdeck/hono",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "author": "Tobias Herber",
8
+ "license": "Apache 2",
9
+ "type": "module",
10
+ "source": "src/index.ts",
11
+ "exports": {
12
+ "require": "./dist/index.cjs",
13
+ "default": "./dist/index.modern.js"
14
+ },
15
+ "main": "./dist/index.cjs",
16
+ "module": "./dist/index.module.js",
17
+ "types": "dist/index.d.ts",
18
+ "unpkg": "./dist/index.umd.js",
19
+ "scripts": {
20
+ "test": "vitest run --passWithNoTests",
21
+ "lint": "prettier src/**/*.ts --check",
22
+ "build": "microbundle"
23
+ },
24
+ "dependencies": {
25
+ "hono": "^4.5.5",
26
+ "@lowerdeck/forwarded-for": "^1.0.0",
27
+ "@lowerdeck/validation": "^1.0.0",
28
+ "@lowerdeck/error": "^1.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "microbundle": "^0.15.1",
32
+ "@lowerdeck/tsconfig": "^1.0.0",
33
+ "typescript": "5.8.2",
34
+ "vitest": "^3.1.2"
35
+ }
36
+ }
package/src/context.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { parseForwardedFor } from '@lowerdeck/forwarded-for';
2
+ import { Context } from 'hono';
3
+
4
+ export let useRequestContext = (c: Context) => {
5
+ let ua = c.req.header('user-agent');
6
+ let ip =
7
+ parseForwardedFor(
8
+ c.req.header('metorial-connecting-ip') ??
9
+ c.req.header('cf-connecting-ip') ??
10
+ c.req.header('x-forwarded-for') ??
11
+ c.req.header('x-real-ip')
12
+ ) ?? '0.0.0.0';
13
+
14
+ return { ua, ip };
15
+ };
package/src/hono.ts ADDED
@@ -0,0 +1,32 @@
1
+ import { internalServerError, isServiceError, notFoundError } from '@lowerdeck/error';
2
+ import { Context, Env, Hono } from 'hono';
3
+ import { cors } from 'hono/cors';
4
+
5
+ export { cors, type Context };
6
+
7
+ export let createHono = <E extends Env>(basePath?: string) => {
8
+ let app = new Hono<E>();
9
+ if (basePath) app = app.basePath(basePath);
10
+
11
+ app.use(async (c, next) => {
12
+ await next();
13
+
14
+ c.res.headers.set('X-Powered-By', 'Metorial');
15
+ });
16
+
17
+ app.notFound(c => {
18
+ return c.json(notFoundError('endpoint', null).toResponse(), 404);
19
+ });
20
+
21
+ app.onError((e, c) => {
22
+ if (isServiceError(e)) {
23
+ return c.json(e.toResponse(), e.data.status);
24
+ }
25
+
26
+ console.error(e);
27
+
28
+ return c.json(internalServerError().toResponse(), 500);
29
+ });
30
+
31
+ return app;
32
+ };
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './context';
2
+ export * from './hono';
3
+ export * from './validation';
@@ -0,0 +1,59 @@
1
+ import { badRequestError, ServiceError, validationError } from '@lowerdeck/error';
2
+ import { ValidationType } from '@lowerdeck/validation';
3
+ import { Context } from 'hono';
4
+
5
+ export let useValidatedBody = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {
6
+ let body: any;
7
+
8
+ if (c.req.header('Content-Type')?.includes('application/x-www-form-urlencoded')) {
9
+ body = Object.fromEntries(new URLSearchParams(await c.req.text()));
10
+ } else {
11
+ try {
12
+ body = await c.req.json();
13
+ } catch (e) {
14
+ throw new ServiceError(
15
+ badRequestError({
16
+ message: 'Invalid JSON body'
17
+ })
18
+ );
19
+ }
20
+ }
21
+
22
+ let val = v.validate(body);
23
+ if (!val.success) {
24
+ throw new ServiceError(
25
+ validationError({
26
+ entity: 'body',
27
+ errors: val.errors
28
+ })
29
+ );
30
+ }
31
+
32
+ return val.value;
33
+ };
34
+
35
+ export let useValidatedQuery = async <T>(c: Context, v: ValidationType<T>): Promise<T> => {
36
+ let body: any;
37
+
38
+ try {
39
+ body = c.req.query();
40
+ } catch (e) {
41
+ throw new ServiceError(
42
+ badRequestError({
43
+ message: 'Invalid JSON body'
44
+ })
45
+ );
46
+ }
47
+
48
+ let val = v.validate(body);
49
+ if (!val.success) {
50
+ throw new ServiceError(
51
+ validationError({
52
+ entity: 'query',
53
+ errors: val.errors
54
+ })
55
+ );
56
+ }
57
+
58
+ return val.value;
59
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "@lowerdeck/tsconfig/base.json",
4
+ "exclude": ["dist"],
5
+ "include": ["src"],
6
+ "compilerOptions": {
7
+ "outDir": "dist",
8
+ "lib": ["es2021"],
9
+ "target": "ES2019"
10
+ }
11
+ }