@ethora/sdk-backend 25.12.20 → 26.2.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 CHANGED
@@ -27,6 +27,15 @@ This guide will walk you through integrating the Ethora SDK into your existing N
27
27
  - `ETHORA_CHAT_APP_SECRET`
28
28
  - `ETHORA_CHAT_BOT_JID` (optional, for chatbot features)
29
29
 
30
+ ## API Documentation (Swagger)
31
+
32
+ Ethora exposes Swagger UI from every running backend instance at:
33
+
34
+ - **Hosted (Ethora main)**: `https://api.ethoradev.com/api-docs/`
35
+ - **Enterprise/self-hosted**: `https://api.<your-domain>/api-docs/`
36
+
37
+ If you are running a separate staging instance, the same pattern applies (e.g. `https://api.asterotoken.com/api-docs/`).
38
+
30
39
  ## Installation
31
40
 
32
41
  ### Step 1: Install the Package
@@ -70,7 +79,7 @@ npm install dotenv
70
79
  In your main application file (e.g., `app.js`, `server.js`, or `index.ts`):
71
80
 
72
81
  ```typescript
73
- import dotenv from "dotenv";
82
+ import dotenv from 'dotenv';
74
83
 
75
84
  // Load environment variables
76
85
  dotenv.config();
@@ -81,7 +90,7 @@ dotenv.config();
81
90
  ### Step 1: Import the SDK
82
91
 
83
92
  ```typescript
84
- import { getEthoraSDKService } from "@ethora/sdk-backend";
93
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
85
94
  ```
86
95
 
87
96
  ### Step 2: Initialize the Service
@@ -92,7 +101,7 @@ You can initialize the service in several ways:
92
101
 
93
102
  ```typescript
94
103
  // services/chatService.ts
95
- import { getEthoraSDKService } from "@ethora/sdk-backend";
104
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
96
105
 
97
106
  // Get the singleton instance
98
107
  const chatService = getEthoraSDKService();
@@ -104,7 +113,7 @@ export default chatService;
104
113
 
105
114
  ```typescript
106
115
  // In your route handler or service
107
- import { getEthoraSDKService } from "@ethora/sdk-backend";
116
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
108
117
 
109
118
  const chatService = getEthoraSDKService();
110
119
  ```
@@ -113,8 +122,8 @@ const chatService = getEthoraSDKService();
113
122
 
114
123
  ```typescript
115
124
  // chat.service.ts
116
- import { Injectable } from "@nestjs/common";
117
- import { getEthoraSDKService } from "@ethora/sdk-backend";
125
+ import { Injectable } from '@nestjs/common';
126
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
118
127
 
119
128
  @Injectable()
120
129
  export class ChatService {
@@ -130,16 +139,16 @@ export class ChatService {
130
139
 
131
140
  ```typescript
132
141
  // routes/chat.ts
133
- import express, { Request, Response } from "express";
134
- import { getEthoraSDKService } from "@ethora/sdk-backend";
135
- import axios from "axios";
142
+ import express, { Request, Response } from 'express';
143
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
144
+ import axios from 'axios';
136
145
 
137
146
  const router = express.Router();
138
147
  const chatService = getEthoraSDKService();
139
148
 
140
149
  // Create a chat room for a workspace
141
150
  router.post(
142
- "/workspaces/:workspaceId/chat",
151
+ '/workspaces/:workspaceId/chat',
143
152
  async (req: Request, res: Response) => {
144
153
  try {
145
154
  const { workspaceId } = req.params;
@@ -148,7 +157,7 @@ router.post(
148
157
  const response = await chatService.createChatRoom(workspaceId, {
149
158
  title: roomData.title || `Chat Room ${workspaceId}`,
150
159
  uuid: workspaceId,
151
- type: roomData.type || "group",
160
+ type: roomData.type || 'group',
152
161
  ...roomData,
153
162
  });
154
163
 
@@ -156,18 +165,18 @@ router.post(
156
165
  } catch (error) {
157
166
  if (axios.isAxiosError(error)) {
158
167
  res.status(error.response?.status || 500).json({
159
- error: "Failed to create chat room",
168
+ error: 'Failed to create chat room',
160
169
  details: error.response?.data,
161
170
  });
162
171
  } else {
163
- res.status(500).json({ error: "Internal server error" });
172
+ res.status(500).json({ error: 'Internal server error' });
164
173
  }
165
174
  }
166
- }
175
+ },
167
176
  );
168
177
 
169
178
  // Create a user
170
- router.post("/users/:userId", async (req: Request, res: Response) => {
179
+ router.post('/users/:userId', async (req: Request, res: Response) => {
171
180
  try {
172
181
  const { userId } = req.params;
173
182
  const userData = req.body;
@@ -177,50 +186,50 @@ router.post("/users/:userId", async (req: Request, res: Response) => {
177
186
  } catch (error) {
178
187
  if (axios.isAxiosError(error)) {
179
188
  res.status(error.response?.status || 500).json({
180
- error: "Failed to create user",
189
+ error: 'Failed to create user',
181
190
  details: error.response?.data,
182
191
  });
183
192
  } else {
184
- res.status(500).json({ error: "Internal server error" });
193
+ res.status(500).json({ error: 'Internal server error' });
185
194
  }
186
195
  }
187
196
  });
188
197
 
189
198
  // Grant user access to chat room
190
199
  router.post(
191
- "/workspaces/:workspaceId/chat/users/:userId",
200
+ '/workspaces/:workspaceId/chat/users/:userId',
192
201
  async (req: Request, res: Response) => {
193
202
  try {
194
203
  const { workspaceId, userId } = req.params;
195
204
 
196
205
  await chatService.grantUserAccessToChatRoom(workspaceId, userId);
197
- res.json({ success: true, message: "Access granted" });
206
+ res.json({ success: true, message: 'Access granted' });
198
207
  } catch (error) {
199
208
  if (axios.isAxiosError(error)) {
200
209
  res.status(error.response?.status || 500).json({
201
- error: "Failed to grant access",
210
+ error: 'Failed to grant access',
202
211
  details: error.response?.data,
203
212
  });
204
213
  } else {
205
- res.status(500).json({ error: "Internal server error" });
214
+ res.status(500).json({ error: 'Internal server error' });
206
215
  }
207
216
  }
208
- }
217
+ },
209
218
  );
210
219
 
211
220
  // Generate client JWT token
212
- router.get("/users/:userId/chat-token", (req: Request, res: Response) => {
221
+ router.get('/users/:userId/chat-token', (req: Request, res: Response) => {
213
222
  try {
214
223
  const { userId } = req.params;
215
224
  const token = chatService.createChatUserJwtToken(userId);
216
225
  res.json({ token });
217
226
  } catch (error) {
218
- res.status(500).json({ error: "Failed to generate token" });
227
+ res.status(500).json({ error: 'Failed to generate token' });
219
228
  }
220
229
  });
221
230
 
222
231
  // Get users
223
- router.get("/users", async (req: Request, res: Response) => {
232
+ router.get('/users', async (req: Request, res: Response) => {
224
233
  try {
225
234
  const { chatName, xmppUsername } = req.query;
226
235
  const params: any = {};
@@ -228,32 +237,32 @@ router.get("/users", async (req: Request, res: Response) => {
228
237
  if (xmppUsername) params.xmppUsername = String(xmppUsername);
229
238
 
230
239
  const response = await chatService.getUsers(
231
- Object.keys(params).length > 0 ? params : undefined
240
+ Object.keys(params).length > 0 ? params : undefined,
232
241
  );
233
242
  res.json({ success: true, data: response });
234
243
  } catch (error) {
235
244
  if (axios.isAxiosError(error)) {
236
245
  res.status(error.response?.status || 500).json({
237
- error: "Failed to get users",
246
+ error: 'Failed to get users',
238
247
  details: error.response?.data,
239
248
  });
240
249
  } else {
241
- res.status(500).json({ error: "Internal server error" });
250
+ res.status(500).json({ error: 'Internal server error' });
242
251
  }
243
252
  }
244
253
  });
245
254
 
246
255
  // Update users (batch)
247
- router.patch("/users", async (req: Request, res: Response) => {
256
+ router.patch('/users', async (req: Request, res: Response) => {
248
257
  try {
249
258
  const { users } = req.body;
250
259
  if (!Array.isArray(users) || users.length === 0) {
251
- return res.status(400).json({ error: "users must be a non-empty array" });
260
+ return res.status(400).json({ error: 'users must be a non-empty array' });
252
261
  }
253
262
  if (users.length > 100) {
254
263
  return res
255
264
  .status(400)
256
- .json({ error: "Maximum 100 users allowed per request" });
265
+ .json({ error: 'Maximum 100 users allowed per request' });
257
266
  }
258
267
 
259
268
  const response = await chatService.updateUsers(users);
@@ -261,11 +270,11 @@ router.patch("/users", async (req: Request, res: Response) => {
261
270
  } catch (error) {
262
271
  if (axios.isAxiosError(error)) {
263
272
  res.status(error.response?.status || 500).json({
264
- error: "Failed to update users",
273
+ error: 'Failed to update users',
265
274
  details: error.response?.data,
266
275
  });
267
276
  } else {
268
- res.status(500).json({ error: "Internal server error" });
277
+ res.status(500).json({ error: 'Internal server error' });
269
278
  }
270
279
  }
271
280
  });
@@ -277,9 +286,9 @@ export default router;
277
286
 
278
287
  ```typescript
279
288
  // chat/chat.service.ts
280
- import { Injectable, HttpException, HttpStatus } from "@nestjs/common";
281
- import { getEthoraSDKService } from "@ethora/sdk-backend";
282
- import axios from "axios";
289
+ import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
290
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
291
+ import axios from 'axios';
283
292
 
284
293
  @Injectable()
285
294
  export class ChatService {
@@ -292,10 +301,10 @@ export class ChatService {
292
301
  if (axios.isAxiosError(error)) {
293
302
  throw new HttpException(
294
303
  {
295
- message: "Failed to create chat room",
304
+ message: 'Failed to create chat room',
296
305
  details: error.response?.data,
297
306
  },
298
- error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
307
+ error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
299
308
  );
300
309
  }
301
310
  throw error;
@@ -309,10 +318,10 @@ export class ChatService {
309
318
  if (axios.isAxiosError(error)) {
310
319
  throw new HttpException(
311
320
  {
312
- message: "Failed to create user",
321
+ message: 'Failed to create user',
313
322
  details: error.response?.data,
314
323
  },
315
- error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
324
+ error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
316
325
  );
317
326
  }
318
327
  throw error;
@@ -325,28 +334,28 @@ export class ChatService {
325
334
  }
326
335
 
327
336
  // chat/chat.controller.ts
328
- import { Controller, Post, Get, Param, Body } from "@nestjs/common";
329
- import { ChatService } from "./chat.service";
337
+ import { Controller, Post, Get, Param, Body } from '@nestjs/common';
338
+ import { ChatService } from './chat.service';
330
339
 
331
- @Controller("chat")
340
+ @Controller('chat')
332
341
  export class ChatController {
333
342
  constructor(private readonly chatService: ChatService) {}
334
343
 
335
- @Post("workspaces/:workspaceId/rooms")
344
+ @Post('workspaces/:workspaceId/rooms')
336
345
  async createChatRoom(
337
- @Param("workspaceId") workspaceId: string,
338
- @Body() roomData: any
346
+ @Param('workspaceId') workspaceId: string,
347
+ @Body() roomData: any,
339
348
  ) {
340
349
  return this.chatService.createChatRoom(workspaceId, roomData);
341
350
  }
342
351
 
343
- @Post("users/:userId")
344
- async createUser(@Param("userId") userId: string, @Body() userData: any) {
352
+ @Post('users/:userId')
353
+ async createUser(@Param('userId') userId: string, @Body() userData: any) {
345
354
  return this.chatService.createUser(userId, userData);
346
355
  }
347
356
 
348
- @Get("users/:userId/token")
349
- getClientToken(@Param("userId") userId: string) {
357
+ @Get('users/:userId/token')
358
+ getClientToken(@Param('userId') userId: string) {
350
359
  return { token: this.chatService.generateClientToken(userId) };
351
360
  }
352
361
  }
@@ -356,15 +365,15 @@ export class ChatController {
356
365
 
357
366
  ```typescript
358
367
  // routes/chat.ts
359
- import { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
360
- import { getEthoraSDKService } from "@ethora/sdk-backend";
368
+ import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
369
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
361
370
 
362
371
  const chatService = getEthoraSDKService();
363
372
 
364
373
  export async function chatRoutes(fastify: FastifyInstance) {
365
374
  // Create chat room
366
375
  fastify.post(
367
- "/workspaces/:workspaceId/chat",
376
+ '/workspaces/:workspaceId/chat',
368
377
  async (request: FastifyRequest, reply: FastifyReply) => {
369
378
  const { workspaceId } = request.params as { workspaceId: string };
370
379
  const roomData = request.body as any;
@@ -372,23 +381,23 @@ export async function chatRoutes(fastify: FastifyInstance) {
372
381
  try {
373
382
  const response = await chatService.createChatRoom(
374
383
  workspaceId,
375
- roomData
384
+ roomData,
376
385
  );
377
386
  return { success: true, data: response };
378
387
  } catch (error) {
379
- reply.code(500).send({ error: "Failed to create chat room" });
388
+ reply.code(500).send({ error: 'Failed to create chat room' });
380
389
  }
381
- }
390
+ },
382
391
  );
383
392
 
384
393
  // Generate client token
385
394
  fastify.get(
386
- "/users/:userId/chat-token",
395
+ '/users/:userId/chat-token',
387
396
  async (request: FastifyRequest, reply: FastifyReply) => {
388
397
  const { userId } = request.params as { userId: string };
389
398
  const token = chatService.createChatUserJwtToken(userId);
390
399
  return { token };
391
- }
400
+ },
392
401
  );
393
402
  }
394
403
  ```
@@ -403,7 +412,7 @@ When creating a new workspace, set up the chat room and initial users:
403
412
  async function setupWorkspaceChat(
404
413
  workspaceId: string,
405
414
  userIds: string[],
406
- adminUserId: string
415
+ adminUserId: string,
407
416
  ) {
408
417
  const chatService = getEthoraSDKService();
409
418
 
@@ -412,15 +421,15 @@ async function setupWorkspaceChat(
412
421
  await chatService.createChatRoom(workspaceId, {
413
422
  title: `Workspace ${workspaceId}`,
414
423
  uuid: workspaceId,
415
- type: "group",
424
+ type: 'group',
416
425
  });
417
426
 
418
427
  // 2. Create users (if they don't exist)
419
428
  for (const userId of userIds) {
420
429
  try {
421
430
  await chatService.createUser(userId, {
422
- firstName: "User",
423
- lastName: "Name",
431
+ firstName: 'User',
432
+ lastName: 'Name',
424
433
  });
425
434
  } catch (error) {
426
435
  // User might already exist, continue
@@ -435,12 +444,12 @@ async function setupWorkspaceChat(
435
444
  try {
436
445
  await chatService.grantChatbotAccessToChatRoom(workspaceId);
437
446
  } catch (error) {
438
- console.warn("Chatbot access not configured or failed");
447
+ console.warn('Chatbot access not configured or failed');
439
448
  }
440
449
 
441
450
  return { success: true };
442
451
  } catch (error) {
443
- console.error("Failed to setup workspace chat:", error);
452
+ console.error('Failed to setup workspace chat:', error);
444
453
  throw error;
445
454
  }
446
455
  }
@@ -453,7 +462,7 @@ When a new user joins your platform:
453
462
  ```typescript
454
463
  async function onboardNewUser(
455
464
  userId: string,
456
- userData: { firstName: string; lastName: string; email: string }
465
+ userData: { firstName: string; lastName: string; email: string },
457
466
  ) {
458
467
  const chatService = getEthoraSDKService();
459
468
 
@@ -474,7 +483,7 @@ async function onboardNewUser(
474
483
  chatToken: clientToken,
475
484
  };
476
485
  } catch (error) {
477
- console.error("Failed to onboard user:", error);
486
+ console.error('Failed to onboard user:', error);
478
487
  throw error;
479
488
  }
480
489
  }
@@ -501,7 +510,7 @@ async function addUserToWorkspace(workspaceId: string, userId: string) {
501
510
 
502
511
  return { success: true };
503
512
  } catch (error) {
504
- console.error("Failed to add user to workspace:", error);
513
+ console.error('Failed to add user to workspace:', error);
505
514
  throw error;
506
515
  }
507
516
  }
@@ -524,13 +533,13 @@ async function cleanupWorkspaceChat(workspaceId: string, userIds: string[]) {
524
533
  try {
525
534
  await chatService.deleteUsers(userIds);
526
535
  } catch (error) {
527
- console.warn("Some users might not exist:", error);
536
+ console.warn('Some users might not exist:', error);
528
537
  }
529
538
  }
530
539
 
531
540
  return { success: true };
532
541
  } catch (error) {
533
- console.error("Failed to cleanup workspace chat:", error);
542
+ console.error('Failed to cleanup workspace chat:', error);
534
543
  throw error;
535
544
  }
536
545
  }
@@ -551,17 +560,17 @@ async function getUsersExample() {
551
560
 
552
561
  // Get users by chat name (group chat)
553
562
  const groupChatUsers = await chatService.getUsers({
554
- chatName: "appId_workspaceId",
563
+ chatName: 'appId_workspaceId',
555
564
  });
556
565
 
557
566
  // Get users by chat name (1-on-1 chat)
558
567
  const oneOnOneUsers = await chatService.getUsers({
559
- chatName: "userA-userB",
568
+ chatName: 'userA-userB',
560
569
  });
561
570
 
562
571
  return { allUsers, groupChatUsers, oneOnOneUsers };
563
572
  } catch (error) {
564
- console.error("Failed to get users:", error);
573
+ console.error('Failed to get users:', error);
565
574
  throw error;
566
575
  }
567
576
  }
@@ -579,34 +588,34 @@ async function updateUsersExample() {
579
588
  // Update multiple users (1-100 users per request)
580
589
  const response = await chatService.updateUsers([
581
590
  {
582
- xmppUsername: "appId_user1",
583
- firstName: "John",
584
- lastName: "Doe",
585
- username: "johndoe",
586
- profileImage: "https://example.com/avatar1.jpg",
591
+ xmppUsername: 'appId_user1',
592
+ firstName: 'John',
593
+ lastName: 'Doe',
594
+ username: 'johndoe',
595
+ profileImage: 'https://example.com/avatar1.jpg',
587
596
  },
588
597
  {
589
- xmppUsername: "appId_user2",
590
- firstName: "Jane",
591
- lastName: "Smith",
592
- username: "janesmith",
598
+ xmppUsername: 'appId_user2',
599
+ firstName: 'Jane',
600
+ lastName: 'Smith',
601
+ username: 'janesmith',
593
602
  },
594
603
  ]);
595
604
 
596
605
  // Check results
597
606
  response.results?.forEach((result: any) => {
598
- if (result.status === "updated") {
607
+ if (result.status === 'updated') {
599
608
  console.log(`User ${result.xmppUsername} updated successfully`);
600
- } else if (result.status === "not-found") {
609
+ } else if (result.status === 'not-found') {
601
610
  console.warn(`User ${result.xmppUsername} not found`);
602
- } else if (result.status === "skipped") {
611
+ } else if (result.status === 'skipped') {
603
612
  console.log(`User ${result.xmppUsername} update skipped`);
604
613
  }
605
614
  });
606
615
 
607
616
  return response;
608
617
  } catch (error) {
609
- console.error("Failed to update users:", error);
618
+ console.error('Failed to update users:', error);
610
619
  throw error;
611
620
  }
612
621
  }
@@ -619,8 +628,8 @@ async function updateUsersExample() {
619
628
  The SDK uses Axios for HTTP requests, so errors are AxiosError instances:
620
629
 
621
630
  ```typescript
622
- import axios from "axios";
623
- import { getEthoraSDKService } from "@ethora/sdk-backend";
631
+ import axios from 'axios';
632
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
624
633
 
625
634
  const chatService = getEthoraSDKService();
626
635
 
@@ -635,20 +644,20 @@ async function createChatRoomSafely(workspaceId: string) {
635
644
  // Handle specific error cases
636
645
  if (status === 422) {
637
646
  // Validation error
638
- console.error("Validation error:", errorData);
647
+ console.error('Validation error:', errorData);
639
648
  } else if (status === 401) {
640
649
  // Authentication error
641
- console.error("Authentication failed - check your credentials");
650
+ console.error('Authentication failed - check your credentials');
642
651
  } else if (status === 404) {
643
652
  // Resource not found
644
- console.error("Resource not found");
653
+ console.error('Resource not found');
645
654
  } else {
646
655
  // Other HTTP errors
647
656
  console.error(`HTTP error ${status}:`, errorData);
648
657
  }
649
658
  } else {
650
659
  // Non-HTTP errors
651
- console.error("Unexpected error:", error);
660
+ console.error('Unexpected error:', error);
652
661
  }
653
662
  throw error;
654
663
  }
@@ -669,17 +678,17 @@ async function ensureChatRoomExists(workspaceId: string) {
669
678
  if (axios.isAxiosError(error)) {
670
679
  const errorData = error.response?.data;
671
680
  const errorMessage =
672
- typeof errorData === "object" && errorData !== null
673
- ? (errorData as { error?: string }).error || ""
674
- : String(errorData || "");
681
+ typeof errorData === 'object' && errorData !== null
682
+ ? (errorData as { error?: string }).error || ''
683
+ : String(errorData || '');
675
684
 
676
685
  // If room already exists, that's okay
677
686
  if (
678
687
  error.response?.status === 422 &&
679
- (errorMessage.includes("already exist") ||
680
- errorMessage.includes("already exists"))
688
+ (errorMessage.includes('already exist') ||
689
+ errorMessage.includes('already exists'))
681
690
  ) {
682
- console.log("Chat room already exists, continuing...");
691
+ console.log('Chat room already exists, continuing...');
683
692
  return; // Success - room exists
684
693
  }
685
694
  }
@@ -710,8 +719,8 @@ Create a service wrapper in your application:
710
719
 
711
720
  ```typescript
712
721
  // services/chatService.ts
713
- import { getEthoraSDKService } from "@ethora/sdk-backend";
714
- import type { ChatRepository } from "@ethora/sdk-backend";
722
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
723
+ import type { ChatRepository } from '@ethora/sdk-backend';
715
724
 
716
725
  class ChatServiceWrapper {
717
726
  private service: ChatRepository;
@@ -743,16 +752,16 @@ Validate environment variables on application startup:
743
752
  // config/validateEnv.ts
744
753
  function validateEthoraConfig() {
745
754
  const required = [
746
- "ETHORA_CHAT_API_URL",
747
- "ETHORA_CHAT_APP_ID",
748
- "ETHORA_CHAT_APP_SECRET",
755
+ 'ETHORA_CHAT_API_URL',
756
+ 'ETHORA_CHAT_APP_ID',
757
+ 'ETHORA_CHAT_APP_SECRET',
749
758
  ];
750
759
 
751
760
  const missing = required.filter((key) => !process.env[key]);
752
761
 
753
762
  if (missing.length > 0) {
754
763
  throw new Error(
755
- `Missing required Ethora environment variables: ${missing.join(", ")}`
764
+ `Missing required Ethora environment variables: ${missing.join(', ')}`,
756
765
  );
757
766
  }
758
767
  }
@@ -766,8 +775,8 @@ validateEthoraConfig();
766
775
  Integrate with your existing logging system:
767
776
 
768
777
  ```typescript
769
- import { getEthoraSDKService } from "@ethora/sdk-backend";
770
- import { logger } from "./utils/logger"; // Your logger
778
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
779
+ import { logger } from './utils/logger'; // Your logger
771
780
 
772
781
  const chatService = getEthoraSDKService();
773
782
 
@@ -789,7 +798,7 @@ async function createChatRoomWithLogging(workspaceId: string) {
789
798
  Use TypeScript types from the SDK:
790
799
 
791
800
  ```typescript
792
- import type { UUID, ApiResponse } from "@ethora/sdk-backend";
801
+ import type { UUID, ApiResponse } from '@ethora/sdk-backend';
793
802
 
794
803
  async function createUserTyped(
795
804
  userId: UUID,
@@ -797,7 +806,7 @@ async function createUserTyped(
797
806
  firstName: string;
798
807
  lastName: string;
799
808
  email: string;
800
- }
809
+ },
801
810
  ): Promise<ApiResponse> {
802
811
  const chatService = getEthoraSDKService();
803
812
  return await chatService.createUser(userId, userData);
@@ -830,7 +839,7 @@ try {
830
839
  } catch (error) {
831
840
  if (axios.isAxiosError(error) && error.response?.status === 422) {
832
841
  // User already exists, continue
833
- console.log("User already exists");
842
+ console.log('User already exists');
834
843
  } else {
835
844
  throw error;
836
845
  }
@@ -878,3 +887,6 @@ Apache 2.0
878
887
  ## Support
879
888
 
880
889
  For issues and questions, please open an issue on the GitHub repository.
890
+
891
+ To run tests with logs run from root:
892
+ TEST_LOG_FILE=logs/chat-repo.log npm test