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,95 @@
1
+ /*
2
+ * Component list template with full CRUD UI
3
+ * Generates a reusable component with:
4
+ * - BLoC integration for state management
5
+ * - List view showing all items
6
+ * - Add, edit, delete action buttons
7
+ * - Loading and error state handling
8
+ *
9
+ * This component expects the BLoC to be provided by a parent widget.
10
+ */
11
+
12
+ import 'package:caravaggio_ui/caravaggio_ui.dart';
13
+ import 'package:flutter/material.dart' hide ErrorWidget;
14
+ import 'package:flutter_bloc/flutter_bloc.dart';
15
+ {% if not empty_component %}
16
+ import 'package:[[project_name]]/[[domain_import_prefix]]/model/[[domain_model_name]].dart';
17
+ import 'package:[[project_name]]/[[domain_import_prefix]]/model/[[domain_model_name]]_failure.dart';
18
+ {% endif %}
19
+ import 'package:[[project_name]]/[[component_import_prefix]]/application/[[component_name]]_bloc.dart';
20
+ import 'package:[[project_name]]/widgets/common/error_widget.dart';
21
+ import 'package:[[project_name]]/widgets/common/loading_widget.dart';
22
+ import 'package:[[project_name]]/widgets/common/unknown_state_widget.dart';
23
+ {% if not empty_component %}
24
+ import 'package:[[project_name]]/core/errors/error_localizer.dart';
25
+ {% endif %}
26
+
27
+ class [[component_pascal]]Component extends StatelessWidget {
28
+ const [[component_pascal]]Component({super.key});
29
+
30
+ @override
31
+ Widget build(BuildContext context) {
32
+ return BlocBuilder<[[component_pascal]]Bloc, [[component_pascal]]State>(
33
+ builder: (BuildContext context, [[component_pascal]]State state) {
34
+ return switch (state) {
35
+ Initial() => const Center(child: Text('Welcome')),
36
+ Loading() => const LoadingWidget(),
37
+ {% if empty_component %}
38
+ Loaded() => const Center(child: Text('Carica completata')),
39
+ {% else %}
40
+ Loaded(
41
+ items: final List<[[domain_model_pascal]]> items,
42
+ isReloading: final bool isReloading,
43
+ ) =>
44
+ Column(
45
+ children: <Widget>[
46
+ if (isReloading) CLinearProgressIndicator.primary(),
47
+ Expanded(
48
+ child: ListView.builder(
49
+ itemCount: items.length,
50
+ itemBuilder: (BuildContext context, int index) {
51
+ final [[domain_model_pascal]] item = items[index];
52
+ return CTile.simple(
53
+ title: item.toString(),
54
+ trailing: Row(
55
+ mainAxisSize: MainAxisSize.min,
56
+ children: <Widget>[
57
+ IconButton(
58
+ icon: const Icon(Icons.edit),
59
+ onPressed: () {
60
+ // Implement: Navigate to edit form
61
+ },
62
+ ),
63
+ IconButton(
64
+ icon: const Icon(Icons.delete),
65
+ onPressed: () {
66
+ context.read<[[component_pascal]]Bloc>().add(
67
+ [[component_pascal]]Event.deleteRequested(item.id.getOrCrash()),
68
+ );
69
+ },
70
+ ),
71
+ ],
72
+ ),
73
+ );
74
+ },
75
+ ),
76
+ ),
77
+ ],
78
+ ),
79
+ {% endif %}
80
+ {% if empty_component %}
81
+ Error(:final message) => ErrorWidget(message: 'Errore: $message'),
82
+ {% else %}
83
+ Error(
84
+ failure: final [[failure_class]] failure,
85
+ ) =>
86
+ ErrorWidget(
87
+ message: ErrorLocalizer.localize[[failure_class]](context, failure),
88
+ ),
89
+ {% endif %}
90
+ _ => const UnknownStateWidget(),
91
+ };
92
+ },
93
+ );
94
+ }
95
+ }
@@ -0,0 +1,9 @@
1
+ part of '[[feature_name]]_bloc.dart';
2
+
3
+ @freezed
4
+ abstract class [[feature_name.capitalize()]]State with _$[[feature_name.capitalize()]]State {
5
+ const factory [[feature_name.capitalize()]]State.initial() = Initial;
6
+ const factory [[feature_name.capitalize()]]State.loading() = Loading;
7
+ const factory [[feature_name.capitalize()]]State.loaded([[feature_name.capitalize()]] items) = Loaded;
8
+ const factory [[feature_name.capitalize()]]State.error(String message) = Error;
9
+ }
@@ -0,0 +1,113 @@
1
+ /*
2
+ * Reusable component widget template
3
+ * Generates a StatelessWidget with BLoC integration for:
4
+ * - Reusable UI components across the app
5
+ * - State management through BLoC pattern
6
+ * - Clean separation of concerns
7
+ *
8
+ * Default handling for all BLoC states (Initial, Loading, Loaded, Error).
9
+ * Replace placeholder copy and layout with your real UI.
10
+ */
11
+
12
+ import 'package:caravaggio_ui/caravaggio_ui.dart';
13
+ import 'package:flutter/material.dart' hide ErrorWidget;
14
+ import 'package:flutter_bloc/flutter_bloc.dart';
15
+ [[bloc_import]]
16
+ import 'package:[[project_name]]/widgets/common/error_widget.dart';
17
+ import 'package:[[project_name]]/widgets/common/loading_widget.dart';
18
+ import 'package:[[project_name]]/widgets/common/unknown_state_widget.dart';
19
+ {% if failure_class %}
20
+ import 'package:[[project_name]]/core/errors/error_localizer.dart';
21
+ {% endif %}
22
+ {% if failure_class and domain_model_name %}
23
+ import 'package:[[project_name]]/[[domain_import_prefix]]/model/[[domain_model_name]].dart';
24
+ import 'package:[[project_name]]/[[domain_import_prefix]]/model/[[domain_model_name]]_failure.dart';
25
+ {% endif %}
26
+
27
+ class [[pascal_name]]Component extends StatelessWidget {
28
+ const [[pascal_name]]Component({super.key});
29
+
30
+ @override
31
+ Widget build(BuildContext context) {
32
+ return BlocBuilder<[[bloc_name]], [[state_name]]>(
33
+ builder: (BuildContext context, [[state_name]] state) {
34
+ {% if failure_class and domain_model_name %}
35
+ return switch (state) {
36
+ Initial() => const Center(
37
+ child: Text('In attesa del primo caricamento'),
38
+ ),
39
+ Loading() => const LoadingWidget(),
40
+ Loaded(
41
+ item: final [[domain_model_pascal]] item,
42
+ isReloading: final bool isReloading,
43
+ ) =>
44
+ Stack(
45
+ alignment: Alignment.center,
46
+ children: <Widget>[
47
+ Padding(
48
+ padding: const EdgeInsets.all(16),
49
+ child: Center(child: Text(item.toString())),
50
+ ),
51
+ if (isReloading)
52
+ Positioned(
53
+ top: 0,
54
+ left: 0,
55
+ right: 0,
56
+ child: CLinearProgressIndicator.primary(),
57
+ ),
58
+ ],
59
+ ),
60
+ Error(
61
+ failure: final [[failure_class]] failure,
62
+ ) =>
63
+ ErrorWidget(
64
+ message: ErrorLocalizer.localize[[failure_class]](context, failure),
65
+ ),
66
+ _ => const UnknownStateWidget(),
67
+ };
68
+ {% elif failure_class %}
69
+ return switch (state) {
70
+ Initial() => const Center(
71
+ child: Text('In attesa del primo caricamento'),
72
+ ),
73
+ Loading() => const LoadingWidget(),
74
+ Loaded(:final item, :final isReloading) => Stack(
75
+ alignment: Alignment.center,
76
+ children: <Widget>[
77
+ Padding(
78
+ padding: const EdgeInsets.all(16),
79
+ child: Center(child: Text(item.toString())),
80
+ ),
81
+ if (isReloading)
82
+ Positioned(
83
+ top: 0,
84
+ left: 0,
85
+ right: 0,
86
+ child: CLinearProgressIndicator.primary(),
87
+ ),
88
+ ],
89
+ ),
90
+ Error(:final failure) => ErrorWidget(
91
+ message: ErrorLocalizer.localize[[failure_class]](context, failure),
92
+ ),
93
+ _ => const UnknownStateWidget(),
94
+ };
95
+ {% else %}
96
+ return switch (state) {
97
+ Initial() => const Center(
98
+ child: Text('In attesa del primo caricamento'),
99
+ ),
100
+ Loading() => const LoadingWidget(),
101
+ Loaded() => const Center(
102
+ child: Text('Contenuto caricato (sostituisci con la tua UI)'),
103
+ ),
104
+ Error(:final message) => ErrorWidget(
105
+ message: 'Errore: $message',
106
+ ),
107
+ _ => const UnknownStateWidget(),
108
+ };
109
+ {% endif %}
110
+ },
111
+ );
112
+ }
113
+ }
@@ -0,0 +1,32 @@
1
+ import 'package:bloc/bloc.dart';
2
+ import 'package:dartz/dartz.dart';
3
+
4
+ /// Base BLoC per form con pattern comuni di submit.
5
+ ///
6
+ /// Estende [Bloc] e fornisce helper methods specifici per gestire
7
+ /// il submit di form con validazione e gestione errori.
8
+ ///
9
+ /// **Pattern utilizzato**: Template Method Pattern
10
+ ///
11
+ /// **Type Parameters**:
12
+ /// - [Event]: Tipo degli eventi del BLoC
13
+ /// - [State]: Tipo dello stato del BLoC
14
+ /// - [Failure]: Tipo dei failure (da dartz Either)
15
+ /// - [Success]: Tipo del valore di successo
16
+ abstract class BaseFormBloc<Event, State, Failure, Success> extends Bloc<Event, State> {
17
+ BaseFormBloc(super.initialState);
18
+
19
+ /// Helper per gestire submit con Either<Failure, Success> direttamente nello stato.
20
+ /// Utile quando lo stato contiene Option<Either<Failure, Success>>.
21
+ Future<void> handleFormSubmitWithEither<SuccessType>(
22
+ Emitter<State> emit,
23
+ Future<Either<Failure, SuccessType>> operation,
24
+ State Function() getSubmittingState,
25
+ State Function(Either<Failure, SuccessType> result) getResultState,
26
+ ) async {
27
+ emit(getSubmittingState());
28
+
29
+ final Either<Failure, SuccessType> result = await operation;
30
+ emit(getResultState(result));
31
+ }
32
+ }
@@ -0,0 +1,61 @@
1
+ /*
2
+ * ErrorLocalizer - Centralized error message handler
3
+ *
4
+ * This is the single point where all user-facing error messages are defined.
5
+ * Each domain failure type has a dedicated localize method with
6
+ * domain-customized messages.
7
+ *
8
+ * TO USE LOCALIZATION (l10n):
9
+ * Import your AppLocalizations and replace the hardcoded strings, e.g.:
10
+ * final l10n = AppLocalizations.of(context)!;
11
+ * return switch (failure) {
12
+ * Unexpected() => l10n.errorUnknown,
13
+ * NotFound() => l10n.errorModelNotFound,
14
+ * ...
15
+ * };
16
+ */
17
+
18
+ import 'package:flutter/material.dart';
19
+ {% if has_login -%}
20
+ import 'package:[[project_name]]/domain/auth/model/auth_failure.dart' as auth_failure;
21
+ {% endif -%}
22
+ {% for df in domain_failures -%}
23
+ import 'package:[[df.import_path]]' as [[df.alias]];
24
+ {% endfor %}
25
+ class ErrorLocalizer {
26
+ {%- if has_login %}
27
+
28
+ /// Map AuthFailure to user-friendly error message
29
+ static String localizeAuthFailure(BuildContext context, auth_failure.AuthFailure failure) {
30
+ return switch (failure) {
31
+ auth_failure.CancelledByUser() => 'Cancelled',
32
+ auth_failure.ServerError() => 'Server error',
33
+ auth_failure.EmailAlreadyInUse() => 'Email already in use',
34
+ auth_failure.InvalidEmailAndPasswordCombination() => 'Invalid email and password combination',
35
+ _ => failure.toString(),
36
+ };
37
+ }
38
+ {% endif -%}
39
+ {%- if domain_failures %}
40
+ {% for df in domain_failures %}
41
+ /// Map [[df.failure_class]] to user-friendly error message
42
+ static String localize[[df.failure_class]](BuildContext context, [[df.alias]].[[df.failure_class]] failure) {
43
+ return switch (failure) {
44
+ [[df.alias]].Unexpected() => 'An unexpected error occurred',
45
+ [[df.alias]].InsufficientPermission() => 'Insufficient permissions',
46
+ [[df.alias]].UnableToUpdate() => '[[df.class_name]] update failed',
47
+ [[df.alias]].NotFound() => '[[df.class_name]] not found',
48
+ _ => failure.toString(),
49
+ };
50
+ }
51
+ {% endfor -%}
52
+ {%- elif not has_login %}
53
+ /// Maps a failure to a user-friendly error message.
54
+ ///
55
+ /// Domain-specific localize methods are added when new domain failures
56
+ /// are introduced (see integration-wiring subagent or docs/architecture/).
57
+ static String localizeFailure(BuildContext context, Object failure) {
58
+ return failure.toString();
59
+ }
60
+ {% endif -%}
61
+ }
@@ -0,0 +1,21 @@
1
+ /// Base mapper that provides default implementations for common methods.
2
+ /// Reduces boilerplate by eliminating duplication of toDomainList and toDtoList.
3
+ abstract class BaseMapper<TDomain, TDto> {
4
+ /// Converts DTO to Domain Entity - must be implemented in each mapper
5
+ TDomain toDomain(TDto dto);
6
+
7
+ /// Converts Domain Entity to DTO - must be implemented in each mapper
8
+ TDto toDto(TDomain domain);
9
+
10
+ /// Converts a list of DTOs to a list of Domain Entities.
11
+ /// Default implementation delegates to [toDomain].
12
+ List<TDomain> toDomainList(List<TDto> dtos) {
13
+ return dtos.map((TDto dto) => toDomain(dto)).toList();
14
+ }
15
+
16
+ /// Converts a list of Domain Entities to a list of DTOs.
17
+ /// Default implementation delegates to [toDto].
18
+ List<TDto> toDtoList(List<TDomain> domains) {
19
+ return domains.map((TDomain domain) => toDto(domain)).toList();
20
+ }
21
+ }
@@ -0,0 +1,78 @@
1
+ import 'package:dartz/dartz.dart';
2
+ import 'package:[[project_name]]/core/infrastructure/repository_error_handler.dart';
3
+
4
+ /// Mixin that provides common helper methods for repositories.
5
+ ///
6
+ /// Eliminates duplicated code across repositories by implementing
7
+ /// common patterns for API calls with centralized error handling.
8
+ ///
9
+ /// **Type Parameter**:
10
+ /// - [TFailure]: The failure type specific to the repository
11
+ mixin BaseRepositoryMixin<TFailure> {
12
+ /// Helper for simple calls (service -> mapper -> domain).
13
+ ///
14
+ /// Handles:
15
+ /// - Service call execution
16
+ /// - DTO -> Domain mapping
17
+ /// - Error handling with conversion to [TFailure]
18
+ Future<Either<TFailure, TDomain>> handleSimpleCall<TDto, TDomain>({
19
+ required Future<TDto> Function() serviceCall,
20
+ required TDomain Function(TDto) mapToDomain,
21
+ required TFailure Function() onUnexpected,
22
+ required TFailure Function() onNotFound,
23
+ required TFailure Function() onInsufficientPermission,
24
+ TFailure Function()? onBadRequest,
25
+ }) {
26
+ return RepositoryErrorHandler.handleCall<TDomain, TFailure>(
27
+ call: () async {
28
+ final TDto dto = await serviceCall();
29
+ return mapToDomain(dto);
30
+ },
31
+ onUnexpected: onUnexpected,
32
+ onNotFound: onNotFound,
33
+ onInsufficientPermission: onInsufficientPermission,
34
+ onBadRequest: onBadRequest,
35
+ );
36
+ }
37
+
38
+ /// Helper for calls that return lists.
39
+ Future<Either<TFailure, List<TDomain>>> handleListCall<TDto, TDomain>({
40
+ required Future<List<TDto>> Function() serviceCall,
41
+ required List<TDomain> Function(List<TDto>) mapToDomainList,
42
+ required TFailure Function() onUnexpected,
43
+ required TFailure Function() onNotFound,
44
+ required TFailure Function() onInsufficientPermission,
45
+ }) {
46
+ return RepositoryErrorHandler.handleCall<List<TDomain>, TFailure>(
47
+ call: () async {
48
+ final List<TDto> dtos = await serviceCall();
49
+ return mapToDomainList(dtos);
50
+ },
51
+ onUnexpected: onUnexpected,
52
+ onNotFound: onNotFound,
53
+ onInsufficientPermission: onInsufficientPermission,
54
+ );
55
+ }
56
+
57
+ /// Helper for calls that return Unit (delete, update without response).
58
+ Future<Either<TFailure, Unit>> handleUnitCall({
59
+ required Future<void> Function() serviceCall,
60
+ required TFailure Function() onUnexpected,
61
+ required TFailure Function() onNotFound,
62
+ required TFailure Function() onInsufficientPermission,
63
+ TFailure Function()? onBadRequest,
64
+ TFailure Function()? onUnableToDelete,
65
+ TFailure Function()? onUnableToUpdate,
66
+ }) {
67
+ return RepositoryErrorHandler.handleCall<Unit, TFailure>(
68
+ call: () async {
69
+ await serviceCall();
70
+ return unit;
71
+ },
72
+ onUnexpected: onUnexpected,
73
+ onNotFound: onNotFound,
74
+ onInsufficientPermission: onInsufficientPermission,
75
+ onBadRequest: onBadRequest ?? onUnableToDelete ?? onUnableToUpdate,
76
+ );
77
+ }
78
+ }
@@ -0,0 +1,11 @@
1
+ // import __STRING_0__;
2
+ import 'package:injectable/injectable.dart';
3
+ // import __STRING_2__;
4
+
5
+ @module
6
+ abstract class FirebaseInjectableModule {
7
+ // @lazySingleton
8
+ // FirebaseAuth get firebaseAuth => FirebaseAuth.instance;
9
+ // @lazySingleton
10
+ // Firestore get firestore => Firestore.instance;
11
+ }
@@ -0,0 +1,22 @@
1
+ // import __STRING_0__;
2
+ // import __STRING_1__;
3
+ // import __STRING_2__;
4
+ // import __STRING_3__;
5
+
6
+ // extension FirestoreX on Firestore {
7
+ // /// The user must be already authenticated when calling this method.
8
+ // /// Otherwise, throws [NotAuthenticatedError].
9
+ // Future<DocumentReference> userDocument() async {
10
+ // final userOption = await getIt<IAuthFacade>().getSignedInUser();
11
+ // final user = userOption.getOrElse(() => throw NotAuthenticatedError());
12
+ // return Firestore.instance
13
+ // .collection(__STRING_4__)
14
+ // .document(user.id.getOrCrash());
15
+ // }
16
+ // }
17
+
18
+ // extension DocumentReferenceX on DocumentReference {
19
+ // CollectionReference get noteCollection => collection(__STRING_5__);
20
+
21
+ // /// Nested subcollection under a [noteCollection]__STRING_6__todos');
22
+ // }
@@ -0,0 +1,44 @@
1
+ import 'package:dartz/dartz.dart';
2
+ import 'package:dio/dio.dart';
3
+
4
+ /// Centralized error handler for repository operations.
5
+ /// Maps DioException HTTP status codes to typed failure objects.
6
+ class RepositoryErrorHandler {
7
+ /// Executes a repository call and maps errors to [TFailure] automatically.
8
+ static Future<Either<TFailure, T>> handleCall<T, TFailure>({
9
+ required Future<T> Function() call,
10
+ required TFailure Function() onUnexpected,
11
+ required TFailure Function() onNotFound,
12
+ required TFailure Function() onInsufficientPermission,
13
+ TFailure Function()? onBadRequest,
14
+ TFailure Function()? onUnauthorized,
15
+ }) async {
16
+ try {
17
+ final T result = await call();
18
+ return right(result);
19
+ } on DioException catch (e, _) {
20
+ final int? statusCode = e.response?.statusCode;
21
+
22
+ switch (statusCode) {
23
+ case 400:
24
+ if (onBadRequest != null) {
25
+ return left(onBadRequest());
26
+ }
27
+ return left(onUnexpected());
28
+ case 401:
29
+ if (onUnauthorized != null) {
30
+ return left(onUnauthorized());
31
+ }
32
+ return left(onInsufficientPermission());
33
+ case 403:
34
+ return left(onInsufficientPermission());
35
+ case 404:
36
+ return left(onNotFound());
37
+ default:
38
+ return left(onUnexpected());
39
+ }
40
+ } on Object catch (e) {
41
+ return left(onUnexpected());
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,3 @@
1
+ abstract class IValidatable {
2
+ bool isValid();
3
+ }
@@ -0,0 +1,5 @@
1
+ import 'package:[[project_name]]/core/model/value_objects.dart';
2
+
3
+ abstract class IEntity {
4
+ UniqueId get id;
5
+ }
@@ -0,0 +1,15 @@
1
+ import 'package:[[project_name]]/core/model/failures.dart';
2
+
3
+ class NotAuthenticatedError extends Error {}
4
+
5
+ class UnexpectedValueError extends Error {
6
+ final ValueFailure<dynamic> valueFailure;
7
+
8
+ UnexpectedValueError(this.valueFailure);
9
+
10
+ @override
11
+ String toString() {
12
+ const String explanation = 'Encountered a ValueFailure at an unrecoverable point. Terminating.';
13
+ return Error.safeToString('[[explanation]] Failure was: [[valueFailure]]');
14
+ }
15
+ }
@@ -0,0 +1,97 @@
1
+ /*
2
+ * Core failure types for domain validation and errors
3
+ * Defines common failure patterns used throughout the application:
4
+ * - ValueFailure: For value object validation failures
5
+ * - Domain-specific failures for business logic errors
6
+ *
7
+ * This provides a comprehensive error handling system that distinguishes
8
+ * between validation errors and business logic errors
9
+ */
10
+
11
+ import 'package:flutter/material.dart';
12
+ import 'package:freezed_annotation/freezed_annotation.dart';
13
+
14
+ part 'failures.freezed.dart';
15
+
16
+ /* Used if the validation of ValueObjects within the domain fails
17
+ * Example:
18
+ *
19
+ * Note({required Description description})
20
+ *
21
+ * Description extends a ValueObject<String> and has a validator that ensures, for example, that the string is not empty.
22
+ * If validation fails, a ValueFailure<String> is returned with the reason for the failure.
23
+ *
24
+ * NOTE: All domain ValueObjects extend the ValueObject<T> defined in core/value_objects.dart
25
+ * Each domain ValueObject has a specific validator in core/value_validators.dart
26
+ * Validators return an Either<ValueFailure<T>, T> representing the result of the validation.
27
+ * If validation fails, a ValueFailure<T> is returned with the reason for the failure.
28
+ * If validation succeeds, the valid value of type T is returned.
29
+ *
30
+ * The Failures defined in the domain folder (e.g. note_failure.dart) are instead used to represent specific errors
31
+ * that can occur during the execution of operations in the domain (e.g. error while saving a note).
32
+ * These Failures are used to handle errors at the application level and not for data validation.
33
+ */
34
+
35
+ @freezed
36
+ abstract class ValueFailure<T> with _$ValueFailure<T> {
37
+ const factory ValueFailure.exceedingLength({required T failedValue, required int max}) = ExceedingLength<T>;
38
+ const factory ValueFailure.empty({required T failedValue}) = Empty<T>;
39
+ const factory ValueFailure.multiline({required T failedValue}) = Multiline<T>;
40
+ const factory ValueFailure.numberTooLarge({required T failedValue, required num max}) = NumberTooLarge<T>;
41
+ const factory ValueFailure.listTooLong({required T failedValue, required int max}) = ListTooLong<T>;
42
+ const factory ValueFailure.invalidEmail({required T failedValue}) = InvalidEmail<T>;
43
+ const factory ValueFailure.shortPassword({required T failedValue}) = ShortPassword<T>;
44
+ const factory ValueFailure.invalidPhotoUrl({required T failedValue}) = InvalidPhotoUrl<T>;
45
+ const factory ValueFailure.passwordsDoNotMatch({required T failedValue}) = PasswordsDoNotMatch<T>;
46
+ }
47
+
48
+ /*
49
+ * TO USE LOCALIZATION (l10n):
50
+ * Import your AppLocalizations and replace the hardcoded strings, e.g.:
51
+ * final l10n = AppLocalizations.of(context)!;
52
+ * return switch (failure) {
53
+ * ExceedingLength<T>() => l10n.errorMaxLengthExceeded,
54
+ * Empty<T>() => l10n.errorEmpty,
55
+ * Multiline<T>() => l10n.errorMultiline,
56
+ * NumberTooLarge<T>() => l10n.errorNumberTooLarge,
57
+ * ListTooLong<T>() => l10n.errorListTooLong,
58
+ * InvalidEmail<T>() => l10n.errorInvalidEmail,
59
+ * ShortPassword<T>() => l10n.errorShortPassword,
60
+ * InvalidPhotoUrl<T>() => l10n.errorInvalidPhotoUrl,
61
+ * PasswordsDoNotMatch<T>() => l10n.errorPasswordsDoNotMatch,
62
+ * ...
63
+ * _ => 'Error',
64
+ * };
65
+ */
66
+
67
+ extension ValueFailureX<T> on ValueFailure<T> {
68
+ String title(BuildContext context, ValueFailure<T> failure) {
69
+ return switch (failure) {
70
+ ExceedingLength<T>() => 'Max length exceeded',
71
+ Empty<T>() => 'Empty',
72
+ Multiline<T>() => 'Multiline',
73
+ NumberTooLarge<T>() => 'Number too large',
74
+ ListTooLong<T>() => 'List too long',
75
+ InvalidEmail<T>() => 'Invalid email',
76
+ ShortPassword<T>() => 'Short password',
77
+ InvalidPhotoUrl<T>() => 'Invalid photo URL',
78
+ PasswordsDoNotMatch<T>() => 'Passwords do not match',
79
+ _ => 'Error',
80
+ };
81
+ }
82
+
83
+ String message(BuildContext context, ValueFailure<T> failure) {
84
+ return switch (failure) {
85
+ ExceedingLength<T>() => 'The value must be less than ${failure.max}',
86
+ Empty<T>() => 'The value cannot be empty',
87
+ Multiline<T>() => 'The value cannot contain multiple lines',
88
+ NumberTooLarge<T>() => 'The value must be less than ${failure.max}',
89
+ ListTooLong<T>() => 'The value must be less than ${failure.max}',
90
+ InvalidEmail<T>() => 'The value must be a valid email',
91
+ ShortPassword<T>() => 'The value must be at least 6 characters',
92
+ InvalidPhotoUrl<T>() => 'The value must be a valid photo URL',
93
+ PasswordsDoNotMatch<T>() => 'The passwords do not match',
94
+ _ => 'Error',
95
+ };
96
+ }
97
+ }