@cranberry-money/shared-utils 4.11.0 → 4.13.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.
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Generic collection and array utilities
3
+ * Pure functions for common array operations with type safety
4
+ */
5
+ /**
6
+ * Sort array of objects by a string field
7
+ * @param items - Array of objects to sort
8
+ * @param fieldName - Name of the field to sort by
9
+ * @returns New sorted array
10
+ */
11
+ export declare function sortByStringField<T>(items: T[], fieldName: keyof T): T[];
12
+ /**
13
+ * Filter array by text search in a specific field (case-insensitive)
14
+ * @param items - Array of objects to filter
15
+ * @param fieldName - Name of the field to search in
16
+ * @param searchTerm - Search term
17
+ * @returns Filtered array
18
+ */
19
+ export declare function filterByTextSearch<T>(items: T[], fieldName: keyof T, searchTerm: string): T[];
20
+ /**
21
+ * Filter array by boolean field
22
+ * @param items - Array of objects to filter
23
+ * @param fieldName - Name of the boolean field
24
+ * @param value - Boolean value to filter by
25
+ * @returns Filtered array
26
+ */
27
+ export declare function filterByBooleanField<T>(items: T[], fieldName: keyof T, value: boolean): T[];
28
+ /**
29
+ * Find item by exact field match
30
+ * @param items - Array of objects to search
31
+ * @param fieldName - Name of the field to match
32
+ * @param value - Value to match
33
+ * @returns Found item or undefined
34
+ */
35
+ export declare function findByField<T>(items: T[], fieldName: keyof T, value: unknown): T | undefined;
36
+ /**
37
+ * Find item by case-insensitive string field match
38
+ * @param items - Array of objects to search
39
+ * @param fieldName - Name of the string field to match
40
+ * @param value - String value to match (case-insensitive)
41
+ * @returns Found item or undefined
42
+ */
43
+ export declare function findByStringField<T>(items: T[], fieldName: keyof T, value: string): T | undefined;
44
+ /**
45
+ * Extract values from a specific field and sort them
46
+ * @param items - Array of objects
47
+ * @param fieldName - Name of the field to extract
48
+ * @returns Sorted array of extracted values
49
+ */
50
+ export declare function extractAndSortField<T, K extends keyof T>(items: T[], fieldName: K): T[K][];
51
+ /**
52
+ * Group array items by the first character of a string field
53
+ * @param items - Array of objects to group
54
+ * @param fieldName - Name of the string field to group by
55
+ * @returns Object with first letters as keys and arrays of items as values
56
+ */
57
+ export declare function groupByFirstLetter<T>(items: T[], fieldName: keyof T): Record<string, T[]>;
58
+ /**
59
+ * Group array items by a field value
60
+ * @param items - Array of objects to group
61
+ * @param fieldName - Name of the field to group by
62
+ * @returns Object with field values as keys and arrays of items as values
63
+ */
64
+ export declare function groupByField<T, K extends keyof T>(items: T[], fieldName: K): Record<string, T[]>;
65
+ /**
66
+ * Check if any item in array has a specific boolean field value
67
+ * @param items - Array of objects to check
68
+ * @param fieldName - Name of the boolean field
69
+ * @param value - Boolean value to check for
70
+ * @returns true if any item matches, false otherwise
71
+ */
72
+ export declare function hasItemWithFieldValue<T>(items: T[], fieldName: keyof T, value: unknown): boolean;
73
+ /**
74
+ * Count items that match a field value
75
+ * @param items - Array of objects to count
76
+ * @param fieldName - Name of the field to check
77
+ * @param value - Value to count
78
+ * @returns Number of matching items
79
+ */
80
+ export declare function countByFieldValue<T>(items: T[], fieldName: keyof T, value: unknown): number;
81
+ //# sourceMappingURL=collections.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collections.d.ts","sourceRoot":"","sources":["../src/collections.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAMxE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,CAAC,EAAE,CAM7F;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,EAAE,CAE3F;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,CAAC,GAAG,SAAS,CAE5F;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAMjG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAE1F;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAazF;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAYhG;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAEhG;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,OAAO,GAAG,MAAM,CAE3F"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Generic collection and array utilities
3
+ * Pure functions for common array operations with type safety
4
+ */
5
+ /**
6
+ * Sort array of objects by a string field
7
+ * @param items - Array of objects to sort
8
+ * @param fieldName - Name of the field to sort by
9
+ * @returns New sorted array
10
+ */
11
+ export function sortByStringField(items, fieldName) {
12
+ return [...items].sort((a, b) => {
13
+ const aValue = String(a[fieldName]);
14
+ const bValue = String(b[fieldName]);
15
+ return aValue.localeCompare(bValue);
16
+ });
17
+ }
18
+ /**
19
+ * Filter array by text search in a specific field (case-insensitive)
20
+ * @param items - Array of objects to filter
21
+ * @param fieldName - Name of the field to search in
22
+ * @param searchTerm - Search term
23
+ * @returns Filtered array
24
+ */
25
+ export function filterByTextSearch(items, fieldName, searchTerm) {
26
+ const lowercaseSearch = searchTerm.toLowerCase();
27
+ return items.filter(item => {
28
+ const fieldValue = String(item[fieldName]).toLowerCase();
29
+ return fieldValue.includes(lowercaseSearch);
30
+ });
31
+ }
32
+ /**
33
+ * Filter array by boolean field
34
+ * @param items - Array of objects to filter
35
+ * @param fieldName - Name of the boolean field
36
+ * @param value - Boolean value to filter by
37
+ * @returns Filtered array
38
+ */
39
+ export function filterByBooleanField(items, fieldName, value) {
40
+ return items.filter(item => Boolean(item[fieldName]) === value);
41
+ }
42
+ /**
43
+ * Find item by exact field match
44
+ * @param items - Array of objects to search
45
+ * @param fieldName - Name of the field to match
46
+ * @param value - Value to match
47
+ * @returns Found item or undefined
48
+ */
49
+ export function findByField(items, fieldName, value) {
50
+ return items.find(item => item[fieldName] === value);
51
+ }
52
+ /**
53
+ * Find item by case-insensitive string field match
54
+ * @param items - Array of objects to search
55
+ * @param fieldName - Name of the string field to match
56
+ * @param value - String value to match (case-insensitive)
57
+ * @returns Found item or undefined
58
+ */
59
+ export function findByStringField(items, fieldName, value) {
60
+ const lowercaseValue = value.toLowerCase();
61
+ return items.find(item => {
62
+ const fieldValue = String(item[fieldName]).toLowerCase();
63
+ return fieldValue === lowercaseValue;
64
+ });
65
+ }
66
+ /**
67
+ * Extract values from a specific field and sort them
68
+ * @param items - Array of objects
69
+ * @param fieldName - Name of the field to extract
70
+ * @returns Sorted array of extracted values
71
+ */
72
+ export function extractAndSortField(items, fieldName) {
73
+ return items.map(item => item[fieldName]).sort();
74
+ }
75
+ /**
76
+ * Group array items by the first character of a string field
77
+ * @param items - Array of objects to group
78
+ * @param fieldName - Name of the string field to group by
79
+ * @returns Object with first letters as keys and arrays of items as values
80
+ */
81
+ export function groupByFirstLetter(items, fieldName) {
82
+ return items.reduce((groups, item) => {
83
+ const fieldValue = String(item[fieldName]);
84
+ const firstLetter = fieldValue.charAt(0).toUpperCase();
85
+ if (!groups[firstLetter]) {
86
+ groups[firstLetter] = [];
87
+ }
88
+ groups[firstLetter].push(item);
89
+ return groups;
90
+ }, {});
91
+ }
92
+ /**
93
+ * Group array items by a field value
94
+ * @param items - Array of objects to group
95
+ * @param fieldName - Name of the field to group by
96
+ * @returns Object with field values as keys and arrays of items as values
97
+ */
98
+ export function groupByField(items, fieldName) {
99
+ return items.reduce((groups, item) => {
100
+ const fieldValue = String(item[fieldName]);
101
+ if (!groups[fieldValue]) {
102
+ groups[fieldValue] = [];
103
+ }
104
+ groups[fieldValue].push(item);
105
+ return groups;
106
+ }, {});
107
+ }
108
+ /**
109
+ * Check if any item in array has a specific boolean field value
110
+ * @param items - Array of objects to check
111
+ * @param fieldName - Name of the boolean field
112
+ * @param value - Boolean value to check for
113
+ * @returns true if any item matches, false otherwise
114
+ */
115
+ export function hasItemWithFieldValue(items, fieldName, value) {
116
+ return items.some(item => item[fieldName] === value);
117
+ }
118
+ /**
119
+ * Count items that match a field value
120
+ * @param items - Array of objects to count
121
+ * @param fieldName - Name of the field to check
122
+ * @param value - Value to count
123
+ * @returns Number of matching items
124
+ */
125
+ export function countByFieldValue(items, fieldName, value) {
126
+ return items.filter(item => item[fieldName] === value).length;
127
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * File download utilities
3
+ * Browser-based file download functionality
4
+ */
5
+ /**
6
+ * Download a blob as a file
7
+ * Creates a temporary download link and triggers the download
8
+ * @param blob - The blob to download
9
+ * @param filename - The filename for the downloaded file
10
+ */
11
+ export declare function downloadBlob(blob: Blob, filename: string): void;
12
+ /**
13
+ * Download text content as a file
14
+ * @param content - Text content to download
15
+ * @param filename - The filename for the downloaded file
16
+ * @param mimeType - MIME type for the file (default: text/plain)
17
+ */
18
+ export declare function downloadTextFile(content: string, filename: string, mimeType?: string): void;
19
+ /**
20
+ * Download JSON data as a file
21
+ * @param data - JavaScript object to serialize and download
22
+ * @param filename - The filename for the downloaded file (should end with .json)
23
+ */
24
+ export declare function downloadJsonFile(data: unknown, filename: string): void;
25
+ /**
26
+ * Generate a timestamped filename
27
+ * @param baseName - Base name for the file (without extension)
28
+ * @param extension - File extension (with or without leading dot)
29
+ * @param includeTime - Whether to include time in timestamp (default: false)
30
+ * @returns Timestamped filename
31
+ */
32
+ export declare function generateTimestampedFilename(baseName: string, extension: string, includeTime?: boolean): string;
33
+ /**
34
+ * Download data URL as a file
35
+ * @param dataUrl - Data URL (data:mime/type;base64,data)
36
+ * @param filename - The filename for the downloaded file
37
+ */
38
+ export declare function downloadDataUrl(dataUrl: string, filename: string): void;
39
+ /**
40
+ * Download CSV data as a file
41
+ * @param data - Array of objects to convert to CSV
42
+ * @param filename - The filename for the downloaded file (should end with .csv)
43
+ * @param headers - Optional custom headers (uses object keys if not provided)
44
+ */
45
+ export declare function downloadCsvFile<T extends Record<string, unknown>>(data: T[], filename: string, headers?: string[]): void;
46
+ //# sourceMappingURL=downloads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloads.d.ts","sourceRoot":"","sources":["../src/downloads.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAS/D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAqB,GAAG,IAAI,CAGzG;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAGtE;AAED;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,GAAE,OAAe,GAAG,MAAM,CASrH;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAOvE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,IAAI,EAAE,CAAC,EAAE,EACT,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,MAAM,EAAE,GACjB,IAAI,CAqBN"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * File download utilities
3
+ * Browser-based file download functionality
4
+ */
5
+ /**
6
+ * Download a blob as a file
7
+ * Creates a temporary download link and triggers the download
8
+ * @param blob - The blob to download
9
+ * @param filename - The filename for the downloaded file
10
+ */
11
+ export function downloadBlob(blob, filename) {
12
+ const url = window.URL.createObjectURL(blob);
13
+ const link = document.createElement('a');
14
+ link.href = url;
15
+ link.setAttribute('download', filename);
16
+ document.body.appendChild(link);
17
+ link.click();
18
+ document.body.removeChild(link);
19
+ window.URL.revokeObjectURL(url);
20
+ }
21
+ /**
22
+ * Download text content as a file
23
+ * @param content - Text content to download
24
+ * @param filename - The filename for the downloaded file
25
+ * @param mimeType - MIME type for the file (default: text/plain)
26
+ */
27
+ export function downloadTextFile(content, filename, mimeType = 'text/plain') {
28
+ const blob = new Blob([content], { type: mimeType });
29
+ downloadBlob(blob, filename);
30
+ }
31
+ /**
32
+ * Download JSON data as a file
33
+ * @param data - JavaScript object to serialize and download
34
+ * @param filename - The filename for the downloaded file (should end with .json)
35
+ */
36
+ export function downloadJsonFile(data, filename) {
37
+ const jsonString = JSON.stringify(data, null, 2);
38
+ downloadTextFile(jsonString, filename, 'application/json');
39
+ }
40
+ /**
41
+ * Generate a timestamped filename
42
+ * @param baseName - Base name for the file (without extension)
43
+ * @param extension - File extension (with or without leading dot)
44
+ * @param includeTime - Whether to include time in timestamp (default: false)
45
+ * @returns Timestamped filename
46
+ */
47
+ export function generateTimestampedFilename(baseName, extension, includeTime = false) {
48
+ const now = new Date();
49
+ const dateString = now.toISOString().split('T')[0]; // YYYY-MM-DD
50
+ const timeString = now.toTimeString().split(' ')[0]?.replace(/:/g, '-') || '00-00-00'; // HH-MM-SS
51
+ const cleanExtension = extension.startsWith('.') ? extension : `.${extension}`;
52
+ const timestamp = includeTime ? `${dateString}-${timeString}` : dateString;
53
+ return `${baseName}-${timestamp}${cleanExtension}`;
54
+ }
55
+ /**
56
+ * Download data URL as a file
57
+ * @param dataUrl - Data URL (data:mime/type;base64,data)
58
+ * @param filename - The filename for the downloaded file
59
+ */
60
+ export function downloadDataUrl(dataUrl, filename) {
61
+ const link = document.createElement('a');
62
+ link.href = dataUrl;
63
+ link.setAttribute('download', filename);
64
+ document.body.appendChild(link);
65
+ link.click();
66
+ document.body.removeChild(link);
67
+ }
68
+ /**
69
+ * Download CSV data as a file
70
+ * @param data - Array of objects to convert to CSV
71
+ * @param filename - The filename for the downloaded file (should end with .csv)
72
+ * @param headers - Optional custom headers (uses object keys if not provided)
73
+ */
74
+ export function downloadCsvFile(data, filename, headers) {
75
+ if (data.length === 0) {
76
+ throw new Error('Cannot create CSV from empty data array');
77
+ }
78
+ const csvHeaders = headers || Object.keys(data[0] || {});
79
+ const csvRows = data.map(row => csvHeaders
80
+ .map(header => {
81
+ const value = row[header];
82
+ // Escape commas and quotes in CSV values
83
+ const stringValue = String(value ?? '');
84
+ return stringValue.includes(',') || stringValue.includes('"')
85
+ ? `"${stringValue.replace(/"/g, '""')}"`
86
+ : stringValue;
87
+ })
88
+ .join(','));
89
+ const csvContent = [csvHeaders.join(','), ...csvRows].join('\n');
90
+ downloadTextFile(csvContent, filename, 'text/csv');
91
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * String formatting utilities
3
+ * Generic string manipulation and formatting functions
4
+ */
5
+ /**
6
+ * Capitalize the first letter of a string and make the rest lowercase
7
+ * @param str - The string to capitalize
8
+ * @returns Capitalized string
9
+ * @example
10
+ * capitalizeFirstLetter('HELLO WORLD') // 'Hello world'
11
+ * capitalizeFirstLetter('low') // 'Low'
12
+ */
13
+ export declare function capitalizeFirstLetter(str: string): string;
14
+ /**
15
+ * Format a status string for display (capitalize first letter, lowercase rest)
16
+ * @param status - The status string to format
17
+ * @returns Formatted status string
18
+ * @example
19
+ * formatStatus('PENDING') // 'Pending'
20
+ * formatStatus('completed') // 'Completed'
21
+ */
22
+ export declare function formatStatus(status: string): string;
23
+ /**
24
+ * Format a risk level string for display (capitalize first letter, lowercase rest)
25
+ * @param riskLevel - The risk level string to format
26
+ * @returns Formatted risk level string
27
+ * @example
28
+ * formatRiskLevel('HIGH') // 'High'
29
+ * formatRiskLevel('moderate') // 'Moderate'
30
+ */
31
+ export declare function formatRiskLevel(riskLevel: string): string;
32
+ /**
33
+ * Capitalize only the first letter, keeping the rest unchanged
34
+ * @param str - The string to capitalize
35
+ * @returns String with first letter capitalized
36
+ * @example
37
+ * capitalizeFirst('hello World') // 'Hello World'
38
+ * capitalizeFirst('iPhone') // 'IPhone'
39
+ */
40
+ export declare function capitalizeFirst(str: string): string;
41
+ /**
42
+ * Format a label by replacing underscores with spaces and capitalizing
43
+ * @param label - The label to format
44
+ * @returns Formatted label
45
+ * @example
46
+ * formatLabel('first_name') // 'First name'
47
+ * formatLabel('user_profile_data') // 'User profile data'
48
+ */
49
+ export declare function formatLabel(label: string): string;
50
+ /**
51
+ * Convert camelCase to Title Case
52
+ * @param str - The camelCase string to convert
53
+ * @returns Title Case string
54
+ * @example
55
+ * camelToTitle('firstName') // 'First Name'
56
+ * camelToTitle('userProfileData') // 'User Profile Data'
57
+ */
58
+ export declare function camelToTitle(str: string): string;
59
+ //# sourceMappingURL=formatting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../src/formatting.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMhD"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * String formatting utilities
3
+ * Generic string manipulation and formatting functions
4
+ */
5
+ /**
6
+ * Capitalize the first letter of a string and make the rest lowercase
7
+ * @param str - The string to capitalize
8
+ * @returns Capitalized string
9
+ * @example
10
+ * capitalizeFirstLetter('HELLO WORLD') // 'Hello world'
11
+ * capitalizeFirstLetter('low') // 'Low'
12
+ */
13
+ export function capitalizeFirstLetter(str) {
14
+ if (!str)
15
+ return str;
16
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
17
+ }
18
+ /**
19
+ * Format a status string for display (capitalize first letter, lowercase rest)
20
+ * @param status - The status string to format
21
+ * @returns Formatted status string
22
+ * @example
23
+ * formatStatus('PENDING') // 'Pending'
24
+ * formatStatus('completed') // 'Completed'
25
+ */
26
+ export function formatStatus(status) {
27
+ return capitalizeFirstLetter(status);
28
+ }
29
+ /**
30
+ * Format a risk level string for display (capitalize first letter, lowercase rest)
31
+ * @param riskLevel - The risk level string to format
32
+ * @returns Formatted risk level string
33
+ * @example
34
+ * formatRiskLevel('HIGH') // 'High'
35
+ * formatRiskLevel('moderate') // 'Moderate'
36
+ */
37
+ export function formatRiskLevel(riskLevel) {
38
+ return capitalizeFirstLetter(riskLevel);
39
+ }
40
+ /**
41
+ * Capitalize only the first letter, keeping the rest unchanged
42
+ * @param str - The string to capitalize
43
+ * @returns String with first letter capitalized
44
+ * @example
45
+ * capitalizeFirst('hello World') // 'Hello World'
46
+ * capitalizeFirst('iPhone') // 'IPhone'
47
+ */
48
+ export function capitalizeFirst(str) {
49
+ if (!str)
50
+ return str;
51
+ return str.charAt(0).toUpperCase() + str.slice(1);
52
+ }
53
+ /**
54
+ * Format a label by replacing underscores with spaces and capitalizing
55
+ * @param label - The label to format
56
+ * @returns Formatted label
57
+ * @example
58
+ * formatLabel('first_name') // 'First name'
59
+ * formatLabel('user_profile_data') // 'User profile data'
60
+ */
61
+ export function formatLabel(label) {
62
+ if (!label)
63
+ return label;
64
+ return capitalizeFirstLetter(label.replace(/_/g, ' '));
65
+ }
66
+ /**
67
+ * Convert camelCase to Title Case
68
+ * @param str - The camelCase string to convert
69
+ * @returns Title Case string
70
+ * @example
71
+ * camelToTitle('firstName') // 'First Name'
72
+ * camelToTitle('userProfileData') // 'User Profile Data'
73
+ */
74
+ export function camelToTitle(str) {
75
+ if (!str)
76
+ return str;
77
+ return str
78
+ .replace(/([A-Z])/g, ' $1')
79
+ .replace(/^./, match => match.toUpperCase())
80
+ .trim();
81
+ }
package/dist/index.d.ts CHANGED
@@ -15,10 +15,14 @@ export { formatPortfolioValue, calculateTotalValue, getMarketAllocation, getCash
15
15
  export type { LiquidationProgress } from './withdrawal';
16
16
  export { formatWithdrawalAmount, formatLiquidationValue, formatSharesQuantity, calculateLiquidationProgress, getTotalEstimatedValue, } from './withdrawal';
17
17
  export type { PasswordValidation, EmailConfirmationValidation, ExtendedPasswordValidation } from './validation';
18
- export { isNumericOnly, validatePassword, isValidTokenFormat, formatVerificationToken, validateEmailConfirmation, isValidEmail, hasUppercase, hasLowercase, hasSpecialCharacter, hasDigit, validatePasswordExtended, isValidPhoneNumber, isValidPhoneFormat, isValidUrl, isValidDate, isEmptyOrWhitespace, meetsMinLength, meetsMaxLength, isInRange, isPositiveNumber, isNonNegativeNumber, isValidFullName, isValidDateOfBirth, formatPhoneNumber, isValidInvestmentAmount, hasMinimumSelection, isSelected, } from './validation';
18
+ export { isNumericOnly, validatePassword, isValidTokenFormat, formatVerificationToken, validateEmailConfirmation, isValidEmail, hasUppercase, hasLowercase, hasSpecialCharacter, hasDigit, validatePasswordExtended, isValidPhoneNumber, isValidPhoneFormat, isValidUrl, isValidDate, isEmptyOrWhitespace, meetsMinLength, meetsMaxLength, isInRange, isPositiveNumber, isNonNegativeNumber, isValidFullName, isValidDateOfBirth, formatPhoneNumber, isValidInvestmentAmount, hasMinimumSelection, isSelected, validatePercentageSum, validateAllocationSum, isInNumberRange, isValidPercentage, } from './validation';
19
19
  export { hasActiveFilters, countActiveFilters, clearAllFilters, updateFilters, isFilterEmpty, removeEmptyFilters, mergeFilters, areFiltersEqual, createFilterPredicate, } from './filters';
20
20
  export type { DeviceInfo, TokenRefreshResponse, TokenRefreshError, AutoRefreshHandler } from './auth';
21
21
  export { isTokenExpired, isTokenExpiringSoon, getTimeUntilExpiry, formatTimeUntilExpiry, parseDeviceInfo, isRefreshSuccess, getRefreshErrorMessage, createAutoRefreshHandler, } from './auth';
22
22
  export type { InstrumentTypeInfo, InstrumentBasicInfo, PriceChangeResult, PriceSnapshot, FormattedPriceChange, TradeableInstrument, MarketDataInfo, } from './instruments';
23
23
  export { formatInstrumentPrice, getCurrencySymbol, getInstrumentType, formatInstrumentName, calculatePriceChange, formatPriceChange, isInstrumentTradeable, formatMarketCap, formatVolume, hasMarketData, } from './instruments';
24
+ export { sortByStringField, filterByTextSearch, filterByBooleanField, findByField, findByStringField, extractAndSortField, groupByFirstLetter, groupByField, hasItemWithFieldValue, countByFieldValue, } from './collections';
25
+ export { downloadBlob, downloadTextFile, downloadJsonFile, generateTimestampedFilename, downloadDataUrl, downloadCsvFile, } from './downloads';
26
+ export { capitalizeFirstLetter, formatStatus, formatRiskLevel, capitalizeFirst, formatLabel, camelToTitle, } from './formatting';
27
+ export { formatQuantityWithSuffix, formatLargeNumber, formatAsPercentage, formatWithSeparators, roundToDecimals, clampNumber, } from './numbers';
24
28
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAGjF,OAAO,EACL,8BAA8B,EAC9B,qCAAqC,EACrC,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,EACrB,YAAY,GACb,MAAM,YAAY,CAAC;AAGpB,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGnE,YAAY,EACV,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,yBAAyB,GAC1B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhH,YAAY,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,4BAA4B,EAC5B,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAGtB,YAAY,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAChH,OAAO,EACL,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,QAAQ,EACR,wBAAwB,EACxB,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,GACX,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,aAAa,EACb,kBAAkB,EAClB,YAAY,EACZ,eAAe,EACf,qBAAqB,GACtB,MAAM,WAAW,CAAC;AAGnB,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACtG,OAAO,EACL,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,QAAQ,CAAC;AAGhB,YAAY,EACV,kBAAkB,EAClB,mBAAmB,EACnB,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,aAAa,GACd,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -15,8 +15,18 @@ export { getTradeStatusBadge, getWithdrawalStatusBadge, getLiquidationStatusBadg
15
15
  // Portfolio utilities
16
16
  export { formatPortfolioValue, calculateTotalValue, getMarketAllocation, getCashAllocation } from './portfolio';
17
17
  export { formatWithdrawalAmount, formatLiquidationValue, formatSharesQuantity, calculateLiquidationProgress, getTotalEstimatedValue, } from './withdrawal';
18
- export { isNumericOnly, validatePassword, isValidTokenFormat, formatVerificationToken, validateEmailConfirmation, isValidEmail, hasUppercase, hasLowercase, hasSpecialCharacter, hasDigit, validatePasswordExtended, isValidPhoneNumber, isValidPhoneFormat, isValidUrl, isValidDate, isEmptyOrWhitespace, meetsMinLength, meetsMaxLength, isInRange, isPositiveNumber, isNonNegativeNumber, isValidFullName, isValidDateOfBirth, formatPhoneNumber, isValidInvestmentAmount, hasMinimumSelection, isSelected, } from './validation';
18
+ export { isNumericOnly, validatePassword, isValidTokenFormat, formatVerificationToken, validateEmailConfirmation, isValidEmail, hasUppercase, hasLowercase, hasSpecialCharacter, hasDigit, validatePasswordExtended, isValidPhoneNumber, isValidPhoneFormat, isValidUrl, isValidDate, isEmptyOrWhitespace, meetsMinLength, meetsMaxLength, isInRange, isPositiveNumber, isNonNegativeNumber, isValidFullName, isValidDateOfBirth, formatPhoneNumber, isValidInvestmentAmount, hasMinimumSelection, isSelected,
19
+ // Enhanced validation utilities (Phase 14)
20
+ validatePercentageSum, validateAllocationSum, isInNumberRange, isValidPercentage, } from './validation';
19
21
  // Filter utilities
20
22
  export { hasActiveFilters, countActiveFilters, clearAllFilters, updateFilters, isFilterEmpty, removeEmptyFilters, mergeFilters, areFiltersEqual, createFilterPredicate, } from './filters';
21
23
  export { isTokenExpired, isTokenExpiringSoon, getTimeUntilExpiry, formatTimeUntilExpiry, parseDeviceInfo, isRefreshSuccess, getRefreshErrorMessage, createAutoRefreshHandler, } from './auth';
22
24
  export { formatInstrumentPrice, getCurrencySymbol, getInstrumentType, formatInstrumentName, calculatePriceChange, formatPriceChange, isInstrumentTradeable, formatMarketCap, formatVolume, hasMarketData, } from './instruments';
25
+ // Collection utilities
26
+ export { sortByStringField, filterByTextSearch, filterByBooleanField, findByField, findByStringField, extractAndSortField, groupByFirstLetter, groupByField, hasItemWithFieldValue, countByFieldValue, } from './collections';
27
+ // Download utilities
28
+ export { downloadBlob, downloadTextFile, downloadJsonFile, generateTimestampedFilename, downloadDataUrl, downloadCsvFile, } from './downloads';
29
+ // String formatting utilities
30
+ export { capitalizeFirstLetter, formatStatus, formatRiskLevel, capitalizeFirst, formatLabel, camelToTitle, } from './formatting';
31
+ // Number formatting utilities
32
+ export { formatQuantityWithSuffix, formatLargeNumber, formatAsPercentage, formatWithSeparators, roundToDecimals, clampNumber, } from './numbers';
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Number formatting utilities
3
+ * Generic number manipulation and formatting functions
4
+ */
5
+ /**
6
+ * Format a large number with K/M suffixes for display
7
+ * @param quantity - The number to format
8
+ * @param decimals - Number of decimal places (default: 1)
9
+ * @returns Formatted number string with suffix
10
+ * @example
11
+ * formatQuantityWithSuffix(1000) // '1.0K'
12
+ * formatQuantityWithSuffix(1500000) // '1.5M'
13
+ * formatQuantityWithSuffix(999) // '999'
14
+ */
15
+ export declare function formatQuantityWithSuffix(quantity: number, decimals?: number): string;
16
+ /**
17
+ * Format a large number with customizable suffixes
18
+ * @param num - The number to format
19
+ * @param suffixes - Array of suffix configurations (default: standard K, M, B, T)
20
+ * @param decimals - Number of decimal places (default: 1)
21
+ * @returns Formatted number string with suffix
22
+ * @example
23
+ * formatLargeNumber(1500000) // '1.5M'
24
+ * formatLargeNumber(1500000000) // '1.5B'
25
+ */
26
+ export declare function formatLargeNumber(num: number, suffixes?: Array<{
27
+ threshold: number;
28
+ suffix: string;
29
+ }>, decimals?: number): string;
30
+ /**
31
+ * Format a number as a percentage
32
+ * @param value - The decimal value to format as percentage
33
+ * @param decimals - Number of decimal places (default: 1)
34
+ * @returns Formatted percentage string
35
+ * @example
36
+ * formatAsPercentage(0.1234) // '12.3%'
37
+ * formatAsPercentage(0.5, 0) // '50%'
38
+ */
39
+ export declare function formatAsPercentage(value: number, decimals?: number): string;
40
+ /**
41
+ * Format a number with thousand separators
42
+ * @param num - The number to format
43
+ * @param separator - The separator character (default: ',')
44
+ * @returns Formatted number string with separators
45
+ * @example
46
+ * formatWithSeparators(1234567) // '1,234,567'
47
+ * formatWithSeparators(1234567, ' ') // '1 234 567'
48
+ */
49
+ export declare function formatWithSeparators(num: number, separator?: string): string;
50
+ /**
51
+ * Round a number to specified decimal places
52
+ * @param num - The number to round
53
+ * @param decimals - Number of decimal places (default: 2)
54
+ * @returns Rounded number
55
+ * @example
56
+ * roundToDecimals(3.14159, 2) // 3.14
57
+ * roundToDecimals(123.456, 0) // 123
58
+ */
59
+ export declare function roundToDecimals(num: number, decimals?: number): number;
60
+ /**
61
+ * Clamp a number between min and max values
62
+ * @param num - The number to clamp
63
+ * @param min - Minimum value
64
+ * @param max - Maximum value
65
+ * @returns Clamped number
66
+ * @example
67
+ * clampNumber(150, 0, 100) // 100
68
+ * clampNumber(-10, 0, 100) // 0
69
+ * clampNumber(50, 0, 100) // 50
70
+ */
71
+ export declare function clampNumber(num: number, min: number, max: number): number;
72
+ //# sourceMappingURL=numbers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"numbers.d.ts","sourceRoot":"","sources":["../src/numbers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,MAAM,CAWvF;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAKpD,EACD,QAAQ,GAAE,MAAU,GACnB,MAAM,CAOR;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,MAAM,CAE9E;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,MAAY,GAAG,MAAM,CAEjF;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,MAAM,CAGzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzE"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Number formatting utilities
3
+ * Generic number manipulation and formatting functions
4
+ */
5
+ /**
6
+ * Format a large number with K/M suffixes for display
7
+ * @param quantity - The number to format
8
+ * @param decimals - Number of decimal places (default: 1)
9
+ * @returns Formatted number string with suffix
10
+ * @example
11
+ * formatQuantityWithSuffix(1000) // '1.0K'
12
+ * formatQuantityWithSuffix(1500000) // '1.5M'
13
+ * formatQuantityWithSuffix(999) // '999'
14
+ */
15
+ export function formatQuantityWithSuffix(quantity, decimals = 1) {
16
+ const million = 1000000;
17
+ const thousand = 1000;
18
+ if (quantity >= million) {
19
+ return `${(quantity / million).toFixed(decimals)}M`;
20
+ }
21
+ else if (quantity >= thousand) {
22
+ return `${(quantity / thousand).toFixed(decimals)}K`;
23
+ }
24
+ else {
25
+ return quantity.toString();
26
+ }
27
+ }
28
+ /**
29
+ * Format a large number with customizable suffixes
30
+ * @param num - The number to format
31
+ * @param suffixes - Array of suffix configurations (default: standard K, M, B, T)
32
+ * @param decimals - Number of decimal places (default: 1)
33
+ * @returns Formatted number string with suffix
34
+ * @example
35
+ * formatLargeNumber(1500000) // '1.5M'
36
+ * formatLargeNumber(1500000000) // '1.5B'
37
+ */
38
+ export function formatLargeNumber(num, suffixes = [
39
+ { threshold: 1000000000000, suffix: 'T' },
40
+ { threshold: 1000000000, suffix: 'B' },
41
+ { threshold: 1000000, suffix: 'M' },
42
+ { threshold: 1000, suffix: 'K' },
43
+ ], decimals = 1) {
44
+ for (const { threshold, suffix } of suffixes) {
45
+ if (num >= threshold) {
46
+ return `${(num / threshold).toFixed(decimals)}${suffix}`;
47
+ }
48
+ }
49
+ return num.toString();
50
+ }
51
+ /**
52
+ * Format a number as a percentage
53
+ * @param value - The decimal value to format as percentage
54
+ * @param decimals - Number of decimal places (default: 1)
55
+ * @returns Formatted percentage string
56
+ * @example
57
+ * formatAsPercentage(0.1234) // '12.3%'
58
+ * formatAsPercentage(0.5, 0) // '50%'
59
+ */
60
+ export function formatAsPercentage(value, decimals = 1) {
61
+ return `${(value * 100).toFixed(decimals)}%`;
62
+ }
63
+ /**
64
+ * Format a number with thousand separators
65
+ * @param num - The number to format
66
+ * @param separator - The separator character (default: ',')
67
+ * @returns Formatted number string with separators
68
+ * @example
69
+ * formatWithSeparators(1234567) // '1,234,567'
70
+ * formatWithSeparators(1234567, ' ') // '1 234 567'
71
+ */
72
+ export function formatWithSeparators(num, separator = ',') {
73
+ return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, separator);
74
+ }
75
+ /**
76
+ * Round a number to specified decimal places
77
+ * @param num - The number to round
78
+ * @param decimals - Number of decimal places (default: 2)
79
+ * @returns Rounded number
80
+ * @example
81
+ * roundToDecimals(3.14159, 2) // 3.14
82
+ * roundToDecimals(123.456, 0) // 123
83
+ */
84
+ export function roundToDecimals(num, decimals = 2) {
85
+ const factor = Math.pow(10, decimals);
86
+ return Math.round(num * factor) / factor;
87
+ }
88
+ /**
89
+ * Clamp a number between min and max values
90
+ * @param num - The number to clamp
91
+ * @param min - Minimum value
92
+ * @param max - Maximum value
93
+ * @returns Clamped number
94
+ * @example
95
+ * clampNumber(150, 0, 100) // 100
96
+ * clampNumber(-10, 0, 100) // 0
97
+ * clampNumber(50, 0, 100) // 50
98
+ */
99
+ export function clampNumber(num, min, max) {
100
+ return Math.min(Math.max(num, min), max);
101
+ }
@@ -202,4 +202,48 @@ export declare function hasMinimumSelection<T>(items: T[], minItems?: number): b
202
202
  * @returns true if value is selected, false otherwise
203
203
  */
204
204
  export declare function isSelected(value: unknown): boolean;
205
+ /**
206
+ * Validates that percentage values sum to a target value (default 100)
207
+ * @param values - Array of percentage values
208
+ * @param target - Target sum value (default: 100)
209
+ * @param tolerance - Allowed tolerance for floating point differences (default: 0.01)
210
+ * @returns true if sum equals target within tolerance, false otherwise
211
+ * @example
212
+ * validatePercentageSum([25, 25, 50]) // true (sums to 100)
213
+ * validatePercentageSum([33.33, 33.33, 33.34]) // true (within tolerance)
214
+ * validatePercentageSum([50, 30, 15]) // false (sums to 95)
215
+ */
216
+ export declare function validatePercentageSum(values: number[], target?: number, tolerance?: number): boolean;
217
+ /**
218
+ * Validates that allocation objects with percentage properties sum to 100%
219
+ * @param allocations - Array of objects with percentage property
220
+ * @param percentageField - Name of the percentage field (default: 'percentage')
221
+ * @param tolerance - Allowed tolerance for floating point differences (default: 0.01)
222
+ * @returns true if sum equals 100 within tolerance, false otherwise
223
+ * @example
224
+ * validateAllocationSum([{percentage: 50}, {percentage: 50}]) // true
225
+ * validateAllocationSum([{percentage: '33.33'}, {percentage: '66.67'}]) // true
226
+ */
227
+ export declare function validateAllocationSum<T extends Record<string, unknown>>(allocations: T[], percentageField?: keyof T, tolerance?: number): boolean;
228
+ /**
229
+ * Validates that a number is within a specified range (inclusive)
230
+ * @param value - The number to validate
231
+ * @param min - Minimum allowed value
232
+ * @param max - Maximum allowed value
233
+ * @returns true if number is within range, false otherwise
234
+ * @example
235
+ * isInNumberRange(50, 0, 100) // true
236
+ * isInNumberRange(150, 0, 100) // false
237
+ */
238
+ export declare function isInNumberRange(value: number, min: number, max: number): boolean;
239
+ /**
240
+ * Validates that a percentage is between 0 and 100
241
+ * @param percentage - The percentage to validate
242
+ * @returns true if valid percentage, false otherwise
243
+ * @example
244
+ * isValidPercentage(50) // true
245
+ * isValidPercentage(150) // false
246
+ * isValidPercentage(-10) // false
247
+ */
248
+ export declare function isValidPercentage(percentage: number): boolean;
205
249
  //# sourceMappingURL=validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,kBAAkB,CAKpF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,OAAO,CAIxE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,2BAA2B,CAQnG;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE7C;AAED;;GAEG;AACH,MAAM,WAAW,0BAA2B,SAAQ,kBAAkB;IACpE,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,0BAA0B,CAUpG;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAK/D;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,SAAI,GAAG,OAAO,CAI9E;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAO/C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAEtE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,OAAO,CAIvE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,SAAK,EAAE,MAAM,SAAM,GAAG,OAAO,CAc1F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE7D;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,SAAM,EAAE,SAAS,SAAW,GAAG,OAAO,CAEtG;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,QAAQ,SAAI,GAAG,OAAO,CAExE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAElD;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,GAAE,MAAY,EAAE,SAAS,GAAE,MAAa,GAAG,OAAO,CAK/G;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrE,WAAW,EAAE,CAAC,EAAE,EAChB,eAAe,GAAE,MAAM,CAA2B,EAClD,SAAS,GAAE,MAAa,GACvB,OAAO,CAST;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAEhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAE7D"}
@@ -273,3 +273,65 @@ export function hasMinimumSelection(items, minItems = 1) {
273
273
  export function isSelected(value) {
274
274
  return value !== null && value !== undefined && value !== '';
275
275
  }
276
+ // ENHANCED VALIDATION UTILITIES (Phase 14)
277
+ /**
278
+ * Validates that percentage values sum to a target value (default 100)
279
+ * @param values - Array of percentage values
280
+ * @param target - Target sum value (default: 100)
281
+ * @param tolerance - Allowed tolerance for floating point differences (default: 0.01)
282
+ * @returns true if sum equals target within tolerance, false otherwise
283
+ * @example
284
+ * validatePercentageSum([25, 25, 50]) // true (sums to 100)
285
+ * validatePercentageSum([33.33, 33.33, 33.34]) // true (within tolerance)
286
+ * validatePercentageSum([50, 30, 15]) // false (sums to 95)
287
+ */
288
+ export function validatePercentageSum(values, target = 100, tolerance = 0.01) {
289
+ if (!Array.isArray(values) || values.length === 0)
290
+ return false;
291
+ const sum = values.reduce((total, value) => total + (value || 0), 0);
292
+ return Math.abs(sum - target) <= tolerance;
293
+ }
294
+ /**
295
+ * Validates that allocation objects with percentage properties sum to 100%
296
+ * @param allocations - Array of objects with percentage property
297
+ * @param percentageField - Name of the percentage field (default: 'percentage')
298
+ * @param tolerance - Allowed tolerance for floating point differences (default: 0.01)
299
+ * @returns true if sum equals 100 within tolerance, false otherwise
300
+ * @example
301
+ * validateAllocationSum([{percentage: 50}, {percentage: 50}]) // true
302
+ * validateAllocationSum([{percentage: '33.33'}, {percentage: '66.67'}]) // true
303
+ */
304
+ export function validateAllocationSum(allocations, percentageField = 'percentage', tolerance = 0.01) {
305
+ if (!Array.isArray(allocations) || allocations.length === 0)
306
+ return false;
307
+ const values = allocations.map(allocation => {
308
+ const value = allocation[percentageField];
309
+ return typeof value === 'string' ? parseFloat(value) || 0 : Number(value) || 0;
310
+ });
311
+ return validatePercentageSum(values, 100, tolerance);
312
+ }
313
+ /**
314
+ * Validates that a number is within a specified range (inclusive)
315
+ * @param value - The number to validate
316
+ * @param min - Minimum allowed value
317
+ * @param max - Maximum allowed value
318
+ * @returns true if number is within range, false otherwise
319
+ * @example
320
+ * isInNumberRange(50, 0, 100) // true
321
+ * isInNumberRange(150, 0, 100) // false
322
+ */
323
+ export function isInNumberRange(value, min, max) {
324
+ return typeof value === 'number' && !isNaN(value) && value >= min && value <= max;
325
+ }
326
+ /**
327
+ * Validates that a percentage is between 0 and 100
328
+ * @param percentage - The percentage to validate
329
+ * @returns true if valid percentage, false otherwise
330
+ * @example
331
+ * isValidPercentage(50) // true
332
+ * isValidPercentage(150) // false
333
+ * isValidPercentage(-10) // false
334
+ */
335
+ export function isValidPercentage(percentage) {
336
+ return isInNumberRange(percentage, 0, 100);
337
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cranberry-money/shared-utils",
3
- "version": "4.11.0",
3
+ "version": "4.13.0",
4
4
  "description": "Shared utility functions for MyPortfolio platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",