@aarekaz/switchboard-core 0.3.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/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @aarekaz/switchboard-core
2
+
3
+ Core types, interfaces, and client API for Switchboard - the universal chat platform SDK.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @aarekaz/switchboard-core
9
+ # or
10
+ pnpm add @aarekaz/switchboard-core
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```typescript
16
+ import { createBot } from '@aarekaz/switchboard-core';
17
+ import '@aarekaz/switchboard-discord'; // Auto-registers Discord adapter
18
+
19
+ const bot = createBot({
20
+ platform: 'discord',
21
+ credentials: {
22
+ token: process.env.DISCORD_TOKEN,
23
+ },
24
+ });
25
+
26
+ bot.onMessage(async (message) => {
27
+ if (message.text.includes('ping')) {
28
+ await bot.reply(message, 'pong!');
29
+ }
30
+ });
31
+
32
+ await bot.start();
33
+ ```
34
+
35
+ ## Documentation
36
+
37
+ For full documentation, visit [github.com/Aarekaz/switchboard](https://github.com/Aarekaz/switchboard)
38
+
39
+ ## License
40
+
41
+ MIT
@@ -0,0 +1,705 @@
1
+ /**
2
+ * Result type for type-safe error handling
3
+ * Inspired by Rust's Result<T, E> type
4
+ */
5
+ type Result<T, E = Error> = {
6
+ ok: true;
7
+ value: T;
8
+ } | {
9
+ ok: false;
10
+ error: E;
11
+ };
12
+ /**
13
+ * Create a successful result
14
+ */
15
+ declare function ok<T>(value: T): Result<T, never>;
16
+ /**
17
+ * Create an error result
18
+ */
19
+ declare function err<E = Error>(error: E): Result<never, E>;
20
+ /**
21
+ * Wrap an async operation in a Result type
22
+ * Catches any errors and converts them to Result<T>
23
+ */
24
+ declare function wrapAsync<T>(fn: () => Promise<T>): Promise<Result<T, Error>>;
25
+ /**
26
+ * Check if a result is successful
27
+ */
28
+ declare function isOk<T, E>(result: Result<T, E>): result is {
29
+ ok: true;
30
+ value: T;
31
+ };
32
+ /**
33
+ * Check if a result is an error
34
+ */
35
+ declare function isErr<T, E>(result: Result<T, E>): result is {
36
+ ok: false;
37
+ error: E;
38
+ };
39
+
40
+ /**
41
+ * Supported chat platforms
42
+ * Extensible to allow custom platforms
43
+ */
44
+ type PlatformType = 'discord' | 'slack' | 'teams' | 'google-chat' | (string & {});
45
+
46
+ /**
47
+ * File attachment
48
+ */
49
+ interface Attachment {
50
+ /** Attachment ID */
51
+ id: string;
52
+ /** File name */
53
+ filename: string;
54
+ /** Download URL */
55
+ url: string;
56
+ /** MIME type (e.g., 'image/png') */
57
+ mimeType: string;
58
+ /** File size in bytes */
59
+ size: number;
60
+ }
61
+ /**
62
+ * Unified message representation across all platforms
63
+ * This is the core abstraction that normalizes messages from different platforms
64
+ */
65
+ interface UnifiedMessage {
66
+ /** Platform-specific message ID */
67
+ id: string;
68
+ /** Channel/room/conversation ID where the message was sent */
69
+ channelId: string;
70
+ /** User ID of the message sender */
71
+ userId: string;
72
+ /** Plain text content of the message */
73
+ text: string;
74
+ /** When the message was sent */
75
+ timestamp: Date;
76
+ /** Thread/reply chain ID (if this message is part of a thread) */
77
+ threadId?: string;
78
+ /** File attachments (images, files, etc.) */
79
+ attachments?: Attachment[];
80
+ /** Which platform this message is from */
81
+ platform: PlatformType;
82
+ /** Original platform message object (for accessing platform-specific fields) */
83
+ _raw: unknown;
84
+ }
85
+ /**
86
+ * Reference to a message - can be either a message ID string or a full UnifiedMessage object.
87
+ *
88
+ * **Why this exists:**
89
+ * Different platforms have different requirements for message operations:
90
+ * - Discord: Only needs message ID for edit/delete/react operations
91
+ * - Slack: Needs both channel ID and message timestamp (ts)
92
+ *
93
+ * **Usage recommendations:**
94
+ * - Passing UnifiedMessage works reliably on ALL platforms
95
+ * - Passing string ID works on Discord, works on Slack if message is in cache (95% of cases)
96
+ *
97
+ * **Platform notes:**
98
+ * - Discord: String IDs always work
99
+ * - Slack: String IDs work if the message was seen by the bot in the last hour.
100
+ * For guaranteed reliability, pass the full message object.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * // Recommended: Works everywhere, always
105
+ * bot.onMessage(async (message) => {
106
+ * await bot.editMessage(message, 'Updated text');
107
+ * await bot.addReaction(message, 'thumbsup');
108
+ * });
109
+ *
110
+ * // Works on Discord, works on Slack if cached
111
+ * await bot.editMessage(message.id, 'Updated text');
112
+ * ```
113
+ */
114
+ type MessageRef = string | UnifiedMessage;
115
+ /**
116
+ * Options for sending messages
117
+ */
118
+ interface SendMessageOptions {
119
+ /** Send message in a specific thread */
120
+ threadId?: string;
121
+ /** Discord-specific options */
122
+ discord?: {
123
+ embeds?: unknown[];
124
+ components?: unknown[];
125
+ };
126
+ /** Slack-specific options */
127
+ slack?: {
128
+ blocks?: unknown[];
129
+ unfurl_links?: boolean;
130
+ unfurl_media?: boolean;
131
+ };
132
+ /** Microsoft Teams-specific options */
133
+ teams?: {
134
+ attachments?: unknown[];
135
+ };
136
+ /** Google Chat-specific options */
137
+ googleChat?: {
138
+ cards?: unknown[];
139
+ };
140
+ }
141
+ /**
142
+ * Options for uploading files
143
+ */
144
+ interface UploadOptions {
145
+ /** File name (if not provided in the File object) */
146
+ filename?: string;
147
+ /** Optional comment/message to send with the file */
148
+ comment?: string;
149
+ /** Send file in a specific thread */
150
+ threadId?: string;
151
+ }
152
+
153
+ /**
154
+ * Event fired when a message is received
155
+ */
156
+ interface MessageEvent {
157
+ type: 'message';
158
+ message: UnifiedMessage;
159
+ }
160
+ /**
161
+ * Event fired when a reaction is added or removed from a message
162
+ */
163
+ interface ReactionEvent {
164
+ type: 'reaction';
165
+ /** Message ID that was reacted to */
166
+ messageId: string;
167
+ /** User ID who added/removed the reaction */
168
+ userId: string;
169
+ /** Emoji string (e.g., '👍', 'tada') */
170
+ emoji: string;
171
+ /** Whether the reaction was added or removed */
172
+ action: 'added' | 'removed';
173
+ }
174
+ /**
175
+ * Event fired when a user joins a channel
176
+ */
177
+ interface UserJoinedEvent {
178
+ type: 'user_joined';
179
+ /** Channel ID the user joined */
180
+ channelId: string;
181
+ /** User ID who joined */
182
+ userId: string;
183
+ }
184
+ /**
185
+ * Event fired when a user leaves a channel
186
+ */
187
+ interface UserLeftEvent {
188
+ type: 'user_left';
189
+ /** Channel ID the user left */
190
+ channelId: string;
191
+ /** User ID who left */
192
+ userId: string;
193
+ }
194
+ /**
195
+ * Event fired when a channel is created
196
+ */
197
+ interface ChannelCreatedEvent {
198
+ type: 'channel_created';
199
+ /** ID of the created channel */
200
+ channelId: string;
201
+ /** Name of the created channel */
202
+ channelName: string;
203
+ }
204
+ /**
205
+ * Event fired when a channel is deleted
206
+ */
207
+ interface ChannelDeletedEvent {
208
+ type: 'channel_deleted';
209
+ /** ID of the deleted channel */
210
+ channelId: string;
211
+ }
212
+ /**
213
+ * Union of all possible events
214
+ */
215
+ type UnifiedEvent = MessageEvent | ReactionEvent | UserJoinedEvent | UserLeftEvent | ChannelCreatedEvent | ChannelDeletedEvent;
216
+
217
+ /**
218
+ * Channel/room type
219
+ */
220
+ type ChannelType = 'text' | 'voice' | 'dm' | 'group_dm' | 'category' | 'unknown';
221
+ /**
222
+ * Unified channel representation
223
+ */
224
+ interface Channel {
225
+ /** Channel ID */
226
+ id: string;
227
+ /** Channel name */
228
+ name: string;
229
+ /** Channel type */
230
+ type: ChannelType;
231
+ /** Whether the channel is private/restricted */
232
+ isPrivate: boolean;
233
+ /** Channel topic/description (if applicable) */
234
+ topic?: string;
235
+ }
236
+
237
+ /**
238
+ * Unified user representation
239
+ */
240
+ interface User {
241
+ /** User ID */
242
+ id: string;
243
+ /** Username */
244
+ username: string;
245
+ /** Display name (may be different from username on some platforms) */
246
+ displayName?: string;
247
+ /** Whether the user is a bot */
248
+ isBot: boolean;
249
+ /** Avatar URL (if available) */
250
+ avatarUrl?: string;
251
+ }
252
+
253
+ /**
254
+ * Platform adapter interface
255
+ * All platform adapters must implement this interface
256
+ */
257
+ interface PlatformAdapter {
258
+ /** Adapter name (e.g., 'discord-adapter') */
259
+ readonly name: string;
260
+ /** Platform type (e.g., 'discord') */
261
+ readonly platform: PlatformType;
262
+ /** Connect to the platform with credentials */
263
+ connect(credentials: unknown): Promise<void>;
264
+ /** Disconnect from the platform */
265
+ disconnect(): Promise<void>;
266
+ /** Check if currently connected */
267
+ isConnected(): boolean;
268
+ /** Send a message to a channel */
269
+ sendMessage(channelId: string, text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
270
+ /** Edit an existing message */
271
+ editMessage(messageRef: MessageRef, newText: string): Promise<Result<UnifiedMessage>>;
272
+ /** Delete a message */
273
+ deleteMessage(messageRef: MessageRef): Promise<Result<void>>;
274
+ /** Add a reaction emoji to a message */
275
+ addReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>>;
276
+ /** Remove a reaction emoji from a message */
277
+ removeReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>>;
278
+ /** Create a thread/reply to a message */
279
+ createThread(messageRef: MessageRef, text: string): Promise<Result<UnifiedMessage>>;
280
+ /** Upload a file to a channel */
281
+ uploadFile(channelId: string, file: unknown, options?: UploadOptions): Promise<Result<UnifiedMessage>>;
282
+ /** Subscribe to platform events */
283
+ onEvent(handler: (event: UnifiedEvent) => void | Promise<void>): void;
284
+ /** Get list of channels */
285
+ getChannels(): Promise<Result<Channel[]>>;
286
+ /** Get list of users (optionally in a specific channel) */
287
+ getUsers(channelId?: string): Promise<Result<User[]>>;
288
+ /** Normalize platform-specific message to UnifiedMessage */
289
+ normalizeMessage(platformMessage: unknown): UnifiedMessage;
290
+ /** Normalize platform-specific event to UnifiedEvent */
291
+ normalizeEvent(platformEvent: unknown): UnifiedEvent | null;
292
+ }
293
+
294
+ /**
295
+ * Message Context
296
+ *
297
+ * A context object passed to message handlers that provides convenient access
298
+ * to message data and bot operations without repetitive parameter passing.
299
+ *
300
+ * Inspired by Hono's context pattern and Telegraf's bot framework.
301
+ */
302
+
303
+ /**
304
+ * Context object provided to message handlers
305
+ *
306
+ * @example
307
+ * ```typescript
308
+ * bot.onMessage(async (ctx) => {
309
+ * // Access message data
310
+ * console.log(ctx.message.text);
311
+ * console.log(ctx.userId);
312
+ * console.log(ctx.channelId);
313
+ *
314
+ * // Use helper methods
315
+ * await ctx.reply('Hello!');
316
+ * await ctx.react('👍');
317
+ * await ctx.edit('Updated!');
318
+ * });
319
+ * ```
320
+ */
321
+ interface MessageContext {
322
+ /**
323
+ * The received message
324
+ */
325
+ readonly message: UnifiedMessage;
326
+ /**
327
+ * Platform this message is from
328
+ */
329
+ readonly platform: PlatformType;
330
+ /**
331
+ * User ID who sent the message (convenience accessor)
332
+ */
333
+ readonly userId: string;
334
+ /**
335
+ * Channel ID where message was sent (convenience accessor)
336
+ */
337
+ readonly channelId: string;
338
+ /**
339
+ * Message text content (convenience accessor)
340
+ */
341
+ readonly text: string;
342
+ /**
343
+ * Thread ID if message is in a thread (convenience accessor)
344
+ */
345
+ readonly threadId: string | undefined;
346
+ /**
347
+ * Reply to the message
348
+ *
349
+ * @param text - Text to reply with
350
+ * @param options - Optional message options
351
+ * @returns Result containing the sent message
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * await ctx.reply('Got your message!');
356
+ *
357
+ * // With options
358
+ * await ctx.reply('Thread reply', { threadId: ctx.threadId });
359
+ * ```
360
+ */
361
+ reply(text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
362
+ /**
363
+ * Add a reaction to the message
364
+ *
365
+ * @param emoji - Emoji to add (Unicode or platform name)
366
+ * @returns Result indicating success or failure
367
+ *
368
+ * @example
369
+ * ```typescript
370
+ * await ctx.react('👍');
371
+ * await ctx.react('thumbsup');
372
+ * ```
373
+ */
374
+ react(emoji: string): Promise<Result<void>>;
375
+ /**
376
+ * Remove a reaction from the message
377
+ *
378
+ * @param emoji - Emoji to remove
379
+ * @returns Result indicating success or failure
380
+ */
381
+ unreact(emoji: string): Promise<Result<void>>;
382
+ /**
383
+ * Edit the message (only works for bot's own messages)
384
+ *
385
+ * @param newText - New message text
386
+ * @returns Result containing the edited message
387
+ *
388
+ * @example
389
+ * ```typescript
390
+ * const sent = await ctx.reply('Processing...');
391
+ * if (sent.ok) {
392
+ * // Create new context for the sent message
393
+ * await bot.editMessage(sent.value, 'Done!');
394
+ * }
395
+ * ```
396
+ */
397
+ edit(newText: string): Promise<Result<UnifiedMessage>>;
398
+ /**
399
+ * Delete the message
400
+ *
401
+ * @returns Result indicating success or failure
402
+ */
403
+ delete(): Promise<Result<void>>;
404
+ /**
405
+ * Create a thread on the message
406
+ *
407
+ * @param text - First message in the thread
408
+ * @returns Result containing the thread message
409
+ *
410
+ * @example
411
+ * ```typescript
412
+ * await ctx.createThread('Let\'s discuss this!');
413
+ * ```
414
+ */
415
+ createThread(text: string): Promise<Result<UnifiedMessage>>;
416
+ /**
417
+ * Send a new message to the same channel
418
+ *
419
+ * @param text - Message text
420
+ * @param options - Optional message options
421
+ * @returns Result containing the sent message
422
+ *
423
+ * @example
424
+ * ```typescript
425
+ * await ctx.send('This is a new message');
426
+ * ```
427
+ */
428
+ send(text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
429
+ }
430
+ /**
431
+ * Function type for message handlers that receive context
432
+ */
433
+ type MessageHandler = (ctx: MessageContext) => void | Promise<void>;
434
+
435
+ /**
436
+ * Bot client - the main interface for interacting with chat platforms
437
+ */
438
+ declare class Bot {
439
+ private readonly adapter;
440
+ private readonly _platform;
441
+ private readonly credentials;
442
+ private eventHandlers;
443
+ constructor(adapter: PlatformAdapter, _platform: PlatformType, credentials: unknown);
444
+ /**
445
+ * Get the platform type
446
+ */
447
+ get platform(): PlatformType;
448
+ /**
449
+ * Start the bot (connect to the platform)
450
+ */
451
+ start(): Promise<void>;
452
+ /**
453
+ * Stop the bot (disconnect from the platform)
454
+ */
455
+ stop(): Promise<void>;
456
+ /**
457
+ * Check if the bot is connected
458
+ */
459
+ isConnected(): boolean;
460
+ /**
461
+ * Send a message to a channel
462
+ */
463
+ sendMessage(channelId: string, text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
464
+ /**
465
+ * Reply to a message
466
+ */
467
+ reply(message: UnifiedMessage, text: string, options?: SendMessageOptions): Promise<Result<UnifiedMessage>>;
468
+ /**
469
+ * Edit an existing message
470
+ */
471
+ editMessage(messageRef: MessageRef, newText: string): Promise<Result<UnifiedMessage>>;
472
+ /**
473
+ * Delete a message
474
+ */
475
+ deleteMessage(messageRef: MessageRef): Promise<Result<void>>;
476
+ /**
477
+ * Add a reaction to a message
478
+ */
479
+ addReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>>;
480
+ /**
481
+ * Remove a reaction from a message
482
+ */
483
+ removeReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>>;
484
+ /**
485
+ * Create a thread (or reply in a thread)
486
+ */
487
+ createThread(messageRef: MessageRef, text: string): Promise<Result<UnifiedMessage>>;
488
+ /**
489
+ * Upload a file to a channel
490
+ */
491
+ uploadFile(channelId: string, file: unknown, options?: UploadOptions): Promise<Result<UnifiedMessage>>;
492
+ /**
493
+ * Get list of channels
494
+ */
495
+ getChannels(): Promise<Result<Channel[]>>;
496
+ /**
497
+ * Get list of users (optionally in a specific channel)
498
+ */
499
+ getUsers(channelId?: string): Promise<Result<User[]>>;
500
+ /**
501
+ * Register a handler for message events
502
+ *
503
+ * Supports both context API (recommended) and legacy message API
504
+ *
505
+ * @example
506
+ * ```typescript
507
+ * // Context API (recommended)
508
+ * bot.onMessage(async (ctx) => {
509
+ * await ctx.reply('Hello!');
510
+ * await ctx.react('thumbsup');
511
+ * });
512
+ *
513
+ * // Legacy API (still supported)
514
+ * bot.onMessage(async (message) => {
515
+ * await bot.reply(message, 'Hello!');
516
+ * });
517
+ * ```
518
+ */
519
+ onMessage(handler: MessageHandler | ((message: UnifiedMessage) => void | Promise<void>)): void;
520
+ /**
521
+ * Create a message context object
522
+ */
523
+ private createContext;
524
+ /**
525
+ * Register a handler for reaction events
526
+ */
527
+ onReaction(handler: (event: ReactionEvent) => void | Promise<void>): void;
528
+ /**
529
+ * Register a handler for any event
530
+ */
531
+ onEvent(handler: (event: UnifiedEvent) => void | Promise<void>): void;
532
+ /**
533
+ * Get the underlying adapter (for advanced use cases)
534
+ */
535
+ getAdapter(): PlatformAdapter;
536
+ /**
537
+ * Internal: Register an event handler
538
+ */
539
+ private on;
540
+ /**
541
+ * Internal: Handle an event from the adapter
542
+ */
543
+ private handleEvent;
544
+ }
545
+
546
+ /**
547
+ * Configuration for creating a bot
548
+ */
549
+ interface BotConfig {
550
+ /**
551
+ * Platform to connect to (e.g., 'discord', 'slack', 'teams', 'google-chat')
552
+ */
553
+ platform: PlatformType;
554
+ /**
555
+ * Platform credentials (structure depends on the platform)
556
+ */
557
+ credentials?: {
558
+ /** Bot token (Discord, Slack) */
559
+ token?: string;
560
+ /** App ID (Teams) */
561
+ appId?: string;
562
+ /** App password (Teams) */
563
+ appPassword?: string;
564
+ /** Custom credentials object */
565
+ [key: string]: unknown;
566
+ };
567
+ /**
568
+ * Custom adapter (for advanced users or custom platforms)
569
+ * If provided, this will be used instead of the registered adapter
570
+ */
571
+ adapter?: PlatformAdapter;
572
+ /**
573
+ * Platform-specific configuration (opt-in)
574
+ */
575
+ platformConfig?: {
576
+ discord?: unknown;
577
+ slack?: unknown;
578
+ teams?: unknown;
579
+ googleChat?: unknown;
580
+ [key: string]: unknown;
581
+ };
582
+ }
583
+ /**
584
+ * Create a bot instance
585
+ *
586
+ * @example
587
+ * ```typescript
588
+ * import { createBot } from '@aarekaz/switchboard-core';
589
+ * import '@aarekaz/switchboard-discord';
590
+ *
591
+ * const bot = createBot({
592
+ * platform: 'discord',
593
+ * credentials: {
594
+ * token: process.env.DISCORD_TOKEN,
595
+ * },
596
+ * });
597
+ *
598
+ * bot.onMessage(async (message) => {
599
+ * if (message.text.includes('ping')) {
600
+ * await bot.reply(message, 'pong!');
601
+ * }
602
+ * });
603
+ *
604
+ * await bot.start();
605
+ * ```
606
+ */
607
+ declare function createBot(config: BotConfig): Bot;
608
+
609
+ /**
610
+ * Global registry for platform adapters
611
+ * Adapters register themselves when their package is imported
612
+ */
613
+ declare class AdapterRegistry {
614
+ private adapters;
615
+ /**
616
+ * Register a platform adapter
617
+ * Called by adapter packages when they're imported
618
+ */
619
+ register(platform: PlatformType, adapter: PlatformAdapter): void;
620
+ /**
621
+ * Get an adapter for a platform
622
+ */
623
+ get(platform: PlatformType): PlatformAdapter | undefined;
624
+ /**
625
+ * Check if an adapter is registered for a platform
626
+ */
627
+ has(platform: PlatformType): boolean;
628
+ /**
629
+ * Get all registered platforms
630
+ */
631
+ getRegisteredPlatforms(): PlatformType[];
632
+ /**
633
+ * Clear all registered adapters (mainly for testing)
634
+ */
635
+ clear(): void;
636
+ }
637
+ /**
638
+ * Global singleton registry instance
639
+ */
640
+ declare const registry: AdapterRegistry;
641
+
642
+ /**
643
+ * Context passed to middleware
644
+ */
645
+ interface MiddlewareContext {
646
+ /** The bot instance */
647
+ bot: Bot;
648
+ /** The event being processed */
649
+ event: UnifiedEvent;
650
+ /** The platform this event is from */
651
+ platform: PlatformType;
652
+ /** Timestamp when the event was received */
653
+ timestamp: Date;
654
+ }
655
+ /**
656
+ * Middleware function type
657
+ */
658
+ type Middleware = (context: MiddlewareContext, next: () => Promise<void>) => Promise<void>;
659
+
660
+ /**
661
+ * Base error class for all Switchboard errors
662
+ */
663
+ declare class SwitchboardError extends Error {
664
+ readonly code: string;
665
+ readonly platform?: PlatformType | undefined;
666
+ constructor(message: string, code: string, platform?: PlatformType | undefined);
667
+ }
668
+ /**
669
+ * Error thrown when no adapter is found for a platform
670
+ */
671
+ declare class AdapterNotFoundError extends SwitchboardError {
672
+ constructor(platform: PlatformType);
673
+ }
674
+ /**
675
+ * Error thrown when connection to a platform fails
676
+ */
677
+ declare class ConnectionError extends SwitchboardError {
678
+ constructor(platform: PlatformType, cause: unknown);
679
+ }
680
+ /**
681
+ * Error thrown when sending a message fails
682
+ */
683
+ declare class MessageSendError extends SwitchboardError {
684
+ constructor(platform: PlatformType, channelId: string, cause: unknown);
685
+ }
686
+ /**
687
+ * Error thrown when editing a message fails
688
+ */
689
+ declare class MessageEditError extends SwitchboardError {
690
+ constructor(platform: PlatformType, messageId: string, cause: unknown);
691
+ }
692
+ /**
693
+ * Error thrown when deleting a message fails
694
+ */
695
+ declare class MessageDeleteError extends SwitchboardError {
696
+ constructor(platform: PlatformType, messageId: string, cause: unknown);
697
+ }
698
+ /**
699
+ * Error thrown when adding/removing a reaction fails
700
+ */
701
+ declare class ReactionError extends SwitchboardError {
702
+ constructor(platform: PlatformType, messageId: string, emoji: string, cause: unknown);
703
+ }
704
+
705
+ export { AdapterNotFoundError, type Attachment, Bot, type BotConfig, type Channel, type ChannelCreatedEvent, type ChannelDeletedEvent, type ChannelType, ConnectionError, MessageDeleteError, MessageEditError, type MessageEvent, type MessageRef, MessageSendError, type Middleware, type MiddlewareContext, type PlatformAdapter, type PlatformType, ReactionError, type ReactionEvent, type Result, type SendMessageOptions, SwitchboardError, type UnifiedEvent, type UnifiedMessage, type UploadOptions, type User, type UserJoinedEvent, type UserLeftEvent, createBot, err, isErr, isOk, ok, registry, wrapAsync };
package/dist/index.js ADDED
@@ -0,0 +1,385 @@
1
+ // src/client/bot.ts
2
+ var Bot = class {
3
+ constructor(adapter, _platform, credentials) {
4
+ this.adapter = adapter;
5
+ this._platform = _platform;
6
+ this.credentials = credentials;
7
+ this.adapter.onEvent(async (event) => {
8
+ await this.handleEvent(event);
9
+ });
10
+ }
11
+ eventHandlers = /* @__PURE__ */ new Map();
12
+ /**
13
+ * Get the platform type
14
+ */
15
+ get platform() {
16
+ return this._platform;
17
+ }
18
+ /**
19
+ * Start the bot (connect to the platform)
20
+ */
21
+ async start() {
22
+ if (!this.adapter.isConnected()) {
23
+ await this.adapter.connect(this.credentials);
24
+ }
25
+ }
26
+ /**
27
+ * Stop the bot (disconnect from the platform)
28
+ */
29
+ async stop() {
30
+ await this.adapter.disconnect();
31
+ }
32
+ /**
33
+ * Check if the bot is connected
34
+ */
35
+ isConnected() {
36
+ return this.adapter.isConnected();
37
+ }
38
+ /**
39
+ * Send a message to a channel
40
+ */
41
+ async sendMessage(channelId, text, options) {
42
+ return this.adapter.sendMessage(channelId, text, options);
43
+ }
44
+ /**
45
+ * Reply to a message
46
+ */
47
+ async reply(message, text, options) {
48
+ const threadId = message.threadId || message.id;
49
+ return this.adapter.sendMessage(message.channelId, text, {
50
+ ...options,
51
+ threadId
52
+ });
53
+ }
54
+ /**
55
+ * Edit an existing message
56
+ */
57
+ async editMessage(messageRef, newText) {
58
+ return this.adapter.editMessage(messageRef, newText);
59
+ }
60
+ /**
61
+ * Delete a message
62
+ */
63
+ async deleteMessage(messageRef) {
64
+ return this.adapter.deleteMessage(messageRef);
65
+ }
66
+ /**
67
+ * Add a reaction to a message
68
+ */
69
+ async addReaction(messageRef, emoji) {
70
+ return this.adapter.addReaction(messageRef, emoji);
71
+ }
72
+ /**
73
+ * Remove a reaction from a message
74
+ */
75
+ async removeReaction(messageRef, emoji) {
76
+ return this.adapter.removeReaction(messageRef, emoji);
77
+ }
78
+ /**
79
+ * Create a thread (or reply in a thread)
80
+ */
81
+ async createThread(messageRef, text) {
82
+ return this.adapter.createThread(messageRef, text);
83
+ }
84
+ /**
85
+ * Upload a file to a channel
86
+ */
87
+ async uploadFile(channelId, file, options) {
88
+ return this.adapter.uploadFile(channelId, file, options);
89
+ }
90
+ /**
91
+ * Get list of channels
92
+ */
93
+ async getChannels() {
94
+ return this.adapter.getChannels();
95
+ }
96
+ /**
97
+ * Get list of users (optionally in a specific channel)
98
+ */
99
+ async getUsers(channelId) {
100
+ return this.adapter.getUsers(channelId);
101
+ }
102
+ /**
103
+ * Register a handler for message events
104
+ *
105
+ * Supports both context API (recommended) and legacy message API
106
+ *
107
+ * @example
108
+ * ```typescript
109
+ * // Context API (recommended)
110
+ * bot.onMessage(async (ctx) => {
111
+ * await ctx.reply('Hello!');
112
+ * await ctx.react('thumbsup');
113
+ * });
114
+ *
115
+ * // Legacy API (still supported)
116
+ * bot.onMessage(async (message) => {
117
+ * await bot.reply(message, 'Hello!');
118
+ * });
119
+ * ```
120
+ */
121
+ onMessage(handler) {
122
+ this.on("message", async (event) => {
123
+ if (event.type === "message") {
124
+ const message = event.message;
125
+ const ctx = this.createContext(message);
126
+ await handler(ctx);
127
+ }
128
+ });
129
+ }
130
+ /**
131
+ * Create a message context object
132
+ */
133
+ createContext(message) {
134
+ return {
135
+ message,
136
+ platform: this._platform,
137
+ userId: message.userId,
138
+ channelId: message.channelId,
139
+ text: message.text,
140
+ threadId: message.threadId,
141
+ // Helper methods
142
+ reply: (text, options) => {
143
+ return this.reply(message, text, options);
144
+ },
145
+ react: (emoji) => {
146
+ return this.addReaction(message, emoji);
147
+ },
148
+ unreact: (emoji) => {
149
+ return this.removeReaction(message, emoji);
150
+ },
151
+ edit: (newText) => {
152
+ return this.editMessage(message, newText);
153
+ },
154
+ delete: () => {
155
+ return this.deleteMessage(message);
156
+ },
157
+ createThread: (text) => {
158
+ return this.createThread(message, text);
159
+ },
160
+ send: (text, options) => {
161
+ return this.sendMessage(message.channelId, text, options);
162
+ }
163
+ };
164
+ }
165
+ /**
166
+ * Register a handler for reaction events
167
+ */
168
+ onReaction(handler) {
169
+ this.on("reaction", async (event) => {
170
+ if (event.type === "reaction") {
171
+ await handler(event);
172
+ }
173
+ });
174
+ }
175
+ /**
176
+ * Register a handler for any event
177
+ */
178
+ onEvent(handler) {
179
+ this.on("*", handler);
180
+ }
181
+ /**
182
+ * Get the underlying adapter (for advanced use cases)
183
+ */
184
+ getAdapter() {
185
+ return this.adapter;
186
+ }
187
+ /**
188
+ * Internal: Register an event handler
189
+ */
190
+ on(eventType, handler) {
191
+ if (!this.eventHandlers.has(eventType)) {
192
+ this.eventHandlers.set(eventType, /* @__PURE__ */ new Set());
193
+ }
194
+ this.eventHandlers.get(eventType).add(handler);
195
+ }
196
+ /**
197
+ * Internal: Handle an event from the adapter
198
+ */
199
+ async handleEvent(event) {
200
+ const typeHandlers = this.eventHandlers.get(event.type);
201
+ if (typeHandlers) {
202
+ for (const handler of typeHandlers) {
203
+ try {
204
+ await handler(event);
205
+ } catch (error) {
206
+ console.error(
207
+ `[Switchboard] Error in ${event.type} handler:`,
208
+ error
209
+ );
210
+ }
211
+ }
212
+ }
213
+ const wildcardHandlers = this.eventHandlers.get("*");
214
+ if (wildcardHandlers) {
215
+ for (const handler of wildcardHandlers) {
216
+ try {
217
+ await handler(event);
218
+ } catch (error) {
219
+ console.error(`[Switchboard] Error in wildcard handler:`, error);
220
+ }
221
+ }
222
+ }
223
+ }
224
+ };
225
+
226
+ // src/adapter/registry.ts
227
+ var AdapterRegistry = class {
228
+ adapters = /* @__PURE__ */ new Map();
229
+ /**
230
+ * Register a platform adapter
231
+ * Called by adapter packages when they're imported
232
+ */
233
+ register(platform, adapter) {
234
+ if (this.adapters.has(platform)) {
235
+ console.warn(
236
+ `[Switchboard] Adapter for platform "${platform}" is already registered. Overwriting.`
237
+ );
238
+ }
239
+ this.adapters.set(platform, adapter);
240
+ }
241
+ /**
242
+ * Get an adapter for a platform
243
+ */
244
+ get(platform) {
245
+ return this.adapters.get(platform);
246
+ }
247
+ /**
248
+ * Check if an adapter is registered for a platform
249
+ */
250
+ has(platform) {
251
+ return this.adapters.has(platform);
252
+ }
253
+ /**
254
+ * Get all registered platforms
255
+ */
256
+ getRegisteredPlatforms() {
257
+ return Array.from(this.adapters.keys());
258
+ }
259
+ /**
260
+ * Clear all registered adapters (mainly for testing)
261
+ */
262
+ clear() {
263
+ this.adapters.clear();
264
+ }
265
+ };
266
+ var registry = new AdapterRegistry();
267
+
268
+ // src/utils/errors.ts
269
+ var SwitchboardError = class extends Error {
270
+ constructor(message, code, platform) {
271
+ super(message);
272
+ this.code = code;
273
+ this.platform = platform;
274
+ this.name = "SwitchboardError";
275
+ if (Error.captureStackTrace) {
276
+ Error.captureStackTrace(this, this.constructor);
277
+ }
278
+ }
279
+ };
280
+ var AdapterNotFoundError = class extends SwitchboardError {
281
+ constructor(platform) {
282
+ super(
283
+ `No adapter found for platform: ${platform}. Did you import @aarekaz/switchboard-${platform}?`,
284
+ "ADAPTER_NOT_FOUND",
285
+ platform
286
+ );
287
+ this.name = "AdapterNotFoundError";
288
+ }
289
+ };
290
+ var ConnectionError = class extends SwitchboardError {
291
+ constructor(platform, cause) {
292
+ super(
293
+ `Failed to connect to ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,
294
+ "CONNECTION_ERROR",
295
+ platform
296
+ );
297
+ this.name = "ConnectionError";
298
+ this.cause = cause;
299
+ }
300
+ };
301
+ var MessageSendError = class extends SwitchboardError {
302
+ constructor(platform, channelId, cause) {
303
+ super(
304
+ `Failed to send message to channel ${channelId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,
305
+ "MESSAGE_SEND_ERROR",
306
+ platform
307
+ );
308
+ this.name = "MessageSendError";
309
+ this.cause = cause;
310
+ }
311
+ };
312
+ var MessageEditError = class extends SwitchboardError {
313
+ constructor(platform, messageId, cause) {
314
+ super(
315
+ `Failed to edit message ${messageId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,
316
+ "MESSAGE_EDIT_ERROR",
317
+ platform
318
+ );
319
+ this.name = "MessageEditError";
320
+ this.cause = cause;
321
+ }
322
+ };
323
+ var MessageDeleteError = class extends SwitchboardError {
324
+ constructor(platform, messageId, cause) {
325
+ super(
326
+ `Failed to delete message ${messageId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,
327
+ "MESSAGE_DELETE_ERROR",
328
+ platform
329
+ );
330
+ this.name = "MessageDeleteError";
331
+ this.cause = cause;
332
+ }
333
+ };
334
+ var ReactionError = class extends SwitchboardError {
335
+ constructor(platform, messageId, emoji, cause) {
336
+ super(
337
+ `Failed to add/remove reaction ${emoji} on message ${messageId} (${platform}): ${cause instanceof Error ? cause.message : String(cause)}`,
338
+ "REACTION_ERROR",
339
+ platform
340
+ );
341
+ this.name = "ReactionError";
342
+ this.cause = cause;
343
+ }
344
+ };
345
+
346
+ // src/client/create-bot.ts
347
+ function createBot(config) {
348
+ let adapter;
349
+ if (config.adapter) {
350
+ adapter = config.adapter;
351
+ } else {
352
+ const registeredAdapter = registry.get(config.platform);
353
+ if (!registeredAdapter) {
354
+ throw new AdapterNotFoundError(config.platform);
355
+ }
356
+ adapter = registeredAdapter;
357
+ }
358
+ return new Bot(adapter, config.platform, config.credentials || {});
359
+ }
360
+
361
+ // src/types/result.ts
362
+ function ok(value) {
363
+ return { ok: true, value };
364
+ }
365
+ function err(error) {
366
+ return { ok: false, error };
367
+ }
368
+ async function wrapAsync(fn) {
369
+ try {
370
+ const value = await fn();
371
+ return ok(value);
372
+ } catch (error) {
373
+ return err(error instanceof Error ? error : new Error(String(error)));
374
+ }
375
+ }
376
+ function isOk(result) {
377
+ return result.ok === true;
378
+ }
379
+ function isErr(result) {
380
+ return result.ok === false;
381
+ }
382
+
383
+ export { AdapterNotFoundError, Bot, ConnectionError, MessageDeleteError, MessageEditError, MessageSendError, ReactionError, SwitchboardError, createBot, err, isErr, isOk, ok, registry, wrapAsync };
384
+ //# sourceMappingURL=index.js.map
385
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client/bot.ts","../src/adapter/registry.ts","../src/utils/errors.ts","../src/client/create-bot.ts","../src/types/result.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { PlatformAdapter } from '../adapter/interface.js';\nimport type { Result } from '../types/result.js';\nimport type { PlatformType } from '../types/platform.js';\nimport type {\n UnifiedMessage,\n MessageRef,\n SendMessageOptions,\n UploadOptions,\n} from '../types/message.js';\nimport type { UnifiedEvent, MessageEvent, ReactionEvent } from '../types/event.js';\nimport type { Channel } from '../types/channel.js';\nimport type { User } from '../types/user.js';\nimport type { MessageContext, MessageHandler } from '../types/context.js';\n\n/**\n * Bot client - the main interface for interacting with chat platforms\n */\nexport class Bot {\n private eventHandlers: Map<string, Set<(event: UnifiedEvent) => void | Promise<void>>> = new Map();\n\n constructor(\n private readonly adapter: PlatformAdapter,\n private readonly _platform: PlatformType,\n private readonly credentials: unknown\n ) {\n // Subscribe to all events from the adapter\n this.adapter.onEvent(async (event) => {\n await this.handleEvent(event);\n });\n }\n\n /**\n * Get the platform type\n */\n get platform(): PlatformType {\n return this._platform;\n }\n\n /**\n * Start the bot (connect to the platform)\n */\n async start(): Promise<void> {\n // Connect to the platform if not already connected\n if (!this.adapter.isConnected()) {\n await this.adapter.connect(this.credentials);\n }\n }\n\n /**\n * Stop the bot (disconnect from the platform)\n */\n async stop(): Promise<void> {\n await this.adapter.disconnect();\n }\n\n /**\n * Check if the bot is connected\n */\n isConnected(): boolean {\n return this.adapter.isConnected();\n }\n\n /**\n * Send a message to a channel\n */\n async sendMessage(\n channelId: string,\n text: string,\n options?: SendMessageOptions\n ): Promise<Result<UnifiedMessage>> {\n return this.adapter.sendMessage(channelId, text, options);\n }\n\n /**\n * Reply to a message\n */\n async reply(\n message: UnifiedMessage,\n text: string,\n options?: SendMessageOptions\n ): Promise<Result<UnifiedMessage>> {\n // If the message is in a thread, reply in the same thread\n const threadId = message.threadId || message.id;\n return this.adapter.sendMessage(message.channelId, text, {\n ...options,\n threadId,\n });\n }\n\n /**\n * Edit an existing message\n */\n async editMessage(\n messageRef: MessageRef,\n newText: string\n ): Promise<Result<UnifiedMessage>> {\n return this.adapter.editMessage(messageRef, newText);\n }\n\n /**\n * Delete a message\n */\n async deleteMessage(messageRef: MessageRef): Promise<Result<void>> {\n return this.adapter.deleteMessage(messageRef);\n }\n\n /**\n * Add a reaction to a message\n */\n async addReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>> {\n return this.adapter.addReaction(messageRef, emoji);\n }\n\n /**\n * Remove a reaction from a message\n */\n async removeReaction(messageRef: MessageRef, emoji: string): Promise<Result<void>> {\n return this.adapter.removeReaction(messageRef, emoji);\n }\n\n /**\n * Create a thread (or reply in a thread)\n */\n async createThread(\n messageRef: MessageRef,\n text: string\n ): Promise<Result<UnifiedMessage>> {\n return this.adapter.createThread(messageRef, text);\n }\n\n /**\n * Upload a file to a channel\n */\n async uploadFile(\n channelId: string,\n file: unknown,\n options?: UploadOptions\n ): Promise<Result<UnifiedMessage>> {\n return this.adapter.uploadFile(channelId, file, options);\n }\n\n /**\n * Get list of channels\n */\n async getChannels(): Promise<Result<Channel[]>> {\n return this.adapter.getChannels();\n }\n\n /**\n * Get list of users (optionally in a specific channel)\n */\n async getUsers(channelId?: string): Promise<Result<User[]>> {\n return this.adapter.getUsers(channelId);\n }\n\n /**\n * Register a handler for message events\n *\n * Supports both context API (recommended) and legacy message API\n *\n * @example\n * ```typescript\n * // Context API (recommended)\n * bot.onMessage(async (ctx) => {\n * await ctx.reply('Hello!');\n * await ctx.react('thumbsup');\n * });\n *\n * // Legacy API (still supported)\n * bot.onMessage(async (message) => {\n * await bot.reply(message, 'Hello!');\n * });\n * ```\n */\n onMessage(handler: MessageHandler | ((message: UnifiedMessage) => void | Promise<void>)): void {\n this.on('message', async (event) => {\n if (event.type === 'message') {\n const message = event.message;\n\n // Detect if handler expects context (by checking parameter count)\n // Context handlers have 1 parameter, legacy handlers might check message properties\n // We'll check if the handler looks like it wants a context by seeing if it's async\n // and calling it with a context object\n\n // Create context object\n const ctx = this.createContext(message);\n\n // Call handler with context\n await handler(ctx as any);\n }\n });\n }\n\n /**\n * Create a message context object\n */\n private createContext(message: UnifiedMessage): MessageContext {\n return {\n message,\n platform: this._platform,\n userId: message.userId,\n channelId: message.channelId,\n text: message.text,\n threadId: message.threadId,\n\n // Helper methods\n reply: (text: string, options?: SendMessageOptions) => {\n return this.reply(message, text, options);\n },\n\n react: (emoji: string) => {\n return this.addReaction(message, emoji);\n },\n\n unreact: (emoji: string) => {\n return this.removeReaction(message, emoji);\n },\n\n edit: (newText: string) => {\n return this.editMessage(message, newText);\n },\n\n delete: () => {\n return this.deleteMessage(message);\n },\n\n createThread: (text: string) => {\n return this.createThread(message, text);\n },\n\n send: (text: string, options?: SendMessageOptions) => {\n return this.sendMessage(message.channelId, text, options);\n },\n };\n }\n\n /**\n * Register a handler for reaction events\n */\n onReaction(handler: (event: ReactionEvent) => void | Promise<void>): void {\n this.on('reaction', async (event) => {\n if (event.type === 'reaction') {\n await handler(event);\n }\n });\n }\n\n /**\n * Register a handler for any event\n */\n onEvent(handler: (event: UnifiedEvent) => void | Promise<void>): void {\n this.on('*', handler);\n }\n\n /**\n * Get the underlying adapter (for advanced use cases)\n */\n getAdapter(): PlatformAdapter {\n return this.adapter;\n }\n\n /**\n * Internal: Register an event handler\n */\n private on(\n eventType: string,\n handler: (event: UnifiedEvent) => void | Promise<void>\n ): void {\n if (!this.eventHandlers.has(eventType)) {\n this.eventHandlers.set(eventType, new Set());\n }\n this.eventHandlers.get(eventType)!.add(handler);\n }\n\n /**\n * Internal: Handle an event from the adapter\n */\n private async handleEvent(event: UnifiedEvent): Promise<void> {\n // Call handlers for this specific event type\n const typeHandlers = this.eventHandlers.get(event.type);\n if (typeHandlers) {\n for (const handler of typeHandlers) {\n try {\n await handler(event);\n } catch (error) {\n console.error(\n `[Switchboard] Error in ${event.type} handler:`,\n error\n );\n }\n }\n }\n\n // Call wildcard handlers\n const wildcardHandlers = this.eventHandlers.get('*');\n if (wildcardHandlers) {\n for (const handler of wildcardHandlers) {\n try {\n await handler(event);\n } catch (error) {\n console.error(`[Switchboard] Error in wildcard handler:`, error);\n }\n }\n }\n }\n}\n","import type { PlatformAdapter } from './interface.js';\nimport type { PlatformType } from '../types/platform.js';\n\n/**\n * Global registry for platform adapters\n * Adapters register themselves when their package is imported\n */\nclass AdapterRegistry {\n private adapters = new Map<PlatformType, PlatformAdapter>();\n\n /**\n * Register a platform adapter\n * Called by adapter packages when they're imported\n */\n register(platform: PlatformType, adapter: PlatformAdapter): void {\n if (this.adapters.has(platform)) {\n console.warn(\n `[Switchboard] Adapter for platform \"${platform}\" is already registered. Overwriting.`\n );\n }\n this.adapters.set(platform, adapter);\n }\n\n /**\n * Get an adapter for a platform\n */\n get(platform: PlatformType): PlatformAdapter | undefined {\n return this.adapters.get(platform);\n }\n\n /**\n * Check if an adapter is registered for a platform\n */\n has(platform: PlatformType): boolean {\n return this.adapters.has(platform);\n }\n\n /**\n * Get all registered platforms\n */\n getRegisteredPlatforms(): PlatformType[] {\n return Array.from(this.adapters.keys());\n }\n\n /**\n * Clear all registered adapters (mainly for testing)\n */\n clear(): void {\n this.adapters.clear();\n }\n}\n\n/**\n * Global singleton registry instance\n */\nexport const registry = new AdapterRegistry();\n","import type { PlatformType } from '../types/platform.js';\n\n/**\n * Base error class for all Switchboard errors\n */\nexport class SwitchboardError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly platform?: PlatformType\n ) {\n super(message);\n this.name = 'SwitchboardError';\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when no adapter is found for a platform\n */\nexport class AdapterNotFoundError extends SwitchboardError {\n constructor(platform: PlatformType) {\n super(\n `No adapter found for platform: ${platform}. Did you import @aarekaz/switchboard-${platform}?`,\n 'ADAPTER_NOT_FOUND',\n platform\n );\n this.name = 'AdapterNotFoundError';\n }\n}\n\n/**\n * Error thrown when connection to a platform fails\n */\nexport class ConnectionError extends SwitchboardError {\n constructor(platform: PlatformType, cause: unknown) {\n super(\n `Failed to connect to ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,\n 'CONNECTION_ERROR',\n platform\n );\n this.name = 'ConnectionError';\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when sending a message fails\n */\nexport class MessageSendError extends SwitchboardError {\n constructor(\n platform: PlatformType,\n channelId: string,\n cause: unknown\n ) {\n super(\n `Failed to send message to channel ${channelId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,\n 'MESSAGE_SEND_ERROR',\n platform\n );\n this.name = 'MessageSendError';\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when editing a message fails\n */\nexport class MessageEditError extends SwitchboardError {\n constructor(\n platform: PlatformType,\n messageId: string,\n cause: unknown\n ) {\n super(\n `Failed to edit message ${messageId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,\n 'MESSAGE_EDIT_ERROR',\n platform\n );\n this.name = 'MessageEditError';\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when deleting a message fails\n */\nexport class MessageDeleteError extends SwitchboardError {\n constructor(\n platform: PlatformType,\n messageId: string,\n cause: unknown\n ) {\n super(\n `Failed to delete message ${messageId} on ${platform}: ${cause instanceof Error ? cause.message : String(cause)}`,\n 'MESSAGE_DELETE_ERROR',\n platform\n );\n this.name = 'MessageDeleteError';\n this.cause = cause;\n }\n}\n\n/**\n * Error thrown when adding/removing a reaction fails\n */\nexport class ReactionError extends SwitchboardError {\n constructor(\n platform: PlatformType,\n messageId: string,\n emoji: string,\n cause: unknown\n ) {\n super(\n `Failed to add/remove reaction ${emoji} on message ${messageId} (${platform}): ${cause instanceof Error ? cause.message : String(cause)}`,\n 'REACTION_ERROR',\n platform\n );\n this.name = 'ReactionError';\n this.cause = cause;\n }\n}\n","import { Bot } from './bot.js';\nimport { registry } from '../adapter/registry.js';\nimport type { PlatformAdapter } from '../adapter/interface.js';\nimport type { PlatformType } from '../types/platform.js';\nimport { AdapterNotFoundError, ConnectionError } from '../utils/errors.js';\n\n/**\n * Configuration for creating a bot\n */\nexport interface BotConfig {\n /**\n * Platform to connect to (e.g., 'discord', 'slack', 'teams', 'google-chat')\n */\n platform: PlatformType;\n\n /**\n * Platform credentials (structure depends on the platform)\n */\n credentials?: {\n /** Bot token (Discord, Slack) */\n token?: string;\n /** App ID (Teams) */\n appId?: string;\n /** App password (Teams) */\n appPassword?: string;\n /** Custom credentials object */\n [key: string]: unknown;\n };\n\n /**\n * Custom adapter (for advanced users or custom platforms)\n * If provided, this will be used instead of the registered adapter\n */\n adapter?: PlatformAdapter;\n\n /**\n * Platform-specific configuration (opt-in)\n */\n platformConfig?: {\n discord?: unknown;\n slack?: unknown;\n teams?: unknown;\n googleChat?: unknown;\n [key: string]: unknown;\n };\n}\n\n/**\n * Create a bot instance\n *\n * @example\n * ```typescript\n * import { createBot } from '@aarekaz/switchboard-core';\n * import '@aarekaz/switchboard-discord';\n *\n * const bot = createBot({\n * platform: 'discord',\n * credentials: {\n * token: process.env.DISCORD_TOKEN,\n * },\n * });\n *\n * bot.onMessage(async (message) => {\n * if (message.text.includes('ping')) {\n * await bot.reply(message, 'pong!');\n * }\n * });\n *\n * await bot.start();\n * ```\n */\nexport function createBot(config: BotConfig): Bot {\n let adapter: PlatformAdapter;\n\n // Use custom adapter if provided\n if (config.adapter) {\n adapter = config.adapter;\n } else {\n // Get adapter from registry\n const registeredAdapter = registry.get(config.platform);\n if (!registeredAdapter) {\n throw new AdapterNotFoundError(config.platform);\n }\n adapter = registeredAdapter;\n }\n\n // Create and return the bot instance\n // Connection happens lazily when bot.start() is called\n return new Bot(adapter, config.platform, config.credentials || {});\n}\n","/**\n * Result type for type-safe error handling\n * Inspired by Rust's Result<T, E> type\n */\nexport type Result<T, E = Error> =\n | { ok: true; value: T }\n | { ok: false; error: E };\n\n/**\n * Create a successful result\n */\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\n/**\n * Create an error result\n */\nexport function err<E = Error>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n\n/**\n * Wrap an async operation in a Result type\n * Catches any errors and converts them to Result<T>\n */\nexport async function wrapAsync<T>(\n fn: () => Promise<T>\n): Promise<Result<T, Error>> {\n try {\n const value = await fn();\n return ok(value);\n } catch (error) {\n return err(error instanceof Error ? error : new Error(String(error)));\n }\n}\n\n/**\n * Check if a result is successful\n */\nexport function isOk<T, E>(result: Result<T, E>): result is { ok: true; value: T } {\n return result.ok === true;\n}\n\n/**\n * Check if a result is an error\n */\nexport function isErr<T, E>(result: Result<T, E>): result is { ok: false; error: E } {\n return result.ok === false;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@aarekaz/switchboard-core",
3
+ "version": "0.3.1",
4
+ "description": "Core types and client API for Switchboard - universal chat platform SDK",
5
+ "keywords": [
6
+ "switchboard",
7
+ "chat",
8
+ "bot",
9
+ "sdk",
10
+ "core"
11
+ ],
12
+ "homepage": "https://github.com/Aarekaz/switchboard#readme",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/Aarekaz/switchboard.git",
16
+ "directory": "packages/core"
17
+ },
18
+ "license": "MIT",
19
+ "author": "Aarekaz",
20
+ "type": "module",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ }
26
+ },
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "files": [
31
+ "dist",
32
+ "README.md"
33
+ ],
34
+ "devDependencies": {
35
+ "tsup": "^8.0.1",
36
+ "typescript": "^5.3.3"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "scripts": {
42
+ "build": "tsup",
43
+ "dev": "tsup --watch",
44
+ "typecheck": "tsc --noEmit",
45
+ "clean": "rm -rf dist *.tsbuildinfo"
46
+ }
47
+ }