@lorikeetai/react-native-sdk 2.0.0 → 2.2.0-beta.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.
@@ -0,0 +1,74 @@
1
+ /**
2
+ * What `POST /v1/conversation/voice/web/create` returns. Your backend calls it
3
+ * with your API credentials and hands the result to the app, which must never
4
+ * hold those credentials itself.
5
+ */
6
+ interface LorikeetCallSession {
7
+ /** The conversation the call belongs to. */
8
+ conversationId: string;
9
+ /** The token that admits this customer to the call's room. */
10
+ token: string;
11
+ /** The voice server to connect to. */
12
+ wsUrl: string;
13
+ }
14
+ /** The web Voice Widget's call states. */
15
+ type LorikeetCallState = 'idle' | 'starting' | 'connecting' | 'active' | 'error';
16
+ /**
17
+ * Why a call is in `error`, from the web Voice Widget's set.
18
+ *
19
+ * - `not_available`: `getSession` returned `null`.
20
+ * - `microphone_denied`: the customer refused microphone access, or it is off
21
+ * for the app in the device's settings.
22
+ * - `microphone_system_denied`: the web Voice Widget's code for a browser
23
+ * blocked by the operating system. Kept so one handler covers both
24
+ * platforms. A mobile device reports `microphone_denied` instead.
25
+ * - `failed`: anything else. `onError` receives the cause.
26
+ */
27
+ type LorikeetCallError = 'not_available' | 'microphone_denied' | 'microphone_system_denied' | 'failed';
28
+ interface UseLorikeetCallOptions {
29
+ /**
30
+ * Fetches a session from your backend for a new call. Called each time the
31
+ * customer starts one, after microphone access is granted, so a refused
32
+ * microphone starts no conversation.
33
+ *
34
+ * Return `null` when calling is not available to this customer, which puts
35
+ * the call in `error` with `not_available`. Throw, or return anything that
36
+ * is not a session, for `failed`.
37
+ */
38
+ getSession: () => Promise<LorikeetCallSession | null>;
39
+ /**
40
+ * Receives the cause whenever a call fails with `failed`. Anything it throws
41
+ * is ignored.
42
+ */
43
+ onError?: (cause: unknown) => void;
44
+ }
45
+ interface LorikeetCall {
46
+ /** End the call, or abandon one still starting. The state returns to `idle`. */
47
+ end: () => void;
48
+ /**
49
+ * Whether the last call ended, by either side, after it began connecting.
50
+ * Reset when the next call starts.
51
+ */
52
+ ended: boolean;
53
+ /** Set while `state` is `error`. */
54
+ error: LorikeetCallError | undefined;
55
+ microphoneMuted: boolean;
56
+ /** Mute or unmute the customer's microphone. Only applies to a call in progress. */
57
+ setMicrophoneMuted: (muted: boolean) => void;
58
+ /** Mute or unmute the agent's audio. */
59
+ setSpeakerMuted: (muted: boolean) => void;
60
+ speakerMuted: boolean;
61
+ /**
62
+ * Start a call from `idle` or `error`. Does nothing in any other state.
63
+ * Never rejects: a failure is reported through `state` and `error`.
64
+ */
65
+ start: () => Promise<void>;
66
+ state: LorikeetCallState;
67
+ }
68
+ /**
69
+ * Runs a Lorikeet voice call over LiveKit's native audio, with the web Voice
70
+ * Widget's states and errors.
71
+ */
72
+ declare function useLorikeetCall({ getSession, onError }: UseLorikeetCallOptions): LorikeetCall;
73
+ export { type LorikeetCall, type LorikeetCallError, type LorikeetCallSession, type LorikeetCallState, type UseLorikeetCallOptions, useLorikeetCall };
74
+ //# sourceMappingURL=voice.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.d.cts","names":[],"sources":["../src/voice/types.ts","../src/voice/use-lorikeet-call.ts"],"mappings":";;;;;UAKiB;;EAEf;;EAEA;;EAEA;;;KAIU;;;;;;;;;;;;KAkBA;UAMK;;;;;;;;;;EAUf,kBAAkB,QAAQ;;;;;EAK1B,WAAW;;UAGI;;EAEf;;;;;EAKA;;EAEA,OAAO;EACP;;EAEA,qBAAqB;;EAErB,kBAAkB;EAClB;;;;;EAKA,aAAa;EACb,OAAO;;;;;;iBC9BO,kBACd,YACA,WACC,yBAAyB"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * What `POST /v1/conversation/voice/web/create` returns. Your backend calls it
3
+ * with your API credentials and hands the result to the app, which must never
4
+ * hold those credentials itself.
5
+ */
6
+ interface LorikeetCallSession {
7
+ /** The conversation the call belongs to. */
8
+ conversationId: string;
9
+ /** The token that admits this customer to the call's room. */
10
+ token: string;
11
+ /** The voice server to connect to. */
12
+ wsUrl: string;
13
+ }
14
+ /** The web Voice Widget's call states. */
15
+ type LorikeetCallState = 'idle' | 'starting' | 'connecting' | 'active' | 'error';
16
+ /**
17
+ * Why a call is in `error`, from the web Voice Widget's set.
18
+ *
19
+ * - `not_available`: `getSession` returned `null`.
20
+ * - `microphone_denied`: the customer refused microphone access, or it is off
21
+ * for the app in the device's settings.
22
+ * - `microphone_system_denied`: the web Voice Widget's code for a browser
23
+ * blocked by the operating system. Kept so one handler covers both
24
+ * platforms. A mobile device reports `microphone_denied` instead.
25
+ * - `failed`: anything else. `onError` receives the cause.
26
+ */
27
+ type LorikeetCallError = 'not_available' | 'microphone_denied' | 'microphone_system_denied' | 'failed';
28
+ interface UseLorikeetCallOptions {
29
+ /**
30
+ * Fetches a session from your backend for a new call. Called each time the
31
+ * customer starts one, after microphone access is granted, so a refused
32
+ * microphone starts no conversation.
33
+ *
34
+ * Return `null` when calling is not available to this customer, which puts
35
+ * the call in `error` with `not_available`. Throw, or return anything that
36
+ * is not a session, for `failed`.
37
+ */
38
+ getSession: () => Promise<LorikeetCallSession | null>;
39
+ /**
40
+ * Receives the cause whenever a call fails with `failed`. Anything it throws
41
+ * is ignored.
42
+ */
43
+ onError?: (cause: unknown) => void;
44
+ }
45
+ interface LorikeetCall {
46
+ /** End the call, or abandon one still starting. The state returns to `idle`. */
47
+ end: () => void;
48
+ /**
49
+ * Whether the last call ended, by either side, after it began connecting.
50
+ * Reset when the next call starts.
51
+ */
52
+ ended: boolean;
53
+ /** Set while `state` is `error`. */
54
+ error: LorikeetCallError | undefined;
55
+ microphoneMuted: boolean;
56
+ /** Mute or unmute the customer's microphone. Only applies to a call in progress. */
57
+ setMicrophoneMuted: (muted: boolean) => void;
58
+ /** Mute or unmute the agent's audio. */
59
+ setSpeakerMuted: (muted: boolean) => void;
60
+ speakerMuted: boolean;
61
+ /**
62
+ * Start a call from `idle` or `error`. Does nothing in any other state.
63
+ * Never rejects: a failure is reported through `state` and `error`.
64
+ */
65
+ start: () => Promise<void>;
66
+ state: LorikeetCallState;
67
+ }
68
+ /**
69
+ * Runs a Lorikeet voice call over LiveKit's native audio, with the web Voice
70
+ * Widget's states and errors.
71
+ */
72
+ declare function useLorikeetCall({ getSession, onError }: UseLorikeetCallOptions): LorikeetCall;
73
+ export { type LorikeetCall, type LorikeetCallError, type LorikeetCallSession, type LorikeetCallState, type UseLorikeetCallOptions, useLorikeetCall };
74
+ //# sourceMappingURL=voice.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.d.ts","names":[],"sources":["../src/voice/types.ts","../src/voice/use-lorikeet-call.ts"],"mappings":";;;;;UAKiB;;EAEf;;EAEA;;EAEA;;;KAIU;;;;;;;;;;;;KAkBA;UAMK;;;;;;;;;;EAUf,kBAAkB,QAAQ;;;;;EAK1B,WAAW;;UAGI;;EAEf;;;;;EAKA;;EAEA,OAAO;EACP;;EAEA,qBAAqB;;EAErB,kBAAkB;EAClB;;;;;EAKA,aAAa;EACb,OAAO;;;;;;iBC9BO,kBACd,YACA,WACC,yBAAyB"}
package/dist/voice.js ADDED
@@ -0,0 +1,174 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { AudioSession, registerGlobals } from "@livekit/react-native";
3
+ import { RemoteAudioTrack, Room, RoomEvent, Track, createLocalAudioTrack } from "livekit-client";
4
+
5
+ // src/voice/use-lorikeet-call.ts
6
+ /** The server's `LORIKEET_AGENT_IDENTITY`. The call ends when it leaves. */
7
+ const LORIKEET_AGENT_IDENTITY = "lorikeet-agent";
8
+ const errorFor = (error) => {
9
+ if (typeof error !== "object" || error === null || !("name" in error)) return "failed";
10
+ return error.name === "SecurityError" || error.name === "NotAllowedError" ? "microphone_denied" : "failed";
11
+ };
12
+ const isSession = (value) => typeof value === "object" && value !== null && "conversationId" in value && typeof value.conversationId === "string" && "token" in value && typeof value.token === "string" && "wsUrl" in value && typeof value.wsUrl === "string";
13
+ /**
14
+ * Runs a Lorikeet voice call over LiveKit's native audio, with the web Voice
15
+ * Widget's states and errors.
16
+ */
17
+ function useLorikeetCall({ getSession, onError }) {
18
+ const [state, setState] = useState("idle");
19
+ const [error, setError] = useState();
20
+ const [ended, setEnded] = useState(false);
21
+ const [microphoneMuted, setMicrophoneMutedState] = useState(false);
22
+ const [speakerMuted, setSpeakerMutedState] = useState(false);
23
+ const stateRef = useRef(state);
24
+ const generationRef = useRef(0);
25
+ const localTrackRef = useRef(null);
26
+ const roomRef = useRef(null);
27
+ const speakerMutedRef = useRef(false);
28
+ const audioSessionActiveRef = useRef(false);
29
+ const audioSessionStoppedRef = useRef(Promise.resolve());
30
+ const optionsRef = useRef({
31
+ getSession,
32
+ onError
33
+ });
34
+ optionsRef.current = {
35
+ getSession,
36
+ onError
37
+ };
38
+ const transitionTo = useCallback((next) => {
39
+ stateRef.current = next;
40
+ setState(next);
41
+ }, []);
42
+ const cleanup = useCallback(() => {
43
+ var _localTrackRef$curren;
44
+ (_localTrackRef$curren = localTrackRef.current) === null || _localTrackRef$curren === void 0 || _localTrackRef$curren.stop();
45
+ localTrackRef.current = null;
46
+ const room = roomRef.current;
47
+ roomRef.current = null;
48
+ const disconnected = (room ? room.disconnect() : Promise.resolve()).catch(() => void 0);
49
+ if (audioSessionActiveRef.current) {
50
+ audioSessionActiveRef.current = false;
51
+ audioSessionStoppedRef.current = disconnected.then(() => AudioSession.stopAudioSession()).catch(() => void 0);
52
+ }
53
+ }, []);
54
+ const resetMutes = useCallback(() => {
55
+ setMicrophoneMutedState(false);
56
+ setSpeakerMutedState(false);
57
+ speakerMutedRef.current = false;
58
+ }, []);
59
+ const end = useCallback(() => {
60
+ generationRef.current += 1;
61
+ cleanup();
62
+ setEnded(stateRef.current === "connecting" || stateRef.current === "active");
63
+ setError(void 0);
64
+ resetMutes();
65
+ transitionTo("idle");
66
+ }, [
67
+ cleanup,
68
+ resetMutes,
69
+ transitionTo
70
+ ]);
71
+ const fail = useCallback((callError) => {
72
+ generationRef.current += 1;
73
+ cleanup();
74
+ setEnded(false);
75
+ setError(callError);
76
+ transitionTo("error");
77
+ }, [cleanup, transitionTo]);
78
+ useEffect(() => {
79
+ return () => {
80
+ generationRef.current += 1;
81
+ cleanup();
82
+ };
83
+ }, [cleanup]);
84
+ const start = useCallback(async () => {
85
+ if (stateRef.current !== "idle" && stateRef.current !== "error") return;
86
+ setError(void 0);
87
+ setEnded(false);
88
+ resetMutes();
89
+ transitionTo("starting");
90
+ const generation = ++generationRef.current;
91
+ const isStale = () => generationRef.current !== generation;
92
+ try {
93
+ if (!("LiveKitReactNativeGlobal" in globalThis)) registerGlobals();
94
+ await audioSessionStoppedRef.current;
95
+ if (isStale()) return;
96
+ audioSessionActiveRef.current = true;
97
+ await AudioSession.startAudioSession();
98
+ if (isStale()) return;
99
+ const localTrack = await createLocalAudioTrack();
100
+ if (isStale()) {
101
+ localTrack.stop();
102
+ return;
103
+ }
104
+ localTrackRef.current = localTrack;
105
+ const session = await optionsRef.current.getSession();
106
+ if (isStale()) return;
107
+ if (session === null) {
108
+ fail("not_available");
109
+ return;
110
+ }
111
+ if (!isSession(session)) throw new TypeError("getSession must resolve to { conversationId, wsUrl, token } or null");
112
+ transitionTo("connecting");
113
+ const room = new Room();
114
+ roomRef.current = room;
115
+ const isCurrentRoom = () => roomRef.current === room;
116
+ room.on(RoomEvent.ParticipantDisconnected, (participant) => {
117
+ if (isCurrentRoom() && participant.identity === "lorikeet-agent") end();
118
+ });
119
+ room.on(RoomEvent.TrackSubscribed, (track) => {
120
+ if (isCurrentRoom() && track instanceof RemoteAudioTrack) track.setVolume(speakerMutedRef.current ? 0 : 1);
121
+ });
122
+ let joined = false;
123
+ room.on(RoomEvent.Disconnected, () => {
124
+ if (isCurrentRoom() && joined) end();
125
+ });
126
+ await room.connect(session.wsUrl, session.token, { autoSubscribe: true });
127
+ if (isStale()) return;
128
+ joined = true;
129
+ await room.localParticipant.publishTrack(localTrack, { source: Track.Source.Microphone });
130
+ if (isStale()) return;
131
+ transitionTo("active");
132
+ } catch (caught) {
133
+ if (isStale()) return;
134
+ const callError = errorFor(caught);
135
+ fail(callError);
136
+ if (callError === "failed") try {
137
+ var _optionsRef$current$o, _optionsRef$current;
138
+ (_optionsRef$current$o = (_optionsRef$current = optionsRef.current).onError) === null || _optionsRef$current$o === void 0 || _optionsRef$current$o.call(_optionsRef$current, caught);
139
+ } catch {}
140
+ }
141
+ }, [
142
+ end,
143
+ fail,
144
+ resetMutes,
145
+ transitionTo
146
+ ]);
147
+ return {
148
+ end,
149
+ ended,
150
+ error,
151
+ microphoneMuted,
152
+ setMicrophoneMuted: useCallback((muted) => {
153
+ const track = localTrackRef.current;
154
+ if (!track) return;
155
+ const reconcile = () => {
156
+ if (localTrackRef.current === track) setMicrophoneMutedState(track.isMuted);
157
+ };
158
+ (muted ? track.mute() : track.unmute()).then(reconcile, reconcile);
159
+ }, []),
160
+ setSpeakerMuted: useCallback((muted) => {
161
+ var _roomRef$current$remo, _roomRef$current;
162
+ speakerMutedRef.current = muted;
163
+ const participants = (_roomRef$current$remo = (_roomRef$current = roomRef.current) === null || _roomRef$current === void 0 ? void 0 : _roomRef$current.remoteParticipants.values()) !== null && _roomRef$current$remo !== void 0 ? _roomRef$current$remo : [];
164
+ for (const participant of participants) for (const { track } of participant.audioTrackPublications.values()) if (track instanceof RemoteAudioTrack) track.setVolume(muted ? 0 : 1);
165
+ setSpeakerMutedState(muted);
166
+ }, []),
167
+ speakerMuted,
168
+ start,
169
+ state
170
+ };
171
+ }
172
+
173
+ export { useLorikeetCall };
174
+ //# sourceMappingURL=voice.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.js","names":[],"sources":["../src/voice/use-lorikeet-call.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react'\n\nimport { AudioSession, registerGlobals } from '@livekit/react-native'\nimport {\n createLocalAudioTrack,\n type LocalAudioTrack,\n RemoteAudioTrack,\n Room,\n RoomEvent,\n Track,\n} from 'livekit-client'\n\nimport type {\n LorikeetCall,\n LorikeetCallError,\n LorikeetCallSession,\n LorikeetCallState,\n UseLorikeetCallOptions,\n} from './types'\n\n/** The server's `LORIKEET_AGENT_IDENTITY`. The call ends when it leaves. */\nexport const LORIKEET_AGENT_IDENTITY = 'lorikeet-agent'\n\n// react-native-webrtc rejects a refused permission with a plain object named\n// `SecurityError`, not a DOMException, so match on the name alone.\nconst errorFor = (error: unknown): LorikeetCallError => {\n if (typeof error !== 'object' || error === null || !('name' in error)) {\n return 'failed'\n }\n return error.name === 'SecurityError' || error.name === 'NotAllowedError'\n ? 'microphone_denied'\n : 'failed'\n}\n\nconst isSession = (value: unknown): value is LorikeetCallSession =>\n typeof value === 'object' &&\n value !== null &&\n 'conversationId' in value &&\n typeof value.conversationId === 'string' &&\n 'token' in value &&\n typeof value.token === 'string' &&\n 'wsUrl' in value &&\n typeof value.wsUrl === 'string'\n\n/**\n * Runs a Lorikeet voice call over LiveKit's native audio, with the web Voice\n * Widget's states and errors.\n */\nexport function useLorikeetCall({\n getSession,\n onError,\n}: UseLorikeetCallOptions): LorikeetCall {\n const [state, setState] = useState<LorikeetCallState>('idle')\n const [error, setError] = useState<LorikeetCallError | undefined>()\n const [ended, setEnded] = useState(false)\n const [microphoneMuted, setMicrophoneMutedState] = useState(false)\n const [speakerMuted, setSpeakerMutedState] = useState(false)\n\n const stateRef = useRef(state)\n const generationRef = useRef(0)\n const localTrackRef = useRef<LocalAudioTrack | null>(null)\n const roomRef = useRef<Room | null>(null)\n const speakerMutedRef = useRef(false)\n const audioSessionActiveRef = useRef(false)\n // The previous call's teardown, so the next call cannot start its audio\n // session only to have that teardown stop it.\n const audioSessionStoppedRef = useRef<Promise<void>>(Promise.resolve())\n // Read at call time, so a host passing inline functions keeps `start` stable.\n const optionsRef = useRef({ getSession, onError })\n optionsRef.current = { getSession, onError }\n\n const transitionTo = useCallback((next: LorikeetCallState) => {\n stateRef.current = next\n setState(next)\n }, [])\n\n const cleanup = useCallback(() => {\n localTrackRef.current?.stop()\n localTrackRef.current = null\n const room = roomRef.current\n roomRef.current = null\n // Nothing is left to report a teardown failure to, and a failed disconnect\n // must not leave the native audio session running.\n const disconnected = (room ? room.disconnect() : Promise.resolve()).catch(\n () => undefined,\n )\n if (audioSessionActiveRef.current) {\n audioSessionActiveRef.current = false\n audioSessionStoppedRef.current = disconnected\n .then(() => AudioSession.stopAudioSession())\n .catch(() => undefined)\n }\n }, [])\n\n const resetMutes = useCallback(() => {\n setMicrophoneMutedState(false)\n setSpeakerMutedState(false)\n speakerMutedRef.current = false\n }, [])\n\n const end = useCallback(() => {\n generationRef.current += 1\n cleanup()\n setEnded(stateRef.current === 'connecting' || stateRef.current === 'active')\n setError(undefined)\n resetMutes()\n transitionTo('idle')\n }, [cleanup, resetMutes, transitionTo])\n\n const fail = useCallback(\n (callError: LorikeetCallError) => {\n generationRef.current += 1\n cleanup()\n setEnded(false)\n setError(callError)\n transitionTo('error')\n },\n [cleanup, transitionTo],\n )\n\n useEffect(() => {\n return () => {\n generationRef.current += 1\n cleanup()\n }\n }, [cleanup])\n\n const start = useCallback(async () => {\n if (stateRef.current !== 'idle' && stateRef.current !== 'error') {\n return\n }\n\n setError(undefined)\n setEnded(false)\n resetMutes()\n transitionTo('starting')\n const generation = ++generationRef.current\n const isStale = () => generationRef.current !== generation\n\n try {\n // `registerGlobals` sets this. Calling it again would replace any iOS\n // audio setup a host that already uses LiveKit configured itself.\n if (!('LiveKitReactNativeGlobal' in globalThis)) {\n registerGlobals()\n }\n await audioSessionStoppedRef.current\n if (isStale()) {\n return\n }\n audioSessionActiveRef.current = true\n await AudioSession.startAudioSession()\n if (isStale()) {\n return\n }\n\n const localTrack = await createLocalAudioTrack()\n if (isStale()) {\n localTrack.stop()\n return\n }\n localTrackRef.current = localTrack\n\n const session = await optionsRef.current.getSession()\n if (isStale()) {\n return\n }\n if (session === null) {\n fail('not_available')\n return\n }\n if (!isSession(session)) {\n throw new TypeError(\n 'getSession must resolve to { conversationId, wsUrl, token } or null',\n )\n }\n transitionTo('connecting')\n\n const room = new Room()\n roomRef.current = room\n const isCurrentRoom = () => roomRef.current === room\n room.on(RoomEvent.ParticipantDisconnected, (participant) => {\n if (\n isCurrentRoom() &&\n participant.identity === LORIKEET_AGENT_IDENTITY\n ) {\n end()\n }\n })\n room.on(RoomEvent.TrackSubscribed, (track) => {\n if (isCurrentRoom() && track instanceof RemoteAudioTrack) {\n track.setVolume(speakerMutedRef.current ? 0 : 1)\n }\n })\n // A failed join also emits Disconnected, just before `connect` rejects,\n // and has to surface as `failed` rather than as a hang-up.\n let joined = false\n room.on(RoomEvent.Disconnected, () => {\n if (isCurrentRoom() && joined) {\n end()\n }\n })\n\n await room.connect(session.wsUrl, session.token, { autoSubscribe: true })\n if (isStale()) {\n return\n }\n joined = true\n await room.localParticipant.publishTrack(localTrack, {\n source: Track.Source.Microphone,\n })\n if (isStale()) {\n return\n }\n transitionTo('active')\n } catch (caught) {\n if (isStale()) {\n return\n }\n const callError = errorFor(caught)\n fail(callError)\n if (callError === 'failed') {\n try {\n optionsRef.current.onError?.(caught)\n } catch {\n // A host's failing error handler must not turn into an unhandled\n // rejection from a `start()` that callers fire and forget.\n }\n }\n }\n }, [end, fail, resetMutes, transitionTo])\n\n const setMicrophoneMuted = useCallback((muted: boolean) => {\n const track = localTrackRef.current\n if (!track) {\n return\n }\n // Report what the track did rather than what was asked, so a mute that\n // fails is not shown as muted while the customer is still heard.\n const reconcile = () => {\n if (localTrackRef.current === track) {\n setMicrophoneMutedState(track.isMuted)\n }\n }\n void (muted ? track.mute() : track.unmute()).then(reconcile, reconcile)\n }, [])\n\n const setSpeakerMuted = useCallback((muted: boolean) => {\n speakerMutedRef.current = muted\n const participants = roomRef.current?.remoteParticipants.values() ?? []\n for (const participant of participants) {\n for (const { track } of participant.audioTrackPublications.values()) {\n if (track instanceof RemoteAudioTrack) {\n track.setVolume(muted ? 0 : 1)\n }\n }\n }\n setSpeakerMutedState(muted)\n }, [])\n\n return {\n end,\n ended,\n error,\n microphoneMuted,\n setMicrophoneMuted,\n setSpeakerMuted,\n speakerMuted,\n start,\n state,\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@lorikeetai/react-native-sdk",
3
- "version": "2.0.0",
3
+ "version": "2.2.0-beta.1",
4
4
  "type": "module",
5
5
  "private": false,
6
- "description": "LorikeetCX React Native widget for embedding the chat widget via WebView",
6
+ "description": "LorikeetCX React Native SDK: the chat widget in a WebView, and in-app voice calls",
7
7
  "keywords": [
8
8
  "lorikeet",
9
9
  "react-native",
10
10
  "webview",
11
- "widget"
11
+ "widget",
12
+ "voice"
12
13
  ],
13
14
  "author": "LorikeetCX.ai",
14
15
  "license": "MIT",
@@ -27,24 +28,51 @@
27
28
  "react-native": "./dist/index.js",
28
29
  "import": "./dist/index.js",
29
30
  "require": "./dist/index.cjs"
31
+ },
32
+ "./voice": {
33
+ "react-native": "./dist/voice.js",
34
+ "import": {
35
+ "types": "./dist/voice.d.ts",
36
+ "default": "./dist/voice.js"
37
+ },
38
+ "require": {
39
+ "types": "./dist/voice.d.cts",
40
+ "default": "./dist/voice.cjs"
41
+ }
30
42
  }
31
43
  },
32
44
  "peerDependencies": {
45
+ "@livekit/react-native": "^3.1.0",
46
+ "@livekit/react-native-webrtc": "^150.1.0",
47
+ "livekit-client": "^2.20.0",
33
48
  "react": ">=18.0.0",
34
49
  "react-native": ">=0.70.0",
35
50
  "react-native-safe-area-context": ">=4.0.0 <6.0.0",
36
51
  "react-native-svg": ">=13.0.0 <16.0.0",
37
52
  "react-native-webview": ">=13.0.0 <14.0.0"
38
53
  },
54
+ "peerDependenciesMeta": {
55
+ "@livekit/react-native": {
56
+ "optional": true
57
+ },
58
+ "@livekit/react-native-webrtc": {
59
+ "optional": true
60
+ },
61
+ "livekit-client": {
62
+ "optional": true
63
+ }
64
+ },
39
65
  "devDependencies": {
40
- "@lorikeetai/tsconfig": "1.0.0",
41
- "@lorikeetai/web-sdk": "1.13.0",
66
+ "@livekit/react-native": "3.1.0",
67
+ "@livekit/react-native-webrtc": "150.1.1",
42
68
  "@testing-library/react": "^16.1.0",
43
69
  "@types/react": "^19.0.0",
44
70
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
45
- "happy-dom": "^20.8.9",
71
+ "happy-dom": "^21.0.4",
72
+ "livekit-client": "^2.23.0",
46
73
  "react": "^19.1.0",
47
74
  "react-dom": "^19.1.0",
75
+ "react-native": "0.81.4",
48
76
  "react-native-safe-area-context": "~5.6.0",
49
77
  "react-native-svg": "^15.13.0",
50
78
  "react-native-webview": "13.15.0",
@@ -52,8 +80,8 @@
52
80
  "tsdown": "^0.22.14",
53
81
  "typescript": "^5.7.2",
54
82
  "unrun": "^0.3.1",
55
- "vite": "^8.2.2",
56
- "vitest": "^4.1.8"
83
+ "vite": "^8.2.4",
84
+ "vitest": "^4.1.12"
57
85
  },
58
86
  "publishConfig": {
59
87
  "access": "public"
@@ -71,8 +99,8 @@
71
99
  "clean": "rimraf dist *.tsbuildinfo",
72
100
  "build": "tsdown",
73
101
  "typecheck": "tsgo --noEmit --checkers 1",
74
- "lint": "oxlint --config ../../oxlint.config.mts --quiet . && oxfmt --config ../../.oxfmtrc.json --check \"**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}\"",
75
- "lint:fix": "oxlint --config ../../oxlint.config.mts --quiet --fix . && oxfmt --config ../../.oxfmtrc.json --write \"**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}\"",
102
+ "lint": "../../node_modules/.bin/oxlint --config ../../oxlint.config.mts --quiet . && ../../node_modules/.bin/oxfmt --config ../../.oxfmtrc.json --check \"**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}\"",
103
+ "lint:fix": "../../node_modules/.bin/oxlint --config ../../oxlint.config.mts --quiet --fix . && ../../node_modules/.bin/oxfmt --config ../../.oxfmtrc.json --write \"**/*.{js,jsx,mjs,cjs,ts,tsx,mts,cts}\"",
76
104
  "test": "vitest run"
77
105
  }
78
106
  }