@chat-adapter/shared 4.1.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -68,7 +68,7 @@ declare function extractFiles(message: AdapterPostableMessage): FileUpload[];
68
68
  /**
69
69
  * Supported platform names for adapter utilities.
70
70
  */
71
- type PlatformName = "slack" | "gchat" | "teams";
71
+ type PlatformName = "slack" | "gchat" | "teams" | "discord";
72
72
  /**
73
73
  * Button style mappings per platform.
74
74
  *
@@ -128,6 +128,82 @@ interface FallbackTextOptions {
128
128
  */
129
129
  declare function cardToFallbackText(card: CardElement, options?: FallbackTextOptions): string;
130
130
 
131
+ /**
132
+ * Buffer conversion utilities for handling file uploads.
133
+ *
134
+ * These utilities handle the conversion of various data types
135
+ * (Buffer, ArrayBuffer, Blob) to Node.js Buffer for file uploads.
136
+ */
137
+
138
+ /**
139
+ * The supported input types for file data.
140
+ */
141
+ type FileDataInput = Buffer | ArrayBuffer | Blob;
142
+ /**
143
+ * Options for buffer conversion.
144
+ */
145
+ interface ToBufferOptions {
146
+ /**
147
+ * The platform name for error messages.
148
+ */
149
+ platform: PlatformName;
150
+ /**
151
+ * If true, throws ValidationError for unsupported types.
152
+ * If false, returns null for unsupported types.
153
+ * Default: true
154
+ */
155
+ throwOnUnsupported?: boolean;
156
+ }
157
+ /**
158
+ * Convert various data types to a Node.js Buffer.
159
+ *
160
+ * Handles:
161
+ * - Buffer: returned as-is
162
+ * - ArrayBuffer: converted using Buffer.from()
163
+ * - Blob: converted via arrayBuffer() then Buffer.from()
164
+ *
165
+ * @param data - The file data to convert
166
+ * @param options - Conversion options
167
+ * @returns Buffer or null if conversion fails and throwOnUnsupported is false
168
+ * @throws ValidationError if data type is unsupported and throwOnUnsupported is true
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * // Throw on unsupported (default behavior)
173
+ * const buffer = await toBuffer(file.data, { platform: "slack" });
174
+ *
175
+ * // Return null on unsupported
176
+ * const buffer = await toBuffer(file.data, { platform: "teams", throwOnUnsupported: false });
177
+ * if (!buffer) continue; // Skip unsupported files
178
+ * ```
179
+ */
180
+ declare function toBuffer(data: FileDataInput | unknown, options: ToBufferOptions): Promise<Buffer | null>;
181
+ /**
182
+ * Synchronous version of toBuffer for non-Blob data.
183
+ *
184
+ * Use this when you know the data is not a Blob (e.g., already validated).
185
+ *
186
+ * @param data - The file data to convert (Buffer or ArrayBuffer only)
187
+ * @param options - Conversion options
188
+ * @returns Buffer or null if conversion fails
189
+ * @throws ValidationError if data is a Blob or unsupported type and throwOnUnsupported is true
190
+ */
191
+ declare function toBufferSync(data: Buffer | ArrayBuffer | unknown, options: ToBufferOptions): Buffer | null;
192
+ /**
193
+ * Convert a Buffer to a data URI string.
194
+ *
195
+ * @param buffer - The buffer to convert
196
+ * @param mimeType - The MIME type (default: application/octet-stream)
197
+ * @returns Data URI string in format `data:{mimeType};base64,{base64Data}`
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * const dataUri = bufferToDataUri(buffer, "image/png");
202
+ * // "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA..."
203
+ * ```
204
+ */
205
+ declare function bufferToDataUri(buffer: Buffer, mimeType?: string): string;
206
+
131
207
  /**
132
208
  * Standardized error types for chat adapters.
133
209
  *
@@ -224,4 +300,4 @@ declare class NetworkError extends AdapterError {
224
300
  constructor(adapter: string, message?: string, originalError?: Error | undefined);
225
301
  }
226
302
 
227
- export { AdapterError, AdapterRateLimitError, AuthenticationError, BUTTON_STYLE_MAPPINGS, type FallbackTextOptions, NetworkError, PermissionError, type PlatformName, ResourceNotFoundError, ValidationError, cardToFallbackText, createEmojiConverter, extractCard, extractFiles, mapButtonStyle };
303
+ export { AdapterError, AdapterRateLimitError, AuthenticationError, BUTTON_STYLE_MAPPINGS, type FallbackTextOptions, type FileDataInput, NetworkError, PermissionError, type PlatformName, ResourceNotFoundError, type ToBufferOptions, ValidationError, bufferToDataUri, cardToFallbackText, createEmojiConverter, extractCard, extractFiles, mapButtonStyle, toBuffer, toBufferSync };
package/dist/index.js CHANGED
@@ -16,56 +16,6 @@ function extractFiles(message) {
16
16
  return [];
17
17
  }
18
18
 
19
- // src/card-utils.ts
20
- import { convertEmojiPlaceholders } from "chat";
21
- var BUTTON_STYLE_MAPPINGS = {
22
- slack: { primary: "primary", danger: "danger" },
23
- gchat: { primary: "primary", danger: "danger" },
24
- // Colors handled via buttonColor
25
- teams: { primary: "positive", danger: "destructive" }
26
- };
27
- function createEmojiConverter(platform) {
28
- return (text) => convertEmojiPlaceholders(text, platform);
29
- }
30
- function mapButtonStyle(style, platform) {
31
- if (!style) return void 0;
32
- return BUTTON_STYLE_MAPPINGS[platform][style];
33
- }
34
- function cardToFallbackText(card, options = {}) {
35
- const { boldFormat = "*", lineBreak = "\n", platform } = options;
36
- const convertText = platform ? createEmojiConverter(platform) : (t) => t;
37
- const parts = [];
38
- if (card.title) {
39
- parts.push(`${boldFormat}${convertText(card.title)}${boldFormat}`);
40
- }
41
- if (card.subtitle) {
42
- parts.push(convertText(card.subtitle));
43
- }
44
- for (const child of card.children) {
45
- const text = childToFallbackText(child, convertText);
46
- if (text) {
47
- parts.push(text);
48
- }
49
- }
50
- return parts.join(lineBreak);
51
- }
52
- function childToFallbackText(child, convertText) {
53
- switch (child.type) {
54
- case "text":
55
- return convertText(child.content);
56
- case "fields":
57
- return child.children.map((f) => `${convertText(f.label)}: ${convertText(f.value)}`).join("\n");
58
- case "actions":
59
- return `[${child.children.map((b) => convertText(b.label)).join("] [")}]`;
60
- case "section":
61
- return child.children.map((c) => childToFallbackText(c, convertText)).filter(Boolean).join("\n");
62
- case "divider":
63
- return "---";
64
- default:
65
- return null;
66
- }
67
- }
68
-
69
19
  // src/errors.ts
70
20
  var AdapterError = class extends Error {
71
21
  /**
@@ -144,6 +94,102 @@ var NetworkError = class extends AdapterError {
144
94
  this.name = "NetworkError";
145
95
  }
146
96
  };
97
+
98
+ // src/buffer-utils.ts
99
+ async function toBuffer(data, options) {
100
+ const { platform, throwOnUnsupported = true } = options;
101
+ if (Buffer.isBuffer(data)) {
102
+ return data;
103
+ }
104
+ if (data instanceof ArrayBuffer) {
105
+ return Buffer.from(data);
106
+ }
107
+ if (data instanceof Blob) {
108
+ const arrayBuffer = await data.arrayBuffer();
109
+ return Buffer.from(arrayBuffer);
110
+ }
111
+ if (throwOnUnsupported) {
112
+ throw new ValidationError(platform, "Unsupported file data type");
113
+ }
114
+ return null;
115
+ }
116
+ function toBufferSync(data, options) {
117
+ const { platform, throwOnUnsupported = true } = options;
118
+ if (Buffer.isBuffer(data)) {
119
+ return data;
120
+ }
121
+ if (data instanceof ArrayBuffer) {
122
+ return Buffer.from(data);
123
+ }
124
+ if (data instanceof Blob) {
125
+ if (throwOnUnsupported) {
126
+ throw new ValidationError(
127
+ platform,
128
+ "Cannot convert Blob synchronously. Use toBuffer() for async conversion."
129
+ );
130
+ }
131
+ return null;
132
+ }
133
+ if (throwOnUnsupported) {
134
+ throw new ValidationError(platform, "Unsupported file data type");
135
+ }
136
+ return null;
137
+ }
138
+ function bufferToDataUri(buffer, mimeType = "application/octet-stream") {
139
+ const base64 = buffer.toString("base64");
140
+ return `data:${mimeType};base64,${base64}`;
141
+ }
142
+
143
+ // src/card-utils.ts
144
+ import { convertEmojiPlaceholders } from "chat";
145
+ var BUTTON_STYLE_MAPPINGS = {
146
+ slack: { primary: "primary", danger: "danger" },
147
+ gchat: { primary: "primary", danger: "danger" },
148
+ // Colors handled via buttonColor
149
+ teams: { primary: "positive", danger: "destructive" },
150
+ discord: { primary: "primary", danger: "danger" }
151
+ };
152
+ function createEmojiConverter(platform) {
153
+ return (text) => convertEmojiPlaceholders(text, platform);
154
+ }
155
+ function mapButtonStyle(style, platform) {
156
+ if (!style) return void 0;
157
+ return BUTTON_STYLE_MAPPINGS[platform][style];
158
+ }
159
+ function cardToFallbackText(card, options = {}) {
160
+ const { boldFormat = "*", lineBreak = "\n", platform } = options;
161
+ const convertText = platform ? createEmojiConverter(platform) : (t) => t;
162
+ const parts = [];
163
+ if (card.title) {
164
+ parts.push(`${boldFormat}${convertText(card.title)}${boldFormat}`);
165
+ }
166
+ if (card.subtitle) {
167
+ parts.push(convertText(card.subtitle));
168
+ }
169
+ for (const child of card.children) {
170
+ const text = childToFallbackText(child, convertText);
171
+ if (text) {
172
+ parts.push(text);
173
+ }
174
+ }
175
+ return parts.join(lineBreak);
176
+ }
177
+ function childToFallbackText(child, convertText) {
178
+ switch (child.type) {
179
+ case "text":
180
+ return convertText(child.content);
181
+ case "fields":
182
+ return child.children.map((f) => `${convertText(f.label)}: ${convertText(f.value)}`).join("\n");
183
+ case "actions":
184
+ return `[${child.children.map((b) => convertText(b.label)).join("] [")}]`;
185
+ case "section":
186
+ return child.children.map((c) => childToFallbackText(c, convertText)).filter(Boolean).join("\n");
187
+ case "divider":
188
+ return "---";
189
+ default:
190
+ return null;
191
+ }
192
+ }
147
193
  export {
148
194
  AdapterError,
149
195
  AdapterRateLimitError,
@@ -153,10 +199,13 @@ export {
153
199
  PermissionError,
154
200
  ResourceNotFoundError,
155
201
  ValidationError,
202
+ bufferToDataUri,
156
203
  cardToFallbackText,
157
204
  createEmojiConverter,
158
205
  extractCard,
159
206
  extractFiles,
160
- mapButtonStyle
207
+ mapButtonStyle,
208
+ toBuffer,
209
+ toBufferSync
161
210
  };
162
211
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/adapter-utils.ts","../src/card-utils.ts","../src/errors.ts"],"sourcesContent":["/**\n * Shared utility functions for chat adapters.\n *\n * These utilities are used across all adapter implementations (Slack, Teams, GChat)\n * to reduce code duplication and ensure consistent behavior.\n */\n\nimport type { CardElement } from \"chat\";\nimport { isCardElement } from \"chat\";\nimport type { AdapterPostableMessage, FileUpload } from \"chat\";\n\n/**\n * Extract CardElement from an AdapterPostableMessage if present.\n *\n * Handles two cases:\n * 1. The message IS a CardElement (type: \"card\")\n * 2. The message is a PostableCard with a `card` property\n *\n * @param message - The message to extract the card from\n * @returns The CardElement if found, null otherwise\n *\n * @example\n * ```typescript\n * // Case 1: Direct CardElement\n * const card = Card({ title: \"Test\" });\n * extractCard(card); // returns the card\n *\n * // Case 2: PostableCard wrapper\n * const message = { card, fallbackText: \"...\" };\n * extractCard(message); // returns the card\n *\n * // Case 3: Non-card message\n * extractCard(\"Hello\"); // returns null\n * extractCard({ markdown: \"**bold**\" }); // returns null\n * ```\n */\nexport function extractCard(\n message: AdapterPostableMessage,\n): CardElement | null {\n if (isCardElement(message)) {\n return message;\n }\n if (typeof message === \"object\" && message !== null && \"card\" in message) {\n return message.card;\n }\n return null;\n}\n\n/**\n * Extract FileUpload array from an AdapterPostableMessage if present.\n *\n * Files can be attached to PostableRaw, PostableMarkdown, PostableAst,\n * or PostableCard messages via the `files` property.\n *\n * @param message - The message to extract files from\n * @returns Array of FileUpload objects, or empty array if none\n *\n * @example\n * ```typescript\n * // With files\n * const message = {\n * markdown: \"**Text**\",\n * files: [{ data: Buffer.from(\"...\"), filename: \"doc.pdf\" }]\n * };\n * extractFiles(message); // returns the files array\n *\n * // Without files\n * extractFiles(\"Hello\"); // returns []\n * extractFiles({ raw: \"text\" }); // returns []\n * ```\n */\nexport function extractFiles(message: AdapterPostableMessage): FileUpload[] {\n if (typeof message === \"object\" && message !== null && \"files\" in message) {\n return (message as { files?: FileUpload[] }).files ?? [];\n }\n return [];\n}\n","/**\n * Shared card conversion utilities for adapters.\n *\n * These utilities reduce duplication across adapter implementations\n * for card-to-platform-format conversions.\n */\n\nimport type { ButtonElement, CardChild, CardElement } from \"chat\";\nimport { convertEmojiPlaceholders } from \"chat\";\n\n/**\n * Supported platform names for adapter utilities.\n */\nexport type PlatformName = \"slack\" | \"gchat\" | \"teams\";\n\n/**\n * Button style mappings per platform.\n *\n * Maps our standard button styles (\"primary\", \"danger\") to\n * platform-specific values.\n */\nexport const BUTTON_STYLE_MAPPINGS: Record<\n PlatformName,\n Record<string, string>\n> = {\n slack: { primary: \"primary\", danger: \"danger\" },\n gchat: { primary: \"primary\", danger: \"danger\" }, // Colors handled via buttonColor\n teams: { primary: \"positive\", danger: \"destructive\" },\n};\n\n/**\n * Create a platform-specific emoji converter function.\n *\n * Returns a function that converts emoji placeholders (e.g., `{{emoji:wave}}`)\n * to the platform's native format.\n *\n * @example\n * ```typescript\n * const convertEmoji = createEmojiConverter(\"slack\");\n * convertEmoji(\"{{emoji:wave}} Hello\"); // \":wave: Hello\"\n * ```\n */\nexport function createEmojiConverter(\n platform: PlatformName,\n): (text: string) => string {\n return (text: string) => convertEmojiPlaceholders(text, platform);\n}\n\n/**\n * Map a button style to the platform-specific value.\n *\n * @example\n * ```typescript\n * mapButtonStyle(\"primary\", \"teams\"); // \"positive\"\n * mapButtonStyle(\"danger\", \"slack\"); // \"danger\"\n * mapButtonStyle(undefined, \"teams\"); // undefined\n * ```\n */\nexport function mapButtonStyle(\n style: ButtonElement[\"style\"],\n platform: PlatformName,\n): string | undefined {\n if (!style) return undefined;\n return BUTTON_STYLE_MAPPINGS[platform][style];\n}\n\n/**\n * Options for fallback text generation.\n */\nexport interface FallbackTextOptions {\n /** Bold format string (default: \"*\" for mrkdwn, \"**\" for markdown) */\n boldFormat?: \"*\" | \"**\";\n /** Line break between sections (default: \"\\n\") */\n lineBreak?: \"\\n\" | \"\\n\\n\";\n /** Platform for emoji conversion (optional) */\n platform?: PlatformName;\n}\n\n/**\n * Generate fallback plain text from a card element.\n *\n * Used when the platform can't render rich cards or for notification previews.\n * Consolidates duplicate implementations from individual adapters.\n *\n * @example\n * ```typescript\n * // Slack-style (mrkdwn)\n * cardToFallbackText(card, { boldFormat: \"*\", platform: \"slack\" });\n *\n * // Teams-style (markdown with double line breaks)\n * cardToFallbackText(card, { boldFormat: \"**\", lineBreak: \"\\n\\n\", platform: \"teams\" });\n * ```\n */\nexport function cardToFallbackText(\n card: CardElement,\n options: FallbackTextOptions = {},\n): string {\n const { boldFormat = \"*\", lineBreak = \"\\n\", platform } = options;\n\n const convertText = platform\n ? createEmojiConverter(platform)\n : (t: string) => t;\n\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(`${boldFormat}${convertText(card.title)}${boldFormat}`);\n }\n\n if (card.subtitle) {\n parts.push(convertText(card.subtitle));\n }\n\n for (const child of card.children) {\n const text = childToFallbackText(child, convertText);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(lineBreak);\n}\n\n/**\n * Convert a card child element to fallback text.\n * Internal helper for cardToFallbackText.\n */\nfunction childToFallbackText(\n child: CardChild,\n convertText: (t: string) => string,\n): string | null {\n switch (child.type) {\n case \"text\":\n return convertText(child.content);\n case \"fields\":\n return child.children\n .map((f) => `${convertText(f.label)}: ${convertText(f.value)}`)\n .join(\"\\n\");\n case \"actions\":\n return `[${child.children.map((b) => convertText(b.label)).join(\"] [\")}]`;\n case \"section\":\n return child.children\n .map((c) => childToFallbackText(c, convertText))\n .filter(Boolean)\n .join(\"\\n\");\n case \"divider\":\n return \"---\";\n default:\n return null;\n }\n}\n","/**\n * Standardized error types for chat adapters.\n *\n * These error classes provide consistent error handling across all\n * adapter implementations.\n */\n\n/**\n * Base error class for adapter operations.\n *\n * All adapter-specific errors should extend this class.\n */\nexport class AdapterError extends Error {\n /**\n * @param message - Human-readable error message\n * @param adapter - Name of the adapter (e.g., \"slack\", \"teams\", \"gchat\")\n * @param code - Optional error code for programmatic handling\n */\n constructor(\n message: string,\n public readonly adapter: string,\n public readonly code?: string,\n ) {\n super(message);\n this.name = \"AdapterError\";\n }\n}\n\n/**\n * Rate limit error - thrown when platform API rate limits are hit.\n *\n * @example\n * ```typescript\n * throw new AdapterRateLimitError(\"slack\", 30);\n * // message: \"Rate limited by slack, retry after 30s\"\n * ```\n */\nexport class AdapterRateLimitError extends AdapterError {\n constructor(\n adapter: string,\n public readonly retryAfter?: number,\n ) {\n super(\n `Rate limited by ${adapter}${retryAfter ? `, retry after ${retryAfter}s` : \"\"}`,\n adapter,\n \"RATE_LIMITED\",\n );\n this.name = \"AdapterRateLimitError\";\n }\n}\n\n/**\n * Authentication error - thrown when credentials are invalid or expired.\n *\n * @example\n * ```typescript\n * throw new AuthenticationError(\"teams\", \"Token expired\");\n * ```\n */\nexport class AuthenticationError extends AdapterError {\n constructor(adapter: string, message?: string) {\n super(\n message || `Authentication failed for ${adapter}`,\n adapter,\n \"AUTH_FAILED\",\n );\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * Not found error - thrown when a requested resource doesn't exist.\n *\n * @example\n * ```typescript\n * throw new ResourceNotFoundError(\"slack\", \"channel\", \"C123456\");\n * // message: \"channel 'C123456' not found in slack\"\n * ```\n */\nexport class ResourceNotFoundError extends AdapterError {\n constructor(\n adapter: string,\n public readonly resourceType: string,\n public readonly resourceId?: string,\n ) {\n const idPart = resourceId ? ` '${resourceId}'` : \"\";\n super(\n `${resourceType}${idPart} not found in ${adapter}`,\n adapter,\n \"NOT_FOUND\",\n );\n this.name = \"ResourceNotFoundError\";\n }\n}\n\n/**\n * Permission error - thrown when the bot lacks required permissions.\n *\n * @example\n * ```typescript\n * throw new PermissionError(\"teams\", \"send messages\", \"channels:write\");\n * ```\n */\nexport class PermissionError extends AdapterError {\n constructor(\n adapter: string,\n public readonly action: string,\n public readonly requiredScope?: string,\n ) {\n const scopePart = requiredScope ? ` (requires: ${requiredScope})` : \"\";\n super(\n `Permission denied: cannot ${action} in ${adapter}${scopePart}`,\n adapter,\n \"PERMISSION_DENIED\",\n );\n this.name = \"PermissionError\";\n }\n}\n\n/**\n * Validation error - thrown when input data is invalid.\n *\n * @example\n * ```typescript\n * throw new ValidationError(\"slack\", \"Message text exceeds 40000 characters\");\n * ```\n */\nexport class ValidationError extends AdapterError {\n constructor(adapter: string, message: string) {\n super(message, adapter, \"VALIDATION_ERROR\");\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Network error - thrown when there's a network/connectivity issue.\n *\n * @example\n * ```typescript\n * throw new NetworkError(\"gchat\", \"Connection timeout after 30s\");\n * ```\n */\nexport class NetworkError extends AdapterError {\n constructor(\n adapter: string,\n message?: string,\n public readonly originalError?: Error,\n ) {\n super(\n message || `Network error communicating with ${adapter}`,\n adapter,\n \"NETWORK_ERROR\",\n );\n this.name = \"NetworkError\";\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/adapter-utils.ts","../src/errors.ts","../src/buffer-utils.ts","../src/card-utils.ts"],"sourcesContent":["/**\n * Shared utility functions for chat adapters.\n *\n * These utilities are used across all adapter implementations (Slack, Teams, GChat)\n * to reduce code duplication and ensure consistent behavior.\n */\n\nimport type { AdapterPostableMessage, CardElement, FileUpload } from \"chat\";\nimport { isCardElement } from \"chat\";\n\n/**\n * Extract CardElement from an AdapterPostableMessage if present.\n *\n * Handles two cases:\n * 1. The message IS a CardElement (type: \"card\")\n * 2. The message is a PostableCard with a `card` property\n *\n * @param message - The message to extract the card from\n * @returns The CardElement if found, null otherwise\n *\n * @example\n * ```typescript\n * // Case 1: Direct CardElement\n * const card = Card({ title: \"Test\" });\n * extractCard(card); // returns the card\n *\n * // Case 2: PostableCard wrapper\n * const message = { card, fallbackText: \"...\" };\n * extractCard(message); // returns the card\n *\n * // Case 3: Non-card message\n * extractCard(\"Hello\"); // returns null\n * extractCard({ markdown: \"**bold**\" }); // returns null\n * ```\n */\nexport function extractCard(\n message: AdapterPostableMessage,\n): CardElement | null {\n if (isCardElement(message)) {\n return message;\n }\n if (typeof message === \"object\" && message !== null && \"card\" in message) {\n return message.card;\n }\n return null;\n}\n\n/**\n * Extract FileUpload array from an AdapterPostableMessage if present.\n *\n * Files can be attached to PostableRaw, PostableMarkdown, PostableAst,\n * or PostableCard messages via the `files` property.\n *\n * @param message - The message to extract files from\n * @returns Array of FileUpload objects, or empty array if none\n *\n * @example\n * ```typescript\n * // With files\n * const message = {\n * markdown: \"**Text**\",\n * files: [{ data: Buffer.from(\"...\"), filename: \"doc.pdf\" }]\n * };\n * extractFiles(message); // returns the files array\n *\n * // Without files\n * extractFiles(\"Hello\"); // returns []\n * extractFiles({ raw: \"text\" }); // returns []\n * ```\n */\nexport function extractFiles(message: AdapterPostableMessage): FileUpload[] {\n if (typeof message === \"object\" && message !== null && \"files\" in message) {\n return (message as { files?: FileUpload[] }).files ?? [];\n }\n return [];\n}\n","/**\n * Standardized error types for chat adapters.\n *\n * These error classes provide consistent error handling across all\n * adapter implementations.\n */\n\n/**\n * Base error class for adapter operations.\n *\n * All adapter-specific errors should extend this class.\n */\nexport class AdapterError extends Error {\n /**\n * @param message - Human-readable error message\n * @param adapter - Name of the adapter (e.g., \"slack\", \"teams\", \"gchat\")\n * @param code - Optional error code for programmatic handling\n */\n constructor(\n message: string,\n public readonly adapter: string,\n public readonly code?: string,\n ) {\n super(message);\n this.name = \"AdapterError\";\n }\n}\n\n/**\n * Rate limit error - thrown when platform API rate limits are hit.\n *\n * @example\n * ```typescript\n * throw new AdapterRateLimitError(\"slack\", 30);\n * // message: \"Rate limited by slack, retry after 30s\"\n * ```\n */\nexport class AdapterRateLimitError extends AdapterError {\n constructor(\n adapter: string,\n public readonly retryAfter?: number,\n ) {\n super(\n `Rate limited by ${adapter}${retryAfter ? `, retry after ${retryAfter}s` : \"\"}`,\n adapter,\n \"RATE_LIMITED\",\n );\n this.name = \"AdapterRateLimitError\";\n }\n}\n\n/**\n * Authentication error - thrown when credentials are invalid or expired.\n *\n * @example\n * ```typescript\n * throw new AuthenticationError(\"teams\", \"Token expired\");\n * ```\n */\nexport class AuthenticationError extends AdapterError {\n constructor(adapter: string, message?: string) {\n super(\n message || `Authentication failed for ${adapter}`,\n adapter,\n \"AUTH_FAILED\",\n );\n this.name = \"AuthenticationError\";\n }\n}\n\n/**\n * Not found error - thrown when a requested resource doesn't exist.\n *\n * @example\n * ```typescript\n * throw new ResourceNotFoundError(\"slack\", \"channel\", \"C123456\");\n * // message: \"channel 'C123456' not found in slack\"\n * ```\n */\nexport class ResourceNotFoundError extends AdapterError {\n constructor(\n adapter: string,\n public readonly resourceType: string,\n public readonly resourceId?: string,\n ) {\n const idPart = resourceId ? ` '${resourceId}'` : \"\";\n super(\n `${resourceType}${idPart} not found in ${adapter}`,\n adapter,\n \"NOT_FOUND\",\n );\n this.name = \"ResourceNotFoundError\";\n }\n}\n\n/**\n * Permission error - thrown when the bot lacks required permissions.\n *\n * @example\n * ```typescript\n * throw new PermissionError(\"teams\", \"send messages\", \"channels:write\");\n * ```\n */\nexport class PermissionError extends AdapterError {\n constructor(\n adapter: string,\n public readonly action: string,\n public readonly requiredScope?: string,\n ) {\n const scopePart = requiredScope ? ` (requires: ${requiredScope})` : \"\";\n super(\n `Permission denied: cannot ${action} in ${adapter}${scopePart}`,\n adapter,\n \"PERMISSION_DENIED\",\n );\n this.name = \"PermissionError\";\n }\n}\n\n/**\n * Validation error - thrown when input data is invalid.\n *\n * @example\n * ```typescript\n * throw new ValidationError(\"slack\", \"Message text exceeds 40000 characters\");\n * ```\n */\nexport class ValidationError extends AdapterError {\n constructor(adapter: string, message: string) {\n super(message, adapter, \"VALIDATION_ERROR\");\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Network error - thrown when there's a network/connectivity issue.\n *\n * @example\n * ```typescript\n * throw new NetworkError(\"gchat\", \"Connection timeout after 30s\");\n * ```\n */\nexport class NetworkError extends AdapterError {\n constructor(\n adapter: string,\n message?: string,\n public readonly originalError?: Error,\n ) {\n super(\n message || `Network error communicating with ${adapter}`,\n adapter,\n \"NETWORK_ERROR\",\n );\n this.name = \"NetworkError\";\n }\n}\n","/**\n * Buffer conversion utilities for handling file uploads.\n *\n * These utilities handle the conversion of various data types\n * (Buffer, ArrayBuffer, Blob) to Node.js Buffer for file uploads.\n */\n\nimport type { PlatformName } from \"./card-utils\";\nimport { ValidationError } from \"./errors\";\n\n/**\n * The supported input types for file data.\n */\nexport type FileDataInput = Buffer | ArrayBuffer | Blob;\n\n/**\n * Options for buffer conversion.\n */\nexport interface ToBufferOptions {\n /**\n * The platform name for error messages.\n */\n platform: PlatformName;\n\n /**\n * If true, throws ValidationError for unsupported types.\n * If false, returns null for unsupported types.\n * Default: true\n */\n throwOnUnsupported?: boolean;\n}\n\n/**\n * Convert various data types to a Node.js Buffer.\n *\n * Handles:\n * - Buffer: returned as-is\n * - ArrayBuffer: converted using Buffer.from()\n * - Blob: converted via arrayBuffer() then Buffer.from()\n *\n * @param data - The file data to convert\n * @param options - Conversion options\n * @returns Buffer or null if conversion fails and throwOnUnsupported is false\n * @throws ValidationError if data type is unsupported and throwOnUnsupported is true\n *\n * @example\n * ```typescript\n * // Throw on unsupported (default behavior)\n * const buffer = await toBuffer(file.data, { platform: \"slack\" });\n *\n * // Return null on unsupported\n * const buffer = await toBuffer(file.data, { platform: \"teams\", throwOnUnsupported: false });\n * if (!buffer) continue; // Skip unsupported files\n * ```\n */\nexport async function toBuffer(\n data: FileDataInput | unknown,\n options: ToBufferOptions,\n): Promise<Buffer | null> {\n const { platform, throwOnUnsupported = true } = options;\n\n if (Buffer.isBuffer(data)) {\n return data;\n }\n\n if (data instanceof ArrayBuffer) {\n return Buffer.from(data);\n }\n\n if (data instanceof Blob) {\n const arrayBuffer = await data.arrayBuffer();\n return Buffer.from(arrayBuffer);\n }\n\n if (throwOnUnsupported) {\n throw new ValidationError(platform, \"Unsupported file data type\");\n }\n\n return null;\n}\n\n/**\n * Synchronous version of toBuffer for non-Blob data.\n *\n * Use this when you know the data is not a Blob (e.g., already validated).\n *\n * @param data - The file data to convert (Buffer or ArrayBuffer only)\n * @param options - Conversion options\n * @returns Buffer or null if conversion fails\n * @throws ValidationError if data is a Blob or unsupported type and throwOnUnsupported is true\n */\nexport function toBufferSync(\n data: Buffer | ArrayBuffer | unknown,\n options: ToBufferOptions,\n): Buffer | null {\n const { platform, throwOnUnsupported = true } = options;\n\n if (Buffer.isBuffer(data)) {\n return data;\n }\n\n if (data instanceof ArrayBuffer) {\n return Buffer.from(data);\n }\n\n if (data instanceof Blob) {\n if (throwOnUnsupported) {\n throw new ValidationError(\n platform,\n \"Cannot convert Blob synchronously. Use toBuffer() for async conversion.\",\n );\n }\n return null;\n }\n\n if (throwOnUnsupported) {\n throw new ValidationError(platform, \"Unsupported file data type\");\n }\n\n return null;\n}\n\n/**\n * Convert a Buffer to a data URI string.\n *\n * @param buffer - The buffer to convert\n * @param mimeType - The MIME type (default: application/octet-stream)\n * @returns Data URI string in format `data:{mimeType};base64,{base64Data}`\n *\n * @example\n * ```typescript\n * const dataUri = bufferToDataUri(buffer, \"image/png\");\n * // \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgA...\"\n * ```\n */\nexport function bufferToDataUri(\n buffer: Buffer,\n mimeType = \"application/octet-stream\",\n): string {\n const base64 = buffer.toString(\"base64\");\n return `data:${mimeType};base64,${base64}`;\n}\n","/**\n * Shared card conversion utilities for adapters.\n *\n * These utilities reduce duplication across adapter implementations\n * for card-to-platform-format conversions.\n */\n\nimport type { ButtonElement, CardChild, CardElement } from \"chat\";\nimport { convertEmojiPlaceholders } from \"chat\";\n\n/**\n * Supported platform names for adapter utilities.\n */\nexport type PlatformName = \"slack\" | \"gchat\" | \"teams\" | \"discord\";\n\n/**\n * Button style mappings per platform.\n *\n * Maps our standard button styles (\"primary\", \"danger\") to\n * platform-specific values.\n */\nexport const BUTTON_STYLE_MAPPINGS: Record<\n PlatformName,\n Record<string, string>\n> = {\n slack: { primary: \"primary\", danger: \"danger\" },\n gchat: { primary: \"primary\", danger: \"danger\" }, // Colors handled via buttonColor\n teams: { primary: \"positive\", danger: \"destructive\" },\n discord: { primary: \"primary\", danger: \"danger\" },\n};\n\n/**\n * Create a platform-specific emoji converter function.\n *\n * Returns a function that converts emoji placeholders (e.g., `{{emoji:wave}}`)\n * to the platform's native format.\n *\n * @example\n * ```typescript\n * const convertEmoji = createEmojiConverter(\"slack\");\n * convertEmoji(\"{{emoji:wave}} Hello\"); // \":wave: Hello\"\n * ```\n */\nexport function createEmojiConverter(\n platform: PlatformName,\n): (text: string) => string {\n return (text: string) => convertEmojiPlaceholders(text, platform);\n}\n\n/**\n * Map a button style to the platform-specific value.\n *\n * @example\n * ```typescript\n * mapButtonStyle(\"primary\", \"teams\"); // \"positive\"\n * mapButtonStyle(\"danger\", \"slack\"); // \"danger\"\n * mapButtonStyle(undefined, \"teams\"); // undefined\n * ```\n */\nexport function mapButtonStyle(\n style: ButtonElement[\"style\"],\n platform: PlatformName,\n): string | undefined {\n if (!style) return undefined;\n return BUTTON_STYLE_MAPPINGS[platform][style];\n}\n\n/**\n * Options for fallback text generation.\n */\nexport interface FallbackTextOptions {\n /** Bold format string (default: \"*\" for mrkdwn, \"**\" for markdown) */\n boldFormat?: \"*\" | \"**\";\n /** Line break between sections (default: \"\\n\") */\n lineBreak?: \"\\n\" | \"\\n\\n\";\n /** Platform for emoji conversion (optional) */\n platform?: PlatformName;\n}\n\n/**\n * Generate fallback plain text from a card element.\n *\n * Used when the platform can't render rich cards or for notification previews.\n * Consolidates duplicate implementations from individual adapters.\n *\n * @example\n * ```typescript\n * // Slack-style (mrkdwn)\n * cardToFallbackText(card, { boldFormat: \"*\", platform: \"slack\" });\n *\n * // Teams-style (markdown with double line breaks)\n * cardToFallbackText(card, { boldFormat: \"**\", lineBreak: \"\\n\\n\", platform: \"teams\" });\n * ```\n */\nexport function cardToFallbackText(\n card: CardElement,\n options: FallbackTextOptions = {},\n): string {\n const { boldFormat = \"*\", lineBreak = \"\\n\", platform } = options;\n\n const convertText = platform\n ? createEmojiConverter(platform)\n : (t: string) => t;\n\n const parts: string[] = [];\n\n if (card.title) {\n parts.push(`${boldFormat}${convertText(card.title)}${boldFormat}`);\n }\n\n if (card.subtitle) {\n parts.push(convertText(card.subtitle));\n }\n\n for (const child of card.children) {\n const text = childToFallbackText(child, convertText);\n if (text) {\n parts.push(text);\n }\n }\n\n return parts.join(lineBreak);\n}\n\n/**\n * Convert a card child element to fallback text.\n * Internal helper for cardToFallbackText.\n */\nfunction childToFallbackText(\n child: CardChild,\n convertText: (t: string) => string,\n): string | null {\n switch (child.type) {\n case \"text\":\n return convertText(child.content);\n case \"fields\":\n return child.children\n .map((f) => `${convertText(f.label)}: ${convertText(f.value)}`)\n .join(\"\\n\");\n case \"actions\":\n return `[${child.children.map((b) => convertText(b.label)).join(\"] [\")}]`;\n case \"section\":\n return child.children\n .map((c) => childToFallbackText(c, convertText))\n .filter(Boolean)\n .join(\"\\n\");\n case \"divider\":\n return \"---\";\n default:\n return null;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chat-adapter/shared",
3
- "version": "4.1.0",
3
+ "version": "4.4.0",
4
4
  "description": "Shared utilities for chat SDK adapters",
5
5
  "type": "module",
6
6
  "exports": {
@@ -15,10 +15,10 @@
15
15
  "dist"
16
16
  ],
17
17
  "dependencies": {
18
- "chat": "4.2.0"
18
+ "chat": "4.4.0"
19
19
  },
20
20
  "devDependencies": {
21
- "@biomejs/biome": "^1.7.3",
21
+ "@biomejs/biome": "^2.3.10",
22
22
  "@types/node": "^22.10.2",
23
23
  "tsup": "^8.3.5",
24
24
  "typescript": "^5.7.2",