yaver-feedback-react-native 0.9.10 → 0.9.12

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.
@@ -46,6 +46,8 @@ const AuthOverlay = () => {
46
46
  const [pickerVisible, setPickerVisible] = (0, react_1.useState)(false);
47
47
  const [token, setToken] = (0, react_1.useState)(null);
48
48
  const activeOverlayRef = (0, react_1.useRef)('none');
49
+ const onboarding = YaverFeedback_1.YaverFeedback.getDogfoodOnboarding();
50
+ const dogfoodLabel = onboarding?.projectName || onboarding?.label || 'this app';
49
51
  const openLogin = (0, react_1.useCallback)(() => {
50
52
  activeOverlayRef.current = 'login';
51
53
  setPickerVisible(false);
@@ -120,10 +122,10 @@ const AuthOverlay = () => {
120
122
  };
121
123
  return (<>
122
124
  <react_native_1.Modal visible={loginVisible} animationType="slide" presentationStyle="fullScreen" onRequestClose={closeAll}>
123
- <LoginScreen_1.YaverLoginScreen onLoggedIn={handleLoggedIn} onCancel={closeAll}/>
125
+ <LoginScreen_1.YaverLoginScreen onLoggedIn={handleLoggedIn} onCancel={closeAll} subtitle={onboarding ? `Sign in to set up ${dogfoodLabel} Dogfood` : undefined}/>
124
126
  </react_native_1.Modal>
125
127
  <react_native_1.Modal visible={pickerVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={closeAll}>
126
- {token && (<MachinePickerScreen_1.YaverMachinePickerScreen token={token} currentDeviceId={YaverFeedback_1.YaverFeedback.getConfig()?.preferredDeviceId} onPick={handleDevicePicked} onCancel={closeAll}/>)}
128
+ {token && (<MachinePickerScreen_1.YaverMachinePickerScreen token={token} currentDeviceId={YaverFeedback_1.YaverFeedback.getConfig()?.preferredDeviceId} title={onboarding ? `Choose a machine for ${dogfoodLabel}` : undefined} onPick={handleDevicePicked} onCancel={closeAll}/>)}
127
129
  </react_native_1.Modal>
128
130
  </>);
129
131
  };
@@ -17,6 +17,13 @@ export interface DogfoodProject {
17
17
  workDir: string;
18
18
  framework: string;
19
19
  lane: DogfoodLane;
20
+ /**
21
+ * Optional automatic recovery lane. The shared onboarding flow uses the
22
+ * browser lane here when a user prefers Hermes or WebRTC for a project that
23
+ * can also render in a browser. The failed preferred attempt remains in the
24
+ * live console; the fallback is never silent.
25
+ */
26
+ fallbackLane?: DogfoodLane;
20
27
  /** Optional source URL for drivers that can clone missing source. */
21
28
  repositoryUrl?: string;
22
29
  /** Optional native target from /remote-runtime/capabilities for WebRTC. */
@@ -29,6 +36,12 @@ export interface DogfoodLaneOption {
29
36
  default: boolean;
30
37
  reason?: string;
31
38
  }
39
+ export interface DogfoodLanePlan {
40
+ preferred: DogfoodLane;
41
+ /** Browser is the cheap, portable recovery lane for RN/Expo/Flutter/web. */
42
+ fallback?: DogfoodLane;
43
+ options: DogfoodLaneOption[];
44
+ }
32
45
  /** One framework-to-lane matrix for Yaver and third-party consumers. */
33
46
  export declare function dogfoodLaneOptions(framework: string, capabilities?: {
34
47
  nativeRuntimeAvailable?: boolean;
@@ -40,6 +53,19 @@ export declare function defaultDogfoodLane(framework: string, capabilities?: {
40
53
  browserRuntimeAvailable?: boolean;
41
54
  selfDevelopment?: boolean;
42
55
  }): DogfoodLane;
56
+ /**
57
+ * Resolve one ordered lane policy for Yaver and embedded SDK hosts.
58
+ *
59
+ * Browser is the onboarding default for browser-capable React Native, Expo,
60
+ * Flutter, and web projects. When the user explicitly prefers Hermes or
61
+ * WebRTC, browser becomes the automatic second attempt. Native-only projects
62
+ * remain WebRTC-only and never advertise a browser recovery they cannot run.
63
+ */
64
+ export declare function dogfoodLanePlan(framework: string, capabilities?: {
65
+ nativeRuntimeAvailable?: boolean;
66
+ browserRuntimeAvailable?: boolean;
67
+ selfDevelopment?: boolean;
68
+ }, preferred?: DogfoodLane | null): DogfoodLanePlan;
43
69
  export interface DogfoodLogLine {
44
70
  text: string;
45
71
  at: number;
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.DogfoodController = exports.dogfoodLogLinesFromDevEvent = exports.DogfoodRuntimeError = void 0;
16
16
  exports.dogfoodLaneOptions = dogfoodLaneOptions;
17
17
  exports.defaultDogfoodLane = defaultDogfoodLane;
18
+ exports.dogfoodLanePlan = dogfoodLanePlan;
18
19
  exports.validateDogfoodProject = validateDogfoodProject;
19
20
  exports.runtimeLogLinesFromDevEvent = runtimeLogLinesFromDevEvent;
20
21
  /** One framework-to-lane matrix for Yaver and third-party consumers. */
@@ -49,6 +50,26 @@ function defaultDogfoodLane(framework, capabilities = {}) {
49
50
  || options.find((option) => option.supported)?.lane
50
51
  || 'browser';
51
52
  }
53
+ /**
54
+ * Resolve one ordered lane policy for Yaver and embedded SDK hosts.
55
+ *
56
+ * Browser is the onboarding default for browser-capable React Native, Expo,
57
+ * Flutter, and web projects. When the user explicitly prefers Hermes or
58
+ * WebRTC, browser becomes the automatic second attempt. Native-only projects
59
+ * remain WebRTC-only and never advertise a browser recovery they cannot run.
60
+ */
61
+ function dogfoodLanePlan(framework, capabilities = {}, preferred) {
62
+ const options = dogfoodLaneOptions(framework, capabilities);
63
+ const supported = (lane) => !!lane && options.some((option) => option.lane === lane && option.supported);
64
+ const resolved = supported(preferred)
65
+ ? preferred
66
+ : defaultDogfoodLane(framework, capabilities);
67
+ return {
68
+ preferred: resolved,
69
+ fallback: resolved !== 'browser' && supported('browser') ? 'browser' : undefined,
70
+ options,
71
+ };
72
+ }
52
73
  class DogfoodRuntimeError extends Error {
53
74
  constructor(failure) {
54
75
  super(failure.error);
@@ -173,8 +194,8 @@ class DogfoodController {
173
194
  phase: 'preparing', attempt, project: this.project,
174
195
  message: `Preparing ${this.project.name}…`, logs: [], startedAt: Date.now(),
175
196
  });
176
- const context = {
177
- project: this.project,
197
+ const makeContext = (project) => ({
198
+ project,
178
199
  attempt,
179
200
  log: (line) => {
180
201
  if (generation !== this.generation)
@@ -201,16 +222,69 @@ class DogfoodController {
201
222
  this.cleanups.set(generation, owned);
202
223
  },
203
224
  isCurrent: () => generation === this.generation,
204
- };
225
+ });
226
+ let activeProject = this.project;
227
+ let context = makeContext(activeProject);
205
228
  try {
206
- await this.driver.prepare?.(context);
207
- if (!context.isCurrent())
208
- throw new DogfoodRuntimeError({
209
- code: 'DOGFOOD_ATTEMPT_REPLACED', error: 'A newer Dogfood attempt replaced this one.',
210
- remedy: 'Wait for the newer attempt.', retryable: true,
229
+ const start = async () => {
230
+ await this.driver.prepare?.(context);
231
+ if (!context.isCurrent())
232
+ throw new DogfoodRuntimeError({
233
+ code: 'DOGFOOD_ATTEMPT_REPLACED', error: 'A newer Dogfood attempt replaced this one.',
234
+ remedy: 'Wait for the newer attempt.', retryable: true,
235
+ });
236
+ context.setPhase('starting', `Starting ${activeProject.name} on the ${activeProject.lane} lane…`);
237
+ return this.driver.start(context);
238
+ };
239
+ let result;
240
+ try {
241
+ result = await start();
242
+ }
243
+ catch (primaryError) {
244
+ const primaryFailure = failureFrom(primaryError);
245
+ const fallbackLane = activeProject.fallbackLane;
246
+ const mayFallback = !!fallbackLane
247
+ && fallbackLane !== activeProject.lane
248
+ && primaryFailure.retryable
249
+ && primaryFailure.code !== 'DOGFOOD_ATTEMPT_REPLACED';
250
+ if (!mayFallback)
251
+ throw primaryError;
252
+ await this.runCleanups('all', generation);
253
+ const fallbackProject = {
254
+ ...activeProject,
255
+ lane: fallbackLane,
256
+ fallbackLane: undefined,
257
+ };
258
+ const fallbackInvalid = validateDogfoodProject(fallbackProject);
259
+ if (fallbackInvalid)
260
+ throw primaryError;
261
+ const fallbackLine = {
262
+ text: `[fallback] ${activeProject.lane} failed (${primaryFailure.code}); trying ${fallbackLane}`,
263
+ at: Date.now(),
264
+ stream: 'system',
265
+ };
266
+ activeProject = fallbackProject;
267
+ this.replace({
268
+ ...this.state,
269
+ project: activeProject,
270
+ phase: 'preparing',
271
+ message: `Recovering ${activeProject.name} in the ${fallbackLane} lane…`,
272
+ logs: [...this.state.logs, fallbackLine].slice(-this.maxLogLines),
273
+ lastOutputAt: fallbackLine.at,
274
+ failure: undefined,
211
275
  });
212
- context.setPhase('starting', `Starting ${this.project.name} on the ${this.project.lane} lane…`);
213
- const result = await this.driver.start(context);
276
+ context = makeContext(activeProject);
277
+ result = await start();
278
+ result = {
279
+ ...result,
280
+ metadata: {
281
+ ...result.metadata,
282
+ preferredLane: this.project.lane,
283
+ fallbackFrom: this.project.lane,
284
+ fallbackReason: primaryFailure.code,
285
+ },
286
+ };
287
+ }
214
288
  if (!context.isCurrent()) {
215
289
  await this.runCleanups('all', generation);
216
290
  throw new DogfoodRuntimeError({
@@ -218,7 +292,7 @@ class DogfoodController {
218
292
  remedy: 'Wait for the newer attempt.', retryable: true,
219
293
  });
220
294
  }
221
- this.replace({ ...this.state, phase: 'ready', message: `${this.project.name} is ready`, result, failure: undefined });
295
+ this.replace({ ...this.state, project: activeProject, phase: 'ready', message: `${activeProject.name} is ready`, result, failure: undefined });
222
296
  return result;
223
297
  }
224
298
  catch (error) {
@@ -32,6 +32,7 @@ export declare const DogfoodStatusRail: React.FC<{
32
32
  export declare const DogfoodLanePicker: React.FC<{
33
33
  options: readonly DogfoodLaneOption[];
34
34
  selected: DogfoodLane;
35
+ fallbackLane?: DogfoodLane;
35
36
  onSelect: (lane: DogfoodLane) => void;
36
37
  colors?: Partial<DogfoodUiColors>;
37
38
  showUnsupportedReasons?: boolean;
@@ -54,7 +54,7 @@ const DogfoodStatusRail = ({ steps, colors: colorOverrides }) => {
54
54
  };
55
55
  exports.DogfoodStatusRail = DogfoodStatusRail;
56
56
  /** One lane selector and one default policy across Yaver, SFMG, and Talos. */
57
- const DogfoodLanePicker = ({ options, selected, onSelect, colors: colorOverrides, showUnsupportedReasons = true }) => {
57
+ const DogfoodLanePicker = ({ options, selected, fallbackLane, onSelect, colors: colorOverrides, showUnsupportedReasons = true }) => {
58
58
  const colors = resolvedColors(colorOverrides);
59
59
  return (<react_native_1.View accessibilityRole="radiogroup" accessibilityLabel="Dogfood runtime lane">
60
60
  <react_native_1.View style={styles.choiceRow}>
@@ -69,7 +69,8 @@ const DogfoodLanePicker = ({ options, selected, onSelect, colors: colorOverrides
69
69
  },
70
70
  ]}>
71
71
  <react_native_1.Text style={[styles.choiceText, { color: colors.text }, active && styles.choiceTextActive]}>
72
- {option.label}{option.default ? ' · default' : ''}
72
+ {option.label}
73
+ {active ? ' · preferred' : fallbackLane === option.lane ? ' · automatic fallback' : option.default ? ' · default' : ''}
73
74
  </react_native_1.Text>
74
75
  </react_native_1.Pressable>);
75
76
  })}