@seaverse/data-service-sdk 0.9.0 → 0.10.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
@@ -42,6 +42,8 @@ SeaVerse Data Service SDK for accessing Firestore with secure token management a
42
42
  - 📝 TypeScript support with full type definitions
43
43
  - 🤖 LLM-friendly documentation with clear examples
44
44
  - 🛡️ Path helper functions to prevent permission-denied errors
45
+ - ✅ Built-in validation (reserved fields, document size)
46
+ - 🗑️ Soft delete support (mark as deleted without removing)
45
47
 
46
48
  ## Three-Tier Permission Model
47
49
 
@@ -113,6 +115,82 @@ All Firestore documents MUST include these three fields:
113
115
 
114
116
  These fields are enforced by Firestore Security Rules and ensure proper data isolation.
115
117
 
118
+ **🎯 Good News for LLM**: When using `FirestoreHelper`, these fields are automatically injected - you don't need to remember them!
119
+
120
+ ### Reserved Fields & Validation
121
+
122
+ **⚠️ IMPORTANT: Fields starting with `_` are reserved for system use!**
123
+
124
+ The SDK automatically validates your data to prevent common mistakes:
125
+
126
+ 1. **Reserved Fields**: You cannot create custom fields starting with `_`
127
+ ```typescript
128
+ // ❌ WRONG - Will throw error
129
+ await helper.addToPublicData('posts', {
130
+ _custom: 'value', // Reserved field!
131
+ title: 'My Post'
132
+ });
133
+
134
+ // ✅ CORRECT
135
+ await helper.addToPublicData('posts', {
136
+ customField: 'value', // No underscore prefix
137
+ title: 'My Post'
138
+ });
139
+ ```
140
+
141
+ 2. **Document Size**: Documents are limited to 256 KB
142
+ ```typescript
143
+ // SDK will automatically check size and throw error if too large
144
+ ```
145
+
146
+ 3. **Automatic Validation**: All `FirestoreHelper` methods validate data automatically
147
+ ```typescript
148
+ // Manual validation if needed
149
+ import { validateFirestoreData, validateDataDetailed } from '@seaverse/data-service-sdk';
150
+
151
+ try {
152
+ validateFirestoreData(myData);
153
+ // Data is valid
154
+ } catch (error) {
155
+ console.error('Validation failed:', error.message);
156
+ }
157
+
158
+ // Or get detailed errors without throwing
159
+ const result = validateDataDetailed(myData);
160
+ if (!result.valid) {
161
+ console.error('Errors:', result.errors);
162
+ }
163
+ ```
164
+
165
+ ### Soft Delete Support
166
+
167
+ **🗑️ Recommended Practice: Use soft delete instead of hard delete**
168
+
169
+ Soft delete marks documents as deleted without removing them from the database:
170
+
171
+ ```typescript
172
+ // ✅ RECOMMENDED: Soft delete (mark as deleted)
173
+ await helper.softDeleteDoc(
174
+ getPublicDataPath(appId, 'posts'),
175
+ 'post-123'
176
+ );
177
+
178
+ // Document is still in database but marked as _deleted = true
179
+ // By default, helper.getPublicData() won't return deleted documents
180
+
181
+ // ❌ Hard delete (only for admins, permanent)
182
+ await helper.deleteDoc(
183
+ getPublicDataPath(appId, 'posts'),
184
+ 'post-123'
185
+ );
186
+ ```
187
+
188
+ **Why soft delete?**
189
+ - ✅ Data recovery possible
190
+ - ✅ Audit trail preserved
191
+ - ✅ Safer for production
192
+ - ✅ Follows industry best practices
193
+
116
194
  ## Installation
117
195
 
118
196
  ```bash
@@ -716,7 +794,20 @@ import type {
716
794
 
717
795
  When using this SDK with LLM-generated code:
718
796
 
719
- 1. **🛡️ MOST IMPORTANT: Use path helper functions to avoid permission-denied errors:**
797
+ 1. **🎯 EASIEST: Use `FirestoreHelper` for everything!**
798
+ ```typescript
799
+ // ✅ BEST PRACTICE - Let helper handle everything
800
+ const { helper } = await initializeWithToken(tokenResponse);
801
+ await helper.addToPublicData('posts', { title: 'Post', content: 'Hello' });
802
+
803
+ // No need to remember:
804
+ // - Required fields (_appId, _createdAt, _createdBy)
805
+ // - Path construction
806
+ // - Data validation
807
+ // - Soft delete logic
808
+ ```
809
+
810
+ 2. **🛡️ Use path helper functions to avoid permission-denied errors:**
720
811
  ```typescript
721
812
  import { getPublicDataPath, getUserDataPath } from '@seaverse/data-service-sdk';
722
813
 
@@ -728,35 +819,40 @@ When using this SDK with LLM-generated code:
728
819
  await addDoc(collection(db, `apps/${appId}/posts`), { ... });
729
820
  ```
730
821
 
731
- 2. **Always include required fields:**
822
+ 3. **⚠️ Never use field names starting with `_`:**
732
823
  ```typescript
733
- {
734
- _appId: appId, // From token response
735
- _createdAt: serverTimestamp(), // Use serverTimestamp()
736
- _createdBy: userId // From token response
737
- }
824
+ // ❌ WRONG - Reserved field
825
+ { _myField: 'value' }
826
+
827
+ // CORRECT
828
+ { myField: 'value' }
738
829
  ```
739
830
 
740
- 3. **Use correct data paths with helpers:**
831
+ 4. **🗑️ Use soft delete instead of hard delete:**
741
832
  ```typescript
742
- // Use path helpers instead of manual strings!
743
- import { getPublicReadPath, getPublicDataPath, getUserDataPath } from '@seaverse/data-service-sdk';
833
+ // RECOMMENDED
834
+ await helper.softDeleteDoc(path, docId);
744
835
 
745
- // publicRead
746
- const configPath = getPublicReadPath(appId, 'config');
836
+ // ❌ Only for admins
837
+ await helper.deleteDoc(path, docId);
838
+ ```
747
839
 
748
- // publicData
749
- const postsPath = getPublicDataPath(appId, 'posts');
840
+ 5. **✅ Validation is automatic, but you can validate manually if needed:**
841
+ ```typescript
842
+ import { validateFirestoreData } from '@seaverse/data-service-sdk';
750
843
 
751
- // userData
752
- const notesPath = getUserDataPath(appId, userId, 'notes');
844
+ try {
845
+ validateFirestoreData(myData);
846
+ } catch (error) {
847
+ console.error('Invalid data:', error.message);
848
+ }
753
849
  ```
754
850
 
755
- 4. **Handle token expiration:**
851
+ 6. **Handle token expiration:**
756
852
  - Tokens expire after 1 hour (3600 seconds)
757
853
  - Check `expires_in` field and refresh when needed
758
854
 
759
- 5. **Use serverTimestamp() for timestamps:**
855
+ 7. **Use serverTimestamp() for timestamps:**
760
856
  ```typescript
761
857
  import { serverTimestamp } from 'firebase/firestore';
762
858
 
@@ -765,7 +861,7 @@ When using this SDK with LLM-generated code:
765
861
  }
766
862
  ```
767
863
 
768
- 6. **Separate guest and authenticated flows:**
864
+ 8. **Separate guest and authenticated flows:**
769
865
  - Use `generateGuestFirestoreToken()` for anonymous users
770
866
  - Use `generateFirestoreToken()` for logged-in users
771
867
 
package/dist/browser.js CHANGED
@@ -4429,6 +4429,260 @@ const PATH_PATTERNS = {
4429
4429
  USER_DATA: 'userData',
4430
4430
  };
4431
4431
 
4432
+ /**
4433
+ * Validation utilities for Firestore data
4434
+ *
4435
+ * These validators help ensure data follows SeaVerse Firestore security rules.
4436
+ * They provide client-side validation before sending data to Firestore.
4437
+ *
4438
+ * 🚨 IMPORTANT: These are CLIENT-SIDE validations only!
4439
+ * The actual security enforcement happens in Firestore Security Rules.
4440
+ * These validators help catch errors early for better DX.
4441
+ */
4442
+ /**
4443
+ * Maximum document size in bytes (256 KB)
4444
+ * This matches the Firestore security rule limit
4445
+ */
4446
+ const MAX_DOCUMENT_SIZE = 262144; // 256 KB
4447
+ /**
4448
+ * System reserved field names that users cannot create
4449
+ *
4450
+ * These fields are managed by the system and cannot be set by users:
4451
+ * - _appId: Application ID (auto-injected)
4452
+ * - _createdBy: Creator user ID (auto-injected)
4453
+ * - _createdAt: Creation timestamp (auto-injected)
4454
+ * - _updatedAt: Last update timestamp (auto-managed)
4455
+ * - _deleted: Soft delete flag (auto-managed)
4456
+ * - _deletedAt: Deletion timestamp (auto-managed)
4457
+ */
4458
+ const ALLOWED_RESERVED_FIELDS = [
4459
+ '_appId',
4460
+ '_createdBy',
4461
+ '_createdAt',
4462
+ '_updatedAt',
4463
+ '_deleted',
4464
+ '_deletedAt',
4465
+ '_updatedBy'
4466
+ ];
4467
+ /**
4468
+ * Common illegal reserved field patterns
4469
+ * Based on Firestore security rules blacklist
4470
+ */
4471
+ const ILLEGAL_RESERVED_FIELDS = [
4472
+ // Single letter prefixes
4473
+ '_a', '_b', '_c', '_d', '_e', '_f', '_g', '_h', '_i', '_j', '_k', '_l', '_m',
4474
+ '_n', '_o', '_p', '_q', '_r', '_s', '_t', '_u', '_v', '_w', '_x', '_y', '_z',
4475
+ '_A', '_B', '_C', '_D', '_E', '_F', '_G', '_H', '_I', '_J', '_K', '_L', '_M',
4476
+ '_N', '_O', '_P', '_Q', '_R', '_S', '_T', '_U', '_V', '_W', '_X', '_Y', '_Z',
4477
+ // Number prefixes
4478
+ '_0', '_1', '_2', '_3', '_4', '_5', '_6', '_7', '_8', '_9',
4479
+ // Multiple underscores
4480
+ '__', '___', '____',
4481
+ // Permission related
4482
+ '_admin', '_user', '_role', '_permission', '_access', '_auth', '_owner', '_public',
4483
+ // Metadata related
4484
+ '_custom', '_data', '_meta', '_info', '_config', '_setting', '_value', '_key',
4485
+ '_id', '_ID', '_ref', '_timestamp', '_time', '_date', '_status', '_type',
4486
+ // Temporary fields
4487
+ '_temp', '_tmp', '_test', '_new', '_old', '_bak', '_backup', '_copy',
4488
+ // Common business fields
4489
+ '_name', '_title', '_description', '_content', '_body', '_text', '_message',
4490
+ '_email', '_phone', '_address', '_city', '_country', '_zip', '_code',
4491
+ '_price', '_amount', '_quantity', '_total', '_subtotal', '_discount', '_tax',
4492
+ '_image', '_avatar', '_photo', '_picture', '_file', '_url', '_link', '_path',
4493
+ '_user_id', '_userId', '_username', '_nickname', '_displayName',
4494
+ '_password', '_token', '_session', '_apiKey', '_secretKey', '_privateKey',
4495
+ // Flag fields
4496
+ '_flag', '_enabled', '_disabled', '_active', '_inactive', '_visible', '_hidden',
4497
+ '_isAdmin', '_isPublic', '_isPrivate', '_isDeleted', '_isActive', '_isEnabled',
4498
+ // State fields
4499
+ '_state', '_mode', '_level', '_priority', '_order', '_index', '_count', '_number',
4500
+ // System fields
4501
+ '_system', '_internal', '_private', '_protected', '_reserved', '_secret', '_hidden'
4502
+ ];
4503
+ /**
4504
+ * Validate that data doesn't contain illegal reserved fields
4505
+ *
4506
+ * Reserved fields (starting with _) are for system use only.
4507
+ * Users can only use allowed system fields.
4508
+ *
4509
+ * @param data - Data object to validate
4510
+ * @throws Error if illegal reserved fields are found
4511
+ *
4512
+ * @example
4513
+ * ```typescript
4514
+ * // ✅ Valid - no reserved fields
4515
+ * validateReservedFields({ title: 'Post', content: 'Hello' });
4516
+ *
4517
+ * // ✅ Valid - allowed system fields
4518
+ * validateReservedFields({ _appId: 'app-1', _createdBy: 'user-1', title: 'Post' });
4519
+ *
4520
+ * // ❌ Invalid - illegal reserved field
4521
+ * validateReservedFields({ _custom: 'value', title: 'Post' });
4522
+ * // Throws: Error: Illegal reserved field "_custom"
4523
+ * ```
4524
+ */
4525
+ function validateReservedFields(data) {
4526
+ const keys = Object.keys(data);
4527
+ for (const key of keys) {
4528
+ // Skip allowed system fields
4529
+ if (ALLOWED_RESERVED_FIELDS.includes(key)) {
4530
+ continue;
4531
+ }
4532
+ // Check if it's a reserved field (starts with _)
4533
+ if (key.startsWith('_')) {
4534
+ // Check if it's in the blacklist
4535
+ if (ILLEGAL_RESERVED_FIELDS.includes(key)) {
4536
+ throw new Error(`Illegal reserved field "${key}". ` +
4537
+ `Fields starting with "_" are reserved for system use. ` +
4538
+ `Please use a field name without the underscore prefix.`);
4539
+ }
4540
+ // Even if not in blacklist, warn about unknown _ fields
4541
+ throw new Error(`Unknown reserved field "${key}". ` +
4542
+ `Fields starting with "_" are reserved for system use. ` +
4543
+ `Allowed system fields: ${ALLOWED_RESERVED_FIELDS.join(', ')}. ` +
4544
+ `Please use a field name without the underscore prefix.`);
4545
+ }
4546
+ }
4547
+ }
4548
+ /**
4549
+ * Estimate document size in bytes
4550
+ *
4551
+ * This is an approximation based on JSON serialization.
4552
+ * Firestore may calculate size differently, but this gives a good estimate.
4553
+ *
4554
+ * @param data - Data object to measure
4555
+ * @returns Estimated size in bytes
4556
+ *
4557
+ * @example
4558
+ * ```typescript
4559
+ * const data = { title: 'My Post', content: 'Long content...' };
4560
+ * const size = estimateDocumentSize(data);
4561
+ * console.log('Document size:', size, 'bytes');
4562
+ * ```
4563
+ */
4564
+ function estimateDocumentSize(data) {
4565
+ try {
4566
+ const json = JSON.stringify(data);
4567
+ // Use Blob if available (browser), otherwise estimate from string length
4568
+ if (typeof Blob !== 'undefined') {
4569
+ return new Blob([json]).size;
4570
+ }
4571
+ else {
4572
+ // Node.js or environments without Blob: estimate from UTF-8 encoded length
4573
+ return Buffer.byteLength(json, 'utf8');
4574
+ }
4575
+ }
4576
+ catch (error) {
4577
+ // Fallback: rough estimate
4578
+ return JSON.stringify(data).length * 2; // Assume ~2 bytes per char for safety
4579
+ }
4580
+ }
4581
+ /**
4582
+ * Validate document size doesn't exceed limit
4583
+ *
4584
+ * Firestore has a maximum document size of 1MB, but we enforce 256KB
4585
+ * to match our security rules limit.
4586
+ *
4587
+ * @param data - Data object to validate
4588
+ * @throws Error if document is too large
4589
+ *
4590
+ * @example
4591
+ * ```typescript
4592
+ * const data = { title: 'Post', content: 'Some content' };
4593
+ * validateDocumentSize(data); // OK
4594
+ *
4595
+ * const hugeData = { content: 'x'.repeat(300000) };
4596
+ * validateDocumentSize(hugeData); // Throws error
4597
+ * ```
4598
+ */
4599
+ function validateDocumentSize(data) {
4600
+ const size = estimateDocumentSize(data);
4601
+ if (size > MAX_DOCUMENT_SIZE) {
4602
+ throw new Error(`Document size (${size} bytes) exceeds maximum allowed size (${MAX_DOCUMENT_SIZE} bytes / 256 KB). ` +
4603
+ `Please reduce the amount of data you're storing in this document.`);
4604
+ }
4605
+ }
4606
+ /**
4607
+ * Validate data before sending to Firestore
4608
+ *
4609
+ * This runs all validations:
4610
+ * - Reserved fields check
4611
+ * - Document size check
4612
+ *
4613
+ * @param data - Data object to validate
4614
+ * @throws Error if validation fails
4615
+ *
4616
+ * @example
4617
+ * ```typescript
4618
+ * // Use this before adding/updating documents
4619
+ * try {
4620
+ * validateFirestoreData(myData);
4621
+ * await addDoc(collection(db, path), myData);
4622
+ * } catch (error) {
4623
+ * console.error('Validation failed:', error.message);
4624
+ * }
4625
+ * ```
4626
+ */
4627
+ function validateFirestoreData(data) {
4628
+ validateReservedFields(data);
4629
+ validateDocumentSize(data);
4630
+ }
4631
+ /**
4632
+ * Check if data contains soft-delete markers
4633
+ *
4634
+ * @param data - Data object to check
4635
+ * @returns True if document is marked as deleted
4636
+ */
4637
+ function isDeleted(data) {
4638
+ return data._deleted === true;
4639
+ }
4640
+ /**
4641
+ * Validate data and return detailed results instead of throwing
4642
+ *
4643
+ * Use this when you want to handle validation errors gracefully
4644
+ * without try/catch blocks.
4645
+ *
4646
+ * @param data - Data object to validate
4647
+ * @returns Validation result with errors
4648
+ *
4649
+ * @example
4650
+ * ```typescript
4651
+ * const result = validateDataDetailed(myData);
4652
+ * if (!result.valid) {
4653
+ * console.error('Validation errors:', result.errors);
4654
+ * // Show errors to user
4655
+ * } else {
4656
+ * // Proceed with save
4657
+ * }
4658
+ * ```
4659
+ */
4660
+ function validateDataDetailed(data) {
4661
+ const errors = [];
4662
+ // Check reserved fields
4663
+ try {
4664
+ validateReservedFields(data);
4665
+ }
4666
+ catch (error) {
4667
+ if (error instanceof Error) {
4668
+ errors.push(error.message);
4669
+ }
4670
+ }
4671
+ // Check document size
4672
+ try {
4673
+ validateDocumentSize(data);
4674
+ }
4675
+ catch (error) {
4676
+ if (error instanceof Error) {
4677
+ errors.push(error.message);
4678
+ }
4679
+ }
4680
+ return {
4681
+ valid: errors.length === 0,
4682
+ errors
4683
+ };
4684
+ }
4685
+
4432
4686
  /**
4433
4687
  * Firestore Helper - LLM-Friendly Firestore Operations
4434
4688
  *
@@ -4523,25 +4777,36 @@ class FirestoreHelper {
4523
4777
  /**
4524
4778
  * Get all documents from publicData collection
4525
4779
  *
4780
+ * By default, this returns only non-deleted documents.
4781
+ * Set includeDeleted=true to include soft-deleted documents.
4782
+ *
4526
4783
  * @param collectionName - Collection name
4784
+ * @param includeDeleted - Include soft-deleted documents (default: false)
4527
4785
  * @returns QuerySnapshot with documents
4528
4786
  *
4529
4787
  * @example
4530
4788
  * ```typescript
4789
+ * // Get only active posts (not deleted)
4531
4790
  * const snapshot = await helper.getPublicData('posts');
4532
4791
  * snapshot.forEach(doc => {
4533
4792
  * console.log(doc.id, doc.data());
4534
4793
  * });
4794
+ *
4795
+ * // Include deleted posts (admin use case)
4796
+ * const allPosts = await helper.getPublicData('posts', true);
4535
4797
  * ```
4536
4798
  */
4537
- async getPublicData(collectionName) {
4799
+ async getPublicData(collectionName, includeDeleted = false) {
4538
4800
  const path = getPublicDataPath(this.appId, collectionName);
4539
- return this.getDocs(path);
4801
+ return this.getDocs(path, includeDeleted);
4540
4802
  }
4541
4803
  /**
4542
4804
  * Get all documents from userData collection (user's private data)
4543
4805
  *
4806
+ * By default, this returns only non-deleted documents.
4807
+ *
4544
4808
  * @param collectionName - Collection name
4809
+ * @param includeDeleted - Include soft-deleted documents (default: false)
4545
4810
  * @returns QuerySnapshot with documents
4546
4811
  *
4547
4812
  * @example
@@ -4552,9 +4817,9 @@ class FirestoreHelper {
4552
4817
  * });
4553
4818
  * ```
4554
4819
  */
4555
- async getUserData(collectionName) {
4820
+ async getUserData(collectionName, includeDeleted = false) {
4556
4821
  const path = getUserDataPath(this.appId, this.userId, collectionName);
4557
- return this.getDocs(path);
4822
+ return this.getDocs(path, includeDeleted);
4558
4823
  }
4559
4824
  /**
4560
4825
  * Get all documents from publicRead collection (read-only for users)
@@ -4634,6 +4899,8 @@ class FirestoreHelper {
4634
4899
  * ```
4635
4900
  */
4636
4901
  async updateDoc(collectionPath, docId, data) {
4902
+ // Validate user data
4903
+ validateFirestoreData(data);
4637
4904
  const { updateDoc, doc, serverTimestamp } = await this.loadFirestore();
4638
4905
  const docRef = doc(this.db, collectionPath, docId);
4639
4906
  return updateDoc(docRef, {
@@ -4643,10 +4910,50 @@ class FirestoreHelper {
4643
4910
  });
4644
4911
  }
4645
4912
  /**
4646
- * Delete document
4913
+ * Soft delete document (mark as deleted without removing)
4914
+ *
4915
+ * This is the RECOMMENDED way to delete documents. It marks the document
4916
+ * as deleted without actually removing it from the database.
4917
+ *
4918
+ * Automatically sets: _deleted = true, _deletedAt = serverTimestamp()
4919
+ *
4920
+ * @param collectionPath - Full collection path
4921
+ * @param docId - Document ID
4922
+ *
4923
+ * @example
4924
+ * ```typescript
4925
+ * // Soft delete a post (recommended)
4926
+ * await helper.softDeleteDoc(
4927
+ * getPublicDataPath(appId, 'posts'),
4928
+ * 'post-123'
4929
+ * );
4930
+ * ```
4931
+ */
4932
+ async softDeleteDoc(collectionPath, docId) {
4933
+ const { updateDoc, doc, serverTimestamp } = await this.loadFirestore();
4934
+ const docRef = doc(this.db, collectionPath, docId);
4935
+ return updateDoc(docRef, {
4936
+ _deleted: true,
4937
+ _deletedAt: serverTimestamp()
4938
+ });
4939
+ }
4940
+ /**
4941
+ * Hard delete document (permanently remove from database)
4942
+ *
4943
+ * ⚠️ WARNING: This permanently removes the document.
4944
+ * Only admins can hard delete. Regular users should use softDeleteDoc().
4647
4945
  *
4648
4946
  * @param collectionPath - Full collection path
4649
4947
  * @param docId - Document ID
4948
+ *
4949
+ * @example
4950
+ * ```typescript
4951
+ * // Hard delete (admin only)
4952
+ * await helper.deleteDoc(
4953
+ * getPublicDataPath(appId, 'posts'),
4954
+ * 'post-123'
4955
+ * );
4956
+ * ```
4650
4957
  */
4651
4958
  async deleteDoc(collectionPath, docId) {
4652
4959
  const { deleteDoc, doc } = await this.loadFirestore();
@@ -4682,22 +4989,34 @@ class FirestoreHelper {
4682
4989
  * Internal: Add document with metadata injection
4683
4990
  */
4684
4991
  async addDocWithMeta(collectionPath, data) {
4992
+ // Validate user data before adding system fields
4993
+ validateFirestoreData(data);
4685
4994
  const { addDoc, collection, serverTimestamp } = await this.loadFirestore();
4686
4995
  const colRef = collection(this.db, collectionPath);
4687
- return addDoc(colRef, {
4996
+ const docData = {
4688
4997
  _appId: this.appId,
4689
4998
  _createdAt: serverTimestamp(),
4690
4999
  _createdBy: this.userId,
4691
5000
  ...data
4692
- });
5001
+ };
5002
+ return addDoc(colRef, docData);
4693
5003
  }
4694
5004
  /**
4695
5005
  * Internal: Get all documents from collection
5006
+ * Optionally filter out soft-deleted documents
4696
5007
  */
4697
- async getDocs(collectionPath) {
4698
- const { getDocs, collection } = await this.loadFirestore();
5008
+ async getDocs(collectionPath, includeDeleted = false) {
5009
+ const { getDocs, collection, query, where } = await this.loadFirestore();
4699
5010
  const colRef = collection(this.db, collectionPath);
4700
- return getDocs(colRef);
5011
+ if (includeDeleted) {
5012
+ // Return all documents (including soft-deleted)
5013
+ return getDocs(colRef);
5014
+ }
5015
+ else {
5016
+ // Filter out soft-deleted documents
5017
+ const q = query(colRef, where('_deleted', '==', false));
5018
+ return getDocs(q);
5019
+ }
4701
5020
  }
4702
5021
  /**
4703
5022
  * Internal: Get collection reference
@@ -4901,5 +5220,5 @@ async function initializeWithToken(tokenResponse) {
4901
5220
  };
4902
5221
  }
4903
5222
 
4904
- export { DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DataServiceClient, ENDPOINTS, FirestoreHelper, PATH_PATTERNS, PathBuilder, addDocWithMeta, getFirebaseConfig, getPublicDataDocPath, getPublicDataPath, getPublicReadDocPath, getPublicReadPath, getUserDataDocPath, getUserDataPath, initializeWithToken, updateDocWithMeta };
5223
+ export { ALLOWED_RESERVED_FIELDS, DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DataServiceClient, ENDPOINTS, FirestoreHelper, MAX_DOCUMENT_SIZE, PATH_PATTERNS, PathBuilder, addDocWithMeta, estimateDocumentSize, getFirebaseConfig, getPublicDataDocPath, getPublicDataPath, getPublicReadDocPath, getPublicReadPath, getUserDataDocPath, getUserDataPath, initializeWithToken, isDeleted, updateDocWithMeta, validateDataDetailed, validateDocumentSize, validateFirestoreData, validateReservedFields };
4905
5224
  //# sourceMappingURL=browser.js.map