react-native-usesense 1.0.0 → 2.1.0
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 +267 -373
- package/android/build.gradle +7 -2
- package/android/src/main/java/com/usesense/reactnative/UseSenseFlowsModule.kt +72 -0
- package/android/src/main/java/com/usesense/reactnative/UseSenseModule.kt +82 -1
- package/android/src/main/java/com/usesense/reactnative/UseSensePackage.kt +6 -1
- package/ios/UseSenseFlowsModule.m +20 -0
- package/ios/UseSenseFlowsModule.swift +111 -0
- package/ios/UseSenseModule.m +5 -0
- package/ios/UseSenseModule.swift +277 -88
- package/ios/V4BridgeDelegate.swift +62 -0
- package/lib/commonjs/flows.js +105 -0
- package/lib/commonjs/flows.js.map +1 -0
- package/lib/commonjs/index.js +171 -98
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/flows.js +99 -0
- package/lib/module/flows.js.map +1 -0
- package/lib/module/index.js +160 -97
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/flows.d.ts +72 -0
- package/lib/typescript/src/flows.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +210 -138
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +3 -1
- package/react-native-usesense.podspec +9 -3
- package/src/flows.ts +159 -0
- package/src/index.ts +313 -159
package/src/index.ts
CHANGED
|
@@ -1,251 +1,405 @@
|
|
|
1
|
-
import { NativeModules, NativeEventEmitter } from 'react-native';
|
|
1
|
+
import { NativeModules, NativeEventEmitter, Platform } from 'react-native';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const LINKING_ERROR =
|
|
4
|
+
`react-native-usesense: the native module could not be found.\n\n` +
|
|
5
|
+
Platform.select({
|
|
6
|
+
ios: `iOS: run \`cd ios && pod install\` and rebuild the app.`,
|
|
7
|
+
android: `Android: rebuild the app after installing the package.`,
|
|
8
|
+
default: '',
|
|
9
|
+
}) +
|
|
10
|
+
`\n\nIf you're using Expo, the plugin must be consumed through a development build; it does not support Expo Go because it wraps a native camera SDK.`;
|
|
11
|
+
|
|
12
|
+
const UseSenseModule = NativeModules.UseSenseModule;
|
|
4
13
|
|
|
5
14
|
if (!UseSenseModule) {
|
|
6
|
-
throw new Error(
|
|
7
|
-
'react-native-usesense: NativeModule not found. Make sure you have linked the library correctly.\n' +
|
|
8
|
-
'iOS: Run `cd ios && pod install`\n' +
|
|
9
|
-
'Android: Rebuild the app after installing the package.',
|
|
10
|
-
);
|
|
15
|
+
throw new Error(LINKING_ERROR);
|
|
11
16
|
}
|
|
12
17
|
|
|
13
|
-
// ─── Types
|
|
18
|
+
// ─── Types ───────────────────────────────────────────────────────────────
|
|
14
19
|
|
|
15
|
-
/**
|
|
16
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Target environment for the UseSense API. `auto` asks the native SDK
|
|
22
|
+
* to detect from the API key prefix (`sk_sandbox_*` → sandbox,
|
|
23
|
+
* `sk_prod_*` → production).
|
|
24
|
+
*/
|
|
25
|
+
export type UseSenseEnvironment = 'sandbox' | 'production' | 'auto';
|
|
17
26
|
|
|
18
|
-
/**
|
|
27
|
+
/** Type of verification session. */
|
|
19
28
|
export type SessionType = 'enrollment' | 'authentication';
|
|
20
29
|
|
|
21
|
-
/**
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Decision returned by the native SDK.
|
|
32
|
+
*
|
|
33
|
+
* These values come straight from the server's verdict and are
|
|
34
|
+
* exposed to the integrator for UI feedback only. The definitive
|
|
35
|
+
* verdict arrives at your backend via an HMAC-signed webhook; never
|
|
36
|
+
* trust the client-side decision for access-control.
|
|
37
|
+
*/
|
|
38
|
+
export type Decision = 'APPROVE' | 'REJECT' | 'MANUAL_REVIEW';
|
|
29
39
|
|
|
30
40
|
/**
|
|
31
|
-
*
|
|
41
|
+
* Branding overrides applied to the native camera UI.
|
|
32
42
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
* - `logoUrl`, `primaryColor`, `buttonRadius`, `fontFamily` are
|
|
44
|
+
* honoured on both iOS and Android.
|
|
45
|
+
* - `displayName`, `redirectUrl` are Android-only fields. They're
|
|
46
|
+
* accepted on iOS for cross-platform API parity but ignored.
|
|
47
|
+
*/
|
|
48
|
+
export interface BrandingConfig {
|
|
49
|
+
logoUrl?: string;
|
|
50
|
+
primaryColor?: string;
|
|
51
|
+
buttonRadius?: number;
|
|
52
|
+
fontFamily?: string;
|
|
53
|
+
displayName?: string;
|
|
54
|
+
redirectUrl?: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Configuration for `UseSense.initialize`. The minimum required
|
|
59
|
+
* field is the API key; every other field has a sensible default.
|
|
40
60
|
*/
|
|
41
61
|
export interface UseSenseConfig {
|
|
42
|
-
/** API key from
|
|
62
|
+
/** API key from watchtower.usesense.ai. */
|
|
43
63
|
apiKey: string;
|
|
44
|
-
/** Target environment. Defaults to `
|
|
64
|
+
/** Target environment. Defaults to `auto` (detected from key prefix). */
|
|
45
65
|
environment?: UseSenseEnvironment;
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Override the API endpoint. Defaults to `https://api.usesense.ai/v1`.
|
|
68
|
+
* Only set this if you're pointing at a staging or self-hosted
|
|
69
|
+
* proxy. Maps to `apiEndpoint` on iOS and `baseUrl` on Android
|
|
70
|
+
* under the hood.
|
|
71
|
+
*/
|
|
72
|
+
apiEndpoint?: string;
|
|
73
|
+
/** SDK-level branding overrides. */
|
|
74
|
+
branding?: BrandingConfig;
|
|
75
|
+
/**
|
|
76
|
+
* Opt in to the on-device antispoof classifier (v4.2). When enabled the
|
|
77
|
+
* native layer runs the bundled CelebA-Spoof model during capture and
|
|
78
|
+
* attaches per-frame spoof probabilities to the uploaded metadata. When
|
|
79
|
+
* disabled (default), the watchtower backend runs the classifier server-side.
|
|
80
|
+
* See `docs/sdk-specs/antispoof-classifier-sdk-spec.md` in the watchtower
|
|
81
|
+
* repo for the rollout plan and crosscheck policy.
|
|
82
|
+
*/
|
|
83
|
+
antispoofOnDeviceEnabled?: boolean;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Opt the session into the LiveSense v4 capture flow. When true the native
|
|
87
|
+
* layer:
|
|
88
|
+
* - sends `x-usesense-sdk-version: v4` on session creation
|
|
89
|
+
* - inserts a constitutive zoom-motion phase between baseline and the
|
|
90
|
+
* active challenge (additive, not a replacement)
|
|
91
|
+
* - tags every captured frame with its capture phase
|
|
92
|
+
* The org must also have `livesense_v4_enabled` set in its features map
|
|
93
|
+
* server-side.
|
|
94
|
+
*/
|
|
95
|
+
liveSenseV4Enabled?: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Request to start a verification session.
|
|
100
|
+
*/
|
|
101
|
+
export interface VerificationRequest {
|
|
102
|
+
sessionType: SessionType;
|
|
103
|
+
/** Your internal user identifier (optional, stored in session metadata). */
|
|
104
|
+
externalUserId?: string;
|
|
105
|
+
/**
|
|
106
|
+
* Required for `authentication` sessions. The previously-enrolled
|
|
107
|
+
* identity you're verifying against.
|
|
108
|
+
*/
|
|
51
109
|
identityId?: string;
|
|
52
|
-
/** Challenge difficulty policy. Defaults to `'standard'`. */
|
|
53
|
-
challengePolicy?: ChallengePolicy;
|
|
54
|
-
/** Enable audio capture for voice deepfake detection. Defaults to `false`. */
|
|
55
|
-
enableAudio?: boolean;
|
|
56
|
-
/** Maximum session duration in milliseconds. Defaults to `60000`. */
|
|
57
|
-
timeout?: number;
|
|
58
110
|
/** Custom key-value pairs attached to the session. */
|
|
59
|
-
metadata?: Record<string, string>;
|
|
111
|
+
metadata?: Record<string, string | number | boolean>;
|
|
60
112
|
}
|
|
61
113
|
|
|
62
114
|
/**
|
|
63
|
-
*
|
|
115
|
+
* Redacted decision object returned when a session completes.
|
|
64
116
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
117
|
+
* This is the ONLY shape the plugin exposes to React Native. Pillar
|
|
118
|
+
* scores (channel trust, liveness, matchsense risk) are intentionally
|
|
119
|
+
* not included — the native SDKs strip those fields before returning
|
|
120
|
+
* across the bridge to prevent reverse-engineering of the server-side
|
|
121
|
+
* scoring logic. If you need full scoring details, consume the signed
|
|
122
|
+
* webhook delivered to your backend.
|
|
67
123
|
*/
|
|
68
124
|
export interface UseSenseResult {
|
|
69
|
-
/** Unique session identifier. */
|
|
70
125
|
sessionId: string;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
/** Reasons contributing to the decision. */
|
|
82
|
-
reasons: string[];
|
|
83
|
-
/** Rule that triggered the decision, if any. */
|
|
84
|
-
ruleTriggered?: string;
|
|
85
|
-
/** Recommended action for the integrator. */
|
|
86
|
-
recommendedAction?: string;
|
|
87
|
-
/** Cryptographic signature for result verification. */
|
|
88
|
-
sessionSignature: string;
|
|
126
|
+
sessionType: string | null;
|
|
127
|
+
identityId: string | null;
|
|
128
|
+
decision: Decision;
|
|
129
|
+
timestamp: string;
|
|
130
|
+
/** Convenience: `decision === 'APPROVE'`. */
|
|
131
|
+
isApproved: boolean;
|
|
132
|
+
/** Convenience: `decision === 'REJECT'`. */
|
|
133
|
+
isRejected: boolean;
|
|
134
|
+
/** Convenience: `decision === 'MANUAL_REVIEW'`. */
|
|
135
|
+
isPendingReview: boolean;
|
|
89
136
|
}
|
|
90
137
|
|
|
91
138
|
/**
|
|
92
|
-
* Error
|
|
139
|
+
* Error thrown when a verification session fails.
|
|
140
|
+
*
|
|
141
|
+
* Matches the native SDKs' error shape: machine-readable code,
|
|
142
|
+
* human-readable message, retry hint.
|
|
93
143
|
*/
|
|
94
144
|
export interface UseSenseError {
|
|
95
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Machine-readable error code. One of
|
|
147
|
+
* `CAMERA_UNAVAILABLE`, `CAMERA_PERMISSION_DENIED`,
|
|
148
|
+
* `MIC_PERMISSION_DENIED`, `NETWORK_ERROR`, `NETWORK_TIMEOUT`,
|
|
149
|
+
* `SESSION_EXPIRED`, `UNAUTHORIZED`, `INVALID_TOKEN`,
|
|
150
|
+
* `SESSION_NOT_FOUND`, `IDENTITY_NOT_FOUND`, `INVALID_REQUEST`,
|
|
151
|
+
* `INVALID_CONFIG`, `QUOTA_EXCEEDED`, `USER_CANCELLED`,
|
|
152
|
+
* `session_cancelled`, `sdk_not_initialized`, `no_view_controller`,
|
|
153
|
+
* `CAPTURE_FAILED`, `ENCODING_FAILED`, `UPLOAD_FAILED`,
|
|
154
|
+
* `FACE_NOT_DETECTED`, `LOW_LIGHT`, `TIMEOUT`, `SERVER_ERROR`,
|
|
155
|
+
* `SERVICE_UNAVAILABLE`, `TOKEN_EXPIRED`, `TOKEN_ALREADY_USED`,
|
|
156
|
+
* `INSUFFICIENT_CREDITS`, `NONCE_MISMATCH`, `UNKNOWN_ERROR`.
|
|
157
|
+
*/
|
|
96
158
|
code: string;
|
|
97
159
|
/** Human-readable error message. */
|
|
98
160
|
message: string;
|
|
99
|
-
/**
|
|
100
|
-
|
|
161
|
+
/** Whether the operation can be retried. */
|
|
162
|
+
isRetryable?: boolean;
|
|
163
|
+
/** Server-specific error code, if any. */
|
|
164
|
+
serverCode?: string;
|
|
101
165
|
}
|
|
102
166
|
|
|
103
167
|
/**
|
|
104
|
-
*
|
|
168
|
+
* Lifecycle event types emitted by the native SDK during a
|
|
169
|
+
* verification session.
|
|
105
170
|
*/
|
|
106
|
-
export type
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
171
|
+
export type UseSenseEventType =
|
|
172
|
+
| 'SESSION_CREATED'
|
|
173
|
+
| 'PERMISSIONS_REQUESTED'
|
|
174
|
+
| 'PERMISSIONS_GRANTED'
|
|
175
|
+
| 'PERMISSIONS_DENIED'
|
|
176
|
+
| 'CAPTURE_STARTED'
|
|
177
|
+
| 'FRAME_CAPTURED'
|
|
178
|
+
| 'CAPTURE_COMPLETED'
|
|
179
|
+
| 'AUDIO_RECORD_STARTED'
|
|
180
|
+
| 'AUDIO_RECORD_COMPLETED'
|
|
181
|
+
| 'CHALLENGE_STARTED'
|
|
182
|
+
| 'CHALLENGE_COMPLETED'
|
|
183
|
+
| 'UPLOAD_STARTED'
|
|
184
|
+
| 'UPLOAD_PROGRESS'
|
|
185
|
+
| 'UPLOAD_COMPLETED'
|
|
186
|
+
| 'COMPLETE_STARTED'
|
|
187
|
+
| 'DECISION_RECEIVED'
|
|
188
|
+
| 'IMAGE_QUALITY_CHECK'
|
|
189
|
+
| 'ERROR'
|
|
190
|
+
| 'UNKNOWN';
|
|
121
191
|
|
|
122
|
-
/**
|
|
123
|
-
export interface
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
192
|
+
/** Single event emitted during a session. */
|
|
193
|
+
export interface UseSenseEvent {
|
|
194
|
+
type: UseSenseEventType;
|
|
195
|
+
/** Unix epoch milliseconds. */
|
|
196
|
+
timestamp: number;
|
|
197
|
+
/** Event-specific payload. Opaque strings; never contains scoring data. */
|
|
198
|
+
data?: Record<string, string>;
|
|
128
199
|
}
|
|
129
200
|
|
|
130
|
-
/**
|
|
201
|
+
/** Handle returned by `UseSense.addListener`. */
|
|
131
202
|
export interface UseSenseSubscription {
|
|
132
|
-
|
|
133
|
-
remove: () => void;
|
|
203
|
+
remove(): void;
|
|
134
204
|
}
|
|
135
205
|
|
|
136
|
-
// ─── Implementation
|
|
206
|
+
// ─── Implementation ──────────────────────────────────────────────────────
|
|
137
207
|
|
|
138
208
|
const eventEmitter = new NativeEventEmitter(UseSenseModule);
|
|
139
209
|
|
|
140
210
|
/**
|
|
141
211
|
* UseSense React Native plugin.
|
|
142
212
|
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
213
|
+
* Human presence verification wrapping the native iOS and Android
|
|
214
|
+
* UseSense SDKs. Every session runs inside a full-screen native
|
|
215
|
+
* camera activity; the plugin is a thin bridge that forwards
|
|
216
|
+
* initialization, verification requests, and lifecycle events to
|
|
217
|
+
* the platform SDK and returns a redacted decision object.
|
|
145
218
|
*
|
|
146
219
|
* @example
|
|
147
220
|
* ```ts
|
|
148
221
|
* import { UseSense } from 'react-native-usesense';
|
|
149
222
|
*
|
|
150
|
-
* await UseSense.initialize({
|
|
151
|
-
*
|
|
152
|
-
*
|
|
223
|
+
* await UseSense.initialize({
|
|
224
|
+
* apiKey: 'sk_sandbox_...',
|
|
225
|
+
* environment: 'sandbox',
|
|
226
|
+
* });
|
|
227
|
+
*
|
|
228
|
+
* const unsubscribe = UseSense.addListener((event) => {
|
|
229
|
+
* console.log(event.type, event.data);
|
|
230
|
+
* });
|
|
231
|
+
*
|
|
232
|
+
* try {
|
|
233
|
+
* const result = await UseSense.startVerification({
|
|
234
|
+
* sessionType: 'enrollment',
|
|
235
|
+
* });
|
|
236
|
+
* console.log(result.decision); // 'APPROVE' | 'REJECT' | 'MANUAL_REVIEW'
|
|
237
|
+
* } finally {
|
|
238
|
+
* unsubscribe.remove();
|
|
239
|
+
* }
|
|
153
240
|
* ```
|
|
154
241
|
*/
|
|
155
242
|
export class UseSense {
|
|
156
243
|
/**
|
|
157
|
-
* Initialize the UseSense plugin. Must be called
|
|
244
|
+
* Initialize the UseSense plugin. Must be called before any other
|
|
245
|
+
* method. Calling again with a different API key or environment
|
|
246
|
+
* replaces the previous configuration.
|
|
158
247
|
*
|
|
159
|
-
* @
|
|
160
|
-
*
|
|
248
|
+
* @throws {UseSenseError} If the API key is empty or initialization
|
|
249
|
+
* fails on the native side.
|
|
161
250
|
*/
|
|
162
251
|
static async initialize(config: UseSenseConfig): Promise<void> {
|
|
163
252
|
if (!config.apiKey) {
|
|
164
|
-
|
|
253
|
+
const err: UseSenseError = {
|
|
254
|
+
code: 'invalid_config',
|
|
255
|
+
message: 'apiKey is required',
|
|
256
|
+
isRetryable: false,
|
|
257
|
+
};
|
|
258
|
+
throw err;
|
|
165
259
|
}
|
|
166
260
|
return UseSenseModule.initialize({
|
|
167
261
|
apiKey: config.apiKey,
|
|
168
|
-
environment: config.environment ?? '
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
enableAudio: config.enableAudio ?? false,
|
|
174
|
-
timeout: config.timeout ?? 60000,
|
|
175
|
-
metadata: config.metadata,
|
|
262
|
+
environment: config.environment ?? 'auto',
|
|
263
|
+
apiEndpoint: config.apiEndpoint,
|
|
264
|
+
branding: config.branding,
|
|
265
|
+
antispoofOnDeviceEnabled: config.antispoofOnDeviceEnabled ?? false,
|
|
266
|
+
liveSenseV4Enabled: config.liveSenseV4Enabled ?? false,
|
|
176
267
|
});
|
|
177
268
|
}
|
|
178
269
|
|
|
179
270
|
/**
|
|
180
|
-
* Start a verification session. Presents a full-screen native
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
* @param options - Optional overrides for session type and identity ID.
|
|
186
|
-
* @returns The verification result (for UI feedback only).
|
|
187
|
-
* @throws {UseSenseError} On failure or cancellation.
|
|
271
|
+
* Start a verification session. Presents a full-screen native
|
|
272
|
+
* camera activity. The returned promise resolves when the session
|
|
273
|
+
* completes or rejects with a `UseSenseError` (including the
|
|
274
|
+
* `USER_CANCELLED` / `session_cancelled` cases when the user
|
|
275
|
+
* dismisses the camera screen).
|
|
188
276
|
*/
|
|
189
|
-
static async
|
|
190
|
-
|
|
277
|
+
static async startVerification(
|
|
278
|
+
request: VerificationRequest,
|
|
279
|
+
): Promise<UseSenseResult> {
|
|
280
|
+
return UseSenseModule.startVerification({
|
|
281
|
+
sessionType: request.sessionType,
|
|
282
|
+
externalUserId: request.externalUserId,
|
|
283
|
+
identityId: request.identityId,
|
|
284
|
+
metadata: request.metadata,
|
|
285
|
+
});
|
|
191
286
|
}
|
|
192
287
|
|
|
193
288
|
/**
|
|
194
|
-
*
|
|
289
|
+
* Subscribe to real-time lifecycle events emitted by the native
|
|
290
|
+
* SDK during a verification session. Call this **before**
|
|
291
|
+
* `startVerification` so the subscription captures the earliest
|
|
292
|
+
* events (`SESSION_CREATED`, `PERMISSIONS_REQUESTED`).
|
|
195
293
|
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
294
|
+
* Returns a subscription handle; call `.remove()` to stop
|
|
295
|
+
* receiving events.
|
|
198
296
|
*/
|
|
199
|
-
static
|
|
200
|
-
|
|
297
|
+
static addListener(
|
|
298
|
+
callback: (event: UseSenseEvent) => void,
|
|
299
|
+
): UseSenseSubscription {
|
|
300
|
+
const subscription = eventEmitter.addListener(
|
|
301
|
+
'UseSenseEvent',
|
|
302
|
+
(raw: UseSenseEvent) => {
|
|
303
|
+
callback(raw);
|
|
304
|
+
},
|
|
305
|
+
);
|
|
306
|
+
return {
|
|
307
|
+
remove: () => subscription.remove(),
|
|
308
|
+
};
|
|
201
309
|
}
|
|
202
310
|
|
|
203
311
|
/**
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
312
|
+
* Clear all event listeners and release native resources. Does not
|
|
313
|
+
* need to be called explicitly during normal usage; the plugin
|
|
314
|
+
* cleans up on process teardown. Useful when you want to re-
|
|
315
|
+
* initialize against a different API key or during test teardown.
|
|
208
316
|
*/
|
|
209
|
-
static async
|
|
210
|
-
return UseSenseModule.
|
|
317
|
+
static async reset(): Promise<void> {
|
|
318
|
+
return UseSenseModule.reset();
|
|
211
319
|
}
|
|
212
320
|
|
|
213
321
|
/**
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* @param callback - Function invoked for each event.
|
|
219
|
-
* @returns A subscription handle with a `remove()` method.
|
|
220
|
-
*
|
|
221
|
-
* @example
|
|
222
|
-
* ```ts
|
|
223
|
-
* const listener = UseSense.addListener((event) => {
|
|
224
|
-
* console.log(event.type);
|
|
225
|
-
* });
|
|
226
|
-
* // Later:
|
|
227
|
-
* listener.remove();
|
|
228
|
-
* ```
|
|
322
|
+
* Whether the native plugin has an active `UseSense` client. Set
|
|
323
|
+
* to `true` after a successful `initialize` and `false` after
|
|
324
|
+
* `reset`. Useful for feature-flagging UI that should only show
|
|
325
|
+
* when the SDK is ready.
|
|
229
326
|
*/
|
|
230
|
-
static
|
|
231
|
-
UseSenseModule.
|
|
232
|
-
const subscription = eventEmitter.addListener('UseSenseEvent', callback);
|
|
233
|
-
return {
|
|
234
|
-
remove: () => {
|
|
235
|
-
subscription.remove();
|
|
236
|
-
UseSenseModule.unsubscribeFromEvents();
|
|
237
|
-
},
|
|
238
|
-
};
|
|
327
|
+
static async isInitialized(): Promise<boolean> {
|
|
328
|
+
return UseSenseModule.isInitialized();
|
|
239
329
|
}
|
|
240
330
|
|
|
331
|
+
// ─── LiveSense v4 ─────────────────────────────────────────────────────
|
|
332
|
+
// Phase 1 ticket R-1.
|
|
333
|
+
//
|
|
334
|
+
// v4 runs the perspective-distortion zoom-motion capture. Passthrough
|
|
335
|
+
// to the native SDK's startV4Session (iOS) / startV4Verification
|
|
336
|
+
// (Android). The returned promise resolves with the opaque verdict;
|
|
337
|
+
// sub-scores and pillar verdicts are never exposed.
|
|
338
|
+
|
|
241
339
|
/**
|
|
242
|
-
*
|
|
340
|
+
* Start a LiveSense v4 session.
|
|
243
341
|
*
|
|
244
|
-
*
|
|
342
|
+
* The session must already have been created on your backend and the
|
|
343
|
+
* session_token + nonce forwarded to the client. The native SDK drives
|
|
344
|
+
* the camera, signs the frame hash chain with the platform-attested
|
|
345
|
+
* key, uploads, and returns the opaque verdict.
|
|
245
346
|
*/
|
|
246
|
-
static
|
|
247
|
-
|
|
347
|
+
static async startV4Verification(
|
|
348
|
+
request: V4VerificationRequest,
|
|
349
|
+
): Promise<V4Verdict> {
|
|
350
|
+
return UseSenseModule.startV4Verification({
|
|
351
|
+
sessionId: request.sessionId,
|
|
352
|
+
sessionToken: request.sessionToken,
|
|
353
|
+
nonce: request.nonce,
|
|
354
|
+
apiBaseUrl: request.apiBaseUrl,
|
|
355
|
+
environment: request.environment ?? 'production',
|
|
356
|
+
displayName: request.displayName,
|
|
357
|
+
brandPrimaryColor: request.brandPrimaryColor,
|
|
358
|
+
});
|
|
248
359
|
}
|
|
249
360
|
}
|
|
250
361
|
|
|
362
|
+
// ─── LiveSense v4 types (R-1) ─────────────────────────────────────────────
|
|
363
|
+
|
|
364
|
+
export interface V4VerificationRequest {
|
|
365
|
+
sessionId: string;
|
|
366
|
+
sessionToken: string;
|
|
367
|
+
nonce: string;
|
|
368
|
+
apiBaseUrl: string;
|
|
369
|
+
environment?: UseSenseEnvironment;
|
|
370
|
+
displayName?: string;
|
|
371
|
+
brandPrimaryColor?: string;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
export type V4Decision = 'pass' | 'fail' | 'review';
|
|
375
|
+
export type V4Confidence = 'high' | 'medium' | 'low';
|
|
376
|
+
export type V4AssuranceLevel = 'mobile_hardware' | 'web_attested' | 'web_unattested';
|
|
377
|
+
export type V4CaptureChannel = 'ios' | 'android' | 'rn' | 'flutter' | 'web';
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Opaque verdict from POST /v1/sessions/:id/result. Matches the server
|
|
381
|
+
* contract exactly; no sub-scores leak through.
|
|
382
|
+
*/
|
|
383
|
+
export interface V4Verdict {
|
|
384
|
+
session_id: string;
|
|
385
|
+
verdict: V4Decision;
|
|
386
|
+
confidence: V4Confidence;
|
|
387
|
+
assurance_level_achieved: V4AssuranceLevel;
|
|
388
|
+
capture_channel: V4CaptureChannel;
|
|
389
|
+
match_sense_embedding_id: string | null;
|
|
390
|
+
timestamp: string;
|
|
391
|
+
}
|
|
392
|
+
|
|
251
393
|
export default UseSense;
|
|
394
|
+
|
|
395
|
+
// Slice 5d: Flows. A parallel surface to Sessions — host apps choose one
|
|
396
|
+
// per call. See guides/flows/sessions-vs-flows in the API docs.
|
|
397
|
+
export {
|
|
398
|
+
UseSenseFlows,
|
|
399
|
+
FlowError,
|
|
400
|
+
type FlowErrorCode,
|
|
401
|
+
type FlowOutcome,
|
|
402
|
+
type FlowRunResult,
|
|
403
|
+
type FlowRunState,
|
|
404
|
+
type RunFlowOptions,
|
|
405
|
+
} from './flows';
|