@umituz/react-native-subscription 2.27.115 → 2.27.117
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 +4 -4
- package/src/domains/credits/infrastructure/CreditsRepository.ts +16 -19
- package/src/domains/credits/utils/creditCalculations.ts +6 -11
- package/src/domains/wallet/infrastructure/repositories/TransactionRepository.ts +17 -41
- package/src/domains/wallet/infrastructure/services/ProductMetadataService.ts +2 -6
- 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/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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Utilities - Case Conversion
|
|
3
|
+
* String case transformation functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Capitalize first letter of a string
|
|
8
|
+
*/
|
|
9
|
+
export function capitalize(str: string): string {
|
|
10
|
+
if (!str) return "";
|
|
11
|
+
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Capitalize first letter of each word
|
|
16
|
+
*/
|
|
17
|
+
export function titleCase(str: string): string {
|
|
18
|
+
if (!str) return "";
|
|
19
|
+
return str
|
|
20
|
+
.toLowerCase()
|
|
21
|
+
.split(" ")
|
|
22
|
+
.map((word) => capitalize(word))
|
|
23
|
+
.join(" ");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Convert string to kebab-case
|
|
28
|
+
*/
|
|
29
|
+
export function kebabCase(str: string): string {
|
|
30
|
+
if (!str) return "";
|
|
31
|
+
return str
|
|
32
|
+
.replace(/([a-z])([A-Z])/g, "$1-$2")
|
|
33
|
+
.replace(/[\s_]+/g, "-")
|
|
34
|
+
.toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Convert string to snake_case
|
|
39
|
+
*/
|
|
40
|
+
export function snakeCase(str: string): string {
|
|
41
|
+
if (!str) return "";
|
|
42
|
+
return str
|
|
43
|
+
.replace(/([a-z])([A-Z])/g, "$1_$2")
|
|
44
|
+
.replace(/[\s-]+/g, "_")
|
|
45
|
+
.toLowerCase();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Convert string to camelCase
|
|
50
|
+
*/
|
|
51
|
+
export function camelCase(str: string): string {
|
|
52
|
+
if (!str) return "";
|
|
53
|
+
return str
|
|
54
|
+
.replace(/[-_\s]+(.)?/g, (_, char) => (char ? char.toUpperCase() : ""))
|
|
55
|
+
.replace(/^[A-Z]/, (char) => char.toLowerCase());
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Convert string to PascalCase
|
|
60
|
+
*/
|
|
61
|
+
export function pascalCase(str: string): string {
|
|
62
|
+
if (!str) return "";
|
|
63
|
+
return capitalize(camelCase(str));
|
|
64
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Utilities - Check Operations
|
|
3
|
+
* String validation and comparison functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Check if value is a non-empty string
|
|
8
|
+
*/
|
|
9
|
+
function isNonEmptyString(value: unknown): value is string {
|
|
10
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check if string contains a substring (case insensitive)
|
|
15
|
+
*/
|
|
16
|
+
export function containsIgnoreCase(str: string, search: string): boolean {
|
|
17
|
+
return str.toLowerCase().includes(search.toLowerCase());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Check if string starts with a prefix (case insensitive)
|
|
22
|
+
*/
|
|
23
|
+
export function startsWithIgnoreCase(str: string, prefix: string): boolean {
|
|
24
|
+
return str.toLowerCase().startsWith(prefix.toLowerCase());
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Check if string ends with a suffix (case insensitive)
|
|
29
|
+
*/
|
|
30
|
+
export function endsWithIgnoreCase(str: string, suffix: string): boolean {
|
|
31
|
+
return str.toLowerCase().endsWith(suffix.toLowerCase());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check if a string is a valid email (basic validation)
|
|
36
|
+
*/
|
|
37
|
+
export function isValidEmailString(value: unknown): value is string {
|
|
38
|
+
if (!isNonEmptyString(value)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
42
|
+
return emailRegex.test(value);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Check if value is a valid URL (basic validation)
|
|
47
|
+
*/
|
|
48
|
+
export function isValidUrlString(value: unknown): value is string {
|
|
49
|
+
if (!isNonEmptyString(value)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
new URL(value as string);
|
|
54
|
+
return true;
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Check if a string is a valid product identifier
|
|
62
|
+
*/
|
|
63
|
+
export function isValidProductIdString(value: unknown): value is string {
|
|
64
|
+
return isNonEmptyString(value) && /^[a-zA-Z0-9._-]+$/.test(value as string);
|
|
65
|
+
}
|