@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 +82 -63
- package/dist/browser.js +483 -99
- package/dist/browser.js.map +1 -1
- package/dist/browser.umd.js +595 -102
- package/dist/browser.umd.js.map +1 -1
- package/dist/index.cjs +592 -98
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +295 -10
- package/dist/index.js +482 -99
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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` (
|
|
31
|
-
-
|
|
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` (
|
|
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 -
|
|
40
|
-
collection(db, `appData/${appId}/publicData/posts`) //
|
|
51
|
+
// ✅ CORRECT - Collection paths have ODD segments
|
|
52
|
+
collection(db, `appData/${appId}/publicData/_data/posts`) // 5 segments (odd)
|
|
41
53
|
|
|
42
|
-
// ✅ CORRECT -
|
|
43
|
-
doc(db, `appData/${appId}/publicData/posts/doc123`) //
|
|
54
|
+
// ✅ CORRECT - Document paths have EVEN segments
|
|
55
|
+
doc(db, `appData/${appId}/publicData/_data/posts/doc123`) // 6 segments (even)
|
|
44
56
|
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
// -
|
|
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
|
-
-
|
|
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 (
|
|
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:
|
|
262
|
-
const {
|
|
266
|
+
// Step 3: Initialize Firebase & get helper
|
|
267
|
+
const { helper } = await initializeWithToken(tokenResponse);
|
|
263
268
|
|
|
264
|
-
// Step 4: Use Firestore
|
|
269
|
+
// Step 4: Use Firestore with helper (automatic required fields!)
|
|
265
270
|
|
|
266
|
-
// Write to publicData
|
|
267
|
-
|
|
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
|
|
278
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
297
|
+
getPublicDataPath,
|
|
298
|
+
getUserDataPath,
|
|
299
|
+
collection,
|
|
300
|
+
addDoc,
|
|
301
|
+
getDocs,
|
|
302
|
+
serverTimestamp
|
|
300
303
|
} from '@seaverse/data-service-sdk';
|
|
301
|
-
import {
|
|
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:
|
|
310
|
-
const {
|
|
332
|
+
// Step 2: Initialize & get helper
|
|
333
|
+
const { helper } = await initializeWithToken(tokenResponse);
|
|
311
334
|
|
|
312
|
-
// Step 3: Guest can write to publicData
|
|
313
|
-
|
|
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);
|