zeti-framework-backend 0.2.4

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,647 @@
1
+ import * as hono from 'hono';
2
+ import { Context, Next } from 'hono';
3
+ import Redis, { RedisOptions } from 'ioredis';
4
+ import { HTTPException } from 'hono/http-exception';
5
+
6
+ /**
7
+ * Tipo da função do Worker
8
+ * Recebe o db e opcionalmente o tenantId
9
+ */
10
+ type WorkerFn = (db: any, tenantId?: string) => Promise<void> | void;
11
+ /**
12
+ * Configuração de um Worker individual (quando precisa de opções extras)
13
+ */
14
+ interface WorkerConfigObject {
15
+ /**
16
+ * Função que será executada periodicamente
17
+ */
18
+ fn: WorkerFn;
19
+ /**
20
+ * Intervalo de execução em segundos
21
+ * @default 30
22
+ */
23
+ intervalSeconds?: number;
24
+ /**
25
+ * Se true, executa para cada tenant separadamente (multi-tenant)
26
+ * @default false
27
+ */
28
+ multiTenant?: boolean;
29
+ /**
30
+ * Se true, executa imediatamente ao iniciar
31
+ * @default true
32
+ */
33
+ runImmediately?: boolean;
34
+ }
35
+ /**
36
+ * Worker pode ser apenas a função ou um objeto com configurações
37
+ *
38
+ * @example Apenas função (usa defaults)
39
+ * ```typescript
40
+ * workers: {
41
+ * email: emailWorker,
42
+ * }
43
+ * ```
44
+ *
45
+ * @example Com configurações
46
+ * ```typescript
47
+ * workers: {
48
+ * email: {
49
+ * fn: emailWorker,
50
+ * intervalSeconds: 60,
51
+ * },
52
+ * }
53
+ * ```
54
+ */
55
+ type WorkerConfig = WorkerFn | WorkerConfigObject;
56
+ /**
57
+ * Configuração de workers no zetiConfig
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * workers: {
62
+ * // Apenas a função (usa intervalSeconds: 30 por padrão)
63
+ * email: emailWorker,
64
+ *
65
+ * // Com configurações customizadas
66
+ * sync: {
67
+ * fn: syncWorker,
68
+ * intervalSeconds: 60,
69
+ * multiTenant: true,
70
+ * },
71
+ * }
72
+ * ```
73
+ */
74
+ type WorkersConfig = Record<string, WorkerConfig>;
75
+
76
+ /**
77
+ * Redis Client para o Zeti Framework
78
+ *
79
+ * Fornece um cliente Redis singleton com configuração flexível
80
+ * e integração com o ciclo de vida do framework.
81
+ */
82
+
83
+ interface RedisConfig {
84
+ /** URL de conexão Redis (ex: redis://localhost:6379) */
85
+ url?: string;
86
+ /** Host do Redis (default: localhost) */
87
+ host?: string;
88
+ /** Porta do Redis (default: 6379) */
89
+ port?: number;
90
+ /** Senha do Redis */
91
+ password?: string;
92
+ /** Número do database (default: 0) */
93
+ db?: number;
94
+ /** Prefixo para todas as chaves (ex: 'auth:') */
95
+ keyPrefix?: string;
96
+ /** Máximo de retries por request (default: 3) */
97
+ maxRetriesPerRequest?: number;
98
+ /** Verificar conexão ao iniciar (default: true) */
99
+ enableReadyCheck?: boolean;
100
+ /** Conectar sob demanda (default: false) */
101
+ lazyConnect?: boolean;
102
+ /** TLS options */
103
+ tls?: RedisOptions['tls'];
104
+ }
105
+ /**
106
+ * Inicializa o cliente Redis com a configuração fornecida.
107
+ *
108
+ * **Fail-Fast**: Se o Redis não conseguir conectar, a aplicação falha ao iniciar.
109
+ * Isso garante que se o Redis está configurado, ele DEVE estar disponível.
110
+ *
111
+ * @param config - Configuração do Redis
112
+ * @returns Promise com a instância do cliente Redis
113
+ * @throws Error se não conseguir conectar ao Redis
114
+ */
115
+ declare function initRedis(config: RedisConfig): Promise<Redis>;
116
+ /**
117
+ * Retorna o cliente Redis singleton.
118
+ *
119
+ * @throws Error se o Redis não foi inicializado
120
+ * @returns Instância do cliente Redis (ioredis)
121
+ */
122
+ declare function getRedisClient(): Redis;
123
+ /**
124
+ * Fecha a conexão Redis graciosamente.
125
+ */
126
+ declare function closeRedis(): Promise<void>;
127
+ /**
128
+ * Retorna a configuração atual do Redis.
129
+ */
130
+ declare function getRedisConfig(): RedisConfig | null;
131
+
132
+ /**
133
+ * Tipos próprios para Prisma
134
+ *
135
+ * Estes tipos são usados para evitar dependência direta de @prisma/client
136
+ * durante o build da biblioteca, já que Prisma é uma peer dependency.
137
+ *
138
+ * O usuário passa o PrismaClient real como generic, então o TypeScript
139
+ * vai inferir os tipos corretos em tempo de uso.
140
+ */
141
+ type PrismaClientBase = {
142
+ $connect: () => Promise<void>;
143
+ $disconnect: () => Promise<void>;
144
+ $transaction: <T>(fn: (client: any) => Promise<T>) => Promise<T>;
145
+ $queryRaw: any;
146
+ $executeRaw: any;
147
+ [key: string]: any;
148
+ };
149
+ declare namespace Prisma {
150
+ interface PrismaClientKnownRequestError extends Error {
151
+ code: string;
152
+ meta?: any;
153
+ clientVersion?: string;
154
+ }
155
+ interface PrismaClientValidationError extends Error {
156
+ message: string;
157
+ }
158
+ interface PrismaClientInitializationError extends Error {
159
+ errorCode?: string;
160
+ clientVersion?: string;
161
+ }
162
+ interface PrismaClientRustPanicError extends Error {
163
+ requestId?: string;
164
+ clientVersion?: string;
165
+ }
166
+ type PrismaClientError = PrismaClientKnownRequestError | PrismaClientValidationError | PrismaClientInitializationError | PrismaClientRustPanicError;
167
+ }
168
+
169
+ interface ValidationError {
170
+ field: string;
171
+ errors: string[];
172
+ }
173
+ interface ErrorTrace {
174
+ path: string;
175
+ method: string;
176
+ timestamp: string;
177
+ errorType: string;
178
+ stack?: string;
179
+ }
180
+ interface ErrorResponse {
181
+ data: null;
182
+ status: number;
183
+ message: string;
184
+ validationErrors?: Record<string, {
185
+ errors: string[];
186
+ }>;
187
+ trace?: ErrorTrace;
188
+ }
189
+ interface ZetiErrorHandlerConfig {
190
+ formatError?: (error: any, context: Context) => ErrorResponse;
191
+ logError?: (error: any, context: Context) => void;
192
+ /** Incluir trace de erro na resposta (default: true em dev, false em prod) */
193
+ includeTrace?: boolean;
194
+ /** Incluir stack trace na resposta (default: false) */
195
+ includeStack?: boolean;
196
+ }
197
+ declare function createErrorHandler(config?: ZetiErrorHandlerConfig): (c: Context, next: () => Promise<void>) => Promise<(Response & hono.TypedResponse<{
198
+ data: null;
199
+ status: number;
200
+ message: string;
201
+ validationErrors?: {
202
+ [x: string]: {
203
+ errors: string[];
204
+ };
205
+ } | undefined;
206
+ trace?: {
207
+ path: string;
208
+ method: string;
209
+ timestamp: string;
210
+ errorType: string;
211
+ stack?: string | undefined;
212
+ } | undefined;
213
+ }, any, "json">) | undefined>;
214
+ declare function handleError(exception: Error | HTTPException | Prisma.PrismaClientKnownRequestError | ValidationError[] | any, config?: ZetiErrorHandlerConfig, trace?: ErrorTrace, context?: {
215
+ tenant?: string;
216
+ }): ErrorResponse;
217
+
218
+ type Middleware = (c: Context, next: Next) => Promise<Response | void> | Response | void;
219
+ interface ZetiDatabaseConfig {
220
+ /**
221
+ * Conexões de banco de dados.
222
+ *
223
+ * Para single-tenant: { default: "connection_string" }
224
+ * Para multi-tenant: { tenantName: "connection_string", ... }
225
+ *
226
+ * O valor do header x-tenant (ou headerName configurado) é usado como chave.
227
+ *
228
+ * @example
229
+ * ```typescript
230
+ * connections: {
231
+ * mulherdevalor: env("TENANT_MULHERDEVALOR_DATABASE_URL"),
232
+ * outroTenant: env("TENANT_OUTRO_DATABASE_URL"),
233
+ * }
234
+ * ```
235
+ */
236
+ connections: Record<string, string>;
237
+ /**
238
+ * Resolver dinâmico para conexões não encontradas em `connections`.
239
+ * Útil para cenários onde os tenants são criados dinamicamente.
240
+ */
241
+ resolver?: (tenantId: string) => Promise<string> | string;
242
+ /** Configuração de cache de conexões */
243
+ cache?: {
244
+ maxClients?: number;
245
+ ttl?: number;
246
+ cleanupInterval?: number;
247
+ };
248
+ }
249
+ interface ZetiPrismaConfig {
250
+ adapter?: 'pg' | 'mysql' | 'sqlite' | 'custom';
251
+ logLevel?: ('query' | 'error' | 'warn')[];
252
+ connectionPool?: {
253
+ max?: number;
254
+ min?: number;
255
+ idleTimeoutMillis?: number;
256
+ connectionTimeoutMillis?: number;
257
+ };
258
+ }
259
+ /**
260
+ * Estratégia de conexão com o banco de dados.
261
+ *
262
+ * - `single`: Usa DATABASE_URL único, zero overhead de multi-tenancy
263
+ * - `dynamic`: Usa tenantSelector para resolver URL e tenantId por request
264
+ */
265
+ type ConnectionStrategy = 'single' | 'dynamic';
266
+ /**
267
+ * Resultado do tenantSelector.
268
+ *
269
+ * @example Row-level isolation (mesmo banco)
270
+ * ```typescript
271
+ * { tenantId: 'abc-123' }
272
+ * ```
273
+ *
274
+ * @example Database sharding (bancos diferentes)
275
+ * ```typescript
276
+ * { databaseUrl: process.env.DB_TENANT_A }
277
+ * ```
278
+ *
279
+ * @example Combinado
280
+ * ```typescript
281
+ * { tenantId: 'abc-123', databaseUrl: process.env.DB_TENANT_A }
282
+ * ```
283
+ */
284
+ interface TenantSelection {
285
+ /** ID do tenant para filtro automático via $extends */
286
+ tenantId?: string;
287
+ /** URL do banco de dados (se diferente de DATABASE_URL) */
288
+ databaseUrl?: string;
289
+ }
290
+ /**
291
+ * Escopo de segurança definido pelo middleware.
292
+ *
293
+ * Quando definido via `context.set('securityScope', scope)`, o framework
294
+ * automaticamente aplica validações em todas as operações do Prisma:
295
+ *
296
+ * - **Leitura**: Adiciona campos ao WHERE (filtro automático)
297
+ * - **Criação**: VALIDA que os campos passados correspondem ao escopo
298
+ * - **Atualização/Deleção**: Adiciona campos ao WHERE (proteção)
299
+ *
300
+ * @example No middleware
301
+ * ```typescript
302
+ * context.set('securityScope', {
303
+ * tenantId: tenant.id,
304
+ * productId: product.id,
305
+ * });
306
+ * ```
307
+ *
308
+ * @example No controller (proteção automática)
309
+ * ```typescript
310
+ * // Isso vai FALHAR se tenantId for diferente do escopo:
311
+ * await db.role.create({ data: { name: 'Admin', tenantId: 'outro-tenant' } });
312
+ * // Error: [Zeti Security] Campo 'tenantId' inválido...
313
+ *
314
+ * // Isso funciona:
315
+ * await db.role.create({ data: { name: 'Admin', tenantId: scopeTenantId } });
316
+ * ```
317
+ */
318
+ interface SecurityScope {
319
+ /** ID do tenant - obrigatório em creates, filtrado em reads */
320
+ tenantId?: string;
321
+ /** ID do produto - obrigatório em creates, filtrado em reads */
322
+ productId?: string;
323
+ /** Campos adicionais customizados */
324
+ [key: string]: string | undefined;
325
+ }
326
+ /**
327
+ * Configuração de conexão do Prisma com suporte a multi-tenancy.
328
+ *
329
+ * @example Single database (app simples)
330
+ * ```typescript
331
+ * prisma: {
332
+ * client: PrismaClient,
333
+ * connectionStrategy: 'single',
334
+ * }
335
+ * ```
336
+ *
337
+ * @example Multi-tenant com row-level isolation
338
+ * ```typescript
339
+ * prisma: {
340
+ * client: PrismaClient,
341
+ * connectionStrategy: 'dynamic',
342
+ * tenantSelector: (context) => ({
343
+ * tenantId: context.req.header('x-tenant-id'),
344
+ * }),
345
+ * }
346
+ * ```
347
+ *
348
+ * @example Multi-database (sharding)
349
+ * ```typescript
350
+ * prisma: {
351
+ * client: PrismaClient,
352
+ * connectionStrategy: 'dynamic',
353
+ * tenantSelector: (context) => {
354
+ * const product = context.req.header('x-product-name');
355
+ * return {
356
+ * tenantId: context.req.header('x-tenant-id'),
357
+ * databaseUrl: process.env[`DB_${product?.toUpperCase()}`],
358
+ * };
359
+ * },
360
+ * }
361
+ * ```
362
+ */
363
+ interface ZetiPrismaConnectionConfig<TPrisma = any> {
364
+ /** Classe do PrismaClient do projeto */
365
+ client: new (...args: any[]) => TPrisma;
366
+ /**
367
+ * Estratégia de conexão.
368
+ * - `single`: Usa DATABASE_URL, sem multi-tenancy
369
+ * - `dynamic`: Usa tenantSelector para resolver conexão
370
+ * @default 'single'
371
+ */
372
+ connectionStrategy?: ConnectionStrategy;
373
+ /**
374
+ * Função que resolve tenantId e/ou databaseUrl por request.
375
+ * Obrigatório se connectionStrategy = 'dynamic'.
376
+ */
377
+ tenantSelector?: (context: Context) => TenantSelection | Promise<TenantSelection>;
378
+ /**
379
+ * Extension customizada aplicada após o filtro de tenant.
380
+ * Permite encadear $extends adicionais.
381
+ */
382
+ customExtension?: (baseClient: TPrisma, selection: TenantSelection) => TPrisma;
383
+ }
384
+ interface ZetiCorsConfig {
385
+ enabled?: boolean;
386
+ origin?: string | string[] | ((origin: string) => boolean);
387
+ allowMethods?: string[];
388
+ allowHeaders?: string[];
389
+ credentials?: boolean;
390
+ maxAge?: number;
391
+ }
392
+ interface ZetiSwaggerHeaderParam {
393
+ name: string;
394
+ description?: string;
395
+ required?: boolean;
396
+ example?: string;
397
+ }
398
+ interface ZetiSwaggerConfig {
399
+ enabled?: boolean;
400
+ title: string;
401
+ version?: string;
402
+ description?: string;
403
+ servers?: Array<{
404
+ url: string;
405
+ description?: string;
406
+ }>;
407
+ outputPath?: string;
408
+ generateOnBuild?: boolean;
409
+ uiPath?: string;
410
+ docPath?: string;
411
+ globalHeaders?: ZetiSwaggerHeaderParam[];
412
+ }
413
+ /**
414
+ * Configuração de headers customizados.
415
+ *
416
+ * Cada header declarado aqui:
417
+ * - Será acessível via `c.req.header(name)`
418
+ * - Aparecerá automaticamente como parâmetro opcional no Swagger
419
+ *
420
+ * @example
421
+ * ```typescript
422
+ * headers: {
423
+ * tenant: "x-tenant", // Header para multi-tenancy
424
+ * timezone: "timezone", // Header de timezone
425
+ * apiKey: "x-internal-api-key", // Header customizado
426
+ * }
427
+ * ```
428
+ */
429
+ interface ZetiHeadersConfig {
430
+ /** Header de tenant para multi-tenancy. Default: "x-tenant" */
431
+ tenant?: string;
432
+ /** Header de timezone. Default: "timezone" */
433
+ timezone?: string;
434
+ /** Permite qualquer header customizado adicional */
435
+ [key: string]: string | undefined;
436
+ }
437
+ interface ZetiTenantConfig {
438
+ enabled?: boolean;
439
+ required?: boolean;
440
+ headerName?: string;
441
+ defaultDatabase?: string;
442
+ }
443
+ interface ZetiMiddlewareConfig {
444
+ global?: Middleware[];
445
+ named?: Record<string, Middleware | ((...args: any[]) => Middleware)>;
446
+ }
447
+ /** Configuração de desenvolvimento do projeto */
448
+ interface ZetiDevConfig {
449
+ /** Porta do servidor. Default: 3333 */
450
+ port?: number;
451
+ /** Caminho do docker-compose. Default: "../infra/local/docker-compose.yaml" */
452
+ dockerCompose?: string;
453
+ /** Portas para limpar antes de iniciar. Default: [3333, 5432] */
454
+ ports?: number[];
455
+ /** Configuração do framework local (para desenvolvimento) */
456
+ framework?: {
457
+ /** Caminho do framework. Default: "../_developer/_npm" */
458
+ path?: string;
459
+ /** Observar mudanças no framework. Default: true */
460
+ watch?: boolean;
461
+ };
462
+ }
463
+
464
+ /** Configuração interna completa do Zeti (após normalização) */
465
+ interface ZetiConfigInternal<TPrisma = any> {
466
+ databases: ZetiDatabaseConfig;
467
+ specialDatabases?: Record<string, string>;
468
+ prisma: ZetiPrismaConfig;
469
+ /** Nova configuração de conexão Prisma com modos de operação */
470
+ prismaConnection?: ZetiPrismaConnectionConfig<TPrisma>;
471
+ swagger: Required<Omit<ZetiSwaggerConfig, 'globalHeaders' | 'description'>> & {
472
+ globalHeaders?: ZetiSwaggerHeaderParam[];
473
+ description?: string;
474
+ };
475
+ tenant?: ZetiTenantConfig;
476
+ cors: Required<Omit<ZetiCorsConfig, 'maxAge'>> & {
477
+ maxAge?: number;
478
+ };
479
+ logger: boolean;
480
+ debug: boolean;
481
+ headers?: ZetiHeadersConfig;
482
+ middlewares: ZetiMiddlewareConfig;
483
+ errorHandler: ZetiErrorHandlerConfig | boolean;
484
+ hooks?: {
485
+ onRouteRegister?: (route: any) => void;
486
+ onRequest?: (context: any) => void | Promise<void>;
487
+ onResponse?: (context: any, response: any) => any;
488
+ };
489
+ dev: Required<ZetiDevConfig>;
490
+ workers?: WorkersConfig;
491
+ }
492
+ /**
493
+ * Configuração do Zeti Framework.
494
+ *
495
+ * A maioria das opções tem defaults sensatos - você só precisa configurar o que for diferente.
496
+ *
497
+ * @example Configuração mínima
498
+ * ```typescript
499
+ * defineZetiConfig({
500
+ * swagger: { title: "My API" },
501
+ * });
502
+ * ```
503
+ */
504
+ interface ZetiConfig {
505
+ /**
506
+ * URL de conexão do banco de dados (para apps single-tenant).
507
+ * Se não informado, usa DATABASE_URL do .env automaticamente.
508
+ */
509
+ databaseUrl?: string;
510
+ /**
511
+ * Configuração de databases (para apps multi-tenant).
512
+ * Se não informado, o framework usa databaseUrl ou DATABASE_URL do .env.
513
+ */
514
+ databases?: ZetiDatabaseConfig;
515
+ specialDatabases?: Record<string, string>;
516
+ /** Configuração do Prisma. Opcional - usa defaults sensatos. */
517
+ prisma?: ZetiPrismaConfig;
518
+ /**
519
+ * Configuração do Swagger. Apenas `title` é obrigatório.
520
+ * Defaults: enabled=true, version="1.0.0", uiPath="/swagger", docPath="/swagger/doc"
521
+ */
522
+ swagger: {
523
+ /** Título da API (obrigatório) */
524
+ title: string;
525
+ /** Versão da API. Default: "1.0.0" */
526
+ version?: string;
527
+ /** Descrição da API */
528
+ description?: string;
529
+ /** Servidores. Default: [{ url: "http://localhost:{port}" }] */
530
+ servers?: Array<{
531
+ url: string;
532
+ description?: string;
533
+ }>;
534
+ /** Habilitar Swagger UI. Default: true */
535
+ enabled?: boolean;
536
+ /** Caminho do arquivo de schemas. Default: "./.zeti/generated/swagger-schemas.ts" */
537
+ outputPath?: string;
538
+ /** Gerar schemas no build. Default: true */
539
+ generateOnBuild?: boolean;
540
+ /** Caminho da UI. Default: "/swagger" */
541
+ uiPath?: string;
542
+ /** Caminho do JSON. Default: "/swagger/doc" */
543
+ docPath?: string;
544
+ /** Headers globais */
545
+ globalHeaders?: ZetiSwaggerHeaderParam[];
546
+ };
547
+ /** Configuração de multi-tenancy. Se não informado, tenant fica desabilitado. */
548
+ tenant?: ZetiTenantConfig;
549
+ /**
550
+ * Configuração de CORS. Opcional - usa defaults permissivos para desenvolvimento.
551
+ * Defaults: enabled=true, origin="*", credentials=true, allowHeaders=["*"]
552
+ */
553
+ cors?: ZetiCorsConfig;
554
+ /** Habilitar logger de requisições. Default: true */
555
+ logger?: boolean;
556
+ /** Habilitar logs de debug. Default: false */
557
+ debug?: boolean;
558
+ /** Configuração de headers customizados */
559
+ headers?: ZetiHeadersConfig;
560
+ /** Configuração de middlewares */
561
+ middlewares?: ZetiMiddlewareConfig;
562
+ /** Configuração do error handler. Default: {} (habilitado com defaults) */
563
+ errorHandler?: ZetiErrorHandlerConfig | boolean;
564
+ /** Hooks do ciclo de vida */
565
+ hooks?: {
566
+ onRouteRegister?: (route: any) => void;
567
+ onRequest?: (context: any) => void | Promise<void>;
568
+ onResponse?: (context: any, response: any) => any;
569
+ };
570
+ /**
571
+ * Configuração de desenvolvimento (usado pelo CLI `zeti dev`).
572
+ * Opcional - usa defaults sensatos.
573
+ */
574
+ dev?: ZetiDevConfig;
575
+ /**
576
+ * Workers que serão iniciados automaticamente com o servidor.
577
+ *
578
+ * @example
579
+ * ```typescript
580
+ * workers: {
581
+ * email: {
582
+ * fn: async (db) => {
583
+ * const emails = await db.email.findMany({ where: { status: 'PENDING' } });
584
+ * // processar...
585
+ * },
586
+ * intervalSeconds: 30,
587
+ * },
588
+ * sync: {
589
+ * fn: async (db, tenantId) => {
590
+ * // sincroniza dados do tenant
591
+ * },
592
+ * intervalSeconds: 60,
593
+ * multiTenant: true,
594
+ * },
595
+ * }
596
+ * ```
597
+ */
598
+ workers?: WorkersConfig;
599
+ /**
600
+ * Configuração do Redis.
601
+ *
602
+ * Se fornecido, o cliente Redis será inicializado automaticamente
603
+ * e disponibilizado via `import { redis } from '@zeti/app'`.
604
+ *
605
+ * @example
606
+ * ```typescript
607
+ * redis: {
608
+ * host: process.env.REDIS_HOST ?? 'localhost',
609
+ * port: parseInt(process.env.REDIS_PORT ?? '6379'),
610
+ * keyPrefix: 'myapp:',
611
+ * }
612
+ * ```
613
+ */
614
+ redis?: RedisConfig;
615
+ }
616
+
617
+ /**
618
+ * Define a configuração do Zeti Framework.
619
+ *
620
+ * Detecta automaticamente se é single-tenant ou multi-tenant e aplica defaults sensatos.
621
+ *
622
+ * @example Configuração mínima (single-tenant)
623
+ * ```typescript
624
+ * export const zetiConfig = defineZetiConfig({
625
+ * swagger: { title: "My API" },
626
+ * });
627
+ * ```
628
+ *
629
+ * @example Multi-tenant
630
+ * ```typescript
631
+ * export const zetiConfig = defineZetiConfig({
632
+ * databases: {
633
+ * connections: {
634
+ * mulherdevalor: env("TENANT_MULHERDEVALOR_DATABASE_URL"),
635
+ * outroTenant: env("TENANT_OUTRO_DATABASE_URL"),
636
+ * },
637
+ * },
638
+ * tenant: { enabled: true, required: true },
639
+ * swagger: { title: "Multi-tenant API" },
640
+ * });
641
+ * ```
642
+ */
643
+ declare function defineZetiConfig(config: ZetiConfig, options?: {
644
+ port?: number;
645
+ }): ZetiConfigInternal;
646
+
647
+ export { type ConnectionStrategy as C, type ErrorResponse as E, type Middleware as M, Prisma as P, type RedisConfig as R, type SecurityScope as S, type TenantSelection as T, type ValidationError as V, type WorkerConfig as W, type ZetiSwaggerConfig as Z, type ZetiConfigInternal as a, type ZetiPrismaConnectionConfig as b, type ErrorTrace as c, type PrismaClientBase as d, type WorkersConfig as e, type ZetiConfig as f, type ZetiCorsConfig as g, type ZetiDatabaseConfig as h, type ZetiDevConfig as i, type ZetiErrorHandlerConfig as j, type ZetiHeadersConfig as k, type ZetiMiddlewareConfig as l, type ZetiPrismaConfig as m, type ZetiSwaggerHeaderParam as n, type ZetiTenantConfig as o, closeRedis as p, createErrorHandler as q, defineZetiConfig as r, getRedisClient as s, getRedisConfig as t, handleError as u, initRedis as v };