pg-query-sdk 1.0.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.
Files changed (62) hide show
  1. package/README.md +553 -0
  2. package/dist/cjs/builders/ConditionBuilder.d.ts +11 -0
  3. package/dist/cjs/builders/ConditionBuilder.js +43 -0
  4. package/dist/cjs/builders/QueryBuilder.d.ts +31 -0
  5. package/dist/cjs/builders/QueryBuilder.js +88 -0
  6. package/dist/cjs/core/Database.d.ts +20 -0
  7. package/dist/cjs/core/Database.js +39 -0
  8. package/dist/cjs/core/ParamContext.d.ts +8 -0
  9. package/dist/cjs/core/ParamContext.js +16 -0
  10. package/dist/cjs/core/QueryExecutor.d.ts +14 -0
  11. package/dist/cjs/core/QueryExecutor.js +39 -0
  12. package/dist/cjs/core/TransactionManager.d.ts +6 -0
  13. package/dist/cjs/core/TransactionManager.js +24 -0
  14. package/dist/cjs/core/UnitOfWork.d.ts +10 -0
  15. package/dist/cjs/core/UnitOfWork.js +27 -0
  16. package/dist/cjs/dialects/Dialect.d.ts +4 -0
  17. package/dist/cjs/dialects/Dialect.js +2 -0
  18. package/dist/cjs/dialects/MysqlDialect.d.ts +5 -0
  19. package/dist/cjs/dialects/MysqlDialect.js +11 -0
  20. package/dist/cjs/dialects/PostgresDialect.d.ts +5 -0
  21. package/dist/cjs/dialects/PostgresDialect.js +11 -0
  22. package/dist/cjs/index.d.ts +7 -0
  23. package/dist/cjs/index.js +20 -0
  24. package/dist/cjs/orm/EntityManager.d.ts +0 -0
  25. package/dist/cjs/orm/EntityManager.js +1 -0
  26. package/dist/cjs/orm/Repository.d.ts +13 -0
  27. package/dist/cjs/orm/Repository.js +30 -0
  28. package/dist/cjs/query/ConditionBuilder.d.ts +11 -0
  29. package/dist/cjs/query/ConditionBuilder.js +67 -0
  30. package/dist/cjs/query/QueryBuilder.d.ts +43 -0
  31. package/dist/cjs/query/QueryBuilder.js +152 -0
  32. package/dist/esm/builders/ConditionBuilder.d.ts +11 -0
  33. package/dist/esm/builders/ConditionBuilder.js +40 -0
  34. package/dist/esm/builders/QueryBuilder.d.ts +31 -0
  35. package/dist/esm/builders/QueryBuilder.js +82 -0
  36. package/dist/esm/core/Database.d.ts +20 -0
  37. package/dist/esm/core/Database.js +33 -0
  38. package/dist/esm/core/ParamContext.d.ts +8 -0
  39. package/dist/esm/core/ParamContext.js +13 -0
  40. package/dist/esm/core/QueryExecutor.d.ts +14 -0
  41. package/dist/esm/core/QueryExecutor.js +36 -0
  42. package/dist/esm/core/TransactionManager.d.ts +6 -0
  43. package/dist/esm/core/TransactionManager.js +21 -0
  44. package/dist/esm/core/UnitOfWork.d.ts +10 -0
  45. package/dist/esm/core/UnitOfWork.js +24 -0
  46. package/dist/esm/dialects/Dialect.d.ts +4 -0
  47. package/dist/esm/dialects/Dialect.js +1 -0
  48. package/dist/esm/dialects/MysqlDialect.d.ts +5 -0
  49. package/dist/esm/dialects/MysqlDialect.js +8 -0
  50. package/dist/esm/dialects/PostgresDialect.d.ts +5 -0
  51. package/dist/esm/dialects/PostgresDialect.js +8 -0
  52. package/dist/esm/index.d.ts +7 -0
  53. package/dist/esm/index.js +7 -0
  54. package/dist/esm/orm/EntityManager.d.ts +0 -0
  55. package/dist/esm/orm/EntityManager.js +1 -0
  56. package/dist/esm/orm/Repository.d.ts +13 -0
  57. package/dist/esm/orm/Repository.js +27 -0
  58. package/dist/esm/query/ConditionBuilder.d.ts +11 -0
  59. package/dist/esm/query/ConditionBuilder.js +64 -0
  60. package/dist/esm/query/QueryBuilder.d.ts +43 -0
  61. package/dist/esm/query/QueryBuilder.js +146 -0
  62. package/package.json +45 -0
package/README.md ADDED
@@ -0,0 +1,553 @@
1
+ # PG QUERY SDK (TypeScript)
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue.svg)](https://www.typescriptlang.org/)
5
+ [![Architecture: Hexagonal](https://img.shields.io/badge/Architecture-Hexagonal-green.svg)](#-arquitetura)
6
+
7
+ Uma biblioteca robusta e tipada para integração com banco de dados **PostgresSQL**. Construída sob os princípios da **Arquitetura Hexagonal**, garantindo que sua integração seja escalável, testável e fácil de manter.
8
+
9
+ **PostgreSQL** SDK com suporte a:
10
+
11
+ - **`Database`**: Ponto de entrada central para todas as operações.
12
+ - **`QueryBuilder`**: Construtor de queries SQL fluente para **SELECT**.
13
+ - **`ConditionBuilder`**: Construtor de cláusulas `WHERE` e `HAVING` complexas.
14
+ - **`QueryExecutor`**: Executor de queries baseado em Pool de conexões.
15
+ - **ORM Básico**: Com `Repository` para abstração de acesso a dados (atualmente apenas `findById` implementado na base).
16
+ - **Transações**: Gerenciamento de transações ACID.
17
+ - Compatível com CommonJS e ESM.
18
+ - Dual build (CJS + ESM).
19
+
20
+ ---
21
+
22
+ # 📦 Instalação
23
+
24
+ ```bash
25
+ npm install pg-query-sdk
26
+ ```
27
+
28
+ Ou localmente:
29
+
30
+ ```bash
31
+ npm install .
32
+ ```
33
+
34
+ ---
35
+
36
+ # 🚀 Primeiros Passos
37
+
38
+ O ponto de entrada principal para interagir com o SDK é a classe `Database`. Ela gerencia a conexão, o dialeto SQL e fornece acesso a todas as funcionalidades.
39
+
40
+ ## Inicializando o Database
41
+
42
+ ```typescript
43
+ import Database from 'pg-query-sdk';
44
+
45
+ const db = new Database({
46
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
47
+ // Opcional: Você pode especificar um dialeto diferente se necessário
48
+ // dialect: new MyCustomDialect(),
49
+ // Opcional: TTL padrão para cache de queries (em segundos). Use 0 para desativar.
50
+ // defaultCacheTTL: 300
51
+ });
52
+
53
+ // Agora 'db' está pronto para ser usado!
54
+ ```
55
+
56
+ ---
57
+
58
+ # 🛠 Funcionalidades Principais
59
+
60
+ ## 1️⃣ QueryBuilder: Construindo Queries SQL (SELECT)
61
+
62
+ O `QueryBuilder` permite construir queries SQL de forma programática e segura, **focando em operações de seleção de dados**. Ele é acessado através do método `table()` da sua instância `Database`.
63
+
64
+ Ele **não executa nada**, apenas retorna a string SQL e os parâmetros. Para executar a query, você deve encadear `.execute()` no final.
65
+
66
+ ### Selecionando Dados
67
+
68
+ ```typescript
69
+ import Database from 'pg-query-sdk';
70
+
71
+ const db = new Database({
72
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
73
+ });
74
+
75
+ async function selectExample() {
76
+ // Construindo a query
77
+ const { query, params } = db.table('users')
78
+ .select(['id', 'name', 'email'])
79
+ .where({ active: true })
80
+ .limit(10)
81
+ .offset(0)
82
+ .orderBy('name', 'ASC')
83
+ .build(); // Apenas constrói a query, não executa
84
+
85
+ console.log('SELECT Query:', query);
86
+ // Ex: SELECT id, name, email FROM users WHERE active = $1 ORDER BY name ASC LIMIT 10 OFFSET 0
87
+ console.log('SELECT Params:', params);
88
+ // Ex: [true]
89
+
90
+ // Executando a query
91
+ const users = await db.table('users')
92
+ .select(['id', 'name', 'email'])
93
+ .where({ active: true })
94
+ .limit(10)
95
+ .offset(0)
96
+ .orderBy('name', 'ASC')
97
+ .execute(); // Executa a query e retorna os resultados
98
+
99
+ console.log('Selected Users:', users);
100
+ }
101
+
102
+ selectExample();
103
+ ```
104
+
105
+ ### Joins
106
+
107
+ ```typescript
108
+ import Database from 'pg-query-sdk';
109
+
110
+ const db = new Database({
111
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
112
+ });
113
+
114
+ async function joinExample() {
115
+ const usersWithOrders = await db.table('users')
116
+ .select(['users.name', 'orders.amount', 'orders.status'])
117
+ .join('orders', 'users.id', 'orders.user_id') // INNER JOIN
118
+ .where({ 'orders.status': 'completed' })
119
+ .execute();
120
+
121
+ console.log('Users with completed orders:', usersWithOrders);
122
+
123
+ const usersAndTheirOrders = await db.table('users')
124
+ .select(['users.name', 'orders.amount', 'orders.status'])
125
+ .leftJoin('orders', 'users.id', 'orders.user_id') // LEFT JOIN
126
+ .orderBy('users.name', 'ASC')
127
+ .execute();
128
+
129
+ console.log('Users and all their orders (if any):', usersAndTheirOrders);
130
+ }
131
+
132
+ joinExample();
133
+ ```
134
+
135
+ ### Group By e Having
136
+
137
+ ```typescript
138
+ import Database from 'pg-query-sdk';
139
+
140
+ const db = new Database({
141
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
142
+ });
143
+
144
+ async function groupByHavingExample() {
145
+ const categorySales = await db.table('products')
146
+ .select(['category', 'COUNT(id) as total_products', 'SUM(price) as total_value'])
147
+ .groupBy('category')
148
+ .having({ 'SUM(price)': { op: '>', value: 1000 } }) // HAVING SUM(price) > 1000
149
+ .orderBy('total_value', 'DESC')
150
+ .execute();
151
+
152
+ console.log('Category sales over 1000:', categorySales);
153
+ }
154
+
155
+ groupByHavingExample();
156
+ ```
157
+
158
+ ### Common Table Expressions (CTEs)
159
+
160
+ ```typescript
161
+ import Database from 'pg-query-sdk';
162
+
163
+ const db = new Database({
164
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
165
+ });
166
+
167
+ async function cteExample() {
168
+ // Subquery para usuários ativos
169
+ const activeUsersSubquery = db.table('users')
170
+ .select(['id', 'name'])
171
+ .where({ active: true });
172
+
173
+ // Query principal usando a CTE
174
+ const result = await db.table('active_users') // Referencia a CTE pelo nome
175
+ .with('active_users', activeUsersSubquery) // Define a CTE
176
+ .select(['name'])
177
+ .orderBy('name', 'ASC')
178
+ .execute();
179
+
180
+ console.log('Users from CTE:', result);
181
+ }
182
+
183
+ cteExample();
184
+ ```
185
+
186
+ ### Subqueries na Cláusula FROM
187
+
188
+ ```typescript
189
+ import Database from 'pg-query-sdk';
190
+
191
+ const db = new Database({
192
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
193
+ });
194
+
195
+ async function fromSubqueryExample() {
196
+ // Subquery para obter o total de pedidos por usuário
197
+ const userOrderCounts = db.table('orders')
198
+ .select(['user_id', 'COUNT(id) as order_count'])
199
+ .groupBy('user_id');
200
+
201
+ // Query principal usando a subquery como tabela
202
+ const usersWithOrderCounts = await db.table('users')
203
+ .select(['users.name', 'uoc.order_count'])
204
+ .fromSubquery(userOrderCounts, 'uoc') // Usa a subquery 'userOrderCounts' como 'uoc'
205
+ .join('users', 'users.id', 'uoc.user_id') // JOIN com a tabela original de usuários
206
+ .where({ 'uoc.order_count': { op: '>', value: 5 } })
207
+ .execute();
208
+
209
+ console.log('Users with more than 5 orders:', usersWithOrderCounts);
210
+ }
211
+
212
+ fromSubqueryExample();
213
+ ```
214
+
215
+ ## 2️⃣ ConditionBuilder: Cláusulas WHERE e HAVING Avançadas
216
+
217
+ O `ConditionBuilder` é usado dentro dos métodos `where()` e `having()` do `QueryBuilder` para construir condições complexas, incluindo operadores, `NULL` checks, expressões raw e agrupamentos `AND`/`OR`.
218
+
219
+ ```typescript
220
+ import Database from 'pg-query-sdk';
221
+
222
+ const db = new Database({
223
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
224
+ });
225
+
226
+ async function complexWhereExample() {
227
+ const products = await db.table('products')
228
+ .select(['name', 'price', 'stock', 'category'])
229
+ .where(conditions => {
230
+ conditions
231
+ .where({ category: 'electronics' }) // category = $1
232
+ .andGroup(group1 => { // AND (...)
233
+ group1
234
+ .where({ stock: { op: '>', value: 0 } }) // stock > $2
235
+ .orGroup(group2 => { // OR (...)
236
+ group2
237
+ .where({ price: { op: '<', value: 100 } }) // price < $3
238
+ .raw('created_at > NOW() - INTERVAL \'1 year\''); // created_at > ...
239
+ });
240
+ })
241
+ .where({ manufacturer: null }); // manufacturer IS NULL
242
+ })
243
+ .execute();
244
+
245
+ console.log('Complex WHERE Products:', products);
246
+ // A query gerada seria algo como:
247
+ // SELECT name, price, stock, category FROM products
248
+ // WHERE category = $1 AND (stock > $2 OR (price < $3 AND created_at > NOW() - INTERVAL '1 year')) AND manufacturer IS NULL
249
+ }
250
+
251
+ complexWhereExample();
252
+ ```
253
+
254
+ ## 3️⃣ QueryExecutor: Execução Direta de Queries
255
+
256
+ O `QueryExecutor` é a camada responsável por interagir diretamente com o driver `pg` para executar queries. Embora o `QueryBuilder` seja preferível para a maioria dos casos, você pode acessar o `QueryExecutor` diretamente através da instância `Database` para queries SQL customizadas, procedimentos armazenados ou comandos DDL.
257
+
258
+ ```typescript
259
+ import Database from 'pg-query-sdk';
260
+
261
+ const db = new Database({
262
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
263
+ });
264
+
265
+ async function directExecuteExample() {
266
+ // Executando uma query simples
267
+ const result = await db.executor.execute(
268
+ 'SELECT version(), NOW() as current_time',
269
+ []
270
+ );
271
+ console.log('Direct Execution Result:', result.rows);
272
+
273
+ // Executando uma query com parâmetros
274
+ const specificUser = await db.executor.execute(
275
+ 'SELECT * FROM users WHERE id = $1',
276
+ [1]
277
+ );
278
+ console.log('Specific User (Direct):', specificUser.rows[0]);
279
+
280
+ // Exemplo de DDL (Data Definition Language) - CUIDADO ao usar em produção!
281
+ // await db.executor.execute('CREATE TABLE IF NOT EXISTS temp_table (id SERIAL PRIMARY KEY, name VARCHAR(255))', []);
282
+ // console.log('temp_table created (if not exists).');
283
+ }
284
+
285
+ directExecuteExample();
286
+ ```
287
+
288
+ ## 4️⃣ Transações ACID
289
+
290
+ O SDK oferece um gerenciador de transações robusto para garantir a atomicidade (ACID) das suas operações de banco de dados. Se qualquer operação dentro da transação falhar, todas as alterações serão revertidas (rollback).
291
+
292
+ ```typescript
293
+ import Database from 'pg-query-sdk';
294
+
295
+ const db = new Database({
296
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
297
+ });
298
+
299
+ async function transactionExample() {
300
+ try {
301
+ const result = await db.transaction(async trxDb => {
302
+ // Dentro desta callback, 'trxDb' é uma instância de Database
303
+ // que está vinculada à transação atual.
304
+ // Todas as operações feitas com 'trxDb' farão parte da mesma transação.
305
+
306
+ // Exemplo: Transferir fundos entre contas
307
+ const senderId = 1;
308
+ const receiverId = 2;
309
+ const amount = 100.00;
310
+
311
+ // 1. Decrementar saldo do remetente
312
+ // Nota: Para INSERT/UPDATE/DELETE, você precisará usar db.executor.execute() diretamente
313
+ // ou implementar esses métodos em um repositório customizado.
314
+ await trxDb.executor.execute(
315
+ 'UPDATE accounts SET balance = balance - $1 WHERE id = $2 AND balance >= $1 RETURNING id, balance',
316
+ [amount, senderId]
317
+ );
318
+ console.log(`Decremented balance for account ${senderId}`);
319
+
320
+ // Simular uma falha para testar o rollback
321
+ // if (true) throw new Error('Simulated failure');
322
+
323
+ // 2. Incrementar saldo do destinatário
324
+ await trxDb.executor.execute(
325
+ 'UPDATE accounts SET balance = balance + $1 WHERE id = $2 RETURNING id, balance',
326
+ [amount, receiverId]
327
+ );
328
+ console.log(`Incremented balance for account ${receiverId}`);
329
+
330
+ // Se tudo ocorrer bem, a transação será commitada automaticamente.
331
+ return `Transaction successful: ${amount} transferred from ${senderId} to ${receiverId}`;
332
+ });
333
+
334
+ console.log(result);
335
+ } catch (error) {
336
+ console.error('Transaction failed:', error.message);
337
+ // Se uma exceção for lançada, a transação será automaticamente revertida (rollback).
338
+ }
339
+ }
340
+
341
+ transactionExample();
342
+ ```
343
+
344
+ ## 5️⃣ ORM Básico com Repositórios
345
+
346
+ O SDK fornece uma base para construir um ORM simples usando a classe `Repository`. Isso ajuda a organizar o código de acesso a dados por entidade.
347
+
348
+ ### Definindo um Repositório Customizado
349
+
350
+ A classe base `Repository<T>` oferece um método `findById` e um `qb()` que retorna um `QueryBuilder` pré-configurado para a tabela. Para operações de `INSERT`, `UPDATE` e `DELETE`, você precisará implementá-las em seus repositórios customizados, utilizando o `QueryExecutor` ou o `QueryBuilder` (para `SELECT` após a operação).
351
+
352
+ ```typescript
353
+ import { Repository, QueryExecutor, Dialect } from 'pg-query-sdk';
354
+
355
+ // 1. Defina a interface para sua entidade
356
+ interface User {
357
+ id: number;
358
+ name: string;
359
+ email: string;
360
+ age?: number;
361
+ active: boolean;
362
+ }
363
+
364
+ // 2. Crie sua classe de repositório estendendo Repository<T>
365
+ class UserRepository extends Repository<User> {
366
+ constructor(executor: QueryExecutor, dialect: Dialect) {
367
+ // O construtor base requer o nome da tabela, o executor e o dialeto
368
+ super('users', executor, dialect);
369
+ }
370
+
371
+ // Método implementado na classe base
372
+ // async findById(id: number): Promise<User | null> { ... }
373
+
374
+ // Exemplo de método customizado para o repositório de usuários
375
+ async findActiveUsers(): Promise<User[]> {
376
+ return this.qb() // 'this.qb()' retorna um QueryBuilder pré-configurado para a tabela 'users'
377
+ .where({ active: true })
378
+ .execute();
379
+ }
380
+
381
+ async findUsersByAgeRange(minAge: number, maxAge: number): Promise<User[]> {
382
+ return this.qb()
383
+ .where(conditions => {
384
+ conditions
385
+ .where({ age: { op: '>=', value: minAge } })
386
+ .where({ age: { op: '<=', value: maxAge } });
387
+ })
388
+ .execute();
389
+ }
390
+
391
+ // Exemplo de implementação de INSERT em um repositório customizado
392
+ async createUser(data: Omit<User, 'id'>): Promise<User> {
393
+ const { query, params } = this.dialect.createInsertQuery(this.table, data as Record<string, any>, ['id', 'name', 'email', 'age', 'active']);
394
+ const result = await this.executor.execute(query, params);
395
+ return result.rows[0];
396
+ }
397
+
398
+ // Exemplo de implementação de UPDATE em um repositório customizado
399
+ async updateUser(id: number, data: Partial<User>): Promise<User | null> {
400
+ const { query, params } = this.dialect.createUpdateQuery(this.table, data as Record<string, any>, { id }, ['id', 'name', 'email', 'age', 'active']);
401
+ const result = await this.executor.execute(query, params);
402
+ return result.rows[0] || null;
403
+ }
404
+
405
+ // Exemplo de implementação de DELETE em um repositório customizado
406
+ async deleteUser(id: number): Promise<boolean> {
407
+ const { query, params } = this.dialect.createDeleteQuery(this.table, { id });
408
+ const result = await this.executor.execute(query, params);
409
+ return result.rowCount > 0;
410
+ }
411
+ }
412
+ ```
413
+
414
+ ### Usando o Repositório Customizado
415
+
416
+ ```typescript
417
+ import Database from 'pg-query-sdk';
418
+ // Importe seu UserRepository definido acima
419
+ import { UserRepository } from './path/to/UserRepository'; // Ajuste o caminho conforme necessário
420
+
421
+ const db = new Database({
422
+ connectionString: 'postgres://user:pass@localhost:5432/your_database',
423
+ });
424
+
425
+ async function repositoryExample() {
426
+ // Obtenha uma instância do seu repositório através do método .repository() do Database
427
+ const userRepository = db.repository(UserRepository);
428
+
429
+ // Usando métodos do repositório base
430
+ const userById = await userRepository.findById(1);
431
+ console.log('User by ID:', userById);
432
+
433
+ // Usando métodos customizados
434
+ const activeUsers = await userRepository.findActiveUsers();
435
+ console.log('Active Users:', activeUsers);
436
+
437
+ const usersInAgeRange = await userRepository.findUsersByAgeRange(25, 35);
438
+ console.log('Users in age range 25-35:', usersInAgeRange);
439
+
440
+ // Criando um novo usuário
441
+ const newUser = await userRepository.createUser({
442
+ name: 'Charlie',
443
+ email: 'charlie@example.com',
444
+ age: 29,
445
+ active: true
446
+ });
447
+ console.log('Created new user:', newUser);
448
+
449
+ // Atualizando um usuário
450
+ const updatedUser = await userRepository.updateUser(newUser.id, { age: 30, active: false });
451
+ console.log('Updated user:', updatedUser);
452
+
453
+ // Deletando um usuário
454
+ const deleted = await userRepository.deleteUser(newUser.id);
455
+ console.log(`User ${newUser.id} deleted: ${deleted}`);
456
+ }
457
+
458
+ repositoryExample();
459
+ ```
460
+
461
+ ### EntityManager (Planejado)
462
+
463
+ O `EntityManager` é um componente planejado para gerenciar múltiplos repositórios e unidades de trabalho, oferecendo uma interface centralizada para operações de persistência mais complexas. Atualmente, esta classe está vazia e será desenvolvida em futuras iterações.
464
+
465
+ ---
466
+
467
+ # ⚙️ Dual Module Support
468
+
469
+ O pacote suporta tanto CommonJS quanto ES Modules, permitindo flexibilidade na sua configuração de projeto.
470
+
471
+ ## CommonJS
472
+
473
+ ```javascript
474
+ const Database = require('pg-query-sdk').default; // Note o .default para a exportação padrão
475
+
476
+ const db = new Database({ /* ... */ });
477
+ ```
478
+
479
+ ## ESM
480
+
481
+ ```typescript
482
+ import Database from 'pg-query-sdk';
483
+
484
+ const db = new Database({ /* ... */ });
485
+ ```
486
+
487
+ Isso funciona graças ao campo `exports` no `package.json`:
488
+
489
+ ```json
490
+ {
491
+ "exports": {
492
+ ".": {
493
+ "require": "./dist/cjs/index.js",
494
+ "import": "./dist/esm/index.js"
495
+ }
496
+ }
497
+ }
498
+ ```
499
+
500
+ ---
501
+
502
+ # 🧱 Estrutura do Projeto
503
+
504
+ ```
505
+ pg-query-sdk/
506
+ src/
507
+ core/
508
+ Database.ts # Ponto de entrada principal
509
+ ParamContext.ts # Gerencia parâmetros para queries seguras
510
+ QueryExecutor.ts # Executa queries no PostgreSQL
511
+ TransactionManager.ts # Gerencia transações
512
+ builders/
513
+ ConditionBuilder.ts # Constrói cláusulas WHERE e HAVING
514
+ QueryBuilder.ts # Constrói queries SQL (apenas SELECT)
515
+ orm/
516
+ EntityManager.ts # (Planejado) Gerenciador de entidades
517
+ Repository.ts # Classe base para repositórios ORM
518
+ dialects/
519
+ Dialect.ts # Interface para dialetos SQL
520
+ PostgresDialect.ts # Implementação do dialeto PostgreSQL
521
+ index.ts # Exportações principais do SDK
522
+ test/ # Testes unitários e de integração
523
+ dist/ # Saída da compilação (CJS e ESM)
524
+ ```
525
+
526
+ ---
527
+
528
+ # 📌 Responsabilidades das Camadas
529
+
530
+ | Camada | Responsabilidade |
531
+ |--------------------|--------------------------------------------------------------------------------------------------------------|
532
+ | `Database` | Ponto de entrada, gerencia conexão, dialeto, transações e acesso a builders/repositórios. |
533
+ | `QueryBuilder` | Construção fluente de queries SQL **apenas para seleção de dados** (SELECT, JOIN, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET, CTEs, Subqueries). |
534
+ | `ConditionBuilder` | Construção de cláusulas `WHERE` e `HAVING` complexas e aninhadas. |
535
+ | `QueryExecutor` | Execução de queries no PostgreSQL e gerenciamento do pool de conexões. |
536
+ | `Repository` | Abstração de acesso a dados para uma entidade específica. A classe base implementa `findById`. Métodos como `insert`, `update`, `delete` devem ser implementados nos repositórios customizados. |
537
+ | `TransactionManager`| Gerenciamento de transações ACID. |
538
+ | `EntityManager` | (Planejado) Gerenciamento de múltiplos repositórios e unidade de trabalho. |
539
+ | `pg` (driver) | Comunicação de baixo nível com o banco de dados PostgreSQL. |
540
+
541
+ ---
542
+
543
+ # 🔐 Segurança
544
+
545
+ - **Parâmetros Preparados**: Todas as queries construídas pelo `QueryBuilder` e `ConditionBuilder` utilizam parâmetros preparados, prevenindo ataques de SQL Injection. O `QueryExecutor` também suporta parâmetros para queries diretas.
546
+ - **Pool de Conexões**: O `QueryExecutor` gerencia um pool de conexões, otimizando o uso de recursos e garantindo que as conexões sejam reutilizadas de forma eficiente.
547
+ - **Liberação de Conexões**: As conexões são sempre liberadas de volta ao pool no bloco `finally` após a execução da query ou transação, evitando vazamentos de conexão.
548
+
549
+ ---
550
+
551
+ ## 📄 Licença
552
+
553
+ Distribuído sob a licença MIT. Veja `LICENSE` para mais informações.
@@ -0,0 +1,11 @@
1
+ import ParamContext from "../core/ParamContext";
2
+ export default class ConditionBuilder {
3
+ private ctx;
4
+ private conditions;
5
+ constructor(ctx: ParamContext);
6
+ where(obj: Record<string, any>): this;
7
+ raw(expression: string): this;
8
+ andGroup(callback: (qb: ConditionBuilder) => void): this;
9
+ orGroup(callback: (qb: ConditionBuilder) => void): this;
10
+ build(prefix?: string): string;
11
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class ConditionBuilder {
4
+ constructor(ctx) {
5
+ this.ctx = ctx;
6
+ this.conditions = [];
7
+ }
8
+ where(obj) {
9
+ Object.entries(obj).forEach(([key, value]) => {
10
+ const placeholder = this.ctx.add(value);
11
+ this.conditions.push(`${key} = ${placeholder}`);
12
+ });
13
+ return this;
14
+ }
15
+ raw(expression) {
16
+ this.conditions.push(expression);
17
+ return this;
18
+ }
19
+ andGroup(callback) {
20
+ const nested = new ConditionBuilder(this.ctx);
21
+ callback(nested);
22
+ const built = nested.build();
23
+ if (built) {
24
+ this.conditions.push(`(${built.replace(/^WHERE /, '')})`);
25
+ }
26
+ return this;
27
+ }
28
+ orGroup(callback) {
29
+ const nested = new ConditionBuilder(this.ctx);
30
+ callback(nested);
31
+ const built = nested.build();
32
+ if (built) {
33
+ this.conditions.push(`OR (${built.replace(/^WHERE /, '')})`);
34
+ }
35
+ return this;
36
+ }
37
+ build(prefix = 'WHERE') {
38
+ if (!this.conditions.length)
39
+ return '';
40
+ return `${prefix} ${this.conditions.join(' AND ')}`;
41
+ }
42
+ }
43
+ exports.default = ConditionBuilder;
@@ -0,0 +1,31 @@
1
+ import QueryExecutor from '../core/QueryExecutor';
2
+ import { Dialect } from "../dialects/Dialect";
3
+ export default class QueryBuilder<T = any> {
4
+ private executor;
5
+ private fields;
6
+ private joins;
7
+ private groupByFields;
8
+ private orderByFields;
9
+ private limitCount?;
10
+ private offsetCount?;
11
+ private ctes;
12
+ private fromClause;
13
+ private condition;
14
+ private havingCondition;
15
+ private ctx;
16
+ constructor(table: string, executor: QueryExecutor, dialect: Dialect);
17
+ select(fields: string[]): this;
18
+ private addJoin;
19
+ join(table: string, localKey: string, foreignKey: string): this;
20
+ leftJoin(table: string, localKey: string, foreignKey: string): this;
21
+ rightJoin(table: string, localKey: string, foreignKey: string): this;
22
+ fromSubquery(sub: QueryBuilder, alias: string): this;
23
+ where(obj: Record<string, any>): this;
24
+ whereSub(column: string, operator: 'IN' | 'NOT IN', sub: QueryBuilder): this;
25
+ limit(limit: number): this;
26
+ build(): {
27
+ query: string;
28
+ params: any[];
29
+ };
30
+ execute(): Promise<any[]>;
31
+ }