@taruvi/sdk 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/.claude/settings.local.json +16 -0
- package/DATAPROVIDER_CONTEXT.md +828 -0
- package/README.md +703 -0
- package/USAGE_EXAMPLE.md +86 -0
- package/package.json +25 -0
- package/src/client.ts +81 -0
- package/src/index.ts +20 -0
- package/src/lib/Database/DatabaseClient.ts +87 -0
- package/src/lib/Database/types.ts +29 -0
- package/src/lib/Function/FunctionsClient.ts +27 -0
- package/src/lib/Function/types.ts +17 -0
- package/src/lib/Secrets/SecretsClient.ts +44 -0
- package/src/lib/Secrets/types.ts +17 -0
- package/src/lib/Settings/SettingsClient.ts +14 -0
- package/src/lib/Settings/types.ts +4 -0
- package/src/lib/Storage/StorageClient.ts +88 -0
- package/src/lib/Storage/types.ts +40 -0
- package/src/lib/auth/AuthClient.ts +64 -0
- package/src/lib/auth/types.ts +9 -0
- package/src/lib/user/UserClient.ts +44 -0
- package/src/lib/user/types.ts +69 -0
- package/src/lib-internal/errors/ErrorClient.ts +12 -0
- package/src/lib-internal/errors/index.ts +25 -0
- package/src/lib-internal/errors/types.ts +105 -0
- package/src/lib-internal/http/HttpClient.ts +96 -0
- package/src/lib-internal/http/types.ts +10 -0
- package/src/lib-internal/routes/AuthRoutes.ts +0 -0
- package/src/lib-internal/routes/DatabaseRoutes.ts +5 -0
- package/src/lib-internal/routes/FunctionRoutes.ts +3 -0
- package/src/lib-internal/routes/RouteBuilder.ts +0 -0
- package/src/lib-internal/routes/SecretsRoutes.ts +5 -0
- package/src/lib-internal/routes/SettingsRoutes.ts +3 -0
- package/src/lib-internal/routes/StorageRoutes.ts +7 -0
- package/src/lib-internal/routes/UserRoutes.ts +9 -0
- package/src/lib-internal/routes/index.ts +0 -0
- package/src/lib-internal/token/TokenClient.ts +30 -0
- package/src/lib-internal/token/types.ts +0 -0
- package/src/types.ts +68 -0
- package/src/utils/enums.ts +12 -0
- package/src/utils/utils.ts +36 -0
- package/tests/mocks/db.json +1 -0
- package/tsconfig.json +43 -0
|
@@ -0,0 +1,828 @@
|
|
|
1
|
+
# Taruvi SDK - Complete Codebase Reference for Data Provider Generation
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
**Taruvi SDK** (`@taruvi-io/sdk`) is a TypeScript SDK for the Taruvi Data Service - a PostgREST-like dynamic API system that provides:
|
|
6
|
+
- Dynamic database table creation and management
|
|
7
|
+
- File storage with bucket-based organization
|
|
8
|
+
- Serverless function execution
|
|
9
|
+
- Multi-tenant architecture with app isolation
|
|
10
|
+
- Rich querying with 32+ filter operators (Refine.dev compatible)
|
|
11
|
+
- JWT-based authentication
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Architecture Principles
|
|
16
|
+
|
|
17
|
+
1. **Dependency Injection** - No singletons; services receive `Client` instance
|
|
18
|
+
2. **Two-Tier API** - Public (`lib/`) vs Internal (`lib-internal/`)
|
|
19
|
+
3. **Query Builder Pattern** - Chainable, fluent APIs with deferred execution
|
|
20
|
+
4. **Type Safety** - Full TypeScript with strict mode
|
|
21
|
+
5. **Tree-Shakeable** - Modular exports for bundle optimization
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Directory Structure
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
src/
|
|
29
|
+
├── client.ts # Main Client orchestrator
|
|
30
|
+
├── index.ts # Public exports
|
|
31
|
+
├── types.ts # Core config + filter types
|
|
32
|
+
│
|
|
33
|
+
├── lib/ # PUBLIC API
|
|
34
|
+
│ ├── auth/AuthClient.ts
|
|
35
|
+
│ ├── user/UserClient.ts
|
|
36
|
+
│ ├── Storage/StorageClient.ts
|
|
37
|
+
│ ├── Database/DatabaseClient.ts
|
|
38
|
+
│ ├── Function/FunctionsClient.ts
|
|
39
|
+
│ ├── Settings/SettingsClient.ts
|
|
40
|
+
│ └── Secrets/SecretsClient.ts
|
|
41
|
+
│
|
|
42
|
+
├── lib-internal/ # INTERNAL API
|
|
43
|
+
│ ├── http/HttpClient.ts
|
|
44
|
+
│ ├── token/TokenClient.ts
|
|
45
|
+
│ ├── routes/*.ts
|
|
46
|
+
│ └── errors/types.ts
|
|
47
|
+
│
|
|
48
|
+
└── utils/
|
|
49
|
+
├── utils.ts # Runtime detection, buildQueryString
|
|
50
|
+
└── enums.ts # MimeTypeCategory, Visibility
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Core Configuration
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
interface TaruviConfig {
|
|
59
|
+
apiKey: string // Site/organization identifier
|
|
60
|
+
appSlug: string // App identifier (multi-tenant)
|
|
61
|
+
baseUrl: string // API endpoint (e.g., https://test-api.taruvi.cloud)
|
|
62
|
+
token?: string // Optional pre-existing JWT token
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Client Initialization
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { Client, Database, Storage, User, Auth, Functions, Secrets } from '@taruvi-io/sdk'
|
|
72
|
+
|
|
73
|
+
const client = new Client({
|
|
74
|
+
apiKey: 'your-site-api-key',
|
|
75
|
+
appSlug: 'my-app',
|
|
76
|
+
baseUrl: 'https://test-api.taruvi.cloud'
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Services are instantiated with the client
|
|
80
|
+
const db = new Database(client, {})
|
|
81
|
+
const storage = new Storage(client, {})
|
|
82
|
+
const user = new User(client)
|
|
83
|
+
const auth = new Auth(client)
|
|
84
|
+
const functions = new Functions(client)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## HTTP Client (Internal)
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
class HttpClient {
|
|
93
|
+
constructor(config: TaruviConfig, tokenClient: TokenClient)
|
|
94
|
+
|
|
95
|
+
// Authentication headers:
|
|
96
|
+
// - Token {apiKey} (developer auth)
|
|
97
|
+
// - Bearer {jwt} (user auth - takes precedence)
|
|
98
|
+
|
|
99
|
+
async get<T>(endpoint: string): Promise<T>
|
|
100
|
+
async post<T, D>(endpoint: string, body: D): Promise<T>
|
|
101
|
+
async put<T, D>(endpoint: string, body: D): Promise<T>
|
|
102
|
+
async patch<T, D>(endpoint: string, body: D): Promise<T>
|
|
103
|
+
async delete<T, D>(endpoint: string, body?: D): Promise<T>
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## API Endpoints
|
|
110
|
+
|
|
111
|
+
### Database Routes
|
|
112
|
+
```
|
|
113
|
+
GET /api/apps/{appSlug}/datatables/{tableName}/data/ # List records
|
|
114
|
+
GET /api/apps/{appSlug}/datatables/{tableName}/data/{id}/ # Get record
|
|
115
|
+
POST /api/apps/{appSlug}/datatables/{tableName}/data/ # Create record
|
|
116
|
+
PATCH /api/apps/{appSlug}/datatables/{tableName}/data/{id}/ # Update record
|
|
117
|
+
DELETE /api/apps/{appSlug}/datatables/{tableName}/data/{id}/ # Delete record
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Storage Routes
|
|
121
|
+
```
|
|
122
|
+
GET /api/apps/{appSlug}/storage/buckets/{bucket}/objects/ # List files
|
|
123
|
+
GET /api/apps/{appSlug}/storage/buckets/{bucket}/objects/{path}/ # Get file
|
|
124
|
+
POST /api/apps/{appSlug}/storage/buckets/{bucket}/objects/batch-upload # Upload
|
|
125
|
+
POST /api/apps/{appSlug}/storage/buckets/{bucket}/objects/batch-delete # Delete
|
|
126
|
+
PUT /api/apps/{appSlug}/storage/buckets/{bucket}/objects/{path}/ # Update metadata
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### User Routes
|
|
130
|
+
```
|
|
131
|
+
GET /api/users/ # List users
|
|
132
|
+
GET /api/users/me/ # Current user
|
|
133
|
+
POST /api/users/ # Create user
|
|
134
|
+
PUT /api/users/{username}/ # Update user
|
|
135
|
+
DELETE /api/users/{username}/ # Delete user
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Function Routes
|
|
139
|
+
```
|
|
140
|
+
POST /api/functions/apps/{appSlug}/functions/{functionSlug}/execute/
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Settings Routes
|
|
144
|
+
```
|
|
145
|
+
GET /api/settings/metadata/
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Secrets Routes
|
|
149
|
+
```
|
|
150
|
+
GET /api/secrets/
|
|
151
|
+
GET /api/secrets/{key}/
|
|
152
|
+
PUT /api/secrets/{key}/
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Query Parameters (Refine.dev Compatible)
|
|
158
|
+
|
|
159
|
+
### Pagination
|
|
160
|
+
- `_start=0` / `_end=10` (offset-based)
|
|
161
|
+
- `page=1` / `pageSize=20` (DRF style)
|
|
162
|
+
- `limit=10` / `offset=0`
|
|
163
|
+
|
|
164
|
+
### Sorting
|
|
165
|
+
- `_sort=created_at,name`
|
|
166
|
+
- `_order=DESC,ASC`
|
|
167
|
+
- `ordering=-created_at` (DRF style: `-` prefix for DESC)
|
|
168
|
+
|
|
169
|
+
### Filtering (32+ Operators)
|
|
170
|
+
```
|
|
171
|
+
# Exact match
|
|
172
|
+
status=active
|
|
173
|
+
|
|
174
|
+
# Comparison
|
|
175
|
+
age_gte=18 # >=
|
|
176
|
+
age_gt=18 # >
|
|
177
|
+
age_lte=65 # <=
|
|
178
|
+
age_lt=65 # <
|
|
179
|
+
age_between=18,65 # range
|
|
180
|
+
|
|
181
|
+
# String operations
|
|
182
|
+
name_contains=john
|
|
183
|
+
name_startswith=J
|
|
184
|
+
name_endswith=son
|
|
185
|
+
name_icontains=john # case-insensitive
|
|
186
|
+
|
|
187
|
+
# Array operations
|
|
188
|
+
status_in=active,pending
|
|
189
|
+
tags_nin=deleted
|
|
190
|
+
|
|
191
|
+
# Null checks
|
|
192
|
+
deleted_at_null=true
|
|
193
|
+
email_nnull=true
|
|
194
|
+
|
|
195
|
+
# Foreign key population
|
|
196
|
+
populate=* # All relations
|
|
197
|
+
populate=user_id,org_id # Specific relations
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Storage-Specific Filters
|
|
201
|
+
```typescript
|
|
202
|
+
interface StorageFilters {
|
|
203
|
+
page?: number
|
|
204
|
+
pageSize?: number
|
|
205
|
+
|
|
206
|
+
// Size filters (bytes)
|
|
207
|
+
size__gte?: number
|
|
208
|
+
size__lte?: number
|
|
209
|
+
min_size?: number
|
|
210
|
+
max_size?: number
|
|
211
|
+
|
|
212
|
+
// Date filters (ISO 8601)
|
|
213
|
+
created_at__gte?: string
|
|
214
|
+
created_at__lte?: string
|
|
215
|
+
created_after?: string
|
|
216
|
+
created_before?: string
|
|
217
|
+
|
|
218
|
+
// Search
|
|
219
|
+
search?: string
|
|
220
|
+
filename__icontains?: string
|
|
221
|
+
prefix?: string
|
|
222
|
+
|
|
223
|
+
// MIME type
|
|
224
|
+
mimetype?: string
|
|
225
|
+
mimetype__in?: string
|
|
226
|
+
mimetype_category?: 'image' | 'video' | 'audio' | 'application' | 'text'
|
|
227
|
+
|
|
228
|
+
// Visibility
|
|
229
|
+
visibility?: 'public' | 'private'
|
|
230
|
+
created_by_me?: boolean
|
|
231
|
+
|
|
232
|
+
// Sorting
|
|
233
|
+
ordering?: string
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Database Filters
|
|
238
|
+
```typescript
|
|
239
|
+
interface DatabaseFilters {
|
|
240
|
+
page?: number
|
|
241
|
+
pageSize?: number
|
|
242
|
+
ordering?: string // "-field" for DESC, "field" for ASC
|
|
243
|
+
|
|
244
|
+
// Dynamic filters - any field with operators
|
|
245
|
+
[key: string]: string | number | boolean | undefined
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Data Types
|
|
252
|
+
|
|
253
|
+
### User Types
|
|
254
|
+
```typescript
|
|
255
|
+
interface UserCreateRequest {
|
|
256
|
+
username: string
|
|
257
|
+
email: string
|
|
258
|
+
password: string
|
|
259
|
+
confirm_password: string
|
|
260
|
+
first_name: string
|
|
261
|
+
last_name: string
|
|
262
|
+
is_active: boolean
|
|
263
|
+
is_staff: boolean
|
|
264
|
+
attributes: string
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
interface UserDataResponse {
|
|
268
|
+
id: number
|
|
269
|
+
username: string
|
|
270
|
+
email: string
|
|
271
|
+
first_name: string
|
|
272
|
+
last_name: string
|
|
273
|
+
full_name: string
|
|
274
|
+
is_active: boolean
|
|
275
|
+
is_staff: boolean
|
|
276
|
+
is_deleted: boolean
|
|
277
|
+
date_joined: string // ISO 8601
|
|
278
|
+
last_login: string
|
|
279
|
+
attributes: string
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
interface UserUpdateRequest {
|
|
283
|
+
username?: string
|
|
284
|
+
email?: string
|
|
285
|
+
password?: string
|
|
286
|
+
confirm_password?: string
|
|
287
|
+
first_name?: string
|
|
288
|
+
last_name?: string
|
|
289
|
+
is_active?: boolean
|
|
290
|
+
is_staff?: boolean
|
|
291
|
+
attributes?: string
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
interface UserList {
|
|
295
|
+
search: string
|
|
296
|
+
is_active: boolean
|
|
297
|
+
is_staff: boolean
|
|
298
|
+
is_superuser: boolean
|
|
299
|
+
is_deleted: boolean
|
|
300
|
+
ordering: string
|
|
301
|
+
page: Number
|
|
302
|
+
page_size: Number
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Storage Types
|
|
307
|
+
```typescript
|
|
308
|
+
interface StorageRequest {
|
|
309
|
+
files: File[]
|
|
310
|
+
paths: string[]
|
|
311
|
+
metadatas: object[]
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
interface StorageUpdateRequest {
|
|
315
|
+
metadata?: Record<string, unknown>
|
|
316
|
+
visibility?: 'public' | 'private'
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
interface StorageResponse {
|
|
320
|
+
id: number
|
|
321
|
+
uuid: string
|
|
322
|
+
filename: string
|
|
323
|
+
file_path: string
|
|
324
|
+
file_url: string
|
|
325
|
+
size: number
|
|
326
|
+
mimetype: string
|
|
327
|
+
metadata?: Record<string, unknown>
|
|
328
|
+
visibility?: string
|
|
329
|
+
created_at: string
|
|
330
|
+
updated_at: string
|
|
331
|
+
}
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Database Types
|
|
335
|
+
```typescript
|
|
336
|
+
interface DatabaseRequest {
|
|
337
|
+
[key: string]: unknown
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
interface DatabaseResponse<T = unknown> {
|
|
341
|
+
id?: string | number
|
|
342
|
+
created_at?: string
|
|
343
|
+
updated_at?: string
|
|
344
|
+
[key: string]: T | string | number | undefined
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Function Types
|
|
349
|
+
```typescript
|
|
350
|
+
interface FunctionRequest {
|
|
351
|
+
async?: boolean
|
|
352
|
+
params?: Record<string, unknown>
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
interface FunctionInvocation {
|
|
356
|
+
invocation_id: number
|
|
357
|
+
celery_task_id: string
|
|
358
|
+
status: string
|
|
359
|
+
created_at: string
|
|
360
|
+
updated_at: string
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
interface FunctionResponse<T = unknown> {
|
|
364
|
+
data: T | null
|
|
365
|
+
invocation: FunctionInvocation
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Secret Types
|
|
370
|
+
```typescript
|
|
371
|
+
interface SecretRequest {
|
|
372
|
+
value: string
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
interface SecretResponse {
|
|
376
|
+
key: string
|
|
377
|
+
value: string
|
|
378
|
+
created_at?: string
|
|
379
|
+
updated_at?: string
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## Current Service Implementations
|
|
386
|
+
|
|
387
|
+
### Database (Query Builder Pattern)
|
|
388
|
+
```typescript
|
|
389
|
+
class Database {
|
|
390
|
+
constructor(client: Client, urlParams: UrlParams, operation?: HttpMethod, body?: object, filters?: DatabaseFilters)
|
|
391
|
+
|
|
392
|
+
from(tableName: string): Database
|
|
393
|
+
filter(filters: DatabaseFilters): Database
|
|
394
|
+
get(recordId: string): Database
|
|
395
|
+
update(body: any): Database
|
|
396
|
+
delete(recordId?: string): Database
|
|
397
|
+
execute(): Promise<T>
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Usage
|
|
401
|
+
const records = await db
|
|
402
|
+
.from('users')
|
|
403
|
+
.filter({ status: 'active', age_gte: 18 })
|
|
404
|
+
.execute()
|
|
405
|
+
|
|
406
|
+
const record = await db
|
|
407
|
+
.from('users')
|
|
408
|
+
.get('123')
|
|
409
|
+
.execute()
|
|
410
|
+
|
|
411
|
+
await db
|
|
412
|
+
.from('users')
|
|
413
|
+
.get('123')
|
|
414
|
+
.update({ status: 'inactive' })
|
|
415
|
+
.execute()
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Storage (Query Builder Pattern)
|
|
419
|
+
```typescript
|
|
420
|
+
class Storage {
|
|
421
|
+
constructor(client: Client, urlParams: BucketUrlParams, operation?: HttpMethod, body?: object, filters?: StorageFilters)
|
|
422
|
+
|
|
423
|
+
from(bucket: string): Storage
|
|
424
|
+
filter(filters: StorageFilters): Storage
|
|
425
|
+
download(path: string): Storage
|
|
426
|
+
upload(filesData: { files: File[], paths: string[], metadatas: object[] }): Storage
|
|
427
|
+
update(path: string, body: object): Storage
|
|
428
|
+
delete(path: string): Storage
|
|
429
|
+
execute(): Promise<T>
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Usage
|
|
433
|
+
const files = await storage
|
|
434
|
+
.from('uploads')
|
|
435
|
+
.filter({ mimetype_category: 'image', size__lte: 5000000 })
|
|
436
|
+
.execute()
|
|
437
|
+
|
|
438
|
+
await storage
|
|
439
|
+
.from('uploads')
|
|
440
|
+
.upload({ files: [file], paths: ['images/photo.jpg'], metadatas: [{ alt: 'Photo' }] })
|
|
441
|
+
.execute()
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
### User (Direct Methods)
|
|
445
|
+
```typescript
|
|
446
|
+
class User {
|
|
447
|
+
constructor(client: Client)
|
|
448
|
+
|
|
449
|
+
getUserData(): Promise<UserDataResponse>
|
|
450
|
+
createUser(userData: UserCreateRequest): Promise<UserCreateResponse>
|
|
451
|
+
updateUser(username: string, body: UserUpdateRequest): Promise<UserCreateResponse>
|
|
452
|
+
deleteUser(username: string): Promise<void>
|
|
453
|
+
list(filters: UserList): Promise<any>
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Usage
|
|
457
|
+
const currentUser = await user.getUserData()
|
|
458
|
+
const users = await user.list({ is_active: true, page: 1, page_size: 20 })
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
### Auth
|
|
462
|
+
```typescript
|
|
463
|
+
class Auth {
|
|
464
|
+
constructor(client: Client)
|
|
465
|
+
|
|
466
|
+
authenticateUser(): Promise<void> // Stores JWT in localStorage
|
|
467
|
+
isUserAuthenticated(): Promise<boolean>
|
|
468
|
+
redirectToLogin(): Promise<void>
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### Functions
|
|
473
|
+
```typescript
|
|
474
|
+
class Functions {
|
|
475
|
+
constructor(client: Client)
|
|
476
|
+
|
|
477
|
+
execute<T>(functionSlug: string, options?: FunctionRequest): Promise<FunctionResponse<T>>
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Usage
|
|
481
|
+
const result = await functions.execute<{ sum: number }>('calculate', {
|
|
482
|
+
async: false,
|
|
483
|
+
params: { a: 1, b: 2 }
|
|
484
|
+
})
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Settings
|
|
488
|
+
```typescript
|
|
489
|
+
class Settings {
|
|
490
|
+
constructor(client: Client)
|
|
491
|
+
|
|
492
|
+
get<T>(): Promise<T>
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Usage
|
|
496
|
+
const config = await settings.get<{ theme: string, features: string[] }>()
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Secrets (Query Builder Pattern)
|
|
500
|
+
```typescript
|
|
501
|
+
class Secrets {
|
|
502
|
+
constructor(client: Client, urlParams?: SecretsUrlParams, body?: object, method?: HttpMethod)
|
|
503
|
+
|
|
504
|
+
list(): Secrets
|
|
505
|
+
get(key: string): Secrets
|
|
506
|
+
update(key: string, body: object): Secrets
|
|
507
|
+
execute<T>(): Promise<T>
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Usage
|
|
511
|
+
const allSecrets = await secrets.list().execute()
|
|
512
|
+
const apiKey = await secrets.get('API_KEY').execute()
|
|
513
|
+
await secrets.update('API_KEY', { value: 'new-key' }).execute()
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## Route Definitions
|
|
519
|
+
|
|
520
|
+
### DatabaseRoutes
|
|
521
|
+
```typescript
|
|
522
|
+
const DatabaseRoutes = {
|
|
523
|
+
baseUrl: (appSlug: string) => `api/apps/${appSlug}`,
|
|
524
|
+
dataTables: (tableName: string): string => `/datatables/${tableName}/data`,
|
|
525
|
+
recordId: (recordId: string): string => `/${recordId}`
|
|
526
|
+
}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### StorageRoutes
|
|
530
|
+
```typescript
|
|
531
|
+
const StorageRoutes = {
|
|
532
|
+
baseUrl: (appslug: string, bucket: string) => `api/apps/${appslug}/storage/buckets/${bucket}/objects`,
|
|
533
|
+
path: (path: string) => "/" + path,
|
|
534
|
+
upload: () => "/batch-upload",
|
|
535
|
+
delete: () => "/batch-delete"
|
|
536
|
+
}
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### UserRoutes
|
|
540
|
+
```typescript
|
|
541
|
+
const UserRoutes = {
|
|
542
|
+
baseUrl: "api/users/",
|
|
543
|
+
getCurrentUser: () => `${UserRoutes.baseUrl}me/`,
|
|
544
|
+
updateUser: (username: string) => `${UserRoutes.baseUrl}${username}/`,
|
|
545
|
+
deleteUser: (username: string) => `${UserRoutes.baseUrl}${username}/`,
|
|
546
|
+
listUser: (filter: string) => `${UserRoutes.baseUrl}${filter}`
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### FunctionRoutes
|
|
551
|
+
```typescript
|
|
552
|
+
const FunctionRoutes = {
|
|
553
|
+
baseUrl: (appSlug: string, functionSlug: string) => `/api/functions/apps/${appSlug}/functions/${functionSlug}`
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### SettingsRoutes
|
|
558
|
+
```typescript
|
|
559
|
+
const SettingsRoutes = {
|
|
560
|
+
metadata: "api/settings/metadata/"
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### SecretsRoutes
|
|
565
|
+
```typescript
|
|
566
|
+
const SecretsRoutes = {
|
|
567
|
+
baseUrl: "api/secrets/",
|
|
568
|
+
get: (key: string) => `api/secrets/${key}/`,
|
|
569
|
+
update: (key: string) => `api/secrets/${key}/`
|
|
570
|
+
}
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## Error Codes
|
|
576
|
+
|
|
577
|
+
```typescript
|
|
578
|
+
enum ErrorCode {
|
|
579
|
+
// General errors (1000-1099)
|
|
580
|
+
UNKNOWN_ERROR = 'UNKNOWN_ERROR',
|
|
581
|
+
INTERNAL_ERROR = 'INTERNAL_ERROR',
|
|
582
|
+
TIMEOUT_ERROR = 'TIMEOUT_ERROR',
|
|
583
|
+
|
|
584
|
+
// Network errors (1100-1199)
|
|
585
|
+
NETWORK_ERROR = 'NETWORK_ERROR',
|
|
586
|
+
CONNECTION_ERROR = 'CONNECTION_ERROR',
|
|
587
|
+
REQUEST_FAILED = 'REQUEST_FAILED',
|
|
588
|
+
|
|
589
|
+
// Authentication errors (1200-1299)
|
|
590
|
+
AUTH_ERROR = 'AUTH_ERROR',
|
|
591
|
+
UNAUTHORIZED = 'UNAUTHORIZED',
|
|
592
|
+
TOKEN_EXPIRED = 'TOKEN_EXPIRED',
|
|
593
|
+
TOKEN_INVALID = 'TOKEN_INVALID',
|
|
594
|
+
SESSION_EXPIRED = 'SESSION_EXPIRED',
|
|
595
|
+
INVALID_CREDENTIALS = 'INVALID_CREDENTIALS',
|
|
596
|
+
|
|
597
|
+
// Authorization errors (1300-1399)
|
|
598
|
+
FORBIDDEN = 'FORBIDDEN',
|
|
599
|
+
INSUFFICIENT_PERMISSIONS = 'INSUFFICIENT_PERMISSIONS',
|
|
600
|
+
|
|
601
|
+
// Validation errors (1400-1499)
|
|
602
|
+
VALIDATION_ERROR = 'VALIDATION_ERROR',
|
|
603
|
+
INVALID_INPUT = 'INVALID_INPUT',
|
|
604
|
+
MISSING_REQUIRED_FIELD = 'MISSING_REQUIRED_FIELD',
|
|
605
|
+
INVALID_FORMAT = 'INVALID_FORMAT',
|
|
606
|
+
|
|
607
|
+
// Database errors (1500-1599)
|
|
608
|
+
DATABASE_ERROR = 'DATABASE_ERROR',
|
|
609
|
+
QUERY_FAILED = 'QUERY_FAILED',
|
|
610
|
+
RECORD_NOT_FOUND = 'RECORD_NOT_FOUND',
|
|
611
|
+
DUPLICATE_ENTRY = 'DUPLICATE_ENTRY',
|
|
612
|
+
CONSTRAINT_VIOLATION = 'CONSTRAINT_VIOLATION',
|
|
613
|
+
|
|
614
|
+
// Storage errors (1600-1699)
|
|
615
|
+
STORAGE_ERROR = 'STORAGE_ERROR',
|
|
616
|
+
FILE_NOT_FOUND = 'FILE_NOT_FOUND',
|
|
617
|
+
FILE_TOO_LARGE = 'FILE_TOO_LARGE',
|
|
618
|
+
INVALID_FILE_TYPE = 'INVALID_FILE_TYPE',
|
|
619
|
+
UPLOAD_FAILED = 'UPLOAD_FAILED',
|
|
620
|
+
DOWNLOAD_FAILED = 'DOWNLOAD_FAILED',
|
|
621
|
+
|
|
622
|
+
// Function errors (1700-1799)
|
|
623
|
+
FUNCTION_ERROR = 'FUNCTION_ERROR',
|
|
624
|
+
FUNCTION_NOT_FOUND = 'FUNCTION_NOT_FOUND',
|
|
625
|
+
FUNCTION_EXECUTION_FAILED = 'FUNCTION_EXECUTION_FAILED',
|
|
626
|
+
FUNCTION_TIMEOUT = 'FUNCTION_TIMEOUT',
|
|
627
|
+
|
|
628
|
+
// Rate limiting errors (1800-1899)
|
|
629
|
+
RATE_LIMIT_EXCEEDED = 'RATE_LIMIT_EXCEEDED',
|
|
630
|
+
QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',
|
|
631
|
+
|
|
632
|
+
// Client errors (1900-1999)
|
|
633
|
+
CONFIGURATION_ERROR = 'CONFIGURATION_ERROR',
|
|
634
|
+
INVALID_API_KEY = 'INVALID_API_KEY',
|
|
635
|
+
INVALID_CONFIG = 'INVALID_CONFIG'
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
enum ErrorSeverity {
|
|
639
|
+
LOW = 'low',
|
|
640
|
+
MEDIUM = 'medium',
|
|
641
|
+
HIGH = 'high',
|
|
642
|
+
CRITICAL = 'critical'
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
interface ErrorResponse {
|
|
646
|
+
code: ErrorCode
|
|
647
|
+
message: string
|
|
648
|
+
severity: ErrorSeverity
|
|
649
|
+
timestamp: string
|
|
650
|
+
requestId?: string
|
|
651
|
+
metadata?: Record<string, any>
|
|
652
|
+
cause?: Error
|
|
653
|
+
}
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
658
|
+
## API Response Format
|
|
659
|
+
|
|
660
|
+
### List Response
|
|
661
|
+
```json
|
|
662
|
+
{
|
|
663
|
+
"data": [
|
|
664
|
+
{
|
|
665
|
+
"id": 1,
|
|
666
|
+
"name": "John Doe",
|
|
667
|
+
"age": 30,
|
|
668
|
+
"user_id": 5,
|
|
669
|
+
"user": {
|
|
670
|
+
"id": 5,
|
|
671
|
+
"name": "Admin User",
|
|
672
|
+
"email": "admin@example.com"
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
],
|
|
676
|
+
"total": 156,
|
|
677
|
+
"meta": {
|
|
678
|
+
"offset": 0,
|
|
679
|
+
"count": 10,
|
|
680
|
+
"has_more": true
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
### Single Record Response
|
|
686
|
+
```json
|
|
687
|
+
{
|
|
688
|
+
"id": 1,
|
|
689
|
+
"name": "John Doe",
|
|
690
|
+
"email": "john@example.com",
|
|
691
|
+
"created_at": "2024-01-15T10:30:00Z",
|
|
692
|
+
"updated_at": "2024-01-15T10:30:00Z"
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
### Error Response
|
|
697
|
+
```json
|
|
698
|
+
{
|
|
699
|
+
"error": "Insert failed",
|
|
700
|
+
"detail": "Validation errors: age: must be >= 0"
|
|
701
|
+
}
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
## Utility Functions
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
// Query string builder
|
|
710
|
+
function buildQueryString(filters: Record<string, unknown> | undefined): string {
|
|
711
|
+
if (!filters || Object.keys(filters).length === 0) {
|
|
712
|
+
return ''
|
|
713
|
+
}
|
|
714
|
+
const params = new URLSearchParams()
|
|
715
|
+
Object.entries(filters).forEach(([key, value]) => {
|
|
716
|
+
if (value !== undefined && value !== null) {
|
|
717
|
+
params.append(key, String(value))
|
|
718
|
+
}
|
|
719
|
+
})
|
|
720
|
+
const queryString = params.toString()
|
|
721
|
+
return queryString ? `?${queryString}` : ''
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Runtime detection
|
|
725
|
+
function isBrowser(): boolean
|
|
726
|
+
function isReactNative(): boolean
|
|
727
|
+
function getRuntimeEnvironment(): 'Browser' | 'ReactNative' | 'Server'
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
---
|
|
731
|
+
|
|
732
|
+
## Enums
|
|
733
|
+
|
|
734
|
+
```typescript
|
|
735
|
+
enum MimeTypeCategory {
|
|
736
|
+
IMAGE = 'image',
|
|
737
|
+
VIDEO = 'video',
|
|
738
|
+
AUDIO = 'audio',
|
|
739
|
+
APPLICATION = 'application',
|
|
740
|
+
TEXT = 'text'
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
enum Visibility {
|
|
744
|
+
PUBLIC = 'public',
|
|
745
|
+
PRIVATE = 'private'
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
enum HttpMethod {
|
|
749
|
+
GET = 'GET',
|
|
750
|
+
POST = 'POST',
|
|
751
|
+
PUT = 'PUT',
|
|
752
|
+
PATCH = 'PATCH',
|
|
753
|
+
DELETE = 'DELETE'
|
|
754
|
+
}
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
---
|
|
758
|
+
|
|
759
|
+
## Key Design Patterns for Data Providers
|
|
760
|
+
|
|
761
|
+
1. **Query Builder Pattern**: Chainable methods returning new instances (immutable)
|
|
762
|
+
```typescript
|
|
763
|
+
from(resource) -> filter(conditions) -> execute()
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
2. **Deferred Execution**: Operations are queued, executed on `.execute()`
|
|
767
|
+
|
|
768
|
+
3. **Route Builders**: Centralized endpoint construction with template functions
|
|
769
|
+
|
|
770
|
+
4. **Filter Objects**: DRF-style query parameters with operators (`field_operator=value`)
|
|
771
|
+
|
|
772
|
+
5. **Generic Types**: `<T>` for response typing
|
|
773
|
+
|
|
774
|
+
6. **HTTP Method Enum**: Type-safe operation selection
|
|
775
|
+
|
|
776
|
+
7. **Dependency Injection**: All services receive `Client` instance
|
|
777
|
+
|
|
778
|
+
---
|
|
779
|
+
|
|
780
|
+
## Implementation Status
|
|
781
|
+
|
|
782
|
+
| Service | Status | Completion | Notes |
|
|
783
|
+
|---------|--------|------------|-------|
|
|
784
|
+
| Client/HTTP | Complete | 90% | Core infrastructure working |
|
|
785
|
+
| User | Functional | 80% | CRUD operations implemented |
|
|
786
|
+
| Auth | Partial | 60% | Basic auth, SSO planned |
|
|
787
|
+
| Storage | Partial | 70% | Query builder working |
|
|
788
|
+
| Database | Planned | 0% | Query builder structure defined |
|
|
789
|
+
| Functions | Complete | 100% | Function execution working |
|
|
790
|
+
| Secrets | Complete | 100% | CRUD operations for secrets |
|
|
791
|
+
| Settings | Functional | 70% | Site config fetching works |
|
|
792
|
+
|
|
793
|
+
---
|
|
794
|
+
|
|
795
|
+
## Dependencies
|
|
796
|
+
|
|
797
|
+
**Peer Dependencies:**
|
|
798
|
+
- `axios`: ^1.0.0
|
|
799
|
+
- `@types/node`: ^18.0.0 || ^20.0.0 || ^22.0.0
|
|
800
|
+
- `mime-db`: ^1.54.0
|
|
801
|
+
|
|
802
|
+
**Dev Dependencies:**
|
|
803
|
+
- `typescript`: ^5.7.3
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
807
|
+
## Token Management
|
|
808
|
+
|
|
809
|
+
The SDK uses localStorage for JWT token storage in browser environments:
|
|
810
|
+
|
|
811
|
+
```typescript
|
|
812
|
+
class TokenClient {
|
|
813
|
+
getToken(): string | null {
|
|
814
|
+
if (isBrowser()) {
|
|
815
|
+
return localStorage.getItem("jwt")
|
|
816
|
+
}
|
|
817
|
+
return null
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
Authentication headers are automatically applied:
|
|
823
|
+
- API Key: `Authorization: Token {apiKey}` (developer auth)
|
|
824
|
+
- JWT: `Authorization: Bearer {jwt}` (user auth - takes precedence)
|
|
825
|
+
|
|
826
|
+
---
|
|
827
|
+
|
|
828
|
+
This reference document provides all necessary context for generating refined data providers that align with the Taruvi SDK's architecture, patterns, and API specifications.
|