@cranberry-money/shared-utils 4.10.0 → 4.12.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.
package/dist/auth.d.ts ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Authentication and token management utilities
3
+ */
4
+ /**
5
+ * Check if a token has expired based on its expiration timestamp
6
+ * @param expiresAt - The expiration timestamp
7
+ * @returns true if token is expired, false otherwise
8
+ */
9
+ export declare function isTokenExpired(expiresAt: string): boolean;
10
+ /**
11
+ * Check if a token will expire within a specified number of minutes
12
+ * @param expiresAt - The expiration timestamp
13
+ * @param minutesBeforeExpiry - Minutes before expiry to consider as "expiring soon"
14
+ * @returns true if token is expiring soon, false otherwise
15
+ */
16
+ export declare function isTokenExpiringSoon(expiresAt: string, minutesBeforeExpiry?: number): boolean;
17
+ /**
18
+ * Get the remaining time until token expiry in minutes
19
+ * @param expiresAt - The expiration timestamp
20
+ * @returns Number of minutes until expiry
21
+ */
22
+ export declare function getTimeUntilExpiry(expiresAt: string): number;
23
+ /**
24
+ * Format the time until expiry in a human-readable format
25
+ * @param expiresAt - The expiration timestamp
26
+ * @returns Formatted time string
27
+ */
28
+ export declare function formatTimeUntilExpiry(expiresAt: string): string;
29
+ /**
30
+ * Device info structure
31
+ */
32
+ export interface DeviceInfo {
33
+ browser: string;
34
+ os: string;
35
+ }
36
+ /**
37
+ * Parse device information from user agent string
38
+ * @param userAgent - The user agent string
39
+ * @returns Parsed device info
40
+ */
41
+ export declare function parseDeviceInfo(userAgent: string): DeviceInfo;
42
+ /**
43
+ * Token refresh response structure
44
+ */
45
+ export interface TokenRefreshResponse {
46
+ status?: number;
47
+ data?: {
48
+ access?: string;
49
+ refresh?: string;
50
+ };
51
+ }
52
+ /**
53
+ * Check if token refresh was successful
54
+ * @param response - The refresh response
55
+ * @returns true if refresh was successful, false otherwise
56
+ */
57
+ export declare function isRefreshSuccess(response: TokenRefreshResponse): boolean;
58
+ /**
59
+ * Token refresh error structure
60
+ */
61
+ export interface TokenRefreshError {
62
+ response?: {
63
+ data?: {
64
+ detail?: string;
65
+ message?: string;
66
+ };
67
+ };
68
+ message?: string;
69
+ }
70
+ /**
71
+ * Get error message from token refresh error
72
+ * @param error - The refresh error
73
+ * @returns Error message string
74
+ */
75
+ export declare function getRefreshErrorMessage(error: TokenRefreshError): string;
76
+ /**
77
+ * Auto refresh handler interface
78
+ */
79
+ export interface AutoRefreshHandler {
80
+ start: () => void;
81
+ stop: () => void;
82
+ }
83
+ /**
84
+ * Create an auto-refresh handler for tokens
85
+ * @param refreshCallback - Callback function to refresh token
86
+ * @param checkInterval - Interval in milliseconds to check for refresh
87
+ * @returns Auto refresh handler with start/stop methods
88
+ */
89
+ export declare function createAutoRefreshHandler(refreshCallback: () => Promise<void>, checkInterval?: number): AutoRefreshHandler;
90
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAkBH;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAIzD;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,mBAAmB,GAAE,MAA6C,GACjE,OAAO,CAMT;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAU5D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAkB/D;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAwB7D;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QACL,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAExE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE;YACL,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,OAAO,CAAC,EAAE,MAAM,CAAC;SAClB,CAAC;KACH,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAWvE;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EACpC,aAAa,GAAE,MAAc,GAC5B,kBAAkB,CAiBpB"}
package/dist/auth.js ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Authentication and token management utilities
3
+ */
4
+ import { DEFAULT_TOKEN_REFRESH_BUFFER_MINUTES, MILLISECONDS_PER_MINUTE, MINUTES_PER_HOUR, HOURS_PER_DAY, TIME_LABEL_EXPIRED, TIME_LABEL_MINUTE, TIME_LABEL_MINUTES, TIME_LABEL_HOUR, TIME_LABEL_HOURS, TIME_LABEL_DAY, TIME_LABEL_DAYS, DEFAULT_UNKNOWN_VALUE, DEFAULT_ERROR_MESSAGE, } from '@cranberry-money/shared-constants';
5
+ /**
6
+ * Check if a token has expired based on its expiration timestamp
7
+ * @param expiresAt - The expiration timestamp
8
+ * @returns true if token is expired, false otherwise
9
+ */
10
+ export function isTokenExpired(expiresAt) {
11
+ const expirationTime = new Date(expiresAt).getTime();
12
+ const currentTime = Date.now();
13
+ return currentTime >= expirationTime;
14
+ }
15
+ /**
16
+ * Check if a token will expire within a specified number of minutes
17
+ * @param expiresAt - The expiration timestamp
18
+ * @param minutesBeforeExpiry - Minutes before expiry to consider as "expiring soon"
19
+ * @returns true if token is expiring soon, false otherwise
20
+ */
21
+ export function isTokenExpiringSoon(expiresAt, minutesBeforeExpiry = DEFAULT_TOKEN_REFRESH_BUFFER_MINUTES) {
22
+ const expirationTime = new Date(expiresAt).getTime();
23
+ const currentTime = Date.now();
24
+ const bufferTime = minutesBeforeExpiry * MILLISECONDS_PER_MINUTE;
25
+ return currentTime + bufferTime >= expirationTime;
26
+ }
27
+ /**
28
+ * Get the remaining time until token expiry in minutes
29
+ * @param expiresAt - The expiration timestamp
30
+ * @returns Number of minutes until expiry
31
+ */
32
+ export function getTimeUntilExpiry(expiresAt) {
33
+ const expirationTime = new Date(expiresAt).getTime();
34
+ const currentTime = Date.now();
35
+ const remainingTime = expirationTime - currentTime;
36
+ if (remainingTime <= 0) {
37
+ return 0;
38
+ }
39
+ return Math.floor(remainingTime / MILLISECONDS_PER_MINUTE);
40
+ }
41
+ /**
42
+ * Format the time until expiry in a human-readable format
43
+ * @param expiresAt - The expiration timestamp
44
+ * @returns Formatted time string
45
+ */
46
+ export function formatTimeUntilExpiry(expiresAt) {
47
+ const minutesRemaining = getTimeUntilExpiry(expiresAt);
48
+ if (minutesRemaining <= 0) {
49
+ return TIME_LABEL_EXPIRED;
50
+ }
51
+ if (minutesRemaining < MINUTES_PER_HOUR) {
52
+ return minutesRemaining === 1 ? `1 ${TIME_LABEL_MINUTE}` : `${minutesRemaining} ${TIME_LABEL_MINUTES}`;
53
+ }
54
+ const hoursRemaining = Math.floor(minutesRemaining / MINUTES_PER_HOUR);
55
+ if (hoursRemaining < HOURS_PER_DAY) {
56
+ return hoursRemaining === 1 ? `1 ${TIME_LABEL_HOUR}` : `${hoursRemaining} ${TIME_LABEL_HOURS}`;
57
+ }
58
+ const daysRemaining = Math.floor(hoursRemaining / HOURS_PER_DAY);
59
+ return daysRemaining === 1 ? `1 ${TIME_LABEL_DAY}` : `${daysRemaining} ${TIME_LABEL_DAYS}`;
60
+ }
61
+ /**
62
+ * Parse device information from user agent string
63
+ * @param userAgent - The user agent string
64
+ * @returns Parsed device info
65
+ */
66
+ export function parseDeviceInfo(userAgent) {
67
+ const browser = userAgent.includes('Chrome')
68
+ ? 'Chrome'
69
+ : userAgent.includes('Firefox')
70
+ ? 'Firefox'
71
+ : userAgent.includes('Safari')
72
+ ? 'Safari'
73
+ : userAgent.includes('Edge')
74
+ ? 'Edge'
75
+ : DEFAULT_UNKNOWN_VALUE;
76
+ const os = userAgent.includes('Windows')
77
+ ? 'Windows'
78
+ : userAgent.includes('Mac')
79
+ ? 'macOS'
80
+ : userAgent.includes('Linux')
81
+ ? 'Linux'
82
+ : userAgent.includes('Android')
83
+ ? 'Android'
84
+ : userAgent.includes('iOS')
85
+ ? 'iOS'
86
+ : DEFAULT_UNKNOWN_VALUE;
87
+ return { browser, os };
88
+ }
89
+ /**
90
+ * Check if token refresh was successful
91
+ * @param response - The refresh response
92
+ * @returns true if refresh was successful, false otherwise
93
+ */
94
+ export function isRefreshSuccess(response) {
95
+ return !!(response?.status === 200 && response?.data?.access && response?.data?.refresh);
96
+ }
97
+ /**
98
+ * Get error message from token refresh error
99
+ * @param error - The refresh error
100
+ * @returns Error message string
101
+ */
102
+ export function getRefreshErrorMessage(error) {
103
+ if (error.response?.data?.detail) {
104
+ return error.response.data.detail;
105
+ }
106
+ if (error.response?.data?.message) {
107
+ return error.response.data.message;
108
+ }
109
+ if (error.message) {
110
+ return error.message;
111
+ }
112
+ return DEFAULT_ERROR_MESSAGE;
113
+ }
114
+ /**
115
+ * Create an auto-refresh handler for tokens
116
+ * @param refreshCallback - Callback function to refresh token
117
+ * @param checkInterval - Interval in milliseconds to check for refresh
118
+ * @returns Auto refresh handler with start/stop methods
119
+ */
120
+ export function createAutoRefreshHandler(refreshCallback, checkInterval = 60000 // 1 minute
121
+ ) {
122
+ let intervalId = null;
123
+ const start = () => {
124
+ if (intervalId)
125
+ return;
126
+ intervalId = setInterval(refreshCallback, checkInterval);
127
+ };
128
+ const stop = () => {
129
+ if (intervalId) {
130
+ clearInterval(intervalId);
131
+ intervalId = null;
132
+ }
133
+ };
134
+ return { start, stop };
135
+ }
@@ -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
+ }
package/dist/index.d.ts CHANGED
@@ -17,4 +17,10 @@ export { formatWithdrawalAmount, formatLiquidationValue, formatSharesQuantity, c
17
17
  export type { PasswordValidation, EmailConfirmationValidation, ExtendedPasswordValidation } from './validation';
18
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';
19
19
  export { hasActiveFilters, countActiveFilters, clearAllFilters, updateFilters, isFilterEmpty, removeEmptyFilters, mergeFilters, areFiltersEqual, createFilterPredicate, } from './filters';
20
+ export type { DeviceInfo, TokenRefreshResponse, TokenRefreshError, AutoRefreshHandler } from './auth';
21
+ export { isTokenExpired, isTokenExpiringSoon, getTimeUntilExpiry, formatTimeUntilExpiry, parseDeviceInfo, isRefreshSuccess, getRefreshErrorMessage, createAutoRefreshHandler, } from './auth';
22
+ export type { InstrumentTypeInfo, InstrumentBasicInfo, PriceChangeResult, PriceSnapshot, FormattedPriceChange, TradeableInstrument, MarketDataInfo, } from './instruments';
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';
20
26
  //# 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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -18,3 +18,9 @@ export { formatWithdrawalAmount, formatLiquidationValue, formatSharesQuantity, c
18
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';
19
19
  // Filter utilities
20
20
  export { hasActiveFilters, countActiveFilters, clearAllFilters, updateFilters, isFilterEmpty, removeEmptyFilters, mergeFilters, areFiltersEqual, createFilterPredicate, } from './filters';
21
+ export { isTokenExpired, isTokenExpiringSoon, getTimeUntilExpiry, formatTimeUntilExpiry, parseDeviceInfo, isRefreshSuccess, getRefreshErrorMessage, createAutoRefreshHandler, } from './auth';
22
+ export { formatInstrumentPrice, getCurrencySymbol, getInstrumentType, formatInstrumentName, calculatePriceChange, formatPriceChange, isInstrumentTradeable, formatMarketCap, formatVolume, hasMarketData, } from './instruments';
23
+ // Collection utilities
24
+ export { sortByStringField, filterByTextSearch, filterByBooleanField, findByField, findByStringField, extractAndSortField, groupByFirstLetter, groupByField, hasItemWithFieldValue, countByFieldValue, } from './collections';
25
+ // Download utilities
26
+ export { downloadBlob, downloadTextFile, downloadJsonFile, generateTimestampedFilename, downloadDataUrl, downloadCsvFile, } from './downloads';
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Instrument and market data formatting utilities
3
+ */
4
+ /**
5
+ * Format instrument price with currency symbol
6
+ * @param price - The price as string
7
+ * @param currency - The currency code
8
+ * @returns Formatted price with currency symbol
9
+ */
10
+ export declare function formatInstrumentPrice(price: string, currency?: string | null): string;
11
+ /**
12
+ * Get currency symbol for display
13
+ * @param currency - The currency code
14
+ * @returns Currency symbol
15
+ */
16
+ export declare function getCurrencySymbol(currency?: string | null): string;
17
+ /**
18
+ * Instrument interface for type determination
19
+ */
20
+ export interface InstrumentTypeInfo {
21
+ isEtf?: boolean;
22
+ isFund?: boolean;
23
+ }
24
+ /**
25
+ * Determine instrument type based on flags
26
+ * @param instrument - The instrument with type flags
27
+ * @returns Instrument type string
28
+ */
29
+ export declare function getInstrumentType(instrument: InstrumentTypeInfo): string;
30
+ /**
31
+ * Basic instrument info for formatting
32
+ */
33
+ export interface InstrumentBasicInfo {
34
+ symbol: string;
35
+ name: string;
36
+ }
37
+ /**
38
+ * Format instrument display name with symbol
39
+ * @param instrument - The instrument with symbol and name
40
+ * @returns Formatted display name
41
+ */
42
+ export declare function formatInstrumentName(instrument: InstrumentBasicInfo): string;
43
+ /**
44
+ * Price change calculation result
45
+ */
46
+ export interface PriceChangeResult {
47
+ change: number;
48
+ changePercent: number;
49
+ isPositive: boolean;
50
+ }
51
+ /**
52
+ * Snapshot data for price change calculation
53
+ */
54
+ export interface PriceSnapshot {
55
+ change: string;
56
+ changePercent: string;
57
+ }
58
+ /**
59
+ * Calculate price change from snapshot
60
+ * @param snapshot - The price snapshot
61
+ * @returns Price change calculation result
62
+ */
63
+ export declare function calculatePriceChange(snapshot: PriceSnapshot): PriceChangeResult;
64
+ /**
65
+ * Formatted price change result
66
+ */
67
+ export interface FormattedPriceChange {
68
+ changeText: string;
69
+ changePercentText: string;
70
+ colorClass: string;
71
+ }
72
+ /**
73
+ * Format price change with appropriate color class
74
+ * @param snapshot - The price snapshot
75
+ * @returns Formatted price change with color class
76
+ */
77
+ export declare function formatPriceChange(snapshot: PriceSnapshot): FormattedPriceChange;
78
+ /**
79
+ * Tradeable instrument info
80
+ */
81
+ export interface TradeableInstrument {
82
+ isActive: boolean;
83
+ isActivelyTrading: boolean;
84
+ }
85
+ /**
86
+ * Check if instrument is actively tradeable
87
+ * @param instrument - The instrument with trading flags
88
+ * @returns true if tradeable, false otherwise
89
+ */
90
+ export declare function isInstrumentTradeable(instrument: TradeableInstrument): boolean;
91
+ /**
92
+ * Format market cap value
93
+ * @param marketCap - The market cap value as string or null
94
+ * @returns Formatted market cap string
95
+ */
96
+ export declare function formatMarketCap(marketCap: string | null | undefined): string;
97
+ /**
98
+ * Format trading volume
99
+ * @param volume - The volume number
100
+ * @returns Formatted volume string
101
+ */
102
+ export declare function formatVolume(volume: number): string;
103
+ /**
104
+ * Market data availability info
105
+ */
106
+ export interface MarketDataInfo {
107
+ openPrice?: string | null;
108
+ dayHigh?: string | null;
109
+ dayLow?: string | null;
110
+ volume?: number | null;
111
+ }
112
+ /**
113
+ * Check if market data is available
114
+ * @param marketData - The market data to check
115
+ * @returns true if market data is available, false otherwise
116
+ */
117
+ export declare function hasMarketData(marketData: MarketDataInfo): boolean;
118
+ //# sourceMappingURL=instruments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"instruments.d.ts","sourceRoot":"","sources":["../src/instruments.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAMrF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAalE;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,kBAAkB,GAAG,MAAM,CAIxE;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,mBAAmB,GAAG,MAAM,CAE5E;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,aAAa,GAAG,iBAAiB,CAS/E;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,aAAa,GAAG,oBAAoB,CAY/E;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,mBAAmB,GAAG,OAAO,CAE9E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAU5E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKnD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,cAAc,GAAG,OAAO,CAEjE"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Instrument and market data formatting utilities
3
+ */
4
+ import { INSTRUMENT_TYPE_ETF, INSTRUMENT_TYPE_FUND, INSTRUMENT_TYPE_STOCK, CURRENCY_USD, CURRENCY_AUD, } from '@cranberry-money/shared-constants';
5
+ /**
6
+ * Format instrument price with currency symbol
7
+ * @param price - The price as string
8
+ * @param currency - The currency code
9
+ * @returns Formatted price with currency symbol
10
+ */
11
+ export function formatInstrumentPrice(price, currency) {
12
+ const numericPrice = parseFloat(price);
13
+ if (isNaN(numericPrice))
14
+ return price;
15
+ const currencySymbol = getCurrencySymbol(currency);
16
+ return `${currencySymbol}${numericPrice.toFixed(2)}`;
17
+ }
18
+ /**
19
+ * Get currency symbol for display
20
+ * @param currency - The currency code
21
+ * @returns Currency symbol
22
+ */
23
+ export function getCurrencySymbol(currency) {
24
+ switch (currency) {
25
+ case CURRENCY_USD:
26
+ return '$';
27
+ case CURRENCY_AUD:
28
+ return 'A$';
29
+ case 'EUR':
30
+ return '€';
31
+ case 'GBP':
32
+ return '£';
33
+ default:
34
+ return '$'; // Default to USD symbol
35
+ }
36
+ }
37
+ /**
38
+ * Determine instrument type based on flags
39
+ * @param instrument - The instrument with type flags
40
+ * @returns Instrument type string
41
+ */
42
+ export function getInstrumentType(instrument) {
43
+ if (instrument.isEtf)
44
+ return INSTRUMENT_TYPE_ETF;
45
+ if (instrument.isFund)
46
+ return INSTRUMENT_TYPE_FUND;
47
+ return INSTRUMENT_TYPE_STOCK;
48
+ }
49
+ /**
50
+ * Format instrument display name with symbol
51
+ * @param instrument - The instrument with symbol and name
52
+ * @returns Formatted display name
53
+ */
54
+ export function formatInstrumentName(instrument) {
55
+ return `${instrument.symbol} - ${instrument.name}`;
56
+ }
57
+ /**
58
+ * Calculate price change from snapshot
59
+ * @param snapshot - The price snapshot
60
+ * @returns Price change calculation result
61
+ */
62
+ export function calculatePriceChange(snapshot) {
63
+ const change = parseFloat(snapshot.change);
64
+ const changePercent = parseFloat(snapshot.changePercent);
65
+ return {
66
+ change,
67
+ changePercent,
68
+ isPositive: change >= 0,
69
+ };
70
+ }
71
+ /**
72
+ * Format price change with appropriate color class
73
+ * @param snapshot - The price snapshot
74
+ * @returns Formatted price change with color class
75
+ */
76
+ export function formatPriceChange(snapshot) {
77
+ const { change, changePercent, isPositive } = calculatePriceChange(snapshot);
78
+ const changeText = `${isPositive ? '+' : ''}${change.toFixed(2)}`;
79
+ const changePercentText = `${isPositive ? '+' : ''}${changePercent.toFixed(2)}%`;
80
+ const colorClass = isPositive ? 'text-positive' : 'text-negative';
81
+ return {
82
+ changeText,
83
+ changePercentText,
84
+ colorClass,
85
+ };
86
+ }
87
+ /**
88
+ * Check if instrument is actively tradeable
89
+ * @param instrument - The instrument with trading flags
90
+ * @returns true if tradeable, false otherwise
91
+ */
92
+ export function isInstrumentTradeable(instrument) {
93
+ return instrument.isActive && instrument.isActivelyTrading;
94
+ }
95
+ /**
96
+ * Format market cap value
97
+ * @param marketCap - The market cap value as string or null
98
+ * @returns Formatted market cap string
99
+ */
100
+ export function formatMarketCap(marketCap) {
101
+ if (!marketCap)
102
+ return 'N/A';
103
+ const marketCapValue = parseFloat(marketCap);
104
+ if (isNaN(marketCapValue))
105
+ return 'N/A';
106
+ if (marketCapValue >= 1e12)
107
+ return `$${(marketCapValue / 1e12).toFixed(1)}T`;
108
+ if (marketCapValue >= 1e9)
109
+ return `$${(marketCapValue / 1e9).toFixed(1)}B`;
110
+ if (marketCapValue >= 1e6)
111
+ return `$${(marketCapValue / 1e6).toFixed(1)}M`;
112
+ return `$${marketCapValue.toFixed(0)}`;
113
+ }
114
+ /**
115
+ * Format trading volume
116
+ * @param volume - The volume number
117
+ * @returns Formatted volume string
118
+ */
119
+ export function formatVolume(volume) {
120
+ if (volume >= 1e9)
121
+ return `${(volume / 1e9).toFixed(1)}B`;
122
+ if (volume >= 1e6)
123
+ return `${(volume / 1e6).toFixed(1)}M`;
124
+ if (volume >= 1e3)
125
+ return `${(volume / 1e3).toFixed(1)}K`;
126
+ return volume.toString();
127
+ }
128
+ /**
129
+ * Check if market data is available
130
+ * @param marketData - The market data to check
131
+ * @returns true if market data is available, false otherwise
132
+ */
133
+ export function hasMarketData(marketData) {
134
+ return Boolean(marketData.openPrice || marketData.dayHigh || marketData.dayLow || marketData.volume);
135
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cranberry-money/shared-utils",
3
- "version": "4.10.0",
3
+ "version": "4.12.0",
4
4
  "description": "Shared utility functions for MyPortfolio platform",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",