@taruvi/sdk 1.0.0 → 1.0.1
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/AI_DOCS.md +1107 -0
- package/package.json +1 -1
- package/src/lib-internal/http/HttpClient.ts +0 -5
package/AI_DOCS.md
ADDED
|
@@ -0,0 +1,1107 @@
|
|
|
1
|
+
# Taruvi SDK - AI-Friendly Documentation
|
|
2
|
+
|
|
3
|
+
**Version**: 1.1.0
|
|
4
|
+
**Purpose**: Backend-as-a-Service TypeScript SDK for Taruvi Platform
|
|
5
|
+
**Architecture**: Dependency Injection, Lazy Loading, Tree-Shakeable
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
### Installation
|
|
12
|
+
```bash
|
|
13
|
+
npm install @taruvi/sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Basic Usage Pattern
|
|
17
|
+
```typescript
|
|
18
|
+
import { Client, Auth, User } from '@taruvi/sdk'
|
|
19
|
+
|
|
20
|
+
const client = new Client({
|
|
21
|
+
apiKey: 'site_key',
|
|
22
|
+
appSlug: 'app_name',
|
|
23
|
+
baseUrl: 'https://test-api.taruvi.cloud'
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
const auth = new Auth(client)
|
|
27
|
+
const user = new User(client)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Core Architecture
|
|
33
|
+
|
|
34
|
+
### Design Pattern: Dependency Injection
|
|
35
|
+
- **No Singletons**: All services require explicit Client instance
|
|
36
|
+
- **Multiple Instances**: Can create multiple clients for different environments
|
|
37
|
+
- **Testable**: Easy to mock dependencies
|
|
38
|
+
|
|
39
|
+
### Client Initialization Flow
|
|
40
|
+
1. Create `Client` with config
|
|
41
|
+
2. Client creates internal `TokenClient` (manages JWT)
|
|
42
|
+
3. Client creates internal `HttpClient` (handles API requests)
|
|
43
|
+
4. Services instantiated with Client reference
|
|
44
|
+
|
|
45
|
+
### Authentication Flow
|
|
46
|
+
- **API Key**: Identifies site/app (sent in `Authorization: Token {key}`)
|
|
47
|
+
- **JWT Token**: User session (sent in `Authorization: Bearer {jwt}`)
|
|
48
|
+
- **Token Storage**: Browser localStorage (`jwt` key)
|
|
49
|
+
- **OAuth Callback**: Extracts tokens from URL hash on client init
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Client Class
|
|
54
|
+
|
|
55
|
+
### Constructor
|
|
56
|
+
```typescript
|
|
57
|
+
new Client(config: TaruviConfig)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Config Interface**:
|
|
61
|
+
```typescript
|
|
62
|
+
interface TaruviConfig {
|
|
63
|
+
apiKey: string // Site identifier (required)
|
|
64
|
+
appSlug: string // App identifier for multi-tenancy (required)
|
|
65
|
+
baseUrl: string // API endpoint (required)
|
|
66
|
+
deskUrl?: string // Login page URL (optional)
|
|
67
|
+
token?: string // Pre-existing JWT (optional)
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Validation**:
|
|
72
|
+
- Throws if `config`, `apiKey`, or `baseUrl` missing
|
|
73
|
+
- Creates TokenClient first, then HttpClient
|
|
74
|
+
- Extracts OAuth tokens from URL hash if present
|
|
75
|
+
|
|
76
|
+
**Internal Properties** (marked @internal, not public API):
|
|
77
|
+
- `httpClient`: HttpClient instance
|
|
78
|
+
- `tokenClient`: TokenClient instance
|
|
79
|
+
|
|
80
|
+
**Public Methods**:
|
|
81
|
+
- `getConfig()`: Returns readonly copy of config
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Services
|
|
86
|
+
|
|
87
|
+
### 1. Auth Service
|
|
88
|
+
|
|
89
|
+
**Status**: 🚧 Partial (60% complete)
|
|
90
|
+
|
|
91
|
+
**Import**:
|
|
92
|
+
```typescript
|
|
93
|
+
import { Auth } from '@taruvi/sdk'
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Constructor**:
|
|
97
|
+
```typescript
|
|
98
|
+
new Auth(client: Client)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**Methods**:
|
|
102
|
+
|
|
103
|
+
#### `authenticateUser()`
|
|
104
|
+
```typescript
|
|
105
|
+
async authenticateUser(): Promise<void>
|
|
106
|
+
```
|
|
107
|
+
- Currently empty (commented out implementation)
|
|
108
|
+
- Intended for email/password login
|
|
109
|
+
|
|
110
|
+
#### `isUserAuthenticated()`
|
|
111
|
+
```typescript
|
|
112
|
+
async isUserAuthenticated(): Promise<boolean>
|
|
113
|
+
```
|
|
114
|
+
- Checks if `jwt` exists in localStorage
|
|
115
|
+
- Returns `true` if token present, `false` otherwise
|
|
116
|
+
|
|
117
|
+
#### `redirectToLogin()`
|
|
118
|
+
```typescript
|
|
119
|
+
async redirectToLogin(): Promise<void>
|
|
120
|
+
```
|
|
121
|
+
- Redirects to login page with current URL as redirect param
|
|
122
|
+
- Uses `deskUrl` from config or Settings service
|
|
123
|
+
- Browser only
|
|
124
|
+
|
|
125
|
+
**Planned Methods** (TODO):
|
|
126
|
+
- `signInWithSSO()`
|
|
127
|
+
- `signOut()`
|
|
128
|
+
- `refreshSession()`
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### 2. User Service
|
|
133
|
+
|
|
134
|
+
**Status**: ✅ Functional (80% complete)
|
|
135
|
+
|
|
136
|
+
**Import**:
|
|
137
|
+
```typescript
|
|
138
|
+
import { User } from '@taruvi/sdk'
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**Constructor**:
|
|
142
|
+
```typescript
|
|
143
|
+
new User(client: Client)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Methods**:
|
|
147
|
+
|
|
148
|
+
#### `getUserData()`
|
|
149
|
+
```typescript
|
|
150
|
+
async getUserData(): Promise<UserDataResponse>
|
|
151
|
+
```
|
|
152
|
+
- Fetches current authenticated user profile
|
|
153
|
+
- Endpoint: `GET api/users/me/`
|
|
154
|
+
|
|
155
|
+
**Response Type**:
|
|
156
|
+
```typescript
|
|
157
|
+
interface UserDataResponse {
|
|
158
|
+
id: number
|
|
159
|
+
username: string
|
|
160
|
+
email: string
|
|
161
|
+
first_name: string
|
|
162
|
+
last_name: string
|
|
163
|
+
full_name: string
|
|
164
|
+
is_active: boolean
|
|
165
|
+
is_staff: boolean
|
|
166
|
+
is_deleted: boolean
|
|
167
|
+
date_joined: string // ISO 8601
|
|
168
|
+
last_login: string // ISO 8601
|
|
169
|
+
attributes: string
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### `createUser()`
|
|
174
|
+
```typescript
|
|
175
|
+
async createUser(userData: UserCreateRequest): Promise<UserCreateResponse>
|
|
176
|
+
```
|
|
177
|
+
- Creates new user account
|
|
178
|
+
- Endpoint: `POST api/users/`
|
|
179
|
+
|
|
180
|
+
**Request Type**:
|
|
181
|
+
```typescript
|
|
182
|
+
interface UserCreateRequest {
|
|
183
|
+
username: string
|
|
184
|
+
email: string
|
|
185
|
+
password: string
|
|
186
|
+
confirm_password: string
|
|
187
|
+
first_name: string
|
|
188
|
+
last_name: string
|
|
189
|
+
is_active: boolean
|
|
190
|
+
is_staff: boolean
|
|
191
|
+
attributes: string
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
#### `updateUser()`
|
|
196
|
+
```typescript
|
|
197
|
+
async updateUser(username: string, body: UserUpdateRequest): Promise<UserCreateResponse>
|
|
198
|
+
```
|
|
199
|
+
- Updates existing user
|
|
200
|
+
- Endpoint: `PUT api/users/{username}/`
|
|
201
|
+
|
|
202
|
+
**Request Type**:
|
|
203
|
+
```typescript
|
|
204
|
+
interface UserUpdateRequest {
|
|
205
|
+
username?: string
|
|
206
|
+
email?: string
|
|
207
|
+
password?: string
|
|
208
|
+
confirm_password?: string
|
|
209
|
+
first_name?: string
|
|
210
|
+
last_name?: string
|
|
211
|
+
is_active?: boolean
|
|
212
|
+
is_staff?: boolean
|
|
213
|
+
attributes?: string
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### `deleteUser()`
|
|
218
|
+
```typescript
|
|
219
|
+
async deleteUser(username: string): Promise<void>
|
|
220
|
+
```
|
|
221
|
+
- Deletes user account
|
|
222
|
+
- Endpoint: `DELETE api/users/{username}/`
|
|
223
|
+
|
|
224
|
+
#### `list()`
|
|
225
|
+
```typescript
|
|
226
|
+
async list(filters: UserList): Promise<unknown>
|
|
227
|
+
```
|
|
228
|
+
- Lists users with filters
|
|
229
|
+
- Endpoint: `GET api/users/?{queryString}`
|
|
230
|
+
|
|
231
|
+
**Filter Type**:
|
|
232
|
+
```typescript
|
|
233
|
+
interface UserList {
|
|
234
|
+
search: string
|
|
235
|
+
is_active: boolean
|
|
236
|
+
is_staff: boolean
|
|
237
|
+
is_superuser: boolean
|
|
238
|
+
is_deleted: boolean
|
|
239
|
+
ordering: string
|
|
240
|
+
page: Number
|
|
241
|
+
page_size: Number
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
### 3. Storage Service
|
|
248
|
+
|
|
249
|
+
**Status**: 🚧 Partial (70% complete)
|
|
250
|
+
|
|
251
|
+
**Import**:
|
|
252
|
+
```typescript
|
|
253
|
+
import { Storage } from '@taruvi/sdk'
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Constructor**:
|
|
257
|
+
```typescript
|
|
258
|
+
new Storage(client: Client, urlParams: BucketUrlParams, operation?: HttpMethod, body?: object, filters?: StorageFilters)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Pattern**: Chainable query builder (immutable)
|
|
262
|
+
|
|
263
|
+
**Methods**:
|
|
264
|
+
|
|
265
|
+
#### `from()`
|
|
266
|
+
```typescript
|
|
267
|
+
from(bucket: string): Storage
|
|
268
|
+
```
|
|
269
|
+
- Selects storage bucket
|
|
270
|
+
- Returns new Storage instance
|
|
271
|
+
|
|
272
|
+
#### `filter()`
|
|
273
|
+
```typescript
|
|
274
|
+
filter(filters: StorageFilters): Storage
|
|
275
|
+
```
|
|
276
|
+
- Applies query filters
|
|
277
|
+
- Returns new Storage instance
|
|
278
|
+
|
|
279
|
+
#### `upload()`
|
|
280
|
+
```typescript
|
|
281
|
+
upload(filesData: { files: File[], metadatas: object[], paths: string[] }): Storage
|
|
282
|
+
```
|
|
283
|
+
- Prepares file upload
|
|
284
|
+
- Creates FormData with files, paths, metadata
|
|
285
|
+
- Returns new Storage instance
|
|
286
|
+
|
|
287
|
+
#### `download()`
|
|
288
|
+
```typescript
|
|
289
|
+
download(path: string): Storage
|
|
290
|
+
```
|
|
291
|
+
- Prepares file download
|
|
292
|
+
- Returns new Storage instance
|
|
293
|
+
|
|
294
|
+
#### `update()`
|
|
295
|
+
```typescript
|
|
296
|
+
update(path: string, body: object): Storage
|
|
297
|
+
```
|
|
298
|
+
- Prepares file metadata update
|
|
299
|
+
- Returns new Storage instance
|
|
300
|
+
|
|
301
|
+
#### `delete()`
|
|
302
|
+
```typescript
|
|
303
|
+
delete(path: string): Storage
|
|
304
|
+
```
|
|
305
|
+
- Prepares file deletion
|
|
306
|
+
- Returns new Storage instance
|
|
307
|
+
|
|
308
|
+
#### `execute()`
|
|
309
|
+
```typescript
|
|
310
|
+
async execute<T>(): Promise<T>
|
|
311
|
+
```
|
|
312
|
+
- Executes the built query
|
|
313
|
+
- Returns response based on operation type
|
|
314
|
+
|
|
315
|
+
**Usage Example**:
|
|
316
|
+
```typescript
|
|
317
|
+
const storage = new Storage(client, {})
|
|
318
|
+
|
|
319
|
+
// List files in bucket
|
|
320
|
+
const files = await storage
|
|
321
|
+
.from('my-bucket')
|
|
322
|
+
.filter({ mimetype_category: 'image' })
|
|
323
|
+
.execute()
|
|
324
|
+
|
|
325
|
+
// Upload files
|
|
326
|
+
await storage
|
|
327
|
+
.from('my-bucket')
|
|
328
|
+
.upload({
|
|
329
|
+
files: [file1, file2],
|
|
330
|
+
paths: ['path1', 'path2'],
|
|
331
|
+
metadatas: [{}, {}]
|
|
332
|
+
})
|
|
333
|
+
.execute()
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
**Filter Options** (StorageFilters):
|
|
337
|
+
```typescript
|
|
338
|
+
interface StorageFilters {
|
|
339
|
+
// Pagination
|
|
340
|
+
page?: number
|
|
341
|
+
pageSize?: number
|
|
342
|
+
|
|
343
|
+
// Size filters (bytes)
|
|
344
|
+
size__gte?: number
|
|
345
|
+
size__lte?: number
|
|
346
|
+
min_size?: number
|
|
347
|
+
max_size?: number
|
|
348
|
+
|
|
349
|
+
// Date filters (ISO 8601)
|
|
350
|
+
created_at__gte?: string
|
|
351
|
+
created_at__lte?: string
|
|
352
|
+
created_after?: string
|
|
353
|
+
created_before?: string
|
|
354
|
+
|
|
355
|
+
// Search
|
|
356
|
+
search?: string
|
|
357
|
+
filename__icontains?: string
|
|
358
|
+
prefix?: string
|
|
359
|
+
|
|
360
|
+
// MIME type
|
|
361
|
+
mimetype?: string
|
|
362
|
+
mimetype_category?: 'image' | 'video' | 'audio' | 'application' | 'text'
|
|
363
|
+
|
|
364
|
+
// Visibility
|
|
365
|
+
visibility?: 'public' | 'private'
|
|
366
|
+
|
|
367
|
+
// User filters
|
|
368
|
+
created_by_me?: boolean
|
|
369
|
+
created_by__username?: string
|
|
370
|
+
|
|
371
|
+
// Sorting
|
|
372
|
+
ordering?: string
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
### 4. Database Service
|
|
379
|
+
|
|
380
|
+
**Status**: 🚧 Partial (70% complete)
|
|
381
|
+
|
|
382
|
+
**Import**:
|
|
383
|
+
```typescript
|
|
384
|
+
import { Database } from '@taruvi/sdk'
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
**Constructor**:
|
|
388
|
+
```typescript
|
|
389
|
+
new Database(client: Client, urlParams: UrlParams, operation?: HttpMethod, body?: object, filters?: DatabaseFilters)
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
**Pattern**: Chainable query builder (immutable)
|
|
393
|
+
|
|
394
|
+
**Methods**:
|
|
395
|
+
|
|
396
|
+
#### `from()`
|
|
397
|
+
```typescript
|
|
398
|
+
from(dataTables: string): Database
|
|
399
|
+
```
|
|
400
|
+
- Selects data table
|
|
401
|
+
- Returns new Database instance
|
|
402
|
+
|
|
403
|
+
#### `filter()`
|
|
404
|
+
```typescript
|
|
405
|
+
filter(filters: DatabaseFilters): Database
|
|
406
|
+
```
|
|
407
|
+
- Applies query filters
|
|
408
|
+
- Returns new Database instance
|
|
409
|
+
|
|
410
|
+
#### `get()`
|
|
411
|
+
```typescript
|
|
412
|
+
get(recordId: string): Database
|
|
413
|
+
```
|
|
414
|
+
- Selects specific record
|
|
415
|
+
- Returns new Database instance
|
|
416
|
+
|
|
417
|
+
#### `update()`
|
|
418
|
+
```typescript
|
|
419
|
+
update(body: any): Database
|
|
420
|
+
```
|
|
421
|
+
- Prepares record update
|
|
422
|
+
- Returns new Database instance
|
|
423
|
+
|
|
424
|
+
#### `delete()`
|
|
425
|
+
```typescript
|
|
426
|
+
delete(recordId?: any): Database
|
|
427
|
+
```
|
|
428
|
+
- Prepares record deletion
|
|
429
|
+
- Returns new Database instance
|
|
430
|
+
|
|
431
|
+
#### `execute()`
|
|
432
|
+
```typescript
|
|
433
|
+
async execute(): Promise<unknown>
|
|
434
|
+
```
|
|
435
|
+
- Executes the built query
|
|
436
|
+
- Returns response based on operation
|
|
437
|
+
|
|
438
|
+
**Usage Example**:
|
|
439
|
+
```typescript
|
|
440
|
+
const database = new Database(client, {})
|
|
441
|
+
|
|
442
|
+
// List records
|
|
443
|
+
const posts = await database
|
|
444
|
+
.from('posts')
|
|
445
|
+
.filter({ page: 1, ordering: '-created_at' })
|
|
446
|
+
.execute()
|
|
447
|
+
|
|
448
|
+
// Get specific record
|
|
449
|
+
const post = await database
|
|
450
|
+
.from('posts')
|
|
451
|
+
.get('post_123')
|
|
452
|
+
.execute()
|
|
453
|
+
|
|
454
|
+
// Update record
|
|
455
|
+
await database
|
|
456
|
+
.from('posts')
|
|
457
|
+
.get('post_123')
|
|
458
|
+
.update({ title: 'New Title' })
|
|
459
|
+
.execute()
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Filter Options** (DatabaseFilters):
|
|
463
|
+
```typescript
|
|
464
|
+
interface DatabaseFilters {
|
|
465
|
+
page?: number
|
|
466
|
+
pageSize?: number
|
|
467
|
+
ordering?: string // "-field" for desc, "field" for asc
|
|
468
|
+
[key: string]: string | number | boolean | undefined // Dynamic filters
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
### 5. Functions Service
|
|
475
|
+
|
|
476
|
+
**Status**: ✅ Functional (70% complete)
|
|
477
|
+
|
|
478
|
+
**Import**:
|
|
479
|
+
```typescript
|
|
480
|
+
import { Functions } from '@taruvi/sdk'
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
**Constructor**:
|
|
484
|
+
```typescript
|
|
485
|
+
new Functions(client: Client)
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
**Methods**:
|
|
489
|
+
|
|
490
|
+
#### `execute()`
|
|
491
|
+
```typescript
|
|
492
|
+
async execute<T = unknown>(functionSlug: string, options?: FunctionRequest): Promise<FunctionResponse<T>>
|
|
493
|
+
```
|
|
494
|
+
- Invokes serverless function
|
|
495
|
+
- Endpoint: `POST api/apps/{appSlug}/functions/{functionSlug}/execute/`
|
|
496
|
+
|
|
497
|
+
**Request Type**:
|
|
498
|
+
```typescript
|
|
499
|
+
interface FunctionRequest {
|
|
500
|
+
async?: boolean // Default: false
|
|
501
|
+
params?: Record<string, unknown> // Function parameters
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Response Type**:
|
|
506
|
+
```typescript
|
|
507
|
+
interface FunctionResponse<T = unknown> {
|
|
508
|
+
data: T | null
|
|
509
|
+
invocation: {
|
|
510
|
+
invocation_id: number
|
|
511
|
+
celery_task_id: string
|
|
512
|
+
status: string
|
|
513
|
+
created_at: string
|
|
514
|
+
updated_at: string
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Usage Example**:
|
|
520
|
+
```typescript
|
|
521
|
+
const functions = new Functions(client)
|
|
522
|
+
|
|
523
|
+
const result = await functions.execute('send-email', {
|
|
524
|
+
async: false,
|
|
525
|
+
params: {
|
|
526
|
+
to: 'user@example.com',
|
|
527
|
+
subject: 'Hello'
|
|
528
|
+
}
|
|
529
|
+
})
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
### 6. Settings Service
|
|
535
|
+
|
|
536
|
+
**Status**: ✅ Functional (70% complete)
|
|
537
|
+
|
|
538
|
+
**Import**:
|
|
539
|
+
```typescript
|
|
540
|
+
import { Settings } from '@taruvi/sdk'
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Constructor**:
|
|
544
|
+
```typescript
|
|
545
|
+
new Settings(client: Client)
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**Methods**:
|
|
549
|
+
|
|
550
|
+
#### `get()`
|
|
551
|
+
```typescript
|
|
552
|
+
async get<T = unknown>(): Promise<T>
|
|
553
|
+
```
|
|
554
|
+
- Fetches site configuration/metadata
|
|
555
|
+
- Endpoint: `GET api/settings/metadata/`
|
|
556
|
+
- Generic return type for flexible response structure
|
|
557
|
+
|
|
558
|
+
**Usage Example**:
|
|
559
|
+
```typescript
|
|
560
|
+
const settings = new Settings(client)
|
|
561
|
+
|
|
562
|
+
const config = await settings.get()
|
|
563
|
+
console.log(config.site_slug)
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
### 7. Secrets Service
|
|
569
|
+
|
|
570
|
+
**Status**: ✅ Functional (70% complete)
|
|
571
|
+
|
|
572
|
+
**Import**:
|
|
573
|
+
```typescript
|
|
574
|
+
import { Secrets } from '@taruvi/sdk'
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
**Constructor**:
|
|
578
|
+
```typescript
|
|
579
|
+
new Secrets(client: Client, urlParams?: SecretsUrlParams, body?: object, method?: HttpMethod)
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
**Pattern**: Chainable query builder (immutable)
|
|
583
|
+
|
|
584
|
+
**Methods**:
|
|
585
|
+
|
|
586
|
+
#### `list()`
|
|
587
|
+
```typescript
|
|
588
|
+
list(): Secrets
|
|
589
|
+
```
|
|
590
|
+
- Lists all secrets
|
|
591
|
+
- Returns new Secrets instance
|
|
592
|
+
|
|
593
|
+
#### `get()`
|
|
594
|
+
```typescript
|
|
595
|
+
get(key: string): Secrets
|
|
596
|
+
```
|
|
597
|
+
- Gets specific secret by key
|
|
598
|
+
- Returns new Secrets instance
|
|
599
|
+
|
|
600
|
+
#### `update()`
|
|
601
|
+
```typescript
|
|
602
|
+
update(key: string, body: object): Secrets
|
|
603
|
+
```
|
|
604
|
+
- Updates secret value
|
|
605
|
+
- Returns new Secrets instance
|
|
606
|
+
|
|
607
|
+
#### `execute()`
|
|
608
|
+
```typescript
|
|
609
|
+
async execute<T = unknown>(): Promise<T>
|
|
610
|
+
```
|
|
611
|
+
- Executes the built query
|
|
612
|
+
- Returns response
|
|
613
|
+
|
|
614
|
+
**Usage Example**:
|
|
615
|
+
```typescript
|
|
616
|
+
const secrets = new Secrets(client)
|
|
617
|
+
|
|
618
|
+
// List secrets
|
|
619
|
+
const allSecrets = await secrets.list().execute()
|
|
620
|
+
|
|
621
|
+
// Get specific secret
|
|
622
|
+
const apiKey = await secrets.get('api_key').execute()
|
|
623
|
+
|
|
624
|
+
// Update secret
|
|
625
|
+
await secrets.update('api_key', { value: 'new_value' }).execute()
|
|
626
|
+
```
|
|
627
|
+
|
|
628
|
+
---
|
|
629
|
+
|
|
630
|
+
## Internal Architecture (Not Public API)
|
|
631
|
+
|
|
632
|
+
### HttpClient (@internal)
|
|
633
|
+
|
|
634
|
+
**Purpose**: Handles all HTTP requests with automatic auth headers
|
|
635
|
+
|
|
636
|
+
**Constructor**:
|
|
637
|
+
```typescript
|
|
638
|
+
new HttpClient(config: TaruviConfig, tokenClient: TokenClient)
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**Authentication Headers**:
|
|
642
|
+
- `Content-Type: application/json` (except FormData)
|
|
643
|
+
- `Authorization: Token {apiKey}` (site/app identification)
|
|
644
|
+
- `Authorization: Bearer {jwt}` (user session, overrides API key if present)
|
|
645
|
+
|
|
646
|
+
**Methods**:
|
|
647
|
+
- `get<T>(endpoint: string): Promise<T>`
|
|
648
|
+
- `post<T, D>(endpoint: string, body: D): Promise<T>`
|
|
649
|
+
- `put<T, D>(endpoint: string, body: D): Promise<T>`
|
|
650
|
+
- `delete<T, D>(endpoint: string, body?: D): Promise<T>`
|
|
651
|
+
- `patch<T, D>(endpoint: string, body: D): Promise<T>`
|
|
652
|
+
|
|
653
|
+
**FormData Handling**:
|
|
654
|
+
- Detects `body instanceof FormData`
|
|
655
|
+
- Omits `Content-Type` header (axios sets with boundary)
|
|
656
|
+
|
|
657
|
+
---
|
|
658
|
+
|
|
659
|
+
### TokenClient (@internal)
|
|
660
|
+
|
|
661
|
+
**Purpose**: Manages JWT token storage and retrieval
|
|
662
|
+
|
|
663
|
+
**Constructor**:
|
|
664
|
+
```typescript
|
|
665
|
+
new TokenClient(token?: string)
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
**Runtime Detection**:
|
|
669
|
+
- Detects Browser vs Server environment
|
|
670
|
+
- Uses `localStorage` in browser
|
|
671
|
+
- Returns `null` in server environment
|
|
672
|
+
|
|
673
|
+
**Methods**:
|
|
674
|
+
- `getToken(): string | null` - Retrieves JWT from localStorage (`jwt` key)
|
|
675
|
+
|
|
676
|
+
**Planned Methods** (TODO):
|
|
677
|
+
- `setToken()`
|
|
678
|
+
- `refreshToken()`
|
|
679
|
+
- `clearToken()`
|
|
680
|
+
- `isTokenExpired()`
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
### Utility Functions
|
|
685
|
+
|
|
686
|
+
#### `buildQueryString()`
|
|
687
|
+
```typescript
|
|
688
|
+
function buildQueryString(filters: Record<string, unknown> | undefined): string
|
|
689
|
+
```
|
|
690
|
+
- Converts filter object to URL query string
|
|
691
|
+
- Skips `undefined` and `null` values
|
|
692
|
+
- Returns `?key=value&key2=value2` or empty string
|
|
693
|
+
|
|
694
|
+
#### `getRuntimeEnvironment()`
|
|
695
|
+
```typescript
|
|
696
|
+
function getRuntimeEnvironment(): string
|
|
697
|
+
```
|
|
698
|
+
- Returns: `'Browser'`, `'ReactNative'`, or `'Server'`
|
|
699
|
+
|
|
700
|
+
#### `isBrowser()`
|
|
701
|
+
```typescript
|
|
702
|
+
function isBrowser(): boolean
|
|
703
|
+
```
|
|
704
|
+
- Checks if `window` and `document` exist
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
## Error Handling
|
|
709
|
+
|
|
710
|
+
**Current State**: Basic error propagation from axios
|
|
711
|
+
|
|
712
|
+
**Planned** (TODO):
|
|
713
|
+
- Custom error classes
|
|
714
|
+
- Error codes
|
|
715
|
+
- Retry logic
|
|
716
|
+
- Better error messages
|
|
717
|
+
|
|
718
|
+
---
|
|
719
|
+
|
|
720
|
+
## TypeScript Types
|
|
721
|
+
|
|
722
|
+
### Exported Types
|
|
723
|
+
|
|
724
|
+
All types are exported from main entry point:
|
|
725
|
+
|
|
726
|
+
```typescript
|
|
727
|
+
// Config
|
|
728
|
+
export type { TaruviConfig }
|
|
729
|
+
|
|
730
|
+
// Filters
|
|
731
|
+
export type { StorageFilters, DatabaseFilters }
|
|
732
|
+
|
|
733
|
+
// User
|
|
734
|
+
export type {
|
|
735
|
+
UserCreateRequest,
|
|
736
|
+
UserResponse,
|
|
737
|
+
UserDataResponse,
|
|
738
|
+
UserUpdateRequest
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Functions
|
|
742
|
+
export type {
|
|
743
|
+
FunctionRequest,
|
|
744
|
+
FunctionResponse,
|
|
745
|
+
FunctionInvocation
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Database
|
|
749
|
+
export type {
|
|
750
|
+
DatabaseRequest,
|
|
751
|
+
DatabaseResponse
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Storage
|
|
755
|
+
export type {
|
|
756
|
+
StorageRequest,
|
|
757
|
+
StorageUpdateRequest,
|
|
758
|
+
StorageResponse
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Settings
|
|
762
|
+
export type {
|
|
763
|
+
SettingsResponse
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Secrets
|
|
767
|
+
export type {
|
|
768
|
+
SecretRequest,
|
|
769
|
+
SecretResponse
|
|
770
|
+
}
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
---
|
|
774
|
+
|
|
775
|
+
## Common Patterns
|
|
776
|
+
|
|
777
|
+
### Pattern 1: Simple Service Usage
|
|
778
|
+
```typescript
|
|
779
|
+
const client = new Client(config)
|
|
780
|
+
const user = new User(client)
|
|
781
|
+
const data = await user.getUserData()
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
### Pattern 2: Query Builder (Storage/Database)
|
|
785
|
+
```typescript
|
|
786
|
+
const storage = new Storage(client, {})
|
|
787
|
+
const files = await storage
|
|
788
|
+
.from('bucket')
|
|
789
|
+
.filter({ page: 1 })
|
|
790
|
+
.execute()
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
### Pattern 3: Multiple Environments
|
|
794
|
+
```typescript
|
|
795
|
+
const prodClient = new Client({ apiKey: 'prod', appSlug: 'prod', baseUrl: 'prod-url' })
|
|
796
|
+
const devClient = new Client({ apiKey: 'dev', appSlug: 'dev', baseUrl: 'dev-url' })
|
|
797
|
+
|
|
798
|
+
const prodUser = new User(prodClient)
|
|
799
|
+
const devUser = new User(devClient)
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
### Pattern 4: Multi-Tenancy
|
|
803
|
+
```typescript
|
|
804
|
+
const customerClient = new Client({
|
|
805
|
+
apiKey: 'site_key',
|
|
806
|
+
appSlug: 'customer-portal',
|
|
807
|
+
baseUrl: 'https://api.taruvi.cloud'
|
|
808
|
+
})
|
|
809
|
+
|
|
810
|
+
const adminClient = new Client({
|
|
811
|
+
apiKey: 'site_key', // Same site
|
|
812
|
+
appSlug: 'admin-dashboard', // Different app (isolated data)
|
|
813
|
+
baseUrl: 'https://api.taruvi.cloud'
|
|
814
|
+
})
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
---
|
|
818
|
+
|
|
819
|
+
## Framework Integration
|
|
820
|
+
|
|
821
|
+
### React Context Pattern
|
|
822
|
+
```typescript
|
|
823
|
+
import { createContext, useContext } from 'react'
|
|
824
|
+
import { Client } from '@taruvi/sdk'
|
|
825
|
+
|
|
826
|
+
const TaruviContext = createContext<Client | null>(null)
|
|
827
|
+
|
|
828
|
+
export function TaruviProvider({ children }) {
|
|
829
|
+
const client = new Client({ /* config */ })
|
|
830
|
+
return <TaruviContext.Provider value={client}>{children}</TaruviContext.Provider>
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
export function useTaruvi() {
|
|
834
|
+
const client = useContext(TaruviContext)
|
|
835
|
+
if (!client) throw new Error('useTaruvi must be used within TaruviProvider')
|
|
836
|
+
return client
|
|
837
|
+
}
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
### Vue 3 Provide/Inject Pattern
|
|
841
|
+
```typescript
|
|
842
|
+
import { provide, inject, InjectionKey } from 'vue'
|
|
843
|
+
import { Client } from '@taruvi/sdk'
|
|
844
|
+
|
|
845
|
+
const TaruviKey: InjectionKey<Client> = Symbol('taruvi')
|
|
846
|
+
|
|
847
|
+
export function setupTaruvi() {
|
|
848
|
+
const client = new Client({ /* config */ })
|
|
849
|
+
provide(TaruviKey, client)
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
export function useTaruvi() {
|
|
853
|
+
const client = inject(TaruviKey)
|
|
854
|
+
if (!client) throw new Error('Taruvi not provided')
|
|
855
|
+
return client
|
|
856
|
+
}
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
## API Endpoint Patterns
|
|
862
|
+
|
|
863
|
+
### Base URL Structure
|
|
864
|
+
```
|
|
865
|
+
{baseUrl}/api/{resource}/{action}
|
|
866
|
+
```
|
|
867
|
+
|
|
868
|
+
### User Endpoints
|
|
869
|
+
- `GET api/users/me/` - Current user
|
|
870
|
+
- `POST api/users/` - Create user
|
|
871
|
+
- `PUT api/users/{username}/` - Update user
|
|
872
|
+
- `DELETE api/users/{username}/` - Delete user
|
|
873
|
+
- `GET api/users/?{filters}` - List users
|
|
874
|
+
|
|
875
|
+
### Storage Endpoints
|
|
876
|
+
- `GET api/apps/{appSlug}/storage/{bucket}/?{filters}` - List files
|
|
877
|
+
- `POST api/apps/{appSlug}/storage/{bucket}/upload/` - Upload files
|
|
878
|
+
- `GET api/apps/{appSlug}/storage/{bucket}/{path}/` - Download file
|
|
879
|
+
- `PUT api/apps/{appSlug}/storage/{bucket}/{path}/` - Update metadata
|
|
880
|
+
- `DELETE api/apps/{appSlug}/storage/{bucket}/{path}/` - Delete file
|
|
881
|
+
|
|
882
|
+
### Database Endpoints
|
|
883
|
+
- `GET sites/eox_site/api/apps/{appSlug}/data/{table}/?{filters}` - List records
|
|
884
|
+
- `GET sites/eox_site/api/apps/{appSlug}/data/{table}/{recordId}/` - Get record
|
|
885
|
+
- `POST sites/eox_site/api/apps/{appSlug}/data/{table}/` - Create record
|
|
886
|
+
- `PUT sites/eox_site/api/apps/{appSlug}/data/{table}/{recordId}/` - Update record
|
|
887
|
+
- `DELETE sites/eox_site/api/apps/{appSlug}/data/{table}/{recordId}/` - Delete record
|
|
888
|
+
|
|
889
|
+
### Functions Endpoints
|
|
890
|
+
- `POST api/apps/{appSlug}/functions/{functionSlug}/execute/` - Execute function
|
|
891
|
+
|
|
892
|
+
### Settings Endpoints
|
|
893
|
+
- `GET api/settings/metadata/` - Get site config
|
|
894
|
+
|
|
895
|
+
### Secrets Endpoints
|
|
896
|
+
- `GET api/secrets/` - List secrets
|
|
897
|
+
- `GET api/secrets/{key}/` - Get secret
|
|
898
|
+
- `PUT api/secrets/{key}/` - Update secret
|
|
899
|
+
|
|
900
|
+
---
|
|
901
|
+
|
|
902
|
+
## Development Status
|
|
903
|
+
|
|
904
|
+
### Completed (✅)
|
|
905
|
+
- Core Client architecture
|
|
906
|
+
- HttpClient with auth headers
|
|
907
|
+
- TokenClient with localStorage
|
|
908
|
+
- User CRUD operations
|
|
909
|
+
- Functions execution
|
|
910
|
+
- Settings retrieval
|
|
911
|
+
- Secrets management
|
|
912
|
+
- Storage query builder (basic)
|
|
913
|
+
- Database query builder (basic)
|
|
914
|
+
|
|
915
|
+
### Partial (🚧)
|
|
916
|
+
- Auth service (login/logout incomplete)
|
|
917
|
+
- Storage service (upload/download working, needs file operations)
|
|
918
|
+
- Database service (CRUD working, needs advanced queries)
|
|
919
|
+
- Token management (get working, refresh/expiry pending)
|
|
920
|
+
|
|
921
|
+
### Planned (📋)
|
|
922
|
+
- Error handling classes
|
|
923
|
+
- Token refresh logic
|
|
924
|
+
- Retry mechanism
|
|
925
|
+
- PATCH HTTP method
|
|
926
|
+
- Real-time subscriptions
|
|
927
|
+
- Offline support
|
|
928
|
+
- Comprehensive tests
|
|
929
|
+
|
|
930
|
+
---
|
|
931
|
+
|
|
932
|
+
## Key Constraints
|
|
933
|
+
|
|
934
|
+
1. **Browser-Only Features**:
|
|
935
|
+
- localStorage (JWT storage)
|
|
936
|
+
- OAuth callback token extraction
|
|
937
|
+
- `redirectToLogin()`
|
|
938
|
+
|
|
939
|
+
2. **Multi-Tenancy**:
|
|
940
|
+
- Same `apiKey` (site) can have multiple `appSlug` (apps)
|
|
941
|
+
- Data is isolated per app
|
|
942
|
+
|
|
943
|
+
3. **Authentication Hierarchy**:
|
|
944
|
+
- API Key: Site/app identification (always sent)
|
|
945
|
+
- JWT Token: User session (overrides API key when present)
|
|
946
|
+
|
|
947
|
+
4. **Immutable Query Builders**:
|
|
948
|
+
- Storage and Database use immutable pattern
|
|
949
|
+
- Each method returns new instance
|
|
950
|
+
- Must call `.execute()` to run query
|
|
951
|
+
|
|
952
|
+
5. **TypeScript Strict Mode**:
|
|
953
|
+
- All types are strictly typed
|
|
954
|
+
- No implicit `any`
|
|
955
|
+
- Full type inference
|
|
956
|
+
|
|
957
|
+
---
|
|
958
|
+
|
|
959
|
+
## Testing Guidance
|
|
960
|
+
|
|
961
|
+
### Mocking Client
|
|
962
|
+
```typescript
|
|
963
|
+
const mockClient = {
|
|
964
|
+
httpClient: {
|
|
965
|
+
get: vi.fn(),
|
|
966
|
+
post: vi.fn(),
|
|
967
|
+
put: vi.fn(),
|
|
968
|
+
delete: vi.fn()
|
|
969
|
+
},
|
|
970
|
+
tokenClient: {
|
|
971
|
+
getToken: vi.fn()
|
|
972
|
+
},
|
|
973
|
+
getConfig: () => ({ apiKey: 'test', appSlug: 'test', baseUrl: 'test' })
|
|
974
|
+
} as unknown as Client
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
### Mocking Services
|
|
978
|
+
```typescript
|
|
979
|
+
const user = new User(mockClient)
|
|
980
|
+
mockClient.httpClient.get.mockResolvedValue({ username: 'test' })
|
|
981
|
+
const data = await user.getUserData()
|
|
982
|
+
```
|
|
983
|
+
|
|
984
|
+
---
|
|
985
|
+
|
|
986
|
+
## Migration Notes
|
|
987
|
+
|
|
988
|
+
### From Singleton Pattern
|
|
989
|
+
**Before**:
|
|
990
|
+
```typescript
|
|
991
|
+
import taruvi from 'taruvi-sdk'
|
|
992
|
+
taruvi.init({ apiKey: 'key' })
|
|
993
|
+
const user = taruvi.user.get()
|
|
994
|
+
```
|
|
995
|
+
|
|
996
|
+
**After**:
|
|
997
|
+
```typescript
|
|
998
|
+
import { Client, User } from '@taruvi/sdk'
|
|
999
|
+
const client = new Client({ apiKey: 'key', appSlug: 'app', baseUrl: 'url' })
|
|
1000
|
+
const user = new User(client)
|
|
1001
|
+
const data = await user.getUserData()
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
### Benefits
|
|
1005
|
+
- Multiple client instances
|
|
1006
|
+
- Better testability
|
|
1007
|
+
- Tree-shaking support
|
|
1008
|
+
- No global state
|
|
1009
|
+
- Explicit dependencies
|
|
1010
|
+
|
|
1011
|
+
---
|
|
1012
|
+
|
|
1013
|
+
## Bundle Size Optimization
|
|
1014
|
+
|
|
1015
|
+
### Tree-Shaking
|
|
1016
|
+
Only import what you need:
|
|
1017
|
+
```typescript
|
|
1018
|
+
// ✅ Good - only User service bundled
|
|
1019
|
+
import { Client, User } from '@taruvi/sdk'
|
|
1020
|
+
|
|
1021
|
+
// ❌ Bad - all services bundled
|
|
1022
|
+
import * as Taruvi from '@taruvi/sdk'
|
|
1023
|
+
```
|
|
1024
|
+
|
|
1025
|
+
### Lazy Loading
|
|
1026
|
+
Services are not pre-instantiated:
|
|
1027
|
+
```typescript
|
|
1028
|
+
const client = new Client(config)
|
|
1029
|
+
// Auth, User, Storage not loaded yet
|
|
1030
|
+
|
|
1031
|
+
const user = new User(client) // Only User loaded now
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
---
|
|
1035
|
+
|
|
1036
|
+
## Security Best Practices
|
|
1037
|
+
|
|
1038
|
+
1. **Never expose API keys in client code**
|
|
1039
|
+
- Use environment variables
|
|
1040
|
+
- Rotate keys regularly
|
|
1041
|
+
|
|
1042
|
+
2. **JWT Storage**
|
|
1043
|
+
- Stored in localStorage (XSS vulnerable)
|
|
1044
|
+
- Consider httpOnly cookies for production
|
|
1045
|
+
|
|
1046
|
+
3. **HTTPS Only**
|
|
1047
|
+
- Always use HTTPS baseUrl
|
|
1048
|
+
- Never send tokens over HTTP
|
|
1049
|
+
|
|
1050
|
+
4. **Token Expiration**
|
|
1051
|
+
- Implement token refresh (planned)
|
|
1052
|
+
- Handle expired token errors
|
|
1053
|
+
|
|
1054
|
+
---
|
|
1055
|
+
|
|
1056
|
+
## Common Issues
|
|
1057
|
+
|
|
1058
|
+
### Issue: "Config is required"
|
|
1059
|
+
**Cause**: Client instantiated without config
|
|
1060
|
+
**Fix**: Pass config object to Client constructor
|
|
1061
|
+
|
|
1062
|
+
### Issue: "API key is required"
|
|
1063
|
+
**Cause**: Config missing `apiKey`
|
|
1064
|
+
**Fix**: Add `apiKey` to config
|
|
1065
|
+
|
|
1066
|
+
### Issue: "Base URL is required"
|
|
1067
|
+
**Cause**: Config missing `baseUrl`
|
|
1068
|
+
**Fix**: Add `baseUrl` to config
|
|
1069
|
+
|
|
1070
|
+
### Issue: Token not found
|
|
1071
|
+
**Cause**: User not authenticated or localStorage unavailable
|
|
1072
|
+
**Fix**: Call auth.authenticateUser() or check browser environment
|
|
1073
|
+
|
|
1074
|
+
### Issue: Query builder not executing
|
|
1075
|
+
**Cause**: Forgot to call `.execute()`
|
|
1076
|
+
**Fix**: Always end query chain with `.execute()`
|
|
1077
|
+
|
|
1078
|
+
---
|
|
1079
|
+
|
|
1080
|
+
## Version History
|
|
1081
|
+
|
|
1082
|
+
### v1.1.0 (Current)
|
|
1083
|
+
- Dependency injection pattern
|
|
1084
|
+
- Lazy service loading
|
|
1085
|
+
- Tree-shaking support
|
|
1086
|
+
- Multi-tenancy support
|
|
1087
|
+
- Query builder pattern for Storage/Database
|
|
1088
|
+
|
|
1089
|
+
### v1.0.0
|
|
1090
|
+
- Initial release
|
|
1091
|
+
- Basic CRUD operations
|
|
1092
|
+
- Simple authentication
|
|
1093
|
+
|
|
1094
|
+
---
|
|
1095
|
+
|
|
1096
|
+
## Related Resources
|
|
1097
|
+
|
|
1098
|
+
- **README.md**: User-facing documentation
|
|
1099
|
+
- **adr.md**: Architecture Decision Record
|
|
1100
|
+
- **USAGE_EXAMPLE.md**: Code examples
|
|
1101
|
+
- **API Spec**: Taruvi API_latest.yaml
|
|
1102
|
+
|
|
1103
|
+
---
|
|
1104
|
+
|
|
1105
|
+
**Last Updated**: 2025-12-12
|
|
1106
|
+
**Maintained By**: Taruvi Team
|
|
1107
|
+
**License**: MIT
|
package/package.json
CHANGED
|
@@ -27,11 +27,6 @@ export class HttpClient {
|
|
|
27
27
|
headers['Content-Type'] = 'application/json'
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
// Site/app API key (developer authentication)
|
|
31
|
-
if (this.config.apiKey) {
|
|
32
|
-
headers['Authorization'] = `Token ${this.config.apiKey}`
|
|
33
|
-
}
|
|
34
|
-
|
|
35
30
|
// Tenant admin session token
|
|
36
31
|
const jwt = this.tokenClient.getToken()
|
|
37
32
|
if (jwt) {
|