@sdk-it/hono 0.42.1 → 0.44.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.
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ app.post(
|
|
|
70
70
|
// URL parameter validation
|
|
71
71
|
userId: {
|
|
72
72
|
select: payload.params.userId,
|
|
73
|
-
against: z.
|
|
73
|
+
against: z.uuid(),
|
|
74
74
|
},
|
|
75
75
|
|
|
76
76
|
// Header validation
|
|
@@ -134,7 +134,7 @@ app.post(
|
|
|
134
134
|
validate('multipart/form-data', (payload) => ({
|
|
135
135
|
userId: {
|
|
136
136
|
select: payload.params.userId,
|
|
137
|
-
against: z.
|
|
137
|
+
against: z.uuid(),
|
|
138
138
|
},
|
|
139
139
|
// File validation
|
|
140
140
|
avatar: {
|
|
@@ -156,10 +156,9 @@ async function parse(schema, input) {
|
|
|
156
156
|
cause: {
|
|
157
157
|
code: "api/validation-failed",
|
|
158
158
|
detail: "The input data is invalid",
|
|
159
|
-
errors: result.error
|
|
159
|
+
errors: z.flattenError(result.error, (issue) => ({
|
|
160
160
|
message: issue.message,
|
|
161
161
|
code: issue.code,
|
|
162
|
-
fatal: issue.fatal,
|
|
163
162
|
path: issue.path.join(".")
|
|
164
163
|
})).fieldErrors
|
|
165
164
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/runtime/output.ts", "../../../src/lib/runtime/validator.ts"],
|
|
4
|
-
"sourcesContent": ["import type { Context } from 'hono';\nimport { getContext } from 'hono/context-storage';\nimport type {\n ContentfulStatusCode,\n RedirectStatusCode,\n} from 'hono/utils/http-status';\n\ntype Data = any;\nfunction send(\n context: Context,\n value: Data | undefined | null,\n status: ContentfulStatusCode,\n headers?: Readonly<Record<string, string>>,\n) {\n if (value === undefined || value === null) {\n return context.body(null, status, headers);\n }\n const responseHeaders = { ...headers };\n responseHeaders['Content-Type'] ??= 'application/json';\n if (responseHeaders['Content-Type'].includes('application/json')) {\n return context.body(JSON.stringify(value), status, responseHeaders);\n }\n return context.body(value, status, responseHeaders);\n}\n\nexport function createOutput(contextFn: () => Context) {\n return {\n nocontent() {\n const context = contextFn();\n return context.body(null, 204, {});\n },\n ok(value: Data | undefined | null, headers?: Record<string, string>) {\n return send(contextFn(), value, 200, headers);\n },\n created(\n valueOrUri: string | Data,\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n if (typeof valueOrUri === 'string') {\n // If no content is provided, we send null\n return send(contextFn(), value, 201, {\n Location: valueOrUri,\n ...(headers || {}),\n });\n } else {\n // valueOrUri is the data\n return send(contextFn(), valueOrUri, 201, headers);\n }\n },\n redirect(uri: string | URL, statusCode?: unknown) {\n const context = contextFn();\n return context.redirect(\n uri.toString(),\n (statusCode as RedirectStatusCode) ?? undefined,\n );\n },\n attachment(buffer: Buffer, filename: string, mimeType: string) {\n const context = contextFn();\n // https://github.com/honojs/hono/issues/3720\n return context.body(buffer as never, 200, {\n 'Content-Type': mimeType,\n 'Content-Disposition': `attachment; filename=\"${filename}\"`,\n 'Content-Length': buffer.length.toString(),\n });\n },\n badRequest(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 400, headers);\n },\n unauthorized(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 401, headers);\n },\n forbidden(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 403, headers);\n },\n notFound(value: Data | undefined | null, headers?: Record<string, string>) {\n return send(contextFn(), value, 404, headers);\n },\n notImplemented(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 501, headers);\n },\n accepted(value: Data | undefined | null, headers?: Record<string, string>) {\n return send(contextFn(), value, 202, headers);\n },\n conflict(value: Data | undefined | null, headers?: Record<string, string>) {\n return send(contextFn(), value, 409, headers);\n },\n unprocessableEntity(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 422, headers);\n },\n internalServerError(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 500, headers);\n },\n serviceUnavailable(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 503, headers);\n },\n };\n}\n\nexport const output = createOutput(() => getContext());\n", "import { parse as parseContentType } from 'fast-content-type-parse';\nimport type { MiddlewareHandler, ValidationTargets } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { HTTPException } from 'hono/http-exception';\nimport z from 'zod';\n\ntype ContentType =\n | 'application/json'\n | 'application/x-www-form-urlencoded'\n | 'multipart/form-data'\n | 'text/plain';\n\ntype ValidatorConfig = Record<\n string,\n { select: unknown; against: z.ZodTypeAny }\n>;\n\ntype ExtractInput<T extends ValidatorConfig> = {\n [K in keyof T]: z.infer<T[K]['against']>;\n};\n\ntype HasUndefined<T> = undefined extends T ? true : false;\n\ntype InferTarget<\n T extends ValidatorConfig,\n S,\n Target extends keyof ValidationTargets,\n> = {\n [K in keyof T as T[K]['select'] extends S ? K : never]: HasUndefined<\n z.infer<T[K]['against']>\n > extends true\n ? z.infer<T[K]['against']> | undefined\n : z.infer<T[K]['against']> extends ValidationTargets[Target]\n ? z.infer<T[K]['against']>\n : z.infer<T[K]['against']>;\n};\n\ntype InferIn<T extends ValidatorConfig> = (keyof InferTarget<\n T,\n QuerySelect | QueriesSelect,\n 'query'\n> extends never\n ? never\n : { query: InferTarget<T, QuerySelect | QueriesSelect, 'query'> }) &\n (keyof InferTarget<T, BodySelect, 'json'> extends never\n ? never\n : { json: InferTarget<T, BodySelect, 'json'> }) &\n (keyof InferTarget<T, ParamsSelect, 'param'> extends never\n ? never\n : { param: InferTarget<T, ParamsSelect, 'param'> }) &\n (keyof InferTarget<T, HeadersSelect, 'header'> extends never\n ? never\n : { header: InferTarget<T, HeadersSelect, 'header'> }) &\n (keyof InferTarget<T, CookieSelect, 'cookie'> extends never\n ? never\n : { cookie: InferTarget<T, CookieSelect, 'cookie'> });\n\n// Marker classes\nclass BodySelect {\n #private = 0;\n}\nclass QuerySelect {\n #private = 0;\n}\nclass QueriesSelect {\n #private = 0;\n}\nclass ParamsSelect {\n #private = 0;\n}\nclass HeadersSelect {\n #private = 0;\n}\nclass CookieSelect {\n #private = 0;\n}\n\ntype SelectorFn<T> = (payload: {\n body: Record<string, BodySelect>;\n query: Record<string, QuerySelect>;\n queries: Record<string, QueriesSelect>;\n params: Record<string, ParamsSelect>;\n headers: Record<string, HeadersSelect>;\n}) => T;\ntype ValidateMiddleware<T extends ValidatorConfig> = MiddlewareHandler<\n {\n Variables: {\n input: ExtractInput<T>;\n };\n },\n string,\n { in: InferIn<T> }\n>;\n\nexport function validate<T extends ValidatorConfig>(\n selector: SelectorFn<T>,\n): ValidateMiddleware<T>;\nexport function validate<T extends ValidatorConfig>(\n expectedContentTypeOrSelector: ContentType,\n selector: SelectorFn<T>,\n): ValidateMiddleware<T>;\nexport function validate<T extends ValidatorConfig>(\n expectedContentTypeOrSelector: ContentType | SelectorFn<T>,\n selector?: SelectorFn<T>,\n): ValidateMiddleware<T> {\n const expectedContentType =\n typeof expectedContentTypeOrSelector === 'string'\n ? expectedContentTypeOrSelector\n : undefined;\n const _selector =\n typeof expectedContentTypeOrSelector === 'function'\n ? expectedContentTypeOrSelector\n : selector;\n if (!_selector) {\n throw new Error('Selector function is required');\n }\n\n return createMiddleware(async (c, next) => {\n const ct = c.req.header('content-type');\n if (c.req.method === 'GET' && ct) {\n throw new HTTPException(415, {\n message: 'Unsupported Media Type',\n cause: {\n code: 'api/unsupported-media-type',\n details: `GET requests cannot have a content type header`,\n },\n });\n }\n if (expectedContentType) {\n verifyContentType(ct, expectedContentType);\n }\n\n const contentType = ct ? parseContentType(ct) : null;\n let body: unknown = null;\n\n switch (contentType?.type) {\n case 'application/json':\n body = await c.req.json();\n break;\n case 'application/x-www-form-urlencoded':\n case 'multipart/form-data':\n body = await c.req.parseBody();\n break;\n default:\n body = {};\n }\n\n const payload = {\n body,\n query: c.req.query(),\n queries: c.req.queries(),\n params: c.req.param(),\n headers: Object.fromEntries(\n Object.entries(c.req.header()).map(([k, v]) => [k, v ?? '']),\n ),\n };\n\n const config = _selector(payload as never);\n const schema = z.object(\n Object.entries(config).reduce(\n (acc, [key, value]) => {\n acc[key] = value.against;\n return acc;\n },\n {} as Record<string, z.ZodTypeAny>,\n ),\n );\n\n const input = Object.entries(config).reduce(\n (acc, [key, value]) => {\n acc[key] = value.select;\n return acc;\n },\n {} as Record<string, unknown>,\n );\n\n const parsed = await parse(schema, input);\n c.set('input', parsed as ExtractInput<T>);\n await next();\n });\n}\n\nexport async function parse<T extends z.ZodRawShape>(\n schema: z.ZodObject<T>,\n input: unknown,\n) {\n const result = await schema.safeParseAsync(input);\n if (!result.success) {\n const error = new HTTPException(400, {\n message: 'Validation failed',\n cause: {\n code: 'api/validation-failed',\n detail: 'The input data is invalid',\n errors: result.error.flatten((issue) => ({\n message: issue.message,\n code: issue.code,\n fatal: issue.fatal,\n path: issue.path.join('.'),\n })).fieldErrors,\n },\n });\n throw error;\n }\n return result.data;\n}\n\nexport const openapi = validate;\n\nexport const consume = (contentType: ContentType) => {\n return createMiddleware(async (context, next) => {\n verifyContentType(context.req.header('content-type'), contentType);\n await next();\n });\n};\n\nexport function verifyContentType(\n actual: string | undefined,\n expected: ContentType,\n): asserts actual is ContentType {\n if (!actual) {\n throw new HTTPException(415, {\n message: 'Unsupported Media Type',\n cause: {\n code: 'api/unsupported-media-type',\n details: 'Missing content type header',\n },\n });\n }\n const { type: incomingContentType } = parseContentType(actual);\n if (incomingContentType !== expected) {\n throw new HTTPException(415, {\n message: 'Unsupported Media Type',\n cause: {\n code: 'api/unsupported-media-type',\n details: `Expected content type: ${expected}, but got: ${incomingContentType}`,\n },\n });\n }\n}\n"],
|
|
5
|
-
"mappings": ";AACA,SAAS,kBAAkB;AAO3B,SAAS,KACP,SACA,OACA,QACA,SACA;AACA,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO,QAAQ,KAAK,MAAM,QAAQ,OAAO;AAAA,EAC3C;AACA,QAAM,kBAAkB,EAAE,GAAG,QAAQ;AACrC,kBAAgB,cAAc,MAAM;AACpC,MAAI,gBAAgB,cAAc,EAAE,SAAS,kBAAkB,GAAG;AAChE,WAAO,QAAQ,KAAK,KAAK,UAAU,KAAK,GAAG,QAAQ,eAAe;AAAA,EACpE;AACA,SAAO,QAAQ,KAAK,OAAO,QAAQ,eAAe;AACpD;AAEO,SAAS,aAAa,WAA0B;AACrD,SAAO;AAAA,IACL,YAAY;AACV,YAAM,UAAU,UAAU;AAC1B,aAAO,QAAQ,KAAK,MAAM,KAAK,CAAC,CAAC;AAAA,IACnC;AAAA,IACA,GAAG,OAAgC,SAAkC;AACnE,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,QACE,YACA,OACA,SACA;AACA,UAAI,OAAO,eAAe,UAAU;AAElC,eAAO,KAAK,UAAU,GAAG,OAAO,KAAK;AAAA,UACnC,UAAU;AAAA,UACV,GAAI,WAAW,CAAC;AAAA,QAClB,CAAC;AAAA,MACH,OAAO;AAEL,eAAO,KAAK,UAAU,GAAG,YAAY,KAAK,OAAO;AAAA,MACnD;AAAA,IACF;AAAA,IACA,SAAS,KAAmB,YAAsB;AAChD,YAAM,UAAU,UAAU;AAC1B,aAAO,QAAQ;AAAA,QACb,IAAI,SAAS;AAAA,QACZ,cAAqC;AAAA,MACxC;AAAA,IACF;AAAA,IACA,WAAW,QAAgB,UAAkB,UAAkB;AAC7D,YAAM,UAAU,UAAU;AAE1B,aAAO,QAAQ,KAAK,QAAiB,KAAK;AAAA,QACxC,gBAAgB;AAAA,QAChB,uBAAuB,yBAAyB,QAAQ;AAAA,QACxD,kBAAkB,OAAO,OAAO,SAAS;AAAA,MAC3C,CAAC;AAAA,IACH;AAAA,IACA,WACE,OACA,SACA;AACA,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,aACE,OACA,SACA;AACA,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,UACE,OACA,SACA;AACA,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,SAAS,OAAgC,SAAkC;AACzE,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,eACE,OACA,SACA;AACA,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,SAAS,OAAgC,SAAkC;AACzE,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,SAAS,OAAgC,SAAkC;AACzE,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,oBACE,OACA,SACA;AACA,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,oBACE,OACA,SACA;AACA,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,IACA,mBACE,OACA,SACA;AACA,aAAO,KAAK,UAAU,GAAG,OAAO,KAAK,OAAO;AAAA,IAC9C;AAAA,EACF;AACF;AAEO,IAAM,SAAS,aAAa,MAAM,WAAW,CAAC;;;ACxHrD,SAAS,SAAS,wBAAwB;AAE1C,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAC9B,OAAO,OAAO;
|
|
4
|
+
"sourcesContent": ["import type { Context } from 'hono';\nimport { getContext } from 'hono/context-storage';\nimport type {\n ContentfulStatusCode,\n RedirectStatusCode,\n} from 'hono/utils/http-status';\n\ntype Data = any;\nfunction send(\n context: Context,\n value: Data | undefined | null,\n status: ContentfulStatusCode,\n headers?: Readonly<Record<string, string>>,\n) {\n if (value === undefined || value === null) {\n return context.body(null, status, headers);\n }\n const responseHeaders = { ...headers };\n responseHeaders['Content-Type'] ??= 'application/json';\n if (responseHeaders['Content-Type'].includes('application/json')) {\n return context.body(JSON.stringify(value), status, responseHeaders);\n }\n return context.body(value, status, responseHeaders);\n}\n\nexport function createOutput(contextFn: () => Context) {\n return {\n nocontent() {\n const context = contextFn();\n return context.body(null, 204, {});\n },\n ok(value: Data | undefined | null, headers?: Record<string, string>) {\n return send(contextFn(), value, 200, headers);\n },\n created(\n valueOrUri: string | Data,\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n if (typeof valueOrUri === 'string') {\n // If no content is provided, we send null\n return send(contextFn(), value, 201, {\n Location: valueOrUri,\n ...(headers || {}),\n });\n } else {\n // valueOrUri is the data\n return send(contextFn(), valueOrUri, 201, headers);\n }\n },\n redirect(uri: string | URL, statusCode?: unknown) {\n const context = contextFn();\n return context.redirect(\n uri.toString(),\n (statusCode as RedirectStatusCode) ?? undefined,\n );\n },\n attachment(buffer: Buffer, filename: string, mimeType: string) {\n const context = contextFn();\n // https://github.com/honojs/hono/issues/3720\n return context.body(buffer as never, 200, {\n 'Content-Type': mimeType,\n 'Content-Disposition': `attachment; filename=\"${filename}\"`,\n 'Content-Length': buffer.length.toString(),\n });\n },\n badRequest(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 400, headers);\n },\n unauthorized(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 401, headers);\n },\n forbidden(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 403, headers);\n },\n notFound(value: Data | undefined | null, headers?: Record<string, string>) {\n return send(contextFn(), value, 404, headers);\n },\n notImplemented(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 501, headers);\n },\n accepted(value: Data | undefined | null, headers?: Record<string, string>) {\n return send(contextFn(), value, 202, headers);\n },\n conflict(value: Data | undefined | null, headers?: Record<string, string>) {\n return send(contextFn(), value, 409, headers);\n },\n unprocessableEntity(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 422, headers);\n },\n internalServerError(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 500, headers);\n },\n serviceUnavailable(\n value: Data | undefined | null,\n headers?: Record<string, string>,\n ) {\n return send(contextFn(), value, 503, headers);\n },\n };\n}\n\nexport const output = createOutput(() => getContext());\n", "import { parse as parseContentType } from 'fast-content-type-parse';\nimport type { MiddlewareHandler, ValidationTargets } from 'hono';\nimport { createMiddleware } from 'hono/factory';\nimport { HTTPException } from 'hono/http-exception';\nimport z from 'zod';\n\ntype ContentType =\n | 'application/json'\n | 'application/x-www-form-urlencoded'\n | 'multipart/form-data'\n | 'text/plain';\n\n// z.ZodType<any> mirrors zod v3's ZodTypeAny: with the default `unknown`\n// output, concrete middlewares stop being assignable to\n// ValidateMiddleware<ValidatorConfig>.\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype ValidatorConfig = Record<\n string,\n { select: unknown; against: z.ZodType<any> }\n>;\n\ntype ExtractInput<T extends ValidatorConfig> = {\n [K in keyof T]: z.infer<T[K]['against']>;\n};\n\ntype HasUndefined<T> = undefined extends T ? true : false;\n\ntype InferTarget<\n T extends ValidatorConfig,\n S,\n Target extends keyof ValidationTargets,\n> = {\n [K in keyof T as T[K]['select'] extends S ? K : never]: HasUndefined<\n z.infer<T[K]['against']>\n > extends true\n ? z.infer<T[K]['against']> | undefined\n : z.infer<T[K]['against']> extends ValidationTargets[Target]\n ? z.infer<T[K]['against']>\n : z.infer<T[K]['against']>;\n};\n\ntype InferIn<T extends ValidatorConfig> = (keyof InferTarget<\n T,\n QuerySelect | QueriesSelect,\n 'query'\n> extends never\n ? never\n : { query: InferTarget<T, QuerySelect | QueriesSelect, 'query'> }) &\n (keyof InferTarget<T, BodySelect, 'json'> extends never\n ? never\n : { json: InferTarget<T, BodySelect, 'json'> }) &\n (keyof InferTarget<T, ParamsSelect, 'param'> extends never\n ? never\n : { param: InferTarget<T, ParamsSelect, 'param'> }) &\n (keyof InferTarget<T, HeadersSelect, 'header'> extends never\n ? never\n : { header: InferTarget<T, HeadersSelect, 'header'> }) &\n (keyof InferTarget<T, CookieSelect, 'cookie'> extends never\n ? never\n : { cookie: InferTarget<T, CookieSelect, 'cookie'> });\n\n// Marker classes\nclass BodySelect {\n #private = 0;\n}\nclass QuerySelect {\n #private = 0;\n}\nclass QueriesSelect {\n #private = 0;\n}\nclass ParamsSelect {\n #private = 0;\n}\nclass HeadersSelect {\n #private = 0;\n}\nclass CookieSelect {\n #private = 0;\n}\n\ntype SelectorFn<T> = (payload: {\n body: Record<string, BodySelect>;\n query: Record<string, QuerySelect>;\n queries: Record<string, QueriesSelect>;\n params: Record<string, ParamsSelect>;\n headers: Record<string, HeadersSelect>;\n}) => T;\ntype ValidateMiddleware<T extends ValidatorConfig> = MiddlewareHandler<\n {\n Variables: {\n input: ExtractInput<T>;\n };\n },\n string,\n { in: InferIn<T> }\n>;\n\nexport function validate<T extends ValidatorConfig>(\n selector: SelectorFn<T>,\n): ValidateMiddleware<T>;\nexport function validate<T extends ValidatorConfig>(\n expectedContentTypeOrSelector: ContentType,\n selector: SelectorFn<T>,\n): ValidateMiddleware<T>;\nexport function validate<T extends ValidatorConfig>(\n expectedContentTypeOrSelector: ContentType | SelectorFn<T>,\n selector?: SelectorFn<T>,\n): ValidateMiddleware<T> {\n const expectedContentType =\n typeof expectedContentTypeOrSelector === 'string'\n ? expectedContentTypeOrSelector\n : undefined;\n const _selector =\n typeof expectedContentTypeOrSelector === 'function'\n ? expectedContentTypeOrSelector\n : selector;\n if (!_selector) {\n throw new Error('Selector function is required');\n }\n\n return createMiddleware(async (c, next) => {\n const ct = c.req.header('content-type');\n if (c.req.method === 'GET' && ct) {\n throw new HTTPException(415, {\n message: 'Unsupported Media Type',\n cause: {\n code: 'api/unsupported-media-type',\n details: `GET requests cannot have a content type header`,\n },\n });\n }\n if (expectedContentType) {\n verifyContentType(ct, expectedContentType);\n }\n\n const contentType = ct ? parseContentType(ct) : null;\n let body: unknown = null;\n\n switch (contentType?.type) {\n case 'application/json':\n body = await c.req.json();\n break;\n case 'application/x-www-form-urlencoded':\n case 'multipart/form-data':\n body = await c.req.parseBody();\n break;\n default:\n body = {};\n }\n\n const payload = {\n body,\n query: c.req.query(),\n queries: c.req.queries(),\n params: c.req.param(),\n headers: Object.fromEntries(\n Object.entries(c.req.header()).map(([k, v]) => [k, v ?? '']),\n ),\n };\n\n const config = _selector(payload as never);\n const schema = z.object(\n Object.entries(config).reduce(\n (acc, [key, value]) => {\n acc[key] = value.against;\n return acc;\n },\n {} as Record<string, z.ZodType>,\n ),\n );\n\n const input = Object.entries(config).reduce(\n (acc, [key, value]) => {\n acc[key] = value.select;\n return acc;\n },\n {} as Record<string, unknown>,\n );\n\n const parsed = await parse(schema, input);\n c.set('input', parsed as ExtractInput<T>);\n await next();\n });\n}\n\nexport async function parse<T extends z.ZodRawShape>(\n schema: z.ZodObject<T>,\n input: unknown,\n) {\n const result = await schema.safeParseAsync(input);\n if (!result.success) {\n const error = new HTTPException(400, {\n message: 'Validation failed',\n cause: {\n code: 'api/validation-failed',\n detail: 'The input data is invalid',\n errors: z.flattenError(result.error, (issue) => ({\n message: issue.message,\n code: issue.code,\n path: issue.path.join('.'),\n })).fieldErrors,\n },\n });\n throw error;\n }\n return result.data;\n}\n\nexport const openapi = validate;\n\nexport const consume = (contentType: ContentType) => {\n return createMiddleware(async (context, next) => {\n verifyContentType(context.req.header('content-type'), contentType);\n await next();\n });\n};\n\nexport function verifyContentType(\n actual: string | undefined,\n expected: ContentType,\n): asserts actual is ContentType {\n if (!actual) {\n throw new HTTPException(415, {\n message: 'Unsupported Media Type',\n cause: {\n code: 'api/unsupported-media-type',\n details: 'Missing content type header',\n },\n });\n }\n const { type: incomingContentType } = parseContentType(actual);\n if (incomingContentType !== expected) {\n throw new HTTPException(415, {\n message: 'Unsupported Media Type',\n cause: {\n code: 'api/unsupported-media-type',\n details: `Expected content type: ${expected}, but got: ${incomingContentType}`,\n },\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,7 +3,7 @@ import z from 'zod';
|
|
|
3
3
|
type ContentType = 'application/json' | 'application/x-www-form-urlencoded' | 'multipart/form-data' | 'text/plain';
|
|
4
4
|
type ValidatorConfig = Record<string, {
|
|
5
5
|
select: unknown;
|
|
6
|
-
against: z.
|
|
6
|
+
against: z.ZodType<any>;
|
|
7
7
|
}>;
|
|
8
8
|
type ExtractInput<T extends ValidatorConfig> = {
|
|
9
9
|
[K in keyof T]: z.infer<T[K]['against']>;
|
|
@@ -57,7 +57,7 @@ type ValidateMiddleware<T extends ValidatorConfig> = MiddlewareHandler<{
|
|
|
57
57
|
}>;
|
|
58
58
|
export declare function validate<T extends ValidatorConfig>(selector: SelectorFn<T>): ValidateMiddleware<T>;
|
|
59
59
|
export declare function validate<T extends ValidatorConfig>(expectedContentTypeOrSelector: ContentType, selector: SelectorFn<T>): ValidateMiddleware<T>;
|
|
60
|
-
export declare function parse<T extends z.ZodRawShape>(schema: z.ZodObject<T>, input: unknown): Promise<z.
|
|
60
|
+
export declare function parse<T extends z.ZodRawShape>(schema: z.ZodObject<T>, input: unknown): Promise<z.core.$InferObjectOutput<T, {}>>;
|
|
61
61
|
export declare const openapi: typeof validate;
|
|
62
62
|
export declare const consume: (contentType: ContentType) => MiddlewareHandler<any, string, {}>;
|
|
63
63
|
export declare function verifyContentType(actual: string | undefined, expected: ContentType): asserts actual is ContentType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime/validator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAGjE,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,KAAK,WAAW,GACZ,kBAAkB,GAClB,mCAAmC,GACnC,qBAAqB,GACrB,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../src/lib/runtime/validator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAGjE,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,KAAK,WAAW,GACZ,kBAAkB,GAClB,mCAAmC,GACnC,qBAAqB,GACrB,YAAY,CAAC;AAMjB,KAAK,eAAe,GAAG,MAAM,CAC3B,MAAM,EACN;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;CAAE,CAC7C,CAAC;AAEF,KAAK,YAAY,CAAC,CAAC,SAAS,eAAe,IAAI;KAC5C,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;CACzC,CAAC;AAEF,KAAK,YAAY,CAAC,CAAC,IAAI,SAAS,SAAS,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC;AAE1D,KAAK,WAAW,CACd,CAAC,SAAS,eAAe,EACzB,CAAC,EACD,MAAM,SAAS,MAAM,iBAAiB,IACpC;KACD,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,YAAY,CAClE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CACzB,SAAS,IAAI,GACV,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,SAAS,GACpC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iBAAiB,CAAC,MAAM,CAAC,GACxD,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,GACxB,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;CAC/B,CAAC;AAEF,KAAK,OAAO,CAAC,CAAC,SAAS,eAAe,IAAI,CAAC,MAAM,WAAW,CAC1D,CAAC,EACD,WAAW,GAAG,aAAa,EAC3B,OAAO,CACR,SAAS,KAAK,GACX,KAAK,GACL;IAAE,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,WAAW,GAAG,aAAa,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GAClE,CAAC,MAAM,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,SAAS,KAAK,GACnD,KAAK,GACL;IAAE,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC,GACjD,CAAC,MAAM,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,SAAS,KAAK,GACtD,KAAK,GACL;IAAE,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,GACrD,CAAC,MAAM,WAAW,CAAC,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,SAAS,KAAK,GACxD,KAAK,GACL;IAAE,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,CAAA;CAAE,CAAC,GACxD,CAAC,MAAM,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,SAAS,KAAK,GACvD,KAAK,GACL;IAAE,MAAM,EAAE,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAA;CAAE,CAAC,CAAC;AAG1D,cAAM,UAAU;;CAEf;AACD,cAAM,WAAW;;CAEhB;AACD,cAAM,aAAa;;CAElB;AACD,cAAM,YAAY;;CAEjB;AACD,cAAM,aAAa;;CAElB;AACD,cAAM,YAAY;;CAEjB;AAED,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CACxC,KAAK,CAAC,CAAC;AACR,KAAK,kBAAkB,CAAC,CAAC,SAAS,eAAe,IAAI,iBAAiB,CACpE;IACE,SAAS,EAAE;QACT,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;KACxB,CAAC;CACH,EACD,MAAM,EACN;IAAE,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;CAAE,CACnB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,CAAC,SAAS,eAAe,EAChD,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,GACtB,kBAAkB,CAAC,CAAC,CAAC,CAAC;AACzB,wBAAgB,QAAQ,CAAC,CAAC,SAAS,eAAe,EAChD,6BAA6B,EAAE,WAAW,EAC1C,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,GACtB,kBAAkB,CAAC,CAAC,CAAC,CAAC;AAkFzB,wBAAsB,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EACjD,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EACtB,KAAK,EAAE,OAAO,6CAmBf;AAED,eAAO,MAAM,OAAO,iBAAW,CAAC;AAEhC,eAAO,MAAM,OAAO,GAAI,aAAa,WAAW,uCAK/C,CAAC;AAEF,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,QAAQ,EAAE,WAAW,GACpB,OAAO,CAAC,MAAM,IAAI,WAAW,CAoB/B"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdk-it/hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"!**/*.test.*"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@sdk-it/core": "0.
|
|
30
|
+
"@sdk-it/core": "0.44.0",
|
|
31
31
|
"hono": "^4.7.4",
|
|
32
|
-
"zod": "^
|
|
32
|
+
"zod": "^4.3.0",
|
|
33
33
|
"fast-content-type-parse": "^3.0.0"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|