@forgebase/sdk 0.0.1 → 0.0.2
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 +88 -299
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,64 +1,27 @@
|
|
|
1
1
|
# ForgeBase TypeScript SDK
|
|
2
2
|
|
|
3
|
-
A powerful, type-safe TypeScript SDK for interacting with ForgeBase services, providing comprehensive database operations,
|
|
3
|
+
A powerful, type-safe TypeScript SDK for interacting with ForgeBase services, providing comprehensive database operations, advanced query capabilities, and type safety.
|
|
4
4
|
|
|
5
5
|
## Core Features
|
|
6
6
|
|
|
7
7
|
- **Type-Safe Query Builder**:
|
|
8
8
|
- Fluent API design
|
|
9
|
-
- Advanced filtering
|
|
9
|
+
- Advanced filtering (`where`, `whereIn`, `whereExists`, etc.)
|
|
10
10
|
- Complex joins and relations
|
|
11
|
-
- Aggregations
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
11
|
+
- Aggregations (`count`, `sum`, `avg`, `min`, `max`)
|
|
12
|
+
- Window functions (`rowNumber`, `rank`, `lag`, `lead`)
|
|
13
|
+
- Recursive CTEs
|
|
14
|
+
- Result transformations (`pivot`, `compute`)
|
|
15
15
|
|
|
16
16
|
- **Database Operations**:
|
|
17
|
-
- CRUD operations
|
|
17
|
+
- CRUD operations (`create`, `update`, `delete`)
|
|
18
18
|
- Batch operations
|
|
19
|
-
- Pagination
|
|
20
|
-
- Sorting
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
- **Security Features**:
|
|
26
|
-
|
|
27
|
-
<!-- - JWKS support
|
|
28
|
-
- Token validation
|
|
29
|
-
- Request signing -->
|
|
30
|
-
- Input sanitization
|
|
31
|
-
- Type validation
|
|
32
|
-
- Error boundaries
|
|
33
|
-
<!-- - Rate limiting -->
|
|
34
|
-
|
|
35
|
-
- **Advanced Querying**:
|
|
36
|
-
- Window functions
|
|
37
|
-
- Common Table Expressions (CTEs)
|
|
38
|
-
- Recursive queries
|
|
39
|
-
- Complex filtering
|
|
40
|
-
- Advanced joins
|
|
41
|
-
- Subqueries
|
|
42
|
-
- Aggregations
|
|
43
|
-
|
|
44
|
-
<!-- - **Real-time Features**:
|
|
45
|
-
|
|
46
|
-
- Live queries
|
|
47
|
-
- Change notifications
|
|
48
|
-
- WebSocket integration
|
|
49
|
-
- Presence tracking
|
|
50
|
-
- Subscription management
|
|
51
|
-
- Connection handling
|
|
52
|
-
- Event buffering -->
|
|
53
|
-
|
|
54
|
-
<!-- - **Performance Features**:
|
|
55
|
-
- Query caching
|
|
56
|
-
- Connection pooling
|
|
57
|
-
- Batch operations
|
|
58
|
-
- Lazy loading
|
|
59
|
-
- Query optimization
|
|
60
|
-
- Result transformation
|
|
61
|
-
- Memory management -->
|
|
19
|
+
- Pagination (`limit`, `offset`)
|
|
20
|
+
- Sorting (`orderBy`)
|
|
21
|
+
|
|
22
|
+
- **Security & Validation**:
|
|
23
|
+
- Input validation
|
|
24
|
+
- Type inference from your interfaces
|
|
62
25
|
|
|
63
26
|
## Installation
|
|
64
27
|
|
|
@@ -72,35 +35,59 @@ pnpm add @forgebase/sdk
|
|
|
72
35
|
|
|
73
36
|
## Basic Usage
|
|
74
37
|
|
|
75
|
-
###
|
|
38
|
+
### Initialization
|
|
39
|
+
|
|
40
|
+
First, define your database schema and initialize the SDK. This provides automatic type safety across your entire application.
|
|
76
41
|
|
|
77
42
|
```typescript
|
|
78
43
|
import { DatabaseSDK } from '@forgebase/sdk/client';
|
|
79
44
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
45
|
+
// 1. Define your entity types
|
|
46
|
+
interface User {
|
|
47
|
+
id: number;
|
|
48
|
+
name: string;
|
|
49
|
+
email: string;
|
|
50
|
+
role: 'admin' | 'user';
|
|
51
|
+
status: 'active' | 'inactive';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface Order {
|
|
55
|
+
id: number;
|
|
56
|
+
user_id: number;
|
|
57
|
+
amount: number;
|
|
58
|
+
status: 'pending' | 'completed';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 2. Define your Database Schema
|
|
62
|
+
interface Schema {
|
|
63
|
+
users: User;
|
|
64
|
+
orders: Order;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 3. Initialize the SDK with your Schema
|
|
68
|
+
const db = new DatabaseSDK<Schema>({
|
|
69
|
+
baseUrl: 'http://localhost:3000',
|
|
70
|
+
axiosConfig: {
|
|
71
|
+
withCredentials: true,
|
|
72
|
+
headers: { 'Content-Type': 'application/json' },
|
|
85
73
|
},
|
|
86
74
|
});
|
|
75
|
+
```
|
|
87
76
|
|
|
88
|
-
|
|
89
|
-
const users = await db
|
|
90
|
-
.table('users')
|
|
91
|
-
.select('id', 'name', 'email')
|
|
92
|
-
.where('status', 'active')
|
|
93
|
-
.execute({
|
|
94
|
-
headers: {
|
|
95
|
-
'some-stuff': 'true',
|
|
96
|
-
},
|
|
97
|
-
});
|
|
77
|
+
### Database Operations
|
|
98
78
|
|
|
99
|
-
|
|
79
|
+
The SDK automatically infers types based on your Schema.
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Query users (Type is inferred as User[])
|
|
83
|
+
const users = await db.table('users').select('id', 'name', 'email').where('status', 'active').query();
|
|
84
|
+
|
|
85
|
+
// Create a new record (Type checking ensures payload matches User)
|
|
100
86
|
const newUser = await db.table('users').create({
|
|
101
87
|
name: 'John Doe',
|
|
102
88
|
email: 'john@example.com',
|
|
103
89
|
role: 'user',
|
|
90
|
+
status: 'active',
|
|
104
91
|
});
|
|
105
92
|
|
|
106
93
|
// Update a record
|
|
@@ -115,74 +102,33 @@ await db.table('users').delete(1);
|
|
|
115
102
|
### Advanced Queries
|
|
116
103
|
|
|
117
104
|
```typescript
|
|
118
|
-
// Complex filtering
|
|
119
|
-
interface User {
|
|
120
|
-
id: number;
|
|
121
|
-
name: string;
|
|
122
|
-
email: string;
|
|
123
|
-
role: string;
|
|
124
|
-
department: string;
|
|
125
|
-
salary: number;
|
|
126
|
-
}
|
|
127
|
-
|
|
105
|
+
// Complex filtering
|
|
128
106
|
const results = await db
|
|
129
|
-
.table
|
|
107
|
+
.table('users') // Type inferred automatically
|
|
130
108
|
.where('status', 'active')
|
|
131
109
|
.andWhere((query) => {
|
|
132
|
-
query.where('role', '
|
|
110
|
+
query.where('role', 'admin').orWhere('email', 'like', '%@company.com');
|
|
133
111
|
})
|
|
134
112
|
.orderBy('name', 'asc')
|
|
135
113
|
.limit(10)
|
|
136
|
-
.
|
|
114
|
+
.query();
|
|
137
115
|
|
|
138
116
|
// Aggregations
|
|
139
|
-
const stats = await db.table
|
|
117
|
+
const stats = await db.table('orders').groupBy('status').sum('amount', 'total_amount').count('id', 'order_count').having('total_amount', '>', 5000).query();
|
|
118
|
+
|
|
119
|
+
// Result type is narrowed:
|
|
120
|
+
// stats.data -> { status: string, total_amount: number, order_count: number }[]
|
|
140
121
|
|
|
141
122
|
// Window Functions
|
|
142
123
|
const rankedUsers = await db
|
|
143
|
-
.table
|
|
124
|
+
.table('users')
|
|
144
125
|
.select('name', 'department', 'salary')
|
|
145
|
-
.
|
|
146
|
-
|
|
147
|
-
orderBy: [{ field: 'salary', direction: 'desc' }],
|
|
148
|
-
})
|
|
149
|
-
.execute();
|
|
150
|
-
|
|
151
|
-
// Advanced Window Functions
|
|
152
|
-
const analysis = await db
|
|
153
|
-
.table<User>('users')
|
|
154
|
-
.windowAdvanced('sum', 'running_total', {
|
|
155
|
-
field: 'salary',
|
|
156
|
-
over: {
|
|
157
|
-
partitionBy: ['department'],
|
|
158
|
-
orderBy: [{ field: 'hire_date', direction: 'asc' }],
|
|
159
|
-
frame: {
|
|
160
|
-
type: 'ROWS',
|
|
161
|
-
start: 'UNBOUNDED PRECEDING',
|
|
162
|
-
end: 'CURRENT ROW',
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
})
|
|
166
|
-
.execute();
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### CTEs and Recursive Queries
|
|
170
|
-
|
|
171
|
-
```typescript
|
|
172
|
-
// Simple CTE
|
|
173
|
-
const highPaidUsers = db.table<User>('users').where('salary', '>', 100000);
|
|
174
|
-
|
|
175
|
-
const result = await db.table<User>('users').with('high_paid', highPaidUsers).execute();
|
|
176
|
-
|
|
177
|
-
// Recursive CTE
|
|
178
|
-
interface Category {
|
|
179
|
-
id: number;
|
|
180
|
-
parent_id: number | null;
|
|
181
|
-
name: string;
|
|
182
|
-
}
|
|
126
|
+
.rank('salary_rank', ['department'], [{ field: 'salary', direction: 'desc' }])
|
|
127
|
+
.query();
|
|
183
128
|
|
|
129
|
+
// Recursive CTEs (e.g., for hierarchical data)
|
|
184
130
|
const categories = await db
|
|
185
|
-
.table
|
|
131
|
+
.table('categories')
|
|
186
132
|
.withRecursive(
|
|
187
133
|
'category_tree',
|
|
188
134
|
// Initial query
|
|
@@ -191,199 +137,42 @@ const categories = await db
|
|
|
191
137
|
db.table('categories').join('category_tree', 'parent_id', 'id'),
|
|
192
138
|
{ unionAll: true },
|
|
193
139
|
)
|
|
194
|
-
.
|
|
140
|
+
.query();
|
|
195
141
|
```
|
|
196
142
|
|
|
197
|
-
|
|
143
|
+
### Transformations
|
|
198
144
|
|
|
199
|
-
|
|
200
|
-
// Subscribe to changes
|
|
201
|
-
const unsubscribe = await db
|
|
202
|
-
.table<User>('users')
|
|
203
|
-
.where('department', 'IT')
|
|
204
|
-
.subscribe({
|
|
205
|
-
onAdd: (user) => console.log('New user:', user),
|
|
206
|
-
onChange: (user) => console.log('User updated:', user),
|
|
207
|
-
onDelete: (id) => console.log('User deleted:', id),
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Later: cleanup subscription
|
|
211
|
-
unsubscribe();
|
|
212
|
-
``` -->
|
|
213
|
-
|
|
214
|
-
<!-- ### Security Features
|
|
145
|
+
You can transform the result set on the client side using `pivot` or `compute`.
|
|
215
146
|
|
|
216
147
|
```typescript
|
|
217
|
-
//
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
signRequests: true,
|
|
230
|
-
privateKey: 'your-private-key',
|
|
231
|
-
keyId: 'your-key-id',
|
|
232
|
-
},
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
// Rate Limiting
|
|
236
|
-
const db = new DatabaseSDK('http://localhost:3000', {
|
|
237
|
-
rateLimit: {
|
|
238
|
-
maxRequests: 100,
|
|
239
|
-
windowMs: 60000, // 1 minute
|
|
240
|
-
},
|
|
241
|
-
});
|
|
242
|
-
``` -->
|
|
148
|
+
// Pivot data
|
|
149
|
+
const pivoted = await db.table('sales').pivot('month', ['Jan', 'Feb', 'Mar'], { type: 'sum', field: 'amount' }).query();
|
|
150
|
+
|
|
151
|
+
// Compute new fields
|
|
152
|
+
const computed = await db
|
|
153
|
+
.table('employees')
|
|
154
|
+
.compute({
|
|
155
|
+
fullName: (row) => `${row.firstName} ${row.lastName}`,
|
|
156
|
+
tax: (row) => row.salary * 0.2,
|
|
157
|
+
})
|
|
158
|
+
.query();
|
|
159
|
+
```
|
|
243
160
|
|
|
244
161
|
## Error Handling
|
|
245
162
|
|
|
163
|
+
The SDK throws standard errors that you can catch and handle.
|
|
164
|
+
|
|
246
165
|
```typescript
|
|
247
166
|
try {
|
|
248
|
-
|
|
167
|
+
await db.table('users').create(data);
|
|
249
168
|
} catch (error) {
|
|
250
|
-
|
|
251
|
-
// Handle query-related errors
|
|
252
|
-
console.error('Query Error:', error.message);
|
|
253
|
-
} else if (error instanceof ValidationError) {
|
|
254
|
-
// Handle validation errors
|
|
255
|
-
console.error('Validation Error:', error.details);
|
|
256
|
-
} else if (error instanceof AuthorizationError) {
|
|
257
|
-
// Handle authorization errors
|
|
258
|
-
console.error('Authorization Error:', error.message);
|
|
259
|
-
} else {
|
|
260
|
-
// Handle other errors
|
|
261
|
-
console.error('Unknown Error:', error);
|
|
262
|
-
}
|
|
169
|
+
console.error('Failed to create user:', error.message);
|
|
263
170
|
}
|
|
264
171
|
```
|
|
265
172
|
|
|
266
|
-
|
|
267
|
-
## Advanced Configuration
|
|
268
|
-
|
|
269
|
-
```typescript
|
|
270
|
-
const db = new DatabaseSDK('http://localhost:3000', {
|
|
271
|
-
// Authentication
|
|
272
|
-
credentials: 'include',
|
|
273
|
-
headers: {
|
|
274
|
-
Authorization: 'Bearer your-token',
|
|
275
|
-
},
|
|
276
|
-
|
|
277
|
-
// Query Options
|
|
278
|
-
queryConfig: {
|
|
279
|
-
maxLimit: 1000,
|
|
280
|
-
defaultLimit: 50,
|
|
281
|
-
maxComplexity: 10,
|
|
282
|
-
},
|
|
283
|
-
|
|
284
|
-
// Cache Configuration
|
|
285
|
-
cache: {
|
|
286
|
-
enabled: true,
|
|
287
|
-
ttl: 300, // 5 minutes
|
|
288
|
-
maxSize: 100, // Maximum number of cached queries
|
|
289
|
-
},
|
|
290
|
-
|
|
291
|
-
// Real-time Configuration
|
|
292
|
-
realtime: {
|
|
293
|
-
enabled: true,
|
|
294
|
-
reconnectDelay: 1000,
|
|
295
|
-
maxRetries: 5,
|
|
296
|
-
},
|
|
297
|
-
|
|
298
|
-
// Performance Tuning
|
|
299
|
-
performance: {
|
|
300
|
-
batchSize: 1000,
|
|
301
|
-
poolSize: 10,
|
|
302
|
-
timeout: 5000,
|
|
303
|
-
},
|
|
304
|
-
});
|
|
305
|
-
``` -->
|
|
306
|
-
|
|
307
|
-
## Type Safety
|
|
308
|
-
|
|
309
|
-
The SDK provides full TypeScript support with generic types:
|
|
173
|
+
## Real-time Updates
|
|
310
174
|
|
|
311
|
-
|
|
312
|
-
interface User {
|
|
313
|
-
id: number;
|
|
314
|
-
name: string;
|
|
315
|
-
email: string;
|
|
316
|
-
role: string;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
interface Order {
|
|
320
|
-
id: number;
|
|
321
|
-
userId: number;
|
|
322
|
-
total: number;
|
|
323
|
-
status: string;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// Type-safe queries
|
|
327
|
-
const users = await db.table<User>('users').select('id', 'name', 'email').where('role', 'admin').execute();
|
|
328
|
-
|
|
329
|
-
// Type-safe joins
|
|
330
|
-
const orders = await db.table<Order>('orders').join<User>('users', 'userId', 'id').select('orders.id', 'users.name', 'orders.total').execute();
|
|
331
|
-
```
|
|
332
|
-
|
|
333
|
-
## Performance Optimization
|
|
334
|
-
|
|
335
|
-
### Query Optimization
|
|
336
|
-
|
|
337
|
-
```typescript
|
|
338
|
-
// Use select to limit returned fields
|
|
339
|
-
const users = await db.table('users').select('id', 'name').where('active', true).execute();
|
|
340
|
-
|
|
341
|
-
// Use indexes effectively
|
|
342
|
-
const result = await db
|
|
343
|
-
.table('users')
|
|
344
|
-
.where('email', 'user@example.com') // Assuming email is indexed
|
|
345
|
-
.first()
|
|
346
|
-
.execute();
|
|
347
|
-
|
|
348
|
-
// Batch operations (WIP*)
|
|
349
|
-
await db.table('users').createMany([
|
|
350
|
-
{ name: 'User 1', email: 'user1@example.com' },
|
|
351
|
-
{ name: 'User 2', email: 'user2@example.com' },
|
|
352
|
-
]);
|
|
353
|
-
```
|
|
354
|
-
|
|
355
|
-
<!-- ### Caching
|
|
356
|
-
|
|
357
|
-
```typescript
|
|
358
|
-
// Enable caching for specific queries
|
|
359
|
-
const users = await db
|
|
360
|
-
.table('users')
|
|
361
|
-
.cache({
|
|
362
|
-
ttl: 300, // 5 minutes
|
|
363
|
-
tags: ['users'],
|
|
364
|
-
})
|
|
365
|
-
.execute();
|
|
366
|
-
|
|
367
|
-
// Invalidate cache
|
|
368
|
-
await db.invalidateCache('users');
|
|
369
|
-
``` -->
|
|
370
|
-
|
|
371
|
-
## Building
|
|
372
|
-
|
|
373
|
-
Run `nx build sdk` to build the library.
|
|
374
|
-
|
|
375
|
-
## Running Tests
|
|
376
|
-
|
|
377
|
-
```bash
|
|
378
|
-
# Run unit tests
|
|
379
|
-
nx test sdk
|
|
380
|
-
|
|
381
|
-
# Run integration tests
|
|
382
|
-
nx test sdk --config=integration
|
|
383
|
-
|
|
384
|
-
# Run tests with coverage
|
|
385
|
-
nx test sdk --coverage
|
|
386
|
-
```
|
|
175
|
+
> ⚠️ **Note**: Real-time features (WebSockets/SSE) are currently experimental and under active development. They are not yet fully documented or recommended for production use.
|
|
387
176
|
|
|
388
177
|
## License
|
|
389
178
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgebase/sdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.js",
|
|
@@ -49,10 +49,10 @@
|
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"axios": "^1.7.9",
|
|
51
51
|
"kysely": "^0.28.11",
|
|
52
|
-
"@forgebase/database": "0.0.
|
|
52
|
+
"@forgebase/database": "0.0.2"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
|
-
"@forgebase/database": "0.0.
|
|
55
|
+
"@forgebase/database": "0.0.2"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"tslib": "^2.3.0",
|