@umituz/react-native-subscription 2.27.114 → 2.27.116
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/package.json +1 -1
- package/src/domains/credits/infrastructure/CreditsRepository.ts +16 -19
- package/src/domains/credits/utils/creditCalculations.ts +6 -11
- package/src/domains/subscription/application/SubscriptionSyncService.ts +3 -0
- package/src/domains/subscription/infrastructure/services/CustomerInfoListenerManager.ts +3 -0
- package/src/domains/subscription/infrastructure/services/RevenueCatInitializer.ts +1 -0
- package/src/domains/subscription/infrastructure/services/RevenueCatService.ts +1 -0
- package/src/domains/subscription/infrastructure/utils/PremiumStatusSyncer.ts +3 -0
- package/src/domains/subscription/presentation/useAuthAwarePurchase.ts +4 -8
- package/src/domains/wallet/infrastructure/repositories/TransactionRepository.ts +18 -42
- package/src/domains/wallet/infrastructure/services/ProductMetadataService.ts +2 -6
- package/src/shared/infrastructure/SubscriptionEventBus.ts +1 -0
- package/src/shared/infrastructure/firestore/collectionUtils.ts +67 -0
- package/src/shared/infrastructure/firestore/index.ts +6 -0
- package/src/shared/infrastructure/firestore/resultUtils.ts +68 -0
- package/src/shared/infrastructure/index.ts +6 -0
- package/src/shared/presentation/hooks/index.ts +6 -0
- package/src/shared/presentation/hooks/useAsyncState.ts +72 -0
- package/src/shared/presentation/hooks/useServiceCall.ts +66 -0
- package/src/shared/types/CommonTypes.ts +6 -1
- package/src/shared/types/ReactTypes.ts +80 -0
- package/src/shared/utils/Result.ts +0 -16
- package/src/shared/utils/arrayUtils.core.ts +81 -0
- package/src/shared/utils/arrayUtils.query.ts +118 -0
- package/src/shared/utils/arrayUtils.transforms.ts +116 -0
- package/src/shared/utils/arrayUtils.ts +19 -0
- package/src/shared/utils/index.ts +14 -0
- package/src/shared/utils/numberUtils.aggregate.ts +35 -0
- package/src/shared/utils/numberUtils.core.ts +73 -0
- package/src/shared/utils/numberUtils.format.ts +42 -0
- package/src/shared/utils/numberUtils.math.ts +48 -0
- package/src/shared/utils/numberUtils.ts +9 -0
- package/src/shared/utils/stringUtils.case.ts +64 -0
- package/src/shared/utils/stringUtils.check.ts +65 -0
- package/src/shared/utils/stringUtils.format.ts +84 -0
- package/src/shared/utils/stringUtils.generate.ts +47 -0
- package/src/shared/utils/stringUtils.modify.ts +67 -0
- package/src/shared/utils/stringUtils.ts +10 -0
- package/src/shared/utils/validators.ts +187 -0
- package/src/utils/dateUtils.compare.ts +65 -0
- package/src/utils/dateUtils.core.ts +67 -0
- package/src/utils/dateUtils.format.ts +138 -0
- package/src/utils/dateUtils.math.ts +112 -0
- package/src/utils/dateUtils.ts +6 -28
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Shared types used across multiple domains
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import type { Platform as SubscriptionPlatform } from "../../domains/subscription/core/SubscriptionConstants";
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Purchase result from any purchase operation
|
|
8
10
|
*/
|
|
@@ -57,9 +59,12 @@ export interface TransactionMetadata {
|
|
|
57
59
|
/**
|
|
58
60
|
* Platform information
|
|
59
61
|
*/
|
|
60
|
-
export type Platform =
|
|
62
|
+
export type Platform = SubscriptionPlatform;
|
|
61
63
|
|
|
62
64
|
/**
|
|
63
65
|
* Purchase source tracking
|
|
64
66
|
*/
|
|
65
67
|
export type PurchaseSource = 'settings' | 'paywall' | 'upgrade_prompt' | 'auto-execution' | 'manual';
|
|
68
|
+
|
|
69
|
+
// Re-export from SubscriptionConstants to maintain compatibility
|
|
70
|
+
export { PLATFORM, PURCHASE_SOURCE, type Platform as SubscriptionPlatformType, type PurchaseSource as PurchaseSourceType } from "../../domains/subscription/core/SubscriptionConstants";
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared React Types
|
|
3
|
+
* Common type definitions for React components and hooks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type VoidCallback = () => void;
|
|
7
|
+
export type AsyncCallback = () => Promise<void> | Promise<void>;
|
|
8
|
+
|
|
9
|
+
export type EventHandler<T = void> = (event: T) => void;
|
|
10
|
+
export type AsyncEventHandler<T = void> = (event: T) => Promise<void>;
|
|
11
|
+
|
|
12
|
+
export interface LoadingState {
|
|
13
|
+
isLoading: boolean;
|
|
14
|
+
isRefreshing: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface ErrorState {
|
|
18
|
+
error: Error | null;
|
|
19
|
+
hasError: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PaginationState {
|
|
23
|
+
page: number;
|
|
24
|
+
perPage: number;
|
|
25
|
+
total: number;
|
|
26
|
+
hasMore: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ScreenProps<T = unknown> {
|
|
30
|
+
route: {
|
|
31
|
+
params?: T;
|
|
32
|
+
};
|
|
33
|
+
navigation: any;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type ButtonProps = {
|
|
37
|
+
onPress: EventHandler | AsyncEventHandler;
|
|
38
|
+
disabled?: boolean;
|
|
39
|
+
loading?: boolean;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export interface SelectableItem {
|
|
43
|
+
id: string;
|
|
44
|
+
selected: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface FormFieldProps<T> {
|
|
48
|
+
value: T;
|
|
49
|
+
onChange: (value: T) => void;
|
|
50
|
+
error?: string;
|
|
51
|
+
label?: string;
|
|
52
|
+
placeholder?: string;
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ListItemProps<T> {
|
|
57
|
+
item: T;
|
|
58
|
+
index: number;
|
|
59
|
+
onPress?: (item: T, index: number) => void;
|
|
60
|
+
onLongPress?: (item: T, index: number) => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface SearchState {
|
|
64
|
+
query: string;
|
|
65
|
+
results: unknown[];
|
|
66
|
+
isSearching: boolean;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface TabItem {
|
|
70
|
+
id: string;
|
|
71
|
+
title: string;
|
|
72
|
+
icon?: string;
|
|
73
|
+
badge?: number | string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ModalProps {
|
|
77
|
+
visible: boolean;
|
|
78
|
+
onClose: () => void;
|
|
79
|
+
title?: string;
|
|
80
|
+
}
|
|
@@ -114,22 +114,6 @@ export function tryCatchSync<T>(
|
|
|
114
114
|
return failure(mappedError);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
|
-
export function tryCatchSync<T>(
|
|
118
|
-
fn: () => T,
|
|
119
|
-
errorMapper?: (error: unknown) => Error
|
|
120
|
-
): Result<T, Error> {
|
|
121
|
-
try {
|
|
122
|
-
const data = fn();
|
|
123
|
-
return success(data);
|
|
124
|
-
} catch {
|
|
125
|
-
const mappedError = errorMapper
|
|
126
|
-
? errorMapper(error)
|
|
127
|
-
: error instanceof Error
|
|
128
|
-
? error
|
|
129
|
-
: new Error(String(error));
|
|
130
|
-
return failure(mappedError);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
117
|
|
|
134
118
|
/** Combine multiple results into one */
|
|
135
119
|
export function combine<T, E>(results: Result<T, E>[]): Result<T[], E> {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array Utilities - Core Operations
|
|
3
|
+
* Basic array manipulation functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if array is empty
|
|
8
|
+
*/
|
|
9
|
+
export function isEmpty<T>(arr: readonly T[] | null | undefined): boolean {
|
|
10
|
+
return !arr || arr.length === 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if array has elements
|
|
15
|
+
*/
|
|
16
|
+
export function isNotEmpty<T>(arr: readonly T[] | null | undefined): boolean {
|
|
17
|
+
return !isEmpty(arr);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get first element of array or null
|
|
22
|
+
*/
|
|
23
|
+
export function first<T>(arr: readonly T[] | null | undefined): T | null {
|
|
24
|
+
if (!arr || arr.length === 0) return null;
|
|
25
|
+
return arr[0] ?? null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Get last element of array or null
|
|
30
|
+
*/
|
|
31
|
+
export function last<T>(arr: readonly T[] | null | undefined): T | null {
|
|
32
|
+
if (!arr || arr.length === 0) return null;
|
|
33
|
+
return arr[arr.length - 1] ?? null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Get nth element of array or null
|
|
38
|
+
*/
|
|
39
|
+
export function nth<T>(arr: readonly T[], index: number): T | null {
|
|
40
|
+
if (index < 0) index = arr.length + index;
|
|
41
|
+
if (index < 0 || index >= arr.length) return null;
|
|
42
|
+
return arr[index] ?? null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Remove first element from array
|
|
47
|
+
*/
|
|
48
|
+
export function removeFirst<T>(arr: T[]): T[] {
|
|
49
|
+
return arr.slice(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Remove last element from array
|
|
54
|
+
*/
|
|
55
|
+
export function removeLast<T>(arr: T[]): T[] {
|
|
56
|
+
return arr.slice(0, -1);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Remove element at index
|
|
61
|
+
*/
|
|
62
|
+
export function removeAt<T>(arr: readonly T[], index: number): T[] {
|
|
63
|
+
return arr.filter((_, i) => i !== index);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Remove elements that match a predicate
|
|
68
|
+
*/
|
|
69
|
+
export function removeWhere<T>(
|
|
70
|
+
arr: readonly T[],
|
|
71
|
+
predicate: (item: T, index: number) => boolean
|
|
72
|
+
): T[] {
|
|
73
|
+
return arr.filter((item, index) => !predicate(item, index));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Unique array elements (removes duplicates)
|
|
78
|
+
*/
|
|
79
|
+
export function unique<T>(arr: readonly T[]): T[] {
|
|
80
|
+
return Array.from(new Set(arr));
|
|
81
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array Utilities - Query Operations
|
|
3
|
+
* Array searching, filtering, and grouping functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Group array elements by a key function
|
|
8
|
+
*/
|
|
9
|
+
export function groupBy<T, K extends string | number | symbol>(
|
|
10
|
+
arr: readonly T[],
|
|
11
|
+
keyFn: (item: T, index: number) => K
|
|
12
|
+
): Record<K, T[]> {
|
|
13
|
+
return arr.reduce((result, item, index) => {
|
|
14
|
+
const key = keyFn(item, index);
|
|
15
|
+
if (!result[key]) {
|
|
16
|
+
result[key] = [];
|
|
17
|
+
}
|
|
18
|
+
result[key].push(item);
|
|
19
|
+
return result;
|
|
20
|
+
}, {} as Record<K, T[]>);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Unique array elements by a key function
|
|
25
|
+
*/
|
|
26
|
+
export function uniqueBy<T, K>(arr: readonly T[], keyFn: (item: T) => K): T[] {
|
|
27
|
+
const seen = new Set<K>();
|
|
28
|
+
return arr.filter((item) => {
|
|
29
|
+
const key = keyFn(item);
|
|
30
|
+
if (seen.has(key)) return false;
|
|
31
|
+
seen.add(key);
|
|
32
|
+
return true;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Shuffle array (Fisher-Yates algorithm)
|
|
38
|
+
*/
|
|
39
|
+
export function shuffle<T>(arr: readonly T[]): T[] {
|
|
40
|
+
const result = [...arr];
|
|
41
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
42
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
43
|
+
const temp = result[i]!;
|
|
44
|
+
result[i] = result[j]!;
|
|
45
|
+
result[j] = temp;
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sort array by a key function
|
|
52
|
+
*/
|
|
53
|
+
export function sortBy<T>(
|
|
54
|
+
arr: readonly T[],
|
|
55
|
+
keyFn: (item: T) => string | number,
|
|
56
|
+
order: "asc" | "desc" = "asc"
|
|
57
|
+
): T[] {
|
|
58
|
+
return [...arr].sort((a, b) => {
|
|
59
|
+
const keyA = keyFn(a);
|
|
60
|
+
const keyB = keyFn(b);
|
|
61
|
+
if (keyA < keyB) return order === "asc" ? -1 : 1;
|
|
62
|
+
if (keyA > keyB) return order === "asc" ? 1 : -1;
|
|
63
|
+
return 0;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Pick random element(s) from array
|
|
69
|
+
*/
|
|
70
|
+
export function sample<T>(arr: readonly T[], count: number = 1): T[] {
|
|
71
|
+
if (arr.length === 0) return [];
|
|
72
|
+
const shuffled = shuffle([...arr]);
|
|
73
|
+
return shuffled.slice(0, Math.min(count, arr.length));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Find intersection of two arrays
|
|
78
|
+
*/
|
|
79
|
+
export function intersection<T>(arr1: readonly T[], arr2: readonly T[]): T[] {
|
|
80
|
+
const set2 = new Set(arr2);
|
|
81
|
+
return arr1.filter((item) => set2.has(item));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Find difference of two arrays (elements in arr1 but not in arr2)
|
|
86
|
+
*/
|
|
87
|
+
export function difference<T>(arr1: readonly T[], arr2: readonly T[]): T[] {
|
|
88
|
+
const set2 = new Set(arr2);
|
|
89
|
+
return arr1.filter((item) => !set2.has(item));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Find symmetric difference of two arrays (elements in either array but not both)
|
|
94
|
+
*/
|
|
95
|
+
export function symmetricDifference<T>(arr1: readonly T[], arr2: readonly T[]): T[] {
|
|
96
|
+
const set1 = new Set(arr1);
|
|
97
|
+
const set2 = new Set(arr2);
|
|
98
|
+
return [
|
|
99
|
+
...arr1.filter((item) => !set2.has(item)),
|
|
100
|
+
...arr2.filter((item) => !set1.has(item)),
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Check if array contains all elements of another array
|
|
106
|
+
*/
|
|
107
|
+
export function containsAll<T>(arr: readonly T[], values: readonly T[]): boolean {
|
|
108
|
+
const arrSet = new Set(arr);
|
|
109
|
+
return values.every((value) => arrSet.has(value));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if array contains any element of another array
|
|
114
|
+
*/
|
|
115
|
+
export function containsAny<T>(arr: readonly T[], values: readonly T[]): boolean {
|
|
116
|
+
const arrSet = new Set(arr);
|
|
117
|
+
return values.some((value) => arrSet.has(value));
|
|
118
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array Utilities - Transformations
|
|
3
|
+
* Array transformation and manipulation functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Chunk array into smaller arrays of specified size
|
|
8
|
+
*/
|
|
9
|
+
export function chunk<T>(arr: readonly T[], size: number): T[][] {
|
|
10
|
+
if (size <= 0) return [];
|
|
11
|
+
const result: T[][] = [];
|
|
12
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
13
|
+
result.push(arr.slice(i, i + size));
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Flatten array one level deep
|
|
20
|
+
*/
|
|
21
|
+
export function flatten<T>(arr: readonly (readonly T[] | T)[]): T[] {
|
|
22
|
+
const result: T[] = [];
|
|
23
|
+
for (const item of arr) {
|
|
24
|
+
if (Array.isArray(item)) {
|
|
25
|
+
result.push(...item);
|
|
26
|
+
} else if (item !== undefined) {
|
|
27
|
+
result.push(item as T);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Flatten array recursively
|
|
35
|
+
*/
|
|
36
|
+
export function flattenDeep<T>(arr: readonly unknown[]): T[] {
|
|
37
|
+
const result: T[] = [];
|
|
38
|
+
const stack = [...arr];
|
|
39
|
+
|
|
40
|
+
while (stack.length > 0) {
|
|
41
|
+
const item = stack.shift();
|
|
42
|
+
if (Array.isArray(item)) {
|
|
43
|
+
stack.unshift(...item);
|
|
44
|
+
} else if (item !== undefined) {
|
|
45
|
+
result.push(item as T);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Zip two arrays together
|
|
54
|
+
*/
|
|
55
|
+
export function zip<T, U>(arr1: readonly T[], arr2: readonly U[]): [T, U][] {
|
|
56
|
+
const length = Math.min(arr1.length, arr2.length);
|
|
57
|
+
const result: [T, U][] = [];
|
|
58
|
+
for (let i = 0; i < length; i++) {
|
|
59
|
+
result.push([arr1[i]!, arr2[i]!]);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Partition array into two arrays based on predicate
|
|
66
|
+
*/
|
|
67
|
+
export function partition<T>(
|
|
68
|
+
arr: readonly T[],
|
|
69
|
+
predicate: (item: T, index: number) => boolean
|
|
70
|
+
): [T[], T[]] {
|
|
71
|
+
const truthy: T[] = [];
|
|
72
|
+
const falsy: T[] = [];
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < arr.length; i++) {
|
|
75
|
+
const item = arr[i];
|
|
76
|
+
if (item !== undefined) {
|
|
77
|
+
if (predicate(item, i)) {
|
|
78
|
+
truthy.push(item);
|
|
79
|
+
} else {
|
|
80
|
+
falsy.push(item);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return [truthy, falsy];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Move element from one index to another
|
|
90
|
+
*/
|
|
91
|
+
export function move<T>(arr: readonly T[], from: number, to: number): T[] {
|
|
92
|
+
if (from < 0 || from >= arr.length || to < 0 || to >= arr.length) {
|
|
93
|
+
return [...arr];
|
|
94
|
+
}
|
|
95
|
+
const result = [...arr];
|
|
96
|
+
const element = result[from];
|
|
97
|
+
if (element === undefined) return result;
|
|
98
|
+
|
|
99
|
+
result.splice(from, 1);
|
|
100
|
+
result.splice(to, 0, element);
|
|
101
|
+
return result;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Swap two elements in array
|
|
106
|
+
*/
|
|
107
|
+
export function swap<T>(arr: readonly T[], index1: number, index2: number): T[] {
|
|
108
|
+
if (index1 < 0 || index1 >= arr.length || index2 < 0 || index2 >= arr.length) {
|
|
109
|
+
return [...arr];
|
|
110
|
+
}
|
|
111
|
+
const result = [...arr];
|
|
112
|
+
const temp = result[index1];
|
|
113
|
+
result[index1] = result[index2]!;
|
|
114
|
+
result[index2] = temp!;
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Array Utilities
|
|
3
|
+
* Re-exports all array utility modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export * from "./arrayUtils.core";
|
|
7
|
+
export * from "./arrayUtils.transforms";
|
|
8
|
+
export * from "./arrayUtils.query";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create an array of numbers from start to end (inclusive)
|
|
12
|
+
*/
|
|
13
|
+
export function range(start: number, end: number, step: number = 1): number[] {
|
|
14
|
+
const result: number[] = [];
|
|
15
|
+
for (let i = start; step > 0 ? i <= end : i >= end; i += step) {
|
|
16
|
+
result.push(i);
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export * from "./arrayUtils";
|
|
6
|
+
export * from "./BaseError";
|
|
7
|
+
export * from "./InsufficientCreditsError";
|
|
8
|
+
export * from "./Logger";
|
|
9
|
+
export * from "./numberUtils";
|
|
10
|
+
export * from "./Result";
|
|
11
|
+
export * from "./stringUtils";
|
|
12
|
+
export * from "./SubscriptionConfig";
|
|
13
|
+
export * from "./SubscriptionError";
|
|
14
|
+
export * from "./validators";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number Utilities - Aggregation
|
|
3
|
+
* Array aggregation functions for numbers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Calculate the sum of an array of numbers
|
|
8
|
+
*/
|
|
9
|
+
export function sum(values: number[]): number {
|
|
10
|
+
return values.reduce((acc, val) => acc + val, 0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Calculate the average of an array of numbers
|
|
15
|
+
*/
|
|
16
|
+
export function average(values: number[]): number {
|
|
17
|
+
if (values.length === 0) return 0;
|
|
18
|
+
return sum(values) / values.length;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Find the minimum value in an array
|
|
23
|
+
*/
|
|
24
|
+
export function min(values: number[]): number | null {
|
|
25
|
+
if (values.length === 0) return null;
|
|
26
|
+
return Math.min(...values);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Find the maximum value in an array
|
|
31
|
+
*/
|
|
32
|
+
export function max(values: number[]): number | null {
|
|
33
|
+
if (values.length === 0) return null;
|
|
34
|
+
return Math.max(...values);
|
|
35
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number Utilities - Core Operations
|
|
3
|
+
* Basic numeric calculation functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Clamp a number between min and max values
|
|
8
|
+
*/
|
|
9
|
+
export function clamp(value: number, min: number, max: number): number {
|
|
10
|
+
return Math.min(Math.max(value, min), max);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Round a number to specified decimal places
|
|
15
|
+
*/
|
|
16
|
+
export function roundTo(value: number, decimals: number = 2): number {
|
|
17
|
+
const multiplier = Math.pow(10, decimals);
|
|
18
|
+
return Math.round(value * multiplier) / multiplier;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Calculate percentage
|
|
23
|
+
*/
|
|
24
|
+
export function calculatePercentage(value: number, total: number): number {
|
|
25
|
+
if (total === 0) return 0;
|
|
26
|
+
return (value / total) * 100;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Calculate percentage and clamp between 0-100
|
|
31
|
+
*/
|
|
32
|
+
export function calculatePercentageClamped(value: number, total: number): number {
|
|
33
|
+
return clamp(calculatePercentage(value, total), 0, 100);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Check if two numbers are approximately equal (within epsilon)
|
|
38
|
+
*/
|
|
39
|
+
export function isApproximatelyEqual(a: number, b: number, epsilon: number = 0.0001): boolean {
|
|
40
|
+
return Math.abs(a - b) < epsilon;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Safe division that returns 0 instead of NaN
|
|
45
|
+
*/
|
|
46
|
+
export function safeDivide(numerator: number, denominator: number): number {
|
|
47
|
+
if (denominator === 0) return 0;
|
|
48
|
+
return numerator / denominator;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Calculate remaining value after subtraction with floor at 0
|
|
53
|
+
*/
|
|
54
|
+
export function calculateRemaining(current: number, cost: number): number {
|
|
55
|
+
return Math.max(0, current - cost);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Check if user can afford a cost
|
|
60
|
+
*/
|
|
61
|
+
export function canAfford(balance: number | null | undefined, cost: number): boolean {
|
|
62
|
+
if (balance === null || balance === undefined) return false;
|
|
63
|
+
return balance >= cost;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Calculate credit percentage for UI display
|
|
68
|
+
*/
|
|
69
|
+
export function calculateCreditPercentage(current: number | null | undefined, max: number): number {
|
|
70
|
+
if (current === null || current === undefined) return 0;
|
|
71
|
+
if (max <= 0) return 100;
|
|
72
|
+
return calculatePercentageClamped(current, max);
|
|
73
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number Utilities - Formatting
|
|
3
|
+
* Number formatting and display functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format number with thousands separator
|
|
8
|
+
*/
|
|
9
|
+
export function formatNumber(value: number, locale: string = "en-US"): string {
|
|
10
|
+
return new Intl.NumberFormat(locale).format(value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Format number as compact string (e.g., 1K, 1M, 1B)
|
|
15
|
+
*/
|
|
16
|
+
export function formatCompactNumber(value: number, locale: string = "en-US"): string {
|
|
17
|
+
return new Intl.NumberFormat(locale, {
|
|
18
|
+
notation: "compact",
|
|
19
|
+
compactDisplay: "short",
|
|
20
|
+
}).format(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Round to nearest multiple (e.g., round to nearest 0.99)
|
|
25
|
+
*/
|
|
26
|
+
export function roundToNearest(value: number, multiple: number): number {
|
|
27
|
+
return Math.round(value / multiple) * multiple;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Floor to nearest multiple
|
|
32
|
+
*/
|
|
33
|
+
export function floorToNearest(value: number, multiple: number): number {
|
|
34
|
+
return Math.floor(value / multiple) * multiple;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ceiling to nearest multiple
|
|
39
|
+
*/
|
|
40
|
+
export function ceilToNearest(value: number, multiple: number): number {
|
|
41
|
+
return Math.ceil(value / multiple) * multiple;
|
|
42
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Number Utilities - Math Operations
|
|
3
|
+
* Advanced mathematical operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { clamp } from "./numberUtils.core";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Linear interpolation between two values
|
|
10
|
+
*/
|
|
11
|
+
export function lerp(start: number, end: number, progress: number): number {
|
|
12
|
+
return start + (end - start) * clamp(progress, 0, 1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Map a value from one range to another
|
|
17
|
+
*/
|
|
18
|
+
export function mapRange(
|
|
19
|
+
value: number,
|
|
20
|
+
inMin: number,
|
|
21
|
+
inMax: number,
|
|
22
|
+
outMin: number,
|
|
23
|
+
outMax: number
|
|
24
|
+
): number {
|
|
25
|
+
const progress = (value - inMin) / (inMax - inMin);
|
|
26
|
+
return lerp(outMin, outMax, progress);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Check if a number is within range (inclusive)
|
|
31
|
+
*/
|
|
32
|
+
export function isInRange(value: number, min: number, max: number): boolean {
|
|
33
|
+
return value >= min && value <= max;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Calculate tax amount
|
|
38
|
+
*/
|
|
39
|
+
export function calculateTax(subtotal: number, taxRate: number): number {
|
|
40
|
+
return subtotal * (taxRate / 100);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Calculate total with tax
|
|
45
|
+
*/
|
|
46
|
+
export function calculateTotalWithTax(subtotal: number, taxRate: number): number {
|
|
47
|
+
return subtotal + calculateTax(subtotal, taxRate);
|
|
48
|
+
}
|