bunql 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 BunQL
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,502 @@
1
+ # BunQL
2
+
3
+ A fluent SQL query builder for Bun with transaction support, built on top of Bun's native SQL bindings.
4
+
5
+ ## Features
6
+
7
+ - ๐Ÿš€ **Fluent API**: Chainable methods for building SQL queries
8
+ - โšก **Auto-Execute**: Queries automatically execute when awaited (no `.execute()` needed!)
9
+ - ๐Ÿ”„ **Transaction Support**: Built-in transaction handling with rollback support
10
+ - ๐Ÿ›ก๏ธ **SQL Injection Protection**: Parameterized queries prevent SQL injection
11
+ - ๐Ÿ—„๏ธ **Multi-Database Support**: Works with PostgreSQL, MySQL, and SQLite
12
+ - ๐Ÿ“ฆ **Zero Dependencies**: Built on Bun's native SQL bindings
13
+ - ๐Ÿงช **TypeScript Support**: Full TypeScript support with type safety
14
+ - ๐Ÿ—๏ธ **Schema Management**: Complete database schema creation and management API
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ bun add bunql
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { BunQL, createQueryBuilder } from 'bunql';
26
+
27
+ // Create a BunQL instance
28
+ const db = new BunQL('postgres://user:pass@localhost:5432/mydb');
29
+ // or use the factory function
30
+ const db = createQueryBuilder('sqlite://myapp.db');
31
+ ```
32
+
33
+ ## Auto-Execute Feature
34
+
35
+ BunQL queries automatically execute when awaited, eliminating the need to call `.execute()`:
36
+
37
+ ```typescript
38
+ // โœ… Auto-executes when awaited
39
+ const users = await db.select('*').from('users');
40
+
41
+ // โœ… Also works with complex queries
42
+ const result = await db.update('users')
43
+ .set({ active: false })
44
+ .where('id', '=', 1);
45
+
46
+ // โœ… You can still use .execute() if you prefer explicit execution
47
+ const users = await db.select('*').from('users').execute();
48
+ ```
49
+
50
+ This makes the API more concise and intuitive while maintaining backward compatibility.
51
+
52
+ ## Usage Examples
53
+
54
+ ### Select Queries
55
+
56
+ ```typescript
57
+ // Basic select (auto-executes when awaited)
58
+ const users = await db.select('*').from('users');
59
+
60
+ // Select with specific columns
61
+ const users = await db.select(['id', 'name', 'email']).from('users');
62
+
63
+ // Select with where clause
64
+ const user = await db.select('*')
65
+ .from('users')
66
+ .where('id', '=', 1)
67
+ .first();
68
+
69
+ // Select with multiple conditions
70
+ const activeUsers = await db.select('*')
71
+ .from('users')
72
+ .where('active', '=', true)
73
+ .where('role', '=', 'admin');
74
+
75
+ // Select with IN clause
76
+ const users = await db.select('*')
77
+ .from('users')
78
+ .whereIn('id', [1, 2, 3]);
79
+
80
+ // Select with ordering and pagination
81
+ const users = await db.select('*')
82
+ .from('users')
83
+ .orderBy('name', 'ASC')
84
+ .limit(10)
85
+ .offset(20);
86
+
87
+ // You can still use .execute() if you prefer explicit execution
88
+ const users = await db.select('*').from('users').execute();
89
+ ```
90
+
91
+ ### Insert Queries
92
+
93
+ ```typescript
94
+ // Single insert (auto-executes when awaited)
95
+ const result = await db.insert('users')
96
+ .values({
97
+ name: 'John Doe',
98
+ email: 'john@example.com',
99
+ active: true
100
+ });
101
+
102
+ console.log('Inserted ID:', result.lastInsertRowid);
103
+
104
+ // Bulk insert
105
+ const users = [
106
+ { name: 'Alice', email: 'alice@example.com' },
107
+ { name: 'Bob', email: 'bob@example.com' }
108
+ ];
109
+
110
+ await db.insert('users').values(users);
111
+ ```
112
+
113
+ ### Update Queries
114
+
115
+ ```typescript
116
+ // Update with where clause (auto-executes when awaited)
117
+ const result = await db.update('users')
118
+ .set({
119
+ name: 'John Smith',
120
+ email: 'johnsmith@example.com'
121
+ })
122
+ .where('id', '=', 1);
123
+
124
+ console.log('Updated rows:', result.affectedRows);
125
+
126
+ // Update multiple fields
127
+ await db.update('users')
128
+ .set({
129
+ active: false,
130
+ updated_at: new Date()
131
+ })
132
+ .where('last_login', '<', new Date('2023-01-01'));
133
+ ```
134
+
135
+ ### Delete Queries
136
+
137
+ ```typescript
138
+ // Delete with where clause (auto-executes when awaited)
139
+ const result = await db.delete('users')
140
+ .where('id', '=', 1);
141
+
142
+ console.log('Deleted rows:', result.affectedRows);
143
+
144
+ // Delete with multiple conditions
145
+ await db.delete('users')
146
+ .where('active', '=', false)
147
+ .where('created_at', '<', new Date('2023-01-01'));
148
+ ```
149
+
150
+ ### Raw SQL Queries
151
+
152
+ ```typescript
153
+ // Execute raw SQL
154
+ await db.run('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
155
+
156
+ // Execute with parameters
157
+ await db.run('UPDATE users SET name = ? WHERE id = ?', ['John', 1]);
158
+
159
+ // Get all results
160
+ const users = await db.all('SELECT * FROM users WHERE active = ?', [true]);
161
+
162
+ // Get single result
163
+ const user = await db.get('SELECT * FROM users WHERE id = ?', [1]);
164
+ ```
165
+
166
+ ### Transactions
167
+
168
+ ```typescript
169
+ // Transaction with automatic rollback on error
170
+ const result = await db.begin(async (tx) => {
171
+ // Insert user
172
+ const userResult = await tx.insert('users')
173
+ .values({ name: 'John', email: 'john@example.com' })
174
+ .execute();
175
+
176
+ // Insert user profile
177
+ await tx.insert('user_profiles')
178
+ .values({
179
+ user_id: userResult.lastInsertRowid,
180
+ bio: 'Software developer'
181
+ })
182
+ .execute();
183
+
184
+ // Update user status
185
+ await tx.update('users')
186
+ .set({ active: true })
187
+ .where('id', '=', userResult.lastInsertRowid)
188
+ .execute();
189
+
190
+ return userResult.lastInsertRowid;
191
+ });
192
+
193
+ console.log('Transaction completed, user ID:', result);
194
+ ```
195
+
196
+ ## Schema Management
197
+
198
+ BunQL provides a comprehensive schema management API that can replace Bunely for database schema operations.
199
+
200
+ ### Creating Tables
201
+
202
+ ```typescript
203
+ // Create a table with columns, indexes, and foreign keys
204
+ await db.schema.createTable('users')
205
+ .addColumn({
206
+ name: 'id',
207
+ type: 'INTEGER',
208
+ primaryKey: true,
209
+ autoIncrement: true
210
+ })
211
+ .addColumn({
212
+ name: 'name',
213
+ type: 'TEXT',
214
+ notNull: true
215
+ })
216
+ .addColumn({
217
+ name: 'email',
218
+ type: 'TEXT',
219
+ unique: true,
220
+ notNull: true
221
+ })
222
+ .addColumn({
223
+ name: 'age',
224
+ type: 'INTEGER',
225
+ defaultValue: 0
226
+ })
227
+ .addIndex({
228
+ name: 'idx_users_email',
229
+ columns: ['email'],
230
+ unique: true
231
+ })
232
+ .execute();
233
+
234
+ // Create table with foreign key constraints
235
+ await db.schema.createTable('posts')
236
+ .addColumn({
237
+ name: 'id',
238
+ type: 'INTEGER',
239
+ primaryKey: true,
240
+ autoIncrement: true
241
+ })
242
+ .addColumn({
243
+ name: 'title',
244
+ type: 'TEXT',
245
+ notNull: true
246
+ })
247
+ .addColumn({
248
+ name: 'user_id',
249
+ type: 'INTEGER',
250
+ notNull: true
251
+ })
252
+ .addForeignKey({
253
+ name: 'fk_posts_user_id',
254
+ columns: ['user_id'],
255
+ referencedTable: 'users',
256
+ referencedColumns: ['id'],
257
+ onDelete: 'CASCADE'
258
+ })
259
+ .execute();
260
+ ```
261
+
262
+ ### Altering Tables
263
+
264
+ ```typescript
265
+ // Add a new column
266
+ await db.schema.alterTable('users')
267
+ .addColumn({
268
+ name: 'bio',
269
+ type: 'TEXT',
270
+ notNull: false
271
+ })
272
+ .execute();
273
+
274
+ // Rename a column
275
+ await db.schema.alterTable('posts')
276
+ .renameColumn('content', 'body')
277
+ .execute();
278
+
279
+ // Add an index
280
+ await db.schema.alterTable('users')
281
+ .addIndex({
282
+ name: 'idx_users_age',
283
+ columns: ['age'],
284
+ unique: false
285
+ })
286
+ .execute();
287
+
288
+ // Drop a column
289
+ await db.schema.alterTable('users')
290
+ .dropColumn('old_column')
291
+ .execute();
292
+ ```
293
+
294
+ ### Schema Introspection
295
+
296
+ ```typescript
297
+ // Check if a table exists
298
+ const exists = await db.schema.hasTable('users');
299
+
300
+ // Get all tables
301
+ const tables = await db.schema.getTables();
302
+
303
+ // Get table information
304
+ const tableInfo = await db.schema.getTableInfo('users');
305
+ console.log(tableInfo);
306
+ // [
307
+ // { name: 'id', type: 'INTEGER', notNull: true, primaryKey: true, ... },
308
+ // { name: 'name', type: 'TEXT', notNull: true, primaryKey: false, ... }
309
+ // ]
310
+
311
+ // Get indexes for a table
312
+ const indexes = await db.schema.getIndexes('users');
313
+
314
+ // Get foreign keys for a table
315
+ const foreignKeys = await db.schema.getForeignKeys('posts');
316
+
317
+ // Get complete table information
318
+ const completeInfo = await db.schema.getCompleteTableInfo('users');
319
+ ```
320
+
321
+ ### Index Management
322
+
323
+ ```typescript
324
+ // Create an index
325
+ await db.schema.createIndex('users', {
326
+ name: 'idx_users_name_email',
327
+ columns: ['name', 'email'],
328
+ unique: false
329
+ });
330
+
331
+ // Make columns unique
332
+ await db.schema.makeColumnsUnique('posts', ['title', 'user_id']);
333
+
334
+ // Drop an index
335
+ await db.schema.dropIndex('idx_users_email');
336
+ ```
337
+
338
+ ### Table Management
339
+
340
+ ```typescript
341
+ // Drop a table
342
+ await db.schema.dropTable('old_table');
343
+
344
+ // Drop table if exists
345
+ await db.schema.dropTable('old_table', true);
346
+ ```
347
+
348
+ ### Complex Queries
349
+
350
+ ```typescript
351
+ // Complex select with joins (using raw SQL)
352
+ const usersWithProfiles = await db.all(`
353
+ SELECT u.*, p.bio, p.avatar
354
+ FROM users u
355
+ LEFT JOIN user_profiles p ON u.id = p.user_id
356
+ WHERE u.active = ?
357
+ ORDER BY u.created_at DESC
358
+ LIMIT ?
359
+ `, [true, 10]);
360
+
361
+ // Using the query builder for complex conditions
362
+ const recentUsers = await db.select(['u.id', 'u.name', 'u.email', 'p.bio'])
363
+ .from('users u')
364
+ .where('u.active', '=', true)
365
+ .where('u.created_at', '>', new Date('2023-01-01'))
366
+ .whereIn('u.role', ['admin', 'user'])
367
+ .orderBy('u.created_at', 'DESC')
368
+ .limit(50)
369
+ .execute();
370
+ ```
371
+
372
+ ## Database Support
373
+
374
+ BunQL works with all databases supported by Bun's native SQL bindings:
375
+
376
+ - **PostgreSQL**: `postgres://user:pass@localhost:5432/db`
377
+ - **MySQL**: `mysql://user:pass@localhost:3306/db`
378
+ - **SQLite**: `sqlite://path/to/database.db` or `:memory:`
379
+
380
+ ## API Reference
381
+
382
+ ### BunQL
383
+
384
+ Main class for building and executing queries.
385
+
386
+ #### Methods
387
+
388
+ - `select(columns?)`: Create a SELECT query
389
+ - `insert(table)`: Create an INSERT query
390
+ - `update(table)`: Create an UPDATE query
391
+ - `delete(table)`: Create a DELETE query
392
+ - `run(query, params?)`: Execute raw SQL
393
+ - `all(query, params?)`: Execute raw SQL and return all results
394
+ - `get(query, params?)`: Execute raw SQL and return first result
395
+ - `begin(callback)`: Execute queries in a transaction
396
+
397
+ ### SelectQuery
398
+
399
+ Methods for building SELECT queries.
400
+
401
+ #### Methods
402
+
403
+ - `from(table)`: Specify the table to select from
404
+ - `where(column, operator, value)`: Add WHERE condition
405
+ - `whereIn(column, values)`: Add WHERE IN condition
406
+ - `whereNotIn(column, values)`: Add WHERE NOT IN condition
407
+ - `orderBy(column, direction?)`: Add ORDER BY clause
408
+ - `limit(count)`: Add LIMIT clause
409
+ - `offset(count)`: Add OFFSET clause
410
+ - `execute()`: Execute the query and return results
411
+ - `first()`: Execute the query and return first result
412
+ - `all()`: Alias for `execute()`
413
+
414
+ ### InsertQuery
415
+
416
+ Methods for building INSERT queries.
417
+
418
+ #### Methods
419
+
420
+ - `values(data)`: Specify values to insert (object or array)
421
+ - `execute()`: Execute the insert query
422
+
423
+ ### UpdateQuery
424
+
425
+ Methods for building UPDATE queries.
426
+
427
+ #### Methods
428
+
429
+ - `set(data)`: Specify values to update
430
+ - `where(column, operator, value)`: Add WHERE condition
431
+ - `execute()`: Execute the update query
432
+
433
+ ### DeleteQuery
434
+
435
+ Methods for building DELETE queries.
436
+
437
+ #### Methods
438
+
439
+ - `where(column, operator, value)`: Add WHERE condition
440
+ - `execute()`: Execute the delete query
441
+
442
+ ## Error Handling
443
+
444
+ ```typescript
445
+ try {
446
+ const result = await db.insert('users')
447
+ .values({ email: 'duplicate@example.com' })
448
+ .execute();
449
+ } catch (error) {
450
+ if (error.code === 'ER_DUP_ENTRY') {
451
+ console.log('Duplicate email detected');
452
+ } else {
453
+ console.error('Database error:', error.message);
454
+ }
455
+ }
456
+ ```
457
+
458
+ ## Performance Tips
459
+
460
+ 1. **Use transactions for bulk operations**:
461
+ ```typescript
462
+ await db.begin(async (tx) => {
463
+ for (const user of users) {
464
+ await tx.insert('users').values(user).execute();
465
+ }
466
+ });
467
+ ```
468
+
469
+ 2. **Use prepared statements** (automatic with parameterized queries):
470
+ ```typescript
471
+ // This automatically uses prepared statements
472
+ const user = await db.select('*')
473
+ .from('users')
474
+ .where('id', '=', userId)
475
+ .first();
476
+ ```
477
+
478
+ 3. **Use bulk inserts for multiple records**:
479
+ ```typescript
480
+ await db.insert('users').values(usersArray).execute();
481
+ ```
482
+
483
+ ## Testing
484
+
485
+ ```bash
486
+ # Run tests
487
+ bun test
488
+
489
+ # Run tests in watch mode
490
+ bun test --watch
491
+
492
+ # Type checking
493
+ bun run typecheck
494
+ ```
495
+
496
+ ## License
497
+
498
+ MIT
499
+
500
+ ## Contributing
501
+
502
+ Contributions are welcome! Please feel free to submit a Pull Request.
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "bunql",
3
+ "version": "1.0.0",
4
+ "description": "A fluent SQL query builder for Bun with transaction support",
5
+ "module": "src/index.ts",
6
+ "type": "module",
7
+ "main": "src/index.ts",
8
+ "types": "src/index.ts",
9
+ "keywords": [
10
+ "bun",
11
+ "sql",
12
+ "query-builder",
13
+ "database",
14
+ "postgresql",
15
+ "mysql",
16
+ "sqlite",
17
+ "transactions"
18
+ ],
19
+ "author": "Your Name",
20
+ "license": "MIT",
21
+ "scripts": {
22
+ "test": "bun test",
23
+ "test:watch": "bun test --watch",
24
+ "build": "bun build src/index.ts --outdir dist --target bun",
25
+ "dev": "bun run src/index.ts",
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "devDependencies": {
29
+ "@types/bun": "latest",
30
+ "typescript": "^5"
31
+ },
32
+ "peerDependencies": {
33
+ "bun-types": "*"
34
+ },
35
+ "files": [
36
+ "src/**/*",
37
+ "README.md",
38
+ "LICENSE"
39
+ ],
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/yourusername/bunql.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/yourusername/bunql/issues"
46
+ },
47
+ "homepage": "https://github.com/yourusername/bunql#readme"
48
+ }