yaver-feedback-react-native 0.9.8 → 0.9.10

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 (37) hide show
  1. package/app.plugin.js +24 -10
  2. package/dist/DogfoodQuickControls.js +7 -0
  3. package/dist/DogfoodRuntime.d.ts +6 -1
  4. package/dist/DogfoodRuntime.js +9 -3
  5. package/dist/DogfoodSessionUi.d.ts +1 -0
  6. package/dist/DogfoodSessionUi.js +5 -2
  7. package/dist/FeedbackModal.js +249 -278
  8. package/dist/FeedbackModalTheme.d.ts +5 -0
  9. package/dist/FeedbackModalTheme.js +29 -0
  10. package/dist/P2PClient.d.ts +2 -0
  11. package/dist/P2PClient.js +1 -1
  12. package/dist/P2PDogfoodDriver.js +17 -0
  13. package/dist/YaverFeedback.js +10 -0
  14. package/dist/__tests__/DogfoodRuntime.test.js +14 -0
  15. package/dist/__tests__/FeedbackModalContract.test.d.ts +1 -0
  16. package/dist/__tests__/FeedbackModalContract.test.js +70 -0
  17. package/dist/__tests__/NativeDogfoodShortcut.test.js +23 -0
  18. package/dist/__tests__/P2PDogfoodDriver.test.js +4 -1
  19. package/dist/__tests__/YaverFeedback.test.js +37 -12
  20. package/dist/index.d.ts +4 -5
  21. package/dist/index.js +4 -5
  22. package/ios/YaverHotReload.swift +3 -4
  23. package/package.json +1 -1
  24. package/src/DogfoodQuickControls.tsx +7 -0
  25. package/src/DogfoodRuntime.ts +21 -4
  26. package/src/DogfoodSessionUi.tsx +6 -2
  27. package/src/FeedbackModal.tsx +319 -372
  28. package/src/FeedbackModalTheme.ts +29 -0
  29. package/src/P2PClient.ts +3 -1
  30. package/src/P2PDogfoodDriver.ts +17 -0
  31. package/src/YaverFeedback.ts +10 -0
  32. package/src/__tests__/DogfoodRuntime.test.ts +15 -0
  33. package/src/__tests__/FeedbackModalContract.test.ts +80 -0
  34. package/src/__tests__/NativeDogfoodShortcut.test.ts +25 -0
  35. package/src/__tests__/P2PDogfoodDriver.test.ts +4 -0
  36. package/src/__tests__/YaverFeedback.test.ts +40 -12
  37. package/src/index.ts +4 -5
package/app.plugin.js CHANGED
@@ -227,7 +227,9 @@ function withYaverAppDelegateHook(config) {
227
227
  // Existing consumers may already have the hot-reload hook from an older
228
228
  // SDK. Still apply newer, independently-versioned native contracts.
229
229
  if (contents.includes("YaverHotReload")) {
230
- config.modResults.contents = patchDogfoodAppShortcut(contents);
230
+ config.modResults.contents = patchDogfoodAppShortcut(
231
+ patchHotReloadBootConfirmation(contents)
232
+ );
231
233
  return config;
232
234
  }
233
235
 
@@ -273,20 +275,14 @@ function withYaverAppDelegateHook(config) {
273
275
  name: Notification.Name("YaverHotReloadBundle"),
274
276
  object: nil
275
277
  )
276
- // Crash-revert safety net: clear the boot-attempt counter once
277
- // RN renders its first frame, OR after 10 s of uptime — whichever
278
- // fires first. If neither fires (bundle crashes before render),
279
- // YaverHotReload.bundleURL() will eventually revert to the
280
- // TestFlight-installed bundle after 3 failed boots. See
281
- // YaverHotReload.swift for the full state machine.
278
+ // Crash-revert safety net: only a real first React frame confirms boot.
279
+ // Uptime is not success: JS can fail while the native process remains
280
+ // alive on its launch screen indefinitely.
282
281
  NotificationCenter.default.addObserver(
283
282
  forName: NSNotification.Name(rawValue: "RCTContentDidAppearNotification"),
284
283
  object: nil,
285
284
  queue: .main
286
285
  ) { _ in YaverHotReload.markBootSuccessful() }
287
- DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
288
- YaverHotReload.markBootSuccessful()
289
- }
290
286
  }
291
287
 
292
288
  @objc private func yaverHandleHotReload(_ notification: Notification) {
@@ -374,6 +370,23 @@ function withYaverAppDelegateHook(config) {
374
370
  });
375
371
  }
376
372
 
373
+ /** Upgrade an already-generated AppDelegate from the pre-0.9.10 boot guard.
374
+ * Older plugins treated ten seconds of process uptime as a successful React
375
+ * boot. A JS exception can leave that process alive on the native splash, so
376
+ * the timer reset the crash counter forever and prevented bundle rollback. */
377
+ function patchHotReloadBootConfirmation(contents) {
378
+ let patched = contents;
379
+ patched = patched.replace(
380
+ / \/\/ Crash-revert safety net: clear the boot-attempt counter once\n \/\/ RN renders its first frame, OR after 10 s of uptime — whichever\n \/\/ fires first\. If neither fires \(bundle crashes before render\),\n \/\/ YaverHotReload\.bundleURL\(\) will eventually revert to the\n \/\/ TestFlight-installed bundle after 3 failed boots\. See\n \/\/ YaverHotReload\.swift for the full state machine\./,
381
+ ` // Crash-revert safety net: only a real first React frame confirms boot.\n // Uptime is not success: JS can fail while the native process remains\n // alive on its launch screen indefinitely.`
382
+ );
383
+ patched = patched.replace(
384
+ / DispatchQueue\.main\.asyncAfter\(deadline: \.now\(\) \+ 10\) \{\n YaverHotReload\.markBootSuccessful\(\)\n \}\n?/g,
385
+ ""
386
+ );
387
+ return patched;
388
+ }
389
+
377
390
  /** Wire the dynamic iOS Home Screen shortcut into the SDK native module.
378
391
  * The shortcut itself is created at runtime only after backend ACL success;
379
392
  * this hook merely consumes a user-selected action on warm/cold launch. */
@@ -820,6 +833,7 @@ const yaverFeedbackPlugin = createRunOncePlugin(
820
833
  // config-plugin seam catches template drift before a consumer discovers it in
821
834
  // an archive build.
822
835
  yaverFeedbackPlugin.__test = {
836
+ patchHotReloadBootConfirmation,
823
837
  patchDogfoodAppShortcut,
824
838
  patchDogfoodSceneDelegate,
825
839
  findAppDelegateClassClose,
@@ -111,9 +111,16 @@ const DogfoodQuickControls = () => {
111
111
  const capability = react_native_1.DeviceEventEmitter.addListener('yaverDogfoodControlCapability', () => {
112
112
  void refresh();
113
113
  });
114
+ // Enrollment approval and Exit Dogfood both change SDK mode while this
115
+ // component remains mounted. Refresh React state immediately; native
116
+ // setEnabled() alone cannot make the fallback Y appear or disappear.
117
+ const mode = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:dogfoodChanged', () => {
118
+ void refresh();
119
+ });
114
120
  return () => {
115
121
  trigger.remove();
116
122
  capability.remove();
123
+ mode.remove();
117
124
  };
118
125
  }, [refresh]);
119
126
  (0, react_1.useEffect)(() => {
@@ -32,9 +32,14 @@ export interface DogfoodLaneOption {
32
32
  /** One framework-to-lane matrix for Yaver and third-party consumers. */
33
33
  export declare function dogfoodLaneOptions(framework: string, capabilities?: {
34
34
  nativeRuntimeAvailable?: boolean;
35
+ browserRuntimeAvailable?: boolean;
35
36
  selfDevelopment?: boolean;
36
37
  }): DogfoodLaneOption[];
37
- export declare function defaultDogfoodLane(framework: string): DogfoodLane;
38
+ export declare function defaultDogfoodLane(framework: string, capabilities?: {
39
+ nativeRuntimeAvailable?: boolean;
40
+ browserRuntimeAvailable?: boolean;
41
+ selfDevelopment?: boolean;
42
+ }): DogfoodLane;
38
43
  export interface DogfoodLogLine {
39
44
  text: string;
40
45
  at: number;
@@ -21,7 +21,10 @@ exports.runtimeLogLinesFromDevEvent = runtimeLogLinesFromDevEvent;
21
21
  function dogfoodLaneOptions(framework, capabilities = {}) {
22
22
  const normalized = String(framework || '').trim().toLowerCase();
23
23
  const reactNative = normalized === 'expo' || normalized === 'react-native';
24
- const browserCapable = reactNative || [
24
+ // The framework matrix is the safe default. A positively detected browser
25
+ // target may add an exception (for example SwiftWasm/Tokamak), but a missing
26
+ // browser binary must not incorrectly remove RN/Flutter's browser build lane.
27
+ const browserCapable = capabilities.browserRuntimeAvailable === true || reactNative || [
25
28
  'flutter', 'web', 'next', 'nextjs', 'vite', 'remix', 'svelte', 'vue', 'angular',
26
29
  ].includes(normalized);
27
30
  const nativeAvailable = capabilities.nativeRuntimeAvailable === true;
@@ -40,8 +43,11 @@ function dogfoodLaneOptions(framework, capabilities = {}) {
40
43
  },
41
44
  ];
42
45
  }
43
- function defaultDogfoodLane(framework) {
44
- return dogfoodLaneOptions(framework).find((option) => option.default && option.supported)?.lane || 'browser';
46
+ function defaultDogfoodLane(framework, capabilities = {}) {
47
+ const options = dogfoodLaneOptions(framework, capabilities);
48
+ return options.find((option) => option.default && option.supported)?.lane
49
+ || options.find((option) => option.supported)?.lane
50
+ || 'browser';
45
51
  }
46
52
  class DogfoodRuntimeError extends Error {
47
53
  constructor(failure) {
@@ -40,6 +40,7 @@ export declare const DogfoodLanePicker: React.FC<{
40
40
  * Logs; Hermes/WebRTC use the same lifecycle and failure/remedy treatment. */
41
41
  export declare const DogfoodLiveConsole: React.FC<{
42
42
  lane: DogfoodLane;
43
+ sourceLabel?: string;
43
44
  phase: DogfoodPhase;
44
45
  message: string;
45
46
  logs: readonly DogfoodLogLine[];
@@ -91,15 +91,17 @@ function runtimeTone(phase, colors) {
91
91
  }
92
92
  /** Shared second-stage live console. Browser lane deliberately names Browser
93
93
  * Logs; Hermes/WebRTC use the same lifecycle and failure/remedy treatment. */
94
- const DogfoodLiveConsole = ({ lane, phase, message, logs, failure, maxLines = 80, colors: colorOverrides, renderText }) => {
94
+ const DogfoodLiveConsole = ({ lane, sourceLabel, phase, message, logs, failure, maxLines = 80, colors: colorOverrides, renderText }) => {
95
95
  const colors = resolvedColors(colorOverrides);
96
96
  const text = logs.slice(-maxLines).map((line) => line.text).join('\n');
97
97
  const title = lane === 'browser' ? 'Browser Logs' : lane === 'hermes' ? 'Hermes Logs' : 'WebRTC Logs';
98
- return (<react_native_1.View style={[styles.console, { backgroundColor: colors.console, borderColor: colors.border }]} accessibilityLabel={title}>
98
+ const accessibleTitle = sourceLabel ? `${title} · ${sourceLabel}` : title;
99
+ return (<react_native_1.View style={[styles.console, { backgroundColor: colors.console, borderColor: colors.border }]} accessibilityLabel={accessibleTitle}>
99
100
  <react_native_1.View style={styles.consoleHeader}>
100
101
  <react_native_1.View style={[styles.statusDot, { backgroundColor: runtimeTone(phase, colors) }]}/>
101
102
  <react_native_1.Text style={[styles.consoleTitle, { color: colors.text }]}>{title}</react_native_1.Text>
102
103
  </react_native_1.View>
104
+ {sourceLabel ? <react_native_1.Text style={[styles.consoleSource, { color: colors.muted }]}>Source · {sourceLabel}</react_native_1.Text> : null}
103
105
  <react_native_1.Text style={[styles.consoleStatus, { color: colors.muted }]}>{message}</react_native_1.Text>
104
106
  {text ? (renderText ? renderText(text) : <react_native_1.Text selectable style={[styles.consoleText, { color: colors.text }]}>{text}</react_native_1.Text>) : (<react_native_1.Text style={[styles.consoleEmpty, { color: colors.muted }]}>Waiting for the first line from the remote PC…</react_native_1.Text>)}
105
107
  {failure ? (<react_native_1.View style={[styles.failure, { borderColor: colors.blocked }]}>
@@ -126,6 +128,7 @@ const styles = react_native_1.StyleSheet.create({
126
128
  console: { width: '100%', maxHeight: 320, overflow: 'hidden', marginTop: 10, borderWidth: 1, borderRadius: 10, padding: 11, gap: 7 },
127
129
  consoleHeader: { flexDirection: 'row', alignItems: 'center', gap: 7 },
128
130
  consoleTitle: { fontSize: 12, fontWeight: '800' },
131
+ consoleSource: { fontSize: 10, lineHeight: 14 },
129
132
  consoleStatus: { fontSize: 11, lineHeight: 16 },
130
133
  consoleText: { fontFamily: 'monospace', fontSize: 10, lineHeight: 15 },
131
134
  consoleEmpty: { fontSize: 10, fontStyle: 'italic' },