whatsapp-cloud 0.0.3 → 0.0.4

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 (52) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +11 -0
  3. package/agent_docs/DESIGN.md +707 -0
  4. package/agent_docs/MESSAGES_NAMESPACE_ANALYSIS.md +368 -0
  5. package/agent_docs/NAMING_DECISION.md +78 -0
  6. package/agent_docs/STRUCTURE.md +711 -0
  7. package/agent_docs/messages-namespace-design.md +357 -0
  8. package/package.json +5 -2
  9. package/src/client/HttpClient.ts +122 -0
  10. package/src/client/WhatsAppClient.ts +55 -0
  11. package/src/client/index.ts +2 -0
  12. package/src/errors.ts +58 -0
  13. package/src/index.ts +16 -1
  14. package/src/schemas/accounts/index.ts +1 -0
  15. package/src/schemas/accounts/phone-number.ts +20 -0
  16. package/src/schemas/business/account.ts +43 -0
  17. package/src/schemas/business/index.ts +2 -0
  18. package/src/schemas/client.ts +50 -0
  19. package/src/schemas/debug.ts +25 -0
  20. package/src/schemas/index.ts +5 -0
  21. package/src/schemas/messages/index.ts +2 -0
  22. package/src/schemas/messages/request.ts +82 -0
  23. package/src/schemas/messages/response.ts +19 -0
  24. package/src/services/accounts/AccountsClient.ts +42 -0
  25. package/src/services/accounts/AccountsService.ts +47 -0
  26. package/src/services/accounts/index.ts +2 -0
  27. package/src/services/accounts/methods/list-phone-numbers.ts +16 -0
  28. package/src/services/business/BusinessClient.ts +42 -0
  29. package/src/services/business/BusinessService.ts +47 -0
  30. package/src/services/business/index.ts +3 -0
  31. package/src/services/business/methods/list-accounts.ts +18 -0
  32. package/src/services/index.ts +2 -0
  33. package/src/services/messages/MessagesClient.ts +38 -0
  34. package/src/services/messages/MessagesService.ts +77 -0
  35. package/src/services/messages/index.ts +8 -0
  36. package/src/services/messages/methods/send-image.ts +33 -0
  37. package/src/services/messages/methods/send-location.ts +32 -0
  38. package/src/services/messages/methods/send-reaction.ts +33 -0
  39. package/src/services/messages/methods/send-text.ts +32 -0
  40. package/src/services/messages/utils/build-message-payload.ts +32 -0
  41. package/src/types/accounts/index.ts +1 -0
  42. package/src/types/accounts/phone-number.ts +9 -0
  43. package/src/types/business/account.ts +10 -0
  44. package/src/types/business/index.ts +2 -0
  45. package/src/types/client.ts +8 -0
  46. package/src/types/debug.ts +8 -0
  47. package/src/types/index.ts +5 -0
  48. package/src/types/messages/index.ts +2 -0
  49. package/src/types/messages/request.ts +27 -0
  50. package/src/types/messages/response.ts +7 -0
  51. package/src/utils/zod-error.ts +28 -0
  52. package/tsconfig.json +4 -1
@@ -0,0 +1,368 @@
1
+ # Messages Namespace Analysis & Recommendations
2
+
3
+ ## 🎯 Current Implementation Review
4
+
5
+ ### What We Have ✅
6
+
7
+ 1. **Clean Service Structure**
8
+ - `MessagesService` class with clear method names
9
+ - Each method delegates to separate function modules
10
+ - Type-safe request/response types
11
+
12
+ 2. **Schema-First Validation**
13
+ - Zod schemas validate requests before API calls
14
+ - Types inferred from schemas
15
+ - Detailed error messages in schemas
16
+
17
+ 3. **Request Transformation**
18
+ - User-friendly camelCase → API snake_case
19
+ - Handles optional fields gracefully
20
+
21
+ 4. **Phone Number ID Resolution**
22
+ - Request override or client default
23
+ - Clear error if missing
24
+
25
+ ## 🔍 Comparison: Our Implementation vs ElevenLabs
26
+
27
+ ### Similarities ✅
28
+
29
+ 1. **Namespace Pattern**: Both use `client.messages.sendText()` style
30
+ 2. **Service Classes**: Both organize methods in service classes
31
+ 3. **Type Safety**: Both use TypeScript for type safety
32
+ 4. **Error Handling**: Both have custom error classes
33
+
34
+ ### Key Differences ⚠️
35
+
36
+ #### 1. **Error Handling in HTTP Client**
37
+
38
+ **ElevenLabs:**
39
+ ```typescript
40
+ // Parses error response body properly
41
+ const errorBody = await getErrorResponseBody(response);
42
+ throw new ElevenLabsError({
43
+ statusCode: response.status,
44
+ body: errorBody,
45
+ rawResponse: response
46
+ });
47
+
48
+ // Handles specific status codes
49
+ switch (statusCode) {
50
+ case 422:
51
+ throw new UnprocessableEntityError(...);
52
+ case 429:
53
+ throw new RateLimitError(...);
54
+ default:
55
+ throw new ElevenLabsError(...);
56
+ }
57
+ ```
58
+
59
+ **Ours:**
60
+ ```typescript
61
+ // Generic error, no structured parsing
62
+ const error = await response.json().catch(() => ({
63
+ error: { message: response.statusText, code: response.status }
64
+ }));
65
+ throw new Error(`API Error: ${error.error?.message}`);
66
+ ```
67
+
68
+ **Missing:**
69
+ - ❌ Proper error response parsing (handles non-JSON responses)
70
+ - ❌ Structured error types (rate limit, validation, etc.)
71
+ - ❌ Error body preservation for debugging
72
+ - ❌ Status code-specific error handling
73
+
74
+ #### 2. **Request Validation Error Handling**
75
+
76
+ **ElevenLabs:**
77
+ - Uses serializers (similar to our Zod schemas)
78
+ - Errors are typed and specific
79
+
80
+ **Ours:**
81
+ ```typescript
82
+ // Current: Generic Zod error
83
+ const validated = sendTextRequestSchema.parse(request);
84
+ // If fails, throws ZodError (not our custom error)
85
+ ```
86
+
87
+ **Missing:**
88
+ - ❌ Transform Zod errors to `WhatsAppValidationError`
89
+ - ❌ Consistent error handling across all methods
90
+
91
+ #### 3. **Response Validation**
92
+
93
+ **ElevenLabs:**
94
+ - Validates responses with serializers
95
+ - Throws typed errors if response doesn't match schema
96
+
97
+ **Ours:**
98
+ ```typescript
99
+ // No response validation
100
+ return client.post<MessageResponse>(...);
101
+ // Type assertion only, no runtime validation
102
+ ```
103
+
104
+ **Missing:**
105
+ - ❌ Response schema validation
106
+ - ❌ Catch API changes early
107
+ - ❌ Better error messages for unexpected responses
108
+
109
+ #### 4. **Request Options Pattern**
110
+
111
+ **ElevenLabs:**
112
+ ```typescript
113
+ // Per-request options
114
+ client.textToSpeech.convert(voiceId, request, {
115
+ timeoutInSeconds: 60,
116
+ maxRetries: 3,
117
+ abortSignal: controller.signal
118
+ });
119
+ ```
120
+
121
+ **Ours:**
122
+ ```typescript
123
+ // No per-request options
124
+ client.messages.sendText(request);
125
+ // Can't override timeout, retries, etc. per request
126
+ ```
127
+
128
+ **Missing:**
129
+ - ❌ Per-request timeout override
130
+ - ❌ Per-request retry configuration
131
+ - ❌ Request cancellation (AbortSignal)
132
+
133
+ #### 5. **Retry Logic**
134
+
135
+ **ElevenLabs:**
136
+ - Built-in retry logic with exponential backoff
137
+ - Configurable per request or globally
138
+ - Handles transient errors automatically
139
+
140
+ **Ours:**
141
+ - ❌ No retry logic
142
+ - ❌ Users must implement retries themselves
143
+
144
+ #### 6. **Logging**
145
+
146
+ **ElevenLabs:**
147
+ - Configurable logging
148
+ - Request/response logging
149
+ - Sensitive data redaction
150
+
151
+ **Ours:**
152
+ - ❌ No logging
153
+ - ❌ Hard to debug API issues
154
+
155
+ #### 7. **Raw Response Access**
156
+
157
+ **ElevenLabs:**
158
+ ```typescript
159
+ const response = await client.textToSpeech.convert(...);
160
+ // Access raw response if needed
161
+ response.rawResponse.headers
162
+ response.rawResponse.status
163
+ ```
164
+
165
+ **Ours:**
166
+ ```typescript
167
+ // No access to raw response
168
+ const response = await client.messages.sendText(...);
169
+ // Can't inspect headers, status, etc.
170
+ ```
171
+
172
+ **Missing:**
173
+ - ❌ Raw response access for advanced use cases
174
+ - ❌ Response metadata (headers, status)
175
+
176
+ ## 🚨 Critical Missing Pieces
177
+
178
+ ### Priority 1: Error Handling (Critical)
179
+
180
+ **Current Issues:**
181
+ 1. Generic `Error` thrown from HTTP client
182
+ 2. No structured error parsing
183
+ 3. Can't distinguish error types programmatically
184
+ 4. No error body preservation
185
+
186
+ **What to Add:**
187
+ 1. Parse WhatsApp API error responses properly
188
+ 2. Create typed error classes (`WhatsAppAPIError`, `WhatsAppRateLimitError`, etc.)
189
+ 3. Preserve error body and status code
190
+ 4. Handle non-JSON error responses
191
+
192
+ ### Priority 2: Request Validation Error Handling (High)
193
+
194
+ **Current Issues:**
195
+ 1. Zod errors thrown directly (not our custom type)
196
+ 2. Inconsistent error handling across methods
197
+
198
+ **What to Add:**
199
+ 1. Use `transformZodError()` in all method functions
200
+ 2. Consistent `WhatsAppValidationError` for all validation failures
201
+
202
+ ### Priority 3: Response Validation (High)
203
+
204
+ **Current Issues:**
205
+ 1. No runtime validation of API responses
206
+ 2. Type assertions only (can fail silently)
207
+ 3. API changes might break code without warning
208
+
209
+ **What to Add:**
210
+ 1. Validate responses with Zod schemas
211
+ 2. Throw `WhatsAppValidationError` if response doesn't match schema
212
+ 3. Catch API changes early
213
+
214
+ ### Priority 4: Better Error Messages (Medium)
215
+
216
+ **Current Issues:**
217
+ 1. Generic error messages
218
+ 2. No context about what failed
219
+
220
+ **What to Add:**
221
+ 1. Include method name in errors
222
+ 2. Include request details (sanitized)
223
+ 3. Better error messages for common failures
224
+
225
+ ## 📋 Recommended Implementation Plan
226
+
227
+ ### Step 1: Improve HTTP Client Error Handling
228
+
229
+ ```typescript
230
+ // src/client/HttpClient.ts
231
+ async post<T>(path: string, body: unknown): Promise<T> {
232
+ const response = await fetch(...);
233
+
234
+ if (!response.ok) {
235
+ const errorBody = await this.parseErrorResponse(response);
236
+ throw this.createAPIError(response.status, errorBody, response);
237
+ }
238
+
239
+ return response.json();
240
+ }
241
+
242
+ private async parseErrorResponse(response: Response): Promise<unknown> {
243
+ const contentType = response.headers.get("content-type")?.toLowerCase();
244
+
245
+ if (contentType?.includes("application/json")) {
246
+ try {
247
+ return await response.json();
248
+ } catch {
249
+ return { message: response.statusText };
250
+ }
251
+ }
252
+
253
+ return { message: await response.text() || response.statusText };
254
+ }
255
+
256
+ private createAPIError(statusCode: number, body: unknown, response: Response): WhatsAppAPIError {
257
+ // Parse WhatsApp error structure
258
+ const error = this.parseWhatsAppError(body);
259
+
260
+ // Handle specific status codes
261
+ if (statusCode === 429) {
262
+ return new WhatsAppRateLimitError(
263
+ error.message,
264
+ error.retryAfter,
265
+ statusCode,
266
+ body,
267
+ response
268
+ );
269
+ }
270
+
271
+ return new WhatsAppAPIError(
272
+ error.code || statusCode,
273
+ error.type || "api_error",
274
+ error.message || `API request failed with status ${statusCode}`,
275
+ statusCode,
276
+ body,
277
+ response
278
+ );
279
+ }
280
+ ```
281
+
282
+ ### Step 2: Add Request Validation Error Handling
283
+
284
+ ```typescript
285
+ // src/services/messages/methods/send-text.ts
286
+ export async function sendText(...) {
287
+ try {
288
+ const validated = sendTextRequestSchema.parse(request);
289
+ } catch (error) {
290
+ if (error instanceof ZodError) {
291
+ throw transformZodError(error);
292
+ }
293
+ throw error;
294
+ }
295
+ // ... rest of function
296
+ }
297
+ ```
298
+
299
+ ### Step 3: Add Response Validation
300
+
301
+ ```typescript
302
+ // src/services/messages/methods/send-text.ts
303
+ export async function sendText(...) {
304
+ // ... make request
305
+ const response = await client.post<MessageResponse>(...);
306
+
307
+ // Validate response
308
+ try {
309
+ return messageResponseSchema.parse(response);
310
+ } catch (error) {
311
+ if (error instanceof ZodError) {
312
+ throw new WhatsAppValidationError(
313
+ "Invalid response from WhatsApp API",
314
+ undefined,
315
+ transformZodError(error).issues
316
+ );
317
+ }
318
+ throw error;
319
+ }
320
+ }
321
+ ```
322
+
323
+ ### Step 4: Improve Error Context
324
+
325
+ ```typescript
326
+ // Wrap errors with context
327
+ try {
328
+ return await client.post(...);
329
+ } catch (error) {
330
+ if (error instanceof WhatsAppAPIError) {
331
+ error.message = `Failed to send text message: ${error.message}`;
332
+ }
333
+ throw error;
334
+ }
335
+ ```
336
+
337
+ ## 🎯 What Makes a Great SDK (Based on ElevenLabs)
338
+
339
+ 1. **Predictable Errors**: Users know what errors to expect
340
+ 2. **Structured Error Data**: Error codes, types, details accessible
341
+ 3. **Response Validation**: Catch API changes early
342
+ 4. **Request Validation**: Clear errors for invalid input
343
+ 5. **Error Context**: Know what operation failed
344
+ 6. **Raw Response Access**: For advanced debugging
345
+ 7. **Retry Logic**: Handle transient errors automatically
346
+ 8. **Logging**: Debug issues easily
347
+
348
+ ## ✅ What We're Doing Right
349
+
350
+ 1. **Schema-First**: Zod schemas are excellent
351
+ 2. **Type Safety**: Full TypeScript coverage
352
+ 3. **Clean API**: Intuitive method names
353
+ 4. **Separation of Concerns**: Well-organized code
354
+ 5. **Request Transformation**: User-friendly API
355
+
356
+ ## 🚀 Next Steps
357
+
358
+ 1. **Implement proper error handling in HttpClient** (Priority 1)
359
+ 2. **Add request validation error handling** (Priority 2)
360
+ 3. **Add response validation** (Priority 2)
361
+ 4. **Test with real API** to identify edge cases
362
+ 5. **Add retry logic** (Priority 3)
363
+ 6. **Add logging** (Priority 3)
364
+
365
+ ---
366
+
367
+ **Summary**: Our foundation is solid, but we need to improve error handling to match industry standards. The main gaps are in HTTP client error parsing, response validation, and consistent error types throughout the codebase.
368
+
@@ -0,0 +1,78 @@
1
+ # Naming Decision: User-Friendly vs API Names
2
+
3
+ ## Current Approach (User-Friendly Names)
4
+
5
+ **Request:**
6
+ ```typescript
7
+ {
8
+ imageUrl: "https://...", // User-friendly
9
+ imageId: "123", // User-friendly
10
+ caption: "..."
11
+ }
12
+ ```
13
+
14
+ **API Payload:**
15
+ ```typescript
16
+ {
17
+ image: {
18
+ link: "...", // API name
19
+ id: "...", // API name
20
+ caption: "..."
21
+ }
22
+ }
23
+ ```
24
+
25
+ **Mapping Required:** ✅ Yes (bloated code)
26
+
27
+ ## Alternative: Use API Names Directly
28
+
29
+ **Request:**
30
+ ```typescript
31
+ {
32
+ link: "https://...", // API name directly
33
+ id: "123", // API name directly
34
+ caption: "..."
35
+ }
36
+ ```
37
+
38
+ **API Payload:**
39
+ ```typescript
40
+ {
41
+ image: {
42
+ link: "...", // Same!
43
+ id: "...", // Same!
44
+ caption: "..."
45
+ }
46
+ }
47
+ ```
48
+
49
+ **Mapping Required:** ❌ No (simpler code)
50
+
51
+ ## Trade-offs
52
+
53
+ ### User-Friendly Names (Current)
54
+ **Pros:**
55
+ - ✅ `imageUrl` is clearer than `link`
56
+ - ✅ `imageId` is clearer than `id`
57
+ - ✅ Better developer experience
58
+
59
+ **Cons:**
60
+ - ❌ Requires mapping code (bloated)
61
+ - ❌ Doesn't match API exactly
62
+ - ❌ More code to maintain
63
+
64
+ ### API Names Directly
65
+ **Pros:**
66
+ - ✅ No mapping needed (simpler)
67
+ - ✅ Matches API exactly
68
+ - ✅ Less code
69
+ - ✅ Easier to maintain
70
+
71
+ **Cons:**
72
+ - ⚠️ `link` is generic (could be confusing)
73
+ - ⚠️ `id` is generic (could be confusing)
74
+
75
+ ## Recommendation
76
+
77
+ **Use API names directly** - The simplicity and exact API matching outweigh the slight loss in clarity. Users can see the API docs and understand `link` and `id` in context.
78
+