wuzapi 1.2.0 → 1.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/README.md CHANGED
@@ -26,6 +26,8 @@ yarn add wuzapi
26
26
 
27
27
  ## Quick Start
28
28
 
29
+ ### Traditional Usage (Global Token)
30
+
29
31
  ```typescript
30
32
  import WuzapiClient from "wuzapi";
31
33
 
@@ -52,6 +54,48 @@ console.log("Connected:", status.Connected);
52
54
  console.log("Logged In:", status.LoggedIn);
53
55
  ```
54
56
 
57
+ ### Flexible Token Usage
58
+
59
+ ```typescript
60
+ import WuzapiClient from "wuzapi";
61
+
62
+ // Create client without token (or with admin token)
63
+ const client = new WuzapiClient({
64
+ apiUrl: "http://localhost:8080",
65
+ token: "admin-token", // Optional: admin token as default
66
+ });
67
+
68
+ // Use specific user token for operations
69
+ const userToken = "user-specific-token";
70
+
71
+ // Connect with user token
72
+ await client.session.connect(
73
+ {
74
+ Subscribe: ["Message", "ReadReceipt"],
75
+ Immediate: false,
76
+ },
77
+ { token: userToken }
78
+ );
79
+
80
+ // Send message with user token
81
+ await client.chat.sendText(
82
+ {
83
+ Phone: "5491155554444",
84
+ Body: "Hello from user-specific token!",
85
+ },
86
+ { token: userToken }
87
+ );
88
+
89
+ // Admin operations with admin token (uses default)
90
+ const users = await client.admin.listUsers();
91
+
92
+ // Or explicitly use admin token
93
+ const newUser = await client.admin.addUser(
94
+ { name: "John", token: "new-user-token" },
95
+ { token: "admin-token" }
96
+ );
97
+ ```
98
+
55
99
  ## Configuration
56
100
 
57
101
  The client requires a configuration object with the following properties:
@@ -59,8 +103,45 @@ The client requires a configuration object with the following properties:
59
103
  ```typescript
60
104
  interface WuzapiConfig {
61
105
  apiUrl: string; // The WuzAPI server URL
62
- token: string; // Your user authentication token
106
+ token?: string; // Your user authentication token (optional)
63
107
  }
108
+
109
+ interface RequestOptions {
110
+ token?: string; // Token override for specific requests
111
+ }
112
+ ```
113
+
114
+ ### Authentication Options
115
+
116
+ You have two ways to handle authentication:
117
+
118
+ 1. **Global Token**: Set a default token in the client configuration
119
+ 2. **Per-Request Token**: Override the token for specific API calls
120
+
121
+ ```typescript
122
+ // Option 1: Global token (traditional usage)
123
+ const client = new WuzapiClient({
124
+ apiUrl: "http://localhost:8080",
125
+ token: "your-default-token",
126
+ });
127
+
128
+ // Option 2: No global token, specify per request
129
+ const client = new WuzapiClient({
130
+ apiUrl: "http://localhost:8080",
131
+ // token is optional
132
+ });
133
+
134
+ // Option 3: Global token with per-request overrides
135
+ const client = new WuzapiClient({
136
+ apiUrl: "http://localhost:8080",
137
+ token: "admin-token", // Default admin token
138
+ });
139
+
140
+ // Use different token for specific operations
141
+ await client.chat.sendText(
142
+ { Phone: "5491155554444", Body: "Hello!" },
143
+ { token: "user-specific-token" }
144
+ );
64
145
  ```
65
146
 
66
147
  ## API Modules
@@ -109,13 +190,22 @@ await client.session.configureS3({
109
190
  Send messages and manage chat interactions.
110
191
 
111
192
  ```typescript
112
- // Send text message
193
+ // Send text message (using global token)
113
194
  await client.chat.sendText({
114
195
  Phone: "5491155554444",
115
196
  Body: "Hello World!",
116
197
  Id: "optional-message-id",
117
198
  });
118
199
 
200
+ // Send with specific token override
201
+ await client.chat.sendText(
202
+ {
203
+ Phone: "5491155554444",
204
+ Body: "Hello with custom token!",
205
+ },
206
+ { token: "user-specific-token" }
207
+ );
208
+
119
209
  // Reply to a message
120
210
  await client.chat.sendText({
121
211
  Phone: "5491155554444",
@@ -247,7 +337,7 @@ await client.group.removePhoto("120362023605733675@g.us");
247
337
  Manage users (requires admin token).
248
338
 
249
339
  ```typescript
250
- // Create admin client
340
+ // Option 1: Create dedicated admin client
251
341
  const adminClient = new WuzapiClient({
252
342
  apiUrl: "http://localhost:8080",
253
343
  token: "your-admin-token",
@@ -256,31 +346,43 @@ const adminClient = new WuzapiClient({
256
346
  // List all users
257
347
  const users = await adminClient.admin.listUsers();
258
348
 
349
+ // Option 2: Use shared client with token override
350
+ const client = new WuzapiClient({
351
+ apiUrl: "http://localhost:8080",
352
+ // No default token or user token as default
353
+ });
354
+
355
+ // Admin operations with explicit admin token
356
+ const users = await client.admin.listUsers({ token: "admin-token" });
357
+
259
358
  // Add new user
260
- const newUser = await adminClient.admin.addUser({
261
- name: "John Doe",
262
- token: "user-token-123",
263
- webhook: "https://example.com/webhook",
264
- events: "Message,ReadReceipt",
265
- proxyConfig: {
266
- enabled: true,
267
- proxyURL: "socks5://user:pass@proxy:port",
268
- },
269
- s3Config: {
270
- enabled: true,
271
- endpoint: "https://s3.amazonaws.com",
272
- region: "us-east-1",
273
- bucket: "user-media-bucket",
274
- accessKey: "AKIA...",
275
- secretKey: "secret...",
276
- pathStyle: false,
277
- mediaDelivery: "both",
278
- retentionDays: 30,
359
+ const newUser = await client.admin.addUser(
360
+ {
361
+ name: "John Doe",
362
+ token: "user-token-123",
363
+ webhook: "https://example.com/webhook",
364
+ events: "Message,ReadReceipt",
365
+ proxyConfig: {
366
+ enabled: true,
367
+ proxyURL: "socks5://user:pass@proxy:port",
368
+ },
369
+ s3Config: {
370
+ enabled: true,
371
+ endpoint: "https://s3.amazonaws.com",
372
+ region: "us-east-1",
373
+ bucket: "user-media-bucket",
374
+ accessKey: "AKIA...",
375
+ secretKey: "secret...",
376
+ pathStyle: false,
377
+ mediaDelivery: "both",
378
+ retentionDays: 30,
379
+ },
279
380
  },
280
- });
381
+ { token: "admin-token" }
382
+ );
281
383
 
282
384
  // Delete user
283
- await adminClient.admin.deleteUser(2);
385
+ await client.admin.deleteUser(2, { token: "admin-token" });
284
386
  ```
285
387
 
286
388
  ### Webhook Module
@@ -301,11 +403,128 @@ console.log("Subscribed events:", webhookConfig.subscribe);
301
403
 
302
404
  WuzAPI sends real-time events to your webhook endpoint. Here's how to handle different types of events:
303
405
 
406
+ ## Webhook Payload Structure
407
+
408
+ When S3 is enabled, webhook payloads will include S3 information based on the `media_delivery` setting configured in your WuzAPI instance.
409
+
410
+ ### Standard Event Payload
411
+
412
+ ```json
413
+ {
414
+ "event": {
415
+ "Info": {
416
+ "ID": "3EB06F9067F80BAB89FF",
417
+ "Type": "text",
418
+ "PushName": "John Doe",
419
+ "Timestamp": "2024-12-25T10:30:00Z",
420
+ "Source": {
421
+ "Chat": "5491155553934@s.whatsapp.net",
422
+ "Sender": "5491155553934@s.whatsapp.net",
423
+ "IsFromMe": false,
424
+ "IsGroup": false
425
+ }
426
+ },
427
+ "Message": {
428
+ "conversation": "Hello, this is a test message!"
429
+ },
430
+ "IsEphemeral": false,
431
+ "IsViewOnce": false,
432
+ "IsDocumentWithCaption": false,
433
+ "IsEdit": false
434
+ }
435
+ }
436
+ ```
437
+
438
+ ### S3 Only (`media_delivery: "s3"`)
439
+
440
+ ```json
441
+ {
442
+ "event": {
443
+ "Info": {
444
+ "ID": "3EB06F9067F80BAB89FF",
445
+ "Type": "image",
446
+ "PushName": "John Doe",
447
+ "Timestamp": "2024-12-25T10:30:00Z",
448
+ "Source": {
449
+ "Chat": "5491155553934@s.whatsapp.net",
450
+ "Sender": "5491155553934@s.whatsapp.net",
451
+ "IsFromMe": false,
452
+ "IsGroup": false
453
+ }
454
+ },
455
+ "Message": {
456
+ "imageMessage": {
457
+ "caption": "Check out this photo!",
458
+ "mimetype": "image/jpeg",
459
+ "width": 1920,
460
+ "height": 1080,
461
+ "fileLength": 245632
462
+ }
463
+ },
464
+ "IsEphemeral": false,
465
+ "IsViewOnce": false
466
+ },
467
+ "s3": {
468
+ "url": "https://my-bucket.s3.us-east-1.amazonaws.com/users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
469
+ "key": "users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
470
+ "bucket": "my-bucket",
471
+ "size": 245632,
472
+ "mimeType": "image/jpeg",
473
+ "fileName": "3EB06F9067F80BAB89FF.jpg"
474
+ }
475
+ }
476
+ ```
477
+
478
+ ### Both S3 and Base64 (`media_delivery: "both"`)
479
+
480
+ ```json
481
+ {
482
+ "event": {
483
+ "Info": { "..." },
484
+ "Message": {
485
+ "imageMessage": {
486
+ "caption": "Check out this photo!",
487
+ "mimetype": "image/jpeg",
488
+ "width": 1920,
489
+ "height": 1080
490
+ }
491
+ }
492
+ },
493
+ "base64": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
494
+ "mimeType": "image/jpeg",
495
+ "fileName": "3EB06F9067F80BAB89FF.jpg",
496
+ "s3": {
497
+ "url": "https://my-bucket.s3.us-east-1.amazonaws.com/users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
498
+ "key": "users/abc123/inbox/5491155553934/2024/12/25/images/3EB06F9067F80BAB89FF.jpg",
499
+ "bucket": "my-bucket",
500
+ "size": 245632,
501
+ "mimeType": "image/jpeg",
502
+ "fileName": "3EB06F9067F80BAB89FF.jpg"
503
+ }
504
+ }
505
+ ```
506
+
507
+ ### Base64 Only (`media_delivery: "base64"`)
508
+
509
+ ```json
510
+ {
511
+ "event": { "..." },
512
+ "base64": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=",
513
+ "mimeType": "image/jpeg",
514
+ "fileName": "3EB06F9067F80BAB89FF.jpg"
515
+ }
516
+ ```
517
+
304
518
  ### Basic Webhook Setup
305
519
 
306
520
  ```typescript
307
521
  import express from "express";
308
- import WuzapiClient from "wuzapi";
522
+ import WuzapiClient, {
523
+ EventType,
524
+ hasS3Media,
525
+ hasBase64Media,
526
+ WebhookPayload,
527
+ } from "wuzapi";
309
528
 
310
529
  const app = express();
311
530
  app.use(express.json());
@@ -315,18 +534,43 @@ const client = new WuzapiClient({
315
534
  token: "your-token",
316
535
  });
317
536
 
318
- // Webhook endpoint
537
+ // Webhook endpoint with S3 media support
319
538
  app.post("/webhook", async (req, res) => {
320
539
  try {
321
- const event = req.body;
322
-
323
- // Handle different event types
324
- if (event.Message) {
325
- await handleMessage(event);
326
- } else if (event.MessageIDs && event.Type) {
327
- await handleReceipt(event);
328
- } else if (event.From && typeof event.Unavailable !== "undefined") {
329
- await handlePresence(event);
540
+ const webhookPayload: WebhookPayload = req.body;
541
+
542
+ // Handle S3 media information if present
543
+ if (hasS3Media(webhookPayload)) {
544
+ console.log("☁️ S3 Media:", {
545
+ url: webhookPayload.s3.url,
546
+ bucket: webhookPayload.s3.bucket,
547
+ size: webhookPayload.s3.size,
548
+ type: webhookPayload.s3.mimeType,
549
+ });
550
+ }
551
+
552
+ if (hasBase64Media(webhookPayload)) {
553
+ console.log("📦 Base64 media included");
554
+ // Process base64 media: webhookPayload.base64
555
+ }
556
+
557
+ // Extract the actual event data
558
+ const event = webhookPayload.event || webhookPayload;
559
+ const eventType = detectEventType(event);
560
+
561
+ // Handle different event types using EventType enum
562
+ switch (eventType) {
563
+ case EventType.MESSAGE:
564
+ await handleMessage(event);
565
+ break;
566
+ case EventType.RECEIPT:
567
+ await handleReceipt(event);
568
+ break;
569
+ case EventType.PRESENCE:
570
+ await handlePresence(event);
571
+ break;
572
+ default:
573
+ console.log(`Unhandled event type: ${eventType}`);
330
574
  }
331
575
 
332
576
  res.status(200).json({ success: true });
@@ -336,6 +580,18 @@ app.post("/webhook", async (req, res) => {
336
580
  }
337
581
  });
338
582
 
583
+ // Helper function to detect event type
584
+ function detectEventType(event: any): EventType | string {
585
+ if (event.Message && event.Info) return EventType.MESSAGE;
586
+ if (event.MessageIDs && event.Type) return EventType.RECEIPT;
587
+ if (event.From && typeof event.Unavailable !== "undefined")
588
+ return EventType.PRESENCE;
589
+ if (event.State && event.MessageSource) return EventType.CHAT_PRESENCE;
590
+ if (event.Codes) return EventType.QR;
591
+ // Add more detection logic as needed
592
+ return "Unknown";
593
+ }
594
+
339
595
  app.listen(3000, () => {
340
596
  console.log("Webhook server running on port 3000");
341
597
  });
@@ -823,23 +1079,23 @@ app.get("/health", (req, res) => {
823
1079
  // Initialize
824
1080
  async function initialize() {
825
1081
  try {
826
- // Connect to WhatsApp with all events
1082
+ // Connect to WhatsApp with all events using EventType enum
827
1083
  await client.session.connect({
828
1084
  Subscribe: [
829
- "Message",
830
- "ReadReceipt",
831
- "Presence",
832
- "ChatPresence",
833
- "GroupInfo",
834
- "Contact",
835
- "PushName",
836
- "Picture",
837
- "QR",
838
- "Connected",
839
- "Disconnected",
840
- "LoggedOut",
841
- "UndecryptableMessage",
842
- "StreamError",
1085
+ EventType.MESSAGE,
1086
+ EventType.RECEIPT,
1087
+ EventType.PRESENCE,
1088
+ EventType.CHAT_PRESENCE,
1089
+ EventType.GROUP_INFO,
1090
+ EventType.CONTACT,
1091
+ EventType.PUSH_NAME,
1092
+ EventType.PICTURE,
1093
+ EventType.QR,
1094
+ EventType.CONNECTED,
1095
+ EventType.DISCONNECTED,
1096
+ EventType.LOGGED_OUT,
1097
+ EventType.UNDECRYPTABLE_MESSAGE,
1098
+ EventType.STREAM_ERROR,
843
1099
  ],
844
1100
  Immediate: false,
845
1101
  });
@@ -868,6 +1124,9 @@ All webhook events are fully typed. Import the specific event types you need:
868
1124
 
869
1125
  ```typescript
870
1126
  import {
1127
+ // Event type enum for all possible events
1128
+ EventType,
1129
+
871
1130
  // Core message types
872
1131
  Message,
873
1132
  MessageEvent,
@@ -887,6 +1146,16 @@ import {
887
1146
  PollCreationMessage,
888
1147
  ReactionMessage,
889
1148
 
1149
+ // Webhook payload types
1150
+ WebhookPayload,
1151
+ S3MediaInfo,
1152
+ S3OnlyWebhookPayload,
1153
+ Base64OnlyWebhookPayload,
1154
+ BothMediaWebhookPayload,
1155
+ hasS3Media,
1156
+ hasBase64Media,
1157
+ hasBothMedia,
1158
+
890
1159
  // Event types
891
1160
  Receipt,
892
1161
  Presence,
@@ -904,33 +1173,64 @@ import {
904
1173
  WhatsAppEvent,
905
1174
  } from "wuzapi";
906
1175
 
907
- // Type-safe event handling with full message structure
908
- async function handleTypedMessage(event: MessageEvent) {
909
- const messageContent = getMessageContent(event.Message);
1176
+ // Type-safe event handling with EventType enum
1177
+ async function handleTypedWebhook(webhookPayload: WebhookPayload) {
1178
+ const event = webhookPayload.event;
910
1179
 
911
- if (messageContent?.type === "image") {
912
- // TypeScript knows this is an ImageMessage
913
- const imageMsg: ImageMessage = messageContent.content;
914
- console.log(`Image dimensions: ${imageMsg.width}x${imageMsg.height}`);
915
- console.log(`File size: ${imageMsg.fileLength} bytes`);
1180
+ // Type-safe event detection using EventType enum
1181
+ const eventType = detectEventType(event);
916
1182
 
917
- // Full type safety and autocompletion
918
- if (imageMsg.caption) {
919
- console.log(`Caption: ${imageMsg.caption}`);
920
- }
921
- }
1183
+ switch (eventType) {
1184
+ case EventType.MESSAGE:
1185
+ const messageEvent = event as MessageEvent;
1186
+ const messageContent = getMessageContent(messageEvent.Message);
922
1187
 
923
- if (messageContent?.type === "buttons") {
924
- // TypeScript knows this is a ButtonsMessage
925
- const buttonsMsg: ButtonsMessage = messageContent.content;
926
- console.log(`Button message: ${buttonsMsg.contentText}`);
1188
+ if (messageContent?.type === "image") {
1189
+ // TypeScript knows this is an ImageMessage
1190
+ const imageMsg: ImageMessage = messageContent.content;
1191
+ console.log(`Image dimensions: ${imageMsg.width}x${imageMsg.height}`);
927
1192
 
928
- buttonsMsg.buttons?.forEach((button, index) => {
929
- console.log(`Button ${index + 1}: ${button.buttonText?.displayText}`);
930
- });
1193
+ // Handle S3 media if available
1194
+ if (hasS3Media(webhookPayload)) {
1195
+ console.log(`S3 URL: ${webhookPayload.s3.url}`);
1196
+ // Download from S3 or process as needed
1197
+ }
1198
+
1199
+ // Handle Base64 media if available
1200
+ if (hasBase64Media(webhookPayload)) {
1201
+ console.log("Processing base64 image data");
1202
+ // Process base64 data: webhookPayload.base64
1203
+ }
1204
+ }
1205
+ break;
1206
+
1207
+ case EventType.RECEIPT:
1208
+ const receiptEvent = event as Receipt;
1209
+ console.log(`Receipt type: ${receiptEvent.Type}`);
1210
+ break;
1211
+
1212
+ case EventType.PRESENCE:
1213
+ const presenceEvent = event as Presence;
1214
+ console.log(`User ${presenceEvent.From.User} presence update`);
1215
+ break;
1216
+
1217
+ default:
1218
+ console.log(`Unhandled event: ${eventType}`);
931
1219
  }
932
1220
  }
933
1221
 
1222
+ // Complete webhook server with S3 support
1223
+ app.post("/webhook", async (req, res) => {
1224
+ try {
1225
+ const payload: WebhookPayload = req.body;
1226
+ await handleTypedWebhook(payload);
1227
+ res.status(200).json({ success: true });
1228
+ } catch (error) {
1229
+ console.error("Webhook error:", error);
1230
+ res.status(500).json({ error: error.message });
1231
+ }
1232
+ });
1233
+
934
1234
  // Helper function to handle all message types generically
935
1235
  function processMessageContent(content: MessageContent) {
936
1236
  switch (content.type) {
@@ -1037,6 +1337,42 @@ try {
1037
1337
  }
1038
1338
  ```
1039
1339
 
1340
+ ### Token Authentication Errors
1341
+
1342
+ When no token is provided (either globally or per-request), the library will throw a specific error:
1343
+
1344
+ ```typescript
1345
+ import { WuzapiError } from "wuzapi";
1346
+
1347
+ // Client without global token
1348
+ const client = new WuzapiClient({
1349
+ apiUrl: "http://localhost:8080",
1350
+ // No token provided
1351
+ });
1352
+
1353
+ try {
1354
+ // This will fail - no token provided
1355
+ await client.chat.sendText({
1356
+ Phone: "5491155554444",
1357
+ Body: "This will fail",
1358
+ });
1359
+ } catch (error) {
1360
+ if (error instanceof WuzapiError && error.code === 401) {
1361
+ console.error("Authentication required:", error.message);
1362
+ // "No authentication token provided. Either set a token in the client config or provide one in the request options."
1363
+ }
1364
+ }
1365
+
1366
+ // Fix by providing token
1367
+ await client.chat.sendText(
1368
+ {
1369
+ Phone: "5491155554444",
1370
+ Body: "Now it works!",
1371
+ },
1372
+ { token: "your-token" }
1373
+ );
1374
+ ```
1375
+
1040
1376
  ### Error Types
1041
1377
 
1042
1378
  - **Network Errors**: Connection issues, timeouts
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AxiosInstance } from 'axios';
2
- import { WuzapiConfig } from './types/common.js';
2
+ import { WuzapiConfig, RequestOptions } from './types/common.js';
3
3
  export declare class WuzapiError extends Error {
4
4
  code: number;
5
5
  details?: unknown;
@@ -9,8 +9,13 @@ export declare class BaseClient {
9
9
  protected axios: AxiosInstance;
10
10
  protected config: WuzapiConfig;
11
11
  constructor(config: WuzapiConfig);
12
- protected request<T>(method: "GET" | "POST" | "DELETE", endpoint: string, data?: unknown): Promise<T>;
13
- protected get<T>(endpoint: string): Promise<T>;
14
- protected post<T>(endpoint: string, data?: unknown): Promise<T>;
15
- protected delete<T>(endpoint: string): Promise<T>;
12
+ /**
13
+ * Resolve the token from request options or instance config
14
+ * Throws an error if no token is available
15
+ */
16
+ private resolveToken;
17
+ protected request<T>(method: "GET" | "POST" | "DELETE", endpoint: string, data?: unknown, options?: RequestOptions): Promise<T>;
18
+ protected get<T>(endpoint: string, options?: RequestOptions): Promise<T>;
19
+ protected post<T>(endpoint: string, data?: unknown, options?: RequestOptions): Promise<T>;
20
+ protected delete<T>(endpoint: string, options?: RequestOptions): Promise<T>;
16
21
  }