@seaverse/data-service-sdk 0.1.0 → 0.3.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/README.md CHANGED
@@ -1,14 +1,39 @@
1
1
  # @seaverse/data-service-sdk
2
2
 
3
- SeaVerse Data Service SDK for managing Firestore tokens in frontend applications.
3
+ SeaVerse Data Service SDK for accessing Firestore with secure token management and three-tier permission model.
4
4
 
5
5
  ## Features
6
6
 
7
- - Generate Firestore tokens for authenticated users
8
- - Generate Firestore tokens for guest users
9
- - TypeScript support with full type definitions
10
- - Promise-based API using axios
11
- - Automatic response unwrapping
7
+ - 🔐 Secure Firestore token generation for authenticated users and guests
8
+ - 🎯 Three-tier permission model (publicRead, publicData, userData)
9
+ - 🚀 Direct Firestore access - no proxy server needed
10
+ - 🔒 Automatic data isolation by app_id
11
+ - 📝 TypeScript support with full type definitions
12
+ - 🤖 LLM-friendly documentation with clear examples
13
+
14
+ ## Three-Tier Permission Model
15
+
16
+ SeaVerse organizes your Firestore data into three permission levels:
17
+
18
+ | Permission Level | Path Pattern | Read Access | Write Access | Use Case |
19
+ |-----------------|--------------|-------------|--------------|----------|
20
+ | **publicRead** | `appData/{app_id}/publicRead/...` | All authenticated users | Admin only | System configs, announcements, static content |
21
+ | **publicData** | `appData/{app_id}/publicData/...` | All authenticated users | All authenticated users | User posts, comments, shared content |
22
+ | **userData** | `appData/{app_id}/userData/{user_id}/...` | Owner only | Owner only | User settings, private notes, personal data |
23
+
24
+ ### Required Fields
25
+
26
+ All Firestore documents MUST include these three fields:
27
+
28
+ ```typescript
29
+ {
30
+ _appId: string, // Your application ID (for data isolation)
31
+ _createdAt: timestamp, // Server timestamp (use serverTimestamp())
32
+ _createdBy: string // User ID who created the document
33
+ }
34
+ ```
35
+
36
+ These fields are enforced by Firestore Security Rules and ensure proper data isolation.
12
37
 
13
38
  ## Installation
14
39
 
@@ -18,29 +43,101 @@ npm install @seaverse/data-service-sdk
18
43
 
19
44
  ## Quick Start
20
45
 
46
+ ### For Authenticated Users
47
+
21
48
  ```typescript
22
49
  import { DataServiceClient } from '@seaverse/data-service-sdk';
50
+ import { AuthClient } from '@seaverse/auth-sdk';
51
+ import { initializeApp } from 'firebase/app';
52
+ import { getAuth, signInWithCustomToken } from 'firebase/auth';
53
+ import { getFirestore, collection, addDoc, getDocs, serverTimestamp } from 'firebase/firestore';
54
+
55
+ // Step 1: Login user with Auth SDK
56
+ const authClient = new AuthClient({ appId: 'my-app-123' });
57
+ const loginResponse = await authClient.loginWithEmail({
58
+ email: 'user@example.com',
59
+ password: 'password123'
60
+ });
23
61
 
24
- // Create client instance
25
- const client = new DataServiceClient();
62
+ // Step 2: Get Firestore token
63
+ const dataClient = new DataServiceClient();
64
+ const firestoreToken = await dataClient.generateFirestoreToken({
65
+ token: loginResponse.token, // JWT from auth
66
+ app_id: 'my-app-123'
67
+ });
26
68
 
27
- // For authenticated users
28
- const token = await client.generateFirestoreToken({
29
- token: 'user-jwt-token',
30
- app_id: 'your-app-id',
69
+ // Step 3: Initialize Firebase
70
+ const app = initializeApp({ projectId: firestoreToken.project_id });
71
+ const auth = getAuth(app);
72
+ await signInWithCustomToken(auth, firestoreToken.custom_token);
73
+ const db = getFirestore(app);
74
+
75
+ // Step 4: Use Firestore with proper paths
76
+ const appId = firestoreToken.app_id;
77
+ const userId = firestoreToken.user_id;
78
+
79
+ // Write to publicData (everyone can write)
80
+ await addDoc(collection(db, `appData/${appId}/publicData/posts`), {
81
+ _appId: appId, // REQUIRED
82
+ _createdAt: serverTimestamp(), // REQUIRED
83
+ _createdBy: userId, // REQUIRED
84
+ title: 'My First Post',
85
+ content: 'Hello world!'
31
86
  });
32
87
 
33
- // For guest users
34
- const guestToken = await client.generateGuestFirestoreToken({
35
- app_id: 'your-app-id',
88
+ // Read from publicData
89
+ const snapshot = await getDocs(collection(db, `appData/${appId}/publicData/posts`));
90
+ snapshot.forEach(doc => {
91
+ console.log(doc.id, doc.data());
92
+ });
93
+
94
+ // Write to userData (private)
95
+ await addDoc(collection(db, `appData/${appId}/userData/${userId}/notes`), {
96
+ _appId: appId, // REQUIRED
97
+ _createdAt: serverTimestamp(), // REQUIRED
98
+ _createdBy: userId, // REQUIRED
99
+ title: 'Private Note',
100
+ content: 'Only I can see this'
101
+ });
102
+ ```
103
+
104
+ ### For Guest Users
105
+
106
+ ```typescript
107
+ import { DataServiceClient } from '@seaverse/data-service-sdk';
108
+ import { initializeApp } from 'firebase/app';
109
+ import { getAuth, signInWithCustomToken } from 'firebase/auth';
110
+ import { getFirestore, collection, addDoc, serverTimestamp } from 'firebase/firestore';
111
+
112
+ // Step 1: Get guest token (no authentication needed!)
113
+ const dataClient = new DataServiceClient();
114
+ const guestToken = await dataClient.generateGuestFirestoreToken({
115
+ app_id: 'my-app-123'
116
+ });
117
+
118
+ // Step 2: Initialize Firebase
119
+ const app = initializeApp({ projectId: guestToken.project_id });
120
+ const auth = getAuth(app);
121
+ await signInWithCustomToken(auth, guestToken.custom_token);
122
+ const db = getFirestore(app);
123
+
124
+ // Step 3: Guest can write to publicData
125
+ await addDoc(collection(db, `appData/${guestToken.app_id}/publicData/comments`), {
126
+ _appId: guestToken.app_id,
127
+ _createdAt: serverTimestamp(),
128
+ _createdBy: guestToken.user_id, // Guest user ID (e.g., 'guest-abc123')
129
+ comment: 'Great app!',
130
+ rating: 5
36
131
  });
132
+
133
+ // Note: Guests CANNOT access userData
37
134
  ```
38
135
 
39
136
  ## API Reference
40
137
 
41
138
  ### DataServiceClient
42
139
 
43
- The main client for interacting with the Data Service API.
140
+ The main client for generating Firestore tokens.
44
141
 
45
142
  #### Constructor
46
143
 
@@ -61,8 +158,8 @@ const client = new DataServiceClient({
61
158
  baseURL: 'https://auth.seaverse.ai',
62
159
  timeout: 15000,
63
160
  headers: {
64
- 'X-Custom-Header': 'value',
65
- },
161
+ 'X-Custom-Header': 'value'
162
+ }
66
163
  });
67
164
  ```
68
165
 
@@ -83,7 +180,7 @@ generateFirestoreToken(
83
180
 
84
181
  ```typescript
85
182
  interface GenerateFirestoreTokenRequest {
86
- token: string; // User's JWT token
183
+ token: string; // User's JWT token from Auth SDK
87
184
  app_id: string; // Application ID
88
185
  }
89
186
  ```
@@ -92,32 +189,27 @@ interface GenerateFirestoreTokenRequest {
92
189
 
93
190
  ```typescript
94
191
  interface FirestoreTokenResponse {
95
- id_token: string; // Firebase ID Token
96
- refresh_token?: string; // Firebase Refresh Token (optional)
97
- project_id: string; // Firebase Project ID
192
+ custom_token: string; // Firebase Custom Token - use with signInWithCustomToken()
193
+ project_id: string; // Firebase Project ID for initializeApp()
98
194
  database_id: string; // Firestore Database ID
99
- app_id?: string; // Application ID
100
- user_id: string; // User ID
101
- role?: string; // User role
102
- expires_in: number; // Expiration time in seconds (typically 3600)
195
+ app_id?: string; // Application ID (use in Firestore paths)
196
+ user_id: string; // User ID (use in Firestore paths)
197
+ role?: string; // User role ('guest', 'user', 'admin')
198
+ expires_in: number; // Token expiration in seconds (typically 3600)
103
199
  }
104
200
  ```
105
201
 
106
202
  **Example:**
107
203
 
108
204
  ```typescript
109
- try {
110
- const response = await client.generateFirestoreToken({
111
- token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
112
- app_id: 'my-app',
113
- });
205
+ const firestoreToken = await client.generateFirestoreToken({
206
+ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
207
+ app_id: 'my-app-123'
208
+ });
114
209
 
115
- console.log('Firestore ID Token:', response.id_token);
116
- console.log('User ID:', response.user_id);
117
- console.log('Expires in:', response.expires_in, 'seconds');
118
- } catch (error) {
119
- console.error('Failed to generate Firestore token:', error);
120
- }
210
+ console.log('Custom Token:', firestoreToken.custom_token);
211
+ console.log('User ID:', firestoreToken.user_id);
212
+ console.log('App ID:', firestoreToken.app_id);
121
213
  ```
122
214
 
123
215
  #### generateGuestFirestoreToken
@@ -141,126 +233,141 @@ interface GenerateGuestFirestoreTokenRequest {
141
233
 
142
234
  **Response:**
143
235
 
144
- Same as `FirestoreTokenResponse` above, with `role` typically set to `'guest'`.
236
+ Same as `FirestoreTokenResponse` above, with `role` set to `'guest'`.
145
237
 
146
238
  **Example:**
147
239
 
148
240
  ```typescript
149
- try {
150
- const response = await client.generateGuestFirestoreToken({
151
- app_id: 'my-app',
152
- });
241
+ const guestToken = await client.generateGuestFirestoreToken({
242
+ app_id: 'my-app-123'
243
+ });
153
244
 
154
- console.log('Guest Firestore ID Token:', response.id_token);
155
- console.log('Guest User ID:', response.user_id);
156
- console.log('Role:', response.role); // 'guest'
157
- } catch (error) {
158
- console.error('Failed to generate guest Firestore token:', error);
159
- }
245
+ console.log('Guest Custom Token:', guestToken.custom_token);
246
+ console.log('Guest User ID:', guestToken.user_id);
247
+ console.log('Role:', guestToken.role); // 'guest'
160
248
  ```
161
249
 
162
- ## Usage Examples
250
+ ## Common Use Cases
163
251
 
164
- ### Browser Environment
252
+ ### Use Case 1: Public Forum with Comments
165
253
 
166
254
  ```typescript
167
- import { DataServiceClient } from '@seaverse/data-service-sdk';
255
+ // Anyone (including guests) can post comments
256
+ await addDoc(collection(db, `appData/${appId}/publicData/comments`), {
257
+ _appId: appId,
258
+ _createdAt: serverTimestamp(),
259
+ _createdBy: userId,
260
+ postId: 'post-123',
261
+ comment: 'Great post!',
262
+ likes: 0
263
+ });
168
264
 
169
- const client = new DataServiceClient();
265
+ // Anyone can read comments
266
+ const comments = await getDocs(
267
+ collection(db, `appData/${appId}/publicData/comments`)
268
+ );
269
+ ```
170
270
 
171
- // Get user token from your auth system
172
- const userToken = localStorage.getItem('authToken');
271
+ ### Use Case 2: User Private Settings
173
272
 
174
- // Generate Firestore token
175
- const firestoreToken = await client.generateFirestoreToken({
176
- token: userToken,
177
- app_id: 'my-app',
273
+ ```typescript
274
+ // Only the user can write to their own settings
275
+ await setDoc(doc(db, `appData/${appId}/userData/${userId}/settings/preferences`), {
276
+ _appId: appId,
277
+ _createdAt: serverTimestamp(),
278
+ _createdBy: userId,
279
+ theme: 'dark',
280
+ notifications: true,
281
+ language: 'en'
178
282
  });
179
283
 
180
- // Use the Firestore token to initialize Firebase
181
- import { initializeApp } from 'firebase/app';
182
- import { getFirestore } from 'firebase/firestore';
183
- import { signInWithCustomToken, getAuth } from 'firebase/auth';
184
-
185
- const firebaseApp = initializeApp({
186
- projectId: firestoreToken.project_id,
187
- // ... other Firebase config
188
- });
284
+ // Only the user can read their own settings
285
+ const settings = await getDoc(
286
+ doc(db, `appData/${appId}/userData/${userId}/settings/preferences`)
287
+ );
288
+ ```
189
289
 
190
- const auth = getAuth(firebaseApp);
191
- await signInWithCustomToken(auth, firestoreToken.id_token);
290
+ ### Use Case 3: System Announcements (Admin Only)
192
291
 
193
- const db = getFirestore(firebaseApp);
194
- // Now you can use Firestore with the authenticated user
292
+ ```typescript
293
+ // Only admins can write to publicRead
294
+ // Regular users and guests can only read
295
+ const announcements = await getDocs(
296
+ collection(db, `appData/${appId}/publicRead/announcements`)
297
+ );
298
+
299
+ announcements.forEach(doc => {
300
+ console.log('Announcement:', doc.data().message);
301
+ });
195
302
  ```
196
303
 
197
- ### Node.js Environment
304
+ ### Use Case 4: Querying Public Data
198
305
 
199
306
  ```typescript
200
- import { DataServiceClient } from '@seaverse/data-service-sdk';
307
+ import { query, where, orderBy, limit } from 'firebase/firestore';
201
308
 
202
- const client = new DataServiceClient();
203
-
204
- async function getFirestoreAccess(userToken: string, appId: string) {
205
- try {
206
- const response = await client.generateFirestoreToken({
207
- token: userToken,
208
- app_id: appId,
209
- });
210
-
211
- return {
212
- token: response.id_token,
213
- projectId: response.project_id,
214
- databaseId: response.database_id,
215
- userId: response.user_id,
216
- expiresIn: response.expires_in,
217
- };
218
- } catch (error) {
219
- console.error('Error getting Firestore access:', error);
220
- throw error;
221
- }
222
- }
309
+ // Query posts created by a specific user
310
+ const userPosts = query(
311
+ collection(db, `appData/${appId}/publicData/posts`),
312
+ where('_createdBy', '==', userId),
313
+ orderBy('_createdAt', 'desc'),
314
+ limit(10)
315
+ );
316
+
317
+ const snapshot = await getDocs(userPosts);
223
318
  ```
224
319
 
225
- ### Guest User Example
320
+ ## Permission Examples
226
321
 
227
- ```typescript
228
- import { DataServiceClient } from '@seaverse/data-service-sdk';
322
+ ### What Users CAN Do
229
323
 
230
- const client = new DataServiceClient();
324
+ **Authenticated Users:**
325
+ - Read `publicRead` data
326
+ - Read and write `publicData` (all users' data)
327
+ - Read and write their own `userData/{userId}` only
328
+ - Update/delete documents where `_createdBy == userId`
231
329
 
232
- // For anonymous/guest users who don't have an account
233
- async function initializeGuestSession(appId: string) {
234
- const guestToken = await client.generateGuestFirestoreToken({
235
- app_id: appId,
236
- });
330
+ **Guest Users:**
331
+ - Read `publicRead` data
332
+ - Read and write `publicData`
333
+ - **Cannot** access any `userData`
237
334
 
238
- // Initialize Firebase with guest token
239
- const auth = getAuth(firebaseApp);
240
- await signInWithCustomToken(auth, guestToken.id_token);
335
+ **Admin Users:**
336
+ - Everything regular users can do
337
+ - Write to `publicRead` data
241
338
 
242
- console.log('Guest session initialized:', guestToken.user_id);
243
- }
244
- ```
339
+ ### What Users CANNOT Do
245
340
 
246
- ## Error Handling
341
+ **All Users:**
342
+ - Access data from a different `app_id`
343
+ - Create documents without required fields (`_appId`, `_createdAt`, `_createdBy`)
344
+ - Modify documents created by other users (except in special cases)
345
+ - Access another user's `userData`
346
+
347
+ ❌ **Guest Users:**
348
+ - Access any `userData` paths
247
349
 
248
- The SDK automatically handles API response errors and throws meaningful error messages:
350
+ ## Error Handling
249
351
 
250
352
  ```typescript
251
353
  try {
252
354
  const token = await client.generateFirestoreToken({
253
355
  token: 'invalid-token',
254
- app_id: 'my-app',
356
+ app_id: 'my-app'
255
357
  });
256
358
  } catch (error) {
257
359
  if (error instanceof Error) {
258
360
  console.error('Error:', error.message);
259
- // Error messages come from the API response
361
+ // Handle error (e.g., invalid token, network error)
260
362
  }
261
363
  }
262
364
  ```
263
365
 
366
+ Common errors:
367
+ - `Invalid token` - JWT token is invalid or expired
368
+ - `permission-denied` - Missing required fields or insufficient permissions
369
+ - `Missing or insufficient permissions` - Trying to access unauthorized data
370
+
264
371
  ## TypeScript Support
265
372
 
266
373
  This SDK is written in TypeScript and provides full type definitions:
@@ -273,10 +380,129 @@ import type {
273
380
  GenerateGuestFirestoreTokenRequest,
274
381
  FirestoreTokenResponse,
275
382
  ApiResponse,
276
- ApiError,
383
+ ApiError
277
384
  } from '@seaverse/data-service-sdk';
278
385
  ```
279
386
 
387
+ ## Best Practices for LLM
388
+
389
+ When using this SDK with LLM-generated code:
390
+
391
+ 1. **Always include required fields:**
392
+ ```typescript
393
+ {
394
+ _appId: appId, // From token response
395
+ _createdAt: serverTimestamp(), // Use serverTimestamp()
396
+ _createdBy: userId // From token response
397
+ }
398
+ ```
399
+
400
+ 2. **Use correct data paths:**
401
+ - publicRead: `appData/${appId}/publicRead/{collection}/{docId}`
402
+ - publicData: `appData/${appId}/publicData/{collection}/{docId}`
403
+ - userData: `appData/${appId}/userData/${userId}/{collection}/{docId}`
404
+
405
+ 3. **Handle token expiration:**
406
+ - Tokens expire after 1 hour (3600 seconds)
407
+ - Check `expires_in` field and refresh when needed
408
+
409
+ 4. **Use serverTimestamp() for timestamps:**
410
+ ```typescript
411
+ import { serverTimestamp } from 'firebase/firestore';
412
+
413
+ {
414
+ _createdAt: serverTimestamp() // Not new Date()!
415
+ }
416
+ ```
417
+
418
+ 5. **Separate guest and authenticated flows:**
419
+ - Use `generateGuestFirestoreToken()` for anonymous users
420
+ - Use `generateFirestoreToken()` for logged-in users
421
+
422
+ ## Complete Example
423
+
424
+ Here's a complete example combining authentication and data access:
425
+
426
+ ```typescript
427
+ import { DataServiceClient } from '@seaverse/data-service-sdk';
428
+ import { AuthClient } from '@seaverse/auth-sdk';
429
+ import { initializeApp } from 'firebase/app';
430
+ import { getAuth, signInWithCustomToken } from 'firebase/auth';
431
+ import { getFirestore, collection, addDoc, getDocs, query, where, serverTimestamp } from 'firebase/firestore';
432
+
433
+ async function completeExample() {
434
+ const appId = 'my-app-123';
435
+
436
+ // 1. Authenticate user
437
+ const authClient = new AuthClient({ appId });
438
+ const loginResponse = await authClient.loginWithEmail({
439
+ email: 'user@example.com',
440
+ password: 'password123'
441
+ });
442
+
443
+ // 2. Get Firestore token
444
+ const dataClient = new DataServiceClient();
445
+ const firestoreToken = await dataClient.generateFirestoreToken({
446
+ token: loginResponse.token,
447
+ app_id: appId
448
+ });
449
+
450
+ // 3. Initialize Firebase
451
+ const app = initializeApp({ projectId: firestoreToken.project_id });
452
+ const auth = getAuth(app);
453
+ await signInWithCustomToken(auth, firestoreToken.custom_token);
454
+ const db = getFirestore(app);
455
+
456
+ const userId = firestoreToken.user_id;
457
+
458
+ // 4. Create a post (publicData)
459
+ const postRef = await addDoc(
460
+ collection(db, `appData/${appId}/publicData/posts`),
461
+ {
462
+ _appId: appId,
463
+ _createdAt: serverTimestamp(),
464
+ _createdBy: userId,
465
+ title: 'My First Post',
466
+ content: 'Hello world!',
467
+ tags: ['introduction', 'first-post']
468
+ }
469
+ );
470
+ console.log('Created post:', postRef.id);
471
+
472
+ // 5. Read all posts
473
+ const postsSnapshot = await getDocs(
474
+ collection(db, `appData/${appId}/publicData/posts`)
475
+ );
476
+ postsSnapshot.forEach(doc => {
477
+ console.log('Post:', doc.id, doc.data());
478
+ });
479
+
480
+ // 6. Query user's own posts
481
+ const myPostsQuery = query(
482
+ collection(db, `appData/${appId}/publicData/posts`),
483
+ where('_createdBy', '==', userId)
484
+ );
485
+ const myPosts = await getDocs(myPostsQuery);
486
+ console.log('My posts count:', myPosts.size);
487
+
488
+ // 7. Save user preferences (private)
489
+ await addDoc(
490
+ collection(db, `appData/${appId}/userData/${userId}/preferences`),
491
+ {
492
+ _appId: appId,
493
+ _createdAt: serverTimestamp(),
494
+ _createdBy: userId,
495
+ theme: 'dark',
496
+ language: 'en',
497
+ notifications: true
498
+ }
499
+ );
500
+ console.log('Saved user preferences');
501
+ }
502
+
503
+ completeExample();
504
+ ```
505
+
280
506
  ## API Endpoints
281
507
 
282
508
  The SDK connects to the following endpoints by default:
@@ -294,3 +520,8 @@ MIT
294
520
  For issues and questions, please visit:
295
521
  - GitHub: https://github.com/seaverseai/sv-sdk
296
522
  - Email: support@seaverse.com
523
+
524
+ ## Related SDKs
525
+
526
+ - [@seaverse/auth-sdk](../auth-sdk) - User authentication and account management
527
+ - [@seaverse/payment-sdk](../payment-sdk) - Payment processing integration