@umituz/react-native-firebase 2.6.0 → 2.6.2

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 (102) hide show
  1. package/package.json +1 -1
  2. package/src/application/auth/index.ts +42 -0
  3. package/src/application/auth/ports/AuthPort.ts.bak +164 -0
  4. package/src/application/auth/ports/AuthPort_part_aa +150 -0
  5. package/src/application/auth/ports/AuthPort_part_ab +14 -0
  6. package/src/application/auth/use-cases/SignInUseCase.ts.bak +253 -0
  7. package/src/application/auth/use-cases/SignInUseCaseHelpers.ts +0 -0
  8. package/src/application/auth/use-cases/SignInUseCaseMain.ts +0 -0
  9. package/src/application/auth/use-cases/SignInUseCase_part_aa +150 -0
  10. package/src/application/auth/use-cases/SignInUseCase_part_ab +103 -0
  11. package/src/application/auth/use-cases/SignOutUseCase.ts.bak +288 -0
  12. package/src/application/auth/use-cases/SignOutUseCaseCleanup.ts +0 -0
  13. package/src/application/auth/use-cases/SignOutUseCaseMain.ts +0 -0
  14. package/src/application/auth/use-cases/SignOutUseCase_part_aa +150 -0
  15. package/src/application/auth/use-cases/SignOutUseCase_part_ab +138 -0
  16. package/src/application/auth/use-cases/index.ts +26 -0
  17. package/src/domains/account-deletion/domain/index.ts +15 -0
  18. package/src/domains/account-deletion/domain/services/UserValidationHelpers.ts.bak +181 -0
  19. package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_aa +150 -0
  20. package/src/domains/account-deletion/domain/services/UserValidationHelpers_part_ab +31 -0
  21. package/src/domains/account-deletion/domain/services/UserValidationService.ts.bak +286 -0
  22. package/src/domains/account-deletion/domain/services/UserValidationService_part_aa +150 -0
  23. package/src/domains/account-deletion/domain/services/UserValidationService_part_ab +136 -0
  24. package/src/domains/account-deletion/index.ts +43 -6
  25. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor.ts.bak +230 -0
  26. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_aa +150 -0
  27. package/src/domains/account-deletion/infrastructure/services/AccountDeletionExecutor_part_ab +80 -0
  28. package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler.ts.bak +174 -0
  29. package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_aa +150 -0
  30. package/src/domains/account-deletion/infrastructure/services/AccountDeletionReauthHandler_part_ab +24 -0
  31. package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository.ts.bak +266 -0
  32. package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_aa +150 -0
  33. package/src/domains/account-deletion/infrastructure/services/AccountDeletionRepository_part_ab +116 -0
  34. package/src/domains/account-deletion/infrastructure/services/AccountDeletionTypes.ts +33 -0
  35. package/src/domains/account-deletion/infrastructure/services/account-deletion.service.ts +39 -227
  36. package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_aa +150 -0
  37. package/src/domains/account-deletion/infrastructure/services/reauthentication.service_part_ab +10 -0
  38. package/src/domains/auth/domain.ts +16 -0
  39. package/src/domains/auth/index.ts +7 -148
  40. package/src/domains/auth/infrastructure.ts.bak +156 -0
  41. package/src/domains/auth/infrastructure_part_aa +150 -0
  42. package/src/domains/auth/infrastructure_part_ab +6 -0
  43. package/src/domains/auth/presentation/hooks/GoogleOAuthHelpers.ts +0 -0
  44. package/src/domains/auth/presentation/hooks/GoogleOAuthHookService.ts.bak +247 -0
  45. package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_aa +150 -0
  46. package/src/domains/auth/presentation/hooks/GoogleOAuthHookService_part_ab +97 -0
  47. package/src/domains/auth/presentation/hooks/GoogleOAuthService.ts +0 -0
  48. package/src/domains/auth/presentation/hooks/useGoogleOAuth.ts +49 -103
  49. package/src/domains/auth/presentation.ts +25 -0
  50. package/src/domains/firestore/domain/entities/Collection.ts +128 -0
  51. package/src/domains/firestore/domain/entities/Collection.ts.bak +288 -0
  52. package/src/domains/firestore/domain/entities/CollectionFactory.ts +55 -0
  53. package/src/domains/firestore/domain/entities/CollectionHelpers.ts +143 -0
  54. package/src/domains/firestore/domain/entities/CollectionUtils.ts +72 -0
  55. package/src/domains/firestore/domain/entities/CollectionValidation.ts +138 -0
  56. package/src/domains/firestore/domain/entities/Collection_part_aa +150 -0
  57. package/src/domains/firestore/domain/entities/Collection_part_ab +138 -0
  58. package/src/domains/firestore/domain/entities/Document.ts.bak +233 -0
  59. package/src/domains/firestore/domain/entities/DocumentHelpers.ts +0 -0
  60. package/src/domains/firestore/domain/entities/DocumentMain.ts +0 -0
  61. package/src/domains/firestore/domain/entities/Document_part_aa +150 -0
  62. package/src/domains/firestore/domain/entities/Document_part_ab +83 -0
  63. package/src/domains/firestore/domain/index.ts +65 -0
  64. package/src/domains/firestore/domain/services/QueryService.ts.bak +182 -0
  65. package/src/domains/firestore/domain/services/QueryServiceAnalysis.ts.bak +169 -0
  66. package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_aa +150 -0
  67. package/src/domains/firestore/domain/services/QueryServiceAnalysis_part_ab +19 -0
  68. package/src/domains/firestore/domain/services/QueryServiceHelpers.ts.bak +151 -0
  69. package/src/domains/firestore/domain/services/QueryServiceHelpers_part_aa +150 -0
  70. package/src/domains/firestore/domain/services/QueryServiceHelpers_part_ab +1 -0
  71. package/src/domains/firestore/domain/services/QueryService_part_aa +150 -0
  72. package/src/domains/firestore/domain/services/QueryService_part_ab +32 -0
  73. package/src/domains/firestore/domain/value-objects/QueryOptions.ts.bak +191 -0
  74. package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization.ts.bak +207 -0
  75. package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_aa +150 -0
  76. package/src/domains/firestore/domain/value-objects/QueryOptionsSerialization_part_ab +57 -0
  77. package/src/domains/firestore/domain/value-objects/QueryOptionsValidation.ts.bak +182 -0
  78. package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_aa +150 -0
  79. package/src/domains/firestore/domain/value-objects/QueryOptionsValidation_part_ab +32 -0
  80. package/src/domains/firestore/domain/value-objects/QueryOptions_part_aa +150 -0
  81. package/src/domains/firestore/domain/value-objects/QueryOptions_part_ab +41 -0
  82. package/src/domains/firestore/domain/value-objects/WhereClause.ts.bak +299 -0
  83. package/src/domains/firestore/domain/value-objects/WhereClauseFactory.ts.bak +207 -0
  84. package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_aa +150 -0
  85. package/src/domains/firestore/domain/value-objects/WhereClauseFactory_part_ab +57 -0
  86. package/src/domains/firestore/domain/value-objects/WhereClause_part_aa +150 -0
  87. package/src/domains/firestore/domain/value-objects/WhereClause_part_ab +149 -0
  88. package/src/domains/firestore/index.ts +9 -6
  89. package/src/index.ts +25 -0
  90. package/src/shared/domain/utils/error-handlers/error-messages.ts +11 -0
  91. package/src/shared/infrastructure/base/ErrorHandler.ts.bak +189 -0
  92. package/src/shared/infrastructure/base/ErrorHandler_part_aa +150 -0
  93. package/src/shared/infrastructure/base/ErrorHandler_part_ab +39 -0
  94. package/src/shared/infrastructure/base/ServiceBase.ts.bak +220 -0
  95. package/src/shared/infrastructure/base/ServiceBase_part_aa +150 -0
  96. package/src/shared/infrastructure/base/ServiceBase_part_ab +70 -0
  97. package/src/shared/infrastructure/base/TypedGuard.ts +131 -0
  98. package/src/shared/infrastructure/base/index.ts +34 -0
  99. package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_aa +150 -0
  100. package/src/shared/infrastructure/config/base/ServiceClientSingleton_part_ab +5 -0
  101. /package/src/domains/account-deletion/infrastructure/services/{reauthentication.service.ts → reauthentication.service.ts.bak} +0 -0
  102. /package/src/shared/infrastructure/config/base/{ServiceClientSingleton.ts → ServiceClientSingleton.ts.bak} +0 -0
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Query Service Helpers
3
+ * Single Responsibility: Provide helper methods for query operations
4
+ *
5
+ * Helper methods for query options manipulation and creation.
6
+ * Separated from main QueryService for better maintainability.
7
+ *
8
+ * Max lines: 150 (enforced for maintainability)
9
+ */
10
+
11
+ import type { Query } from 'firebase/firestore';
12
+ import { QueryOptions, createQueryOptions } from '../value-objects/QueryOptions';
13
+ import { WhereClause } from '../value-objects/WhereClause';
14
+ import type { Collection } from '../entities/Collection';
15
+ import type { QueryBuilderResult } from './QueryService';
16
+
17
+ /**
18
+ * Create query options from filters object
19
+ */
20
+ export function createOptionsFromFilters(filters: Record<string, unknown>): QueryOptions {
21
+ const whereClauses = Object.entries(filters).map(([field, value]) =>
22
+ WhereClause.equals(field, value)
23
+ );
24
+
25
+ return createQueryOptions({ where: whereClauses });
26
+ }
27
+
28
+ /**
29
+ * Merge multiple query options
30
+ * Later options override earlier ones for conflicting settings
31
+ */
32
+ export function mergeOptions(...options: QueryOptions[]): QueryOptions {
33
+ if (options.length === 0) {
34
+ return createQueryOptions();
35
+ }
36
+
37
+ if (options.length === 1) {
38
+ return options[0];
39
+ }
40
+
41
+ const merged = options.reduce((acc, opt) => {
42
+ return QueryOptions.create({
43
+ where: [...acc.whereClauses, ...opt.whereClauses] as WhereClause[],
44
+ sort: opt.sortOptions.length > 0 ? [...opt.sortOptions] as any[] : [...acc.sortOptions] as any[],
45
+ dateRange: opt.dateRange ?? acc.dateRange,
46
+ pagination: opt.pagination ?? acc.pagination,
47
+ });
48
+ });
49
+
50
+ return merged ?? createQueryOptions();
51
+ }
52
+
53
+ /**
54
+ * Create paginated query options
55
+ */
56
+ export function createPaginatedOptions(limit: number, cursor?: number): QueryOptions {
57
+ return createQueryOptions({
58
+ pagination: { limit, cursor },
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Create sorted query options
64
+ */
65
+ export function createSortedOptions(
66
+ field: string,
67
+ direction: 'asc' | 'desc' = 'asc'
68
+ ): QueryOptions {
69
+ return createQueryOptions({
70
+ sort: [{ field, direction }],
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Optimize query options
76
+ * Removes redundant clauses and sorts
77
+ */
78
+ export function optimizeOptions(options: QueryOptions): QueryOptions {
79
+ // Remove duplicate where clauses
80
+ const uniqueWheres = options.whereClauses.filter((clause, index, self) =>
81
+ index === self.findIndex(c => c.equals(clause))
82
+ );
83
+
84
+ // Remove duplicate sorts
85
+ const uniqueSorts = options.sortOptions.filter((sort, index, self) =>
86
+ index === self.findIndex(s => s.field === sort.field && s.direction === sort.direction)
87
+ );
88
+
89
+ return QueryOptions.create({
90
+ where: uniqueWheres,
91
+ sort: uniqueSorts,
92
+ dateRange: options.dateRange,
93
+ pagination: options.pagination,
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Build query from collection entity
99
+ */
100
+ export function buildQueryFromCollection(
101
+ collection: Collection,
102
+ options: QueryOptions,
103
+ queryService: any
104
+ ): QueryBuilderResult {
105
+ return queryService.buildQuery(collection.getPath(), options);
106
+ }
107
+
108
+ /**
109
+ * Get query description for logging
110
+ */
111
+ export function describeQuery(options: QueryOptions): string {
112
+ const parts: string[] = [];
113
+
114
+ if (options.hasWhereClauses()) {
115
+ parts.push(`where: ${options.whereClauses.length} clause(s)`);
116
+ }
117
+
118
+ if (options.hasSort()) {
119
+ parts.push(`sort: ${options.sortOptions.length} field(s)`);
120
+ }
121
+
122
+ if (options.hasDateRange()) {
123
+ parts.push('date range: yes');
124
+ }
125
+
126
+ if (options.hasPagination()) {
127
+ parts.push(`limit: ${options.pagination?.limit || 'none'}`);
128
+ }
129
+
130
+ return parts.length > 0 ? parts.join(', ') : 'no filters';
131
+ }
132
+
133
+ /**
134
+ * Clone query options with modifications
135
+ */
136
+ export function cloneOptions(
137
+ base: QueryOptions,
138
+ modifications: {
139
+ where?: WhereClause[];
140
+ sort?: Array<{ field: string; direction: 'asc' | 'desc' }>;
141
+ dateRange?: { field: string; startDate?: Date; endDate?: Date };
142
+ pagination?: { limit?: number; cursor?: number };
143
+ }
144
+ ): QueryOptions {
145
+ return QueryOptions.create({
146
+ where: modifications.where ?? [...base.whereClauses] as WhereClause[],
147
+ sort: modifications.sort ?? [...base.sortOptions] as any[],
148
+ dateRange: modifications.dateRange ?? base.dateRange ?? undefined,
149
+ pagination: modifications.pagination ?? base.pagination ?? undefined,
150
+ });
151
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Query Service Helpers
3
+ * Single Responsibility: Provide helper methods for query operations
4
+ *
5
+ * Helper methods for query options manipulation and creation.
6
+ * Separated from main QueryService for better maintainability.
7
+ *
8
+ * Max lines: 150 (enforced for maintainability)
9
+ */
10
+
11
+ import type { Query } from 'firebase/firestore';
12
+ import { QueryOptions, createQueryOptions } from '../value-objects/QueryOptions';
13
+ import { WhereClause } from '../value-objects/WhereClause';
14
+ import type { Collection } from '../entities/Collection';
15
+ import type { QueryBuilderResult } from './QueryService';
16
+
17
+ /**
18
+ * Create query options from filters object
19
+ */
20
+ export function createOptionsFromFilters(filters: Record<string, unknown>): QueryOptions {
21
+ const whereClauses = Object.entries(filters).map(([field, value]) =>
22
+ WhereClause.equals(field, value)
23
+ );
24
+
25
+ return createQueryOptions({ where: whereClauses });
26
+ }
27
+
28
+ /**
29
+ * Merge multiple query options
30
+ * Later options override earlier ones for conflicting settings
31
+ */
32
+ export function mergeOptions(...options: QueryOptions[]): QueryOptions {
33
+ if (options.length === 0) {
34
+ return createQueryOptions();
35
+ }
36
+
37
+ if (options.length === 1) {
38
+ return options[0];
39
+ }
40
+
41
+ const merged = options.reduce((acc, opt) => {
42
+ return QueryOptions.create({
43
+ where: [...acc.whereClauses, ...opt.whereClauses] as WhereClause[],
44
+ sort: opt.sortOptions.length > 0 ? [...opt.sortOptions] as any[] : [...acc.sortOptions] as any[],
45
+ dateRange: opt.dateRange ?? acc.dateRange,
46
+ pagination: opt.pagination ?? acc.pagination,
47
+ });
48
+ });
49
+
50
+ return merged ?? createQueryOptions();
51
+ }
52
+
53
+ /**
54
+ * Create paginated query options
55
+ */
56
+ export function createPaginatedOptions(limit: number, cursor?: number): QueryOptions {
57
+ return createQueryOptions({
58
+ pagination: { limit, cursor },
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Create sorted query options
64
+ */
65
+ export function createSortedOptions(
66
+ field: string,
67
+ direction: 'asc' | 'desc' = 'asc'
68
+ ): QueryOptions {
69
+ return createQueryOptions({
70
+ sort: [{ field, direction }],
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Optimize query options
76
+ * Removes redundant clauses and sorts
77
+ */
78
+ export function optimizeOptions(options: QueryOptions): QueryOptions {
79
+ // Remove duplicate where clauses
80
+ const uniqueWheres = options.whereClauses.filter((clause, index, self) =>
81
+ index === self.findIndex(c => c.equals(clause))
82
+ );
83
+
84
+ // Remove duplicate sorts
85
+ const uniqueSorts = options.sortOptions.filter((sort, index, self) =>
86
+ index === self.findIndex(s => s.field === sort.field && s.direction === sort.direction)
87
+ );
88
+
89
+ return QueryOptions.create({
90
+ where: uniqueWheres,
91
+ sort: uniqueSorts,
92
+ dateRange: options.dateRange,
93
+ pagination: options.pagination,
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Build query from collection entity
99
+ */
100
+ export function buildQueryFromCollection(
101
+ collection: Collection,
102
+ options: QueryOptions,
103
+ queryService: any
104
+ ): QueryBuilderResult {
105
+ return queryService.buildQuery(collection.getPath(), options);
106
+ }
107
+
108
+ /**
109
+ * Get query description for logging
110
+ */
111
+ export function describeQuery(options: QueryOptions): string {
112
+ const parts: string[] = [];
113
+
114
+ if (options.hasWhereClauses()) {
115
+ parts.push(`where: ${options.whereClauses.length} clause(s)`);
116
+ }
117
+
118
+ if (options.hasSort()) {
119
+ parts.push(`sort: ${options.sortOptions.length} field(s)`);
120
+ }
121
+
122
+ if (options.hasDateRange()) {
123
+ parts.push('date range: yes');
124
+ }
125
+
126
+ if (options.hasPagination()) {
127
+ parts.push(`limit: ${options.pagination?.limit || 'none'}`);
128
+ }
129
+
130
+ return parts.length > 0 ? parts.join(', ') : 'no filters';
131
+ }
132
+
133
+ /**
134
+ * Clone query options with modifications
135
+ */
136
+ export function cloneOptions(
137
+ base: QueryOptions,
138
+ modifications: {
139
+ where?: WhereClause[];
140
+ sort?: Array<{ field: string; direction: 'asc' | 'desc' }>;
141
+ dateRange?: { field: string; startDate?: Date; endDate?: Date };
142
+ pagination?: { limit?: number; cursor?: number };
143
+ }
144
+ ): QueryOptions {
145
+ return QueryOptions.create({
146
+ where: modifications.where ?? [...base.whereClauses] as WhereClause[],
147
+ sort: modifications.sort ?? [...base.sortOptions] as any[],
148
+ dateRange: modifications.dateRange ?? base.dateRange ?? undefined,
149
+ pagination: modifications.pagination ?? base.pagination ?? undefined,
150
+ });
@@ -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
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Query Options Value Object (Main)
3
+ * Single Responsibility: Encapsulate query configuration options
4
+ *
5
+ * Value object that represents query options in a type-safe way.
6
+ * Provides validation and business logic for query configuration.
7
+ *
8
+ * Max lines: 150 (enforced for maintainability)
9
+ */
10
+
11
+ import type { WhereFilterOp, OrderByDirection } from 'firebase/firestore';
12
+ import { WhereClause } from './WhereClause';
13
+
14
+ /**
15
+ * Sort options
16
+ */
17
+ export interface SortOptions {
18
+ readonly field: string;
19
+ readonly direction: OrderByDirection;
20
+ }
21
+
22
+ /**
23
+ * Date range options
24
+ */
25
+ export interface DateRangeOptions {
26
+ readonly field: string;
27
+ readonly startDate?: Date;
28
+ readonly endDate?: Date;
29
+ }
30
+
31
+ /**
32
+ * Pagination options
33
+ */
34
+ export interface PaginationOptions {
35
+ readonly cursor?: number;
36
+ readonly limit?: number;
37
+ readonly startAfter?: number;
38
+ readonly startAt?: number;
39
+ }
40
+
41
+ /**
42
+ * Query options value object
43
+ * Immutable configuration for Firestore queries
44
+ */
45
+ export class QueryOptions {
46
+ readonly whereClauses: readonly WhereClause[];
47
+ readonly sortOptions: readonly SortOptions[];
48
+ readonly dateRange: DateRangeOptions | null;
49
+ readonly pagination: PaginationOptions | null;
50
+
51
+ private constructor(
52
+ whereClauses: WhereClause[],
53
+ sortOptions: SortOptions[],
54
+ dateRange: DateRangeOptions | null,
55
+ pagination: PaginationOptions | null
56
+ ) {
57
+ this.whereClauses = Object.freeze(whereClauses);
58
+ this.sortOptions = Object.freeze(sortOptions);
59
+ this.dateRange = dateRange ? Object.freeze(dateRange) : null;
60
+ this.pagination = pagination ? Object.freeze(pagination) : null;
61
+ }
62
+
63
+ /**
64
+ * Create empty query options
65
+ */
66
+ static empty(): QueryOptions {
67
+ return new QueryOptions([], [], null, null);
68
+ }
69
+
70
+ /**
71
+ * Create query options from partial configuration
72
+ */
73
+ static create(options: {
74
+ where?: WhereClause[];
75
+ sort?: SortOptions[];
76
+ dateRange?: DateRangeOptions;
77
+ pagination?: PaginationOptions;
78
+ }): QueryOptions {
79
+ return new QueryOptions(
80
+ options.where || [],
81
+ options.sort || [],
82
+ options.dateRange || null,
83
+ options.pagination || null
84
+ );
85
+ }
86
+
87
+ /**
88
+ * Add where clause
89
+ */
90
+ withWhere(clause: WhereClause): QueryOptions {
91
+ return new QueryOptions(
92
+ [...this.whereClauses, clause] as WhereClause[],
93
+ this.sortOptions,
94
+ this.dateRange,
95
+ this.pagination
96
+ );
97
+ }
98
+
99
+ /**
100
+ * Add sort option
101
+ */
102
+ withSort(sort: SortOptions): QueryOptions {
103
+ return new QueryOptions(
104
+ this.whereClauses,
105
+ [...this.sortOptions, sort] as SortOptions[],
106
+ this.dateRange,
107
+ this.pagination
108
+ );
109
+ }
110
+
111
+ /**
112
+ * Set date range
113
+ */
114
+ withDateRange(dateRange: DateRangeOptions): QueryOptions {
115
+ return new QueryOptions(
116
+ this.whereClauses,
117
+ this.sortOptions,
118
+ dateRange,
119
+ this.pagination
120
+ );
121
+ }
122
+
123
+ /**
124
+ * Set pagination
125
+ */
126
+ withPagination(pagination: PaginationOptions): QueryOptions {
127
+ return new QueryOptions(
128
+ this.whereClauses,
129
+ this.sortOptions,
130
+ this.dateRange,
131
+ pagination
132
+ );
133
+ }
134
+
135
+ /**
136
+ * Remove all where clauses
137
+ */
138
+ clearWhere(): QueryOptions {
139
+ return new QueryOptions([], this.sortOptions, this.dateRange, this.pagination);
140
+ }
141
+
142
+ /**
143
+ * Remove all sort options
144
+ */
145
+ clearSort(): QueryOptions {
146
+ return new QueryOptions(this.whereClauses, [], this.dateRange, this.pagination);
147
+ }
148
+
149
+ /**
150
+ * Remove date range
151
+ */
152
+ clearDateRange(): QueryOptions {
153
+ return new QueryOptions(this.whereClauses, this.sortOptions, null, this.pagination);
154
+ }
155
+
156
+ /**
157
+ * Remove pagination
158
+ */
159
+ clearPagination(): QueryOptions {
160
+ return new QueryOptions(this.whereClauses, this.sortOptions, this.dateRange, null);
161
+ }
162
+
163
+ /**
164
+ * Clone with modifications
165
+ */
166
+ clone(modifications: {
167
+ where?: WhereClause[];
168
+ sort?: SortOptions[];
169
+ dateRange?: DateRangeOptions | null;
170
+ pagination?: PaginationOptions | null;
171
+ }): 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
+ });
178
+ }
179
+ }
180
+
181
+ /**
182
+ * Factory function to create query options
183
+ */
184
+ export function createQueryOptions(options?: {
185
+ where?: WhereClause[];
186
+ sort?: SortOptions[];
187
+ dateRange?: DateRangeOptions;
188
+ pagination?: PaginationOptions;
189
+ }): QueryOptions {
190
+ return options ? QueryOptions.create(options) : QueryOptions.empty();
191
+ }