yaver-feedback-react-native 0.9.4 → 0.9.7
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 +31 -16
- package/android/src/main/java/io/yaver/feedback/YaverHotReloadModule.java +59 -0
- package/app.plugin.js +200 -3
- package/dist/AuthOverlay.js +13 -2
- package/dist/FeedbackModal.js +70 -7
- package/dist/YaverFeedback.d.ts +34 -9
- package/dist/YaverFeedback.js +304 -20
- package/dist/__tests__/NativeDogfoodShortcut.test.d.ts +1 -0
- package/dist/__tests__/NativeDogfoodShortcut.test.js +52 -0
- package/dist/__tests__/ReportIdentity.test.js +6 -3
- package/dist/__tests__/YaverFeedback.test.js +97 -4
- package/dist/__tests__/deviceDogfood.test.js +2 -2
- package/dist/__tests__/dogfoodPolicy.test.js +1 -1
- package/dist/auth.d.ts +7 -0
- package/dist/auth.js +28 -0
- package/dist/deviceDogfood.d.ts +6 -2
- package/dist/deviceDogfood.js +6 -3
- package/dist/dogfoodPolicy.d.ts +32 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +5 -2
- package/dist/preferences.d.ts +2 -0
- package/dist/preferences.js +24 -0
- package/ios/YaverHotReload.m +8 -0
- package/ios/YaverHotReload.swift +37 -0
- package/package.json +2 -2
- package/src/AuthOverlay.tsx +11 -2
- package/src/FeedbackModal.tsx +98 -6
- package/src/YaverFeedback.ts +271 -19
- package/src/__tests__/NativeDogfoodShortcut.test.ts +59 -0
- package/src/__tests__/ReportIdentity.test.ts +6 -4
- package/src/__tests__/YaverFeedback.test.ts +104 -5
- package/src/__tests__/deviceDogfood.test.ts +2 -2
- package/src/__tests__/dogfoodPolicy.test.ts +1 -1
- package/src/auth.ts +29 -0
- package/src/deviceDogfood.ts +8 -3
- package/src/dogfoodPolicy.ts +32 -1
- package/src/index.ts +5 -2
- package/src/preferences.ts +20 -0
package/src/auth.ts
CHANGED
|
@@ -202,6 +202,35 @@ export async function clearSelectedDeviceId(): Promise<void> {
|
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
/** Verify that this full Yaver session owns the exact registered Dogfood app.
|
|
206
|
+
* Any login is not enough, and installation-scoped SDK tokens return false. */
|
|
207
|
+
export async function getDogfoodAccountAccess(appId: string, token: string, installationId?: string): Promise<{
|
|
208
|
+
authenticated: boolean;
|
|
209
|
+
ownerAuthorized: boolean;
|
|
210
|
+
installationAuthorized: boolean;
|
|
211
|
+
}> {
|
|
212
|
+
try {
|
|
213
|
+
const controller = new AbortController();
|
|
214
|
+
const timeout = setTimeout(() => controller.abort(), 5_000);
|
|
215
|
+
const query = new URLSearchParams({ appId });
|
|
216
|
+
if (installationId) query.set('installationId', installationId);
|
|
217
|
+
const response = await fetch(`${convexSiteUrl}/dogfood/access?${query.toString()}`, {
|
|
218
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
219
|
+
signal: controller.signal,
|
|
220
|
+
});
|
|
221
|
+
clearTimeout(timeout);
|
|
222
|
+
if (!response.ok) return { authenticated: false, ownerAuthorized: false, installationAuthorized: false };
|
|
223
|
+
const result = await response.json();
|
|
224
|
+
return {
|
|
225
|
+
authenticated: result?.authenticated === true,
|
|
226
|
+
ownerAuthorized: result?.ownerAuthorized === true,
|
|
227
|
+
installationAuthorized: result?.installationAuthorized === true,
|
|
228
|
+
};
|
|
229
|
+
} catch {
|
|
230
|
+
return { authenticated: false, ownerAuthorized: false, installationAuthorized: false };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
205
234
|
// ─── Token validation ──────────────────────────────────────────────────
|
|
206
235
|
|
|
207
236
|
export async function validateToken(token: string): Promise<User | null> {
|
package/src/deviceDogfood.ts
CHANGED
|
@@ -15,6 +15,9 @@ export interface DeviceDogfoodOptions {
|
|
|
15
15
|
appId: string;
|
|
16
16
|
label?: string;
|
|
17
17
|
backendUrl?: string;
|
|
18
|
+
/** Full Yaver OAuth token. Required for enrollment; never persisted with the
|
|
19
|
+
* installation private key. The backend binds its user to this key. */
|
|
20
|
+
authToken?: string;
|
|
18
21
|
/** Advanced bare-RN integration. Expo apps use SecureStore automatically. */
|
|
19
22
|
secureStore?: SecureStoreLike;
|
|
20
23
|
}
|
|
@@ -79,8 +82,9 @@ function sessionMessage(appId: string, installationId: string, challenge: string
|
|
|
79
82
|
return new TextEncoder().encode(`yaver-dogfood-session-v1\n${appId}\n${installationId}\n${challenge}`);
|
|
80
83
|
}
|
|
81
84
|
|
|
82
|
-
/** Account-
|
|
83
|
-
* handle; possession is proven by the private key retained in
|
|
85
|
+
/** Account-bound third-party Dogfood enrollment. The installation ID is a
|
|
86
|
+
* public lookup handle; possession is proven by the private key retained in
|
|
87
|
+
* this app, while enrollment is bound to the signed-in Yaver account. */
|
|
84
88
|
export class YaverDeviceDogfood {
|
|
85
89
|
private readonly backendUrl: string;
|
|
86
90
|
private readonly store: SecureStoreLike;
|
|
@@ -118,9 +122,10 @@ export class YaverDeviceDogfood {
|
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
async enroll(platform = 'unknown'): Promise<{ status: DeviceDogfoodState; installationId: string }> {
|
|
125
|
+
if (!this.options.authToken) throw new Error('Sign in to Yaver before registering this device for Dogfood.');
|
|
121
126
|
const identity = await this.identity();
|
|
122
127
|
const started = await responseJSON(await fetch(`${this.backendUrl}/dogfood/enroll/start`, {
|
|
123
|
-
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
128
|
+
method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.options.authToken}` },
|
|
124
129
|
body: JSON.stringify({ appId: this.options.appId, installationId: identity.installationId, registrationSlot: identity.registrationSlot, publicKey: identity.publicKey, platform, label: this.options.label }),
|
|
125
130
|
}));
|
|
126
131
|
if (started.status === 'active') return { status: 'active', installationId: identity.installationId };
|
package/src/dogfoodPolicy.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
export interface DogfoodAccessSnapshot {
|
|
2
|
+
appId: string;
|
|
3
|
+
/** A Yaver session exists locally; not itself an authorization decision. */
|
|
4
|
+
yaverAuthenticated: boolean;
|
|
5
|
+
/** Backend-confirmed owner/maintainer of this exact appId. */
|
|
6
|
+
ownerAuthorized: boolean;
|
|
7
|
+
installationId?: string;
|
|
8
|
+
deviceState: 'unknown' | 'unregistered' | 'pending' | 'active' | 'cancelled' | 'revoked' | 'superseded';
|
|
9
|
+
/** True only for a backend-approved device key or an authenticated owner. */
|
|
10
|
+
authorized: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface DogfoodFlowSnapshot {
|
|
14
|
+
phase: 'idle' | 'denied' | 'auth-required' | 'machine-required' | 'opening' | 'error';
|
|
15
|
+
appId?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
1
19
|
export interface SDKDogfoodConfig {
|
|
2
20
|
/** Explicit opt-in. Omitted/false keeps the normal Feedback SDK. */
|
|
3
21
|
enabled?: boolean;
|
|
@@ -5,7 +23,7 @@ export interface SDKDogfoodConfig {
|
|
|
5
23
|
accountIds?: string[];
|
|
6
24
|
/** The third-party app's currently authenticated account ID. */
|
|
7
25
|
currentAccountId?: string;
|
|
8
|
-
/** Account-
|
|
26
|
+
/** Account-bound installation identity resolved by YaverDeviceDogfood. */
|
|
9
27
|
appId?: string;
|
|
10
28
|
installationId?: string;
|
|
11
29
|
installationStatus?: 'pending' | 'active' | 'cancelled' | 'revoked' | 'superseded';
|
|
@@ -13,6 +31,19 @@ export interface SDKDogfoodConfig {
|
|
|
13
31
|
onExit?: () => void | Promise<void>;
|
|
14
32
|
/** Optional app label shown beside Dogfood mode. */
|
|
15
33
|
label?: string;
|
|
34
|
+
/** Project/framework hints used by the zero-orchestration onboarding UI. */
|
|
35
|
+
projectName?: string;
|
|
36
|
+
framework?: string;
|
|
37
|
+
/** Advanced override for staging/self-hosted enrollment. */
|
|
38
|
+
backendUrl?: string;
|
|
39
|
+
/** Presentation-only ACL hook, e.g. `() => user.isAdmin`. Server-side OAuth
|
|
40
|
+
* or device-signature verification remains the authority boundary. */
|
|
41
|
+
canShow?: (access: DogfoodAccessSnapshot) => boolean | Promise<boolean>;
|
|
42
|
+
/** Optional callback alternative to onDogfoodFlowState(). */
|
|
43
|
+
onStateChange?: (state: DogfoodFlowSnapshot) => void;
|
|
44
|
+
/** ACL-backed Home Screen/App Shortcut. Dynamic and absent until Yaver
|
|
45
|
+
* owner auth or this installation's approved key authorizes it. */
|
|
46
|
+
appShortcut?: boolean | { label?: string };
|
|
16
47
|
}
|
|
17
48
|
|
|
18
49
|
export interface SDKDogfoodStatus {
|
package/src/index.ts
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
export { YaverFeedback } from './YaverFeedback';
|
|
32
|
-
export type { DogfoodOnboardingOptions } from './YaverFeedback';
|
|
32
|
+
export type { DogfoodOnboardingOptions, DogfoodFlowPhase, DogfoodFlowState } from './YaverFeedback';
|
|
33
33
|
export { captureStoreScreenshots } from './storeShots';
|
|
34
34
|
export type {
|
|
35
35
|
CaptureStoreScreenshotsOptions,
|
|
@@ -81,7 +81,7 @@ export { FloatingButton } from './FloatingButton';
|
|
|
81
81
|
export { YaverModeBadge, hideYaverModeBadge, showYaverModeBadge, isYaverModeBadgeHidden } from './YaverModeBadge';
|
|
82
82
|
export type { YaverModeBadgeProps } from './YaverModeBadge';
|
|
83
83
|
export { resolveSDKDogfood } from './dogfoodPolicy';
|
|
84
|
-
export type { SDKDogfoodConfig, SDKDogfoodStatus } from './dogfoodPolicy';
|
|
84
|
+
export type { DogfoodAccessSnapshot, DogfoodFlowSnapshot, SDKDogfoodConfig, SDKDogfoodStatus } from './dogfoodPolicy';
|
|
85
85
|
export { YaverDeviceDogfood } from './deviceDogfood';
|
|
86
86
|
export type { DeviceDogfoodOptions, DeviceDogfoodSession, DeviceDogfoodState } from './deviceDogfood';
|
|
87
87
|
export {
|
|
@@ -114,6 +114,8 @@ export {
|
|
|
114
114
|
getQuickIconDisabled,
|
|
115
115
|
setQuickIconDisabled,
|
|
116
116
|
clearQuickIconDisabled,
|
|
117
|
+
getPreferredDogfoodLane,
|
|
118
|
+
setPreferredDogfoodLane,
|
|
117
119
|
} from './preferences';
|
|
118
120
|
export {
|
|
119
121
|
configureAuthEndpoints,
|
|
@@ -125,6 +127,7 @@ export {
|
|
|
125
127
|
getUser,
|
|
126
128
|
saveUser,
|
|
127
129
|
getSelectedDeviceId,
|
|
130
|
+
getDogfoodAccountAccess,
|
|
128
131
|
saveSelectedDeviceId,
|
|
129
132
|
clearSelectedDeviceId,
|
|
130
133
|
validateToken,
|
package/src/preferences.ts
CHANGED
|
@@ -159,6 +159,7 @@ export async function clearQuickIconColorPreset(): Promise<void> {
|
|
|
159
159
|
|
|
160
160
|
const PREFERRED_RUNNER_KEY = 'yaver_feedback_preferred_runner';
|
|
161
161
|
const PREFERRED_MODEL_KEY = 'yaver_feedback_preferred_model';
|
|
162
|
+
const PREFERRED_DOGFOOD_LANE_PREFIX = 'yaver_feedback_dogfood_lane_';
|
|
162
163
|
|
|
163
164
|
export async function getPreferredRunner(): Promise<string | null> {
|
|
164
165
|
if (!AsyncStorage) return null;
|
|
@@ -205,3 +206,22 @@ export async function setPreferredModel(model: string | null): Promise<void> {
|
|
|
205
206
|
/* best-effort */
|
|
206
207
|
}
|
|
207
208
|
}
|
|
209
|
+
|
|
210
|
+
export async function getPreferredDogfoodLane(appId: string): Promise<'browser' | 'hermes' | 'webrtc' | null> {
|
|
211
|
+
if (!AsyncStorage || !appId) return null;
|
|
212
|
+
try {
|
|
213
|
+
const value = await AsyncStorage.getItem(`${PREFERRED_DOGFOOD_LANE_PREFIX}${appId}`);
|
|
214
|
+
return value === 'browser' || value === 'hermes' || value === 'webrtc' ? value : null;
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export async function setPreferredDogfoodLane(appId: string, lane: 'browser' | 'hermes' | 'webrtc'): Promise<void> {
|
|
221
|
+
if (!AsyncStorage || !appId) return;
|
|
222
|
+
try {
|
|
223
|
+
await AsyncStorage.setItem(`${PREFERRED_DOGFOOD_LANE_PREFIX}${appId}`, lane);
|
|
224
|
+
} catch {
|
|
225
|
+
/* best-effort */
|
|
226
|
+
}
|
|
227
|
+
}
|