@sudobility/subscription_lib 0.0.1

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 (52) hide show
  1. package/dist/core/index.d.ts +6 -0
  2. package/dist/core/index.d.ts.map +1 -0
  3. package/dist/core/index.js +5 -0
  4. package/dist/core/service.d.ts +74 -0
  5. package/dist/core/service.d.ts.map +1 -0
  6. package/dist/core/service.js +251 -0
  7. package/dist/core/singleton.d.ts +65 -0
  8. package/dist/core/singleton.d.ts.map +1 -0
  9. package/dist/core/singleton.js +73 -0
  10. package/dist/hooks/index.d.ts +9 -0
  11. package/dist/hooks/index.d.ts.map +1 -0
  12. package/dist/hooks/index.js +8 -0
  13. package/dist/hooks/useSubscribable.d.ts +50 -0
  14. package/dist/hooks/useSubscribable.d.ts.map +1 -0
  15. package/dist/hooks/useSubscribable.js +80 -0
  16. package/dist/hooks/useSubscriptionForPeriod.d.ts +48 -0
  17. package/dist/hooks/useSubscriptionForPeriod.d.ts.map +1 -0
  18. package/dist/hooks/useSubscriptionForPeriod.js +59 -0
  19. package/dist/hooks/useSubscriptionPeriods.d.ts +38 -0
  20. package/dist/hooks/useSubscriptionPeriods.d.ts.map +1 -0
  21. package/dist/hooks/useSubscriptionPeriods.js +44 -0
  22. package/dist/hooks/useSubscriptions.d.ts +43 -0
  23. package/dist/hooks/useSubscriptions.d.ts.map +1 -0
  24. package/dist/hooks/useSubscriptions.js +80 -0
  25. package/dist/hooks/useUserSubscription.d.ts +39 -0
  26. package/dist/hooks/useUserSubscription.d.ts.map +1 -0
  27. package/dist/hooks/useUserSubscription.js +76 -0
  28. package/dist/index.d.ts +12 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +13 -0
  31. package/dist/types/adapter.d.ts +149 -0
  32. package/dist/types/adapter.d.ts.map +1 -0
  33. package/dist/types/adapter.js +7 -0
  34. package/dist/types/index.d.ts +8 -0
  35. package/dist/types/index.d.ts.map +1 -0
  36. package/dist/types/index.js +4 -0
  37. package/dist/types/period.d.ts +18 -0
  38. package/dist/types/period.d.ts.map +1 -0
  39. package/dist/types/period.js +25 -0
  40. package/dist/types/subscription.d.ts +95 -0
  41. package/dist/types/subscription.d.ts.map +1 -0
  42. package/dist/types/subscription.js +6 -0
  43. package/dist/utils/index.d.ts +6 -0
  44. package/dist/utils/index.d.ts.map +1 -0
  45. package/dist/utils/index.js +5 -0
  46. package/dist/utils/level-calculator.d.ts +68 -0
  47. package/dist/utils/level-calculator.d.ts.map +1 -0
  48. package/dist/utils/level-calculator.js +164 -0
  49. package/dist/utils/period-parser.d.ts +45 -0
  50. package/dist/utils/period-parser.d.ts.map +1 -0
  51. package/dist/utils/period-parser.js +113 -0
  52. package/package.json +55 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Subscription Period Types
3
+ *
4
+ * Standard subscription periods and utilities for period comparison.
5
+ */
6
+ /**
7
+ * Standard subscription periods
8
+ */
9
+ export type SubscriptionPeriod = 'weekly' | 'monthly' | 'quarterly' | 'yearly' | 'lifetime';
10
+ /**
11
+ * Period ranking for comparison (higher = longer)
12
+ */
13
+ export declare const PERIOD_RANKS: Record<SubscriptionPeriod, number>;
14
+ /**
15
+ * All supported periods in order
16
+ */
17
+ export declare const ALL_PERIODS: SubscriptionPeriod[];
18
+ //# sourceMappingURL=period.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"period.d.ts","sourceRoot":"","sources":["../../src/types/period.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,SAAS,GACT,WAAW,GACX,QAAQ,GACR,UAAU,CAAC;AAEf;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,kBAAkB,EAAE,MAAM,CAM3D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,EAAE,kBAAkB,EAM3C,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Subscription Period Types
3
+ *
4
+ * Standard subscription periods and utilities for period comparison.
5
+ */
6
+ /**
7
+ * Period ranking for comparison (higher = longer)
8
+ */
9
+ export const PERIOD_RANKS = {
10
+ weekly: 1,
11
+ monthly: 2,
12
+ quarterly: 3,
13
+ yearly: 4,
14
+ lifetime: 5,
15
+ };
16
+ /**
17
+ * All supported periods in order
18
+ */
19
+ export const ALL_PERIODS = [
20
+ 'weekly',
21
+ 'monthly',
22
+ 'quarterly',
23
+ 'yearly',
24
+ 'lifetime',
25
+ ];
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Subscription Types
3
+ *
4
+ * Clean abstraction types for subscription data, independent of RevenueCat SDK.
5
+ */
6
+ import type { SubscriptionPeriod } from './period';
7
+ /**
8
+ * Product pricing and billing information
9
+ */
10
+ export interface SubscriptionProduct {
11
+ /** Product ID from the store */
12
+ productId: string;
13
+ /** Product display name */
14
+ name: string;
15
+ /** Product description */
16
+ description?: string;
17
+ /** Numeric price value */
18
+ price: number;
19
+ /** Formatted price string (e.g., "$9.99") */
20
+ priceString: string;
21
+ /** Currency code (e.g., "USD") */
22
+ currency: string;
23
+ /** Parsed subscription period */
24
+ period: SubscriptionPeriod;
25
+ /** Raw ISO 8601 period duration */
26
+ periodDuration: string;
27
+ /** Trial period in ISO 8601 format (e.g., "P7D") */
28
+ trialPeriod?: string;
29
+ /** Introductory price string */
30
+ introPrice?: string;
31
+ /** Introductory price period in ISO 8601 format */
32
+ introPricePeriod?: string;
33
+ /** Number of intro price billing cycles */
34
+ introPriceCycles?: number;
35
+ }
36
+ /**
37
+ * Package with optional product (undefined for free tier)
38
+ */
39
+ export interface SubscriptionPackage {
40
+ /** Package identifier (e.g., "pro_monthly", "free") */
41
+ packageId: string;
42
+ /** Package display name */
43
+ name: string;
44
+ /** Product information, undefined for free tier */
45
+ product?: SubscriptionProduct;
46
+ /** Entitlement identifiers granted by this package */
47
+ entitlements: string[];
48
+ }
49
+ /**
50
+ * Offer containing packages
51
+ */
52
+ export interface SubscriptionOffer {
53
+ /** Offer identifier */
54
+ offerId: string;
55
+ /** Offer metadata from RevenueCat */
56
+ metadata?: Record<string, unknown>;
57
+ /** Packages in this offer */
58
+ packages: SubscriptionPackage[];
59
+ }
60
+ /**
61
+ * Current subscription status
62
+ */
63
+ export interface CurrentSubscription {
64
+ /** Whether user has an active subscription */
65
+ isActive: boolean;
66
+ /** Product identifier of the current subscription */
67
+ productId?: string;
68
+ /** Package identifier of the current subscription */
69
+ packageId?: string;
70
+ /** Active entitlement identifiers */
71
+ entitlements: string[];
72
+ /** Current subscription period */
73
+ period?: SubscriptionPeriod;
74
+ /** Subscription expiration date */
75
+ expirationDate?: Date;
76
+ /** Whether subscription will auto-renew */
77
+ willRenew?: boolean;
78
+ }
79
+ /**
80
+ * Configuration for the free tier
81
+ */
82
+ export interface FreeTierConfig {
83
+ /** Package ID for the free tier */
84
+ packageId: string;
85
+ /** Display name for the free tier */
86
+ name: string;
87
+ }
88
+ /**
89
+ * Package with calculated level for upgrade eligibility
90
+ */
91
+ export interface PackageWithLevel extends SubscriptionPackage {
92
+ /** Calculated entitlement level (0 for free, higher = better) */
93
+ level: number;
94
+ }
95
+ //# sourceMappingURL=subscription.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.d.ts","sourceRoot":"","sources":["../../src/types/subscription.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAEnD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0BAA0B;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2CAA2C;IAC3C,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,sDAAsD;IACtD,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,uBAAuB;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,6BAA6B;IAC7B,QAAQ,EAAE,mBAAmB,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,QAAQ,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kCAAkC;IAClC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,mCAAmC;IACnC,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mCAAmC;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,mBAAmB;IAC3D,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Subscription Types
3
+ *
4
+ * Clean abstraction types for subscription data, independent of RevenueCat SDK.
5
+ */
6
+ export {};
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Utility Exports
3
+ */
4
+ export { parseISO8601Period, getPeriodRank, comparePeriods, isPeriodGreaterOrEqual, } from './period-parser';
5
+ export { calculatePackageLevels, addLevelsToPackages, getPackageLevel, findUpgradeablePackages, } from './level-calculator';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,eAAe,EACf,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Utility Exports
3
+ */
4
+ export { parseISO8601Period, getPeriodRank, comparePeriods, isPeriodGreaterOrEqual, } from './period-parser';
5
+ export { calculatePackageLevels, addLevelsToPackages, getPackageLevel, findUpgradeablePackages, } from './level-calculator';
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Level Calculator Utilities
3
+ *
4
+ * Calculate entitlement levels based on price comparison within the same period.
5
+ */
6
+ import type { PackageWithLevel, SubscriptionPackage } from '../types/subscription';
7
+ /**
8
+ * Calculate entitlement levels for packages
9
+ *
10
+ * Level is determined by comparing prices of packages with the same period.
11
+ * Higher price = higher level. Free tier always has level 0.
12
+ *
13
+ * @param packages List of packages to calculate levels for
14
+ * @returns Map of packageId to level
15
+ *
16
+ * @example
17
+ * // Given packages:
18
+ * // - free (no product)
19
+ * // - basic_monthly ($5)
20
+ * // - pro_monthly ($10)
21
+ * // - basic_yearly ($50)
22
+ * // - pro_yearly ($100)
23
+ * //
24
+ * // Returns:
25
+ * // - free: 0
26
+ * // - basic_monthly: 1
27
+ * // - pro_monthly: 2
28
+ * // - basic_yearly: 1
29
+ * // - pro_yearly: 2
30
+ */
31
+ export declare function calculatePackageLevels(packages: SubscriptionPackage[]): Map<string, number>;
32
+ /**
33
+ * Add level information to packages
34
+ *
35
+ * @param packages List of packages
36
+ * @returns Packages with level information
37
+ */
38
+ export declare function addLevelsToPackages(packages: SubscriptionPackage[]): PackageWithLevel[];
39
+ /**
40
+ * Get the level of a specific package
41
+ *
42
+ * @param packageId Package ID to look up
43
+ * @param packages All packages for context
44
+ * @returns Level number (0 for free, higher = better)
45
+ */
46
+ export declare function getPackageLevel(packageId: string, packages: SubscriptionPackage[]): number;
47
+ /**
48
+ * Parameters for findUpgradeablePackages
49
+ */
50
+ export interface FindUpgradeableParams {
51
+ /** Current package ID */
52
+ packageId?: string | null;
53
+ /** Current product ID (fallback if packageId not found) */
54
+ productId?: string | null;
55
+ }
56
+ /**
57
+ * Find packages that are upgrades from the current package
58
+ *
59
+ * A package is an upgrade if:
60
+ * - Its period is >= current period, AND
61
+ * - Its level is >= current level (but not the exact same package)
62
+ *
63
+ * @param current Current package/product identifiers (or null/undefined for no subscription)
64
+ * @param packages All available packages
65
+ * @returns List of package IDs that are valid upgrades
66
+ */
67
+ export declare function findUpgradeablePackages(current: string | FindUpgradeableParams | null | undefined, packages: SubscriptionPackage[]): string[];
68
+ //# sourceMappingURL=level-calculator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"level-calculator.d.ts","sourceRoot":"","sources":["../../src/utils/level-calculator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAG/B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,mBAAmB,EAAE,GAC9B,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAiDrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,mBAAmB,EAAE,GAC9B,gBAAgB,EAAE,CAOpB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,mBAAmB,EAAE,GAC9B,MAAM,CAGR;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,yBAAyB;IACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,GAAG,SAAS,EAC1D,QAAQ,EAAE,mBAAmB,EAAE,GAC9B,MAAM,EAAE,CA2EV"}
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Level Calculator Utilities
3
+ *
4
+ * Calculate entitlement levels based on price comparison within the same period.
5
+ */
6
+ /**
7
+ * Calculate entitlement levels for packages
8
+ *
9
+ * Level is determined by comparing prices of packages with the same period.
10
+ * Higher price = higher level. Free tier always has level 0.
11
+ *
12
+ * @param packages List of packages to calculate levels for
13
+ * @returns Map of packageId to level
14
+ *
15
+ * @example
16
+ * // Given packages:
17
+ * // - free (no product)
18
+ * // - basic_monthly ($5)
19
+ * // - pro_monthly ($10)
20
+ * // - basic_yearly ($50)
21
+ * // - pro_yearly ($100)
22
+ * //
23
+ * // Returns:
24
+ * // - free: 0
25
+ * // - basic_monthly: 1
26
+ * // - pro_monthly: 2
27
+ * // - basic_yearly: 1
28
+ * // - pro_yearly: 2
29
+ */
30
+ export function calculatePackageLevels(packages) {
31
+ const levels = new Map();
32
+ // Group packages by period
33
+ const byPeriod = new Map();
34
+ for (const pkg of packages) {
35
+ if (!pkg.product) {
36
+ // Free tier
37
+ levels.set(pkg.packageId, 0);
38
+ continue;
39
+ }
40
+ const period = pkg.product.period;
41
+ const existing = byPeriod.get(period) ?? [];
42
+ existing.push(pkg);
43
+ byPeriod.set(period, existing);
44
+ }
45
+ // For each period, sort by price and assign levels
46
+ for (const [_period, periodPackages] of byPeriod) {
47
+ // Sort by price ascending
48
+ const sorted = [...periodPackages].sort((a, b) => {
49
+ const priceA = a.product?.price ?? 0;
50
+ const priceB = b.product?.price ?? 0;
51
+ return priceA - priceB;
52
+ });
53
+ // Assign levels (1, 2, 3, ...)
54
+ // Same price = same level
55
+ let currentLevel = 0;
56
+ let lastPrice = null;
57
+ for (const pkg of sorted) {
58
+ const price = pkg.product?.price ?? 0;
59
+ if (lastPrice === null || price > lastPrice) {
60
+ currentLevel++;
61
+ lastPrice = price;
62
+ }
63
+ levels.set(pkg.packageId, currentLevel);
64
+ }
65
+ }
66
+ return levels;
67
+ }
68
+ /**
69
+ * Add level information to packages
70
+ *
71
+ * @param packages List of packages
72
+ * @returns Packages with level information
73
+ */
74
+ export function addLevelsToPackages(packages) {
75
+ const levels = calculatePackageLevels(packages);
76
+ return packages.map(pkg => ({
77
+ ...pkg,
78
+ level: levels.get(pkg.packageId) ?? 0,
79
+ }));
80
+ }
81
+ /**
82
+ * Get the level of a specific package
83
+ *
84
+ * @param packageId Package ID to look up
85
+ * @param packages All packages for context
86
+ * @returns Level number (0 for free, higher = better)
87
+ */
88
+ export function getPackageLevel(packageId, packages) {
89
+ const levels = calculatePackageLevels(packages);
90
+ return levels.get(packageId) ?? 0;
91
+ }
92
+ /**
93
+ * Find packages that are upgrades from the current package
94
+ *
95
+ * A package is an upgrade if:
96
+ * - Its period is >= current period, AND
97
+ * - Its level is >= current level (but not the exact same package)
98
+ *
99
+ * @param current Current package/product identifiers (or null/undefined for no subscription)
100
+ * @param packages All available packages
101
+ * @returns List of package IDs that are valid upgrades
102
+ */
103
+ export function findUpgradeablePackages(current, packages) {
104
+ // Normalize parameters
105
+ let currentPackageId;
106
+ let currentProductId;
107
+ if (typeof current === 'string') {
108
+ currentPackageId = current;
109
+ }
110
+ else if (current) {
111
+ currentPackageId = current.packageId;
112
+ currentProductId = current.productId;
113
+ }
114
+ // No current subscription = all packages are subscribable
115
+ if (!currentPackageId && !currentProductId) {
116
+ return packages.map(p => p.packageId);
117
+ }
118
+ // Try to find by packageId first, then by productId
119
+ let currentPkg = currentPackageId
120
+ ? packages.find(p => p.packageId === currentPackageId)
121
+ : undefined;
122
+ if (!currentPkg && currentProductId) {
123
+ currentPkg = packages.find(p => p.product?.productId === currentProductId);
124
+ }
125
+ if (!currentPkg) {
126
+ // Current package not found, return all
127
+ return packages.map(p => p.packageId);
128
+ }
129
+ // Use the matched package's ID (important when matched by productId)
130
+ const matchedPackageId = currentPkg.packageId;
131
+ // Free tier current = all paid packages are upgrades
132
+ if (!currentPkg.product) {
133
+ return packages.filter(p => p.product !== undefined).map(p => p.packageId);
134
+ }
135
+ const levels = calculatePackageLevels(packages);
136
+ const currentLevel = levels.get(matchedPackageId) ?? 0;
137
+ const currentPeriod = currentPkg.product.period;
138
+ const upgrades = [];
139
+ for (const pkg of packages) {
140
+ // Skip free tier (can't upgrade to free)
141
+ if (!pkg.product)
142
+ continue;
143
+ // Skip current package (by packageId)
144
+ if (pkg.packageId === matchedPackageId) {
145
+ continue;
146
+ }
147
+ const pkgLevel = levels.get(pkg.packageId) ?? 0;
148
+ const pkgPeriod = pkg.product.period;
149
+ // Must have >= period and >= level
150
+ const periodRanks = {
151
+ weekly: 1,
152
+ monthly: 2,
153
+ quarterly: 3,
154
+ yearly: 4,
155
+ lifetime: 5,
156
+ };
157
+ const isPeriodOk = periodRanks[pkgPeriod] >= periodRanks[currentPeriod];
158
+ const isLevelOk = pkgLevel >= currentLevel;
159
+ if (isPeriodOk && isLevelOk) {
160
+ upgrades.push(pkg.packageId);
161
+ }
162
+ }
163
+ return upgrades;
164
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Period Parser Utilities
3
+ *
4
+ * Parse ISO 8601 duration strings to standard subscription periods.
5
+ */
6
+ import type { SubscriptionPeriod } from '../types/period';
7
+ /**
8
+ * Parse ISO 8601 duration string to SubscriptionPeriod
9
+ *
10
+ * @param duration ISO 8601 duration (e.g., "P1M", "P1Y", "P7D", "P1W")
11
+ * @returns Parsed SubscriptionPeriod
12
+ *
13
+ * @example
14
+ * parseISO8601Period("P1M") // "monthly"
15
+ * parseISO8601Period("P1Y") // "yearly"
16
+ * parseISO8601Period("P7D") // "weekly"
17
+ * parseISO8601Period("P1W") // "weekly"
18
+ * parseISO8601Period("P3M") // "quarterly"
19
+ * parseISO8601Period(null) // "lifetime"
20
+ */
21
+ export declare function parseISO8601Period(duration: string | null | undefined): SubscriptionPeriod;
22
+ /**
23
+ * Get numeric rank for a period (higher = longer)
24
+ *
25
+ * @param period SubscriptionPeriod
26
+ * @returns Numeric rank (1-5)
27
+ */
28
+ export declare function getPeriodRank(period: SubscriptionPeriod): number;
29
+ /**
30
+ * Compare two periods
31
+ *
32
+ * @param a First period
33
+ * @param b Second period
34
+ * @returns Negative if a < b, 0 if equal, positive if a > b
35
+ */
36
+ export declare function comparePeriods(a: SubscriptionPeriod, b: SubscriptionPeriod): number;
37
+ /**
38
+ * Check if period A is greater than or equal to period B
39
+ *
40
+ * @param a Period to check
41
+ * @param b Period to compare against
42
+ * @returns true if a >= b
43
+ */
44
+ export declare function isPeriodGreaterOrEqual(a: SubscriptionPeriod, b: SubscriptionPeriod): boolean;
45
+ //# sourceMappingURL=period-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"period-parser.d.ts","sourceRoot":"","sources":["../../src/utils/period-parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAClC,kBAAkB,CA2DpB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAEhE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,CAAC,EAAE,kBAAkB,EACrB,CAAC,EAAE,kBAAkB,GACpB,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,CAAC,EAAE,kBAAkB,EACrB,CAAC,EAAE,kBAAkB,GACpB,OAAO,CAET"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Period Parser Utilities
3
+ *
4
+ * Parse ISO 8601 duration strings to standard subscription periods.
5
+ */
6
+ import { PERIOD_RANKS } from '../types/period';
7
+ /**
8
+ * Parse ISO 8601 duration string to SubscriptionPeriod
9
+ *
10
+ * @param duration ISO 8601 duration (e.g., "P1M", "P1Y", "P7D", "P1W")
11
+ * @returns Parsed SubscriptionPeriod
12
+ *
13
+ * @example
14
+ * parseISO8601Period("P1M") // "monthly"
15
+ * parseISO8601Period("P1Y") // "yearly"
16
+ * parseISO8601Period("P7D") // "weekly"
17
+ * parseISO8601Period("P1W") // "weekly"
18
+ * parseISO8601Period("P3M") // "quarterly"
19
+ * parseISO8601Period(null) // "lifetime"
20
+ */
21
+ export function parseISO8601Period(duration) {
22
+ if (!duration) {
23
+ return 'lifetime';
24
+ }
25
+ const normalized = duration.toUpperCase();
26
+ // Weekly: P1W or P7D
27
+ if (normalized === 'P1W' || normalized === 'P7D') {
28
+ return 'weekly';
29
+ }
30
+ // Monthly: P1M
31
+ if (normalized === 'P1M') {
32
+ return 'monthly';
33
+ }
34
+ // Quarterly: P3M
35
+ if (normalized === 'P3M') {
36
+ return 'quarterly';
37
+ }
38
+ // Yearly: P1Y or P12M
39
+ if (normalized === 'P1Y' || normalized === 'P12M') {
40
+ return 'yearly';
41
+ }
42
+ // Try to parse more complex durations
43
+ const match = normalized.match(/^P(\d+)([DWMY])$/);
44
+ if (match) {
45
+ const value = parseInt(match[1], 10);
46
+ const unit = match[2];
47
+ switch (unit) {
48
+ case 'D':
49
+ if (value === 7)
50
+ return 'weekly';
51
+ if (value >= 28 && value <= 31)
52
+ return 'monthly';
53
+ if (value >= 84 && value <= 93)
54
+ return 'quarterly';
55
+ if (value >= 365)
56
+ return 'yearly';
57
+ break;
58
+ case 'W':
59
+ if (value === 1)
60
+ return 'weekly';
61
+ if (value === 4 || value === 5)
62
+ return 'monthly';
63
+ if (value === 13)
64
+ return 'quarterly';
65
+ if (value === 52)
66
+ return 'yearly';
67
+ break;
68
+ case 'M':
69
+ if (value === 1)
70
+ return 'monthly';
71
+ if (value === 3)
72
+ return 'quarterly';
73
+ if (value === 6)
74
+ return 'quarterly'; // Treat 6 months as quarterly for simplicity
75
+ if (value === 12)
76
+ return 'yearly';
77
+ break;
78
+ case 'Y':
79
+ return 'yearly';
80
+ }
81
+ }
82
+ // Default to monthly for unrecognized patterns
83
+ return 'monthly';
84
+ }
85
+ /**
86
+ * Get numeric rank for a period (higher = longer)
87
+ *
88
+ * @param period SubscriptionPeriod
89
+ * @returns Numeric rank (1-5)
90
+ */
91
+ export function getPeriodRank(period) {
92
+ return PERIOD_RANKS[period];
93
+ }
94
+ /**
95
+ * Compare two periods
96
+ *
97
+ * @param a First period
98
+ * @param b Second period
99
+ * @returns Negative if a < b, 0 if equal, positive if a > b
100
+ */
101
+ export function comparePeriods(a, b) {
102
+ return getPeriodRank(a) - getPeriodRank(b);
103
+ }
104
+ /**
105
+ * Check if period A is greater than or equal to period B
106
+ *
107
+ * @param a Period to check
108
+ * @param b Period to compare against
109
+ * @returns true if a >= b
110
+ */
111
+ export function isPeriodGreaterOrEqual(a, b) {
112
+ return getPeriodRank(a) >= getPeriodRank(b);
113
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@sudobility/subscription_lib",
3
+ "version": "0.0.1",
4
+ "description": "Cross-platform subscription management with RevenueCat adapter pattern",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "dev": "tsc --watch",
22
+ "clean": "rm -rf dist",
23
+ "typecheck": "bunx tsc --noEmit",
24
+ "lint": "bunx eslint src",
25
+ "lint:fix": "bunx eslint src --fix",
26
+ "format": "bunx prettier --write \"src/**/*.{ts,tsx,js,jsx,json}\"",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest",
29
+ "prepublishOnly": "bun run build"
30
+ },
31
+ "peerDependencies": {
32
+ "react": "^19.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "vitest": "^4.0.4",
36
+ "@eslint/js": "^9.0.0",
37
+ "@types/bun": "^1.2.8",
38
+ "@types/react": "^19.2.5",
39
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
40
+ "@typescript-eslint/parser": "^8.0.0",
41
+ "eslint": "^9.0.0",
42
+ "eslint-config-prettier": "^10.0.0",
43
+ "eslint-plugin-prettier": "^5.0.0",
44
+ "prettier": "^3.0.0",
45
+ "typescript": "~5.9.3"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/sudobility/subscription_lib.git"
53
+ },
54
+ "license": "MIT"
55
+ }