yaver-feedback-react-native 0.5.3 → 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 +6 -3
@@ -0,0 +1,293 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.YaverDiscovery = void 0;
4
+ function getAsyncStorage() {
5
+ try {
6
+ const mod = require('@react-native-async-storage/async-storage');
7
+ const candidate = mod?.default ?? mod;
8
+ if (candidate && typeof candidate.getItem === 'function') {
9
+ return candidate;
10
+ }
11
+ return null;
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ const STORAGE_KEY = 'yaver_feedback_agent';
18
+ const DEFAULT_PORT = 18080;
19
+ const TIMEOUT_MS = 2000;
20
+ // Common LAN subnets and host suffixes to scan
21
+ const SUBNETS = ['192.168.1', '192.168.0', '10.0.0', '10.0.1'];
22
+ const HOST_SUFFIXES = [1, 2, 50, 100, 101, 200];
23
+ /**
24
+ * Device discovery for finding Yaver agents on the local network or via Convex.
25
+ *
26
+ * Three discovery strategies (tried in order):
27
+ * 1. **Convex cloud** — fetch agent IP from Convex `/devices/list` (for cloud machines)
28
+ * 2. **Stored connection** — try cached URL from last successful connection
29
+ * 3. **LAN scan** — probe common LAN IPs via `/health` endpoint
30
+ */
31
+ class YaverDiscovery {
32
+ /**
33
+ * Discover an agent. Tries Convex cloud first (if configured),
34
+ * then stored connection, then LAN scan.
35
+ */
36
+ static async discover(options) {
37
+ // Strategy 1: Convex cloud discovery (for cloud machines)
38
+ if (options?.convexUrl && options?.authToken) {
39
+ const result = await YaverDiscovery.discoverFromConvex(options.convexUrl, options.authToken, options.preferredDeviceId);
40
+ if (result) {
41
+ await YaverDiscovery.store(result);
42
+ return result;
43
+ }
44
+ }
45
+ // Strategy 2: Try stored connection
46
+ const stored = await YaverDiscovery.getStored();
47
+ if (stored) {
48
+ const result = await YaverDiscovery.probe(stored.url);
49
+ if (result) {
50
+ return result;
51
+ }
52
+ await YaverDiscovery.clear();
53
+ }
54
+ // Strategy 3: Scan common LAN IPs in parallel
55
+ const candidates = [];
56
+ for (const subnet of SUBNETS) {
57
+ for (const suffix of HOST_SUFFIXES) {
58
+ candidates.push(`http://${subnet}.${suffix}:${DEFAULT_PORT}`);
59
+ }
60
+ }
61
+ const results = await Promise.allSettled(candidates.map((url) => YaverDiscovery.probe(url)));
62
+ for (const r of results) {
63
+ if (r.status === 'fulfilled' && r.value) {
64
+ await YaverDiscovery.store(r.value);
65
+ return r.value;
66
+ }
67
+ }
68
+ return null;
69
+ }
70
+ /**
71
+ * Fetch the agent URL from Convex device list or cloud machines.
72
+ * No hardcoded IPs needed — Convex knows where the agent is.
73
+ */
74
+ static async discoverFromConvex(convexUrl, authToken, preferredDeviceId) {
75
+ const base = convexUrl.replace(/\/$/, '');
76
+ try {
77
+ // Try cloud machines first (CPU/GPU managed machines)
78
+ const machinesRes = await fetch(`${base}/machines`, {
79
+ headers: { Authorization: `Bearer ${authToken}` },
80
+ });
81
+ if (machinesRes.ok) {
82
+ const { machines } = await machinesRes.json();
83
+ const activeMachine = (machines ?? []).find((m) => m.status === 'active' && m.serverIp);
84
+ if (activeMachine?.serverIp) {
85
+ const url = `http://${activeMachine.serverIp}:${DEFAULT_PORT}`;
86
+ const probed = await YaverDiscovery.probe(url);
87
+ if (probed)
88
+ return probed;
89
+ }
90
+ }
91
+ // Fall back to device list (personal machines registered with Convex)
92
+ const devicesRes = await fetch(`${base}/devices/list`, {
93
+ headers: { Authorization: `Bearer ${authToken}` },
94
+ });
95
+ if (!devicesRes.ok)
96
+ return null;
97
+ const data = await devicesRes.json();
98
+ const devices = data.devices ?? data ?? [];
99
+ // Find preferred device or first online one
100
+ const target = preferredDeviceId
101
+ ? devices.find((d) => d.deviceId === preferredDeviceId)
102
+ : devices.find((d) => d.isOnline);
103
+ if (!target?.quicHost)
104
+ return null;
105
+ // Try direct connection first (same LAN)
106
+ const port = target.httpPort ?? DEFAULT_PORT;
107
+ const directUrl = `http://${target.quicHost}:${port}`;
108
+ const directResult = await YaverDiscovery.probe(directUrl);
109
+ if (directResult)
110
+ return directResult;
111
+ // Direct connection failed — try via HTTP relay (off-LAN)
112
+ const relayResult = await YaverDiscovery.discoverViaRelay(base, authToken, target.deviceId);
113
+ if (relayResult)
114
+ return relayResult;
115
+ return null;
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ /**
122
+ * Discover agent via relay HTTP proxy.
123
+ * Fetches relay server list from Convex platformConfig, then probes
124
+ * `{relayHttpUrl}/d/{deviceId}/health` to reach the agent over the internet.
125
+ */
126
+ static async discoverViaRelay(convexUrl, authToken, deviceId) {
127
+ try {
128
+ // Fetch relay server list from user settings first, then platform config
129
+ const settingsRes = await fetch(`${convexUrl}/auth/validate`, {
130
+ headers: { Authorization: `Bearer ${authToken}` },
131
+ });
132
+ let relayUrl;
133
+ let relayPassword;
134
+ if (settingsRes.ok) {
135
+ const settingsData = await settingsRes.json();
136
+ relayUrl = settingsData.relayUrl;
137
+ relayPassword = settingsData.relayPassword;
138
+ }
139
+ // If no user-level relay, fetch platform relay servers
140
+ if (!relayUrl) {
141
+ const configRes = await fetch(`${convexUrl}/platform-config?key=relay_servers`);
142
+ if (configRes.ok) {
143
+ const configData = await configRes.json();
144
+ const servers = typeof configData.value === 'string'
145
+ ? JSON.parse(configData.value)
146
+ : configData.value;
147
+ if (Array.isArray(servers) && servers.length > 0) {
148
+ // Pick the first (highest priority) relay with an httpUrl
149
+ const relay = servers.find((s) => s.httpUrl);
150
+ if (relay) {
151
+ relayUrl = relay.httpUrl;
152
+ }
153
+ }
154
+ }
155
+ }
156
+ if (!relayUrl)
157
+ return null;
158
+ // Probe agent through relay: {relayHttpUrl}/d/{deviceId}/health
159
+ const relayBase = `${relayUrl.replace(/\/$/, '')}/d/${deviceId}`;
160
+ const result = await YaverDiscovery.probeWithHeaders(relayBase, {
161
+ 'X-Relay-Password': relayPassword || '',
162
+ });
163
+ return result;
164
+ }
165
+ catch {
166
+ return null;
167
+ }
168
+ }
169
+ /**
170
+ * Probe with extra headers (e.g. relay password).
171
+ */
172
+ static async probeWithHeaders(url, headers) {
173
+ const base = url.replace(/\/$/, '');
174
+ const start = Date.now();
175
+ try {
176
+ const controller = new AbortController();
177
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS + 3000); // relay adds latency
178
+ const response = await fetch(`${base}/health`, {
179
+ method: 'GET',
180
+ headers,
181
+ signal: controller.signal,
182
+ });
183
+ clearTimeout(timeoutId);
184
+ if (!response.ok)
185
+ return null;
186
+ const latency = Date.now() - start;
187
+ let hostname = 'Unknown';
188
+ let version = 'unknown';
189
+ try {
190
+ const data = await response.json();
191
+ hostname = data.hostname ?? data.name ?? 'Unknown';
192
+ version = data.version ?? 'unknown';
193
+ }
194
+ catch {
195
+ // Health endpoint might return plain text
196
+ }
197
+ return { url: base, hostname, version, latency };
198
+ }
199
+ catch {
200
+ return null;
201
+ }
202
+ }
203
+ /**
204
+ * Probe a specific URL for a running Yaver agent.
205
+ * Hits the `/health` endpoint with a 2s timeout.
206
+ */
207
+ static async probe(url) {
208
+ const base = url.replace(/\/$/, '');
209
+ const start = Date.now();
210
+ try {
211
+ const controller = new AbortController();
212
+ const timeoutId = setTimeout(() => controller.abort(), TIMEOUT_MS);
213
+ const response = await fetch(`${base}/health`, {
214
+ method: 'GET',
215
+ signal: controller.signal,
216
+ });
217
+ clearTimeout(timeoutId);
218
+ if (!response.ok) {
219
+ return null;
220
+ }
221
+ const latency = Date.now() - start;
222
+ let hostname = 'Unknown';
223
+ let version = 'unknown';
224
+ try {
225
+ const data = await response.json();
226
+ hostname = data.hostname ?? data.name ?? 'Unknown';
227
+ version = data.version ?? 'unknown';
228
+ }
229
+ catch {
230
+ // Health endpoint might return plain text — that's fine
231
+ }
232
+ return { url: base, hostname, version, latency };
233
+ }
234
+ catch {
235
+ return null;
236
+ }
237
+ }
238
+ /**
239
+ * Manually connect to a specific agent URL.
240
+ * Probes the URL and stores the connection if successful.
241
+ */
242
+ static async connect(url) {
243
+ const result = await YaverDiscovery.probe(url);
244
+ if (result) {
245
+ await YaverDiscovery.store(result);
246
+ }
247
+ return result;
248
+ }
249
+ /** Get the cached agent connection from storage. */
250
+ static async getStored() {
251
+ const storage = getAsyncStorage();
252
+ if (!storage)
253
+ return null;
254
+ try {
255
+ const raw = await storage.getItem(STORAGE_KEY);
256
+ if (!raw)
257
+ return null;
258
+ const parsed = JSON.parse(raw);
259
+ if (parsed && typeof parsed.url === 'string') {
260
+ return { url: parsed.url, hostname: parsed.hostname ?? 'Unknown' };
261
+ }
262
+ return null;
263
+ }
264
+ catch {
265
+ return null;
266
+ }
267
+ }
268
+ /** Store a successful discovery result. */
269
+ static async store(result) {
270
+ const storage = getAsyncStorage();
271
+ if (!storage)
272
+ return;
273
+ try {
274
+ await storage.setItem(STORAGE_KEY, JSON.stringify({ url: result.url, hostname: result.hostname }));
275
+ }
276
+ catch {
277
+ // Storage failure is non-fatal
278
+ }
279
+ }
280
+ /** Clear the stored agent connection. */
281
+ static async clear() {
282
+ const storage = getAsyncStorage();
283
+ if (!storage)
284
+ return;
285
+ try {
286
+ await storage.removeItem(STORAGE_KEY);
287
+ }
288
+ catch {
289
+ // Storage failure is non-fatal
290
+ }
291
+ }
292
+ }
293
+ exports.YaverDiscovery = YaverDiscovery;
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ /**
3
+ * Full-screen modal for composing and sending a feedback report.
4
+ * Renders when triggered by shake, floating button, or manual call.
5
+ *
6
+ * Supports three feedback modes:
7
+ * - Live: stream events to the agent as they happen
8
+ * - Narrated: record everything, send on stop
9
+ * - Batch: dump everything at end (default)
10
+ */
11
+ export declare const FeedbackModal: React.FC;