@ooneex/database 0.0.1 → 0.0.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.
package/README.md CHANGED
@@ -1,30 +1,28 @@
1
1
  # @ooneex/database
2
2
 
3
- A comprehensive TypeScript/JavaScript database library designed for Bun runtime. This package provides a unified interface for database operations with support for SQLite, PostgreSQL, MySQL, and Redis, along with TypeORM adapters for advanced ORM functionality.
3
+ A database connection and management library for TypeScript applications with TypeORM integration. This package provides unified interfaces for Redis, PostgreSQL, and SQLite databases with automatic connection handling, repository management, and seamless integration with the Ooneex framework.
4
4
 
5
5
  ![Bun](https://img.shields.io/badge/Bun-Compatible-orange?style=flat-square&logo=bun)
6
+ ![Deno](https://img.shields.io/badge/Deno-Compatible-blue?style=flat-square&logo=deno)
7
+ ![Node.js](https://img.shields.io/badge/Node.js-Compatible-green?style=flat-square&logo=node.js)
6
8
  ![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue?style=flat-square&logo=typescript)
7
9
  ![MIT License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)
8
10
 
9
11
  ## Features
10
12
 
11
- ✅ **Multi-Database Support** - SQLite, PostgreSQL, MySQL, and Redis support
13
+ ✅ **Multiple Databases** - Support for PostgreSQL, SQLite, and Redis
12
14
 
13
- ✅ **Bun Native** - Built specifically for Bun runtime with Bun.SQL
15
+ ✅ **TypeORM Integration** - Full TypeORM support for relational databases
14
16
 
15
- ✅ **TypeORM Integration** - Dedicated adapters for PostgreSQL and SQLite
17
+ ✅ **Repository Pattern** - Easy access to TypeORM repositories
16
18
 
17
- ✅ **Type-Safe** - Full TypeScript support with proper type definitions
18
-
19
- ✅ **Connection Management** - Robust connection opening, closing, and lifecycle management
20
-
21
- ✅ **Database Operations** - Create, drop, and manage database instances
19
+ ✅ **Connection Management** - Automatic connection handling and reconnection
22
20
 
23
- ✅ **Error Handling** - Comprehensive error handling with custom exceptions
21
+ ✅ **Container Integration** - Works seamlessly with dependency injection
24
22
 
25
- ✅ **Environment Support** - Automatic environment variable detection
23
+ ✅ **Type-Safe** - Full TypeScript support with proper type definitions
26
24
 
27
- ✅ **Zero Configuration** - Works out of the box with sensible defaults
25
+ ✅ **Decorators** - Register database services with decorators
28
26
 
29
27
  ## Installation
30
28
 
@@ -33,289 +31,227 @@ A comprehensive TypeScript/JavaScript database library designed for Bun runtime.
33
31
  bun add @ooneex/database
34
32
  ```
35
33
 
36
- ## Usage
37
-
38
- ### TypeORM PostgreSQL Adapter
34
+ ### pnpm
35
+ ```bash
36
+ pnpm add @ooneex/database
37
+ ```
39
38
 
40
- ```typescript
41
- import { TypeormPgDatabaseAdapter } from '@ooneex/database';
42
- import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
39
+ ### Yarn
40
+ ```bash
41
+ yarn add @ooneex/database
42
+ ```
43
43
 
44
- @Entity()
45
- class User {
46
- @PrimaryGeneratedColumn()
47
- id: number;
44
+ ### npm
45
+ ```bash
46
+ npm install @ooneex/database
47
+ ```
48
48
 
49
- @Column()
50
- name: string;
49
+ ## Usage
51
50
 
52
- @Column()
53
- email: string;
54
- }
51
+ ### PostgreSQL with TypeORM
55
52
 
56
- const adapter = new TypeormPgDatabaseAdapter({
57
- url: 'postgresql://user:pass@localhost:5432/mydb',
58
- synchronize: true,
59
- entities: [User]
53
+ ```typescript
54
+ import { TypeormPgDatabase } from '@ooneex/database';
55
+ import { UserEntity } from './entities/UserEntity';
56
+
57
+ const database = new TypeormPgDatabase({
58
+ host: 'localhost',
59
+ port: 5432,
60
+ username: 'postgres',
61
+ password: 'password',
62
+ database: 'myapp'
60
63
  });
61
64
 
62
- // Open and get repository
63
- const userRepository = await adapter.open(User);
65
+ // Get repository for an entity
66
+ const userRepository = await database.open(UserEntity);
64
67
 
65
- // Create user
66
- const user = userRepository.create({
67
- name: 'John Doe',
68
- email: 'john@example.com'
69
- });
70
- await userRepository.save(user);
71
-
72
- // Find users
68
+ // Use TypeORM repository methods
73
69
  const users = await userRepository.find();
70
+ const user = await userRepository.findOneBy({ id: '123' });
74
71
 
75
- // Close connection
76
- await adapter.close();
72
+ // Close connection when done
73
+ await database.close();
77
74
  ```
78
75
 
79
- ### TypeORM SQLite Adapter
76
+ ### SQLite with TypeORM
80
77
 
81
78
  ```typescript
82
- import { TypeormSqliteDatabaseAdapter } from '@ooneex/database';
83
- import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
79
+ import { TypeormSqliteDatabase } from '@ooneex/database';
80
+ import { ProductEntity } from './entities/ProductEntity';
84
81
 
85
- @Entity()
86
- class Product {
87
- @PrimaryGeneratedColumn()
88
- id: number;
82
+ const database = new TypeormSqliteDatabase({
83
+ database: './data/app.db'
84
+ });
89
85
 
90
- @Column()
91
- name: string;
86
+ // Get repository
87
+ const productRepository = await database.open(ProductEntity);
92
88
 
93
- @Column('decimal')
94
- price: number;
95
- }
96
-
97
- const adapter = new TypeormSqliteDatabaseAdapter({
98
- database: './products.db',
99
- synchronize: true,
100
- entities: [Product]
89
+ // Query products
90
+ const products = await productRepository.find({
91
+ where: { category: 'electronics' },
92
+ take: 10
101
93
  });
102
94
 
103
- // Open and get repository
104
- const productRepository = await adapter.open(Product);
95
+ await database.close();
96
+ ```
97
+
98
+ ### Redis Database
99
+
100
+ ```typescript
101
+ import { RedisDatabase } from '@ooneex/database';
105
102
 
106
- // Create product
107
- const product = productRepository.create({
108
- name: 'Laptop',
109
- price: 999.99
103
+ const redis = new RedisDatabase({
104
+ url: 'redis://localhost:6379'
110
105
  });
111
- await productRepository.save(product);
106
+
107
+ // Open connection and get client
108
+ const client = await redis.open();
109
+
110
+ // Use Redis commands
111
+ await client.set('key', 'value');
112
+ const value = await client.get('key');
112
113
 
113
114
  // Close connection
114
- await adapter.close();
115
+ await redis.close();
115
116
  ```
116
117
 
117
- ### Error Handling
118
+ ### Using Environment Variables
118
119
 
119
120
  ```typescript
120
- import { Database, DatabaseException } from '@ooneex/database';
121
-
122
- try {
123
- const db = new Database('invalid://connection');
124
- await db.open();
125
- } catch (error) {
126
- if (error instanceof DatabaseException) {
127
- console.error('Database error:', error.message);
128
- console.error('Error data:', error.data);
129
- }
130
- }
131
- ```
121
+ import { TypeormPgDatabase } from '@ooneex/database';
132
122
 
133
- ### Environment Configuration
123
+ // Automatically uses environment variables
124
+ const database = new TypeormPgDatabase();
134
125
 
135
- ```bash
136
- # Set in your .env file
137
- DATABASE_URL=sqlite://./myapp.db
138
- # or
139
- DATABASE_URL=postgresql://user:password@localhost:5432/mydb
140
- # or
141
- DATABASE_URL=mysql://user:password@localhost:3306/mydb
142
-
143
- # For SQLite adapter
144
- SQLITE_DATABASE_PATH=./myapp.db
145
-
146
- # For Redis adapter
147
- REDIS_URL=redis://localhost:6379
148
- # or
149
- VALKEY_URL=redis://localhost:6379
126
+ // Environment variables:
127
+ // DATABASE_HOST
128
+ // DATABASE_PORT
129
+ // DATABASE_USERNAME
130
+ // DATABASE_PASSWORD
131
+ // DATABASE_NAME
150
132
  ```
151
133
 
152
134
  ## API Reference
153
135
 
154
- ### `Database` Class
136
+ ### Classes
155
137
 
156
- The main database class providing connection management and basic operations.
138
+ #### `TypeormPgDatabase`
157
139
 
158
- #### Constructor
140
+ PostgreSQL database adapter using TypeORM.
159
141
 
142
+ **Constructor:**
160
143
  ```typescript
161
- constructor(connectionString?: string | URL, options?: Bun.SQL.Options)
144
+ new TypeormPgDatabase(options?: PostgresConnectionOptions)
162
145
  ```
163
146
 
164
147
  **Parameters:**
165
- - `connectionString` - Database connection string or URL object
166
- - `options` - Bun.SQL connection options
167
-
168
- **Example:**
169
- ```typescript
170
- const db = new Database('sqlite://./app.db', { timeout: 5000 });
171
- ```
148
+ - `options.host` - Database host (default: `DATABASE_HOST` env var)
149
+ - `options.port` - Database port (default: `DATABASE_PORT` env var)
150
+ - `options.username` - Database username (default: `DATABASE_USERNAME` env var)
151
+ - `options.password` - Database password (default: `DATABASE_PASSWORD` env var)
152
+ - `options.database` - Database name (default: `DATABASE_NAME` env var)
153
+ - `options.synchronize` - Auto-sync schema (default: false)
154
+ - `options.logging` - Enable query logging (default: false)
172
155
 
173
- #### Methods
156
+ **Methods:**
174
157
 
175
- ##### `getClient(): Bun.SQL`
176
- Returns the underlying Bun.SQL client instance.
158
+ ##### `open<Entity>(entity: EntityTarget<Entity>, database?: string): Promise<Repository<Entity>>`
177
159
 
178
- **Returns:** The Bun.SQL client
160
+ Opens connection and returns a TypeORM repository for the specified entity.
179
161
 
180
- **Example:**
181
- ```typescript
182
- const client = db.getClient();
183
- const result = await client`SELECT * FROM users`;
184
- ```
162
+ **Parameters:**
163
+ - `entity` - TypeORM entity class
164
+ - `database` - Optional database name override
185
165
 
186
- ##### `open(): Promise<void>`
187
- Opens the database connection.
166
+ **Returns:** TypeORM Repository instance
188
167
 
189
168
  **Example:**
190
169
  ```typescript
191
- await db.open();
170
+ const userRepo = await database.open(UserEntity);
171
+ const users = await userRepo.find();
192
172
  ```
193
173
 
194
174
  ##### `close(): Promise<void>`
195
- Closes the database connection.
196
175
 
197
- **Example:**
198
- ```typescript
199
- await db.close();
200
- ```
176
+ Closes the database connection.
201
177
 
202
178
  ##### `drop(): Promise<void>`
203
- Drops the database. **Caution: This is destructive and cannot be undone.**
204
179
 
205
- **Example:**
206
- ```typescript
207
- await db.drop(); // Permanently deletes the database
208
- ```
180
+ Drops the database (use with caution!).
209
181
 
210
- ### `TypeormPgDatabaseAdapter` Class
182
+ ---
211
183
 
212
- TypeORM adapter for PostgreSQL databases.
184
+ #### `TypeormSqliteDatabase`
213
185
 
214
- #### Constructor
186
+ SQLite database adapter using TypeORM.
215
187
 
188
+ **Constructor:**
216
189
  ```typescript
217
- constructor(options: Omit<PostgresConnectionOptions, "type">)
190
+ new TypeormSqliteDatabase(options?: SqliteConnectionOptions)
218
191
  ```
219
192
 
220
193
  **Parameters:**
221
- - `options` - PostgreSQL connection options (without type field)
194
+ - `options.database` - Path to SQLite database file
222
195
 
223
- #### Methods
196
+ **Methods:**
224
197
 
225
- ##### `getSource(): DataSource`
226
- Returns the TypeORM DataSource instance.
198
+ Same interface as `TypeormPgDatabase`.
227
199
 
228
- ##### `open<Entity>(entity: EntityTarget<Entity>): Promise<Repository<Entity>>`
229
- Opens connection and returns repository for the specified entity.
200
+ ---
230
201
 
231
- ##### `close(): Promise<void>`
232
- Closes the database connection.
202
+ #### `AbstractTypeormSqliteDatabase`
233
203
 
234
- ##### `drop(): Promise<void>`
235
- Drops the database schema.
204
+ Abstract base class for creating custom SQLite database adapters.
236
205
 
237
- ##### `getEntityManager(): EntityManager`
238
- Returns the TypeORM EntityManager.
206
+ **Example:**
207
+ ```typescript
208
+ class MyDatabase extends AbstractTypeormSqliteDatabase {
209
+ protected getEntities() {
210
+ return [UserEntity, ProductEntity];
211
+ }
212
+ }
213
+ ```
239
214
 
240
- ### `TypeormSqliteDatabaseAdapter` Class
215
+ ---
241
216
 
242
- TypeORM adapter for SQLite databases.
217
+ #### `RedisDatabase`
243
218
 
244
- #### Constructor
219
+ Redis database adapter using Bun's built-in Redis client.
245
220
 
221
+ **Constructor:**
246
222
  ```typescript
247
- constructor(options: Omit<SqliteConnectionOptions, "type">)
223
+ new RedisDatabase(options?: RedisConnectionOptionsType)
248
224
  ```
249
225
 
250
226
  **Parameters:**
251
- - `options` - SQLite connection options (without type field)
227
+ - `options.url` - Redis connection URL (default: `REDIS_URL` env var)
228
+ - `options.connectionTimeout` - Connection timeout in ms (default: 10000)
229
+ - `options.idleTimeout` - Idle timeout in ms (default: 30000)
230
+ - `options.autoReconnect` - Enable auto reconnection (default: true)
231
+ - `options.maxRetries` - Maximum retry attempts (default: 3)
232
+ - `options.tls` - TLS configuration (optional)
252
233
 
253
- #### Methods
234
+ **Methods:**
254
235
 
255
- Same methods as `TypeormPgDatabaseAdapter` but optimized for SQLite.
236
+ ##### `open(): Promise<RedisClient>`
256
237
 
257
- ### `RedisDatabaseAdapter` Class
238
+ Opens connection and returns the Redis client.
258
239
 
259
- Redis adapter using Bun's native Redis client.
260
-
261
- #### Constructor
240
+ **Returns:** Bun RedisClient instance
262
241
 
242
+ **Example:**
263
243
  ```typescript
264
- constructor(options: RedisConnectionOptions = {})
244
+ const client = await redis.open();
245
+ await client.set('session:123', JSON.stringify(sessionData));
265
246
  ```
266
247
 
267
- **Parameters:**
268
- - `options` - Redis connection configuration
269
-
270
- **Options:**
271
- - `url` - Redis connection URL (defaults to environment variables or localhost)
272
- - `connectionTimeout` - Connection timeout in milliseconds (default: 10000)
273
- - `idleTimeout` - Idle timeout in milliseconds (default: 0)
274
- - `autoReconnect` - Whether to automatically reconnect (default: true)
275
- - `maxRetries` - Maximum reconnection attempts (default: 10)
276
- - `enableOfflineQueue` - Queue commands when disconnected (default: true)
277
- - `enableAutoPipelining` - Automatically pipeline commands (default: true)
278
- - `tls` - TLS configuration (default: false)
279
-
280
- #### Methods
281
-
282
- ##### `getClient(): RedisClient`
283
- Returns the underlying Bun Redis client instance.
284
-
285
- ##### `open(): Promise<void>`
286
- Opens the Redis connection.
287
-
288
248
  ##### `close(): Promise<void>`
249
+
289
250
  Closes the Redis connection.
290
251
 
291
252
  ##### `drop(): Promise<void>`
292
- Flushes the current Redis database (FLUSHDB).
293
-
294
- ##### `ping(): Promise<string>`
295
- Pings the Redis server.
296
-
297
- ##### `info(section?: string): Promise<string>`
298
- Gets Redis server information.
299
253
 
300
- ##### `isConnected(): boolean`
301
- Returns connection status.
302
-
303
- ##### `getBufferedAmount(): number`
304
- Returns buffered data amount in bytes.
305
-
306
- ### `DatabaseException` Class
307
-
308
- Custom exception class for database-related errors.
309
-
310
- #### Constructor
311
-
312
- ```typescript
313
- constructor(message: string, data?: T)
314
- ```
315
-
316
- **Parameters:**
317
- - `message` - Error message
318
- - `data` - Additional error data
254
+ Flushes all data from Redis (use with caution!).
319
255
 
320
256
  ### Interfaces
321
257
 
@@ -323,265 +259,240 @@ constructor(message: string, data?: T)
323
259
 
324
260
  ```typescript
325
261
  interface IDatabase {
326
- open(): Promise<void>;
327
- close(): Promise<void>;
328
- drop(): Promise<void>;
262
+ open: () => Promise<void>;
263
+ close: () => Promise<void>;
264
+ drop: () => Promise<void>;
329
265
  }
330
266
  ```
331
267
 
332
- #### `ITypeormDatabaseAdapter`
268
+ #### `ITypeormDatabase`
333
269
 
334
270
  ```typescript
335
- interface ITypeormDatabaseAdapter {
336
- open(entity: any): Promise<any>;
337
- close(): Promise<void>;
338
- drop(): Promise<void>;
271
+ interface ITypeormDatabase {
272
+ open: <Entity extends ObjectLiteral>(
273
+ entity: EntityTarget<Entity>,
274
+ database?: string
275
+ ) => Promise<Repository<Entity>>;
276
+ close: () => Promise<void>;
277
+ drop: () => Promise<void>;
339
278
  }
340
279
  ```
341
280
 
342
- ## Supported Database URLs
281
+ #### `IRedisDatabase`
343
282
 
344
- ### SQLite
345
283
  ```typescript
346
- // File-based database
347
- 'sqlite://./database.db'
348
- 'sqlite:database.db'
349
- './database.db'
350
-
351
- // In-memory database
352
- 'sqlite://:memory:'
353
- ':memory:'
284
+ interface IRedisDatabase {
285
+ open: () => Promise<RedisClient>;
286
+ close: () => Promise<void>;
287
+ drop: () => Promise<void>;
288
+ }
354
289
  ```
355
290
 
356
- ### PostgreSQL
357
- ```typescript
358
- 'postgresql://user:password@localhost:5432/database'
359
- 'postgres://user:password@localhost:5432/database'
360
- ```
291
+ ### Types
361
292
 
362
- ### MySQL
363
- ```typescript
364
- 'mysql://user:password@localhost:3306/database'
365
- 'mysql2://user:password@localhost:3306/database'
366
- ```
293
+ #### `DatabaseClassType`
367
294
 
368
- ### Redis
369
295
  ```typescript
370
- // Standard Redis URL
371
- 'redis://localhost:6379'
372
-
373
- // With authentication
374
- 'redis://username:password@localhost:6379'
375
-
376
- // With database number
377
- 'redis://localhost:6379/0'
296
+ type DatabaseClassType = new (...args: any[]) => IDatabase | IRedisDatabase | ITypeormDatabase;
297
+ ```
378
298
 
379
- // TLS connections
380
- 'rediss://localhost:6379'
381
- 'redis+tls://localhost:6379'
299
+ #### `RedisConnectionOptionsType`
382
300
 
383
- // Unix socket connections
384
- 'redis+unix:///path/to/socket'
385
- 'redis+tls+unix:///path/to/socket'
301
+ ```typescript
302
+ type RedisConnectionOptionsType = {
303
+ url?: string;
304
+ connectionTimeout?: number;
305
+ idleTimeout?: number;
306
+ autoReconnect?: boolean;
307
+ maxRetries?: number;
308
+ enableOfflineQueue?: boolean;
309
+ enableAutoPipelining?: boolean;
310
+ tls?: boolean | {
311
+ rejectUnauthorized?: boolean;
312
+ ca?: string;
313
+ cert?: string;
314
+ key?: string;
315
+ };
316
+ };
386
317
  ```
387
318
 
388
- ## Best Practices
389
-
390
- ### Connection Management
391
- - Always call `close()` when done with database operations
392
- - Use try-catch blocks for proper error handling
393
- - Consider connection pooling for high-traffic applications
319
+ ## Advanced Usage
394
320
 
395
- ### Redis Adapter
321
+ ### Integration with Ooneex App
396
322
 
397
323
  ```typescript
398
- import { RedisDatabaseAdapter } from '@ooneex/database';
399
-
400
- const adapter = new RedisDatabaseAdapter({
401
- url: 'redis://localhost:6379',
402
- connectionTimeout: 10000,
403
- autoReconnect: true,
404
- maxRetries: 10
324
+ import { App } from '@ooneex/app';
325
+ import { TypeormPgDatabase } from '@ooneex/database';
326
+
327
+ const database = new TypeormPgDatabase({
328
+ host: 'localhost',
329
+ port: 5432,
330
+ username: 'postgres',
331
+ password: 'password',
332
+ database: 'myapp'
405
333
  });
406
334
 
407
- // Open connection
408
- await adapter.open();
409
-
410
- // Get Redis client for operations
411
- const client = adapter.getClient();
412
-
413
- // Basic operations
414
- await client.set('user:1', 'Alice');
415
- const user = await client.get('user:1');
416
-
417
- // Hash operations
418
- await client.hmset('user:2', ['name', 'Bob', 'email', 'bob@example.com']);
419
- const userFields = await client.hmget('user:2', ['name', 'email']);
420
-
421
- // Set operations
422
- await client.sadd('tags', 'redis', 'database', 'cache');
423
- const tags = await client.smembers('tags');
424
-
425
- // Utility methods
426
- const pingResult = await adapter.ping();
427
- const serverInfo = await adapter.info('server');
335
+ const app = new App({
336
+ database,
337
+ // ... other config
338
+ });
428
339
 
429
- // Close connection
430
- await adapter.close();
340
+ await app.run();
431
341
  ```
432
342
 
433
- ### Error Handling
434
- - Catch `DatabaseException` specifically for database errors
435
- - Log error details for debugging
436
- - Implement retry logic for transient failures
437
-
438
- ### Security
439
- - Never hardcode database credentials
440
- - Use environment variables for sensitive information
441
- - Validate input data before database operations
442
-
443
- ### Performance
444
- - Use prepared statements when possible
445
- - Implement proper indexing strategies
446
- - Monitor query performance in production
447
-
448
- ## TypeORM Integration
449
-
450
- This package provides specialized adapters for TypeORM, allowing you to leverage the full power of TypeORM while maintaining the simplicity of the Ooneex database interface.
451
-
452
- ### PostgreSQL with TypeORM
343
+ ### Using in Controllers
453
344
 
454
345
  ```typescript
455
- import { TypeormPgDatabaseAdapter } from '@ooneex/database';
456
-
457
- const adapter = new TypeormPgDatabaseAdapter({
458
- url: process.env.DATABASE_URL,
459
- synchronize: false, // Set to true only in development
460
- entities: [User, Product, Order],
461
- migrations: ['./migrations/**/*.ts'],
462
- extra: {
463
- max: 20, // Maximum number of connections
464
- idleTimeoutMillis: 30000
346
+ import { Route } from '@ooneex/routing';
347
+ import type { IController, ContextType } from '@ooneex/controller';
348
+ import { UserEntity } from '../entities/UserEntity';
349
+
350
+ @Route.http({
351
+ name: 'api.users.list',
352
+ path: '/api/users',
353
+ method: 'GET',
354
+ description: 'List all users'
355
+ })
356
+ class UserListController implements IController {
357
+ public async index(context: ContextType): Promise<IResponse> {
358
+ const { database } = context;
359
+
360
+ const userRepository = await database?.open(UserEntity);
361
+ const users = await userRepository?.find({
362
+ take: 100,
363
+ order: { createdAt: 'DESC' }
364
+ });
365
+
366
+ return context.response.json({ users });
465
367
  }
466
- });
368
+ }
467
369
  ```
468
370
 
469
- ### SQLite with TypeORM
371
+ ### Container Integration with Decorators
470
372
 
471
373
  ```typescript
472
- import { TypeormSqliteDatabaseAdapter } from '@ooneex/database';
473
-
474
- const adapter = new TypeormSqliteDatabaseAdapter({
475
- database: './myapp.db',
476
- synchronize: true,
477
- entities: [User, Product],
478
- enableWAL: true, // Write-Ahead Logging for better performance
479
- busyTimeout: 30000
480
- });
481
- ```
482
-
483
- ## Redis Integration
374
+ import { container, EContainerScope } from '@ooneex/container';
375
+ import { TypeormPgDatabase, decorator } from '@ooneex/database';
376
+
377
+ // Register with decorator
378
+ @decorator.database()
379
+ class AppDatabase extends TypeormPgDatabase {
380
+ constructor() {
381
+ super({
382
+ host: 'localhost',
383
+ port: 5432,
384
+ database: 'myapp'
385
+ });
386
+ }
387
+ }
484
388
 
485
- This package provides a native Redis adapter built specifically for Bun's Redis client, offering high-performance Redis operations with full TypeScript support.
389
+ // Resolve from container
390
+ const database = container.get(AppDatabase);
391
+ ```
486
392
 
487
- ### Basic Redis Usage
393
+ ### Error Handling
488
394
 
489
395
  ```typescript
490
- import { RedisDatabaseAdapter } from '@ooneex/database';
396
+ import { TypeormPgDatabase, DatabaseException } from '@ooneex/database';
491
397
 
492
- const adapter = new RedisDatabaseAdapter({
493
- url: process.env.REDIS_URL,
494
- connectionTimeout: 5000,
495
- autoReconnect: true
496
- });
497
-
498
- await adapter.open();
499
- const client = adapter.getClient();
398
+ try {
399
+ const database = new TypeormPgDatabase();
400
+ const userRepo = await database.open(UserEntity);
401
+ const users = await userRepo.find();
402
+ } catch (error) {
403
+ if (error instanceof DatabaseException) {
404
+ console.error('Database Error:', error.message);
405
+ console.error('Status:', error.status);
406
+ }
407
+ }
408
+ ```
500
409
 
501
- // String operations
502
- await client.set('key', 'value');
503
- const value = await client.get('key');
410
+ ### Transaction Support
504
411
 
505
- // Numeric operations
506
- await client.incr('counter');
507
- await client.decr('counter');
412
+ ```typescript
413
+ import { TypeormPgDatabase } from '@ooneex/database';
414
+ import { UserEntity, OrderEntity } from './entities';
508
415
 
509
- // Hash operations
510
- await client.hmset('user:1', ['name', 'Alice', 'email', 'alice@example.com']);
511
- const userData = await client.hmget('user:1', ['name', 'email']);
416
+ const database = new TypeormPgDatabase();
417
+ const userRepo = await database.open(UserEntity);
512
418
 
513
- // Set operations
514
- await client.sadd('tags', 'redis', 'cache');
515
- const allTags = await client.smembers('tags');
419
+ // Access the entity manager for transactions
420
+ const entityManager = userRepo.manager;
516
421
 
517
- await adapter.close();
422
+ await entityManager.transaction(async (transactionalManager) => {
423
+ const user = await transactionalManager.findOneBy(UserEntity, { id: '123' });
424
+
425
+ const order = transactionalManager.create(OrderEntity, {
426
+ userId: user.id,
427
+ total: 99.99
428
+ });
429
+
430
+ await transactionalManager.save(order);
431
+
432
+ user.ordersCount += 1;
433
+ await transactionalManager.save(user);
434
+ });
518
435
  ```
519
436
 
520
- ### Redis Pub/Sub
437
+ ### Multiple Database Connections
521
438
 
522
439
  ```typescript
523
- const publisher = new RedisDatabaseAdapter();
524
- const subscriber = new RedisDatabaseAdapter();
440
+ import { TypeormPgDatabase, TypeormSqliteDatabase } from '@ooneex/database';
525
441
 
526
- await publisher.open();
527
- await subscriber.open();
442
+ // Main PostgreSQL database
443
+ const mainDb = new TypeormPgDatabase({
444
+ host: 'localhost',
445
+ database: 'main_app'
446
+ });
528
447
 
529
- // Set up subscription
530
- const subClient = subscriber.getClient();
531
- await subClient.subscribe('notifications', (message, channel) => {
532
- console.log(`Received: ${message} on ${channel}`);
448
+ // Analytics SQLite database
449
+ const analyticsDb = new TypeormSqliteDatabase({
450
+ database: './analytics.db'
533
451
  });
534
452
 
535
- // Publish message
536
- const pubClient = publisher.getClient();
537
- await pubClient.publish('notifications', 'Hello subscribers!');
453
+ // Use both databases
454
+ const userRepo = await mainDb.open(UserEntity);
455
+ const analyticsRepo = await analyticsDb.open(AnalyticsEntity);
538
456
  ```
539
457
 
540
458
  ### Redis Caching Pattern
541
459
 
542
460
  ```typescript
543
- async function getUserWithCache(userId: number) {
544
- const cacheKey = `user:${userId}`;
545
-
546
- // Try cache first
547
- const cached = await client.get(cacheKey);
548
- if (cached) {
549
- return JSON.parse(cached);
461
+ import { RedisDatabase } from '@ooneex/database';
462
+
463
+ class CacheService {
464
+ private readonly redis: RedisDatabase;
465
+ private client: Bun.RedisClient | null = null;
466
+
467
+ constructor() {
468
+ this.redis = new RedisDatabase();
550
469
  }
551
-
552
- // Fetch from database
553
- const user = await database.getUser(userId);
554
-
555
- // Cache with expiration
556
- await client.set(cacheKey, JSON.stringify(user));
557
- await client.expire(cacheKey, 3600); // 1 hour
558
-
559
- return user;
560
- }
561
- ```
562
470
 
563
- ### Redis Rate Limiting
471
+ public async init(): Promise<void> {
472
+ this.client = await this.redis.open();
473
+ }
564
474
 
565
- ```typescript
566
- async function rateLimit(ip: string, limit: number = 100, windowSecs: number = 3600) {
567
- const key = `ratelimit:${ip}`;
568
-
569
- const count = await client.incr(key);
570
-
571
- if (count === 1) {
572
- await client.expire(key, windowSecs);
475
+ public async get<T>(key: string): Promise<T | null> {
476
+ const value = await this.client?.get(key);
477
+ return value ? JSON.parse(value) : null;
478
+ }
479
+
480
+ public async set<T>(key: string, value: T, ttl?: number): Promise<void> {
481
+ await this.client?.set(key, JSON.stringify(value));
482
+ if (ttl) {
483
+ await this.client?.expire(key, ttl);
484
+ }
485
+ }
486
+
487
+ public async delete(key: string): Promise<void> {
488
+ await this.client?.del(key);
573
489
  }
574
-
575
- return {
576
- allowed: count <= limit,
577
- remaining: Math.max(0, limit - count)
578
- };
579
490
  }
580
491
  ```
581
492
 
582
493
  ## License
583
494
 
584
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
495
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
585
496
 
586
497
  ## Contributing
587
498
 
@@ -600,7 +511,6 @@ Contributions are welcome! Please feel free to submit a Pull Request. For major
600
511
  - Follow the existing code style
601
512
  - Update documentation for API changes
602
513
  - Ensure all tests pass before submitting PR
603
- - Test with different database types (SQLite, PostgreSQL, MySQL)
604
514
 
605
515
  ---
606
516
 
package/dist/index.d.ts CHANGED
@@ -1,11 +1,7 @@
1
- import { Exception } from "@ooneex/exception";
2
- declare class DatabaseException extends Exception {
3
- constructor(message: string, data?: Record<string, unknown>);
4
- }
5
- import { RedisClient as RedisClient2 } from "bun";
1
+ import { DataSource, EntityManager, EntityTarget as EntityTarget2, ObjectLiteral as ObjectLiteral2, Repository as Repository2 } from "typeorm";
6
2
  import { RedisClient } from "bun";
7
3
  import { EntityTarget, ObjectLiteral, Repository } from "typeorm";
8
- type DatabaseClassType = new (...args: any[]) => IDatabase | IRedisDatabaseAdapter | ITypeormDatabaseAdapter;
4
+ type DatabaseClassType = new (...args: any[]) => IDatabase | IRedisDatabase | ITypeormDatabase;
9
5
  type RedisConnectionOptionsType = {
10
6
  url?: string;
11
7
  connectionTimeout?: number;
@@ -26,17 +22,34 @@ interface IDatabase {
26
22
  close: () => Promise<void>;
27
23
  drop: () => Promise<void>;
28
24
  }
29
- interface IRedisDatabaseAdapter {
25
+ interface IRedisDatabase {
30
26
  open: () => Promise<RedisClient>;
31
27
  close: () => Promise<void>;
32
28
  drop: () => Promise<void>;
33
29
  }
34
- interface ITypeormDatabaseAdapter {
35
- open: <Entity extends ObjectLiteral>(entity: EntityTarget<Entity>) => Promise<Repository<Entity>>;
30
+ interface ITypeormDatabase {
31
+ open: <Entity extends ObjectLiteral>(entity: EntityTarget<Entity>, database?: string) => Promise<Repository<Entity>>;
36
32
  close: () => Promise<void>;
37
33
  drop: () => Promise<void>;
38
34
  }
39
- declare class RedisDatabaseAdapter implements IRedisDatabaseAdapter {
35
+ declare abstract class AbstractTypeormSqliteDatabase implements ITypeormDatabase {
36
+ protected source: DataSource;
37
+ abstract getSource(database?: string): DataSource;
38
+ open<Entity extends ObjectLiteral2>(entity: EntityTarget2<Entity>, database?: string): Promise<Repository2<Entity>>;
39
+ close(database?: string): Promise<void>;
40
+ drop(database?: string): Promise<void>;
41
+ getEntityManager(database?: string): EntityManager;
42
+ }
43
+ import { Exception } from "@ooneex/exception";
44
+ declare class DatabaseException extends Exception {
45
+ constructor(message: string, data?: Record<string, unknown>);
46
+ }
47
+ import { EContainerScope } from "@ooneex/container";
48
+ declare const decorator: {
49
+ database: (scope?: EContainerScope) => (target: DatabaseClassType) => void;
50
+ };
51
+ import { RedisClient as RedisClient2 } from "bun";
52
+ declare class RedisDatabase implements IRedisDatabase {
40
53
  private readonly options;
41
54
  private client;
42
55
  private connectionUrl;
@@ -46,26 +59,22 @@ declare class RedisDatabaseAdapter implements IRedisDatabaseAdapter {
46
59
  close(): Promise<void>;
47
60
  drop(): Promise<void>;
48
61
  }
49
- import { DataSource, EntityManager, EntityTarget as EntityTarget2, ObjectLiteral as ObjectLiteral2, Repository as Repository2 } from "typeorm";
50
- import { PostgresConnectionOptions } from "typeorm/driver/postgres/PostgresConnectionOptions";
51
- declare class TypeormPgDatabaseAdapter implements ITypeormDatabaseAdapter {
52
- private source;
53
- constructor(options: Omit<PostgresConnectionOptions, "type">);
54
- getSource(): DataSource;
55
- open<Entity extends ObjectLiteral2>(entity: EntityTarget2<Entity>): Promise<Repository2<Entity>>;
56
- close(): Promise<void>;
57
- drop(): Promise<void>;
58
- getEntityManager(): EntityManager;
59
- }
60
62
  import { DataSource as DataSource2, EntityManager as EntityManager2, EntityTarget as EntityTarget3, ObjectLiteral as ObjectLiteral3, Repository as Repository3 } from "typeorm";
61
- import { SqliteConnectionOptions } from "typeorm/driver/sqlite/SqliteConnectionOptions";
62
- declare class TypeormSqliteDatabaseAdapter implements ITypeormDatabaseAdapter {
63
+ import { PostgresConnectionOptions } from "typeorm/driver/postgres/PostgresConnectionOptions";
64
+ declare class TypeormPgDatabase implements ITypeormDatabase {
63
65
  private source;
64
- constructor(options: Omit<SqliteConnectionOptions, "type">);
66
+ constructor(options?: Omit<PostgresConnectionOptions, "type">);
65
67
  getSource(): DataSource2;
66
68
  open<Entity extends ObjectLiteral3>(entity: EntityTarget3<Entity>): Promise<Repository3<Entity>>;
67
69
  close(): Promise<void>;
68
70
  drop(): Promise<void>;
69
71
  getEntityManager(): EntityManager2;
70
72
  }
71
- export { TypeormSqliteDatabaseAdapter, TypeormPgDatabaseAdapter, RedisDatabaseAdapter, RedisConnectionOptionsType, ITypeormDatabaseAdapter, IRedisDatabaseAdapter, IDatabase, DatabaseException, DatabaseClassType };
73
+ import { DataSource as DataSource3 } from "typeorm";
74
+ import { SqliteConnectionOptions } from "typeorm/driver/sqlite/SqliteConnectionOptions";
75
+ declare class TypeormSqliteDatabase extends AbstractTypeormSqliteDatabase {
76
+ private readonly options;
77
+ constructor(options?: Omit<SqliteConnectionOptions, "type"> | undefined);
78
+ getSource(database?: string): DataSource3;
79
+ }
80
+ export { decorator, TypeormSqliteDatabase, TypeormPgDatabase, RedisDatabase, RedisConnectionOptionsType, ITypeormDatabase, IRedisDatabase, IDatabase, DatabaseException, DatabaseClassType, AbstractTypeormSqliteDatabase };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{Exception as r}from"@ooneex/exception";import{HttpStatus as a}from"@ooneex/http-status";class i extends r{constructor(t,e={}){super(t,{status:a.Code.InternalServerError,data:e});this.name="DatabaseException"}}var{RedisClient:o}=globalThis.Bun;class s{options;client;connectionUrl;constructor(t={}){this.options=t;if(this.connectionUrl=t.url||Bun.env.DATABASE_REDIS_URL||"",!this.connectionUrl)throw new i("No Redis connection URL provided. The 'url' option must be specified or set the following environment variable: DATABASE_REDIS_URL.",{...t,connectionUrl:this.connectionUrl});this.client=new o(this.connectionUrl,{connectionTimeout:t.connectionTimeout||1e4,idleTimeout:t.idleTimeout||0,autoReconnect:t.autoReconnect??!0,maxRetries:t.maxRetries||10,enableOfflineQueue:t.enableOfflineQueue??!0,enableAutoPipelining:t.enableAutoPipelining??!0,...t.tls!==void 0&&{tls:t.tls}})}getClient(){return this.client}async open(){try{if(!this.client.connected)await this.client.connect();return this.client}catch(t){throw new i(`Failed to open Redis connection: ${t instanceof Error?t.message:String(t)}`,{connectionUrl:this.connectionUrl,options:this.options,error:t})}}async close(){try{if(this.client.connected)this.client.close()}catch(t){throw new i(`Failed to close Redis connection: ${t instanceof Error?t.message:String(t)}`,{connectionUrl:this.connectionUrl,error:t})}}async drop(){try{if(!this.client.connected)await this.open();await this.client.send("FLUSHDB",[])}catch(t){throw new i(`Failed to drop Redis database: ${t instanceof Error?t.message:String(t)}`,{connectionUrl:this.connectionUrl,error:t})}}}import{DataSource as n}from"typeorm";class c{source;constructor(t){let e=(t.url||Bun.env.DATABASE_URL||"").trim();if(!e)throw new i("No database URL provided. Please set DATABASE_URL environment variable or provide a url in the options.",{...t,url:e});this.source=new n({synchronize:!1,entities:[],extra:{max:10},...t,url:e,type:"postgres"})}getSource(){return this.source}async open(t){let e=this.getSource();if(!e.isInitialized)await e.initialize();return e.getRepository(t)}async close(){let t=this.getSource();if(t.isInitialized)await t.destroy()}async drop(){let t=this.getSource();if(t.isInitialized)await t.dropDatabase()}getEntityManager(){return this.getSource().manager}}import{DataSource as p}from"typeorm";class y{source;constructor(t){let e=t.database||Bun.env.SQLITE_DATABASE_PATH||"";if(!e)throw new i("No database path provided. The 'database' option must be specified with a valid file path or ':memory:' for in-memory database. Alternatively, set the SQLITE_DATABASE_PATH environment variable.",{...t,database:e});this.source=new p({synchronize:!1,entities:[],enableWAL:!0,busyErrorRetry:2000,busyTimeout:30000,...t,database:e,type:"sqlite"})}getSource(){return this.source}async open(t){let e=this.getSource();if(!e.isInitialized)await e.initialize();return e.getRepository(t)}async close(){let t=this.getSource();if(t.isInitialized)await t.destroy()}async drop(){let t=this.getSource();if(t.isInitialized)await t.dropDatabase()}getEntityManager(){return this.getSource().manager}}export{y as TypeormSqliteDatabaseAdapter,c as TypeormPgDatabaseAdapter,s as RedisDatabaseAdapter,i as DatabaseException};
2
+ class i{source;async open(t,e){let a=this.getSource(e);if(!a.isInitialized)await a.initialize();return a.getRepository(t)}async close(t){let e=this.getSource(t);if(e.isInitialized)await e.destroy()}async drop(t){let e=this.getSource(t);if(e.isInitialized)await e.dropDatabase()}getEntityManager(t){return this.getSource(t).manager}}import{Exception as c}from"@ooneex/exception";import{HttpStatus as p}from"@ooneex/http-status";class r extends c{constructor(t,e={}){super(t,{status:p.Code.InternalServerError,data:e});this.name="DatabaseException"}}import{container as m,EContainerScope as y}from"@ooneex/container";var D={database:(t=y.Singleton)=>{return(e)=>{m.add(e,t)}}};var{RedisClient:u}=globalThis.Bun;class o{options;client;connectionUrl;constructor(t={}){this.options=t;if(this.connectionUrl=t.url||Bun.env.DATABASE_REDIS_URL||"",!this.connectionUrl)throw new r("No Redis connection URL provided. The 'url' option must be specified or set the following environment variable: DATABASE_REDIS_URL.",{...t,connectionUrl:this.connectionUrl});this.client=new u(this.connectionUrl,{connectionTimeout:t.connectionTimeout||1e4,idleTimeout:t.idleTimeout||0,autoReconnect:t.autoReconnect??!0,maxRetries:t.maxRetries||10,enableOfflineQueue:t.enableOfflineQueue??!0,enableAutoPipelining:t.enableAutoPipelining??!0,...t.tls!==void 0&&{tls:t.tls}})}getClient(){return this.client}async open(){try{if(!this.client.connected)await this.client.connect();return this.client}catch(t){throw new r(`Failed to open Redis connection: ${t instanceof Error?t.message:String(t)}`,{connectionUrl:this.connectionUrl,options:this.options,error:t})}}async close(){try{if(this.client.connected)this.client.close()}catch(t){throw new r(`Failed to close Redis connection: ${t instanceof Error?t.message:String(t)}`,{connectionUrl:this.connectionUrl,error:t})}}async drop(){try{if(!this.client.connected)await this.open();await this.client.send("FLUSHDB",[])}catch(t){throw new r(`Failed to drop Redis database: ${t instanceof Error?t.message:String(t)}`,{connectionUrl:this.connectionUrl,error:t})}}}import{DataSource as l}from"typeorm";class s{source;constructor(t={}){let e=(t.url||Bun.env.DATABASE_URL||"").trim();if(!e)throw new r("No database URL provided. Please set DATABASE_URL environment variable or provide a url in the options.",{...t,url:e});this.source=new l({synchronize:!1,entities:[],extra:{max:10},...t,url:e,type:"postgres"})}getSource(){return this.source}async open(t){let e=this.getSource();if(!e.isInitialized)await e.initialize();return e.getRepository(t)}async close(){let t=this.getSource();if(t.isInitialized)await t.destroy()}async drop(){let t=this.getSource();if(t.isInitialized)await t.dropDatabase()}getEntityManager(){return this.getSource().manager}}import{DataSource as d}from"typeorm";class n extends i{options;constructor(t=void 0){super();this.options=t}getSource(t){if(!t&&this.source)return this.source;if(t=t||this.options?.database||Bun.env.SQLITE_DATABASE_PATH||"",!t)throw new r("No database path provided. The 'database' option must be specified with a valid file path or ':memory:' for in-memory database. Alternatively, set the SQLITE_DATABASE_PATH environment variable.",{...this.options||{},database:t});return this.source=new d({synchronize:!1,entities:[],enableWAL:!0,busyErrorRetry:2000,busyTimeout:30000,...this.options||{},database:t,type:"sqlite"}),this.source}}export{D as decorator,n as TypeormSqliteDatabase,s as TypeormPgDatabase,o as RedisDatabase,r as DatabaseException,i as AbstractTypeormSqliteDatabase};
3
3
 
4
- //# debugId=6EB87EF735D078EC64756E2164756E21
4
+ //# debugId=99403C65D68784B064756E2164756E21
package/dist/index.js.map CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["src/DatabaseException.ts", "src/RedisDatabaseAdapter.ts", "src/TypeormPgDatabaseAdapter.ts", "src/TypeormSqliteDatabaseAdapter.ts"],
3
+ "sources": ["src/AbstractTypeormSqliteDatabase.ts", "src/DatabaseException.ts", "src/decorators.ts", "src/RedisDatabase.ts", "src/TypeormPgDatabase.ts", "src/TypeormSqliteDatabase.ts"],
4
4
  "sourcesContent": [
5
+ "import type { DataSource, EntityManager, EntityTarget, ObjectLiteral, Repository } from \"typeorm\";\nimport type { ITypeormDatabase } from \"./types\";\n\nexport abstract class AbstractTypeormSqliteDatabase implements ITypeormDatabase {\n protected source: DataSource;\n public abstract getSource(database?: string): DataSource;\n\n public async open<Entity extends ObjectLiteral>(\n entity: EntityTarget<Entity>,\n database?: string,\n ): Promise<Repository<Entity>> {\n const source = this.getSource(database);\n\n if (!source.isInitialized) {\n await source.initialize();\n }\n\n return source.getRepository(entity);\n }\n\n public async close(database?: string): Promise<void> {\n const source = this.getSource(database);\n if (source.isInitialized) {\n await source.destroy();\n }\n }\n\n public async drop(database?: string): Promise<void> {\n const source = this.getSource(database);\n if (source.isInitialized) {\n await source.dropDatabase();\n }\n }\n\n public getEntityManager(database?: string): EntityManager {\n return this.getSource(database).manager;\n }\n}\n",
5
6
  "import { Exception } from \"@ooneex/exception\";\nimport { HttpStatus } from \"@ooneex/http-status\";\n\nexport class DatabaseException extends Exception {\n constructor(message: string, data: Record<string, unknown> = {}) {\n super(message, {\n status: HttpStatus.Code.InternalServerError,\n data,\n });\n this.name = \"DatabaseException\";\n }\n}\n",
6
- "import { RedisClient } from \"bun\";\nimport { DatabaseException } from \"./DatabaseException\";\nimport type { IRedisDatabaseAdapter, RedisConnectionOptionsType } from \"./types\";\n\nexport class RedisDatabaseAdapter implements IRedisDatabaseAdapter {\n private client: RedisClient;\n private connectionUrl: string;\n\n constructor(private readonly options: RedisConnectionOptionsType = {}) {\n this.connectionUrl = options.url || Bun.env.DATABASE_REDIS_URL || \"\";\n\n if (!this.connectionUrl) {\n throw new DatabaseException(\n \"No Redis connection URL provided. The 'url' option must be specified or set the following environment variable: DATABASE_REDIS_URL.\",\n {\n ...options,\n connectionUrl: this.connectionUrl,\n },\n );\n }\n\n // Create Redis client with options\n this.client = new RedisClient(this.connectionUrl, {\n connectionTimeout: options.connectionTimeout || 10_000,\n idleTimeout: options.idleTimeout || 0,\n autoReconnect: options.autoReconnect ?? true,\n maxRetries: options.maxRetries || 10,\n enableOfflineQueue: options.enableOfflineQueue ?? true,\n enableAutoPipelining: options.enableAutoPipelining ?? true,\n ...(options.tls !== undefined && { tls: options.tls }),\n });\n }\n\n public getClient(): RedisClient {\n return this.client;\n }\n\n public async open(): Promise<RedisClient> {\n try {\n if (!this.client.connected) {\n await this.client.connect();\n }\n\n return this.client;\n } catch (error) {\n throw new DatabaseException(\n `Failed to open Redis connection: ${error instanceof Error ? error.message : String(error)}`,\n {\n connectionUrl: this.connectionUrl,\n options: this.options,\n error,\n },\n );\n }\n }\n\n public async close(): Promise<void> {\n try {\n if (this.client.connected) {\n this.client.close();\n }\n } catch (error) {\n throw new DatabaseException(\n `Failed to close Redis connection: ${error instanceof Error ? error.message : String(error)}`,\n {\n connectionUrl: this.connectionUrl,\n error,\n },\n );\n }\n }\n\n public async drop(): Promise<void> {\n try {\n if (!this.client.connected) {\n await this.open();\n }\n\n // Use FLUSHDB to clear the current database\n await this.client.send(\"FLUSHDB\", []);\n } catch (error) {\n throw new DatabaseException(\n `Failed to drop Redis database: ${error instanceof Error ? error.message : String(error)}`,\n {\n connectionUrl: this.connectionUrl,\n error,\n },\n );\n }\n }\n}\n",
7
- "import { DataSource, type EntityManager, type EntityTarget, type ObjectLiteral, type Repository } from \"typeorm\";\nimport type { PostgresConnectionOptions } from \"typeorm/driver/postgres/PostgresConnectionOptions.js\";\nimport { DatabaseException } from \"./DatabaseException\";\nimport type { ITypeormDatabaseAdapter } from \"./types\";\n\nexport class TypeormPgDatabaseAdapter implements ITypeormDatabaseAdapter {\n private source: DataSource;\n\n constructor(options: Omit<PostgresConnectionOptions, \"type\">) {\n const url = (options.url || Bun.env.DATABASE_URL || \"\").trim();\n\n if (!url) {\n throw new DatabaseException(\n \"No database URL provided. Please set DATABASE_URL environment variable or provide a url in the options.\",\n {\n ...options,\n url,\n },\n );\n }\n\n this.source = new DataSource({\n synchronize: false,\n entities: [],\n extra: {\n max: 10,\n // idleTimeoutMillis: 30000,\n },\n ...options,\n url,\n type: \"postgres\",\n });\n }\n\n public getSource(): DataSource {\n return this.source;\n }\n\n public async open<Entity extends ObjectLiteral>(entity: EntityTarget<Entity>): Promise<Repository<Entity>> {\n const source = this.getSource();\n\n if (!source.isInitialized) {\n await source.initialize();\n }\n\n return source.getRepository(entity);\n }\n\n public async close(): Promise<void> {\n const source = this.getSource();\n if (source.isInitialized) {\n await source.destroy();\n }\n }\n\n public async drop(): Promise<void> {\n const source = this.getSource();\n if (source.isInitialized) {\n await source.dropDatabase();\n }\n }\n\n public getEntityManager(): EntityManager {\n return this.getSource().manager;\n }\n}\n",
8
- "import { DataSource, type EntityManager, type EntityTarget, type ObjectLiteral, type Repository } from \"typeorm\";\nimport type { SqliteConnectionOptions } from \"typeorm/driver/sqlite/SqliteConnectionOptions.js\";\nimport { DatabaseException } from \"./DatabaseException\";\nimport type { ITypeormDatabaseAdapter } from \"./types\";\n\nexport class TypeormSqliteDatabaseAdapter implements ITypeormDatabaseAdapter {\n private source: DataSource;\n\n constructor(options: Omit<SqliteConnectionOptions, \"type\">) {\n const database = options.database || Bun.env.SQLITE_DATABASE_PATH || \"\";\n\n if (!database) {\n throw new DatabaseException(\n \"No database path provided. The 'database' option must be specified with a valid file path or ':memory:' for in-memory database. Alternatively, set the SQLITE_DATABASE_PATH environment variable.\",\n {\n ...options,\n database,\n },\n );\n }\n\n this.source = new DataSource({\n synchronize: false,\n entities: [],\n enableWAL: true,\n busyErrorRetry: 2000,\n busyTimeout: 30_000,\n ...options,\n database,\n type: \"sqlite\",\n });\n }\n\n public getSource(): DataSource {\n return this.source;\n }\n\n public async open<Entity extends ObjectLiteral>(entity: EntityTarget<Entity>): Promise<Repository<Entity>> {\n const source = this.getSource();\n\n if (!source.isInitialized) {\n await source.initialize();\n }\n\n return source.getRepository(entity);\n }\n\n public async close(): Promise<void> {\n const source = this.getSource();\n if (source.isInitialized) {\n await source.destroy();\n }\n }\n\n public async drop(): Promise<void> {\n const source = this.getSource();\n if (source.isInitialized) {\n await source.dropDatabase();\n }\n }\n\n public getEntityManager(): EntityManager {\n return this.getSource().manager;\n }\n}\n"
7
+ "import { container, EContainerScope } from \"@ooneex/container\";\nimport type { DatabaseClassType } from \"./types\";\n\nexport const decorator = {\n database: (scope: EContainerScope = EContainerScope.Singleton) => {\n return (target: DatabaseClassType): void => {\n container.add(target, scope);\n };\n },\n};\n",
8
+ "import { RedisClient } from \"bun\";\nimport { DatabaseException } from \"./DatabaseException\";\nimport type { IRedisDatabase, RedisConnectionOptionsType } from \"./types\";\n\nexport class RedisDatabase implements IRedisDatabase {\n private client: RedisClient;\n private connectionUrl: string;\n\n constructor(private readonly options: RedisConnectionOptionsType = {}) {\n this.connectionUrl = options.url || Bun.env.DATABASE_REDIS_URL || \"\";\n\n if (!this.connectionUrl) {\n throw new DatabaseException(\n \"No Redis connection URL provided. The 'url' option must be specified or set the following environment variable: DATABASE_REDIS_URL.\",\n {\n ...options,\n connectionUrl: this.connectionUrl,\n },\n );\n }\n\n // Create Redis client with options\n this.client = new RedisClient(this.connectionUrl, {\n connectionTimeout: options.connectionTimeout || 10_000,\n idleTimeout: options.idleTimeout || 0,\n autoReconnect: options.autoReconnect ?? true,\n maxRetries: options.maxRetries || 10,\n enableOfflineQueue: options.enableOfflineQueue ?? true,\n enableAutoPipelining: options.enableAutoPipelining ?? true,\n ...(options.tls !== undefined && { tls: options.tls }),\n });\n }\n\n public getClient(): RedisClient {\n return this.client;\n }\n\n public async open(): Promise<RedisClient> {\n try {\n if (!this.client.connected) {\n await this.client.connect();\n }\n\n return this.client;\n } catch (error) {\n throw new DatabaseException(\n `Failed to open Redis connection: ${error instanceof Error ? error.message : String(error)}`,\n {\n connectionUrl: this.connectionUrl,\n options: this.options,\n error,\n },\n );\n }\n }\n\n public async close(): Promise<void> {\n try {\n if (this.client.connected) {\n this.client.close();\n }\n } catch (error) {\n throw new DatabaseException(\n `Failed to close Redis connection: ${error instanceof Error ? error.message : String(error)}`,\n {\n connectionUrl: this.connectionUrl,\n error,\n },\n );\n }\n }\n\n public async drop(): Promise<void> {\n try {\n if (!this.client.connected) {\n await this.open();\n }\n\n // Use FLUSHDB to clear the current database\n await this.client.send(\"FLUSHDB\", []);\n } catch (error) {\n throw new DatabaseException(\n `Failed to drop Redis database: ${error instanceof Error ? error.message : String(error)}`,\n {\n connectionUrl: this.connectionUrl,\n error,\n },\n );\n }\n }\n}\n",
9
+ "import { DataSource, type EntityManager, type EntityTarget, type ObjectLiteral, type Repository } from \"typeorm\";\nimport type { PostgresConnectionOptions } from \"typeorm/driver/postgres/PostgresConnectionOptions.js\";\nimport { DatabaseException } from \"./DatabaseException\";\nimport type { ITypeormDatabase } from \"./types\";\n\nexport class TypeormPgDatabase implements ITypeormDatabase {\n private source: DataSource;\n\n constructor(options: Omit<PostgresConnectionOptions, \"type\"> = {}) {\n const url = (options.url || Bun.env.DATABASE_URL || \"\").trim();\n\n if (!url) {\n throw new DatabaseException(\n \"No database URL provided. Please set DATABASE_URL environment variable or provide a url in the options.\",\n {\n ...options,\n url,\n },\n );\n }\n\n this.source = new DataSource({\n synchronize: false,\n entities: [],\n extra: {\n max: 10,\n // idleTimeoutMillis: 30000,\n },\n ...options,\n url,\n type: \"postgres\",\n });\n }\n\n public getSource(): DataSource {\n return this.source;\n }\n\n public async open<Entity extends ObjectLiteral>(entity: EntityTarget<Entity>): Promise<Repository<Entity>> {\n const source = this.getSource();\n\n if (!source.isInitialized) {\n await source.initialize();\n }\n\n return source.getRepository(entity);\n }\n\n public async close(): Promise<void> {\n const source = this.getSource();\n if (source.isInitialized) {\n await source.destroy();\n }\n }\n\n public async drop(): Promise<void> {\n const source = this.getSource();\n if (source.isInitialized) {\n await source.dropDatabase();\n }\n }\n\n public getEntityManager(): EntityManager {\n return this.getSource().manager;\n }\n}\n",
10
+ "import { DataSource } from \"typeorm\";\nimport type { SqliteConnectionOptions } from \"typeorm/driver/sqlite/SqliteConnectionOptions.js\";\nimport { AbstractTypeormSqliteDatabase } from \"./AbstractTypeormSqliteDatabase\";\nimport { DatabaseException } from \"./DatabaseException\";\n\nexport class TypeormSqliteDatabase extends AbstractTypeormSqliteDatabase {\n constructor(private readonly options: Omit<SqliteConnectionOptions, \"type\"> | undefined = undefined) {\n super();\n }\n\n public getSource(database?: string): DataSource {\n if (!database && this.source) {\n return this.source;\n }\n\n database = database || this.options?.database || Bun.env.SQLITE_DATABASE_PATH || \"\";\n\n if (!database) {\n throw new DatabaseException(\n \"No database path provided. The 'database' option must be specified with a valid file path or ':memory:' for in-memory database. Alternatively, set the SQLITE_DATABASE_PATH environment variable.\",\n {\n ...(this.options || {}),\n database,\n },\n );\n }\n\n this.source = new DataSource({\n synchronize: false,\n entities: [],\n enableWAL: true,\n busyErrorRetry: 2000,\n busyTimeout: 30_000,\n ...(this.options || {}),\n database,\n type: \"sqlite\",\n });\n\n return this.source;\n }\n}\n"
9
11
  ],
10
- "mappings": "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",
11
- "debugId": "6EB87EF735D078EC64756E2164756E21",
12
+ "mappings": "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",
13
+ "debugId": "99403C65D68784B064756E2164756E21",
12
14
  "names": []
13
15
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ooneex/database",
3
- "description": "",
4
- "version": "0.0.1",
3
+ "description": "Database connection and management utilities with TypeORM integration for Ooneex applications",
4
+ "version": "0.0.4",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist",
@@ -25,15 +25,23 @@
25
25
  "test": "bun test tests",
26
26
  "build": "bunup",
27
27
  "lint": "tsgo --noEmit && bunx biome lint",
28
- "publish:prod": "bun publish --tolerate-republish --access public",
29
- "publish:pack": "bun pm pack --destination ./dist",
30
- "publish:dry": "bun publish --dry-run"
28
+ "publish": "bun publish --access public || true"
31
29
  },
32
30
  "dependencies": {
31
+ "@ooneex/container": "0.0.2",
33
32
  "@ooneex/exception": "0.0.1",
34
33
  "@ooneex/http-status": "0.0.1",
35
34
  "typeorm": "^0.3.27"
36
35
  },
37
- "peerDependencies": {},
38
- "devDependencies": {}
36
+ "devDependencies": {},
37
+ "keywords": [
38
+ "bun",
39
+ "database",
40
+ "db",
41
+ "ooneex",
42
+ "query",
43
+ "sql",
44
+ "typeorm",
45
+ "typescript"
46
+ ]
39
47
  }
Binary file