@seaverse/data-service-sdk 0.5.2 → 0.7.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 +188 -34
- package/dist/browser.js +646 -8
- package/dist/browser.js.map +1 -1
- package/dist/browser.umd.js +766 -11
- package/dist/browser.umd.js.map +1 -1
- package/dist/index.cjs +763 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +476 -6
- package/dist/index.js +645 -8
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
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
|
|
@@ -10,6 +22,7 @@ SeaVerse Data Service SDK for accessing Firestore with secure token management a
|
|
|
10
22
|
- 🔒 Automatic data isolation by app_id
|
|
11
23
|
- 📝 TypeScript support with full type definitions
|
|
12
24
|
- 🤖 LLM-friendly documentation with clear examples
|
|
25
|
+
- 🛡️ Path helper functions to prevent permission-denied errors
|
|
13
26
|
|
|
14
27
|
## Three-Tier Permission Model
|
|
15
28
|
|
|
@@ -130,14 +143,112 @@ const { DataServiceClient } = require('@seaverse/data-service-sdk');
|
|
|
130
143
|
import { DataServiceClient } from '@seaverse/data-service-sdk';
|
|
131
144
|
```
|
|
132
145
|
|
|
146
|
+
## Path Helper Functions (🚨 Recommended for LLM)
|
|
147
|
+
|
|
148
|
+
To prevent `permission-denied` errors caused by incorrect paths, we provide helper functions that generate the correct Firestore paths automatically.
|
|
149
|
+
|
|
150
|
+
### Why Use Path Helpers?
|
|
151
|
+
|
|
152
|
+
**Problem**: LLM or developers might accidentally use wrong paths:
|
|
153
|
+
```typescript
|
|
154
|
+
// ❌ WRONG - Will cause permission-denied!
|
|
155
|
+
collection(db, `apps/${appId}/publicArticles`) // Not matching security rules!
|
|
156
|
+
collection(db, `apps/${appId}/users/${userId}/articles`) // Not matching security rules!
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Solution**: Use path helper functions:
|
|
160
|
+
```typescript
|
|
161
|
+
import { getPublicDataPath, getUserDataPath } from '@seaverse/data-service-sdk';
|
|
162
|
+
|
|
163
|
+
// ✅ CORRECT - Guaranteed to match security rules
|
|
164
|
+
collection(db, getPublicDataPath(appId, 'posts')) // → appData/{appId}/publicData/posts
|
|
165
|
+
collection(db, getUserDataPath(appId, userId, 'notes')) // → appData/{appId}/userData/{userId}/notes
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Available Path Helpers
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
import {
|
|
172
|
+
getPublicReadPath, // For read-only public data
|
|
173
|
+
getPublicDataPath, // For public read/write data
|
|
174
|
+
getUserDataPath, // For private user data
|
|
175
|
+
getPublicReadDocPath, // For specific public document
|
|
176
|
+
getPublicDataDocPath, // For specific public data document
|
|
177
|
+
getUserDataDocPath, // For specific user document
|
|
178
|
+
PathBuilder // For advanced path building
|
|
179
|
+
} from '@seaverse/data-service-sdk';
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Basic Usage Examples
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// Public data that everyone can read/write
|
|
186
|
+
const postsPath = getPublicDataPath(appId, 'posts');
|
|
187
|
+
await addDoc(collection(db, postsPath), {
|
|
188
|
+
_appId: appId,
|
|
189
|
+
_createdAt: serverTimestamp(),
|
|
190
|
+
_createdBy: userId,
|
|
191
|
+
title: 'My Post'
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// Private user data
|
|
195
|
+
const notesPath = getUserDataPath(appId, userId, 'notes');
|
|
196
|
+
await addDoc(collection(db, notesPath), {
|
|
197
|
+
_appId: appId,
|
|
198
|
+
_createdAt: serverTimestamp(),
|
|
199
|
+
_createdBy: userId,
|
|
200
|
+
content: 'Private note'
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Public read-only data (admin writes only)
|
|
204
|
+
const configPath = getPublicReadPath(appId, 'config');
|
|
205
|
+
const configs = await getDocs(collection(db, configPath));
|
|
206
|
+
|
|
207
|
+
// Access specific document
|
|
208
|
+
const docPath = getPublicDataDocPath(appId, 'posts', 'post-123');
|
|
209
|
+
const docSnap = await getDoc(doc(db, docPath));
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Advanced: PathBuilder
|
|
213
|
+
|
|
214
|
+
For complex path construction:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import { PathBuilder } from '@seaverse/data-service-sdk';
|
|
218
|
+
|
|
219
|
+
const builder = new PathBuilder(appId);
|
|
220
|
+
|
|
221
|
+
// Build collection path
|
|
222
|
+
const path = builder.publicData('posts').build();
|
|
223
|
+
// Returns: 'appData/my-app/publicData/posts'
|
|
224
|
+
|
|
225
|
+
// Build document path
|
|
226
|
+
const docPath = builder.publicData('posts').doc('post-123').build();
|
|
227
|
+
// Returns: 'appData/my-app/publicData/posts/post-123'
|
|
228
|
+
|
|
229
|
+
// Build user data path
|
|
230
|
+
const userPath = builder.userData(userId, 'notes').build();
|
|
231
|
+
// Returns: 'appData/my-app/userData/user-123/notes'
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Error Prevention
|
|
235
|
+
|
|
236
|
+
Path helpers validate inputs to prevent common mistakes:
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// ❌ These will throw errors:
|
|
240
|
+
getPublicDataPath('my-app', 'posts/comments'); // Error: cannot contain /
|
|
241
|
+
getPublicDataPath('my-app', ''); // Error: must be non-empty string
|
|
242
|
+
getUserDataPath('my-app', '', 'notes'); // Error: userId must be non-empty
|
|
243
|
+
```
|
|
244
|
+
|
|
133
245
|
## Quick Start
|
|
134
246
|
|
|
135
|
-
### 🚀 Easiest Way (
|
|
247
|
+
### 🚀 Easiest Way (🤖 LLM-Recommended)
|
|
136
248
|
|
|
137
249
|
```typescript
|
|
138
250
|
import { DataServiceClient, initializeWithToken } from '@seaverse/data-service-sdk';
|
|
139
251
|
import { AuthClient } from '@seaverse/auth-sdk';
|
|
140
|
-
import { collection, addDoc, getDocs, serverTimestamp } from 'firebase/firestore';
|
|
141
252
|
|
|
142
253
|
// Step 1: Login user
|
|
143
254
|
const authClient = new AuthClient({ appId: 'my-app-123' });
|
|
@@ -153,41 +264,65 @@ const tokenResponse = await dataClient.generateFirestoreToken({
|
|
|
153
264
|
app_id: 'my-app-123'
|
|
154
265
|
});
|
|
155
266
|
|
|
156
|
-
// Step 3:
|
|
157
|
-
const {
|
|
267
|
+
// Step 3: Initialize Firebase & get helper
|
|
268
|
+
const { helper } = await initializeWithToken(tokenResponse);
|
|
158
269
|
|
|
159
|
-
// Step 4: Use Firestore
|
|
270
|
+
// Step 4: Use Firestore with helper (automatic required fields!)
|
|
160
271
|
|
|
161
|
-
// Write to publicData
|
|
162
|
-
await
|
|
163
|
-
_appId: appId, // REQUIRED
|
|
164
|
-
_createdAt: serverTimestamp(), // REQUIRED
|
|
165
|
-
_createdBy: userId, // REQUIRED
|
|
272
|
+
// ✅ LLM-FRIENDLY: Write to publicData - required fields auto-injected!
|
|
273
|
+
await helper.addToPublicData('posts', {
|
|
166
274
|
title: 'My First Post',
|
|
167
275
|
content: 'Hello world!'
|
|
168
276
|
});
|
|
169
277
|
|
|
170
|
-
// Read from publicData
|
|
171
|
-
const
|
|
172
|
-
|
|
278
|
+
// ✅ LLM-FRIENDLY: Read from publicData
|
|
279
|
+
const posts = await helper.getPublicData('posts');
|
|
280
|
+
posts.forEach(doc => {
|
|
173
281
|
console.log(doc.id, doc.data());
|
|
174
282
|
});
|
|
175
283
|
|
|
176
|
-
// Write to userData (private)
|
|
177
|
-
await
|
|
284
|
+
// ✅ LLM-FRIENDLY: Write to userData (private) - auto-isolated!
|
|
285
|
+
await helper.addToUserData('notes', {
|
|
286
|
+
title: 'Private Note',
|
|
287
|
+
content: 'Only I can see this'
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
<details>
|
|
292
|
+
<summary>📖 Advanced: Manual Way (for fine-grained control)</summary>
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import {
|
|
296
|
+
DataServiceClient,
|
|
297
|
+
initializeWithToken,
|
|
298
|
+
getPublicDataPath,
|
|
299
|
+
getUserDataPath,
|
|
300
|
+
collection,
|
|
301
|
+
addDoc,
|
|
302
|
+
getDocs,
|
|
303
|
+
serverTimestamp
|
|
304
|
+
} from '@seaverse/data-service-sdk';
|
|
305
|
+
import { AuthClient } from '@seaverse/auth-sdk';
|
|
306
|
+
|
|
307
|
+
// ... login and initialize ...
|
|
308
|
+
const { db, appId, userId } = await initializeWithToken(tokenResponse);
|
|
309
|
+
|
|
310
|
+
// Manual way: Use path helpers + required fields
|
|
311
|
+
const postsPath = getPublicDataPath(appId, 'posts');
|
|
312
|
+
await addDoc(collection(db, postsPath), {
|
|
178
313
|
_appId: appId, // REQUIRED
|
|
179
314
|
_createdAt: serverTimestamp(), // REQUIRED
|
|
180
315
|
_createdBy: userId, // REQUIRED
|
|
181
|
-
title: '
|
|
182
|
-
content: '
|
|
316
|
+
title: 'My First Post',
|
|
317
|
+
content: 'Hello world!'
|
|
183
318
|
});
|
|
184
319
|
```
|
|
320
|
+
</details>
|
|
185
321
|
|
|
186
|
-
### 👤 For Guest Users (Even Simpler!)
|
|
322
|
+
### 👤 For Guest Users (🤖 Even Simpler!)
|
|
187
323
|
|
|
188
324
|
```typescript
|
|
189
325
|
import { DataServiceClient, initializeWithToken } from '@seaverse/data-service-sdk';
|
|
190
|
-
import { collection, addDoc, serverTimestamp } from 'firebase/firestore';
|
|
191
326
|
|
|
192
327
|
// Step 1: Get guest token (no authentication needed!)
|
|
193
328
|
const dataClient = new DataServiceClient();
|
|
@@ -195,14 +330,11 @@ const tokenResponse = await dataClient.generateGuestFirestoreToken({
|
|
|
195
330
|
app_id: 'my-app-123'
|
|
196
331
|
});
|
|
197
332
|
|
|
198
|
-
// Step 2:
|
|
199
|
-
const {
|
|
333
|
+
// Step 2: Initialize & get helper
|
|
334
|
+
const { helper } = await initializeWithToken(tokenResponse);
|
|
200
335
|
|
|
201
|
-
// Step 3: Guest can write to publicData
|
|
202
|
-
await
|
|
203
|
-
_appId: appId,
|
|
204
|
-
_createdAt: serverTimestamp(),
|
|
205
|
-
_createdBy: userId, // Guest user ID (e.g., 'guest-abc123')
|
|
336
|
+
// Step 3: Guest can write to publicData (automatic required fields!)
|
|
337
|
+
await helper.addToPublicData('comments', {
|
|
206
338
|
comment: 'Great app!',
|
|
207
339
|
rating: 5
|
|
208
340
|
});
|
|
@@ -566,7 +698,19 @@ import type {
|
|
|
566
698
|
|
|
567
699
|
When using this SDK with LLM-generated code:
|
|
568
700
|
|
|
569
|
-
1.
|
|
701
|
+
1. **🛡️ MOST IMPORTANT: Use path helper functions to avoid permission-denied errors:**
|
|
702
|
+
```typescript
|
|
703
|
+
import { getPublicDataPath, getUserDataPath } from '@seaverse/data-service-sdk';
|
|
704
|
+
|
|
705
|
+
// ✅ CORRECT - Use helpers
|
|
706
|
+
const path = getPublicDataPath(appId, 'posts');
|
|
707
|
+
await addDoc(collection(db, path), { ... });
|
|
708
|
+
|
|
709
|
+
// ❌ WRONG - Manual paths may be incorrect
|
|
710
|
+
await addDoc(collection(db, `apps/${appId}/posts`), { ... });
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
2. **Always include required fields:**
|
|
570
714
|
```typescript
|
|
571
715
|
{
|
|
572
716
|
_appId: appId, // From token response
|
|
@@ -575,16 +719,26 @@ When using this SDK with LLM-generated code:
|
|
|
575
719
|
}
|
|
576
720
|
```
|
|
577
721
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
722
|
+
3. **Use correct data paths with helpers:**
|
|
723
|
+
```typescript
|
|
724
|
+
// Use path helpers instead of manual strings!
|
|
725
|
+
import { getPublicReadPath, getPublicDataPath, getUserDataPath } from '@seaverse/data-service-sdk';
|
|
726
|
+
|
|
727
|
+
// publicRead
|
|
728
|
+
const configPath = getPublicReadPath(appId, 'config');
|
|
729
|
+
|
|
730
|
+
// publicData
|
|
731
|
+
const postsPath = getPublicDataPath(appId, 'posts');
|
|
732
|
+
|
|
733
|
+
// userData
|
|
734
|
+
const notesPath = getUserDataPath(appId, userId, 'notes');
|
|
735
|
+
```
|
|
582
736
|
|
|
583
|
-
|
|
737
|
+
4. **Handle token expiration:**
|
|
584
738
|
- Tokens expire after 1 hour (3600 seconds)
|
|
585
739
|
- Check `expires_in` field and refresh when needed
|
|
586
740
|
|
|
587
|
-
|
|
741
|
+
5. **Use serverTimestamp() for timestamps:**
|
|
588
742
|
```typescript
|
|
589
743
|
import { serverTimestamp } from 'firebase/firestore';
|
|
590
744
|
|
|
@@ -593,7 +747,7 @@ When using this SDK with LLM-generated code:
|
|
|
593
747
|
}
|
|
594
748
|
```
|
|
595
749
|
|
|
596
|
-
|
|
750
|
+
6. **Separate guest and authenticated flows:**
|
|
597
751
|
- Use `generateGuestFirestoreToken()` for anonymous users
|
|
598
752
|
- Use `generateFirestoreToken()` for logged-in users
|
|
599
753
|
|