@finatic/client 0.0.142 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/LICENSE +39 -0
- package/README.md +54 -425
- package/dist/index.d.ts +7329 -1579
- package/dist/index.js +8110 -6114
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8047 -6085
- package/dist/index.mjs.map +1 -1
- package/package.json +77 -33
- package/dist/types/core/client/ApiClient.d.ts +0 -270
- package/dist/types/core/client/FinaticConnect.d.ts +0 -332
- package/dist/types/core/portal/PortalUI.d.ts +0 -37
- package/dist/types/index.d.ts +0 -12
- package/dist/types/lib/logger/index.d.ts +0 -2
- package/dist/types/lib/logger/logger.d.ts +0 -4
- package/dist/types/lib/logger/logger.types.d.ts +0 -28
- package/dist/types/mocks/MockApiClient.d.ts +0 -171
- package/dist/types/mocks/MockDataProvider.d.ts +0 -139
- package/dist/types/mocks/MockFactory.d.ts +0 -53
- package/dist/types/mocks/utils.d.ts +0 -24
- package/dist/types/themes/portalPresets.d.ts +0 -9
- package/dist/types/types/api/auth.d.ts +0 -93
- package/dist/types/types/api/broker.d.ts +0 -421
- package/dist/types/types/api/core.d.ts +0 -46
- package/dist/types/types/api/errors.d.ts +0 -31
- package/dist/types/types/api/orders.d.ts +0 -39
- package/dist/types/types/api/portfolio.d.ts +0 -55
- package/dist/types/types/common/pagination.d.ts +0 -33
- package/dist/types/types/connect.d.ts +0 -58
- package/dist/types/types/index.d.ts +0 -13
- package/dist/types/types/portal.d.ts +0 -204
- package/dist/types/types/ui/theme.d.ts +0 -104
- package/dist/types/utils/brokerUtils.d.ts +0 -30
- package/dist/types/utils/errors.d.ts +0 -45
- package/dist/types/utils/events.d.ts +0 -12
- package/dist/types/utils/themeUtils.d.ts +0 -34
- package/src/core/client/ApiClient.ts +0 -2004
- package/src/core/client/FinaticConnect.ts +0 -1606
- package/src/core/portal/PortalUI.ts +0 -335
- package/src/index.d.ts +0 -23
- package/src/index.ts +0 -100
- package/src/lib/logger/index.ts +0 -3
- package/src/lib/logger/logger.ts +0 -332
- package/src/lib/logger/logger.types.ts +0 -34
- package/src/mocks/MockApiClient.ts +0 -1058
- package/src/mocks/MockDataProvider.ts +0 -986
- package/src/mocks/MockFactory.ts +0 -97
- package/src/mocks/utils.ts +0 -133
- package/src/themes/portalPresets.ts +0 -1307
- package/src/types/api/auth.ts +0 -112
- package/src/types/api/broker.ts +0 -461
- package/src/types/api/core.ts +0 -53
- package/src/types/api/errors.ts +0 -35
- package/src/types/api/orders.ts +0 -45
- package/src/types/api/portfolio.ts +0 -59
- package/src/types/common/pagination.ts +0 -164
- package/src/types/connect.ts +0 -56
- package/src/types/index.ts +0 -25
- package/src/types/portal.ts +0 -214
- package/src/types/ui/theme.ts +0 -105
- package/src/utils/brokerUtils.ts +0 -104
- package/src/utils/errors.ts +0 -104
- package/src/utils/events.ts +0 -66
- package/src/utils/themeUtils.ts +0 -165
package/src/utils/brokerUtils.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Broker filtering utility functions
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { setupLogger, buildLoggerExtra, LoggerExtra } from '../lib/logger';
|
|
6
|
-
|
|
7
|
-
const brokerLogger = setupLogger('FinaticClientSDK.BrokerUtils', undefined, {
|
|
8
|
-
codebase: 'FinaticClientSDK',
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
const buildBrokerExtra = (functionName: string, metadata?: Record<string, unknown>): LoggerExtra => ({
|
|
12
|
-
module: 'BrokerUtils',
|
|
13
|
-
function: functionName,
|
|
14
|
-
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
// Supported broker names and their corresponding IDs (including aliases)
|
|
18
|
-
const SUPPORTED_BROKERS: Record<string, string> = {
|
|
19
|
-
'alpaca': 'alpaca',
|
|
20
|
-
'robinhood': 'robinhood',
|
|
21
|
-
'tasty_trade': 'tasty_trade',
|
|
22
|
-
'ninja_trader': 'ninja_trader',
|
|
23
|
-
'tradovate': 'tradovate', // Alias for ninja_trader
|
|
24
|
-
'interactive_brokers': 'interactive_brokers',
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Convert broker names to broker IDs, filtering out unsupported ones
|
|
29
|
-
* @param brokerNames Array of broker names to convert
|
|
30
|
-
* @returns Object containing valid broker IDs and any warnings about unsupported names
|
|
31
|
-
*/
|
|
32
|
-
export function convertBrokerNamesToIds(brokerNames: string[]): {
|
|
33
|
-
brokerIds: string[];
|
|
34
|
-
warnings: string[];
|
|
35
|
-
} {
|
|
36
|
-
const brokerIds: string[] = [];
|
|
37
|
-
const warnings: string[] = [];
|
|
38
|
-
|
|
39
|
-
for (const brokerName of brokerNames) {
|
|
40
|
-
const brokerId = SUPPORTED_BROKERS[brokerName.toLowerCase()];
|
|
41
|
-
if (brokerId) {
|
|
42
|
-
brokerIds.push(brokerId);
|
|
43
|
-
} else {
|
|
44
|
-
warnings.push(`Broker name '${brokerName}' is not supported. Supported brokers: ${Object.keys(SUPPORTED_BROKERS).join(', ')}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return { brokerIds, warnings };
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Append broker filter parameters to a portal URL
|
|
53
|
-
* @param baseUrl The base portal URL (may already have query parameters)
|
|
54
|
-
* @param brokerNames Array of broker names to filter by
|
|
55
|
-
* @returns The portal URL with broker filter parameters appended
|
|
56
|
-
*/
|
|
57
|
-
export function appendBrokerFilterToURL(baseUrl: string, brokerNames?: string[]): string {
|
|
58
|
-
if (!brokerNames || brokerNames.length === 0) {
|
|
59
|
-
return baseUrl;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
const url = new URL(baseUrl);
|
|
64
|
-
const { brokerIds, warnings } = convertBrokerNamesToIds(brokerNames);
|
|
65
|
-
|
|
66
|
-
// Log warnings for unsupported broker names
|
|
67
|
-
warnings.forEach(warning =>
|
|
68
|
-
brokerLogger.warn('Unsupported broker name provided', buildBrokerExtra('appendBrokerFilterToURL', {
|
|
69
|
-
warning,
|
|
70
|
-
})),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
// Only add broker filter if we have valid broker IDs
|
|
74
|
-
if (brokerIds.length > 0) {
|
|
75
|
-
const encodedBrokers = btoa(JSON.stringify(brokerIds));
|
|
76
|
-
url.searchParams.set('brokers', encodedBrokers);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return url.toString();
|
|
80
|
-
} catch (error) {
|
|
81
|
-
brokerLogger.exception('Failed to append broker filter to URL', error, buildBrokerExtra('appendBrokerFilterToURL', {
|
|
82
|
-
base_url: baseUrl,
|
|
83
|
-
brokers_count: brokerNames?.length ?? 0,
|
|
84
|
-
}));
|
|
85
|
-
return baseUrl;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Get list of supported broker names
|
|
91
|
-
* @returns Array of supported broker names
|
|
92
|
-
*/
|
|
93
|
-
export function getSupportedBrokerNames(): string[] {
|
|
94
|
-
return Object.keys(SUPPORTED_BROKERS);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Check if a broker name is supported
|
|
99
|
-
* @param brokerName The broker name to check
|
|
100
|
-
* @returns True if the broker is supported, false otherwise
|
|
101
|
-
*/
|
|
102
|
-
export function isBrokerSupported(brokerName: string): boolean {
|
|
103
|
-
return brokerName.toLowerCase() in SUPPORTED_BROKERS;
|
|
104
|
-
}
|
package/src/utils/errors.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
export class BaseError extends Error {
|
|
2
|
-
constructor(
|
|
3
|
-
message: string,
|
|
4
|
-
public readonly code: string = 'UNKNOWN_ERROR'
|
|
5
|
-
) {
|
|
6
|
-
super(message);
|
|
7
|
-
this.name = this.constructor.name;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class ApiError extends BaseError {
|
|
12
|
-
constructor(
|
|
13
|
-
public readonly status: number,
|
|
14
|
-
message: string,
|
|
15
|
-
public readonly details?: Record<string, any>
|
|
16
|
-
) {
|
|
17
|
-
super(message, `API_ERROR_${status}`);
|
|
18
|
-
this.name = 'ApiError';
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export class SessionError extends ApiError {
|
|
23
|
-
constructor(message: string, details?: Record<string, any>) {
|
|
24
|
-
super(400, message, details);
|
|
25
|
-
this.name = 'SessionError';
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export class AuthenticationError extends ApiError {
|
|
30
|
-
constructor(message: string, details?: Record<string, any>) {
|
|
31
|
-
super(401, message, details);
|
|
32
|
-
this.name = 'AuthenticationError';
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export class AuthorizationError extends ApiError {
|
|
37
|
-
constructor(message: string, details?: Record<string, any>) {
|
|
38
|
-
super(403, message, details);
|
|
39
|
-
this.name = 'AuthorizationError';
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export class RateLimitError extends ApiError {
|
|
44
|
-
constructor(message: string, details?: Record<string, any>) {
|
|
45
|
-
super(429, message, details);
|
|
46
|
-
this.name = 'RateLimitError';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export class TokenError extends BaseError {
|
|
51
|
-
constructor(message: string) {
|
|
52
|
-
super(message, 'TOKEN_ERROR');
|
|
53
|
-
this.name = 'TokenError';
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export class ValidationError extends BaseError {
|
|
58
|
-
constructor(message: string) {
|
|
59
|
-
super(message, 'VALIDATION_ERROR');
|
|
60
|
-
this.name = 'ValidationError';
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export class NetworkError extends BaseError {
|
|
65
|
-
constructor(message: string) {
|
|
66
|
-
super(message, 'NETWORK_ERROR');
|
|
67
|
-
this.name = 'NetworkError';
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export class SecurityError extends BaseError {
|
|
72
|
-
constructor(message: string) {
|
|
73
|
-
super(message, 'SECURITY_ERROR');
|
|
74
|
-
this.name = 'SecurityError';
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export class CompanyAccessError extends ApiError {
|
|
79
|
-
constructor(message: string, details?: Record<string, any>) {
|
|
80
|
-
super(403, message, details);
|
|
81
|
-
this.name = 'CompanyAccessError';
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export class OrderError extends ApiError {
|
|
86
|
-
constructor(message: string, details?: Record<string, any>) {
|
|
87
|
-
super(500, message, details);
|
|
88
|
-
this.name = 'OrderError';
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export class OrderValidationError extends ApiError {
|
|
93
|
-
constructor(message: string, details?: Record<string, any>) {
|
|
94
|
-
super(400, message, details);
|
|
95
|
-
this.name = 'OrderValidationError';
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export class TradingNotEnabledError extends ApiError {
|
|
100
|
-
constructor(message: string, details?: Record<string, any>) {
|
|
101
|
-
super(403, message, details);
|
|
102
|
-
this.name = 'TradingNotEnabledError';
|
|
103
|
-
}
|
|
104
|
-
}
|
package/src/utils/events.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { setupLogger, buildLoggerExtra } from '../lib/logger';
|
|
2
|
-
|
|
3
|
-
type EventCallback = (...args: any[]) => void;
|
|
4
|
-
|
|
5
|
-
const eventsLogger = setupLogger('FinaticClientSDK.Events', undefined, {
|
|
6
|
-
codebase: 'FinaticClientSDK',
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
export class EventEmitter {
|
|
10
|
-
private events: Map<string, Set<EventCallback>> = new Map();
|
|
11
|
-
|
|
12
|
-
public on(event: string, callback: EventCallback): void {
|
|
13
|
-
if (!this.events.has(event)) {
|
|
14
|
-
this.events.set(event, new Set());
|
|
15
|
-
}
|
|
16
|
-
this.events.get(event)!.add(callback);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
public off(event: string, callback: EventCallback): void {
|
|
20
|
-
if (this.events.has(event)) {
|
|
21
|
-
this.events.get(event)!.delete(callback);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
public once(event: string, callback: EventCallback): void {
|
|
26
|
-
const onceCallback = (...args: any[]) => {
|
|
27
|
-
callback(...args);
|
|
28
|
-
this.off(event, onceCallback);
|
|
29
|
-
};
|
|
30
|
-
this.on(event, onceCallback);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
public emit(event: string, ...args: any[]): void {
|
|
34
|
-
if (this.events.has(event)) {
|
|
35
|
-
this.events.get(event)!.forEach(callback => {
|
|
36
|
-
try {
|
|
37
|
-
callback(...args);
|
|
38
|
-
} catch (error) {
|
|
39
|
-
eventsLogger.exception('Error in event handler', error, {
|
|
40
|
-
module: 'EventEmitter',
|
|
41
|
-
function: 'emit',
|
|
42
|
-
...buildLoggerExtra({
|
|
43
|
-
event_name: event,
|
|
44
|
-
}),
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
public removeAllListeners(event?: string): void {
|
|
52
|
-
if (event) {
|
|
53
|
-
this.events.delete(event);
|
|
54
|
-
} else {
|
|
55
|
-
this.events.clear();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
public listenerCount(event: string): number {
|
|
60
|
-
return this.events.get(event)?.size || 0;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
public listeners(event: string): EventCallback[] {
|
|
64
|
-
return Array.from(this.events.get(event) || []);
|
|
65
|
-
}
|
|
66
|
-
}
|
package/src/utils/themeUtils.ts
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { PortalTheme, PortalThemeConfig } from '../types/portal';
|
|
2
|
-
import { portalThemePresets } from '../themes/portalPresets';
|
|
3
|
-
import { setupLogger, buildLoggerExtra, LoggerExtra } from '../lib/logger';
|
|
4
|
-
|
|
5
|
-
const themeLogger = setupLogger('FinaticClientSDK.ThemeUtils', undefined, {
|
|
6
|
-
codebase: 'FinaticClientSDK',
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
const buildThemeExtra = (functionName: string, metadata?: Record<string, unknown>): LoggerExtra => ({
|
|
10
|
-
module: 'ThemeUtils',
|
|
11
|
-
function: functionName,
|
|
12
|
-
...(metadata ? buildLoggerExtra(metadata) : {}),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Generate a portal URL with theme parameters
|
|
17
|
-
* @param baseUrl The base portal URL
|
|
18
|
-
* @param theme The theme configuration
|
|
19
|
-
* @returns The portal URL with theme parameters
|
|
20
|
-
*/
|
|
21
|
-
export function generatePortalThemeURL(baseUrl: string, theme?: PortalTheme): string {
|
|
22
|
-
if (!theme) {
|
|
23
|
-
return baseUrl;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const url = new URL(baseUrl);
|
|
28
|
-
|
|
29
|
-
if (theme.preset) {
|
|
30
|
-
// Use preset theme
|
|
31
|
-
url.searchParams.set('theme', theme.preset);
|
|
32
|
-
} else if (theme.custom) {
|
|
33
|
-
// Use custom theme
|
|
34
|
-
const encodedTheme = btoa(JSON.stringify(theme.custom));
|
|
35
|
-
url.searchParams.set('theme', 'custom');
|
|
36
|
-
url.searchParams.set('themeObject', encodedTheme);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return url.toString();
|
|
40
|
-
} catch (error) {
|
|
41
|
-
themeLogger.exception('Failed to generate theme URL', error, buildThemeExtra('generatePortalThemeURL', {
|
|
42
|
-
base_url: baseUrl,
|
|
43
|
-
has_theme: Boolean(theme),
|
|
44
|
-
}));
|
|
45
|
-
return baseUrl;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Generate a portal URL with theme parameters, appending to existing query params
|
|
51
|
-
* @param baseUrl The base portal URL (may already have query parameters)
|
|
52
|
-
* @param theme The theme configuration
|
|
53
|
-
* @returns The portal URL with theme parameters appended
|
|
54
|
-
*/
|
|
55
|
-
export function appendThemeToURL(baseUrl: string, theme?: PortalTheme): string {
|
|
56
|
-
if (!theme) {
|
|
57
|
-
return baseUrl;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
const url = new URL(baseUrl);
|
|
62
|
-
|
|
63
|
-
if (theme.preset) {
|
|
64
|
-
// Use preset theme
|
|
65
|
-
url.searchParams.set('theme', theme.preset);
|
|
66
|
-
} else if (theme.custom) {
|
|
67
|
-
// Use custom theme
|
|
68
|
-
const encodedTheme = btoa(JSON.stringify(theme.custom));
|
|
69
|
-
url.searchParams.set('theme', 'custom');
|
|
70
|
-
url.searchParams.set('themeObject', encodedTheme);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return url.toString();
|
|
74
|
-
} catch (error) {
|
|
75
|
-
themeLogger.exception('Failed to append theme to URL', error, buildThemeExtra('appendThemeToURL', {
|
|
76
|
-
base_url: baseUrl,
|
|
77
|
-
has_theme: Boolean(theme),
|
|
78
|
-
}));
|
|
79
|
-
return baseUrl;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Get a theme configuration by preset name
|
|
85
|
-
* @param preset The preset theme name
|
|
86
|
-
* @returns The theme configuration or undefined if not found
|
|
87
|
-
*/
|
|
88
|
-
export function getThemePreset(preset: string): PortalThemeConfig | undefined {
|
|
89
|
-
return portalThemePresets[preset];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Validate a custom theme configuration
|
|
94
|
-
* @param theme The theme configuration to validate
|
|
95
|
-
* @returns True if valid, false otherwise
|
|
96
|
-
*/
|
|
97
|
-
export function validateCustomTheme(theme: PortalThemeConfig): boolean {
|
|
98
|
-
try {
|
|
99
|
-
// Only validate what's provided - everything else gets defaults
|
|
100
|
-
if (theme.mode && !['dark', 'light', 'auto'].includes(theme.mode)) {
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// If colors are provided, validate the structure
|
|
105
|
-
if (theme.colors) {
|
|
106
|
-
// Check that any provided color sections have valid structure
|
|
107
|
-
const colorSections = ['background', 'status', 'text', 'border', 'input', 'button'];
|
|
108
|
-
for (const section of colorSections) {
|
|
109
|
-
const colorSection = theme.colors[section as keyof typeof theme.colors];
|
|
110
|
-
if (colorSection && typeof colorSection !== 'object') {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// If typography is provided, validate structure
|
|
117
|
-
if (theme.typography) {
|
|
118
|
-
if (theme.typography.fontSize && typeof theme.typography.fontSize !== 'object') {
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
if (theme.typography.fontWeight && typeof theme.typography.fontWeight !== 'object') {
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// If effects are provided, validate structure
|
|
127
|
-
if (theme.effects) {
|
|
128
|
-
if (theme.effects.animations && typeof theme.effects.animations !== 'object') {
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
if (theme.effects.shadows && typeof theme.effects.shadows !== 'object') {
|
|
132
|
-
return false;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return true;
|
|
137
|
-
} catch (error) {
|
|
138
|
-
themeLogger.exception('Theme validation error', error, buildThemeExtra('validateCustomTheme'));
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Create a custom theme from a preset with modifications
|
|
145
|
-
* @param preset The base preset theme
|
|
146
|
-
* @param modifications Partial theme modifications
|
|
147
|
-
* @returns The modified theme configuration
|
|
148
|
-
*/
|
|
149
|
-
export function createCustomThemeFromPreset(
|
|
150
|
-
preset: string,
|
|
151
|
-
modifications: Partial<PortalThemeConfig>
|
|
152
|
-
): PortalThemeConfig | null {
|
|
153
|
-
const baseTheme = getThemePreset(preset);
|
|
154
|
-
if (!baseTheme) {
|
|
155
|
-
themeLogger.warn('Preset theme not found', buildThemeExtra('createCustomThemeFromPreset', {
|
|
156
|
-
preset,
|
|
157
|
-
}));
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
...baseTheme,
|
|
163
|
-
...modifications,
|
|
164
|
-
};
|
|
165
|
-
}
|