@ethora/sdk-backend 26.1.1 → 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
@@ -79,7 +79,7 @@ npm install dotenv
79
79
  In your main application file (e.g., `app.js`, `server.js`, or `index.ts`):
80
80
 
81
81
  ```typescript
82
- import dotenv from "dotenv";
82
+ import dotenv from 'dotenv';
83
83
 
84
84
  // Load environment variables
85
85
  dotenv.config();
@@ -90,7 +90,7 @@ dotenv.config();
90
90
  ### Step 1: Import the SDK
91
91
 
92
92
  ```typescript
93
- import { getEthoraSDKService } from "@ethora/sdk-backend";
93
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
94
94
  ```
95
95
 
96
96
  ### Step 2: Initialize the Service
@@ -101,7 +101,7 @@ You can initialize the service in several ways:
101
101
 
102
102
  ```typescript
103
103
  // services/chatService.ts
104
- import { getEthoraSDKService } from "@ethora/sdk-backend";
104
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
105
105
 
106
106
  // Get the singleton instance
107
107
  const chatService = getEthoraSDKService();
@@ -113,7 +113,7 @@ export default chatService;
113
113
 
114
114
  ```typescript
115
115
  // In your route handler or service
116
- import { getEthoraSDKService } from "@ethora/sdk-backend";
116
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
117
117
 
118
118
  const chatService = getEthoraSDKService();
119
119
  ```
@@ -122,8 +122,8 @@ const chatService = getEthoraSDKService();
122
122
 
123
123
  ```typescript
124
124
  // chat.service.ts
125
- import { Injectable } from "@nestjs/common";
126
- import { getEthoraSDKService } from "@ethora/sdk-backend";
125
+ import { Injectable } from '@nestjs/common';
126
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
127
127
 
128
128
  @Injectable()
129
129
  export class ChatService {
@@ -139,16 +139,16 @@ export class ChatService {
139
139
 
140
140
  ```typescript
141
141
  // routes/chat.ts
142
- import express, { Request, Response } from "express";
143
- import { getEthoraSDKService } from "@ethora/sdk-backend";
144
- import axios from "axios";
142
+ import express, { Request, Response } from 'express';
143
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
144
+ import axios from 'axios';
145
145
 
146
146
  const router = express.Router();
147
147
  const chatService = getEthoraSDKService();
148
148
 
149
149
  // Create a chat room for a workspace
150
150
  router.post(
151
- "/workspaces/:workspaceId/chat",
151
+ '/workspaces/:workspaceId/chat',
152
152
  async (req: Request, res: Response) => {
153
153
  try {
154
154
  const { workspaceId } = req.params;
@@ -157,7 +157,7 @@ router.post(
157
157
  const response = await chatService.createChatRoom(workspaceId, {
158
158
  title: roomData.title || `Chat Room ${workspaceId}`,
159
159
  uuid: workspaceId,
160
- type: roomData.type || "group",
160
+ type: roomData.type || 'group',
161
161
  ...roomData,
162
162
  });
163
163
 
@@ -165,18 +165,18 @@ router.post(
165
165
  } catch (error) {
166
166
  if (axios.isAxiosError(error)) {
167
167
  res.status(error.response?.status || 500).json({
168
- error: "Failed to create chat room",
168
+ error: 'Failed to create chat room',
169
169
  details: error.response?.data,
170
170
  });
171
171
  } else {
172
- res.status(500).json({ error: "Internal server error" });
172
+ res.status(500).json({ error: 'Internal server error' });
173
173
  }
174
174
  }
175
- }
175
+ },
176
176
  );
177
177
 
178
178
  // Create a user
179
- router.post("/users/:userId", async (req: Request, res: Response) => {
179
+ router.post('/users/:userId', async (req: Request, res: Response) => {
180
180
  try {
181
181
  const { userId } = req.params;
182
182
  const userData = req.body;
@@ -186,50 +186,50 @@ router.post("/users/:userId", async (req: Request, res: Response) => {
186
186
  } catch (error) {
187
187
  if (axios.isAxiosError(error)) {
188
188
  res.status(error.response?.status || 500).json({
189
- error: "Failed to create user",
189
+ error: 'Failed to create user',
190
190
  details: error.response?.data,
191
191
  });
192
192
  } else {
193
- res.status(500).json({ error: "Internal server error" });
193
+ res.status(500).json({ error: 'Internal server error' });
194
194
  }
195
195
  }
196
196
  });
197
197
 
198
198
  // Grant user access to chat room
199
199
  router.post(
200
- "/workspaces/:workspaceId/chat/users/:userId",
200
+ '/workspaces/:workspaceId/chat/users/:userId',
201
201
  async (req: Request, res: Response) => {
202
202
  try {
203
203
  const { workspaceId, userId } = req.params;
204
204
 
205
205
  await chatService.grantUserAccessToChatRoom(workspaceId, userId);
206
- res.json({ success: true, message: "Access granted" });
206
+ res.json({ success: true, message: 'Access granted' });
207
207
  } catch (error) {
208
208
  if (axios.isAxiosError(error)) {
209
209
  res.status(error.response?.status || 500).json({
210
- error: "Failed to grant access",
210
+ error: 'Failed to grant access',
211
211
  details: error.response?.data,
212
212
  });
213
213
  } else {
214
- res.status(500).json({ error: "Internal server error" });
214
+ res.status(500).json({ error: 'Internal server error' });
215
215
  }
216
216
  }
217
- }
217
+ },
218
218
  );
219
219
 
220
220
  // Generate client JWT token
221
- router.get("/users/:userId/chat-token", (req: Request, res: Response) => {
221
+ router.get('/users/:userId/chat-token', (req: Request, res: Response) => {
222
222
  try {
223
223
  const { userId } = req.params;
224
224
  const token = chatService.createChatUserJwtToken(userId);
225
225
  res.json({ token });
226
226
  } catch (error) {
227
- res.status(500).json({ error: "Failed to generate token" });
227
+ res.status(500).json({ error: 'Failed to generate token' });
228
228
  }
229
229
  });
230
230
 
231
231
  // Get users
232
- router.get("/users", async (req: Request, res: Response) => {
232
+ router.get('/users', async (req: Request, res: Response) => {
233
233
  try {
234
234
  const { chatName, xmppUsername } = req.query;
235
235
  const params: any = {};
@@ -237,32 +237,32 @@ router.get("/users", async (req: Request, res: Response) => {
237
237
  if (xmppUsername) params.xmppUsername = String(xmppUsername);
238
238
 
239
239
  const response = await chatService.getUsers(
240
- Object.keys(params).length > 0 ? params : undefined
240
+ Object.keys(params).length > 0 ? params : undefined,
241
241
  );
242
242
  res.json({ success: true, data: response });
243
243
  } catch (error) {
244
244
  if (axios.isAxiosError(error)) {
245
245
  res.status(error.response?.status || 500).json({
246
- error: "Failed to get users",
246
+ error: 'Failed to get users',
247
247
  details: error.response?.data,
248
248
  });
249
249
  } else {
250
- res.status(500).json({ error: "Internal server error" });
250
+ res.status(500).json({ error: 'Internal server error' });
251
251
  }
252
252
  }
253
253
  });
254
254
 
255
255
  // Update users (batch)
256
- router.patch("/users", async (req: Request, res: Response) => {
256
+ router.patch('/users', async (req: Request, res: Response) => {
257
257
  try {
258
258
  const { users } = req.body;
259
259
  if (!Array.isArray(users) || users.length === 0) {
260
- 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' });
261
261
  }
262
262
  if (users.length > 100) {
263
263
  return res
264
264
  .status(400)
265
- .json({ error: "Maximum 100 users allowed per request" });
265
+ .json({ error: 'Maximum 100 users allowed per request' });
266
266
  }
267
267
 
268
268
  const response = await chatService.updateUsers(users);
@@ -270,11 +270,11 @@ router.patch("/users", async (req: Request, res: Response) => {
270
270
  } catch (error) {
271
271
  if (axios.isAxiosError(error)) {
272
272
  res.status(error.response?.status || 500).json({
273
- error: "Failed to update users",
273
+ error: 'Failed to update users',
274
274
  details: error.response?.data,
275
275
  });
276
276
  } else {
277
- res.status(500).json({ error: "Internal server error" });
277
+ res.status(500).json({ error: 'Internal server error' });
278
278
  }
279
279
  }
280
280
  });
@@ -286,9 +286,9 @@ export default router;
286
286
 
287
287
  ```typescript
288
288
  // chat/chat.service.ts
289
- import { Injectable, HttpException, HttpStatus } from "@nestjs/common";
290
- import { getEthoraSDKService } from "@ethora/sdk-backend";
291
- import axios from "axios";
289
+ import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
290
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
291
+ import axios from 'axios';
292
292
 
293
293
  @Injectable()
294
294
  export class ChatService {
@@ -301,10 +301,10 @@ export class ChatService {
301
301
  if (axios.isAxiosError(error)) {
302
302
  throw new HttpException(
303
303
  {
304
- message: "Failed to create chat room",
304
+ message: 'Failed to create chat room',
305
305
  details: error.response?.data,
306
306
  },
307
- error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
307
+ error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
308
308
  );
309
309
  }
310
310
  throw error;
@@ -318,10 +318,10 @@ export class ChatService {
318
318
  if (axios.isAxiosError(error)) {
319
319
  throw new HttpException(
320
320
  {
321
- message: "Failed to create user",
321
+ message: 'Failed to create user',
322
322
  details: error.response?.data,
323
323
  },
324
- error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR
324
+ error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
325
325
  );
326
326
  }
327
327
  throw error;
@@ -334,28 +334,28 @@ export class ChatService {
334
334
  }
335
335
 
336
336
  // chat/chat.controller.ts
337
- import { Controller, Post, Get, Param, Body } from "@nestjs/common";
338
- import { ChatService } from "./chat.service";
337
+ import { Controller, Post, Get, Param, Body } from '@nestjs/common';
338
+ import { ChatService } from './chat.service';
339
339
 
340
- @Controller("chat")
340
+ @Controller('chat')
341
341
  export class ChatController {
342
342
  constructor(private readonly chatService: ChatService) {}
343
343
 
344
- @Post("workspaces/:workspaceId/rooms")
344
+ @Post('workspaces/:workspaceId/rooms')
345
345
  async createChatRoom(
346
- @Param("workspaceId") workspaceId: string,
347
- @Body() roomData: any
346
+ @Param('workspaceId') workspaceId: string,
347
+ @Body() roomData: any,
348
348
  ) {
349
349
  return this.chatService.createChatRoom(workspaceId, roomData);
350
350
  }
351
351
 
352
- @Post("users/:userId")
353
- async createUser(@Param("userId") userId: string, @Body() userData: any) {
352
+ @Post('users/:userId')
353
+ async createUser(@Param('userId') userId: string, @Body() userData: any) {
354
354
  return this.chatService.createUser(userId, userData);
355
355
  }
356
356
 
357
- @Get("users/:userId/token")
358
- getClientToken(@Param("userId") userId: string) {
357
+ @Get('users/:userId/token')
358
+ getClientToken(@Param('userId') userId: string) {
359
359
  return { token: this.chatService.generateClientToken(userId) };
360
360
  }
361
361
  }
@@ -365,15 +365,15 @@ export class ChatController {
365
365
 
366
366
  ```typescript
367
367
  // routes/chat.ts
368
- import { FastifyInstance, FastifyRequest, FastifyReply } from "fastify";
369
- import { getEthoraSDKService } from "@ethora/sdk-backend";
368
+ import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
369
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
370
370
 
371
371
  const chatService = getEthoraSDKService();
372
372
 
373
373
  export async function chatRoutes(fastify: FastifyInstance) {
374
374
  // Create chat room
375
375
  fastify.post(
376
- "/workspaces/:workspaceId/chat",
376
+ '/workspaces/:workspaceId/chat',
377
377
  async (request: FastifyRequest, reply: FastifyReply) => {
378
378
  const { workspaceId } = request.params as { workspaceId: string };
379
379
  const roomData = request.body as any;
@@ -381,23 +381,23 @@ export async function chatRoutes(fastify: FastifyInstance) {
381
381
  try {
382
382
  const response = await chatService.createChatRoom(
383
383
  workspaceId,
384
- roomData
384
+ roomData,
385
385
  );
386
386
  return { success: true, data: response };
387
387
  } catch (error) {
388
- reply.code(500).send({ error: "Failed to create chat room" });
388
+ reply.code(500).send({ error: 'Failed to create chat room' });
389
389
  }
390
- }
390
+ },
391
391
  );
392
392
 
393
393
  // Generate client token
394
394
  fastify.get(
395
- "/users/:userId/chat-token",
395
+ '/users/:userId/chat-token',
396
396
  async (request: FastifyRequest, reply: FastifyReply) => {
397
397
  const { userId } = request.params as { userId: string };
398
398
  const token = chatService.createChatUserJwtToken(userId);
399
399
  return { token };
400
- }
400
+ },
401
401
  );
402
402
  }
403
403
  ```
@@ -412,7 +412,7 @@ When creating a new workspace, set up the chat room and initial users:
412
412
  async function setupWorkspaceChat(
413
413
  workspaceId: string,
414
414
  userIds: string[],
415
- adminUserId: string
415
+ adminUserId: string,
416
416
  ) {
417
417
  const chatService = getEthoraSDKService();
418
418
 
@@ -421,15 +421,15 @@ async function setupWorkspaceChat(
421
421
  await chatService.createChatRoom(workspaceId, {
422
422
  title: `Workspace ${workspaceId}`,
423
423
  uuid: workspaceId,
424
- type: "group",
424
+ type: 'group',
425
425
  });
426
426
 
427
427
  // 2. Create users (if they don't exist)
428
428
  for (const userId of userIds) {
429
429
  try {
430
430
  await chatService.createUser(userId, {
431
- firstName: "User",
432
- lastName: "Name",
431
+ firstName: 'User',
432
+ lastName: 'Name',
433
433
  });
434
434
  } catch (error) {
435
435
  // User might already exist, continue
@@ -444,12 +444,12 @@ async function setupWorkspaceChat(
444
444
  try {
445
445
  await chatService.grantChatbotAccessToChatRoom(workspaceId);
446
446
  } catch (error) {
447
- console.warn("Chatbot access not configured or failed");
447
+ console.warn('Chatbot access not configured or failed');
448
448
  }
449
449
 
450
450
  return { success: true };
451
451
  } catch (error) {
452
- console.error("Failed to setup workspace chat:", error);
452
+ console.error('Failed to setup workspace chat:', error);
453
453
  throw error;
454
454
  }
455
455
  }
@@ -462,7 +462,7 @@ When a new user joins your platform:
462
462
  ```typescript
463
463
  async function onboardNewUser(
464
464
  userId: string,
465
- userData: { firstName: string; lastName: string; email: string }
465
+ userData: { firstName: string; lastName: string; email: string },
466
466
  ) {
467
467
  const chatService = getEthoraSDKService();
468
468
 
@@ -483,7 +483,7 @@ async function onboardNewUser(
483
483
  chatToken: clientToken,
484
484
  };
485
485
  } catch (error) {
486
- console.error("Failed to onboard user:", error);
486
+ console.error('Failed to onboard user:', error);
487
487
  throw error;
488
488
  }
489
489
  }
@@ -510,7 +510,7 @@ async function addUserToWorkspace(workspaceId: string, userId: string) {
510
510
 
511
511
  return { success: true };
512
512
  } catch (error) {
513
- console.error("Failed to add user to workspace:", error);
513
+ console.error('Failed to add user to workspace:', error);
514
514
  throw error;
515
515
  }
516
516
  }
@@ -533,13 +533,13 @@ async function cleanupWorkspaceChat(workspaceId: string, userIds: string[]) {
533
533
  try {
534
534
  await chatService.deleteUsers(userIds);
535
535
  } catch (error) {
536
- console.warn("Some users might not exist:", error);
536
+ console.warn('Some users might not exist:', error);
537
537
  }
538
538
  }
539
539
 
540
540
  return { success: true };
541
541
  } catch (error) {
542
- console.error("Failed to cleanup workspace chat:", error);
542
+ console.error('Failed to cleanup workspace chat:', error);
543
543
  throw error;
544
544
  }
545
545
  }
@@ -560,17 +560,17 @@ async function getUsersExample() {
560
560
 
561
561
  // Get users by chat name (group chat)
562
562
  const groupChatUsers = await chatService.getUsers({
563
- chatName: "appId_workspaceId",
563
+ chatName: 'appId_workspaceId',
564
564
  });
565
565
 
566
566
  // Get users by chat name (1-on-1 chat)
567
567
  const oneOnOneUsers = await chatService.getUsers({
568
- chatName: "userA-userB",
568
+ chatName: 'userA-userB',
569
569
  });
570
570
 
571
571
  return { allUsers, groupChatUsers, oneOnOneUsers };
572
572
  } catch (error) {
573
- console.error("Failed to get users:", error);
573
+ console.error('Failed to get users:', error);
574
574
  throw error;
575
575
  }
576
576
  }
@@ -588,34 +588,34 @@ async function updateUsersExample() {
588
588
  // Update multiple users (1-100 users per request)
589
589
  const response = await chatService.updateUsers([
590
590
  {
591
- xmppUsername: "appId_user1",
592
- firstName: "John",
593
- lastName: "Doe",
594
- username: "johndoe",
595
- 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',
596
596
  },
597
597
  {
598
- xmppUsername: "appId_user2",
599
- firstName: "Jane",
600
- lastName: "Smith",
601
- username: "janesmith",
598
+ xmppUsername: 'appId_user2',
599
+ firstName: 'Jane',
600
+ lastName: 'Smith',
601
+ username: 'janesmith',
602
602
  },
603
603
  ]);
604
604
 
605
605
  // Check results
606
606
  response.results?.forEach((result: any) => {
607
- if (result.status === "updated") {
607
+ if (result.status === 'updated') {
608
608
  console.log(`User ${result.xmppUsername} updated successfully`);
609
- } else if (result.status === "not-found") {
609
+ } else if (result.status === 'not-found') {
610
610
  console.warn(`User ${result.xmppUsername} not found`);
611
- } else if (result.status === "skipped") {
611
+ } else if (result.status === 'skipped') {
612
612
  console.log(`User ${result.xmppUsername} update skipped`);
613
613
  }
614
614
  });
615
615
 
616
616
  return response;
617
617
  } catch (error) {
618
- console.error("Failed to update users:", error);
618
+ console.error('Failed to update users:', error);
619
619
  throw error;
620
620
  }
621
621
  }
@@ -628,8 +628,8 @@ async function updateUsersExample() {
628
628
  The SDK uses Axios for HTTP requests, so errors are AxiosError instances:
629
629
 
630
630
  ```typescript
631
- import axios from "axios";
632
- import { getEthoraSDKService } from "@ethora/sdk-backend";
631
+ import axios from 'axios';
632
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
633
633
 
634
634
  const chatService = getEthoraSDKService();
635
635
 
@@ -644,20 +644,20 @@ async function createChatRoomSafely(workspaceId: string) {
644
644
  // Handle specific error cases
645
645
  if (status === 422) {
646
646
  // Validation error
647
- console.error("Validation error:", errorData);
647
+ console.error('Validation error:', errorData);
648
648
  } else if (status === 401) {
649
649
  // Authentication error
650
- console.error("Authentication failed - check your credentials");
650
+ console.error('Authentication failed - check your credentials');
651
651
  } else if (status === 404) {
652
652
  // Resource not found
653
- console.error("Resource not found");
653
+ console.error('Resource not found');
654
654
  } else {
655
655
  // Other HTTP errors
656
656
  console.error(`HTTP error ${status}:`, errorData);
657
657
  }
658
658
  } else {
659
659
  // Non-HTTP errors
660
- console.error("Unexpected error:", error);
660
+ console.error('Unexpected error:', error);
661
661
  }
662
662
  throw error;
663
663
  }
@@ -678,17 +678,17 @@ async function ensureChatRoomExists(workspaceId: string) {
678
678
  if (axios.isAxiosError(error)) {
679
679
  const errorData = error.response?.data;
680
680
  const errorMessage =
681
- typeof errorData === "object" && errorData !== null
682
- ? (errorData as { error?: string }).error || ""
683
- : String(errorData || "");
681
+ typeof errorData === 'object' && errorData !== null
682
+ ? (errorData as { error?: string }).error || ''
683
+ : String(errorData || '');
684
684
 
685
685
  // If room already exists, that's okay
686
686
  if (
687
687
  error.response?.status === 422 &&
688
- (errorMessage.includes("already exist") ||
689
- errorMessage.includes("already exists"))
688
+ (errorMessage.includes('already exist') ||
689
+ errorMessage.includes('already exists'))
690
690
  ) {
691
- console.log("Chat room already exists, continuing...");
691
+ console.log('Chat room already exists, continuing...');
692
692
  return; // Success - room exists
693
693
  }
694
694
  }
@@ -719,8 +719,8 @@ Create a service wrapper in your application:
719
719
 
720
720
  ```typescript
721
721
  // services/chatService.ts
722
- import { getEthoraSDKService } from "@ethora/sdk-backend";
723
- import type { ChatRepository } from "@ethora/sdk-backend";
722
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
723
+ import type { ChatRepository } from '@ethora/sdk-backend';
724
724
 
725
725
  class ChatServiceWrapper {
726
726
  private service: ChatRepository;
@@ -752,16 +752,16 @@ Validate environment variables on application startup:
752
752
  // config/validateEnv.ts
753
753
  function validateEthoraConfig() {
754
754
  const required = [
755
- "ETHORA_CHAT_API_URL",
756
- "ETHORA_CHAT_APP_ID",
757
- "ETHORA_CHAT_APP_SECRET",
755
+ 'ETHORA_CHAT_API_URL',
756
+ 'ETHORA_CHAT_APP_ID',
757
+ 'ETHORA_CHAT_APP_SECRET',
758
758
  ];
759
759
 
760
760
  const missing = required.filter((key) => !process.env[key]);
761
761
 
762
762
  if (missing.length > 0) {
763
763
  throw new Error(
764
- `Missing required Ethora environment variables: ${missing.join(", ")}`
764
+ `Missing required Ethora environment variables: ${missing.join(', ')}`,
765
765
  );
766
766
  }
767
767
  }
@@ -775,8 +775,8 @@ validateEthoraConfig();
775
775
  Integrate with your existing logging system:
776
776
 
777
777
  ```typescript
778
- import { getEthoraSDKService } from "@ethora/sdk-backend";
779
- import { logger } from "./utils/logger"; // Your logger
778
+ import { getEthoraSDKService } from '@ethora/sdk-backend';
779
+ import { logger } from './utils/logger'; // Your logger
780
780
 
781
781
  const chatService = getEthoraSDKService();
782
782
 
@@ -798,7 +798,7 @@ async function createChatRoomWithLogging(workspaceId: string) {
798
798
  Use TypeScript types from the SDK:
799
799
 
800
800
  ```typescript
801
- import type { UUID, ApiResponse } from "@ethora/sdk-backend";
801
+ import type { UUID, ApiResponse } from '@ethora/sdk-backend';
802
802
 
803
803
  async function createUserTyped(
804
804
  userId: UUID,
@@ -806,7 +806,7 @@ async function createUserTyped(
806
806
  firstName: string;
807
807
  lastName: string;
808
808
  email: string;
809
- }
809
+ },
810
810
  ): Promise<ApiResponse> {
811
811
  const chatService = getEthoraSDKService();
812
812
  return await chatService.createUser(userId, userData);
@@ -839,7 +839,7 @@ try {
839
839
  } catch (error) {
840
840
  if (axios.isAxiosError(error) && error.response?.status === 422) {
841
841
  // User already exists, continue
842
- console.log("User already exists");
842
+ console.log('User already exists');
843
843
  } else {
844
844
  throw error;
845
845
  }
@@ -887,3 +887,6 @@ Apache 2.0
887
887
  ## Support
888
888
 
889
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