@umituz/react-native-firebase 2.6.1 → 2.6.3

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 (94) hide show
  1. package/package.json +1 -1
  2. package/src/application/auth/ports/AuthPort_part_aa +150 -0
  3. package/src/application/auth/ports/AuthPort_part_ab +14 -0
  4. package/src/application/auth/use-cases/SignInUseCaseHelpers.ts +0 -0
  5. package/src/application/auth/use-cases/SignInUseCaseMain.ts +0 -0
  6. package/src/application/auth/use-cases/SignInUseCase_part_aa +150 -0
  7. package/src/application/auth/use-cases/SignInUseCase_part_ab +103 -0
  8. package/src/application/auth/use-cases/SignOutUseCaseCleanup.ts +0 -0
  9. package/src/application/auth/use-cases/SignOutUseCaseMain.ts +0 -0
  10. package/src/application/auth/use-cases/SignOutUseCase_part_aa +150 -0
  11. package/src/application/auth/use-cases/SignOutUseCase_part_ab +138 -0
  12. package/src/domains/account-deletion/domain/services/UserValidationHelpers.ts.bak +181 -0
  13. package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_aa +150 -0
  14. package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_ab +31 -0
  15. package/src/domains/account-deletion/domain/services/{UserValidationService.ts → UserValidationService.ts.bak} +1 -10
  16. package/src/domains/account-deletion/domain/services/UserValidationService_part_aa +150 -0
  17. package/src/domains/account-deletion/domain/services/UserValidationService_part_ab +136 -0
  18. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_aa +150 -0
  19. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_ab +80 -0
  20. package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_aa +150 -0
  21. package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_ab +24 -0
  22. package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_aa +150 -0
  23. package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_ab +116 -0
  24. package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_aa +150 -0
  25. package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_ab +10 -0
  26. package/src/domains/auth/infrastructure_part_aa +150 -0
  27. package/src/domains/auth/infrastructure_part_ab +6 -0
  28. package/src/domains/auth/presentation/hooks/GoogleOAuthHelpers.ts +0 -0
  29. package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_aa +150 -0
  30. package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_ab +97 -0
  31. package/src/domains/auth/presentation/hooks/GoogleOAuthService.ts +0 -0
  32. package/src/domains/firestore/domain/entities/Collection.ts +31 -191
  33. package/src/domains/firestore/domain/entities/Collection.ts.bak +288 -0
  34. package/src/domains/firestore/domain/entities/CollectionFactory.ts +55 -0
  35. package/src/domains/firestore/domain/entities/CollectionHelpers.ts +143 -0
  36. package/src/domains/firestore/domain/entities/CollectionUtils.ts +72 -0
  37. package/src/domains/firestore/domain/entities/CollectionValidation.ts +138 -0
  38. package/src/domains/firestore/domain/entities/Collection_part_aa +150 -0
  39. package/src/domains/firestore/domain/entities/Collection_part_ab +138 -0
  40. package/src/domains/firestore/domain/entities/DocumentHelpers.ts +0 -0
  41. package/src/domains/firestore/domain/entities/DocumentMain.ts +0 -0
  42. package/src/domains/firestore/domain/entities/Document_part_aa +150 -0
  43. package/src/domains/firestore/domain/entities/Document_part_ab +83 -0
  44. package/src/domains/firestore/domain/index.ts +35 -8
  45. package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_aa +150 -0
  46. package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_ab +19 -0
  47. package/src/domains/firestore/domain/services/QueryServiceHelpers_part_aa +150 -0
  48. package/src/domains/firestore/domain/services/QueryServiceHelpers_part_ab +1 -0
  49. package/src/domains/firestore/domain/services/QueryService_part_aa +150 -0
  50. package/src/domains/firestore/domain/services/QueryService_part_ab +32 -0
  51. package/src/domains/firestore/domain/value-objects/QueryOptions.ts +20 -68
  52. package/src/domains/firestore/domain/value-objects/QueryOptions.ts.bak +6 -135
  53. package/src/domains/firestore/domain/value-objects/QueryOptionsFactory.ts +95 -0
  54. package/src/domains/firestore/domain/value-objects/QueryOptionsHelpers.ts +110 -0
  55. package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_aa +150 -0
  56. package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_ab +57 -0
  57. package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_aa +150 -0
  58. package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_ab +32 -0
  59. package/src/domains/firestore/domain/value-objects/QueryOptions_part_aa +150 -0
  60. package/src/domains/firestore/domain/value-objects/QueryOptions_part_ab +41 -0
  61. package/src/domains/firestore/domain/value-objects/WhereClause.ts +35 -205
  62. package/src/domains/firestore/domain/value-objects/WhereClause.ts.bak +299 -0
  63. package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts +44 -150
  64. package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts.bak +207 -0
  65. package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_aa +150 -0
  66. package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_ab +57 -0
  67. package/src/domains/firestore/domain/value-objects/WhereClauseHelpers.ts +123 -0
  68. package/src/domains/firestore/domain/value-objects/WhereClauseValidation.ts +83 -0
  69. package/src/domains/firestore/domain/value-objects/WhereClause_part_aa +150 -0
  70. package/src/domains/firestore/domain/value-objects/WhereClause_part_ab +149 -0
  71. package/src/shared/infrastructure/base/ErrorHandler_part_aa +150 -0
  72. package/src/shared/infrastructure/base/ErrorHandler_part_ab +39 -0
  73. package/src/shared/infrastructure/base/ServiceBase_part_aa +150 -0
  74. package/src/shared/infrastructure/base/ServiceBase_part_ab +70 -0
  75. package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_aa +150 -0
  76. package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_ab +5 -0
  77. /package/src/application/auth/ports/{AuthPort.ts → AuthPort.ts.bak} +0 -0
  78. /package/src/application/auth/use-cases/{SignInUseCase.ts → SignInUseCase.ts.bak} +0 -0
  79. /package/src/application/auth/use-cases/{SignOutUseCase.ts → SignOutUseCase.ts.bak} +0 -0
  80. /package/src/domains/account-deletion/infrastructure/services/{AccountDeletionExecutor.ts → AccountDeletionExecutor.ts.bak} +0 -0
  81. /package/src/domains/account-deletion/infrastructure/services/{AccountDeletionReauthHandler.ts → AccountDeletionReauthHandler.ts.bak} +0 -0
  82. /package/src/domains/account-deletion/infrastructure/services/{AccountDeletionRepository.ts → AccountDeletionRepository.ts.bak} +0 -0
  83. /package/src/domains/account-deletion/infrastructure/services/{reauthentication.service.ts → reauthentication.service.ts.bak} +0 -0
  84. /package/src/domains/auth/{infrastructure.ts → infrastructure.ts.bak} +0 -0
  85. /package/src/domains/auth/presentation/hooks/{GoogleOAuthHookService.ts → GoogleOAuthHookService.ts.bak} +0 -0
  86. /package/src/domains/firestore/domain/entities/{Document.ts → Document.ts.bak} +0 -0
  87. /package/src/domains/firestore/domain/services/{QueryService.ts → QueryService.ts.bak} +0 -0
  88. /package/src/domains/firestore/domain/services/{QueryServiceAnalysis.ts → QueryServiceAnalysis.ts.bak} +0 -0
  89. /package/src/domains/firestore/domain/services/{QueryServiceHelpers.ts → QueryServiceHelpers.ts.bak} +0 -0
  90. /package/src/domains/firestore/domain/value-objects/{QueryOptionsSerialization.ts → QueryOptionsSerialization.ts.bak} +0 -0
  91. /package/src/domains/firestore/domain/value-objects/{QueryOptionsValidation.ts → QueryOptionsValidation.ts.bak} +0 -0
  92. /package/src/shared/infrastructure/base/{ErrorHandler.ts → ErrorHandler.ts.bak} +0 -0
  93. /package/src/shared/infrastructure/base/{ServiceBase.ts → ServiceBase.ts.bak} +0 -0
  94. /package/src/shared/infrastructure/config/base/{ServiceClientSingleton.ts → ServiceClientSingleton.ts.bak} +0 -0
@@ -0,0 +1,150 @@
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;
@@ -0,0 +1,32 @@
1
+ }
2
+
3
+ /**
4
+ * Build query with date range
5
+ */
6
+ buildDateRangeQuery(
7
+ collectionPath: string,
8
+ field: string,
9
+ startDate?: Date,
10
+ endDate?: Date
11
+ ): Query {
12
+ const options = createQueryOptions({
13
+ dateRange: { field, startDate, endDate },
14
+ });
15
+
16
+ return this.buildQuery(collectionPath, options).query;
17
+ }
18
+
19
+ /**
20
+ * Validate query options
21
+ */
22
+ validateOptions(options: QueryOptions): { valid: boolean; errors: string[] } {
23
+ return options.validate();
24
+ }
25
+
26
+ /**
27
+ * Check if query is empty (no filters)
28
+ */
29
+ isEmptyQuery(options: QueryOptions): boolean {
30
+ return options.isEmpty();
31
+ }
32
+ }
@@ -2,35 +2,24 @@
2
2
  * Query Options Value Object (Main)
3
3
  * Single Responsibility: Encapsulate query configuration options
4
4
  *
5
- * Value object that represents query options in a type-safe way.
6
- * Provides validation and business logic for query configuration.
7
- *
8
5
  * Max lines: 150 (enforced for maintainability)
9
6
  */
10
7
 
11
8
  import type { WhereFilterOp, OrderByDirection } from 'firebase/firestore';
12
9
  import { WhereClause } from './WhereClause';
10
+ import * as Factory from './QueryOptionsFactory';
13
11
 
14
- /**
15
- * Sort options
16
- */
17
12
  export interface SortOptions {
18
13
  readonly field: string;
19
14
  readonly direction: OrderByDirection;
20
15
  }
21
16
 
22
- /**
23
- * Date range options
24
- */
25
17
  export interface DateRangeOptions {
26
18
  readonly field: string;
27
19
  readonly startDate?: Date;
28
20
  readonly endDate?: Date;
29
21
  }
30
22
 
31
- /**
32
- * Pagination options
33
- */
34
23
  export interface PaginationOptions {
35
24
  readonly cursor?: number;
36
25
  readonly limit?: number;
@@ -38,10 +27,6 @@ export interface PaginationOptions {
38
27
  readonly startAt?: number;
39
28
  }
40
29
 
41
- /**
42
- * Query options value object
43
- * Immutable configuration for Firestore queries
44
- */
45
30
  export class QueryOptions {
46
31
  readonly whereClauses: readonly WhereClause[];
47
32
  readonly sortOptions: readonly SortOptions[];
@@ -60,16 +45,10 @@ export class QueryOptions {
60
45
  this.pagination = pagination ? Object.freeze(pagination) : null;
61
46
  }
62
47
 
63
- /**
64
- * Create empty query options
65
- */
66
48
  static empty(): QueryOptions {
67
49
  return new QueryOptions([], [], null, null);
68
50
  }
69
51
 
70
- /**
71
- * Create query options from partial configuration
72
- */
73
52
  static create(options: {
74
53
  where?: WhereClause[];
75
54
  sort?: SortOptions[];
@@ -84,103 +63,76 @@ export class QueryOptions {
84
63
  );
85
64
  }
86
65
 
87
- /**
88
- * Add where clause
89
- */
90
66
  withWhere(clause: WhereClause): QueryOptions {
91
67
  return new QueryOptions(
92
68
  [...this.whereClauses, clause] as WhereClause[],
93
- this.sortOptions,
69
+ [...this.sortOptions] as SortOptions[],
94
70
  this.dateRange,
95
71
  this.pagination
96
72
  );
97
73
  }
98
74
 
99
- /**
100
- * Add sort option
101
- */
102
75
  withSort(sort: SortOptions): QueryOptions {
103
76
  return new QueryOptions(
104
- this.whereClauses,
77
+ [...this.whereClauses] as WhereClause[],
105
78
  [...this.sortOptions, sort] as SortOptions[],
106
79
  this.dateRange,
107
80
  this.pagination
108
81
  );
109
82
  }
110
83
 
111
- /**
112
- * Set date range
113
- */
114
84
  withDateRange(dateRange: DateRangeOptions): QueryOptions {
115
85
  return new QueryOptions(
116
- this.whereClauses,
117
- this.sortOptions,
86
+ [...this.whereClauses] as WhereClause[],
87
+ [...this.sortOptions] as SortOptions[],
118
88
  dateRange,
119
89
  this.pagination
120
90
  );
121
91
  }
122
92
 
123
- /**
124
- * Set pagination
125
- */
126
93
  withPagination(pagination: PaginationOptions): QueryOptions {
127
94
  return new QueryOptions(
128
- this.whereClauses,
129
- this.sortOptions,
95
+ [...this.whereClauses] as WhereClause[],
96
+ [...this.sortOptions] as SortOptions[],
130
97
  this.dateRange,
131
98
  pagination
132
99
  );
133
100
  }
134
101
 
135
- /**
136
- * Remove all where clauses
137
- */
102
+ withLimit(limit: number): QueryOptions {
103
+ return this.withPagination({
104
+ ...this.pagination,
105
+ limit,
106
+ } as PaginationOptions);
107
+ }
108
+
138
109
  clearWhere(): QueryOptions {
139
- return new QueryOptions([], this.sortOptions, this.dateRange, this.pagination);
110
+ return new QueryOptions([], [...this.sortOptions] as SortOptions[], this.dateRange, this.pagination);
140
111
  }
141
112
 
142
- /**
143
- * Remove all sort options
144
- */
145
113
  clearSort(): QueryOptions {
146
- return new QueryOptions(this.whereClauses, [], this.dateRange, this.pagination);
114
+ return new QueryOptions([...this.whereClauses] as WhereClause[], [], this.dateRange, this.pagination);
147
115
  }
148
116
 
149
- /**
150
- * Remove date range
151
- */
152
117
  clearDateRange(): QueryOptions {
153
- return new QueryOptions(this.whereClauses, this.sortOptions, null, this.pagination);
118
+ return new QueryOptions([...this.whereClauses] as WhereClause[], [...this.sortOptions] as SortOptions[], null, this.pagination);
154
119
  }
155
120
 
156
- /**
157
- * Remove pagination
158
- */
159
121
  clearPagination(): QueryOptions {
160
- return new QueryOptions(this.whereClauses, this.sortOptions, this.dateRange, null);
122
+ return new QueryOptions([...this.whereClauses] as WhereClause[], [...this.sortOptions] as SortOptions[], this.dateRange, null);
161
123
  }
162
124
 
163
- /**
164
- * Clone with modifications
165
- */
166
125
  clone(modifications: {
167
126
  where?: WhereClause[];
168
127
  sort?: SortOptions[];
169
128
  dateRange?: DateRangeOptions | null;
170
129
  pagination?: PaginationOptions | null;
171
130
  }): QueryOptions {
172
- return QueryOptions.create({
173
- where: modifications.where ?? [...this.whereClauses] as WhereClause[],
174
- sort: modifications.sort ?? [...this.sortOptions] as SortOptions[],
175
- dateRange: modifications.dateRange ?? this.dateRange ?? null,
176
- pagination: modifications.pagination ?? this.pagination ?? null,
177
- });
131
+ return Factory.clone(this, modifications);
178
132
  }
179
133
  }
180
134
 
181
- /**
182
- * Factory function to create query options
183
- */
135
+ // Factory function
184
136
  export function createQueryOptions(options?: {
185
137
  where?: WhereClause[];
186
138
  sort?: SortOptions[];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Query Options Value Object
2
+ * Query Options Value Object (Main)
3
3
  * Single Responsibility: Encapsulate query configuration options
4
4
  *
5
5
  * Value object that represents query options in a type-safe way.
@@ -89,7 +89,7 @@ export class QueryOptions {
89
89
  */
90
90
  withWhere(clause: WhereClause): QueryOptions {
91
91
  return new QueryOptions(
92
- [...this.whereClauses, clause] as WhereClause[] as WhereClause[],
92
+ [...this.whereClauses, clause] as WhereClause[],
93
93
  this.sortOptions,
94
94
  this.dateRange,
95
95
  this.pagination
@@ -160,135 +160,6 @@ export class QueryOptions {
160
160
  return new QueryOptions(this.whereClauses, this.sortOptions, this.dateRange, null);
161
161
  }
162
162
 
163
- /**
164
- * Check if has any where clauses
165
- */
166
- hasWhereClauses(): boolean {
167
- return this.whereClauses.length > 0;
168
- }
169
-
170
- /**
171
- * Check if has any sort options
172
- */
173
- hasSort(): boolean {
174
- return this.sortOptions.length > 0;
175
- }
176
-
177
- /**
178
- * Check if has date range
179
- */
180
- hasDateRange(): boolean {
181
- return this.dateRange !== null;
182
- }
183
-
184
- /**
185
- * Check if has pagination
186
- */
187
- hasPagination(): boolean {
188
- return this.pagination !== null;
189
- }
190
-
191
- /**
192
- * Check if is empty (no options set)
193
- */
194
- isEmpty(): boolean {
195
- return !this.hasWhereClauses() &&
196
- !this.hasSort() &&
197
- !this.hasDateRange() &&
198
- !this.hasPagination();
199
- }
200
-
201
- /**
202
- * Validate query options
203
- */
204
- validate(): { valid: boolean; errors: string[] } {
205
- const errors: string[] = [];
206
-
207
- // Validate where clauses
208
- for (const clause of this.whereClauses) {
209
- const clauseValidation = clause.validate();
210
- if (!clauseValidation.valid) {
211
- errors.push(...clauseValidation.errors);
212
- }
213
- }
214
-
215
- // Validate sort options
216
- for (const sort of this.sortOptions) {
217
- if (!sort.field || typeof sort.field !== 'string') {
218
- errors.push('Sort field must be a non-empty string');
219
- }
220
- if (sort.direction !== 'asc' && sort.direction !== 'desc') {
221
- errors.push('Sort direction must be "asc" or "desc"');
222
- }
223
- }
224
-
225
- // Validate date range
226
- if (this.dateRange) {
227
- if (!this.dateRange.field || typeof this.dateRange.field !== 'string') {
228
- errors.push('Date range field must be a non-empty string');
229
- }
230
- if (this.dateRange.startDate && !(this.dateRange.startDate instanceof Date)) {
231
- errors.push('Start date must be a Date instance');
232
- }
233
- if (this.dateRange.endDate && !(this.dateRange.endDate instanceof Date)) {
234
- errors.push('End date must be a Date instance');
235
- }
236
- if (
237
- this.dateRange.startDate &&
238
- this.dateRange.endDate &&
239
- this.dateRange.startDate > this.dateRange.endDate
240
- ) {
241
- errors.push('Start date must be before end date');
242
- }
243
- }
244
-
245
- // Validate pagination
246
- if (this.pagination) {
247
- if (this.pagination.limit !== undefined && (typeof this.pagination.limit !== 'number' || this.pagination.limit <= 0)) {
248
- errors.push('Pagination limit must be a positive number');
249
- }
250
- }
251
-
252
- return {
253
- valid: errors.length === 0,
254
- errors,
255
- };
256
- }
257
-
258
- /**
259
- * Convert to plain object (for serialization)
260
- */
261
- toObject(): {
262
- where: WhereClause[];
263
- sort: SortOptions[];
264
- dateRange: DateRangeOptions | null;
265
- pagination: PaginationOptions | null;
266
- } {
267
- return {
268
- where: [...this.whereClauses] as WhereClause[],
269
- sort: [...this.sortOptions] as SortOptions[],
270
- dateRange: this.dateRange,
271
- pagination: this.pagination,
272
- };
273
- }
274
-
275
- /**
276
- * Create from plain object
277
- */
278
- static fromObject(obj: {
279
- where?: Array<{ field: string; operator: WhereFilterOp; value: unknown }>;
280
- sort?: SortOptions[];
281
- dateRange?: DateRangeOptions;
282
- pagination?: PaginationOptions;
283
- }): QueryOptions {
284
- return QueryOptions.create({
285
- where: obj.where?.map(w => WhereClause.create(w.field, w.operator, w.value)) || [],
286
- sort: obj.sort || [],
287
- dateRange: obj.dateRange || null,
288
- pagination: obj.pagination || null,
289
- });
290
- }
291
-
292
163
  /**
293
164
  * Clone with modifications
294
165
  */
@@ -299,10 +170,10 @@ export class QueryOptions {
299
170
  pagination?: PaginationOptions | null;
300
171
  }): QueryOptions {
301
172
  return QueryOptions.create({
302
- where: modifications.where ?? this.whereClauses,
303
- sort: modifications.sort ?? this.sortOptions,
304
- dateRange: modifications.dateRange ?? this.dateRange,
305
- pagination: modifications.pagination ?? this.pagination,
173
+ where: modifications.where ?? [...this.whereClauses] as WhereClause[],
174
+ sort: modifications.sort ?? [...this.sortOptions] as SortOptions[],
175
+ dateRange: modifications.dateRange ?? this.dateRange ?? null,
176
+ pagination: modifications.pagination ?? this.pagination ?? null,
306
177
  });
307
178
  }
308
179
  }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Query Options Factory
3
+ * Single Responsibility: Create query options
4
+ *
5
+ * Max lines: 150 (enforced for maintainability)
6
+ */
7
+
8
+ import type { WhereFilterOp, OrderByDirection } from 'firebase/firestore';
9
+ import type { SortOptions, DateRangeOptions, PaginationOptions } from './QueryOptions';
10
+ import { WhereClause } from './WhereClause';
11
+ import { QueryOptions } from './QueryOptions';
12
+
13
+ /**
14
+ * Create empty query options
15
+ */
16
+ export function empty(): QueryOptions {
17
+ return QueryOptions.empty();
18
+ }
19
+
20
+ /**
21
+ * Create query options from partial configuration
22
+ */
23
+ export function create(options: {
24
+ where?: WhereClause[];
25
+ sort?: SortOptions[];
26
+ dateRange?: DateRangeOptions;
27
+ pagination?: PaginationOptions;
28
+ }): QueryOptions {
29
+ return QueryOptions.create(options);
30
+ }
31
+
32
+ /**
33
+ * Create query options with where clause
34
+ */
35
+ export function withWhere(
36
+ baseOptions: QueryOptions,
37
+ field: string,
38
+ operator: WhereFilterOp,
39
+ value: unknown
40
+ ): QueryOptions {
41
+ const clause = new WhereClause(field, operator, value);
42
+ return baseOptions.withWhere(clause);
43
+ }
44
+
45
+ /**
46
+ * Create query options with sort
47
+ */
48
+ export function withSort(
49
+ baseOptions: QueryOptions,
50
+ field: string,
51
+ direction: OrderByDirection
52
+ ): QueryOptions {
53
+ return baseOptions.withSort({ field, direction });
54
+ }
55
+
56
+ /**
57
+ * Create query options with date range
58
+ */
59
+ export function withDateRange(
60
+ baseOptions: QueryOptions,
61
+ field: string,
62
+ startDate?: Date,
63
+ endDate?: Date
64
+ ): QueryOptions {
65
+ return baseOptions.withDateRange({ field, startDate, endDate });
66
+ }
67
+
68
+ /**
69
+ * Create query options with pagination
70
+ */
71
+ export function withPagination(
72
+ baseOptions: QueryOptions,
73
+ pagination: PaginationOptions
74
+ ): QueryOptions {
75
+ return baseOptions.withPagination(pagination);
76
+ }
77
+
78
+ /**
79
+ * Create query options with limit
80
+ */
81
+ export function withLimit(baseOptions: QueryOptions, limit: number): QueryOptions {
82
+ return baseOptions.withLimit(limit);
83
+ }
84
+
85
+ /**
86
+ * Clone query options with modifications
87
+ */
88
+ export function clone(baseOptions: QueryOptions, modifications: {
89
+ where?: WhereClause[];
90
+ sort?: SortOptions[];
91
+ dateRange?: DateRangeOptions | null;
92
+ pagination?: PaginationOptions | null;
93
+ }): QueryOptions {
94
+ return baseOptions.clone(modifications);
95
+ }