react-jssip-kit 1.0.0 → 1.0.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/dist/index.js CHANGED
@@ -290,19 +290,35 @@ var SipStateStore = class {
290
290
  constructor() {
291
291
  this.state = getInitialSipState();
292
292
  this.lastState = getInitialSipState();
293
+ this.publicState = {
294
+ sipStatus: this.state.sipStatus,
295
+ error: this.state.error,
296
+ sessions: this.state.sessions
297
+ };
293
298
  this.listeners = /* @__PURE__ */ new Set();
299
+ this.publicListeners = /* @__PURE__ */ new Set();
294
300
  this.pendingState = null;
295
301
  this.updateScheduled = false;
296
302
  }
297
303
  getState() {
298
304
  return this.state;
299
305
  }
306
+ getPublicState() {
307
+ return this.publicState;
308
+ }
300
309
  onChange(fn) {
301
310
  this.listeners.add(fn);
302
311
  fn(this.state);
303
312
  return () => this.listeners.delete(fn);
304
313
  }
314
+ onPublicChange(fn) {
315
+ this.publicListeners.add(fn);
316
+ return () => this.publicListeners.delete(fn);
317
+ }
305
318
  subscribe(fn) {
319
+ return this.onPublicChange(fn);
320
+ }
321
+ subscribeInternal(fn) {
306
322
  return this.onChange(fn);
307
323
  }
308
324
  setState(partial) {
@@ -314,6 +330,11 @@ var SipStateStore = class {
314
330
  }
315
331
  this.state = next;
316
332
  this.lastState = next;
333
+ this.publicState = {
334
+ sipStatus: next.sipStatus,
335
+ error: next.error,
336
+ sessions: next.sessions
337
+ };
317
338
  this.emit();
318
339
  }
319
340
  batchSet(partial) {
@@ -334,6 +355,8 @@ var SipStateStore = class {
334
355
  emit() {
335
356
  for (const fn of this.listeners)
336
357
  fn(this.state);
358
+ for (const fn of this.publicListeners)
359
+ fn(this.publicState);
337
360
  }
338
361
  };
339
362
 
@@ -395,18 +418,52 @@ var SipDebugRuntime = class {
395
418
  };
396
419
 
397
420
  // src/core/modules/event/sip-event-manager.adapter.ts
421
+ function getSessionFromPayload(payload) {
422
+ return payload?.session ?? null;
423
+ }
424
+ function getSessionId(session) {
425
+ return String(session.id ?? "");
426
+ }
398
427
  function createSipEventManager(client) {
399
428
  return {
400
429
  onUA(event, handler) {
401
430
  return client.on(event, handler);
402
431
  },
403
432
  onSession(sessionId, event, handler) {
404
- const session = client.getSession(sessionId);
405
- if (!session)
406
- return () => {
407
- };
408
- session.on(event, handler);
409
- return () => session.off(event, handler);
433
+ const wrapped = (payload) => {
434
+ handler(payload);
435
+ };
436
+ let attachedSession = null;
437
+ const detach = () => {
438
+ if (!attachedSession)
439
+ return;
440
+ attachedSession.off(event, wrapped);
441
+ attachedSession = null;
442
+ };
443
+ const attach = (session) => {
444
+ if (!session)
445
+ return;
446
+ const id = getSessionId(session);
447
+ if (!id || id !== sessionId)
448
+ return;
449
+ if (attachedSession === session)
450
+ return;
451
+ detach();
452
+ attachedSession = session;
453
+ attachedSession.on(event, wrapped);
454
+ };
455
+ const offNewSession = client.on("newRTCSession", (payload) => {
456
+ attach(getSessionFromPayload(payload));
457
+ });
458
+ attach(client.getSession(sessionId) ?? null);
459
+ const offDisconnected = client.on("disconnected", () => {
460
+ detach();
461
+ });
462
+ return () => {
463
+ offNewSession();
464
+ offDisconnected();
465
+ detach();
466
+ };
410
467
  }
411
468
  };
412
469
  }
@@ -921,15 +978,6 @@ var SessionManager = class {
921
978
  };
922
979
 
923
980
  // src/core/modules/session/session.state.projector.ts
924
- function toSessionMaps(sessions) {
925
- const sessionsById = {};
926
- const sessionIds = [];
927
- for (const session of sessions) {
928
- sessionsById[session.id] = session;
929
- sessionIds.push(session.id);
930
- }
931
- return { sessionsById, sessionIds };
932
- }
933
981
  function holdOtherSessions(state, sessionId, holdFn) {
934
982
  const current = state.getState();
935
983
  current.sessionIds.forEach((id) => {
@@ -954,19 +1002,26 @@ function upsertSessionState(state, sessionId, partial) {
954
1002
  acceptedAt: null
955
1003
  };
956
1004
  const nextSession = { ...base, ...partial };
957
- const sessions = current.sessionIds.map(
958
- (id) => id === sessionId ? nextSession : current.sessionsById[id]
959
- );
960
- if (!existing) {
961
- sessions.push(nextSession);
962
- }
963
- const { sessionsById, sessionIds } = toSessionMaps(sessions);
964
- state.setState({ sessions, sessionsById, sessionIds });
1005
+ const sessionsById = {
1006
+ ...current.sessionsById,
1007
+ [sessionId]: nextSession
1008
+ };
1009
+ const sessionIds = existing ? current.sessionIds : [...current.sessionIds, sessionId];
1010
+ const sessions = existing ? current.sessions.map(
1011
+ (session) => session.id === sessionId ? nextSession : session
1012
+ ) : [...current.sessions, nextSession];
1013
+ state.setState({ sessionsById, sessionIds, sessions });
965
1014
  }
966
1015
  function removeSessionState(state, sessionId) {
967
1016
  const current = state.getState();
968
- const sessions = current.sessionIds.filter((id) => id !== sessionId).map((id) => current.sessionsById[id]);
969
- const { sessionsById, sessionIds } = toSessionMaps(sessions);
1017
+ if (!current.sessionsById[sessionId])
1018
+ return;
1019
+ const sessionsById = { ...current.sessionsById };
1020
+ delete sessionsById[sessionId];
1021
+ const sessionIds = current.sessionIds.filter((id) => id !== sessionId);
1022
+ const sessions = current.sessions.filter(
1023
+ (session) => session.id !== sessionId
1024
+ );
970
1025
  state.setState({
971
1026
  sessions,
972
1027
  sessionsById,
@@ -1144,18 +1199,18 @@ var SessionLifecycle = class {
1144
1199
  }
1145
1200
  handleNewRTCSession(e) {
1146
1201
  const session = e.session;
1147
- const sessionId = String(
1148
- session?.id ?? crypto.randomUUID?.() ?? Date.now()
1149
- );
1202
+ const sessionId = String(session.id ?? crypto.randomUUID?.() ?? Date.now());
1150
1203
  const currentSessions = this.state.getState().sessions;
1151
1204
  if (currentSessions.length >= this.getMaxSessionCount()) {
1152
1205
  try {
1153
- session.terminate?.({
1206
+ const terminateOptions = {
1154
1207
  status_code: 486,
1155
1208
  reason_phrase: "Busy Here"
1156
- });
1209
+ };
1210
+ session.terminate(terminateOptions);
1157
1211
  } catch {
1158
1212
  }
1213
+ return;
1159
1214
  }
1160
1215
  const rtc = this.sessionManager.getOrCreateRtc(sessionId, session);
1161
1216
  this.sessionManager.setSession(sessionId, session);
@@ -1254,7 +1309,7 @@ var SessionLifecycle = class {
1254
1309
  clearTimeout(retryTimer);
1255
1310
  retryTimer = null;
1256
1311
  };
1257
- const stopRetry = (opts = {}) => {
1312
+ const stopRetry = () => {
1258
1313
  if (stopped)
1259
1314
  return;
1260
1315
  stopped = true;
@@ -1355,8 +1410,8 @@ var SessionLifecycle = class {
1355
1410
  session.on?.("peerconnection", onPeer);
1356
1411
  }
1357
1412
  session.on?.("confirmed", onConfirmed);
1358
- session.on?.("ended", (e) => stopRetry());
1359
- session.on?.("failed", (e) => stopRetry());
1413
+ session.on?.("ended", () => stopRetry());
1414
+ session.on?.("failed", () => stopRetry());
1360
1415
  }
1361
1416
  bindRemoteIncomingAudio(sessionId, session) {
1362
1417
  const maxAttempts = 50;
@@ -1632,8 +1687,9 @@ var SessionModule = class {
1632
1687
  }
1633
1688
  hangupAll(options) {
1634
1689
  const ids = this.getSessionIds();
1635
- ids.forEach((id) => this.hangupSession(id, options));
1636
- return ids.length > 0;
1690
+ if (ids.length === 0)
1691
+ return false;
1692
+ return ids.every((id) => this.hangupSession(id, options));
1637
1693
  }
1638
1694
  toggleMuteSession(sessionId) {
1639
1695
  const resolved = this.resolveExistingSessionId(sessionId);
@@ -1662,7 +1718,7 @@ var SessionModule = class {
1662
1718
  this.deps.sessionManager.hold(resolved);
1663
1719
  return true;
1664
1720
  }
1665
- return true;
1721
+ return false;
1666
1722
  }
1667
1723
  sendDTMFSession(sessionId, tones, options) {
1668
1724
  const resolved = this.resolveExistingSessionId(sessionId);
@@ -1671,8 +1727,9 @@ var SessionModule = class {
1671
1727
  const sessionState = this.deps.state.getState().sessionsById[resolved];
1672
1728
  if (sessionState?.status === CallStatus.Active) {
1673
1729
  this.deps.sessionManager.sendDTMF(resolved, tones, options);
1730
+ return true;
1674
1731
  }
1675
- return true;
1732
+ return false;
1676
1733
  }
1677
1734
  transferSession(sessionId, target, options) {
1678
1735
  const resolved = this.resolveExistingSessionId(sessionId);
@@ -1681,9 +1738,37 @@ var SessionModule = class {
1681
1738
  const sessionState = this.deps.state.getState().sessionsById[resolved];
1682
1739
  if (sessionState?.status === CallStatus.Active) {
1683
1740
  this.deps.sessionManager.transfer(resolved, target, options);
1741
+ return true;
1684
1742
  }
1743
+ return false;
1744
+ }
1745
+ sendInfoSession(sessionId, contentType, body, options) {
1746
+ const resolved = this.resolveExistingSessionId(sessionId);
1747
+ if (!resolved)
1748
+ return false;
1749
+ const sessionState = this.deps.state.getState().sessionsById[resolved];
1750
+ if (sessionState?.status !== CallStatus.Active && sessionState?.status !== CallStatus.Hold) {
1751
+ return false;
1752
+ }
1753
+ const session = this.deps.sessionManager.getSession(resolved);
1754
+ if (!session)
1755
+ return false;
1756
+ session.sendInfo(contentType, body, options);
1685
1757
  return true;
1686
1758
  }
1759
+ updateSession(sessionId, options) {
1760
+ const resolved = this.resolveExistingSessionId(sessionId);
1761
+ if (!resolved)
1762
+ return false;
1763
+ const sessionState = this.deps.state.getState().sessionsById[resolved];
1764
+ if (sessionState?.status !== CallStatus.Active && sessionState?.status !== CallStatus.Hold) {
1765
+ return false;
1766
+ }
1767
+ const session = this.deps.sessionManager.getSession(resolved);
1768
+ if (!session)
1769
+ return false;
1770
+ return session.renegotiate(options);
1771
+ }
1687
1772
  getSession(sessionId) {
1688
1773
  return this.deps.sessionManager.getSession(sessionId);
1689
1774
  }
@@ -1726,7 +1811,9 @@ var SessionModule = class {
1726
1811
  }
1727
1812
  cleanupSession(sessionId, session) {
1728
1813
  const targetSession = session ?? this.deps.sessionManager.getSession(sessionId) ?? this.deps.sessionManager.getRtc(sessionId)?.currentSession;
1729
- this.detachSessionHandlers(sessionId, targetSession);
1814
+ if (targetSession) {
1815
+ this.detachSessionHandlers(sessionId, targetSession);
1816
+ }
1730
1817
  this.deps.micRecovery.disable(sessionId);
1731
1818
  this.deps.sessionManager.cleanupSession(sessionId);
1732
1819
  removeSessionState(this.deps.state, sessionId);
@@ -1881,8 +1968,8 @@ var SipClient = class extends EventTargetEmitter {
1881
1968
  getIceCandidateReadyDelayMs: () => this.iceCandidateReadyDelayMs
1882
1969
  });
1883
1970
  this.debugRuntime = new SipDebugRuntime({
1884
- getState: () => this.stateStore.getState(),
1885
- onChange: (listener) => this.stateStore.onChange(listener),
1971
+ getState: () => this.stateStore.getPublicState(),
1972
+ onChange: (listener) => this.stateStore.onPublicChange(listener),
1886
1973
  getSessions: () => this.getSessions(),
1887
1974
  setDebugEnabled: (enabled) => this.sessionModule.setDebugEnabled(enabled)
1888
1975
  });
@@ -1891,7 +1978,7 @@ var SipClient = class extends EventTargetEmitter {
1891
1978
  );
1892
1979
  }
1893
1980
  get state() {
1894
- return this.stateStore.getState();
1981
+ return this.stateStore.getPublicState();
1895
1982
  }
1896
1983
  connect(uri, password, config) {
1897
1984
  this.disconnect();
@@ -1936,9 +2023,12 @@ var SipClient = class extends EventTargetEmitter {
1936
2023
  const ua = this.userAgent.getUA();
1937
2024
  const session = ua?.call(target, callOptions);
1938
2025
  if (session && callOptions.mediaStream) {
1939
- const sessionId = String(session?.id ?? "");
2026
+ const sessionId = String(session.id ?? "");
1940
2027
  if (sessionId) {
1941
- this.sessionModule.setSessionMedia(sessionId, callOptions.mediaStream);
2028
+ this.sessionModule.setSessionMedia(
2029
+ sessionId,
2030
+ callOptions.mediaStream
2031
+ );
1942
2032
  this.sessionModule.setSession(sessionId, session);
1943
2033
  }
1944
2034
  }
@@ -1947,13 +2037,40 @@ var SipClient = class extends EventTargetEmitter {
1947
2037
  this.cleanupAllSessions();
1948
2038
  }
1949
2039
  }
2040
+ sendMessage(target, body, options) {
2041
+ try {
2042
+ const ua = this.userAgent.getUA();
2043
+ if (!ua)
2044
+ return false;
2045
+ ua.sendMessage(target, body, options);
2046
+ return true;
2047
+ } catch (e) {
2048
+ console.error(e);
2049
+ return false;
2050
+ }
2051
+ }
2052
+ sendOptions(target, body, options) {
2053
+ try {
2054
+ const ua = this.userAgent.getUA();
2055
+ if (!ua)
2056
+ return false;
2057
+ const optionsUa = ua;
2058
+ if (typeof optionsUa.sendOptions !== "function")
2059
+ return false;
2060
+ optionsUa.sendOptions(target, body, options);
2061
+ return true;
2062
+ } catch (e) {
2063
+ console.error(e);
2064
+ return false;
2065
+ }
2066
+ }
1950
2067
  hangupAll(options) {
1951
2068
  const ids = this.getSessionIds();
1952
2069
  ids.forEach((id) => this.hangupSession(id, options));
1953
2070
  return ids.length > 0;
1954
2071
  }
1955
2072
  onChange(fn) {
1956
- return this.stateStore.onChange(fn);
2073
+ return this.stateStore.onPublicChange(fn);
1957
2074
  }
1958
2075
  setDebug(debug) {
1959
2076
  this.debugPattern = debug;
@@ -1989,6 +2106,20 @@ var SipClient = class extends EventTargetEmitter {
1989
2106
  transferSession(sessionId, target, options) {
1990
2107
  return this.sessionModule.transferSession(sessionId, target, options);
1991
2108
  }
2109
+ sendInfoSession(sessionId, contentType, body, options) {
2110
+ return this.sessionModule.sendInfoSession(
2111
+ sessionId,
2112
+ contentType,
2113
+ body,
2114
+ options
2115
+ );
2116
+ }
2117
+ updateSession(sessionId, options) {
2118
+ return this.sessionModule.updateSession(sessionId, options);
2119
+ }
2120
+ reinviteSession(sessionId, options) {
2121
+ return this.sessionModule.updateSession(sessionId, options);
2122
+ }
1992
2123
  setSessionMedia(sessionId, stream) {
1993
2124
  this.sessionModule.setSessionMedia(sessionId, stream);
1994
2125
  }
@@ -2056,6 +2187,8 @@ function createSipKernel() {
2056
2187
  register: () => client.registerUA(),
2057
2188
  setDebug: (debug) => client.setDebug(debug),
2058
2189
  call: (target, options) => client.call(target, options),
2190
+ sendMessage: (target, body, options) => client.sendMessage(target, body, options),
2191
+ sendOptions: (target, body, options) => client.sendOptions(target, body, options),
2059
2192
  answer: (sessionId, options) => client.answerSession(sessionId, options),
2060
2193
  hangup: (sessionId, options) => client.hangupSession(sessionId, options),
2061
2194
  hangupAll: (options) => client.hangupAll(options),
@@ -2063,6 +2196,9 @@ function createSipKernel() {
2063
2196
  toggleHold: (sessionId) => client.toggleHoldSession(sessionId),
2064
2197
  sendDTMF: (sessionId, tones, options) => client.sendDTMFSession(sessionId, tones, options),
2065
2198
  transfer: (sessionId, target, options) => client.transferSession(sessionId, target, options),
2199
+ sendInfo: (sessionId, contentType, body, options) => client.sendInfoSession(sessionId, contentType, body, options),
2200
+ update: (sessionId, options) => client.updateSession(sessionId, options),
2201
+ reinvite: (sessionId, options) => client.reinviteSession(sessionId, options),
2066
2202
  getSession: (sessionId) => client.getSession(sessionId),
2067
2203
  getSessionIds: () => client.getSessionIds(),
2068
2204
  getSessions: () => client.getSessions(),
@@ -2100,6 +2236,8 @@ function useSipActions() {
2100
2236
  register: commands.register,
2101
2237
  setDebug: commands.setDebug,
2102
2238
  call: commands.call,
2239
+ sendMessage: commands.sendMessage,
2240
+ sendOptions: commands.sendOptions,
2103
2241
  answer: commands.answer,
2104
2242
  hangup: commands.hangup,
2105
2243
  hangupAll: commands.hangupAll,
@@ -2107,6 +2245,9 @@ function useSipActions() {
2107
2245
  toggleHold: commands.toggleHold,
2108
2246
  sendDTMF: commands.sendDTMF,
2109
2247
  transfer: commands.transfer,
2248
+ sendInfo: commands.sendInfo,
2249
+ update: commands.update,
2250
+ reinvite: commands.reinvite,
2110
2251
  getSession: commands.getSession,
2111
2252
  getSessionIds: commands.getSessionIds,
2112
2253
  getSessions: commands.getSessions,
@@ -2135,22 +2276,12 @@ function useSipSelector(selector, equalityFn = Object.is) {
2135
2276
  return useSyncExternalStore(store.subscribe, getSelection, getSelection);
2136
2277
  }
2137
2278
 
2138
- // src/hooks/useActiveSipSession.ts
2139
- function useActiveSipSession() {
2140
- return useSipSelector((state) => {
2141
- const activeId = state.sessionIds.find(
2142
- (id) => state.sessionsById[id]?.status === CallStatus.Active
2143
- );
2144
- return activeId ? state.sessionsById[activeId] : null;
2145
- });
2146
- }
2147
-
2148
2279
  // src/hooks/useSipSession.ts
2149
2280
  function useSipSession(sessionId) {
2150
2281
  return useSipSelector((state) => {
2151
2282
  if (!sessionId)
2152
2283
  return null;
2153
- return state.sessionsById[sessionId] ?? null;
2284
+ return state.sessions.find((session) => session.id === sessionId) ?? null;
2154
2285
  });
2155
2286
  }
2156
2287
  function useSipSessions() {
@@ -2175,25 +2306,15 @@ function useSipSessionEvent(sessionId, event, handler) {
2175
2306
  }
2176
2307
  function useSessionMedia(sessionId) {
2177
2308
  const { media } = useSipKernel();
2178
- const { sessionIds, sessionsById } = useSipSelector(
2179
- (state) => ({
2180
- sessionIds: state.sessionIds,
2181
- sessionsById: state.sessionsById
2182
- }),
2183
- (prev, next) => prev.sessionIds === next.sessionIds && prev.sessionsById === next.sessionsById
2184
- );
2185
- const [peerConnection, setPeerConnection] = useState(
2186
- null
2187
- );
2309
+ const sessions = useSipSelector((state) => state.sessions);
2310
+ const [peerConnection, setPeerConnection] = useState(null);
2188
2311
  const [remoteStream, setRemoteStream] = useState(null);
2189
2312
  const resolvedSessionId = useMemo(() => {
2190
2313
  if (sessionId)
2191
2314
  return sessionId;
2192
- const activeId = sessionIds.find(
2193
- (id) => sessionsById[id]?.status === CallStatus.Active
2194
- );
2195
- return activeId ?? sessionIds[0];
2196
- }, [sessionId, sessionIds, sessionsById]);
2315
+ const active = sessions.find((s) => s.status === CallStatus.Active);
2316
+ return active?.id ?? sessions[0]?.id;
2317
+ }, [sessionId, sessions]);
2197
2318
  const session = useMemo(
2198
2319
  () => resolvedSessionId ? media.getSession(resolvedSessionId) : null,
2199
2320
  [media, resolvedSessionId]
@@ -2201,8 +2322,8 @@ function useSessionMedia(sessionId) {
2201
2322
  const sessionState = useMemo(() => {
2202
2323
  if (!resolvedSessionId)
2203
2324
  return null;
2204
- return sessionsById[resolvedSessionId] ?? null;
2205
- }, [sessionsById, resolvedSessionId]);
2325
+ return sessions.find((s) => s.id === resolvedSessionId) ?? null;
2326
+ }, [sessions, resolvedSessionId]);
2206
2327
  useEffect(() => {
2207
2328
  if (!resolvedSessionId) {
2208
2329
  setPeerConnection(null);
@@ -2256,15 +2377,14 @@ function CallPlayer({ sessionId }) {
2256
2377
  const { remoteStream } = useSessionMedia(sessionId);
2257
2378
  const audioRef = useRef(null);
2258
2379
  useEffect(() => {
2259
- if (!audioRef.current)
2380
+ const audioEl = audioRef.current;
2381
+ if (!audioEl)
2260
2382
  return;
2261
- audioRef.current.srcObject = remoteStream;
2262
- audioRef.current.play?.().catch(() => {
2383
+ audioEl.srcObject = remoteStream;
2384
+ audioEl.play?.().catch(() => {
2263
2385
  });
2264
2386
  return () => {
2265
- if (audioRef.current) {
2266
- audioRef.current.srcObject = null;
2267
- }
2387
+ audioEl.srcObject = null;
2268
2388
  };
2269
2389
  }, [remoteStream]);
2270
2390
  return /* @__PURE__ */ jsx("audio", { ref: audioRef, autoPlay: true, playsInline: true });
@@ -2274,6 +2394,6 @@ function SipProvider(props) {
2274
2394
  return /* @__PURE__ */ jsx(SipContext.Provider, { value: contextValue, children: props.children });
2275
2395
  }
2276
2396
 
2277
- export { CallPlayer, CallStatus, SipProvider, SipStatus, createSipClientInstance, createSipEventManager, createSipKernel, useActiveSipSession, useSessionMedia, useSipActions, useSipEvent, useSipKernel, useSipSelector, useSipSession, useSipSessionEvent, useSipSessions, useSipState };
2397
+ export { CallPlayer, CallStatus, SipProvider, SipStatus, createSipClientInstance, createSipEventManager, createSipKernel, useSessionMedia, useSipActions, useSipEvent, useSipKernel, useSipSelector, useSipSession, useSipSessionEvent, useSipSessions, useSipState };
2278
2398
  //# sourceMappingURL=out.js.map
2279
2399
  //# sourceMappingURL=index.js.map