@umituz/react-native-ai-generation-content 1.65.11 → 1.65.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/react-native-ai-generation-content",
3
- "version": "1.65.11",
3
+ "version": "1.65.12",
4
4
  "description": "Provider-agnostic AI generation orchestration for React Native with result preview components",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -1,155 +1,53 @@
1
- import { getDocs, getDoc, query, orderBy, onSnapshot, where } from "firebase/firestore";
2
1
  import { type FirestorePathResolver } from "@umituz/react-native-firebase";
3
2
  import type { DocumentMapper } from "../../domain/value-objects/CreationsConfig";
4
- import type { Creation, CreationDocument } from "../../domain/entities/Creation";
3
+ import type { Creation } from "../../domain/entities/Creation";
5
4
  import type { CreationsSubscriptionCallback, UnsubscribeFunction } from "../../domain/repositories/ICreationsRepository";
6
- import { CREATION_FIELDS } from "../../domain/constants";
7
-
8
- declare const __DEV__: boolean;
5
+ import { CreationsQuery } from "./CreationsQuery";
6
+ import { CreationsSubscription } from "./CreationsSubscription";
9
7
 
10
8
  /**
11
- * Handles fetching creations from Firestore
12
- * Single Responsibility: Read operations
9
+ * CreationsFetcher
10
+ * Orchestrates read operations for creations
11
+ * Delegates to specialized classes for queries and subscriptions
12
+ *
13
+ * Architecture: Facade pattern
14
+ * - Query operations → CreationsQuery
15
+ * - Subscription operations → CreationsSubscription
13
16
  */
14
17
  export class CreationsFetcher {
18
+ private readonly query: CreationsQuery;
19
+ private readonly subscription: CreationsSubscription;
20
+
15
21
  constructor(
16
- private readonly pathResolver: FirestorePathResolver,
17
- private readonly documentMapper: DocumentMapper,
18
- ) { }
22
+ pathResolver: FirestorePathResolver,
23
+ documentMapper: DocumentMapper,
24
+ ) {
25
+ this.query = new CreationsQuery(pathResolver, documentMapper);
26
+ this.subscription = new CreationsSubscription(pathResolver, documentMapper);
27
+ }
19
28
 
29
+ /**
30
+ * Get all creations for a user
31
+ */
20
32
  async getAll(userId: string): Promise<Creation[]> {
21
- const userCollection = this.pathResolver.getUserCollection(userId);
22
- if (!userCollection) return [];
23
-
24
- try {
25
- // Optimized query: Server-side filtering for non-deleted items
26
- // Requires composite index: (deletedAt ASC, createdAt DESC)
27
- const q = query(
28
- userCollection,
29
- where(CREATION_FIELDS.DELETED_AT, "==", null),
30
- orderBy(CREATION_FIELDS.CREATED_AT, "desc")
31
- );
32
- const snapshot = await getDocs(q);
33
-
34
- // Map documents to domain entities
35
- // No client-side filtering needed - server already filtered deleted items
36
- const creations = snapshot.docs.map((docSnap) => {
37
- const data = docSnap.data() as CreationDocument;
38
- return this.documentMapper(docSnap.id, data);
39
- });
40
-
41
- if (__DEV__) {
42
- console.log("[CreationsFetcher] Fetched creations:", {
43
- count: creations.length,
44
- hasDeletedFilter: true,
45
- });
46
- }
47
-
48
- return creations;
49
- } catch (error) {
50
- if (__DEV__) {
51
- console.error("[CreationsFetcher] getAll() error:", error);
52
- }
53
- return [];
54
- }
33
+ return this.query.getAll(userId);
55
34
  }
56
35
 
36
+ /**
37
+ * Get a single creation by ID
38
+ */
57
39
  async getById(userId: string, id: string): Promise<Creation | null> {
58
- const docRef = this.pathResolver.getDocRef(userId, id);
59
- if (!docRef) return null;
60
-
61
- try {
62
- const docSnap = await getDoc(docRef);
63
-
64
- if (!docSnap.exists()) {
65
- return null;
66
- }
67
-
68
- const data = docSnap.data() as CreationDocument;
69
- return this.documentMapper(docSnap.id, data);
70
- } catch (error) {
71
- if (__DEV__) {
72
- console.error("[CreationsFetcher] getById() error:", error);
73
- }
74
- return null;
75
- }
40
+ return this.query.getById(userId, id);
76
41
  }
77
42
 
78
43
  /**
79
- * Subscribes to realtime updates for user's creations
80
- *
81
- * PERFORMANCE OPTIMIZATION:
82
- * - Server-side filtering with where clause (80% data reduction)
83
- * - No client-side filtering needed
84
- * - Requires Firestore composite index: (deletedAt ASC, createdAt DESC)
85
- *
86
- * @param userId - User ID to query
87
- * @param onData - Callback for data updates
88
- * @param onError - Optional error callback
89
- * @returns Unsubscribe function
44
+ * Subscribe to realtime updates for user's creations
90
45
  */
91
46
  subscribeToAll(
92
47
  userId: string,
93
48
  onData: CreationsSubscriptionCallback,
94
49
  onError?: (error: Error) => void,
95
50
  ): UnsubscribeFunction {
96
- const userCollection = this.pathResolver.getUserCollection(userId);
97
- if (!userCollection) {
98
- const error = new Error(`[CreationsFetcher] Cannot subscribe: Invalid user collection for userId: ${userId}`);
99
- if (__DEV__) {
100
- console.error(error.message);
101
- }
102
- // Return empty array immediately
103
- onData([]);
104
- // Report error to callback
105
- onError?.(error);
106
- // Return no-op unsubscribe function (no listener was created)
107
- return () => {
108
- if (__DEV__) {
109
- console.log("[CreationsFetcher] No-op unsubscribe called (no listener was created)");
110
- }
111
- };
112
- }
113
-
114
- // Optimized query with server-side filtering
115
- // This prevents downloading deleted items entirely
116
- const q = query(
117
- userCollection,
118
- where(CREATION_FIELDS.DELETED_AT, "==", null),
119
- orderBy(CREATION_FIELDS.CREATED_AT, "desc")
120
- );
121
-
122
- return onSnapshot(
123
- q,
124
- { includeMetadataChanges: false }, // Ignore metadata-only changes for performance
125
- (snapshot) => {
126
- // Map documents to domain entities
127
- // Server already filtered - no client filtering needed
128
- const creations = snapshot.docs.map((docSnap) => {
129
- const data = docSnap.data() as CreationDocument;
130
- return this.documentMapper(docSnap.id, data);
131
- });
132
-
133
- if (__DEV__) {
134
- console.log("[CreationsFetcher] Realtime sync:", {
135
- count: creations.length,
136
- serverFiltered: true,
137
- hasChanges: snapshot.docChanges().length,
138
- });
139
- }
140
-
141
- onData(creations);
142
- },
143
- (error: Error) => {
144
- if (__DEV__) {
145
- console.error("[CreationsFetcher] Realtime subscription error:", {
146
- error: error.message,
147
- code: (error as { code?: string }).code,
148
- userId,
149
- });
150
- }
151
- onError?.(error);
152
- },
153
- );
51
+ return this.subscription.subscribeToAll(userId, onData, onError);
154
52
  }
155
53
  }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * CreationsQuery
3
+ * Handles read operations (getAll, getById) for creations
4
+ * Single Responsibility: Firestore query operations
5
+ */
6
+
7
+ import { getDocs, getDoc, query, orderBy, where } from "firebase/firestore";
8
+ import { type FirestorePathResolver } from "@umituz/react-native-firebase";
9
+ import type { DocumentMapper } from "../../domain/value-objects/CreationsConfig";
10
+ import type { Creation, CreationDocument } from "../../domain/entities/Creation";
11
+ import { CREATION_FIELDS } from "../../domain/constants";
12
+
13
+ declare const __DEV__: boolean;
14
+
15
+ /**
16
+ * Handles query operations for creations
17
+ */
18
+ export class CreationsQuery {
19
+ constructor(
20
+ private readonly pathResolver: FirestorePathResolver,
21
+ private readonly documentMapper: DocumentMapper,
22
+ ) { }
23
+
24
+ /**
25
+ * Get all creations for a user
26
+ * Optimized query: Server-side filtering for non-deleted items
27
+ */
28
+ async getAll(userId: string): Promise<Creation[]> {
29
+ const userCollection = this.pathResolver.getUserCollection(userId);
30
+ if (!userCollection) return [];
31
+
32
+ try {
33
+ const q = query(
34
+ userCollection,
35
+ where(CREATION_FIELDS.DELETED_AT, "==", null),
36
+ orderBy(CREATION_FIELDS.CREATED_AT, "desc")
37
+ );
38
+ const snapshot = await getDocs(q);
39
+
40
+ const creations = snapshot.docs.map((docSnap) => {
41
+ const data = docSnap.data() as CreationDocument;
42
+ return this.documentMapper(docSnap.id, data);
43
+ });
44
+
45
+ if (__DEV__) {
46
+ console.log("[CreationsQuery] Fetched creations:", {
47
+ count: creations.length,
48
+ hasDeletedFilter: true,
49
+ });
50
+ }
51
+
52
+ return creations;
53
+ } catch (error) {
54
+ if (__DEV__) {
55
+ console.error("[CreationsQuery] getAll() error:", error);
56
+ }
57
+ return [];
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Get a single creation by ID
63
+ */
64
+ async getById(userId: string, id: string): Promise<Creation | null> {
65
+ const docRef = this.pathResolver.getDocRef(userId, id);
66
+ if (!docRef) return null;
67
+
68
+ try {
69
+ const docSnap = await getDoc(docRef);
70
+
71
+ if (!docSnap.exists()) {
72
+ return null;
73
+ }
74
+
75
+ const data = docSnap.data() as CreationDocument;
76
+ return this.documentMapper(docSnap.id, data);
77
+ } catch (error) {
78
+ if (__DEV__) {
79
+ console.error("[CreationsQuery] getById() error:", error);
80
+ }
81
+ return null;
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * CreationsSubscription
3
+ * Handles realtime subscription operations for creations
4
+ * Single Responsibility: Firestore realtime listeners
5
+ */
6
+
7
+ import { query, orderBy, onSnapshot, where } from "firebase/firestore";
8
+ import { type FirestorePathResolver } from "@umituz/react-native-firebase";
9
+ import type { DocumentMapper } from "../../domain/value-objects/CreationsConfig";
10
+ import type { Creation, CreationDocument } from "../../domain/entities/Creation";
11
+ import type { CreationsSubscriptionCallback, UnsubscribeFunction } from "../../domain/repositories/ICreationsRepository";
12
+ import { CREATION_FIELDS } from "../../domain/constants";
13
+
14
+ declare const __DEV__: boolean;
15
+
16
+ /**
17
+ * Handles realtime subscriptions for creations
18
+ */
19
+ export class CreationsSubscription {
20
+ constructor(
21
+ private readonly pathResolver: FirestorePathResolver,
22
+ private readonly documentMapper: DocumentMapper,
23
+ ) { }
24
+
25
+ /**
26
+ * Subscribes to realtime updates for user's creations
27
+ *
28
+ * PERFORMANCE OPTIMIZATION:
29
+ * - Server-side filtering with where clause (80% data reduction)
30
+ * - No client-side filtering needed
31
+ * - Requires Firestore composite index: (deletedAt ASC, createdAt DESC)
32
+ *
33
+ * @param userId - User ID to query
34
+ * @param onData - Callback for data updates
35
+ * @param onError - Optional error callback
36
+ * @returns Unsubscribe function
37
+ */
38
+ subscribeToAll(
39
+ userId: string,
40
+ onData: CreationsSubscriptionCallback,
41
+ onError?: (error: Error) => void,
42
+ ): UnsubscribeFunction {
43
+ const userCollection = this.pathResolver.getUserCollection(userId);
44
+
45
+ if (!userCollection) {
46
+ return this.handleInvalidCollection(userId, onData, onError);
47
+ }
48
+
49
+ return this.createRealtimeListener(userCollection, onData, onError);
50
+ }
51
+
52
+ /**
53
+ * Handles case when user collection is invalid
54
+ */
55
+ private handleInvalidCollection(
56
+ userId: string,
57
+ onData: CreationsSubscriptionCallback,
58
+ onError?: (error: Error) => void,
59
+ ): UnsubscribeFunction {
60
+ const error = new Error(`[CreationsSubscription] Cannot subscribe: Invalid user collection for userId: ${userId}`);
61
+
62
+ if (__DEV__) {
63
+ console.error(error.message);
64
+ }
65
+
66
+ // Return empty array immediately
67
+ onData([]);
68
+
69
+ // Report error to callback
70
+ onError?.(error);
71
+
72
+ // Return no-op unsubscribe function
73
+ return () => {
74
+ if (__DEV__) {
75
+ console.log("[CreationsSubscription] No-op unsubscribe called (no listener was created)");
76
+ }
77
+ };
78
+ }
79
+
80
+ /**
81
+ * Creates the realtime listener with optimized query
82
+ */
83
+ private createRealtimeListener(
84
+ userCollection: any,
85
+ onData: CreationsSubscriptionCallback,
86
+ onError?: (error: Error) => void,
87
+ ): UnsubscribeFunction {
88
+ const q = query(
89
+ userCollection,
90
+ where(CREATION_FIELDS.DELETED_AT, "==", null),
91
+ orderBy(CREATION_FIELDS.CREATED_AT, "desc")
92
+ );
93
+
94
+ return onSnapshot(
95
+ q,
96
+ { includeMetadataChanges: false },
97
+ (snapshot) => {
98
+ const creations = snapshot.docs.map((docSnap) => {
99
+ const data = docSnap.data() as CreationDocument;
100
+ return this.documentMapper(docSnap.id, data);
101
+ });
102
+
103
+ if (__DEV__) {
104
+ console.log("[CreationsSubscription] Realtime sync:", {
105
+ count: creations.length,
106
+ serverFiltered: true,
107
+ hasChanges: snapshot.docChanges().length,
108
+ });
109
+ }
110
+
111
+ onData(creations);
112
+ },
113
+ (error: Error) => {
114
+ if (__DEV__) {
115
+ console.error("[CreationsSubscription] Realtime subscription error:", {
116
+ error: error.message,
117
+ code: (error as { code?: string }).code,
118
+ userId,
119
+ });
120
+ }
121
+ onError?.(error);
122
+ },
123
+ );
124
+ }
125
+ }