@sun-asterisk/sunlint 1.3.48 → 1.3.49

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 (152) hide show
  1. package/core/file-targeting-service.js +148 -15
  2. package/core/init-command.js +118 -70
  3. package/core/project-detector.js +517 -0
  4. package/core/tui-select.js +245 -0
  5. package/engines/arch-detect/rules/layered/l001-presentation-layer.js +7 -15
  6. package/engines/arch-detect/rules/layered/l002-business-layer.js +7 -15
  7. package/engines/arch-detect/rules/layered/l003-data-layer.js +7 -15
  8. package/engines/arch-detect/rules/layered/l004-model-layer.js +7 -15
  9. package/engines/arch-detect/rules/layered/l005-layer-separation.js +22 -2
  10. package/engines/arch-detect/rules/layered/l006-dependency-direction.js +8 -5
  11. package/engines/arch-detect/rules/modular/m005-no-deep-imports.js +67 -29
  12. package/engines/arch-detect/rules/presentation/pr001-view-layer.js +16 -9
  13. package/engines/arch-detect/rules/presentation/pr006-router-layer.js +33 -8
  14. package/engines/arch-detect/rules/presentation/pr007-interactor-layer.js +35 -6
  15. package/engines/arch-detect/rules/project-scanner/ps003-framework-detection.js +56 -10
  16. package/package.json +1 -1
  17. package/skill-assets/sunlint-code-quality/rules/dart/C006-verb-noun-functions.md +45 -0
  18. package/skill-assets/sunlint-code-quality/rules/dart/C013-no-dead-code.md +53 -0
  19. package/skill-assets/sunlint-code-quality/rules/dart/C014-dependency-injection.md +92 -0
  20. package/skill-assets/sunlint-code-quality/rules/dart/C017-no-constructor-logic.md +62 -0
  21. package/skill-assets/sunlint-code-quality/rules/dart/C018-generic-errors.md +57 -0
  22. package/skill-assets/sunlint-code-quality/rules/dart/C019-error-log-level.md +50 -0
  23. package/skill-assets/sunlint-code-quality/rules/dart/C020-no-unused-imports.md +46 -0
  24. package/skill-assets/sunlint-code-quality/rules/dart/C022-no-unused-variables.md +50 -0
  25. package/skill-assets/sunlint-code-quality/rules/dart/C023-no-duplicate-names.md +56 -0
  26. package/skill-assets/sunlint-code-quality/rules/dart/C024-centralize-constants.md +75 -0
  27. package/skill-assets/sunlint-code-quality/rules/dart/C029-catch-log-root-cause.md +53 -0
  28. package/skill-assets/sunlint-code-quality/rules/dart/C030-custom-error-classes.md +86 -0
  29. package/skill-assets/sunlint-code-quality/rules/dart/C033-separate-data-access.md +90 -0
  30. package/skill-assets/sunlint-code-quality/rules/dart/C035-error-context-logging.md +62 -0
  31. package/skill-assets/sunlint-code-quality/rules/dart/C041-no-hardcoded-secrets.md +75 -0
  32. package/skill-assets/sunlint-code-quality/rules/dart/C042-boolean-naming.md +73 -0
  33. package/skill-assets/sunlint-code-quality/rules/dart/C052-widget-parsing.md +84 -0
  34. package/skill-assets/sunlint-code-quality/rules/dart/C060-superclass-logic.md +91 -0
  35. package/skill-assets/sunlint-code-quality/rules/dart/C067-no-hardcoded-config.md +108 -0
  36. package/skill-assets/sunlint-code-quality/rules/go-gin/AGENTS.md +149 -0
  37. package/skill-assets/sunlint-code-quality/rules/go-gin/GN001-abort-after-response.md +75 -0
  38. package/skill-assets/sunlint-code-quality/rules/go-gin/GN002-request-context.md +64 -0
  39. package/skill-assets/sunlint-code-quality/rules/go-gin/GN003-bind-error-handling.md +70 -0
  40. package/skill-assets/sunlint-code-quality/rules/go-gin/GN004-dependency-injection.md +78 -0
  41. package/skill-assets/sunlint-code-quality/rules/go-gin/GN005-route-groups-middleware.md +71 -0
  42. package/skill-assets/sunlint-code-quality/rules/go-gin/GN006-http-status-codes.md +91 -0
  43. package/skill-assets/sunlint-code-quality/rules/go-gin/GN007-release-mode.md +64 -0
  44. package/skill-assets/sunlint-code-quality/rules/go-gin/GN008-struct-validation-tags.md +90 -0
  45. package/skill-assets/sunlint-code-quality/rules/go-gin/GN009-recovery-middleware.md +68 -0
  46. package/skill-assets/sunlint-code-quality/rules/go-gin/GN010-context-scope.md +68 -0
  47. package/skill-assets/sunlint-code-quality/rules/go-gin/GN011-middleware-concerns.md +92 -0
  48. package/skill-assets/sunlint-code-quality/rules/go-gin/GN012-no-log-sensitive.md +84 -0
  49. package/skill-assets/sunlint-code-quality/rules/java/J001-try-with-resources.md +86 -0
  50. package/skill-assets/sunlint-code-quality/rules/java/J002-equals-and-hashcode.md +88 -0
  51. package/skill-assets/sunlint-code-quality/rules/java/J003-string-comparison.md +72 -0
  52. package/skill-assets/sunlint-code-quality/rules/java/J004-use-java-time.md +91 -0
  53. package/skill-assets/sunlint-code-quality/rules/java/J005-no-print-stack-trace.md +80 -0
  54. package/skill-assets/sunlint-code-quality/rules/java/J006-no-system-println.md +89 -0
  55. package/skill-assets/sunlint-code-quality/rules/java/J007-proper-logger.md +91 -0
  56. package/skill-assets/sunlint-code-quality/rules/java/J008-thread-safe-singleton.md +119 -0
  57. package/skill-assets/sunlint-code-quality/rules/java/J009-utility-class-constructor.md +82 -0
  58. package/skill-assets/sunlint-code-quality/rules/java/J010-preserve-stack-trace.md +119 -0
  59. package/skill-assets/sunlint-code-quality/rules/java/J011-null-safe-compare.md +88 -0
  60. package/skill-assets/sunlint-code-quality/rules/java/J012-use-enum-collections.md +104 -0
  61. package/skill-assets/sunlint-code-quality/rules/java/J013-return-empty-not-null.md +102 -0
  62. package/skill-assets/sunlint-code-quality/rules/java/J014-hardcoded-crypto-key.md +108 -0
  63. package/skill-assets/sunlint-code-quality/rules/java/J015-optional-instead-of-null.md +109 -0
  64. package/skill-assets/sunlint-code-quality/rules/php-laravel/AGENTS.md +124 -0
  65. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV001-form-request-validation.md +64 -0
  66. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV002-eager-load-no-n-plus-1.md +58 -0
  67. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV003-config-not-env.md +54 -0
  68. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV004-fillable-mass-assignment.md +51 -0
  69. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV005-policies-gates-authorization.md +71 -0
  70. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV006-queue-heavy-tasks.md +68 -0
  71. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV007-hash-passwords.md +51 -0
  72. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV008-route-model-binding.md +67 -0
  73. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV009-api-resources.md +72 -0
  74. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV010-chunk-large-datasets.md +58 -0
  75. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV011-db-transactions.md +73 -0
  76. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV012-service-layer.md +78 -0
  77. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV013-testing-factories.md +75 -0
  78. package/skill-assets/sunlint-code-quality/rules/php-laravel/LV014-service-container.md +61 -0
  79. package/skill-assets/sunlint-code-quality/rules/python/P001-mutable-default-argument.md +55 -0
  80. package/skill-assets/sunlint-code-quality/rules/python/P002-specify-file-encoding.md +45 -0
  81. package/skill-assets/sunlint-code-quality/rules/python/P003-context-manager-for-resources.md +54 -0
  82. package/skill-assets/sunlint-code-quality/rules/python/P004-no-bare-except.md +65 -0
  83. package/skill-assets/sunlint-code-quality/rules/python/P005-use-isinstance.md +60 -0
  84. package/skill-assets/sunlint-code-quality/rules/python/P006-timezone-aware-datetime.md +58 -0
  85. package/skill-assets/sunlint-code-quality/rules/python/P007-use-pathlib.md +62 -0
  86. package/skill-assets/sunlint-code-quality/rules/python/P008-no-wildcard-import.md +52 -0
  87. package/skill-assets/sunlint-code-quality/rules/python/P009-logging-lazy-format.md +50 -0
  88. package/skill-assets/sunlint-code-quality/rules/python/P010-exception-chaining.md +57 -0
  89. package/skill-assets/sunlint-code-quality/rules/python/P011-subprocess-check.md +59 -0
  90. package/skill-assets/sunlint-code-quality/rules/python/P012-requests-timeout.md +70 -0
  91. package/skill-assets/sunlint-code-quality/rules/python/P013-no-global-statement.md +73 -0
  92. package/skill-assets/sunlint-code-quality/rules/python/P014-no-modify-collection-while-iterating.md +66 -0
  93. package/skill-assets/sunlint-code-quality/rules/python/P015-prefer-fstrings.md +61 -0
  94. package/skill-assets/sunlint-code-quality/rules/ruby-rails/AGENTS.md +121 -0
  95. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR001-strong-parameters.md +55 -0
  96. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR002-eager-load-includes.md +51 -0
  97. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR003-service-objects.md +99 -0
  98. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR004-active-job-background.md +67 -0
  99. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR005-pagination.md +53 -0
  100. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR006-find-each-batches.md +53 -0
  101. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR007-http-status-codes.md +76 -0
  102. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR008-before-action-auth.md +77 -0
  103. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR009-rails-credentials.md +61 -0
  104. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR010-scopes.md +57 -0
  105. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR011-counter-cache.md +59 -0
  106. package/skill-assets/sunlint-code-quality/rules/ruby-rails/RR012-render-json-status.md +42 -0
  107. package/skill-assets/sunlint-code-quality/rules/swift/C006-verb-noun-functions.md +37 -0
  108. package/skill-assets/sunlint-code-quality/rules/swift/C013-no-dead-code.md +55 -0
  109. package/skill-assets/sunlint-code-quality/rules/swift/C014-dependency-injection.md +69 -0
  110. package/skill-assets/sunlint-code-quality/rules/swift/C017-no-constructor-logic.md +66 -0
  111. package/skill-assets/sunlint-code-quality/rules/swift/C018-generic-errors.md +64 -0
  112. package/skill-assets/sunlint-code-quality/rules/swift/C019-error-log-level.md +64 -0
  113. package/skill-assets/sunlint-code-quality/rules/swift/C020-no-unused-imports.md +47 -0
  114. package/skill-assets/sunlint-code-quality/rules/swift/C022-no-unused-variables.md +46 -0
  115. package/skill-assets/sunlint-code-quality/rules/swift/C023-no-duplicate-names.md +55 -0
  116. package/skill-assets/sunlint-code-quality/rules/swift/C024-centralize-constants.md +68 -0
  117. package/skill-assets/sunlint-code-quality/rules/swift/C029-catch-log-root-cause.md +69 -0
  118. package/skill-assets/sunlint-code-quality/rules/swift/C030-custom-error-classes.md +77 -0
  119. package/skill-assets/sunlint-code-quality/rules/swift/C033-separate-data-access.md +89 -0
  120. package/skill-assets/sunlint-code-quality/rules/swift/C035-error-context-logging.md +66 -0
  121. package/skill-assets/sunlint-code-quality/rules/swift/C041-no-hardcoded-secrets.md +65 -0
  122. package/skill-assets/sunlint-code-quality/rules/swift/C042-boolean-naming.md +60 -0
  123. package/skill-assets/sunlint-code-quality/rules/swift/C052-controller-parsing.md +67 -0
  124. package/skill-assets/sunlint-code-quality/rules/swift/C060-superclass-logic.md +95 -0
  125. package/skill-assets/sunlint-code-quality/rules/swift/C067-no-hardcoded-config.md +80 -0
  126. package/skill-assets/sunlint-code-quality/rules/swift/S003-sql-injection.md +65 -0
  127. package/skill-assets/sunlint-code-quality/rules/swift/S004-no-log-credentials.md +67 -0
  128. package/skill-assets/sunlint-code-quality/rules/swift/S005-server-authorization.md +73 -0
  129. package/skill-assets/sunlint-code-quality/rules/swift/S006-default-credentials.md +76 -0
  130. package/skill-assets/sunlint-code-quality/rules/swift/S007-output-encoding.md +96 -0
  131. package/skill-assets/sunlint-code-quality/rules/swift/S009-approved-crypto.md +86 -0
  132. package/skill-assets/sunlint-code-quality/rules/swift/S010-csprng.md +71 -0
  133. package/skill-assets/sunlint-code-quality/rules/swift/S011-insecure-deserialization.md +74 -0
  134. package/skill-assets/sunlint-code-quality/rules/swift/S012-secrets-management.md +81 -0
  135. package/skill-assets/sunlint-code-quality/rules/swift/S013-tls-connections.md +67 -0
  136. package/skill-assets/sunlint-code-quality/rules/swift/S017-parameterized-queries.md +86 -0
  137. package/skill-assets/sunlint-code-quality/rules/swift/S019-session-management.md +131 -0
  138. package/skill-assets/sunlint-code-quality/rules/swift/S020-kvc-injection.md +91 -0
  139. package/skill-assets/sunlint-code-quality/rules/swift/S025-input-validation.md +125 -0
  140. package/skill-assets/sunlint-code-quality/rules/swift/S029-brute-force-protection.md +120 -0
  141. package/skill-assets/sunlint-code-quality/rules/swift/S036-path-traversal.md +102 -0
  142. package/skill-assets/sunlint-code-quality/rules/swift/S039-tls-certificate-validation.md +109 -0
  143. package/skill-assets/sunlint-code-quality/rules/swift/S041-logout-invalidation.md +103 -0
  144. package/skill-assets/sunlint-code-quality/rules/swift/S043-password-hashing.md +116 -0
  145. package/skill-assets/sunlint-code-quality/rules/swift/S044-critical-changes-reauth.md +145 -0
  146. package/skill-assets/sunlint-code-quality/rules/swift/S045-debug-info-exposure.md +116 -0
  147. package/skill-assets/sunlint-code-quality/rules/swift/S046-unvalidated-redirect.md +140 -0
  148. package/skill-assets/sunlint-code-quality/rules/swift/S051-token-expiry.md +134 -0
  149. package/skill-assets/sunlint-code-quality/rules/swift/S053-jwt-validation.md +139 -0
  150. package/skill-assets/sunlint-code-quality/rules/swift/S059-background-snapshot-protection.md +113 -0
  151. package/skill-assets/sunlint-code-quality/rules/swift/S060-data-protection-api.md +106 -0
  152. package/skill-assets/sunlint-code-quality/rules/swift/S061-jailbreak-detection.md +132 -0
@@ -0,0 +1,50 @@
1
+ ---
2
+ title: Do Not Leave Unused Variables
3
+ impact: LOW
4
+ impactDescription: reduces code noise and potential bugs
5
+ tags: variables, cleanup, quality
6
+ ---
7
+
8
+ ## Do Not Leave Unused Variables
9
+
10
+ Unused variables suggest incomplete refactoring or bugs.
11
+
12
+ **Incorrect (unused variables):**
13
+
14
+ ```dart
15
+ void processOrder(Order order) {
16
+ final user = order.user; // Never used
17
+ final total = order.total; // Never used
18
+ final items = order.items;
19
+
20
+ return items.map((i) => i.name).toList();
21
+ }
22
+ ```
23
+
24
+ **Correct (only needed variables):**
25
+
26
+ ```dart
27
+ void processOrder(Order order) {
28
+ return order.items.map((i) => i.name).toList();
29
+ }
30
+
31
+ // If destructuring/pattern matching, prefix with _ for intentionally ignored
32
+ void handleEvent(Map<String, dynamic> event) {
33
+ final type = event['type'] as String;
34
+ final _ = event['payload']; // Intentionally ignored
35
+ print('Event: $type');
36
+ }
37
+ ```
38
+
39
+ **Enable via analysis_options.yaml:**
40
+
41
+ ```yaml
42
+ # analysis_options.yaml
43
+ linter:
44
+ rules:
45
+ - unused_local_variable
46
+ - unused_element
47
+ - avoid_unused_constructor_parameters
48
+ ```
49
+
50
+ **Tools:** dart analyze, `unused_local_variable` lint rule
@@ -0,0 +1,56 @@
1
+ ---
2
+ title: No Duplicate Variable Names In Scope
3
+ impact: HIGH
4
+ impactDescription: prevents shadowing bugs
5
+ tags: variables, shadowing, scope, quality
6
+ ---
7
+
8
+ ## No Duplicate Variable Names In Scope
9
+
10
+ Variable shadowing causes subtle bugs where inner variables hide outer ones.
11
+
12
+ **Incorrect (shadowed variables):**
13
+
14
+ ```dart
15
+ final user = getCurrentUser();
16
+
17
+ void processOrder(Order order) {
18
+ final user = order.user; // Shadows outer user!
19
+
20
+ // Which user is this?
21
+ print(user.name);
22
+ }
23
+
24
+ // Same name in nested scope
25
+ for (final item in items) {
26
+ final item = transform(item); // Shadows loop variable!
27
+ }
28
+ ```
29
+
30
+ **Correct (unique names):**
31
+
32
+ ```dart
33
+ final currentUser = getCurrentUser();
34
+
35
+ void processOrder(Order order) {
36
+ final orderUser = order.user; // Clear distinction
37
+
38
+ print(orderUser.name);
39
+ }
40
+
41
+ // Different names in nested scope
42
+ for (final item in items) {
43
+ final transformedItem = transform(item);
44
+ }
45
+ ```
46
+
47
+ **Enable via analysis_options.yaml:**
48
+
49
+ ```yaml
50
+ # analysis_options.yaml
51
+ linter:
52
+ rules:
53
+ - no_leading_underscores_for_local_identifiers
54
+ ```
55
+
56
+ **Tools:** dart analyze, Code Review
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: Centralize Constants
3
+ impact: HIGH
4
+ impactDescription: makes values easy to find and update
5
+ tags: constants, magic-numbers, configuration, quality
6
+ ---
7
+
8
+ ## Centralize Constants
9
+
10
+ Magic numbers scattered throughout code are hard to find and change.
11
+
12
+ **Incorrect (magic numbers):**
13
+
14
+ ```dart
15
+ if (password.length < 8) { }
16
+ if (retryCount > 3) { }
17
+ if (status == 1) { }
18
+ await Future.delayed(const Duration(milliseconds: 300000));
19
+ if (user.role == 'admin') { }
20
+ ```
21
+
22
+ **Correct (centralized constants):**
23
+
24
+ ```dart
25
+ // lib/constants/app_constants.dart
26
+ class AppConstants {
27
+ AppConstants._(); // Prevent instantiation
28
+
29
+ static const int passwordMinLength = 8;
30
+ static const int maxRetryAttempts = 3;
31
+ static const Duration sessionTimeout = Duration(minutes: 5);
32
+ }
33
+
34
+ class OrderStatus {
35
+ OrderStatus._();
36
+
37
+ static const int pending = 1;
38
+ static const int approved = 2;
39
+ static const int shipped = 3;
40
+ }
41
+
42
+ class UserRole {
43
+ UserRole._();
44
+
45
+ static const String admin = 'admin';
46
+ static const String user = 'user';
47
+ static const String guest = 'guest';
48
+ }
49
+
50
+ // Usage
51
+ if (password.length < AppConstants.passwordMinLength) { }
52
+ if (retryCount > AppConstants.maxRetryAttempts) { }
53
+ if (status == OrderStatus.pending) { }
54
+ await Future.delayed(AppConstants.sessionTimeout);
55
+ if (user.role == UserRole.admin) { }
56
+ ```
57
+
58
+ **Alternative: use enums for type-safe values:**
59
+
60
+ ```dart
61
+ enum OrderStatus { pending, approved, shipped }
62
+ enum UserRole { admin, user, guest }
63
+
64
+ // Usage
65
+ if (order.status == OrderStatus.pending) { }
66
+ if (user.role == UserRole.admin) { }
67
+ ```
68
+
69
+ **Benefits:**
70
+ - Single source of truth
71
+ - Self-documenting
72
+ - Easy to update
73
+ - Type safety with enums
74
+
75
+ **Tools:** dart analyze, Code Review
@@ -0,0 +1,53 @@
1
+ ---
2
+ title: All Catch Blocks Must Log Root Cause
3
+ impact: HIGH
4
+ impactDescription: enables debugging and incident response
5
+ tags: error-handling, logging, debugging, observability, quality
6
+ ---
7
+
8
+ ## All Catch Blocks Must Log Root Cause
9
+
10
+ Silent failures make debugging impossible. Without proper logging, you cannot trace issues in production.
11
+
12
+ **Incorrect (silent or minimal logging):**
13
+
14
+ ```dart
15
+ try {
16
+ await processPayment(order);
17
+ } catch (e) {
18
+ // Empty catch - silent failure!
19
+ }
20
+
21
+ try {
22
+ await saveUser(user);
23
+ } catch (e) {
24
+ return null; // No logging, no context
25
+ }
26
+ ```
27
+
28
+ **Correct (comprehensive error logging):**
29
+
30
+ ```dart
31
+ try {
32
+ await processPayment(order);
33
+ } catch (e, st) {
34
+ logger.e(
35
+ 'Payment processing failed',
36
+ error: e,
37
+ stackTrace: st,
38
+ );
39
+ logger.e('Context: orderId=${order.id}, userId=${order.userId}, amount=${order.amount}');
40
+ throw PaymentFailedException(
41
+ 'Payment could not be processed',
42
+ cause: e,
43
+ );
44
+ }
45
+ ```
46
+
47
+ **Log context should include:**
48
+ - Error object and stack trace (`error:`, `stackTrace:`)
49
+ - Relevant entity IDs (order, user, etc.)
50
+ - Input that caused the error
51
+ - Timing information
52
+
53
+ **Tools:** logger package, Code Review
@@ -0,0 +1,86 @@
1
+ ---
2
+ title: Use Custom Exception Classes
3
+ impact: HIGH
4
+ impactDescription: enables proper error categorization and handling
5
+ tags: error-handling, custom-errors, exceptions, patterns, quality
6
+ ---
7
+
8
+ ## Use Custom Exception Classes
9
+
10
+ Custom exception classes enable proper error handling, categorization, and monitoring. They provide clear contracts for callers.
11
+
12
+ **Incorrect (generic exceptions):**
13
+
14
+ ```dart
15
+ throw Exception('User not found');
16
+ throw Exception('Invalid input');
17
+ throw Exception('Network error');
18
+ ```
19
+
20
+ **Correct (custom exception hierarchy):**
21
+
22
+ ```dart
23
+ // Base application exception
24
+ class AppException implements Exception {
25
+ const AppException(this.message, {this.code, this.context});
26
+
27
+ final String message;
28
+ final String? code;
29
+ final Map<String, dynamic>? context;
30
+
31
+ @override
32
+ String toString() => 'AppException($code): $message';
33
+ }
34
+
35
+ // Specific exceptions
36
+ class UserNotFoundException extends AppException {
37
+ UserNotFoundException(String userId)
38
+ : super(
39
+ 'User $userId not found',
40
+ code: 'USER_NOT_FOUND',
41
+ context: {'userId': userId},
42
+ );
43
+ }
44
+
45
+ class ValidationException extends AppException {
46
+ ValidationException({required String field, required String message})
47
+ : super(
48
+ message,
49
+ code: 'VALIDATION_ERROR',
50
+ context: {'field': field},
51
+ );
52
+ }
53
+
54
+ class NetworkException extends AppException {
55
+ NetworkException({required int statusCode, required String endpoint})
56
+ : super(
57
+ 'Network request failed with status $statusCode',
58
+ code: 'NETWORK_ERROR',
59
+ context: {'statusCode': statusCode, 'endpoint': endpoint},
60
+ );
61
+ }
62
+
63
+ // Usage
64
+ throw UserNotFoundException(userId);
65
+ throw ValidationException(field: 'email', message: 'Invalid email format');
66
+ throw NetworkException(statusCode: response.statusCode, endpoint: '/users');
67
+
68
+ // Handling
69
+ try {
70
+ await fetchUser(id);
71
+ } on UserNotFoundException catch (e) {
72
+ showNotFoundScreen();
73
+ } on NetworkException catch (e) {
74
+ showNetworkError();
75
+ } on AppException catch (e) {
76
+ showGenericError(e.message);
77
+ }
78
+ ```
79
+
80
+ **Benefits:**
81
+ - Type-safe error handling with `on`
82
+ - Structured error context
83
+ - Consistent error format
84
+ - Clear error hierarchy
85
+
86
+ **Tools:** Code Review, dart_analyzer
@@ -0,0 +1,90 @@
1
+ ---
2
+ title: Separate Processing And Data Access
3
+ impact: HIGH
4
+ impactDescription: enables testable business logic
5
+ tags: separation, repository, service, architecture, quality
6
+ ---
7
+
8
+ ## Separate Processing And Data Access
9
+
10
+ Mixing business logic with database/API access creates tight coupling and makes testing require real databases or network.
11
+
12
+ **Incorrect (mixed concerns):**
13
+
14
+ ```dart
15
+ class OrderService {
16
+ final database = DatabaseHelper.instance;
17
+
18
+ Future<double> calculateDiscount(String userId) async {
19
+ // Business logic mixed with data access
20
+ final userMap = await database.query(
21
+ 'users',
22
+ where: 'id = ?',
23
+ whereArgs: [userId],
24
+ );
25
+ final orderMaps = await database.query(
26
+ 'orders',
27
+ where: 'user_id = ?',
28
+ whereArgs: [userId],
29
+ );
30
+
31
+ double discount = 0;
32
+ if (orderMaps.length > 10) discount += 5;
33
+ if (userMap.first['is_premium'] == 1) discount += 10;
34
+
35
+ return discount;
36
+ }
37
+ }
38
+ ```
39
+
40
+ **Correct (separated layers):**
41
+
42
+ ```dart
43
+ // Repository - data access only
44
+ abstract class IUserRepository {
45
+ Future<User?> findById(String userId);
46
+ }
47
+
48
+ abstract class IOrderRepository {
49
+ Future<List<Order>> findByUserId(String userId);
50
+ }
51
+
52
+ class SqlUserRepository implements IUserRepository {
53
+ SqlUserRepository(this._db);
54
+ final DatabaseHelper _db;
55
+
56
+ @override
57
+ Future<User?> findById(String userId) async {
58
+ final maps = await _db.query('users', where: 'id = ?', whereArgs: [userId]);
59
+ return maps.isEmpty ? null : User.fromMap(maps.first);
60
+ }
61
+ }
62
+
63
+ // Service - business logic only
64
+ class DiscountService {
65
+ DiscountService({
66
+ required IUserRepository userRepo,
67
+ required IOrderRepository orderRepo,
68
+ }) : _userRepo = userRepo,
69
+ _orderRepo = orderRepo;
70
+
71
+ final IUserRepository _userRepo;
72
+ final IOrderRepository _orderRepo;
73
+
74
+ Future<double> calculateDiscount(String userId) async {
75
+ final user = await _userRepo.findById(userId);
76
+ final orders = await _orderRepo.findByUserId(userId);
77
+
78
+ return _computeDiscount(user, orders);
79
+ }
80
+
81
+ double _computeDiscount(User? user, List<Order> orders) {
82
+ double discount = 0;
83
+ if (orders.length > 10) discount += 5;
84
+ if (user?.isPremium == true) discount += 10;
85
+ return discount;
86
+ }
87
+ }
88
+ ```
89
+
90
+ **Tools:** Code Review, Architecture Review
@@ -0,0 +1,62 @@
1
+ ---
2
+ title: Log All Relevant Context On Errors
3
+ impact: HIGH
4
+ impactDescription: enables quick debugging and incident response
5
+ tags: error-handling, logging, context, debugging, quality
6
+ ---
7
+
8
+ ## Log All Relevant Context On Errors
9
+
10
+ Context-rich logs enable quick debugging. Without proper context, finding root causes is like finding a needle in a haystack.
11
+
12
+ **Incorrect (minimal context):**
13
+
14
+ ```dart
15
+ logger.e('Error occurred');
16
+ logger.e(e.toString());
17
+ ```
18
+
19
+ **Correct (comprehensive context):**
20
+
21
+ ```dart
22
+ final stopwatch = Stopwatch()..start();
23
+
24
+ try {
25
+ await processOrder(order);
26
+ } catch (e, st) {
27
+ logger.e(
28
+ 'Failed to process order - '
29
+ 'orderId=${order.id}, userId=${user.id}, '
30
+ 'itemCount=${order.items.length}, total=${order.total}, '
31
+ 'processingTimeMs=${stopwatch.elapsedMilliseconds}',
32
+ error: e,
33
+ stackTrace: st,
34
+ );
35
+ rethrow;
36
+ }
37
+ ```
38
+
39
+ **Essential context to include:**
40
+ - Error object and stack trace (`error:`, `stackTrace:`)
41
+ - Entity identifiers (orderId, userId, etc.)
42
+ - Input that caused the issue (item count, amounts)
43
+ - Timing information (`elapsedMilliseconds`)
44
+ - Device/session context where applicable (Flutter)
45
+
46
+ **Flutter-specific context:**
47
+
48
+ ```dart
49
+ try {
50
+ await submitForm(formData);
51
+ } catch (e, st) {
52
+ logger.e(
53
+ 'Form submission failed - '
54
+ 'screen=${ModalRoute.of(context)?.settings.name}, '
55
+ 'userId=${authProvider.userId}',
56
+ error: e,
57
+ stackTrace: st,
58
+ );
59
+ }
60
+ ```
61
+
62
+ **Tools:** logger package, Code Review
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: No Hardcoded Secrets
3
+ impact: HIGH
4
+ impactDescription: prevents credential leakage from source code
5
+ tags: security, secrets, credentials, api-keys, quality
6
+ ---
7
+
8
+ ## No Hardcoded Secrets
9
+
10
+ Secrets hardcoded in source code will be exposed in version control, app binaries, and decompilation.
11
+
12
+ **Incorrect (hardcoded secrets):**
13
+
14
+ ```dart
15
+ // BAD: Hardcoded API key in source code
16
+ const apiKey = 'sk_live_abc123xyz456';
17
+ const dbPassword = 'MySecretP@ss!';
18
+ const jwtSecret = 'super-secret-jwt-key';
19
+ const stripeKey = 'pk_live_51Abc...';
20
+ ```
21
+
22
+ **Correct (externalized secrets - Flutter):**
23
+
24
+ ```dart
25
+ // Option 1: dart-define at build time (recommended for CI/CD)
26
+ // Run: flutter build apk --dart-define=API_KEY=sk_live_xxx
27
+ const apiKey = String.fromEnvironment('API_KEY');
28
+ const apiUrl = String.fromEnvironment('API_URL', defaultValue: 'https://api.dev.example.com');
29
+
30
+ // Option 2: flutter_secure_storage for runtime secrets
31
+ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
32
+
33
+ class SecretRepository {
34
+ final _storage = const FlutterSecureStorage();
35
+
36
+ Future<String?> getApiKey() => _storage.read(key: 'api_key');
37
+ Future<void> saveApiKey(String key) => _storage.write(key: 'api_key', value: key);
38
+ }
39
+
40
+ // Option 3: Remote config (Firebase Remote Config)
41
+ final remoteConfig = FirebaseRemoteConfig.instance;
42
+ final featureFlag = remoteConfig.getBool('new_feature_enabled');
43
+ ```
44
+
45
+ **For .env files (development only):**
46
+
47
+ ```dart
48
+ // pubspec.yaml - add envied package
49
+ // dev_dependencies:
50
+ // envied_generator: ^0.5.0
51
+
52
+ // lib/config/env.dart
53
+ import 'package:envied/envied.dart';
54
+ part 'env.g.dart';
55
+
56
+ @Envied(path: '.env')
57
+ abstract class Env {
58
+ @EnviedField(varName: 'API_KEY', obfuscate: true)
59
+ static final String apiKey = _Env.apiKey;
60
+ }
61
+ ```
62
+
63
+ **Never commit:**
64
+
65
+ ```gitignore
66
+ # .gitignore
67
+ .env
68
+ .env.*
69
+ *.keystore
70
+ *.jks
71
+ google-services.json # Contains API keys
72
+ GoogleService-Info.plist
73
+ ```
74
+
75
+ **Tools:** GitLeaks, git-secrets, flutter_secure_storage, envied
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: Boolean Names Is/Has/Should
3
+ impact: HIGH
4
+ impactDescription: makes conditions instantly readable
5
+ tags: naming, booleans, readability, quality
6
+ ---
7
+
8
+ ## Boolean Names Is/Has/Should
9
+
10
+ Boolean variable prefixes make conditions instantly readable.
11
+
12
+ **Incorrect (unclear boolean variable names):**
13
+
14
+ ```dart
15
+ final active = user.status == 'active';
16
+ final admin = checkAdminRole(user);
17
+ final items = cart.isNotEmpty;
18
+ final update = needsRefresh();
19
+ ```
20
+
21
+ **Correct (boolean prefixes for variables):**
22
+
23
+ ```dart
24
+ final isActive = user.status == 'active';
25
+ final isAdmin = checkAdminRole(user);
26
+ final hasItems = cart.isNotEmpty;
27
+ final shouldUpdate = needsRefresh();
28
+ final canEdit = hasPermission(user, 'edit');
29
+ final willExpire = expirationDate.isBefore(tomorrow);
30
+ ```
31
+
32
+ **Boolean prefixes:**
33
+
34
+ | Prefix | Use For |
35
+ |--------|---------|
36
+ | `is` | State (isActive, isEnabled, isLoading) |
37
+ | `has` | Ownership (hasPermission, hasError, hasItems) |
38
+ | `should` | Decision (shouldUpdate, shouldRetry) |
39
+ | `can` | Capability (canEdit, canDelete, canSubmit) |
40
+ | `will` | Future (willExpire, willRetry) |
41
+
42
+ **In Flutter widgets:**
43
+
44
+ ```dart
45
+ class OrderCard extends StatelessWidget {
46
+ const OrderCard({
47
+ super.key,
48
+ required this.order,
49
+ required this.isSelected,
50
+ required this.canCancel,
51
+ required this.onTap,
52
+ });
53
+
54
+ final Order order;
55
+ final bool isSelected;
56
+ final bool canCancel;
57
+ final VoidCallback onTap;
58
+
59
+ @override
60
+ Widget build(BuildContext context) {
61
+ return Card(
62
+ color: isSelected ? Colors.blue[100] : Colors.white,
63
+ child: ListTile(
64
+ title: Text(order.id),
65
+ trailing: canCancel ? CancelButton(order: order) : null,
66
+ onTap: onTap,
67
+ ),
68
+ );
69
+ }
70
+ }
71
+ ```
72
+
73
+ **Tools:** Code Review, dart_analyzer
@@ -0,0 +1,84 @@
1
+ ---
2
+ title: Separate Parsing From Widgets And Controllers
3
+ impact: HIGH
4
+ impactDescription: keeps widgets/controllers thin and focused
5
+ tags: widget, controller, parsing, transformation, patterns, quality
6
+ ---
7
+
8
+ ## Separate Parsing From Widgets And Controllers
9
+
10
+ Widgets and controllers should be thin — only handling UI/flow concerns. Data transformation logic should be extracted into mappers or presenters.
11
+
12
+ **Incorrect (transformation in widget/controller):**
13
+
14
+ ```dart
15
+ class UserProfilePage extends StatelessWidget {
16
+ const UserProfilePage({super.key, required this.user});
17
+ final User user;
18
+
19
+ @override
20
+ Widget build(BuildContext context) {
21
+ // Transformation logic inside widget
22
+ final fullName = '${user.firstName} ${user.lastName}';
23
+ final email = user.email.toLowerCase();
24
+ final createdAt = DateFormat('yyyy-MM-dd').format(user.createdAt);
25
+ final age = DateTime.now().difference(user.birthDate).inDays ~/ 365;
26
+ final avatarUrl = 'https://cdn.example.com/avatars/${user.id}.jpg';
27
+
28
+ return Column(
29
+ children: [
30
+ Text(fullName),
31
+ Text(email),
32
+ Text('Joined: $createdAt'),
33
+ Text('Age: $age'),
34
+ Image.network(avatarUrl),
35
+ ],
36
+ );
37
+ }
38
+ }
39
+ ```
40
+
41
+ **Correct (separate mapper/presenter):**
42
+
43
+ ```dart
44
+ // Presenter / ViewModel
45
+ class UserProfilePresenter {
46
+ UserProfilePresenter(this._user);
47
+ final User _user;
48
+
49
+ String get fullName => '${_user.firstName} ${_user.lastName}';
50
+ String get email => _user.email.toLowerCase();
51
+ String get joinedDate => DateFormat('yyyy-MM-dd').format(_user.createdAt);
52
+ int get age => DateTime.now().difference(_user.birthDate).inDays ~/ 365;
53
+ String get avatarUrl => 'https://cdn.example.com/avatars/${_user.id}.jpg';
54
+ }
55
+
56
+ // Thin widget
57
+ class UserProfilePage extends StatelessWidget {
58
+ const UserProfilePage({super.key, required this.user});
59
+ final User user;
60
+
61
+ @override
62
+ Widget build(BuildContext context) {
63
+ final presenter = UserProfilePresenter(user);
64
+
65
+ return Column(
66
+ children: [
67
+ Text(presenter.fullName),
68
+ Text(presenter.email),
69
+ Text('Joined: ${presenter.joinedDate}'),
70
+ Text('Age: ${presenter.age}'),
71
+ Image.network(presenter.avatarUrl),
72
+ ],
73
+ );
74
+ }
75
+ }
76
+ ```
77
+
78
+ **Benefits:**
79
+ - Reusable transformation logic
80
+ - Testable presenters/mappers
81
+ - Clean, readable widgets
82
+ - Consistent display format
83
+
84
+ **Tools:** Code Review, Architecture Review