yaver-feedback-react-native 0.5.2 → 0.5.4

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.
Files changed (50) hide show
  1. package/README.md +8 -0
  2. package/dist/AuthOverlay.d.ts +16 -0
  3. package/dist/AuthOverlay.js +104 -0
  4. package/dist/BlackBox.d.ts +154 -0
  5. package/dist/BlackBox.js +395 -0
  6. package/dist/ConnectionScreen.d.ts +13 -0
  7. package/dist/ConnectionScreen.js +373 -0
  8. package/dist/Discovery.d.ts +59 -0
  9. package/dist/Discovery.js +293 -0
  10. package/dist/FeedbackModal.d.ts +11 -0
  11. package/dist/FeedbackModal.js +623 -0
  12. package/dist/FixReport.d.ts +23 -0
  13. package/dist/FixReport.js +282 -0
  14. package/dist/FloatingButton.d.ts +71 -0
  15. package/dist/FloatingButton.js +778 -0
  16. package/dist/LoginScreen.d.ts +14 -0
  17. package/dist/LoginScreen.js +317 -0
  18. package/dist/MachinePickerScreen.d.ts +19 -0
  19. package/dist/MachinePickerScreen.js +175 -0
  20. package/dist/P2PClient.d.ts +136 -0
  21. package/dist/P2PClient.js +357 -0
  22. package/dist/ShakeDetector.d.ts +39 -0
  23. package/dist/ShakeDetector.js +111 -0
  24. package/dist/YaverFeedback.d.ts +198 -0
  25. package/dist/YaverFeedback.js +679 -0
  26. package/dist/YaverUpdates.d.ts +78 -0
  27. package/dist/YaverUpdates.js +272 -0
  28. package/dist/__tests__/Discovery.test.d.ts +1 -0
  29. package/dist/__tests__/Discovery.test.js +164 -0
  30. package/dist/__tests__/P2PClient.test.d.ts +1 -0
  31. package/dist/__tests__/P2PClient.test.js +169 -0
  32. package/dist/__tests__/SDKToken.test.d.ts +1 -0
  33. package/dist/__tests__/SDKToken.test.js +215 -0
  34. package/dist/__tests__/YaverFeedback.test.d.ts +1 -0
  35. package/dist/__tests__/YaverFeedback.test.js +161 -0
  36. package/dist/__tests__/types.test.d.ts +1 -0
  37. package/dist/__tests__/types.test.js +219 -0
  38. package/dist/auth.d.ts +105 -0
  39. package/dist/auth.js +282 -0
  40. package/dist/capture.d.ts +27 -0
  41. package/dist/capture.js +74 -0
  42. package/dist/expo.d.ts +15 -0
  43. package/dist/expo.js +62 -0
  44. package/dist/index.d.ts +48 -0
  45. package/dist/index.js +80 -0
  46. package/dist/types.d.ts +282 -0
  47. package/dist/types.js +2 -0
  48. package/dist/upload.d.ts +13 -0
  49. package/dist/upload.js +59 -0
  50. package/package.json +10 -6
package/dist/auth.d.ts ADDED
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Authentication + device/agent discovery API used by the Yaver Feedback SDK.
3
+ *
4
+ * This module is a trimmed SDK-local port of mobile/src/lib/auth.ts. It only
5
+ * covers what the embedded login/machine-picker flow needs:
6
+ *
7
+ * - Device-code login (`POST /auth/device-code` + `GET /auth/device-code/poll`)
8
+ * so users can sign in via any OAuth provider (apple/google/github/gitlab/
9
+ * microsoft) on yaver.io without requiring deep-link wiring in the host app.
10
+ * - Email / password sign-up + login (no 2FA flow — for SDK simplicity).
11
+ * - Token validation + refresh.
12
+ * - `/devices/list` → owned + shared (guest) remote dev machines.
13
+ *
14
+ * All calls target the public Yaver Convex site URL by default; callers may
15
+ * override via `init()` config to point at staging.
16
+ *
17
+ * Token persistence uses `@react-native-async-storage/async-storage` (already
18
+ * a peer dep). SecureStore is intentionally avoided to keep the SDK portable
19
+ * to any RN host app.
20
+ */
21
+ export declare const DEFAULT_CONVEX_SITE_URL = "https://shocking-echidna-394.eu-west-1.convex.site";
22
+ export declare const DEFAULT_WEB_BASE_URL = "https://yaver.io";
23
+ /** Override the Convex site URL + web base (staging vs prod). */
24
+ export declare function configureAuthEndpoints(opts: {
25
+ convexSiteUrl?: string;
26
+ webBaseUrl?: string;
27
+ }): void;
28
+ export declare function getConvexSiteUrl(): string;
29
+ export declare function getWebBaseUrl(): string;
30
+ export type OAuthProvider = 'google' | 'microsoft' | 'apple' | 'github' | 'gitlab';
31
+ export interface User {
32
+ id: string;
33
+ email: string;
34
+ name: string;
35
+ provider?: string;
36
+ avatarUrl?: string;
37
+ }
38
+ export declare function getToken(): Promise<string | null>;
39
+ export declare function saveToken(token: string): Promise<void>;
40
+ export declare function clearToken(): Promise<void>;
41
+ export declare function getUser(): Promise<User | null>;
42
+ export declare function saveUser(user: User): Promise<void>;
43
+ export declare function getSelectedDeviceId(): Promise<string | null>;
44
+ export declare function saveSelectedDeviceId(deviceId: string): Promise<void>;
45
+ export declare function clearSelectedDeviceId(): Promise<void>;
46
+ export declare function validateToken(token: string): Promise<User | null>;
47
+ export interface DeviceCodeStart {
48
+ userCode: string;
49
+ deviceCode: string;
50
+ expiresAt: number;
51
+ verificationUrl: string;
52
+ }
53
+ /**
54
+ * Start a device-code flow. The user opens `verificationUrl`, signs in with
55
+ * any OAuth provider on yaver.io, and the SDK polls `pollDeviceCode` until
56
+ * a session token is issued.
57
+ */
58
+ export declare function startDeviceCode(opts?: {
59
+ machineName?: string;
60
+ platform?: string;
61
+ preferredProvider?: OAuthProvider;
62
+ }): Promise<DeviceCodeStart>;
63
+ export type DeviceCodePoll = {
64
+ status: 'pending';
65
+ } | {
66
+ status: 'authorized';
67
+ token: string;
68
+ } | {
69
+ status: 'expired';
70
+ };
71
+ export declare function pollDeviceCode(deviceCode: string): Promise<DeviceCodePoll>;
72
+ export declare function signupWithEmail(fullName: string, email: string, password: string): Promise<{
73
+ token: string;
74
+ userId: string;
75
+ }>;
76
+ export declare function loginWithEmail(email: string, password: string): Promise<{
77
+ token: string;
78
+ userId: string;
79
+ requires2fa?: boolean;
80
+ }>;
81
+ export interface RemoteDevice {
82
+ deviceId: string;
83
+ name: string;
84
+ platform: string;
85
+ isOnline: boolean;
86
+ needsAuth: boolean;
87
+ runnerDown: boolean;
88
+ lastHeartbeat: number;
89
+ isGuest: boolean;
90
+ hostName?: string;
91
+ hostEmail?: string;
92
+ accessScope: 'owner' | 'shared-scoped' | 'shared-legacy';
93
+ quicHost: string;
94
+ quicPort: number;
95
+ publicKey?: string;
96
+ }
97
+ export interface DeviceList {
98
+ owned: RemoteDevice[];
99
+ shared: RemoteDevice[];
100
+ }
101
+ /**
102
+ * Fetch the set of remote dev machines this user can reach. Splits into
103
+ * owned (user is the host) vs shared (host invited them as a guest).
104
+ */
105
+ export declare function listReachableDevices(token: string): Promise<DeviceList>;
package/dist/auth.js ADDED
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ /**
3
+ * Authentication + device/agent discovery API used by the Yaver Feedback SDK.
4
+ *
5
+ * This module is a trimmed SDK-local port of mobile/src/lib/auth.ts. It only
6
+ * covers what the embedded login/machine-picker flow needs:
7
+ *
8
+ * - Device-code login (`POST /auth/device-code` + `GET /auth/device-code/poll`)
9
+ * so users can sign in via any OAuth provider (apple/google/github/gitlab/
10
+ * microsoft) on yaver.io without requiring deep-link wiring in the host app.
11
+ * - Email / password sign-up + login (no 2FA flow — for SDK simplicity).
12
+ * - Token validation + refresh.
13
+ * - `/devices/list` → owned + shared (guest) remote dev machines.
14
+ *
15
+ * All calls target the public Yaver Convex site URL by default; callers may
16
+ * override via `init()` config to point at staging.
17
+ *
18
+ * Token persistence uses `@react-native-async-storage/async-storage` (already
19
+ * a peer dep). SecureStore is intentionally avoided to keep the SDK portable
20
+ * to any RN host app.
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.DEFAULT_WEB_BASE_URL = exports.DEFAULT_CONVEX_SITE_URL = void 0;
24
+ exports.configureAuthEndpoints = configureAuthEndpoints;
25
+ exports.getConvexSiteUrl = getConvexSiteUrl;
26
+ exports.getWebBaseUrl = getWebBaseUrl;
27
+ exports.getToken = getToken;
28
+ exports.saveToken = saveToken;
29
+ exports.clearToken = clearToken;
30
+ exports.getUser = getUser;
31
+ exports.saveUser = saveUser;
32
+ exports.getSelectedDeviceId = getSelectedDeviceId;
33
+ exports.saveSelectedDeviceId = saveSelectedDeviceId;
34
+ exports.clearSelectedDeviceId = clearSelectedDeviceId;
35
+ exports.validateToken = validateToken;
36
+ exports.startDeviceCode = startDeviceCode;
37
+ exports.pollDeviceCode = pollDeviceCode;
38
+ exports.signupWithEmail = signupWithEmail;
39
+ exports.loginWithEmail = loginWithEmail;
40
+ exports.listReachableDevices = listReachableDevices;
41
+ // AsyncStorage is an optional peer dep — degrade gracefully if missing.
42
+ let AsyncStorage = null;
43
+ try {
44
+ AsyncStorage = require('@react-native-async-storage/async-storage').default;
45
+ }
46
+ catch {
47
+ // not installed — token persistence disabled, caller must pass authToken
48
+ }
49
+ const TOKEN_KEY = 'yaver_feedback_auth_token';
50
+ const USER_KEY = 'yaver_feedback_user';
51
+ const DEVICE_KEY = 'yaver_feedback_selected_device';
52
+ exports.DEFAULT_CONVEX_SITE_URL = 'https://shocking-echidna-394.eu-west-1.convex.site';
53
+ exports.DEFAULT_WEB_BASE_URL = 'https://yaver.io';
54
+ let convexSiteUrl = exports.DEFAULT_CONVEX_SITE_URL;
55
+ let webBaseUrl = exports.DEFAULT_WEB_BASE_URL;
56
+ /** Override the Convex site URL + web base (staging vs prod). */
57
+ function configureAuthEndpoints(opts) {
58
+ if (opts.convexSiteUrl)
59
+ convexSiteUrl = opts.convexSiteUrl;
60
+ if (opts.webBaseUrl)
61
+ webBaseUrl = opts.webBaseUrl;
62
+ }
63
+ function getConvexSiteUrl() {
64
+ return convexSiteUrl;
65
+ }
66
+ function getWebBaseUrl() {
67
+ return webBaseUrl;
68
+ }
69
+ // ─── Token persistence ────────────────────────────────────────────────
70
+ async function getToken() {
71
+ if (!AsyncStorage)
72
+ return null;
73
+ try {
74
+ return await AsyncStorage.getItem(TOKEN_KEY);
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ }
80
+ async function saveToken(token) {
81
+ if (!AsyncStorage)
82
+ return;
83
+ try {
84
+ await AsyncStorage.setItem(TOKEN_KEY, token);
85
+ }
86
+ catch {
87
+ // best effort
88
+ }
89
+ }
90
+ async function clearToken() {
91
+ if (!AsyncStorage)
92
+ return;
93
+ try {
94
+ await AsyncStorage.removeItem(TOKEN_KEY);
95
+ await AsyncStorage.removeItem(USER_KEY);
96
+ }
97
+ catch {
98
+ // best effort
99
+ }
100
+ }
101
+ async function getUser() {
102
+ if (!AsyncStorage)
103
+ return null;
104
+ try {
105
+ const raw = await AsyncStorage.getItem(USER_KEY);
106
+ if (!raw)
107
+ return null;
108
+ return JSON.parse(raw);
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ async function saveUser(user) {
115
+ if (!AsyncStorage)
116
+ return;
117
+ try {
118
+ await AsyncStorage.setItem(USER_KEY, JSON.stringify(user));
119
+ }
120
+ catch {
121
+ // best effort
122
+ }
123
+ }
124
+ async function getSelectedDeviceId() {
125
+ if (!AsyncStorage)
126
+ return null;
127
+ try {
128
+ return await AsyncStorage.getItem(DEVICE_KEY);
129
+ }
130
+ catch {
131
+ return null;
132
+ }
133
+ }
134
+ async function saveSelectedDeviceId(deviceId) {
135
+ if (!AsyncStorage)
136
+ return;
137
+ try {
138
+ await AsyncStorage.setItem(DEVICE_KEY, deviceId);
139
+ }
140
+ catch {
141
+ // best effort
142
+ }
143
+ }
144
+ async function clearSelectedDeviceId() {
145
+ if (!AsyncStorage)
146
+ return;
147
+ try {
148
+ await AsyncStorage.removeItem(DEVICE_KEY);
149
+ }
150
+ catch {
151
+ // best effort
152
+ }
153
+ }
154
+ // ─── Token validation ──────────────────────────────────────────────────
155
+ async function validateToken(token) {
156
+ try {
157
+ const controller = new AbortController();
158
+ const timeout = setTimeout(() => controller.abort(), 5000);
159
+ const res = await fetch(`${convexSiteUrl}/auth/validate`, {
160
+ method: 'GET',
161
+ headers: { Authorization: `Bearer ${token}` },
162
+ signal: controller.signal,
163
+ });
164
+ clearTimeout(timeout);
165
+ if (!res.ok)
166
+ return null;
167
+ const data = await res.json();
168
+ const u = data.user;
169
+ return {
170
+ id: u.userId ?? u.id,
171
+ email: u.email,
172
+ name: u.fullName ?? u.name,
173
+ provider: u.provider,
174
+ avatarUrl: u.avatarUrl,
175
+ };
176
+ }
177
+ catch {
178
+ return null;
179
+ }
180
+ }
181
+ /**
182
+ * Start a device-code flow. The user opens `verificationUrl`, signs in with
183
+ * any OAuth provider on yaver.io, and the SDK polls `pollDeviceCode` until
184
+ * a session token is issued.
185
+ */
186
+ async function startDeviceCode(opts) {
187
+ const res = await fetch(`${convexSiteUrl}/auth/device-code`, {
188
+ method: 'POST',
189
+ headers: { 'Content-Type': 'application/json' },
190
+ body: JSON.stringify({
191
+ machineName: opts?.machineName,
192
+ platform: opts?.platform,
193
+ preferredProvider: opts?.preferredProvider,
194
+ environment: 'feedback-sdk',
195
+ }),
196
+ });
197
+ if (!res.ok) {
198
+ const data = await res.json().catch(() => ({}));
199
+ throw new Error(data.error ?? 'Failed to start device-code');
200
+ }
201
+ const data = await res.json();
202
+ const params = new URLSearchParams({ code: data.userCode });
203
+ if (opts?.preferredProvider) {
204
+ params.set('preferredProvider', opts.preferredProvider);
205
+ }
206
+ return {
207
+ userCode: data.userCode,
208
+ deviceCode: data.deviceCode,
209
+ expiresAt: data.expiresAt,
210
+ verificationUrl: `${webBaseUrl}/auth/device?${params.toString()}`,
211
+ };
212
+ }
213
+ async function pollDeviceCode(deviceCode) {
214
+ try {
215
+ const res = await fetch(`${convexSiteUrl}/auth/device-code/poll?device_code=${encodeURIComponent(deviceCode)}`);
216
+ if (!res.ok)
217
+ return { status: 'expired' };
218
+ const data = await res.json();
219
+ if (data.status === 'authorized' && typeof data.token === 'string') {
220
+ return { status: 'authorized', token: data.token };
221
+ }
222
+ if (data.status === 'pending')
223
+ return { status: 'pending' };
224
+ return { status: 'expired' };
225
+ }
226
+ catch {
227
+ return { status: 'pending' }; // network blip — let caller keep polling
228
+ }
229
+ }
230
+ // ─── Email / password (no 2FA) ────────────────────────────────────────
231
+ async function signupWithEmail(fullName, email, password) {
232
+ const res = await fetch(`${convexSiteUrl}/auth/signup`, {
233
+ method: 'POST',
234
+ headers: { 'Content-Type': 'application/json' },
235
+ body: JSON.stringify({ fullName, email, password }),
236
+ });
237
+ if (!res.ok) {
238
+ const data = await res.json().catch(() => ({}));
239
+ throw new Error(data.error ?? 'Signup failed');
240
+ }
241
+ return res.json();
242
+ }
243
+ async function loginWithEmail(email, password) {
244
+ const res = await fetch(`${convexSiteUrl}/auth/login`, {
245
+ method: 'POST',
246
+ headers: { 'Content-Type': 'application/json' },
247
+ body: JSON.stringify({ email, password }),
248
+ });
249
+ if (!res.ok) {
250
+ const data = await res.json().catch(() => ({}));
251
+ throw new Error(data.error ?? 'Login failed');
252
+ }
253
+ const data = await res.json();
254
+ if (data?.requires2fa) {
255
+ // SDK login surface does not handle 2FA — direct the user to complete
256
+ // sign-in through the web flow (device-code) which supports it.
257
+ throw new Error('2FA is enabled on this account. Sign in via the device-code flow instead.');
258
+ }
259
+ return { token: data.token, userId: data.userId };
260
+ }
261
+ /**
262
+ * Fetch the set of remote dev machines this user can reach. Splits into
263
+ * owned (user is the host) vs shared (host invited them as a guest).
264
+ */
265
+ async function listReachableDevices(token) {
266
+ try {
267
+ const res = await fetch(`${convexSiteUrl}/devices/list`, {
268
+ headers: { Authorization: `Bearer ${token}` },
269
+ });
270
+ if (!res.ok)
271
+ return { owned: [], shared: [] };
272
+ const data = await res.json();
273
+ const all = (data.devices ?? []);
274
+ return {
275
+ owned: all.filter((d) => !d.isGuest),
276
+ shared: all.filter((d) => d.isGuest),
277
+ };
278
+ }
279
+ catch {
280
+ return { owned: [], shared: [] };
281
+ }
282
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Screen capture and audio recording helpers.
3
+ *
4
+ * Screenshot capture requires `react-native-view-shot` as a peer dependency.
5
+ * Audio recording requires `react-native-audio-recorder-player` or a
6
+ * similar library — the implementation below uses a minimal approach
7
+ * that works when one of those is available.
8
+ */
9
+ /**
10
+ * Capture the current screen as a PNG image.
11
+ * Requires `react-native-view-shot` to be installed.
12
+ * @returns File path of the captured screenshot.
13
+ */
14
+ export declare function captureScreenshot(): Promise<string>;
15
+ /**
16
+ * Start recording an audio voice note.
17
+ * Requires `react-native-audio-recorder-player` to be installed.
18
+ */
19
+ export declare function startAudioRecording(): Promise<void>;
20
+ /**
21
+ * Stop the current audio recording.
22
+ * @returns Object with the file path and duration in seconds.
23
+ */
24
+ export declare function stopAudioRecording(): Promise<{
25
+ path: string;
26
+ duration: number;
27
+ }>;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /**
3
+ * Screen capture and audio recording helpers.
4
+ *
5
+ * Screenshot capture requires `react-native-view-shot` as a peer dependency.
6
+ * Audio recording requires `react-native-audio-recorder-player` or a
7
+ * similar library — the implementation below uses a minimal approach
8
+ * that works when one of those is available.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.captureScreenshot = captureScreenshot;
12
+ exports.startAudioRecording = startAudioRecording;
13
+ exports.stopAudioRecording = stopAudioRecording;
14
+ let audioRecorderModule = null;
15
+ /**
16
+ * Capture the current screen as a PNG image.
17
+ * Requires `react-native-view-shot` to be installed.
18
+ * @returns File path of the captured screenshot.
19
+ */
20
+ async function captureScreenshot() {
21
+ try {
22
+ const ViewShot = require('react-native-view-shot');
23
+ const uri = await ViewShot.captureScreen({
24
+ format: 'png',
25
+ quality: 0.9,
26
+ });
27
+ return uri;
28
+ }
29
+ catch (err) {
30
+ throw new Error('[YaverFeedback] Screenshot capture failed. Make sure react-native-view-shot is installed. ' +
31
+ String(err));
32
+ }
33
+ }
34
+ /**
35
+ * Start recording an audio voice note.
36
+ * Requires `react-native-audio-recorder-player` to be installed.
37
+ */
38
+ async function startAudioRecording() {
39
+ try {
40
+ const AudioRecorderPlayer = require('react-native-audio-recorder-player').default;
41
+ audioRecorderModule = new AudioRecorderPlayer();
42
+ await audioRecorderModule.startRecorder();
43
+ }
44
+ catch (err) {
45
+ audioRecorderModule = null;
46
+ throw new Error('[YaverFeedback] Audio recording failed to start. Make sure react-native-audio-recorder-player is installed. ' +
47
+ String(err));
48
+ }
49
+ }
50
+ /**
51
+ * Stop the current audio recording.
52
+ * @returns Object with the file path and duration in seconds.
53
+ */
54
+ async function stopAudioRecording() {
55
+ if (!audioRecorderModule) {
56
+ throw new Error('[YaverFeedback] No audio recording in progress.');
57
+ }
58
+ try {
59
+ const result = await audioRecorderModule.stopRecorder();
60
+ const recorder = audioRecorderModule;
61
+ audioRecorderModule = null;
62
+ // result is the file path on most implementations
63
+ const path = typeof result === 'string' ? result : result?.uri ?? '';
64
+ // Duration tracking — recorder-player provides currentPosition in ms
65
+ const durationMs = typeof recorder.currentPosition === 'number'
66
+ ? recorder.currentPosition
67
+ : 0;
68
+ return { path, duration: durationMs / 1000 };
69
+ }
70
+ catch (err) {
71
+ audioRecorderModule = null;
72
+ throw new Error('[YaverFeedback] Failed to stop audio recording. ' + String(err));
73
+ }
74
+ }
package/dist/expo.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { FeedbackConfig } from './types';
2
+ /**
3
+ * Initialize the Yaver Feedback SDK for Expo projects.
4
+ *
5
+ * Attempts to read the agent URL from Expo Constants (`expo.extra.yaverAgentUrl`
6
+ * in app.json). If not set, the SDK auto-discovers agents on the local network.
7
+ *
8
+ * Defaults:
9
+ * - trigger: 'shake'
10
+ * - feedbackMode: 'batch'
11
+ * - enabled: __DEV__ (only active in development)
12
+ *
13
+ * @param overrides - Optional partial config to override defaults
14
+ */
15
+ export declare function initExpo(overrides?: Partial<FeedbackConfig>): void;
package/dist/expo.js ADDED
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.initExpo = initExpo;
4
+ /**
5
+ * Expo-specific auto-initialization for the Yaver Feedback SDK.
6
+ *
7
+ * Reads agent URL from Expo Constants manifest extra if available,
8
+ * otherwise falls back to LAN auto-discovery.
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * import { initExpo, FeedbackModal } from '@yaver/feedback-react-native';
13
+ *
14
+ * initExpo(); // auto-discovers your dev machine
15
+ *
16
+ * function App() {
17
+ * return (
18
+ * <>
19
+ * <YourApp />
20
+ * <FeedbackModal />
21
+ * </>
22
+ * );
23
+ * }
24
+ * ```
25
+ */
26
+ const YaverFeedback_1 = require("./YaverFeedback");
27
+ /**
28
+ * Initialize the Yaver Feedback SDK for Expo projects.
29
+ *
30
+ * Attempts to read the agent URL from Expo Constants (`expo.extra.yaverAgentUrl`
31
+ * in app.json). If not set, the SDK auto-discovers agents on the local network.
32
+ *
33
+ * Defaults:
34
+ * - trigger: 'shake'
35
+ * - feedbackMode: 'batch'
36
+ * - enabled: __DEV__ (only active in development)
37
+ *
38
+ * @param overrides - Optional partial config to override defaults
39
+ */
40
+ function initExpo(overrides) {
41
+ let agentUrl;
42
+ // Try reading agent URL from Expo Constants manifest extra
43
+ try {
44
+ // Dynamic require so this doesn't hard-fail if expo-constants isn't installed
45
+ const Constants = require('expo-constants').default;
46
+ agentUrl =
47
+ Constants.expoConfig?.extra?.yaverAgentUrl ??
48
+ Constants.manifest?.extra?.yaverAgentUrl ??
49
+ Constants.manifest2?.extra?.expoClient?.extra?.yaverAgentUrl;
50
+ }
51
+ catch {
52
+ // expo-constants not available — auto-discovery will be used
53
+ }
54
+ YaverFeedback_1.YaverFeedback.init({
55
+ authToken: '', // LAN auto-discovery doesn't require a token
56
+ trigger: 'shake',
57
+ feedbackMode: 'batch',
58
+ enabled: __DEV__,
59
+ ...overrides,
60
+ ...(agentUrl ? { agentUrl } : {}),
61
+ });
62
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @yaver/feedback-react-native — Visual feedback SDK for Yaver.
3
+ *
4
+ * Shake-to-report, screenshots, voice annotations, P2P connection,
5
+ * device discovery, and live/narrated/batch feedback modes for vibe coding.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { YaverFeedback, FeedbackProvider } from '@yaver/feedback-react-native';
10
+ *
11
+ * YaverFeedback.init({
12
+ * agentUrl: 'http://192.168.1.10:18080',
13
+ * authToken: 'your-token',
14
+ * trigger: 'shake',
15
+ * feedbackMode: 'live',
16
+ * });
17
+ *
18
+ * // Wrap your app root:
19
+ * <FeedbackProvider>
20
+ * <App />
21
+ * </FeedbackProvider>
22
+ * ```
23
+ */
24
+ export { YaverFeedback } from './YaverFeedback';
25
+ export { BlackBox } from './BlackBox';
26
+ export { YaverUpdates } from './YaverUpdates';
27
+ export type { YaverUpdatesConfig, PendingUpdate } from './YaverUpdates';
28
+ export { initExpo } from './expo';
29
+ export { YaverDiscovery } from './Discovery';
30
+ export { P2PClient } from './P2PClient';
31
+ export { YaverConnectionScreen } from './ConnectionScreen';
32
+ export { YaverLoginScreen } from './LoginScreen';
33
+ export type { YaverLoginScreenProps } from './LoginScreen';
34
+ export { YaverMachinePickerScreen } from './MachinePickerScreen';
35
+ export type { YaverMachinePickerProps } from './MachinePickerScreen';
36
+ export { AuthOverlay } from './AuthOverlay';
37
+ export { ShakeDetector } from './ShakeDetector';
38
+ export { FloatingButton } from './FloatingButton';
39
+ export { FeedbackModal } from './FeedbackModal';
40
+ export { FixReport } from './FixReport';
41
+ export { configureAuthEndpoints, getConvexSiteUrl, getWebBaseUrl, getToken, saveToken, clearToken, getUser, saveUser, getSelectedDeviceId, saveSelectedDeviceId, clearSelectedDeviceId, validateToken, startDeviceCode, pollDeviceCode, signupWithEmail, loginWithEmail, listReachableDevices, DEFAULT_CONVEX_SITE_URL, DEFAULT_WEB_BASE_URL, } from './auth';
42
+ export type { OAuthProvider, User, DeviceCodeStart, DeviceCodePoll, RemoteDevice, DeviceList, } from './auth';
43
+ export { captureScreenshot, startAudioRecording, stopAudioRecording } from './capture';
44
+ export { uploadFeedback } from './upload';
45
+ export type { FeedbackConfig, FeedbackBundle, FeedbackMetadata, DeviceInfo, AppInfo, TimelineEvent, FeedbackReport, AgentCommentary, FeedbackStreamEvent, VoiceCapability, CapturedError, TestFix, TestSession, } from './types';
46
+ export type { BlackBoxEvent, BlackBoxConfig, BlackBoxCommand, CommandHandler } from './BlackBox';
47
+ export type { DiscoveryResult } from './Discovery';
48
+ export type { FeedbackEvent } from './P2PClient';