@bernierllc/chat-suite 1.0.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/.eslintrc.js +26 -0
- package/README.md +542 -0
- package/__tests__/integration/ChatSuite.test.ts +235 -0
- package/__tests__/unit/ConfigManager.test.ts +122 -0
- package/__tests__/unit/MessageOrchestrator.test.ts +223 -0
- package/__tests__/unit/UserManager.test.ts +208 -0
- package/dist/ChatSuite.d.ts +76 -0
- package/dist/ChatSuite.d.ts.map +1 -0
- package/dist/ChatSuite.js +273 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/neverhub/discovery.d.ts +40 -0
- package/dist/neverhub/discovery.d.ts.map +1 -0
- package/dist/neverhub/discovery.js +106 -0
- package/dist/neverhub/event-handlers.d.ts +38 -0
- package/dist/neverhub/event-handlers.d.ts.map +1 -0
- package/dist/neverhub/event-handlers.js +125 -0
- package/dist/neverhub/index.d.ts +4 -0
- package/dist/neverhub/index.d.ts.map +1 -0
- package/dist/neverhub/index.js +26 -0
- package/dist/neverhub/service-registration.d.ts +29 -0
- package/dist/neverhub/service-registration.d.ts.map +1 -0
- package/dist/neverhub/service-registration.js +66 -0
- package/dist/providers/ChatKitProvider.d.ts +53 -0
- package/dist/providers/ChatKitProvider.d.ts.map +1 -0
- package/dist/providers/ChatKitProvider.js +167 -0
- package/dist/providers/ExternalProviders.d.ts +101 -0
- package/dist/providers/ExternalProviders.d.ts.map +1 -0
- package/dist/providers/ExternalProviders.js +153 -0
- package/dist/providers/index.d.ts +3 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +25 -0
- package/dist/services/ConfigManager.d.ts +50 -0
- package/dist/services/ConfigManager.d.ts.map +1 -0
- package/dist/services/ConfigManager.js +95 -0
- package/dist/services/HealthMonitor.d.ts +58 -0
- package/dist/services/HealthMonitor.d.ts.map +1 -0
- package/dist/services/HealthMonitor.js +148 -0
- package/dist/services/MessageOrchestrator.d.ts +63 -0
- package/dist/services/MessageOrchestrator.d.ts.map +1 -0
- package/dist/services/MessageOrchestrator.js +212 -0
- package/dist/services/ProviderManager.d.ts +83 -0
- package/dist/services/ProviderManager.d.ts.map +1 -0
- package/dist/services/ProviderManager.js +204 -0
- package/dist/services/UserManager.d.ts +61 -0
- package/dist/services/UserManager.d.ts.map +1 -0
- package/dist/services/UserManager.js +141 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +28 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +26 -0
- package/dist/types/message-types.d.ts +80 -0
- package/dist/types/message-types.d.ts.map +1 -0
- package/dist/types/message-types.js +32 -0
- package/dist/types/provider-types.d.ts +96 -0
- package/dist/types/provider-types.d.ts.map +1 -0
- package/dist/types/provider-types.js +9 -0
- package/dist/types/suite-types.d.ts +101 -0
- package/dist/types/suite-types.d.ts.map +1 -0
- package/dist/types/suite-types.js +9 -0
- package/dist/utils/config-inheritance.d.ts +24 -0
- package/dist/utils/config-inheritance.d.ts.map +1 -0
- package/dist/utils/config-inheritance.js +167 -0
- package/dist/utils/error-handling.d.ts +37 -0
- package/dist/utils/error-handling.d.ts.map +1 -0
- package/dist/utils/error-handling.js +102 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +27 -0
- package/dist/utils/message-bridge.d.ts +68 -0
- package/dist/utils/message-bridge.d.ts.map +1 -0
- package/dist/utils/message-bridge.js +126 -0
- package/dist/utils/user-mapping.d.ts +63 -0
- package/dist/utils/user-mapping.d.ts.map +1 -0
- package/dist/utils/user-mapping.js +110 -0
- package/jest.config.cjs +30 -0
- package/package.json +67 -0
- package/src/ChatSuite.ts +347 -0
- package/src/index.ts +25 -0
- package/src/neverhub/discovery.ts +137 -0
- package/src/neverhub/event-handlers.ts +141 -0
- package/src/neverhub/index.ts +11 -0
- package/src/neverhub/service-registration.ts +89 -0
- package/src/providers/ChatKitProvider.ts +193 -0
- package/src/providers/ExternalProviders.ts +171 -0
- package/src/providers/index.ts +10 -0
- package/src/services/ConfigManager.ts +112 -0
- package/src/services/HealthMonitor.ts +169 -0
- package/src/services/MessageOrchestrator.ts +271 -0
- package/src/services/ProviderManager.ts +247 -0
- package/src/services/UserManager.ts +176 -0
- package/src/services/index.ts +13 -0
- package/src/types/index.ts +11 -0
- package/src/types/message-types.ts +94 -0
- package/src/types/provider-types.ts +116 -0
- package/src/types/suite-types.ts +117 -0
- package/src/utils/config-inheritance.ts +189 -0
- package/src/utils/error-handling.ts +114 -0
- package/src/utils/index.ts +12 -0
- package/src/utils/message-bridge.ts +164 -0
- package/src/utils/user-mapping.ts +132 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
Provider,
|
|
11
|
+
ProviderMessage,
|
|
12
|
+
ProviderMessageResult,
|
|
13
|
+
ProviderHealth
|
|
14
|
+
} from '../types';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Base class for external providers (Slack, Discord, Teams)
|
|
18
|
+
* These are optional peer dependencies and will be implemented when those packages are available
|
|
19
|
+
*/
|
|
20
|
+
abstract class ExternalProvider implements Provider {
|
|
21
|
+
public abstract readonly type: 'slack' | 'discord' | 'teams';
|
|
22
|
+
public abstract readonly name: string;
|
|
23
|
+
|
|
24
|
+
protected initialized = false;
|
|
25
|
+
|
|
26
|
+
public async initialize(): Promise<void> {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`${this.name} provider is not implemented yet. ` +
|
|
29
|
+
`Install ${this.name} package to enable this provider.`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public isInitialized(): boolean {
|
|
34
|
+
return this.initialized;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public async sendMessage(_message: ProviderMessage): Promise<ProviderMessageResult> {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`${this.name} provider is not available. ` +
|
|
40
|
+
`Install ${this.name} package to send messages.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public async getHealth(): Promise<ProviderHealth> {
|
|
45
|
+
return {
|
|
46
|
+
status: 'unknown',
|
|
47
|
+
lastCheck: new Date(),
|
|
48
|
+
errorCount: 0,
|
|
49
|
+
details: {
|
|
50
|
+
error: `${this.name} provider not installed`
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async shutdown(): Promise<void> {
|
|
56
|
+
this.initialized = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Slack provider placeholder
|
|
62
|
+
*
|
|
63
|
+
* To enable Slack integration:
|
|
64
|
+
* 1. Install @bernierllc/chat-integration-slack
|
|
65
|
+
* 2. Import SlackProvider from that package
|
|
66
|
+
* 3. Register with the chat suite
|
|
67
|
+
*
|
|
68
|
+
* Example:
|
|
69
|
+
* ```typescript
|
|
70
|
+
* import { SlackProvider } from '@bernierllc/chat-integration-slack';
|
|
71
|
+
*
|
|
72
|
+
* const slackProvider = new SlackProvider({
|
|
73
|
+
* token: process.env.SLACK_BOT_TOKEN,
|
|
74
|
+
* channels: ['#general', '#dev']
|
|
75
|
+
* });
|
|
76
|
+
*
|
|
77
|
+
* await chatSuite.registerProvider(slackProvider);
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export class SlackProvider extends ExternalProvider {
|
|
81
|
+
public readonly type = 'slack' as const;
|
|
82
|
+
public readonly name = '@bernierllc/chat-integration-slack';
|
|
83
|
+
|
|
84
|
+
constructor(_config?: Record<string, unknown>) {
|
|
85
|
+
super();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Discord provider placeholder
|
|
91
|
+
*
|
|
92
|
+
* To enable Discord integration:
|
|
93
|
+
* 1. Install @bernierllc/chat-integration-discord
|
|
94
|
+
* 2. Import DiscordProvider from that package
|
|
95
|
+
* 3. Register with the chat suite
|
|
96
|
+
*
|
|
97
|
+
* Example:
|
|
98
|
+
* ```typescript
|
|
99
|
+
* import { DiscordProvider } from '@bernierllc/chat-integration-discord';
|
|
100
|
+
*
|
|
101
|
+
* const discordProvider = new DiscordProvider({
|
|
102
|
+
* token: process.env.DISCORD_BOT_TOKEN,
|
|
103
|
+
* guilds: ['123456789012345678']
|
|
104
|
+
* });
|
|
105
|
+
*
|
|
106
|
+
* await chatSuite.registerProvider(discordProvider);
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export class DiscordProvider extends ExternalProvider {
|
|
110
|
+
public readonly type = 'discord' as const;
|
|
111
|
+
public readonly name = '@bernierllc/chat-integration-discord';
|
|
112
|
+
|
|
113
|
+
constructor(_config?: Record<string, unknown>) {
|
|
114
|
+
super();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Microsoft Teams provider placeholder
|
|
120
|
+
*
|
|
121
|
+
* To enable Teams integration:
|
|
122
|
+
* 1. Install @bernierllc/chat-integration-teams
|
|
123
|
+
* 2. Import TeamsProvider from that package
|
|
124
|
+
* 3. Register with the chat suite
|
|
125
|
+
*
|
|
126
|
+
* Example:
|
|
127
|
+
* ```typescript
|
|
128
|
+
* import { TeamsProvider } from '@bernierllc/chat-integration-teams';
|
|
129
|
+
*
|
|
130
|
+
* const teamsProvider = new TeamsProvider({
|
|
131
|
+
* tenantId: process.env.TEAMS_TENANT_ID,
|
|
132
|
+
* clientId: process.env.TEAMS_CLIENT_ID,
|
|
133
|
+
* clientSecret: process.env.TEAMS_CLIENT_SECRET
|
|
134
|
+
* });
|
|
135
|
+
*
|
|
136
|
+
* await chatSuite.registerProvider(teamsProvider);
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export class TeamsProvider extends ExternalProvider {
|
|
140
|
+
public readonly type = 'teams' as const;
|
|
141
|
+
public readonly name = '@bernierllc/chat-integration-teams';
|
|
142
|
+
|
|
143
|
+
constructor(_config?: Record<string, unknown>) {
|
|
144
|
+
super();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Check if an external provider package is available
|
|
150
|
+
*/
|
|
151
|
+
export function isProviderAvailable(providerName: string): boolean {
|
|
152
|
+
try {
|
|
153
|
+
require.resolve(providerName);
|
|
154
|
+
return true;
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get list of available external providers
|
|
162
|
+
*/
|
|
163
|
+
export function getAvailableProviders(): string[] {
|
|
164
|
+
const providers = [
|
|
165
|
+
'@bernierllc/chat-integration-slack',
|
|
166
|
+
'@bernierllc/chat-integration-discord',
|
|
167
|
+
'@bernierllc/chat-integration-teams'
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
return providers.filter(isProviderAvailable);
|
|
171
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export * from './ChatKitProvider';
|
|
10
|
+
export * from './ExternalProviders';
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ChatSuiteConfig } from '../types';
|
|
10
|
+
import { DEFAULT_SUITE_CONFIG, mergeConfigs, validateConfig } from '../utils/config-inheritance';
|
|
11
|
+
import { ChatSuiteError, ChatSuiteErrorType } from '../utils/error-handling';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Manages chat suite configuration with validation and inheritance
|
|
15
|
+
*/
|
|
16
|
+
export class ConfigManager {
|
|
17
|
+
private config: ChatSuiteConfig;
|
|
18
|
+
private configOverrides: Map<string, unknown> = new Map();
|
|
19
|
+
|
|
20
|
+
constructor(initialConfig?: Partial<ChatSuiteConfig>) {
|
|
21
|
+
this.config = mergeConfigs(DEFAULT_SUITE_CONFIG, initialConfig || {});
|
|
22
|
+
this.validateConfiguration();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the current configuration
|
|
27
|
+
*/
|
|
28
|
+
public getConfig(): Readonly<ChatSuiteConfig> {
|
|
29
|
+
return this.config;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Update configuration
|
|
34
|
+
*/
|
|
35
|
+
public updateConfig(updates: Partial<ChatSuiteConfig>): void {
|
|
36
|
+
const newConfig = mergeConfigs(this.config, updates);
|
|
37
|
+
this.validateConfiguration(newConfig);
|
|
38
|
+
this.config = newConfig;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get a specific configuration value
|
|
43
|
+
*/
|
|
44
|
+
public get<K extends keyof ChatSuiteConfig>(key: K): ChatSuiteConfig[K] {
|
|
45
|
+
return this.config[key];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set a configuration override
|
|
50
|
+
*/
|
|
51
|
+
public setOverride(key: string, value: unknown): void {
|
|
52
|
+
this.configOverrides.set(key, value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get a configuration override
|
|
57
|
+
*/
|
|
58
|
+
public getOverride(key: string): unknown {
|
|
59
|
+
return this.configOverrides.get(key);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Clear all overrides
|
|
64
|
+
*/
|
|
65
|
+
public clearOverrides(): void {
|
|
66
|
+
this.configOverrides.clear();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Reset to default configuration
|
|
71
|
+
*/
|
|
72
|
+
public reset(): void {
|
|
73
|
+
this.config = { ...DEFAULT_SUITE_CONFIG };
|
|
74
|
+
this.configOverrides.clear();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validate the current configuration
|
|
79
|
+
*/
|
|
80
|
+
private validateConfiguration(config: ChatSuiteConfig = this.config): void {
|
|
81
|
+
const validation = validateConfig(config);
|
|
82
|
+
if (!validation.valid) {
|
|
83
|
+
throw new ChatSuiteError(
|
|
84
|
+
ChatSuiteErrorType.CONFIGURATION_ERROR,
|
|
85
|
+
`Configuration validation failed: ${validation.errors.join(', ')}`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Export configuration as JSON
|
|
92
|
+
*/
|
|
93
|
+
public toJSON(): string {
|
|
94
|
+
return JSON.stringify(this.config, null, 2);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Load configuration from JSON
|
|
99
|
+
*/
|
|
100
|
+
public fromJSON(json: string): void {
|
|
101
|
+
try {
|
|
102
|
+
const parsed = JSON.parse(json) as Partial<ChatSuiteConfig>;
|
|
103
|
+
this.updateConfig(parsed);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
throw new ChatSuiteError(
|
|
106
|
+
ChatSuiteErrorType.CONFIGURATION_ERROR,
|
|
107
|
+
'Failed to parse configuration JSON',
|
|
108
|
+
error instanceof Error ? error : undefined
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Provider, ProviderType, ChatSuiteHealth } from '../types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Health check result
|
|
13
|
+
*/
|
|
14
|
+
export interface HealthCheckResult {
|
|
15
|
+
healthy: boolean;
|
|
16
|
+
timestamp: Date;
|
|
17
|
+
details?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Monitors health of all chat suite components
|
|
22
|
+
*/
|
|
23
|
+
export class HealthMonitor {
|
|
24
|
+
private providers: Map<ProviderType, Provider> = new Map();
|
|
25
|
+
private lastHealthCheck?: Date;
|
|
26
|
+
private healthCheckInterval?: NodeJS.Timeout;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Register a provider for health monitoring
|
|
30
|
+
*/
|
|
31
|
+
public registerProvider(provider: Provider): void {
|
|
32
|
+
this.providers.set(provider.type, provider);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Unregister a provider
|
|
37
|
+
*/
|
|
38
|
+
public unregisterProvider(providerType: ProviderType): void {
|
|
39
|
+
this.providers.delete(providerType);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Start periodic health checks
|
|
44
|
+
*/
|
|
45
|
+
public startMonitoring(intervalMs: number = 60000): void {
|
|
46
|
+
this.stopMonitoring();
|
|
47
|
+
this.healthCheckInterval = setInterval(() => {
|
|
48
|
+
void this.checkHealth();
|
|
49
|
+
}, intervalMs);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Stop periodic health checks
|
|
54
|
+
*/
|
|
55
|
+
public stopMonitoring(): void {
|
|
56
|
+
if (this.healthCheckInterval) {
|
|
57
|
+
clearInterval(this.healthCheckInterval);
|
|
58
|
+
this.healthCheckInterval = undefined;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Perform health check on all components
|
|
64
|
+
*/
|
|
65
|
+
public async checkHealth(): Promise<ChatSuiteHealth> {
|
|
66
|
+
const timestamp = new Date();
|
|
67
|
+
this.lastHealthCheck = timestamp;
|
|
68
|
+
|
|
69
|
+
const providerStatuses: ChatSuiteHealth['providers'] = {} as ChatSuiteHealth['providers'];
|
|
70
|
+
|
|
71
|
+
// Check each provider
|
|
72
|
+
for (const [type, provider] of this.providers) {
|
|
73
|
+
try {
|
|
74
|
+
const health = await provider.getHealth();
|
|
75
|
+
providerStatuses[type] = {
|
|
76
|
+
status: health.status,
|
|
77
|
+
latency: health.latency,
|
|
78
|
+
errorCount: health.errorCount
|
|
79
|
+
};
|
|
80
|
+
} catch (error) {
|
|
81
|
+
providerStatuses[type] = {
|
|
82
|
+
status: 'unhealthy',
|
|
83
|
+
errorCount: 1
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Determine overall status
|
|
89
|
+
const statuses = Object.values(providerStatuses).map(p => p.status);
|
|
90
|
+
let overallStatus: ChatSuiteHealth['status'] = 'healthy';
|
|
91
|
+
|
|
92
|
+
if (statuses.some(s => s === 'unhealthy')) {
|
|
93
|
+
overallStatus = 'unhealthy';
|
|
94
|
+
} else if (statuses.some(s => s === 'degraded')) {
|
|
95
|
+
overallStatus = 'degraded';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
status: overallStatus,
|
|
100
|
+
providers: providerStatuses,
|
|
101
|
+
timestamp
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get health status for a specific provider
|
|
107
|
+
*/
|
|
108
|
+
public async checkProviderHealth(providerType: ProviderType): Promise<HealthCheckResult> {
|
|
109
|
+
const provider = this.providers.get(providerType);
|
|
110
|
+
if (!provider) {
|
|
111
|
+
return {
|
|
112
|
+
healthy: false,
|
|
113
|
+
timestamp: new Date(),
|
|
114
|
+
details: { error: 'Provider not registered' }
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const health = await provider.getHealth();
|
|
120
|
+
return {
|
|
121
|
+
healthy: health.status === 'healthy',
|
|
122
|
+
timestamp: new Date(),
|
|
123
|
+
details: {
|
|
124
|
+
status: health.status,
|
|
125
|
+
latency: health.latency,
|
|
126
|
+
errorCount: health.errorCount,
|
|
127
|
+
uptime: health.uptime
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
healthy: false,
|
|
133
|
+
timestamp: new Date(),
|
|
134
|
+
details: {
|
|
135
|
+
error: error instanceof Error ? error.message : 'Health check failed'
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Get the last health check timestamp
|
|
143
|
+
*/
|
|
144
|
+
public getLastCheckTime(): Date | undefined {
|
|
145
|
+
return this.lastHealthCheck;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get all registered providers
|
|
150
|
+
*/
|
|
151
|
+
public getRegisteredProviders(): ProviderType[] {
|
|
152
|
+
return Array.from(this.providers.keys());
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Check if monitoring is active
|
|
157
|
+
*/
|
|
158
|
+
public isMonitoring(): boolean {
|
|
159
|
+
return this.healthCheckInterval !== undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Clean up resources
|
|
164
|
+
*/
|
|
165
|
+
public shutdown(): void {
|
|
166
|
+
this.stopMonitoring();
|
|
167
|
+
this.providers.clear();
|
|
168
|
+
}
|
|
169
|
+
}
|