@umituz/web-firebase 1.0.5 → 2.0.1

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 (69) hide show
  1. package/README.md +555 -0
  2. package/dist/application/index.d.mts +273 -0
  3. package/dist/application/index.d.ts +273 -0
  4. package/dist/application/index.js +490 -0
  5. package/dist/application/index.mjs +19 -0
  6. package/dist/chunk-34DL2QWQ.mjs +87 -0
  7. package/dist/chunk-4FP2ELQ5.mjs +96 -0
  8. package/dist/chunk-7TX3OU3O.mjs +721 -0
  9. package/dist/chunk-I6WGBPFB.mjs +439 -0
  10. package/dist/chunk-RZ4QR6TB.mjs +96 -0
  11. package/dist/chunk-U2XI4MGO.mjs +397 -0
  12. package/dist/domain/index.d.mts +325 -0
  13. package/dist/domain/index.d.ts +325 -0
  14. package/dist/domain/index.js +662 -0
  15. package/dist/domain/index.mjs +36 -0
  16. package/dist/file.repository.interface-v5vHgVsZ.d.mts +241 -0
  17. package/dist/file.repository.interface-v5vHgVsZ.d.ts +241 -0
  18. package/dist/firebase.entity-xvfEPjXZ.d.mts +15 -0
  19. package/dist/firebase.entity-xvfEPjXZ.d.ts +15 -0
  20. package/dist/index.d.mts +14 -96
  21. package/dist/index.d.ts +14 -96
  22. package/dist/index.js +1717 -78
  23. package/dist/index.mjs +88 -175
  24. package/dist/infrastructure/index.d.mts +170 -0
  25. package/dist/infrastructure/index.d.ts +170 -0
  26. package/dist/infrastructure/index.js +856 -0
  27. package/dist/infrastructure/index.mjs +46 -0
  28. package/dist/presentation/index.d.mts +25 -0
  29. package/dist/presentation/index.d.ts +25 -0
  30. package/dist/presentation/index.js +105 -0
  31. package/dist/presentation/index.mjs +6 -0
  32. package/dist/user.repository.interface-DS74TsJ5.d.mts +298 -0
  33. package/dist/user.repository.interface-DS74TsJ5.d.ts +298 -0
  34. package/package.json +37 -11
  35. package/src/application/dto/auth.dto.ts +69 -0
  36. package/src/application/dto/index.ts +7 -0
  37. package/src/application/dto/user.dto.ts +64 -0
  38. package/src/application/index.ts +7 -0
  39. package/src/application/use-cases/auth/reset-password.use-case.ts +66 -0
  40. package/src/application/use-cases/auth/sign-in-with-google.use-case.ts +86 -0
  41. package/src/application/use-cases/auth/sign-in.use-case.ts +77 -0
  42. package/src/application/use-cases/auth/sign-out.use-case.ts +22 -0
  43. package/src/application/use-cases/auth/sign-up.use-case.ts +99 -0
  44. package/src/application/use-cases/index.ts +12 -0
  45. package/src/application/use-cases/user/delete-account.use-case.ts +77 -0
  46. package/src/application/use-cases/user/update-profile.use-case.ts +98 -0
  47. package/src/domain/entities/file.entity.ts +151 -0
  48. package/src/domain/entities/timestamp.entity.ts +116 -0
  49. package/src/domain/entities/user.entity.ts +193 -0
  50. package/src/domain/errors/auth.errors.ts +115 -0
  51. package/src/domain/errors/repository.errors.ts +121 -0
  52. package/src/domain/index.ts +25 -2
  53. package/src/domain/interfaces/auth.repository.interface.ts +83 -0
  54. package/src/domain/interfaces/file.repository.interface.ts +143 -0
  55. package/src/domain/interfaces/user.repository.interface.ts +75 -0
  56. package/src/domain/value-objects/email.vo.ts +105 -0
  57. package/src/domain/value-objects/file-path.vo.ts +184 -0
  58. package/src/domain/value-objects/user-id.vo.ts +87 -0
  59. package/src/index.ts +19 -4
  60. package/src/infrastructure/firebase/auth.adapter.ts +220 -0
  61. package/src/infrastructure/firebase/client.ts +141 -0
  62. package/src/infrastructure/firebase/firestore.adapter.ts +190 -0
  63. package/src/infrastructure/firebase/storage.adapter.ts +323 -0
  64. package/src/infrastructure/index.ts +10 -5
  65. package/src/infrastructure/utils/storage.util.ts +3 -3
  66. package/src/presentation/hooks/useAuth.ts +153 -0
  67. package/src/presentation/hooks/useFirestore.ts +125 -0
  68. package/src/presentation/hooks/useStorage.ts +141 -0
  69. package/src/presentation/providers/FirebaseProvider.tsx +40 -0
package/dist/index.js CHANGED
@@ -20,97 +20,1700 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- FirestoreRepository: () => FirestoreRepository,
23
+ AuthAdapter: () => AuthAdapter,
24
+ AuthError: () => AuthError,
25
+ AuthErrorCode: () => AuthErrorCode,
26
+ DeleteAccountUseCase: () => DeleteAccountUseCase,
27
+ Email: () => Email,
28
+ FileErrorCode: () => FileErrorCode,
29
+ FilePath: () => FilePath,
30
+ FirestoreAdapter: () => FirestoreAdapter,
31
+ RepositoryError: () => RepositoryError,
32
+ RepositoryErrorCode: () => RepositoryErrorCode,
33
+ ResetPasswordUseCase: () => ResetPasswordUseCase,
34
+ SignInUseCase: () => SignInUseCase,
35
+ SignInWithGoogleUseCase: () => SignInWithGoogleUseCase,
36
+ SignOutUseCase: () => SignOutUseCase,
37
+ SignUpUseCase: () => SignUpUseCase,
38
+ StorageAdapter: () => StorageAdapter,
39
+ Timestamp: () => Timestamp,
40
+ USER_COLLECTIONS: () => USER_COLLECTIONS,
41
+ USER_SUBCOLLECTIONS: () => USER_SUBCOLLECTIONS,
42
+ UpdateProfileUseCase: () => UpdateProfileUseCase,
43
+ UserId: () => UserId,
44
+ analytics: () => analytics,
45
+ app: () => app,
46
+ auth: () => auth,
47
+ createAuthError: () => createAuthError,
48
+ createRepositoryError: () => createRepositoryError,
49
+ db: () => db,
24
50
  deleteFile: () => deleteFile,
51
+ functions: () => functions,
52
+ getFirebaseAnalytics: () => getFirebaseAnalytics,
53
+ getFirebaseApp: () => getFirebaseApp,
54
+ getFirebaseAuth: () => getFirebaseAuth,
55
+ getFirebaseDB: () => getFirebaseDB,
56
+ getFirebaseFunctions: () => getFirebaseFunctions,
57
+ getFirebaseInstances: () => getFirebaseInstances,
58
+ getFirebaseStorage: () => getFirebaseStorage,
25
59
  initializeFirebase: () => initializeFirebase,
60
+ serverTimestamp: () => serverTimestamp,
61
+ storage: () => storage,
26
62
  uploadBase64: () => uploadBase64,
27
63
  uploadFile: () => uploadFile,
28
64
  useFirebaseAuth: () => useFirebaseAuth
29
65
  });
30
66
  module.exports = __toCommonJS(index_exports);
31
67
 
32
- // src/infrastructure/services/firebase.service.ts
68
+ // src/domain/entities/user.entity.ts
69
+ var USER_COLLECTIONS = {
70
+ USERS: "users",
71
+ CONTENT: "content",
72
+ ANALYTICS: "analytics",
73
+ CONNECTED_ACCOUNTS: "connectedAccounts"
74
+ };
75
+ var USER_SUBCOLLECTIONS = {
76
+ CONTENT: "content",
77
+ ANALYTICS: "analytics",
78
+ SCHEDULED: "scheduled",
79
+ PUBLISHED: "published",
80
+ DRAFTS: "drafts"
81
+ };
82
+
83
+ // src/domain/entities/file.entity.ts
84
+ var FileErrorCode = /* @__PURE__ */ ((FileErrorCode2) => {
85
+ FileErrorCode2["FILE_TOO_LARGE"] = "FILE_TOO_LARGE";
86
+ FileErrorCode2["INVALID_TYPE"] = "INVALID_TYPE";
87
+ FileErrorCode2["INVALID_EXTENSION"] = "INVALID_EXTENSION";
88
+ FileErrorCode2["UPLOAD_FAILED"] = "UPLOAD_FAILED";
89
+ FileErrorCode2["DOWNLOAD_FAILED"] = "DOWNLOAD_FAILED";
90
+ FileErrorCode2["DELETE_FAILED"] = "DELETE_FAILED";
91
+ FileErrorCode2["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
92
+ FileErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
93
+ FileErrorCode2["CANCELLED"] = "CANCELLED";
94
+ return FileErrorCode2;
95
+ })(FileErrorCode || {});
96
+
97
+ // src/domain/entities/timestamp.entity.ts
98
+ var Timestamp = class _Timestamp {
99
+ constructor(seconds, nanoseconds) {
100
+ this.seconds = seconds;
101
+ this.nanoseconds = nanoseconds;
102
+ }
103
+ /**
104
+ * Create Timestamp from JavaScript Date
105
+ */
106
+ static fromDate(date) {
107
+ const milliseconds = date.getTime();
108
+ const seconds = Math.floor(milliseconds / 1e3);
109
+ const nanoseconds = milliseconds % 1e3 * 1e6;
110
+ return new _Timestamp(seconds, nanoseconds);
111
+ }
112
+ /**
113
+ * Create Timestamp from milliseconds
114
+ */
115
+ static fromMillis(milliseconds) {
116
+ const seconds = Math.floor(milliseconds / 1e3);
117
+ const nanoseconds = milliseconds % 1e3 * 1e6;
118
+ return new _Timestamp(seconds, nanoseconds);
119
+ }
120
+ /**
121
+ * Create Timestamp from Firestore Timestamp
122
+ */
123
+ static fromFirestoreTimestamp(timestamp) {
124
+ return new _Timestamp(timestamp.seconds, timestamp.nanoseconds);
125
+ }
126
+ /**
127
+ * Get current timestamp
128
+ */
129
+ static now() {
130
+ return _Timestamp.fromDate(/* @__PURE__ */ new Date());
131
+ }
132
+ /**
133
+ * Convert to JavaScript Date
134
+ */
135
+ toDate() {
136
+ return new Date(this.seconds * 1e3 + this.nanoseconds / 1e6);
137
+ }
138
+ /**
139
+ * Convert to milliseconds
140
+ */
141
+ toMillis() {
142
+ return this.seconds * 1e3 + this.nanoseconds / 1e6;
143
+ }
144
+ /**
145
+ * Convert to Firestore Timestamp
146
+ */
147
+ toFirestoreTimestamp() {
148
+ return {
149
+ seconds: this.seconds,
150
+ nanoseconds: this.nanoseconds
151
+ };
152
+ }
153
+ /**
154
+ * Convert to ISO string
155
+ */
156
+ toISOString() {
157
+ return this.toDate().toISOString();
158
+ }
159
+ /**
160
+ * Check if timestamp is in the past
161
+ */
162
+ isPast() {
163
+ return this.toMillis() < Date.now();
164
+ }
165
+ /**
166
+ * Check if timestamp is in the future
167
+ */
168
+ isFuture() {
169
+ return this.toMillis() > Date.now();
170
+ }
171
+ /**
172
+ * Get difference in milliseconds with another timestamp
173
+ */
174
+ diff(other) {
175
+ return this.toMillis() - other.toMillis();
176
+ }
177
+ };
178
+ var serverTimestamp = {
179
+ __type__: "serverTimestamp"
180
+ };
181
+
182
+ // src/domain/value-objects/email.vo.ts
183
+ var Email = class _Email {
184
+ value;
185
+ validated;
186
+ constructor(email) {
187
+ this.value = email.trim().toLowerCase();
188
+ this.validated = this.isValid();
189
+ if (!this.validated) {
190
+ throw new Error(`Invalid email address: ${email}`);
191
+ }
192
+ }
193
+ /**
194
+ * Validate email format
195
+ */
196
+ isValid() {
197
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
198
+ return emailRegex.test(this.value);
199
+ }
200
+ /**
201
+ * Get email value
202
+ */
203
+ getValue() {
204
+ return this.value;
205
+ }
206
+ /**
207
+ * Get local part (before @)
208
+ */
209
+ getLocalPart() {
210
+ return this.value.split("@")[0];
211
+ }
212
+ /**
213
+ * Get domain part (after @)
214
+ */
215
+ getDomain() {
216
+ return this.value.split("@")[1] || "";
217
+ }
218
+ /**
219
+ * Check if email is from a specific domain
220
+ */
221
+ isFromDomain(domain) {
222
+ return this.getDomain() === domain.toLowerCase();
223
+ }
224
+ /**
225
+ * Check if email is from a corporate domain (not gmail, yahoo, etc.)
226
+ */
227
+ isCorporate() {
228
+ const freeDomains = [
229
+ "gmail.com",
230
+ "yahoo.com",
231
+ "hotmail.com",
232
+ "outlook.com",
233
+ "aol.com",
234
+ "icloud.com",
235
+ "protonmail.com"
236
+ ];
237
+ return !freeDomains.includes(this.getDomain());
238
+ }
239
+ /**
240
+ * Mask email for display (e.g., u***@gmail.com)
241
+ */
242
+ mask() {
243
+ const [local, domain] = this.value.split("@");
244
+ if (local.length <= 2) {
245
+ return `${local[0]}***@${domain}`;
246
+ }
247
+ return `${local[0]}${"*".repeat(local.length - 2)}${local[local.length - 1]}@${domain}`;
248
+ }
249
+ /**
250
+ * Convert to string
251
+ */
252
+ toString() {
253
+ return this.value;
254
+ }
255
+ /**
256
+ * Check equality with another email
257
+ */
258
+ equals(other) {
259
+ return this.value === other.value;
260
+ }
261
+ /**
262
+ * Create Email from string (returns null if invalid)
263
+ */
264
+ static create(email) {
265
+ try {
266
+ return new _Email(email);
267
+ } catch {
268
+ return null;
269
+ }
270
+ }
271
+ };
272
+
273
+ // src/domain/errors/repository.errors.ts
274
+ var RepositoryError = class extends Error {
275
+ constructor(message, code, originalError) {
276
+ super(message);
277
+ this.code = code;
278
+ this.originalError = originalError;
279
+ this.name = "RepositoryError";
280
+ }
281
+ };
282
+ var RepositoryErrorCode = /* @__PURE__ */ ((RepositoryErrorCode2) => {
283
+ RepositoryErrorCode2["DOCUMENT_NOT_FOUND"] = "DOCUMENT_NOT_FOUND";
284
+ RepositoryErrorCode2["DOCUMENT_ALREADY_EXISTS"] = "DOCUMENT_ALREADY_EXISTS";
285
+ RepositoryErrorCode2["DOCUMENT_INVALID"] = "DOCUMENT_INVALID";
286
+ RepositoryErrorCode2["COLLECTION_NOT_FOUND"] = "COLLECTION_NOT_FOUND";
287
+ RepositoryErrorCode2["COLLECTION_INVALID"] = "COLLECTION_INVALID";
288
+ RepositoryErrorCode2["QUERY_INVALID"] = "QUERY_INVALID";
289
+ RepositoryErrorCode2["QUERY_FAILED"] = "QUERY_FAILED";
290
+ RepositoryErrorCode2["TRANSACTION_FAILED"] = "TRANSACTION_FAILED";
291
+ RepositoryErrorCode2["TRANSACTION_ABORTED"] = "TRANSACTION_ABORTED";
292
+ RepositoryErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
293
+ RepositoryErrorCode2["TIMEOUT"] = "TIMEOUT";
294
+ RepositoryErrorCode2["OFFLINE"] = "OFFLINE";
295
+ RepositoryErrorCode2["PERMISSION_DENIED"] = "PERMISSION_DENIED";
296
+ RepositoryErrorCode2["UNAUTHORIZED"] = "UNAUTHORIZED";
297
+ RepositoryErrorCode2["VALIDATION_FAILED"] = "VALIDATION_FAILED";
298
+ RepositoryErrorCode2["INVALID_DATA"] = "INVALID_DATA";
299
+ RepositoryErrorCode2["CONFLICT"] = "CONFLICT";
300
+ RepositoryErrorCode2["VERSION_MISMATCH"] = "VERSION_MISMATCH";
301
+ RepositoryErrorCode2["STORAGE_ERROR"] = "STORAGE_ERROR";
302
+ RepositoryErrorCode2["FILE_NOT_FOUND"] = "FILE_NOT_FOUND";
303
+ RepositoryErrorCode2["FILE_TOO_LARGE"] = "FILE_TOO_LARGE";
304
+ RepositoryErrorCode2["INVALID_FILE_TYPE"] = "INVALID_FILE_TYPE";
305
+ RepositoryErrorCode2["UNKNOWN"] = "UNKNOWN";
306
+ return RepositoryErrorCode2;
307
+ })(RepositoryErrorCode || {});
308
+ function createRepositoryError(code, message, originalError) {
309
+ const defaultMessage = getRepositoryErrorMessage(code);
310
+ return new RepositoryError(message || defaultMessage, code, originalError);
311
+ }
312
+ function getRepositoryErrorMessage(code) {
313
+ switch (code) {
314
+ case "DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */:
315
+ return "Document not found";
316
+ case "DOCUMENT_ALREADY_EXISTS" /* DOCUMENT_ALREADY_EXISTS */:
317
+ return "Document already exists";
318
+ case "DOCUMENT_INVALID" /* DOCUMENT_INVALID */:
319
+ return "Document is invalid";
320
+ case "COLLECTION_NOT_FOUND" /* COLLECTION_NOT_FOUND */:
321
+ return "Collection not found";
322
+ case "COLLECTION_INVALID" /* COLLECTION_INVALID */:
323
+ return "Collection is invalid";
324
+ case "QUERY_INVALID" /* QUERY_INVALID */:
325
+ return "Query is invalid";
326
+ case "QUERY_FAILED" /* QUERY_FAILED */:
327
+ return "Query failed";
328
+ case "TRANSACTION_FAILED" /* TRANSACTION_FAILED */:
329
+ return "Transaction failed";
330
+ case "TRANSACTION_ABORTED" /* TRANSACTION_ABORTED */:
331
+ return "Transaction aborted";
332
+ case "NETWORK_ERROR" /* NETWORK_ERROR */:
333
+ return "Network error";
334
+ case "TIMEOUT" /* TIMEOUT */:
335
+ return "Request timeout";
336
+ case "OFFLINE" /* OFFLINE */:
337
+ return "Client is offline";
338
+ case "PERMISSION_DENIED" /* PERMISSION_DENIED */:
339
+ return "Permission denied";
340
+ case "UNAUTHORIZED" /* UNAUTHORIZED */:
341
+ return "Unauthorized";
342
+ case "VALIDATION_FAILED" /* VALIDATION_FAILED */:
343
+ return "Validation failed";
344
+ case "INVALID_DATA" /* INVALID_DATA */:
345
+ return "Invalid data";
346
+ case "CONFLICT" /* CONFLICT */:
347
+ return "Conflict occurred";
348
+ case "VERSION_MISMATCH" /* VERSION_MISMATCH */:
349
+ return "Version mismatch";
350
+ case "STORAGE_ERROR" /* STORAGE_ERROR */:
351
+ return "Storage error";
352
+ case "FILE_NOT_FOUND" /* FILE_NOT_FOUND */:
353
+ return "File not found";
354
+ case "FILE_TOO_LARGE" /* FILE_TOO_LARGE */:
355
+ return "File is too large";
356
+ case "INVALID_FILE_TYPE" /* INVALID_FILE_TYPE */:
357
+ return "Invalid file type";
358
+ case "UNKNOWN" /* UNKNOWN */:
359
+ default:
360
+ return "An unknown error occurred";
361
+ }
362
+ }
363
+
364
+ // src/domain/value-objects/user-id.vo.ts
365
+ var UserId = class _UserId {
366
+ value;
367
+ constructor(id) {
368
+ this.value = id;
369
+ this.validate();
370
+ }
371
+ /**
372
+ * Validate user ID
373
+ */
374
+ validate() {
375
+ if (!this.value) {
376
+ throw createRepositoryError("INVALID_DATA" /* INVALID_DATA */, "User ID cannot be empty");
377
+ }
378
+ if (this.value.length < 20) {
379
+ throw createRepositoryError(
380
+ "INVALID_DATA" /* INVALID_DATA */,
381
+ "User ID is too short (must be at least 20 characters)"
382
+ );
383
+ }
384
+ const validPattern = /^[a-zA-Z0-9_-]+$/;
385
+ if (!validPattern.test(this.value)) {
386
+ throw createRepositoryError(
387
+ "INVALID_DATA" /* INVALID_DATA */,
388
+ "User ID contains invalid characters"
389
+ );
390
+ }
391
+ }
392
+ /**
393
+ * Get user ID value
394
+ */
395
+ getValue() {
396
+ return this.value;
397
+ }
398
+ /**
399
+ * Check if this is a temporary ID (not yet persisted)
400
+ */
401
+ isTemporary() {
402
+ return this.value.startsWith("temp_");
403
+ }
404
+ /**
405
+ * Convert to string
406
+ */
407
+ toString() {
408
+ return this.value;
409
+ }
410
+ /**
411
+ * Check equality with another user ID
412
+ */
413
+ equals(other) {
414
+ return this.value === other.value;
415
+ }
416
+ /**
417
+ * Generate a temporary user ID
418
+ */
419
+ static generateTemp() {
420
+ return new _UserId(`temp_${Date.now()}_${Math.random().toString(36).substring(7)}`);
421
+ }
422
+ /**
423
+ * Create UserId from string (returns null if invalid)
424
+ */
425
+ static create(id) {
426
+ try {
427
+ return new _UserId(id);
428
+ } catch {
429
+ return null;
430
+ }
431
+ }
432
+ };
433
+
434
+ // src/domain/value-objects/file-path.vo.ts
435
+ var FilePath = class _FilePath {
436
+ value;
437
+ parts;
438
+ constructor(path) {
439
+ this.value = path.trim();
440
+ this.parts = this.value.split("/").filter((p) => p.length > 0);
441
+ this.validate();
442
+ }
443
+ /**
444
+ * Validate file path
445
+ */
446
+ validate() {
447
+ if (!this.value) {
448
+ throw createRepositoryError("INVALID_DATA" /* INVALID_DATA */, "File path cannot be empty");
449
+ }
450
+ if (this.value.startsWith("/") || this.value.endsWith("/")) {
451
+ throw createRepositoryError(
452
+ "INVALID_DATA" /* INVALID_DATA */,
453
+ "File path should not start or end with /"
454
+ );
455
+ }
456
+ if (this.value.includes("//")) {
457
+ throw createRepositoryError("INVALID_DATA" /* INVALID_DATA */, "File path should not contain //");
458
+ }
459
+ if (this.parts.length === 0) {
460
+ throw createRepositoryError("INVALID_DATA" /* INVALID_DATA */, "File path must have at least one segment");
461
+ }
462
+ for (const part of this.parts) {
463
+ if (/[<>:"|?*]/.test(part)) {
464
+ throw createRepositoryError(
465
+ "INVALID_DATA" /* INVALID_DATA */,
466
+ `Invalid characters in path segment: ${part}`
467
+ );
468
+ }
469
+ if (part === "." || part === "..") {
470
+ throw createRepositoryError(
471
+ "INVALID_DATA" /* INVALID_DATA */,
472
+ `Invalid path segment: ${part}`
473
+ );
474
+ }
475
+ }
476
+ }
477
+ /**
478
+ * Get file path value
479
+ */
480
+ getValue() {
481
+ return this.value;
482
+ }
483
+ /**
484
+ * Get path parts
485
+ */
486
+ getParts() {
487
+ return [...this.parts];
488
+ }
489
+ /**
490
+ * Get filename (last part of path)
491
+ */
492
+ getFileName() {
493
+ return this.parts[this.parts.length - 1] || "";
494
+ }
495
+ /**
496
+ * Get directory path (all parts except last)
497
+ */
498
+ getDirectory() {
499
+ return this.parts.slice(0, -1).join("/");
500
+ }
501
+ /**
502
+ * Get file extension
503
+ */
504
+ getExtension() {
505
+ const fileName = this.getFileName();
506
+ const lastDot = fileName.lastIndexOf(".");
507
+ return lastDot > 0 ? fileName.substring(lastDot + 1) : "";
508
+ }
509
+ /**
510
+ * Get file name without extension
511
+ */
512
+ getFileNameWithoutExtension() {
513
+ const fileName = this.getFileName();
514
+ const lastDot = fileName.lastIndexOf(".");
515
+ return lastDot > 0 ? fileName.substring(0, lastDot) : fileName;
516
+ }
517
+ /**
518
+ * Check if path is in a specific directory
519
+ */
520
+ isInDirectory(directory) {
521
+ return this.value.startsWith(directory + "/");
522
+ }
523
+ /**
524
+ * Check if file has a specific extension
525
+ */
526
+ hasExtension(extension) {
527
+ return this.getExtension().toLowerCase() === extension.toLowerCase();
528
+ }
529
+ /**
530
+ * Create a new path by appending segments
531
+ */
532
+ append(...segments) {
533
+ return new _FilePath([...this.parts, ...segments].join("/"));
534
+ }
535
+ /**
536
+ * Create a new path in a parent directory
537
+ */
538
+ withParent(parent) {
539
+ return new _FilePath([parent, ...this.parts].join("/"));
540
+ }
541
+ /**
542
+ * Create a new path with a different filename
543
+ */
544
+ withFileName(fileName) {
545
+ const dir = this.getDirectory();
546
+ return dir ? new _FilePath(`${dir}/${fileName}`) : new _FilePath(fileName);
547
+ }
548
+ /**
549
+ * Convert to string
550
+ */
551
+ toString() {
552
+ return this.value;
553
+ }
554
+ /**
555
+ * Check equality with another file path
556
+ */
557
+ equals(other) {
558
+ return this.value === other.value;
559
+ }
560
+ /**
561
+ * Create user path (users/{userId}/...)
562
+ */
563
+ static userPath(userId, ...segments) {
564
+ return new _FilePath(["users", userId, ...segments].join("/"));
565
+ }
566
+ /**
567
+ * Create public path (public/...)
568
+ */
569
+ static publicPath(...segments) {
570
+ return new _FilePath(["public", ...segments].join("/"));
571
+ }
572
+ /**
573
+ * Create FilePath from string (returns null if invalid)
574
+ */
575
+ static create(path) {
576
+ try {
577
+ return new _FilePath(path);
578
+ } catch {
579
+ return null;
580
+ }
581
+ }
582
+ };
583
+
584
+ // src/domain/errors/auth.errors.ts
585
+ var AuthError = class extends Error {
586
+ constructor(message, code, originalError) {
587
+ super(message);
588
+ this.code = code;
589
+ this.originalError = originalError;
590
+ this.name = "AuthError";
591
+ }
592
+ };
593
+ var AuthErrorCode = /* @__PURE__ */ ((AuthErrorCode2) => {
594
+ AuthErrorCode2["USER_NOT_FOUND"] = "USER_NOT_FOUND";
595
+ AuthErrorCode2["USER_ALREADY_EXISTS"] = "USER_ALREADY_EXISTS";
596
+ AuthErrorCode2["INVALID_CREDENTIALS"] = "INVALID_CREDENTIALS";
597
+ AuthErrorCode2["WEAK_PASSWORD"] = "WEAK_PASSWORD";
598
+ AuthErrorCode2["EMAIL_NOT_VERIFIED"] = "EMAIL_NOT_VERIFIED";
599
+ AuthErrorCode2["SESSION_EXPIRED"] = "SESSION_EXPIRED";
600
+ AuthErrorCode2["UNAUTHENTICATED"] = "UNAUTHENTICATED";
601
+ AuthErrorCode2["TOO_MANY_REQUESTS"] = "TOO_MANY_REQUESTS";
602
+ AuthErrorCode2["OAUTH_ERROR"] = "OAUTH_ERROR";
603
+ AuthErrorCode2["OAUTH_CANCELLED"] = "OAUTH_CANCELLED";
604
+ AuthErrorCode2["OAUTH_ACCOUNT_EXISTS"] = "OAUTH_ACCOUNT_EXISTS";
605
+ AuthErrorCode2["SIGN_IN_FAILED"] = "SIGN_IN_FAILED";
606
+ AuthErrorCode2["SIGN_UP_FAILED"] = "SIGN_UP_FAILED";
607
+ AuthErrorCode2["SIGN_OUT_FAILED"] = "SIGN_OUT_FAILED";
608
+ AuthErrorCode2["PASSWORD_RESET_FAILED"] = "PASSWORD_RESET_FAILED";
609
+ AuthErrorCode2["EMAIL_VERIFICATION_FAILED"] = "EMAIL_VERIFICATION_FAILED";
610
+ AuthErrorCode2["PROFILE_UPDATE_FAILED"] = "PROFILE_UPDATE_FAILED";
611
+ AuthErrorCode2["EMAIL_UPDATE_FAILED"] = "EMAIL_UPDATE_FAILED";
612
+ AuthErrorCode2["PASSWORD_UPDATE_FAILED"] = "PASSWORD_UPDATE_FAILED";
613
+ AuthErrorCode2["ACCOUNT_DELETE_FAILED"] = "ACCOUNT_DELETE_FAILED";
614
+ AuthErrorCode2["REAUTHENTICATION_REQUIRED"] = "REAUTHENTICATION_REQUIRED";
615
+ AuthErrorCode2["REAUTHENTICATION_FAILED"] = "REAUTHENTICATION_FAILED";
616
+ AuthErrorCode2["UNKNOWN"] = "UNKNOWN";
617
+ return AuthErrorCode2;
618
+ })(AuthErrorCode || {});
619
+ function createAuthError(code, message, originalError) {
620
+ const defaultMessage = getAuthErrorMessage(code);
621
+ return new AuthError(message || defaultMessage, code, originalError);
622
+ }
623
+ function getAuthErrorMessage(code) {
624
+ switch (code) {
625
+ case "USER_NOT_FOUND" /* USER_NOT_FOUND */:
626
+ return "User not found";
627
+ case "USER_ALREADY_EXISTS" /* USER_ALREADY_EXISTS */:
628
+ return "User already exists";
629
+ case "INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */:
630
+ return "Invalid credentials";
631
+ case "WEAK_PASSWORD" /* WEAK_PASSWORD */:
632
+ return "Password is too weak";
633
+ case "EMAIL_NOT_VERIFIED" /* EMAIL_NOT_VERIFIED */:
634
+ return "Email not verified";
635
+ case "SESSION_EXPIRED" /* SESSION_EXPIRED */:
636
+ return "Session expired";
637
+ case "UNAUTHENTICATED" /* UNAUTHENTICATED */:
638
+ return "User not authenticated";
639
+ case "TOO_MANY_REQUESTS" /* TOO_MANY_REQUESTS */:
640
+ return "Too many requests";
641
+ case "OAUTH_ERROR" /* OAUTH_ERROR */:
642
+ return "OAuth error occurred";
643
+ case "OAUTH_CANCELLED" /* OAUTH_CANCELLED */:
644
+ return "OAuth cancelled by user";
645
+ case "OAUTH_ACCOUNT_EXISTS" /* OAUTH_ACCOUNT_EXISTS */:
646
+ return "Account already exists with this provider";
647
+ case "SIGN_IN_FAILED" /* SIGN_IN_FAILED */:
648
+ return "Sign in failed";
649
+ case "SIGN_UP_FAILED" /* SIGN_UP_FAILED */:
650
+ return "Sign up failed";
651
+ case "SIGN_OUT_FAILED" /* SIGN_OUT_FAILED */:
652
+ return "Sign out failed";
653
+ case "PASSWORD_RESET_FAILED" /* PASSWORD_RESET_FAILED */:
654
+ return "Password reset failed";
655
+ case "EMAIL_VERIFICATION_FAILED" /* EMAIL_VERIFICATION_FAILED */:
656
+ return "Email verification failed";
657
+ case "PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */:
658
+ return "Profile update failed";
659
+ case "EMAIL_UPDATE_FAILED" /* EMAIL_UPDATE_FAILED */:
660
+ return "Email update failed";
661
+ case "PASSWORD_UPDATE_FAILED" /* PASSWORD_UPDATE_FAILED */:
662
+ return "Password update failed";
663
+ case "ACCOUNT_DELETE_FAILED" /* ACCOUNT_DELETE_FAILED */:
664
+ return "Account deletion failed";
665
+ case "REAUTHENTICATION_REQUIRED" /* REAUTHENTICATION_REQUIRED */:
666
+ return "Reauthentication required";
667
+ case "REAUTHENTICATION_FAILED" /* REAUTHENTICATION_FAILED */:
668
+ return "Reauthentication failed";
669
+ case "UNKNOWN" /* UNKNOWN */:
670
+ default:
671
+ return "An unknown error occurred";
672
+ }
673
+ }
674
+
675
+ // src/application/use-cases/auth/sign-in.use-case.ts
676
+ var SignInUseCase = class {
677
+ constructor(authRepository) {
678
+ this.authRepository = authRepository;
679
+ }
680
+ /**
681
+ * Execute sign in use case
682
+ */
683
+ async execute(dto) {
684
+ try {
685
+ this.validateDTO(dto);
686
+ const result = await this.authRepository.signIn(dto.email, dto.password);
687
+ return result;
688
+ } catch (error) {
689
+ throw this.handleError(error);
690
+ }
691
+ }
692
+ /**
693
+ * Validate DTO
694
+ */
695
+ validateDTO(dto) {
696
+ if (!dto.email) {
697
+ throw createAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "Email is required");
698
+ }
699
+ if (!dto.password) {
700
+ throw createAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "Password is required");
701
+ }
702
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
703
+ if (!emailRegex.test(dto.email)) {
704
+ throw createAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "Invalid email format");
705
+ }
706
+ }
707
+ /**
708
+ * Handle errors
709
+ */
710
+ handleError(error) {
711
+ if (error instanceof Error) {
712
+ const message = error.message.toLowerCase();
713
+ if (message.includes("user-not-found") || message.includes("invalid-email")) {
714
+ return createAuthError("USER_NOT_FOUND" /* USER_NOT_FOUND */, "User not found", error);
715
+ }
716
+ if (message.includes("wrong-password") || message.includes("invalid-credential")) {
717
+ return createAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "Invalid credentials", error);
718
+ }
719
+ if (message.includes("too-many-requests")) {
720
+ return createAuthError("TOO_MANY_REQUESTS" /* TOO_MANY_REQUESTS */, "Too many attempts", error);
721
+ }
722
+ if (message.includes("user-disabled")) {
723
+ return createAuthError("USER_NOT_FOUND" /* USER_NOT_FOUND */, "Account disabled", error);
724
+ }
725
+ }
726
+ return createAuthError("SIGN_IN_FAILED" /* SIGN_IN_FAILED */, "Sign in failed", error);
727
+ }
728
+ };
729
+
730
+ // src/application/use-cases/auth/sign-up.use-case.ts
731
+ var SignUpUseCase = class {
732
+ constructor(authRepository, userRepository) {
733
+ this.authRepository = authRepository;
734
+ this.userRepository = userRepository;
735
+ }
736
+ /**
737
+ * Execute sign up use case
738
+ */
739
+ async execute(dto) {
740
+ try {
741
+ this.validateDTO(dto);
742
+ const existingUser = await this.userRepository.getUserByEmail(dto.email);
743
+ if (existingUser) {
744
+ throw createAuthError("USER_ALREADY_EXISTS" /* USER_ALREADY_EXISTS */, "User already exists");
745
+ }
746
+ const result = await this.authRepository.signUp(dto.email, dto.password, dto.displayName);
747
+ return {
748
+ ...result,
749
+ userId: result.user.uid,
750
+ emailVerified: result.user.emailVerified
751
+ };
752
+ } catch (error) {
753
+ throw this.handleError(error);
754
+ }
755
+ }
756
+ /**
757
+ * Validate DTO
758
+ */
759
+ validateDTO(dto) {
760
+ if (!dto.email) {
761
+ throw createAuthError("SIGN_UP_FAILED" /* SIGN_UP_FAILED */, "Email is required");
762
+ }
763
+ if (!dto.password) {
764
+ throw createAuthError("SIGN_UP_FAILED" /* SIGN_UP_FAILED */, "Password is required");
765
+ }
766
+ if (!dto.displayName || dto.displayName.trim().length === 0) {
767
+ throw createAuthError("SIGN_UP_FAILED" /* SIGN_UP_FAILED */, "Display name is required");
768
+ }
769
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
770
+ if (!emailRegex.test(dto.email)) {
771
+ throw createAuthError("SIGN_UP_FAILED" /* SIGN_UP_FAILED */, "Invalid email format");
772
+ }
773
+ if (dto.password.length < 6) {
774
+ throw createAuthError("WEAK_PASSWORD" /* WEAK_PASSWORD */, "Password must be at least 6 characters");
775
+ }
776
+ }
777
+ /**
778
+ * Handle errors
779
+ */
780
+ handleError(error) {
781
+ if (error instanceof Error && "code" in error) {
782
+ const code = error.code;
783
+ if (code === "auth/email-already-in-use") {
784
+ return createAuthError("USER_ALREADY_EXISTS" /* USER_ALREADY_EXISTS */, "Email already in use", error);
785
+ }
786
+ if (code === "auth/invalid-email") {
787
+ return createAuthError("SIGN_UP_FAILED" /* SIGN_UP_FAILED */, "Invalid email", error);
788
+ }
789
+ if (code === "auth/weak-password") {
790
+ return createAuthError("WEAK_PASSWORD" /* WEAK_PASSWORD */, "Password is too weak", error);
791
+ }
792
+ if (code === "auth/operation-not-allowed") {
793
+ return createAuthError("SIGN_UP_FAILED" /* SIGN_UP_FAILED */, "Email/password accounts not enabled", error);
794
+ }
795
+ }
796
+ return createAuthError("SIGN_UP_FAILED" /* SIGN_UP_FAILED */, "Sign up failed", error);
797
+ }
798
+ };
799
+
800
+ // src/application/use-cases/auth/sign-in-with-google.use-case.ts
801
+ var SignInWithGoogleUseCase = class {
802
+ constructor(authRepository, userRepository) {
803
+ this.authRepository = authRepository;
804
+ this.userRepository = userRepository;
805
+ }
806
+ /**
807
+ * Execute Google sign in use case
808
+ */
809
+ async execute() {
810
+ try {
811
+ const result = await this.authRepository.signInWithGoogle();
812
+ const existingUser = await this.userRepository.getUser(result.user.uid);
813
+ if (!existingUser) {
814
+ const createUserDTO = {
815
+ id: result.user.uid,
816
+ email: result.user.email || "",
817
+ displayName: result.user.displayName || "",
818
+ photoURL: result.user.photoURL || void 0,
819
+ phoneNumber: result.user.phoneNumber || void 0,
820
+ emailVerified: result.user.emailVerified
821
+ };
822
+ await this.authRepository.createUserDocument(result.user.uid, createUserDTO);
823
+ }
824
+ return result;
825
+ } catch (error) {
826
+ throw this.handleError(error);
827
+ }
828
+ }
829
+ /**
830
+ * Handle errors
831
+ */
832
+ handleError(error) {
833
+ if (error instanceof Error && "code" in error) {
834
+ const code = error.code;
835
+ if (code === "auth/popup-closed-by-user") {
836
+ return createAuthError("OAUTH_CANCELLED" /* OAUTH_CANCELLED */, "Google sign in cancelled", error);
837
+ }
838
+ if (code === "auth/popup-blocked") {
839
+ return createAuthError("OAUTH_ERROR" /* OAUTH_ERROR */, "Popup blocked by browser", error);
840
+ }
841
+ if (code === "auth/account-exists-with-different-credential") {
842
+ return createAuthError(
843
+ "OAUTH_ACCOUNT_EXISTS" /* OAUTH_ACCOUNT_EXISTS */,
844
+ "Account already exists with different credential",
845
+ error
846
+ );
847
+ }
848
+ if (code === "auth/auth-domain-policy-required") {
849
+ return createAuthError("OAUTH_ERROR" /* OAUTH_ERROR */, "Auth domain not configured", error);
850
+ }
851
+ if (code === "auth/unauthorized-domain") {
852
+ return createAuthError("OAUTH_ERROR" /* OAUTH_ERROR */, "Unauthorized domain", error);
853
+ }
854
+ if (code === "auth/cancelled-popup-request") {
855
+ return createAuthError("OAUTH_CANCELLED" /* OAUTH_CANCELLED */, "Sign in cancelled", error);
856
+ }
857
+ }
858
+ return createAuthError("OAUTH_ERROR" /* OAUTH_ERROR */, "Google sign in failed", error);
859
+ }
860
+ };
861
+
862
+ // src/application/use-cases/auth/reset-password.use-case.ts
863
+ var ResetPasswordUseCase = class {
864
+ constructor(authRepository) {
865
+ this.authRepository = authRepository;
866
+ }
867
+ /**
868
+ * Execute password reset use case
869
+ */
870
+ async execute(dto) {
871
+ try {
872
+ this.validateDTO(dto);
873
+ await this.authRepository.sendPasswordReset(dto.email);
874
+ } catch (error) {
875
+ throw this.handleError(error);
876
+ }
877
+ }
878
+ /**
879
+ * Validate DTO
880
+ */
881
+ validateDTO(dto) {
882
+ if (!dto.email) {
883
+ throw createAuthError("PASSWORD_RESET_FAILED" /* PASSWORD_RESET_FAILED */, "Email is required");
884
+ }
885
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
886
+ if (!emailRegex.test(dto.email)) {
887
+ throw createAuthError("PASSWORD_RESET_FAILED" /* PASSWORD_RESET_FAILED */, "Invalid email format");
888
+ }
889
+ }
890
+ /**
891
+ * Handle errors
892
+ */
893
+ handleError(error) {
894
+ if (error instanceof Error && "code" in error) {
895
+ const code = error.code;
896
+ if (code === "auth/invalid-email") {
897
+ return createAuthError("PASSWORD_RESET_FAILED" /* PASSWORD_RESET_FAILED */, "Invalid email", error);
898
+ }
899
+ if (code === "auth/user-not-found") {
900
+ return createAuthError("PASSWORD_RESET_FAILED" /* PASSWORD_RESET_FAILED */, "Password reset email sent if account exists");
901
+ }
902
+ if (code === "auth/too-many-requests") {
903
+ return createAuthError("TOO_MANY_REQUESTS" /* TOO_MANY_REQUESTS */, "Too many requests", error);
904
+ }
905
+ }
906
+ return createAuthError("PASSWORD_RESET_FAILED" /* PASSWORD_RESET_FAILED */, "Password reset failed", error);
907
+ }
908
+ };
909
+
910
+ // src/application/use-cases/auth/sign-out.use-case.ts
911
+ var SignOutUseCase = class {
912
+ constructor(authRepository) {
913
+ this.authRepository = authRepository;
914
+ }
915
+ /**
916
+ * Execute sign out use case
917
+ */
918
+ async execute() {
919
+ try {
920
+ await this.authRepository.signOut();
921
+ } catch (error) {
922
+ throw createAuthError("SIGN_OUT_FAILED" /* SIGN_OUT_FAILED */, "Sign out failed", error);
923
+ }
924
+ }
925
+ };
926
+
927
+ // src/application/use-cases/user/update-profile.use-case.ts
928
+ var UpdateProfileUseCase = class {
929
+ constructor(authRepository) {
930
+ this.authRepository = authRepository;
931
+ }
932
+ /**
933
+ * Execute profile update use case
934
+ */
935
+ async execute(dto) {
936
+ try {
937
+ this.validateDTO(dto);
938
+ const currentUser = this.authRepository.getCurrentUser();
939
+ if (!currentUser) {
940
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
941
+ }
942
+ await this.authRepository.updateProfile(dto);
943
+ } catch (error) {
944
+ throw this.handleError(error);
945
+ }
946
+ }
947
+ /**
948
+ * Validate DTO
949
+ */
950
+ validateDTO(dto) {
951
+ if (!dto.displayName && !dto.photoURL) {
952
+ throw createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "At least one field must be provided");
953
+ }
954
+ if (dto.displayName !== void 0) {
955
+ if (typeof dto.displayName !== "string") {
956
+ throw createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Display name must be a string");
957
+ }
958
+ if (dto.displayName.trim().length === 0) {
959
+ throw createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Display name cannot be empty");
960
+ }
961
+ if (dto.displayName.length > 100) {
962
+ throw createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Display name too long (max 100 characters)");
963
+ }
964
+ }
965
+ if (dto.photoURL !== void 0 && dto.photoURL !== null) {
966
+ if (typeof dto.photoURL !== "string") {
967
+ throw createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Photo URL must be a string");
968
+ }
969
+ if (dto.photoURL && !this.isValidURL(dto.photoURL)) {
970
+ throw createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Invalid photo URL");
971
+ }
972
+ }
973
+ }
974
+ /**
975
+ * Validate URL format
976
+ */
977
+ isValidURL(url) {
978
+ try {
979
+ new URL(url);
980
+ return true;
981
+ } catch {
982
+ return false;
983
+ }
984
+ }
985
+ /**
986
+ * Handle errors
987
+ */
988
+ handleError(error) {
989
+ if (error instanceof Error && "code" in error) {
990
+ const code = error.code;
991
+ if (code === "auth/requires-recent-login") {
992
+ return createAuthError("REAUTHENTICATION_REQUIRED" /* REAUTHENTICATION_REQUIRED */, "Please reauthenticate first", error);
993
+ }
994
+ if (code === "auth/invalid-photo-url") {
995
+ return createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Invalid photo URL", error);
996
+ }
997
+ }
998
+ return createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Profile update failed", error);
999
+ }
1000
+ };
1001
+
1002
+ // src/application/use-cases/user/delete-account.use-case.ts
1003
+ var DeleteAccountUseCase = class {
1004
+ constructor(authRepository) {
1005
+ this.authRepository = authRepository;
1006
+ }
1007
+ /**
1008
+ * Execute account deletion use case
1009
+ */
1010
+ async execute(dto) {
1011
+ try {
1012
+ this.validateDTO(dto);
1013
+ const currentUser = this.authRepository.getCurrentUser();
1014
+ if (!currentUser) {
1015
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
1016
+ }
1017
+ if (!currentUser.email) {
1018
+ throw createAuthError("ACCOUNT_DELETE_FAILED" /* ACCOUNT_DELETE_FAILED */, "User email not available");
1019
+ }
1020
+ await this.authRepository.deleteAccount(dto.password);
1021
+ } catch (error) {
1022
+ throw this.handleError(error);
1023
+ }
1024
+ }
1025
+ /**
1026
+ * Validate DTO
1027
+ */
1028
+ validateDTO(dto) {
1029
+ if (!dto.password) {
1030
+ throw createAuthError("ACCOUNT_DELETE_FAILED" /* ACCOUNT_DELETE_FAILED */, "Password is required");
1031
+ }
1032
+ if (dto.password.length < 1) {
1033
+ throw createAuthError("ACCOUNT_DELETE_FAILED" /* ACCOUNT_DELETE_FAILED */, "Password cannot be empty");
1034
+ }
1035
+ }
1036
+ /**
1037
+ * Handle errors
1038
+ */
1039
+ handleError(error) {
1040
+ if (error instanceof Error && "code" in error) {
1041
+ const code = error.code;
1042
+ if (code === "auth/requires-recent-login") {
1043
+ return createAuthError("REAUTHENTICATION_REQUIRED" /* REAUTHENTICATION_REQUIRED */, "Please reauthenticate first", error);
1044
+ }
1045
+ if (code === "auth/wrong-password") {
1046
+ return createAuthError("REAUTHENTICATION_FAILED" /* REAUTHENTICATION_FAILED */, "Invalid password", error);
1047
+ }
1048
+ if (code === "auth/too-many-requests") {
1049
+ return createAuthError("TOO_MANY_REQUESTS" /* TOO_MANY_REQUESTS */, "Too many requests", error);
1050
+ }
1051
+ if (code === "auth/user-not-found") {
1052
+ return createAuthError("ACCOUNT_DELETE_FAILED" /* ACCOUNT_DELETE_FAILED */, "User not found", error);
1053
+ }
1054
+ }
1055
+ return createAuthError("ACCOUNT_DELETE_FAILED" /* ACCOUNT_DELETE_FAILED */, "Account deletion failed", error);
1056
+ }
1057
+ };
1058
+
1059
+ // src/infrastructure/firebase/client.ts
33
1060
  var import_app = require("firebase/app");
34
- var import_auth = require("firebase/auth");
1061
+ var import_auth8 = require("firebase/auth");
35
1062
  var import_firestore = require("firebase/firestore");
36
1063
  var import_storage = require("firebase/storage");
1064
+ var import_analytics = require("firebase/analytics");
37
1065
  var import_functions = require("firebase/functions");
38
- function initializeFirebase(config, appName) {
39
- const existing = (0, import_app.getApps)().find((a) => a.name === (appName ?? "[DEFAULT]"));
40
- const app = existing ?? (0, import_app.initializeApp)(config, appName);
1066
+ var firebaseConfig = {
1067
+ apiKey: process.env.VITE_FIREBASE_API_KEY,
1068
+ authDomain: process.env.VITE_FIREBASE_AUTH_DOMAIN,
1069
+ projectId: process.env.VITE_FIREBASE_PROJECT_ID,
1070
+ storageBucket: process.env.VITE_FIREBASE_STORAGE_BUCKET,
1071
+ messagingSenderId: process.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
1072
+ appId: process.env.VITE_FIREBASE_APP_ID,
1073
+ measurementId: process.env.VITE_FIREBASE_MEASUREMENT_ID
1074
+ };
1075
+ var app;
1076
+ var auth;
1077
+ var db;
1078
+ var storage;
1079
+ var functions;
1080
+ var analytics = null;
1081
+ function initializeFirebase() {
1082
+ if (!(0, import_app.getApps)().length) {
1083
+ app = (0, import_app.initializeApp)(firebaseConfig);
1084
+ } else {
1085
+ app = (0, import_app.getApps)()[0];
1086
+ }
1087
+ return app;
1088
+ }
1089
+ function getFirebaseApp() {
1090
+ return app || initializeFirebase();
1091
+ }
1092
+ function getFirebaseAuth() {
1093
+ if (!auth) {
1094
+ const firebaseApp = getFirebaseApp();
1095
+ if (typeof window !== "undefined") {
1096
+ try {
1097
+ auth = (0, import_auth8.getAuth)(firebaseApp);
1098
+ } catch (e) {
1099
+ console.warn("getAuth failed, trying initializeAuth...", e);
1100
+ auth = (0, import_auth8.initializeAuth)(firebaseApp, {
1101
+ persistence: import_auth8.browserLocalPersistence
1102
+ });
1103
+ }
1104
+ } else {
1105
+ auth = (0, import_auth8.getAuth)(firebaseApp);
1106
+ }
1107
+ }
1108
+ return auth;
1109
+ }
1110
+ function getFirebaseDB() {
1111
+ if (!db) {
1112
+ db = (0, import_firestore.getFirestore)(getFirebaseApp());
1113
+ }
1114
+ return db;
1115
+ }
1116
+ function getFirebaseStorage() {
1117
+ if (!storage) {
1118
+ storage = (0, import_storage.getStorage)(getFirebaseApp());
1119
+ }
1120
+ return storage;
1121
+ }
1122
+ function getFirebaseFunctions() {
1123
+ if (!functions) {
1124
+ functions = (0, import_functions.getFunctions)(getFirebaseApp());
1125
+ }
1126
+ return functions;
1127
+ }
1128
+ function getFirebaseAnalytics() {
1129
+ if (!analytics && typeof window !== "undefined") {
1130
+ analytics = (0, import_analytics.getAnalytics)(getFirebaseApp());
1131
+ }
1132
+ return analytics;
1133
+ }
1134
+ function getFirebaseInstances() {
41
1135
  return {
42
- app,
43
- auth: (0, import_auth.getAuth)(app),
44
- db: (0, import_firestore.getFirestore)(app),
45
- storage: (0, import_storage.getStorage)(app),
46
- functions: (0, import_functions.getFunctions)(app)
1136
+ app: getFirebaseApp(),
1137
+ auth: getFirebaseAuth(),
1138
+ db: getFirebaseDB(),
1139
+ storage: getFirebaseStorage(),
1140
+ functions: getFirebaseFunctions(),
1141
+ analytics: getFirebaseAnalytics()
47
1142
  };
48
1143
  }
49
1144
 
50
- // src/infrastructure/services/firestore.repository.ts
1145
+ // src/infrastructure/firebase/auth.adapter.ts
1146
+ var import_auth9 = require("firebase/auth");
1147
+ var import_auth10 = require("firebase/auth");
1148
+ var AuthAdapter = class {
1149
+ get auth() {
1150
+ return getFirebaseAuth();
1151
+ }
1152
+ // Authentication Methods
1153
+ async signIn(email, password) {
1154
+ try {
1155
+ return await (0, import_auth9.signInWithEmailAndPassword)(this.auth, email, password);
1156
+ } catch (error) {
1157
+ throw this.handleAuthError(error);
1158
+ }
1159
+ }
1160
+ async signUp(email, password, displayName) {
1161
+ try {
1162
+ const result = await (0, import_auth9.createUserWithEmailAndPassword)(this.auth, email, password);
1163
+ await (0, import_auth9.updateProfile)(result.user, { displayName });
1164
+ await (0, import_auth9.sendEmailVerification)(result.user);
1165
+ return result;
1166
+ } catch (error) {
1167
+ throw this.handleAuthError(error);
1168
+ }
1169
+ }
1170
+ async signInWithGoogle() {
1171
+ try {
1172
+ const provider = new import_auth10.GoogleAuthProvider();
1173
+ provider.addScope("profile");
1174
+ provider.addScope("email");
1175
+ return await (0, import_auth9.signInWithPopup)(this.auth, provider);
1176
+ } catch (error) {
1177
+ throw this.handleAuthError(error);
1178
+ }
1179
+ }
1180
+ async signOut() {
1181
+ try {
1182
+ await (0, import_auth9.signOut)(this.auth);
1183
+ } catch (error) {
1184
+ throw createAuthError("SIGN_OUT_FAILED" /* SIGN_OUT_FAILED */, "Sign out failed", error);
1185
+ }
1186
+ }
1187
+ async sendPasswordReset(email) {
1188
+ try {
1189
+ await (0, import_auth9.sendPasswordResetEmail)(this.auth, email);
1190
+ } catch (error) {
1191
+ throw this.handleAuthError(error);
1192
+ }
1193
+ }
1194
+ async resendEmailVerification() {
1195
+ try {
1196
+ const user = this.auth.currentUser;
1197
+ if (!user) {
1198
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
1199
+ }
1200
+ await (0, import_auth9.sendEmailVerification)(user);
1201
+ } catch (error) {
1202
+ throw createAuthError("EMAIL_VERIFICATION_FAILED" /* EMAIL_VERIFICATION_FAILED */, "Failed to resend verification", error);
1203
+ }
1204
+ }
1205
+ // Profile Management
1206
+ async updateProfile(updates) {
1207
+ try {
1208
+ const user = this.auth.currentUser;
1209
+ if (!user) {
1210
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
1211
+ }
1212
+ await (0, import_auth9.updateProfile)(user, updates);
1213
+ } catch (error) {
1214
+ throw createAuthError("PROFILE_UPDATE_FAILED" /* PROFILE_UPDATE_FAILED */, "Profile update failed", error);
1215
+ }
1216
+ }
1217
+ async updateEmail(newEmail, password) {
1218
+ try {
1219
+ const user = this.auth.currentUser;
1220
+ if (!user || !user.email) {
1221
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
1222
+ }
1223
+ const credential = import_auth9.EmailAuthProvider.credential(user.email, password);
1224
+ await (0, import_auth9.reauthenticateWithCredential)(user, credential);
1225
+ await (0, import_auth9.updateEmail)(user, newEmail);
1226
+ } catch (error) {
1227
+ throw createAuthError("EMAIL_UPDATE_FAILED" /* EMAIL_UPDATE_FAILED */, "Email update failed", error);
1228
+ }
1229
+ }
1230
+ async updatePassword(currentPassword, newPassword) {
1231
+ try {
1232
+ const user = this.auth.currentUser;
1233
+ if (!user || !user.email) {
1234
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
1235
+ }
1236
+ const credential = import_auth9.EmailAuthProvider.credential(user.email, currentPassword);
1237
+ await (0, import_auth9.reauthenticateWithCredential)(user, credential);
1238
+ await (0, import_auth9.updatePassword)(user, newPassword);
1239
+ } catch (error) {
1240
+ throw createAuthError("PASSWORD_UPDATE_FAILED" /* PASSWORD_UPDATE_FAILED */, "Password update failed", error);
1241
+ }
1242
+ }
1243
+ async deleteAccount(password) {
1244
+ try {
1245
+ const user = this.auth.currentUser;
1246
+ if (!user || !user.email) {
1247
+ throw createAuthError("UNAUTHENTICATED" /* UNAUTHENTICATED */, "No user logged in");
1248
+ }
1249
+ const credential = import_auth9.EmailAuthProvider.credential(user.email, password);
1250
+ await (0, import_auth9.reauthenticateWithCredential)(user, credential);
1251
+ await user.delete();
1252
+ } catch (error) {
1253
+ throw createAuthError("ACCOUNT_DELETE_FAILED" /* ACCOUNT_DELETE_FAILED */, "Account deletion failed", error);
1254
+ }
1255
+ }
1256
+ // State Management
1257
+ getCurrentUser() {
1258
+ return this.auth.currentUser;
1259
+ }
1260
+ onAuthStateChanged(callback) {
1261
+ return this.auth.onAuthStateChanged(callback);
1262
+ }
1263
+ // Note: User document operations should be handled by UserAdapter
1264
+ // These methods are part of IAuthRepository interface but should be implemented separately
1265
+ async createUserDocument(_userId, _data) {
1266
+ throw new Error("createUserDocument should be handled by UserAdapter");
1267
+ }
1268
+ async updateLastLogin(_userId) {
1269
+ throw new Error("updateLastLogin should be handled by UserAdapter");
1270
+ }
1271
+ /**
1272
+ * Handle Firebase Auth errors
1273
+ */
1274
+ handleAuthError(error) {
1275
+ if (error instanceof Error && "code" in error) {
1276
+ const code = error.code;
1277
+ switch (code) {
1278
+ case "auth/user-not-found":
1279
+ return createAuthError("USER_NOT_FOUND" /* USER_NOT_FOUND */, "User not found", error);
1280
+ case "auth/wrong-password":
1281
+ case "auth/invalid-credential":
1282
+ return createAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "Invalid credentials", error);
1283
+ case "auth/email-already-in-use":
1284
+ return createAuthError("USER_ALREADY_EXISTS" /* USER_ALREADY_EXISTS */, "Email already in use", error);
1285
+ case "auth/weak-password":
1286
+ return createAuthError("WEAK_PASSWORD" /* WEAK_PASSWORD */, "Password is too weak", error);
1287
+ case "auth/invalid-email":
1288
+ return createAuthError("INVALID_CREDENTIALS" /* INVALID_CREDENTIALS */, "Invalid email", error);
1289
+ case "auth/user-disabled":
1290
+ return createAuthError("USER_NOT_FOUND" /* USER_NOT_FOUND */, "Account disabled", error);
1291
+ case "auth/too-many-requests":
1292
+ return createAuthError("TOO_MANY_REQUESTS" /* TOO_MANY_REQUESTS */, "Too many requests", error);
1293
+ case "auth/popup-closed-by-user":
1294
+ return createAuthError("OAUTH_CANCELLED" /* OAUTH_CANCELLED */, "Sign in cancelled", error);
1295
+ case "auth/account-exists-with-different-credential":
1296
+ return createAuthError("OAUTH_ACCOUNT_EXISTS" /* OAUTH_ACCOUNT_EXISTS */, "Account exists with different provider", error);
1297
+ case "auth/requires-recent-login":
1298
+ return createAuthError("REAUTHENTICATION_REQUIRED" /* REAUTHENTICATION_REQUIRED */, "Please reauthenticate", error);
1299
+ default:
1300
+ return createAuthError("UNKNOWN" /* UNKNOWN */, `Auth error: ${code}`, error);
1301
+ }
1302
+ }
1303
+ return createAuthError("UNKNOWN" /* UNKNOWN */, "Unknown auth error", error);
1304
+ }
1305
+ };
1306
+
1307
+ // src/infrastructure/firebase/firestore.adapter.ts
51
1308
  var import_firestore2 = require("firebase/firestore");
52
- var FirestoreRepository = class {
53
- constructor(db, collectionName) {
54
- this.db = db;
55
- this.collectionName = collectionName;
56
- }
57
- getCollection(parentPath) {
58
- const fullPath = parentPath ? `${parentPath}/${this.collectionName}` : this.collectionName;
59
- return (0, import_firestore2.collection)(this.db, fullPath);
60
- }
61
- getDocRef(id, parentPath) {
62
- const fullPath = parentPath ? `${parentPath}/${this.collectionName}` : this.collectionName;
63
- return (0, import_firestore2.doc)(this.db, fullPath, id);
64
- }
65
- async getById(id, parentPath) {
66
- const snap = await (0, import_firestore2.getDoc)(this.getDocRef(id, parentPath));
67
- return snap.exists() ? { id: snap.id, ...snap.data() } : null;
68
- }
69
- async getAll(constraints = [], parentPath) {
70
- const q = (0, import_firestore2.query)(this.getCollection(parentPath), ...constraints);
71
- const snap = await (0, import_firestore2.getDocs)(q);
72
- return snap.docs.map((d) => ({ id: d.id, ...d.data() }));
73
- }
74
- async create(id, data, parentPath) {
75
- await (0, import_firestore2.setDoc)(this.getDocRef(id, parentPath), {
76
- ...data,
77
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
78
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1309
+ var FirestoreAdapter = class {
1310
+ get db() {
1311
+ return getFirebaseDB();
1312
+ }
1313
+ USERS_COLLECTION = "users";
1314
+ async getUser(userId) {
1315
+ try {
1316
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1317
+ const snap = await (0, import_firestore2.getDoc)(docRef);
1318
+ if (!snap.exists()) {
1319
+ return null;
1320
+ }
1321
+ return snap.data();
1322
+ } catch (error) {
1323
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "User not found", error);
1324
+ }
1325
+ }
1326
+ async getUserByEmail(email) {
1327
+ try {
1328
+ const q = (0, import_firestore2.query)((0, import_firestore2.collection)(this.db, this.USERS_COLLECTION), (0, import_firestore2.where)("profile.email", "==", email));
1329
+ const snap = await (0, import_firestore2.getDocs)(q);
1330
+ if (snap.empty) {
1331
+ return null;
1332
+ }
1333
+ const doc2 = snap.docs[0];
1334
+ return doc2.data();
1335
+ } catch (error) {
1336
+ throw createRepositoryError("QUERY_FAILED" /* QUERY_FAILED */, "Failed to query user", error);
1337
+ }
1338
+ }
1339
+ async createUser(userId, data) {
1340
+ try {
1341
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1342
+ await (0, import_firestore2.setDoc)(docRef, data, { merge: true });
1343
+ } catch (error) {
1344
+ throw createRepositoryError("DOCUMENT_INVALID" /* DOCUMENT_INVALID */, "Failed to create user", error);
1345
+ }
1346
+ }
1347
+ async updateUser(userId, data) {
1348
+ try {
1349
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1350
+ await (0, import_firestore2.updateDoc)(docRef, {
1351
+ ...data,
1352
+ "profile.updatedAt": Date.now()
1353
+ });
1354
+ } catch (error) {
1355
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update user", error);
1356
+ }
1357
+ }
1358
+ async deleteUser(userId) {
1359
+ try {
1360
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1361
+ await (0, import_firestore2.deleteDoc)(docRef);
1362
+ } catch (error) {
1363
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to delete user", error);
1364
+ }
1365
+ }
1366
+ async updateProfile(userId, updates) {
1367
+ try {
1368
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1369
+ const updateData = {
1370
+ "profile.updatedAt": Date.now()
1371
+ };
1372
+ if (updates.displayName !== void 0) {
1373
+ updateData["profile.displayName"] = updates.displayName;
1374
+ }
1375
+ if (updates.photoURL !== void 0) {
1376
+ updateData["profile.photoURL"] = updates.photoURL;
1377
+ }
1378
+ if (updates.phoneNumber !== void 0) {
1379
+ updateData["profile.phoneNumber"] = updates.phoneNumber;
1380
+ }
1381
+ await (0, import_firestore2.updateDoc)(docRef, updateData);
1382
+ } catch (error) {
1383
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update profile", error);
1384
+ }
1385
+ }
1386
+ async updateSettings(userId, settings) {
1387
+ try {
1388
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1389
+ await (0, import_firestore2.updateDoc)(docRef, {
1390
+ settings: {
1391
+ ...settings,
1392
+ updatedAt: Date.now()
1393
+ }
1394
+ });
1395
+ } catch (error) {
1396
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update settings", error);
1397
+ }
1398
+ }
1399
+ async updateSubscription(userId, subscription) {
1400
+ try {
1401
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1402
+ await (0, import_firestore2.updateDoc)(docRef, {
1403
+ subscription: {
1404
+ ...subscription,
1405
+ updatedAt: Date.now()
1406
+ }
1407
+ });
1408
+ } catch (error) {
1409
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update subscription", error);
1410
+ }
1411
+ }
1412
+ async updateLastLogin(userId) {
1413
+ try {
1414
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1415
+ await (0, import_firestore2.updateDoc)(docRef, {
1416
+ "profile.lastLoginAt": Date.now()
1417
+ });
1418
+ } catch (error) {
1419
+ throw createRepositoryError("DOCUMENT_NOT_FOUND" /* DOCUMENT_NOT_FOUND */, "Failed to update last login", error);
1420
+ }
1421
+ }
1422
+ async queryUsers(constraints) {
1423
+ try {
1424
+ const q = (0, import_firestore2.query)((0, import_firestore2.collection)(this.db, this.USERS_COLLECTION), ...constraints);
1425
+ const snap = await (0, import_firestore2.getDocs)(q);
1426
+ return snap.docs.map((doc2) => doc2.data());
1427
+ } catch (error) {
1428
+ throw createRepositoryError("QUERY_FAILED" /* QUERY_FAILED */, "Failed to query users", error);
1429
+ }
1430
+ }
1431
+ subscribeToUser(userId, callback, onError) {
1432
+ const docRef = (0, import_firestore2.doc)(this.db, this.USERS_COLLECTION, userId);
1433
+ const unsubscribe = (0, import_firestore2.onSnapshot)(
1434
+ docRef,
1435
+ (snap) => {
1436
+ if (snap.exists()) {
1437
+ callback(snap.data());
1438
+ } else {
1439
+ callback(null);
1440
+ }
1441
+ },
1442
+ (error) => {
1443
+ onError?.(error);
1444
+ }
1445
+ );
1446
+ return unsubscribe;
1447
+ }
1448
+ };
1449
+
1450
+ // src/infrastructure/firebase/storage.adapter.ts
1451
+ var import_storage2 = require("firebase/storage");
1452
+ var StorageAdapter = class {
1453
+ get storage() {
1454
+ return getFirebaseStorage();
1455
+ }
1456
+ // Upload Methods
1457
+ async uploadFile(userId, path, file, options) {
1458
+ const storageRef = (0, import_storage2.ref)(this.storage, `users/${userId}/${path}`);
1459
+ const uploadTask = (0, import_storage2.uploadBytesResumable)(storageRef, file);
1460
+ return new Promise((resolve, reject) => {
1461
+ uploadTask.on(
1462
+ "state_changed",
1463
+ (snapshot) => {
1464
+ if (options?.onProgress) {
1465
+ const progress = {
1466
+ bytesTransferred: snapshot.bytesTransferred,
1467
+ totalBytes: snapshot.totalBytes,
1468
+ progress: snapshot.bytesTransferred / snapshot.totalBytes * 100,
1469
+ state: "running"
1470
+ };
1471
+ options.onProgress(progress);
1472
+ }
1473
+ },
1474
+ (error) => reject(createRepositoryError("STORAGE_ERROR" /* STORAGE_ERROR */, "Upload failed", error)),
1475
+ async () => {
1476
+ const downloadURL = await (0, import_storage2.getDownloadURL)(uploadTask.snapshot.ref);
1477
+ const metadata = await (0, import_storage2.getMetadata)(uploadTask.snapshot.ref);
1478
+ resolve({
1479
+ id: uploadTask.snapshot.ref.name,
1480
+ name: metadata.name || uploadTask.snapshot.ref.name,
1481
+ fullPath: metadata.fullPath || uploadTask.snapshot.ref.fullPath,
1482
+ downloadURL,
1483
+ contentType: metadata.contentType || "",
1484
+ size: metadata.size || 0,
1485
+ createdAt: metadata.timeCreated ? new Date(metadata.timeCreated).getTime() : Date.now()
1486
+ });
1487
+ }
1488
+ );
79
1489
  });
80
1490
  }
81
- async update(id, data, parentPath) {
82
- await (0, import_firestore2.updateDoc)(this.getDocRef(id, parentPath), {
83
- ...data,
84
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1491
+ async uploadImage(userId, file, filename) {
1492
+ const name = filename || `${Date.now()}_${file.name}`;
1493
+ return this.uploadFile(userId, `images/${name}`, file);
1494
+ }
1495
+ async uploadVideo(userId, file, filename) {
1496
+ const name = filename || `${Date.now()}_${file.name}`;
1497
+ return this.uploadFile(userId, `videos/${name}`, file);
1498
+ }
1499
+ async uploadDocument(userId, file, filename) {
1500
+ const name = filename || `${Date.now()}_${file.name}`;
1501
+ return this.uploadFile(userId, `documents/${name}`, file);
1502
+ }
1503
+ async uploadProfilePicture(userId, file) {
1504
+ const storageRef = (0, import_storage2.ref)(this.storage, `users/${userId}/profile/${Date.now()}_${file.name}`);
1505
+ await (0, import_storage2.uploadBytes)(storageRef, file);
1506
+ const downloadURL = await (0, import_storage2.getDownloadURL)(storageRef);
1507
+ return {
1508
+ id: storageRef.name,
1509
+ name: file.name,
1510
+ fullPath: storageRef.fullPath,
1511
+ downloadURL,
1512
+ contentType: file.type,
1513
+ size: file.size,
1514
+ createdAt: Date.now()
1515
+ };
1516
+ }
1517
+ // Download Methods
1518
+ async getDownloadURL(path) {
1519
+ try {
1520
+ const storageRef = (0, import_storage2.ref)(this.storage, path);
1521
+ return await (0, import_storage2.getDownloadURL)(storageRef);
1522
+ } catch (error) {
1523
+ throw createRepositoryError("FILE_NOT_FOUND" /* FILE_NOT_FOUND */, "File not found", error);
1524
+ }
1525
+ }
1526
+ // Delete Methods
1527
+ async deleteFile(path) {
1528
+ try {
1529
+ const storageRef = (0, import_storage2.ref)(this.storage, path);
1530
+ await (0, import_storage2.deleteObject)(storageRef);
1531
+ } catch (error) {
1532
+ throw createRepositoryError("FILE_NOT_FOUND" /* FILE_NOT_FOUND */, "File not found", error);
1533
+ }
1534
+ }
1535
+ async deleteUserFiles(userId) {
1536
+ try {
1537
+ const userRef = (0, import_storage2.ref)(this.storage, `users/${userId}`);
1538
+ const result = await (0, import_storage2.listAll)(userRef);
1539
+ for (const prefix of result.prefixes) {
1540
+ const prefixResult = await (0, import_storage2.listAll)(prefix);
1541
+ await Promise.all(prefixResult.items.map((item) => (0, import_storage2.deleteObject)(item)));
1542
+ }
1543
+ await Promise.all(result.items.map((item) => (0, import_storage2.deleteObject)(item)));
1544
+ } catch (error) {
1545
+ throw createRepositoryError("STORAGE_ERROR" /* STORAGE_ERROR */, "Failed to delete user files", error);
1546
+ }
1547
+ }
1548
+ async deleteImage(userId, filename) {
1549
+ await this.deleteFile(`users/${userId}/images/${filename}`);
1550
+ }
1551
+ async deleteVideo(userId, filename) {
1552
+ await this.deleteFile(`users/${userId}/videos/${filename}`);
1553
+ }
1554
+ async deleteProfilePicture(userId, filename) {
1555
+ await this.deleteFile(`users/${userId}/profile/${filename}`);
1556
+ }
1557
+ // List Methods
1558
+ async listUserFiles(userId, path) {
1559
+ const userRef = (0, import_storage2.ref)(this.storage, path ? `users/${userId}/${path}` : `users/${userId}`);
1560
+ const result = await (0, import_storage2.listAll)(userRef);
1561
+ const urls = await Promise.all(result.items.map((item) => (0, import_storage2.getDownloadURL)(item)));
1562
+ return urls;
1563
+ }
1564
+ async listUserImages(userId) {
1565
+ return this.listUserFiles(userId, "images");
1566
+ }
1567
+ async listUserVideos(userId) {
1568
+ return this.listUserFiles(userId, "videos");
1569
+ }
1570
+ // Metadata
1571
+ async getFileMetadata(path) {
1572
+ try {
1573
+ const storageRef = (0, import_storage2.ref)(this.storage, path);
1574
+ const metadata = await (0, import_storage2.getMetadata)(storageRef);
1575
+ return {
1576
+ id: storageRef.name,
1577
+ name: metadata.name,
1578
+ fullPath: metadata.fullPath,
1579
+ contentType: metadata.contentType || "application/octet-stream",
1580
+ size: metadata.size,
1581
+ createdAt: metadata.timeCreated ? new Date(metadata.timeCreated).getTime() : Date.now(),
1582
+ updatedAt: metadata.updated ? new Date(metadata.updated).getTime() : Date.now(),
1583
+ userId: this.extractUserId(path) || "unknown",
1584
+ type: this.extractFileType(metadata.contentType || "")
1585
+ };
1586
+ } catch (error) {
1587
+ throw createRepositoryError("FILE_NOT_FOUND" /* FILE_NOT_FOUND */, "File not found", error);
1588
+ }
1589
+ }
1590
+ async queryFiles(userId, _filters) {
1591
+ const userRef = (0, import_storage2.ref)(this.storage, `users/${userId}`);
1592
+ const result = await (0, import_storage2.listAll)(userRef);
1593
+ const files = await Promise.all(
1594
+ result.items.map(async (item) => {
1595
+ const metadata = await (0, import_storage2.getMetadata)(item);
1596
+ return {
1597
+ id: item.name,
1598
+ name: metadata.name,
1599
+ fullPath: metadata.fullPath,
1600
+ contentType: metadata.contentType || "application/octet-stream",
1601
+ size: metadata.size,
1602
+ createdAt: metadata.timeCreated ? new Date(metadata.timeCreated).getTime() : Date.now(),
1603
+ updatedAt: metadata.updated ? new Date(metadata.updated).getTime() : Date.now(),
1604
+ userId,
1605
+ type: this.extractFileType(metadata.contentType || "")
1606
+ };
1607
+ })
1608
+ );
1609
+ return {
1610
+ files,
1611
+ totalCount: files.length,
1612
+ hasMore: false
1613
+ };
1614
+ }
1615
+ async getStorageStats(userId) {
1616
+ const { files, totalCount } = await this.queryFiles(userId);
1617
+ const stats = {
1618
+ totalFiles: totalCount,
1619
+ totalSize: files.reduce((sum, file) => sum + file.size, 0),
1620
+ filesByType: {
1621
+ image: 0,
1622
+ video: 0,
1623
+ audio: 0,
1624
+ document: 0,
1625
+ other: 0
1626
+ },
1627
+ filesByCategory: {
1628
+ profile: 0,
1629
+ content: 0,
1630
+ document: 0,
1631
+ attachment: 0,
1632
+ backup: 0
1633
+ }
1634
+ };
1635
+ files.forEach((file) => {
1636
+ stats.filesByType[file.type]++;
1637
+ stats.lastUploadAt = Math.max(stats.lastUploadAt || 0, file.createdAt);
85
1638
  });
1639
+ return stats;
1640
+ }
1641
+ // Validation
1642
+ validateFile(file, options) {
1643
+ const maxSizeBytes = options?.maxSizeBytes || (options?.maxSizeMB ? options.maxSizeMB * 1024 * 1024 : 10 * 1024 * 1024);
1644
+ if (file.size > maxSizeBytes) {
1645
+ return false;
1646
+ }
1647
+ if (options?.allowedTypes && !options.allowedTypes.includes(file.type)) {
1648
+ return false;
1649
+ }
1650
+ return true;
1651
+ }
1652
+ isImageFile(file) {
1653
+ return file.type.startsWith("image/");
1654
+ }
1655
+ isVideoFile(file) {
1656
+ return file.type.startsWith("video/");
86
1657
  }
87
- async delete(id, parentPath) {
88
- await (0, import_firestore2.deleteDoc)(this.getDocRef(id, parentPath));
1658
+ isDocumentFile(file) {
1659
+ const docTypes = [
1660
+ "application/pdf",
1661
+ "application/msword",
1662
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1663
+ "application/vnd.ms-excel",
1664
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1665
+ "text/plain"
1666
+ ];
1667
+ return docTypes.includes(file.type);
1668
+ }
1669
+ // Utility Methods
1670
+ generateUniqueFilename(originalName) {
1671
+ const timestamp = Date.now();
1672
+ const random = Math.random().toString(36).substring(2, 8);
1673
+ const extension = this.getFileExtension(originalName);
1674
+ return `${timestamp}_${random}.${extension}`;
1675
+ }
1676
+ getFileExtension(filename) {
1677
+ return filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2);
1678
+ }
1679
+ // Helper Methods
1680
+ extractUserId(path) {
1681
+ const match = path.match(/users\/([^\/]+)/);
1682
+ return match ? match[1] : "";
1683
+ }
1684
+ extractFileType(contentType) {
1685
+ if (contentType.startsWith("image/")) return "image";
1686
+ if (contentType.startsWith("video/")) return "video";
1687
+ if (contentType.startsWith("audio/")) return "audio";
1688
+ if (contentType.includes("pdf") || contentType.includes("document") || contentType.includes("text")) {
1689
+ return "document";
1690
+ }
1691
+ return "other";
89
1692
  }
90
1693
  };
91
1694
 
92
1695
  // src/infrastructure/utils/storage.util.ts
93
- var import_storage2 = require("firebase/storage");
94
- async function uploadFile(storage, path, file) {
95
- const storageRef = (0, import_storage2.ref)(storage, path);
96
- await (0, import_storage2.uploadBytes)(storageRef, file);
97
- const url = await (0, import_storage2.getDownloadURL)(storageRef);
1696
+ var import_storage3 = require("firebase/storage");
1697
+ async function uploadFile(storage2, path, file) {
1698
+ const storageRef = (0, import_storage3.ref)(storage2, path);
1699
+ await (0, import_storage3.uploadBytes)(storageRef, file);
1700
+ const url = await (0, import_storage3.getDownloadURL)(storageRef);
98
1701
  return { url, path };
99
1702
  }
100
- async function uploadBase64(storage, path, base64, mimeType = "image/jpeg") {
101
- const storageRef = (0, import_storage2.ref)(storage, path);
1703
+ async function uploadBase64(storage2, path, base64, mimeType = "image/jpeg") {
1704
+ const storageRef = (0, import_storage3.ref)(storage2, path);
102
1705
  const dataUrl = base64.startsWith("data:") ? base64 : `data:${mimeType};base64,${base64}`;
103
- await (0, import_storage2.uploadString)(storageRef, dataUrl, "data_url");
104
- const url = await (0, import_storage2.getDownloadURL)(storageRef);
1706
+ await (0, import_storage3.uploadString)(storageRef, dataUrl, "data_url");
1707
+ const url = await (0, import_storage3.getDownloadURL)(storageRef);
105
1708
  return { url, path };
106
1709
  }
107
- async function deleteFile(storage, path) {
108
- await (0, import_storage2.deleteObject)((0, import_storage2.ref)(storage, path));
1710
+ async function deleteFile(storage2, path) {
1711
+ await (0, import_storage3.deleteObject)((0, import_storage3.ref)(storage2, path));
109
1712
  }
110
1713
 
111
1714
  // src/presentation/hooks/useFirebaseAuth.ts
112
1715
  var import_react = require("react");
113
- var import_auth2 = require("firebase/auth");
1716
+ var import_auth12 = require("firebase/auth");
114
1717
  function mapUser(u) {
115
1718
  return {
116
1719
  uid: u.uid,
@@ -120,56 +1723,56 @@ function mapUser(u) {
120
1723
  emailVerified: u.emailVerified
121
1724
  };
122
1725
  }
123
- function useFirebaseAuth(auth, options) {
1726
+ function useFirebaseAuth(auth2, options) {
124
1727
  const [user, setUser] = (0, import_react.useState)(null);
125
1728
  const [loading, setLoading] = (0, import_react.useState)(true);
126
1729
  (0, import_react.useEffect)(() => {
127
- const unsub = (0, import_auth2.onAuthStateChanged)(auth, (firebaseUser) => {
1730
+ const unsub = (0, import_auth12.onAuthStateChanged)(auth2, (firebaseUser) => {
128
1731
  const mapped = firebaseUser ? mapUser(firebaseUser) : null;
129
1732
  setUser(mapped);
130
1733
  setLoading(false);
131
1734
  options?.onUserChange?.(mapped);
132
1735
  });
133
1736
  return unsub;
134
- }, [auth, options]);
1737
+ }, [auth2, options]);
135
1738
  const signIn = (0, import_react.useCallback)(
136
- (email, password) => (0, import_auth2.signInWithEmailAndPassword)(auth, email, password),
137
- [auth]
1739
+ (email, password) => (0, import_auth12.signInWithEmailAndPassword)(auth2, email, password),
1740
+ [auth2]
138
1741
  );
139
1742
  const signUp = (0, import_react.useCallback)(
140
1743
  async (email, password, name) => {
141
- const cred = await (0, import_auth2.createUserWithEmailAndPassword)(auth, email, password);
142
- if (name && cred.user) await (0, import_auth2.updateProfile)(cred.user, { displayName: name });
1744
+ const cred = await (0, import_auth12.createUserWithEmailAndPassword)(auth2, email, password);
1745
+ if (name && cred.user) await (0, import_auth12.updateProfile)(cred.user, { displayName: name });
143
1746
  return cred;
144
1747
  },
145
- [auth]
1748
+ [auth2]
146
1749
  );
147
- const signOut = (0, import_react.useCallback)(() => (0, import_auth2.signOut)(auth), [auth]);
1750
+ const signOut = (0, import_react.useCallback)(() => (0, import_auth12.signOut)(auth2), [auth2]);
148
1751
  const updateUserProfile = (0, import_react.useCallback)(
149
1752
  async (name, photoURL) => {
150
- if (!auth.currentUser) throw new Error("No authenticated user");
151
- await (0, import_auth2.updateProfile)(auth.currentUser, {
1753
+ if (!auth2.currentUser) throw new Error("No authenticated user");
1754
+ await (0, import_auth12.updateProfile)(auth2.currentUser, {
152
1755
  displayName: name,
153
1756
  ...photoURL !== void 0 && { photoURL }
154
1757
  });
155
1758
  },
156
- [auth]
1759
+ [auth2]
157
1760
  );
158
1761
  const updateUserPassword = (0, import_react.useCallback)(
159
1762
  async (newPassword) => {
160
- if (!auth.currentUser) throw new Error("No authenticated user");
161
- await (0, import_auth2.updatePassword)(auth.currentUser, newPassword);
1763
+ if (!auth2.currentUser) throw new Error("No authenticated user");
1764
+ await (0, import_auth12.updatePassword)(auth2.currentUser, newPassword);
162
1765
  },
163
- [auth]
1766
+ [auth2]
164
1767
  );
165
1768
  const resetPassword = (0, import_react.useCallback)(
166
- (email) => (0, import_auth2.sendPasswordResetEmail)(auth, email),
167
- [auth]
1769
+ (email) => (0, import_auth12.sendPasswordResetEmail)(auth2, email),
1770
+ [auth2]
168
1771
  );
169
1772
  const getIdToken = (0, import_react.useCallback)(async () => {
170
- if (!auth.currentUser) throw new Error("No authenticated user");
171
- return auth.currentUser.getIdToken();
172
- }, [auth]);
1773
+ if (!auth2.currentUser) throw new Error("No authenticated user");
1774
+ return auth2.currentUser.getIdToken();
1775
+ }, [auth2]);
173
1776
  return {
174
1777
  user,
175
1778
  loading,
@@ -185,9 +1788,45 @@ function useFirebaseAuth(auth, options) {
185
1788
  }
186
1789
  // Annotate the CommonJS export names for ESM import in node:
187
1790
  0 && (module.exports = {
188
- FirestoreRepository,
1791
+ AuthAdapter,
1792
+ AuthError,
1793
+ AuthErrorCode,
1794
+ DeleteAccountUseCase,
1795
+ Email,
1796
+ FileErrorCode,
1797
+ FilePath,
1798
+ FirestoreAdapter,
1799
+ RepositoryError,
1800
+ RepositoryErrorCode,
1801
+ ResetPasswordUseCase,
1802
+ SignInUseCase,
1803
+ SignInWithGoogleUseCase,
1804
+ SignOutUseCase,
1805
+ SignUpUseCase,
1806
+ StorageAdapter,
1807
+ Timestamp,
1808
+ USER_COLLECTIONS,
1809
+ USER_SUBCOLLECTIONS,
1810
+ UpdateProfileUseCase,
1811
+ UserId,
1812
+ analytics,
1813
+ app,
1814
+ auth,
1815
+ createAuthError,
1816
+ createRepositoryError,
1817
+ db,
189
1818
  deleteFile,
1819
+ functions,
1820
+ getFirebaseAnalytics,
1821
+ getFirebaseApp,
1822
+ getFirebaseAuth,
1823
+ getFirebaseDB,
1824
+ getFirebaseFunctions,
1825
+ getFirebaseInstances,
1826
+ getFirebaseStorage,
190
1827
  initializeFirebase,
1828
+ serverTimestamp,
1829
+ storage,
191
1830
  uploadBase64,
192
1831
  uploadFile,
193
1832
  useFirebaseAuth