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 +21 -0
- package/README.md +502 -0
- package/package.json +48 -0
- package/src/index.test.ts +271 -0
- package/src/index.ts +395 -0
- package/src/schema.test.ts +402 -0
- package/src/schema.ts +576 -0
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
|
+
}
|