rnww-plugin-microphone 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/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # RNWW Plugin Microphone
2
+
3
+ React Native와 WebView 간 마이크 기능을 연결하는 플러그인입니다.
4
+
5
+ ## 설치
6
+
7
+ ```bash
8
+ npm install rnww-plugin-microphone
9
+ # or
10
+ yarn add rnww-plugin-microphone
11
+ ```
12
+
13
+ ## 기능
14
+
15
+ - 마이크 권한 확인 및 요청
16
+ - 실시간 오디오 스트리밍 (PCM 16-bit)
17
+ - 샘플레이트 및 청크 크기 조정 가능
18
+ - Android 및 iOS 지원
19
+
20
+ ### 브릿지 핸들러
21
+
22
+ 다음 이벤트 핸들러가 등록됩니다:
23
+
24
+ - `checkMicrophonePermission` - 마이크 권한 확인
25
+ - `requestMicrophonePermission` - 마이크 권한 요청
26
+ - `startRecording` - 녹음 시작 (스트리밍)
27
+ - `stopRecording` - 녹음 중지
28
+ - `getMicrophoneStatus` - 마이크 상태 조회
29
+
30
+ ### 이벤트
31
+
32
+ - `onAudioChunk` - 실시간 오디오 청크 데이터 수신
33
+
34
+ ## 라이선스
35
+
36
+ MIT
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 카메라 브릿지 핸들러
3
+ * 의존성 주입을 통해 동작하는 순수한 브릿지 로직
4
+ */
5
+ import type { IBridge, IPlatform, ICameraModule } from '../types';
6
+ /**
7
+ * 카메라 브릿지 설정
8
+ */
9
+ export interface CameraBridgeConfig {
10
+ /**
11
+ * 브릿지 구현체
12
+ */
13
+ bridge: IBridge;
14
+ /**
15
+ * 플랫폼 구현체
16
+ */
17
+ platform: IPlatform;
18
+ /**
19
+ * 카메라 모듈 인스턴스
20
+ * 이 패키지의 src/module/index.ts를 직접 전달
21
+ */
22
+ cameraModule: ICameraModule;
23
+ /**
24
+ * 로거 (선택적)
25
+ */
26
+ logger?: {
27
+ log: (...args: any[]) => void;
28
+ warn: (...args: any[]) => void;
29
+ error: (...args: any[]) => void;
30
+ };
31
+ }
32
+ /**
33
+ * 카메라 브릿지 핸들러를 등록합니다
34
+ * @param config 브릿지 설정
35
+ */
36
+ export declare const registerCameraHandlers: (config: CameraBridgeConfig) => void;
37
+ //# sourceMappingURL=camera-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera-bridge.d.ts","sourceRoot":"","sources":["../../src/bridge/camera-bridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACR,OAAO,EACP,SAAS,EACT,aAAa,EAChB,MAAM,UAAU,CAAC;AAElB;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAC/B;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,YAAY,EAAE,aAAa,CAAC;IAE5B;;OAEG;IACH,MAAM,CAAC,EAAE;QACL,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAC9B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAC/B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;KACnC,CAAC;CACL;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,QAAQ,kBAAkB,SA+NhE,CAAC"}
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ /**
3
+ * 카메라 브릿지 핸들러
4
+ * 의존성 주입을 통해 동작하는 순수한 브릿지 로직
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.registerCameraHandlers = void 0;
8
+ /**
9
+ * 카메라 브릿지 핸들러를 등록합니다
10
+ * @param config 브릿지 설정
11
+ */
12
+ const registerCameraHandlers = (config) => {
13
+ const { bridge, platform, cameraModule: Camera, logger = console } = config;
14
+ // Android와 iOS만 지원
15
+ if (platform.OS !== 'android' && platform.OS !== 'ios') {
16
+ logger.log('[Bridge] Camera handlers skipped (Android/iOS only)');
17
+ return;
18
+ }
19
+ // 이벤트 리스너 구독 객체 저장 (메모리 누수 방지)
20
+ let frameSubscription = null;
21
+ // 카메라 권한 확인
22
+ bridge.registerHandler('checkCameraPermission', async (_payload, respond) => {
23
+ try {
24
+ const result = await Camera.checkCameraPermission();
25
+ respond({ success: true, ...result });
26
+ }
27
+ catch (error) {
28
+ respond({
29
+ success: false,
30
+ granted: false,
31
+ status: 'error',
32
+ error: error instanceof Error ? error.message : 'Failed to check camera permission'
33
+ });
34
+ }
35
+ });
36
+ // 카메라 권한 요청
37
+ bridge.registerHandler('requestCameraPermission', async (_payload, respond) => {
38
+ try {
39
+ const result = await Camera.requestCameraPermission();
40
+ respond({ success: true, ...result });
41
+ }
42
+ catch (error) {
43
+ respond({
44
+ success: false,
45
+ granted: false,
46
+ status: 'error',
47
+ error: error instanceof Error ? error.message : 'Failed to request camera permission'
48
+ });
49
+ }
50
+ });
51
+ // 사진 촬영
52
+ bridge.registerHandler('takePhoto', async (payload, respond) => {
53
+ try {
54
+ const options = payload;
55
+ const facing = options?.facing || 'back'; // 기본값: 후면 카메라
56
+ const result = await Camera.takePhoto(facing);
57
+ respond(result);
58
+ }
59
+ catch (error) {
60
+ respond({
61
+ success: false,
62
+ error: error instanceof Error ? error.message : 'Failed to take photo'
63
+ });
64
+ }
65
+ });
66
+ // 카메라 스트리밍 시작
67
+ bridge.registerHandler('startCamera', async (payload, respond) => {
68
+ try {
69
+ // 기존 리스너 정리 (중복 방지)
70
+ if (frameSubscription) {
71
+ try {
72
+ frameSubscription.remove();
73
+ }
74
+ catch (e) {
75
+ logger.warn('[Bridge] Failed to remove existing listener:', e);
76
+ }
77
+ }
78
+ // 새 리스너 등록
79
+ try {
80
+ frameSubscription = Camera.addListener('onCameraFrame', (data) => {
81
+ bridge.sendToWeb('onCameraFrame', data);
82
+ });
83
+ }
84
+ catch (e) {
85
+ logger.error('[Bridge] Failed to register frame listener:', e);
86
+ }
87
+ // 파라미터 정규화 (호환성 유지)
88
+ const options = payload;
89
+ const result = await Camera.startCamera(options);
90
+ respond(result);
91
+ }
92
+ catch (error) {
93
+ logger.error('[Bridge] startCamera error:', error);
94
+ respond({
95
+ success: false,
96
+ error: error instanceof Error ? error.message : 'Failed to start camera'
97
+ });
98
+ }
99
+ });
100
+ // 카메라 중지
101
+ bridge.registerHandler('stopCamera', async (_payload, respond) => {
102
+ try {
103
+ const result = await Camera.stopCamera();
104
+ // 이벤트 리스너 정리 (메모리 누수 방지)
105
+ if (frameSubscription) {
106
+ try {
107
+ frameSubscription.remove();
108
+ frameSubscription = null;
109
+ }
110
+ catch (e) {
111
+ logger.warn('[Bridge] Failed to remove frame listener:', e);
112
+ }
113
+ }
114
+ respond(result);
115
+ }
116
+ catch (error) {
117
+ respond({
118
+ success: false,
119
+ error: error instanceof Error ? error.message : 'Failed to stop camera'
120
+ });
121
+ }
122
+ });
123
+ // 카메라 상태 확인
124
+ bridge.registerHandler('getCameraStatus', async (_payload, respond) => {
125
+ try {
126
+ const status = await Camera.getCameraStatus();
127
+ respond(status);
128
+ }
129
+ catch (error) {
130
+ respond({
131
+ isStreaming: false,
132
+ facing: 'back',
133
+ hasCamera: false,
134
+ error: error instanceof Error ? error.message : 'Failed to get camera status'
135
+ });
136
+ }
137
+ });
138
+ // 크래시 로그 조회
139
+ bridge.registerHandler('getCrashLogs', async (_payload, respond) => {
140
+ try {
141
+ const result = await Camera.getCrashLogs();
142
+ respond(result);
143
+ }
144
+ catch (error) {
145
+ respond({
146
+ success: false,
147
+ error: error instanceof Error ? error.message : 'Failed to get crash logs'
148
+ });
149
+ }
150
+ });
151
+ // 크래시 로그 공유
152
+ bridge.registerHandler('shareCrashLog', async (payload, respond) => {
153
+ try {
154
+ const { filePath } = payload;
155
+ if (!filePath) {
156
+ respond({ success: false, error: 'filePath is required' });
157
+ return;
158
+ }
159
+ const result = await Camera.shareCrashLog(filePath);
160
+ respond(result);
161
+ }
162
+ catch (error) {
163
+ respond({
164
+ success: false,
165
+ error: error instanceof Error ? error.message : 'Failed to share crash log'
166
+ });
167
+ }
168
+ });
169
+ // 크래시 로그 삭제
170
+ bridge.registerHandler('clearCrashLogs', async (_payload, respond) => {
171
+ try {
172
+ const result = await Camera.clearCrashLogs();
173
+ respond(result);
174
+ }
175
+ catch (error) {
176
+ respond({
177
+ success: false,
178
+ error: error instanceof Error ? error.message : 'Failed to clear crash logs'
179
+ });
180
+ }
181
+ });
182
+ // 디버그 로그 가져오기
183
+ bridge.registerHandler('getDebugLog', async (_payload, respond) => {
184
+ try {
185
+ const result = await Camera.getDebugLog();
186
+ respond(result);
187
+ }
188
+ catch (error) {
189
+ respond({
190
+ success: false,
191
+ error: error instanceof Error ? error.message : 'Failed to get debug log'
192
+ });
193
+ }
194
+ });
195
+ // 디버그 로그 공유하기
196
+ bridge.registerHandler('shareDebugLog', async (_payload, respond) => {
197
+ try {
198
+ const result = await Camera.shareDebugLog();
199
+ respond(result);
200
+ }
201
+ catch (error) {
202
+ respond({
203
+ success: false,
204
+ error: error instanceof Error ? error.message : 'Failed to share debug log'
205
+ });
206
+ }
207
+ });
208
+ // 디버그 로그 삭제
209
+ bridge.registerHandler('clearDebugLog', async (_payload, respond) => {
210
+ try {
211
+ const result = await Camera.clearDebugLog();
212
+ respond(result);
213
+ }
214
+ catch (error) {
215
+ respond({
216
+ success: false,
217
+ error: error instanceof Error ? error.message : 'Failed to clear debug log'
218
+ });
219
+ }
220
+ });
221
+ logger.log('[Bridge] Camera handlers registered');
222
+ };
223
+ exports.registerCameraHandlers = registerCameraHandlers;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * 마이크 브릿지 메인 엔트리 포인트
3
+ *
4
+ * NPM 패키지로 사용 시 이 파일을 import하세요.
5
+ */
6
+ export { registerMicrophoneHandlers } from './microphone-bridge';
7
+ export type { MicrophoneBridgeConfig } from './microphone-bridge';
8
+ export * from '../types';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bridge/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAGlE,cAAc,UAAU,CAAC"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ /**
3
+ * 마이크 브릿지 메인 엔트리 포인트
4
+ *
5
+ * NPM 패키지로 사용 시 이 파일을 import하세요.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.registerMicrophoneHandlers = void 0;
23
+ // 메인 export (의존성 주입 방식)
24
+ var microphone_bridge_1 = require("./microphone-bridge");
25
+ Object.defineProperty(exports, "registerMicrophoneHandlers", { enumerable: true, get: function () { return microphone_bridge_1.registerMicrophoneHandlers; } });
26
+ // 타입 정의
27
+ __exportStar(require("../types"), exports);
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 마이크 브릿지 핸들러
3
+ * 의존성 주입을 통해 동작하는 순수한 브릿지 로직
4
+ */
5
+ import type { IBridge, IPlatform, IMicrophoneModule } from '../types';
6
+ /**
7
+ * 마이크 브릿지 설정
8
+ */
9
+ export interface MicrophoneBridgeConfig {
10
+ /**
11
+ * 브릿지 구현체
12
+ */
13
+ bridge: IBridge;
14
+ /**
15
+ * 플랫폼 구현체
16
+ */
17
+ platform: IPlatform;
18
+ /**
19
+ * 마이크 모듈 인스턴스
20
+ * 이 패키지의 src/module/index.ts를 직접 전달
21
+ */
22
+ microphoneModule: IMicrophoneModule;
23
+ /**
24
+ * 로거 (선택적)
25
+ */
26
+ logger?: {
27
+ log: (...args: any[]) => void;
28
+ warn: (...args: any[]) => void;
29
+ error: (...args: any[]) => void;
30
+ };
31
+ }
32
+ /**
33
+ * 마이크 브릿지 핸들러를 등록합니다
34
+ * @param config 브릿지 설정
35
+ */
36
+ export declare const registerMicrophoneHandlers: (config: MicrophoneBridgeConfig) => void;
37
+ //# sourceMappingURL=microphone-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microphone-bridge.d.ts","sourceRoot":"","sources":["../../src/bridge/microphone-bridge.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACR,OAAO,EACP,SAAS,EACT,iBAAiB,EACpB,MAAM,UAAU,CAAC;AAElB;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC;;OAEG;IACH,MAAM,EAAE,OAAO,CAAC;IAEhB;;OAEG;IACH,QAAQ,EAAE,SAAS,CAAC;IAEpB;;;OAGG;IACH,gBAAgB,EAAE,iBAAiB,CAAC;IAEpC;;OAEG;IACH,MAAM,CAAC,EAAE;QACL,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAC9B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAC/B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;KACnC,CAAC;CACL;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,sBAAsB,SAuHxE,CAAC"}
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * 마이크 브릿지 핸들러
4
+ * 의존성 주입을 통해 동작하는 순수한 브릿지 로직
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.registerMicrophoneHandlers = void 0;
8
+ /**
9
+ * 마이크 브릿지 핸들러를 등록합니다
10
+ * @param config 브릿지 설정
11
+ */
12
+ const registerMicrophoneHandlers = (config) => {
13
+ const { bridge, platform, microphoneModule: Microphone, logger = console } = config;
14
+ // Android와 iOS만 지원
15
+ if (platform.OS !== 'android' && platform.OS !== 'ios') {
16
+ logger.log('[Bridge] Microphone handlers skipped (Android/iOS only)');
17
+ return;
18
+ }
19
+ // 이벤트 리스너 구독 객체 저장 (메모리 누수 방지)
20
+ let audioChunkSubscription = null;
21
+ // 마이크 권한 확인
22
+ bridge.registerHandler('checkMicrophonePermission', async (_payload, respond) => {
23
+ try {
24
+ const result = await Microphone.checkMicrophonePermission();
25
+ respond({ success: true, ...result });
26
+ }
27
+ catch (error) {
28
+ respond({
29
+ success: false,
30
+ granted: false,
31
+ status: 'error',
32
+ error: error instanceof Error ? error.message : 'Failed to check microphone permission'
33
+ });
34
+ }
35
+ });
36
+ // 마이크 권한 요청
37
+ bridge.registerHandler('requestMicrophonePermission', async (_payload, respond) => {
38
+ try {
39
+ const result = await Microphone.requestMicrophonePermission();
40
+ respond({ success: true, ...result });
41
+ }
42
+ catch (error) {
43
+ respond({
44
+ success: false,
45
+ granted: false,
46
+ status: 'error',
47
+ error: error instanceof Error ? error.message : 'Failed to request microphone permission'
48
+ });
49
+ }
50
+ });
51
+ // 녹음 시작 (스트리밍)
52
+ bridge.registerHandler('startRecording', async (payload, respond) => {
53
+ try {
54
+ // 기존 리스너 정리 (중복 방지)
55
+ if (audioChunkSubscription) {
56
+ try {
57
+ audioChunkSubscription.remove();
58
+ }
59
+ catch (e) {
60
+ logger.warn('[Bridge] Failed to remove existing listener:', e);
61
+ }
62
+ }
63
+ // 새 리스너 등록
64
+ try {
65
+ audioChunkSubscription = Microphone.addListener('onAudioChunk', (data) => {
66
+ bridge.sendToWeb('onAudioChunk', data);
67
+ });
68
+ }
69
+ catch (e) {
70
+ logger.error('[Bridge] Failed to register audio chunk listener:', e);
71
+ }
72
+ // 파라미터 정규화
73
+ const options = payload;
74
+ const result = await Microphone.startRecording(options);
75
+ respond(result);
76
+ }
77
+ catch (error) {
78
+ logger.error('[Bridge] startRecording error:', error);
79
+ respond({
80
+ success: false,
81
+ error: error instanceof Error ? error.message : 'Failed to start recording'
82
+ });
83
+ }
84
+ });
85
+ // 녹음 중지
86
+ bridge.registerHandler('stopRecording', async (_payload, respond) => {
87
+ try {
88
+ const result = await Microphone.stopRecording();
89
+ // 이벤트 리스너 정리 (메모리 누수 방지)
90
+ if (audioChunkSubscription) {
91
+ try {
92
+ audioChunkSubscription.remove();
93
+ audioChunkSubscription = null;
94
+ }
95
+ catch (e) {
96
+ logger.warn('[Bridge] Failed to remove audio chunk listener:', e);
97
+ }
98
+ }
99
+ respond(result);
100
+ }
101
+ catch (error) {
102
+ respond({
103
+ success: false,
104
+ error: error instanceof Error ? error.message : 'Failed to stop recording'
105
+ });
106
+ }
107
+ });
108
+ // 마이크 상태 조회
109
+ bridge.registerHandler('getMicrophoneStatus', async (_payload, respond) => {
110
+ try {
111
+ const status = await Microphone.getMicrophoneStatus();
112
+ respond(status);
113
+ }
114
+ catch (error) {
115
+ respond({
116
+ isStreaming: false,
117
+ hasMicrophone: false,
118
+ error: error instanceof Error ? error.message : 'Failed to get microphone status'
119
+ });
120
+ }
121
+ });
122
+ logger.log('[Bridge] Microphone handlers registered');
123
+ };
124
+ exports.registerMicrophoneHandlers = registerMicrophoneHandlers;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 브릿지 통신 추상화 인터페이스
3
+ */
4
+ /**
5
+ * 브릿지 통신 인터페이스
6
+ * 기존 프로젝트와의 호환성을 위해 타입을 느슨하게 설정
7
+ */
8
+ export interface IBridge {
9
+ /**
10
+ * 네이티브에서 호출할 수 있는 핸들러를 등록합니다
11
+ * @param eventName 이벤트 이름
12
+ * @param handler 핸들러 함수
13
+ */
14
+ registerHandler(eventName: string, handler: any): void;
15
+ /**
16
+ * 웹으로 메시지를 전송합니다
17
+ * @param eventName 이벤트 이름
18
+ * @param data 전송할 데이터
19
+ */
20
+ sendToWeb(eventName: string, data: any): void;
21
+ }
22
+ //# sourceMappingURL=bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../../src/types/bridge.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB;;;;OAIG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC;IAEvD;;;;OAIG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;CAC/C"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * 브릿지 통신 추상화 인터페이스
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,149 @@
1
+ /**
2
+ * 카메라 모듈 인터페이스
3
+ */
4
+ /**
5
+ * 카메라 권한 상태
6
+ */
7
+ export interface CameraPermissionResult {
8
+ granted: boolean;
9
+ status: 'granted' | 'denied' | 'restricted' | 'unavailable' | 'error';
10
+ }
11
+ /**
12
+ * 카메라 방향
13
+ */
14
+ export type CameraFacing = 'front' | 'back';
15
+ /**
16
+ * 사진 촬영 결과
17
+ */
18
+ export interface PhotoResult {
19
+ success: boolean;
20
+ uri?: string;
21
+ error?: string;
22
+ }
23
+ /**
24
+ * 카메라 시작 옵션
25
+ */
26
+ export interface CameraStartOptions {
27
+ facing?: CameraFacing;
28
+ fps?: number;
29
+ quality?: number;
30
+ maxWidth?: number;
31
+ maxHeight?: number;
32
+ }
33
+ /**
34
+ * 카메라 상태
35
+ */
36
+ export interface CameraStatus {
37
+ isStreaming: boolean;
38
+ facing: CameraFacing;
39
+ hasCamera: boolean;
40
+ error?: string;
41
+ }
42
+ /**
43
+ * 크래시 로그 결과
44
+ */
45
+ export interface CrashLogsResult {
46
+ success: boolean;
47
+ logs?: string[];
48
+ error?: string;
49
+ }
50
+ /**
51
+ * 이벤트 구독 객체
52
+ */
53
+ export interface EventSubscription {
54
+ remove(): void;
55
+ }
56
+ /**
57
+ * 카메라 프레임 데이터
58
+ */
59
+ export interface CameraFrameData {
60
+ timestamp: number;
61
+ data: string;
62
+ width: number;
63
+ height: number;
64
+ }
65
+ /**
66
+ * 카메라 모듈 인터페이스
67
+ */
68
+ export interface ICameraModule {
69
+ /**
70
+ * 카메라 권한 확인
71
+ */
72
+ checkCameraPermission(): Promise<CameraPermissionResult>;
73
+ /**
74
+ * 카메라 권한 요청
75
+ */
76
+ requestCameraPermission(): Promise<CameraPermissionResult>;
77
+ /**
78
+ * 사진 촬영
79
+ * @param facing 카메라 방향
80
+ */
81
+ takePhoto(facing: CameraFacing): Promise<PhotoResult>;
82
+ /**
83
+ * 카메라 스트리밍 시작
84
+ * @param options 카메라 옵션
85
+ */
86
+ startCamera(options?: CameraStartOptions): Promise<{
87
+ success: boolean;
88
+ error?: string;
89
+ }>;
90
+ /**
91
+ * 카메라 스트리밍 중지
92
+ */
93
+ stopCamera(): Promise<{
94
+ success: boolean;
95
+ error?: string;
96
+ }>;
97
+ /**
98
+ * 카메라 상태 조회
99
+ */
100
+ getCameraStatus(): Promise<CameraStatus>;
101
+ /**
102
+ * 이벤트 리스너 등록
103
+ * @param eventName 이벤트 이름
104
+ * @param listener 리스너 함수
105
+ */
106
+ addListener(eventName: string, listener: (data: any) => void): EventSubscription;
107
+ /**
108
+ * 크래시 로그 조회
109
+ */
110
+ getCrashLogs(): Promise<CrashLogsResult>;
111
+ /**
112
+ * 크래시 로그 공유
113
+ * @param filePath 파일 경로
114
+ */
115
+ shareCrashLog(filePath: string): Promise<{
116
+ success: boolean;
117
+ error?: string;
118
+ }>;
119
+ /**
120
+ * 크래시 로그 삭제
121
+ */
122
+ clearCrashLogs(): Promise<{
123
+ success: boolean;
124
+ error?: string;
125
+ }>;
126
+ /**
127
+ * 디버그 로그 가져오기
128
+ */
129
+ getDebugLog(): Promise<{
130
+ success: boolean;
131
+ log?: string;
132
+ error?: string;
133
+ }>;
134
+ /**
135
+ * 디버그 로그 공유하기
136
+ */
137
+ shareDebugLog(): Promise<{
138
+ success: boolean;
139
+ error?: string;
140
+ }>;
141
+ /**
142
+ * 디버그 로그 삭제
143
+ */
144
+ clearDebugLog(): Promise<{
145
+ success: boolean;
146
+ error?: string;
147
+ }>;
148
+ }
149
+ //# sourceMappingURL=camera-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"camera-module.d.ts","sourceRoot":"","sources":["../../src/types/camera-module.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * 카메라 모듈 인터페이스
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,7 @@
1
+ /**
2
+ * 타입 정의 모음
3
+ */
4
+ export * from './platform';
5
+ export * from './bridge';
6
+ export * from './microphone-module';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /**
3
+ * 타입 정의 모음
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ __exportStar(require("./platform"), exports);
21
+ __exportStar(require("./bridge"), exports);
22
+ __exportStar(require("./microphone-module"), exports);
@@ -0,0 +1,84 @@
1
+ /**
2
+ * 마이크 모듈 인터페이스
3
+ */
4
+ /**
5
+ * 마이크 권한 상태
6
+ */
7
+ export interface MicrophonePermissionResult {
8
+ granted: boolean;
9
+ status: 'granted' | 'denied' | 'restricted' | 'unavailable' | 'error';
10
+ }
11
+ /**
12
+ * 녹음 시작 옵션
13
+ */
14
+ export interface MicrophoneStartOptions {
15
+ /** 샘플레이트 (8000-48000, 기본값: 44100) */
16
+ sampleRate?: number;
17
+ /** 청크 크기 (512-8192 bytes, 기본값: 2048 약 23ms) */
18
+ chunkSize?: number;
19
+ }
20
+ /**
21
+ * 마이크 상태
22
+ */
23
+ export interface MicrophoneStatus {
24
+ isStreaming: boolean;
25
+ hasMicrophone: boolean;
26
+ error?: string;
27
+ }
28
+ /**
29
+ * 이벤트 구독 객체
30
+ */
31
+ export interface EventSubscription {
32
+ remove(): void;
33
+ }
34
+ /**
35
+ * 오디오 청크 데이터
36
+ */
37
+ export interface AudioChunkData {
38
+ type: 'audioChunk';
39
+ timestamp: number;
40
+ base64: string;
41
+ chunkSize: number;
42
+ chunkNumber: number;
43
+ sampleRate: number;
44
+ encoding: 'pcm_16bit';
45
+ }
46
+ /**
47
+ * 마이크 모듈 인터페이스
48
+ */
49
+ export interface IMicrophoneModule {
50
+ /**
51
+ * 마이크 권한 확인
52
+ */
53
+ checkMicrophonePermission(): Promise<MicrophonePermissionResult>;
54
+ /**
55
+ * 마이크 권한 요청
56
+ */
57
+ requestMicrophonePermission(): Promise<MicrophonePermissionResult>;
58
+ /**
59
+ * 녹음 시작 (스트리밍)
60
+ * @param options 녹음 옵션
61
+ */
62
+ startRecording(options?: MicrophoneStartOptions): Promise<{
63
+ success: boolean;
64
+ error?: string;
65
+ }>;
66
+ /**
67
+ * 녹음 중지
68
+ */
69
+ stopRecording(): Promise<{
70
+ success: boolean;
71
+ error?: string;
72
+ }>;
73
+ /**
74
+ * 마이크 상태 조회
75
+ */
76
+ getMicrophoneStatus(): Promise<MicrophoneStatus>;
77
+ /**
78
+ * 이벤트 리스너 등록
79
+ * @param eventName 이벤트 이름
80
+ * @param listener 리스너 함수
81
+ */
82
+ addListener(eventName: string, listener: (data: AudioChunkData) => void): EventSubscription;
83
+ }
84
+ //# sourceMappingURL=microphone-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"microphone-module.d.ts","sourceRoot":"","sources":["../../src/types/microphone-module.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,YAAY,GAAG,aAAa,GAAG,OAAO,CAAC;CACvE;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,qCAAqC;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,IAAI,IAAI,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,WAAW,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,yBAAyB,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAEjE;;OAEG;IACH,2BAA2B,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAAC;IAEnE;;;OAGG;IACH,cAAc,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAEhG;;OAEG;IACH,aAAa,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE/D;;OAEG;IACH,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAEjD;;;;OAIG;IACH,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,GAAG,iBAAiB,CAAC;CAC7F"}
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * 마이크 모듈 인터페이스
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 플랫폼 추상화 인터페이스
3
+ */
4
+ export interface IPlatform {
5
+ /**
6
+ * 현재 실행 중인 플랫폼
7
+ */
8
+ OS: 'ios' | 'android' | 'windows' | 'macos' | 'web';
9
+ }
10
+ //# sourceMappingURL=platform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../../src/types/platform.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;OAEG;IACH,EAAE,EAAE,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,KAAK,CAAC;CACrD"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "rnww-plugin-microphone",
3
+ "version": "1.0.1",
4
+ "description": "React Native WebView Microphone Plugin with Expo support",
5
+ "main": "lib/bridge/index.js",
6
+ "types": "lib/bridge/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "prepare": "npm run build",
10
+ "clean": "rimraf lib"
11
+ },
12
+ "keywords": [
13
+ "react-native",
14
+ "expo",
15
+ "microphone",
16
+ "audio",
17
+ "recording",
18
+ "webview",
19
+ "bridge",
20
+ "expo-module"
21
+ ],
22
+ "author": "",
23
+ "license": "MIT",
24
+ "files": [
25
+ "lib/",
26
+ "src/module/",
27
+ "README.md"
28
+ ],
29
+ "peerDependencies": {
30
+ "expo": "*",
31
+ "expo-modules-core": "*"
32
+ },
33
+ "devDependencies": {
34
+ "@types/react": "^19.0.0",
35
+ "@types/react-native": "^0.73.0",
36
+ "rimraf": "^5.0.10",
37
+ "typescript": "^5.9.3"
38
+ }
39
+ }