hono 3.5.6 → 3.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/middleware/secure-headers/index.js +24 -7
- package/dist/cjs/request.js +10 -1
- package/dist/cjs/utils/body.js +10 -7
- package/dist/cjs/utils/buffer.js +28 -0
- package/dist/cjs/validator/validator.js +19 -3
- package/dist/middleware/secure-headers/index.js +24 -7
- package/dist/request.js +10 -1
- package/dist/types/middleware/secure-headers/index.d.ts +39 -12
- package/dist/types/request.d.ts +12 -1
- package/dist/types/utils/body.d.ts +1 -1
- package/dist/types/utils/buffer.d.ts +1 -0
- package/dist/utils/body.js +10 -7
- package/dist/utils/buffer.js +27 -0
- package/dist/validator/validator.js +19 -3
- package/package.json +1 -1
|
@@ -51,18 +51,35 @@ const DEFAULT_OPTIONS = {
|
|
|
51
51
|
};
|
|
52
52
|
const secureHeaders = (customOptions) => {
|
|
53
53
|
const options = { ...DEFAULT_OPTIONS, ...customOptions };
|
|
54
|
-
const headersToSet = Object.entries(HEADERS_MAP).filter(([key]) => options[key]).map(([,
|
|
54
|
+
const headersToSet = Object.entries(HEADERS_MAP).filter(([key]) => options[key]).map(([key, defaultValue]) => {
|
|
55
|
+
const overrideValue = options[key];
|
|
56
|
+
if (typeof overrideValue === "string")
|
|
57
|
+
return [defaultValue[0], overrideValue];
|
|
58
|
+
return defaultValue;
|
|
59
|
+
});
|
|
60
|
+
if (options.contentSecurityPolicy) {
|
|
61
|
+
const cspDirectives = Object.entries(options.contentSecurityPolicy).map(([directive, value]) => {
|
|
62
|
+
directive = directive.replace(
|
|
63
|
+
/[A-Z]+(?![a-z])|[A-Z]/g,
|
|
64
|
+
(match, offset) => (offset ? "-" : "") + match.toLowerCase()
|
|
65
|
+
);
|
|
66
|
+
return `${directive} ${Array.isArray(value) ? value.join(" ") : value}`;
|
|
67
|
+
}).join("; ");
|
|
68
|
+
headersToSet.push(["Content-Security-Policy", cspDirectives]);
|
|
69
|
+
}
|
|
70
|
+
if (options.reportingEndpoints) {
|
|
71
|
+
const reportingEndpoints = options.reportingEndpoints.map((endpoint) => `${endpoint.name}="${endpoint.url}"`).join(", ");
|
|
72
|
+
headersToSet.push(["Reporting-Endpoints", reportingEndpoints]);
|
|
73
|
+
}
|
|
74
|
+
if (options.reportTo) {
|
|
75
|
+
const reportToOptions = options.reportTo.map((option) => JSON.stringify(option)).join(", ");
|
|
76
|
+
headersToSet.push(["Report-To", reportToOptions]);
|
|
77
|
+
}
|
|
55
78
|
return async (ctx, next) => {
|
|
56
79
|
await next();
|
|
57
80
|
headersToSet.forEach(([header, value]) => {
|
|
58
81
|
ctx.res.headers.set(header, value);
|
|
59
82
|
});
|
|
60
|
-
if (options.contentSecurityPolicy) {
|
|
61
|
-
const cspDirectives = Object.entries(options.contentSecurityPolicy).map(([directive, sources]) => {
|
|
62
|
-
return `${directive} ${sources.join(" ")}`;
|
|
63
|
-
}).join("; ");
|
|
64
|
-
ctx.res.headers.set("Content-Security-Policy", cspDirectives);
|
|
65
|
-
}
|
|
66
83
|
ctx.res.headers.delete("X-Powered-By");
|
|
67
84
|
};
|
|
68
85
|
};
|
package/dist/cjs/request.js
CHANGED
|
@@ -32,6 +32,11 @@ class HonoRequest {
|
|
|
32
32
|
const cachedBody = bodyCache[key];
|
|
33
33
|
if (cachedBody)
|
|
34
34
|
return cachedBody;
|
|
35
|
+
if (bodyCache.arrayBuffer) {
|
|
36
|
+
return (async () => {
|
|
37
|
+
return await new Response(bodyCache.arrayBuffer)[key]();
|
|
38
|
+
})();
|
|
39
|
+
}
|
|
35
40
|
return bodyCache[key] = raw[key]();
|
|
36
41
|
};
|
|
37
42
|
this.raw = request;
|
|
@@ -84,7 +89,11 @@ class HonoRequest {
|
|
|
84
89
|
}
|
|
85
90
|
}
|
|
86
91
|
async parseBody() {
|
|
87
|
-
|
|
92
|
+
if (this.bodyCache.parsedBody)
|
|
93
|
+
return this.bodyCache.parsedBody;
|
|
94
|
+
const parsedBody = await (0, import_body.parseBody)(this);
|
|
95
|
+
this.bodyCache.parsedBody = parsedBody;
|
|
96
|
+
return parsedBody;
|
|
88
97
|
}
|
|
89
98
|
json() {
|
|
90
99
|
return this.cachedBody("json");
|
package/dist/cjs/utils/body.js
CHANGED
|
@@ -21,15 +21,18 @@ __export(body_exports, {
|
|
|
21
21
|
parseBody: () => parseBody
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(body_exports);
|
|
24
|
-
const parseBody = async (
|
|
24
|
+
const parseBody = async (request) => {
|
|
25
25
|
let body = {};
|
|
26
|
-
const contentType =
|
|
26
|
+
const contentType = request.headers.get("Content-Type");
|
|
27
27
|
if (contentType && (contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded"))) {
|
|
28
|
-
const
|
|
29
|
-
(
|
|
30
|
-
form
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
const formData = await request.formData();
|
|
29
|
+
if (formData) {
|
|
30
|
+
const form = {};
|
|
31
|
+
formData.forEach((value, key) => {
|
|
32
|
+
form[key] = value;
|
|
33
|
+
});
|
|
34
|
+
body = form;
|
|
35
|
+
}
|
|
33
36
|
}
|
|
34
37
|
return body;
|
|
35
38
|
};
|
package/dist/cjs/utils/buffer.js
CHANGED
|
@@ -18,6 +18,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
19
|
var buffer_exports = {};
|
|
20
20
|
__export(buffer_exports, {
|
|
21
|
+
bufferToFormData: () => bufferToFormData,
|
|
21
22
|
bufferToString: () => bufferToString,
|
|
22
23
|
equal: () => equal,
|
|
23
24
|
timingSafeEqual: () => timingSafeEqual
|
|
@@ -59,8 +60,35 @@ const bufferToString = (buffer) => {
|
|
|
59
60
|
}
|
|
60
61
|
return buffer;
|
|
61
62
|
};
|
|
63
|
+
const _decodeURIComponent = decodeURIComponent;
|
|
64
|
+
const bufferToFormData = (arrayBuffer, contentType) => {
|
|
65
|
+
const decoder = new TextDecoder("utf-8");
|
|
66
|
+
const content = decoder.decode(arrayBuffer);
|
|
67
|
+
const formData = new FormData();
|
|
68
|
+
const boundaryMatch = contentType.match(/boundary=(.+)/);
|
|
69
|
+
const boundary = boundaryMatch ? boundaryMatch[1] : "";
|
|
70
|
+
if (contentType.startsWith("multipart/form-data") && boundary) {
|
|
71
|
+
const parts = content.split("--" + boundary).slice(1, -1);
|
|
72
|
+
for (const part of parts) {
|
|
73
|
+
const [header, body] = part.split("\r\n\r\n");
|
|
74
|
+
const nameMatch = header.match(/name="([^"]+)"/);
|
|
75
|
+
if (nameMatch) {
|
|
76
|
+
const name = nameMatch[1];
|
|
77
|
+
formData.append(name, body.trim());
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
81
|
+
const pairs = content.split("&");
|
|
82
|
+
for (const pair of pairs) {
|
|
83
|
+
const [key, value] = pair.split("=");
|
|
84
|
+
formData.append(_decodeURIComponent(key), _decodeURIComponent(value));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return formData;
|
|
88
|
+
};
|
|
62
89
|
// Annotate the CommonJS export names for ESM import in node:
|
|
63
90
|
0 && (module.exports = {
|
|
91
|
+
bufferToFormData,
|
|
64
92
|
bufferToString,
|
|
65
93
|
equal,
|
|
66
94
|
timingSafeEqual
|
|
@@ -22,13 +22,17 @@ __export(validator_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(validator_exports);
|
|
24
24
|
var import_cookie = require("../helper/cookie");
|
|
25
|
+
var import_buffer = require("../utils/buffer");
|
|
25
26
|
const validator = (target, validationFunc) => {
|
|
26
27
|
return async (c, next) => {
|
|
27
28
|
let value = {};
|
|
28
29
|
switch (target) {
|
|
29
30
|
case "json":
|
|
30
31
|
try {
|
|
31
|
-
|
|
32
|
+
const arrayBuffer = c.req.bodyCache.arrayBuffer ?? await c.req.raw.arrayBuffer();
|
|
33
|
+
value = await new Response(arrayBuffer).json();
|
|
34
|
+
c.req.bodyCache.json = value;
|
|
35
|
+
c.req.bodyCache.arrayBuffer = arrayBuffer;
|
|
32
36
|
} catch {
|
|
33
37
|
console.error("Error: Malformed JSON in request body");
|
|
34
38
|
return c.json(
|
|
@@ -40,9 +44,21 @@ const validator = (target, validationFunc) => {
|
|
|
40
44
|
);
|
|
41
45
|
}
|
|
42
46
|
break;
|
|
43
|
-
case "form":
|
|
44
|
-
|
|
47
|
+
case "form": {
|
|
48
|
+
const contentType = c.req.header("Content-Type");
|
|
49
|
+
if (contentType) {
|
|
50
|
+
const arrayBuffer = c.req.bodyCache.arrayBuffer ?? await c.req.raw.arrayBuffer();
|
|
51
|
+
const formData = (0, import_buffer.bufferToFormData)(arrayBuffer, contentType);
|
|
52
|
+
const form = {};
|
|
53
|
+
formData.forEach((value2, key) => {
|
|
54
|
+
form[key] = value2;
|
|
55
|
+
});
|
|
56
|
+
value = form;
|
|
57
|
+
c.req.bodyCache.formData = formData;
|
|
58
|
+
c.req.bodyCache.arrayBuffer = arrayBuffer;
|
|
59
|
+
}
|
|
45
60
|
break;
|
|
61
|
+
}
|
|
46
62
|
case "query":
|
|
47
63
|
value = Object.fromEntries(
|
|
48
64
|
Object.entries(c.req.queries()).map(([k, v]) => {
|
|
@@ -29,18 +29,35 @@ var DEFAULT_OPTIONS = {
|
|
|
29
29
|
};
|
|
30
30
|
var secureHeaders = (customOptions) => {
|
|
31
31
|
const options = { ...DEFAULT_OPTIONS, ...customOptions };
|
|
32
|
-
const headersToSet = Object.entries(HEADERS_MAP).filter(([key]) => options[key]).map(([,
|
|
32
|
+
const headersToSet = Object.entries(HEADERS_MAP).filter(([key]) => options[key]).map(([key, defaultValue]) => {
|
|
33
|
+
const overrideValue = options[key];
|
|
34
|
+
if (typeof overrideValue === "string")
|
|
35
|
+
return [defaultValue[0], overrideValue];
|
|
36
|
+
return defaultValue;
|
|
37
|
+
});
|
|
38
|
+
if (options.contentSecurityPolicy) {
|
|
39
|
+
const cspDirectives = Object.entries(options.contentSecurityPolicy).map(([directive, value]) => {
|
|
40
|
+
directive = directive.replace(
|
|
41
|
+
/[A-Z]+(?![a-z])|[A-Z]/g,
|
|
42
|
+
(match, offset) => (offset ? "-" : "") + match.toLowerCase()
|
|
43
|
+
);
|
|
44
|
+
return `${directive} ${Array.isArray(value) ? value.join(" ") : value}`;
|
|
45
|
+
}).join("; ");
|
|
46
|
+
headersToSet.push(["Content-Security-Policy", cspDirectives]);
|
|
47
|
+
}
|
|
48
|
+
if (options.reportingEndpoints) {
|
|
49
|
+
const reportingEndpoints = options.reportingEndpoints.map((endpoint) => `${endpoint.name}="${endpoint.url}"`).join(", ");
|
|
50
|
+
headersToSet.push(["Reporting-Endpoints", reportingEndpoints]);
|
|
51
|
+
}
|
|
52
|
+
if (options.reportTo) {
|
|
53
|
+
const reportToOptions = options.reportTo.map((option) => JSON.stringify(option)).join(", ");
|
|
54
|
+
headersToSet.push(["Report-To", reportToOptions]);
|
|
55
|
+
}
|
|
33
56
|
return async (ctx, next) => {
|
|
34
57
|
await next();
|
|
35
58
|
headersToSet.forEach(([header, value]) => {
|
|
36
59
|
ctx.res.headers.set(header, value);
|
|
37
60
|
});
|
|
38
|
-
if (options.contentSecurityPolicy) {
|
|
39
|
-
const cspDirectives = Object.entries(options.contentSecurityPolicy).map(([directive, sources]) => {
|
|
40
|
-
return `${directive} ${sources.join(" ")}`;
|
|
41
|
-
}).join("; ");
|
|
42
|
-
ctx.res.headers.set("Content-Security-Policy", cspDirectives);
|
|
43
|
-
}
|
|
44
61
|
ctx.res.headers.delete("X-Powered-By");
|
|
45
62
|
};
|
|
46
63
|
};
|
package/dist/request.js
CHANGED
|
@@ -10,6 +10,11 @@ var HonoRequest = class {
|
|
|
10
10
|
const cachedBody = bodyCache[key];
|
|
11
11
|
if (cachedBody)
|
|
12
12
|
return cachedBody;
|
|
13
|
+
if (bodyCache.arrayBuffer) {
|
|
14
|
+
return (async () => {
|
|
15
|
+
return await new Response(bodyCache.arrayBuffer)[key]();
|
|
16
|
+
})();
|
|
17
|
+
}
|
|
13
18
|
return bodyCache[key] = raw[key]();
|
|
14
19
|
};
|
|
15
20
|
this.raw = request;
|
|
@@ -62,7 +67,11 @@ var HonoRequest = class {
|
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
async parseBody() {
|
|
65
|
-
|
|
70
|
+
if (this.bodyCache.parsedBody)
|
|
71
|
+
return this.bodyCache.parsedBody;
|
|
72
|
+
const parsedBody = await parseBody(this);
|
|
73
|
+
this.bodyCache.parsedBody = parsedBody;
|
|
74
|
+
return parsedBody;
|
|
66
75
|
}
|
|
67
76
|
json() {
|
|
68
77
|
return this.cachedBody("json");
|
|
@@ -2,29 +2,56 @@ import type { MiddlewareHandler } from '../../types';
|
|
|
2
2
|
interface ContentSecurityPolicyOptions {
|
|
3
3
|
defaultSrc?: string[];
|
|
4
4
|
baseUri?: string[];
|
|
5
|
+
childSrc?: string[];
|
|
6
|
+
connectSrc?: string[];
|
|
5
7
|
fontSrc?: string[];
|
|
8
|
+
formAction?: string[];
|
|
6
9
|
frameAncestors?: string[];
|
|
10
|
+
frameSrc?: string[];
|
|
7
11
|
imgSrc?: string[];
|
|
12
|
+
manifestSrc?: string[];
|
|
13
|
+
mediaSrc?: string[];
|
|
8
14
|
objectSrc?: string[];
|
|
15
|
+
reportTo?: string;
|
|
16
|
+
sandbox?: string[];
|
|
9
17
|
scriptSrc?: string[];
|
|
10
18
|
scriptSrcAttr?: string[];
|
|
19
|
+
scriptSrcElem?: string[];
|
|
11
20
|
styleSrc?: string[];
|
|
21
|
+
styleSrcAttr?: string[];
|
|
22
|
+
styleSrcElem?: string[];
|
|
12
23
|
upgradeInsecureRequests?: string[];
|
|
24
|
+
workerSrc?: string[];
|
|
13
25
|
}
|
|
26
|
+
interface ReportToOptions {
|
|
27
|
+
group: string;
|
|
28
|
+
max_age: number;
|
|
29
|
+
endpoints: ReportToEndpoint[];
|
|
30
|
+
}
|
|
31
|
+
interface ReportToEndpoint {
|
|
32
|
+
url: string;
|
|
33
|
+
}
|
|
34
|
+
interface ReportingEndpointOptions {
|
|
35
|
+
name: string;
|
|
36
|
+
url: string;
|
|
37
|
+
}
|
|
38
|
+
declare type overridableHeader = boolean | string;
|
|
14
39
|
interface SecureHeadersOptions {
|
|
15
40
|
contentSecurityPolicy?: ContentSecurityPolicyOptions;
|
|
16
|
-
crossOriginEmbedderPolicy?:
|
|
17
|
-
crossOriginResourcePolicy?:
|
|
18
|
-
crossOriginOpenerPolicy?:
|
|
19
|
-
originAgentCluster:
|
|
20
|
-
referrerPolicy?:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
crossOriginEmbedderPolicy?: overridableHeader;
|
|
42
|
+
crossOriginResourcePolicy?: overridableHeader;
|
|
43
|
+
crossOriginOpenerPolicy?: overridableHeader;
|
|
44
|
+
originAgentCluster: overridableHeader;
|
|
45
|
+
referrerPolicy?: overridableHeader;
|
|
46
|
+
reportingEndpoints?: ReportingEndpointOptions[];
|
|
47
|
+
reportTo?: ReportToOptions[];
|
|
48
|
+
strictTransportSecurity?: overridableHeader;
|
|
49
|
+
xContentTypeOptions?: overridableHeader;
|
|
50
|
+
xDnsPrefetchControl?: overridableHeader;
|
|
51
|
+
xDownloadOptions?: overridableHeader;
|
|
52
|
+
xFrameOptions?: overridableHeader;
|
|
53
|
+
xPermittedCrossDomainPolicies?: overridableHeader;
|
|
54
|
+
xXssProtection?: overridableHeader;
|
|
28
55
|
}
|
|
29
56
|
export declare const secureHeaders: (customOptions?: Partial<SecureHeadersOptions>) => MiddlewareHandler;
|
|
30
57
|
export {};
|
package/dist/types/request.d.ts
CHANGED
|
@@ -2,12 +2,22 @@ import type { Input, InputToDataByTarget, ParamKeys, ParamKeyToRecord, RemoveQue
|
|
|
2
2
|
import type { BodyData } from './utils/body';
|
|
3
3
|
import type { Cookie } from './utils/cookie';
|
|
4
4
|
import type { UnionToIntersection } from './utils/types';
|
|
5
|
+
declare type Body = {
|
|
6
|
+
json: any;
|
|
7
|
+
text: string;
|
|
8
|
+
arrayBuffer: ArrayBuffer;
|
|
9
|
+
blob: Blob;
|
|
10
|
+
formData: FormData;
|
|
11
|
+
};
|
|
12
|
+
declare type BodyCache = Partial<Body & {
|
|
13
|
+
parsedBody: BodyData;
|
|
14
|
+
}>;
|
|
5
15
|
export declare class HonoRequest<P extends string = '/', I extends Input['out'] = {}> {
|
|
6
16
|
raw: Request;
|
|
7
17
|
private paramData;
|
|
8
18
|
private vData;
|
|
9
19
|
path: string;
|
|
10
|
-
|
|
20
|
+
bodyCache: BodyCache;
|
|
11
21
|
constructor(request: Request, path?: string, paramData?: Record<string, string> | undefined);
|
|
12
22
|
param<P2 extends string = P>(key: RemoveQuestion<ParamKeys<P2>>): UndefinedIfHavingQuestion<ParamKeys<P2>>;
|
|
13
23
|
param<P2 extends string = P>(): UnionToIntersection<ParamKeyToRecord<ParamKeys<P2>>>;
|
|
@@ -56,3 +66,4 @@ export declare class HonoRequest<P extends string = '/', I extends Input['out']
|
|
|
56
66
|
get referrer(): string;
|
|
57
67
|
get signal(): AbortSignal;
|
|
58
68
|
}
|
|
69
|
+
export {};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { HonoRequest } from '../request';
|
|
2
2
|
export declare type BodyData = Record<string, string | File>;
|
|
3
|
-
export declare const parseBody: <T extends BodyData = BodyData>(
|
|
3
|
+
export declare const parseBody: <T extends BodyData = BodyData>(request: HonoRequest | Request) => Promise<T>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export declare const equal: (a: ArrayBuffer, b: ArrayBuffer) => boolean;
|
|
2
2
|
export declare const timingSafeEqual: (a: string | object | boolean, b: string | object | boolean, hashFunction?: Function) => Promise<boolean>;
|
|
3
3
|
export declare const bufferToString: (buffer: ArrayBuffer) => string;
|
|
4
|
+
export declare const bufferToFormData: (arrayBuffer: ArrayBuffer, contentType: string) => FormData;
|
package/dist/utils/body.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
// src/utils/body.ts
|
|
2
|
-
var parseBody = async (
|
|
2
|
+
var parseBody = async (request) => {
|
|
3
3
|
let body = {};
|
|
4
|
-
const contentType =
|
|
4
|
+
const contentType = request.headers.get("Content-Type");
|
|
5
5
|
if (contentType && (contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded"))) {
|
|
6
|
-
const
|
|
7
|
-
(
|
|
8
|
-
form
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
const formData = await request.formData();
|
|
7
|
+
if (formData) {
|
|
8
|
+
const form = {};
|
|
9
|
+
formData.forEach((value, key) => {
|
|
10
|
+
form[key] = value;
|
|
11
|
+
});
|
|
12
|
+
body = form;
|
|
13
|
+
}
|
|
11
14
|
}
|
|
12
15
|
return body;
|
|
13
16
|
};
|
package/dist/utils/buffer.js
CHANGED
|
@@ -35,7 +35,34 @@ var bufferToString = (buffer) => {
|
|
|
35
35
|
}
|
|
36
36
|
return buffer;
|
|
37
37
|
};
|
|
38
|
+
var _decodeURIComponent = decodeURIComponent;
|
|
39
|
+
var bufferToFormData = (arrayBuffer, contentType) => {
|
|
40
|
+
const decoder = new TextDecoder("utf-8");
|
|
41
|
+
const content = decoder.decode(arrayBuffer);
|
|
42
|
+
const formData = new FormData();
|
|
43
|
+
const boundaryMatch = contentType.match(/boundary=(.+)/);
|
|
44
|
+
const boundary = boundaryMatch ? boundaryMatch[1] : "";
|
|
45
|
+
if (contentType.startsWith("multipart/form-data") && boundary) {
|
|
46
|
+
const parts = content.split("--" + boundary).slice(1, -1);
|
|
47
|
+
for (const part of parts) {
|
|
48
|
+
const [header, body] = part.split("\r\n\r\n");
|
|
49
|
+
const nameMatch = header.match(/name="([^"]+)"/);
|
|
50
|
+
if (nameMatch) {
|
|
51
|
+
const name = nameMatch[1];
|
|
52
|
+
formData.append(name, body.trim());
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
} else if (contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
56
|
+
const pairs = content.split("&");
|
|
57
|
+
for (const pair of pairs) {
|
|
58
|
+
const [key, value] = pair.split("=");
|
|
59
|
+
formData.append(_decodeURIComponent(key), _decodeURIComponent(value));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return formData;
|
|
63
|
+
};
|
|
38
64
|
export {
|
|
65
|
+
bufferToFormData,
|
|
39
66
|
bufferToString,
|
|
40
67
|
equal,
|
|
41
68
|
timingSafeEqual
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// src/validator/validator.ts
|
|
2
2
|
import { getCookie } from "../helper/cookie/index.js";
|
|
3
|
+
import { bufferToFormData } from "../utils/buffer.js";
|
|
3
4
|
var validator = (target, validationFunc) => {
|
|
4
5
|
return async (c, next) => {
|
|
5
6
|
let value = {};
|
|
6
7
|
switch (target) {
|
|
7
8
|
case "json":
|
|
8
9
|
try {
|
|
9
|
-
|
|
10
|
+
const arrayBuffer = c.req.bodyCache.arrayBuffer ?? await c.req.raw.arrayBuffer();
|
|
11
|
+
value = await new Response(arrayBuffer).json();
|
|
12
|
+
c.req.bodyCache.json = value;
|
|
13
|
+
c.req.bodyCache.arrayBuffer = arrayBuffer;
|
|
10
14
|
} catch {
|
|
11
15
|
console.error("Error: Malformed JSON in request body");
|
|
12
16
|
return c.json(
|
|
@@ -18,9 +22,21 @@ var validator = (target, validationFunc) => {
|
|
|
18
22
|
);
|
|
19
23
|
}
|
|
20
24
|
break;
|
|
21
|
-
case "form":
|
|
22
|
-
|
|
25
|
+
case "form": {
|
|
26
|
+
const contentType = c.req.header("Content-Type");
|
|
27
|
+
if (contentType) {
|
|
28
|
+
const arrayBuffer = c.req.bodyCache.arrayBuffer ?? await c.req.raw.arrayBuffer();
|
|
29
|
+
const formData = bufferToFormData(arrayBuffer, contentType);
|
|
30
|
+
const form = {};
|
|
31
|
+
formData.forEach((value2, key) => {
|
|
32
|
+
form[key] = value2;
|
|
33
|
+
});
|
|
34
|
+
value = form;
|
|
35
|
+
c.req.bodyCache.formData = formData;
|
|
36
|
+
c.req.bodyCache.arrayBuffer = arrayBuffer;
|
|
37
|
+
}
|
|
23
38
|
break;
|
|
39
|
+
}
|
|
24
40
|
case "query":
|
|
25
41
|
value = Object.fromEntries(
|
|
26
42
|
Object.entries(c.req.queries()).map(([k, v]) => {
|