@seaverse/data-service-sdk 0.5.1 → 0.6.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 +163 -15
- package/dist/browser.js +258 -3
- package/dist/browser.js.map +1 -1
- package/dist/browser.umd.js +265 -2
- package/dist/browser.umd.js.map +1 -1
- package/dist/index.cjs +265 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +186 -1
- package/dist/index.js +258 -3
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ SeaVerse Data Service SDK for accessing Firestore with secure token management a
|
|
|
10
10
|
- 🔒 Automatic data isolation by app_id
|
|
11
11
|
- 📝 TypeScript support with full type definitions
|
|
12
12
|
- 🤖 LLM-friendly documentation with clear examples
|
|
13
|
+
- 🛡️ Path helper functions to prevent permission-denied errors
|
|
13
14
|
|
|
14
15
|
## Three-Tier Permission Model
|
|
15
16
|
|
|
@@ -130,12 +131,116 @@ const { DataServiceClient } = require('@seaverse/data-service-sdk');
|
|
|
130
131
|
import { DataServiceClient } from '@seaverse/data-service-sdk';
|
|
131
132
|
```
|
|
132
133
|
|
|
134
|
+
## Path Helper Functions (🚨 Recommended for LLM)
|
|
135
|
+
|
|
136
|
+
To prevent `permission-denied` errors caused by incorrect paths, we provide helper functions that generate the correct Firestore paths automatically.
|
|
137
|
+
|
|
138
|
+
### Why Use Path Helpers?
|
|
139
|
+
|
|
140
|
+
**Problem**: LLM or developers might accidentally use wrong paths:
|
|
141
|
+
```typescript
|
|
142
|
+
// ❌ WRONG - Will cause permission-denied!
|
|
143
|
+
collection(db, `apps/${appId}/publicArticles`) // Not matching security rules!
|
|
144
|
+
collection(db, `apps/${appId}/users/${userId}/articles`) // Not matching security rules!
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Solution**: Use path helper functions:
|
|
148
|
+
```typescript
|
|
149
|
+
import { getPublicDataPath, getUserDataPath } from '@seaverse/data-service-sdk';
|
|
150
|
+
|
|
151
|
+
// ✅ CORRECT - Guaranteed to match security rules
|
|
152
|
+
collection(db, getPublicDataPath(appId, 'posts')) // → appData/{appId}/publicData/posts
|
|
153
|
+
collection(db, getUserDataPath(appId, userId, 'notes')) // → appData/{appId}/userData/{userId}/notes
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Available Path Helpers
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import {
|
|
160
|
+
getPublicReadPath, // For read-only public data
|
|
161
|
+
getPublicDataPath, // For public read/write data
|
|
162
|
+
getUserDataPath, // For private user data
|
|
163
|
+
getPublicReadDocPath, // For specific public document
|
|
164
|
+
getPublicDataDocPath, // For specific public data document
|
|
165
|
+
getUserDataDocPath, // For specific user document
|
|
166
|
+
PathBuilder // For advanced path building
|
|
167
|
+
} from '@seaverse/data-service-sdk';
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Basic Usage Examples
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// Public data that everyone can read/write
|
|
174
|
+
const postsPath = getPublicDataPath(appId, 'posts');
|
|
175
|
+
await addDoc(collection(db, postsPath), {
|
|
176
|
+
_appId: appId,
|
|
177
|
+
_createdAt: serverTimestamp(),
|
|
178
|
+
_createdBy: userId,
|
|
179
|
+
title: 'My Post'
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Private user data
|
|
183
|
+
const notesPath = getUserDataPath(appId, userId, 'notes');
|
|
184
|
+
await addDoc(collection(db, notesPath), {
|
|
185
|
+
_appId: appId,
|
|
186
|
+
_createdAt: serverTimestamp(),
|
|
187
|
+
_createdBy: userId,
|
|
188
|
+
content: 'Private note'
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Public read-only data (admin writes only)
|
|
192
|
+
const configPath = getPublicReadPath(appId, 'config');
|
|
193
|
+
const configs = await getDocs(collection(db, configPath));
|
|
194
|
+
|
|
195
|
+
// Access specific document
|
|
196
|
+
const docPath = getPublicDataDocPath(appId, 'posts', 'post-123');
|
|
197
|
+
const docSnap = await getDoc(doc(db, docPath));
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Advanced: PathBuilder
|
|
201
|
+
|
|
202
|
+
For complex path construction:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { PathBuilder } from '@seaverse/data-service-sdk';
|
|
206
|
+
|
|
207
|
+
const builder = new PathBuilder(appId);
|
|
208
|
+
|
|
209
|
+
// Build collection path
|
|
210
|
+
const path = builder.publicData('posts').build();
|
|
211
|
+
// Returns: 'appData/my-app/publicData/posts'
|
|
212
|
+
|
|
213
|
+
// Build document path
|
|
214
|
+
const docPath = builder.publicData('posts').doc('post-123').build();
|
|
215
|
+
// Returns: 'appData/my-app/publicData/posts/post-123'
|
|
216
|
+
|
|
217
|
+
// Build user data path
|
|
218
|
+
const userPath = builder.userData(userId, 'notes').build();
|
|
219
|
+
// Returns: 'appData/my-app/userData/user-123/notes'
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Error Prevention
|
|
223
|
+
|
|
224
|
+
Path helpers validate inputs to prevent common mistakes:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// ❌ These will throw errors:
|
|
228
|
+
getPublicDataPath('my-app', 'posts/comments'); // Error: cannot contain /
|
|
229
|
+
getPublicDataPath('my-app', ''); // Error: must be non-empty string
|
|
230
|
+
getUserDataPath('my-app', '', 'notes'); // Error: userId must be non-empty
|
|
231
|
+
```
|
|
232
|
+
|
|
133
233
|
## Quick Start
|
|
134
234
|
|
|
135
235
|
### 🚀 Easiest Way (Recommended - Auto Firebase Setup)
|
|
136
236
|
|
|
137
237
|
```typescript
|
|
138
|
-
import {
|
|
238
|
+
import {
|
|
239
|
+
DataServiceClient,
|
|
240
|
+
initializeWithToken,
|
|
241
|
+
getPublicDataPath, // 🛡️ Use path helpers!
|
|
242
|
+
getUserDataPath // 🛡️ Use path helpers!
|
|
243
|
+
} from '@seaverse/data-service-sdk';
|
|
139
244
|
import { AuthClient } from '@seaverse/auth-sdk';
|
|
140
245
|
import { collection, addDoc, getDocs, serverTimestamp } from 'firebase/firestore';
|
|
141
246
|
|
|
@@ -156,10 +261,11 @@ const tokenResponse = await dataClient.generateFirestoreToken({
|
|
|
156
261
|
// Step 3: Auto-initialize Firebase (ONE LINE!)
|
|
157
262
|
const { db, appId, userId } = await initializeWithToken(tokenResponse);
|
|
158
263
|
|
|
159
|
-
// Step 4: Use Firestore directly!
|
|
264
|
+
// Step 4: Use Firestore directly with path helpers!
|
|
160
265
|
|
|
161
266
|
// Write to publicData (everyone can write)
|
|
162
|
-
|
|
267
|
+
const postsPath = getPublicDataPath(appId, 'posts'); // 🛡️ Safe path!
|
|
268
|
+
await addDoc(collection(db, postsPath), {
|
|
163
269
|
_appId: appId, // REQUIRED
|
|
164
270
|
_createdAt: serverTimestamp(), // REQUIRED
|
|
165
271
|
_createdBy: userId, // REQUIRED
|
|
@@ -168,13 +274,14 @@ await addDoc(collection(db, `appData/${appId}/publicData/posts`), {
|
|
|
168
274
|
});
|
|
169
275
|
|
|
170
276
|
// Read from publicData
|
|
171
|
-
const snapshot = await getDocs(collection(db,
|
|
277
|
+
const snapshot = await getDocs(collection(db, postsPath));
|
|
172
278
|
snapshot.forEach(doc => {
|
|
173
279
|
console.log(doc.id, doc.data());
|
|
174
280
|
});
|
|
175
281
|
|
|
176
282
|
// Write to userData (private)
|
|
177
|
-
|
|
283
|
+
const notesPath = getUserDataPath(appId, userId, 'notes'); // 🛡️ Safe path!
|
|
284
|
+
await addDoc(collection(db, notesPath), {
|
|
178
285
|
_appId: appId, // REQUIRED
|
|
179
286
|
_createdAt: serverTimestamp(), // REQUIRED
|
|
180
287
|
_createdBy: userId, // REQUIRED
|
|
@@ -186,7 +293,11 @@ await addDoc(collection(db, `appData/${appId}/userData/${userId}/notes`), {
|
|
|
186
293
|
### 👤 For Guest Users (Even Simpler!)
|
|
187
294
|
|
|
188
295
|
```typescript
|
|
189
|
-
import {
|
|
296
|
+
import {
|
|
297
|
+
DataServiceClient,
|
|
298
|
+
initializeWithToken,
|
|
299
|
+
getPublicDataPath // 🛡️ Use path helpers!
|
|
300
|
+
} from '@seaverse/data-service-sdk';
|
|
190
301
|
import { collection, addDoc, serverTimestamp } from 'firebase/firestore';
|
|
191
302
|
|
|
192
303
|
// Step 1: Get guest token (no authentication needed!)
|
|
@@ -199,7 +310,8 @@ const tokenResponse = await dataClient.generateGuestFirestoreToken({
|
|
|
199
310
|
const { db, appId, userId } = await initializeWithToken(tokenResponse);
|
|
200
311
|
|
|
201
312
|
// Step 3: Guest can write to publicData
|
|
202
|
-
|
|
313
|
+
const commentsPath = getPublicDataPath(appId, 'comments'); // 🛡️ Safe path!
|
|
314
|
+
await addDoc(collection(db, commentsPath), {
|
|
203
315
|
_appId: appId,
|
|
204
316
|
_createdAt: serverTimestamp(),
|
|
205
317
|
_createdBy: userId, // Guest user ID (e.g., 'guest-abc123')
|
|
@@ -237,6 +349,20 @@ const app = initializeApp({
|
|
|
237
349
|
|
|
238
350
|
const auth = getAuth(app);
|
|
239
351
|
await signInWithCustomToken(auth, tokenResponse.custom_token);
|
|
352
|
+
|
|
353
|
+
// ⚠️ IMPORTANT: Must specify database_id!
|
|
354
|
+
const db = getFirestore(app, tokenResponse.database_id);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**🚨 CRITICAL: Always Specify database_id**
|
|
358
|
+
|
|
359
|
+
When initializing Firestore, you MUST pass the `database_id` from the token response:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
// ✅ CORRECT - Specify database_id
|
|
363
|
+
const db = getFirestore(app, tokenResponse.database_id);
|
|
364
|
+
|
|
365
|
+
// ❌ WRONG - Will try to use "(default)" database which may not exist
|
|
240
366
|
const db = getFirestore(app);
|
|
241
367
|
```
|
|
242
368
|
|
|
@@ -552,7 +678,19 @@ import type {
|
|
|
552
678
|
|
|
553
679
|
When using this SDK with LLM-generated code:
|
|
554
680
|
|
|
555
|
-
1.
|
|
681
|
+
1. **🛡️ MOST IMPORTANT: Use path helper functions to avoid permission-denied errors:**
|
|
682
|
+
```typescript
|
|
683
|
+
import { getPublicDataPath, getUserDataPath } from '@seaverse/data-service-sdk';
|
|
684
|
+
|
|
685
|
+
// ✅ CORRECT - Use helpers
|
|
686
|
+
const path = getPublicDataPath(appId, 'posts');
|
|
687
|
+
await addDoc(collection(db, path), { ... });
|
|
688
|
+
|
|
689
|
+
// ❌ WRONG - Manual paths may be incorrect
|
|
690
|
+
await addDoc(collection(db, `apps/${appId}/posts`), { ... });
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
2. **Always include required fields:**
|
|
556
694
|
```typescript
|
|
557
695
|
{
|
|
558
696
|
_appId: appId, // From token response
|
|
@@ -561,16 +699,26 @@ When using this SDK with LLM-generated code:
|
|
|
561
699
|
}
|
|
562
700
|
```
|
|
563
701
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
702
|
+
3. **Use correct data paths with helpers:**
|
|
703
|
+
```typescript
|
|
704
|
+
// Use path helpers instead of manual strings!
|
|
705
|
+
import { getPublicReadPath, getPublicDataPath, getUserDataPath } from '@seaverse/data-service-sdk';
|
|
706
|
+
|
|
707
|
+
// publicRead
|
|
708
|
+
const configPath = getPublicReadPath(appId, 'config');
|
|
709
|
+
|
|
710
|
+
// publicData
|
|
711
|
+
const postsPath = getPublicDataPath(appId, 'posts');
|
|
712
|
+
|
|
713
|
+
// userData
|
|
714
|
+
const notesPath = getUserDataPath(appId, userId, 'notes');
|
|
715
|
+
```
|
|
568
716
|
|
|
569
|
-
|
|
717
|
+
4. **Handle token expiration:**
|
|
570
718
|
- Tokens expire after 1 hour (3600 seconds)
|
|
571
719
|
- Check `expires_in` field and refresh when needed
|
|
572
720
|
|
|
573
|
-
|
|
721
|
+
5. **Use serverTimestamp() for timestamps:**
|
|
574
722
|
```typescript
|
|
575
723
|
import { serverTimestamp } from 'firebase/firestore';
|
|
576
724
|
|
|
@@ -579,7 +727,7 @@ When using this SDK with LLM-generated code:
|
|
|
579
727
|
}
|
|
580
728
|
```
|
|
581
729
|
|
|
582
|
-
|
|
730
|
+
6. **Separate guest and authenticated flows:**
|
|
583
731
|
- Use `generateGuestFirestoreToken()` for anonymous users
|
|
584
732
|
- Use `generateFirestoreToken()` for logged-in users
|
|
585
733
|
|
package/dist/browser.js
CHANGED
|
@@ -4251,8 +4251,9 @@ async function initializeWithToken(tokenResponse) {
|
|
|
4251
4251
|
// Sign in with custom token
|
|
4252
4252
|
const auth = getAuth(app);
|
|
4253
4253
|
await signInWithCustomToken(auth, tokenResponse.custom_token);
|
|
4254
|
-
// Get Firestore instance
|
|
4255
|
-
|
|
4254
|
+
// Get Firestore instance with correct database ID
|
|
4255
|
+
// IMPORTANT: Must specify database_id, not just use default!
|
|
4256
|
+
const db = getFirestore(app, tokenResponse.database_id);
|
|
4256
4257
|
return {
|
|
4257
4258
|
app,
|
|
4258
4259
|
auth,
|
|
@@ -4262,5 +4263,259 @@ async function initializeWithToken(tokenResponse) {
|
|
|
4262
4263
|
};
|
|
4263
4264
|
}
|
|
4264
4265
|
|
|
4265
|
-
|
|
4266
|
+
/**
|
|
4267
|
+
* Firestore Path Helper Functions
|
|
4268
|
+
*
|
|
4269
|
+
* These helper functions generate correct Firestore paths that match
|
|
4270
|
+
* the security rules. Use these instead of manually constructing paths
|
|
4271
|
+
* to avoid permission-denied errors.
|
|
4272
|
+
*
|
|
4273
|
+
* 🚨 IMPORTANT: These paths are designed to work with SeaVerse Firestore Rules
|
|
4274
|
+
*
|
|
4275
|
+
* Permission Levels:
|
|
4276
|
+
* - publicRead: Read-only for all users, write for admins only
|
|
4277
|
+
* - publicData: Read/write for all authenticated users
|
|
4278
|
+
* - userData: Read/write only for the data owner
|
|
4279
|
+
*/
|
|
4280
|
+
/**
|
|
4281
|
+
* Generate path for publicRead data (read-only for users, write for admins)
|
|
4282
|
+
*
|
|
4283
|
+
* @param appId - Your application ID
|
|
4284
|
+
* @param collection - Collection name (e.g., 'announcements', 'config')
|
|
4285
|
+
* @returns Firestore path string
|
|
4286
|
+
*
|
|
4287
|
+
* @example
|
|
4288
|
+
* ```typescript
|
|
4289
|
+
* // Read system announcements
|
|
4290
|
+
* const path = getPublicReadPath('my-app', 'announcements');
|
|
4291
|
+
* // Returns: 'appData/my-app/publicRead/announcements'
|
|
4292
|
+
*
|
|
4293
|
+
* const snapshot = await getDocs(collection(db, path));
|
|
4294
|
+
* ```
|
|
4295
|
+
*/
|
|
4296
|
+
function getPublicReadPath(appId, collectionName) {
|
|
4297
|
+
validateSegment('appId', appId);
|
|
4298
|
+
validateSegment('collectionName', collectionName);
|
|
4299
|
+
return `appData/${appId}/publicRead/${collectionName}`;
|
|
4300
|
+
}
|
|
4301
|
+
/**
|
|
4302
|
+
* Generate path for publicData (read/write for all authenticated users)
|
|
4303
|
+
*
|
|
4304
|
+
* @param appId - Your application ID
|
|
4305
|
+
* @param collection - Collection name (e.g., 'posts', 'comments')
|
|
4306
|
+
* @returns Firestore path string
|
|
4307
|
+
*
|
|
4308
|
+
* @example
|
|
4309
|
+
* ```typescript
|
|
4310
|
+
* // Write a public post
|
|
4311
|
+
* const path = getPublicDataPath('my-app', 'posts');
|
|
4312
|
+
* // Returns: 'appData/my-app/publicData/posts'
|
|
4313
|
+
*
|
|
4314
|
+
* await addDoc(collection(db, path), {
|
|
4315
|
+
* _appId: appId,
|
|
4316
|
+
* _createdAt: serverTimestamp(),
|
|
4317
|
+
* _createdBy: userId,
|
|
4318
|
+
* title: 'My Post'
|
|
4319
|
+
* });
|
|
4320
|
+
* ```
|
|
4321
|
+
*/
|
|
4322
|
+
function getPublicDataPath(appId, collectionName) {
|
|
4323
|
+
validateSegment('appId', appId);
|
|
4324
|
+
validateSegment('collectionName', collectionName);
|
|
4325
|
+
return `appData/${appId}/publicData/${collectionName}`;
|
|
4326
|
+
}
|
|
4327
|
+
/**
|
|
4328
|
+
* Generate path for userData (private, read/write only by owner)
|
|
4329
|
+
*
|
|
4330
|
+
* @param appId - Your application ID
|
|
4331
|
+
* @param userId - User ID who owns this data
|
|
4332
|
+
* @param collection - Collection name (e.g., 'notes', 'settings')
|
|
4333
|
+
* @returns Firestore path string
|
|
4334
|
+
*
|
|
4335
|
+
* @example
|
|
4336
|
+
* ```typescript
|
|
4337
|
+
* // Write private user notes
|
|
4338
|
+
* const path = getUserDataPath('my-app', 'user-123', 'notes');
|
|
4339
|
+
* // Returns: 'appData/my-app/userData/user-123/notes'
|
|
4340
|
+
*
|
|
4341
|
+
* await addDoc(collection(db, path), {
|
|
4342
|
+
* _appId: appId,
|
|
4343
|
+
* _createdAt: serverTimestamp(),
|
|
4344
|
+
* _createdBy: userId,
|
|
4345
|
+
* content: 'Private note'
|
|
4346
|
+
* });
|
|
4347
|
+
* ```
|
|
4348
|
+
*/
|
|
4349
|
+
function getUserDataPath(appId, userId, collectionName) {
|
|
4350
|
+
validateSegment('appId', appId);
|
|
4351
|
+
validateSegment('userId', userId);
|
|
4352
|
+
validateSegment('collectionName', collectionName);
|
|
4353
|
+
return `appData/${appId}/userData/${userId}/${collectionName}`;
|
|
4354
|
+
}
|
|
4355
|
+
/**
|
|
4356
|
+
* Generate path for a specific document in publicRead
|
|
4357
|
+
*
|
|
4358
|
+
* @param appId - Your application ID
|
|
4359
|
+
* @param collection - Collection name
|
|
4360
|
+
* @param docId - Document ID
|
|
4361
|
+
* @returns Firestore document path string
|
|
4362
|
+
*
|
|
4363
|
+
* @example
|
|
4364
|
+
* ```typescript
|
|
4365
|
+
* const path = getPublicReadDocPath('my-app', 'announcements', 'announcement-1');
|
|
4366
|
+
* // Returns: 'appData/my-app/publicRead/announcements/announcement-1'
|
|
4367
|
+
*
|
|
4368
|
+
* const docSnap = await getDoc(doc(db, path));
|
|
4369
|
+
* ```
|
|
4370
|
+
*/
|
|
4371
|
+
function getPublicReadDocPath(appId, collectionName, docId) {
|
|
4372
|
+
validateSegment('appId', appId);
|
|
4373
|
+
validateSegment('collectionName', collectionName);
|
|
4374
|
+
validateSegment('docId', docId);
|
|
4375
|
+
return `appData/${appId}/publicRead/${collectionName}/${docId}`;
|
|
4376
|
+
}
|
|
4377
|
+
/**
|
|
4378
|
+
* Generate path for a specific document in publicData
|
|
4379
|
+
*
|
|
4380
|
+
* @param appId - Your application ID
|
|
4381
|
+
* @param collection - Collection name
|
|
4382
|
+
* @param docId - Document ID
|
|
4383
|
+
* @returns Firestore document path string
|
|
4384
|
+
*
|
|
4385
|
+
* @example
|
|
4386
|
+
* ```typescript
|
|
4387
|
+
* const path = getPublicDataDocPath('my-app', 'posts', 'post-123');
|
|
4388
|
+
* // Returns: 'appData/my-app/publicData/posts/post-123'
|
|
4389
|
+
*
|
|
4390
|
+
* const docSnap = await getDoc(doc(db, path));
|
|
4391
|
+
* ```
|
|
4392
|
+
*/
|
|
4393
|
+
function getPublicDataDocPath(appId, collectionName, docId) {
|
|
4394
|
+
validateSegment('appId', appId);
|
|
4395
|
+
validateSegment('collectionName', collectionName);
|
|
4396
|
+
validateSegment('docId', docId);
|
|
4397
|
+
return `appData/${appId}/publicData/${collectionName}/${docId}`;
|
|
4398
|
+
}
|
|
4399
|
+
/**
|
|
4400
|
+
* Generate path for a specific document in userData
|
|
4401
|
+
*
|
|
4402
|
+
* @param appId - Your application ID
|
|
4403
|
+
* @param userId - User ID who owns this data
|
|
4404
|
+
* @param collection - Collection name
|
|
4405
|
+
* @param docId - Document ID
|
|
4406
|
+
* @returns Firestore document path string
|
|
4407
|
+
*
|
|
4408
|
+
* @example
|
|
4409
|
+
* ```typescript
|
|
4410
|
+
* const path = getUserDataDocPath('my-app', 'user-123', 'notes', 'note-456');
|
|
4411
|
+
* // Returns: 'appData/my-app/userData/user-123/notes/note-456'
|
|
4412
|
+
*
|
|
4413
|
+
* const docSnap = await getDoc(doc(db, path));
|
|
4414
|
+
* ```
|
|
4415
|
+
*/
|
|
4416
|
+
function getUserDataDocPath(appId, userId, collectionName, docId) {
|
|
4417
|
+
validateSegment('appId', appId);
|
|
4418
|
+
validateSegment('userId', userId);
|
|
4419
|
+
validateSegment('collectionName', collectionName);
|
|
4420
|
+
validateSegment('docId', docId);
|
|
4421
|
+
return `appData/${appId}/userData/${userId}/${collectionName}/${docId}`;
|
|
4422
|
+
}
|
|
4423
|
+
/**
|
|
4424
|
+
* Validate a path segment to ensure it doesn't contain invalid characters
|
|
4425
|
+
*
|
|
4426
|
+
* @param name - Parameter name for error messages
|
|
4427
|
+
* @param value - The segment value to validate
|
|
4428
|
+
* @throws Error if the segment is invalid
|
|
4429
|
+
*/
|
|
4430
|
+
function validateSegment(name, value) {
|
|
4431
|
+
if (!value || typeof value !== 'string') {
|
|
4432
|
+
throw new Error(`${name} must be a non-empty string`);
|
|
4433
|
+
}
|
|
4434
|
+
if (value.includes('/')) {
|
|
4435
|
+
throw new Error(`${name} cannot contain forward slashes (/). Got: "${value}"`);
|
|
4436
|
+
}
|
|
4437
|
+
if (value.trim() !== value) {
|
|
4438
|
+
throw new Error(`${name} cannot start or end with whitespace. Got: "${value}"`);
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
/**
|
|
4442
|
+
* Path builder for advanced use cases
|
|
4443
|
+
* Provides a fluent interface for building Firestore paths
|
|
4444
|
+
*
|
|
4445
|
+
* @example
|
|
4446
|
+
* ```typescript
|
|
4447
|
+
* // Build a path step by step
|
|
4448
|
+
* const builder = new PathBuilder('my-app');
|
|
4449
|
+
* const path = builder.publicData('posts').build();
|
|
4450
|
+
* // Returns: 'appData/my-app/publicData/posts'
|
|
4451
|
+
*
|
|
4452
|
+
* // With document ID
|
|
4453
|
+
* const docPath = builder.publicData('posts').doc('post-123').build();
|
|
4454
|
+
* // Returns: 'appData/my-app/publicData/posts/post-123'
|
|
4455
|
+
* ```
|
|
4456
|
+
*/
|
|
4457
|
+
class PathBuilder {
|
|
4458
|
+
constructor(appId) {
|
|
4459
|
+
this.appId = appId;
|
|
4460
|
+
this.segments = ['appData'];
|
|
4461
|
+
validateSegment('appId', appId);
|
|
4462
|
+
this.segments.push(appId);
|
|
4463
|
+
}
|
|
4464
|
+
/**
|
|
4465
|
+
* Add publicRead collection to path
|
|
4466
|
+
*/
|
|
4467
|
+
publicRead(collectionName) {
|
|
4468
|
+
validateSegment('collectionName', collectionName);
|
|
4469
|
+
this.segments.push('publicRead', collectionName);
|
|
4470
|
+
return this;
|
|
4471
|
+
}
|
|
4472
|
+
/**
|
|
4473
|
+
* Add publicData collection to path
|
|
4474
|
+
*/
|
|
4475
|
+
publicData(collectionName) {
|
|
4476
|
+
validateSegment('collectionName', collectionName);
|
|
4477
|
+
this.segments.push('publicData', collectionName);
|
|
4478
|
+
return this;
|
|
4479
|
+
}
|
|
4480
|
+
/**
|
|
4481
|
+
* Add userData collection to path
|
|
4482
|
+
*/
|
|
4483
|
+
userData(userId, collectionName) {
|
|
4484
|
+
validateSegment('userId', userId);
|
|
4485
|
+
validateSegment('collectionName', collectionName);
|
|
4486
|
+
this.segments.push('userData', userId, collectionName);
|
|
4487
|
+
return this;
|
|
4488
|
+
}
|
|
4489
|
+
/**
|
|
4490
|
+
* Add document ID to path
|
|
4491
|
+
*/
|
|
4492
|
+
doc(docId) {
|
|
4493
|
+
validateSegment('docId', docId);
|
|
4494
|
+
this.segments.push(docId);
|
|
4495
|
+
return this;
|
|
4496
|
+
}
|
|
4497
|
+
/**
|
|
4498
|
+
* Build the final path string
|
|
4499
|
+
*/
|
|
4500
|
+
build() {
|
|
4501
|
+
return this.segments.join('/');
|
|
4502
|
+
}
|
|
4503
|
+
}
|
|
4504
|
+
/**
|
|
4505
|
+
* Common path patterns as constants for frequently used paths
|
|
4506
|
+
*/
|
|
4507
|
+
const PATH_PATTERNS = {
|
|
4508
|
+
/**
|
|
4509
|
+
* Base pattern for all SeaVerse data
|
|
4510
|
+
*/
|
|
4511
|
+
APP_DATA: 'appData',
|
|
4512
|
+
/**
|
|
4513
|
+
* Permission layers
|
|
4514
|
+
*/
|
|
4515
|
+
PUBLIC_READ: 'publicRead',
|
|
4516
|
+
PUBLIC_DATA: 'publicData',
|
|
4517
|
+
USER_DATA: 'userData',
|
|
4518
|
+
};
|
|
4519
|
+
|
|
4520
|
+
export { DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DataServiceClient, ENDPOINTS, PATH_PATTERNS, PathBuilder, getFirebaseConfig, getPublicDataDocPath, getPublicDataPath, getPublicReadDocPath, getPublicReadPath, getUserDataDocPath, getUserDataPath, initializeWithToken };
|
|
4266
4521
|
//# sourceMappingURL=browser.js.map
|