@purplecallio/react-native 0.1.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.
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * Defensive bridge to the *optional* peer dependency
4
+ * `react-native-incall-manager`, which manages call audio routing
5
+ * (speaker / earpiece / Bluetooth) — something `react-native-webrtc` does
6
+ * not handle on its own.
7
+ *
8
+ * It is listed in package.json as an optional peerDependency
9
+ * (`peerDependenciesMeta.react-native-incall-manager.optional = true`), so
10
+ * consumers who don't need custom audio routing behavior can skip
11
+ * installing it entirely. We load it with `require()` inside a try/catch so
12
+ * a missing install never crashes module evaluation — it just disables
13
+ * automatic audio routing and logs a one-time warning.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.inCallManager = exports.InCallManagerBridge = void 0;
17
+ exports.loadInCallManager = loadInCallManager;
18
+ let warnedMissing = false;
19
+ function warnMissingOnce() {
20
+ if (warnedMissing)
21
+ return;
22
+ warnedMissing = true;
23
+ // eslint-disable-next-line no-console
24
+ console.warn('[@purplecallio/react-native] react-native-incall-manager is not installed. ' +
25
+ 'Call audio routing (speaker/earpiece switching on join/leave, setSpeakerphoneOn()) ' +
26
+ 'will not be managed automatically. Install it for production call UX: ' +
27
+ 'npm install react-native-incall-manager');
28
+ }
29
+ /**
30
+ * Default loader: defensively requires the real optional native module.
31
+ * Kept as a plain function (rather than inlined) so `InCallManagerBridge`
32
+ * can be constructed with a fake loader in tests, without needing the real
33
+ * native package installed or mocked at the module resolution level.
34
+ */
35
+ function loadInCallManager() {
36
+ try {
37
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
38
+ const mod = require('react-native-incall-manager');
39
+ return (mod && mod.default ? mod.default : mod);
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ }
45
+ class InCallManagerBridge {
46
+ constructor(load = loadInCallManager) {
47
+ this.load = load;
48
+ }
49
+ resolve() {
50
+ const mgr = this.load();
51
+ if (!mgr)
52
+ warnMissingOnce();
53
+ return mgr;
54
+ }
55
+ /** Called automatically by `<PurpleCallioProvider>` on `join()`. */
56
+ start(media) {
57
+ this.resolve()?.start({ media, auto: true });
58
+ }
59
+ /** Called automatically by `<PurpleCallioProvider>` on `leave()`/unmount. */
60
+ stop() {
61
+ this.resolve()?.stop();
62
+ }
63
+ /** Force call audio to the loudspeaker (true) or earpiece/default route (false). */
64
+ setSpeakerphoneOn(enabled) {
65
+ const mgr = this.resolve();
66
+ if (!mgr)
67
+ return;
68
+ if (typeof mgr.setForceSpeakerphoneOn === 'function') {
69
+ mgr.setForceSpeakerphoneOn(enabled);
70
+ }
71
+ else {
72
+ mgr.setSpeakerphoneOn?.(enabled);
73
+ }
74
+ }
75
+ }
76
+ exports.InCallManagerBridge = InCallManagerBridge;
77
+ /** Shared default instance used by the provider and the standalone `setSpeakerphoneOn` export. */
78
+ exports.inCallManager = new InCallManagerBridge();
79
+ //# sourceMappingURL=inCallManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inCallManager.js","sourceRoot":"","sources":["../../src/optional/inCallManager.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;AA4BH,8CAQC;AA3BD,IAAI,aAAa,GAAG,KAAK,CAAC;AAC1B,SAAS,eAAe;IACtB,IAAI,aAAa;QAAE,OAAO;IAC1B,aAAa,GAAG,IAAI,CAAC;IACrB,sCAAsC;IACtC,OAAO,CAAC,IAAI,CACV,6EAA6E;QAC3E,qFAAqF;QACrF,wEAAwE;QACxE,yCAAyC,CAC5C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB;IAC/B,IAAI,CAAC;QACH,8DAA8D;QAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,6BAA6B,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAsB,CAAC;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAa,mBAAmB;IAC9B,YAA6B,OAAuC,iBAAiB;QAAxD,SAAI,GAAJ,IAAI,CAAoD;IAAG,CAAC;IAEjF,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG;YAAE,eAAe,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,oEAAoE;IACpE,KAAK,CAAC,KAAwB;QAC5B,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,6EAA6E;IAC7E,IAAI;QACF,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC;IACzB,CAAC;IAED,oFAAoF;IACpF,iBAAiB,CAAC,OAAgB;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG;YAAE,OAAO;QACjB,IAAI,OAAO,GAAG,CAAC,sBAAsB,KAAK,UAAU,EAAE,CAAC;YACrD,GAAG,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,iBAAiB,EAAE,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;CACF;AA7BD,kDA6BC;AAED,kGAAkG;AACrF,QAAA,aAAa,GAAG,IAAI,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Defensive bridge to the *optional* peer dependency
3
+ * `@react-native-community/netinfo`.
4
+ *
5
+ * This package deliberately does NOT invent any reconnection logic on top
6
+ * of network state — that's core-SDK territory (see README "Known
7
+ * Limitations" re: the `'reconnecting'` connection state). All this bridge
8
+ * does is surface raw connectivity info so a consuming app can show its own
9
+ * "you're offline" UI via `useNetworkState()`.
10
+ */
11
+ export type NetInfoStateTypeLike = string;
12
+ export interface NetInfoStateLike {
13
+ type: NetInfoStateTypeLike;
14
+ isConnected: boolean | null;
15
+ isInternetReachable: boolean | null;
16
+ }
17
+ export type NetInfoUnsubscribe = () => void;
18
+ export interface NetInfoLike {
19
+ addEventListener(listener: (state: NetInfoStateLike) => void): NetInfoUnsubscribe;
20
+ fetch(): Promise<NetInfoStateLike>;
21
+ }
22
+ /**
23
+ * Default loader: defensively requires the real optional native module.
24
+ * Kept as a plain function so `NetInfoBridge` can be constructed with a
25
+ * fake loader in tests, without needing the real native package installed.
26
+ */
27
+ export declare function loadNetInfo(): NetInfoLike | null;
28
+ export declare class NetInfoBridge {
29
+ private readonly load;
30
+ constructor(load?: () => NetInfoLike | null);
31
+ private resolve;
32
+ isSupported(): boolean;
33
+ subscribe(listener: (state: NetInfoStateLike) => void): NetInfoUnsubscribe;
34
+ fetch(): Promise<NetInfoStateLike | null>;
35
+ }
36
+ /** Shared default instance used by `useNetworkState()`. */
37
+ export declare const netInfo: NetInfoBridge;
38
+ //# sourceMappingURL=netInfo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"netInfo.d.ts","sourceRoot":"","sources":["../../src/optional/netInfo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE1C,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,oBAAoB,CAAC;IAC3B,WAAW,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5B,mBAAmB,EAAE,OAAO,GAAG,IAAI,CAAC;CACrC;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,IAAI,CAAC;AAE5C,MAAM,WAAW,WAAW;IAC1B,gBAAgB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,kBAAkB,CAAC;IAClF,KAAK,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACpC;AAcD;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,WAAW,GAAG,IAAI,CAQhD;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,GAAE,MAAM,WAAW,GAAG,IAAkB;IAEzE,OAAO,CAAC,OAAO;IAMf,WAAW,IAAI,OAAO;IAItB,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,kBAAkB;IAM1E,KAAK,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;CAK1C;AAED,2DAA2D;AAC3D,eAAO,MAAM,OAAO,eAAsB,CAAC"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ /**
3
+ * Defensive bridge to the *optional* peer dependency
4
+ * `@react-native-community/netinfo`.
5
+ *
6
+ * This package deliberately does NOT invent any reconnection logic on top
7
+ * of network state — that's core-SDK territory (see README "Known
8
+ * Limitations" re: the `'reconnecting'` connection state). All this bridge
9
+ * does is surface raw connectivity info so a consuming app can show its own
10
+ * "you're offline" UI via `useNetworkState()`.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.netInfo = exports.NetInfoBridge = void 0;
14
+ exports.loadNetInfo = loadNetInfo;
15
+ let warnedMissing = false;
16
+ function warnMissingOnce() {
17
+ if (warnedMissing)
18
+ return;
19
+ warnedMissing = true;
20
+ // eslint-disable-next-line no-console
21
+ console.warn('[@purplecallio/react-native] @react-native-community/netinfo is not installed. ' +
22
+ 'useNetworkState() will report { supported: false } and never update. Install it ' +
23
+ 'to surface real connectivity state: npm install @react-native-community/netinfo');
24
+ }
25
+ /**
26
+ * Default loader: defensively requires the real optional native module.
27
+ * Kept as a plain function so `NetInfoBridge` can be constructed with a
28
+ * fake loader in tests, without needing the real native package installed.
29
+ */
30
+ function loadNetInfo() {
31
+ try {
32
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
33
+ const mod = require('@react-native-community/netinfo');
34
+ return (mod && mod.default ? mod.default : mod);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ }
40
+ class NetInfoBridge {
41
+ constructor(load = loadNetInfo) {
42
+ this.load = load;
43
+ }
44
+ resolve() {
45
+ const mod = this.load();
46
+ if (!mod)
47
+ warnMissingOnce();
48
+ return mod;
49
+ }
50
+ isSupported() {
51
+ return this.resolve() !== null;
52
+ }
53
+ subscribe(listener) {
54
+ const mod = this.resolve();
55
+ if (!mod)
56
+ return () => { };
57
+ return mod.addEventListener(listener);
58
+ }
59
+ fetch() {
60
+ const mod = this.resolve();
61
+ if (!mod)
62
+ return Promise.resolve(null);
63
+ return mod.fetch();
64
+ }
65
+ }
66
+ exports.NetInfoBridge = NetInfoBridge;
67
+ /** Shared default instance used by `useNetworkState()`. */
68
+ exports.netInfo = new NetInfoBridge();
69
+ //# sourceMappingURL=netInfo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"netInfo.js","sourceRoot":"","sources":["../../src/optional/netInfo.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAkCH,kCAQC;AAzBD,IAAI,aAAa,GAAG,KAAK,CAAC;AAC1B,SAAS,eAAe;IACtB,IAAI,aAAa;QAAE,OAAO;IAC1B,aAAa,GAAG,IAAI,CAAC;IACrB,sCAAsC;IACtC,OAAO,CAAC,IAAI,CACV,iFAAiF;QAC/E,kFAAkF;QAClF,iFAAiF,CACpF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAgB,WAAW;IACzB,IAAI,CAAC;QACH,8DAA8D;QAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,iCAAiC,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAgB,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAa,aAAa;IACxB,YAA6B,OAAiC,WAAW;QAA5C,SAAI,GAAJ,IAAI,CAAwC;IAAG,CAAC;IAErE,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,GAAG;YAAE,eAAe,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC;IACjC,CAAC;IAED,SAAS,CAAC,QAA2C;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG;YAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;QAC1B,OAAO,GAAG,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC;IAED,KAAK;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,GAAG;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF;AAxBD,sCAwBC;AAED,2DAA2D;AAC9C,QAAA,OAAO,GAAG,IAAI,aAAa,EAAE,CAAC"}
@@ -0,0 +1,29 @@
1
+ export interface RequestMediaPermissionsOptions {
2
+ /** Request microphone (RECORD_AUDIO) access. Default: true. */
3
+ audio?: boolean;
4
+ /** Request camera access. Default: true. */
5
+ video?: boolean;
6
+ }
7
+ /**
8
+ * Requests the runtime OS permissions needed for `getUserMedia()` to
9
+ * succeed on-device.
10
+ *
11
+ * - **Android** requires an explicit runtime permission request before
12
+ * camera/microphone access (`PermissionsAndroid.requestMultiple`), on top
13
+ * of declaring the permissions in AndroidManifest.xml (see README).
14
+ * - **iOS** has no equivalent upfront JS-level permission API: the system
15
+ * prompt appears automatically the first time `getUserMedia()` is called,
16
+ * *provided* the app's Info.plist declares `NSCameraUsageDescription`
17
+ * and/or `NSMicrophoneUsageDescription` (an app-level native config this
18
+ * package cannot inject — see README). So on iOS this function is a
19
+ * deliberate no-op that resolves `true`; if Info.plist is missing the
20
+ * required keys, `getUserMedia()` itself will reject when called, not
21
+ * this function.
22
+ *
23
+ * `<PurpleCallioProvider>` calls this automatically before `join()` for
24
+ * whichever of `audio`/`video` are requested, but it's also exported
25
+ * standalone for apps that want to prompt earlier (e.g. on a "join call"
26
+ * button) for better UX.
27
+ */
28
+ export declare function requestMediaPermissions(options?: RequestMediaPermissionsOptions): Promise<boolean>;
29
+ //# sourceMappingURL=permissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,8BAA8B;IAC7C,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,GAAE,8BAAmC,GAC3C,OAAO,CAAC,OAAO,CAAC,CAelB"}
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requestMediaPermissions = requestMediaPermissions;
4
+ const react_native_1 = require("react-native");
5
+ /**
6
+ * Requests the runtime OS permissions needed for `getUserMedia()` to
7
+ * succeed on-device.
8
+ *
9
+ * - **Android** requires an explicit runtime permission request before
10
+ * camera/microphone access (`PermissionsAndroid.requestMultiple`), on top
11
+ * of declaring the permissions in AndroidManifest.xml (see README).
12
+ * - **iOS** has no equivalent upfront JS-level permission API: the system
13
+ * prompt appears automatically the first time `getUserMedia()` is called,
14
+ * *provided* the app's Info.plist declares `NSCameraUsageDescription`
15
+ * and/or `NSMicrophoneUsageDescription` (an app-level native config this
16
+ * package cannot inject — see README). So on iOS this function is a
17
+ * deliberate no-op that resolves `true`; if Info.plist is missing the
18
+ * required keys, `getUserMedia()` itself will reject when called, not
19
+ * this function.
20
+ *
21
+ * `<PurpleCallioProvider>` calls this automatically before `join()` for
22
+ * whichever of `audio`/`video` are requested, but it's also exported
23
+ * standalone for apps that want to prompt earlier (e.g. on a "join call"
24
+ * button) for better UX.
25
+ */
26
+ async function requestMediaPermissions(options = {}) {
27
+ const { audio = true, video = true } = options;
28
+ if (react_native_1.Platform.OS !== 'android') {
29
+ return true;
30
+ }
31
+ const permissions = [];
32
+ if (audio)
33
+ permissions.push(react_native_1.PermissionsAndroid.PERMISSIONS.RECORD_AUDIO);
34
+ if (video)
35
+ permissions.push(react_native_1.PermissionsAndroid.PERMISSIONS.CAMERA);
36
+ if (permissions.length === 0)
37
+ return true;
38
+ const results = await react_native_1.PermissionsAndroid.requestMultiple(permissions);
39
+ return permissions.every((permission) => results[permission] === react_native_1.PermissionsAndroid.RESULTS.GRANTED);
40
+ }
41
+ //# sourceMappingURL=permissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permissions.js","sourceRoot":"","sources":["../src/permissions.ts"],"names":[],"mappings":";;AA8BA,0DAiBC;AA/CD,+CAA4D;AAS5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACI,KAAK,UAAU,uBAAuB,CAC3C,UAA0C,EAAE;IAE5C,MAAM,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAE/C,IAAI,uBAAQ,CAAC,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,WAAW,GAAgG,EAAE,CAAC;IACpH,IAAI,KAAK;QAAE,WAAW,CAAC,IAAI,CAAC,iCAAkB,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IACzE,IAAI,KAAK;QAAE,WAAW,CAAC,IAAI,CAAC,iCAAkB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IAEnE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE1C,MAAM,OAAO,GAAG,MAAM,iCAAkB,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;IACtE,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,iCAAkB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACvG,CAAC"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@purplecallio/react-native",
3
+ "version": "0.1.0",
4
+ "description": "Official React Native components and hooks for PurpleCallio, a developer-first real-time communication platform for integrating voice, video and WebRTC communication into applications.",
5
+ "homepage": "https://purplecallio.com",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "react-native": "./dist/index.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "require": "./dist/index.js",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc",
21
+ "dev": "tsc --watch",
22
+ "test": "vitest run"
23
+ },
24
+ "keywords": [
25
+ "purplecallio",
26
+ "react-native",
27
+ "mobile",
28
+ "video",
29
+ "webrtc",
30
+ "voice",
31
+ "real-time",
32
+ "sdk",
33
+ "typescript"
34
+ ],
35
+ "license": "MIT",
36
+ "peerDependencies": {
37
+ "react": ">=18.0.0",
38
+ "react-native": ">=0.70.0",
39
+ "react-native-webrtc": ">=1.100.0",
40
+ "react-native-incall-manager": ">=4.0.0",
41
+ "@react-native-community/netinfo": ">=9.0.0"
42
+ },
43
+ "peerDependenciesMeta": {
44
+ "react-native-incall-manager": {
45
+ "optional": true
46
+ },
47
+ "@react-native-community/netinfo": {
48
+ "optional": true
49
+ }
50
+ },
51
+ "dependencies": {
52
+ "@purplecallio/sdk": "^0.1.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.10.5",
56
+ "@types/react": "^19.2.17",
57
+ "@types/react-test-renderer": "^19.2.0",
58
+ "react": "^19.2.0",
59
+ "react-native": "^0.87.1",
60
+ "react-native-webrtc": "^124.0.8",
61
+ "react-test-renderer": "^19.2.0",
62
+ "typescript": "^5.7.3",
63
+ "vitest": "^2.1.9"
64
+ }
65
+ }