@seaverse/data-service-sdk 0.9.0 → 0.10.1

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/dist/index.d.ts CHANGED
@@ -16,14 +16,14 @@ export { CollectionReference, DocumentData, DocumentReference, DocumentSnapshot,
16
16
  * 2. publicData/ - Read-write public data (e.g., user-generated content)
17
17
  * Read: All authenticated users | Write: All authenticated users
18
18
  *
19
- * 3. userData/{userId}/ - Private user data (e.g., personal settings, private notes)
19
+ * 3. userData/{userId}/_data/ - Private user data (e.g., personal settings, private notes)
20
20
  * Read: Owner only | Write: Owner only
21
21
  *
22
22
  * DATA PATH STRUCTURE:
23
23
  * -------------------
24
- * appData/{app_id}/publicRead/{collection}/{docId}
25
- * appData/{app_id}/publicData/{collection}/{docId}
26
- * appData/{app_id}/userData/{userId}/{collection}/{docId}
24
+ * appData/{app_id}/publicRead/_data/{collection}/{docId}
25
+ * appData/{app_id}/publicData/_data/{collection}/{docId}
26
+ * appData/{app_id}/userData/{userId}/_data/{collection}/{docId}
27
27
  *
28
28
  * 🚨 CRITICAL FIRESTORE PATH RULES (FOR LLM):
29
29
  * ------------------------------------------
@@ -260,9 +260,9 @@ interface DataServiceClientOptions {
260
260
  * ---------------------------
261
261
  * Your Firestore data is organized in three permission levels:
262
262
  *
263
- * 1. publicRead/ - System configs, announcements (Read: Everyone, Write: Admin only)
264
- * 2. publicData/ - User posts, shared content (Read: Everyone, Write: Everyone)
265
- * 3. userData/{userId}/ - Private user data (Read/Write: Owner only)
263
+ * 1. publicRead/_data/ - System configs, announcements (Read: Everyone, Write: Admin only)
264
+ * 2. publicData/_data/ - User posts, shared content (Read: Everyone, Write: Everyone)
265
+ * 3. userData/{userId}/_data/ - Private user data (Read/Write: Owner only)
266
266
  *
267
267
  * QUICK START FOR LLM:
268
268
  * -------------------
@@ -314,7 +314,7 @@ interface DataServiceClientOptions {
314
314
  * const snapshot = await getDocs(collection(db, `appData/${appId}/publicData/posts`));
315
315
  *
316
316
  * // Write to userData (only owner can write)
317
- * await addDoc(collection(db, `appData/${appId}/userData/${userId}/notes`), {
317
+ * await addDoc(collection(db, `appData/${appId}/userData/${userId}/_data/notes`), {
318
318
  * _appId: appId, // REQUIRED
319
319
  * _createdAt: serverTimestamp(), // REQUIRED
320
320
  * _createdBy: userId, // REQUIRED
@@ -544,22 +544,33 @@ declare class FirestoreHelper {
544
544
  /**
545
545
  * Get all documents from publicData collection
546
546
  *
547
+ * By default, this returns only non-deleted documents.
548
+ * Set includeDeleted=true to include soft-deleted documents.
549
+ *
547
550
  * @param collectionName - Collection name
551
+ * @param includeDeleted - Include soft-deleted documents (default: false)
548
552
  * @returns QuerySnapshot with documents
549
553
  *
550
554
  * @example
551
555
  * ```typescript
556
+ * // Get only active posts (not deleted)
552
557
  * const snapshot = await helper.getPublicData('posts');
553
558
  * snapshot.forEach(doc => {
554
559
  * console.log(doc.id, doc.data());
555
560
  * });
561
+ *
562
+ * // Include deleted posts (admin use case)
563
+ * const allPosts = await helper.getPublicData('posts', true);
556
564
  * ```
557
565
  */
558
- getPublicData(collectionName: string): Promise<QuerySnapshot>;
566
+ getPublicData(collectionName: string, includeDeleted?: boolean): Promise<QuerySnapshot>;
559
567
  /**
560
568
  * Get all documents from userData collection (user's private data)
561
569
  *
570
+ * By default, this returns only non-deleted documents.
571
+ *
562
572
  * @param collectionName - Collection name
573
+ * @param includeDeleted - Include soft-deleted documents (default: false)
563
574
  * @returns QuerySnapshot with documents
564
575
  *
565
576
  * @example
@@ -570,7 +581,7 @@ declare class FirestoreHelper {
570
581
  * });
571
582
  * ```
572
583
  */
573
- getUserData(collectionName: string): Promise<QuerySnapshot>;
584
+ getUserData(collectionName: string, includeDeleted?: boolean): Promise<QuerySnapshot>;
574
585
  /**
575
586
  * Get all documents from publicRead collection (read-only for users)
576
587
  *
@@ -638,10 +649,43 @@ declare class FirestoreHelper {
638
649
  */
639
650
  updateDoc(collectionPath: string, docId: string, data: Record<string, any>): Promise<void>;
640
651
  /**
641
- * Delete document
652
+ * Soft delete document (mark as deleted without removing)
653
+ *
654
+ * This is the RECOMMENDED way to delete documents. It marks the document
655
+ * as deleted without actually removing it from the database.
656
+ *
657
+ * Automatically sets: _deleted = true, _deletedAt = serverTimestamp()
642
658
  *
643
659
  * @param collectionPath - Full collection path
644
660
  * @param docId - Document ID
661
+ *
662
+ * @example
663
+ * ```typescript
664
+ * // Soft delete a post (recommended)
665
+ * await helper.softDeleteDoc(
666
+ * getPublicDataPath(appId, 'posts'),
667
+ * 'post-123'
668
+ * );
669
+ * ```
670
+ */
671
+ softDeleteDoc(collectionPath: string, docId: string): Promise<void>;
672
+ /**
673
+ * Hard delete document (permanently remove from database)
674
+ *
675
+ * ⚠️ WARNING: This permanently removes the document.
676
+ * Only admins can hard delete. Regular users should use softDeleteDoc().
677
+ *
678
+ * @param collectionPath - Full collection path
679
+ * @param docId - Document ID
680
+ *
681
+ * @example
682
+ * ```typescript
683
+ * // Hard delete (admin only)
684
+ * await helper.deleteDoc(
685
+ * getPublicDataPath(appId, 'posts'),
686
+ * 'post-123'
687
+ * );
688
+ * ```
645
689
  */
646
690
  deleteDoc(collectionPath: string, docId: string): Promise<void>;
647
691
  /**
@@ -668,6 +712,7 @@ declare class FirestoreHelper {
668
712
  private addDocWithMeta;
669
713
  /**
670
714
  * Internal: Get all documents from collection
715
+ * Optionally filter out soft-deleted documents
671
716
  */
672
717
  private getDocs;
673
718
  /**
@@ -868,7 +913,7 @@ declare function getPublicDataPath(appId: string, collectionName: string): strin
868
913
  * ```typescript
869
914
  * // Write private user notes
870
915
  * const path = getUserDataPath('my-app', 'user-123', 'notes');
871
- * // Returns: 'appData/my-app/userData/user-123/notes'
916
+ * // Returns: 'appData/my-app/userData/user-123/_data/notes'
872
917
  *
873
918
  * await addDoc(collection(db, path), {
874
919
  * _appId: appId,
@@ -925,7 +970,7 @@ declare function getPublicDataDocPath(appId: string, collectionName: string, doc
925
970
  * @example
926
971
  * ```typescript
927
972
  * const path = getUserDataDocPath('my-app', 'user-123', 'notes', 'note-456');
928
- * // Returns: 'appData/my-app/userData/user-123/notes/note-456'
973
+ * // Returns: 'appData/my-app/userData/user-123/_data/notes/note-456'
929
974
  *
930
975
  * const docSnap = await getDoc(doc(db, path));
931
976
  * ```
@@ -988,5 +1033,149 @@ declare const PATH_PATTERNS: {
988
1033
  readonly USER_DATA: "userData";
989
1034
  };
990
1035
 
991
- export { DEFAULT_BASE_URL, DEFAULT_TIMEOUT, DataServiceClient, ENDPOINTS, FirestoreHelper, PATH_PATTERNS, PathBuilder, addDocWithMeta, getFirebaseConfig, getPublicDataDocPath, getPublicDataPath, getPublicReadDocPath, getPublicReadPath, getUserDataDocPath, getUserDataPath, initializeWithToken, updateDocWithMeta };
992
- export type { ApiError, ApiResponse, DataServiceClientOptions, FirebaseConfig, FirestoreTokenResponse, GenerateFirestoreTokenRequest, GenerateGuestFirestoreTokenRequest };
1036
+ /**
1037
+ * Validation utilities for Firestore data
1038
+ *
1039
+ * These validators help ensure data follows SeaVerse Firestore security rules.
1040
+ * They provide client-side validation before sending data to Firestore.
1041
+ *
1042
+ * 🚨 IMPORTANT: These are CLIENT-SIDE validations only!
1043
+ * The actual security enforcement happens in Firestore Security Rules.
1044
+ * These validators help catch errors early for better DX.
1045
+ */
1046
+ /**
1047
+ * Maximum document size in bytes (256 KB)
1048
+ * This matches the Firestore security rule limit
1049
+ */
1050
+ declare const MAX_DOCUMENT_SIZE = 262144;
1051
+ /**
1052
+ * System reserved field names that users cannot create
1053
+ *
1054
+ * These fields are managed by the system and cannot be set by users:
1055
+ * - _appId: Application ID (auto-injected)
1056
+ * - _createdBy: Creator user ID (auto-injected)
1057
+ * - _createdAt: Creation timestamp (auto-injected)
1058
+ * - _updatedAt: Last update timestamp (auto-managed)
1059
+ * - _deleted: Soft delete flag (auto-managed)
1060
+ * - _deletedAt: Deletion timestamp (auto-managed)
1061
+ */
1062
+ declare const ALLOWED_RESERVED_FIELDS: string[];
1063
+ /**
1064
+ * Validate that data doesn't contain illegal reserved fields
1065
+ *
1066
+ * Reserved fields (starting with _) are for system use only.
1067
+ * Users can only use allowed system fields.
1068
+ *
1069
+ * @param data - Data object to validate
1070
+ * @throws Error if illegal reserved fields are found
1071
+ *
1072
+ * @example
1073
+ * ```typescript
1074
+ * // ✅ Valid - no reserved fields
1075
+ * validateReservedFields({ title: 'Post', content: 'Hello' });
1076
+ *
1077
+ * // ✅ Valid - allowed system fields
1078
+ * validateReservedFields({ _appId: 'app-1', _createdBy: 'user-1', title: 'Post' });
1079
+ *
1080
+ * // ❌ Invalid - illegal reserved field
1081
+ * validateReservedFields({ _custom: 'value', title: 'Post' });
1082
+ * // Throws: Error: Illegal reserved field "_custom"
1083
+ * ```
1084
+ */
1085
+ declare function validateReservedFields(data: Record<string, any>): void;
1086
+ /**
1087
+ * Estimate document size in bytes
1088
+ *
1089
+ * This is an approximation based on JSON serialization.
1090
+ * Firestore may calculate size differently, but this gives a good estimate.
1091
+ *
1092
+ * @param data - Data object to measure
1093
+ * @returns Estimated size in bytes
1094
+ *
1095
+ * @example
1096
+ * ```typescript
1097
+ * const data = { title: 'My Post', content: 'Long content...' };
1098
+ * const size = estimateDocumentSize(data);
1099
+ * console.log('Document size:', size, 'bytes');
1100
+ * ```
1101
+ */
1102
+ declare function estimateDocumentSize(data: Record<string, any>): number;
1103
+ /**
1104
+ * Validate document size doesn't exceed limit
1105
+ *
1106
+ * Firestore has a maximum document size of 1MB, but we enforce 256KB
1107
+ * to match our security rules limit.
1108
+ *
1109
+ * @param data - Data object to validate
1110
+ * @throws Error if document is too large
1111
+ *
1112
+ * @example
1113
+ * ```typescript
1114
+ * const data = { title: 'Post', content: 'Some content' };
1115
+ * validateDocumentSize(data); // OK
1116
+ *
1117
+ * const hugeData = { content: 'x'.repeat(300000) };
1118
+ * validateDocumentSize(hugeData); // Throws error
1119
+ * ```
1120
+ */
1121
+ declare function validateDocumentSize(data: Record<string, any>): void;
1122
+ /**
1123
+ * Validate data before sending to Firestore
1124
+ *
1125
+ * This runs all validations:
1126
+ * - Reserved fields check
1127
+ * - Document size check
1128
+ *
1129
+ * @param data - Data object to validate
1130
+ * @throws Error if validation fails
1131
+ *
1132
+ * @example
1133
+ * ```typescript
1134
+ * // Use this before adding/updating documents
1135
+ * try {
1136
+ * validateFirestoreData(myData);
1137
+ * await addDoc(collection(db, path), myData);
1138
+ * } catch (error) {
1139
+ * console.error('Validation failed:', error.message);
1140
+ * }
1141
+ * ```
1142
+ */
1143
+ declare function validateFirestoreData(data: Record<string, any>): void;
1144
+ /**
1145
+ * Check if data contains soft-delete markers
1146
+ *
1147
+ * @param data - Data object to check
1148
+ * @returns True if document is marked as deleted
1149
+ */
1150
+ declare function isDeleted(data: Record<string, any>): boolean;
1151
+ /**
1152
+ * Validation result for detailed error reporting
1153
+ */
1154
+ interface ValidationResult {
1155
+ valid: boolean;
1156
+ errors: string[];
1157
+ }
1158
+ /**
1159
+ * Validate data and return detailed results instead of throwing
1160
+ *
1161
+ * Use this when you want to handle validation errors gracefully
1162
+ * without try/catch blocks.
1163
+ *
1164
+ * @param data - Data object to validate
1165
+ * @returns Validation result with errors
1166
+ *
1167
+ * @example
1168
+ * ```typescript
1169
+ * const result = validateDataDetailed(myData);
1170
+ * if (!result.valid) {
1171
+ * console.error('Validation errors:', result.errors);
1172
+ * // Show errors to user
1173
+ * } else {
1174
+ * // Proceed with save
1175
+ * }
1176
+ * ```
1177
+ */
1178
+ declare function validateDataDetailed(data: Record<string, any>): ValidationResult;
1179
+
1180
+ 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 };
1181
+ export type { ApiError, ApiResponse, DataServiceClientOptions, FirebaseConfig, FirestoreTokenResponse, GenerateFirestoreTokenRequest, GenerateGuestFirestoreTokenRequest, ValidationResult };