@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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Collection Entity
2
+ * Collection Entity (Main)
3
3
  * Single Responsibility: Represent a Firestore collection with metadata
4
4
  *
5
5
  * Domain entity that encapsulates collection information and metadata.
@@ -9,7 +9,22 @@
9
9
  */
10
10
 
11
11
  import type { CollectionReference, Query } from 'firebase/firestore';
12
- import type { Document } from './Document';
12
+ import {
13
+ isValidCollectionName,
14
+ isValidCollectionPath,
15
+ extractCollectionNameFromPath,
16
+ extractParentCollectionPath,
17
+ isUserCollectionPath,
18
+ extractUserIdFromPath,
19
+ createSubCollectionPath as createSubCollectionPathUtil,
20
+ } from './CollectionValidation';
21
+ import {
22
+ getCollectionDepth,
23
+ collectionToObject,
24
+ isQueryReference,
25
+ isCollectionReference as isCollectionReferenceUtil,
26
+ } from './CollectionUtils';
27
+ import { fromReference, fromQuery } from './CollectionFactory';
13
28
 
14
29
  /**
15
30
  * Collection metadata
@@ -46,243 +61,68 @@ export class Collection<TDocument = unknown> {
46
61
  static fromReference<TDocument = unknown>(
47
62
  reference: CollectionReference<TDocument>
48
63
  ): Collection<TDocument> {
49
- return new Collection(reference, {
50
- name: reference.id,
51
- path: reference.path,
52
- parentPath: reference.parent?.path || null,
53
- });
64
+ return fromReference(reference);
54
65
  }
55
66
 
56
67
  /**
57
68
  * Create collection from query
58
69
  */
59
70
  static fromQuery<TDocument = unknown>(query: Query<TDocument>, name: string, path: string): Collection<TDocument> {
60
- return new Collection(query, {
61
- name,
62
- path,
63
- parentPath: path.split('/').slice(0, -2).join('/') || null,
64
- });
71
+ return fromQuery(query, name, path);
65
72
  }
66
73
 
67
- /**
68
- * Get collection name
69
- */
70
74
  getName(): string {
71
75
  return this.name;
72
76
  }
73
77
 
74
- /**
75
- * Get collection path
76
- */
77
78
  getPath(): string {
78
79
  return this.path;
79
80
  }
80
81
 
81
- /**
82
- * Get parent path if exists
83
- */
84
- getParentPath(): string | null {
82
+ getParentPath(): string | undefined {
85
83
  return this.parentPath;
86
84
  }
87
85
 
88
- /**
89
- * Check if collection is nested (has parent)
90
- */
91
86
  isNested(): boolean {
92
- return this.parentPath !== null;
87
+ return this.parentPath !== undefined;
93
88
  }
94
89
 
95
- /**
96
- * Check if collection is root level (no parent)
97
- */
98
90
  isRootLevel(): boolean {
99
- return this.parentPath === null;
91
+ return this.parentPath === undefined;
100
92
  }
101
93
 
102
- /**
103
- * Get collection depth in hierarchy
104
- * Root collections have depth 0
105
- */
106
94
  getDepth(): number {
107
- if (!this.parentPath) return 0;
108
- return this.path.split('/').length / 2 - 1;
95
+ return getCollectionDepth(this);
109
96
  }
110
97
 
111
- /**
112
- * Get the underlying reference
113
- */
114
98
  getReference(): CollectionReference<TDocument> | Query<TDocument> {
115
99
  return this.reference;
116
100
  }
117
101
 
118
- /**
119
- * Check if collection is a query (has filters/limits)
120
- */
121
102
  isQuery(): boolean {
122
- return 'type' in this.reference && this.reference.type === 'query';
103
+ return isQueryReference(this.reference);
123
104
  }
124
105
 
125
- /**
126
- * Check if collection is a collection reference (no filters)
127
- */
128
106
  isCollectionReference(): boolean {
129
- return !this.isQuery();
130
- }
131
-
132
- /**
133
- * Validate collection name format
134
- * Collection names must match Firestore requirements
135
- */
136
- static isValidName(name: string): boolean {
137
- // Collection names must be non-empty strings
138
- if (!name || typeof name !== 'string' || name.trim() === '') {
139
- return false;
140
- }
141
-
142
- // Cannot contain special characters
143
- const invalidChars = /[\/\\.\s]/;
144
- if (invalidChars.test(name)) {
145
- return false;
146
- }
147
-
148
- // Cannot start or end with double underscore
149
- if (name.startsWith('__') || name.endsWith('__')) {
150
- return false;
151
- }
152
-
153
- // Reasonable length limit
154
- if (name.length > 100) {
155
- return false;
156
- }
157
-
158
- return true;
159
- }
160
-
161
- /**
162
- * Validate collection path format
163
- * Paths must follow Firestore path structure
164
- */
165
- static isValidPath(path: string): boolean {
166
- if (!path || typeof path !== 'string' || path.trim() === '') {
167
- return false;
168
- }
169
-
170
- const segments = path.split('/');
171
- if (segments.length < 2 || segments.length % 2 !== 0) {
172
- return false;
173
- }
174
-
175
- return segments.every(segment => this.isValidName(segment));
176
- }
177
-
178
- /**
179
- * Extract collection name from path
180
- */
181
- static extractNameFromPath(path: string): string | null {
182
- if (!this.isValidPath(path)) {
183
- return null;
184
- }
185
-
186
- const segments = path.split('/');
187
- return segments[segments.length - 1] || null;
107
+ return isCollectionReferenceUtil(this.reference);
188
108
  }
189
109
 
190
- /**
191
- * Extract parent path from collection path
192
- */
193
- static extractParentPath(path: string): string | null {
194
- if (!this.isValidPath(path)) {
195
- return null;
196
- }
197
-
198
- const segments = path.split('/');
199
- if (segments.length <= 2) {
200
- return null;
201
- }
202
-
203
- return segments.slice(0, -1).join('/');
204
- }
205
-
206
- /**
207
- * Convert to plain object (for serialization)
208
- */
209
110
  toObject(): CollectionMetadata {
210
- return {
211
- name: this.name,
212
- path: this.path,
213
- parentPath: this.parentPath || undefined,
214
- };
111
+ return collectionToObject(this);
215
112
  }
216
113
 
217
- /**
218
- * Create a sub-collection path
219
- */
220
114
  createSubCollectionPath(subCollectionName: string): string | null {
221
- if (!Collection.isValidName(subCollectionName)) {
222
- return null;
223
- }
224
-
225
- return `${this.path}/${subCollectionName}`;
226
- }
227
-
228
- /**
229
- * Check if collection is a sub-collection of another
230
- */
231
- isSubCollectionOf(other: Collection): boolean {
232
- return this.parentPath === other.path;
233
- }
234
-
235
- /**
236
- * Check if collection is parent of another
237
- */
238
- isParentOf(other: Collection): boolean {
239
- return other.isSubCollectionOf(this);
115
+ return createSubCollectionPathUtil(this.path, subCollectionName);
240
116
  }
241
117
 
242
- /**
243
- * Get collection ID (similar to name but more explicit)
244
- */
245
- getId(): string {
246
- return this.name;
247
- }
248
-
249
- /**
250
- * Check if this is a user collection (users/{userId}/{collection})
251
- */
252
118
  isUserCollection(): boolean {
253
- return this.parentPath?.startsWith('users/') || false;
119
+ return isUserCollectionPath(this.path);
254
120
  }
255
121
 
256
- /**
257
- * Extract user ID from user collection path
258
- * Returns null if not a user collection
259
- */
260
122
  extractUserId(): string | null {
261
- if (!this.isUserCollection()) return null;
262
-
263
- const segments = this.path.split('/');
264
- if (segments.length >= 3 && segments[0] === 'users') {
265
- return segments[1];
266
- }
267
-
268
- return null;
123
+ return extractUserIdFromPath(this.path);
269
124
  }
270
125
  }
271
126
 
272
- /**
273
- * Factory function to create collection entity
274
- */
275
- export function createCollection<TDocument = unknown>(
276
- reference: CollectionReference<TDocument> | Query<TDocument>,
277
- name?: string,
278
- path?: string
279
- ): Collection<TDocument> {
280
- if ('type' in reference && reference.type === 'query') {
281
- if (!name || !path) {
282
- throw new Error('name and path are required for query collections');
283
- }
284
- return Collection.fromQuery(reference, name, path);
285
- }
286
-
287
- return Collection.fromReference(reference as CollectionReference<TDocument>);
288
- }
127
+ // Re-export factory function for backward compatibility
128
+ export { createCollection, fromReference, fromQuery } from './CollectionFactory';
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Collection Entity
3
+ * Single Responsibility: Represent a Firestore collection with metadata
4
+ *
5
+ * Domain entity that encapsulates collection information and metadata.
6
+ * Provides business logic for collection operations.
7
+ *
8
+ * Max lines: 150 (enforced for maintainability)
9
+ */
10
+
11
+ import type { CollectionReference, Query } from 'firebase/firestore';
12
+ import type { Document } from './Document';
13
+
14
+ /**
15
+ * Collection metadata
16
+ */
17
+ export interface CollectionMetadata {
18
+ readonly name: string;
19
+ readonly path: string;
20
+ readonly parentPath?: string;
21
+ }
22
+
23
+ /**
24
+ * Collection entity
25
+ * Represents a Firestore collection with metadata
26
+ */
27
+ export class Collection<TDocument = unknown> {
28
+ readonly name: string;
29
+ readonly path: string;
30
+ readonly parentPath: string | undefined;
31
+ private readonly reference: CollectionReference<TDocument> | Query<TDocument>;
32
+
33
+ constructor(
34
+ reference: CollectionReference<TDocument> | Query<TDocument>,
35
+ metadata: CollectionMetadata
36
+ ) {
37
+ this.reference = reference;
38
+ this.name = metadata.name;
39
+ this.path = metadata.path;
40
+ this.parentPath = metadata.parentPath || undefined;
41
+ }
42
+
43
+ /**
44
+ * Create collection from collection reference
45
+ */
46
+ static fromReference<TDocument = unknown>(
47
+ reference: CollectionReference<TDocument>
48
+ ): Collection<TDocument> {
49
+ return new Collection(reference, {
50
+ name: reference.id,
51
+ path: reference.path,
52
+ parentPath: reference.parent?.path || null,
53
+ });
54
+ }
55
+
56
+ /**
57
+ * Create collection from query
58
+ */
59
+ static fromQuery<TDocument = unknown>(query: Query<TDocument>, name: string, path: string): Collection<TDocument> {
60
+ return new Collection(query, {
61
+ name,
62
+ path,
63
+ parentPath: path.split('/').slice(0, -2).join('/') || null,
64
+ });
65
+ }
66
+
67
+ /**
68
+ * Get collection name
69
+ */
70
+ getName(): string {
71
+ return this.name;
72
+ }
73
+
74
+ /**
75
+ * Get collection path
76
+ */
77
+ getPath(): string {
78
+ return this.path;
79
+ }
80
+
81
+ /**
82
+ * Get parent path if exists
83
+ */
84
+ getParentPath(): string | null {
85
+ return this.parentPath;
86
+ }
87
+
88
+ /**
89
+ * Check if collection is nested (has parent)
90
+ */
91
+ isNested(): boolean {
92
+ return this.parentPath !== null;
93
+ }
94
+
95
+ /**
96
+ * Check if collection is root level (no parent)
97
+ */
98
+ isRootLevel(): boolean {
99
+ return this.parentPath === null;
100
+ }
101
+
102
+ /**
103
+ * Get collection depth in hierarchy
104
+ * Root collections have depth 0
105
+ */
106
+ getDepth(): number {
107
+ if (!this.parentPath) return 0;
108
+ return this.path.split('/').length / 2 - 1;
109
+ }
110
+
111
+ /**
112
+ * Get the underlying reference
113
+ */
114
+ getReference(): CollectionReference<TDocument> | Query<TDocument> {
115
+ return this.reference;
116
+ }
117
+
118
+ /**
119
+ * Check if collection is a query (has filters/limits)
120
+ */
121
+ isQuery(): boolean {
122
+ return 'type' in this.reference && this.reference.type === 'query';
123
+ }
124
+
125
+ /**
126
+ * Check if collection is a collection reference (no filters)
127
+ */
128
+ isCollectionReference(): boolean {
129
+ return !this.isQuery();
130
+ }
131
+
132
+ /**
133
+ * Validate collection name format
134
+ * Collection names must match Firestore requirements
135
+ */
136
+ static isValidName(name: string): boolean {
137
+ // Collection names must be non-empty strings
138
+ if (!name || typeof name !== 'string' || name.trim() === '') {
139
+ return false;
140
+ }
141
+
142
+ // Cannot contain special characters
143
+ const invalidChars = /[\/\\.\s]/;
144
+ if (invalidChars.test(name)) {
145
+ return false;
146
+ }
147
+
148
+ // Cannot start or end with double underscore
149
+ if (name.startsWith('__') || name.endsWith('__')) {
150
+ return false;
151
+ }
152
+
153
+ // Reasonable length limit
154
+ if (name.length > 100) {
155
+ return false;
156
+ }
157
+
158
+ return true;
159
+ }
160
+
161
+ /**
162
+ * Validate collection path format
163
+ * Paths must follow Firestore path structure
164
+ */
165
+ static isValidPath(path: string): boolean {
166
+ if (!path || typeof path !== 'string' || path.trim() === '') {
167
+ return false;
168
+ }
169
+
170
+ const segments = path.split('/');
171
+ if (segments.length < 2 || segments.length % 2 !== 0) {
172
+ return false;
173
+ }
174
+
175
+ return segments.every(segment => this.isValidName(segment));
176
+ }
177
+
178
+ /**
179
+ * Extract collection name from path
180
+ */
181
+ static extractNameFromPath(path: string): string | null {
182
+ if (!this.isValidPath(path)) {
183
+ return null;
184
+ }
185
+
186
+ const segments = path.split('/');
187
+ return segments[segments.length - 1] || null;
188
+ }
189
+
190
+ /**
191
+ * Extract parent path from collection path
192
+ */
193
+ static extractParentPath(path: string): string | null {
194
+ if (!this.isValidPath(path)) {
195
+ return null;
196
+ }
197
+
198
+ const segments = path.split('/');
199
+ if (segments.length <= 2) {
200
+ return null;
201
+ }
202
+
203
+ return segments.slice(0, -1).join('/');
204
+ }
205
+
206
+ /**
207
+ * Convert to plain object (for serialization)
208
+ */
209
+ toObject(): CollectionMetadata {
210
+ return {
211
+ name: this.name,
212
+ path: this.path,
213
+ parentPath: this.parentPath || undefined,
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Create a sub-collection path
219
+ */
220
+ createSubCollectionPath(subCollectionName: string): string | null {
221
+ if (!Collection.isValidName(subCollectionName)) {
222
+ return null;
223
+ }
224
+
225
+ return `${this.path}/${subCollectionName}`;
226
+ }
227
+
228
+ /**
229
+ * Check if collection is a sub-collection of another
230
+ */
231
+ isSubCollectionOf(other: Collection): boolean {
232
+ return this.parentPath === other.path;
233
+ }
234
+
235
+ /**
236
+ * Check if collection is parent of another
237
+ */
238
+ isParentOf(other: Collection): boolean {
239
+ return other.isSubCollectionOf(this);
240
+ }
241
+
242
+ /**
243
+ * Get collection ID (similar to name but more explicit)
244
+ */
245
+ getId(): string {
246
+ return this.name;
247
+ }
248
+
249
+ /**
250
+ * Check if this is a user collection (users/{userId}/{collection})
251
+ */
252
+ isUserCollection(): boolean {
253
+ return this.parentPath?.startsWith('users/') || false;
254
+ }
255
+
256
+ /**
257
+ * Extract user ID from user collection path
258
+ * Returns null if not a user collection
259
+ */
260
+ extractUserId(): string | null {
261
+ if (!this.isUserCollection()) return null;
262
+
263
+ const segments = this.path.split('/');
264
+ if (segments.length >= 3 && segments[0] === 'users') {
265
+ return segments[1];
266
+ }
267
+
268
+ return null;
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Factory function to create collection entity
274
+ */
275
+ export function createCollection<TDocument = unknown>(
276
+ reference: CollectionReference<TDocument> | Query<TDocument>,
277
+ name?: string,
278
+ path?: string
279
+ ): Collection<TDocument> {
280
+ if ('type' in reference && reference.type === 'query') {
281
+ if (!name || !path) {
282
+ throw new Error('name and path are required for query collections');
283
+ }
284
+ return Collection.fromQuery(reference, name, path);
285
+ }
286
+
287
+ return Collection.fromReference(reference as CollectionReference<TDocument>);
288
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Collection Factory
3
+ * Single Responsibility: Create collection entities
4
+ *
5
+ * Max lines: 150 (enforced for maintainability)
6
+ */
7
+
8
+ import type { CollectionReference, Query } from 'firebase/firestore';
9
+ import { Collection } from './Collection';
10
+
11
+ /**
12
+ * Create collection from collection reference
13
+ */
14
+ export function fromReference<TDocument = unknown>(
15
+ reference: CollectionReference<TDocument>
16
+ ): Collection<TDocument> {
17
+ return new Collection(reference, {
18
+ name: reference.id,
19
+ path: reference.path,
20
+ parentPath: reference.parent?.path || undefined,
21
+ });
22
+ }
23
+
24
+ /**
25
+ * Create collection from query
26
+ */
27
+ export function fromQuery<TDocument = unknown>(
28
+ query: Query<TDocument>,
29
+ name: string,
30
+ path: string
31
+ ): Collection<TDocument> {
32
+ return new Collection(query, {
33
+ name,
34
+ path,
35
+ parentPath: path.split('/').slice(0, -2).join('/') || undefined,
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Factory function to create collection entity
41
+ */
42
+ export function createCollection<TDocument = unknown>(
43
+ reference: CollectionReference<TDocument> | Query<TDocument>,
44
+ name?: string,
45
+ path?: string
46
+ ): Collection<TDocument> {
47
+ if ('type' in reference && reference.type === 'query') {
48
+ if (!name || !path) {
49
+ throw new Error('name and path are required for query collections');
50
+ }
51
+ return fromQuery(reference, name, path);
52
+ }
53
+
54
+ return fromReference(reference as CollectionReference<TDocument>);
55
+ }