@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.
Files changed (105) hide show
  1. package/.eslintrc.js +26 -0
  2. package/README.md +542 -0
  3. package/__tests__/integration/ChatSuite.test.ts +235 -0
  4. package/__tests__/unit/ConfigManager.test.ts +122 -0
  5. package/__tests__/unit/MessageOrchestrator.test.ts +223 -0
  6. package/__tests__/unit/UserManager.test.ts +208 -0
  7. package/dist/ChatSuite.d.ts +76 -0
  8. package/dist/ChatSuite.d.ts.map +1 -0
  9. package/dist/ChatSuite.js +273 -0
  10. package/dist/index.d.ts +7 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +37 -0
  13. package/dist/neverhub/discovery.d.ts +40 -0
  14. package/dist/neverhub/discovery.d.ts.map +1 -0
  15. package/dist/neverhub/discovery.js +106 -0
  16. package/dist/neverhub/event-handlers.d.ts +38 -0
  17. package/dist/neverhub/event-handlers.d.ts.map +1 -0
  18. package/dist/neverhub/event-handlers.js +125 -0
  19. package/dist/neverhub/index.d.ts +4 -0
  20. package/dist/neverhub/index.d.ts.map +1 -0
  21. package/dist/neverhub/index.js +26 -0
  22. package/dist/neverhub/service-registration.d.ts +29 -0
  23. package/dist/neverhub/service-registration.d.ts.map +1 -0
  24. package/dist/neverhub/service-registration.js +66 -0
  25. package/dist/providers/ChatKitProvider.d.ts +53 -0
  26. package/dist/providers/ChatKitProvider.d.ts.map +1 -0
  27. package/dist/providers/ChatKitProvider.js +167 -0
  28. package/dist/providers/ExternalProviders.d.ts +101 -0
  29. package/dist/providers/ExternalProviders.d.ts.map +1 -0
  30. package/dist/providers/ExternalProviders.js +153 -0
  31. package/dist/providers/index.d.ts +3 -0
  32. package/dist/providers/index.d.ts.map +1 -0
  33. package/dist/providers/index.js +25 -0
  34. package/dist/services/ConfigManager.d.ts +50 -0
  35. package/dist/services/ConfigManager.d.ts.map +1 -0
  36. package/dist/services/ConfigManager.js +95 -0
  37. package/dist/services/HealthMonitor.d.ts +58 -0
  38. package/dist/services/HealthMonitor.d.ts.map +1 -0
  39. package/dist/services/HealthMonitor.js +148 -0
  40. package/dist/services/MessageOrchestrator.d.ts +63 -0
  41. package/dist/services/MessageOrchestrator.d.ts.map +1 -0
  42. package/dist/services/MessageOrchestrator.js +212 -0
  43. package/dist/services/ProviderManager.d.ts +83 -0
  44. package/dist/services/ProviderManager.d.ts.map +1 -0
  45. package/dist/services/ProviderManager.js +204 -0
  46. package/dist/services/UserManager.d.ts +61 -0
  47. package/dist/services/UserManager.d.ts.map +1 -0
  48. package/dist/services/UserManager.js +141 -0
  49. package/dist/services/index.d.ts +6 -0
  50. package/dist/services/index.d.ts.map +1 -0
  51. package/dist/services/index.js +28 -0
  52. package/dist/types/index.d.ts +4 -0
  53. package/dist/types/index.d.ts.map +1 -0
  54. package/dist/types/index.js +26 -0
  55. package/dist/types/message-types.d.ts +80 -0
  56. package/dist/types/message-types.d.ts.map +1 -0
  57. package/dist/types/message-types.js +32 -0
  58. package/dist/types/provider-types.d.ts +96 -0
  59. package/dist/types/provider-types.d.ts.map +1 -0
  60. package/dist/types/provider-types.js +9 -0
  61. package/dist/types/suite-types.d.ts +101 -0
  62. package/dist/types/suite-types.d.ts.map +1 -0
  63. package/dist/types/suite-types.js +9 -0
  64. package/dist/utils/config-inheritance.d.ts +24 -0
  65. package/dist/utils/config-inheritance.d.ts.map +1 -0
  66. package/dist/utils/config-inheritance.js +167 -0
  67. package/dist/utils/error-handling.d.ts +37 -0
  68. package/dist/utils/error-handling.d.ts.map +1 -0
  69. package/dist/utils/error-handling.js +102 -0
  70. package/dist/utils/index.d.ts +5 -0
  71. package/dist/utils/index.d.ts.map +1 -0
  72. package/dist/utils/index.js +27 -0
  73. package/dist/utils/message-bridge.d.ts +68 -0
  74. package/dist/utils/message-bridge.d.ts.map +1 -0
  75. package/dist/utils/message-bridge.js +126 -0
  76. package/dist/utils/user-mapping.d.ts +63 -0
  77. package/dist/utils/user-mapping.d.ts.map +1 -0
  78. package/dist/utils/user-mapping.js +110 -0
  79. package/jest.config.cjs +30 -0
  80. package/package.json +67 -0
  81. package/src/ChatSuite.ts +347 -0
  82. package/src/index.ts +25 -0
  83. package/src/neverhub/discovery.ts +137 -0
  84. package/src/neverhub/event-handlers.ts +141 -0
  85. package/src/neverhub/index.ts +11 -0
  86. package/src/neverhub/service-registration.ts +89 -0
  87. package/src/providers/ChatKitProvider.ts +193 -0
  88. package/src/providers/ExternalProviders.ts +171 -0
  89. package/src/providers/index.ts +10 -0
  90. package/src/services/ConfigManager.ts +112 -0
  91. package/src/services/HealthMonitor.ts +169 -0
  92. package/src/services/MessageOrchestrator.ts +271 -0
  93. package/src/services/ProviderManager.ts +247 -0
  94. package/src/services/UserManager.ts +176 -0
  95. package/src/services/index.ts +13 -0
  96. package/src/types/index.ts +11 -0
  97. package/src/types/message-types.ts +94 -0
  98. package/src/types/provider-types.ts +116 -0
  99. package/src/types/suite-types.ts +117 -0
  100. package/src/utils/config-inheritance.ts +189 -0
  101. package/src/utils/error-handling.ts +114 -0
  102. package/src/utils/index.ts +12 -0
  103. package/src/utils/message-bridge.ts +164 -0
  104. package/src/utils/user-mapping.ts +132 -0
  105. package/tsconfig.json +31 -0
@@ -0,0 +1,271 @@
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
+ ProviderType,
12
+ ProviderMessage,
13
+ ProviderMessageResult,
14
+ OrchestratedMessage,
15
+ BroadcastConfig,
16
+ SuiteResult
17
+ } from '../types';
18
+ import { MessagePriority, MessageStatus } from '../types';
19
+ import { MessageBridge } from '../utils/message-bridge';
20
+ import { ChatSuiteError, ChatSuiteErrorType, retryOperation } from '../utils/error-handling';
21
+
22
+ /**
23
+ * Orchestrates messages across all providers
24
+ */
25
+ export class MessageOrchestrator {
26
+ private messageBridge?: MessageBridge;
27
+ private messages: Map<string, OrchestratedMessage> = new Map();
28
+
29
+ /**
30
+ * Set message bridge for cross-platform sync
31
+ */
32
+ public setMessageBridge(bridge: MessageBridge): void {
33
+ this.messageBridge = bridge;
34
+ }
35
+
36
+ /**
37
+ * Send a message through a specific provider
38
+ */
39
+ public async sendMessage(
40
+ provider: Provider,
41
+ message: ProviderMessage
42
+ ): Promise<SuiteResult<ProviderMessageResult>> {
43
+ try {
44
+ if (!provider.isInitialized()) {
45
+ return {
46
+ success: false,
47
+ error: `Provider ${provider.type} is not initialized`
48
+ };
49
+ }
50
+
51
+ const result = await retryOperation(
52
+ () => provider.sendMessage(message),
53
+ 3,
54
+ 1000
55
+ );
56
+
57
+ return { success: true, data: result };
58
+ } catch (error) {
59
+ return {
60
+ success: false,
61
+ error: error instanceof Error ? error.message : 'Failed to send message'
62
+ };
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Broadcast a message to multiple platforms
68
+ */
69
+ public async broadcastMessage(
70
+ providers: Map<ProviderType, Provider>,
71
+ config: BroadcastConfig
72
+ ): Promise<SuiteResult<ProviderMessageResult[]>> {
73
+ try {
74
+ const results: ProviderMessageResult[] = [];
75
+ const targetProviders = config.platforms
76
+ .map(type => providers.get(type))
77
+ .filter((p): p is Provider => p !== undefined);
78
+
79
+ if (targetProviders.length === 0) {
80
+ return {
81
+ success: false,
82
+ error: 'No valid providers found for broadcast'
83
+ };
84
+ }
85
+
86
+ // Create orchestrated message
87
+ const orchestratedMessage: OrchestratedMessage = {
88
+ id: this.generateMessageId(),
89
+ content: config.content,
90
+ priority: config.priority || MessagePriority.NORMAL,
91
+ status: MessageStatus.SENDING,
92
+ targetProviders: config.platforms,
93
+ metadata: config.metadata,
94
+ createdAt: new Date(),
95
+ updatedAt: new Date()
96
+ };
97
+
98
+ this.messages.set(orchestratedMessage.id, orchestratedMessage);
99
+
100
+ // Send to all providers
101
+ for (const provider of targetProviders) {
102
+ const providerMessage: ProviderMessage = {
103
+ content: config.content,
104
+ channelId: config.channels?.[0],
105
+ metadata: config.metadata
106
+ };
107
+
108
+ const result = await this.sendMessage(provider, providerMessage);
109
+ if (result.success && result.data) {
110
+ results.push(result.data);
111
+ }
112
+ }
113
+
114
+ // Update orchestrated message status
115
+ orchestratedMessage.status = results.length > 0 ? MessageStatus.SENT : MessageStatus.FAILED;
116
+ orchestratedMessage.updatedAt = new Date();
117
+
118
+ return { success: true, data: results };
119
+ } catch (error) {
120
+ return {
121
+ success: false,
122
+ error: error instanceof Error ? error.message : 'Failed to broadcast message'
123
+ };
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Bridge a message from one provider to another
129
+ */
130
+ public async bridgeMessage(
131
+ sourceProvider: Provider,
132
+ targetProvider: Provider,
133
+ message: ProviderMessage
134
+ ): Promise<SuiteResult<ProviderMessageResult>> {
135
+ try {
136
+ if (!this.messageBridge) {
137
+ throw new ChatSuiteError(
138
+ ChatSuiteErrorType.CONFIGURATION_ERROR,
139
+ 'Message bridge not configured'
140
+ );
141
+ }
142
+
143
+ if (!this.messageBridge.shouldBridge(sourceProvider.type, targetProvider.type)) {
144
+ return {
145
+ success: false,
146
+ error: `Bridging from ${sourceProvider.type} to ${targetProvider.type} is not enabled`
147
+ };
148
+ }
149
+
150
+ // Transform message for target provider
151
+ const transformedMessage = this.messageBridge.transformMessage(
152
+ message,
153
+ sourceProvider.type,
154
+ targetProvider.type
155
+ );
156
+
157
+ // Send to target provider
158
+ const result = await this.sendMessage(targetProvider, transformedMessage);
159
+
160
+ if (result.success && result.data) {
161
+ // Record the bridge
162
+ this.messageBridge.recordBridgedMessage({
163
+ originalMessageId: result.data.messageId || 'unknown',
164
+ sourceProvider: sourceProvider.type,
165
+ targetProvider: targetProvider.type,
166
+ targetMessageId: result.data.messageId,
167
+ timestamp: new Date()
168
+ });
169
+ }
170
+
171
+ return result;
172
+ } catch (error) {
173
+ return {
174
+ success: false,
175
+ error: error instanceof Error ? error.message : 'Failed to bridge message'
176
+ };
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Get an orchestrated message by ID
182
+ */
183
+ public getMessage(messageId: string): OrchestratedMessage | undefined {
184
+ return this.messages.get(messageId);
185
+ }
186
+
187
+ /**
188
+ * Get all messages
189
+ */
190
+ public getAllMessages(): OrchestratedMessage[] {
191
+ return Array.from(this.messages.values());
192
+ }
193
+
194
+ /**
195
+ * Get messages by status
196
+ */
197
+ public getMessagesByStatus(status: MessageStatus): OrchestratedMessage[] {
198
+ return Array.from(this.messages.values()).filter(m => m.status === status);
199
+ }
200
+
201
+ /**
202
+ * Update message status
203
+ */
204
+ public updateMessageStatus(messageId: string, status: MessageStatus): boolean {
205
+ const message = this.messages.get(messageId);
206
+ if (!message) {
207
+ return false;
208
+ }
209
+
210
+ message.status = status;
211
+ message.updatedAt = new Date();
212
+ return true;
213
+ }
214
+
215
+ /**
216
+ * Clear all messages
217
+ */
218
+ public clearMessages(): void {
219
+ this.messages.clear();
220
+ }
221
+
222
+ /**
223
+ * Get message count
224
+ */
225
+ public getMessageCount(): number {
226
+ return this.messages.size;
227
+ }
228
+
229
+ /**
230
+ * Get statistics
231
+ */
232
+ public getStats(): {
233
+ total: number;
234
+ byStatus: Record<MessageStatus, number>;
235
+ byPriority: Record<MessagePriority, number>;
236
+ } {
237
+ const byStatus: Record<MessageStatus, number> = {
238
+ [MessageStatus.PENDING]: 0,
239
+ [MessageStatus.ROUTING]: 0,
240
+ [MessageStatus.SENDING]: 0,
241
+ [MessageStatus.SENT]: 0,
242
+ [MessageStatus.FAILED]: 0,
243
+ [MessageStatus.RETRYING]: 0
244
+ };
245
+
246
+ const byPriority: Record<MessagePriority, number> = {
247
+ [MessagePriority.LOW]: 0,
248
+ [MessagePriority.NORMAL]: 0,
249
+ [MessagePriority.HIGH]: 0,
250
+ [MessagePriority.URGENT]: 0
251
+ };
252
+
253
+ this.messages.forEach(message => {
254
+ byStatus[message.status]++;
255
+ byPriority[message.priority]++;
256
+ });
257
+
258
+ return {
259
+ total: this.messages.size,
260
+ byStatus,
261
+ byPriority
262
+ };
263
+ }
264
+
265
+ /**
266
+ * Generate a unique message ID
267
+ */
268
+ private generateMessageId(): string {
269
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
270
+ }
271
+ }
@@ -0,0 +1,247 @@
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, SuiteResult } from '../types';
10
+ import { ChatSuiteError, ChatSuiteErrorType } from '../utils/error-handling';
11
+
12
+ /**
13
+ * Provider registration information
14
+ */
15
+ export interface ProviderRegistration {
16
+ provider: Provider;
17
+ priority: number;
18
+ enabled: boolean;
19
+ }
20
+
21
+ /**
22
+ * Manages all chat providers
23
+ */
24
+ export class ProviderManager {
25
+ private providers: Map<ProviderType, ProviderRegistration> = new Map();
26
+ private initializedProviders: Set<ProviderType> = new Set();
27
+
28
+ /**
29
+ * Register a provider
30
+ */
31
+ public registerProvider(registration: ProviderRegistration): SuiteResult<void> {
32
+ try {
33
+ if (this.providers.has(registration.provider.type)) {
34
+ return {
35
+ success: false,
36
+ error: `Provider ${registration.provider.type} is already registered`
37
+ };
38
+ }
39
+
40
+ this.providers.set(registration.provider.type, registration);
41
+ return { success: true };
42
+ } catch (error) {
43
+ return {
44
+ success: false,
45
+ error: error instanceof Error ? error.message : 'Failed to register provider'
46
+ };
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Initialize a provider
52
+ */
53
+ public async initializeProvider(providerType: ProviderType): Promise<SuiteResult<void>> {
54
+ try {
55
+ const registration = this.providers.get(providerType);
56
+ if (!registration) {
57
+ return {
58
+ success: false,
59
+ error: `Provider ${providerType} not registered`
60
+ };
61
+ }
62
+
63
+ if (!registration.enabled) {
64
+ return {
65
+ success: false,
66
+ error: `Provider ${providerType} is not enabled`
67
+ };
68
+ }
69
+
70
+ await registration.provider.initialize();
71
+ this.initializedProviders.add(providerType);
72
+ return { success: true };
73
+ } catch (error) {
74
+ return {
75
+ success: false,
76
+ error: error instanceof Error ? error.message : 'Failed to initialize provider'
77
+ };
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Initialize all enabled providers
83
+ */
84
+ public async initializeAll(): Promise<{
85
+ successful: ProviderType[];
86
+ failed: Array<{ type: ProviderType; error: string }>;
87
+ }> {
88
+ const successful: ProviderType[] = [];
89
+ const failed: Array<{ type: ProviderType; error: string }> = [];
90
+
91
+ for (const [type, registration] of this.providers) {
92
+ if (!registration.enabled) {
93
+ continue;
94
+ }
95
+
96
+ const result = await this.initializeProvider(type);
97
+ if (result.success) {
98
+ successful.push(type);
99
+ } else {
100
+ failed.push({ type, error: result.error || 'Unknown error' });
101
+ }
102
+ }
103
+
104
+ return { successful, failed };
105
+ }
106
+
107
+ /**
108
+ * Get a provider by type
109
+ */
110
+ public getProvider(providerType: ProviderType): Provider | undefined {
111
+ return this.providers.get(providerType)?.provider;
112
+ }
113
+
114
+ /**
115
+ * Check if a provider is initialized
116
+ */
117
+ public isInitialized(providerType: ProviderType): boolean {
118
+ return this.initializedProviders.has(providerType);
119
+ }
120
+
121
+ /**
122
+ * Get all enabled providers sorted by priority
123
+ */
124
+ public getEnabledProviders(): Provider[] {
125
+ const enabled = Array.from(this.providers.values())
126
+ .filter(reg => reg.enabled)
127
+ .sort((a, b) => a.priority - b.priority);
128
+
129
+ return enabled.map(reg => reg.provider);
130
+ }
131
+
132
+ /**
133
+ * Get all initialized providers
134
+ */
135
+ public getInitializedProviders(): Provider[] {
136
+ return Array.from(this.initializedProviders)
137
+ .map(type => this.providers.get(type)?.provider)
138
+ .filter((p): p is Provider => p !== undefined);
139
+ }
140
+
141
+ /**
142
+ * Get provider by priority (1 = highest)
143
+ */
144
+ public getProviderByPriority(priority: number): Provider | undefined {
145
+ const registration = Array.from(this.providers.values())
146
+ .find(reg => reg.priority === priority && reg.enabled);
147
+
148
+ return registration?.provider;
149
+ }
150
+
151
+ /**
152
+ * Get highest priority provider
153
+ */
154
+ public getPrimaryProvider(): Provider | undefined {
155
+ const enabled = Array.from(this.providers.values())
156
+ .filter(reg => reg.enabled)
157
+ .sort((a, b) => a.priority - b.priority);
158
+
159
+ return enabled[0]?.provider;
160
+ }
161
+
162
+ /**
163
+ * Shutdown a provider
164
+ */
165
+ public async shutdownProvider(providerType: ProviderType): Promise<SuiteResult<void>> {
166
+ try {
167
+ const provider = this.providers.get(providerType)?.provider;
168
+ if (!provider) {
169
+ return {
170
+ success: false,
171
+ error: `Provider ${providerType} not found`
172
+ };
173
+ }
174
+
175
+ if (!this.initializedProviders.has(providerType)) {
176
+ return { success: true }; // Already shutdown
177
+ }
178
+
179
+ await provider.shutdown();
180
+ this.initializedProviders.delete(providerType);
181
+ return { success: true };
182
+ } catch (error) {
183
+ return {
184
+ success: false,
185
+ error: error instanceof Error ? error.message : 'Failed to shutdown provider'
186
+ };
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Shutdown all providers
192
+ */
193
+ public async shutdownAll(): Promise<void> {
194
+ const shutdownPromises = Array.from(this.initializedProviders).map(type =>
195
+ this.shutdownProvider(type)
196
+ );
197
+
198
+ await Promise.all(shutdownPromises);
199
+ }
200
+
201
+ /**
202
+ * Unregister a provider
203
+ */
204
+ public unregisterProvider(providerType: ProviderType): SuiteResult<void> {
205
+ if (this.initializedProviders.has(providerType)) {
206
+ throw new ChatSuiteError(
207
+ ChatSuiteErrorType.PROVIDER_ERROR,
208
+ `Cannot unregister initialized provider ${providerType}. Shutdown first.`
209
+ );
210
+ }
211
+
212
+ const deleted = this.providers.delete(providerType);
213
+ if (!deleted) {
214
+ return {
215
+ success: false,
216
+ error: `Provider ${providerType} not registered`
217
+ };
218
+ }
219
+
220
+ return { success: true };
221
+ }
222
+
223
+ /**
224
+ * Get provider count
225
+ */
226
+ public getProviderCount(): {
227
+ total: number;
228
+ enabled: number;
229
+ initialized: number;
230
+ } {
231
+ const enabled = Array.from(this.providers.values()).filter(r => r.enabled).length;
232
+
233
+ return {
234
+ total: this.providers.size,
235
+ enabled,
236
+ initialized: this.initializedProviders.size
237
+ };
238
+ }
239
+
240
+ /**
241
+ * Clear all providers
242
+ */
243
+ public clear(): void {
244
+ this.providers.clear();
245
+ this.initializedProviders.clear();
246
+ }
247
+ }
@@ -0,0 +1,176 @@
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 { ProviderType } from '../types';
10
+ import { UserMapper, UserIdentity, createUnifiedId } from '../utils/user-mapping';
11
+ import type { SuiteResult } from '../types';
12
+
13
+ /**
14
+ * User registration data
15
+ */
16
+ export interface UserRegistration {
17
+ provider: ProviderType;
18
+ platformUserId: string;
19
+ displayName?: string;
20
+ email?: string;
21
+ metadata?: Record<string, unknown>;
22
+ }
23
+
24
+ /**
25
+ * Manages users across all chat providers
26
+ */
27
+ export class UserManager {
28
+ private mapper: UserMapper;
29
+
30
+ constructor() {
31
+ this.mapper = new UserMapper();
32
+ }
33
+
34
+ /**
35
+ * Register a new user or update existing user
36
+ */
37
+ public registerUser(registration: UserRegistration): SuiteResult<UserIdentity> {
38
+ try {
39
+ // Check if user already exists
40
+ const existingId = this.mapper.getUnifiedId(
41
+ registration.provider,
42
+ registration.platformUserId
43
+ );
44
+
45
+ if (existingId) {
46
+ // Update existing user
47
+ const identity = this.mapper.getIdentity(existingId);
48
+ if (identity) {
49
+ if (registration.displayName) {
50
+ identity.displayName = registration.displayName;
51
+ }
52
+ if (registration.email) {
53
+ identity.email = registration.email;
54
+ }
55
+ if (registration.metadata) {
56
+ identity.metadata = { ...identity.metadata, ...registration.metadata };
57
+ }
58
+ this.mapper.mapUser(identity);
59
+ return { success: true, data: identity };
60
+ }
61
+ }
62
+
63
+ // Create new user
64
+ const platformIds = new Map<ProviderType, string>();
65
+ platformIds.set(registration.provider, registration.platformUserId);
66
+
67
+ const newIdentity: UserIdentity = {
68
+ unifiedId: createUnifiedId(),
69
+ platformIds,
70
+ displayName: registration.displayName,
71
+ email: registration.email,
72
+ metadata: registration.metadata
73
+ };
74
+
75
+ this.mapper.mapUser(newIdentity);
76
+ return { success: true, data: newIdentity };
77
+ } catch (error) {
78
+ return {
79
+ success: false,
80
+ error: error instanceof Error ? error.message : 'Failed to register user'
81
+ };
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Link a platform account to an existing user
87
+ */
88
+ public linkPlatform(
89
+ unifiedId: string,
90
+ provider: ProviderType,
91
+ platformUserId: string
92
+ ): SuiteResult<UserIdentity> {
93
+ try {
94
+ const success = this.mapper.addPlatformId(unifiedId, provider, platformUserId);
95
+ if (!success) {
96
+ return { success: false, error: 'User not found' };
97
+ }
98
+
99
+ const identity = this.mapper.getIdentity(unifiedId);
100
+ return { success: true, data: identity };
101
+ } catch (error) {
102
+ return {
103
+ success: false,
104
+ error: error instanceof Error ? error.message : 'Failed to link platform'
105
+ };
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Get unified user ID from platform-specific ID
111
+ */
112
+ public getUnifiedId(provider: ProviderType, platformUserId: string): string | undefined {
113
+ return this.mapper.getUnifiedId(provider, platformUserId);
114
+ }
115
+
116
+ /**
117
+ * Get platform-specific user ID
118
+ */
119
+ public getPlatformId(unifiedId: string, provider: ProviderType): string | undefined {
120
+ return this.mapper.getPlatformId(unifiedId, provider);
121
+ }
122
+
123
+ /**
124
+ * Get complete user identity
125
+ */
126
+ public getUser(unifiedId: string): SuiteResult<UserIdentity> {
127
+ const identity = this.mapper.getIdentity(unifiedId);
128
+ if (!identity) {
129
+ return { success: false, error: 'User not found' };
130
+ }
131
+ return { success: true, data: identity };
132
+ }
133
+
134
+ /**
135
+ * Get all users
136
+ */
137
+ public getAllUsers(): UserIdentity[] {
138
+ return this.mapper.getAllUsers();
139
+ }
140
+
141
+ /**
142
+ * Remove a user
143
+ */
144
+ public removeUser(unifiedId: string): SuiteResult<void> {
145
+ const success = this.mapper.removeUser(unifiedId);
146
+ if (!success) {
147
+ return { success: false, error: 'User not found' };
148
+ }
149
+ return { success: true };
150
+ }
151
+
152
+ /**
153
+ * Get user count
154
+ */
155
+ public getUserCount(): number {
156
+ return this.mapper.size();
157
+ }
158
+
159
+ /**
160
+ * Search users by display name or email
161
+ */
162
+ public searchUsers(query: string): UserIdentity[] {
163
+ const lowerQuery = query.toLowerCase();
164
+ return this.mapper.getAllUsers().filter(user =>
165
+ user.displayName?.toLowerCase().includes(lowerQuery) ||
166
+ user.email?.toLowerCase().includes(lowerQuery)
167
+ );
168
+ }
169
+
170
+ /**
171
+ * Clear all users
172
+ */
173
+ public clear(): void {
174
+ this.mapper.clear();
175
+ }
176
+ }
@@ -0,0 +1,13 @@
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 './ConfigManager';
10
+ export * from './UserManager';
11
+ export * from './HealthMonitor';
12
+ export * from './ProviderManager';
13
+ export * from './MessageOrchestrator';
@@ -0,0 +1,11 @@
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 './provider-types';
10
+ export * from './message-types';
11
+ export * from './suite-types';