coregen-cli 3.1.6__py3-none-any.whl

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 (172) hide show
  1. coregen.py +1585 -0
  2. coregen_cli-3.1.6.dist-info/METADATA +1211 -0
  3. coregen_cli-3.1.6.dist-info/RECORD +172 -0
  4. coregen_cli-3.1.6.dist-info/WHEEL +5 -0
  5. coregen_cli-3.1.6.dist-info/entry_points.txt +2 -0
  6. coregen_cli-3.1.6.dist-info/top_level.txt +2 -0
  7. generators/__init__.py +1 -0
  8. generators/assets/__init__.py +1 -0
  9. generators/assets/assets.py +30 -0
  10. generators/assets/main.py +4 -0
  11. generators/config/__init__.py +1 -0
  12. generators/config/analisy_options.py +42 -0
  13. generators/config/main.py +11 -0
  14. generators/config/pubspec.py +139 -0
  15. generators/cursor/__init__.py +3 -0
  16. generators/cursor/setup.py +87 -0
  17. generators/helpers/__init__.py +56 -0
  18. generators/helpers/component.py +1030 -0
  19. generators/helpers/config.py +255 -0
  20. generators/helpers/data_source.py +180 -0
  21. generators/helpers/domain.py +469 -0
  22. generators/helpers/feature.py +654 -0
  23. generators/helpers/navigation.py +274 -0
  24. generators/helpers/page.py +63 -0
  25. generators/helpers/project.py +33 -0
  26. generators/helpers/utils.py +338 -0
  27. generators/helpers/validation.py +364 -0
  28. generators/initializator.py +68 -0
  29. generators/main.py +81 -0
  30. generators/static/assets/logo.png +0 -0
  31. generators/static/assets/mock/auth_user.json +5 -0
  32. generators/static/assets/svgs/apple.svg +4 -0
  33. generators/static/assets/svgs/google.svg +2 -0
  34. generators/static/cursor/AGENTS.md.jinja +37 -0
  35. generators/static/cursor/agents/doc-writer.md +20 -0
  36. generators/static/cursor/agents/epic-orchestrator.md +28 -0
  37. generators/static/cursor/agents/feature-implementer.md +29 -0
  38. generators/static/cursor/agents/integration-wiring.md +30 -0
  39. generators/static/cursor/agents/layer-application.md +21 -0
  40. generators/static/cursor/agents/layer-guardian.md +21 -0
  41. generators/static/cursor/agents/layer-infrastructure.md +31 -0
  42. generators/static/cursor/agents/layer-model.md +24 -0
  43. generators/static/cursor/agents/layer-presentation.md +33 -0
  44. generators/static/cursor/docs/architecture/APIS_AND_INTEGRATION.md.jinja +42 -0
  45. generators/static/cursor/docs/architecture/CARAVAGGIO_COMPONENTS.md.jinja +317 -0
  46. generators/static/cursor/docs/architecture/DDD_LAYERS.md.jinja +32 -0
  47. generators/static/cursor/docs/architecture/FILE_TEMPLATES.md.jinja +76 -0
  48. generators/static/cursor/docs/architecture/MOCK_AND_REMOTE_DATA.md.jinja +87 -0
  49. generators/static/cursor/docs/architecture/REFERENCE_IMPLEMENTATIONS.md.jinja +48 -0
  50. generators/static/cursor/docs/architecture/WIDGETS_AND_CARAVAGGIO.md.jinja +76 -0
  51. generators/static/cursor/docs/epics/README.md +17 -0
  52. generators/static/cursor/lib/domain/AGENTS.md +23 -0
  53. generators/static/cursor/lib/features/AGENTS.md +23 -0
  54. generators/static/cursor/lib/widgets/AGENTS.md +21 -0
  55. generators/static/cursor/rules/apis-layer.mdc.jinja +21 -0
  56. generators/static/cursor/rules/application-layer.mdc.jinja +12 -0
  57. generators/static/cursor/rules/architecture-core.mdc.jinja +43 -0
  58. generators/static/cursor/rules/dart-conventions.mdc.jinja +14 -0
  59. generators/static/cursor/rules/domain-layer.mdc.jinja +24 -0
  60. generators/static/cursor/rules/login-context.mdc.jinja +16 -0
  61. generators/static/cursor/rules/presentation-layer.mdc.jinja +15 -0
  62. generators/static/cursor/rules/quality-gate.mdc.jinja +14 -0
  63. generators/static/cursor/rules/ui-caravaggio.mdc.jinja +22 -0
  64. generators/static/cursor/skills/caravaggio-ui/SKILL.md +138 -0
  65. generators/static/cursor/skills/epic-delivery/SKILL.md +74 -0
  66. generators/static/cursor/skills/epic-delivery/templates/epic-status.md +14 -0
  67. generators/static/cursor/skills/epic-delivery/templates/epic.md +36 -0
  68. generators/static/templates/apis/common/constants_template.jinja +48 -0
  69. generators/static/templates/apis/common/data_source_config_template.jinja +47 -0
  70. generators/static/templates/apis/core/api_injectable_module_template.jinja +14 -0
  71. generators/static/templates/apis/interceptors/api_logger_template.jinja +135 -0
  72. generators/static/templates/apis/interceptors/auth_interceptor_template.jinja +207 -0
  73. generators/static/templates/auth/application/auth_bloc_template.jinja +36 -0
  74. generators/static/templates/auth/application/auth_event_template.jinja +7 -0
  75. generators/static/templates/auth/application/auth_state_template.jinja +8 -0
  76. generators/static/templates/auth/infrastructure/auth_facade_module_template.jinja +16 -0
  77. generators/static/templates/auth/infrastructure/auth_facade_template.jinja +288 -0
  78. generators/static/templates/auth/infrastructure/mock_auth_facade_template.jinja +106 -0
  79. generators/static/templates/auth/model/auth_failure_template.jinja +11 -0
  80. generators/static/templates/auth/model/i_auth_facade_template.jinja +24 -0
  81. generators/static/templates/auth/model/user_template.jinja +34 -0
  82. generators/static/templates/auth/model/value_objects_template.jinja +54 -0
  83. generators/static/templates/auth/presentation/login_screen_template.jinja +24 -0
  84. generators/static/templates/auth/sign_in_form/application/sign_in_form_bloc_template.jinja +139 -0
  85. generators/static/templates/auth/sign_in_form/application/sign_in_form_event_template.jinja +15 -0
  86. generators/static/templates/auth/sign_in_form/application/sign_in_form_state_template.jinja +26 -0
  87. generators/static/templates/auth/sign_in_form/presentation/apple_form_template.jinja +16 -0
  88. generators/static/templates/auth/sign_in_form/presentation/email_password_form_template_INACTIVE.jinja +94 -0
  89. generators/static/templates/auth/sign_in_form/presentation/google_form_template.jinja +15 -0
  90. generators/static/templates/auth/sign_in_form/presentation/sign_in_form_template.jinja +381 -0
  91. generators/static/templates/component/component_bloc_template.jinja +49 -0
  92. generators/static/templates/component/component_event_template.jinja +7 -0
  93. generators/static/templates/component/component_form_bloc_template.jinja +56 -0
  94. generators/static/templates/component/component_form_event_template.jinja +7 -0
  95. generators/static/templates/component/component_form_state_template.jinja +18 -0
  96. generators/static/templates/component/component_form_widget_template.jinja +130 -0
  97. generators/static/templates/component/component_list_widget_template.jinja +95 -0
  98. generators/static/templates/component/component_state_template.jinja +9 -0
  99. generators/static/templates/component/component_widget_template.jinja +113 -0
  100. generators/static/templates/core/bloc/base_form_bloc_template.jinja +32 -0
  101. generators/static/templates/core/errors/error_localizer_template.jinja +61 -0
  102. generators/static/templates/core/infrastructure/base_mapper_template.jinja +21 -0
  103. generators/static/templates/core/infrastructure/base_repository_mixin_template.jinja +78 -0
  104. generators/static/templates/core/infrastructure/firebase_injectable_module_template.jinja +11 -0
  105. generators/static/templates/core/infrastructure/firestore_helpers_template.jinja +22 -0
  106. generators/static/templates/core/infrastructure/repository_error_handler_template.jinja +44 -0
  107. generators/static/templates/core/model/common_interfaces_template.jinja +3 -0
  108. generators/static/templates/core/model/entity_template.jinja +5 -0
  109. generators/static/templates/core/model/errors_template.jinja +15 -0
  110. generators/static/templates/core/model/failures_template.jinja +97 -0
  111. generators/static/templates/core/model/value_objects_template.jinja +135 -0
  112. generators/static/templates/core/model/value_validators_template.jinja +105 -0
  113. generators/static/templates/core/presentation/app_drawer_template.jinja +52 -0
  114. generators/static/templates/core/presentation/app_widget_template.jinja +41 -0
  115. generators/static/templates/core/presentation/bottom_nav_bar_template.jinja +32 -0
  116. generators/static/templates/domain/domain_mapper_template.jinja +39 -0
  117. generators/static/templates/domain/domain_remote_service_template.jinja +46 -0
  118. generators/static/templates/domain/domain_repository_template.jinja +90 -0
  119. generators/static/templates/domain/domain_service_module_template.jinja +23 -0
  120. generators/static/templates/domain/enum_template.jinja +3 -0
  121. generators/static/templates/domain/i_domain_service_template.jinja +18 -0
  122. generators/static/templates/domain/mock_domain_service_template.jinja +73 -0
  123. generators/static/templates/feature/feature_bloc_template.jinja +104 -0
  124. generators/static/templates/feature/feature_dto_template.jinja +26 -0
  125. generators/static/templates/feature/feature_entity_template.jinja +23 -0
  126. generators/static/templates/feature/feature_event_template.jinja +20 -0
  127. generators/static/templates/feature/feature_extensions_template.jinja +25 -0
  128. generators/static/templates/feature/feature_failure_template.jinja +11 -0
  129. generators/static/templates/feature/feature_page_template.jinja +113 -0
  130. generators/static/templates/feature/feature_repository_template.jinja +112 -0
  131. generators/static/templates/feature/feature_state_template.jinja +20 -0
  132. generators/static/templates/feature/i_feature_repository_template.jinja +21 -0
  133. generators/static/templates/feature/value_object_field_template.jinja +0 -0
  134. generators/static/templates/feature/value_object_template.jinja +0 -0
  135. generators/static/templates/feature/value_validators_template.jinja +57 -0
  136. generators/static/templates/home/home_screen_template.jinja +68 -0
  137. generators/static/templates/home/presentation/home_screen_template.jinja +52 -0
  138. generators/static/templates/home/screen_template.jinja +12 -0
  139. generators/static/templates/injection_template.jinja +8 -0
  140. generators/static/templates/logging/analytics_logging_template.jinja +42 -0
  141. generators/static/templates/logging/console_template.jinja +50 -0
  142. generators/static/templates/logging/logger_injectable_module_template.jinja +24 -0
  143. generators/static/templates/logging/logger_template.jinja +118 -0
  144. generators/static/templates/main_template.jinja +43 -0
  145. generators/static/templates/page_template.jinja +29 -0
  146. generators/static/templates/router_template.jinja +26 -0
  147. generators/static/templates/splash/presentation/splash_screen_auth_template.jinja +97 -0
  148. generators/static/templates/splash/presentation/splash_screen_template.jinja +51 -0
  149. generators/static/templates/storage/storage_repository_template.jinja +80 -0
  150. generators/static/templates/user_profile/infrastructure/user_profile_dto_template.jinja +27 -0
  151. generators/static/templates/user_profile/infrastructure/user_profile_mapper_template.jinja +43 -0
  152. generators/static/templates/user_profile/infrastructure/user_profile_repository_template.jinja +82 -0
  153. generators/static/templates/user_profile/infrastructure/user_profile_service_template.jinja +27 -0
  154. generators/static/templates/user_profile/model/i_user_profile_repository_template.jinja +34 -0
  155. generators/static/templates/user_profile/model/user_profile_failure_template.jinja +11 -0
  156. generators/static/templates/user_profile/model/user_profile_template.jinja +31 -0
  157. generators/static/templates/widgets/common/custom_scaffold_template.jinja +52 -0
  158. generators/static/templates/widgets/common/error_widget_template.jinja +22 -0
  159. generators/static/templates/widgets/common/loading_widget_template.jinja +14 -0
  160. generators/static/templates/widgets/common/unknown_state_widget_template.jinja +23 -0
  161. generators/templates/__init__.py +1 -0
  162. generators/templates/_core/core_generator.py +121 -0
  163. generators/templates/apis/apis_generator.py +50 -0
  164. generators/templates/auth/auth_generator.py +40 -0
  165. generators/templates/auth/sign_in_form_generator.py +22 -0
  166. generators/templates/copier.py +51 -0
  167. generators/templates/home/home_generator.py +26 -0
  168. generators/templates/lib/lib_generator.py +40 -0
  169. generators/templates/logging/logging_generator.py +28 -0
  170. generators/templates/main.py +75 -0
  171. generators/templates/splash/splash_generator.py +20 -0
  172. generators/templates/storage/storage_generator.py +5 -0
@@ -0,0 +1,288 @@
1
+ import 'package:dartz/dartz.dart';
2
+ import 'package:injectable/injectable.dart';
3
+ import 'package:[[project_name]]/storage/storage_repository.dart';
4
+ import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
5
+ import 'package:[[project_name]]/domain/auth/model/user.dart' as auth_user;
6
+ import 'package:[[project_name]]/domain/auth/model/auth_failure.dart';
7
+ import 'package:[[project_name]]/domain/auth/model/i_auth_facade.dart';
8
+
9
+ /// NOTE: This class is currently a skeleton implementation. Uncomment and implement
10
+ /// the necessary parts to enable authentication functionality.
11
+ ///
12
+ /// This facade supports multiple authentication providers (Firebase, Google, Apple, etc.).
13
+ /// Implement the specific authentication logic for your chosen provider(s).
14
+ ///
15
+ /// To avoid circular dependency with IUserProfileRepository (UserProfileRepository
16
+ /// depends on IAuthFacade), AuthFacade does NOT inject IUserProfileRepository.
17
+ /// When implementing getSignedInUser/sign-in flows that need to sync UserProfile,
18
+ /// resolve IUserProfileRepository lazily (e.g. via GetIt) only when needed.
19
+
20
+ @lazySingleton
21
+ class AuthFacade implements IAuthFacade {
22
+ // final FirebaseAuth _firebaseAuth; // Uncomment the following line to use Firebase Authentication
23
+ // final FirebaseUserMapper _firebaseUserMapper; // Uncomment the following line to use Firebase Authentication
24
+ final StorageRepository _storageRepository;
25
+
26
+ AuthFacade(
27
+ // this._firebaseAuth, // Uncomment the following line to use Firebase Authentication
28
+ // this._firebaseUserMapper, // Uncomment the following line to use Firebase Authentication
29
+ this._storageRepository,
30
+ );
31
+
32
+ @override
33
+ Future<Option<auth_user.User>> getSignedInUser() async {
34
+ // Step 1: Get auth user from authentication provider
35
+ // Uncomment the following lines to use Firebase Authentication
36
+ // final firebaseUser = _firebaseAuth.currentUser;
37
+ // if (firebaseUser == null) {
38
+ // return none();
39
+ // }
40
+ // final authUser = _firebaseUserMapper.toDomain(firebaseUser);
41
+ // if (authUser == null) {
42
+ // return none();
43
+ // }
44
+
45
+ // Stub: For now, return none() until authentication provider is implemented
46
+ // TODO: Replace with actual authentication provider user retrieval
47
+ // final authUser = ...;
48
+
49
+ // Step 2: Get or create UserProfile using authId
50
+ // The UserProfile is synced with the authentication User
51
+ // Both use the same ID (authId) for correlation
52
+ // try {
53
+ // final authId = authUser.id.getOrCrash();
54
+ // final userProfileResult = await _userProfileRepository.getByAuthId(authId);
55
+ //
56
+ // final UserProfile userProfile = userProfileResult.fold(
57
+ // (failure) {
58
+ // // UserProfile doesn't exist, create it from auth User
59
+ // final newUserProfile = authUser.toUserProfile();
60
+ // return newUserProfile;
61
+ // },
62
+ // (existingProfile) {
63
+ // // UserProfile exists, update it with latest auth data if needed
64
+ // // Check if name or email changed
65
+ // if (existingProfile.name.getOrCrash() != authUser.name.getOrCrash() ||
66
+ // existingProfile.emailAddress.getOrCrash() != authUser.emailAddress.getOrCrash()) {
67
+ // final updatedProfile = authUser.toUserProfileWithTimestamps(
68
+ // createdAt: existingProfile.createdAt,
69
+ // updatedAt: DateTime.now(),
70
+ // avatarUrl: existingProfile.avatarUrl,
71
+ // );
72
+ // return updatedProfile;
73
+ // }
74
+ // return existingProfile;
75
+ // },
76
+ // );
77
+ //
78
+ // // Save/update UserProfile if it was created or updated
79
+ // if (userProfileResult.isLeft()) {
80
+ // await _userProfileRepository.createOrUpdate(userProfile);
81
+ // } else {
82
+ // // Check if update is needed
83
+ // final needsUpdate = userProfile.name.getOrCrash() != authUser.name.getOrCrash() ||
84
+ // userProfile.emailAddress.getOrCrash() != authUser.emailAddress.getOrCrash();
85
+ // if (needsUpdate) {
86
+ // await _userProfileRepository.createOrUpdate(userProfile);
87
+ // }
88
+ // }
89
+ //
90
+ // // Step 3: Return UserProfile mapped to User (for AuthBloc compatibility)
91
+ // return optionOf(userProfile.toUser());
92
+ // } catch (e) {
93
+ // // If UserProfile sync fails, fall back to auth User
94
+ // return optionOf(authUser);
95
+ // }
96
+
97
+ return none();
98
+ }
99
+
100
+ @override
101
+ Future<Either<AuthFailure, Unit>> registerWithEmailAndPassword({
102
+ required EmailAddress emailAddress,
103
+ required Password password,
104
+ }) async {
105
+ final String emailAddressStr = emailAddress.value.getOrElse(() => throw 'INVALID EMAIL');
106
+ final String passwordStr = password.value.getOrElse(() => throw 'INVALID PASSWORD');
107
+
108
+ // Uncomment the following code to implement Email/Password Registration with Firebase Authentication
109
+
110
+ // try {
111
+ // final userCredential = await _firebaseAuth.createUserWithEmailAndPassword(
112
+ // email: emailAddressStr,
113
+ // password: passwordStr,
114
+ // );
115
+
116
+ // // Uncomment to save the token if needed
117
+
118
+ // // // User registered and logged
119
+ // // final user = userCredential.user;
120
+
121
+ // // // Get ID token (JWT)
122
+ // // final idToken = await user?.getIdToken();
123
+
124
+ // // if (idToken != null) {
125
+ // // await _storageRepository.saveToken(idToken);
126
+ // // }
127
+
128
+ // // Create UserProfile after successful registration
129
+ // // The UserProfile is automatically synced with the authentication User
130
+ // // Both use the same ID (authId) for correlation
131
+ // // if (user != null) {
132
+ // // final authUser = _firebaseUserMapper.toDomain(user);
133
+ // // if (authUser != null) {
134
+ // // final userProfile = authUser.toUserProfile();
135
+ // // await _userProfileRepository.createOrUpdate(userProfile);
136
+ // // }
137
+ // // }
138
+
139
+ // return right(unit);
140
+ // } on PlatformException catch (e) {
141
+ // if (e.code == __STRING_12__) {
142
+ // return left(const AuthFailure.emailAlreadyInUse());
143
+ // } else {
144
+ // return left(const AuthFailure.serverError());
145
+ // }
146
+ // }
147
+
148
+ return right(unit);
149
+ }
150
+
151
+ @override
152
+ Future<Either<AuthFailure, Unit>> signInWithEmailAndPassword({
153
+ required EmailAddress emailAddress,
154
+ required Password password,
155
+ }) async {
156
+ final String emailAddressStr = emailAddress.value.getOrElse(() => throw 'INVALID EMAIL');
157
+ final String passwordStr = password.value.getOrElse(() => throw 'INVALID PASSWORD');
158
+
159
+ // Uncomment the following code to implement Email/Password Sign-In with Firebase Authentication
160
+
161
+ // try {
162
+ // final userCredential = await _firebaseAuth.signInWithEmailAndPassword(
163
+ // email: emailAddressStr,
164
+ // password: passwordStr,
165
+ // );
166
+
167
+ // // Uncomment to save the token if needed
168
+
169
+ // // // User logged
170
+ // // final user = userCredential.user;
171
+
172
+ // // // Get ID token (JWT)
173
+ // // final idToken = await user?.getIdToken();
174
+
175
+ // // if (idToken != null) {
176
+ // // await _storageRepository.saveToken(idToken);
177
+ // // }
178
+
179
+ // // Sync UserProfile after successful sign-in
180
+ // // The UserProfile is automatically synced with the authentication User
181
+ // // Both use the same ID (authId) for correlation
182
+ // // if (user != null) {
183
+ // // final authUser = _firebaseUserMapper.toDomain(user);
184
+ // // if (authUser != null) {
185
+ // // final authId = authUser.id.getOrCrash();
186
+ // // final userProfileResult = await _userProfileRepository.getByAuthId(authId);
187
+ // //
188
+ // // final UserProfile userProfile = userProfileResult.fold(
189
+ // // (failure) {
190
+ // // // UserProfile doesn't exist, create it from auth User
191
+ // // return authUser.toUserProfile();
192
+ // // },
193
+ // // (existingProfile) {
194
+ // // // UserProfile exists, update it with latest auth data if needed
195
+ // // if (existingProfile.name.getOrCrash() != authUser.name.getOrCrash() ||
196
+ // // existingProfile.emailAddress.getOrCrash() != authUser.emailAddress.getOrCrash()) {
197
+ // // return authUser.toUserProfileWithTimestamps(
198
+ // // createdAt: existingProfile.createdAt,
199
+ // // updatedAt: DateTime.now(),
200
+ // // avatarUrl: existingProfile.avatarUrl,
201
+ // // );
202
+ // // }
203
+ // // return existingProfile;
204
+ // // },
205
+ // // );
206
+ // //
207
+ // // // Save/update UserProfile
208
+ // // await _userProfileRepository.createOrUpdate(userProfile);
209
+ // // }
210
+ // // }
211
+
212
+ // return right(unit);
213
+ // } on PlatformException catch (e) {
214
+ // if (e.code == __STRING_15__ || e.code == __STRING_16__) {
215
+ // return left(const AuthFailure.invalidEmailAndPasswordCombination());
216
+ // }
217
+ // return left(const AuthFailure.serverError());
218
+ // }
219
+
220
+ return right(unit);
221
+ }
222
+
223
+ @override
224
+ Future<Either<AuthFailure, Unit>> signInWithGoogle() async {
225
+ // TODO: Implement Google Sign-In
226
+ // Example implementation with Firebase:
227
+ // try {
228
+ // final GoogleSignIn googleSignIn = GoogleSignIn();
229
+ // final GoogleSignInAccount? googleUser = await googleSignIn.signIn();
230
+ // if (googleUser == null) {
231
+ // return left(const AuthFailure.cancelledByUser());
232
+ // }
233
+ // final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
234
+ // final credential = GoogleAuthProvider.credential(
235
+ // accessToken: googleAuth.accessToken,
236
+ // idToken: googleAuth.idToken,
237
+ // );
238
+ // await _firebaseAuth.signInWithCredential(credential);
239
+ // // Sync UserProfile similar to signInWithEmailAndPassword
240
+ // return right(unit);
241
+ // } catch (e) {
242
+ // return left(const AuthFailure.serverError());
243
+ // }
244
+ return right(unit);
245
+ }
246
+
247
+ @override
248
+ Future<Either<AuthFailure, Unit>> signInWithApple() async {
249
+ // TODO: Implement Apple Sign-In
250
+ // Example implementation with Firebase:
251
+ // try {
252
+ // final appleProvider = AppleAuthProvider();
253
+ // final userCredential = await _firebaseAuth.signInWithProvider(appleProvider);
254
+ // // Sync UserProfile similar to signInWithEmailAndPassword
255
+ // return right(unit);
256
+ // } catch (e) {
257
+ // if (e.code == 'sign_in_canceled') {
258
+ // return left(const AuthFailure.cancelledByUser());
259
+ // }
260
+ // return left(const AuthFailure.serverError());
261
+ // }
262
+ return right(unit);
263
+ }
264
+
265
+ @override
266
+ Future<Either<AuthFailure, Unit>> registerWithGoogle() async {
267
+ // TODO: Implement Google Registration
268
+ // For Google Sign-In, registration and sign-in are typically the same flow
269
+ // You can reuse the signInWithGoogle implementation or create a unified method
270
+ return await signInWithGoogle();
271
+ }
272
+
273
+ @override
274
+ Future<Either<AuthFailure, Unit>> registerWithApple() async {
275
+ // TODO: Implement Apple Registration
276
+ // For Apple Sign-In, registration and sign-in are typically the same flow
277
+ // You can reuse the signInWithApple implementation or create a unified method
278
+ return await signInWithApple();
279
+ }
280
+
281
+ @override
282
+ Future<void> signOut() async {
283
+ await Future.wait(<Future<void>>[
284
+ // _firebaseAuth.signOut(), // Uncomment the following line to use Firebase Authentication
285
+ _storageRepository.removeToken(),
286
+ ]);
287
+ }
288
+ }
@@ -0,0 +1,106 @@
1
+ import 'dart:convert';
2
+
3
+ import 'package:dartz/dartz.dart';
4
+ import 'package:flutter/services.dart';
5
+ import 'package:injectable/injectable.dart';
6
+ import 'package:shared_preferences/shared_preferences.dart';
7
+ import 'package:[[project_name]]/core/model/value_objects.dart';
8
+ import 'package:[[project_name]]/domain/auth/model/auth_failure.dart';
9
+ import 'package:[[project_name]]/domain/auth/model/i_auth_facade.dart';
10
+ import 'package:[[project_name]]/domain/auth/model/user.dart' as auth_user;
11
+ import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
12
+ import 'package:[[project_name]]/storage/storage_repository.dart';
13
+
14
+ /// Mock authentication — any sign-in succeeds using [assets/mock/auth_user.json].
15
+ @lazySingleton
16
+ class MockAuthFacade implements IAuthFacade {
17
+ static const String _assetPath = 'assets/mock/auth_user.json';
18
+ static const String _userPrefsKey = 'mock_auth_user_json';
19
+
20
+ final StorageRepository _storageRepository;
21
+
22
+ MockAuthFacade(this._storageRepository);
23
+
24
+ Future<auth_user.User> _seedUser() async {
25
+ final String raw = await rootBundle.loadString(_assetPath);
26
+ final Map<String, dynamic> json = jsonDecode(raw) as Map<String, dynamic>;
27
+ return auth_user.User(
28
+ id: UniqueId.fromUniqueString(json['id'] as String),
29
+ name: StringSingleLine(json['name'] as String),
30
+ emailAddress: EmailAddress(json['emailAddress'] as String),
31
+ );
32
+ }
33
+
34
+ Future<void> _persistUser(auth_user.User user) async {
35
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
36
+ await prefs.setString(
37
+ _userPrefsKey,
38
+ jsonEncode(<String, String>{
39
+ 'id': user.id.getOrCrash(),
40
+ 'name': user.name.getOrCrash(),
41
+ 'emailAddress': user.emailAddress.getOrCrash(),
42
+ }),
43
+ );
44
+ await _storageRepository.saveToken(
45
+ const TokenData(token: 'mock-access-token', refreshToken: 'mock-refresh-token'),
46
+ );
47
+ }
48
+
49
+ Future<Option<auth_user.User>> _readUser() async {
50
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
51
+ final String? raw = prefs.getString(_userPrefsKey);
52
+ if (raw == null || raw.isEmpty) {
53
+ return none();
54
+ }
55
+ final Map<String, dynamic> json = jsonDecode(raw) as Map<String, dynamic>;
56
+ return some(
57
+ auth_user.User(
58
+ id: UniqueId.fromUniqueString(json['id'] as String),
59
+ name: StringSingleLine(json['name'] as String),
60
+ emailAddress: EmailAddress(json['emailAddress'] as String),
61
+ ),
62
+ );
63
+ }
64
+
65
+ Future<Either<AuthFailure, Unit>> _signIn() async {
66
+ final auth_user.User user = await _seedUser();
67
+ await _persistUser(user);
68
+ return right(unit);
69
+ }
70
+
71
+ @override
72
+ Future<Option<auth_user.User>> getSignedInUser() => _readUser();
73
+
74
+ @override
75
+ Future<Either<AuthFailure, Unit>> registerWithEmailAndPassword({
76
+ required EmailAddress emailAddress,
77
+ required Password password,
78
+ }) =>
79
+ _signIn();
80
+
81
+ @override
82
+ Future<Either<AuthFailure, Unit>> signInWithEmailAndPassword({
83
+ required EmailAddress emailAddress,
84
+ required Password password,
85
+ }) =>
86
+ _signIn();
87
+
88
+ @override
89
+ Future<Either<AuthFailure, Unit>> signInWithGoogle() => _signIn();
90
+
91
+ @override
92
+ Future<Either<AuthFailure, Unit>> signInWithApple() => _signIn();
93
+
94
+ @override
95
+ Future<Either<AuthFailure, Unit>> registerWithGoogle() => _signIn();
96
+
97
+ @override
98
+ Future<Either<AuthFailure, Unit>> registerWithApple() => _signIn();
99
+
100
+ @override
101
+ Future<void> signOut() async {
102
+ final SharedPreferences prefs = await SharedPreferences.getInstance();
103
+ await prefs.remove(_userPrefsKey);
104
+ await _storageRepository.removeToken();
105
+ }
106
+ }
@@ -0,0 +1,11 @@
1
+ import 'package:freezed_annotation/freezed_annotation.dart';
2
+
3
+ part 'auth_failure.freezed.dart';
4
+
5
+ @freezed
6
+ abstract class AuthFailure with _$AuthFailure {
7
+ const factory AuthFailure.cancelledByUser() = CancelledByUser;
8
+ const factory AuthFailure.serverError() = ServerError;
9
+ const factory AuthFailure.emailAlreadyInUse() = EmailAlreadyInUse;
10
+ const factory AuthFailure.invalidEmailAndPasswordCombination() = InvalidEmailAndPasswordCombination;
11
+ }
@@ -0,0 +1,24 @@
1
+ import 'package:dartz/dartz.dart';
2
+ import 'package:[[project_name]]/domain/auth/model/auth_failure.dart';
3
+ import 'package:[[project_name]]/domain/auth/model/user.dart';
4
+ import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
5
+
6
+ abstract class IAuthFacade {
7
+ /// Get the currently signed-in user
8
+ /// Returns User mapped from UserProfile (database entity)
9
+ /// The UserProfile is automatically synced with the authentication provider
10
+ Future<Option<User>> getSignedInUser();
11
+ Future<Either<AuthFailure, Unit>> registerWithEmailAndPassword({
12
+ required EmailAddress emailAddress,
13
+ required Password password,
14
+ });
15
+ Future<Either<AuthFailure, Unit>> signInWithEmailAndPassword({
16
+ required EmailAddress emailAddress,
17
+ required Password password,
18
+ });
19
+ Future<Either<AuthFailure, Unit>> signInWithGoogle();
20
+ Future<Either<AuthFailure, Unit>> signInWithApple();
21
+ Future<Either<AuthFailure, Unit>> registerWithGoogle();
22
+ Future<Either<AuthFailure, Unit>> registerWithApple();
23
+ Future<void> signOut();
24
+ }
@@ -0,0 +1,34 @@
1
+ /*
2
+ * User entity (authentication)
3
+ *
4
+ * NOTE: This entity is used by AuthBloc and the authentication flow.
5
+ * It is mapped from UserProfile (database entity) which contains additional fields.
6
+ *
7
+ * Flow: Auth → UserProfile sync → User (mapped)
8
+ * Both entities use the same ID (authId) for correlation.
9
+ */
10
+
11
+ import 'package:dartz/dartz.dart';
12
+ import 'package:flutter/foundation.dart';
13
+ import 'package:freezed_annotation/freezed_annotation.dart';
14
+ import 'package:[[project_name]]/domain/auth/model/value_objects.dart';
15
+ import 'package:[[project_name]]/core/model/entity.dart';
16
+ import 'package:[[project_name]]/core/model/failures.dart';
17
+ import 'package:[[project_name]]/core/model/value_objects.dart';
18
+
19
+ part 'user.freezed.dart';
20
+
21
+ @freezed
22
+ abstract class User with _$User implements IEntity {
23
+ const factory User({
24
+ required UniqueId id,
25
+ required StringSingleLine name,
26
+ required EmailAddress emailAddress,
27
+ }) = _User;
28
+ }
29
+
30
+ extension UserX on User {
31
+ Option<ValueFailure<dynamic>> get failureOption {
32
+ return name.failureOrUnit.andThen(emailAddress.failureOrUnit).fold((ValueFailure<dynamic> l) => some(l), (Unit r) => none());
33
+ }
34
+ }
@@ -0,0 +1,54 @@
1
+ import 'package:dartz/dartz.dart';
2
+ import 'package:[[project_name]]/core/model/failures.dart';
3
+ import 'package:[[project_name]]/core/model/value_objects.dart';
4
+ import 'package:[[project_name]]/core/model/value_validators.dart';
5
+
6
+ class EmailAddress extends ValueObject<String> {
7
+ @override
8
+ final Either<ValueFailure<String>, String> value;
9
+
10
+ factory EmailAddress(String input) {
11
+ return EmailAddress._(
12
+ validateEmailAddress(input),
13
+ );
14
+ }
15
+
16
+ const EmailAddress._(this.value);
17
+ }
18
+
19
+ class Password extends ValueObject<String> {
20
+ @override
21
+ final Either<ValueFailure<String>, String> value;
22
+
23
+ factory Password(String input) {
24
+ return Password._(
25
+ validatePassword(input),
26
+ );
27
+ }
28
+
29
+ String get rawValue {
30
+ return value.fold((ValueFailure<String> l) => l.failedValue, (String r) => r);
31
+ }
32
+
33
+ const Password._(this.value);
34
+ }
35
+
36
+ class ConfirmPassword extends ValueObject<String> {
37
+ @override
38
+ final Either<ValueFailure<String>, String> value;
39
+
40
+ factory ConfirmPassword(
41
+ String password,
42
+ String confirmPassword,
43
+ ) {
44
+ return ConfirmPassword._(
45
+ validateConfirmPassword(password, confirmPassword),
46
+ );
47
+ }
48
+
49
+ String get rawValue {
50
+ return value.fold((ValueFailure<String> l) => l.failedValue, (String r) => r);
51
+ }
52
+
53
+ const ConfirmPassword._(this.value);
54
+ }
@@ -0,0 +1,24 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter_bloc/flutter_bloc.dart';
3
+ import 'package:[[project_name]]/features/auth/sign_in_form/application/sign_in_form_bloc.dart';
4
+ import 'package:[[project_name]]/injection.dart';
5
+ import 'package:[[project_name]]/features/auth/sign_in_form/presentation/sign_in_form.dart';
6
+ import 'package:[[project_name]]/domain/auth/model/i_auth_facade.dart';
7
+
8
+ class LoginPage extends StatelessWidget {
9
+ static const String routeName = '/login';
10
+ static const String logoHeroTag = 'auth-logo';
11
+
12
+ const LoginPage({super.key});
13
+
14
+ @override
15
+ Widget build(BuildContext context) {
16
+ return Scaffold(
17
+ backgroundColor: Colors.transparent,
18
+ body: BlocProvider<SignInFormBloc>(
19
+ create: (BuildContext context) => SignInFormBloc(getIt<IAuthFacade>()),
20
+ child: const SignInForm(),
21
+ ),
22
+ );
23
+ }
24
+ }