zeti-framework-backend 0.2.4 → 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.
- package/README.md +0 -0
- package/dist/chunk-SSL7KSRJ.js +190 -0
- package/dist/chunk-SSL7KSRJ.js.map +1 -0
- package/dist/chunk-VW7PIVLA.js +64 -0
- package/dist/chunk-VW7PIVLA.js.map +1 -0
- package/dist/error-handler-LBN3A7N3.js +10 -0
- package/dist/error-handler-LBN3A7N3.js.map +1 -0
- package/dist/index.d.ts +2 -54
- package/dist/index.js +25 -261
- package/dist/index.js.map +1 -1
- package/dist/prisma/index.d.ts +59 -0
- package/dist/prisma/index.js +10 -0
- package/dist/prisma/index.js.map +1 -0
- package/package.json +5 -1
package/README.md
ADDED
|
Binary file
|
|
@@ -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,64 @@
|
|
|
1
|
+
// src/prisma/config.ts
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
function findDatabaseUrl() {
|
|
4
|
+
if (process.env.DATABASE_URL) {
|
|
5
|
+
return process.env.DATABASE_URL;
|
|
6
|
+
}
|
|
7
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
8
|
+
if (!value) continue;
|
|
9
|
+
if (key.endsWith("_DATABASE_URL") || key.startsWith("DATABASE_") && key.endsWith("_URL")) {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
throw new Error(
|
|
14
|
+
"Nenhuma URL de banco de dados encontrada no .env. Configure DATABASE_URL ou uma vari\xE1vel que termine com _DATABASE_URL."
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
function definePrismaConfig(zetiConfigOrOptions, options) {
|
|
18
|
+
let finalOptions;
|
|
19
|
+
let zetiConfig;
|
|
20
|
+
if (zetiConfigOrOptions && "databases" in zetiConfigOrOptions) {
|
|
21
|
+
zetiConfig = zetiConfigOrOptions;
|
|
22
|
+
finalOptions = options;
|
|
23
|
+
} else if (zetiConfigOrOptions) {
|
|
24
|
+
finalOptions = zetiConfigOrOptions;
|
|
25
|
+
}
|
|
26
|
+
const schema = finalOptions?.schema || "prisma/schema.prisma";
|
|
27
|
+
const migrationsPath = finalOptions?.migrationsPath || "prisma/migrations";
|
|
28
|
+
let databaseUrl;
|
|
29
|
+
if (zetiConfig) {
|
|
30
|
+
if (zetiConfig.databases?.connections?.default) {
|
|
31
|
+
databaseUrl = zetiConfig.databases.connections.default;
|
|
32
|
+
} else if (zetiConfig.databases?.connections) {
|
|
33
|
+
const firstConnection = Object.values(zetiConfig.databases.connections)[0];
|
|
34
|
+
if (firstConnection) {
|
|
35
|
+
databaseUrl = firstConnection;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!databaseUrl) {
|
|
40
|
+
databaseUrl = findDatabaseUrl();
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
schema,
|
|
44
|
+
migrations: {
|
|
45
|
+
path: migrationsPath
|
|
46
|
+
},
|
|
47
|
+
datasource: {
|
|
48
|
+
url: databaseUrl
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function getAllDatabaseUrls(zetiConfig) {
|
|
53
|
+
const urls = {};
|
|
54
|
+
if (zetiConfig.databases.connections) {
|
|
55
|
+
Object.assign(urls, zetiConfig.databases.connections);
|
|
56
|
+
}
|
|
57
|
+
return urls;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
definePrismaConfig,
|
|
62
|
+
getAllDatabaseUrls
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=chunk-VW7PIVLA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/prisma/config.ts"],"sourcesContent":["import \"dotenv/config\";\r\nimport type { ZetiConfigInternal } from '../types/config';\r\n\r\nexport interface PrismaConfigOptions {\r\n /** Caminho do schema. Default: \"prisma/schema.prisma\" */\r\n schema?: string;\r\n /** Caminho das migrations. Default: \"prisma/migrations\" */\r\n migrationsPath?: string;\r\n}\r\n\r\ninterface PrismaDefineConfig {\r\n schema: string;\r\n migrations: {\r\n path: string;\r\n };\r\n datasource: {\r\n url: string;\r\n };\r\n}\r\n\r\n/**\r\n * Busca a URL do banco de dados no .env.\r\n * Procura por DATABASE_URL ou qualquer variável que termine com _DATABASE_URL.\r\n */\r\nfunction findDatabaseUrl(): string {\r\n // 1. Tenta DATABASE_URL (padrão para single-tenant e migrations)\r\n if (process.env.DATABASE_URL) {\r\n return process.env.DATABASE_URL;\r\n }\r\n\r\n // 2. Tenta encontrar qualquer URL de banco (para migrations de multi-tenant)\r\n for (const [key, value] of Object.entries(process.env)) {\r\n if (!value) continue;\r\n\r\n // Qualquer variável que termine com _DATABASE_URL\r\n if (key.endsWith('_DATABASE_URL') || (key.startsWith('DATABASE_') && key.endsWith('_URL'))) {\r\n return value;\r\n }\r\n }\r\n\r\n throw new Error(\r\n 'Nenhuma URL de banco de dados encontrada no .env. ' +\r\n 'Configure DATABASE_URL ou uma variável que termine com _DATABASE_URL.'\r\n );\r\n}\r\n\r\n/**\r\n * Gera a configuração do Prisma.\r\n * \r\n * Aceita opcionalmente um ZetiConfig, mas NÃO É NECESSÁRIO passá-lo.\r\n * A função busca automaticamente a URL do banco no .env.\r\n * \r\n * @example Uso simples (recomendado para evitar importar zeti.config.ts)\r\n * ```typescript\r\n * // prisma.config.ts\r\n * import { definePrismaConfig } from \"zeti-framework\";\r\n * \r\n * export default definePrismaConfig();\r\n * ```\r\n * \r\n * @example Uso com config (opcional)\r\n * ```typescript\r\n * // prisma.config.ts\r\n * import { zetiConfig } from \"./zeti.config\";\r\n * import { definePrismaConfig } from \"zeti-framework\";\r\n * \r\n * export default definePrismaConfig(zetiConfig);\r\n * ```\r\n */\r\nexport function definePrismaConfig(\r\n zetiConfigOrOptions?: ZetiConfigInternal | PrismaConfigOptions,\r\n options?: PrismaConfigOptions\r\n): PrismaDefineConfig {\r\n // Detecta se o primeiro argumento é um ZetiConfig ou PrismaConfigOptions\r\n let finalOptions: PrismaConfigOptions | undefined;\r\n let zetiConfig: ZetiConfigInternal | undefined;\r\n\r\n if (zetiConfigOrOptions && 'databases' in zetiConfigOrOptions) {\r\n // É um ZetiConfig\r\n zetiConfig = zetiConfigOrOptions;\r\n finalOptions = options;\r\n } else if (zetiConfigOrOptions) {\r\n // É PrismaConfigOptions\r\n finalOptions = zetiConfigOrOptions as PrismaConfigOptions;\r\n }\r\n\r\n const schema = finalOptions?.schema || \"prisma/schema.prisma\";\r\n const migrationsPath = finalOptions?.migrationsPath || \"prisma/migrations\";\r\n\r\n // Pega a URL do banco de dados\r\n let databaseUrl: string | undefined;\r\n\r\n if (zetiConfig) {\r\n // Se passou zetiConfig, tenta pegar do config primeiro\r\n if (zetiConfig.databases?.connections?.default) {\r\n databaseUrl = zetiConfig.databases.connections.default;\r\n } else if (zetiConfig.databases?.connections) {\r\n const firstConnection = Object.values(zetiConfig.databases.connections)[0];\r\n if (firstConnection) {\r\n databaseUrl = firstConnection;\r\n }\r\n }\r\n }\r\n\r\n // Se não encontrou no config (ou não passou config), busca no .env\r\n if (!databaseUrl) {\r\n databaseUrl = findDatabaseUrl();\r\n }\r\n\r\n return {\r\n schema,\r\n migrations: {\r\n path: migrationsPath,\r\n },\r\n datasource: {\r\n url: databaseUrl,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Retorna todas as URLs de banco de dados configuradas (para multi-tenant migrations).\r\n * \r\n * @example\r\n * ```typescript\r\n * const urls = getAllDatabaseUrls(zetiConfig);\r\n * for (const [name, url] of Object.entries(urls)) {\r\n * console.log(`Migrating ${name}...`);\r\n * // executar migration para cada tenant\r\n * }\r\n * ```\r\n */\r\nexport function getAllDatabaseUrls(zetiConfig: ZetiConfigInternal): Record<string, string> {\r\n const urls: Record<string, string> = {};\r\n\r\n // Carrega conexões do config\r\n if (zetiConfig.databases.connections) {\r\n Object.assign(urls, zetiConfig.databases.connections);\r\n }\r\n\r\n return urls;\r\n}\r\n"],"mappings":";AAAA,OAAO;AAwBP,SAAS,kBAA0B;AAEjC,MAAI,QAAQ,IAAI,cAAc;AAC5B,WAAO,QAAQ,IAAI;AAAA,EACrB;AAGA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG,GAAG;AACtD,QAAI,CAAC,MAAO;AAGZ,QAAI,IAAI,SAAS,eAAe,KAAM,IAAI,WAAW,WAAW,KAAK,IAAI,SAAS,MAAM,GAAI;AAC1F,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EAEF;AACF;AAyBO,SAAS,mBACd,qBACA,SACoB;AAEpB,MAAI;AACJ,MAAI;AAEJ,MAAI,uBAAuB,eAAe,qBAAqB;AAE7D,iBAAa;AACb,mBAAe;AAAA,EACjB,WAAW,qBAAqB;AAE9B,mBAAe;AAAA,EACjB;AAEA,QAAM,SAAS,cAAc,UAAU;AACvC,QAAM,iBAAiB,cAAc,kBAAkB;AAGvD,MAAI;AAEJ,MAAI,YAAY;AAEd,QAAI,WAAW,WAAW,aAAa,SAAS;AAC9C,oBAAc,WAAW,UAAU,YAAY;AAAA,IACjD,WAAW,WAAW,WAAW,aAAa;AAC5C,YAAM,kBAAkB,OAAO,OAAO,WAAW,UAAU,WAAW,EAAE,CAAC;AACzE,UAAI,iBAAiB;AACnB,sBAAc;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AAGA,MAAI,CAAC,aAAa;AAChB,kBAAc,gBAAgB;AAAA,EAChC;AAEA,SAAO;AAAA,IACL;AAAA,IACA,YAAY;AAAA,MACV,MAAM;AAAA,IACR;AAAA,IACA,YAAY;AAAA,MACV,KAAK;AAAA,IACP;AAAA,EACF;AACF;AAcO,SAAS,mBAAmB,YAAwD;AACzF,QAAM,OAA+B,CAAC;AAGtC,MAAI,WAAW,UAAU,aAAa;AACpC,WAAO,OAAO,MAAM,WAAW,UAAU,WAAW;AAAA,EACtD;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { TenantManager } from './tenants/index.js';
|
|
|
8
8
|
export { MetricsCollector, TenantMetrics } from './tenants/index.js';
|
|
9
9
|
import Redis from 'ioredis';
|
|
10
10
|
export { default as Redis } from 'ioredis';
|
|
11
|
+
export { PrismaConfigOptions, definePrismaConfig, getAllDatabaseUrls } from './prisma/index.js';
|
|
11
12
|
import { z } from 'zod';
|
|
12
13
|
export { GenerateRegistryOptions, GenerateSwaggerTypesOptions, generateRegistry, generateSwaggerTypes, runPrebuild } from './scripts/index.js';
|
|
13
14
|
import 'hono/http-exception';
|
|
@@ -162,59 +163,6 @@ declare function hasRedis(): boolean;
|
|
|
162
163
|
declare function getRedisConfiguration(): RedisConfig | null;
|
|
163
164
|
declare function shutdownZeti(): Promise<void>;
|
|
164
165
|
|
|
165
|
-
interface PrismaConfigOptions {
|
|
166
|
-
/** Caminho do schema. Default: "prisma/schema.prisma" */
|
|
167
|
-
schema?: string;
|
|
168
|
-
/** Caminho das migrations. Default: "prisma/migrations" */
|
|
169
|
-
migrationsPath?: string;
|
|
170
|
-
}
|
|
171
|
-
interface PrismaDefineConfig {
|
|
172
|
-
schema: string;
|
|
173
|
-
migrations: {
|
|
174
|
-
path: string;
|
|
175
|
-
};
|
|
176
|
-
datasource: {
|
|
177
|
-
url: string;
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
/**
|
|
181
|
-
* Gera a configuração do Prisma.
|
|
182
|
-
*
|
|
183
|
-
* Aceita opcionalmente um ZetiConfig, mas NÃO É NECESSÁRIO passá-lo.
|
|
184
|
-
* A função busca automaticamente a URL do banco no .env.
|
|
185
|
-
*
|
|
186
|
-
* @example Uso simples (recomendado para evitar importar zeti.config.ts)
|
|
187
|
-
* ```typescript
|
|
188
|
-
* // prisma.config.ts
|
|
189
|
-
* import { definePrismaConfig } from "zeti-framework";
|
|
190
|
-
*
|
|
191
|
-
* export default definePrismaConfig();
|
|
192
|
-
* ```
|
|
193
|
-
*
|
|
194
|
-
* @example Uso com config (opcional)
|
|
195
|
-
* ```typescript
|
|
196
|
-
* // prisma.config.ts
|
|
197
|
-
* import { zetiConfig } from "./zeti.config";
|
|
198
|
-
* import { definePrismaConfig } from "zeti-framework";
|
|
199
|
-
*
|
|
200
|
-
* export default definePrismaConfig(zetiConfig);
|
|
201
|
-
* ```
|
|
202
|
-
*/
|
|
203
|
-
declare function definePrismaConfig(zetiConfigOrOptions?: ZetiConfigInternal | PrismaConfigOptions, options?: PrismaConfigOptions): PrismaDefineConfig;
|
|
204
|
-
/**
|
|
205
|
-
* Retorna todas as URLs de banco de dados configuradas (para multi-tenant migrations).
|
|
206
|
-
*
|
|
207
|
-
* @example
|
|
208
|
-
* ```typescript
|
|
209
|
-
* const urls = getAllDatabaseUrls(zetiConfig);
|
|
210
|
-
* for (const [name, url] of Object.entries(urls)) {
|
|
211
|
-
* console.log(`Migrating ${name}...`);
|
|
212
|
-
* // executar migration para cada tenant
|
|
213
|
-
* }
|
|
214
|
-
* ```
|
|
215
|
-
*/
|
|
216
|
-
declare function getAllDatabaseUrls(zetiConfig: ZetiConfigInternal): Record<string, string>;
|
|
217
|
-
|
|
218
166
|
declare function badRequestError(message: string, cause?: any): never;
|
|
219
167
|
declare function unauthorizedError(message: string, cause?: any): never;
|
|
220
168
|
declare function forbiddenError(message: string, cause?: any): never;
|
|
@@ -330,4 +278,4 @@ interface DatabaseContext {
|
|
|
330
278
|
*/
|
|
331
279
|
declare function getDbFromContext<TPrisma = any>(c: Context): TPrisma;
|
|
332
280
|
|
|
333
|
-
export { type DatabaseContext, FILE_SCHEMA_SYMBOL,
|
|
281
|
+
export { type DatabaseContext, FILE_SCHEMA_SYMBOL, RedisConfig, SchemaDefinition, type StartZetiOptions, SwaggerRegistry, TenantManager, WorkerConfig, WorkerInstance, WorkerManager, type ZetiApp, ZetiConfigInternal, ZetiMethodOptions, type ZetiMiddlewareHandler, ZetiPrismaConnectionConfig, autoInitializeZetiApp, badRequestError, conflictError, createAppProxy, createDatabaseMiddleware, createZetiApp, env, forbiddenError, getDbFromContext, getPrismaClient, getPrismaConnectionConfig, getRedis, getRedisConfiguration, getWorkerManager, getZetiApp, getZetiAppTyped, goneError, hasRedis, honoRoutesZeti, initializeZetiApp, internalServerError, isFileSchema, isMultipleFileSchema, loadEnv, notFoundError, shutdownZeti, startZeti, stopAllWorkers, unauthorizedError, unprocessableEntityError, zFile, zFiles };
|