@seaverse/data-service-sdk 0.6.0 → 0.8.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
@@ -2,6 +2,18 @@
2
2
 
3
3
  SeaVerse Data Service SDK for accessing Firestore with secure token management and three-tier permission model.
4
4
 
5
+ ## 🤖 For LLM Users
6
+
7
+ **👉 Want the simplest way to use this SDK? Check out the [LLM Quick Start Guide](./LLM-QUICK-START.md)**
8
+
9
+ The Quick Start guide shows you the **recommended LLM-friendly API** that automatically handles:
10
+ - ✅ Required fields (`_appId`, `_createdAt`, `_createdBy`)
11
+ - ✅ Correct Firestore paths
12
+ - ✅ Server timestamps
13
+ - ✅ Single import source
14
+
15
+ **Most common LLM mistakes prevented!**
16
+
5
17
  ## Features
6
18
 
7
19
  - 🔐 Secure Firestore token generation for authenticated users and guests
@@ -18,8 +30,8 @@ SeaVerse organizes your Firestore data into three permission levels:
18
30
 
19
31
  | Permission Level | Path Pattern | Read Access | Write Access | Use Case |
20
32
  |-----------------|--------------|-------------|--------------|----------|
21
- | **publicRead** | `appData/{app_id}/publicRead/{collection}/{docId}` | All authenticated users | Admin only | System configs, announcements, static content |
22
- | **publicData** | `appData/{app_id}/publicData/{collection}/{docId}` | All authenticated users | All authenticated users | User posts, comments, shared content |
33
+ | **publicRead** | `appData/{app_id}/publicRead/_data/{collection}/{docId}` | All authenticated users | Admin only | System configs, announcements, static content |
34
+ | **publicData** | `appData/{app_id}/publicData/_data/{collection}/{docId}` | All authenticated users | All authenticated users | User posts, comments, shared content |
23
35
  | **userData** | `appData/{app_id}/userData/{user_id}/{collection}/{docId}` | Owner only | Owner only | User settings, private notes, personal data |
24
36
 
25
37
  ### 🚨 CRITICAL: Firestore Path Rules (For LLM)
@@ -27,33 +39,31 @@ SeaVerse organizes your Firestore data into three permission levels:
27
39
  **Firestore path segments MUST follow these rules:**
28
40
 
29
41
  1. **Collection paths = ODD number of segments** (1, 3, 5, 7...)
30
- - Example: `appData/my-app/publicData/posts` (4 segments) WRONG!
31
- - Correct: Use collection() function, Firestore handles this automatically
42
+ - Example: `appData/my-app/publicData/_data/posts` (5 segments) CORRECT!
43
+ - Firestore requires odd-numbered segments for collections
32
44
 
33
45
  2. **Document paths = EVEN number of segments** (2, 4, 6, 8...)
34
- - Example: `appData/my-app/publicData/posts/doc123` (5 segments) ✅ CORRECT!
46
+ - Example: `appData/my-app/publicData/_data/posts/doc123` (6 segments) ✅ CORRECT!
35
47
 
36
48
  3. **How to use correctly:**
37
49
 
38
50
  ```typescript
39
- // ✅ CORRECT - Using collection() for collection paths
40
- collection(db, `appData/${appId}/publicData/posts`) // Firestore handles this as a collection
51
+ // ✅ CORRECT - Collection paths have ODD segments
52
+ collection(db, `appData/${appId}/publicData/_data/posts`) // 5 segments (odd)
41
53
 
42
- // ✅ CORRECT - Using doc() for document paths
43
- doc(db, `appData/${appId}/publicData/posts/doc123`) // 5 segments (odd) = document
54
+ // ✅ CORRECT - Document paths have EVEN segments
55
+ doc(db, `appData/${appId}/publicData/_data/posts/doc123`) // 6 segments (even)
44
56
 
45
- // WRONG - Manually counting segments
46
- // Don't worry about counting! Use Firebase functions:
47
- // - collection() for collections
48
- // - doc() for documents
49
- // - addDoc() automatically adds document ID
57
+ // 💡 TIP: Always use path helper functions to avoid counting!
58
+ // - getPublicDataPath(appId, 'posts') // Returns correct collection path
59
+ // - getPublicDataDocPath(appId, 'posts', 'doc123') // Returns correct document path
50
60
  ```
51
61
 
52
62
  **Path Structure Examples:**
53
63
 
54
64
  ```typescript
55
65
  // Public Data (everyone can read/write)
56
- const postsRef = collection(db, `appData/${appId}/publicData/posts`);
66
+ const postsRef = collection(db, `appData/${appId}/publicData/_data/posts`);
57
67
  await addDoc(postsRef, { ...data }); // Firestore adds document ID
58
68
 
59
69
  // User Private Data (owner only)
@@ -61,13 +71,14 @@ const notesRef = collection(db, `appData/${appId}/userData/${userId}/notes`);
61
71
  await addDoc(notesRef, { ...data });
62
72
 
63
73
  // Public Read-Only (everyone can read, admin can write)
64
- const configRef = collection(db, `appData/${appId}/publicRead/config`);
74
+ const configRef = collection(db, `appData/${appId}/publicRead/_data/config`);
65
75
  await getDocs(configRef);
66
76
  ```
67
77
 
68
78
  **The pattern is always:**
69
- - `appData` → `{app_id}` → `{permission_layer}` → `{collection}` → (auto-generated doc ID)
70
- - This gives you 5 segments total = document path
79
+ - `appData` → `{app_id}` → `{permission_layer}` → `_data` → `{collection}` → (auto-generated doc ID)
80
+ - Collection: 5 segments (odd)
81
+ - Document: 6 segments (even) ✅
71
82
 
72
83
  ### Required Fields
73
84
 
@@ -232,17 +243,11 @@ getUserDataPath('my-app', '', 'notes'); // Error: userId must be non-empty
232
243
 
233
244
  ## Quick Start
234
245
 
235
- ### 🚀 Easiest Way (Recommended - Auto Firebase Setup)
246
+ ### 🚀 Easiest Way (🤖 LLM-Recommended)
236
247
 
237
248
  ```typescript
238
- import {
239
- DataServiceClient,
240
- initializeWithToken,
241
- getPublicDataPath, // 🛡️ Use path helpers!
242
- getUserDataPath // 🛡️ Use path helpers!
243
- } from '@seaverse/data-service-sdk';
249
+ import { DataServiceClient, initializeWithToken } from '@seaverse/data-service-sdk';
244
250
  import { AuthClient } from '@seaverse/auth-sdk';
245
- import { collection, addDoc, getDocs, serverTimestamp } from 'firebase/firestore';
246
251
 
247
252
  // Step 1: Login user
248
253
  const authClient = new AuthClient({ appId: 'my-app-123' });
@@ -258,47 +263,65 @@ const tokenResponse = await dataClient.generateFirestoreToken({
258
263
  app_id: 'my-app-123'
259
264
  });
260
265
 
261
- // Step 3: Auto-initialize Firebase (ONE LINE!)
262
- const { db, appId, userId } = await initializeWithToken(tokenResponse);
266
+ // Step 3: Initialize Firebase & get helper
267
+ const { helper } = await initializeWithToken(tokenResponse);
263
268
 
264
- // Step 4: Use Firestore directly with path helpers!
269
+ // Step 4: Use Firestore with helper (automatic required fields!)
265
270
 
266
- // Write to publicData (everyone can write)
267
- const postsPath = getPublicDataPath(appId, 'posts'); // 🛡️ Safe path!
268
- await addDoc(collection(db, postsPath), {
269
- _appId: appId, // REQUIRED
270
- _createdAt: serverTimestamp(), // REQUIRED
271
- _createdBy: userId, // REQUIRED
271
+ // ✅ LLM-FRIENDLY: Write to publicData - required fields auto-injected!
272
+ await helper.addToPublicData('posts', {
272
273
  title: 'My First Post',
273
274
  content: 'Hello world!'
274
275
  });
275
276
 
276
- // Read from publicData
277
- const snapshot = await getDocs(collection(db, postsPath));
278
- snapshot.forEach(doc => {
277
+ // ✅ LLM-FRIENDLY: Read from publicData
278
+ const posts = await helper.getPublicData('posts');
279
+ posts.forEach(doc => {
279
280
  console.log(doc.id, doc.data());
280
281
  });
281
282
 
282
- // Write to userData (private)
283
- const notesPath = getUserDataPath(appId, userId, 'notes'); // 🛡️ Safe path!
284
- await addDoc(collection(db, notesPath), {
285
- _appId: appId, // REQUIRED
286
- _createdAt: serverTimestamp(), // REQUIRED
287
- _createdBy: userId, // REQUIRED
283
+ // ✅ LLM-FRIENDLY: Write to userData (private) - auto-isolated!
284
+ await helper.addToUserData('notes', {
288
285
  title: 'Private Note',
289
286
  content: 'Only I can see this'
290
287
  });
291
288
  ```
292
289
 
293
- ### 👤 For Guest Users (Even Simpler!)
290
+ <details>
291
+ <summary>📖 Advanced: Manual Way (for fine-grained control)</summary>
294
292
 
295
293
  ```typescript
296
294
  import {
297
295
  DataServiceClient,
298
296
  initializeWithToken,
299
- getPublicDataPath // 🛡️ Use path helpers!
297
+ getPublicDataPath,
298
+ getUserDataPath,
299
+ collection,
300
+ addDoc,
301
+ getDocs,
302
+ serverTimestamp
300
303
  } from '@seaverse/data-service-sdk';
301
- import { collection, addDoc, serverTimestamp } from 'firebase/firestore';
304
+ import { AuthClient } from '@seaverse/auth-sdk';
305
+
306
+ // ... login and initialize ...
307
+ const { db, appId, userId } = await initializeWithToken(tokenResponse);
308
+
309
+ // Manual way: Use path helpers + required fields
310
+ const postsPath = getPublicDataPath(appId, 'posts');
311
+ await addDoc(collection(db, postsPath), {
312
+ _appId: appId, // REQUIRED
313
+ _createdAt: serverTimestamp(), // REQUIRED
314
+ _createdBy: userId, // REQUIRED
315
+ title: 'My First Post',
316
+ content: 'Hello world!'
317
+ });
318
+ ```
319
+ </details>
320
+
321
+ ### 👤 For Guest Users (🤖 Even Simpler!)
322
+
323
+ ```typescript
324
+ import { DataServiceClient, initializeWithToken } from '@seaverse/data-service-sdk';
302
325
 
303
326
  // Step 1: Get guest token (no authentication needed!)
304
327
  const dataClient = new DataServiceClient();
@@ -306,15 +329,11 @@ const tokenResponse = await dataClient.generateGuestFirestoreToken({
306
329
  app_id: 'my-app-123'
307
330
  });
308
331
 
309
- // Step 2: Auto-initialize Firebase (ONE LINE!)
310
- const { db, appId, userId } = await initializeWithToken(tokenResponse);
332
+ // Step 2: Initialize & get helper
333
+ const { helper } = await initializeWithToken(tokenResponse);
311
334
 
312
- // Step 3: Guest can write to publicData
313
- const commentsPath = getPublicDataPath(appId, 'comments'); // 🛡️ Safe path!
314
- await addDoc(collection(db, commentsPath), {
315
- _appId: appId,
316
- _createdAt: serverTimestamp(),
317
- _createdBy: userId, // Guest user ID (e.g., 'guest-abc123')
335
+ // Step 3: Guest can write to publicData (automatic required fields!)
336
+ await helper.addToPublicData('comments', {
318
337
  comment: 'Great app!',
319
338
  rating: 5
320
339
  });
@@ -529,7 +548,7 @@ const tokenResponse = await client.generateGuestFirestoreToken({ app_id: 'my-app
529
548
  const { db, appId, userId } = await initializeWithToken(tokenResponse);
530
549
 
531
550
  // Ready to use Firestore
532
- await addDoc(collection(db, `appData/${appId}/publicData/posts`), { ... });
551
+ await addDoc(collection(db, `appData/${appId}/publicData/_data/posts`), { ... });
533
552
  ```
534
553
 
535
554
  **Note:** This function requires Firebase SDK to be installed separately:
@@ -543,7 +562,7 @@ npm install firebase
543
562
 
544
563
  ```typescript
545
564
  // Anyone (including guests) can post comments
546
- await addDoc(collection(db, `appData/${appId}/publicData/comments`), {
565
+ await addDoc(collection(db, `appData/${appId}/publicData/_data/comments`), {
547
566
  _appId: appId,
548
567
  _createdAt: serverTimestamp(),
549
568
  _createdBy: userId,
@@ -554,7 +573,7 @@ await addDoc(collection(db, `appData/${appId}/publicData/comments`), {
554
573
 
555
574
  // Anyone can read comments
556
575
  const comments = await getDocs(
557
- collection(db, `appData/${appId}/publicData/comments`)
576
+ collection(db, `appData/${appId}/publicData/_data/comments`)
558
577
  );
559
578
  ```
560
579
 
@@ -583,7 +602,7 @@ const settings = await getDoc(
583
602
  // Only admins can write to publicRead
584
603
  // Regular users and guests can only read
585
604
  const announcements = await getDocs(
586
- collection(db, `appData/${appId}/publicRead/announcements`)
605
+ collection(db, `appData/${appId}/publicRead/_data/announcements`)
587
606
  );
588
607
 
589
608
  announcements.forEach(doc => {
@@ -598,7 +617,7 @@ import { query, where, orderBy, limit } from 'firebase/firestore';
598
617
 
599
618
  // Query posts created by a specific user
600
619
  const userPosts = query(
601
- collection(db, `appData/${appId}/publicData/posts`),
620
+ collection(db, `appData/${appId}/publicData/_data/posts`),
602
621
  where('_createdBy', '==', userId),
603
622
  orderBy('_createdAt', 'desc'),
604
623
  limit(10)
@@ -769,7 +788,7 @@ async function completeExample() {
769
788
 
770
789
  // 4. Create a post (publicData)
771
790
  const postRef = await addDoc(
772
- collection(db, `appData/${appId}/publicData/posts`),
791
+ collection(db, `appData/${appId}/publicData/_data/posts`),
773
792
  {
774
793
  _appId: appId,
775
794
  _createdAt: serverTimestamp(),
@@ -783,7 +802,7 @@ async function completeExample() {
783
802
 
784
803
  // 5. Read all posts
785
804
  const postsSnapshot = await getDocs(
786
- collection(db, `appData/${appId}/publicData/posts`)
805
+ collection(db, `appData/${appId}/publicData/_data/posts`)
787
806
  );
788
807
  postsSnapshot.forEach(doc => {
789
808
  console.log('Post:', doc.id, doc.data());
@@ -791,7 +810,7 @@ async function completeExample() {
791
810
 
792
811
  // 6. Query user's own posts
793
812
  const myPostsQuery = query(
794
- collection(db, `appData/${appId}/publicData/posts`),
813
+ collection(db, `appData/${appId}/publicData/_data/posts`),
795
814
  where('_createdBy', '==', userId)
796
815
  );
797
816
  const myPosts = await getDocs(myPostsQuery);