@shware/http 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/__tests__/index.test.cjs +27 -0
- package/dist/__tests__/index.test.cjs.map +1 -0
- package/dist/__tests__/index.test.d.cts +2 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.mjs +25 -0
- package/dist/__tests__/index.test.mjs.map +1 -0
- package/dist/cache/index.cjs +32 -0
- package/dist/cache/index.cjs.map +1 -0
- package/dist/cache/index.d.cts +9 -0
- package/dist/cache/index.d.ts +9 -0
- package/dist/cache/index.mjs +7 -0
- package/dist/cache/index.mjs.map +1 -0
- package/dist/error/detail.cjs +93 -0
- package/dist/error/detail.cjs.map +1 -0
- package/dist/error/detail.d.cts +99 -0
- package/dist/error/detail.d.ts +99 -0
- package/dist/error/detail.mjs +67 -0
- package/dist/error/detail.mjs.map +1 -0
- package/dist/error/i18n/en-us.cjs +31 -0
- package/dist/error/i18n/en-us.cjs.map +1 -0
- package/dist/error/i18n/en-us.d.cts +3 -0
- package/dist/error/i18n/en-us.d.ts +3 -0
- package/dist/error/i18n/en-us.mjs +6 -0
- package/dist/error/i18n/en-us.mjs.map +1 -0
- package/dist/error/index.cjs +63 -0
- package/dist/error/index.cjs.map +1 -0
- package/dist/error/index.d.cts +19 -0
- package/dist/error/index.d.ts +19 -0
- package/dist/error/index.mjs +35 -0
- package/dist/error/index.mjs.map +1 -0
- package/dist/error/parse.cjs +52 -0
- package/dist/error/parse.cjs.map +1 -0
- package/dist/error/parse.d.cts +16 -0
- package/dist/error/parse.d.ts +16 -0
- package/dist/error/parse.mjs +27 -0
- package/dist/error/parse.mjs.map +1 -0
- package/dist/error/reason.cjs +19 -0
- package/dist/error/reason.cjs.map +1 -0
- package/dist/error/reason.d.cts +66 -0
- package/dist/error/reason.d.ts +66 -0
- package/dist/error/reason.mjs +1 -0
- package/dist/error/reason.mjs.map +1 -0
- package/dist/error/status.cjs +263 -0
- package/dist/error/status.cjs.map +1 -0
- package/dist/error/status.d.cts +69 -0
- package/dist/error/status.d.ts +69 -0
- package/dist/error/status.mjs +234 -0
- package/dist/error/status.mjs.map +1 -0
- package/dist/google-one-tap/index.cjs +51 -0
- package/dist/google-one-tap/index.cjs.map +1 -0
- package/dist/google-one-tap/index.d.cts +17 -0
- package/dist/google-one-tap/index.d.ts +17 -0
- package/dist/google-one-tap/index.mjs +26 -0
- package/dist/google-one-tap/index.mjs.map +1 -0
- package/dist/google-one-tap/types.cjs +19 -0
- package/dist/google-one-tap/types.cjs.map +1 -0
- package/dist/google-one-tap/types.d.cts +155 -0
- package/dist/google-one-tap/types.d.ts +155 -0
- package/dist/google-one-tap/types.mjs +1 -0
- package/dist/google-one-tap/types.mjs.map +1 -0
- package/dist/hono/__tests__/authorizer.test.cjs +483 -0
- package/dist/hono/__tests__/authorizer.test.cjs.map +1 -0
- package/dist/hono/__tests__/authorizer.test.d.cts +2 -0
- package/dist/hono/__tests__/authorizer.test.d.ts +2 -0
- package/dist/hono/__tests__/authorizer.test.mjs +481 -0
- package/dist/hono/__tests__/authorizer.test.mjs.map +1 -0
- package/dist/hono/__tests__/csrf.test.cjs +162 -0
- package/dist/hono/__tests__/csrf.test.cjs.map +1 -0
- package/dist/hono/__tests__/csrf.test.d.cts +2 -0
- package/dist/hono/__tests__/csrf.test.d.ts +2 -0
- package/dist/hono/__tests__/csrf.test.mjs +160 -0
- package/dist/hono/__tests__/csrf.test.mjs.map +1 -0
- package/dist/hono/authorizer.cjs +67 -0
- package/dist/hono/authorizer.cjs.map +1 -0
- package/dist/hono/authorizer.d.cts +18 -0
- package/dist/hono/authorizer.d.ts +18 -0
- package/dist/hono/authorizer.mjs +42 -0
- package/dist/hono/authorizer.mjs.map +1 -0
- package/dist/hono/csrf.cjs +95 -0
- package/dist/hono/csrf.cjs.map +1 -0
- package/dist/hono/csrf.d.cts +65 -0
- package/dist/hono/csrf.d.ts +65 -0
- package/dist/hono/csrf.mjs +70 -0
- package/dist/hono/csrf.mjs.map +1 -0
- package/dist/hono/geolocation.cjs +75 -0
- package/dist/hono/geolocation.cjs.map +1 -0
- package/dist/hono/geolocation.d.cts +17 -0
- package/dist/hono/geolocation.d.ts +17 -0
- package/dist/hono/geolocation.mjs +50 -0
- package/dist/hono/geolocation.mjs.map +1 -0
- package/dist/hono/handler.cjs +68 -0
- package/dist/hono/handler.cjs.map +1 -0
- package/dist/hono/handler.d.cts +13 -0
- package/dist/hono/handler.d.ts +13 -0
- package/dist/hono/handler.mjs +42 -0
- package/dist/hono/handler.mjs.map +1 -0
- package/dist/hono/index.cjs +45 -0
- package/dist/hono/index.cjs.map +1 -0
- package/dist/hono/index.d.cts +12 -0
- package/dist/hono/index.d.ts +12 -0
- package/dist/hono/index.mjs +15 -0
- package/dist/hono/index.mjs.map +1 -0
- package/dist/hono/validator.cjs +64 -0
- package/dist/hono/validator.cjs.map +1 -0
- package/dist/hono/validator.d.cts +28 -0
- package/dist/hono/validator.d.ts +28 -0
- package/dist/hono/validator.mjs +38 -0
- package/dist/hono/validator.mjs.map +1 -0
- package/dist/index-BnPgRQDl.d.cts +129 -0
- package/dist/index-BnPgRQDl.d.ts +129 -0
- package/dist/index.cjs +91 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.mjs +46 -0
- package/dist/index.mjs.map +1 -0
- package/dist/iso/iso_3601_1.cjs +292 -0
- package/dist/iso/iso_3601_1.cjs.map +1 -0
- package/dist/iso/iso_3601_1.d.cts +9 -0
- package/dist/iso/iso_3601_1.d.ts +9 -0
- package/dist/iso/iso_3601_1.mjs +267 -0
- package/dist/iso/iso_3601_1.mjs.map +1 -0
- package/dist/max-length/index.cjs +94 -0
- package/dist/max-length/index.cjs.map +1 -0
- package/dist/max-length/index.d.cts +1 -0
- package/dist/max-length/index.d.ts +1 -0
- package/dist/max-length/index.mjs +48 -0
- package/dist/max-length/index.mjs.map +1 -0
- package/dist/message.cjs +32 -0
- package/dist/message.cjs.map +1 -0
- package/dist/message.d.cts +3 -0
- package/dist/message.d.ts +3 -0
- package/dist/message.mjs +7 -0
- package/dist/message.mjs.map +1 -0
- package/dist/polyfills/index.cjs +7 -0
- package/dist/polyfills/index.cjs.map +1 -0
- package/dist/polyfills/index.d.cts +2 -0
- package/dist/polyfills/index.d.ts +2 -0
- package/dist/polyfills/index.mjs +5 -0
- package/dist/polyfills/index.mjs.map +1 -0
- package/dist/response.cjs +73 -0
- package/dist/response.cjs.map +1 -0
- package/dist/response.d.cts +65 -0
- package/dist/response.d.ts +65 -0
- package/dist/response.mjs +44 -0
- package/dist/response.mjs.map +1 -0
- package/dist/snowflake.cjs +75 -0
- package/dist/snowflake.cjs.map +1 -0
- package/dist/snowflake.d.cts +29 -0
- package/dist/snowflake.d.ts +29 -0
- package/dist/snowflake.mjs +49 -0
- package/dist/snowflake.mjs.map +1 -0
- package/dist/utils/__tests__/ip.test.cjs +38 -0
- package/dist/utils/__tests__/ip.test.cjs.map +1 -0
- package/dist/utils/__tests__/ip.test.d.cts +2 -0
- package/dist/utils/__tests__/ip.test.d.ts +2 -0
- package/dist/utils/__tests__/ip.test.mjs +36 -0
- package/dist/utils/__tests__/ip.test.mjs.map +1 -0
- package/dist/utils/invariant.cjs +37 -0
- package/dist/utils/invariant.cjs.map +1 -0
- package/dist/utils/invariant.d.cts +3 -0
- package/dist/utils/invariant.d.ts +3 -0
- package/dist/utils/invariant.mjs +12 -0
- package/dist/utils/invariant.mjs.map +1 -0
- package/dist/utils/ip.cjs +53 -0
- package/dist/utils/ip.cjs.map +1 -0
- package/dist/utils/ip.d.cts +3 -0
- package/dist/utils/ip.d.ts +3 -0
- package/dist/utils/ip.mjs +28 -0
- package/dist/utils/ip.mjs.map +1 -0
- package/dist/utils/promise.cjs +47 -0
- package/dist/utils/promise.cjs.map +1 -0
- package/dist/utils/promise.d.cts +3 -0
- package/dist/utils/promise.d.ts +3 -0
- package/dist/utils/promise.mjs +22 -0
- package/dist/utils/promise.mjs.map +1 -0
- package/dist/utils/string.cjs +33 -0
- package/dist/utils/string.cjs.map +1 -0
- package/dist/utils/string.d.cts +6 -0
- package/dist/utils/string.d.ts +6 -0
- package/dist/utils/string.mjs +8 -0
- package/dist/utils/string.mjs.map +1 -0
- package/dist/utils/timing.cjs +74 -0
- package/dist/utils/timing.cjs.map +1 -0
- package/dist/utils/timing.d.cts +13 -0
- package/dist/utils/timing.d.ts +13 -0
- package/dist/utils/timing.mjs +49 -0
- package/dist/utils/timing.mjs.map +1 -0
- package/dist/utils/token-bucket.cjs +73 -0
- package/dist/utils/token-bucket.cjs.map +1 -0
- package/dist/utils/token-bucket.d.cts +20 -0
- package/dist/utils/token-bucket.d.ts +20 -0
- package/dist/utils/token-bucket.mjs +48 -0
- package/dist/utils/token-bucket.mjs.map +1 -0
- package/dist/vaild.cjs +51 -0
- package/dist/vaild.cjs.map +1 -0
- package/dist/vaild.d.cts +13 -0
- package/dist/vaild.d.ts +13 -0
- package/dist/vaild.mjs +26 -0
- package/dist/vaild.mjs.map +1 -0
- package/dist/webhook/index.cjs +77 -0
- package/dist/webhook/index.cjs.map +1 -0
- package/dist/webhook/index.d.cts +10 -0
- package/dist/webhook/index.d.ts +10 -0
- package/dist/webhook/index.mjs +52 -0
- package/dist/webhook/index.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hono/csrf.ts"],"sourcesContent":["import { timingSafeEqual } from 'crypto';\nimport { getCookie } from 'hono/cookie';\nimport { METHOD_NAME_ALL } from 'hono/router';\nimport { RegExpRouter } from 'hono/router/reg-exp-router';\nimport { SmartRouter } from 'hono/router/smart-router';\nimport { TrieRouter } from 'hono/router/trie-router';\nimport { Status } from '../error/status';\nimport type { MiddlewareHandler } from 'hono';\n\ntype HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';\n\nexport type CSRFIgnoreRule = string | { path: string; methods?: [HTTPMethod, ...HTTPMethod[]] };\n\nexport interface CSRFConfig {\n /**\n * Cookie name for CSRF token\n * @default 'XSRF-TOKEN'\n */\n cookieName?: string;\n\n /**\n * Header name for CSRF token\n * @default 'X-XSRF-TOKEN'\n */\n headerName?: string;\n\n /**\n * Ignore rules for specific paths and methods\n * @example\n * [\n * { path: '/api/webhook/*', methods: ['POST'] },\n * { path: '/auth/apple/callback' }, // ignores all methods\n * ]\n */\n ignores?: CSRFIgnoreRule[];\n\n /**\n * Skip CSRF check for these methods\n * @default ['GET', 'HEAD', 'OPTIONS']\n */\n safeMethods?: HTTPMethod[];\n\n /**\n * Custom error message\n * @default 'CSRF token validation failed'\n */\n errorMessage?: string;\n}\n\n/** use timing safe compare to prevent timing attack */\nfunction safeCompare(a: string, b: string): boolean {\n if (typeof a !== 'string' || typeof b !== 'string') return false;\n if (a.length === 0 || b.length === 0) return false;\n\n const bufferA = Buffer.from(a, 'utf-8');\n const bufferB = Buffer.from(b, 'utf-8');\n if (bufferA.length !== bufferB.length) return false;\n return timingSafeEqual(bufferA, bufferB);\n}\n\n/**\n * Create CSRF protection middleware\n *\n * @example\n * ```ts\n * import { Hono } from 'hono';\n * import { csrf } from '@shware/http/hono';\n *\n * const app = new Hono();\n *\n * // basic usage\n * app.use(csrf());\n *\n * // with configuration\n * app.use(csrf({\n * cookieName: 'csrf-token',\n * headerName: 'X-CSRF-Token',\n * ignores: [\n * { path: '/api/webhook/*', methods: ['POST'] },\n * { path: '/auth/apple/callback' },\n * ]\n * }));\n * ```\n */\nexport function csrf(config: CSRFConfig = {}): MiddlewareHandler {\n const cookieName = config.cookieName ?? 'XSRF-TOKEN';\n const headerName = config.headerName ?? 'X-XSRF-TOKEN';\n const safeMethods = new Set(config.safeMethods ?? ['GET', 'HEAD', 'OPTIONS']);\n const errorMessage = config.errorMessage ?? 'CSRF token validation failed';\n\n // initialize router for matching ignore rules\n const router = new SmartRouter<boolean>({\n routers: [new RegExpRouter(), new TrieRouter()],\n });\n\n // register ignore rules\n if (config.ignores) {\n for (const rule of config.ignores) {\n if (typeof rule === 'string') {\n router.add(METHOD_NAME_ALL, rule, true);\n } else if (rule.methods && rule.methods.length > 0) {\n for (const method of rule.methods) {\n router.add(method, rule.path, true);\n }\n } else {\n // if no methods are specified, ignore all methods\n router.add(METHOD_NAME_ALL, rule.path, true);\n }\n }\n }\n\n // check if the request should be ignored\n function shouldIgnore(method: string, path: string): boolean {\n if (safeMethods.has(method)) {\n return true;\n }\n const [matchedAll] = router.match(METHOD_NAME_ALL, path);\n if (matchedAll.length > 0) {\n return true;\n }\n const [matchedMethod] = router.match(method, path);\n return matchedMethod.length > 0;\n }\n\n // return middleware\n return async (c, next) => {\n const method = c.req.method;\n const path = c.req.path;\n\n if (shouldIgnore(method, path)) {\n await next();\n return;\n }\n\n const cookieToken = getCookie(c, cookieName);\n const headerToken = c.req.header(headerName);\n\n if (!cookieToken || !headerToken) {\n throw Status.permissionDenied(errorMessage).error();\n }\n\n if (!safeCompare(cookieToken, headerToken)) {\n throw Status.permissionDenied(errorMessage).error();\n }\n\n await next();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAgC;AAChC,oBAA0B;AAC1B,oBAAgC;AAChC,4BAA6B;AAC7B,0BAA4B;AAC5B,yBAA2B;AAC3B,oBAAuB;AA4CvB,SAAS,YAAY,GAAW,GAAoB;AAClD,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AAC3D,MAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EAAG,QAAO;AAE7C,QAAM,UAAU,OAAO,KAAK,GAAG,OAAO;AACtC,QAAM,UAAU,OAAO,KAAK,GAAG,OAAO;AACtC,MAAI,QAAQ,WAAW,QAAQ,OAAQ,QAAO;AAC9C,aAAO,+BAAgB,SAAS,OAAO;AACzC;AA0BO,SAAS,KAAK,SAAqB,CAAC,GAAsB;AAC/D,QAAM,aAAa,OAAO,cAAc;AACxC,QAAM,aAAa,OAAO,cAAc;AACxC,QAAM,cAAc,IAAI,IAAI,OAAO,eAAe,CAAC,OAAO,QAAQ,SAAS,CAAC;AAC5E,QAAM,eAAe,OAAO,gBAAgB;AAG5C,QAAM,SAAS,IAAI,gCAAqB;AAAA,IACtC,SAAS,CAAC,IAAI,mCAAa,GAAG,IAAI,8BAAW,CAAC;AAAA,EAChD,CAAC;AAGD,MAAI,OAAO,SAAS;AAClB,eAAW,QAAQ,OAAO,SAAS;AACjC,UAAI,OAAO,SAAS,UAAU;AAC5B,eAAO,IAAI,+BAAiB,MAAM,IAAI;AAAA,MACxC,WAAW,KAAK,WAAW,KAAK,QAAQ,SAAS,GAAG;AAClD,mBAAW,UAAU,KAAK,SAAS;AACjC,iBAAO,IAAI,QAAQ,KAAK,MAAM,IAAI;AAAA,QACpC;AAAA,MACF,OAAO;AAEL,eAAO,IAAI,+BAAiB,KAAK,MAAM,IAAI;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAGA,WAAS,aAAa,QAAgB,MAAuB;AAC3D,QAAI,YAAY,IAAI,MAAM,GAAG;AAC3B,aAAO;AAAA,IACT;AACA,UAAM,CAAC,UAAU,IAAI,OAAO,MAAM,+BAAiB,IAAI;AACvD,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AACA,UAAM,CAAC,aAAa,IAAI,OAAO,MAAM,QAAQ,IAAI;AACjD,WAAO,cAAc,SAAS;AAAA,EAChC;AAGA,SAAO,OAAO,GAAG,SAAS;AACxB,UAAM,SAAS,EAAE,IAAI;AACrB,UAAM,OAAO,EAAE,IAAI;AAEnB,QAAI,aAAa,QAAQ,IAAI,GAAG;AAC9B,YAAM,KAAK;AACX;AAAA,IACF;AAEA,UAAM,kBAAc,yBAAU,GAAG,UAAU;AAC3C,UAAM,cAAc,EAAE,IAAI,OAAO,UAAU;AAE3C,QAAI,CAAC,eAAe,CAAC,aAAa;AAChC,YAAM,qBAAO,iBAAiB,YAAY,EAAE,MAAM;AAAA,IACpD;AAEA,QAAI,CAAC,YAAY,aAAa,WAAW,GAAG;AAC1C,YAAM,qBAAO,iBAAiB,YAAY,EAAE,MAAM;AAAA,IACpD;AAEA,UAAM,KAAK;AAAA,EACb;AACF;","names":[]}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { MiddlewareHandler } from 'hono';
|
|
2
|
+
|
|
3
|
+
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
|
4
|
+
type CSRFIgnoreRule = string | {
|
|
5
|
+
path: string;
|
|
6
|
+
methods?: [HTTPMethod, ...HTTPMethod[]];
|
|
7
|
+
};
|
|
8
|
+
interface CSRFConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Cookie name for CSRF token
|
|
11
|
+
* @default 'XSRF-TOKEN'
|
|
12
|
+
*/
|
|
13
|
+
cookieName?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Header name for CSRF token
|
|
16
|
+
* @default 'X-XSRF-TOKEN'
|
|
17
|
+
*/
|
|
18
|
+
headerName?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Ignore rules for specific paths and methods
|
|
21
|
+
* @example
|
|
22
|
+
* [
|
|
23
|
+
* { path: '/api/webhook/*', methods: ['POST'] },
|
|
24
|
+
* { path: '/auth/apple/callback' }, // ignores all methods
|
|
25
|
+
* ]
|
|
26
|
+
*/
|
|
27
|
+
ignores?: CSRFIgnoreRule[];
|
|
28
|
+
/**
|
|
29
|
+
* Skip CSRF check for these methods
|
|
30
|
+
* @default ['GET', 'HEAD', 'OPTIONS']
|
|
31
|
+
*/
|
|
32
|
+
safeMethods?: HTTPMethod[];
|
|
33
|
+
/**
|
|
34
|
+
* Custom error message
|
|
35
|
+
* @default 'CSRF token validation failed'
|
|
36
|
+
*/
|
|
37
|
+
errorMessage?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create CSRF protection middleware
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { Hono } from 'hono';
|
|
45
|
+
* import { csrf } from '@shware/http/hono';
|
|
46
|
+
*
|
|
47
|
+
* const app = new Hono();
|
|
48
|
+
*
|
|
49
|
+
* // basic usage
|
|
50
|
+
* app.use(csrf());
|
|
51
|
+
*
|
|
52
|
+
* // with configuration
|
|
53
|
+
* app.use(csrf({
|
|
54
|
+
* cookieName: 'csrf-token',
|
|
55
|
+
* headerName: 'X-CSRF-Token',
|
|
56
|
+
* ignores: [
|
|
57
|
+
* { path: '/api/webhook/*', methods: ['POST'] },
|
|
58
|
+
* { path: '/auth/apple/callback' },
|
|
59
|
+
* ]
|
|
60
|
+
* }));
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
declare function csrf(config?: CSRFConfig): MiddlewareHandler;
|
|
64
|
+
|
|
65
|
+
export { type CSRFConfig, type CSRFIgnoreRule, csrf };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { MiddlewareHandler } from 'hono';
|
|
2
|
+
|
|
3
|
+
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
|
4
|
+
type CSRFIgnoreRule = string | {
|
|
5
|
+
path: string;
|
|
6
|
+
methods?: [HTTPMethod, ...HTTPMethod[]];
|
|
7
|
+
};
|
|
8
|
+
interface CSRFConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Cookie name for CSRF token
|
|
11
|
+
* @default 'XSRF-TOKEN'
|
|
12
|
+
*/
|
|
13
|
+
cookieName?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Header name for CSRF token
|
|
16
|
+
* @default 'X-XSRF-TOKEN'
|
|
17
|
+
*/
|
|
18
|
+
headerName?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Ignore rules for specific paths and methods
|
|
21
|
+
* @example
|
|
22
|
+
* [
|
|
23
|
+
* { path: '/api/webhook/*', methods: ['POST'] },
|
|
24
|
+
* { path: '/auth/apple/callback' }, // ignores all methods
|
|
25
|
+
* ]
|
|
26
|
+
*/
|
|
27
|
+
ignores?: CSRFIgnoreRule[];
|
|
28
|
+
/**
|
|
29
|
+
* Skip CSRF check for these methods
|
|
30
|
+
* @default ['GET', 'HEAD', 'OPTIONS']
|
|
31
|
+
*/
|
|
32
|
+
safeMethods?: HTTPMethod[];
|
|
33
|
+
/**
|
|
34
|
+
* Custom error message
|
|
35
|
+
* @default 'CSRF token validation failed'
|
|
36
|
+
*/
|
|
37
|
+
errorMessage?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create CSRF protection middleware
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```ts
|
|
44
|
+
* import { Hono } from 'hono';
|
|
45
|
+
* import { csrf } from '@shware/http/hono';
|
|
46
|
+
*
|
|
47
|
+
* const app = new Hono();
|
|
48
|
+
*
|
|
49
|
+
* // basic usage
|
|
50
|
+
* app.use(csrf());
|
|
51
|
+
*
|
|
52
|
+
* // with configuration
|
|
53
|
+
* app.use(csrf({
|
|
54
|
+
* cookieName: 'csrf-token',
|
|
55
|
+
* headerName: 'X-CSRF-Token',
|
|
56
|
+
* ignores: [
|
|
57
|
+
* { path: '/api/webhook/*', methods: ['POST'] },
|
|
58
|
+
* { path: '/auth/apple/callback' },
|
|
59
|
+
* ]
|
|
60
|
+
* }));
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
declare function csrf(config?: CSRFConfig): MiddlewareHandler;
|
|
64
|
+
|
|
65
|
+
export { type CSRFConfig, type CSRFIgnoreRule, csrf };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// src/hono/csrf.ts
|
|
2
|
+
import { timingSafeEqual } from "crypto";
|
|
3
|
+
import { getCookie } from "hono/cookie";
|
|
4
|
+
import { METHOD_NAME_ALL } from "hono/router";
|
|
5
|
+
import { RegExpRouter } from "hono/router/reg-exp-router";
|
|
6
|
+
import { SmartRouter } from "hono/router/smart-router";
|
|
7
|
+
import { TrieRouter } from "hono/router/trie-router";
|
|
8
|
+
import { Status } from "../error/status.mjs";
|
|
9
|
+
function safeCompare(a, b) {
|
|
10
|
+
if (typeof a !== "string" || typeof b !== "string") return false;
|
|
11
|
+
if (a.length === 0 || b.length === 0) return false;
|
|
12
|
+
const bufferA = Buffer.from(a, "utf-8");
|
|
13
|
+
const bufferB = Buffer.from(b, "utf-8");
|
|
14
|
+
if (bufferA.length !== bufferB.length) return false;
|
|
15
|
+
return timingSafeEqual(bufferA, bufferB);
|
|
16
|
+
}
|
|
17
|
+
function csrf(config = {}) {
|
|
18
|
+
const cookieName = config.cookieName ?? "XSRF-TOKEN";
|
|
19
|
+
const headerName = config.headerName ?? "X-XSRF-TOKEN";
|
|
20
|
+
const safeMethods = new Set(config.safeMethods ?? ["GET", "HEAD", "OPTIONS"]);
|
|
21
|
+
const errorMessage = config.errorMessage ?? "CSRF token validation failed";
|
|
22
|
+
const router = new SmartRouter({
|
|
23
|
+
routers: [new RegExpRouter(), new TrieRouter()]
|
|
24
|
+
});
|
|
25
|
+
if (config.ignores) {
|
|
26
|
+
for (const rule of config.ignores) {
|
|
27
|
+
if (typeof rule === "string") {
|
|
28
|
+
router.add(METHOD_NAME_ALL, rule, true);
|
|
29
|
+
} else if (rule.methods && rule.methods.length > 0) {
|
|
30
|
+
for (const method of rule.methods) {
|
|
31
|
+
router.add(method, rule.path, true);
|
|
32
|
+
}
|
|
33
|
+
} else {
|
|
34
|
+
router.add(METHOD_NAME_ALL, rule.path, true);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function shouldIgnore(method, path) {
|
|
39
|
+
if (safeMethods.has(method)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const [matchedAll] = router.match(METHOD_NAME_ALL, path);
|
|
43
|
+
if (matchedAll.length > 0) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
const [matchedMethod] = router.match(method, path);
|
|
47
|
+
return matchedMethod.length > 0;
|
|
48
|
+
}
|
|
49
|
+
return async (c, next) => {
|
|
50
|
+
const method = c.req.method;
|
|
51
|
+
const path = c.req.path;
|
|
52
|
+
if (shouldIgnore(method, path)) {
|
|
53
|
+
await next();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const cookieToken = getCookie(c, cookieName);
|
|
57
|
+
const headerToken = c.req.header(headerName);
|
|
58
|
+
if (!cookieToken || !headerToken) {
|
|
59
|
+
throw Status.permissionDenied(errorMessage).error();
|
|
60
|
+
}
|
|
61
|
+
if (!safeCompare(cookieToken, headerToken)) {
|
|
62
|
+
throw Status.permissionDenied(errorMessage).error();
|
|
63
|
+
}
|
|
64
|
+
await next();
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
csrf
|
|
69
|
+
};
|
|
70
|
+
//# sourceMappingURL=csrf.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hono/csrf.ts"],"sourcesContent":["import { timingSafeEqual } from 'crypto';\nimport { getCookie } from 'hono/cookie';\nimport { METHOD_NAME_ALL } from 'hono/router';\nimport { RegExpRouter } from 'hono/router/reg-exp-router';\nimport { SmartRouter } from 'hono/router/smart-router';\nimport { TrieRouter } from 'hono/router/trie-router';\nimport { Status } from '../error/status';\nimport type { MiddlewareHandler } from 'hono';\n\ntype HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';\n\nexport type CSRFIgnoreRule = string | { path: string; methods?: [HTTPMethod, ...HTTPMethod[]] };\n\nexport interface CSRFConfig {\n /**\n * Cookie name for CSRF token\n * @default 'XSRF-TOKEN'\n */\n cookieName?: string;\n\n /**\n * Header name for CSRF token\n * @default 'X-XSRF-TOKEN'\n */\n headerName?: string;\n\n /**\n * Ignore rules for specific paths and methods\n * @example\n * [\n * { path: '/api/webhook/*', methods: ['POST'] },\n * { path: '/auth/apple/callback' }, // ignores all methods\n * ]\n */\n ignores?: CSRFIgnoreRule[];\n\n /**\n * Skip CSRF check for these methods\n * @default ['GET', 'HEAD', 'OPTIONS']\n */\n safeMethods?: HTTPMethod[];\n\n /**\n * Custom error message\n * @default 'CSRF token validation failed'\n */\n errorMessage?: string;\n}\n\n/** use timing safe compare to prevent timing attack */\nfunction safeCompare(a: string, b: string): boolean {\n if (typeof a !== 'string' || typeof b !== 'string') return false;\n if (a.length === 0 || b.length === 0) return false;\n\n const bufferA = Buffer.from(a, 'utf-8');\n const bufferB = Buffer.from(b, 'utf-8');\n if (bufferA.length !== bufferB.length) return false;\n return timingSafeEqual(bufferA, bufferB);\n}\n\n/**\n * Create CSRF protection middleware\n *\n * @example\n * ```ts\n * import { Hono } from 'hono';\n * import { csrf } from '@shware/http/hono';\n *\n * const app = new Hono();\n *\n * // basic usage\n * app.use(csrf());\n *\n * // with configuration\n * app.use(csrf({\n * cookieName: 'csrf-token',\n * headerName: 'X-CSRF-Token',\n * ignores: [\n * { path: '/api/webhook/*', methods: ['POST'] },\n * { path: '/auth/apple/callback' },\n * ]\n * }));\n * ```\n */\nexport function csrf(config: CSRFConfig = {}): MiddlewareHandler {\n const cookieName = config.cookieName ?? 'XSRF-TOKEN';\n const headerName = config.headerName ?? 'X-XSRF-TOKEN';\n const safeMethods = new Set(config.safeMethods ?? ['GET', 'HEAD', 'OPTIONS']);\n const errorMessage = config.errorMessage ?? 'CSRF token validation failed';\n\n // initialize router for matching ignore rules\n const router = new SmartRouter<boolean>({\n routers: [new RegExpRouter(), new TrieRouter()],\n });\n\n // register ignore rules\n if (config.ignores) {\n for (const rule of config.ignores) {\n if (typeof rule === 'string') {\n router.add(METHOD_NAME_ALL, rule, true);\n } else if (rule.methods && rule.methods.length > 0) {\n for (const method of rule.methods) {\n router.add(method, rule.path, true);\n }\n } else {\n // if no methods are specified, ignore all methods\n router.add(METHOD_NAME_ALL, rule.path, true);\n }\n }\n }\n\n // check if the request should be ignored\n function shouldIgnore(method: string, path: string): boolean {\n if (safeMethods.has(method)) {\n return true;\n }\n const [matchedAll] = router.match(METHOD_NAME_ALL, path);\n if (matchedAll.length > 0) {\n return true;\n }\n const [matchedMethod] = router.match(method, path);\n return matchedMethod.length > 0;\n }\n\n // return middleware\n return async (c, next) => {\n const method = c.req.method;\n const path = c.req.path;\n\n if (shouldIgnore(method, path)) {\n await next();\n return;\n }\n\n const cookieToken = getCookie(c, cookieName);\n const headerToken = c.req.header(headerName);\n\n if (!cookieToken || !headerToken) {\n throw Status.permissionDenied(errorMessage).error();\n }\n\n if (!safeCompare(cookieToken, headerToken)) {\n throw Status.permissionDenied(errorMessage).error();\n }\n\n await next();\n };\n}\n"],"mappings":";AAAA,SAAS,uBAAuB;AAChC,SAAS,iBAAiB;AAC1B,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAC7B,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AA4CvB,SAAS,YAAY,GAAW,GAAoB;AAClD,MAAI,OAAO,MAAM,YAAY,OAAO,MAAM,SAAU,QAAO;AAC3D,MAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EAAG,QAAO;AAE7C,QAAM,UAAU,OAAO,KAAK,GAAG,OAAO;AACtC,QAAM,UAAU,OAAO,KAAK,GAAG,OAAO;AACtC,MAAI,QAAQ,WAAW,QAAQ,OAAQ,QAAO;AAC9C,SAAO,gBAAgB,SAAS,OAAO;AACzC;AA0BO,SAAS,KAAK,SAAqB,CAAC,GAAsB;AAC/D,QAAM,aAAa,OAAO,cAAc;AACxC,QAAM,aAAa,OAAO,cAAc;AACxC,QAAM,cAAc,IAAI,IAAI,OAAO,eAAe,CAAC,OAAO,QAAQ,SAAS,CAAC;AAC5E,QAAM,eAAe,OAAO,gBAAgB;AAG5C,QAAM,SAAS,IAAI,YAAqB;AAAA,IACtC,SAAS,CAAC,IAAI,aAAa,GAAG,IAAI,WAAW,CAAC;AAAA,EAChD,CAAC;AAGD,MAAI,OAAO,SAAS;AAClB,eAAW,QAAQ,OAAO,SAAS;AACjC,UAAI,OAAO,SAAS,UAAU;AAC5B,eAAO,IAAI,iBAAiB,MAAM,IAAI;AAAA,MACxC,WAAW,KAAK,WAAW,KAAK,QAAQ,SAAS,GAAG;AAClD,mBAAW,UAAU,KAAK,SAAS;AACjC,iBAAO,IAAI,QAAQ,KAAK,MAAM,IAAI;AAAA,QACpC;AAAA,MACF,OAAO;AAEL,eAAO,IAAI,iBAAiB,KAAK,MAAM,IAAI;AAAA,MAC7C;AAAA,IACF;AAAA,EACF;AAGA,WAAS,aAAa,QAAgB,MAAuB;AAC3D,QAAI,YAAY,IAAI,MAAM,GAAG;AAC3B,aAAO;AAAA,IACT;AACA,UAAM,CAAC,UAAU,IAAI,OAAO,MAAM,iBAAiB,IAAI;AACvD,QAAI,WAAW,SAAS,GAAG;AACzB,aAAO;AAAA,IACT;AACA,UAAM,CAAC,aAAa,IAAI,OAAO,MAAM,QAAQ,IAAI;AACjD,WAAO,cAAc,SAAS;AAAA,EAChC;AAGA,SAAO,OAAO,GAAG,SAAS;AACxB,UAAM,SAAS,EAAE,IAAI;AACrB,UAAM,OAAO,EAAE,IAAI;AAEnB,QAAI,aAAa,QAAQ,IAAI,GAAG;AAC9B,YAAM,KAAK;AACX;AAAA,IACF;AAEA,UAAM,cAAc,UAAU,GAAG,UAAU;AAC3C,UAAM,cAAc,EAAE,IAAI,OAAO,UAAU;AAE3C,QAAI,CAAC,eAAe,CAAC,aAAa;AAChC,YAAM,OAAO,iBAAiB,YAAY,EAAE,MAAM;AAAA,IACpD;AAEA,QAAI,CAAC,YAAY,aAAa,WAAW,GAAG;AAC1C,YAAM,OAAO,iBAAiB,YAAY,EAAE,MAAM;AAAA,IACpD;AAEA,UAAM,KAAK;AAAA,EACb;AACF;","names":[]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/hono/geolocation.ts
|
|
21
|
+
var geolocation_exports = {};
|
|
22
|
+
__export(geolocation_exports, {
|
|
23
|
+
geolocation: () => geolocation
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(geolocation_exports);
|
|
26
|
+
var import_adapter = require("hono/adapter");
|
|
27
|
+
var import_ip = require("../utils/ip.cjs");
|
|
28
|
+
var runtime = (0, import_adapter.getRuntimeKey)();
|
|
29
|
+
function geolocation(c) {
|
|
30
|
+
if (runtime === "workerd") {
|
|
31
|
+
return {
|
|
32
|
+
ip_address: c.req.header("true-client-ip") ?? c.req.header("cf-connecting-ip") ?? null,
|
|
33
|
+
city: c.req.header("cf-ipcity") ?? null,
|
|
34
|
+
country: c.req.header("cf-ipcountry") ?? null,
|
|
35
|
+
continent: c.req.header("cf-ipcontinent") ?? null,
|
|
36
|
+
longitude: c.req.header("cf-iplongitude") ? Number(c.req.header("cf-iplongitude")) : null,
|
|
37
|
+
latitude: c.req.header("cf-iplatitude") ? Number(c.req.header("cf-iplatitude")) : null,
|
|
38
|
+
region: c.req.header("cf-region-code") ?? null,
|
|
39
|
+
metro_code: c.req.header("cf-metro-code") ?? null,
|
|
40
|
+
postal_code: c.req.header("cf-postal-code") ?? null,
|
|
41
|
+
time_zone: c.req.header("cf-timezone") ?? null
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (c.req.header("x-vercel-id")) {
|
|
45
|
+
return {
|
|
46
|
+
ip_address: c.req.header("x-real-ip") ?? null,
|
|
47
|
+
city: c.req.header("x-vercel-ip-city") ?? null,
|
|
48
|
+
country: c.req.header("x-vercel-ip-country") ?? null,
|
|
49
|
+
continent: c.req.header("x-vercel-ip-continent") ?? null,
|
|
50
|
+
longitude: c.req.header("x-vercel-ip-longitude") ? Number(c.req.header("x-vercel-ip-longitude")) : null,
|
|
51
|
+
latitude: c.req.header("x-vercel-ip-latitude") ? Number(c.req.header("x-vercel-ip-latitude")) : null,
|
|
52
|
+
region: c.req.header("x-vercel-ip-country-region") ?? null,
|
|
53
|
+
metro_code: null,
|
|
54
|
+
postal_code: c.req.header("x-vercel-ip-postal-code") ?? null,
|
|
55
|
+
time_zone: null
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
ip_address: (0, import_ip.extractIpAddress)(c.req.header("CloudFront-Viewer-Address")),
|
|
60
|
+
city: c.req.header("CloudFront-Viewer-City") ?? null,
|
|
61
|
+
country: c.req.header("CloudFront-Viewer-Country") ?? null,
|
|
62
|
+
continent: null,
|
|
63
|
+
longitude: c.req.header("CloudFront-Viewer-Longitude") ? Number(c.req.header("CloudFront-Viewer-Longitude")) : null,
|
|
64
|
+
latitude: c.req.header("CloudFront-Viewer-Latitude") ? Number(c.req.header("CloudFront-Viewer-Latitude")) : null,
|
|
65
|
+
region: c.req.header("CloudFront-Viewer-Country-Region") ?? null,
|
|
66
|
+
metro_code: c.req.header("CloudFront-Viewer-Metro-Code") ?? null,
|
|
67
|
+
postal_code: c.req.header("CloudFront-Viewer-Postal-Code") ?? null,
|
|
68
|
+
time_zone: c.req.header("CloudFront-Viewer-Time-Zone") ?? null
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
72
|
+
0 && (module.exports = {
|
|
73
|
+
geolocation
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=geolocation.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hono/geolocation.ts"],"sourcesContent":["import { getRuntimeKey } from 'hono/adapter';\nimport { extractIpAddress } from '../utils/ip';\nimport type { Context } from 'hono';\n\nexport type Geolocation = {\n ip_address: string | null;\n city: string | null;\n country: string | null; // ISO 3166-1 alpha-2\n continent: string | null;\n longitude: number | null;\n latitude: number | null;\n region: string | null; // ISO 3166-2\n metro_code: string | null;\n postal_code: string | null;\n time_zone: string | null;\n};\n\nconst runtime = getRuntimeKey();\nexport function geolocation(c: Context): Geolocation | null {\n /** reference: https://developers.cloudflare.com/rules/transform/managed-transforms/reference/#add-visitor-location-headers */\n if (runtime === 'workerd') {\n return {\n ip_address: c.req.header('true-client-ip') ?? c.req.header('cf-connecting-ip') ?? null,\n city: c.req.header('cf-ipcity') ?? null,\n country: c.req.header('cf-ipcountry') ?? null,\n continent: c.req.header('cf-ipcontinent') ?? null,\n longitude: c.req.header('cf-iplongitude') ? Number(c.req.header('cf-iplongitude')) : null,\n latitude: c.req.header('cf-iplatitude') ? Number(c.req.header('cf-iplatitude')) : null,\n region: c.req.header('cf-region-code') ?? null,\n metro_code: c.req.header('cf-metro-code') ?? null,\n postal_code: c.req.header('cf-postal-code') ?? null,\n time_zone: c.req.header('cf-timezone') ?? null,\n };\n }\n\n /** https://github.com/vercel/vercel/blob/main/packages/functions/src/headers.ts */\n if (c.req.header('x-vercel-id')) {\n return {\n ip_address: c.req.header('x-real-ip') ?? null,\n city: c.req.header('x-vercel-ip-city') ?? null,\n country: c.req.header('x-vercel-ip-country') ?? null,\n continent: c.req.header('x-vercel-ip-continent') ?? null,\n longitude: c.req.header('x-vercel-ip-longitude')\n ? Number(c.req.header('x-vercel-ip-longitude'))\n : null,\n latitude: c.req.header('x-vercel-ip-latitude')\n ? Number(c.req.header('x-vercel-ip-latitude'))\n : null,\n region: c.req.header('x-vercel-ip-country-region') ?? null,\n metro_code: null,\n postal_code: c.req.header('x-vercel-ip-postal-code') ?? null,\n time_zone: null,\n };\n }\n\n // cloudfront\n // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/adding-cloudfront-headers.html#cloudfront-headers-viewer-location\n return {\n ip_address: extractIpAddress(c.req.header('CloudFront-Viewer-Address')),\n city: c.req.header('CloudFront-Viewer-City') ?? null,\n country: c.req.header('CloudFront-Viewer-Country') ?? null,\n continent: null,\n longitude: c.req.header('CloudFront-Viewer-Longitude')\n ? Number(c.req.header('CloudFront-Viewer-Longitude'))\n : null,\n latitude: c.req.header('CloudFront-Viewer-Latitude')\n ? Number(c.req.header('CloudFront-Viewer-Latitude'))\n : null,\n region: c.req.header('CloudFront-Viewer-Country-Region') ?? null,\n metro_code: c.req.header('CloudFront-Viewer-Metro-Code') ?? null,\n postal_code: c.req.header('CloudFront-Viewer-Postal-Code') ?? null,\n time_zone: c.req.header('CloudFront-Viewer-Time-Zone') ?? null,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAA8B;AAC9B,gBAAiC;AAgBjC,IAAM,cAAU,8BAAc;AACvB,SAAS,YAAY,GAAgC;AAE1D,MAAI,YAAY,WAAW;AACzB,WAAO;AAAA,MACL,YAAY,EAAE,IAAI,OAAO,gBAAgB,KAAK,EAAE,IAAI,OAAO,kBAAkB,KAAK;AAAA,MAClF,MAAM,EAAE,IAAI,OAAO,WAAW,KAAK;AAAA,MACnC,SAAS,EAAE,IAAI,OAAO,cAAc,KAAK;AAAA,MACzC,WAAW,EAAE,IAAI,OAAO,gBAAgB,KAAK;AAAA,MAC7C,WAAW,EAAE,IAAI,OAAO,gBAAgB,IAAI,OAAO,EAAE,IAAI,OAAO,gBAAgB,CAAC,IAAI;AAAA,MACrF,UAAU,EAAE,IAAI,OAAO,eAAe,IAAI,OAAO,EAAE,IAAI,OAAO,eAAe,CAAC,IAAI;AAAA,MAClF,QAAQ,EAAE,IAAI,OAAO,gBAAgB,KAAK;AAAA,MAC1C,YAAY,EAAE,IAAI,OAAO,eAAe,KAAK;AAAA,MAC7C,aAAa,EAAE,IAAI,OAAO,gBAAgB,KAAK;AAAA,MAC/C,WAAW,EAAE,IAAI,OAAO,aAAa,KAAK;AAAA,IAC5C;AAAA,EACF;AAGA,MAAI,EAAE,IAAI,OAAO,aAAa,GAAG;AAC/B,WAAO;AAAA,MACL,YAAY,EAAE,IAAI,OAAO,WAAW,KAAK;AAAA,MACzC,MAAM,EAAE,IAAI,OAAO,kBAAkB,KAAK;AAAA,MAC1C,SAAS,EAAE,IAAI,OAAO,qBAAqB,KAAK;AAAA,MAChD,WAAW,EAAE,IAAI,OAAO,uBAAuB,KAAK;AAAA,MACpD,WAAW,EAAE,IAAI,OAAO,uBAAuB,IAC3C,OAAO,EAAE,IAAI,OAAO,uBAAuB,CAAC,IAC5C;AAAA,MACJ,UAAU,EAAE,IAAI,OAAO,sBAAsB,IACzC,OAAO,EAAE,IAAI,OAAO,sBAAsB,CAAC,IAC3C;AAAA,MACJ,QAAQ,EAAE,IAAI,OAAO,4BAA4B,KAAK;AAAA,MACtD,YAAY;AAAA,MACZ,aAAa,EAAE,IAAI,OAAO,yBAAyB,KAAK;AAAA,MACxD,WAAW;AAAA,IACb;AAAA,EACF;AAIA,SAAO;AAAA,IACL,gBAAY,4BAAiB,EAAE,IAAI,OAAO,2BAA2B,CAAC;AAAA,IACtE,MAAM,EAAE,IAAI,OAAO,wBAAwB,KAAK;AAAA,IAChD,SAAS,EAAE,IAAI,OAAO,2BAA2B,KAAK;AAAA,IACtD,WAAW;AAAA,IACX,WAAW,EAAE,IAAI,OAAO,6BAA6B,IACjD,OAAO,EAAE,IAAI,OAAO,6BAA6B,CAAC,IAClD;AAAA,IACJ,UAAU,EAAE,IAAI,OAAO,4BAA4B,IAC/C,OAAO,EAAE,IAAI,OAAO,4BAA4B,CAAC,IACjD;AAAA,IACJ,QAAQ,EAAE,IAAI,OAAO,kCAAkC,KAAK;AAAA,IAC5D,YAAY,EAAE,IAAI,OAAO,8BAA8B,KAAK;AAAA,IAC5D,aAAa,EAAE,IAAI,OAAO,+BAA+B,KAAK;AAAA,IAC9D,WAAW,EAAE,IAAI,OAAO,6BAA6B,KAAK;AAAA,EAC5D;AACF;","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
|
|
3
|
+
type Geolocation = {
|
|
4
|
+
ip_address: string | null;
|
|
5
|
+
city: string | null;
|
|
6
|
+
country: string | null;
|
|
7
|
+
continent: string | null;
|
|
8
|
+
longitude: number | null;
|
|
9
|
+
latitude: number | null;
|
|
10
|
+
region: string | null;
|
|
11
|
+
metro_code: string | null;
|
|
12
|
+
postal_code: string | null;
|
|
13
|
+
time_zone: string | null;
|
|
14
|
+
};
|
|
15
|
+
declare function geolocation(c: Context): Geolocation | null;
|
|
16
|
+
|
|
17
|
+
export { type Geolocation, geolocation };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Context } from 'hono';
|
|
2
|
+
|
|
3
|
+
type Geolocation = {
|
|
4
|
+
ip_address: string | null;
|
|
5
|
+
city: string | null;
|
|
6
|
+
country: string | null;
|
|
7
|
+
continent: string | null;
|
|
8
|
+
longitude: number | null;
|
|
9
|
+
latitude: number | null;
|
|
10
|
+
region: string | null;
|
|
11
|
+
metro_code: string | null;
|
|
12
|
+
postal_code: string | null;
|
|
13
|
+
time_zone: string | null;
|
|
14
|
+
};
|
|
15
|
+
declare function geolocation(c: Context): Geolocation | null;
|
|
16
|
+
|
|
17
|
+
export { type Geolocation, geolocation };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// src/hono/geolocation.ts
|
|
2
|
+
import { getRuntimeKey } from "hono/adapter";
|
|
3
|
+
import { extractIpAddress } from "../utils/ip.mjs";
|
|
4
|
+
var runtime = getRuntimeKey();
|
|
5
|
+
function geolocation(c) {
|
|
6
|
+
if (runtime === "workerd") {
|
|
7
|
+
return {
|
|
8
|
+
ip_address: c.req.header("true-client-ip") ?? c.req.header("cf-connecting-ip") ?? null,
|
|
9
|
+
city: c.req.header("cf-ipcity") ?? null,
|
|
10
|
+
country: c.req.header("cf-ipcountry") ?? null,
|
|
11
|
+
continent: c.req.header("cf-ipcontinent") ?? null,
|
|
12
|
+
longitude: c.req.header("cf-iplongitude") ? Number(c.req.header("cf-iplongitude")) : null,
|
|
13
|
+
latitude: c.req.header("cf-iplatitude") ? Number(c.req.header("cf-iplatitude")) : null,
|
|
14
|
+
region: c.req.header("cf-region-code") ?? null,
|
|
15
|
+
metro_code: c.req.header("cf-metro-code") ?? null,
|
|
16
|
+
postal_code: c.req.header("cf-postal-code") ?? null,
|
|
17
|
+
time_zone: c.req.header("cf-timezone") ?? null
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (c.req.header("x-vercel-id")) {
|
|
21
|
+
return {
|
|
22
|
+
ip_address: c.req.header("x-real-ip") ?? null,
|
|
23
|
+
city: c.req.header("x-vercel-ip-city") ?? null,
|
|
24
|
+
country: c.req.header("x-vercel-ip-country") ?? null,
|
|
25
|
+
continent: c.req.header("x-vercel-ip-continent") ?? null,
|
|
26
|
+
longitude: c.req.header("x-vercel-ip-longitude") ? Number(c.req.header("x-vercel-ip-longitude")) : null,
|
|
27
|
+
latitude: c.req.header("x-vercel-ip-latitude") ? Number(c.req.header("x-vercel-ip-latitude")) : null,
|
|
28
|
+
region: c.req.header("x-vercel-ip-country-region") ?? null,
|
|
29
|
+
metro_code: null,
|
|
30
|
+
postal_code: c.req.header("x-vercel-ip-postal-code") ?? null,
|
|
31
|
+
time_zone: null
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
ip_address: extractIpAddress(c.req.header("CloudFront-Viewer-Address")),
|
|
36
|
+
city: c.req.header("CloudFront-Viewer-City") ?? null,
|
|
37
|
+
country: c.req.header("CloudFront-Viewer-Country") ?? null,
|
|
38
|
+
continent: null,
|
|
39
|
+
longitude: c.req.header("CloudFront-Viewer-Longitude") ? Number(c.req.header("CloudFront-Viewer-Longitude")) : null,
|
|
40
|
+
latitude: c.req.header("CloudFront-Viewer-Latitude") ? Number(c.req.header("CloudFront-Viewer-Latitude")) : null,
|
|
41
|
+
region: c.req.header("CloudFront-Viewer-Country-Region") ?? null,
|
|
42
|
+
metro_code: c.req.header("CloudFront-Viewer-Metro-Code") ?? null,
|
|
43
|
+
postal_code: c.req.header("CloudFront-Viewer-Postal-Code") ?? null,
|
|
44
|
+
time_zone: c.req.header("CloudFront-Viewer-Time-Zone") ?? null
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export {
|
|
48
|
+
geolocation
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=geolocation.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hono/geolocation.ts"],"sourcesContent":["import { getRuntimeKey } from 'hono/adapter';\nimport { extractIpAddress } from '../utils/ip';\nimport type { Context } from 'hono';\n\nexport type Geolocation = {\n ip_address: string | null;\n city: string | null;\n country: string | null; // ISO 3166-1 alpha-2\n continent: string | null;\n longitude: number | null;\n latitude: number | null;\n region: string | null; // ISO 3166-2\n metro_code: string | null;\n postal_code: string | null;\n time_zone: string | null;\n};\n\nconst runtime = getRuntimeKey();\nexport function geolocation(c: Context): Geolocation | null {\n /** reference: https://developers.cloudflare.com/rules/transform/managed-transforms/reference/#add-visitor-location-headers */\n if (runtime === 'workerd') {\n return {\n ip_address: c.req.header('true-client-ip') ?? c.req.header('cf-connecting-ip') ?? null,\n city: c.req.header('cf-ipcity') ?? null,\n country: c.req.header('cf-ipcountry') ?? null,\n continent: c.req.header('cf-ipcontinent') ?? null,\n longitude: c.req.header('cf-iplongitude') ? Number(c.req.header('cf-iplongitude')) : null,\n latitude: c.req.header('cf-iplatitude') ? Number(c.req.header('cf-iplatitude')) : null,\n region: c.req.header('cf-region-code') ?? null,\n metro_code: c.req.header('cf-metro-code') ?? null,\n postal_code: c.req.header('cf-postal-code') ?? null,\n time_zone: c.req.header('cf-timezone') ?? null,\n };\n }\n\n /** https://github.com/vercel/vercel/blob/main/packages/functions/src/headers.ts */\n if (c.req.header('x-vercel-id')) {\n return {\n ip_address: c.req.header('x-real-ip') ?? null,\n city: c.req.header('x-vercel-ip-city') ?? null,\n country: c.req.header('x-vercel-ip-country') ?? null,\n continent: c.req.header('x-vercel-ip-continent') ?? null,\n longitude: c.req.header('x-vercel-ip-longitude')\n ? Number(c.req.header('x-vercel-ip-longitude'))\n : null,\n latitude: c.req.header('x-vercel-ip-latitude')\n ? Number(c.req.header('x-vercel-ip-latitude'))\n : null,\n region: c.req.header('x-vercel-ip-country-region') ?? null,\n metro_code: null,\n postal_code: c.req.header('x-vercel-ip-postal-code') ?? null,\n time_zone: null,\n };\n }\n\n // cloudfront\n // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/adding-cloudfront-headers.html#cloudfront-headers-viewer-location\n return {\n ip_address: extractIpAddress(c.req.header('CloudFront-Viewer-Address')),\n city: c.req.header('CloudFront-Viewer-City') ?? null,\n country: c.req.header('CloudFront-Viewer-Country') ?? null,\n continent: null,\n longitude: c.req.header('CloudFront-Viewer-Longitude')\n ? Number(c.req.header('CloudFront-Viewer-Longitude'))\n : null,\n latitude: c.req.header('CloudFront-Viewer-Latitude')\n ? Number(c.req.header('CloudFront-Viewer-Latitude'))\n : null,\n region: c.req.header('CloudFront-Viewer-Country-Region') ?? null,\n metro_code: c.req.header('CloudFront-Viewer-Metro-Code') ?? null,\n postal_code: c.req.header('CloudFront-Viewer-Postal-Code') ?? null,\n time_zone: c.req.header('CloudFront-Viewer-Time-Zone') ?? null,\n };\n}\n"],"mappings":";AAAA,SAAS,qBAAqB;AAC9B,SAAS,wBAAwB;AAgBjC,IAAM,UAAU,cAAc;AACvB,SAAS,YAAY,GAAgC;AAE1D,MAAI,YAAY,WAAW;AACzB,WAAO;AAAA,MACL,YAAY,EAAE,IAAI,OAAO,gBAAgB,KAAK,EAAE,IAAI,OAAO,kBAAkB,KAAK;AAAA,MAClF,MAAM,EAAE,IAAI,OAAO,WAAW,KAAK;AAAA,MACnC,SAAS,EAAE,IAAI,OAAO,cAAc,KAAK;AAAA,MACzC,WAAW,EAAE,IAAI,OAAO,gBAAgB,KAAK;AAAA,MAC7C,WAAW,EAAE,IAAI,OAAO,gBAAgB,IAAI,OAAO,EAAE,IAAI,OAAO,gBAAgB,CAAC,IAAI;AAAA,MACrF,UAAU,EAAE,IAAI,OAAO,eAAe,IAAI,OAAO,EAAE,IAAI,OAAO,eAAe,CAAC,IAAI;AAAA,MAClF,QAAQ,EAAE,IAAI,OAAO,gBAAgB,KAAK;AAAA,MAC1C,YAAY,EAAE,IAAI,OAAO,eAAe,KAAK;AAAA,MAC7C,aAAa,EAAE,IAAI,OAAO,gBAAgB,KAAK;AAAA,MAC/C,WAAW,EAAE,IAAI,OAAO,aAAa,KAAK;AAAA,IAC5C;AAAA,EACF;AAGA,MAAI,EAAE,IAAI,OAAO,aAAa,GAAG;AAC/B,WAAO;AAAA,MACL,YAAY,EAAE,IAAI,OAAO,WAAW,KAAK;AAAA,MACzC,MAAM,EAAE,IAAI,OAAO,kBAAkB,KAAK;AAAA,MAC1C,SAAS,EAAE,IAAI,OAAO,qBAAqB,KAAK;AAAA,MAChD,WAAW,EAAE,IAAI,OAAO,uBAAuB,KAAK;AAAA,MACpD,WAAW,EAAE,IAAI,OAAO,uBAAuB,IAC3C,OAAO,EAAE,IAAI,OAAO,uBAAuB,CAAC,IAC5C;AAAA,MACJ,UAAU,EAAE,IAAI,OAAO,sBAAsB,IACzC,OAAO,EAAE,IAAI,OAAO,sBAAsB,CAAC,IAC3C;AAAA,MACJ,QAAQ,EAAE,IAAI,OAAO,4BAA4B,KAAK;AAAA,MACtD,YAAY;AAAA,MACZ,aAAa,EAAE,IAAI,OAAO,yBAAyB,KAAK;AAAA,MACxD,WAAW;AAAA,IACb;AAAA,EACF;AAIA,SAAO;AAAA,IACL,YAAY,iBAAiB,EAAE,IAAI,OAAO,2BAA2B,CAAC;AAAA,IACtE,MAAM,EAAE,IAAI,OAAO,wBAAwB,KAAK;AAAA,IAChD,SAAS,EAAE,IAAI,OAAO,2BAA2B,KAAK;AAAA,IACtD,WAAW;AAAA,IACX,WAAW,EAAE,IAAI,OAAO,6BAA6B,IACjD,OAAO,EAAE,IAAI,OAAO,6BAA6B,CAAC,IAClD;AAAA,IACJ,UAAU,EAAE,IAAI,OAAO,4BAA4B,IAC/C,OAAO,EAAE,IAAI,OAAO,4BAA4B,CAAC,IACjD;AAAA,IACJ,QAAQ,EAAE,IAAI,OAAO,kCAAkC,KAAK;AAAA,IAC5D,YAAY,EAAE,IAAI,OAAO,8BAA8B,KAAK;AAAA,IAC5D,aAAa,EAAE,IAAI,OAAO,+BAA+B,KAAK;AAAA,IAC9D,WAAW,EAAE,IAAI,OAAO,6BAA6B,KAAK;AAAA,EAC5D;AACF;","names":[]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/hono/handler.ts
|
|
21
|
+
var handler_exports = {};
|
|
22
|
+
__export(handler_exports, {
|
|
23
|
+
errorHandler: () => errorHandler,
|
|
24
|
+
isAxiosError: () => isAxiosError
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(handler_exports);
|
|
27
|
+
var import_detail = require("../error/detail.cjs");
|
|
28
|
+
var import_status = require("../error/status.cjs");
|
|
29
|
+
function isAxiosError(payload) {
|
|
30
|
+
return payload !== null && typeof payload === "object" && "isAxiosError" in payload && payload.isAxiosError === true;
|
|
31
|
+
}
|
|
32
|
+
function errorHandler(error, c) {
|
|
33
|
+
const requestId = c.get("requestId");
|
|
34
|
+
const servingData = `${c.req.method}: ${c.req.path}`;
|
|
35
|
+
const details = import_detail.Details.new().requestInfo({ requestId, servingData });
|
|
36
|
+
if (error instanceof import_status.StatusError) {
|
|
37
|
+
error.body?.error?.details?.push(...details.list);
|
|
38
|
+
const badRequest = error.body?.error?.details.find((d) => d.type === import_detail.DetailType.BAD_REQUEST);
|
|
39
|
+
if (badRequest) console.warn(servingData, badRequest);
|
|
40
|
+
return c.json(error.body, error.status);
|
|
41
|
+
}
|
|
42
|
+
if (error instanceof SyntaxError) {
|
|
43
|
+
if (/^Cannot convert .* to a BigInt$/.test(error.message)) {
|
|
44
|
+
return import_status.Status.invalidArgument(`Invalid number. ${error.message}`).response(details);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (isAxiosError(error)) {
|
|
48
|
+
console.error({
|
|
49
|
+
status: error.status,
|
|
50
|
+
message: error.message,
|
|
51
|
+
request: {
|
|
52
|
+
method: error.config?.method,
|
|
53
|
+
url: error.config?.url,
|
|
54
|
+
data: error.config?.data
|
|
55
|
+
},
|
|
56
|
+
response: { data: error.response?.data }
|
|
57
|
+
});
|
|
58
|
+
return import_status.Status.internal("Axios error").response(details);
|
|
59
|
+
}
|
|
60
|
+
console.error(`Unknown error: ${servingData}`, error);
|
|
61
|
+
return import_status.Status.internal("Unknown error").response(details);
|
|
62
|
+
}
|
|
63
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
64
|
+
0 && (module.exports = {
|
|
65
|
+
errorHandler,
|
|
66
|
+
isAxiosError
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=handler.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hono/handler.ts"],"sourcesContent":["import { DetailType, Details } from '../error/detail';\nimport { Status, StatusError } from '../error/status';\nimport type { AxiosError } from 'axios';\nimport type { Context } from 'hono';\nimport type { RequestIdVariables } from 'hono/request-id';\nimport type { Bindings, HTTPResponseError } from 'hono/types';\nimport type { ContentfulStatusCode } from 'hono/utils/http-status';\n\nexport type Env = {\n Variables: RequestIdVariables;\n Bindings?: Bindings;\n};\n\nexport function isAxiosError(payload: unknown): payload is AxiosError {\n return (\n payload !== null &&\n typeof payload === 'object' &&\n 'isAxiosError' in payload &&\n payload.isAxiosError === true\n );\n}\n\nexport function errorHandler<E extends Env = never>(\n error: Error | HTTPResponseError,\n c: Context<E>\n): Response | Promise<Response> {\n const requestId = c.get('requestId');\n const servingData = `${c.req.method}: ${c.req.path}`;\n const details = Details.new().requestInfo({ requestId, servingData });\n\n if (error instanceof StatusError) {\n error.body?.error?.details?.push(...details.list);\n const badRequest = error.body?.error?.details.find((d) => d.type === DetailType.BAD_REQUEST);\n if (badRequest) console.warn(servingData, badRequest);\n return c.json(error.body, error.status as ContentfulStatusCode);\n }\n\n if (error instanceof SyntaxError) {\n if (/^Cannot convert .* to a BigInt$/.test(error.message)) {\n return Status.invalidArgument(`Invalid number. ${error.message}`).response(details);\n }\n }\n\n if (isAxiosError(error)) {\n console.error({\n status: error.status,\n message: error.message,\n request: {\n method: error.config?.method,\n url: error.config?.url,\n data: error.config?.data,\n },\n response: { data: error.response?.data },\n });\n return Status.internal('Axios error').response(details);\n }\n\n console.error(`Unknown error: ${servingData}`, error);\n return Status.internal('Unknown error').response(details);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAoC;AACpC,oBAAoC;AAY7B,SAAS,aAAa,SAAyC;AACpE,SACE,YAAY,QACZ,OAAO,YAAY,YACnB,kBAAkB,WAClB,QAAQ,iBAAiB;AAE7B;AAEO,SAAS,aACd,OACA,GAC8B;AAC9B,QAAM,YAAY,EAAE,IAAI,WAAW;AACnC,QAAM,cAAc,GAAG,EAAE,IAAI,MAAM,KAAK,EAAE,IAAI,IAAI;AAClD,QAAM,UAAU,sBAAQ,IAAI,EAAE,YAAY,EAAE,WAAW,YAAY,CAAC;AAEpE,MAAI,iBAAiB,2BAAa;AAChC,UAAM,MAAM,OAAO,SAAS,KAAK,GAAG,QAAQ,IAAI;AAChD,UAAM,aAAa,MAAM,MAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,yBAAW,WAAW;AAC3F,QAAI,WAAY,SAAQ,KAAK,aAAa,UAAU;AACpD,WAAO,EAAE,KAAK,MAAM,MAAM,MAAM,MAA8B;AAAA,EAChE;AAEA,MAAI,iBAAiB,aAAa;AAChC,QAAI,kCAAkC,KAAK,MAAM,OAAO,GAAG;AACzD,aAAO,qBAAO,gBAAgB,mBAAmB,MAAM,OAAO,EAAE,EAAE,SAAS,OAAO;AAAA,IACpF;AAAA,EACF;AAEA,MAAI,aAAa,KAAK,GAAG;AACvB,YAAQ,MAAM;AAAA,MACZ,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf,SAAS;AAAA,QACP,QAAQ,MAAM,QAAQ;AAAA,QACtB,KAAK,MAAM,QAAQ;AAAA,QACnB,MAAM,MAAM,QAAQ;AAAA,MACtB;AAAA,MACA,UAAU,EAAE,MAAM,MAAM,UAAU,KAAK;AAAA,IACzC,CAAC;AACD,WAAO,qBAAO,SAAS,aAAa,EAAE,SAAS,OAAO;AAAA,EACxD;AAEA,UAAQ,MAAM,kBAAkB,WAAW,IAAI,KAAK;AACpD,SAAO,qBAAO,SAAS,eAAe,EAAE,SAAS,OAAO;AAC1D;","names":[]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AxiosError } from 'axios';
|
|
2
|
+
import { Context } from 'hono';
|
|
3
|
+
import { RequestIdVariables } from 'hono/request-id';
|
|
4
|
+
import { Bindings, HTTPResponseError } from 'hono/types';
|
|
5
|
+
|
|
6
|
+
type Env = {
|
|
7
|
+
Variables: RequestIdVariables;
|
|
8
|
+
Bindings?: Bindings;
|
|
9
|
+
};
|
|
10
|
+
declare function isAxiosError(payload: unknown): payload is AxiosError;
|
|
11
|
+
declare function errorHandler<E extends Env = never>(error: Error | HTTPResponseError, c: Context<E>): Response | Promise<Response>;
|
|
12
|
+
|
|
13
|
+
export { type Env, errorHandler, isAxiosError };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AxiosError } from 'axios';
|
|
2
|
+
import { Context } from 'hono';
|
|
3
|
+
import { RequestIdVariables } from 'hono/request-id';
|
|
4
|
+
import { Bindings, HTTPResponseError } from 'hono/types';
|
|
5
|
+
|
|
6
|
+
type Env = {
|
|
7
|
+
Variables: RequestIdVariables;
|
|
8
|
+
Bindings?: Bindings;
|
|
9
|
+
};
|
|
10
|
+
declare function isAxiosError(payload: unknown): payload is AxiosError;
|
|
11
|
+
declare function errorHandler<E extends Env = never>(error: Error | HTTPResponseError, c: Context<E>): Response | Promise<Response>;
|
|
12
|
+
|
|
13
|
+
export { type Env, errorHandler, isAxiosError };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/hono/handler.ts
|
|
2
|
+
import { DetailType, Details } from "../error/detail.mjs";
|
|
3
|
+
import { Status, StatusError } from "../error/status.mjs";
|
|
4
|
+
function isAxiosError(payload) {
|
|
5
|
+
return payload !== null && typeof payload === "object" && "isAxiosError" in payload && payload.isAxiosError === true;
|
|
6
|
+
}
|
|
7
|
+
function errorHandler(error, c) {
|
|
8
|
+
const requestId = c.get("requestId");
|
|
9
|
+
const servingData = `${c.req.method}: ${c.req.path}`;
|
|
10
|
+
const details = Details.new().requestInfo({ requestId, servingData });
|
|
11
|
+
if (error instanceof StatusError) {
|
|
12
|
+
error.body?.error?.details?.push(...details.list);
|
|
13
|
+
const badRequest = error.body?.error?.details.find((d) => d.type === DetailType.BAD_REQUEST);
|
|
14
|
+
if (badRequest) console.warn(servingData, badRequest);
|
|
15
|
+
return c.json(error.body, error.status);
|
|
16
|
+
}
|
|
17
|
+
if (error instanceof SyntaxError) {
|
|
18
|
+
if (/^Cannot convert .* to a BigInt$/.test(error.message)) {
|
|
19
|
+
return Status.invalidArgument(`Invalid number. ${error.message}`).response(details);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (isAxiosError(error)) {
|
|
23
|
+
console.error({
|
|
24
|
+
status: error.status,
|
|
25
|
+
message: error.message,
|
|
26
|
+
request: {
|
|
27
|
+
method: error.config?.method,
|
|
28
|
+
url: error.config?.url,
|
|
29
|
+
data: error.config?.data
|
|
30
|
+
},
|
|
31
|
+
response: { data: error.response?.data }
|
|
32
|
+
});
|
|
33
|
+
return Status.internal("Axios error").response(details);
|
|
34
|
+
}
|
|
35
|
+
console.error(`Unknown error: ${servingData}`, error);
|
|
36
|
+
return Status.internal("Unknown error").response(details);
|
|
37
|
+
}
|
|
38
|
+
export {
|
|
39
|
+
errorHandler,
|
|
40
|
+
isAxiosError
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=handler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hono/handler.ts"],"sourcesContent":["import { DetailType, Details } from '../error/detail';\nimport { Status, StatusError } from '../error/status';\nimport type { AxiosError } from 'axios';\nimport type { Context } from 'hono';\nimport type { RequestIdVariables } from 'hono/request-id';\nimport type { Bindings, HTTPResponseError } from 'hono/types';\nimport type { ContentfulStatusCode } from 'hono/utils/http-status';\n\nexport type Env = {\n Variables: RequestIdVariables;\n Bindings?: Bindings;\n};\n\nexport function isAxiosError(payload: unknown): payload is AxiosError {\n return (\n payload !== null &&\n typeof payload === 'object' &&\n 'isAxiosError' in payload &&\n payload.isAxiosError === true\n );\n}\n\nexport function errorHandler<E extends Env = never>(\n error: Error | HTTPResponseError,\n c: Context<E>\n): Response | Promise<Response> {\n const requestId = c.get('requestId');\n const servingData = `${c.req.method}: ${c.req.path}`;\n const details = Details.new().requestInfo({ requestId, servingData });\n\n if (error instanceof StatusError) {\n error.body?.error?.details?.push(...details.list);\n const badRequest = error.body?.error?.details.find((d) => d.type === DetailType.BAD_REQUEST);\n if (badRequest) console.warn(servingData, badRequest);\n return c.json(error.body, error.status as ContentfulStatusCode);\n }\n\n if (error instanceof SyntaxError) {\n if (/^Cannot convert .* to a BigInt$/.test(error.message)) {\n return Status.invalidArgument(`Invalid number. ${error.message}`).response(details);\n }\n }\n\n if (isAxiosError(error)) {\n console.error({\n status: error.status,\n message: error.message,\n request: {\n method: error.config?.method,\n url: error.config?.url,\n data: error.config?.data,\n },\n response: { data: error.response?.data },\n });\n return Status.internal('Axios error').response(details);\n }\n\n console.error(`Unknown error: ${servingData}`, error);\n return Status.internal('Unknown error').response(details);\n}\n"],"mappings":";AAAA,SAAS,YAAY,eAAe;AACpC,SAAS,QAAQ,mBAAmB;AAY7B,SAAS,aAAa,SAAyC;AACpE,SACE,YAAY,QACZ,OAAO,YAAY,YACnB,kBAAkB,WAClB,QAAQ,iBAAiB;AAE7B;AAEO,SAAS,aACd,OACA,GAC8B;AAC9B,QAAM,YAAY,EAAE,IAAI,WAAW;AACnC,QAAM,cAAc,GAAG,EAAE,IAAI,MAAM,KAAK,EAAE,IAAI,IAAI;AAClD,QAAM,UAAU,QAAQ,IAAI,EAAE,YAAY,EAAE,WAAW,YAAY,CAAC;AAEpE,MAAI,iBAAiB,aAAa;AAChC,UAAM,MAAM,OAAO,SAAS,KAAK,GAAG,QAAQ,IAAI;AAChD,UAAM,aAAa,MAAM,MAAM,OAAO,QAAQ,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW,WAAW;AAC3F,QAAI,WAAY,SAAQ,KAAK,aAAa,UAAU;AACpD,WAAO,EAAE,KAAK,MAAM,MAAM,MAAM,MAA8B;AAAA,EAChE;AAEA,MAAI,iBAAiB,aAAa;AAChC,QAAI,kCAAkC,KAAK,MAAM,OAAO,GAAG;AACzD,aAAO,OAAO,gBAAgB,mBAAmB,MAAM,OAAO,EAAE,EAAE,SAAS,OAAO;AAAA,IACpF;AAAA,EACF;AAEA,MAAI,aAAa,KAAK,GAAG;AACvB,YAAQ,MAAM;AAAA,MACZ,QAAQ,MAAM;AAAA,MACd,SAAS,MAAM;AAAA,MACf,SAAS;AAAA,QACP,QAAQ,MAAM,QAAQ;AAAA,QACtB,KAAK,MAAM,QAAQ;AAAA,QACnB,MAAM,MAAM,QAAQ;AAAA,MACtB;AAAA,MACA,UAAU,EAAE,MAAM,MAAM,UAAU,KAAK;AAAA,IACzC,CAAC;AACD,WAAO,OAAO,SAAS,aAAa,EAAE,SAAS,OAAO;AAAA,EACxD;AAEA,UAAQ,MAAM,kBAAkB,WAAW,IAAI,KAAK;AACpD,SAAO,OAAO,SAAS,eAAe,EAAE,SAAS,OAAO;AAC1D;","names":[]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/hono/index.ts
|
|
21
|
+
var hono_exports = {};
|
|
22
|
+
__export(hono_exports, {
|
|
23
|
+
authorizer: () => import_authorizer.authorizer,
|
|
24
|
+
bigintId: () => import_validator.bigintId,
|
|
25
|
+
csrf: () => import_csrf.csrf,
|
|
26
|
+
errorHandler: () => import_handler.errorHandler,
|
|
27
|
+
geolocation: () => import_geolocation.geolocation,
|
|
28
|
+
zValidator: () => import_validator.zValidator
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(hono_exports);
|
|
31
|
+
var import_validator = require("./validator.cjs");
|
|
32
|
+
var import_handler = require("./handler.cjs");
|
|
33
|
+
var import_geolocation = require("./geolocation.cjs");
|
|
34
|
+
var import_authorizer = require("./authorizer.cjs");
|
|
35
|
+
var import_csrf = require("./csrf.cjs");
|
|
36
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
37
|
+
0 && (module.exports = {
|
|
38
|
+
authorizer,
|
|
39
|
+
bigintId,
|
|
40
|
+
csrf,
|
|
41
|
+
errorHandler,
|
|
42
|
+
geolocation,
|
|
43
|
+
zValidator
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/hono/index.ts"],"sourcesContent":["export { zValidator, bigintId } from './validator';\nexport { errorHandler } from './handler';\nexport { geolocation } from './geolocation';\nexport { authorizer, type AuthorizerConfig } from './authorizer';\nexport { csrf, type CSRFConfig, type CSRFIgnoreRule } from './csrf';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAAqC;AACrC,qBAA6B;AAC7B,yBAA4B;AAC5B,wBAAkD;AAClD,kBAA2D;","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { bigintId, zValidator } from './validator.cjs';
|
|
2
|
+
export { errorHandler } from './handler.cjs';
|
|
3
|
+
export { geolocation } from './geolocation.cjs';
|
|
4
|
+
export { AuthorizerConfig, authorizer } from './authorizer.cjs';
|
|
5
|
+
export { CSRFConfig, CSRFIgnoreRule, csrf } from './csrf.cjs';
|
|
6
|
+
import 'zod/mini';
|
|
7
|
+
import 'hono';
|
|
8
|
+
import 'zod/v4/core';
|
|
9
|
+
import 'zod';
|
|
10
|
+
import 'axios';
|
|
11
|
+
import 'hono/request-id';
|
|
12
|
+
import 'hono/types';
|