@umituz/react-native-subscription 1.1.1 → 1.3.0

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 (89) hide show
  1. package/package.json +7 -23
  2. package/src/index.ts +50 -19
  3. package/src/presentation/hooks/__tests__/useUserTier.authenticated.test.ts +79 -0
  4. package/src/presentation/hooks/__tests__/useUserTier.guest.test.ts +70 -0
  5. package/src/presentation/hooks/__tests__/useUserTier.states.test.ts +167 -0
  6. package/src/presentation/hooks/usePremiumGate.ts +116 -0
  7. package/src/presentation/hooks/useUserTier.ts +78 -0
  8. package/src/presentation/hooks/useUserTierWithRepository.ts +171 -0
  9. package/src/utils/__tests__/authUtils.test.ts +52 -0
  10. package/src/utils/__tests__/edgeCases.test.ts +84 -0
  11. package/src/utils/__tests__/premiumUtils.test.ts +178 -0
  12. package/src/utils/__tests__/tierUtils.test.ts +148 -0
  13. package/src/utils/__tests__/validation.test.ts +108 -0
  14. package/src/utils/authUtils.ts +65 -0
  15. package/src/utils/premiumAsyncUtils.ts +60 -0
  16. package/src/utils/premiumStatusUtils.ts +79 -0
  17. package/src/utils/premiumUtils.ts +9 -0
  18. package/src/utils/tierUtils.ts +97 -0
  19. package/src/utils/types.ts +37 -0
  20. package/src/utils/userTierUtils.ts +81 -0
  21. package/src/utils/validation.ts +119 -0
  22. package/lib/application/ports/ISubscriptionRepository.d.ts +0 -25
  23. package/lib/application/ports/ISubscriptionRepository.d.ts.map +0 -1
  24. package/lib/application/ports/ISubscriptionRepository.js +0 -9
  25. package/lib/application/ports/ISubscriptionRepository.js.map +0 -1
  26. package/lib/application/ports/ISubscriptionService.d.ts +0 -28
  27. package/lib/application/ports/ISubscriptionService.d.ts.map +0 -1
  28. package/lib/application/ports/ISubscriptionService.js +0 -6
  29. package/lib/application/ports/ISubscriptionService.js.map +0 -1
  30. package/lib/domain/entities/SubscriptionStatus.d.ts +0 -31
  31. package/lib/domain/entities/SubscriptionStatus.d.ts.map +0 -1
  32. package/lib/domain/entities/SubscriptionStatus.js +0 -39
  33. package/lib/domain/entities/SubscriptionStatus.js.map +0 -1
  34. package/lib/domain/errors/SubscriptionError.d.ts +0 -18
  35. package/lib/domain/errors/SubscriptionError.d.ts.map +0 -1
  36. package/lib/domain/errors/SubscriptionError.js +0 -30
  37. package/lib/domain/errors/SubscriptionError.js.map +0 -1
  38. package/lib/domain/value-objects/SubscriptionConfig.d.ts +0 -15
  39. package/lib/domain/value-objects/SubscriptionConfig.d.ts.map +0 -1
  40. package/lib/domain/value-objects/SubscriptionConfig.js +0 -6
  41. package/lib/domain/value-objects/SubscriptionConfig.js.map +0 -1
  42. package/lib/index.d.ts +0 -33
  43. package/lib/index.d.ts.map +0 -1
  44. package/lib/index.js +0 -43
  45. package/lib/index.js.map +0 -1
  46. package/lib/infrastructure/services/ActivationHandler.d.ts +0 -20
  47. package/lib/infrastructure/services/ActivationHandler.d.ts.map +0 -1
  48. package/lib/infrastructure/services/ActivationHandler.js +0 -71
  49. package/lib/infrastructure/services/ActivationHandler.js.map +0 -1
  50. package/lib/infrastructure/services/SubscriptionService.d.ts +0 -22
  51. package/lib/infrastructure/services/SubscriptionService.d.ts.map +0 -1
  52. package/lib/infrastructure/services/SubscriptionService.js +0 -110
  53. package/lib/infrastructure/services/SubscriptionService.js.map +0 -1
  54. package/lib/presentation/hooks/useSubscription.d.ts +0 -33
  55. package/lib/presentation/hooks/useSubscription.d.ts.map +0 -1
  56. package/lib/presentation/hooks/useSubscription.js +0 -129
  57. package/lib/presentation/hooks/useSubscription.js.map +0 -1
  58. package/lib/utils/dateUtils.d.ts +0 -39
  59. package/lib/utils/dateUtils.d.ts.map +0 -1
  60. package/lib/utils/dateUtils.js +0 -117
  61. package/lib/utils/dateUtils.js.map +0 -1
  62. package/lib/utils/dateValidationUtils.d.ts +0 -20
  63. package/lib/utils/dateValidationUtils.d.ts.map +0 -1
  64. package/lib/utils/dateValidationUtils.js +0 -39
  65. package/lib/utils/dateValidationUtils.js.map +0 -1
  66. package/lib/utils/periodUtils.d.ts +0 -38
  67. package/lib/utils/periodUtils.d.ts.map +0 -1
  68. package/lib/utils/periodUtils.js +0 -70
  69. package/lib/utils/periodUtils.js.map +0 -1
  70. package/lib/utils/planDetectionUtils.d.ts +0 -17
  71. package/lib/utils/planDetectionUtils.d.ts.map +0 -1
  72. package/lib/utils/planDetectionUtils.js +0 -31
  73. package/lib/utils/planDetectionUtils.js.map +0 -1
  74. package/lib/utils/priceUtils.d.ts +0 -23
  75. package/lib/utils/priceUtils.d.ts.map +0 -1
  76. package/lib/utils/priceUtils.js +0 -29
  77. package/lib/utils/priceUtils.js.map +0 -1
  78. package/lib/utils/subscriptionConstants.d.ts +0 -62
  79. package/lib/utils/subscriptionConstants.d.ts.map +0 -1
  80. package/lib/utils/subscriptionConstants.js +0 -61
  81. package/lib/utils/subscriptionConstants.js.map +0 -1
  82. package/src/utils/dateUtils.test.ts +0 -116
  83. package/src/utils/dateUtils.ts +0 -147
  84. package/src/utils/periodUtils.ts +0 -104
  85. package/src/utils/planDetectionUtils.test.ts +0 -47
  86. package/src/utils/planDetectionUtils.ts +0 -40
  87. package/src/utils/priceUtils.test.ts +0 -35
  88. package/src/utils/priceUtils.ts +0 -31
  89. package/src/utils/subscriptionConstants.ts +0 -70
@@ -0,0 +1,108 @@
1
+ /**
2
+ * User Tier Validation Tests
3
+ *
4
+ * Tests for validation functions and type guards
5
+ */
6
+
7
+ import {
8
+ isValidUserTier,
9
+ isUserTierInfo,
10
+ validateUserId,
11
+ validateIsGuest,
12
+ validateIsPremium,
13
+ validateFetcher,
14
+ type UserTier,
15
+ type UserTierInfo,
16
+ type PremiumStatusFetcher,
17
+ } from '../validation';
18
+
19
+ describe('isValidUserTier', () => {
20
+ it('should return true for valid tiers', () => {
21
+ expect(isValidUserTier('guest')).toBe(true);
22
+ expect(isValidUserTier('freemium')).toBe(true);
23
+ expect(isValidUserTier('premium')).toBe(true);
24
+ });
25
+
26
+ it('should return false for invalid values', () => {
27
+ expect(isValidUserTier('invalid')).toBe(false);
28
+ expect(isValidUserTier('')).toBe(false);
29
+ expect(isValidUserTier(null)).toBe(false);
30
+ expect(isValidUserTier(undefined)).toBe(false);
31
+ expect(isValidUserTier(123)).toBe(false);
32
+ expect(isValidUserTier({})).toBe(false);
33
+ });
34
+ });
35
+
36
+ describe('isUserTierInfo', () => {
37
+ it('should return true for valid UserTierInfo', () => {
38
+ const validInfo: UserTierInfo = {
39
+ tier: 'premium',
40
+ isPremium: true,
41
+ isGuest: false,
42
+ isAuthenticated: true,
43
+ userId: 'user123',
44
+ };
45
+ expect(isUserTierInfo(validInfo)).toBe(true);
46
+ });
47
+
48
+ it('should return false for invalid objects', () => {
49
+ expect(isUserTierInfo(null)).toBe(false);
50
+ expect(isUserTierInfo(undefined)).toBe(false);
51
+ expect(isUserTierInfo('string')).toBe(false);
52
+ expect(isUserTierInfo({})).toBe(false);
53
+ expect(isUserTierInfo({ tier: 'invalid' })).toBe(false);
54
+ expect(isUserTierInfo({ tier: 'premium' })).toBe(false);
55
+ });
56
+ });
57
+
58
+ describe('validateUserId', () => {
59
+ it('should not throw for valid userId', () => {
60
+ expect(() => validateUserId('user123')).not.toThrow();
61
+ expect(() => validateUserId(null)).not.toThrow();
62
+ });
63
+
64
+ it('should throw for invalid userId', () => {
65
+ expect(() => validateUserId('')).toThrow(TypeError);
66
+ expect(() => validateUserId(' ')).toThrow(TypeError);
67
+ expect(() => validateUserId(123 as any)).toThrow(TypeError);
68
+ });
69
+ });
70
+
71
+ describe('validateIsGuest', () => {
72
+ it('should not throw for valid isGuest', () => {
73
+ expect(() => validateIsGuest(true)).not.toThrow();
74
+ expect(() => validateIsGuest(false)).not.toThrow();
75
+ });
76
+
77
+ it('should throw for invalid isGuest', () => {
78
+ expect(() => validateIsGuest('true' as any)).toThrow(TypeError);
79
+ expect(() => validateIsGuest(1 as any)).toThrow(TypeError);
80
+ });
81
+ });
82
+
83
+ describe('validateIsPremium', () => {
84
+ it('should not throw for valid isPremium', () => {
85
+ expect(() => validateIsPremium(true)).not.toThrow();
86
+ expect(() => validateIsPremium(false)).not.toThrow();
87
+ });
88
+
89
+ it('should throw for invalid isPremium', () => {
90
+ expect(() => validateIsPremium('true' as any)).toThrow(TypeError);
91
+ expect(() => validateIsPremium(1 as any)).toThrow(TypeError);
92
+ });
93
+ });
94
+
95
+ describe('validateFetcher', () => {
96
+ it('should not throw for valid fetcher', () => {
97
+ const validFetcher: PremiumStatusFetcher = {
98
+ isPremium: async () => true,
99
+ };
100
+ expect(() => validateFetcher(validFetcher)).not.toThrow();
101
+ });
102
+
103
+ it('should throw for invalid fetcher', () => {
104
+ expect(() => validateFetcher(null as any)).toThrow(TypeError);
105
+ expect(() => validateFetcher({} as any)).toThrow(TypeError);
106
+ expect(() => validateFetcher({ isPremium: 'not a function' } as any)).toThrow(TypeError);
107
+ });
108
+ });
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Authentication Utilities
3
+ *
4
+ * Centralized logic for authentication checks
5
+ */
6
+
7
+ import { validateIsGuest, validateUserId } from './validation';
8
+
9
+ /**
10
+ * Check if user is authenticated
11
+ *
12
+ * This is the SINGLE SOURCE OF TRUTH for authentication check.
13
+ * All apps should use this function for consistent authentication logic.
14
+ *
15
+ * @param isGuest - Whether user is a guest
16
+ * @param userId - User ID (null for guests)
17
+ * @returns Whether user is authenticated
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // Guest user
22
+ * isAuthenticated(true, null); // false
23
+ *
24
+ * // Authenticated user
25
+ * isAuthenticated(false, 'user123'); // true
26
+ * ```
27
+ */
28
+ export function isAuthenticated(
29
+ isGuest: boolean,
30
+ userId: string | null,
31
+ ): boolean {
32
+ validateIsGuest(isGuest);
33
+ validateUserId(userId);
34
+
35
+ return !isGuest && userId !== null;
36
+ }
37
+
38
+ /**
39
+ * Check if user is guest
40
+ *
41
+ * This is the SINGLE SOURCE OF TRUTH for guest check.
42
+ * All apps should use this function for consistent guest logic.
43
+ *
44
+ * @param isGuest - Whether user is a guest
45
+ * @param userId - User ID (null for guests)
46
+ * @returns Whether user is a guest
47
+ *
48
+ * @example
49
+ * ```typescript
50
+ * // Guest user
51
+ * isGuest(true, null); // true
52
+ *
53
+ * // Authenticated user
54
+ * isGuest(false, 'user123'); // false
55
+ * ```
56
+ */
57
+ export function isGuest(
58
+ isGuestFlag: boolean,
59
+ userId: string | null,
60
+ ): boolean {
61
+ validateIsGuest(isGuestFlag);
62
+ validateUserId(userId);
63
+
64
+ return isGuestFlag || userId === null;
65
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Async Premium Utilities
3
+ *
4
+ * Async premium status fetching and tier determination
5
+ */
6
+
7
+ import { getIsPremium } from './premiumStatusUtils';
8
+ import type { PremiumStatusFetcher } from './types';
9
+
10
+ /**
11
+ * Get user tier info asynchronously with fetcher
12
+ *
13
+ * This function combines getUserTierInfo and getIsPremium logic.
14
+ * All tier determination logic is centralized here.
15
+ *
16
+ * @param isGuest - Whether user is a guest
17
+ * @param userId - User ID (null for guests)
18
+ * @param fetcher - Premium status fetcher (app-specific implementation)
19
+ * @returns Promise<UserTierInfo> - User tier information
20
+ */
21
+ export async function getUserTierInfoAsync(
22
+ isGuestFlag: boolean,
23
+ userId: string | null,
24
+ fetcher: PremiumStatusFetcher,
25
+ ): Promise<import('./types').UserTierInfo> {
26
+ // Import here to avoid circular dependency
27
+ const { getUserTierInfo } = await import('./tierUtils');
28
+
29
+ // Get isPremium using centralized logic (async mode)
30
+ const isPremium = await getIsPremium(isGuestFlag, userId, fetcher);
31
+
32
+ // Get tier info using centralized logic
33
+ return getUserTierInfo(isGuestFlag, userId, isPremium);
34
+ }
35
+
36
+ /**
37
+ * Check if user has premium access (async version with fetcher)
38
+ *
39
+ * This function combines getIsPremium and checkPremiumAccess logic.
40
+ * Guest users NEVER have premium access.
41
+ *
42
+ * @param isGuest - Whether user is a guest
43
+ * @param userId - User ID (null for guests)
44
+ * @param fetcher - Premium status fetcher (app-specific implementation)
45
+ * @returns Promise<boolean> - Whether user has premium access
46
+ */
47
+ export async function checkPremiumAccessAsync(
48
+ isGuestFlag: boolean,
49
+ userId: string | null,
50
+ fetcher: PremiumStatusFetcher,
51
+ ): Promise<boolean> {
52
+ // Import here to avoid circular dependency
53
+ const { checkPremiumAccess } = await import('./tierUtils');
54
+
55
+ // Get isPremium using centralized logic (async mode)
56
+ const isPremium = await getIsPremium(isGuestFlag, userId, fetcher);
57
+
58
+ // Apply premium access check logic
59
+ return checkPremiumAccess(isGuestFlag, userId, isPremium);
60
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Premium Status Utilities
3
+ *
4
+ * Core premium status determination logic
5
+ */
6
+
7
+ import { validateIsGuest, validateUserId, validateFetcher } from './validation';
8
+ import type { PremiumStatusFetcher } from './types';
9
+
10
+ /**
11
+ * Get isPremium value with centralized logic
12
+ *
13
+ * This function handles the complete logic for determining premium status:
14
+ * - Guest users NEVER have premium (returns false immediately)
15
+ * - Authenticated users: uses provided isPremium value OR fetches using fetcher
16
+ *
17
+ * This is the SINGLE SOURCE OF TRUTH for isPremium determination.
18
+ * All apps should use this function instead of directly calling their premium service.
19
+ *
20
+ * Two usage modes:
21
+ * 1. Sync mode: If you already have isPremium value, pass it directly
22
+ * 2. Async mode: If you need to fetch from database, pass a fetcher function
23
+ *
24
+ * @param isGuest - Whether user is a guest
25
+ * @param userId - User ID (null for guests)
26
+ * @param isPremiumOrFetcher - Either boolean (sync) or PremiumStatusFetcher (async)
27
+ * @returns boolean (sync) or Promise<boolean> (async) - Whether user has premium subscription
28
+ */
29
+ // Sync overload: when isPremium value is already known
30
+ export function getIsPremium(
31
+ isGuestFlag: boolean,
32
+ userId: string | null,
33
+ isPremium: boolean,
34
+ ): boolean;
35
+
36
+ // Async overload: when fetcher is provided
37
+ export function getIsPremium(
38
+ isGuestFlag: boolean,
39
+ userId: string | null,
40
+ fetcher: PremiumStatusFetcher,
41
+ ): Promise<boolean>;
42
+
43
+ // Implementation
44
+ export function getIsPremium(
45
+ isGuestFlag: boolean,
46
+ userId: string | null,
47
+ isPremiumOrFetcher: boolean | PremiumStatusFetcher,
48
+ ): boolean | Promise<boolean> {
49
+ validateIsGuest(isGuestFlag);
50
+ validateUserId(userId);
51
+
52
+ // Guest users NEVER have premium - this is centralized logic
53
+ if (isGuestFlag || userId === null) {
54
+ return false;
55
+ }
56
+
57
+ // Check if it's a boolean (sync mode) or fetcher (async mode)
58
+ if (typeof isPremiumOrFetcher === 'boolean') {
59
+ // Sync mode: return the provided isPremium value
60
+ return isPremiumOrFetcher;
61
+ }
62
+
63
+ // Async mode: validate fetcher and fetch premium status
64
+ validateFetcher(isPremiumOrFetcher);
65
+
66
+ // Authenticated users: fetch premium status using app's fetcher
67
+ // Package handles the logic, app handles the database operation
68
+ return (async () => {
69
+ try {
70
+ return await isPremiumOrFetcher.isPremium(userId);
71
+ } catch (error) {
72
+ // If fetcher throws, assume not premium (fail-safe)
73
+ // Apps should handle errors in their fetcher implementation
74
+ throw new Error(
75
+ `Failed to fetch premium status: ${error instanceof Error ? error.message : String(error)}`
76
+ );
77
+ }
78
+ })();
79
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Premium Utilities
3
+ *
4
+ * Re-export of premium status utilities for backward compatibility
5
+ */
6
+
7
+ // Re-export for backward compatibility
8
+ export { getIsPremium } from './premiumStatusUtils';
9
+ export { getUserTierInfoAsync, checkPremiumAccessAsync } from './premiumAsyncUtils';
@@ -0,0 +1,97 @@
1
+ /**
2
+ * User Tier Core Utilities
3
+ *
4
+ * Core logic for determining user tier and premium status
5
+ */
6
+
7
+ import { validateIsGuest, validateUserId, validateIsPremium } from './validation';
8
+ import type { UserTierInfo } from './types';
9
+
10
+ /**
11
+ * Determine user tier from auth state and premium status
12
+ *
13
+ * This is the SINGLE SOURCE OF TRUTH for tier determination.
14
+ * All apps should use this function for consistent tier logic.
15
+ *
16
+ * @param isGuest - Whether user is a guest
17
+ * @param userId - User ID (null for guests)
18
+ * @param isPremium - Whether user has active premium subscription
19
+ * @returns User tier information
20
+ *
21
+ * @example
22
+ * ```typescript
23
+ * const tierInfo = getUserTierInfo(false, 'user123', true);
24
+ * // Returns: { tier: 'premium', isPremium: true, isGuest: false, isAuthenticated: true, userId: 'user123' }
25
+ *
26
+ * const guestInfo = getUserTierInfo(true, null, false);
27
+ * // Returns: { tier: 'guest', isPremium: false, isGuest: true, isAuthenticated: false, userId: null }
28
+ * ```
29
+ */
30
+ export function getUserTierInfo(
31
+ isGuestFlag: boolean,
32
+ userId: string | null,
33
+ isPremium: boolean,
34
+ ): UserTierInfo {
35
+ validateIsGuest(isGuestFlag);
36
+ validateUserId(userId);
37
+ validateIsPremium(isPremium);
38
+
39
+ // Guest users are always freemium, never premium
40
+ if (isGuestFlag || userId === null) {
41
+ return {
42
+ tier: 'guest',
43
+ isPremium: false,
44
+ isGuest: true,
45
+ isAuthenticated: false,
46
+ userId: null,
47
+ };
48
+ }
49
+
50
+ // Authenticated users: premium or freemium
51
+ return {
52
+ tier: isPremium ? 'premium' : 'freemium',
53
+ isPremium,
54
+ isGuest: false,
55
+ isAuthenticated: true,
56
+ userId,
57
+ };
58
+ }
59
+
60
+ /**
61
+ * Check if user has premium access (synchronous version)
62
+ *
63
+ * Guest users NEVER have premium access, regardless of isPremium value.
64
+ *
65
+ * @param isGuest - Whether user is a guest
66
+ * @param userId - User ID (null for guests)
67
+ * @param isPremium - Whether user has active premium subscription
68
+ * @returns Whether user has premium access
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * // Guest user - always false
73
+ * checkPremiumAccess(true, null, true); // false
74
+ *
75
+ * // Authenticated premium user
76
+ * checkPremiumAccess(false, 'user123', true); // true
77
+ *
78
+ * // Authenticated freemium user
79
+ * checkPremiumAccess(false, 'user123', false); // false
80
+ * ```
81
+ */
82
+ export function checkPremiumAccess(
83
+ isGuestFlag: boolean,
84
+ userId: string | null,
85
+ isPremium: boolean,
86
+ ): boolean {
87
+ validateIsGuest(isGuestFlag);
88
+ validateUserId(userId);
89
+ validateIsPremium(isPremium);
90
+
91
+ // Guest users never have premium access
92
+ if (isGuestFlag || userId === null) {
93
+ return false;
94
+ }
95
+
96
+ return isPremium;
97
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * User Tier Types
3
+ *
4
+ * Type definitions for user tier system
5
+ */
6
+
7
+ export type UserTier = 'guest' | 'freemium' | 'premium';
8
+
9
+ export interface UserTierInfo {
10
+ /** User tier classification */
11
+ tier: UserTier;
12
+
13
+ /** Whether user has premium access */
14
+ isPremium: boolean;
15
+
16
+ /** Whether user is a guest (not authenticated) */
17
+ isGuest: boolean;
18
+
19
+ /** Whether user is authenticated */
20
+ isAuthenticated: boolean;
21
+
22
+ /** User ID (null for guests) */
23
+ userId: string | null;
24
+ }
25
+
26
+ /**
27
+ * Premium status fetcher interface
28
+ * Apps should implement this to provide premium status from their database
29
+ */
30
+ export interface PremiumStatusFetcher {
31
+ /**
32
+ * Check if user has active premium subscription
33
+ * @param userId - User ID (never null, this is only called for authenticated users)
34
+ * @returns Promise<boolean> - Whether user has premium subscription
35
+ */
36
+ isPremium(userId: string): Promise<boolean>;
37
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Tier Comparison Utilities
3
+ *
4
+ * Utilities for comparing and checking user tiers
5
+ */
6
+
7
+ import type { UserTier } from './types';
8
+
9
+ /**
10
+ * Compare two tiers to determine if first tier has higher or equal access than second
11
+ *
12
+ * Tier hierarchy: guest < freemium < premium
13
+ *
14
+ * @param tier1 - First tier to compare
15
+ * @param tier2 - Second tier to compare
16
+ * @returns Whether tier1 has higher or equal access than tier2
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * hasTierAccess('premium', 'freemium'); // true
21
+ * hasTierAccess('freemium', 'premium'); // false
22
+ * hasTierAccess('premium', 'premium'); // true
23
+ * ```
24
+ */
25
+ export function hasTierAccess(tier1: UserTier, tier2: UserTier): boolean {
26
+ const tierLevels: Record<UserTier, number> = {
27
+ guest: 0,
28
+ freemium: 1,
29
+ premium: 2,
30
+ };
31
+
32
+ return tierLevels[tier1] >= tierLevels[tier2];
33
+ }
34
+
35
+ /**
36
+ * Check if tier is premium
37
+ *
38
+ * @param tier - Tier to check
39
+ * @returns Whether tier is premium
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * isTierPremium('premium'); // true
44
+ * isTierPremium('freemium'); // false
45
+ * ```
46
+ */
47
+ export function isTierPremium(tier: UserTier): boolean {
48
+ return tier === 'premium';
49
+ }
50
+
51
+ /**
52
+ * Check if tier is freemium
53
+ *
54
+ * @param tier - Tier to check
55
+ * @returns Whether tier is freemium
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * isTierFreemium('freemium'); // true
60
+ * isTierFreemium('premium'); // false
61
+ * ```
62
+ */
63
+ export function isTierFreemium(tier: UserTier): boolean {
64
+ return tier === 'freemium';
65
+ }
66
+
67
+ /**
68
+ * Check if tier is guest
69
+ *
70
+ * @param tier - Tier to check
71
+ * @returns Whether tier is guest
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * isTierGuest('guest'); // true
76
+ * isTierGuest('premium'); // false
77
+ * ```
78
+ */
79
+ export function isTierGuest(tier: UserTier): boolean {
80
+ return tier === 'guest';
81
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * User Tier Validation Utilities
3
+ *
4
+ * Type guards and validation functions for user tier system
5
+ */
6
+
7
+ import type { UserTier, UserTierInfo } from './types';
8
+
9
+ /**
10
+ * Type guard to check if a value is a valid UserTier
11
+ *
12
+ * @param value - Value to check
13
+ * @returns Whether value is a valid UserTier
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * if (isValidUserTier(someValue)) {
18
+ * // TypeScript knows someValue is UserTier
19
+ * }
20
+ * ```
21
+ */
22
+ export function isValidUserTier(value: unknown): value is UserTier {
23
+ return value === 'guest' || value === 'freemium' || value === 'premium';
24
+ }
25
+
26
+ /**
27
+ * Type guard to check if an object is a valid UserTierInfo
28
+ *
29
+ * @param value - Value to check
30
+ * @returns Whether value is a valid UserTierInfo
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * if (isUserTierInfo(someValue)) {
35
+ * // TypeScript knows someValue is UserTierInfo
36
+ * }
37
+ * ```
38
+ */
39
+ export function isUserTierInfo(value: unknown): value is UserTierInfo {
40
+ if (typeof value !== 'object' || value === null) {
41
+ return false;
42
+ }
43
+
44
+ const obj = value as Record<string, unknown>;
45
+
46
+ return (
47
+ isValidUserTier(obj.tier) &&
48
+ typeof obj.isPremium === 'boolean' &&
49
+ typeof obj.isGuest === 'boolean' &&
50
+ typeof obj.isAuthenticated === 'boolean' &&
51
+ (obj.userId === null || typeof obj.userId === 'string')
52
+ );
53
+ }
54
+
55
+ /**
56
+ * Validate userId parameter
57
+ *
58
+ * @param userId - User ID to validate
59
+ * @throws {TypeError} If userId is invalid
60
+ */
61
+ export function validateUserId(userId: string | null): void {
62
+ if (userId !== null && typeof userId !== 'string') {
63
+ throw new TypeError(
64
+ `Invalid userId: expected string or null, got ${typeof userId}`
65
+ );
66
+ }
67
+
68
+ if (userId !== null && userId.trim() === '') {
69
+ throw new TypeError('Invalid userId: cannot be empty string');
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Validate isGuest parameter
75
+ *
76
+ * @param isGuest - isGuest flag to validate
77
+ * @throws {TypeError} If isGuest is invalid
78
+ */
79
+ export function validateIsGuest(isGuest: boolean): void {
80
+ if (typeof isGuest !== 'boolean') {
81
+ throw new TypeError(
82
+ `Invalid isGuest: expected boolean, got ${typeof isGuest}`
83
+ );
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Validate isPremium parameter
89
+ *
90
+ * @param isPremium - isPremium flag to validate
91
+ * @throws {TypeError} If isPremium is invalid
92
+ */
93
+ export function validateIsPremium(isPremium: boolean): void {
94
+ if (typeof isPremium !== 'boolean') {
95
+ throw new TypeError(
96
+ `Invalid isPremium: expected boolean, got ${typeof isPremium}`
97
+ );
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Validate PremiumStatusFetcher
103
+ *
104
+ * @param fetcher - Fetcher to validate
105
+ * @throws {TypeError} If fetcher is invalid
106
+ */
107
+ export function validateFetcher(fetcher: import('./types').PremiumStatusFetcher): void {
108
+ if (typeof fetcher !== 'object' || fetcher === null) {
109
+ throw new TypeError(
110
+ `Invalid fetcher: expected object, got ${typeof fetcher}`
111
+ );
112
+ }
113
+
114
+ if (typeof fetcher.isPremium !== 'function') {
115
+ throw new TypeError(
116
+ 'Invalid fetcher: isPremium must be a function'
117
+ );
118
+ }
119
+ }
@@ -1,25 +0,0 @@
1
- /**
2
- * Subscription Repository Interface
3
- * Port for database operations
4
- *
5
- * SECURITY: Apps must implement this interface with their database.
6
- * Never expose database credentials or allow direct database access.
7
- */
8
- import type { SubscriptionStatus } from '../../domain/entities/SubscriptionStatus';
9
- export interface ISubscriptionRepository {
10
- /**
11
- * Get subscription status for a user
12
- * Returns null if user not found
13
- */
14
- getSubscriptionStatus(userId: string): Promise<SubscriptionStatus | null>;
15
- /**
16
- * Update subscription status for a user
17
- */
18
- updateSubscriptionStatus(userId: string, status: Partial<SubscriptionStatus>): Promise<SubscriptionStatus>;
19
- /**
20
- * Check if subscription is valid (not expired)
21
- * SECURITY: Always validate expiration server-side
22
- */
23
- isSubscriptionValid(status: SubscriptionStatus): boolean;
24
- }
25
- //# sourceMappingURL=ISubscriptionRepository.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ISubscriptionRepository.d.ts","sourceRoot":"","sources":["../../../src/application/ports/ISubscriptionRepository.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAEnF,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAE1E;;OAEG;IACH,wBAAwB,CACtB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAClC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAE/B;;;OAGG;IACH,mBAAmB,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC;CAC1D"}
@@ -1,9 +0,0 @@
1
- /**
2
- * Subscription Repository Interface
3
- * Port for database operations
4
- *
5
- * SECURITY: Apps must implement this interface with their database.
6
- * Never expose database credentials or allow direct database access.
7
- */
8
- export {};
9
- //# sourceMappingURL=ISubscriptionRepository.js.map