yaver-feedback-react-native 0.9.0 → 0.9.3
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.
- package/README.md +102 -1
- package/app.plugin.js +126 -9
- package/dist/AuthOverlay.d.ts +1 -14
- package/dist/AuthOverlay.js +9 -62
- package/dist/BlackBox.js +9 -1
- package/dist/DeployPanel.js +65 -0
- package/dist/Discovery.js +13 -8
- package/dist/DogfoodRuntime.d.ts +124 -0
- package/dist/DogfoodRuntime.js +273 -0
- package/dist/FeedbackModal.js +505 -65
- package/dist/LoginScreen.d.ts +1 -5
- package/dist/LoginScreen.js +2 -6
- package/dist/MachinePickerScreen.d.ts +1 -3
- package/dist/MachinePickerScreen.js +6 -18
- package/dist/P2PClient.d.ts +178 -2
- package/dist/P2PClient.js +493 -3
- package/dist/P2PDogfoodDriver.d.ts +12 -0
- package/dist/P2PDogfoodDriver.js +118 -0
- package/dist/PairDeviceModal.d.ts +2 -3
- package/dist/ShakeDetector.js +2 -0
- package/dist/VibeChatScreen.d.ts +11 -1
- package/dist/VibeChatScreen.js +200 -41
- package/dist/YaverFeedback.d.ts +132 -0
- package/dist/YaverFeedback.js +633 -65
- package/dist/YaverModeBadge.d.ts +22 -0
- package/dist/YaverModeBadge.js +219 -0
- package/dist/__tests__/AuthDevices.test.js +1 -48
- package/dist/__tests__/BlackBox.relayPassword.test.d.ts +1 -0
- package/dist/__tests__/BlackBox.relayPassword.test.js +105 -0
- package/dist/__tests__/BlackBoxAutoStart.test.d.ts +1 -0
- package/dist/__tests__/BlackBoxAutoStart.test.js +91 -0
- package/dist/__tests__/BlackBoxAutoStartColdStart.test.d.ts +1 -0
- package/dist/__tests__/BlackBoxAutoStartColdStart.test.js +156 -0
- package/dist/__tests__/BrowserLaneIcon.test.d.ts +3 -0
- package/dist/__tests__/BrowserLaneIcon.test.js +80 -0
- package/dist/__tests__/DogfoodRuntime.test.d.ts +1 -0
- package/dist/__tests__/DogfoodRuntime.test.js +116 -0
- package/dist/__tests__/P2PClient.test.js +2 -2
- package/dist/__tests__/P2PDogfoodDriver.test.d.ts +1 -0
- package/dist/__tests__/P2PDogfoodDriver.test.js +64 -0
- package/dist/__tests__/ReportIdentity.test.d.ts +25 -0
- package/dist/__tests__/ReportIdentity.test.js +180 -0
- package/dist/__tests__/SDKToken.test.js +1 -1
- package/dist/__tests__/ShakeToggle.test.d.ts +1 -0
- package/dist/__tests__/ShakeToggle.test.js +121 -0
- package/dist/__tests__/YaverFeedback.test.js +13 -3
- package/dist/__tests__/deviceDogfood.test.d.ts +1 -0
- package/dist/__tests__/deviceDogfood.test.js +86 -0
- package/dist/__tests__/dogfoodPolicy.test.d.ts +1 -0
- package/dist/__tests__/dogfoodPolicy.test.js +33 -0
- package/dist/__tests__/pickTargetDevice.test.d.ts +1 -0
- package/dist/__tests__/pickTargetDevice.test.js +89 -0
- package/dist/__tests__/reloadActions.test.d.ts +1 -0
- package/dist/__tests__/reloadActions.test.js +129 -0
- package/dist/__tests__/reloadActionsParity.test.d.ts +1 -0
- package/dist/__tests__/reloadActionsParity.test.js +42 -0
- package/dist/__tests__/types.test.js +5 -5
- package/dist/_core/ansi.d.ts +117 -0
- package/dist/_core/ansi.js +468 -0
- package/dist/_core/ansi.test.d.ts +1 -0
- package/dist/_core/ansi.test.js +225 -0
- package/dist/_core/buildFeedbackPrompt.d.ts +4 -9
- package/dist/_core/buildFeedbackPrompt.js +18 -72
- package/dist/_core/constants.d.ts +19 -6
- package/dist/_core/constants.js +20 -7
- package/dist/_core/device.d.ts +1 -5
- package/dist/_core/device.js +5 -14
- package/dist/_core/endpoints.d.ts +0 -7
- package/dist/_core/endpoints.js +0 -7
- package/dist/_core/index.d.ts +4 -0
- package/dist/_core/index.js +4 -0
- package/dist/_core/remoteless.d.ts +44 -0
- package/dist/_core/remoteless.js +75 -0
- package/dist/_core/trace.d.ts +47 -0
- package/dist/_core/trace.js +38 -0
- package/dist/_core/trace.test.d.ts +1 -0
- package/dist/_core/trace.test.js +60 -0
- package/dist/auth.d.ts +3 -60
- package/dist/auth.js +6 -88
- package/dist/deviceDogfood.d.ts +53 -0
- package/dist/deviceDogfood.js +137 -0
- package/dist/dogfoodPolicy.d.ts +25 -0
- package/dist/dogfoodPolicy.js +24 -0
- package/dist/index.d.ts +17 -4
- package/dist/index.js +33 -7
- package/dist/reloadActions.d.ts +88 -0
- package/dist/reloadActions.js +200 -0
- package/dist/storeShots.d.ts +67 -0
- package/dist/storeShots.js +137 -0
- package/dist/types.d.ts +208 -8
- package/package.json +13 -4
- package/src/AuthOverlay.tsx +20 -106
- package/src/BlackBox.ts +10 -1
- package/src/DeployPanel.tsx +74 -0
- package/src/Discovery.ts +13 -8
- package/src/DogfoodRuntime.ts +373 -0
- package/src/FeedbackModal.tsx +619 -79
- package/src/LoginScreen.tsx +2 -22
- package/src/MachinePickerScreen.tsx +6 -21
- package/src/P2PClient.ts +580 -4
- package/src/P2PDogfoodDriver.ts +132 -0
- package/src/PairDeviceModal.tsx +2 -3
- package/src/ShakeDetector.ts +1 -0
- package/src/VibeChatScreen.tsx +232 -42
- package/src/YaverFeedback.ts +631 -68
- package/src/YaverModeBadge.tsx +234 -0
- package/src/__tests__/AuthDevices.test.ts +1 -52
- package/src/__tests__/BlackBox.relayPassword.test.ts +129 -0
- package/src/__tests__/BlackBoxAutoStart.test.ts +111 -0
- package/src/__tests__/BlackBoxAutoStartColdStart.test.ts +191 -0
- package/src/__tests__/BrowserLaneIcon.test.ts +85 -0
- package/src/__tests__/DogfoodRuntime.test.ts +135 -0
- package/src/__tests__/P2PClient.test.ts +2 -2
- package/src/__tests__/P2PDogfoodDriver.test.ts +80 -0
- package/src/__tests__/ReportIdentity.test.ts +212 -0
- package/src/__tests__/SDKToken.test.ts +1 -1
- package/src/__tests__/ShakeToggle.test.ts +153 -0
- package/src/__tests__/YaverFeedback.test.ts +15 -3
- package/src/__tests__/deviceDogfood.test.ts +77 -0
- package/src/__tests__/dogfoodPolicy.test.ts +34 -0
- package/src/__tests__/pickTargetDevice.test.ts +101 -0
- package/src/__tests__/reloadActions.test.ts +171 -0
- package/src/__tests__/reloadActionsParity.test.ts +49 -0
- package/src/__tests__/types.test.ts +5 -5
- package/src/_core/ansi.test.ts +250 -0
- package/src/_core/ansi.ts +475 -0
- package/src/_core/buildFeedbackPrompt.ts +18 -95
- package/src/_core/constants.ts +20 -6
- package/src/_core/device.ts +5 -16
- package/src/_core/endpoints.ts +0 -7
- package/src/_core/index.ts +4 -0
- package/src/_core/remoteless.ts +110 -0
- package/src/_core/trace.test.ts +58 -0
- package/src/_core/trace.ts +75 -0
- package/src/auth.ts +7 -156
- package/src/deviceDogfood.ts +171 -0
- package/src/dogfoodPolicy.ts +40 -0
- package/src/index.ts +61 -11
- package/src/reloadActions.ts +273 -0
- package/src/storeShots.ts +189 -0
- package/src/types.ts +209 -8
- package/dist/GuestOnboardingScreen.d.ts +0 -8
- package/dist/GuestOnboardingScreen.js +0 -282
- package/src/GuestOnboardingScreen.tsx +0 -307
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ Starting with **0.6.0**, the SDK login screen mirrors the Yaver mobile app: nati
|
|
|
60
60
|
Drop in `<FeedbackModal />` and the first time a user triggers feedback without a cached session, they get:
|
|
61
61
|
|
|
62
62
|
1. A full-screen login modal — Apple (native) / Google / GitHub / GitLab / Microsoft (in-app browser) / email + password (inline).
|
|
63
|
-
2. A picker of the
|
|
63
|
+
2. A picker of the authenticated account's own dev boxes.
|
|
64
64
|
|
|
65
65
|
Both screens persist their selection to `AsyncStorage`, so subsequent launches reconnect silently.
|
|
66
66
|
|
|
@@ -89,6 +89,107 @@ function App() {
|
|
|
89
89
|
|
|
90
90
|
Shake your phone to open the feedback modal. On mobile the quick-access icon stays hidden until that first shake, then remains tappable for the rest of the session unless the user hides it. For launch, the sheet exposes three actions only: hot reload, vibing, and screenshot & fix.
|
|
91
91
|
|
|
92
|
+
### Account-scoped Dogfood mode
|
|
93
|
+
|
|
94
|
+
An app can send only explicitly approved app accounts directly into Vibing,
|
|
95
|
+
while everyone else keeps the normal Feedback sheet:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
YaverFeedback.init({
|
|
99
|
+
dogfood: {
|
|
100
|
+
enabled: true,
|
|
101
|
+
currentAccountId: session.user.id,
|
|
102
|
+
accountIds: ['approved-app-account-id'],
|
|
103
|
+
label: 'My App',
|
|
104
|
+
onExit: () => setDogfoodPreference(false),
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The match is exact and fails closed when the current account is absent. The
|
|
110
|
+
small `Y` identifies Dogfood mode; tapping it offers a confirmed exit back to
|
|
111
|
+
Feedback mode. This allowlist controls SDK presentation only—tasks and reloads
|
|
112
|
+
still require the user's normal Yaver authentication.
|
|
113
|
+
|
|
114
|
+
### Device-enrolled Dogfood for apps without their own OAuth/backend
|
|
115
|
+
|
|
116
|
+
Mount `FeedbackModal`, then call the reusable wizard from the host app's
|
|
117
|
+
Settings button:
|
|
118
|
+
|
|
119
|
+
```tsx
|
|
120
|
+
YaverFeedback.beginDogfoodOnboarding({
|
|
121
|
+
appId: 'io.example.app',
|
|
122
|
+
label: 'Example',
|
|
123
|
+
projectName: 'example',
|
|
124
|
+
framework: 'expo',
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The wizard runs Yaver OAuth first, then machine, coding runner/model, project,
|
|
129
|
+
and Browser/Hermes/WebRTC selection. It renders raw build logs and exposes the
|
|
130
|
+
ready preview. The app also creates a random installation ID and an Ed25519 key
|
|
131
|
+
inside SecureStore. The ID is a public handle, not a credential: enrollment
|
|
132
|
+
and each short session require a server challenge signed by the private key.
|
|
133
|
+
An owner approves, cancels, or revokes the installation from Yaver Settings or
|
|
134
|
+
MCP. Re-registering rotates the key/ID, preserves only the logical local slot,
|
|
135
|
+
and supersedes that slot's prior active generation without disabling another
|
|
136
|
+
phone.
|
|
137
|
+
|
|
138
|
+
Install `expo-secure-store` and `expo-crypto` in Expo hosts. Bare React Native
|
|
139
|
+
hosts may provide a `secureStore` implementation. Runtime/build controls still
|
|
140
|
+
require full Yaver OAuth; the account-free installation token defaults to
|
|
141
|
+
`feedback` + `blackbox` and cannot be rotated into a long-lived owner token.
|
|
142
|
+
|
|
143
|
+
### Embeddable Dogfood runtime
|
|
144
|
+
|
|
145
|
+
App owners can also embed the actual source → dev-server → render lifecycle.
|
|
146
|
+
`DogfoodController` is deliberately headless: constructing it does nothing;
|
|
147
|
+
your own button calls `trigger()`, your preview renders `result.url`, and your
|
|
148
|
+
console renders `snapshot.logs`. Retry and partial-session cleanup are handled
|
|
149
|
+
by the controller.
|
|
150
|
+
|
|
151
|
+
```tsx
|
|
152
|
+
import {
|
|
153
|
+
DogfoodController,
|
|
154
|
+
P2PClient,
|
|
155
|
+
createP2PDogfoodDriver,
|
|
156
|
+
} from 'yaver-feedback-react-native';
|
|
157
|
+
|
|
158
|
+
const client = new P2PClient(agentUrl, signedInUserToken);
|
|
159
|
+
const dogfood = new DogfoodController(
|
|
160
|
+
{
|
|
161
|
+
name: 'My App',
|
|
162
|
+
workDir: '/workspace/my-app',
|
|
163
|
+
framework: 'expo', // also react-native, flutter, vite, next
|
|
164
|
+
lane: 'browser', // Hermes is Expo/RN only; Flutter uses browser/WebRTC
|
|
165
|
+
},
|
|
166
|
+
createP2PDogfoodDriver(client),
|
|
167
|
+
{
|
|
168
|
+
onChange(snapshot) {
|
|
169
|
+
setPhase(snapshot.phase);
|
|
170
|
+
setConsoleText(snapshot.logs.map((line) => line.text).join('\n'));
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// Explicit user intent: call only from your Dogfood button.
|
|
176
|
+
const onDogfoodPress = () => dogfood.trigger().then(({ url }) => setPreviewUrl(url));
|
|
177
|
+
const onRetryPress = () => dogfood.retry();
|
|
178
|
+
const onLeaveScreen = () => dogfood.stop();
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The default P2P driver calls the same `/dev/start`, `/dev/status`, `/dev/stop`,
|
|
182
|
+
and `/dev/events` endpoints as Yaver's Projects browser lane. Package-manager
|
|
183
|
+
and compiler output—including `$ npm install`, Expo/Metro, and Flutter web
|
|
184
|
+
output—arrives uncollapsed in `snapshot.logs`. Starting tools requires the
|
|
185
|
+
normal signed-in Yaver user token; a narrow feedback-only SDK token cannot
|
|
186
|
+
spawn processes.
|
|
187
|
+
|
|
188
|
+
For Yaver itself, installing `yaver-cli` supplies these agent endpoints. The
|
|
189
|
+
native Yaver app checks the selected box for a real Yaver checkout and offers
|
|
190
|
+
`Clone Yaver source` when it is absent; cloning still occurs only after that
|
|
191
|
+
explicit tap.
|
|
192
|
+
|
|
92
193
|
### Quick Icon Styling
|
|
93
194
|
|
|
94
195
|
The quick icon is configurable at compile time through `YaverFeedback.init(...)`, with render-time overrides still available through `<QuickActionIcon />` props when needed. If you do nothing, the SDK uses a high-visibility orange bubble by default so it stays distinct from the blue/indigo FAB styling many mobile apps already have.
|
package/app.plugin.js
CHANGED
|
@@ -387,14 +387,135 @@ function withYaverAndroidHotReload(config) {
|
|
|
387
387
|
},
|
|
388
388
|
]);
|
|
389
389
|
|
|
390
|
-
// Patch MainApplication to register the package and use hot bundle
|
|
390
|
+
// Patch MainApplication to register the package and use hot bundle.
|
|
391
|
+
//
|
|
392
|
+
// MainApplication is Kotlin on React Native 0.73+ (every current Expo
|
|
393
|
+
// template) and Java before that. The two need genuinely different code —
|
|
394
|
+
// `new Foo()`, `final`, and `@Override` are all syntax errors in Kotlin —
|
|
395
|
+
// so branch on the language Expo reports rather than assuming.
|
|
391
396
|
config = withMainApplication(config, (config) => {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
if (contents.includes("YaverHotReload")) {
|
|
397
|
+
if (config.modResults.contents.includes("YaverHotReload")) {
|
|
395
398
|
return config;
|
|
396
399
|
}
|
|
400
|
+
config.modResults.contents =
|
|
401
|
+
config.modResults.language === "kt"
|
|
402
|
+
? patchMainApplicationKotlin(config.modResults.contents)
|
|
403
|
+
: patchMainApplicationJava(config.modResults.contents);
|
|
404
|
+
return config;
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
return config;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Insert `snippet` immediately before the closing brace of the method whose
|
|
412
|
+
* signature contains `anchor`, by matching braces from the method's opening
|
|
413
|
+
* one.
|
|
414
|
+
*
|
|
415
|
+
* The boot guard has to run at the END of onCreate: it touches
|
|
416
|
+
* reactNativeHost, and reaching that before super.onCreate() and
|
|
417
|
+
* SoLoader.init() would initialise React before its native libraries are
|
|
418
|
+
* loaded. Returns the contents unchanged if the anchor isn't found — a
|
|
419
|
+
* missing safety net is survivable, a corrupted MainApplication is not.
|
|
420
|
+
*/
|
|
421
|
+
function insertAtEndOfMethod(contents, anchor, snippet) {
|
|
422
|
+
const anchorIdx = contents.indexOf(anchor);
|
|
423
|
+
if (anchorIdx === -1) return contents;
|
|
424
|
+
const open = contents.indexOf("{", anchorIdx);
|
|
425
|
+
if (open === -1) return contents;
|
|
426
|
+
|
|
427
|
+
let depth = 0;
|
|
428
|
+
for (let i = open; i < contents.length; i++) {
|
|
429
|
+
const ch = contents[i];
|
|
430
|
+
if (ch === "{") depth++;
|
|
431
|
+
else if (ch === "}") {
|
|
432
|
+
depth--;
|
|
433
|
+
if (depth === 0) {
|
|
434
|
+
return contents.slice(0, i) + snippet + contents.slice(i);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return contents;
|
|
439
|
+
}
|
|
397
440
|
|
|
441
|
+
/** Kotlin MainApplication (React Native 0.73+). */
|
|
442
|
+
function patchMainApplicationKotlin(contents) {
|
|
443
|
+
// Imports. Kotlin takes no semicolons.
|
|
444
|
+
contents = contents.replace(
|
|
445
|
+
"import com.facebook.react.ReactApplication",
|
|
446
|
+
"import com.facebook.react.ReactApplication\nimport io.yaver.feedback.YaverHotReloadModule\nimport io.yaver.feedback.YaverHotReloadPackage"
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
// Register the package. Anchor on `return packages` rather than on
|
|
450
|
+
// `packages.add(` — the template's only occurrence of that is inside a
|
|
451
|
+
// commented-out example line.
|
|
452
|
+
contents = contents.replace(
|
|
453
|
+
/(\n([ \t]*)return packages\n)/,
|
|
454
|
+
"\n$2packages.add(YaverHotReloadPackage())\n$1"
|
|
455
|
+
);
|
|
456
|
+
|
|
457
|
+
// Load a hot-pushed bundle when one is present. Inserted before
|
|
458
|
+
// getJSMainModuleName, which every Expo template defines.
|
|
459
|
+
if (!contents.includes("getJSBundleFile")) {
|
|
460
|
+
contents = contents.replace(
|
|
461
|
+
/(\n([ \t]*)override fun getJSMainModuleName\(\))/,
|
|
462
|
+
`
|
|
463
|
+
$2override fun getJSBundleFile(): String? {
|
|
464
|
+
$2 // Yaver Feedback SDK: load hot-reloaded bundle if available
|
|
465
|
+
$2 val hotBundle = YaverHotReloadModule.getSavedBundleFile(application.applicationContext)
|
|
466
|
+
$2 return hotBundle?.absolutePath ?: super.getJSBundleFile()
|
|
467
|
+
$2}
|
|
468
|
+
$1`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Crash-revert safety net: clear the boot-attempt counter once the React
|
|
473
|
+
// context initialises (bundle loaded successfully), AND via a 10-s fallback
|
|
474
|
+
// in case that listener never fires (e.g. an infinite loop in the root
|
|
475
|
+
// component). If neither fires, YaverHotReloadModule.getSavedBundleFile()
|
|
476
|
+
// reverts to the APK-bundled bundle after 3 failed cold starts. Parity with
|
|
477
|
+
// YaverHotReload.swift on iOS.
|
|
478
|
+
if (!contents.includes("yaverHotReloadBootListener")) {
|
|
479
|
+
contents = insertAtEndOfMethod(
|
|
480
|
+
contents,
|
|
481
|
+
"override fun onCreate()",
|
|
482
|
+
`
|
|
483
|
+
// Yaver Feedback SDK hot-reload crash-revert safety net
|
|
484
|
+
val yaverHotReloadCtx: android.content.Context = applicationContext
|
|
485
|
+
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed(
|
|
486
|
+
{ YaverHotReloadModule.markBootSuccessful(yaverHotReloadCtx) },
|
|
487
|
+
10000
|
|
488
|
+
)
|
|
489
|
+
try {
|
|
490
|
+
// Anonymous object, not a lambda: SAM conversion needs a Java interface
|
|
491
|
+
// or a Kotlin \`fun interface\`, and as of RN 0.81
|
|
492
|
+
// com.facebook.react.ReactInstanceEventListener is a plain Kotlin
|
|
493
|
+
// interface (ReactInstanceEventListener.kt). A lambda there fails
|
|
494
|
+
// :app:compileReleaseKotlin with "Argument type mismatch: actual type is
|
|
495
|
+
// 'Function0<Unit>'" — i.e. the host app cannot build a release AAB at
|
|
496
|
+
// all. The Java path below already did this correctly.
|
|
497
|
+
reactNativeHost.reactInstanceManager.addReactInstanceEventListener(
|
|
498
|
+
object : com.facebook.react.ReactInstanceEventListener {
|
|
499
|
+
override fun onReactContextInitialized(
|
|
500
|
+
context: com.facebook.react.bridge.ReactContext
|
|
501
|
+
) {
|
|
502
|
+
YaverHotReloadModule.markBootSuccessful(yaverHotReloadCtx)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
)
|
|
506
|
+
} catch (yaverHotReloadBootListener: Throwable) {
|
|
507
|
+
// Bridgeless / New Architecture does not expose reactInstanceManager;
|
|
508
|
+
// the 10-s fallback above still covers us.
|
|
509
|
+
}
|
|
510
|
+
`
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return contents;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** Java MainApplication (React Native < 0.73). */
|
|
518
|
+
function patchMainApplicationJava(contents) {
|
|
398
519
|
// Add import
|
|
399
520
|
contents = contents.replace(
|
|
400
521
|
"import com.facebook.react.ReactApplication",
|
|
@@ -468,11 +589,7 @@ function withYaverAndroidHotReload(config) {
|
|
|
468
589
|
contents.slice(0, insertionPoint) + bootGuard + contents.slice(insertionPoint);
|
|
469
590
|
}
|
|
470
591
|
|
|
471
|
-
|
|
472
|
-
return config;
|
|
473
|
-
});
|
|
474
|
-
|
|
475
|
-
return config;
|
|
592
|
+
return contents;
|
|
476
593
|
}
|
|
477
594
|
|
|
478
595
|
function withYaverFeedback(config, props) {
|
package/dist/AuthOverlay.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
/**
|
|
3
|
-
* Presentation layer for the SDK's auth + machine-picker modals.
|
|
4
|
-
*
|
|
5
|
-
* Mounts automatically inside `<FeedbackModal />`, so consumers of the SDK
|
|
6
|
-
* get in-app login with no extra wiring. It listens for events emitted by
|
|
7
|
-
* `YaverFeedback.showLogin()` / `showMachinePicker()`:
|
|
8
|
-
*
|
|
9
|
-
* yaverFeedback:startLogin → show login modal
|
|
10
|
-
* yaverFeedback:startMachinePicker → show machine picker
|
|
11
|
-
*
|
|
12
|
-
* The overlay closes itself once login/pick succeeds, then re-emits
|
|
13
|
-
* `yaverFeedback:startReport` so the user continues straight into the
|
|
14
|
-
* feedback flow they originally triggered.
|
|
15
|
-
*/
|
|
2
|
+
/** Owner sign-in and owner-machine selection for the Feedback SDK. */
|
|
16
3
|
export declare const AuthOverlay: React.FC;
|
package/dist/AuthOverlay.js
CHANGED
|
@@ -38,74 +38,47 @@ const react_1 = __importStar(require("react"));
|
|
|
38
38
|
const react_native_1 = require("react-native");
|
|
39
39
|
const LoginScreen_1 = require("./LoginScreen");
|
|
40
40
|
const MachinePickerScreen_1 = require("./MachinePickerScreen");
|
|
41
|
-
const GuestOnboardingScreen_1 = require("./GuestOnboardingScreen");
|
|
42
41
|
const YaverFeedback_1 = require("./YaverFeedback");
|
|
43
42
|
const auth_1 = require("./auth");
|
|
44
|
-
/**
|
|
45
|
-
* Presentation layer for the SDK's auth + machine-picker modals.
|
|
46
|
-
*
|
|
47
|
-
* Mounts automatically inside `<FeedbackModal />`, so consumers of the SDK
|
|
48
|
-
* get in-app login with no extra wiring. It listens for events emitted by
|
|
49
|
-
* `YaverFeedback.showLogin()` / `showMachinePicker()`:
|
|
50
|
-
*
|
|
51
|
-
* yaverFeedback:startLogin → show login modal
|
|
52
|
-
* yaverFeedback:startMachinePicker → show machine picker
|
|
53
|
-
*
|
|
54
|
-
* The overlay closes itself once login/pick succeeds, then re-emits
|
|
55
|
-
* `yaverFeedback:startReport` so the user continues straight into the
|
|
56
|
-
* feedback flow they originally triggered.
|
|
57
|
-
*/
|
|
43
|
+
/** Owner sign-in and owner-machine selection for the Feedback SDK. */
|
|
58
44
|
const AuthOverlay = () => {
|
|
59
45
|
const [loginVisible, setLoginVisible] = (0, react_1.useState)(false);
|
|
60
|
-
const [guestVisible, setGuestVisible] = (0, react_1.useState)(false);
|
|
61
46
|
const [pickerVisible, setPickerVisible] = (0, react_1.useState)(false);
|
|
62
47
|
const [token, setToken] = (0, react_1.useState)(null);
|
|
63
|
-
const [pendingInviteCode, setPendingInviteCode] = (0, react_1.useState)(null);
|
|
64
48
|
const activeOverlayRef = (0, react_1.useRef)('none');
|
|
65
49
|
const openLogin = (0, react_1.useCallback)(() => {
|
|
66
50
|
activeOverlayRef.current = 'login';
|
|
67
|
-
setGuestVisible(false);
|
|
68
51
|
setPickerVisible(false);
|
|
69
52
|
setLoginVisible(true);
|
|
70
53
|
}, []);
|
|
71
|
-
const openGuest = (0, react_1.useCallback)(() => {
|
|
72
|
-
activeOverlayRef.current = 'guest';
|
|
73
|
-
setLoginVisible(false);
|
|
74
|
-
setPickerVisible(false);
|
|
75
|
-
setGuestVisible(true);
|
|
76
|
-
}, []);
|
|
77
54
|
const openPicker = (0, react_1.useCallback)(() => {
|
|
78
55
|
activeOverlayRef.current = 'picker';
|
|
79
56
|
setLoginVisible(false);
|
|
80
|
-
setGuestVisible(false);
|
|
81
57
|
setPickerVisible(true);
|
|
82
58
|
}, []);
|
|
83
59
|
const closeAll = (0, react_1.useCallback)(() => {
|
|
84
60
|
activeOverlayRef.current = 'none';
|
|
85
61
|
setLoginVisible(false);
|
|
86
|
-
setGuestVisible(false);
|
|
87
62
|
setPickerVisible(false);
|
|
88
63
|
}, []);
|
|
89
64
|
(0, react_1.useEffect)(() => {
|
|
90
65
|
let mounted = true;
|
|
91
|
-
(
|
|
92
|
-
const cached = await (0, auth_1.getToken)();
|
|
66
|
+
void (0, auth_1.getToken)().then((cached) => {
|
|
93
67
|
if (mounted && cached)
|
|
94
68
|
setToken(cached);
|
|
95
|
-
})
|
|
69
|
+
});
|
|
96
70
|
const loginSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startLogin', () => {
|
|
97
|
-
if (activeOverlayRef.current
|
|
98
|
-
|
|
99
|
-
openLogin();
|
|
71
|
+
if (activeOverlayRef.current === 'none')
|
|
72
|
+
openLogin();
|
|
100
73
|
});
|
|
101
74
|
const pickerSub = react_native_1.DeviceEventEmitter.addListener('yaverFeedback:startMachinePicker', async () => {
|
|
102
75
|
if (activeOverlayRef.current !== 'none')
|
|
103
76
|
return;
|
|
104
77
|
const cached = await (0, auth_1.getToken)();
|
|
105
|
-
if (cached)
|
|
78
|
+
if (cached) {
|
|
106
79
|
setToken(cached);
|
|
107
|
-
if (cached)
|
|
108
80
|
openPicker();
|
|
81
|
+
}
|
|
109
82
|
});
|
|
110
83
|
return () => {
|
|
111
84
|
mounted = false;
|
|
@@ -113,49 +86,23 @@ const AuthOverlay = () => {
|
|
|
113
86
|
pickerSub.remove();
|
|
114
87
|
};
|
|
115
88
|
}, [openLogin, openPicker]);
|
|
116
|
-
const
|
|
89
|
+
const handleLoggedIn = async (newToken) => {
|
|
117
90
|
setToken(newToken);
|
|
118
91
|
await YaverFeedback_1.YaverFeedback.setAuthToken(newToken);
|
|
119
|
-
const devices = await (0, auth_1.listReachableDevices)(newToken).catch(() => ({ owned: [], shared: [] }));
|
|
120
|
-
const cleanedInviteCode = (inviteCode ?? '').trim().toUpperCase();
|
|
121
|
-
if (cleanedInviteCode) {
|
|
122
|
-
setPendingInviteCode(cleanedInviteCode);
|
|
123
|
-
openGuest();
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
if (devices.owned.length === 0 && devices.shared.length === 0) {
|
|
127
|
-
openGuest();
|
|
128
|
-
return;
|
|
129
|
-
}
|
|
130
92
|
openPicker();
|
|
131
93
|
};
|
|
132
|
-
const handleLoggedIn = async (newToken, opts) => {
|
|
133
|
-
await continueAfterAuth(newToken, opts?.inviteCode);
|
|
134
|
-
};
|
|
135
94
|
const handleDevicePicked = async (device) => {
|
|
136
95
|
await YaverFeedback_1.YaverFeedback.setPreferredDevice(device.deviceId);
|
|
137
96
|
closeAll();
|
|
138
|
-
// Continue straight into the feedback flow the user originally triggered.
|
|
139
97
|
react_native_1.DeviceEventEmitter.emit('yaverFeedback:startReport');
|
|
140
98
|
};
|
|
141
99
|
return (<>
|
|
142
100
|
<react_native_1.Modal visible={loginVisible} animationType="slide" presentationStyle="fullScreen" onRequestClose={closeAll}>
|
|
143
|
-
<LoginScreen_1.YaverLoginScreen onLoggedIn={handleLoggedIn} onCancel={closeAll}
|
|
101
|
+
<LoginScreen_1.YaverLoginScreen onLoggedIn={handleLoggedIn} onCancel={closeAll}/>
|
|
144
102
|
</react_native_1.Modal>
|
|
145
|
-
|
|
146
103
|
<react_native_1.Modal visible={pickerVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={closeAll}>
|
|
147
104
|
{token && (<MachinePickerScreen_1.YaverMachinePickerScreen token={token} currentDeviceId={YaverFeedback_1.YaverFeedback.getConfig()?.preferredDeviceId} onPick={handleDevicePicked} onCancel={closeAll}/>)}
|
|
148
105
|
</react_native_1.Modal>
|
|
149
|
-
|
|
150
|
-
<react_native_1.Modal visible={guestVisible && !!token} animationType="slide" presentationStyle="fullScreen" onRequestClose={closeAll}>
|
|
151
|
-
{token && (<GuestOnboardingScreen_1.YaverGuestOnboardingScreen token={token} initialInviteCode={pendingInviteCode ?? YaverFeedback_1.YaverFeedback.getConfig()?.guestInviteCode} onContinue={() => {
|
|
152
|
-
setPendingInviteCode(null);
|
|
153
|
-
openPicker();
|
|
154
|
-
}} onCancel={() => {
|
|
155
|
-
setPendingInviteCode(null);
|
|
156
|
-
openPicker();
|
|
157
|
-
}}/>)}
|
|
158
|
-
</react_native_1.Modal>
|
|
159
106
|
</>);
|
|
160
107
|
};
|
|
161
108
|
exports.AuthOverlay = AuthOverlay;
|
package/dist/BlackBox.js
CHANGED
|
@@ -3,6 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BlackBox = void 0;
|
|
4
4
|
const react_native_1 = require("react-native");
|
|
5
5
|
const YaverFeedback_1 = require("./YaverFeedback");
|
|
6
|
+
function unrefTimer(timer) {
|
|
7
|
+
const maybeNodeTimer = timer;
|
|
8
|
+
if (typeof maybeNodeTimer.unref === 'function') {
|
|
9
|
+
maybeNodeTimer.unref();
|
|
10
|
+
}
|
|
11
|
+
}
|
|
6
12
|
class BlackBox {
|
|
7
13
|
/**
|
|
8
14
|
* Start the black box stream. Call after `YaverFeedback.init()`.
|
|
@@ -18,7 +24,7 @@ class BlackBox {
|
|
|
18
24
|
}
|
|
19
25
|
BlackBox.baseUrl = feedbackConfig.agentUrl.replace(/\/$/, '');
|
|
20
26
|
BlackBox.authToken = feedbackConfig.authToken ?? null;
|
|
21
|
-
BlackBox.relayPassword =
|
|
27
|
+
BlackBox.relayPassword = YaverFeedback_1.YaverFeedback.getRelayPassword();
|
|
22
28
|
BlackBox.deviceId = config?.deviceId ?? BlackBox.generateDeviceId();
|
|
23
29
|
BlackBox.appName = config?.appName ?? '';
|
|
24
30
|
BlackBox.flushInterval = config?.flushInterval ?? 2000;
|
|
@@ -29,6 +35,7 @@ class BlackBox {
|
|
|
29
35
|
if (BlackBox.flushTimer)
|
|
30
36
|
clearInterval(BlackBox.flushTimer);
|
|
31
37
|
BlackBox.flushTimer = setInterval(() => BlackBox.flush(), BlackBox.flushInterval);
|
|
38
|
+
unrefTimer(BlackBox.flushTimer);
|
|
32
39
|
// Log the session start
|
|
33
40
|
BlackBox.push({
|
|
34
41
|
type: 'lifecycle',
|
|
@@ -343,6 +350,7 @@ class BlackBox {
|
|
|
343
350
|
if (BlackBox.started)
|
|
344
351
|
BlackBox.connectSSE();
|
|
345
352
|
}, 5000);
|
|
353
|
+
unrefTimer(BlackBox.sseReconnectTimer);
|
|
346
354
|
}
|
|
347
355
|
// ─── Internal ────────────────────────────────────────────────────
|
|
348
356
|
static push(event) {
|
package/dist/DeployPanel.js
CHANGED
|
@@ -151,6 +151,45 @@ const DeployPanel = ({ onClose }) => {
|
|
|
151
151
|
</react_native_1.Text>
|
|
152
152
|
</react_native_1.Pressable>);
|
|
153
153
|
};
|
|
154
|
+
// First-class App Store screenshots: walk the app's routes on THIS
|
|
155
|
+
// device, screenshot each, upload to the agent (which runs the ASC
|
|
156
|
+
// backend). Closes the overlay first so the captures are clean (no
|
|
157
|
+
// modal in frame), then reports via an alert.
|
|
158
|
+
const runStoreShots = () => {
|
|
159
|
+
const cfg = YaverFeedback_1.YaverFeedback.getConfig();
|
|
160
|
+
const ss = cfg?.storeShots;
|
|
161
|
+
if (!ss?.routes?.length) {
|
|
162
|
+
setStatus('Set config.storeShots.routes (and a navigationRef) to enable.');
|
|
163
|
+
setStatusTone('error');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const app = resolveAppSlug();
|
|
167
|
+
onClose();
|
|
168
|
+
// Defer so the overlay is fully dismissed before the first capture.
|
|
169
|
+
setTimeout(async () => {
|
|
170
|
+
try {
|
|
171
|
+
const res = await YaverFeedback_1.YaverFeedback.captureStoreScreenshots({
|
|
172
|
+
app,
|
|
173
|
+
routes: ss.routes,
|
|
174
|
+
navigationRef: ss.navigationRef,
|
|
175
|
+
screens: ss.screens,
|
|
176
|
+
submit: ss.submit,
|
|
177
|
+
});
|
|
178
|
+
const msg = res.ok
|
|
179
|
+
? res.submitted
|
|
180
|
+
? 'Submitted for App Store review 🎉'
|
|
181
|
+
: res.staged
|
|
182
|
+
? `Uploaded ${res.uploaded} screenshots — staged. One tap left in App Store Connect.`
|
|
183
|
+
: `Uploaded ${res.uploaded} App Store screenshots.`
|
|
184
|
+
: res.message || 'Capture failed.';
|
|
185
|
+
react_native_1.Alert.alert('App Store screenshots', msg);
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
react_native_1.Alert.alert('App Store screenshots', e?.message ?? 'Capture failed.');
|
|
189
|
+
}
|
|
190
|
+
}, 500);
|
|
191
|
+
};
|
|
192
|
+
const storeShotsEnabled = (YaverFeedback_1.YaverFeedback.getConfig()?.storeShots?.routes?.length ?? 0) > 0;
|
|
154
193
|
const triggerDeploy = async (machine) => {
|
|
155
194
|
if (!options)
|
|
156
195
|
return;
|
|
@@ -233,6 +272,13 @@ const DeployPanel = ({ onClose }) => {
|
|
|
233
272
|
<react_native_1.ActivityIndicator color="rgba(255,255,255,0.6)"/>
|
|
234
273
|
</react_native_1.View>) : error ? (<react_native_1.Text style={styles.error}>{error}</react_native_1.Text>) : options ? (<react_native_1.ScrollView style={styles.list}>{options.devices.map(machineRow)}</react_native_1.ScrollView>) : null}
|
|
235
274
|
|
|
275
|
+
{storeShotsEnabled && (<react_native_1.Pressable onPress={runStoreShots} disabled={shipping} style={({ pressed }) => [styles.shotsBtn, pressed && styles.rowPressed]}>
|
|
276
|
+
<react_native_1.Text style={styles.shotsBtnText}>📸 App Store screenshots</react_native_1.Text>
|
|
277
|
+
<react_native_1.Text style={styles.shotsBtnSub}>
|
|
278
|
+
capture this app on-device + upload to App Store Connect
|
|
279
|
+
</react_native_1.Text>
|
|
280
|
+
</react_native_1.Pressable>)}
|
|
281
|
+
|
|
236
282
|
{status && (<react_native_1.Text style={[
|
|
237
283
|
styles.status,
|
|
238
284
|
statusTone === 'success' && styles.statusSuccess,
|
|
@@ -339,6 +385,25 @@ const styles = react_native_1.StyleSheet.create({
|
|
|
339
385
|
rowMetaWarning: {
|
|
340
386
|
color: 'rgb(255,178,115)',
|
|
341
387
|
},
|
|
388
|
+
shotsBtn: {
|
|
389
|
+
marginTop: 12,
|
|
390
|
+
paddingVertical: 12,
|
|
391
|
+
paddingHorizontal: 14,
|
|
392
|
+
borderRadius: 10,
|
|
393
|
+
backgroundColor: 'rgba(124,109,255,0.16)',
|
|
394
|
+
borderWidth: 1,
|
|
395
|
+
borderColor: 'rgba(124,109,255,0.4)',
|
|
396
|
+
},
|
|
397
|
+
shotsBtnText: {
|
|
398
|
+
color: '#fff',
|
|
399
|
+
fontSize: 14,
|
|
400
|
+
fontWeight: '600',
|
|
401
|
+
},
|
|
402
|
+
shotsBtnSub: {
|
|
403
|
+
color: 'rgba(255,255,255,0.55)',
|
|
404
|
+
fontSize: 12,
|
|
405
|
+
marginTop: 2,
|
|
406
|
+
},
|
|
342
407
|
status: {
|
|
343
408
|
color: 'rgba(255,255,255,0.55)',
|
|
344
409
|
fontSize: 12,
|
package/dist/Discovery.js
CHANGED
|
@@ -34,6 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.YaverDiscovery = void 0;
|
|
37
|
+
function unrefTimer(timer) {
|
|
38
|
+
const maybeNodeTimer = timer;
|
|
39
|
+
if (typeof maybeNodeTimer.unref === 'function') {
|
|
40
|
+
maybeNodeTimer.unref();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
37
43
|
function getAsyncStorage() {
|
|
38
44
|
try {
|
|
39
45
|
const mod = require('@react-native-async-storage/async-storage');
|
|
@@ -173,12 +179,6 @@ class YaverDiscovery {
|
|
|
173
179
|
needsAuth: !!d.needsAuth,
|
|
174
180
|
runnerDown: !!d.runnerDown,
|
|
175
181
|
lastHeartbeat: d.lastHeartbeat ?? 0,
|
|
176
|
-
isGuest: !!d.isGuest,
|
|
177
|
-
hostUserId: d.hostUserId,
|
|
178
|
-
hostName: d.hostName,
|
|
179
|
-
hostEmail: d.hostEmail,
|
|
180
|
-
hostUserIdString: d.hostUserIdString,
|
|
181
|
-
accessScope: d.accessScope ?? 'owner',
|
|
182
182
|
quicHost: d.quicHost ?? d.host ?? '',
|
|
183
183
|
quicPort: d.quicPort ?? 0,
|
|
184
184
|
httpPort: d.httpPort ?? d.quicPort,
|
|
@@ -333,14 +333,15 @@ class YaverDiscovery {
|
|
|
333
333
|
static async probe(url) {
|
|
334
334
|
const base = url.replace(/\/$/, '');
|
|
335
335
|
const start = Date.now();
|
|
336
|
+
let timeoutId = null;
|
|
336
337
|
try {
|
|
337
338
|
const controller = new AbortController();
|
|
338
|
-
|
|
339
|
+
timeoutId = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
340
|
+
unrefTimer(timeoutId);
|
|
339
341
|
const response = await fetch(`${base}/health`, {
|
|
340
342
|
method: 'GET',
|
|
341
343
|
signal: controller.signal,
|
|
342
344
|
});
|
|
343
|
-
clearTimeout(timeoutId);
|
|
344
345
|
if (!response.ok)
|
|
345
346
|
return null;
|
|
346
347
|
const latency = Date.now() - start;
|
|
@@ -359,6 +360,10 @@ class YaverDiscovery {
|
|
|
359
360
|
catch {
|
|
360
361
|
return null;
|
|
361
362
|
}
|
|
363
|
+
finally {
|
|
364
|
+
if (timeoutId)
|
|
365
|
+
clearTimeout(timeoutId);
|
|
366
|
+
}
|
|
362
367
|
}
|
|
363
368
|
static async connect(url) {
|
|
364
369
|
const result = await YaverDiscovery.probe(url);
|