yaver-feedback-react-native 0.5.3 → 0.5.5

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 (52) hide show
  1. package/README.md +30 -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 +133 -0
  24. package/dist/YaverFeedback.d.ts +198 -0
  25. package/dist/YaverFeedback.js +707 -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
  51. package/src/ShakeDetector.ts +22 -1
  52. package/src/YaverFeedback.ts +29 -0
@@ -0,0 +1,395 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlackBox = void 0;
4
+ const react_native_1 = require("react-native");
5
+ const YaverFeedback_1 = require("./YaverFeedback");
6
+ class BlackBox {
7
+ /**
8
+ * Start the black box stream. Call after `YaverFeedback.init()`.
9
+ *
10
+ * The stream sends buffered events to the agent every `flushInterval` ms,
11
+ * or immediately when the buffer reaches `maxBufferSize`.
12
+ */
13
+ static start(config) {
14
+ const feedbackConfig = YaverFeedback_1.YaverFeedback.getConfig();
15
+ if (!feedbackConfig?.agentUrl) {
16
+ console.warn('[BlackBox] No agent URL. Call YaverFeedback.init() first or set agentUrl.');
17
+ return;
18
+ }
19
+ BlackBox.baseUrl = feedbackConfig.agentUrl.replace(/\/$/, '');
20
+ BlackBox.authToken = feedbackConfig.authToken ?? null;
21
+ BlackBox.deviceId = config?.deviceId ?? BlackBox.generateDeviceId();
22
+ BlackBox.appName = config?.appName ?? '';
23
+ BlackBox.flushInterval = config?.flushInterval ?? 2000;
24
+ BlackBox.maxBufferSize = config?.maxBufferSize ?? 50;
25
+ BlackBox.buffer = [];
26
+ BlackBox.started = true;
27
+ // Start periodic flush
28
+ if (BlackBox.flushTimer)
29
+ clearInterval(BlackBox.flushTimer);
30
+ BlackBox.flushTimer = setInterval(() => BlackBox.flush(), BlackBox.flushInterval);
31
+ // Log the session start
32
+ BlackBox.push({
33
+ type: 'lifecycle',
34
+ message: 'Black box streaming started',
35
+ timestamp: Date.now(),
36
+ });
37
+ // Connect SSE command channel for receiving agent commands (reload, etc.)
38
+ BlackBox.connectSSE();
39
+ }
40
+ /** Stop the black box stream and flush remaining events. */
41
+ static stop() {
42
+ if (!BlackBox.started)
43
+ return;
44
+ BlackBox.push({
45
+ type: 'lifecycle',
46
+ message: 'Black box streaming stopped',
47
+ timestamp: Date.now(),
48
+ });
49
+ BlackBox.flush();
50
+ if (BlackBox.flushTimer) {
51
+ clearInterval(BlackBox.flushTimer);
52
+ BlackBox.flushTimer = null;
53
+ }
54
+ BlackBox.disconnectSSE();
55
+ BlackBox.started = false;
56
+ }
57
+ /** Whether the black box is currently streaming. */
58
+ static get isStreaming() {
59
+ return BlackBox.started;
60
+ }
61
+ // ─── Logging ─────────────────────────────────────────────────────
62
+ static log(message, source, metadata) {
63
+ BlackBox.push({ type: 'log', level: 'info', message, timestamp: Date.now(), source, metadata });
64
+ }
65
+ static warn(message, source, metadata) {
66
+ BlackBox.push({ type: 'log', level: 'warn', message, timestamp: Date.now(), source, metadata });
67
+ }
68
+ static error(message, source, metadata) {
69
+ BlackBox.push({ type: 'log', level: 'error', message, timestamp: Date.now(), source, metadata });
70
+ }
71
+ // ─── Track events ────────────────────────────────────────────────
72
+ //
73
+ // Business-event ingest. Routed into the agent's analytics ledger
74
+ // via the same BlackBox stream so the dev doesn't pay Mixpanel
75
+ // / Amplitude for "the user tapped Purchase." Zero dashboards in
76
+ // yaver; export to CSV / webhook into PostHog if you want charts.
77
+ /**
78
+ * Record a business event. Fires into the analytics ledger on
79
+ * the agent — see GET /analytics/events.csv.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * BlackBox.track('purchase_completed', {
84
+ * amount: '9.99',
85
+ * currency: 'USD',
86
+ * plan: 'pro',
87
+ * });
88
+ * ```
89
+ */
90
+ static track(name, props, route) {
91
+ if (!name)
92
+ return;
93
+ BlackBox.push({
94
+ type: 'track',
95
+ message: name,
96
+ timestamp: Date.now(),
97
+ route,
98
+ metadata: props,
99
+ });
100
+ }
101
+ // ─── Errors ──────────────────────────────────────────────────────
102
+ /** Record a caught error with stack trace. Also adds to the feedback error buffer. */
103
+ static captureError(err, isFatal = false, metadata) {
104
+ const stack = (err.stack ?? '').split('\n').filter((l) => l.trim());
105
+ BlackBox.push({
106
+ type: 'error',
107
+ message: err.message,
108
+ timestamp: Date.now(),
109
+ stack,
110
+ isFatal,
111
+ metadata,
112
+ });
113
+ // Also feed into the feedback SDK error buffer
114
+ YaverFeedback_1.YaverFeedback.attachError(err, metadata);
115
+ }
116
+ // ─── Navigation ──────────────────────────────────────────────────
117
+ /** Record a screen/route navigation event. */
118
+ static navigation(route, prevRoute, metadata) {
119
+ BlackBox.push({
120
+ type: 'navigation',
121
+ message: `Navigate: ${prevRoute ? prevRoute + ' -> ' : ''}${route}`,
122
+ timestamp: Date.now(),
123
+ route,
124
+ prevRoute,
125
+ metadata,
126
+ });
127
+ }
128
+ // ─── Lifecycle ───────────────────────────────────────────────────
129
+ /** Record an app lifecycle event (mount, unmount, background, foreground). */
130
+ static lifecycle(event, metadata) {
131
+ BlackBox.push({ type: 'lifecycle', message: event, timestamp: Date.now(), metadata });
132
+ }
133
+ // ─── Network ─────────────────────────────────────────────────────
134
+ /** Record a network request/response. */
135
+ static networkRequest(method, url, status, durationMs, metadata) {
136
+ const msg = status != null
137
+ ? `${method} ${url} → ${status}`
138
+ : `${method} ${url}`;
139
+ BlackBox.push({
140
+ type: 'network',
141
+ message: msg,
142
+ timestamp: Date.now(),
143
+ duration: durationMs,
144
+ metadata,
145
+ });
146
+ }
147
+ // ─── State ───────────────────────────────────────────────────────
148
+ /** Record a state change event (Redux action, context update, etc.). */
149
+ static stateChange(description, metadata) {
150
+ BlackBox.push({ type: 'state', message: description, timestamp: Date.now(), metadata });
151
+ }
152
+ // ─── Render ──────────────────────────────────────────────────────
153
+ /** Record a render/re-render event with optional duration. */
154
+ static render(component, durationMs, metadata) {
155
+ BlackBox.push({
156
+ type: 'render',
157
+ message: component,
158
+ timestamp: Date.now(),
159
+ duration: durationMs,
160
+ metadata,
161
+ });
162
+ }
163
+ // ─── Console wrapping (opt-in) ───────────────────────────────────
164
+ /**
165
+ * Wrap `console.log`, `console.warn`, and `console.error` to also
166
+ * stream them to the black box. **Call this explicitly** — the SDK
167
+ * never auto-hooks console.
168
+ *
169
+ * Call `BlackBox.unwrapConsole()` to restore originals.
170
+ */
171
+ static wrapConsole() {
172
+ if (BlackBox.originalConsole)
173
+ return; // Already wrapped
174
+ BlackBox.originalConsole = {
175
+ log: console.log,
176
+ warn: console.warn,
177
+ error: console.error,
178
+ };
179
+ console.log = (...args) => {
180
+ BlackBox.originalConsole.log(...args);
181
+ BlackBox.log(args.map(String).join(' '));
182
+ };
183
+ console.warn = (...args) => {
184
+ BlackBox.originalConsole.warn(...args);
185
+ BlackBox.warn(args.map(String).join(' '));
186
+ };
187
+ console.error = (...args) => {
188
+ BlackBox.originalConsole.error(...args);
189
+ BlackBox.error(args.map(String).join(' '));
190
+ };
191
+ }
192
+ /** Restore original console methods. */
193
+ static unwrapConsole() {
194
+ if (!BlackBox.originalConsole)
195
+ return;
196
+ console.log = BlackBox.originalConsole.log;
197
+ console.warn = BlackBox.originalConsole.warn;
198
+ console.error = BlackBox.originalConsole.error;
199
+ BlackBox.originalConsole = null;
200
+ }
201
+ // ─── Error handler wrapper (opt-in) ──────────────────────────────
202
+ /**
203
+ * Returns a pass-through error handler that streams errors to the
204
+ * black box AND calls the next handler. Same pattern as
205
+ * `YaverFeedback.wrapErrorHandler`, but streams in real-time.
206
+ *
207
+ * @example
208
+ * const existing = ErrorUtils.getGlobalHandler();
209
+ * ErrorUtils.setGlobalHandler(BlackBox.wrapErrorHandler(existing));
210
+ */
211
+ static wrapErrorHandler(next) {
212
+ return (error, isFatal) => {
213
+ BlackBox.captureError(error, isFatal ?? false);
214
+ next?.(error, isFatal);
215
+ };
216
+ }
217
+ // ─── Command channel (agent → SDK) ──────────────────────────────
218
+ /**
219
+ * Register a handler for commands pushed by the agent.
220
+ * The primary use case is receiving "reload" commands when the vibe coder
221
+ * triggers a reload from the Yaver mobile app.
222
+ *
223
+ * @example
224
+ * BlackBox.onCommand((cmd) => {
225
+ * if (cmd.command === 'reload') {
226
+ * DevSettings.reload(); // or Updates.reloadAsync()
227
+ * }
228
+ * });
229
+ */
230
+ static onCommand(handler) {
231
+ BlackBox.commandHandlers.push(handler);
232
+ // Return unsubscribe function
233
+ return () => {
234
+ BlackBox.commandHandlers = BlackBox.commandHandlers.filter(h => h !== handler);
235
+ };
236
+ }
237
+ /** Whether the SSE command channel is connected. */
238
+ static get isCommandChannelConnected() {
239
+ return BlackBox.sseConnected;
240
+ }
241
+ /**
242
+ * Connect to the agent's /blackbox/stream SSE endpoint.
243
+ * This persistent connection allows the agent to push commands (reload, etc.)
244
+ * back to the SDK. Events are still sent via batch POST /blackbox/events.
245
+ */
246
+ static async connectSSE() {
247
+ if (!BlackBox.baseUrl || !BlackBox.authToken)
248
+ return;
249
+ // Disconnect any existing connection
250
+ BlackBox.disconnectSSE();
251
+ const controller = new AbortController();
252
+ BlackBox.sseAbortController = controller;
253
+ const url = `${BlackBox.baseUrl}/blackbox/command-stream?device=${encodeURIComponent(BlackBox.deviceId)}`;
254
+ try {
255
+ const response = await fetch(url, {
256
+ method: 'GET',
257
+ headers: {
258
+ Authorization: `Bearer ${BlackBox.authToken}`,
259
+ 'X-Device-ID': BlackBox.deviceId,
260
+ 'X-Platform': react_native_1.Platform.OS,
261
+ 'X-App-Name': BlackBox.appName,
262
+ Accept: 'text/event-stream',
263
+ },
264
+ // @ts-ignore — React Native supports signal on fetch
265
+ signal: controller.signal,
266
+ });
267
+ if (!response.ok || !response.body) {
268
+ BlackBox.scheduleSSEReconnect();
269
+ return;
270
+ }
271
+ BlackBox.sseConnected = true;
272
+ // Read SSE stream
273
+ const reader = response.body.getReader();
274
+ const decoder = new TextDecoder();
275
+ let buffer = '';
276
+ while (true) {
277
+ const { done, value } = await reader.read();
278
+ if (done)
279
+ break;
280
+ buffer += decoder.decode(value, { stream: true });
281
+ const lines = buffer.split('\n');
282
+ buffer = lines.pop() ?? '';
283
+ for (const line of lines) {
284
+ if (!line.startsWith('data: '))
285
+ continue;
286
+ try {
287
+ const msg = JSON.parse(line.slice(6));
288
+ if (msg.type === 'command' && msg.command) {
289
+ const cmd = msg.command;
290
+ for (const handler of BlackBox.commandHandlers) {
291
+ try {
292
+ handler(cmd);
293
+ }
294
+ catch {
295
+ // Handler error — don't break the loop
296
+ }
297
+ }
298
+ }
299
+ }
300
+ catch {
301
+ // Malformed SSE data — skip
302
+ }
303
+ }
304
+ }
305
+ }
306
+ catch (err) {
307
+ // AbortError is expected on disconnect
308
+ if (err instanceof Error && err.name === 'AbortError')
309
+ return;
310
+ }
311
+ finally {
312
+ BlackBox.sseConnected = false;
313
+ }
314
+ // Reconnect if still running
315
+ if (BlackBox.started) {
316
+ BlackBox.scheduleSSEReconnect();
317
+ }
318
+ }
319
+ static disconnectSSE() {
320
+ if (BlackBox.sseAbortController) {
321
+ BlackBox.sseAbortController.abort();
322
+ BlackBox.sseAbortController = null;
323
+ }
324
+ if (BlackBox.sseReconnectTimer) {
325
+ clearTimeout(BlackBox.sseReconnectTimer);
326
+ BlackBox.sseReconnectTimer = null;
327
+ }
328
+ BlackBox.sseConnected = false;
329
+ }
330
+ static scheduleSSEReconnect() {
331
+ if (!BlackBox.started)
332
+ return;
333
+ if (BlackBox.sseReconnectTimer)
334
+ return;
335
+ // Reconnect after 5s
336
+ BlackBox.sseReconnectTimer = setTimeout(() => {
337
+ BlackBox.sseReconnectTimer = null;
338
+ if (BlackBox.started)
339
+ BlackBox.connectSSE();
340
+ }, 5000);
341
+ }
342
+ // ─── Internal ────────────────────────────────────────────────────
343
+ static push(event) {
344
+ if (!BlackBox.started)
345
+ return;
346
+ BlackBox.buffer.push(event);
347
+ if (BlackBox.buffer.length >= BlackBox.maxBufferSize) {
348
+ BlackBox.flush();
349
+ }
350
+ }
351
+ static async flush() {
352
+ if (!BlackBox.baseUrl || !BlackBox.authToken || BlackBox.buffer.length === 0)
353
+ return;
354
+ const events = BlackBox.buffer;
355
+ BlackBox.buffer = [];
356
+ try {
357
+ await fetch(`${BlackBox.baseUrl}/blackbox/events`, {
358
+ method: 'POST',
359
+ headers: {
360
+ Authorization: `Bearer ${BlackBox.authToken}`,
361
+ 'Content-Type': 'application/json',
362
+ 'X-Device-ID': BlackBox.deviceId,
363
+ 'X-Platform': react_native_1.Platform.OS,
364
+ 'X-App-Name': BlackBox.appName,
365
+ },
366
+ body: JSON.stringify(events),
367
+ });
368
+ }
369
+ catch {
370
+ // Re-add failed events to buffer (capped to avoid memory growth)
371
+ if (BlackBox.buffer.length + events.length <= BlackBox.maxBufferSize * 2) {
372
+ BlackBox.buffer = [...events, ...BlackBox.buffer];
373
+ }
374
+ }
375
+ }
376
+ static generateDeviceId() {
377
+ return 'xxxxxxxx'.replace(/x/g, () => Math.floor(Math.random() * 16).toString(16));
378
+ }
379
+ }
380
+ exports.BlackBox = BlackBox;
381
+ BlackBox.baseUrl = null;
382
+ BlackBox.authToken = null;
383
+ BlackBox.deviceId = '';
384
+ BlackBox.appName = '';
385
+ BlackBox.buffer = [];
386
+ BlackBox.flushTimer = null;
387
+ BlackBox.flushInterval = 2000;
388
+ BlackBox.maxBufferSize = 50;
389
+ BlackBox.started = false;
390
+ BlackBox.originalConsole = null;
391
+ // SSE command channel — persistent connection to /blackbox/stream
392
+ BlackBox.sseAbortController = null;
393
+ BlackBox.sseReconnectTimer = null;
394
+ BlackBox.commandHandlers = [];
395
+ BlackBox.sseConnected = false;
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ /**
3
+ * Full-screen connection UI for discovering and connecting to a Yaver agent.
4
+ *
5
+ * Shows connection status, auto-discovery, manual URL entry, and a
6
+ * Start/Stop testing toggle with recording timer.
7
+ *
8
+ * Usage:
9
+ * ```tsx
10
+ * {__DEV__ && <YaverConnectionScreen />}
11
+ * ```
12
+ */
13
+ export declare const YaverConnectionScreen: React.FC;