@umituz/react-native-subscription 2.14.99 → 2.14.101
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.
- package/README.md +211 -394
- package/package.json +3 -3
- package/src/application/README.md +46 -225
- package/src/application/ports/README.md +42 -97
- package/src/domain/README.md +36 -384
- package/src/domain/constants/README.md +0 -56
- package/src/domain/entities/README.md +43 -169
- package/src/domain/entities/SubscriptionStatus.ts +1 -1
- package/src/domain/errors/README.md +33 -287
- package/src/domain/value-objects/README.md +43 -179
- package/src/domains/README.md +50 -238
- package/src/domains/README.md.bak +274 -0
- package/src/domains/config/README.md +93 -383
- package/src/domains/config/domain/README.md +23 -376
- package/src/domains/config/domain/entities/README.md +34 -343
- package/src/domains/paywall/README.md +99 -369
- package/src/domains/paywall/components/README.md +34 -178
- package/src/domains/paywall/entities/README.md +34 -193
- package/src/domains/paywall/hooks/README.md +34 -122
- package/src/domains/wallet/README.md +34 -275
- package/src/domains/wallet/README.md.bak +209 -0
- package/src/domains/wallet/domain/README.md +34 -101
- package/src/domains/wallet/domain/entities/README.md +34 -115
- package/src/domains/wallet/domain/errors/README.md +34 -151
- package/src/domains/wallet/infrastructure/README.md +34 -89
- package/src/domains/wallet/presentation/components/README.md +34 -224
- package/src/domains/wallet/presentation/components/TransactionItem.tsx +1 -1
- package/src/domains/wallet/presentation/hooks/README.md +34 -248
- package/src/infrastructure/README.md +37 -496
- package/src/infrastructure/mappers/README.md +0 -13
- package/src/infrastructure/repositories/README.md +74 -360
- package/src/infrastructure/services/ActivationHandler.ts +1 -1
- package/src/infrastructure/services/README.md +95 -370
- package/src/infrastructure/services/SubscriptionService.ts +1 -1
- package/src/presentation/README.md +123 -408
- package/src/presentation/README.md.bak +172 -0
- package/src/presentation/components/README.md +151 -179
- package/src/presentation/components/README.md.bak +217 -0
- package/src/presentation/components/details/CreditRow.md +65 -310
- package/src/presentation/components/details/DetailRow.md +63 -255
- package/src/presentation/components/details/PremiumDetailsCard.md +65 -238
- package/src/presentation/components/details/PremiumStatusBadge.md +64 -239
- package/src/presentation/components/details/README.md +97 -447
- package/src/presentation/components/feedback/PaywallFeedbackModal.md +63 -287
- package/src/presentation/components/feedback/README.md +97 -445
- package/src/presentation/components/paywall/PaywallModal.md +66 -416
- package/src/presentation/components/paywall/README.md +50 -186
- package/src/presentation/components/sections/README.md +97 -466
- package/src/presentation/components/sections/SubscriptionSection.md +92 -244
- package/src/presentation/hooks/README.md +154 -741
- package/src/presentation/hooks/useAuthAwarePurchase.md +58 -325
- package/src/presentation/hooks/useAuthGate.md +61 -375
- package/src/presentation/hooks/useAuthSubscriptionSync.md +66 -370
- package/src/presentation/hooks/useCreditChecker.md +73 -378
- package/src/presentation/hooks/useCredits.md +74 -313
- package/src/presentation/hooks/useCredits.md.bak +231 -0
- package/src/presentation/hooks/useCreditsGate.md +66 -318
- package/src/presentation/hooks/useDeductCredit.md +0 -76
- package/src/presentation/hooks/useDeductCredit.ts +1 -1
- package/src/presentation/hooks/useDevTestCallbacks.md +63 -394
- package/src/presentation/hooks/useFeatureGate.md +105 -150
- package/src/presentation/hooks/useFeatureGate.md.bak +284 -0
- package/src/presentation/hooks/useInitializeCredits.md +64 -430
- package/src/presentation/hooks/usePaywall.md +61 -306
- package/src/presentation/hooks/usePaywallOperations.md +64 -458
- package/src/presentation/hooks/usePaywallVisibility.md +67 -316
- package/src/presentation/hooks/usePremium.md +84 -226
- package/src/presentation/hooks/usePremiumGate.md +60 -395
- package/src/presentation/hooks/usePremiumWithCredits.md +64 -401
- package/src/presentation/hooks/useSubscription.md +66 -422
- package/src/presentation/hooks/useSubscriptionDetails.md +65 -410
- package/src/presentation/hooks/useSubscriptionGate.md +80 -164
- package/src/presentation/hooks/useSubscriptionSettingsConfig.md +66 -346
- package/src/presentation/hooks/useSubscriptionStatus.md +66 -396
- package/src/presentation/hooks/useUserTier.md +63 -328
- package/src/presentation/hooks/useUserTierWithRepository.md +64 -424
- package/src/presentation/screens/README.md +48 -190
- package/src/presentation/types/README.md +0 -16
- package/src/presentation/utils/README.md +0 -21
- package/src/presentation/utils/subscriptionDateUtils.ts +1 -1
- package/src/revenuecat/README.md +99 -518
- package/src/revenuecat/application/README.md +35 -150
- package/src/revenuecat/application/ports/README.md +34 -162
- package/src/revenuecat/domain/README.md +42 -141
- package/src/revenuecat/domain/constants/README.md +34 -176
- package/src/revenuecat/domain/entities/README.md +34 -374
- package/src/revenuecat/domain/errors/README.md +47 -191
- package/src/revenuecat/domain/types/README.md +34 -366
- package/src/revenuecat/domain/value-objects/README.md +34 -434
- package/src/revenuecat/infrastructure/README.md +34 -43
- package/src/revenuecat/infrastructure/config/README.md +32 -23
- package/src/revenuecat/infrastructure/handlers/README.md +34 -211
- package/src/revenuecat/infrastructure/managers/README.md +34 -42
- package/src/revenuecat/infrastructure/services/README.md +35 -318
- package/src/revenuecat/infrastructure/utils/README.md +34 -375
- package/src/revenuecat/presentation/README.md +34 -176
- package/src/revenuecat/presentation/hooks/README.md +29 -35
- package/src/utils/README.md +38 -525
|
@@ -2,175 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
Core domain entities for subscription management.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
### 1. Self-Validation
|
|
48
|
-
|
|
49
|
-
Entities validate themselves on creation:
|
|
50
|
-
|
|
51
|
-
```typescript
|
|
52
|
-
class SubscriptionStatus {
|
|
53
|
-
private constructor(data: SubscriptionStatusData) {
|
|
54
|
-
this.validate(data);
|
|
55
|
-
// ...
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
private validate(data: SubscriptionStatusData): void {
|
|
59
|
-
if (data.isPremium && !data.isActive) {
|
|
60
|
-
throw new ValidationError('Premium users must be active');
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
### 2. Immutable State
|
|
67
|
-
|
|
68
|
-
Entities cannot be modified after creation:
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
const status = SubscriptionStatus.create({...});
|
|
72
|
-
// status.isActive = false; // Error: Cannot assign to read-only property
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
### 3. Business Rules
|
|
76
|
-
|
|
77
|
-
Business logic is encapsulated in entities:
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
class SubscriptionStatus {
|
|
81
|
-
isExpired(): boolean {
|
|
82
|
-
if (!this.expirationDate) return false;
|
|
83
|
-
return new Date(this.expirationDate) < new Date();
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
requiresRenewal(): boolean {
|
|
87
|
-
return this.isPremium && this.expirationDate && this.willRenew;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
## Creating Entities
|
|
93
|
-
|
|
94
|
-
### Factory Method
|
|
95
|
-
|
|
96
|
-
```typescript
|
|
97
|
-
const status = SubscriptionStatus.create({
|
|
98
|
-
type: 'premium',
|
|
99
|
-
isActive: true,
|
|
100
|
-
isPremium: true,
|
|
101
|
-
expirationDate: null,
|
|
102
|
-
willRenew: false,
|
|
103
|
-
});
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Validation
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
try {
|
|
110
|
-
const status = SubscriptionStatus.create({
|
|
111
|
-
type: 'premium',
|
|
112
|
-
isActive: false, // Invalid!
|
|
113
|
-
isPremium: true,
|
|
114
|
-
});
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error('Validation failed:', error.message);
|
|
117
|
-
}
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
## Domain Services
|
|
121
|
-
|
|
122
|
-
Business operations that don't naturally fit in entities:
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
class SubscriptionDomainService {
|
|
126
|
-
canUpgrade(currentStatus: SubscriptionStatus): boolean {
|
|
127
|
-
return !currentStatus.isPremium;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
calculateDaysUntilExpiry(status: SubscriptionStatus): number | null {
|
|
131
|
-
if (!status.expirationDate) return null;
|
|
132
|
-
const diff = new Date(status.expirationDate).getTime() - Date.now();
|
|
133
|
-
return Math.ceil(diff / (1000 * 60 * 60 * 24));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Best Practices
|
|
139
|
-
|
|
140
|
-
1. **Keep entities pure** - No framework dependencies
|
|
141
|
-
2. **Validate invariants** - Ensure valid state
|
|
142
|
-
3. **Use value objects** - For complex attributes
|
|
143
|
-
4. **Encapsulate logic** - Keep business rules inside entities
|
|
144
|
-
5. **Make immutable** - Prevent direct state modification
|
|
145
|
-
|
|
146
|
-
## Testing
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
describe('SubscriptionStatus', () => {
|
|
150
|
-
it('should create valid premium status', () => {
|
|
151
|
-
const status = SubscriptionStatus.create({
|
|
152
|
-
type: 'premium',
|
|
153
|
-
isActive: true,
|
|
154
|
-
isPremium: true,
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
expect(status.isPremium).toBe(true);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('should reject invalid premium status', () => {
|
|
161
|
-
expect(() => {
|
|
162
|
-
SubscriptionStatus.create({
|
|
163
|
-
type: 'premium',
|
|
164
|
-
isActive: false,
|
|
165
|
-
isPremium: true,
|
|
166
|
-
});
|
|
167
|
-
}).toThrow();
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
## Related
|
|
5
|
+
## Location
|
|
6
|
+
|
|
7
|
+
`src/domain/entities/`
|
|
8
|
+
|
|
9
|
+
## Strategy
|
|
10
|
+
|
|
11
|
+
Domain entities represent the core business concepts and rules of the subscription system. They are framework-agnostic and contain only business logic, ensuring pure domain modeling.
|
|
12
|
+
|
|
13
|
+
## Restrictions
|
|
14
|
+
|
|
15
|
+
### REQUIRED
|
|
16
|
+
|
|
17
|
+
- MUST validate themselves on creation
|
|
18
|
+
- MUST remain immutable after creation
|
|
19
|
+
- MUST encapsulate business logic internally
|
|
20
|
+
- MUST use value objects for complex attributes
|
|
21
|
+
- MUST be framework-agnostic
|
|
22
|
+
|
|
23
|
+
### PROHIBITED
|
|
24
|
+
|
|
25
|
+
- MUST NOT have framework dependencies
|
|
26
|
+
- MUST NOT expose internal state directly
|
|
27
|
+
- MUST NOT allow direct state modification
|
|
28
|
+
- MUST NOT contain infrastructure concerns
|
|
29
|
+
|
|
30
|
+
### CRITICAL
|
|
31
|
+
|
|
32
|
+
- Always validate invariants to ensure valid state
|
|
33
|
+
- Prevent direct state modification through immutability
|
|
34
|
+
- Keep business rules encapsulated within entities
|
|
35
|
+
- Maintain purity - no external dependencies
|
|
36
|
+
|
|
37
|
+
## AI Agent Guidelines
|
|
38
|
+
|
|
39
|
+
When working with domain entities:
|
|
40
|
+
1. Keep entities pure - no framework dependencies
|
|
41
|
+
2. Validate invariants - ensure valid state
|
|
42
|
+
3. Use value objects - for complex attributes
|
|
43
|
+
4. Encapsulate logic - keep business rules inside entities
|
|
44
|
+
5. Make immutable - prevent direct state modification
|
|
45
|
+
|
|
46
|
+
## Related Documentation
|
|
173
47
|
|
|
174
48
|
- [Value Objects](../value-objects/README.md)
|
|
175
49
|
- [Domain Errors](../errors/README.md)
|
|
176
|
-
- [Domain Layer](
|
|
50
|
+
- [Domain Layer](../README.md)
|
|
@@ -2,306 +2,52 @@
|
|
|
2
2
|
|
|
3
3
|
Domain-specific error types for subscription system.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Location
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`src/domain/errors/`
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Strategy
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
Error (JavaScript)
|
|
13
|
-
└── SubscriptionError (Domain base)
|
|
14
|
-
├── SubscriptionValidationError
|
|
15
|
-
├── SubscriptionRepositoryError
|
|
16
|
-
├── SubscriptionOperationError
|
|
17
|
-
└── InsufficientCreditsError
|
|
18
|
-
```
|
|
11
|
+
Domain errors provide typed, contextual error handling for business logic failures. They make error handling explicit and type-safe, enabling precise error management.
|
|
19
12
|
|
|
20
|
-
##
|
|
13
|
+
## Restrictions
|
|
21
14
|
|
|
22
|
-
###
|
|
15
|
+
### REQUIRED
|
|
23
16
|
|
|
24
|
-
|
|
17
|
+
- MUST use specific error types (not generic Error)
|
|
18
|
+
- MUST include contextual information
|
|
19
|
+
- MUST document all error codes
|
|
20
|
+
- MUST handle errors gracefully with user-friendly messages
|
|
21
|
+
- MUST log errors for debugging
|
|
22
|
+
- MUST use type guards for type-safe error handling
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
class SubscriptionError extends Error {
|
|
28
|
-
constructor(
|
|
29
|
-
message: string,
|
|
30
|
-
public readonly code: string,
|
|
31
|
-
public readonly context?: Record<string, any>
|
|
32
|
-
) {
|
|
33
|
-
super(message);
|
|
34
|
-
this.name = 'SubscriptionError';
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
```
|
|
24
|
+
### PROHIBITED
|
|
38
25
|
|
|
39
|
-
|
|
26
|
+
- MUST NOT swallow errors without handling
|
|
27
|
+
- MUST NOT use generic Error class
|
|
28
|
+
- MUST NOT expose sensitive information in error messages
|
|
29
|
+
- MUST NOT ignore error conditions
|
|
40
30
|
|
|
41
|
-
|
|
31
|
+
### CRITICAL
|
|
42
32
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
33
|
+
- Always handle or rethrow errors
|
|
34
|
+
- Include relevant context in error objects
|
|
35
|
+
- Use type guards to enable type-safe error handling
|
|
36
|
+
- Show user-friendly messages while logging technical details
|
|
46
37
|
|
|
47
|
-
|
|
38
|
+
## AI Agent Guidelines
|
|
48
39
|
|
|
49
|
-
|
|
40
|
+
When working with domain errors:
|
|
41
|
+
1. Use specific error types - don't use generic Error
|
|
42
|
+
2. Include context - add relevant data to errors
|
|
43
|
+
3. Document error codes - list all possible errors
|
|
44
|
+
4. Handle gracefully - show user-friendly messages
|
|
45
|
+
5. Log errors - track for debugging
|
|
46
|
+
6. Don't swallow errors - always handle or rethrow
|
|
47
|
+
7. Use type guards - enable type-safe error handling
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
throw new SubscriptionRepositoryError('Database connection failed');
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### InsufficientCreditsError
|
|
56
|
-
|
|
57
|
-
Thrown when user doesn't have enough credits.
|
|
58
|
-
|
|
59
|
-
```typescript
|
|
60
|
-
throw new InsufficientCreditsError(
|
|
61
|
-
'Not enough credits',
|
|
62
|
-
{
|
|
63
|
-
required: 10,
|
|
64
|
-
available: 5,
|
|
65
|
-
featureId: 'export',
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## Usage
|
|
71
|
-
|
|
72
|
-
### Throwing Errors
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
function deductCredits(amount: number) {
|
|
76
|
-
if (amount <= 0) {
|
|
77
|
-
throw new SubscriptionValidationError(
|
|
78
|
-
'Amount must be positive',
|
|
79
|
-
'INVALID_AMOUNT',
|
|
80
|
-
{ amount }
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (currentBalance < amount) {
|
|
85
|
-
throw new InsufficientCreditsError(
|
|
86
|
-
'Insufficient credits',
|
|
87
|
-
{
|
|
88
|
-
required: amount,
|
|
89
|
-
available: currentBalance,
|
|
90
|
-
}
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### Catching Errors
|
|
97
|
-
|
|
98
|
-
```typescript
|
|
99
|
-
try {
|
|
100
|
-
await purchasePackage(packageToPurchase);
|
|
101
|
-
} catch (error) {
|
|
102
|
-
if (error instanceof InsufficientCreditsError) {
|
|
103
|
-
console.log(`Need ${error.context.required} credits`);
|
|
104
|
-
showPaywall();
|
|
105
|
-
} else if (error instanceof SubscriptionValidationError) {
|
|
106
|
-
console.error('Validation error:', error.message);
|
|
107
|
-
} else if (error instanceof SubscriptionError) {
|
|
108
|
-
console.error('Subscription error:', error.code, error.message);
|
|
109
|
-
} else {
|
|
110
|
-
console.error('Unexpected error:', error);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### Type Guards
|
|
116
|
-
|
|
117
|
-
```typescript
|
|
118
|
-
function isSubscriptionError(error: unknown): error is SubscriptionError {
|
|
119
|
-
return error instanceof SubscriptionError;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function isInsufficientCreditsError(error: unknown): error is InsufficientCreditsError {
|
|
123
|
-
return error instanceof InsufficientCreditsError;
|
|
124
|
-
}
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Error Codes
|
|
128
|
-
|
|
129
|
-
| Code | Description |
|
|
130
|
-
|------|-------------|
|
|
131
|
-
| `INVALID_USER_ID` | User ID is invalid or missing |
|
|
132
|
-
| `INVALID_PRODUCT_ID` | Product ID is not recognized |
|
|
133
|
-
| `INVALID_AMOUNT` | Amount is invalid (e.g., negative) |
|
|
134
|
-
| `INSUFFICIENT_CREDITS` | User doesn't have enough credits |
|
|
135
|
-
| `SUBSCRIPTION_EXPIRED` | Subscription has expired |
|
|
136
|
-
| `SUBSCRIPTION_NOT_FOUND` | Subscription doesn't exist |
|
|
137
|
-
| `REPOSITORY_ERROR` | Database/repository error |
|
|
138
|
-
| `NETWORK_ERROR` | Network connectivity issue |
|
|
139
|
-
| `PURCHASE_CANCELLED` | User cancelled purchase |
|
|
140
|
-
| `PURCHASE_FAILED` | Purchase operation failed |
|
|
141
|
-
|
|
142
|
-
## Error Context
|
|
143
|
-
|
|
144
|
-
Errors can include contextual information:
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
interface InsufficientCreditsContext {
|
|
148
|
-
required: number;
|
|
149
|
-
available: number;
|
|
150
|
-
featureId?: string;
|
|
151
|
-
currency?: string;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
throw new InsufficientCreditsError(
|
|
155
|
-
'Insufficient credits',
|
|
156
|
-
{
|
|
157
|
-
required: 10,
|
|
158
|
-
available: 5,
|
|
159
|
-
featureId: 'export',
|
|
160
|
-
currency: 'credits',
|
|
161
|
-
}
|
|
162
|
-
);
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
## Custom Errors
|
|
166
|
-
|
|
167
|
-
Create your own domain errors:
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
class FeatureAccessError extends SubscriptionError {
|
|
171
|
-
constructor(
|
|
172
|
-
featureId: string,
|
|
173
|
-
public readonly requiredTier: UserTier,
|
|
174
|
-
public readonly currentTier: UserTier
|
|
175
|
-
) {
|
|
176
|
-
super(
|
|
177
|
-
`Feature "${featureId}" requires ${requiredTier} tier`,
|
|
178
|
-
'FEATURE_ACCESS_DENIED',
|
|
179
|
-
{ featureId, requiredTier, currentTier }
|
|
180
|
-
);
|
|
181
|
-
this.name = 'FeatureAccessError';
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Usage
|
|
186
|
-
if (!canAccessFeature('advanced_analytics')) {
|
|
187
|
-
throw new FeatureAccessError(
|
|
188
|
-
'advanced_analytics',
|
|
189
|
-
'premium',
|
|
190
|
-
userTier
|
|
191
|
-
);
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
## Error Handling Patterns
|
|
196
|
-
|
|
197
|
-
### 1. Try-Catch with Type Guards
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
try {
|
|
201
|
-
await executeOperation();
|
|
202
|
-
} catch (error) {
|
|
203
|
-
if (error instanceof InsufficientCreditsError) {
|
|
204
|
-
handleInsufficientCredits(error);
|
|
205
|
-
} else if (error instanceof SubscriptionError) {
|
|
206
|
-
handleSubscriptionError(error);
|
|
207
|
-
} else {
|
|
208
|
-
handleUnexpectedError(error);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
### 2. Result Type
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
type Result<T, E extends Error = Error> =
|
|
217
|
-
| { success: true; data: T }
|
|
218
|
-
| { success: false; error: E };
|
|
219
|
-
|
|
220
|
-
async function deductCredits(
|
|
221
|
-
amount: number
|
|
222
|
-
): Promise<Result<number, InsufficientCreditsError>> {
|
|
223
|
-
if (currentBalance < amount) {
|
|
224
|
-
return {
|
|
225
|
-
success: false,
|
|
226
|
-
error: new InsufficientCreditsError('...', {...}),
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return { success: true, data: newBalance };
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Usage
|
|
234
|
-
const result = await deductCredits(10);
|
|
235
|
-
if (result.success) {
|
|
236
|
-
console.log('New balance:', result.data);
|
|
237
|
-
} else {
|
|
238
|
-
console.error('Error:', result.error.message);
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### 3. Error Boundary
|
|
243
|
-
|
|
244
|
-
```typescript
|
|
245
|
-
class SubscriptionErrorBoundary extends React.Component {
|
|
246
|
-
state = { hasError: false, error: null };
|
|
247
|
-
|
|
248
|
-
static getDerivedStateFromError(error: Error) {
|
|
249
|
-
return { hasError: true, error };
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
componentDidCatch(error: Error, errorInfo: any) {
|
|
253
|
-
if (error instanceof SubscriptionError) {
|
|
254
|
-
analytics().logEvent('subscription_error', {
|
|
255
|
-
code: error.code,
|
|
256
|
-
message: error.message,
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
render() {
|
|
262
|
-
if (this.state.hasError) {
|
|
263
|
-
return <ErrorFallback error={this.state.error} />;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return this.props.children;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
## Best Practices
|
|
272
|
-
|
|
273
|
-
1. **Use specific error types** - Don't use generic Error
|
|
274
|
-
2. **Include context** - Add relevant data to errors
|
|
275
|
-
3. **Document error codes** - List all possible errors
|
|
276
|
-
4. **Handle gracefully** - Show user-friendly messages
|
|
277
|
-
5. **Log errors** - Track for debugging
|
|
278
|
-
6. **Don't swallow errors** - Always handle or rethrow
|
|
279
|
-
7. **Use type guards** - Enable type-safe error handling
|
|
280
|
-
|
|
281
|
-
## Testing
|
|
282
|
-
|
|
283
|
-
```typescript
|
|
284
|
-
describe('InsufficientCreditsError', () => {
|
|
285
|
-
it('should create error with context', () => {
|
|
286
|
-
const error = new InsufficientCreditsError('Not enough credits', {
|
|
287
|
-
required: 10,
|
|
288
|
-
available: 5,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
expect(error.context.required).toBe(10);
|
|
292
|
-
expect(error.context.available).toBe(5);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('should be instance of SubscriptionError', () => {
|
|
296
|
-
const error = new InsufficientCreditsError('...', {...});
|
|
297
|
-
|
|
298
|
-
expect(error).toBeInstanceOf(SubscriptionError);
|
|
299
|
-
});
|
|
300
|
-
});
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
## Related
|
|
49
|
+
## Related Documentation
|
|
304
50
|
|
|
305
51
|
- [Domain Entities](../entities/README.md)
|
|
306
52
|
- [Value Objects](../value-objects/README.md)
|
|
307
|
-
- [Domain Layer](
|
|
53
|
+
- [Domain Layer](../README.md)
|