@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taruvi/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Taruvi SDK",
5
5
  "main": "src/index.ts",
6
6
  "type": "module",
@@ -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) {