vg-x07df 1.2.0 → 1.3.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.cjs CHANGED
@@ -4,11 +4,15 @@ var React = require('react');
4
4
  var socket_ioClient = require('socket.io-client');
5
5
  var zustand = require('zustand');
6
6
  var immer = require('zustand/middleware/immer');
7
+ var mitt = require('mitt');
7
8
  var zod = require('zod');
9
+ var livekitClient = require('livekit-client');
10
+ var componentsReact = require('@livekit/components-react');
8
11
 
9
12
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
13
 
11
14
  var React__default = /*#__PURE__*/_interopDefault(React);
15
+ var mitt__default = /*#__PURE__*/_interopDefault(mitt);
12
16
 
13
17
  // src/provider/RtcProvider.tsx
14
18
 
@@ -134,10 +138,10 @@ function setGlobalLoggerOptions(options) {
134
138
 
135
139
  // src/state/types.ts
136
140
  var defaultState = {
137
- session: { status: "IDLE", initiatedByMe: false },
138
- room: {
139
- participants: {}
140
- },
141
+ initiated: false,
142
+ session: null,
143
+ incomingInvite: null,
144
+ outgoingInvites: {},
141
145
  errors: []
142
146
  };
143
147
 
@@ -160,14 +164,14 @@ var useRtcStore = zustand.create()(
160
164
  var rtcStore = useRtcStore;
161
165
 
162
166
  // src/state/errors.ts
163
- function pushError(code, message, context, logger3) {
167
+ function pushError(code, message, context, logger4) {
164
168
  const error = {
165
169
  code,
166
170
  message,
167
171
  timestamp: Date.now(),
168
172
  context
169
173
  };
170
- logger3?.("error", message, { code, context });
174
+ logger4?.("error", message, { code, context });
171
175
  rtcStore.getState().addError(error);
172
176
  }
173
177
  function clearErrors(predicate) {
@@ -179,73 +183,73 @@ function clearErrors(predicate) {
179
183
  }
180
184
  });
181
185
  }
182
- function pushSocketValidationError(eventType, issues, payload, logger3) {
186
+ function pushSocketValidationError(eventType, issues, payload, logger4) {
183
187
  pushError(
184
188
  "SOCKET_PAYLOAD",
185
189
  `Invalid ${eventType} event payload`,
186
190
  { eventType, issues, payload },
187
- logger3
191
+ logger4
188
192
  );
189
193
  }
190
- function pushIdentityGuardError(reason, expected, received, logger3) {
194
+ function pushIdentityGuardError(reason, expected, received, logger4) {
191
195
  pushError(
192
196
  "JOIN_FLOW",
193
197
  // Use new error code
194
198
  `Identity guard failed: ${reason}`,
195
199
  { expected, received },
196
- logger3
200
+ logger4
197
201
  );
198
202
  }
199
- function pushLiveKitConnectError(message, error, logger3) {
203
+ function pushLiveKitConnectError(message, error, logger4) {
200
204
  pushError(
201
205
  "LIVEKIT_CONNECT",
202
206
  `LiveKit connection failed: ${message}`,
203
207
  { originalError: error },
204
- logger3
208
+ logger4
205
209
  );
206
210
  }
207
- function pushStaleEventError(eventType, reason, context, logger3) {
211
+ function pushStaleEventError(eventType, reason, context, logger4) {
208
212
  pushError(
209
213
  "JOIN_FLOW",
210
214
  // Use new error code
211
215
  `Ignored stale ${eventType} event: ${reason}`,
212
216
  context,
213
- logger3
217
+ logger4
214
218
  );
215
219
  }
216
- function pushApiError(operation, error, logger3) {
220
+ function pushApiError(operation, error, logger4) {
217
221
  const errorMessage = error instanceof Error ? error.message : String(error);
218
222
  pushError(
219
223
  "API_ERROR",
220
224
  `API ${operation} failed: ${errorMessage}`,
221
225
  { operation, originalError: error },
222
- logger3
226
+ logger4
223
227
  );
224
228
  }
225
- function pushNetworkError(operation, error, logger3) {
229
+ function pushNetworkError(operation, error, logger4) {
226
230
  const errorMessage = error instanceof Error ? error.message : String(error);
227
231
  pushError(
228
232
  "NETWORK",
229
233
  `Network error during ${operation}: ${errorMessage}`,
230
234
  { operation, originalError: error },
231
- logger3
235
+ logger4
232
236
  );
233
237
  }
234
- function pushMediaPermissionError(device, error, logger3) {
238
+ function pushMediaPermissionError(device, error, logger4) {
235
239
  pushError(
236
240
  "MEDIA_PERMISSION",
237
241
  `${device} permission denied`,
238
242
  { device, originalError: error },
239
- logger3
243
+ logger4
240
244
  );
241
245
  }
242
- function pushDeviceError(operation, device, error, logger3) {
246
+ function pushDeviceError(operation, device, error, logger4) {
243
247
  const errorMessage = error instanceof Error ? error.message : String(error);
244
248
  pushError(
245
249
  "DEVICE_SWITCH",
246
250
  `Failed to ${operation} ${device}: ${errorMessage}`,
247
251
  { operation, device, originalError: error },
248
- logger3
252
+ logger4
249
253
  );
250
254
  }
251
255
 
@@ -306,248 +310,37 @@ var BaseSocketHandler = class {
306
310
  return this.options.livekit;
307
311
  }
308
312
  };
309
-
310
- // src/utils/participant-adapter.ts
311
- function findCaller(participants) {
312
- return participants.find((p) => p.role === "CALLER" || p.role === "HOST") || null;
313
- }
314
- function extractCallerInfo(participants) {
315
- const caller = findCaller(participants);
316
- if (!caller) return null;
317
- const result = {
318
- id: caller.id,
319
- name: [caller.firstName, caller.lastName].filter(Boolean).join(" ") || `Guest ${caller.id}`
320
- };
321
- if (caller.profilePhoto) {
322
- result.avatarUrl = caller.profilePhoto;
323
- }
324
- return result;
325
- }
326
-
327
- // src/core/events/event-bus.ts
328
- var logger = createLogger("core:event-bus");
329
313
  var EventBus = class {
330
- constructor(debugMode = false) {
331
- this.listeners = /* @__PURE__ */ new Map();
332
- this.isDebugMode = false;
333
- this.eventHistory = [];
334
- this.maxHistorySize = 100;
335
- this.isDebugMode = debugMode;
336
- }
337
- /**
338
- * Subscribe to events of a specific type
339
- */
340
- on(eventType, handler, filter) {
341
- const actualHandler = filter ? (event) => {
342
- if (filter(event)) {
343
- handler(event);
344
- }
345
- } : handler;
346
- if (!this.listeners.has(eventType)) {
347
- this.listeners.set(eventType, /* @__PURE__ */ new Set());
348
- }
349
- this.listeners.get(eventType)?.add(actualHandler);
350
- if (this.isDebugMode) {
351
- logger.debug(`Event listener added for: ${eventType}`, {
352
- eventType,
353
- totalListeners: this.listeners.get(eventType)?.size
354
- });
355
- }
356
- return {
357
- unsubscribe: () => {
358
- const handlers = this.listeners.get(eventType);
359
- if (handlers) {
360
- handlers.delete(actualHandler);
361
- if (handlers.size === 0) {
362
- this.listeners.delete(eventType);
363
- }
364
- }
365
- if (this.isDebugMode) {
366
- logger.debug(`Event listener removed for: ${eventType}`, {
367
- eventType,
368
- remainingListeners: handlers?.size || 0
369
- });
370
- }
371
- }
372
- };
314
+ constructor() {
315
+ this.emitter = mitt__default.default();
373
316
  }
374
- /**
375
- * Subscribe to events matching a pattern (e.g., "call:*")
376
- */
377
- onPattern(pattern, handler, filter) {
378
- const regex = new RegExp(pattern.replace(/\*/g, ".*"));
379
- const patternHandler = (event) => {
380
- if (regex.test(event.type)) {
381
- if (filter && !filter(event)) return;
382
- handler(event);
383
- }
317
+ on(eventType, handler) {
318
+ const wrappedHandler = (event) => {
319
+ handler(event);
384
320
  };
385
- const patternKey = `__pattern__${pattern}`;
386
- if (!this.listeners.has(patternKey)) {
387
- this.listeners.set(patternKey, /* @__PURE__ */ new Set());
388
- }
389
- this.listeners.get(patternKey)?.add(patternHandler);
390
- if (this.isDebugMode) {
391
- logger.debug(`Pattern listener added: ${pattern}`, { pattern });
392
- }
321
+ this.emitter.on(eventType.toString(), wrappedHandler);
393
322
  return {
394
323
  unsubscribe: () => {
395
- const handlers = this.listeners.get(patternKey);
396
- if (handlers) {
397
- handlers.delete(patternHandler);
398
- if (handlers.size === 0) {
399
- this.listeners.delete(patternKey);
400
- }
401
- }
402
- if (this.isDebugMode) {
403
- logger.debug(`Pattern listener removed: ${pattern}`, { pattern });
404
- }
324
+ this.emitter.off(eventType.toString(), wrappedHandler);
405
325
  }
406
326
  };
407
327
  }
408
- /**
409
- * Subscribe to a single event occurrence
410
- */
411
- once(eventType, handler, filter) {
412
- const subscription = this.on(
413
- eventType,
414
- (event) => {
415
- handler(event);
416
- subscription.unsubscribe();
417
- },
418
- filter
419
- );
420
- return subscription;
421
- }
422
- /**
423
- * Emit an event
424
- */
425
- emit(eventType, payload, source = "user") {
328
+ emit(eventType, payload) {
426
329
  const event = {
427
330
  type: eventType.toString(),
428
331
  payload,
429
- timestamp: Date.now(),
430
- source
332
+ timestamp: Date.now()
431
333
  };
432
- this.addToHistory(event);
433
- if (this.isDebugMode) {
434
- logger.debug(`Event emitted: ${eventType}`, {
435
- eventType,
436
- payload,
437
- source,
438
- timestamp: event.timestamp
439
- });
440
- }
441
- const exactListeners = this.listeners.get(eventType.toString());
442
- if (exactListeners) {
443
- for (const handler of exactListeners) {
444
- try {
445
- handler(event);
446
- } catch (error) {
447
- logger.error(`Error in event handler for ${eventType}`, {
448
- error,
449
- eventType,
450
- payload
451
- });
452
- }
453
- }
454
- }
455
- this.listeners.forEach((handlers, key) => {
456
- if (key.startsWith("__pattern__")) {
457
- for (const handler of handlers) {
458
- try {
459
- handler(event);
460
- } catch (error) {
461
- logger.error(`Error in pattern handler for ${eventType}`, {
462
- error,
463
- eventType,
464
- pattern: key,
465
- payload
466
- });
467
- }
468
- }
469
- }
470
- });
334
+ this.emitter.emit(eventType.toString(), event);
471
335
  }
472
- /**
473
- * Remove all listeners for a specific event type
474
- */
475
336
  off(eventType) {
476
- this.listeners.delete(eventType.toString());
477
- if (this.isDebugMode) {
478
- logger.debug(`All listeners removed for: ${eventType}`, { eventType });
479
- }
337
+ this.emitter.off(eventType.toString());
480
338
  }
481
- /**
482
- * Remove all listeners
483
- */
484
339
  removeAllListeners() {
485
- const totalListeners = Array.from(this.listeners.values()).reduce(
486
- (sum, handlers) => sum + handlers.size,
487
- 0
488
- );
489
- this.listeners.clear();
490
- if (this.isDebugMode) {
491
- logger.debug("All listeners removed", { removedCount: totalListeners });
492
- }
493
- }
494
- /**
495
- * Get the count of listeners for an event type
496
- */
497
- listenerCount(eventType) {
498
- return this.listeners.get(eventType.toString())?.size || 0;
499
- }
500
- /**
501
- * Get all event types that have listeners
502
- */
503
- getEventTypes() {
504
- return Array.from(this.listeners.keys());
505
- }
506
- /**
507
- * Enable or disable debug mode
508
- */
509
- setDebugMode(enabled) {
510
- this.isDebugMode = enabled;
511
- logger.debug(`Debug mode ${enabled ? "enabled" : "disabled"}`);
512
- }
513
- /**
514
- * Get event history for debugging
515
- */
516
- getEventHistory() {
517
- return [...this.eventHistory];
518
- }
519
- /**
520
- * Clear event history
521
- */
522
- clearHistory() {
523
- this.eventHistory = [];
524
- if (this.isDebugMode) {
525
- logger.debug("Event history cleared");
526
- }
527
- }
528
- /**
529
- * Get events matching a filter
530
- */
531
- getEventsWhere(filter) {
532
- return this.eventHistory.filter(filter);
533
- }
534
- addToHistory(event) {
535
- this.eventHistory.push(event);
536
- if (this.eventHistory.length > this.maxHistorySize) {
537
- this.eventHistory.shift();
538
- }
539
- }
540
- /**
541
- * Set maximum history size
542
- */
543
- setMaxHistorySize(size) {
544
- this.maxHistorySize = size;
545
- if (this.eventHistory.length > size) {
546
- this.eventHistory = this.eventHistory.slice(-size);
547
- }
340
+ this.emitter.all.clear();
548
341
  }
549
342
  };
550
- var eventBus = new EventBus(false);
343
+ var eventBus = new EventBus();
551
344
 
552
345
  // src/core/events/types.ts
553
346
  var SdkEventType = /* @__PURE__ */ ((SdkEventType2) => {
@@ -572,163 +365,482 @@ var SdkEventType = /* @__PURE__ */ ((SdkEventType2) => {
572
365
  SdkEventType2["ERROR_OCCURRED"] = "error:occurred";
573
366
  return SdkEventType2;
574
367
  })(SdkEventType || {});
575
- var socketParticipantSchema = zod.z.object({
576
- id: zod.z.string(),
577
- firstName: zod.z.string().nullable(),
578
- lastName: zod.z.string().nullable(),
579
- username: zod.z.string().nullable(),
580
- profilePhoto: zod.z.string().nullable(),
581
- role: zod.z.enum(["CALLER", "CALLEE", "HOST", "MEMBER"]).optional()
582
- });
583
- var callIncomingSchema = zod.z.object({
368
+ var callCancelledSchema = zod.z.object({
584
369
  callId: zod.z.string(),
585
- type: zod.z.enum(["AUDIO", "VIDEO"]),
586
- participants: zod.z.array(socketParticipantSchema).min(1),
587
- // Required, at least 1 participant
588
- timestamp: zod.z.number()
589
- });
590
- var callParticipantAcceptedSchema = zod.z.object({
591
- callId: zod.z.string(),
592
- participantId: zod.z.string(),
593
- participant: socketParticipantSchema,
594
- acceptedAt: zod.z.string().optional()
595
- });
596
- var callJoinInfoSchema = zod.z.object({
370
+ status: zod.z.literal("cancelled"),
371
+ cancelledAt: zod.z.string()
372
+ }).strict();
373
+ var callCreatedSchema = zod.z.object({
597
374
  callId: zod.z.string(),
598
- token: zod.z.string(),
599
- url: zod.z.string().optional(),
375
+ status: zod.z.literal("pending"),
600
376
  roomName: zod.z.string(),
601
- expiresAt: zod.z.number().optional()
602
- });
377
+ createdAt: zod.z.string(),
378
+ host: zod.z.object({
379
+ firstName: zod.z.string().nullable(),
380
+ lastName: zod.z.string().nullable(),
381
+ username: zod.z.string().nullable(),
382
+ email: zod.z.string().nullable(),
383
+ profilePhoto: zod.z.string().nullable(),
384
+ userId: zod.z.string()
385
+ }).strict(),
386
+ participants: zod.z.array(zod.z.object({
387
+ firstName: zod.z.string().nullable(),
388
+ lastName: zod.z.string().nullable(),
389
+ username: zod.z.string().nullable(),
390
+ email: zod.z.string().nullable(),
391
+ profilePhoto: zod.z.string().nullable(),
392
+ userId: zod.z.string()
393
+ }).strict())
394
+ }).strict();
603
395
  var callEndedSchema = zod.z.object({
604
396
  callId: zod.z.string(),
605
- reason: zod.z.string().optional(),
606
- endedAt: zod.z.string().optional()
607
- }).passthrough();
608
- var callTimeoutSchema = zod.z.object({
397
+ endedAt: zod.z.string(),
398
+ endedByUserId: zod.z.string().optional(),
399
+ reason: zod.z.string().optional()
400
+ }).strict();
401
+ var callInviteSchema = zod.z.object({
609
402
  callId: zod.z.string(),
610
- reason: zod.z.string().optional(),
611
- timestamp: zod.z.number().optional()
612
- });
613
- var callParticipantDeclinedSchema = zod.z.object({
403
+ status: zod.z.enum(["pending", "active", "ended"]),
404
+ caller: zod.z.object({
405
+ firstName: zod.z.string().nullable(),
406
+ lastName: zod.z.string().nullable(),
407
+ username: zod.z.string().nullable(),
408
+ email: zod.z.string().nullable(),
409
+ profilePhoto: zod.z.string().nullable(),
410
+ userId: zod.z.string()
411
+ }).strict(),
412
+ mode: zod.z.enum(["VIDEO", "AUDIO"]),
413
+ expiresAt: zod.z.string(),
414
+ expiresInMs: zod.z.number()
415
+ }).strict();
416
+ var callInviteAcceptedSchema = zod.z.object({
417
+ callId: zod.z.string(),
418
+ status: zod.z.enum(["pending", "active", "ended"]),
419
+ participant: zod.z.object({
420
+ firstName: zod.z.string().nullable(),
421
+ lastName: zod.z.string().nullable(),
422
+ username: zod.z.string().nullable(),
423
+ email: zod.z.string().nullable(),
424
+ profilePhoto: zod.z.string().nullable(),
425
+ userId: zod.z.string()
426
+ }).strict(),
427
+ acceptedAt: zod.z.string()
428
+ }).strict();
429
+ var callInviteCancelledSchema = zod.z.object({
614
430
  callId: zod.z.string(),
615
- participantId: zod.z.string(),
616
- participant: socketParticipantSchema,
617
- declinedAt: zod.z.string().optional()
618
- });
619
- var callCanceledSchema = zod.z.object({
431
+ status: zod.z.enum(["pending", "active", "ended"]),
432
+ cancelledByUserId: zod.z.string(),
433
+ cancelledAt: zod.z.string(),
434
+ reason: zod.z.string()
435
+ }).strict();
436
+ var callInviteDeclinedSchema = zod.z.object({
620
437
  callId: zod.z.string(),
438
+ status: zod.z.enum(["pending", "active", "ended"]),
439
+ participant: zod.z.object({
440
+ firstName: zod.z.string().nullable(),
441
+ lastName: zod.z.string().nullable(),
442
+ username: zod.z.string().nullable(),
443
+ email: zod.z.string().nullable(),
444
+ profilePhoto: zod.z.string().nullable(),
445
+ userId: zod.z.string()
446
+ }).strict(),
621
447
  reason: zod.z.string().optional(),
622
- timestamp: zod.z.number().optional(),
623
- by: socketParticipantSchema.optional()
624
- });
448
+ declinedAt: zod.z.string()
449
+ }).strict();
450
+ var callInviteMissedSchema = zod.z.object({
451
+ callId: zod.z.string(),
452
+ status: zod.z.enum(["pending", "active", "ended"]),
453
+ participant: zod.z.object({
454
+ firstName: zod.z.string().nullable(),
455
+ lastName: zod.z.string().nullable(),
456
+ username: zod.z.string().nullable(),
457
+ email: zod.z.string().nullable(),
458
+ profilePhoto: zod.z.string().nullable(),
459
+ userId: zod.z.string()
460
+ }).strict(),
461
+ missedAt: zod.z.string()
462
+ }).strict();
463
+ var callInviteSentSchema = zod.z.object({
464
+ callId: zod.z.string(),
465
+ invitee: zod.z.object({
466
+ firstName: zod.z.string().nullable(),
467
+ lastName: zod.z.string().nullable(),
468
+ username: zod.z.string().nullable(),
469
+ email: zod.z.string().nullable(),
470
+ profilePhoto: zod.z.string().nullable(),
471
+ userId: zod.z.string()
472
+ }).strict(),
473
+ status: zod.z.literal("sent")
474
+ }).strict();
475
+ var callJoinInfoSchema = zod.z.object({
476
+ callId: zod.z.string(),
477
+ token: zod.z.string(),
478
+ roomName: zod.z.string(),
479
+ lkUrl: zod.z.string(),
480
+ role: zod.z.enum(["HOST", "PARTICIPANT", "GUEST"]),
481
+ participantId: zod.z.string().optional()
482
+ }).strict();
483
+ var callMissedSchema = zod.z.object({
484
+ callId: zod.z.string(),
485
+ endedAt: zod.z.string()
486
+ }).strict();
487
+ var callParticipantAddedSchema = zod.z.object({
488
+ callId: zod.z.string(),
489
+ status: zod.z.enum(["pending", "active", "ended"]),
490
+ participant: zod.z.object({
491
+ participantId: zod.z.string(),
492
+ userId: zod.z.string(),
493
+ firstName: zod.z.string().optional(),
494
+ lastName: zod.z.string().optional(),
495
+ username: zod.z.string().optional(),
496
+ email: zod.z.string().optional(),
497
+ profilePhoto: zod.z.string().optional(),
498
+ role: zod.z.enum(["HOST", "PARTICIPANT", "GUEST"]),
499
+ joinState: zod.z.enum(["not_joined", "joined"])
500
+ }).strict()
501
+ }).strict();
625
502
 
626
- // src/core/socketio/handlers/call-incoming.handler.ts
627
- var CallIncomingHandler = class extends BaseSocketHandler {
503
+ // src/core/socketio/handlers/invite.handler.ts
504
+ var InviteHandler = class extends BaseSocketHandler {
628
505
  constructor() {
629
506
  super(...arguments);
630
- this.eventName = "call.incoming";
631
- this.schema = callIncomingSchema;
507
+ this.eventName = "call:invite";
508
+ this.schema = callInviteSchema;
632
509
  }
633
510
  handle(data) {
634
- const callerInfo = extractCallerInfo(data.participants);
635
- if (!callerInfo) {
636
- this.logger.error("No active caller found in participants", data);
637
- return;
638
- }
511
+ this.logger.info("Incoming call invitation", {
512
+ callId: data.callId,
513
+ caller: data.caller.userId,
514
+ mode: data.mode
515
+ });
639
516
  this.updateStore((state) => {
517
+ state.incomingInvite = {
518
+ callId: data.callId,
519
+ caller: {
520
+ userId: data.caller.userId,
521
+ firstName: data.caller.firstName,
522
+ lastName: data.caller.lastName,
523
+ username: data.caller.username,
524
+ email: data.caller.email,
525
+ profilePhoto: data.caller.profilePhoto
526
+ },
527
+ mode: data.mode,
528
+ expiresAt: data.expiresAt,
529
+ expiresInMs: data.expiresInMs
530
+ };
640
531
  state.session = {
641
532
  id: data.callId,
642
- status: "RINGING",
643
- mode: data.type,
644
- initiatedByMe: false
533
+ status: "pending",
534
+ mode: data.mode,
535
+ role: "PARTICIPANT"
645
536
  };
646
- state.room.participants = {};
647
537
  });
648
- eventBus.emit(
649
- "call:incoming" /* CALL_INCOMING */,
650
- {
651
- callId: data.callId,
652
- caller: callerInfo,
653
- type: data.type,
654
- timestamp: data.timestamp,
655
- participants: data.participants
538
+ eventBus.emit("call:incoming" /* CALL_INCOMING */, {
539
+ callId: data.callId,
540
+ caller: {
541
+ id: data.caller.userId,
542
+ firstName: data.caller.firstName,
543
+ lastName: data.caller.lastName,
544
+ avatarUrl: data.caller.profilePhoto
656
545
  },
657
- "socket"
658
- );
659
- this.logger.debug("Incoming call event emitted", {
546
+ type: data.mode,
547
+ timestamp: Date.now()
548
+ });
549
+ this.logger.debug("Incoming invite state updated");
550
+ }
551
+ };
552
+
553
+ // src/core/socketio/handlers/invite-sent.handler.ts
554
+ var InviteSentHandler = class extends BaseSocketHandler {
555
+ constructor() {
556
+ super(...arguments);
557
+ this.eventName = "call:inviteSent";
558
+ this.schema = callInviteSentSchema;
559
+ }
560
+ handle(data) {
561
+ const currentState = rtcStore.getState();
562
+ this.logger.info("Invitation sent to participant", {
563
+ callId: data.callId,
564
+ invitee: data.invitee.userId
565
+ });
566
+ if (currentState.session?.id !== data.callId) {
567
+ pushStaleEventError("call:inviteSent", "callId mismatch", {
568
+ eventCallId: data.callId,
569
+ sessionCallId: currentState.session?.id
570
+ });
571
+ this.logger.warn("Ignoring invite sent for different call", { callId: data.callId });
572
+ return;
573
+ }
574
+ this.updateStore((state) => {
575
+ const userId = data.invitee.userId;
576
+ state.outgoingInvites[userId] = {
577
+ userId,
578
+ status: "sent",
579
+ participant: {
580
+ userId: data.invitee.userId,
581
+ firstName: data.invitee.firstName,
582
+ lastName: data.invitee.lastName,
583
+ username: data.invitee.username,
584
+ email: data.invitee.email,
585
+ profilePhoto: data.invitee.profilePhoto
586
+ }
587
+ };
588
+ });
589
+ eventBus.emit("participant:invited" /* PARTICIPANT_INVITED */, {
590
+ callId: data.callId,
591
+ participant: {
592
+ id: data.invitee.userId,
593
+ firstName: data.invitee.firstName,
594
+ lastName: data.invitee.lastName,
595
+ avatarUrl: data.invitee.profilePhoto
596
+ },
597
+ timestamp: Date.now()
598
+ });
599
+ this.logger.debug("Outgoing invite tracked", {
600
+ userId: data.invitee.userId
601
+ });
602
+ }
603
+ };
604
+
605
+ // src/core/socketio/handlers/invite-accepted.handler.ts
606
+ var InviteAcceptedHandler = class extends BaseSocketHandler {
607
+ constructor() {
608
+ super(...arguments);
609
+ this.eventName = "call:inviteAccepted";
610
+ this.schema = callInviteAcceptedSchema;
611
+ }
612
+ handle(data) {
613
+ const currentState = rtcStore.getState();
614
+ this.logger.info("Participant accepted invitation", {
660
615
  callId: data.callId,
661
- caller: callerInfo.name,
662
- type: data.type
616
+ participant: data.participant.userId
617
+ });
618
+ if (currentState.session?.id !== data.callId) {
619
+ pushStaleEventError("call:inviteAccepted", "callId mismatch", {
620
+ eventCallId: data.callId,
621
+ sessionCallId: currentState.session?.id
622
+ });
623
+ this.logger.warn("Ignoring accept event for different call", { callId: data.callId });
624
+ return;
625
+ }
626
+ this.updateStore((state) => {
627
+ const userId = data.participant.userId;
628
+ if (state.outgoingInvites[userId]) {
629
+ state.outgoingInvites[userId].status = "accepted";
630
+ } else {
631
+ state.outgoingInvites[userId] = {
632
+ userId,
633
+ status: "accepted",
634
+ participant: {
635
+ userId: data.participant.userId,
636
+ firstName: data.participant.firstName,
637
+ lastName: data.participant.lastName,
638
+ username: data.participant.username,
639
+ email: data.participant.email,
640
+ profilePhoto: data.participant.profilePhoto
641
+ }
642
+ };
643
+ }
644
+ });
645
+ this.logger.debug("Outgoing invite marked as accepted", {
646
+ userId: data.participant.userId
663
647
  });
664
648
  }
665
649
  };
666
650
 
667
- // src/core/socketio/handlers/call-accepted.handler.ts
668
- var CallParticipantAcceptedHandler = class extends BaseSocketHandler {
651
+ // src/core/socketio/handlers/invite-declined.handler.ts
652
+ var InviteDeclinedHandler = class extends BaseSocketHandler {
669
653
  constructor() {
670
654
  super(...arguments);
671
- this.eventName = "call.participant-accepted";
672
- this.schema = callParticipantAcceptedSchema;
655
+ this.eventName = "call:inviteDeclined";
656
+ this.schema = callInviteDeclinedSchema;
673
657
  }
674
- async handle(data) {
658
+ handle(data) {
675
659
  const currentState = rtcStore.getState();
676
- if (currentState.session.id !== data.callId) {
677
- pushStaleEventError("call.participant-accepted", "callId mismatch", {
660
+ this.logger.info("Participant declined invitation", {
661
+ callId: data.callId,
662
+ participant: data.participant.userId,
663
+ reason: data.reason
664
+ });
665
+ if (currentState.session?.id !== data.callId) {
666
+ pushStaleEventError("call:inviteDeclined", "callId mismatch", {
678
667
  eventCallId: data.callId,
679
- sessionCallId: currentState.session.id
668
+ sessionCallId: currentState.session?.id
680
669
  });
670
+ this.logger.warn("Ignoring decline event for different call", { callId: data.callId });
681
671
  return;
682
672
  }
683
673
  this.updateStore((state) => {
684
- state.session.status = "ACCEPTED";
674
+ const userId = data.participant.userId;
675
+ if (state.outgoingInvites[userId]) {
676
+ state.outgoingInvites[userId].status = "declined";
677
+ } else {
678
+ state.outgoingInvites[userId] = {
679
+ userId,
680
+ status: "declined",
681
+ participant: {
682
+ userId: data.participant.userId,
683
+ firstName: data.participant.firstName,
684
+ lastName: data.participant.lastName,
685
+ username: data.participant.username,
686
+ email: data.participant.email,
687
+ profilePhoto: data.participant.profilePhoto
688
+ }
689
+ };
690
+ }
691
+ });
692
+ eventBus.emit("call:declined" /* CALL_DECLINED */, {
693
+ callId: data.callId,
694
+ participantId: data.participant.userId,
695
+ reason: data.reason,
696
+ timestamp: Date.now()
697
+ });
698
+ this.logger.debug("Outgoing invite marked as declined", {
699
+ userId: data.participant.userId
685
700
  });
686
701
  }
687
702
  };
688
703
 
689
- // src/core/socketio/handlers/call-declined.handler.ts
690
- var CallParticipantDeclinedHandler = class extends BaseSocketHandler {
704
+ // src/core/socketio/handlers/invite-missed.handler.ts
705
+ var InviteMissedHandler = class extends BaseSocketHandler {
691
706
  constructor() {
692
707
  super(...arguments);
693
- this.eventName = "call.participant-declined";
694
- this.schema = callParticipantDeclinedSchema;
708
+ this.eventName = "call:inviteMissed";
709
+ this.schema = callInviteMissedSchema;
695
710
  }
696
711
  handle(data) {
712
+ const currentState = rtcStore.getState();
713
+ this.logger.info("Participant missed invitation", {
714
+ callId: data.callId,
715
+ participant: data.participant.userId
716
+ });
717
+ if (currentState.session?.id !== data.callId) {
718
+ pushStaleEventError("call:inviteMissed", "callId mismatch", {
719
+ eventCallId: data.callId,
720
+ sessionCallId: currentState.session?.id
721
+ });
722
+ this.logger.warn("Ignoring missed invite for different call", { callId: data.callId });
723
+ return;
724
+ }
697
725
  this.updateStore((state) => {
698
- if (state.session.id === data.callId) {
699
- state.session.status = "IDLE";
726
+ const userId = data.participant.userId;
727
+ if (state.outgoingInvites[userId]) {
728
+ state.outgoingInvites[userId].status = "missed";
729
+ } else {
730
+ state.outgoingInvites[userId] = {
731
+ userId,
732
+ status: "missed",
733
+ participant: {
734
+ userId: data.participant.userId,
735
+ firstName: data.participant.firstName,
736
+ lastName: data.participant.lastName,
737
+ username: data.participant.username,
738
+ email: data.participant.email,
739
+ profilePhoto: data.participant.profilePhoto
740
+ }
741
+ };
700
742
  }
701
743
  });
702
- eventBus.emit(
703
- "call:declined" /* CALL_DECLINED */,
704
- {
705
- callId: data.callId,
706
- participantId: data.participantId,
707
- reason: "declined",
708
- timestamp: Date.now()
709
- },
710
- "socket"
711
- );
744
+ this.logger.debug("Outgoing invite marked as missed", {
745
+ userId: data.participant.userId
746
+ });
747
+ }
748
+ };
749
+
750
+ // src/core/socketio/handlers/invite-cancelled.handler.ts
751
+ var InviteCancelledHandler = class extends BaseSocketHandler {
752
+ constructor() {
753
+ super(...arguments);
754
+ this.eventName = "call:inviteCancelled";
755
+ this.schema = callInviteCancelledSchema;
756
+ }
757
+ handle(data) {
758
+ const currentState = rtcStore.getState();
759
+ this.logger.info("Invitation cancelled by host", {
760
+ callId: data.callId,
761
+ cancelledBy: data.cancelledByUserId,
762
+ reason: data.reason
763
+ });
764
+ if (currentState.incomingInvite?.callId !== data.callId) {
765
+ this.logger.debug("Ignoring cancelled invite for different call", {
766
+ callId: data.callId
767
+ });
768
+ return;
769
+ }
770
+ this.updateStore((state) => {
771
+ state.incomingInvite = null;
772
+ state.session = null;
773
+ });
774
+ eventBus.emit("call:canceled" /* CALL_CANCELED */, {
775
+ callId: data.callId,
776
+ reason: data.reason,
777
+ timestamp: Date.now()
778
+ });
779
+ this.logger.debug("Incoming invite cleared due to cancellation");
780
+ }
781
+ };
782
+
783
+ // src/core/socketio/handlers/call-created.handler.ts
784
+ var SessionCreatedHandler = class extends BaseSocketHandler {
785
+ constructor() {
786
+ super(...arguments);
787
+ this.eventName = "call:created";
788
+ this.schema = callCreatedSchema;
789
+ }
790
+ handle(data) {
791
+ this.logger.info("Call session created", {
792
+ callId: data.callId,
793
+ roomName: data.roomName,
794
+ host: data.host.userId,
795
+ participantCount: data.participants.length
796
+ });
797
+ this.updateStore((state) => {
798
+ state.initiated = true;
799
+ state.session = {
800
+ id: data.callId,
801
+ status: "pending",
802
+ mode: "VIDEO",
803
+ role: "HOST"
804
+ };
805
+ state.outgoingInvites = {};
806
+ });
807
+ eventBus.emit("call:initiated" /* CALL_INITIATED */, {
808
+ callId: data.callId,
809
+ participants: data.participants.map((p) => p.userId),
810
+ type: "VIDEO",
811
+ // Default, TODO: get from API request context
812
+ timestamp: Date.now()
813
+ });
814
+ this.logger.debug("Session created for outbound call");
712
815
  }
713
816
  };
714
817
 
715
818
  // src/core/socketio/handlers/call-ended.handler.ts
716
- var CallEndedHandler = class extends BaseSocketHandler {
819
+ var SessionEndedHandler = class extends BaseSocketHandler {
717
820
  constructor() {
718
821
  super(...arguments);
719
- this.eventName = "call.ended";
822
+ this.eventName = "call:ended";
720
823
  this.schema = callEndedSchema;
721
824
  }
722
825
  handle(data) {
826
+ const currentState = rtcStore.getState();
827
+ this.logger.info("Call session ended", {
828
+ callId: data.callId,
829
+ endedBy: data.endedByUserId,
830
+ reason: data.reason
831
+ });
832
+ if (currentState.session?.id !== data.callId) {
833
+ pushStaleEventError("call:ended", "callId mismatch", {
834
+ eventCallId: data.callId,
835
+ sessionCallId: currentState.session?.id
836
+ });
837
+ this.logger.warn("Ignoring end event for different call", { callId: data.callId });
838
+ return;
839
+ }
723
840
  this.updateStore((state) => {
724
- if (state.session.id !== data.callId) {
725
- pushStaleEventError("call.ended", "callId mismatch", {
726
- eventCallId: data.callId,
727
- sessionCallId: state.session.id
728
- });
729
- return;
730
- }
731
- state.session.status = "ENDED";
841
+ state.initiated = false;
842
+ state.session = null;
843
+ state.outgoingInvites = {};
732
844
  state.room.participants = {};
733
845
  });
734
846
  if (this.livekit) {
@@ -736,83 +848,165 @@ var CallEndedHandler = class extends BaseSocketHandler {
736
848
  this.logger.error("Error disconnecting from LiveKit", { error });
737
849
  });
738
850
  }
851
+ eventBus.emit("call:ended" /* CALL_ENDED */, {
852
+ callId: data.callId,
853
+ endedBy: data.endedByUserId,
854
+ reason: "user",
855
+ timestamp: Date.now()
856
+ });
857
+ this.logger.debug("Session cleared and LiveKit disconnected");
739
858
  }
740
859
  };
741
860
 
742
- // src/core/socketio/handlers/call-join-info.handler.ts
743
- var CallJoinInfoHandler = class extends BaseSocketHandler {
861
+ // src/core/socketio/handlers/call-cancelled.handler.ts
862
+ var SessionCancelledHandler = class extends BaseSocketHandler {
744
863
  constructor() {
745
864
  super(...arguments);
746
- this.eventName = "call.join-info";
747
- this.schema = callJoinInfoSchema;
865
+ this.eventName = "call:cancelled";
866
+ this.schema = callCancelledSchema;
748
867
  }
749
- async handle(data) {
868
+ handle(data) {
869
+ const currentState = rtcStore.getState();
870
+ this.logger.info("Call session cancelled", {
871
+ callId: data.callId,
872
+ status: data.status
873
+ });
874
+ if (currentState.session?.id !== data.callId) {
875
+ pushStaleEventError("call:cancelled", "callId mismatch", {
876
+ eventCallId: data.callId,
877
+ sessionCallId: currentState.session?.id
878
+ });
879
+ this.logger.warn("Ignoring cancel event for different call", { callId: data.callId });
880
+ return;
881
+ }
750
882
  this.updateStore((state) => {
751
- state.session.livekitInfo = {
752
- token: data.token,
753
- url: data.url,
754
- roomName: data.roomName,
755
- callId: data.callId
756
- };
757
- state.session.status = "READY_TO_JOIN";
883
+ state.initiated = false;
884
+ state.session = null;
885
+ state.outgoingInvites = {};
886
+ state.room.participants = {};
758
887
  });
759
- eventBus.emit(
760
- "join-info:received" /* JOIN_INFO_RECEIVED */,
761
- {
762
- callId: data.callId,
763
- timestamp: Date.now(),
764
- url: data.url,
765
- roomName: data.roomName,
766
- token: data.token
767
- },
768
- "socket"
769
- );
888
+ eventBus.emit("call:canceled" /* CALL_CANCELED */, {
889
+ callId: data.callId,
890
+ reason: "cancelled by host",
891
+ timestamp: Date.now()
892
+ });
893
+ this.logger.debug("Session cleared due to cancellation");
770
894
  }
771
895
  };
772
896
 
773
- // src/core/socketio/handlers/call-timeout.handler.ts
774
- var CallTimeoutHandler = class extends BaseSocketHandler {
897
+ // src/core/socketio/handlers/call-missed.handler.ts
898
+ var SessionMissedHandler = class extends BaseSocketHandler {
775
899
  constructor() {
776
900
  super(...arguments);
777
- this.eventName = "call.timeout";
778
- this.schema = callTimeoutSchema;
901
+ this.eventName = "call:missed";
902
+ this.schema = callMissedSchema;
779
903
  }
780
904
  handle(data) {
781
- const reason = data.reason || "timeout";
782
- this.logger.info(`Call timeout: ${reason}`, { callId: data.callId });
905
+ const currentState = rtcStore.getState();
906
+ this.logger.info("Call missed by all participants", {
907
+ callId: data.callId
908
+ });
909
+ if (currentState.session?.id !== data.callId) {
910
+ pushStaleEventError("call:missed", "callId mismatch", {
911
+ eventCallId: data.callId,
912
+ sessionCallId: currentState.session?.id
913
+ });
914
+ this.logger.warn("Ignoring missed call for different session", { callId: data.callId });
915
+ return;
916
+ }
783
917
  this.updateStore((state) => {
784
- if (state.session.id === data.callId) {
785
- state.session.status = "ENDED";
786
- state.room.participants = {};
787
- }
918
+ state.initiated = false;
919
+ state.session = null;
920
+ state.outgoingInvites = {};
921
+ state.room.participants = {};
922
+ });
923
+ eventBus.emit("call:timeout" /* CALL_TIMEOUT */, {
924
+ callId: data.callId,
925
+ reason: "All participants missed/declined",
926
+ timestamp: Date.now()
788
927
  });
928
+ this.logger.debug("Session cleared - call was missed by all");
789
929
  }
790
930
  };
791
931
 
792
- // src/core/socketio/handlers/call-canceled.handler.ts
793
- var CallCanceledHandler = class extends BaseSocketHandler {
932
+ // src/core/socketio/handlers/join-info.handler.ts
933
+ var JoinInfoHandler = class extends BaseSocketHandler {
794
934
  constructor() {
795
935
  super(...arguments);
796
- this.eventName = "call.canceled";
797
- this.schema = callCanceledSchema;
936
+ this.eventName = "call:joinInfo";
937
+ this.schema = callJoinInfoSchema;
798
938
  }
799
939
  handle(data) {
800
- const reason = data.reason || "canceled";
801
- this.logger.info(`Call canceled: ${reason}`, {
940
+ const currentState = rtcStore.getState();
941
+ this.logger.info("Received LiveKit join info", {
802
942
  callId: data.callId,
803
- by: data.by?.id
943
+ roomName: data.roomName,
944
+ role: data.role
804
945
  });
946
+ if (currentState.session?.id !== data.callId) {
947
+ pushStaleEventError("call:joinInfo", "callId mismatch", {
948
+ eventCallId: data.callId,
949
+ sessionCallId: currentState.session?.id
950
+ });
951
+ this.logger.warn("Ignoring join info for unknown call", { callId: data.callId });
952
+ return;
953
+ }
805
954
  this.updateStore((state) => {
806
- if (state.session.id === data.callId) {
807
- state.session.status = "ENDED";
808
- state.room.participants = {};
955
+ if (state.session?.id === data.callId) {
956
+ state.session.livekitInfo = {
957
+ token: data.token,
958
+ roomName: data.roomName,
959
+ url: data.lkUrl
960
+ };
961
+ state.session.role = data.role;
809
962
  }
810
963
  });
964
+ eventBus.emit("join-info:received" /* JOIN_INFO_RECEIVED */, {
965
+ callId: data.callId,
966
+ participantId: data.participantId || "",
967
+ timestamp: Date.now(),
968
+ url: data.lkUrl,
969
+ token: data.token
970
+ });
971
+ this.logger.debug("Session updated with LiveKit credentials");
972
+ }
973
+ };
974
+
975
+ // src/core/socketio/handlers/participant-added.handler.ts
976
+ var ParticipantAddedHandler = class extends BaseSocketHandler {
977
+ constructor() {
978
+ super(...arguments);
979
+ this.eventName = "call:participantAdded";
980
+ this.schema = callParticipantAddedSchema;
981
+ }
982
+ handle(data) {
983
+ const currentState = rtcStore.getState();
984
+ this.logger.info("Participant added to call", {
985
+ callId: data.callId,
986
+ participantId: data.participant.participantId,
987
+ userId: data.participant.userId,
988
+ role: data.participant.role
989
+ });
990
+ if (currentState.session?.id !== data.callId) {
991
+ pushStaleEventError("call:participantAdded", "callId mismatch", {
992
+ eventCallId: data.callId,
993
+ sessionCallId: currentState.session?.id
994
+ });
995
+ this.logger.warn("Ignoring participant added for different call", { callId: data.callId });
996
+ return;
997
+ }
998
+ eventBus.emit("participant:updated" /* PARTICIPANT_UPDATED */, {
999
+ participantId: data.participant.participantId,
1000
+ timestamp: Date.now()
1001
+ });
1002
+ this.logger.debug("Participant added event processed", {
1003
+ participantId: data.participant.participantId
1004
+ });
811
1005
  }
812
1006
  };
813
1007
 
814
1008
  // src/core/socketio/handlers/handler.registry.ts
815
- var logger2 = createLogger("socketio:registry");
1009
+ var logger = createLogger("socketio:registry");
816
1010
  var SocketHandlerRegistry = class {
817
1011
  constructor(options = {}) {
818
1012
  this.options = options;
@@ -821,34 +1015,52 @@ var SocketHandlerRegistry = class {
821
1015
  }
822
1016
  initializeHandlers() {
823
1017
  const handlers = [
824
- new CallIncomingHandler(this.options),
825
- new CallParticipantAcceptedHandler(this.options),
826
- new CallParticipantDeclinedHandler(this.options),
827
- new CallEndedHandler(this.options),
828
- new CallJoinInfoHandler(this.options),
829
- new CallTimeoutHandler(this.options),
830
- new CallCanceledHandler(this.options)
1018
+ // Phase 1: Core flow handlers
1019
+ new InviteHandler(this.options),
1020
+ new JoinInfoHandler(this.options),
1021
+ new SessionEndedHandler(this.options),
1022
+ new SessionCreatedHandler(this.options),
1023
+ // Phase 2: Invite management handlers
1024
+ new InviteAcceptedHandler(this.options),
1025
+ new InviteDeclinedHandler(this.options),
1026
+ new InviteCancelledHandler(this.options),
1027
+ new InviteSentHandler(this.options),
1028
+ // Phase 3: Edge case handlers
1029
+ new InviteMissedHandler(this.options),
1030
+ new SessionCancelledHandler(this.options),
1031
+ new SessionMissedHandler(this.options),
1032
+ new ParticipantAddedHandler(this.options)
831
1033
  ];
832
1034
  for (const handler of handlers) {
833
1035
  this.handlers.set(handler.eventName, handler);
834
1036
  }
1037
+ logger.info(`Registered ${handlers.length} socket event handlers`);
835
1038
  }
836
1039
  registerEventListeners(socket) {
837
1040
  for (const [eventName, handler] of this.handlers) {
838
1041
  socket.on(eventName, (rawData) => {
839
1042
  handler.handleRaw(rawData).catch((error) => {
840
- logger2.error(`Handler error for ${eventName}:`, error);
1043
+ logger.error(`Handler error for ${eventName}:`, error);
841
1044
  });
842
1045
  });
843
1046
  }
1047
+ logger.debug("Event listeners registered on socket");
844
1048
  }
845
1049
  removeEventListeners(socket) {
846
1050
  for (const eventName of this.handlers.keys()) {
847
1051
  socket.off(eventName);
848
1052
  }
1053
+ logger.debug("Event listeners removed from socket");
849
1054
  }
850
1055
  destroy() {
851
1056
  this.handlers.clear();
1057
+ logger.debug("Handler registry destroyed");
1058
+ }
1059
+ /**
1060
+ * Get all registered event names
1061
+ */
1062
+ getRegisteredEvents() {
1063
+ return Array.from(this.handlers.keys());
852
1064
  }
853
1065
  };
854
1066
 
@@ -1402,17 +1614,17 @@ var request = (config, options) => {
1402
1614
  // src/generated/api/services.ts
1403
1615
  var CallsService = class {
1404
1616
  /**
1405
- * @returns any Call started successfully
1617
+ * @returns any Invites sent successfully
1406
1618
  * @throws ApiError
1407
1619
  */
1408
- static postSignalCalls(data) {
1620
+ static postSignalCallsInvite(data) {
1409
1621
  const {
1410
1622
  appId,
1411
1623
  requestBody
1412
1624
  } = data;
1413
1625
  return request(OpenAPI, {
1414
1626
  method: "POST",
1415
- url: "/signal/calls",
1627
+ url: "/signal/calls/invite",
1416
1628
  query: {
1417
1629
  appId
1418
1630
  },
@@ -1420,7 +1632,9 @@ var CallsService = class {
1420
1632
  mediaType: "application/json",
1421
1633
  errors: {
1422
1634
  400: `Invalid request`,
1423
- 401: `Authentication required`
1635
+ 401: `Authentication required`,
1636
+ 403: `User does not have permission to invite`,
1637
+ 404: `Call not found`
1424
1638
  }
1425
1639
  });
1426
1640
  }
@@ -1458,7 +1672,8 @@ var CallsService = class {
1458
1672
  static postSignalCallsByCallIdDecline(data) {
1459
1673
  const {
1460
1674
  callId,
1461
- appId
1675
+ appId,
1676
+ requestBody
1462
1677
  } = data;
1463
1678
  return request(OpenAPI, {
1464
1679
  method: "POST",
@@ -1469,6 +1684,8 @@ var CallsService = class {
1469
1684
  query: {
1470
1685
  appId
1471
1686
  },
1687
+ body: requestBody,
1688
+ mediaType: "application/json",
1472
1689
  errors: {
1473
1690
  400: `Invalid request`,
1474
1691
  401: `Authentication required`,
@@ -1479,17 +1696,17 @@ var CallsService = class {
1479
1696
  });
1480
1697
  }
1481
1698
  /**
1482
- * @returns any Left call successfully
1699
+ * @returns any Call cancelled successfully
1483
1700
  * @throws ApiError
1484
1701
  */
1485
- static postSignalCallsByCallIdLeave(data) {
1702
+ static postSignalCallsByCallIdCancel(data) {
1486
1703
  const {
1487
1704
  callId,
1488
1705
  appId
1489
1706
  } = data;
1490
1707
  return request(OpenAPI, {
1491
1708
  method: "POST",
1492
- url: "/signal/calls/{callId}/leave",
1709
+ url: "/signal/calls/{callId}/cancel",
1493
1710
  path: {
1494
1711
  callId
1495
1712
  },
@@ -1499,151 +1716,180 @@ var CallsService = class {
1499
1716
  errors: {
1500
1717
  400: `Invalid request`,
1501
1718
  401: `Authentication required`,
1502
- 403: `User is not a participant`,
1719
+ 403: `User does not have permission to cancel`,
1503
1720
  404: `Call not found`,
1504
- 409: `Call cannot be left in current state`
1721
+ 409: `Call cannot be cancelled in current state`
1505
1722
  }
1506
1723
  });
1507
1724
  }
1508
1725
  /**
1509
- * @returns any Call retrieved successfully
1726
+ * @returns any Transfer initiated successfully
1510
1727
  * @throws ApiError
1511
1728
  */
1512
- static getSignalCallsByCallId(data) {
1729
+ static postSignalCallsByCallIdTransfer(data) {
1513
1730
  const {
1514
1731
  callId,
1515
- appId
1732
+ appId,
1733
+ requestBody
1516
1734
  } = data;
1517
1735
  return request(OpenAPI, {
1518
- method: "GET",
1519
- url: "/signal/calls/{callId}",
1736
+ method: "POST",
1737
+ url: "/signal/calls/{callId}/transfer",
1520
1738
  path: {
1521
1739
  callId
1522
1740
  },
1523
1741
  query: {
1524
1742
  appId
1525
1743
  },
1744
+ body: requestBody,
1745
+ mediaType: "application/json",
1526
1746
  errors: {
1527
1747
  400: `Invalid request`,
1528
1748
  401: `Authentication required`,
1529
- 403: `User does not have access to this call`,
1530
- 404: `Call not found`
1749
+ 403: `User does not have permission to transfer`,
1750
+ 404: `Call not found`,
1751
+ 409: `Call cannot be transferred in current state`
1531
1752
  }
1532
1753
  });
1533
1754
  }
1534
1755
  /**
1535
- * @returns any Participant info retrieved successfully
1756
+ * @returns any Participant kicked successfully
1536
1757
  * @throws ApiError
1537
1758
  */
1538
- static getSignalCallsParticipantsByIdentity(data) {
1759
+ static postSignalCallsByCallIdKick(data) {
1539
1760
  const {
1540
- identity,
1541
- appId
1761
+ callId,
1762
+ appId,
1763
+ requestBody
1542
1764
  } = data;
1543
1765
  return request(OpenAPI, {
1544
- method: "GET",
1545
- url: "/signal/calls/participants/{identity}",
1766
+ method: "POST",
1767
+ url: "/signal/calls/{callId}/kick",
1546
1768
  path: {
1547
- identity
1769
+ callId
1548
1770
  },
1549
1771
  query: {
1550
1772
  appId
1551
1773
  },
1774
+ body: requestBody,
1775
+ mediaType: "application/json",
1552
1776
  errors: {
1553
1777
  400: `Invalid request`,
1554
1778
  401: `Authentication required`,
1555
- 404: `Participant not found`
1779
+ 403: `User does not have permission to kick`,
1780
+ 404: `Call not found`,
1781
+ 409: `Participant cannot be kicked in current state`
1556
1782
  }
1557
1783
  });
1558
1784
  }
1559
- };
1560
-
1561
- // src/core/signal/signal.client.ts
1562
- var SignalClient = class {
1563
- constructor(config) {
1564
- this.logger = createLogger("signal");
1565
- this.config = config;
1566
- }
1567
- async initiate(params) {
1568
- try {
1569
- return await CallsService.postSignalCalls({
1570
- appId: this.config.appId,
1571
- requestBody: {
1572
- mode: params.mode || "AUDIO",
1573
- participants: params.invitees.map((userId) => ({ userId }))
1574
- }
1575
- });
1576
- } catch (error) {
1577
- this.handleApiError("initiate", error);
1578
- throw error;
1579
- }
1580
- }
1581
- async accept(callId) {
1582
- try {
1583
- const response = await CallsService.postSignalCallsByCallIdAccept({
1584
- callId,
1585
- appId: this.config.appId
1586
- });
1587
- return {
1588
- ...response,
1589
- callId,
1590
- state: "ACTIVE",
1591
- message: "Call accepted"
1592
- };
1593
- } catch (error) {
1594
- this.handleApiError("accept", error);
1595
- throw error;
1596
- }
1597
- }
1598
- async decline(callId) {
1599
- try {
1600
- const response = await CallsService.postSignalCallsByCallIdDecline({
1601
- callId,
1602
- appId: this.config.appId
1603
- });
1604
- return {
1605
- ...response,
1606
- callId,
1607
- state: "ENDED",
1608
- message: "Call declined"
1609
- };
1610
- } catch (error) {
1611
- this.handleApiError("decline", error);
1612
- throw error;
1613
- }
1785
+ /**
1786
+ * @returns any Participant muted successfully
1787
+ * @throws ApiError
1788
+ */
1789
+ static postSignalCallsByCallIdMute(data) {
1790
+ const {
1791
+ callId,
1792
+ appId,
1793
+ requestBody
1794
+ } = data;
1795
+ return request(OpenAPI, {
1796
+ method: "POST",
1797
+ url: "/signal/calls/{callId}/mute",
1798
+ path: {
1799
+ callId
1800
+ },
1801
+ query: {
1802
+ appId
1803
+ },
1804
+ body: requestBody,
1805
+ mediaType: "application/json",
1806
+ errors: {
1807
+ 400: `Invalid request`,
1808
+ 401: `Authentication required`,
1809
+ 403: `User does not have permission to mute`,
1810
+ 404: `Call not found`,
1811
+ 409: `Participant cannot be muted in current state`
1812
+ }
1813
+ });
1614
1814
  }
1615
- async leave(callId) {
1616
- try {
1617
- const response = await CallsService.postSignalCallsByCallIdLeave({
1618
- callId,
1619
- appId: this.config.appId
1620
- });
1621
- return {
1622
- ...response,
1623
- callId,
1624
- state: "ENDED",
1625
- message: "Left call"
1626
- };
1627
- } catch (error) {
1628
- this.handleApiError("leave", error);
1629
- throw error;
1630
- }
1815
+ /**
1816
+ * @returns any Call ended successfully
1817
+ * @throws ApiError
1818
+ */
1819
+ static postSignalCallsByCallIdEnd(data) {
1820
+ const {
1821
+ callId,
1822
+ appId
1823
+ } = data;
1824
+ return request(OpenAPI, {
1825
+ method: "POST",
1826
+ url: "/signal/calls/{callId}/end",
1827
+ path: {
1828
+ callId
1829
+ },
1830
+ query: {
1831
+ appId
1832
+ },
1833
+ errors: {
1834
+ 400: `Invalid request`,
1835
+ 401: `Authentication required`,
1836
+ 403: `Only host can end call`,
1837
+ 404: `Call not found`
1838
+ }
1839
+ });
1631
1840
  }
1632
- handleApiError(operation, error) {
1633
- const errorMessage = error?.body?.message || error?.message || "Unknown error";
1634
- const errorCode = error?.status || error?.code || "UNKNOWN";
1635
- this.logger.error(`Signal API error during ${operation}`, {
1636
- operation,
1637
- errorCode,
1638
- errorMessage,
1639
- error
1841
+ /**
1842
+ * @returns any Left call successfully
1843
+ * @throws ApiError
1844
+ */
1845
+ static postSignalCallsByCallIdLeave(data) {
1846
+ const {
1847
+ callId,
1848
+ appId
1849
+ } = data;
1850
+ return request(OpenAPI, {
1851
+ method: "POST",
1852
+ url: "/signal/calls/{callId}/leave",
1853
+ path: {
1854
+ callId
1855
+ },
1856
+ query: {
1857
+ appId
1858
+ },
1859
+ errors: {
1860
+ 400: `Invalid request`,
1861
+ 401: `Authentication required`,
1862
+ 404: `Call not found`
1863
+ }
1640
1864
  });
1641
1865
  }
1642
1866
  };
1643
1867
 
1644
1868
  // src/core/signal/api.config.ts
1869
+ var logger2 = createLogger("api-config");
1645
1870
  var OpenApiConfigService = class _OpenApiConfigService {
1646
1871
  constructor() {
1872
+ this.configured = false;
1873
+ OpenAPI.interceptors.request.use((request2) => {
1874
+ const headers = request2.headers;
1875
+ let authHeader = null;
1876
+ if (headers instanceof Headers) {
1877
+ authHeader = headers.get("Authorization") || null;
1878
+ } else if (headers && typeof headers === "object") {
1879
+ authHeader = headers["Authorization"] || null;
1880
+ }
1881
+ if (authHeader) {
1882
+ logger2.debug("API Request with Authorization header", {
1883
+ hasAuth: true,
1884
+ authPrefix: authHeader.substring(0, 20) + "..."
1885
+ });
1886
+ } else {
1887
+ logger2.warn("API Request WITHOUT Authorization header", {
1888
+ hasAuth: false
1889
+ });
1890
+ }
1891
+ return request2;
1892
+ });
1647
1893
  }
1648
1894
  static getInstance() {
1649
1895
  if (!_OpenApiConfigService.instance) {
@@ -1657,7 +1903,15 @@ var OpenApiConfigService = class _OpenApiConfigService {
1657
1903
  const tokenFn = config.token;
1658
1904
  OpenAPI.TOKEN = async (_options) => {
1659
1905
  const result = tokenFn();
1660
- return typeof result === "string" ? result : await result;
1906
+ const token = typeof result === "string" ? result : await result;
1907
+ if (!token) {
1908
+ logger2.warn("Token provider returned empty token");
1909
+ } else {
1910
+ logger2.debug("Token resolved successfully", {
1911
+ tokenPrefix: token.substring(0, 10) + "..."
1912
+ });
1913
+ }
1914
+ return token;
1661
1915
  };
1662
1916
  } else {
1663
1917
  OpenAPI.TOKEN = config.token;
@@ -1667,6 +1921,14 @@ var OpenApiConfigService = class _OpenApiConfigService {
1667
1921
  if (config.headers) {
1668
1922
  OpenAPI.HEADERS = config.headers;
1669
1923
  }
1924
+ this.configured = true;
1925
+ logger2.info("API configuration completed", {
1926
+ baseUrl: config.baseUrl,
1927
+ hasToken: !!config.token
1928
+ });
1929
+ }
1930
+ isConfigured() {
1931
+ return this.configured;
1670
1932
  }
1671
1933
  setToken(token) {
1672
1934
  OpenAPI.TOKEN = token;
@@ -1674,92 +1936,119 @@ var OpenApiConfigService = class _OpenApiConfigService {
1674
1936
  };
1675
1937
  var apiConfig = OpenApiConfigService.getInstance();
1676
1938
 
1677
- // src/services/call-actions.ts
1678
- function createCallActions(signal, auth) {
1679
- const logger3 = createLogger("call-actions");
1939
+ // src/services/calls.service.ts
1940
+ var logger3 = createLogger("calls-service");
1941
+ function createCallsService(config) {
1942
+ const { appId } = config;
1943
+ const ensureApiConfigured = () => {
1944
+ if (!apiConfig.isConfigured()) {
1945
+ logger3.error("API not configured before making call service request");
1946
+ throw new Error(
1947
+ "API configuration missing. Ensure the SDK is properly initialized."
1948
+ );
1949
+ }
1950
+ };
1680
1951
  async function initiate(params) {
1681
- const response = await signal.initiate(params);
1682
- rtcStore.getState().patch((state) => {
1683
- state.session = {
1684
- id: response.id,
1685
- status: "CALLING",
1686
- // Caller initiated, waiting for acceptance
1687
- mode: response.mode,
1688
- // Identity context: I initiated this call, so I'm the caller
1689
- myRole: "CALLER",
1690
- initiatedByMe: true
1691
- };
1692
- state.room.participants = {};
1693
- logger3.debug("Call initiated - waiting for participants to join", {
1694
- callId: response.id,
1695
- invitedCount: response.participants?.length || 0
1696
- });
1952
+ ensureApiConfigured();
1953
+ const requestBody = {
1954
+ mode: params.mode || "AUDIO",
1955
+ participants: params.invitees.map((userId) => ({ userId }))
1956
+ };
1957
+ if (params.callId) {
1958
+ requestBody.callId = params.callId;
1959
+ }
1960
+ return CallsService.postSignalCallsInvite({
1961
+ appId,
1962
+ requestBody
1697
1963
  });
1698
- return response;
1699
1964
  }
1700
1965
  async function accept(callId) {
1701
- const response = await signal.accept(callId);
1702
- rtcStore.getState().patch((state) => {
1703
- state.session = {
1704
- ...state.session,
1705
- id: callId,
1706
- status: "ACCEPTED",
1707
- // Call accepted but not yet joined media
1708
- myRole: "CALLEE",
1709
- initiatedByMe: false
1710
- };
1711
- state.room.participants = {};
1966
+ ensureApiConfigured();
1967
+ return CallsService.postSignalCallsByCallIdAccept({
1968
+ callId,
1969
+ appId
1712
1970
  });
1713
- return response;
1714
1971
  }
1715
1972
  async function decline(callId, reason) {
1716
- logger3.debug("Starting decline action", { callId, reason });
1717
- try {
1718
- const response = await signal.decline(callId);
1719
- logger3.info("Decline API success", { callId, response });
1720
- rtcStore.getState().patch((state) => {
1721
- if (state.session.id === callId) {
1722
- state.session.status = response.state;
1723
- }
1724
- });
1725
- eventBus.emit(
1726
- "call:declined" /* CALL_DECLINED */,
1727
- {
1728
- callId,
1729
- reason,
1730
- timestamp: Date.now()
1731
- },
1732
- "user"
1733
- );
1734
- return response;
1735
- } catch (error) {
1736
- logger3.error("Decline API failed", { callId, error });
1737
- rtcStore.getState().patch((state) => {
1738
- state.session.status = "IDLE";
1739
- logger3.warn("Force-cleared session due to API failure");
1740
- });
1741
- eventBus.emit(
1742
- "call:declined" /* CALL_DECLINED */,
1743
- {
1744
- callId,
1745
- reason: "api_error",
1746
- timestamp: Date.now()
1747
- },
1748
- "user"
1749
- );
1750
- throw error;
1973
+ ensureApiConfigured();
1974
+ const payload = {
1975
+ callId,
1976
+ appId
1977
+ };
1978
+ if (reason) {
1979
+ payload.requestBody = { reason };
1751
1980
  }
1981
+ return CallsService.postSignalCallsByCallIdDecline(payload);
1982
+ }
1983
+ async function cancel(callId) {
1984
+ ensureApiConfigured();
1985
+ return CallsService.postSignalCallsByCallIdCancel({
1986
+ callId,
1987
+ appId
1988
+ });
1752
1989
  }
1753
1990
  async function leave(callId) {
1754
- rtcStore.getState().patch((state) => {
1755
- state.session.status = "IDLE";
1991
+ ensureApiConfigured();
1992
+ return CallsService.postSignalCallsByCallIdLeave({
1993
+ callId,
1994
+ appId
1995
+ });
1996
+ }
1997
+ async function end(callId) {
1998
+ ensureApiConfigured();
1999
+ return CallsService.postSignalCallsByCallIdEnd({
2000
+ callId,
2001
+ appId
2002
+ });
2003
+ }
2004
+ async function transfer(callId, targetParticipantId, reason) {
2005
+ ensureApiConfigured();
2006
+ const requestBody = {
2007
+ targetParticipantId
2008
+ };
2009
+ if (reason) {
2010
+ requestBody.reason = reason;
2011
+ }
2012
+ return CallsService.postSignalCallsByCallIdTransfer({
2013
+ callId,
2014
+ appId,
2015
+ requestBody
2016
+ });
2017
+ }
2018
+ async function kick(callId, participantId, reason) {
2019
+ ensureApiConfigured();
2020
+ const requestBody = {
2021
+ participantId
2022
+ };
2023
+ if (reason) {
2024
+ requestBody.reason = reason;
2025
+ }
2026
+ return CallsService.postSignalCallsByCallIdKick({
2027
+ callId,
2028
+ appId,
2029
+ requestBody
2030
+ });
2031
+ }
2032
+ async function mute(callId, participantId) {
2033
+ ensureApiConfigured();
2034
+ return CallsService.postSignalCallsByCallIdMute({
2035
+ callId,
2036
+ appId,
2037
+ requestBody: {
2038
+ participantId
2039
+ }
1756
2040
  });
1757
2041
  }
1758
2042
  return {
1759
2043
  initiate,
1760
2044
  accept,
1761
2045
  decline,
1762
- leave
2046
+ cancel,
2047
+ leave,
2048
+ end,
2049
+ transfer,
2050
+ kick,
2051
+ mute
1763
2052
  };
1764
2053
  }
1765
2054
 
@@ -1778,12 +2067,14 @@ function buildSdk(opts) {
1778
2067
  setGlobalLoggerOptions(loggerOptions);
1779
2068
  const auth = new AuthManager(opts.authProvider);
1780
2069
  const socket = SocketManager.getInstance();
1781
- const signal = new SignalClient({
2070
+ const callsService = createCallsService({ appId: opts.appId });
2071
+ apiConfig.configure({
1782
2072
  baseUrl: opts.signalHost,
1783
- appId: opts.appId,
1784
- authManager: auth
2073
+ token: async () => {
2074
+ const token = auth.getCurrentToken();
2075
+ return token || "";
2076
+ }
1785
2077
  });
1786
- const callActions = createCallActions(signal);
1787
2078
  const cleanup = () => {
1788
2079
  socket.destroy();
1789
2080
  rtcStore.getState().reset();
@@ -1792,10 +2083,9 @@ function buildSdk(opts) {
1792
2083
  store: rtcStore,
1793
2084
  auth,
1794
2085
  socket,
1795
- signal,
1796
- ...callActions,
2086
+ calls: callsService,
1797
2087
  cleanup,
1798
- // API configuration
2088
+ // API configuration - can be called again to override if needed
1799
2089
  configureApi: (config) => {
1800
2090
  apiConfig.configure(config);
1801
2091
  }
@@ -1810,24 +2100,6 @@ function RtcProvider({
1810
2100
  }) {
1811
2101
  const sdk = React.useMemo(() => buildSdk(options), [options]);
1812
2102
  React.useEffect(() => {
1813
- try {
1814
- sdk.configureApi({
1815
- baseUrl: options.signalHost,
1816
- token: async () => {
1817
- const token = sdk.auth.getCurrentToken();
1818
- return token || "";
1819
- }
1820
- });
1821
- options.log?.("info", "API configured successfully");
1822
- } catch (error) {
1823
- options.log?.("error", "Failed to configure API", error);
1824
- rtcStore.getState().addError({
1825
- code: "API_CONFIG_ERROR",
1826
- message: "Failed to configure API",
1827
- timestamp: Date.now(),
1828
- context: error
1829
- });
1830
- }
1831
2103
  sdk.socket.initialize(
1832
2104
  options.signalHost,
1833
2105
  sdk.auth,
@@ -1862,36 +2134,104 @@ var useSdk = () => {
1862
2134
  // src/hooks/useCallState.ts
1863
2135
  function useCallState() {
1864
2136
  const session = useRtcStore((state) => state.session);
2137
+ if (!session) {
2138
+ return {
2139
+ id: null,
2140
+ status: null,
2141
+ mode: null,
2142
+ roomName: null
2143
+ };
2144
+ }
1865
2145
  return {
1866
2146
  id: session.id,
1867
2147
  status: session.status,
1868
2148
  mode: session.mode,
1869
- roomName: session.livekitInfo?.roomName
2149
+ roomName: session.livekitInfo?.roomName || null
1870
2150
  };
1871
2151
  }
1872
2152
 
2153
+ // src/hooks/useSessionId.ts
2154
+ function useSessionId() {
2155
+ return useRtcStore((state) => state.session?.id ?? null);
2156
+ }
2157
+
2158
+ // src/hooks/useIncomingInvite.ts
2159
+ function useIncomingInvite() {
2160
+ return useRtcStore((state) => state.incomingInvite);
2161
+ }
2162
+
1873
2163
  // src/hooks/useCallActions.ts
1874
2164
  function useCallActions() {
1875
2165
  const sdk = useSdk();
2166
+ const sessionId = useSessionId();
2167
+ const incomingInvite = useIncomingInvite();
1876
2168
  return {
1877
2169
  initiate: (participants, type) => {
1878
- return sdk.initiate({ invitees: participants, mode: type });
2170
+ return sdk.calls.initiate({ invitees: participants, mode: type });
2171
+ },
2172
+ invite: (participants) => {
2173
+ if (!sessionId) {
2174
+ throw new Error("No active session to invite participants to");
2175
+ }
2176
+ const session = sdk.store.getState().session;
2177
+ const mode = session?.mode || "VIDEO";
2178
+ return sdk.calls.initiate({
2179
+ invitees: participants,
2180
+ mode,
2181
+ callId: sessionId
2182
+ });
2183
+ },
2184
+ accept: () => {
2185
+ if (!incomingInvite) {
2186
+ throw new Error("No incoming invite to accept");
2187
+ }
2188
+ return sdk.calls.accept(incomingInvite.callId);
2189
+ },
2190
+ decline: (reason) => {
2191
+ if (!incomingInvite) {
2192
+ throw new Error("No incoming invite to decline");
2193
+ }
2194
+ return sdk.calls.decline(incomingInvite.callId, reason);
2195
+ },
2196
+ cancel: () => {
2197
+ if (!sessionId) {
2198
+ throw new Error("No active session to cancel");
2199
+ }
2200
+ return sdk.calls.cancel(sessionId);
2201
+ },
2202
+ leave: () => {
2203
+ if (!sessionId) {
2204
+ throw new Error("No active session to leave");
2205
+ }
2206
+ return sdk.calls.leave(sessionId);
1879
2207
  },
1880
- accept: (callId) => {
1881
- return sdk.accept(callId);
2208
+ end: () => {
2209
+ if (!sessionId) {
2210
+ throw new Error("No active session to end");
2211
+ }
2212
+ return sdk.calls.end(sessionId);
1882
2213
  },
1883
- decline: (callId) => {
1884
- return sdk.decline(callId);
2214
+ transfer: (targetParticipantId, reason) => {
2215
+ if (!sessionId) {
2216
+ throw new Error("No active session to transfer");
2217
+ }
2218
+ return sdk.calls.transfer(sessionId, targetParticipantId, reason);
1885
2219
  },
1886
- end: (callId) => {
1887
- return sdk.leave(callId);
2220
+ kick: (participantId, reason) => {
2221
+ if (!sessionId) {
2222
+ throw new Error("No active session to kick participant from");
2223
+ }
2224
+ return sdk.calls.kick(sessionId, participantId, reason);
1888
2225
  },
1889
- cancel: (callId) => {
1890
- return sdk.leave(callId);
2226
+ mute: (participantId) => {
2227
+ if (!sessionId) {
2228
+ throw new Error("No active session to mute participant in");
2229
+ }
2230
+ return sdk.calls.mute(sessionId, participantId);
1891
2231
  }
1892
2232
  };
1893
2233
  }
1894
- function useEvent(eventType, callback, filter) {
2234
+ function useEvent(eventType, callback) {
1895
2235
  const [lastEvent, setLastEvent] = React.useState(
1896
2236
  void 0
1897
2237
  );
@@ -1904,17 +2244,111 @@ function useEvent(eventType, callback, filter) {
1904
2244
  callbackRef.current(event);
1905
2245
  }
1906
2246
  };
1907
- const subscription = eventType.includes("*") ? eventBus.onPattern(eventType, handler, filter) : eventBus.on(eventType, handler, filter);
2247
+ const subscription = eventBus.on(eventType, handler);
1908
2248
  return () => {
1909
2249
  subscription.unsubscribe();
1910
2250
  };
1911
- }, [eventType, filter]);
2251
+ }, [eventType]);
1912
2252
  return lastEvent;
1913
2253
  }
1914
2254
 
2255
+ // src/hooks/useOutgoingInvites.ts
2256
+ function useOutgoingInvites() {
2257
+ return useRtcStore((state) => state.outgoingInvites);
2258
+ }
2259
+
2260
+ // src/hooks/useInviteAccepted.ts
2261
+ function useInviteAccepted() {
2262
+ return useRtcStore((state) => {
2263
+ return Object.values(state.outgoingInvites).some(
2264
+ (invite) => invite.status === "accepted"
2265
+ );
2266
+ });
2267
+ }
2268
+
2269
+ // src/hooks/useCallInitiated.ts
2270
+ function useCallInitiated() {
2271
+ return useRtcStore((state) => state.initiated);
2272
+ }
2273
+
2274
+ // src/hooks/useSessionDuration.ts
2275
+ function useSessionDuration() {
2276
+ return 0;
2277
+ }
2278
+ var defaultRoomOptions = {
2279
+ adaptiveStream: true,
2280
+ dynacast: true
2281
+ };
2282
+ function useRoom(options) {
2283
+ const [room] = React.useState(() => new livekitClient.Room(options || defaultRoomOptions));
2284
+ const livekitInfo = useRtcStore((state) => state.session?.livekitInfo);
2285
+ React.useEffect(() => {
2286
+ if (!livekitInfo) {
2287
+ return;
2288
+ }
2289
+ room.connect(livekitInfo.url, livekitInfo.token);
2290
+ return () => {
2291
+ room.disconnect();
2292
+ };
2293
+ }, [livekitInfo, room]);
2294
+ if (!livekitInfo) {
2295
+ return null;
2296
+ }
2297
+ return room;
2298
+ }
2299
+ function useParticipantMetadata(participantOrIdentity) {
2300
+ const room = useRoom();
2301
+ const participants = componentsReact.useParticipants(room ? { room } : {});
2302
+ let resolvedParticipant;
2303
+ if (typeof participantOrIdentity === "string") {
2304
+ resolvedParticipant = participants.find(
2305
+ (p) => p.identity === participantOrIdentity
2306
+ );
2307
+ } else if (participantOrIdentity) {
2308
+ resolvedParticipant = participantOrIdentity;
2309
+ }
2310
+ const livekitInfo = componentsReact.useParticipantInfo(
2311
+ resolvedParticipant ? { participant: resolvedParticipant } : {}
2312
+ );
2313
+ if (!livekitInfo.metadata) {
2314
+ return null;
2315
+ }
2316
+ try {
2317
+ const metadata = JSON.parse(livekitInfo.metadata);
2318
+ if (!metadata.userId || !metadata.role) {
2319
+ console.warn("Invalid participant metadata: missing required fields", metadata);
2320
+ return null;
2321
+ }
2322
+ return {
2323
+ userId: metadata.userId,
2324
+ role: metadata.role,
2325
+ firstName: metadata.firstName || "",
2326
+ lastName: metadata.lastName || "",
2327
+ username: metadata.username || "",
2328
+ email: metadata.email || "",
2329
+ profilePhoto: metadata.profilePhoto || ""
2330
+ };
2331
+ } catch (error) {
2332
+ console.error("Failed to parse participant metadata:", error);
2333
+ return null;
2334
+ }
2335
+ }
2336
+
1915
2337
  exports.RtcProvider = RtcProvider;
1916
2338
  exports.SdkEventType = SdkEventType;
1917
2339
  exports.apiConfig = apiConfig;
2340
+ exports.callCancelledSchema = callCancelledSchema;
2341
+ exports.callCreatedSchema = callCreatedSchema;
2342
+ exports.callEndedSchema = callEndedSchema;
2343
+ exports.callInviteAcceptedSchema = callInviteAcceptedSchema;
2344
+ exports.callInviteCancelledSchema = callInviteCancelledSchema;
2345
+ exports.callInviteDeclinedSchema = callInviteDeclinedSchema;
2346
+ exports.callInviteMissedSchema = callInviteMissedSchema;
2347
+ exports.callInviteSchema = callInviteSchema;
2348
+ exports.callInviteSentSchema = callInviteSentSchema;
2349
+ exports.callJoinInfoSchema = callJoinInfoSchema;
2350
+ exports.callMissedSchema = callMissedSchema;
2351
+ exports.callParticipantAddedSchema = callParticipantAddedSchema;
1918
2352
  exports.clearErrors = clearErrors;
1919
2353
  exports.eventBus = eventBus;
1920
2354
  exports.pushApiError = pushApiError;
@@ -1927,8 +2361,16 @@ exports.pushNetworkError = pushNetworkError;
1927
2361
  exports.pushSocketValidationError = pushSocketValidationError;
1928
2362
  exports.pushStaleEventError = pushStaleEventError;
1929
2363
  exports.useCallActions = useCallActions;
2364
+ exports.useCallInitiated = useCallInitiated;
1930
2365
  exports.useCallState = useCallState;
1931
2366
  exports.useEvent = useEvent;
2367
+ exports.useIncomingInvite = useIncomingInvite;
2368
+ exports.useInviteAccepted = useInviteAccepted;
2369
+ exports.useOutgoingInvites = useOutgoingInvites;
2370
+ exports.useParticipantMetadata = useParticipantMetadata;
2371
+ exports.useRoom = useRoom;
1932
2372
  exports.useSdk = useSdk;
2373
+ exports.useSessionDuration = useSessionDuration;
2374
+ exports.useSessionId = useSessionId;
1933
2375
  //# sourceMappingURL=index.cjs.map
1934
2376
  //# sourceMappingURL=index.cjs.map