@umituz/react-native-firebase 2.6.3 → 2.6.5

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.
Files changed (108) hide show
  1. package/package.json +1 -1
  2. package/src/application/auth/index.ts +2 -34
  3. package/src/application/auth/use-cases/index.ts +1 -21
  4. package/src/domains/account-deletion/domain/index.ts +1 -8
  5. package/src/domains/account-deletion/index.ts +0 -42
  6. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor.ts +79 -0
  7. package/src/domains/account-deletion/infrastructure/services/AccountDeletionTypes.ts +0 -1
  8. package/src/domains/account-deletion/infrastructure/services/account-deletion.service.ts +2 -14
  9. package/src/domains/auth/index.ts +3 -12
  10. package/src/domains/auth/infrastructure/config/FirebaseAuthClient.ts +48 -60
  11. package/src/domains/auth/infrastructure/config/index.ts +2 -0
  12. package/src/domains/auth/infrastructure/config/initializers/index.ts +1 -0
  13. package/src/domains/auth/infrastructure/services/index.ts +16 -0
  14. package/src/domains/auth/infrastructure/services/utils/index.ts +1 -0
  15. package/src/domains/auth/infrastructure/stores/index.ts +1 -0
  16. package/src/domains/auth/infrastructure/utils/index.ts +1 -0
  17. package/src/domains/auth/infrastructure.ts +11 -0
  18. package/src/domains/auth/presentation/hooks/useGoogleOAuth.ts +18 -59
  19. package/src/domains/firestore/domain/entities/Collection.ts +0 -2
  20. package/src/domains/firestore/domain/index.ts +6 -2
  21. package/src/domains/firestore/domain/value-objects/WhereClause.ts +0 -14
  22. package/src/domains/firestore/index.ts +0 -1
  23. package/src/domains/firestore/infrastructure/config/FirestoreClient.ts +42 -60
  24. package/src/domains/firestore/presentation/hooks/useFirestoreMutation.ts +1 -1
  25. package/src/domains/firestore/presentation/hooks/useFirestoreQuery.ts +1 -1
  26. package/src/shared/infrastructure/base/ErrorHandler.ts +81 -0
  27. package/src/shared/infrastructure/base/ServiceBase.ts +62 -0
  28. package/src/shared/infrastructure/config/base/ServiceClientSingleton.ts +39 -0
  29. package/src/application/auth/ports/AuthPort.ts.bak +0 -164
  30. package/src/application/auth/ports/AuthPort_part_aa +0 -150
  31. package/src/application/auth/ports/AuthPort_part_ab +0 -14
  32. package/src/application/auth/use-cases/SignInUseCase.ts.bak +0 -253
  33. package/src/application/auth/use-cases/SignInUseCaseHelpers.ts +0 -0
  34. package/src/application/auth/use-cases/SignInUseCaseMain.ts +0 -0
  35. package/src/application/auth/use-cases/SignInUseCase_part_aa +0 -150
  36. package/src/application/auth/use-cases/SignInUseCase_part_ab +0 -103
  37. package/src/application/auth/use-cases/SignOutUseCase.ts.bak +0 -288
  38. package/src/application/auth/use-cases/SignOutUseCaseCleanup.ts +0 -0
  39. package/src/application/auth/use-cases/SignOutUseCaseMain.ts +0 -0
  40. package/src/application/auth/use-cases/SignOutUseCase_part_aa +0 -150
  41. package/src/application/auth/use-cases/SignOutUseCase_part_ab +0 -138
  42. package/src/domains/account-deletion/domain/services/UserValidationHelpers.ts.bak +0 -181
  43. package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_aa +0 -150
  44. package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_ab +0 -31
  45. package/src/domains/account-deletion/domain/services/UserValidationService.ts.bak +0 -286
  46. package/src/domains/account-deletion/domain/services/UserValidationService_part_aa +0 -150
  47. package/src/domains/account-deletion/domain/services/UserValidationService_part_ab +0 -136
  48. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor.ts.bak +0 -230
  49. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_aa +0 -150
  50. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_ab +0 -80
  51. package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler.ts.bak +0 -174
  52. package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_aa +0 -150
  53. package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_ab +0 -24
  54. package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository.ts.bak +0 -266
  55. package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_aa +0 -150
  56. package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_ab +0 -116
  57. package/src/domains/account-deletion/infrastructure/services/reauthentication.service.ts.bak +0 -160
  58. package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_aa +0 -150
  59. package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_ab +0 -10
  60. package/src/domains/auth/infrastructure.ts.bak +0 -156
  61. package/src/domains/auth/infrastructure_part_aa +0 -150
  62. package/src/domains/auth/infrastructure_part_ab +0 -6
  63. package/src/domains/auth/presentation/hooks/GoogleOAuthHelpers.ts +0 -0
  64. package/src/domains/auth/presentation/hooks/GoogleOAuthHookService.ts.bak +0 -247
  65. package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_aa +0 -150
  66. package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_ab +0 -97
  67. package/src/domains/auth/presentation/hooks/GoogleOAuthService.ts +0 -0
  68. package/src/domains/firestore/domain/entities/Collection.ts.bak +0 -288
  69. package/src/domains/firestore/domain/entities/Collection_part_aa +0 -150
  70. package/src/domains/firestore/domain/entities/Collection_part_ab +0 -138
  71. package/src/domains/firestore/domain/entities/Document.ts.bak +0 -233
  72. package/src/domains/firestore/domain/entities/DocumentHelpers.ts +0 -0
  73. package/src/domains/firestore/domain/entities/DocumentMain.ts +0 -0
  74. package/src/domains/firestore/domain/entities/Document_part_aa +0 -150
  75. package/src/domains/firestore/domain/entities/Document_part_ab +0 -83
  76. package/src/domains/firestore/domain/services/QueryService.ts.bak +0 -182
  77. package/src/domains/firestore/domain/services/QueryServiceAnalysis.ts.bak +0 -169
  78. package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_aa +0 -150
  79. package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_ab +0 -19
  80. package/src/domains/firestore/domain/services/QueryServiceHelpers.ts.bak +0 -151
  81. package/src/domains/firestore/domain/services/QueryServiceHelpers_part_aa +0 -150
  82. package/src/domains/firestore/domain/services/QueryServiceHelpers_part_ab +0 -1
  83. package/src/domains/firestore/domain/services/QueryService_part_aa +0 -150
  84. package/src/domains/firestore/domain/services/QueryService_part_ab +0 -32
  85. package/src/domains/firestore/domain/value-objects/QueryOptions.ts.bak +0 -191
  86. package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization.ts.bak +0 -207
  87. package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_aa +0 -150
  88. package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_ab +0 -57
  89. package/src/domains/firestore/domain/value-objects/QueryOptionsValidation.ts.bak +0 -182
  90. package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_aa +0 -150
  91. package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_ab +0 -32
  92. package/src/domains/firestore/domain/value-objects/QueryOptions_part_aa +0 -150
  93. package/src/domains/firestore/domain/value-objects/QueryOptions_part_ab +0 -41
  94. package/src/domains/firestore/domain/value-objects/WhereClause.ts.bak +0 -299
  95. package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts.bak +0 -207
  96. package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_aa +0 -150
  97. package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_ab +0 -57
  98. package/src/domains/firestore/domain/value-objects/WhereClause_part_aa +0 -150
  99. package/src/domains/firestore/domain/value-objects/WhereClause_part_ab +0 -149
  100. package/src/shared/infrastructure/base/ErrorHandler.ts.bak +0 -189
  101. package/src/shared/infrastructure/base/ErrorHandler_part_aa +0 -150
  102. package/src/shared/infrastructure/base/ErrorHandler_part_ab +0 -39
  103. package/src/shared/infrastructure/base/ServiceBase.ts.bak +0 -220
  104. package/src/shared/infrastructure/base/ServiceBase_part_aa +0 -150
  105. package/src/shared/infrastructure/base/ServiceBase_part_ab +0 -70
  106. package/src/shared/infrastructure/config/base/ServiceClientSingleton.ts.bak +0 -155
  107. package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_aa +0 -150
  108. package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_ab +0 -5
@@ -1,233 +0,0 @@
1
- /**
2
- * Document Entity
3
- * Single Responsibility: Represent a Firestore document with metadata
4
- *
5
- * Domain entity that encapsulates document data and metadata.
6
- * Provides business logic for document operations.
7
- *
8
- * Max lines: 150 (enforced for maintainability)
9
- */
10
-
11
- import type { DocumentData, DocumentSnapshot, Timestamp } from 'firebase/firestore';
12
-
13
- /**
14
- * Document metadata
15
- */
16
- export interface DocumentMetadata {
17
- readonly id: string;
18
- readonly createdAt: Timestamp | null;
19
- readonly updatedAt: Timestamp | null;
20
- readonly exists: boolean;
21
- }
22
-
23
- /**
24
- * Document entity
25
- * Represents a Firestore document with data and metadata
26
- */
27
- export class Document<T extends DocumentData = DocumentData> {
28
- readonly id: string;
29
- readonly data: T | null;
30
- readonly metadata: DocumentMetadata;
31
- readonly path: string;
32
-
33
- constructor(snapshot: DocumentSnapshot<T>, path: string) {
34
- this.id = snapshot.id;
35
- this.data = snapshot.data() || null;
36
- this.path = path;
37
- this.metadata = {
38
- id: snapshot.id,
39
- createdAt: this.extractTimestamp(snapshot, 'createdAt'),
40
- updatedAt: this.extractTimestamp(snapshot, 'updatedAt'),
41
- exists: snapshot.exists(),
42
- };
43
- }
44
-
45
- /**
46
- * Create document from snapshot with custom path
47
- */
48
- static fromSnapshot<T extends DocumentData>(
49
- snapshot: DocumentSnapshot<T>,
50
- path: string
51
- ): Document<T> {
52
- return new Document(snapshot, path);
53
- }
54
-
55
- /**
56
- * Check if document exists
57
- */
58
- exists(): boolean {
59
- return this.metadata.exists && this.data !== null;
60
- }
61
-
62
- /**
63
- * Get field value from document data
64
- */
65
- getField<K extends keyof T>(field: K): T[K] | null {
66
- return this.data?.[field] ?? null;
67
- }
68
-
69
- /**
70
- * Check if document has a specific field
71
- */
72
- hasField<K extends keyof T>(field: K): boolean {
73
- return this.data !== null && field in this.data;
74
- }
75
-
76
- /**
77
- * Get document age in milliseconds
78
- * Returns null if createdAt is not set
79
- */
80
- getAge(): number | null {
81
- if (!this.metadata.createdAt) return null;
82
- return Date.now() - this.metadata.createdAt.toMillis();
83
- }
84
-
85
- /**
86
- * Check if document is recent (created within specified milliseconds)
87
- */
88
- isRecent(maxAgeMs: number): boolean {
89
- const age = this.getAge();
90
- return age !== null && age <= maxAgeMs;
91
- }
92
-
93
- /**
94
- * Check if document was updated after creation (modified)
95
- */
96
- isModified(): boolean {
97
- if (!this.metadata.createdAt || !this.metadata.updatedAt) return false;
98
- return this.metadata.updatedAt.toMillis() > this.metadata.createdAt.toMillis();
99
- }
100
-
101
- /**
102
- * Get time since last update in milliseconds
103
- * Returns null if updatedAt is not set
104
- */
105
- getTimeSinceUpdate(): number | null {
106
- if (!this.metadata.updatedAt) return null;
107
- return Date.now() - this.metadata.updatedAt.toMillis();
108
- }
109
-
110
- /**
111
- * Check if document is stale (not updated within specified milliseconds)
112
- */
113
- isStale(maxStaleMs: number): boolean {
114
- const timeSinceUpdate = this.getTimeSinceUpdate();
115
- return timeSinceUpdate !== null && timeSinceUpdate > maxStaleMs;
116
- }
117
-
118
- /**
119
- * Convert to plain object (for serialization)
120
- */
121
- toObject(): { id: string; data: T | null; metadata: DocumentMetadata } {
122
- return {
123
- id: this.id,
124
- data: this.data,
125
- metadata: this.metadata,
126
- };
127
- }
128
-
129
- /**
130
- * Extract timestamp field from document snapshot
131
- */
132
- private extractTimestamp(
133
- snapshot: DocumentSnapshot<T>,
134
- field: string
135
- ): Timestamp | null {
136
- const data = snapshot.data();
137
- if (!data) return null;
138
-
139
- const value = data[field as keyof T];
140
- if (value && typeof value === 'object' && 'toDate' in value && typeof value.toDate === 'function') {
141
- return value as unknown as Timestamp;
142
- }
143
-
144
- return null;
145
- }
146
-
147
- /**
148
- * Create a new document with updated data
149
- * Useful for immutability patterns
150
- */
151
- withData<K extends keyof T>(field: K, value: T[K]): Document<T> {
152
- if (!this.data) {
153
- throw new Error('Cannot update null document data');
154
- }
155
-
156
- const newData = { ...this.data, [field]: value };
157
- return new Document(
158
- {
159
- id: this.id,
160
- exists: true,
161
- data() {
162
- return newData;
163
- },
164
- } as unknown as DocumentSnapshot<T>,
165
- this.path
166
- );
167
- }
168
-
169
- /**
170
- * Validate document data against required fields
171
- */
172
- validateRequiredFields(requiredFields: (keyof T)[]): { valid: boolean; missing: (keyof T)[] } {
173
- if (!this.data) {
174
- return { valid: false, missing: requiredFields };
175
- }
176
-
177
- const missing = requiredFields.filter(field => !this.hasField(field));
178
- return {
179
- valid: missing.length === 0,
180
- missing,
181
- };
182
- }
183
-
184
- /**
185
- * Check if document matches filter criteria
186
- */
187
- matches(filter: Partial<T>): boolean {
188
- if (!this.data) return false;
189
-
190
- return Object.entries(filter).every(([key, value]) => {
191
- const fieldValue = this.data![key as keyof T];
192
- return fieldValue === value;
193
- });
194
- }
195
-
196
- /**
197
- * Check if document matches filter criteria
198
- */
199
- matches(filter: Partial<T>): boolean {
200
- if (!this.data) return false;
201
-
202
- return Object.entries(filter).every(([key, value]) => {
203
- return this.data[key as keyof T] === value;
204
- });
205
- }
206
-
207
- /**
208
- * Get document size in bytes (approximate)
209
- * Useful for quota management
210
- */
211
- getSize(): number {
212
- if (!this.data) return 0;
213
-
214
- return JSON.stringify(this.data).length;
215
- }
216
-
217
- /**
218
- * Check if document size exceeds limit
219
- */
220
- exceedsSizeLimit(maxBytes: number): boolean {
221
- return this.getSize() > maxBytes;
222
- }
223
- }
224
-
225
- /**
226
- * Factory function to create document entity
227
- */
228
- export function createDocument<T extends DocumentData>(
229
- snapshot: DocumentSnapshot<T>,
230
- path: string
231
- ): Document<T> {
232
- return Document.fromSnapshot(snapshot, path);
233
- }
@@ -1,150 +0,0 @@
1
- /**
2
- * Document Entity
3
- * Single Responsibility: Represent a Firestore document with metadata
4
- *
5
- * Domain entity that encapsulates document data and metadata.
6
- * Provides business logic for document operations.
7
- *
8
- * Max lines: 150 (enforced for maintainability)
9
- */
10
-
11
- import type { DocumentData, DocumentSnapshot, Timestamp } from 'firebase/firestore';
12
-
13
- /**
14
- * Document metadata
15
- */
16
- export interface DocumentMetadata {
17
- readonly id: string;
18
- readonly createdAt: Timestamp | null;
19
- readonly updatedAt: Timestamp | null;
20
- readonly exists: boolean;
21
- }
22
-
23
- /**
24
- * Document entity
25
- * Represents a Firestore document with data and metadata
26
- */
27
- export class Document<T extends DocumentData = DocumentData> {
28
- readonly id: string;
29
- readonly data: T | null;
30
- readonly metadata: DocumentMetadata;
31
- readonly path: string;
32
-
33
- constructor(snapshot: DocumentSnapshot<T>, path: string) {
34
- this.id = snapshot.id;
35
- this.data = snapshot.data() || null;
36
- this.path = path;
37
- this.metadata = {
38
- id: snapshot.id,
39
- createdAt: this.extractTimestamp(snapshot, 'createdAt'),
40
- updatedAt: this.extractTimestamp(snapshot, 'updatedAt'),
41
- exists: snapshot.exists(),
42
- };
43
- }
44
-
45
- /**
46
- * Create document from snapshot with custom path
47
- */
48
- static fromSnapshot<T extends DocumentData>(
49
- snapshot: DocumentSnapshot<T>,
50
- path: string
51
- ): Document<T> {
52
- return new Document(snapshot, path);
53
- }
54
-
55
- /**
56
- * Check if document exists
57
- */
58
- exists(): boolean {
59
- return this.metadata.exists && this.data !== null;
60
- }
61
-
62
- /**
63
- * Get field value from document data
64
- */
65
- getField<K extends keyof T>(field: K): T[K] | null {
66
- return this.data?.[field] ?? null;
67
- }
68
-
69
- /**
70
- * Check if document has a specific field
71
- */
72
- hasField<K extends keyof T>(field: K): boolean {
73
- return this.data !== null && field in this.data;
74
- }
75
-
76
- /**
77
- * Get document age in milliseconds
78
- * Returns null if createdAt is not set
79
- */
80
- getAge(): number | null {
81
- if (!this.metadata.createdAt) return null;
82
- return Date.now() - this.metadata.createdAt.toMillis();
83
- }
84
-
85
- /**
86
- * Check if document is recent (created within specified milliseconds)
87
- */
88
- isRecent(maxAgeMs: number): boolean {
89
- const age = this.getAge();
90
- return age !== null && age <= maxAgeMs;
91
- }
92
-
93
- /**
94
- * Check if document was updated after creation (modified)
95
- */
96
- isModified(): boolean {
97
- if (!this.metadata.createdAt || !this.metadata.updatedAt) return false;
98
- return this.metadata.updatedAt.toMillis() > this.metadata.createdAt.toMillis();
99
- }
100
-
101
- /**
102
- * Get time since last update in milliseconds
103
- * Returns null if updatedAt is not set
104
- */
105
- getTimeSinceUpdate(): number | null {
106
- if (!this.metadata.updatedAt) return null;
107
- return Date.now() - this.metadata.updatedAt.toMillis();
108
- }
109
-
110
- /**
111
- * Check if document is stale (not updated within specified milliseconds)
112
- */
113
- isStale(maxStaleMs: number): boolean {
114
- const timeSinceUpdate = this.getTimeSinceUpdate();
115
- return timeSinceUpdate !== null && timeSinceUpdate > maxStaleMs;
116
- }
117
-
118
- /**
119
- * Convert to plain object (for serialization)
120
- */
121
- toObject(): { id: string; data: T | null; metadata: DocumentMetadata } {
122
- return {
123
- id: this.id,
124
- data: this.data,
125
- metadata: this.metadata,
126
- };
127
- }
128
-
129
- /**
130
- * Extract timestamp field from document snapshot
131
- */
132
- private extractTimestamp(
133
- snapshot: DocumentSnapshot<T>,
134
- field: string
135
- ): Timestamp | null {
136
- const data = snapshot.data();
137
- if (!data) return null;
138
-
139
- const value = data[field as keyof T];
140
- if (value && typeof value === 'object' && 'toDate' in value && typeof value.toDate === 'function') {
141
- return value as unknown as Timestamp;
142
- }
143
-
144
- return null;
145
- }
146
-
147
- /**
148
- * Create a new document with updated data
149
- * Useful for immutability patterns
150
- */
@@ -1,83 +0,0 @@
1
- withData<K extends keyof T>(field: K, value: T[K]): Document<T> {
2
- if (!this.data) {
3
- throw new Error('Cannot update null document data');
4
- }
5
-
6
- const newData = { ...this.data, [field]: value };
7
- return new Document(
8
- {
9
- id: this.id,
10
- exists: true,
11
- data() {
12
- return newData;
13
- },
14
- } as unknown as DocumentSnapshot<T>,
15
- this.path
16
- );
17
- }
18
-
19
- /**
20
- * Validate document data against required fields
21
- */
22
- validateRequiredFields(requiredFields: (keyof T)[]): { valid: boolean; missing: (keyof T)[] } {
23
- if (!this.data) {
24
- return { valid: false, missing: requiredFields };
25
- }
26
-
27
- const missing = requiredFields.filter(field => !this.hasField(field));
28
- return {
29
- valid: missing.length === 0,
30
- missing,
31
- };
32
- }
33
-
34
- /**
35
- * Check if document matches filter criteria
36
- */
37
- matches(filter: Partial<T>): boolean {
38
- if (!this.data) return false;
39
-
40
- return Object.entries(filter).every(([key, value]) => {
41
- const fieldValue = this.data![key as keyof T];
42
- return fieldValue === value;
43
- });
44
- }
45
-
46
- /**
47
- * Check if document matches filter criteria
48
- */
49
- matches(filter: Partial<T>): boolean {
50
- if (!this.data) return false;
51
-
52
- return Object.entries(filter).every(([key, value]) => {
53
- return this.data[key as keyof T] === value;
54
- });
55
- }
56
-
57
- /**
58
- * Get document size in bytes (approximate)
59
- * Useful for quota management
60
- */
61
- getSize(): number {
62
- if (!this.data) return 0;
63
-
64
- return JSON.stringify(this.data).length;
65
- }
66
-
67
- /**
68
- * Check if document size exceeds limit
69
- */
70
- exceedsSizeLimit(maxBytes: number): boolean {
71
- return this.getSize() > maxBytes;
72
- }
73
- }
74
-
75
- /**
76
- * Factory function to create document entity
77
- */
78
- export function createDocument<T extends DocumentData>(
79
- snapshot: DocumentSnapshot<T>,
80
- path: string
81
- ): Document<T> {
82
- return Document.fromSnapshot(snapshot, path);
83
- }
@@ -1,182 +0,0 @@
1
- /**
2
- * Query Service (Main Builder)
3
- * Single Responsibility: Build and validate Firestore queries
4
- *
5
- * Domain service that encapsulates query building logic.
6
- * Moves business logic from infrastructure to domain layer.
7
- *
8
- * Max lines: 150 (enforced for maintainability)
9
- */
10
-
11
- import type { Query, Firestore, WhereFilterOp } from 'firebase/firestore';
12
- import { collection, query, where, orderBy, limit, startAfter, startAt } from 'firebase/firestore';
13
- import { QueryOptions, createQueryOptions } from '../value-objects/QueryOptions';
14
- import { WhereClause } from '../value-objects/WhereClause';
15
-
16
- /**
17
- * Query builder result
18
- */
19
- export interface QueryBuilderResult {
20
- readonly query: Query;
21
- readonly options: QueryOptions;
22
- }
23
-
24
- /**
25
- * Query service
26
- * Provides query building and validation functionality
27
- */
28
- export class QueryService {
29
- private readonly db: Firestore;
30
-
31
- constructor(db: Firestore) {
32
- this.db = db;
33
- }
34
-
35
- /**
36
- * Build a query from options
37
- * Main method for query construction
38
- */
39
- buildQuery(collectionPath: string, options: QueryOptions): QueryBuilderResult {
40
- const validation = options.validate();
41
- if (!validation.valid) {
42
- throw new Error(`Invalid query options: ${validation.errors.join(', ')}`);
43
- }
44
-
45
- const collectionRef = collection(this.db, collectionPath);
46
- let q: Query = collectionRef;
47
-
48
- // Apply where clauses
49
- for (const clause of options.whereClauses) {
50
- q = query(q, where(clause.field, clause.operator, clause.value));
51
- }
52
-
53
- // Apply sort options
54
- for (const sort of options.sortOptions) {
55
- q = query(q, orderBy(sort.field, sort.direction));
56
- }
57
-
58
- // Apply date range
59
- if (options.dateRange) {
60
- const { field, startDate, endDate } = options.dateRange;
61
- if (startDate) {
62
- q = query(q, where(field, '>=', startDate));
63
- }
64
- if (endDate) {
65
- q = query(q, where(field, '<=', endDate));
66
- }
67
- }
68
-
69
- // Apply pagination
70
- if (options.pagination) {
71
- const { cursor, limit: limitValue, startAfter: startAfterValue, startAt: startAtValue } = options.pagination;
72
-
73
- if (startAfterValue !== undefined) {
74
- q = query(q, startAfter(startAfterValue));
75
- }
76
-
77
- if (startAtValue !== undefined) {
78
- q = query(q, startAt(startAtValue));
79
- }
80
-
81
- if (limitValue !== undefined) {
82
- q = query(q, limit(limitValue));
83
- }
84
- }
85
-
86
- return { query: q, options };
87
- }
88
-
89
- /**
90
- * Build simple query with single where clause
91
- */
92
- buildSimpleQuery(
93
- collectionPath: string,
94
- field: string,
95
- operator: WhereFilterOp,
96
- value: unknown
97
- ): Query {
98
- const options = createQueryOptions({
99
- where: [WhereClause.create(field, operator, value)],
100
- });
101
-
102
- return this.buildQuery(collectionPath, options).query;
103
- }
104
-
105
- /**
106
- * Build query with equality filter
107
- */
108
- buildEqualsQuery(collectionPath: string, field: string, value: unknown): Query {
109
- return this.buildSimpleQuery(collectionPath, field, '==', value);
110
- }
111
-
112
- /**
113
- * Build query with multiple equality filters
114
- */
115
- buildMultiEqualsQuery(
116
- collectionPath: string,
117
- filters: Record<string, unknown>
118
- ): Query {
119
- const whereClauses = Object.entries(filters).map(([field, value]) =>
120
- WhereClause.equals(field, value)
121
- );
122
-
123
- const options = createQueryOptions({ where: whereClauses });
124
- return this.buildQuery(collectionPath, options).query;
125
- }
126
-
127
- /**
128
- * Build query with sorting
129
- */
130
- buildSortedQuery(
131
- collectionPath: string,
132
- sortField: string,
133
- direction: 'asc' | 'desc' = 'asc'
134
- ): Query {
135
- const options = createQueryOptions({
136
- sort: [{ field: sortField, direction }],
137
- });
138
-
139
- return this.buildQuery(collectionPath, options).query;
140
- }
141
-
142
- /**
143
- * Build query with limit
144
- */
145
- buildLimitedQuery(collectionPath: string, limitValue: number): Query {
146
- const options = createQueryOptions({
147
- pagination: { limit: limitValue },
148
- });
149
-
150
- return this.buildQuery(collectionPath, options).query;
151
- }
152
-
153
- /**
154
- * Build query with date range
155
- */
156
- buildDateRangeQuery(
157
- collectionPath: string,
158
- field: string,
159
- startDate?: Date,
160
- endDate?: Date
161
- ): Query {
162
- const options = createQueryOptions({
163
- dateRange: { field, startDate, endDate },
164
- });
165
-
166
- return this.buildQuery(collectionPath, options).query;
167
- }
168
-
169
- /**
170
- * Validate query options
171
- */
172
- validateOptions(options: QueryOptions): { valid: boolean; errors: string[] } {
173
- return options.validate();
174
- }
175
-
176
- /**
177
- * Check if query is empty (no filters)
178
- */
179
- isEmptyQuery(options: QueryOptions): boolean {
180
- return options.isEmpty();
181
- }
182
- }