@crane-technologies/database 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +475 -0
- package/dist/components/Database.d.ts +288 -0
- package/dist/components/Database.d.ts.map +1 -0
- package/dist/components/Database.js +554 -0
- package/dist/components/Database.js.map +1 -0
- package/dist/components/Logger.d.ts +63 -0
- package/dist/components/Logger.d.ts.map +1 -0
- package/dist/components/Logger.js +147 -0
- package/dist/components/Logger.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/BulkInsertOptions.d.ts +20 -0
- package/dist/interfaces/BulkInsertOptions.d.ts.map +1 -0
- package/dist/interfaces/BulkInsertOptions.js +3 -0
- package/dist/interfaces/BulkInsertOptions.js.map +1 -0
- package/dist/interfaces/DatabaseConfig.d.ts +52 -0
- package/dist/interfaces/DatabaseConfig.d.ts.map +1 -0
- package/dist/interfaces/DatabaseConfig.js +3 -0
- package/dist/interfaces/DatabaseConfig.js.map +1 -0
- package/dist/interfaces/Dependancy.d.ts +15 -0
- package/dist/interfaces/Dependancy.d.ts.map +1 -0
- package/dist/interfaces/Dependancy.js +3 -0
- package/dist/interfaces/Dependancy.js.map +1 -0
- package/dist/interfaces/QueryList.d.ts +4 -0
- package/dist/interfaces/QueryList.d.ts.map +1 -0
- package/dist/interfaces/QueryList.js +3 -0
- package/dist/interfaces/QueryList.js.map +1 -0
- package/dist/utils/createQueries.d.ts +65 -0
- package/dist/utils/createQueries.d.ts.map +1 -0
- package/dist/utils/createQueries.js +121 -0
- package/dist/utils/createQueries.js.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# @crane-technologies/database
|
|
2
|
+
|
|
3
|
+
Componente de base de datos PostgreSQL con connection pooling, transacciones, gestión de dependencias y **autocompletado de queries con TypeScript**.
|
|
4
|
+
|
|
5
|
+
## ✨ Características
|
|
6
|
+
|
|
7
|
+
- ✅ **Autocompletado completo** con `createQueries()`
|
|
8
|
+
- ✅ Connection pooling automático
|
|
9
|
+
- ✅ Transacciones ACID con dependencias entre queries
|
|
10
|
+
- ✅ Soporte para queries planas, anidadas y mixtas
|
|
11
|
+
- ✅ Raw SQL cuando lo necesites
|
|
12
|
+
- ✅ Backward compatible con código existente
|
|
13
|
+
- ✅ Logs configurables (4 niveles)
|
|
14
|
+
- ✅ 100% TypeScript con tipos completos
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 📦 Instalación
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @crane-technologies/database
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 🚀 Inicio Rápido
|
|
27
|
+
|
|
28
|
+
### **Con `createQueries()` (Recomendado ⭐)**
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import Database, {
|
|
32
|
+
createQueries,
|
|
33
|
+
DatabaseConfig,
|
|
34
|
+
} from "@crane-technologies/database";
|
|
35
|
+
|
|
36
|
+
// 1. Crear queries con autocompletado
|
|
37
|
+
const queries = createQueries({
|
|
38
|
+
users: {
|
|
39
|
+
getById: "SELECT * FROM users WHERE id = $1",
|
|
40
|
+
create: "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *",
|
|
41
|
+
update: "UPDATE users SET name = $1 WHERE id = $2 RETURNING *",
|
|
42
|
+
delete: "DELETE FROM users WHERE id = $1",
|
|
43
|
+
},
|
|
44
|
+
products: {
|
|
45
|
+
getAll: "SELECT * FROM products",
|
|
46
|
+
getById: "SELECT * FROM products WHERE id = $1",
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// 2. Configurar base de datos
|
|
51
|
+
const config: DatabaseConfig = {
|
|
52
|
+
connectionString: process.env.DATABASE_URL!,
|
|
53
|
+
ssl: { rejectUnauthorized: false },
|
|
54
|
+
max: 10,
|
|
55
|
+
logLevel: 2, // 0=NONE, 1=ERROR, 2=DEBUG, 3=ALL
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// 3. Inicializar
|
|
59
|
+
const db = Database.getInstance(config, queries);
|
|
60
|
+
|
|
61
|
+
// 4. Usar con autocompletado! 🎉
|
|
62
|
+
const user = await db.query(queries.users.getById, [123]);
|
|
63
|
+
const newUser = await db.query(queries.users.create, [
|
|
64
|
+
"John",
|
|
65
|
+
"john@email.com",
|
|
66
|
+
]);
|
|
67
|
+
const products = await db.query(queries.products.getAll);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 📚 Casos de Uso
|
|
73
|
+
|
|
74
|
+
### **1. Queries Planas (sin anidación)**
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
const queries = createQueries({
|
|
78
|
+
getUser: "SELECT * FROM users WHERE id = $1",
|
|
79
|
+
createUser: "INSERT INTO users (name) VALUES ($1) RETURNING *",
|
|
80
|
+
deleteUser: "DELETE FROM users WHERE id = $1",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const db = Database.getInstance(config, queries);
|
|
84
|
+
|
|
85
|
+
await db.query(queries.getUser, [123]);
|
|
86
|
+
await db.query(queries.createUser, ["Alice"]);
|
|
87
|
+
await db.query(queries.deleteUser, [456]);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### **2. Queries Anidadas (organizadas por dominio)**
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const queries = createQueries({
|
|
96
|
+
users: {
|
|
97
|
+
getById: "SELECT * FROM users WHERE id = $1",
|
|
98
|
+
create: "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *",
|
|
99
|
+
update: "UPDATE users SET name = $1 WHERE id = $2 RETURNING *",
|
|
100
|
+
},
|
|
101
|
+
products: {
|
|
102
|
+
getAll: "SELECT * FROM products",
|
|
103
|
+
getById: "SELECT * FROM products WHERE id = $1",
|
|
104
|
+
create: "INSERT INTO products (name, price) VALUES ($1, $2) RETURNING *",
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const db = Database.getInstance(config, queries);
|
|
109
|
+
|
|
110
|
+
// Autocompletado funciona en todos los niveles!
|
|
111
|
+
await db.query(queries.users.getById, [123]);
|
|
112
|
+
await db.query(queries.products.create, ["Laptop", 999.99]);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### **3. Queries Mixtas (planas + anidadas)**
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const queries = createQueries({
|
|
121
|
+
// Queries planas para cosas simples
|
|
122
|
+
testConnection: "SELECT NOW() as time",
|
|
123
|
+
checkHealth: "SELECT 1",
|
|
124
|
+
|
|
125
|
+
// Queries anidadas para dominios complejos
|
|
126
|
+
users: {
|
|
127
|
+
getById: "SELECT * FROM users WHERE id = $1",
|
|
128
|
+
create: "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *",
|
|
129
|
+
},
|
|
130
|
+
products: {
|
|
131
|
+
getAll: "SELECT * FROM products",
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const db = Database.getInstance(config, queries);
|
|
136
|
+
|
|
137
|
+
// Planas
|
|
138
|
+
await db.query(queries.testConnection);
|
|
139
|
+
await db.query(queries.checkHealth);
|
|
140
|
+
|
|
141
|
+
// Anidadas
|
|
142
|
+
await db.query(queries.users.getById, [123]);
|
|
143
|
+
await db.query(queries.products.getAll);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### **4. Raw SQL (cuando lo necesites)**
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Raw SQL sigue funcionando
|
|
152
|
+
const result = await db.query("SELECT COUNT(*) FROM users WHERE active = $1", [
|
|
153
|
+
true,
|
|
154
|
+
]);
|
|
155
|
+
const count = result.rows[0].count;
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### **5. Transacciones Simples**
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const results = await db.transaction(
|
|
164
|
+
[queries.users.create, queries.users.create],
|
|
165
|
+
[
|
|
166
|
+
["Alice", "alice@email.com"],
|
|
167
|
+
["Bob", "bob@email.com"],
|
|
168
|
+
]
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
console.log("Usuarios creados:", results.length);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
### **6. Transacciones con Dependencias**
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { Dependency } from "@crane-technologies/database";
|
|
180
|
+
|
|
181
|
+
const queries = createQueries({
|
|
182
|
+
users: {
|
|
183
|
+
create: "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING *",
|
|
184
|
+
},
|
|
185
|
+
profiles: {
|
|
186
|
+
create: "INSERT INTO profiles (user_id, bio) VALUES ($1, $2) RETURNING *",
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const db = Database.getInstance(config, queries);
|
|
191
|
+
|
|
192
|
+
// El ID del usuario se inyecta automáticamente en el perfil
|
|
193
|
+
const dependencies: Dependency[] = [
|
|
194
|
+
{ sourceIndex: 0, targetIndex: 1, targetParamIndex: 0 },
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
const results = await db.transaction(
|
|
198
|
+
[queries.users.create, queries.profiles.create],
|
|
199
|
+
[
|
|
200
|
+
["John", "john@email.com"],
|
|
201
|
+
[null, "John's bio"], // null será reemplazado por user.id
|
|
202
|
+
],
|
|
203
|
+
dependencies
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
console.log("Usuario creado:", results[0].rows[0]);
|
|
207
|
+
console.log("Perfil creado:", results[1].rows[0]);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 📊 Niveles de Log
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
export type LogLevel = 0 | 1 | 2 | 3;
|
|
216
|
+
|
|
217
|
+
// 0 = NONE - Sin logs
|
|
218
|
+
// 1 = ERROR - Solo errores
|
|
219
|
+
// 2 = DEBUG - Errores + debug
|
|
220
|
+
// 3 = ALL - Todos los logs (errores + debug + warnings + info)
|
|
221
|
+
|
|
222
|
+
const config: DatabaseConfig = {
|
|
223
|
+
connectionString: process.env.DATABASE_URL!,
|
|
224
|
+
logLevel: 2, // DEBUG
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// Cambiar dinámicamente
|
|
228
|
+
db.setLogLevel(1); // Solo errores
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## 🔄 Backward Compatibility
|
|
234
|
+
|
|
235
|
+
El paquete es 100% compatible con código existente:
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
// ✅ Forma antigua (sigue funcionando)
|
|
239
|
+
const db = Database.getInstance(config, {
|
|
240
|
+
getUser: "SELECT * FROM users WHERE id = $1",
|
|
241
|
+
});
|
|
242
|
+
await db.query("getUser", [123]);
|
|
243
|
+
|
|
244
|
+
// ✅ Forma nueva (con autocompletado)
|
|
245
|
+
const queries = createQueries({
|
|
246
|
+
users: {
|
|
247
|
+
getById: "SELECT * FROM users WHERE id = $1",
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
const db = Database.getInstance(config, queries);
|
|
251
|
+
await db.query(queries.users.getById, [123]);
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## 🛡️ TypeScript
|
|
257
|
+
|
|
258
|
+
El paquete incluye definiciones de tipos completas:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
import {
|
|
262
|
+
Database,
|
|
263
|
+
createQueries,
|
|
264
|
+
QueryReference,
|
|
265
|
+
DatabaseConfig,
|
|
266
|
+
Dependency,
|
|
267
|
+
Logger,
|
|
268
|
+
LogLevel,
|
|
269
|
+
QueryList,
|
|
270
|
+
} from "@crane-technologies/database";
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## 📚 API Reference
|
|
276
|
+
|
|
277
|
+
### `createQueries(queries)`
|
|
278
|
+
|
|
279
|
+
Crea un objeto de queries con autocompletado.
|
|
280
|
+
|
|
281
|
+
**Parámetros:**
|
|
282
|
+
|
|
283
|
+
- `queries: Record<string, any>` - Objeto con queries (planas, anidadas o mixtas)
|
|
284
|
+
|
|
285
|
+
**Retorna:** Objeto con queries y `__flatMap` interno
|
|
286
|
+
|
|
287
|
+
**Ejemplo:**
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
const queries = createQueries({
|
|
291
|
+
users: {
|
|
292
|
+
getById: "SELECT * FROM users WHERE id = $1",
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
### `Database.getInstance(config, queries)`
|
|
300
|
+
|
|
301
|
+
Obtiene la instancia singleton de Database.
|
|
302
|
+
|
|
303
|
+
**Parámetros:**
|
|
304
|
+
|
|
305
|
+
- `config: DatabaseConfig` - Configuración de conexión
|
|
306
|
+
- `queries?: any` - Resultado de `createQueries()` o objeto plano
|
|
307
|
+
|
|
308
|
+
**Retorna:** `Database`
|
|
309
|
+
|
|
310
|
+
**Ejemplo:**
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
const db = Database.getInstance(config, queries);
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### `db.query(query, params?)`
|
|
319
|
+
|
|
320
|
+
Ejecuta una query (con QueryReference, string key o raw SQL).
|
|
321
|
+
|
|
322
|
+
**Parámetros:**
|
|
323
|
+
|
|
324
|
+
- `query: string | QueryReference` - Query a ejecutar
|
|
325
|
+
- `params?: any[]` - Parámetros de la query
|
|
326
|
+
|
|
327
|
+
**Retorna:** `Promise<QueryResult>`
|
|
328
|
+
|
|
329
|
+
**Ejemplos:**
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
// Con QueryReference
|
|
333
|
+
await db.query(queries.users.getById, [123]);
|
|
334
|
+
|
|
335
|
+
// Con string key
|
|
336
|
+
await db.query("users.getById", [123]);
|
|
337
|
+
|
|
338
|
+
// Raw SQL
|
|
339
|
+
await db.query("SELECT * FROM users WHERE id = $1", [123]);
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
### `db.transaction(queryArray, paramsArray, dependencies?)`
|
|
345
|
+
|
|
346
|
+
Ejecuta múltiples queries en una transacción atómica.
|
|
347
|
+
|
|
348
|
+
**Parámetros:**
|
|
349
|
+
|
|
350
|
+
- `queryArray: (string | QueryReference)[]` - Array de queries
|
|
351
|
+
- `paramsArray: any[][]` - Parámetros para cada query
|
|
352
|
+
- `dependencies?: Dependency[]` - Dependencias entre queries
|
|
353
|
+
|
|
354
|
+
**Retorna:** `Promise<QueryResult[]>`
|
|
355
|
+
|
|
356
|
+
**Ejemplo:**
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
await db.transaction(
|
|
360
|
+
[queries.users.create, queries.profiles.create],
|
|
361
|
+
[
|
|
362
|
+
["John", "john@email.com"],
|
|
363
|
+
[null, "Bio"],
|
|
364
|
+
],
|
|
365
|
+
[{ sourceIndex: 0, targetIndex: 1, targetParamIndex: 0 }]
|
|
366
|
+
);
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
### `db.close()`
|
|
372
|
+
|
|
373
|
+
Cierra todas las conexiones del pool.
|
|
374
|
+
|
|
375
|
+
**Retorna:** `Promise<void>`
|
|
376
|
+
|
|
377
|
+
**Ejemplo:**
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
await db.close();
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
### `db.setLogLevel(level)`
|
|
386
|
+
|
|
387
|
+
Cambia el nivel de log dinámicamente.
|
|
388
|
+
|
|
389
|
+
**Parámetros:**
|
|
390
|
+
|
|
391
|
+
- `level: 0 | 1 | 2 | 3` - Nuevo nivel de log
|
|
392
|
+
|
|
393
|
+
**Ejemplo:**
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
db.setLogLevel(1); // Solo errores
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## 🔒 Cerrar Conexiones
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
process.on("SIGTERM", async () => {
|
|
405
|
+
const db = Database.getInstance(config);
|
|
406
|
+
await db.close();
|
|
407
|
+
process.exit(0);
|
|
408
|
+
});
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## 🧪 Testing
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
npm test
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## 📄 Licencia
|
|
422
|
+
|
|
423
|
+
MIT
|
|
424
|
+
|
|
425
|
+
---
|
|
426
|
+
|
|
427
|
+
## 🤝 Contribuir
|
|
428
|
+
|
|
429
|
+
Issues y Pull Requests son bienvenidos en: https://github.com/Crane/database
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## 📝 Changelog
|
|
434
|
+
|
|
435
|
+
### v2.0.0 (Actual)
|
|
436
|
+
|
|
437
|
+
- ✨ **NEW:** `createQueries()` con autocompletado completo
|
|
438
|
+
- ✨ **NEW:** Soporte para queries planas, anidadas y mixtas
|
|
439
|
+
- ✨ **NEW:** `QueryReference` type para mejor DX
|
|
440
|
+
- ✅ Backward compatible con v1.x.x
|
|
441
|
+
- 📚 Documentación actualizada con ejemplos
|
|
442
|
+
|
|
443
|
+
### v1.0.0
|
|
444
|
+
|
|
445
|
+
- 🎉 Release inicial
|
|
446
|
+
- Connection pooling
|
|
447
|
+
- Transacciones con dependencias
|
|
448
|
+
- Sistema de logs configurable
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
### `db.bulkInsert(table, columns, rows, options?)`
|
|
453
|
+
|
|
454
|
+
Usa `COPY … FROM STDIN` para insertar muchos registros de forma nativa.
|
|
455
|
+
|
|
456
|
+
**Parámetros:**
|
|
457
|
+
|
|
458
|
+
- `table`: nombre de la tabla destino
|
|
459
|
+
- `columns`: columnas que se van a poblar (ordenadas)
|
|
460
|
+
- `rows`: matriz con los valores (cada fila puede tener `null`)
|
|
461
|
+
- `options?`: [`BulkInsertOptions`](./interfaces/BulkInsertOptions.ts) para delimitar y agregar cabecera
|
|
462
|
+
|
|
463
|
+
**Ejemplo:**
|
|
464
|
+
|
|
465
|
+
```ts
|
|
466
|
+
await db.bulkInsert(
|
|
467
|
+
"test_users",
|
|
468
|
+
["name", "email"],
|
|
469
|
+
[
|
|
470
|
+
["Bulk 1", "bulk1@example.com"],
|
|
471
|
+
["Bulk 2", "bulk2@example.com"],
|
|
472
|
+
],
|
|
473
|
+
{ header: false }
|
|
474
|
+
);
|
|
475
|
+
```
|