@seaverse/data-service-sdk 0.5.2 → 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.
@@ -4269,11 +4269,273 @@
4269
4269
  };
4270
4270
  }
4271
4271
 
4272
+ /**
4273
+ * Firestore Path Helper Functions
4274
+ *
4275
+ * These helper functions generate correct Firestore paths that match
4276
+ * the security rules. Use these instead of manually constructing paths
4277
+ * to avoid permission-denied errors.
4278
+ *
4279
+ * 🚨 IMPORTANT: These paths are designed to work with SeaVerse Firestore Rules
4280
+ *
4281
+ * Permission Levels:
4282
+ * - publicRead: Read-only for all users, write for admins only
4283
+ * - publicData: Read/write for all authenticated users
4284
+ * - userData: Read/write only for the data owner
4285
+ */
4286
+ /**
4287
+ * Generate path for publicRead data (read-only for users, write for admins)
4288
+ *
4289
+ * @param appId - Your application ID
4290
+ * @param collection - Collection name (e.g., 'announcements', 'config')
4291
+ * @returns Firestore path string
4292
+ *
4293
+ * @example
4294
+ * ```typescript
4295
+ * // Read system announcements
4296
+ * const path = getPublicReadPath('my-app', 'announcements');
4297
+ * // Returns: 'appData/my-app/publicRead/announcements'
4298
+ *
4299
+ * const snapshot = await getDocs(collection(db, path));
4300
+ * ```
4301
+ */
4302
+ function getPublicReadPath(appId, collectionName) {
4303
+ validateSegment('appId', appId);
4304
+ validateSegment('collectionName', collectionName);
4305
+ return `appData/${appId}/publicRead/${collectionName}`;
4306
+ }
4307
+ /**
4308
+ * Generate path for publicData (read/write for all authenticated users)
4309
+ *
4310
+ * @param appId - Your application ID
4311
+ * @param collection - Collection name (e.g., 'posts', 'comments')
4312
+ * @returns Firestore path string
4313
+ *
4314
+ * @example
4315
+ * ```typescript
4316
+ * // Write a public post
4317
+ * const path = getPublicDataPath('my-app', 'posts');
4318
+ * // Returns: 'appData/my-app/publicData/posts'
4319
+ *
4320
+ * await addDoc(collection(db, path), {
4321
+ * _appId: appId,
4322
+ * _createdAt: serverTimestamp(),
4323
+ * _createdBy: userId,
4324
+ * title: 'My Post'
4325
+ * });
4326
+ * ```
4327
+ */
4328
+ function getPublicDataPath(appId, collectionName) {
4329
+ validateSegment('appId', appId);
4330
+ validateSegment('collectionName', collectionName);
4331
+ return `appData/${appId}/publicData/${collectionName}`;
4332
+ }
4333
+ /**
4334
+ * Generate path for userData (private, read/write only by owner)
4335
+ *
4336
+ * @param appId - Your application ID
4337
+ * @param userId - User ID who owns this data
4338
+ * @param collection - Collection name (e.g., 'notes', 'settings')
4339
+ * @returns Firestore path string
4340
+ *
4341
+ * @example
4342
+ * ```typescript
4343
+ * // Write private user notes
4344
+ * const path = getUserDataPath('my-app', 'user-123', 'notes');
4345
+ * // Returns: 'appData/my-app/userData/user-123/notes'
4346
+ *
4347
+ * await addDoc(collection(db, path), {
4348
+ * _appId: appId,
4349
+ * _createdAt: serverTimestamp(),
4350
+ * _createdBy: userId,
4351
+ * content: 'Private note'
4352
+ * });
4353
+ * ```
4354
+ */
4355
+ function getUserDataPath(appId, userId, collectionName) {
4356
+ validateSegment('appId', appId);
4357
+ validateSegment('userId', userId);
4358
+ validateSegment('collectionName', collectionName);
4359
+ return `appData/${appId}/userData/${userId}/${collectionName}`;
4360
+ }
4361
+ /**
4362
+ * Generate path for a specific document in publicRead
4363
+ *
4364
+ * @param appId - Your application ID
4365
+ * @param collection - Collection name
4366
+ * @param docId - Document ID
4367
+ * @returns Firestore document path string
4368
+ *
4369
+ * @example
4370
+ * ```typescript
4371
+ * const path = getPublicReadDocPath('my-app', 'announcements', 'announcement-1');
4372
+ * // Returns: 'appData/my-app/publicRead/announcements/announcement-1'
4373
+ *
4374
+ * const docSnap = await getDoc(doc(db, path));
4375
+ * ```
4376
+ */
4377
+ function getPublicReadDocPath(appId, collectionName, docId) {
4378
+ validateSegment('appId', appId);
4379
+ validateSegment('collectionName', collectionName);
4380
+ validateSegment('docId', docId);
4381
+ return `appData/${appId}/publicRead/${collectionName}/${docId}`;
4382
+ }
4383
+ /**
4384
+ * Generate path for a specific document in publicData
4385
+ *
4386
+ * @param appId - Your application ID
4387
+ * @param collection - Collection name
4388
+ * @param docId - Document ID
4389
+ * @returns Firestore document path string
4390
+ *
4391
+ * @example
4392
+ * ```typescript
4393
+ * const path = getPublicDataDocPath('my-app', 'posts', 'post-123');
4394
+ * // Returns: 'appData/my-app/publicData/posts/post-123'
4395
+ *
4396
+ * const docSnap = await getDoc(doc(db, path));
4397
+ * ```
4398
+ */
4399
+ function getPublicDataDocPath(appId, collectionName, docId) {
4400
+ validateSegment('appId', appId);
4401
+ validateSegment('collectionName', collectionName);
4402
+ validateSegment('docId', docId);
4403
+ return `appData/${appId}/publicData/${collectionName}/${docId}`;
4404
+ }
4405
+ /**
4406
+ * Generate path for a specific document in userData
4407
+ *
4408
+ * @param appId - Your application ID
4409
+ * @param userId - User ID who owns this data
4410
+ * @param collection - Collection name
4411
+ * @param docId - Document ID
4412
+ * @returns Firestore document path string
4413
+ *
4414
+ * @example
4415
+ * ```typescript
4416
+ * const path = getUserDataDocPath('my-app', 'user-123', 'notes', 'note-456');
4417
+ * // Returns: 'appData/my-app/userData/user-123/notes/note-456'
4418
+ *
4419
+ * const docSnap = await getDoc(doc(db, path));
4420
+ * ```
4421
+ */
4422
+ function getUserDataDocPath(appId, userId, collectionName, docId) {
4423
+ validateSegment('appId', appId);
4424
+ validateSegment('userId', userId);
4425
+ validateSegment('collectionName', collectionName);
4426
+ validateSegment('docId', docId);
4427
+ return `appData/${appId}/userData/${userId}/${collectionName}/${docId}`;
4428
+ }
4429
+ /**
4430
+ * Validate a path segment to ensure it doesn't contain invalid characters
4431
+ *
4432
+ * @param name - Parameter name for error messages
4433
+ * @param value - The segment value to validate
4434
+ * @throws Error if the segment is invalid
4435
+ */
4436
+ function validateSegment(name, value) {
4437
+ if (!value || typeof value !== 'string') {
4438
+ throw new Error(`${name} must be a non-empty string`);
4439
+ }
4440
+ if (value.includes('/')) {
4441
+ throw new Error(`${name} cannot contain forward slashes (/). Got: "${value}"`);
4442
+ }
4443
+ if (value.trim() !== value) {
4444
+ throw new Error(`${name} cannot start or end with whitespace. Got: "${value}"`);
4445
+ }
4446
+ }
4447
+ /**
4448
+ * Path builder for advanced use cases
4449
+ * Provides a fluent interface for building Firestore paths
4450
+ *
4451
+ * @example
4452
+ * ```typescript
4453
+ * // Build a path step by step
4454
+ * const builder = new PathBuilder('my-app');
4455
+ * const path = builder.publicData('posts').build();
4456
+ * // Returns: 'appData/my-app/publicData/posts'
4457
+ *
4458
+ * // With document ID
4459
+ * const docPath = builder.publicData('posts').doc('post-123').build();
4460
+ * // Returns: 'appData/my-app/publicData/posts/post-123'
4461
+ * ```
4462
+ */
4463
+ class PathBuilder {
4464
+ constructor(appId) {
4465
+ this.appId = appId;
4466
+ this.segments = ['appData'];
4467
+ validateSegment('appId', appId);
4468
+ this.segments.push(appId);
4469
+ }
4470
+ /**
4471
+ * Add publicRead collection to path
4472
+ */
4473
+ publicRead(collectionName) {
4474
+ validateSegment('collectionName', collectionName);
4475
+ this.segments.push('publicRead', collectionName);
4476
+ return this;
4477
+ }
4478
+ /**
4479
+ * Add publicData collection to path
4480
+ */
4481
+ publicData(collectionName) {
4482
+ validateSegment('collectionName', collectionName);
4483
+ this.segments.push('publicData', collectionName);
4484
+ return this;
4485
+ }
4486
+ /**
4487
+ * Add userData collection to path
4488
+ */
4489
+ userData(userId, collectionName) {
4490
+ validateSegment('userId', userId);
4491
+ validateSegment('collectionName', collectionName);
4492
+ this.segments.push('userData', userId, collectionName);
4493
+ return this;
4494
+ }
4495
+ /**
4496
+ * Add document ID to path
4497
+ */
4498
+ doc(docId) {
4499
+ validateSegment('docId', docId);
4500
+ this.segments.push(docId);
4501
+ return this;
4502
+ }
4503
+ /**
4504
+ * Build the final path string
4505
+ */
4506
+ build() {
4507
+ return this.segments.join('/');
4508
+ }
4509
+ }
4510
+ /**
4511
+ * Common path patterns as constants for frequently used paths
4512
+ */
4513
+ const PATH_PATTERNS = {
4514
+ /**
4515
+ * Base pattern for all SeaVerse data
4516
+ */
4517
+ APP_DATA: 'appData',
4518
+ /**
4519
+ * Permission layers
4520
+ */
4521
+ PUBLIC_READ: 'publicRead',
4522
+ PUBLIC_DATA: 'publicData',
4523
+ USER_DATA: 'userData',
4524
+ };
4525
+
4272
4526
  exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
4273
4527
  exports.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
4274
4528
  exports.DataServiceClient = DataServiceClient;
4275
4529
  exports.ENDPOINTS = ENDPOINTS;
4530
+ exports.PATH_PATTERNS = PATH_PATTERNS;
4531
+ exports.PathBuilder = PathBuilder;
4276
4532
  exports.getFirebaseConfig = getFirebaseConfig;
4533
+ exports.getPublicDataDocPath = getPublicDataDocPath;
4534
+ exports.getPublicDataPath = getPublicDataPath;
4535
+ exports.getPublicReadDocPath = getPublicReadDocPath;
4536
+ exports.getPublicReadPath = getPublicReadPath;
4537
+ exports.getUserDataDocPath = getUserDataDocPath;
4538
+ exports.getUserDataPath = getUserDataPath;
4277
4539
  exports.initializeWithToken = initializeWithToken;
4278
4540
 
4279
4541
  }));