zeti-framework-backend 0.2.5 → 0.2.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.
@@ -0,0 +1,190 @@
1
+ // src/utils/error-handler.ts
2
+ import { HTTPException } from "hono/http-exception";
3
+ function isJsonParseError(err) {
4
+ if (!err) return false;
5
+ const message = err.message?.toLowerCase() || "";
6
+ const name = err.name?.toLowerCase() || "";
7
+ return name === "syntaxerror" || message.includes("json") || message.includes("unexpected token") || message.includes("unexpected end") || message.includes("malformed") || message.includes("parse") || message.includes("invalid character");
8
+ }
9
+ function getJsonErrorDetails(err) {
10
+ const message = err.message || "";
11
+ const positionMatch = message.match(/position\s*(\d+)/i);
12
+ const lineMatch = message.match(/line\s*(\d+)/i);
13
+ const columnMatch = message.match(/column\s*(\d+)/i);
14
+ let details = "JSON inv\xE1lido no corpo da requisi\xE7\xE3o";
15
+ if (positionMatch) {
16
+ details += ` (posi\xE7\xE3o ${positionMatch[1]})`;
17
+ }
18
+ if (lineMatch && columnMatch) {
19
+ details += ` (linha ${lineMatch[1]}, coluna ${columnMatch[1]})`;
20
+ }
21
+ const tokenMatch = message.match(/unexpected token\s*['"]?([^'"]+)['"]?/i);
22
+ if (tokenMatch) {
23
+ details += `. Caractere inesperado: "${tokenMatch[1]}"`;
24
+ }
25
+ if (message.includes("unexpected end")) {
26
+ details += ". Verifique se o JSON est\xE1 completo (faltando } ou ])";
27
+ }
28
+ return details;
29
+ }
30
+ function createErrorHandler(config) {
31
+ const isDev = process.env.NODE_ENV !== "production";
32
+ const includeTrace = config?.includeTrace ?? isDev;
33
+ const includeStack = config?.includeStack ?? false;
34
+ return async (c, next) => {
35
+ try {
36
+ await next();
37
+ } catch (err) {
38
+ const tenant = c.req.header("x-tenant") || c.req.header("X-Tenant") || "not-set";
39
+ const trace = {
40
+ path: c.req.path,
41
+ method: c.req.method,
42
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
43
+ errorType: err?.constructor?.name || "Error"
44
+ };
45
+ if (includeStack && err?.stack) {
46
+ trace.stack = err.stack;
47
+ }
48
+ console.error(`
49
+ \u274C [${trace.timestamp}] Error in ${trace.method} ${trace.path}`);
50
+ console.error(` Tenant: ${tenant}`);
51
+ console.error(` Type: ${trace.errorType}`);
52
+ console.error(` Message: ${err?.message || "Unknown error"}`);
53
+ if (err?.cause) {
54
+ console.error(` Cause: ${err.cause?.message || err.cause}`);
55
+ }
56
+ if (isDev && err?.stack) {
57
+ console.error(` Stack:
58
+ ${err.stack.split("\n").slice(0, 8).map((l) => ` ${l}`).join("\n")}`);
59
+ }
60
+ const response = handleError(err, config, includeTrace ? trace : void 0, { tenant });
61
+ if (config?.logError) {
62
+ config.logError(err, c);
63
+ }
64
+ return c.json(response, response.status);
65
+ }
66
+ };
67
+ }
68
+ function handleError(exception, config, trace, context) {
69
+ if (config?.formatError) {
70
+ return config.formatError(exception, {});
71
+ }
72
+ const isDev = process.env.NODE_ENV !== "production";
73
+ const includeDetails = config?.includeTrace ?? isDev;
74
+ let status = 500;
75
+ let message = "Internal server error";
76
+ let validationErrors = void 0;
77
+ if (isJsonParseError(exception)) {
78
+ status = 400;
79
+ message = getJsonErrorDetails(exception);
80
+ validationErrors = {
81
+ body: {
82
+ errors: [
83
+ "O corpo da requisi\xE7\xE3o cont\xE9m JSON inv\xE1lido",
84
+ "Verifique: aspas duplas em strings, v\xEDrgulas entre campos, chaves/colchetes balanceados"
85
+ ]
86
+ }
87
+ };
88
+ } else if (exception instanceof HTTPException) {
89
+ status = exception.status;
90
+ message = exception.message || "Erro na requisi\xE7\xE3o";
91
+ if (exception.cause && isJsonParseError(exception.cause)) {
92
+ status = 400;
93
+ message = getJsonErrorDetails(exception.cause);
94
+ validationErrors = {
95
+ body: {
96
+ errors: [
97
+ "O corpo da requisi\xE7\xE3o cont\xE9m JSON inv\xE1lido",
98
+ "Verifique: aspas duplas em strings, v\xEDrgulas entre campos, chaves/colchetes balanceados"
99
+ ]
100
+ }
101
+ };
102
+ } else if (status === 400 || status === 422) {
103
+ if (exception.cause && typeof exception.cause === "object") {
104
+ const cause = exception.cause;
105
+ if (cause.validationErrors) {
106
+ validationErrors = cause.validationErrors;
107
+ } else if (cause.issues && Array.isArray(cause.issues)) {
108
+ validationErrors = cause.issues.reduce((acc, issue) => {
109
+ const field = issue.path?.join(".") || "unknown";
110
+ if (!acc[field]) {
111
+ acc[field] = { errors: [] };
112
+ }
113
+ acc[field].errors.push(issue.message);
114
+ return acc;
115
+ }, {});
116
+ }
117
+ }
118
+ }
119
+ } else if (exception && typeof exception === "object" && "code" in exception && exception.code && typeof exception.code === "string" && exception.code.startsWith("P")) {
120
+ const prismaError = exception;
121
+ switch (prismaError.code) {
122
+ case "P2003": {
123
+ status = 422;
124
+ const fieldName = prismaError.meta?.field_name;
125
+ if (fieldName) {
126
+ validationErrors = {
127
+ [fieldName]: {
128
+ errors: ["Campo inv\xE1lido ou o registro n\xE3o existe"]
129
+ }
130
+ };
131
+ } else {
132
+ message = "Campo inv\xE1lido ou o registro n\xE3o existe";
133
+ }
134
+ break;
135
+ }
136
+ case "P2002": {
137
+ status = 409;
138
+ message = "Registro j\xE1 cadastrado";
139
+ break;
140
+ }
141
+ case "P2025": {
142
+ status = 404;
143
+ message = "Registro n\xE3o encontrado";
144
+ break;
145
+ }
146
+ default: {
147
+ message = prismaError.message || "Erro no banco de dados";
148
+ break;
149
+ }
150
+ }
151
+ } else if (Array.isArray(exception)) {
152
+ status = 422;
153
+ message = "";
154
+ validationErrors = exception.reduce((acc, error) => {
155
+ acc[error.field] = {
156
+ errors: error.errors
157
+ };
158
+ return acc;
159
+ }, {});
160
+ } else if (exception instanceof Error) {
161
+ if (includeDetails) {
162
+ message = exception.message || "Erro interno do servidor";
163
+ }
164
+ }
165
+ const response = {
166
+ data: null,
167
+ status,
168
+ message
169
+ };
170
+ if ((status === 400 || status === 422) && validationErrors && Object.keys(validationErrors).length > 0) {
171
+ response.validationErrors = validationErrors;
172
+ }
173
+ if (trace) {
174
+ response.trace = trace;
175
+ if (includeDetails && context?.tenant) {
176
+ response.debug = {
177
+ tenant: context.tenant,
178
+ errorMessage: exception?.message,
179
+ errorCause: exception?.cause?.message || exception?.cause
180
+ };
181
+ }
182
+ }
183
+ return response;
184
+ }
185
+
186
+ export {
187
+ createErrorHandler,
188
+ handleError
189
+ };
190
+ //# sourceMappingURL=chunk-SSL7KSRJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/error-handler.ts"],"sourcesContent":["import type { Context } from 'hono';\nimport { HTTPException } from 'hono/http-exception';\nimport type { Prisma } from '../types/prisma';\n\nexport interface ValidationError {\n field: string;\n errors: string[];\n}\n\nexport interface ErrorTrace {\n path: string;\n method: string;\n timestamp: string;\n errorType: string;\n stack?: string;\n}\n\nexport interface ErrorResponse {\n data: null;\n status: number;\n message: string;\n validationErrors?: Record<string, { errors: string[] }>;\n trace?: ErrorTrace;\n}\n\nexport interface ZetiErrorHandlerConfig {\n formatError?: (error: any, context: Context) => ErrorResponse;\n logError?: (error: any, context: Context) => void;\n /** Incluir trace de erro na resposta (default: true em dev, false em prod) */\n includeTrace?: boolean;\n /** Incluir stack trace na resposta (default: false) */\n includeStack?: boolean;\n}\n\n/**\n * Detecta se o erro é relacionado a JSON malformado\n */\nfunction isJsonParseError(err: any): boolean {\n if (!err) return false;\n \n const message = err.message?.toLowerCase() || '';\n const name = err.name?.toLowerCase() || '';\n \n return (\n name === 'syntaxerror' ||\n message.includes('json') ||\n message.includes('unexpected token') ||\n message.includes('unexpected end') ||\n message.includes('malformed') ||\n message.includes('parse') ||\n message.includes('invalid character')\n );\n}\n\n/**\n * Extrai informações detalhadas do erro de JSON\n */\nfunction getJsonErrorDetails(err: any): string {\n const message = err.message || '';\n \n // Tenta extrair posição do erro\n const positionMatch = message.match(/position\\s*(\\d+)/i);\n const lineMatch = message.match(/line\\s*(\\d+)/i);\n const columnMatch = message.match(/column\\s*(\\d+)/i);\n \n let details = 'JSON inválido no corpo da requisição';\n \n if (positionMatch) {\n details += ` (posição ${positionMatch[1]})`;\n }\n if (lineMatch && columnMatch) {\n details += ` (linha ${lineMatch[1]}, coluna ${columnMatch[1]})`;\n }\n \n // Adiciona dica sobre o caractere problemático\n const tokenMatch = message.match(/unexpected token\\s*['\"]?([^'\"]+)['\"]?/i);\n if (tokenMatch) {\n details += `. Caractere inesperado: \"${tokenMatch[1]}\"`;\n }\n \n // Dicas comuns\n if (message.includes('unexpected end')) {\n details += '. Verifique se o JSON está completo (faltando } ou ])';\n }\n \n return details;\n}\n\nexport function createErrorHandler(config?: ZetiErrorHandlerConfig) {\n const isDev = process.env.NODE_ENV !== 'production';\n const includeTrace = config?.includeTrace ?? isDev;\n const includeStack = config?.includeStack ?? false;\n \n return async (c: Context, next: () => Promise<void>) => {\n try {\n await next();\n } catch (err: any) {\n // Extrai informações do request para debug\n const tenant = c.req.header('x-tenant') || c.req.header('X-Tenant') || 'not-set';\n \n // Cria trace do erro\n const trace: ErrorTrace = {\n path: c.req.path,\n method: c.req.method,\n timestamp: new Date().toISOString(),\n errorType: err?.constructor?.name || 'Error',\n };\n \n if (includeStack && err?.stack) {\n trace.stack = err.stack;\n }\n \n // Log do erro no console com detalhes\n console.error(`\\n❌ [${trace.timestamp}] Error in ${trace.method} ${trace.path}`);\n console.error(` Tenant: ${tenant}`);\n console.error(` Type: ${trace.errorType}`);\n console.error(` Message: ${err?.message || 'Unknown error'}`);\n \n // Log de causa aninhada se existir\n if (err?.cause) {\n console.error(` Cause: ${err.cause?.message || err.cause}`);\n }\n \n if (isDev && err?.stack) {\n console.error(` Stack:\\n${err.stack.split('\\n').slice(0, 8).map((l: string) => ` ${l}`).join('\\n')}`);\n }\n \n const response = handleError(err, config, includeTrace ? trace : undefined, { tenant });\n \n if (config?.logError) {\n config.logError(err, c);\n }\n\n return c.json(response, response.status as any);\n }\n };\n}\n\nexport function handleError(\n exception: Error | HTTPException | Prisma.PrismaClientKnownRequestError | ValidationError[] | any,\n config?: ZetiErrorHandlerConfig,\n trace?: ErrorTrace,\n context?: { tenant?: string }\n): ErrorResponse {\n if (config?.formatError) {\n return config.formatError(exception, {} as Context);\n }\n\n const isDev = process.env.NODE_ENV !== 'production';\n const includeDetails = config?.includeTrace ?? isDev;\n \n let status = 500;\n let message = 'Internal server error';\n let validationErrors: Record<string, { errors: string[] }> | undefined = undefined;\n\n // Verifica se é erro de JSON malformado PRIMEIRO\n if (isJsonParseError(exception)) {\n status = 400;\n message = getJsonErrorDetails(exception);\n validationErrors = {\n body: {\n errors: [\n 'O corpo da requisição contém JSON inválido',\n 'Verifique: aspas duplas em strings, vírgulas entre campos, chaves/colchetes balanceados',\n ],\n },\n };\n } else if (exception instanceof HTTPException) {\n status = exception.status;\n message = exception.message || 'Erro na requisição';\n\n // Verifica se a causa é erro de JSON\n if (exception.cause && isJsonParseError(exception.cause)) {\n status = 400;\n message = getJsonErrorDetails(exception.cause);\n validationErrors = {\n body: {\n errors: [\n 'O corpo da requisição contém JSON inválido',\n 'Verifique: aspas duplas em strings, vírgulas entre campos, chaves/colchetes balanceados',\n ],\n },\n };\n } else if (status === 400 || status === 422) {\n if (exception.cause && typeof exception.cause === 'object') {\n const cause = exception.cause as any;\n if (cause.validationErrors) {\n validationErrors = cause.validationErrors;\n } else if (cause.issues && Array.isArray(cause.issues)) {\n validationErrors = cause.issues.reduce((acc: any, issue: any) => {\n const field = issue.path?.join('.') || 'unknown';\n if (!acc[field]) {\n acc[field] = { errors: [] };\n }\n acc[field].errors.push(issue.message);\n return acc;\n }, {});\n }\n }\n }\n } else if (exception && typeof exception === 'object' && 'code' in exception && exception.code && typeof exception.code === 'string' && exception.code.startsWith('P')) {\n const prismaError = exception as any as Prisma.PrismaClientKnownRequestError;\n switch (prismaError.code) {\n case 'P2003': {\n status = 422;\n const fieldName = (prismaError.meta as any)?.field_name as string;\n if (fieldName) {\n validationErrors = {\n [fieldName]: {\n errors: ['Campo inválido ou o registro não existe'],\n },\n };\n } else {\n message = 'Campo inválido ou o registro não existe';\n }\n break;\n }\n\n case 'P2002': {\n status = 409;\n message = 'Registro já cadastrado';\n break;\n }\n\n case 'P2025': {\n status = 404;\n message = 'Registro não encontrado';\n break;\n }\n\n default: {\n message = prismaError.message || 'Erro no banco de dados';\n break;\n }\n }\n } else if (Array.isArray(exception)) {\n status = 422;\n message = '';\n validationErrors = exception.reduce((acc, error: ValidationError) => {\n acc[error.field] = {\n errors: error.errors,\n };\n return acc;\n }, {} as Record<string, { errors: string[] }>);\n } else if (exception instanceof Error) {\n // Em desenvolvimento, mostra a mensagem real do erro\n if (includeDetails) {\n message = exception.message || 'Erro interno do servidor';\n }\n }\n\n const response: ErrorResponse = {\n data: null,\n status,\n message,\n };\n\n if ((status === 400 || status === 422) && validationErrors && Object.keys(validationErrors).length > 0) {\n response.validationErrors = validationErrors;\n }\n\n // Adiciona trace se disponível\n if (trace) {\n response.trace = trace;\n \n // Em desenvolvimento, adiciona informações extras\n if (includeDetails && context?.tenant) {\n (response as any).debug = {\n tenant: context.tenant,\n errorMessage: exception?.message,\n errorCause: exception?.cause?.message || exception?.cause,\n };\n }\n }\n\n return response;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,10 @@
1
+ import {
2
+ createErrorHandler,
3
+ handleError
4
+ } from "./chunk-SSL7KSRJ.js";
5
+ import "./chunk-7D4SUZUM.js";
6
+ export {
7
+ createErrorHandler,
8
+ handleError
9
+ };
10
+ //# sourceMappingURL=error-handler-LBN3A7N3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.js CHANGED
@@ -14,6 +14,10 @@ import {
14
14
  definePrismaConfig,
15
15
  getAllDatabaseUrls
16
16
  } from "./chunk-VW7PIVLA.js";
17
+ import {
18
+ createErrorHandler,
19
+ handleError
20
+ } from "./chunk-SSL7KSRJ.js";
17
21
  import {
18
22
  FILE_SCHEMA_SYMBOL,
19
23
  generateSwagger,
@@ -10109,6 +10113,9 @@ import { createRequire } from "module";
10109
10113
  import { Hono } from "hono";
10110
10114
  import { z } from "zod";
10111
10115
  import { zValidator } from "@hono/zod-validator";
10116
+ import { logger } from "hono/logger";
10117
+ import { cors } from "hono/cors";
10118
+ import { swaggerUI } from "@hono/swagger-ui";
10112
10119
  var require2 = createRequire(import.meta.url);
10113
10120
  var debugEnabled = false;
10114
10121
  function debug(category, message, data) {
@@ -10183,13 +10190,11 @@ function createZetiApp(options) {
10183
10190
  );
10184
10191
  const swaggerRegistry = new SwaggerRegistry();
10185
10192
  if (config.logger !== false) {
10186
- const { logger } = require2("hono/logger");
10187
10193
  honoApp.use("*", logger());
10188
10194
  }
10189
10195
  if (config.cors?.enabled !== false) {
10190
- const { cors } = require2("hono/cors");
10191
10196
  honoApp.use("*", cors({
10192
- origin: config.cors?.origin || "*",
10197
+ origin: config.cors?.origin,
10193
10198
  allowMethods: config.cors?.allowMethods || ["GET", "HEAD", "PUT", "PATCH", "POST", "DELETE", "OPTIONS"],
10194
10199
  allowHeaders: config.cors?.allowHeaders || ["*"],
10195
10200
  credentials: config.cors?.credentials !== false,
@@ -10208,8 +10213,8 @@ function createZetiApp(options) {
10208
10213
  });
10209
10214
  if (config.errorHandler !== false) {
10210
10215
  const errorHandlerConfig = typeof config.errorHandler === "object" ? config.errorHandler : void 0;
10211
- honoApp.onError((err, c) => {
10212
- const { handleError: handleError2 } = require2("../utils/error-handler");
10216
+ honoApp.onError(async (err, c) => {
10217
+ const { handleError: handleError2 } = await import("./error-handler-LBN3A7N3.js");
10213
10218
  const isDev = process.env.NODE_ENV !== "production";
10214
10219
  const includeTrace = errorHandlerConfig?.includeTrace ?? isDev;
10215
10220
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString();
@@ -10232,7 +10237,6 @@ function createZetiApp(options) {
10232
10237
  const docPath = config.swagger.docPath || "/swagger/doc";
10233
10238
  let cachedSwaggerSpec = null;
10234
10239
  let cachedSchemas = null;
10235
- const { swaggerUI } = require2("@hono/swagger-ui");
10236
10240
  honoApp.get(uiPath, swaggerUI({ url: docPath }));
10237
10241
  honoApp.get(docPath, async (c) => {
10238
10242
  const { generateSwagger: generateSwagger2 } = await import("./generator-KC24DE6M.js");
@@ -11239,7 +11243,7 @@ function defineZetiConfig(config, options) {
11239
11243
  ...config.swagger,
11240
11244
  servers: config.swagger.servers || swaggerDefaults.servers
11241
11245
  };
11242
- const cors = {
11246
+ const cors2 = {
11243
11247
  ...DEFAULT_CORS,
11244
11248
  ...config.cors
11245
11249
  };
@@ -11253,7 +11257,7 @@ function defineZetiConfig(config, options) {
11253
11257
  };
11254
11258
  const baseConfig = {
11255
11259
  swagger,
11256
- cors,
11260
+ cors: cors2,
11257
11261
  prisma,
11258
11262
  dev,
11259
11263
  logger: config.logger ?? true,
@@ -11293,216 +11297,31 @@ function defineZetiConfig(config, options) {
11293
11297
  };
11294
11298
  }
11295
11299
 
11296
- // src/utils/error-handler.ts
11297
- import { HTTPException } from "hono/http-exception";
11298
- function isJsonParseError(err) {
11299
- if (!err) return false;
11300
- const message = err.message?.toLowerCase() || "";
11301
- const name = err.name?.toLowerCase() || "";
11302
- return name === "syntaxerror" || message.includes("json") || message.includes("unexpected token") || message.includes("unexpected end") || message.includes("malformed") || message.includes("parse") || message.includes("invalid character");
11303
- }
11304
- function getJsonErrorDetails(err) {
11305
- const message = err.message || "";
11306
- const positionMatch = message.match(/position\s*(\d+)/i);
11307
- const lineMatch = message.match(/line\s*(\d+)/i);
11308
- const columnMatch = message.match(/column\s*(\d+)/i);
11309
- let details = "JSON inv\xE1lido no corpo da requisi\xE7\xE3o";
11310
- if (positionMatch) {
11311
- details += ` (posi\xE7\xE3o ${positionMatch[1]})`;
11312
- }
11313
- if (lineMatch && columnMatch) {
11314
- details += ` (linha ${lineMatch[1]}, coluna ${columnMatch[1]})`;
11315
- }
11316
- const tokenMatch = message.match(/unexpected token\s*['"]?([^'"]+)['"]?/i);
11317
- if (tokenMatch) {
11318
- details += `. Caractere inesperado: "${tokenMatch[1]}"`;
11319
- }
11320
- if (message.includes("unexpected end")) {
11321
- details += ". Verifique se o JSON est\xE1 completo (faltando } ou ])";
11322
- }
11323
- return details;
11324
- }
11325
- function createErrorHandler(config) {
11326
- const isDev = process.env.NODE_ENV !== "production";
11327
- const includeTrace = config?.includeTrace ?? isDev;
11328
- const includeStack = config?.includeStack ?? false;
11329
- return async (c, next) => {
11330
- try {
11331
- await next();
11332
- } catch (err) {
11333
- const tenant = c.req.header("x-tenant") || c.req.header("X-Tenant") || "not-set";
11334
- const trace = {
11335
- path: c.req.path,
11336
- method: c.req.method,
11337
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
11338
- errorType: err?.constructor?.name || "Error"
11339
- };
11340
- if (includeStack && err?.stack) {
11341
- trace.stack = err.stack;
11342
- }
11343
- console.error(`
11344
- \u274C [${trace.timestamp}] Error in ${trace.method} ${trace.path}`);
11345
- console.error(` Tenant: ${tenant}`);
11346
- console.error(` Type: ${trace.errorType}`);
11347
- console.error(` Message: ${err?.message || "Unknown error"}`);
11348
- if (err?.cause) {
11349
- console.error(` Cause: ${err.cause?.message || err.cause}`);
11350
- }
11351
- if (isDev && err?.stack) {
11352
- console.error(` Stack:
11353
- ${err.stack.split("\n").slice(0, 8).map((l) => ` ${l}`).join("\n")}`);
11354
- }
11355
- const response = handleError(err, config, includeTrace ? trace : void 0, { tenant });
11356
- if (config?.logError) {
11357
- config.logError(err, c);
11358
- }
11359
- return c.json(response, response.status);
11360
- }
11361
- };
11362
- }
11363
- function handleError(exception, config, trace, context) {
11364
- if (config?.formatError) {
11365
- return config.formatError(exception, {});
11366
- }
11367
- const isDev = process.env.NODE_ENV !== "production";
11368
- const includeDetails = config?.includeTrace ?? isDev;
11369
- let status = 500;
11370
- let message = "Internal server error";
11371
- let validationErrors = void 0;
11372
- if (isJsonParseError(exception)) {
11373
- status = 400;
11374
- message = getJsonErrorDetails(exception);
11375
- validationErrors = {
11376
- body: {
11377
- errors: [
11378
- "O corpo da requisi\xE7\xE3o cont\xE9m JSON inv\xE1lido",
11379
- "Verifique: aspas duplas em strings, v\xEDrgulas entre campos, chaves/colchetes balanceados"
11380
- ]
11381
- }
11382
- };
11383
- } else if (exception instanceof HTTPException) {
11384
- status = exception.status;
11385
- message = exception.message || "Erro na requisi\xE7\xE3o";
11386
- if (exception.cause && isJsonParseError(exception.cause)) {
11387
- status = 400;
11388
- message = getJsonErrorDetails(exception.cause);
11389
- validationErrors = {
11390
- body: {
11391
- errors: [
11392
- "O corpo da requisi\xE7\xE3o cont\xE9m JSON inv\xE1lido",
11393
- "Verifique: aspas duplas em strings, v\xEDrgulas entre campos, chaves/colchetes balanceados"
11394
- ]
11395
- }
11396
- };
11397
- } else if (status === 400 || status === 422) {
11398
- if (exception.cause && typeof exception.cause === "object") {
11399
- const cause = exception.cause;
11400
- if (cause.validationErrors) {
11401
- validationErrors = cause.validationErrors;
11402
- } else if (cause.issues && Array.isArray(cause.issues)) {
11403
- validationErrors = cause.issues.reduce((acc, issue) => {
11404
- const field = issue.path?.join(".") || "unknown";
11405
- if (!acc[field]) {
11406
- acc[field] = { errors: [] };
11407
- }
11408
- acc[field].errors.push(issue.message);
11409
- return acc;
11410
- }, {});
11411
- }
11412
- }
11413
- }
11414
- } else if (exception && typeof exception === "object" && "code" in exception && exception.code && typeof exception.code === "string" && exception.code.startsWith("P")) {
11415
- const prismaError = exception;
11416
- switch (prismaError.code) {
11417
- case "P2003": {
11418
- status = 422;
11419
- const fieldName = prismaError.meta?.field_name;
11420
- if (fieldName) {
11421
- validationErrors = {
11422
- [fieldName]: {
11423
- errors: ["Campo inv\xE1lido ou o registro n\xE3o existe"]
11424
- }
11425
- };
11426
- } else {
11427
- message = "Campo inv\xE1lido ou o registro n\xE3o existe";
11428
- }
11429
- break;
11430
- }
11431
- case "P2002": {
11432
- status = 409;
11433
- message = "Registro j\xE1 cadastrado";
11434
- break;
11435
- }
11436
- case "P2025": {
11437
- status = 404;
11438
- message = "Registro n\xE3o encontrado";
11439
- break;
11440
- }
11441
- default: {
11442
- message = prismaError.message || "Erro no banco de dados";
11443
- break;
11444
- }
11445
- }
11446
- } else if (Array.isArray(exception)) {
11447
- status = 422;
11448
- message = "";
11449
- validationErrors = exception.reduce((acc, error) => {
11450
- acc[error.field] = {
11451
- errors: error.errors
11452
- };
11453
- return acc;
11454
- }, {});
11455
- } else if (exception instanceof Error) {
11456
- if (includeDetails) {
11457
- message = exception.message || "Erro interno do servidor";
11458
- }
11459
- }
11460
- const response = {
11461
- data: null,
11462
- status,
11463
- message
11464
- };
11465
- if ((status === 400 || status === 422) && validationErrors && Object.keys(validationErrors).length > 0) {
11466
- response.validationErrors = validationErrors;
11467
- }
11468
- if (trace) {
11469
- response.trace = trace;
11470
- if (includeDetails && context?.tenant) {
11471
- response.debug = {
11472
- tenant: context.tenant,
11473
- errorMessage: exception?.message,
11474
- errorCause: exception?.cause?.message || exception?.cause
11475
- };
11476
- }
11477
- }
11478
- return response;
11479
- }
11480
-
11481
11300
  // src/utils/http-exceptions.ts
11482
- import { HTTPException as HTTPException2 } from "hono/http-exception";
11301
+ import { HTTPException } from "hono/http-exception";
11483
11302
  function badRequestError(message, cause) {
11484
- throw new HTTPException2(400, { message, cause });
11303
+ throw new HTTPException(400, { message, cause });
11485
11304
  }
11486
11305
  function unauthorizedError(message, cause) {
11487
- throw new HTTPException2(401, { message, cause });
11306
+ throw new HTTPException(401, { message, cause });
11488
11307
  }
11489
11308
  function forbiddenError(message, cause) {
11490
- throw new HTTPException2(403, { message, cause });
11309
+ throw new HTTPException(403, { message, cause });
11491
11310
  }
11492
11311
  function notFoundError(message, cause) {
11493
- throw new HTTPException2(404, { message, cause });
11312
+ throw new HTTPException(404, { message, cause });
11494
11313
  }
11495
11314
  function conflictError(message, cause) {
11496
- throw new HTTPException2(409, { message, cause });
11315
+ throw new HTTPException(409, { message, cause });
11497
11316
  }
11498
11317
  function goneError(message, cause) {
11499
- throw new HTTPException2(410, { message, cause });
11318
+ throw new HTTPException(410, { message, cause });
11500
11319
  }
11501
11320
  function unprocessableEntityError(message, cause) {
11502
- throw new HTTPException2(422, { message, cause });
11321
+ throw new HTTPException(422, { message, cause });
11503
11322
  }
11504
11323
  function internalServerError(message, cause) {
11505
- throw new HTTPException2(500, { message, cause });
11324
+ throw new HTTPException(500, { message, cause });
11506
11325
  }
11507
11326
 
11508
11327
  // src/utils/middleware.ts