@seaverse/data-service-sdk 0.1.0 → 0.2.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
+ });
61
+
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
+ });
23
68
 
24
- // Create client instance
25
- const client = new DataServiceClient();
69
+ // Step 3: Initialize Firebase
70
+ const app = initializeApp({ projectId: firestoreToken.project_id });
71
+ const auth = getAuth(app);
72
+ await signInWithCustomToken(auth, firestoreToken.id_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!'
86
+ });
26
87
 
27
- // For authenticated users
28
- const token = await client.generateFirestoreToken({
29
- token: 'user-jwt-token',
30
- 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());
31
92
  });
32
93
 
33
- // For guest users
34
- const guestToken = await client.generateGuestFirestoreToken({
35
- app_id: 'your-app-id',
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'
36
101
  });
37
102
  ```
38
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.id_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
131
+ });
132
+
133
+ // Note: Guests CANNOT access userData
134
+ ```
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,28 @@ interface GenerateFirestoreTokenRequest {
92
189
 
93
190
  ```typescript
94
191
  interface FirestoreTokenResponse {
95
- id_token: string; // Firebase ID Token
192
+ id_token: string; // Firebase ID Token for signInWithCustomToken()
96
193
  refresh_token?: string; // Firebase Refresh Token (optional)
97
- project_id: string; // Firebase Project ID
194
+ project_id: string; // Firebase Project ID for initializeApp()
98
195
  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)
196
+ app_id?: string; // Application ID (use in Firestore paths)
197
+ user_id: string; // User ID (use in Firestore paths)
198
+ role?: string; // User role ('guest', 'user', 'admin')
199
+ expires_in: number; // Token expiration in seconds (typically 3600)
103
200
  }
104
201
  ```
105
202
 
106
203
  **Example:**
107
204
 
108
205
  ```typescript
109
- try {
110
- const response = await client.generateFirestoreToken({
111
- token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
112
- app_id: 'my-app',
113
- });
206
+ const firestoreToken = await client.generateFirestoreToken({
207
+ token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
208
+ app_id: 'my-app-123'
209
+ });
114
210
 
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
- }
211
+ console.log('Token:', firestoreToken.id_token);
212
+ console.log('User ID:', firestoreToken.user_id);
213
+ console.log('App ID:', firestoreToken.app_id);
121
214
  ```
122
215
 
123
216
  #### generateGuestFirestoreToken
@@ -141,126 +234,141 @@ interface GenerateGuestFirestoreTokenRequest {
141
234
 
142
235
  **Response:**
143
236
 
144
- Same as `FirestoreTokenResponse` above, with `role` typically set to `'guest'`.
237
+ Same as `FirestoreTokenResponse` above, with `role` set to `'guest'`.
145
238
 
146
239
  **Example:**
147
240
 
148
241
  ```typescript
149
- try {
150
- const response = await client.generateGuestFirestoreToken({
151
- app_id: 'my-app',
152
- });
242
+ const guestToken = await client.generateGuestFirestoreToken({
243
+ app_id: 'my-app-123'
244
+ });
153
245
 
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
- }
246
+ console.log('Guest Token:', guestToken.id_token);
247
+ console.log('Guest User ID:', guestToken.user_id);
248
+ console.log('Role:', guestToken.role); // 'guest'
160
249
  ```
161
250
 
162
- ## Usage Examples
251
+ ## Common Use Cases
163
252
 
164
- ### Browser Environment
253
+ ### Use Case 1: Public Forum with Comments
165
254
 
166
255
  ```typescript
167
- import { DataServiceClient } from '@seaverse/data-service-sdk';
256
+ // Anyone (including guests) can post comments
257
+ await addDoc(collection(db, `appData/${appId}/publicData/comments`), {
258
+ _appId: appId,
259
+ _createdAt: serverTimestamp(),
260
+ _createdBy: userId,
261
+ postId: 'post-123',
262
+ comment: 'Great post!',
263
+ likes: 0
264
+ });
168
265
 
169
- const client = new DataServiceClient();
266
+ // Anyone can read comments
267
+ const comments = await getDocs(
268
+ collection(db, `appData/${appId}/publicData/comments`)
269
+ );
270
+ ```
170
271
 
171
- // Get user token from your auth system
172
- const userToken = localStorage.getItem('authToken');
272
+ ### Use Case 2: User Private Settings
173
273
 
174
- // Generate Firestore token
175
- const firestoreToken = await client.generateFirestoreToken({
176
- token: userToken,
177
- app_id: 'my-app',
274
+ ```typescript
275
+ // Only the user can write to their own settings
276
+ await setDoc(doc(db, `appData/${appId}/userData/${userId}/settings/preferences`), {
277
+ _appId: appId,
278
+ _createdAt: serverTimestamp(),
279
+ _createdBy: userId,
280
+ theme: 'dark',
281
+ notifications: true,
282
+ language: 'en'
178
283
  });
179
284
 
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
- });
285
+ // Only the user can read their own settings
286
+ const settings = await getDoc(
287
+ doc(db, `appData/${appId}/userData/${userId}/settings/preferences`)
288
+ );
289
+ ```
189
290
 
190
- const auth = getAuth(firebaseApp);
191
- await signInWithCustomToken(auth, firestoreToken.id_token);
291
+ ### Use Case 3: System Announcements (Admin Only)
192
292
 
193
- const db = getFirestore(firebaseApp);
194
- // Now you can use Firestore with the authenticated user
293
+ ```typescript
294
+ // Only admins can write to publicRead
295
+ // Regular users and guests can only read
296
+ const announcements = await getDocs(
297
+ collection(db, `appData/${appId}/publicRead/announcements`)
298
+ );
299
+
300
+ announcements.forEach(doc => {
301
+ console.log('Announcement:', doc.data().message);
302
+ });
195
303
  ```
196
304
 
197
- ### Node.js Environment
305
+ ### Use Case 4: Querying Public Data
198
306
 
199
307
  ```typescript
200
- import { DataServiceClient } from '@seaverse/data-service-sdk';
308
+ import { query, where, orderBy, limit } from 'firebase/firestore';
201
309
 
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
- }
310
+ // Query posts created by a specific user
311
+ const userPosts = query(
312
+ collection(db, `appData/${appId}/publicData/posts`),
313
+ where('_createdBy', '==', userId),
314
+ orderBy('_createdAt', 'desc'),
315
+ limit(10)
316
+ );
317
+
318
+ const snapshot = await getDocs(userPosts);
223
319
  ```
224
320
 
225
- ### Guest User Example
321
+ ## Permission Examples
226
322
 
227
- ```typescript
228
- import { DataServiceClient } from '@seaverse/data-service-sdk';
323
+ ### What Users CAN Do
229
324
 
230
- const client = new DataServiceClient();
325
+ **Authenticated Users:**
326
+ - Read `publicRead` data
327
+ - Read and write `publicData` (all users' data)
328
+ - Read and write their own `userData/{userId}` only
329
+ - Update/delete documents where `_createdBy == userId`
231
330
 
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
- });
331
+ **Guest Users:**
332
+ - Read `publicRead` data
333
+ - Read and write `publicData`
334
+ - **Cannot** access any `userData`
237
335
 
238
- // Initialize Firebase with guest token
239
- const auth = getAuth(firebaseApp);
240
- await signInWithCustomToken(auth, guestToken.id_token);
336
+ **Admin Users:**
337
+ - Everything regular users can do
338
+ - Write to `publicRead` data
241
339
 
242
- console.log('Guest session initialized:', guestToken.user_id);
243
- }
244
- ```
340
+ ### What Users CANNOT Do
245
341
 
246
- ## Error Handling
342
+ **All Users:**
343
+ - Access data from a different `app_id`
344
+ - Create documents without required fields (`_appId`, `_createdAt`, `_createdBy`)
345
+ - Modify documents created by other users (except in special cases)
346
+ - Access another user's `userData`
347
+
348
+ ❌ **Guest Users:**
349
+ - Access any `userData` paths
247
350
 
248
- The SDK automatically handles API response errors and throws meaningful error messages:
351
+ ## Error Handling
249
352
 
250
353
  ```typescript
251
354
  try {
252
355
  const token = await client.generateFirestoreToken({
253
356
  token: 'invalid-token',
254
- app_id: 'my-app',
357
+ app_id: 'my-app'
255
358
  });
256
359
  } catch (error) {
257
360
  if (error instanceof Error) {
258
361
  console.error('Error:', error.message);
259
- // Error messages come from the API response
362
+ // Handle error (e.g., invalid token, network error)
260
363
  }
261
364
  }
262
365
  ```
263
366
 
367
+ Common errors:
368
+ - `Invalid token` - JWT token is invalid or expired
369
+ - `permission-denied` - Missing required fields or insufficient permissions
370
+ - `Missing or insufficient permissions` - Trying to access unauthorized data
371
+
264
372
  ## TypeScript Support
265
373
 
266
374
  This SDK is written in TypeScript and provides full type definitions:
@@ -273,10 +381,129 @@ import type {
273
381
  GenerateGuestFirestoreTokenRequest,
274
382
  FirestoreTokenResponse,
275
383
  ApiResponse,
276
- ApiError,
384
+ ApiError
277
385
  } from '@seaverse/data-service-sdk';
278
386
  ```
279
387
 
388
+ ## Best Practices for LLM
389
+
390
+ When using this SDK with LLM-generated code:
391
+
392
+ 1. **Always include required fields:**
393
+ ```typescript
394
+ {
395
+ _appId: appId, // From token response
396
+ _createdAt: serverTimestamp(), // Use serverTimestamp()
397
+ _createdBy: userId // From token response
398
+ }
399
+ ```
400
+
401
+ 2. **Use correct data paths:**
402
+ - publicRead: `appData/${appId}/publicRead/{collection}/{docId}`
403
+ - publicData: `appData/${appId}/publicData/{collection}/{docId}`
404
+ - userData: `appData/${appId}/userData/${userId}/{collection}/{docId}`
405
+
406
+ 3. **Handle token expiration:**
407
+ - Tokens expire after 1 hour (3600 seconds)
408
+ - Check `expires_in` field and refresh when needed
409
+
410
+ 4. **Use serverTimestamp() for timestamps:**
411
+ ```typescript
412
+ import { serverTimestamp } from 'firebase/firestore';
413
+
414
+ {
415
+ _createdAt: serverTimestamp() // Not new Date()!
416
+ }
417
+ ```
418
+
419
+ 5. **Separate guest and authenticated flows:**
420
+ - Use `generateGuestFirestoreToken()` for anonymous users
421
+ - Use `generateFirestoreToken()` for logged-in users
422
+
423
+ ## Complete Example
424
+
425
+ Here's a complete example combining authentication and data access:
426
+
427
+ ```typescript
428
+ import { DataServiceClient } from '@seaverse/data-service-sdk';
429
+ import { AuthClient } from '@seaverse/auth-sdk';
430
+ import { initializeApp } from 'firebase/app';
431
+ import { getAuth, signInWithCustomToken } from 'firebase/auth';
432
+ import { getFirestore, collection, addDoc, getDocs, query, where, serverTimestamp } from 'firebase/firestore';
433
+
434
+ async function completeExample() {
435
+ const appId = 'my-app-123';
436
+
437
+ // 1. Authenticate user
438
+ const authClient = new AuthClient({ appId });
439
+ const loginResponse = await authClient.loginWithEmail({
440
+ email: 'user@example.com',
441
+ password: 'password123'
442
+ });
443
+
444
+ // 2. Get Firestore token
445
+ const dataClient = new DataServiceClient();
446
+ const firestoreToken = await dataClient.generateFirestoreToken({
447
+ token: loginResponse.token,
448
+ app_id: appId
449
+ });
450
+
451
+ // 3. Initialize Firebase
452
+ const app = initializeApp({ projectId: firestoreToken.project_id });
453
+ const auth = getAuth(app);
454
+ await signInWithCustomToken(auth, firestoreToken.id_token);
455
+ const db = getFirestore(app);
456
+
457
+ const userId = firestoreToken.user_id;
458
+
459
+ // 4. Create a post (publicData)
460
+ const postRef = await addDoc(
461
+ collection(db, `appData/${appId}/publicData/posts`),
462
+ {
463
+ _appId: appId,
464
+ _createdAt: serverTimestamp(),
465
+ _createdBy: userId,
466
+ title: 'My First Post',
467
+ content: 'Hello world!',
468
+ tags: ['introduction', 'first-post']
469
+ }
470
+ );
471
+ console.log('Created post:', postRef.id);
472
+
473
+ // 5. Read all posts
474
+ const postsSnapshot = await getDocs(
475
+ collection(db, `appData/${appId}/publicData/posts`)
476
+ );
477
+ postsSnapshot.forEach(doc => {
478
+ console.log('Post:', doc.id, doc.data());
479
+ });
480
+
481
+ // 6. Query user's own posts
482
+ const myPostsQuery = query(
483
+ collection(db, `appData/${appId}/publicData/posts`),
484
+ where('_createdBy', '==', userId)
485
+ );
486
+ const myPosts = await getDocs(myPostsQuery);
487
+ console.log('My posts count:', myPosts.size);
488
+
489
+ // 7. Save user preferences (private)
490
+ await addDoc(
491
+ collection(db, `appData/${appId}/userData/${userId}/preferences`),
492
+ {
493
+ _appId: appId,
494
+ _createdAt: serverTimestamp(),
495
+ _createdBy: userId,
496
+ theme: 'dark',
497
+ language: 'en',
498
+ notifications: true
499
+ }
500
+ );
501
+ console.log('Saved user preferences');
502
+ }
503
+
504
+ completeExample();
505
+ ```
506
+
280
507
  ## API Endpoints
281
508
 
282
509
  The SDK connects to the following endpoints by default:
@@ -294,3 +521,8 @@ MIT
294
521
  For issues and questions, please visit:
295
522
  - GitHub: https://github.com/seaverseai/sv-sdk
296
523
  - Email: support@seaverse.com
524
+
525
+ ## Related SDKs
526
+
527
+ - [@seaverse/auth-sdk](../auth-sdk) - User authentication and account management
528
+ - [@seaverse/payment-sdk](../payment-sdk) - Payment processing integration