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.
@@ -1,196 +1,268 @@
1
- /** Environment for the UseSense API. */
2
- export type UseSenseEnvironment = 'production' | 'sandbox';
3
- /** Session type for verification. */
1
+ /**
2
+ * Target environment for the UseSense API. `auto` asks the native SDK
3
+ * to detect from the API key prefix (`sk_sandbox_*` → sandbox,
4
+ * `sk_prod_*` → production).
5
+ */
6
+ export type UseSenseEnvironment = 'sandbox' | 'production' | 'auto';
7
+ /** Type of verification session. */
4
8
  export type SessionType = 'enrollment' | 'authentication';
5
- /** Challenge policy controlling verification difficulty. */
6
- export type ChallengePolicy = 'standard' | 'enhanced' | 'adaptive';
7
- /** Decision returned by the verification engine. */
8
- export type UseSenseDecision = 'approved' | 'rejected' | 'manual_review';
9
- /** Processing stage during server-side analysis. */
10
- export type ProcessingStage = 'deepsense' | 'livesense' | 'matchsense' | 'fusion';
11
9
  /**
12
- * Configuration for initializing the UseSense plugin.
10
+ * Decision returned by the native SDK.
13
11
  *
14
- * @example
15
- * ```ts
16
- * await UseSense.initialize({
17
- * apiKey: 'your_api_key',
18
- * environment: 'sandbox',
19
- * });
20
- * ```
12
+ * These values come straight from the server's verdict and are
13
+ * exposed to the integrator for UI feedback only. The definitive
14
+ * verdict arrives at your backend via an HMAC-signed webhook; never
15
+ * trust the client-side decision for access-control.
16
+ */
17
+ export type Decision = 'APPROVE' | 'REJECT' | 'MANUAL_REVIEW';
18
+ /**
19
+ * Branding overrides applied to the native camera UI.
20
+ *
21
+ * - `logoUrl`, `primaryColor`, `buttonRadius`, `fontFamily` are
22
+ * honoured on both iOS and Android.
23
+ * - `displayName`, `redirectUrl` are Android-only fields. They're
24
+ * accepted on iOS for cross-platform API parity but ignored.
25
+ */
26
+ export interface BrandingConfig {
27
+ logoUrl?: string;
28
+ primaryColor?: string;
29
+ buttonRadius?: number;
30
+ fontFamily?: string;
31
+ displayName?: string;
32
+ redirectUrl?: string;
33
+ }
34
+ /**
35
+ * Configuration for `UseSense.initialize`. The minimum required
36
+ * field is the API key; every other field has a sensible default.
21
37
  */
22
38
  export interface UseSenseConfig {
23
- /** API key from the UseSense dashboard. */
39
+ /** API key from watchtower.usesense.ai. */
24
40
  apiKey: string;
25
- /** Target environment. Defaults to `'sandbox'`. */
41
+ /** Target environment. Defaults to `auto` (detected from key prefix). */
26
42
  environment?: UseSenseEnvironment;
27
- /** Your organization ID (optional, inferred from API key). */
28
- organizationId?: string;
29
- /** Default session type. Defaults to `'enrollment'`. */
30
- sessionType?: SessionType;
31
- /** Identity ID for authentication sessions. */
43
+ /**
44
+ * Override the API endpoint. Defaults to `https://api.usesense.ai/v1`.
45
+ * Only set this if you're pointing at a staging or self-hosted
46
+ * proxy. Maps to `apiEndpoint` on iOS and `baseUrl` on Android
47
+ * under the hood.
48
+ */
49
+ apiEndpoint?: string;
50
+ /** SDK-level branding overrides. */
51
+ branding?: BrandingConfig;
52
+ /**
53
+ * Opt in to the on-device antispoof classifier (v4.2). When enabled the
54
+ * native layer runs the bundled CelebA-Spoof model during capture and
55
+ * attaches per-frame spoof probabilities to the uploaded metadata. When
56
+ * disabled (default), the watchtower backend runs the classifier server-side.
57
+ * See `docs/sdk-specs/antispoof-classifier-sdk-spec.md` in the watchtower
58
+ * repo for the rollout plan and crosscheck policy.
59
+ */
60
+ antispoofOnDeviceEnabled?: boolean;
61
+ /**
62
+ * Opt the session into the LiveSense v4 capture flow. When true the native
63
+ * layer:
64
+ * - sends `x-usesense-sdk-version: v4` on session creation
65
+ * - inserts a constitutive zoom-motion phase between baseline and the
66
+ * active challenge (additive, not a replacement)
67
+ * - tags every captured frame with its capture phase
68
+ * The org must also have `livesense_v4_enabled` set in its features map
69
+ * server-side.
70
+ */
71
+ liveSenseV4Enabled?: boolean;
72
+ }
73
+ /**
74
+ * Request to start a verification session.
75
+ */
76
+ export interface VerificationRequest {
77
+ sessionType: SessionType;
78
+ /** Your internal user identifier (optional, stored in session metadata). */
79
+ externalUserId?: string;
80
+ /**
81
+ * Required for `authentication` sessions. The previously-enrolled
82
+ * identity you're verifying against.
83
+ */
32
84
  identityId?: string;
33
- /** Challenge difficulty policy. Defaults to `'standard'`. */
34
- challengePolicy?: ChallengePolicy;
35
- /** Enable audio capture for voice deepfake detection. Defaults to `false`. */
36
- enableAudio?: boolean;
37
- /** Maximum session duration in milliseconds. Defaults to `60000`. */
38
- timeout?: number;
39
85
  /** Custom key-value pairs attached to the session. */
40
- metadata?: Record<string, string>;
86
+ metadata?: Record<string, string | number | boolean>;
41
87
  }
42
88
  /**
43
- * Result returned when a verification session completes.
89
+ * Redacted decision object returned when a session completes.
44
90
  *
45
- * **Important:** This result is for UI feedback only.
46
- * The definitive verdict arrives at your backend via HMAC-signed webhook.
91
+ * This is the ONLY shape the plugin exposes to React Native. Pillar
92
+ * scores (channel trust, liveness, matchsense risk) are intentionally
93
+ * not included — the native SDKs strip those fields before returning
94
+ * across the bridge to prevent reverse-engineering of the server-side
95
+ * scoring logic. If you need full scoring details, consume the signed
96
+ * webhook delivered to your backend.
47
97
  */
48
98
  export interface UseSenseResult {
49
- /** Unique session identifier. */
50
99
  sessionId: string;
51
- /** Verification decision. */
52
- decision: UseSenseDecision;
53
- /** DeepSense channel trust score (0-100). */
54
- channelTrustScore: number;
55
- /** LiveSense liveness score (0-100). */
56
- livenessScore: number;
57
- /** MatchSense risk score (0-100). Lower is better. */
58
- matchSenseRiskScore: number;
59
- /** Fused presence confidence score (0-100). */
60
- presenceConfidence: number;
61
- /** Reasons contributing to the decision. */
62
- reasons: string[];
63
- /** Rule that triggered the decision, if any. */
64
- ruleTriggered?: string;
65
- /** Recommended action for the integrator. */
66
- recommendedAction?: string;
67
- /** Cryptographic signature for result verification. */
68
- sessionSignature: string;
100
+ sessionType: string | null;
101
+ identityId: string | null;
102
+ decision: Decision;
103
+ timestamp: string;
104
+ /** Convenience: `decision === 'APPROVE'`. */
105
+ isApproved: boolean;
106
+ /** Convenience: `decision === 'REJECT'`. */
107
+ isRejected: boolean;
108
+ /** Convenience: `decision === 'MANUAL_REVIEW'`. */
109
+ isPendingReview: boolean;
69
110
  }
70
111
  /**
71
- * Error returned when a verification session fails.
112
+ * Error thrown when a verification session fails.
113
+ *
114
+ * Matches the native SDKs' error shape: machine-readable code,
115
+ * human-readable message, retry hint.
72
116
  */
73
117
  export interface UseSenseError {
74
- /** Machine-readable error code. */
118
+ /**
119
+ * Machine-readable error code. One of
120
+ * `CAMERA_UNAVAILABLE`, `CAMERA_PERMISSION_DENIED`,
121
+ * `MIC_PERMISSION_DENIED`, `NETWORK_ERROR`, `NETWORK_TIMEOUT`,
122
+ * `SESSION_EXPIRED`, `UNAUTHORIZED`, `INVALID_TOKEN`,
123
+ * `SESSION_NOT_FOUND`, `IDENTITY_NOT_FOUND`, `INVALID_REQUEST`,
124
+ * `INVALID_CONFIG`, `QUOTA_EXCEEDED`, `USER_CANCELLED`,
125
+ * `session_cancelled`, `sdk_not_initialized`, `no_view_controller`,
126
+ * `CAPTURE_FAILED`, `ENCODING_FAILED`, `UPLOAD_FAILED`,
127
+ * `FACE_NOT_DETECTED`, `LOW_LIGHT`, `TIMEOUT`, `SERVER_ERROR`,
128
+ * `SERVICE_UNAVAILABLE`, `TOKEN_EXPIRED`, `TOKEN_ALREADY_USED`,
129
+ * `INSUFFICIENT_CREDITS`, `NONCE_MISMATCH`, `UNKNOWN_ERROR`.
130
+ */
75
131
  code: string;
76
132
  /** Human-readable error message. */
77
133
  message: string;
78
- /** Additional error details. */
79
- details?: Record<string, any>;
134
+ /** Whether the operation can be retried. */
135
+ isRetryable?: boolean;
136
+ /** Server-specific error code, if any. */
137
+ serverCode?: string;
80
138
  }
81
139
  /**
82
- * Events emitted during the verification session lifecycle.
140
+ * Lifecycle event types emitted by the native SDK during a
141
+ * verification session.
83
142
  */
84
- export type UseSenseEvent = {
85
- type: 'session_started';
86
- sessionId: string;
87
- } | {
88
- type: 'challenge_presented';
89
- challengeType: string;
90
- } | {
91
- type: 'challenge_completed';
92
- challengeType: string;
93
- } | {
94
- type: 'processing';
95
- stage: ProcessingStage;
96
- } | {
97
- type: 'session_completed';
98
- result: UseSenseResult;
99
- } | {
100
- type: 'session_error';
101
- error: UseSenseError;
102
- };
103
- /** Options for starting a verification session. */
104
- export interface StartSessionOptions {
105
- /** Session type. Overrides the value set in {@link UseSenseConfig}. */
106
- sessionType?: SessionType;
107
- /** Identity ID for authentication sessions. */
108
- identityId?: string;
143
+ export type UseSenseEventType = 'SESSION_CREATED' | 'PERMISSIONS_REQUESTED' | 'PERMISSIONS_GRANTED' | 'PERMISSIONS_DENIED' | 'CAPTURE_STARTED' | 'FRAME_CAPTURED' | 'CAPTURE_COMPLETED' | 'AUDIO_RECORD_STARTED' | 'AUDIO_RECORD_COMPLETED' | 'CHALLENGE_STARTED' | 'CHALLENGE_COMPLETED' | 'UPLOAD_STARTED' | 'UPLOAD_PROGRESS' | 'UPLOAD_COMPLETED' | 'COMPLETE_STARTED' | 'DECISION_RECEIVED' | 'IMAGE_QUALITY_CHECK' | 'ERROR' | 'UNKNOWN';
144
+ /** Single event emitted during a session. */
145
+ export interface UseSenseEvent {
146
+ type: UseSenseEventType;
147
+ /** Unix epoch milliseconds. */
148
+ timestamp: number;
149
+ /** Event-specific payload. Opaque strings; never contains scoring data. */
150
+ data?: Record<string, string>;
109
151
  }
110
- /** Status of a verification session. */
111
- export interface SessionStatus {
112
- /** Current session status. */
113
- status: string;
114
- /** Result, if the session has completed. */
115
- result?: UseSenseResult;
116
- }
117
- /** Subscription handle returned by {@link UseSense.addListener}. */
152
+ /** Handle returned by `UseSense.addListener`. */
118
153
  export interface UseSenseSubscription {
119
- /** Remove the event listener. */
120
- remove: () => void;
154
+ remove(): void;
121
155
  }
122
156
  /**
123
157
  * UseSense React Native plugin.
124
158
  *
125
- * Provides human presence verification by wrapping the native UseSense
126
- * iOS and Android SDKs into a single cross-platform API.
159
+ * Human presence verification wrapping the native iOS and Android
160
+ * UseSense SDKs. Every session runs inside a full-screen native
161
+ * camera activity; the plugin is a thin bridge that forwards
162
+ * initialization, verification requests, and lifecycle events to
163
+ * the platform SDK and returns a redacted decision object.
127
164
  *
128
165
  * @example
129
166
  * ```ts
130
167
  * import { UseSense } from 'react-native-usesense';
131
168
  *
132
- * await UseSense.initialize({ apiKey: 'your_key', environment: 'sandbox' });
133
- * const result = await UseSense.startSession({ sessionType: 'enrollment' });
134
- * console.log(result.decision);
169
+ * await UseSense.initialize({
170
+ * apiKey: 'sk_sandbox_...',
171
+ * environment: 'sandbox',
172
+ * });
173
+ *
174
+ * const unsubscribe = UseSense.addListener((event) => {
175
+ * console.log(event.type, event.data);
176
+ * });
177
+ *
178
+ * try {
179
+ * const result = await UseSense.startVerification({
180
+ * sessionType: 'enrollment',
181
+ * });
182
+ * console.log(result.decision); // 'APPROVE' | 'REJECT' | 'MANUAL_REVIEW'
183
+ * } finally {
184
+ * unsubscribe.remove();
185
+ * }
135
186
  * ```
136
187
  */
137
188
  export declare class UseSense {
138
189
  /**
139
- * Initialize the UseSense plugin. Must be called once before any other method.
190
+ * Initialize the UseSense plugin. Must be called before any other
191
+ * method. Calling again with a different API key or environment
192
+ * replaces the previous configuration.
140
193
  *
141
- * @param config - Plugin configuration including API key and environment.
142
- * @throws {UseSenseError} If the API key is invalid or configuration is malformed.
194
+ * @throws {UseSenseError} If the API key is empty or initialization
195
+ * fails on the native side.
143
196
  */
144
197
  static initialize(config: UseSenseConfig): Promise<void>;
145
198
  /**
146
- * Start a verification session. Presents a full-screen native camera UI.
147
- *
148
- * The returned promise resolves when the session completes and the modal
149
- * dismisses, or rejects if an error occurs.
150
- *
151
- * @param options - Optional overrides for session type and identity ID.
152
- * @returns The verification result (for UI feedback only).
153
- * @throws {UseSenseError} On failure or cancellation.
199
+ * Start a verification session. Presents a full-screen native
200
+ * camera activity. The returned promise resolves when the session
201
+ * completes or rejects with a `UseSenseError` (including the
202
+ * `USER_CANCELLED` / `session_cancelled` cases when the user
203
+ * dismisses the camera screen).
154
204
  */
155
- static startSession(options?: StartSessionOptions): Promise<UseSenseResult>;
205
+ static startVerification(request: VerificationRequest): Promise<UseSenseResult>;
156
206
  /**
157
- * Cancel an in-progress verification session.
207
+ * Subscribe to real-time lifecycle events emitted by the native
208
+ * SDK during a verification session. Call this **before**
209
+ * `startVerification` so the subscription captures the earliest
210
+ * events (`SESSION_CREATED`, `PERMISSIONS_REQUESTED`).
158
211
  *
159
- * The camera UI is dismissed and the `startSession` promise rejects
160
- * with a `session_cancelled` error.
212
+ * Returns a subscription handle; call `.remove()` to stop
213
+ * receiving events.
161
214
  */
162
- static cancelSession(): Promise<void>;
215
+ static addListener(callback: (event: UseSenseEvent) => void): UseSenseSubscription;
163
216
  /**
164
- * Get the status of a verification session by ID.
165
- *
166
- * @param sessionId - The session ID to query.
167
- * @returns The current status and optional result.
217
+ * Clear all event listeners and release native resources. Does not
218
+ * need to be called explicitly during normal usage; the plugin
219
+ * cleans up on process teardown. Useful when you want to re-
220
+ * initialize against a different API key or during test teardown.
168
221
  */
169
- static getSessionStatus(sessionId: string): Promise<SessionStatus>;
222
+ static reset(): Promise<void>;
170
223
  /**
171
- * Subscribe to real-time events during the verification lifecycle.
172
- *
173
- * Call this before `startSession()` to receive all events.
174
- *
175
- * @param callback - Function invoked for each event.
176
- * @returns A subscription handle with a `remove()` method.
177
- *
178
- * @example
179
- * ```ts
180
- * const listener = UseSense.addListener((event) => {
181
- * console.log(event.type);
182
- * });
183
- * // Later:
184
- * listener.remove();
185
- * ```
224
+ * Whether the native plugin has an active `UseSense` client. Set
225
+ * to `true` after a successful `initialize` and `false` after
226
+ * `reset`. Useful for feature-flagging UI that should only show
227
+ * when the SDK is ready.
186
228
  */
187
- static addListener(callback: (event: UseSenseEvent) => void): UseSenseSubscription;
229
+ static isInitialized(): Promise<boolean>;
188
230
  /**
189
- * Get the native SDK version string.
231
+ * Start a LiveSense v4 session.
190
232
  *
191
- * @returns Version string in semver format (e.g. `"1.0.0"`).
233
+ * The session must already have been created on your backend and the
234
+ * session_token + nonce forwarded to the client. The native SDK drives
235
+ * the camera, signs the frame hash chain with the platform-attested
236
+ * key, uploads, and returns the opaque verdict.
192
237
  */
193
- static getSdkVersion(): string;
238
+ static startV4Verification(request: V4VerificationRequest): Promise<V4Verdict>;
239
+ }
240
+ export interface V4VerificationRequest {
241
+ sessionId: string;
242
+ sessionToken: string;
243
+ nonce: string;
244
+ apiBaseUrl: string;
245
+ environment?: UseSenseEnvironment;
246
+ displayName?: string;
247
+ brandPrimaryColor?: string;
248
+ }
249
+ export type V4Decision = 'pass' | 'fail' | 'review';
250
+ export type V4Confidence = 'high' | 'medium' | 'low';
251
+ export type V4AssuranceLevel = 'mobile_hardware' | 'web_attested' | 'web_unattested';
252
+ export type V4CaptureChannel = 'ios' | 'android' | 'rn' | 'flutter' | 'web';
253
+ /**
254
+ * Opaque verdict from POST /v1/sessions/:id/result. Matches the server
255
+ * contract exactly; no sub-scores leak through.
256
+ */
257
+ export interface V4Verdict {
258
+ session_id: string;
259
+ verdict: V4Decision;
260
+ confidence: V4Confidence;
261
+ assurance_level_achieved: V4AssuranceLevel;
262
+ capture_channel: V4CaptureChannel;
263
+ match_sense_embedding_id: string | null;
264
+ timestamp: string;
194
265
  }
195
266
  export default UseSense;
267
+ export { UseSenseFlows, FlowError, type FlowErrorCode, type FlowOutcome, type FlowRunResult, type FlowRunState, type RunFlowOptions, } from './flows';
196
268
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-usesense",
3
- "version": "1.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "React Native plugin for human presence verification. Wraps the native UseSense iOS and Android SDKs.",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -60,6 +60,7 @@
60
60
  "react-native": ">=0.73.0"
61
61
  },
62
62
  "devDependencies": {
63
+ "@types/jest": "^29.5.14",
63
64
  "@types/react": "^18.2.0",
64
65
  "@typescript-eslint/eslint-plugin": "^7.0.0",
65
66
  "@typescript-eslint/parser": "^7.0.0",
@@ -69,6 +70,7 @@
69
70
  "react": "^18.2.0",
70
71
  "react-native": "^0.73.0",
71
72
  "react-native-builder-bob": "^0.23.0",
73
+ "ts-jest": "^29.4.11",
72
74
  "typescript": "^5.3.0"
73
75
  },
74
76
  "codegenConfig": {
@@ -11,7 +11,9 @@ Pod::Spec.new do |s|
11
11
  s.authors = { "UseSense Technologies Ltd." => "support@usesense.ai" }
12
12
  s.source = { :git => package["repository"]["url"], :tag => "v#{s.version}" }
13
13
 
14
- s.platforms = { :ios => "14.0" }
14
+ # Minimum iOS 15.0 to match the native UseSense iOS SDK's floor
15
+ # (v4.x uses SwiftUI features that require iOS 15.0 and up).
16
+ s.platforms = { :ios => "15.0" }
15
17
  s.swift_version = "5.9"
16
18
 
17
19
  s.source_files = "ios/**/*.{swift,h,m}"
@@ -19,6 +21,10 @@ Pod::Spec.new do |s|
19
21
  # React Native dependency (supports both old and new architecture)
20
22
  install_modules_dependencies(s)
21
23
 
22
- # Native UseSense iOS SDK
23
- s.dependency "UseSenseSDK", "~> 1.0"
24
+ # Native UseSense iOS SDK. Minimum 4.2.2 — earlier 4.x releases had
25
+ # terminal-screen centering bugs, and the 1.x series used a pre-
26
+ # redaction result type with a completely different UseSenseConfig
27
+ # init signature. See CHANGELOG [2.0.0] for the full rewrite
28
+ # rationale.
29
+ s.dependency "UseSenseSDK", "~> 4.3"
24
30
  end
package/src/flows.ts ADDED
@@ -0,0 +1,159 @@
1
+ /**
2
+ * React Native wrapper for the UseSense Flows runner.
3
+ *
4
+ * Coexists with the existing Sessions API (`UseSense.startSession`, etc.);
5
+ * this is a parallel surface, not a replacement. See `guides/flows/sessions-vs-flows`
6
+ * in the API docs for when to use which.
7
+ *
8
+ * Usage:
9
+ *
10
+ * import { UseSenseFlows, FlowError } from 'react-native-usesense';
11
+ *
12
+ * try {
13
+ * const result = await UseSenseFlows.runFlow({ flowRunId, sdkToken });
14
+ * console.log(result.outcome); // 'APPROVE' | 'REJECT' | 'MANUAL_REVIEW' | null
15
+ * } catch (e) {
16
+ * if (e instanceof FlowError) {
17
+ * // e.code: 'token_expired' | 'token_invalid' | ...
18
+ * }
19
+ * }
20
+ */
21
+
22
+ import { NativeModules, Platform } from 'react-native';
23
+
24
+ const LINKING_ERROR =
25
+ `react-native-usesense (flows): the native module could not be found.\n\n` +
26
+ Platform.select({
27
+ ios: `iOS: run \`cd ios && pod install\` and rebuild the app.`,
28
+ android: `Android: rebuild the app after installing the package.`,
29
+ default: '',
30
+ });
31
+
32
+ // Lazy lookup so this module doesn't throw at import time on platforms that
33
+ // haven't been re-linked yet. The Sessions module mirrors this pattern.
34
+ function nativeModule() {
35
+ const m = NativeModules.UseSenseFlowsModule;
36
+ if (!m) throw new Error(LINKING_ERROR);
37
+ return m as { runFlow: (flowRunId: string, sdkToken: string, apiBaseUrl: string | null) => Promise<NativeFlowRunResult> };
38
+ }
39
+
40
+ /**
41
+ * Server-driven flow run state. Host apps see only the terminal ones via
42
+ * `FlowRunResult`; intermediate states drive the runner internally.
43
+ */
44
+ export type FlowRunState =
45
+ | 'pending'
46
+ | 'in_progress'
47
+ | 'stalled'
48
+ | 'awaiting_review'
49
+ | 'completed'
50
+ | 'errored'
51
+ | 'abandoned'
52
+ | 'cancelled';
53
+
54
+ /** Terminal outcome of a flow run. */
55
+ export type FlowOutcome = 'APPROVE' | 'REJECT' | 'MANUAL_REVIEW';
56
+
57
+ /**
58
+ * Uniform error taxonomy across every SDK. See `guides/flows/errors` in the
59
+ * API docs for per-code recovery patterns.
60
+ */
61
+ export type FlowErrorCode =
62
+ | 'token_expired'
63
+ | 'token_invalid'
64
+ | 'network_unavailable'
65
+ | 'permission_denied'
66
+ | 'provider_unavailable'
67
+ | 'cancelled'
68
+ | 'unsupported_action'
69
+ /** Server form validation failed. The native runner handles this inline
70
+ * (per-field highlights) and never reports terminal — but if a host app
71
+ * drives advance() outside the runner, the 422 surfaces with this code. */
72
+ | 'invalid_input'
73
+ | 'unknown';
74
+
75
+ const FLOW_ERROR_CODES: readonly FlowErrorCode[] = [
76
+ 'token_expired',
77
+ 'token_invalid',
78
+ 'network_unavailable',
79
+ 'permission_denied',
80
+ 'provider_unavailable',
81
+ 'cancelled',
82
+ 'unsupported_action',
83
+ 'invalid_input',
84
+ 'unknown',
85
+ ] as const;
86
+
87
+ function normaliseCode(code: unknown): FlowErrorCode {
88
+ return typeof code === 'string' && (FLOW_ERROR_CODES as readonly string[]).includes(code)
89
+ ? (code as FlowErrorCode)
90
+ : 'unknown';
91
+ }
92
+
93
+ /** Result returned to the host app's `runFlow` promise on terminal state. */
94
+ export interface FlowRunResult {
95
+ flowRunId: string;
96
+ state: FlowRunState;
97
+ outcome: FlowOutcome | null;
98
+ }
99
+
100
+ interface NativeFlowRunResult {
101
+ flowRunId: string;
102
+ state: FlowRunState;
103
+ outcome: FlowOutcome | null;
104
+ }
105
+
106
+ /**
107
+ * Typed error thrown by `runFlow` when the native runner reports a failure.
108
+ * Mirrors the web / iOS / Android / Flutter SDKs so host apps catch one
109
+ * taxonomy regardless of platform.
110
+ */
111
+ export class FlowError extends Error {
112
+ readonly code: FlowErrorCode;
113
+ constructor(code: FlowErrorCode, message: string) {
114
+ super(message);
115
+ this.name = 'FlowError';
116
+ this.code = code;
117
+ }
118
+ }
119
+
120
+ /** Options for `UseSenseFlows.runFlow`. */
121
+ export interface RunFlowOptions {
122
+ flowRunId: string;
123
+ sdkToken: string;
124
+ /** Defaults to `https://api.usesense.ai`. */
125
+ apiBaseUrl?: string;
126
+ }
127
+
128
+ /**
129
+ * Public entry point for the Flows runner. Sessions APIs are untouched;
130
+ * this is a parallel namespace.
131
+ */
132
+ export const UseSenseFlows = {
133
+ /**
134
+ * Run an operator-authored Flow inside the host app. Resolves with a
135
+ * [FlowRunResult] when the run reaches a terminal state (completed,
136
+ * cancelled, errored, abandoned). Rejects with a [FlowError] on transport
137
+ * / token / unsupported-action faults.
138
+ */
139
+ async runFlow(options: RunFlowOptions): Promise<FlowRunResult> {
140
+ try {
141
+ const native = await nativeModule().runFlow(
142
+ options.flowRunId,
143
+ options.sdkToken,
144
+ options.apiBaseUrl ?? null,
145
+ );
146
+ return {
147
+ flowRunId: native.flowRunId,
148
+ state: native.state,
149
+ outcome: native.outcome,
150
+ };
151
+ } catch (e) {
152
+ // RN bridge errors are surfaced as `Error` with `.code` and `.message`
153
+ // properties when the native side calls `reject(code, message, null)`.
154
+ const code = normaliseCode((e as { code?: string }).code);
155
+ const message = (e as { message?: string }).message ?? 'Flow failed';
156
+ throw new FlowError(code, message);
157
+ }
158
+ },
159
+ };