react-native-usesense 1.0.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.
@@ -0,0 +1,158 @@
1
+ package com.usesense.reactnative
2
+
3
+ import com.facebook.react.bridge.*
4
+ import com.facebook.react.modules.core.DeviceEventManagerModule
5
+ import com.usesense.sdk.*
6
+
7
+ class UseSenseModule(private val reactContext: ReactApplicationContext) :
8
+ ReactContextBaseJavaModule(reactContext) {
9
+
10
+ override fun getName(): String = "UseSenseModule"
11
+
12
+ private var eventUnsubscribe: (() -> Unit)? = null
13
+
14
+ @ReactMethod
15
+ fun initialize(configMap: ReadableMap) {
16
+ val apiKey = configMap.getString("apiKey")
17
+ ?: throw IllegalArgumentException("apiKey is required")
18
+
19
+ val branding = if (configMap.hasKey("branding")) {
20
+ val b = configMap.getMap("branding")
21
+ BrandingConfig(
22
+ logoUrl = b?.getString("logoUrl"),
23
+ primaryColor = b?.getString("primaryColor") ?: "#4F63F5",
24
+ buttonRadius = if (b?.hasKey("buttonRadius") == true) b.getInt("buttonRadius") else 12,
25
+ fontFamily = b?.getString("fontFamily"),
26
+ )
27
+ } else null
28
+
29
+ val environment = when (configMap.getString("environment")) {
30
+ "sandbox" -> UseSenseEnvironment.SANDBOX
31
+ "production" -> UseSenseEnvironment.PRODUCTION
32
+ else -> UseSenseEnvironment.AUTO
33
+ }
34
+
35
+ val config = UseSenseConfig(
36
+ apiKey = apiKey,
37
+ environment = environment,
38
+ baseUrl = configMap.getString("baseUrl") ?: UseSenseConfig.DEFAULT_BASE_URL,
39
+ gatewayKey = configMap.getString("gatewayKey"),
40
+ branding = branding,
41
+ googleCloudProjectNumber = if (configMap.hasKey("googleCloudProjectNumber"))
42
+ configMap.getDouble("googleCloudProjectNumber").toLong()
43
+ else UseSenseConfig.DEFAULT_GOOGLE_CLOUD_PROJECT_NUMBER,
44
+ )
45
+
46
+ val context = reactContext.applicationContext
47
+ UseSense.initialize(context, config)
48
+ }
49
+
50
+ @ReactMethod
51
+ fun startVerification(requestMap: ReadableMap, promise: Promise) {
52
+ val activity = currentActivity
53
+ if (activity == null) {
54
+ promise.reject("NO_ACTIVITY", "No current activity available")
55
+ return
56
+ }
57
+
58
+ val sessionType = when (requestMap.getString("sessionType")) {
59
+ "authentication" -> SessionType.AUTHENTICATION
60
+ else -> SessionType.ENROLLMENT
61
+ }
62
+
63
+ val metadata: Map<String, Any>? = if (requestMap.hasKey("metadata")) {
64
+ requestMap.getMap("metadata")?.toHashMap()
65
+ } else null
66
+
67
+ val request = VerificationRequest(
68
+ sessionType = sessionType,
69
+ externalUserId = requestMap.getString("externalUserId"),
70
+ identityId = requestMap.getString("identityId"),
71
+ metadata = metadata,
72
+ )
73
+
74
+ UseSense.startVerification(activity, request, object : UseSenseCallback {
75
+ override fun onSuccess(result: UseSenseResult) {
76
+ val map = Arguments.createMap().apply {
77
+ putString("sessionId", result.sessionId)
78
+ putString("sessionType", result.sessionType)
79
+ putString("identityId", result.identityId)
80
+ putString("decision", result.decision)
81
+ putString("timestamp", result.timestamp)
82
+ putBoolean("isApproved", result.isApproved)
83
+ putBoolean("isRejected", result.isRejected)
84
+ putBoolean("isPendingReview", result.isPendingReview)
85
+ }
86
+ promise.resolve(map)
87
+ }
88
+
89
+ override fun onError(error: UseSenseError) {
90
+ val details = Arguments.createMap().apply {
91
+ putInt("code", error.code)
92
+ putString("serverCode", error.serverCode)
93
+ putString("message", error.message)
94
+ putBoolean("isRetryable", error.isRetryable)
95
+ }
96
+ promise.reject(
97
+ error.code.toString(),
98
+ error.message,
99
+ Exception(error.message),
100
+ details,
101
+ )
102
+ }
103
+
104
+ override fun onCancelled() {
105
+ promise.reject("CANCELLED", "Verification was cancelled by the user")
106
+ }
107
+ })
108
+ }
109
+
110
+ @ReactMethod
111
+ fun subscribeToEvents() {
112
+ eventUnsubscribe?.invoke()
113
+ eventUnsubscribe = UseSense.onEvent { event ->
114
+ val map = Arguments.createMap().apply {
115
+ putString("type", event.type.name)
116
+ putDouble("timestamp", event.timestamp.toDouble())
117
+ if (event.data != null) {
118
+ putMap("data", Arguments.makeNativeMap(event.data as Map<String, Any>))
119
+ }
120
+ }
121
+ sendEvent("UseSenseEvent", map)
122
+ }
123
+ }
124
+
125
+ @ReactMethod
126
+ fun unsubscribeFromEvents() {
127
+ eventUnsubscribe?.invoke()
128
+ eventUnsubscribe = null
129
+ }
130
+
131
+ @ReactMethod
132
+ fun reset() {
133
+ eventUnsubscribe?.invoke()
134
+ eventUnsubscribe = null
135
+ UseSense.reset()
136
+ }
137
+
138
+ @ReactMethod
139
+ fun isInitialized(promise: Promise) {
140
+ promise.resolve(UseSense.isInitialized)
141
+ }
142
+
143
+ private fun sendEvent(eventName: String, params: WritableMap) {
144
+ reactContext
145
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
146
+ .emit(eventName, params)
147
+ }
148
+
149
+ @ReactMethod
150
+ fun addListener(eventType: String) {
151
+ // Required for NativeEventEmitter
152
+ }
153
+
154
+ @ReactMethod
155
+ fun removeListeners(count: Int) {
156
+ // Required for NativeEventEmitter
157
+ }
158
+ }
@@ -0,0 +1,17 @@
1
+ package com.usesense.reactnative
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class UseSensePackage : ReactPackage {
9
+
10
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
11
+ return listOf(UseSenseModule(reactContext))
12
+ }
13
+
14
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
15
+ return emptyList()
16
+ }
17
+ }
@@ -0,0 +1,25 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface RCT_EXTERN_MODULE(UseSenseModule, RCTEventEmitter)
5
+
6
+ RCT_EXTERN_METHOD(initialize:(NSDictionary *)config)
7
+
8
+ RCT_EXTERN_METHOD(startSession:(NSDictionary *)options
9
+ resolver:(RCTPromiseResolveBlock)resolve
10
+ rejecter:(RCTPromiseRejectBlock)reject)
11
+
12
+ RCT_EXTERN_METHOD(cancelSession:(RCTPromiseResolveBlock)resolve
13
+ rejecter:(RCTPromiseRejectBlock)reject)
14
+
15
+ RCT_EXTERN_METHOD(getSessionStatus:(NSString *)sessionId
16
+ resolver:(RCTPromiseResolveBlock)resolve
17
+ rejecter:(RCTPromiseRejectBlock)reject)
18
+
19
+ RCT_EXTERN_METHOD(subscribeToEvents)
20
+
21
+ RCT_EXTERN_METHOD(unsubscribeFromEvents)
22
+
23
+ RCT_EXTERN_METHOD(getSdkVersion)
24
+
25
+ @end
@@ -0,0 +1,148 @@
1
+ import Foundation
2
+ import React
3
+ import UseSenseSDK
4
+
5
+ @objc(UseSenseModule)
6
+ class UseSenseModule: RCTEventEmitter {
7
+
8
+ private var hasListeners = false
9
+
10
+ override func supportedEvents() -> [String]! {
11
+ return ["UseSenseEvent"]
12
+ }
13
+
14
+ override func startObserving() {
15
+ hasListeners = true
16
+ }
17
+
18
+ override func stopObserving() {
19
+ hasListeners = false
20
+ }
21
+
22
+ @objc
23
+ func initialize(_ config: NSDictionary) {
24
+ guard let apiKey = config["apiKey"] as? String else {
25
+ return
26
+ }
27
+
28
+ let environment: UseSenseEnvironment = {
29
+ switch config["environment"] as? String {
30
+ case "production": return .production
31
+ case "sandbox": return .sandbox
32
+ default: return .sandbox
33
+ }
34
+ }()
35
+
36
+ let useSenseConfig = UseSenseConfig(
37
+ apiKey: apiKey,
38
+ environment: environment,
39
+ organizationId: config["organizationId"] as? String,
40
+ sessionType: (config["sessionType"] as? String) == "authentication" ? .authentication : .enrollment,
41
+ challengePolicy: {
42
+ switch config["challengePolicy"] as? String {
43
+ case "enhanced": return .enhanced
44
+ case "adaptive": return .adaptive
45
+ default: return .standard
46
+ }
47
+ }(),
48
+ enableAudio: config["enableAudio"] as? Bool ?? false,
49
+ timeout: config["timeout"] as? TimeInterval ?? 60000,
50
+ metadata: config["metadata"] as? [String: String]
51
+ )
52
+
53
+ UseSense.initialize(config: useSenseConfig)
54
+ }
55
+
56
+ @objc
57
+ func startSession(_ options: NSDictionary, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
58
+ DispatchQueue.main.async {
59
+ guard let viewController = RCTPresentedViewController() else {
60
+ reject("no_view_controller", "No view controller available", nil)
61
+ return
62
+ }
63
+
64
+ let sessionType: SessionType = (options["sessionType"] as? String) == "authentication" ? .authentication : .enrollment
65
+ let identityId = options["identityId"] as? String
66
+
67
+ UseSense.startSession(
68
+ from: viewController,
69
+ sessionType: sessionType,
70
+ identityId: identityId
71
+ ) { result in
72
+ switch result {
73
+ case .success(let useSenseResult):
74
+ let resultDict: [String: Any] = [
75
+ "sessionId": useSenseResult.sessionId,
76
+ "decision": useSenseResult.decision.rawValue,
77
+ "channelTrustScore": useSenseResult.channelTrustScore,
78
+ "livenessScore": useSenseResult.livenessScore,
79
+ "matchSenseRiskScore": useSenseResult.matchSenseRiskScore,
80
+ "presenceConfidence": useSenseResult.presenceConfidence,
81
+ "reasons": useSenseResult.reasons,
82
+ "ruleTriggered": useSenseResult.ruleTriggered as Any,
83
+ "recommendedAction": useSenseResult.recommendedAction as Any,
84
+ "sessionSignature": useSenseResult.sessionSignature,
85
+ ]
86
+ resolve(resultDict)
87
+
88
+ case .failure(let error):
89
+ reject(error.code, error.message, nil)
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ @objc
96
+ func cancelSession(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
97
+ UseSense.cancelSession()
98
+ resolve(nil)
99
+ }
100
+
101
+ @objc
102
+ func getSessionStatus(_ sessionId: String, resolver resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) {
103
+ UseSense.getSessionStatus(sessionId: sessionId) { result in
104
+ switch result {
105
+ case .success(let status):
106
+ var dict: [String: Any] = ["status": status.status]
107
+ if let r = status.result {
108
+ dict["result"] = [
109
+ "sessionId": r.sessionId,
110
+ "decision": r.decision.rawValue,
111
+ "channelTrustScore": r.channelTrustScore,
112
+ "livenessScore": r.livenessScore,
113
+ "matchSenseRiskScore": r.matchSenseRiskScore,
114
+ "presenceConfidence": r.presenceConfidence,
115
+ "reasons": r.reasons,
116
+ "sessionSignature": r.sessionSignature,
117
+ ]
118
+ }
119
+ resolve(dict)
120
+ case .failure(let error):
121
+ reject(error.code, error.message, nil)
122
+ }
123
+ }
124
+ }
125
+
126
+ @objc
127
+ func subscribeToEvents() {
128
+ UseSense.onEvent { [weak self] event in
129
+ guard let self = self, self.hasListeners else { return }
130
+ self.sendEvent(withName: "UseSenseEvent", body: event.toDictionary())
131
+ }
132
+ }
133
+
134
+ @objc
135
+ func unsubscribeFromEvents() {
136
+ UseSense.removeEventListener()
137
+ }
138
+
139
+ @objc
140
+ func getSdkVersion() -> String {
141
+ return UseSense.sdkVersion
142
+ }
143
+
144
+ @objc
145
+ override static func requiresMainQueueSetup() -> Bool {
146
+ return false
147
+ }
148
+ }
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.UseSense = void 0;
7
+ var _reactNative = require("react-native");
8
+ const {
9
+ UseSenseModule
10
+ } = _reactNative.NativeModules;
11
+ if (!UseSenseModule) {
12
+ throw new Error('react-native-usesense: NativeModule not found. Make sure you have linked the library correctly.\n' + 'iOS: Run `cd ios && pod install`\n' + 'Android: Rebuild the app after installing the package.');
13
+ }
14
+
15
+ // ─── Types ───────────────────────────────────────────────────────────
16
+
17
+ /** Environment for the UseSense API. */
18
+
19
+ /** Session type for verification. */
20
+
21
+ /** Challenge policy controlling verification difficulty. */
22
+
23
+ /** Decision returned by the verification engine. */
24
+
25
+ /** Processing stage during server-side analysis. */
26
+
27
+ /**
28
+ * Configuration for initializing the UseSense plugin.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * await UseSense.initialize({
33
+ * apiKey: 'your_api_key',
34
+ * environment: 'sandbox',
35
+ * });
36
+ * ```
37
+ */
38
+
39
+ /**
40
+ * Result returned when a verification session completes.
41
+ *
42
+ * **Important:** This result is for UI feedback only.
43
+ * The definitive verdict arrives at your backend via HMAC-signed webhook.
44
+ */
45
+
46
+ /**
47
+ * Error returned when a verification session fails.
48
+ */
49
+
50
+ /**
51
+ * Events emitted during the verification session lifecycle.
52
+ */
53
+
54
+ /** Options for starting a verification session. */
55
+
56
+ /** Status of a verification session. */
57
+
58
+ /** Subscription handle returned by {@link UseSense.addListener}. */
59
+
60
+ // ─── Implementation ──────────────────────────────────────────────────
61
+
62
+ const eventEmitter = new _reactNative.NativeEventEmitter(UseSenseModule);
63
+
64
+ /**
65
+ * UseSense React Native plugin.
66
+ *
67
+ * Provides human presence verification by wrapping the native UseSense
68
+ * iOS and Android SDKs into a single cross-platform API.
69
+ *
70
+ * @example
71
+ * ```ts
72
+ * import { UseSense } from 'react-native-usesense';
73
+ *
74
+ * await UseSense.initialize({ apiKey: 'your_key', environment: 'sandbox' });
75
+ * const result = await UseSense.startSession({ sessionType: 'enrollment' });
76
+ * console.log(result.decision);
77
+ * ```
78
+ */
79
+ class UseSense {
80
+ /**
81
+ * Initialize the UseSense plugin. Must be called once before any other method.
82
+ *
83
+ * @param config - Plugin configuration including API key and environment.
84
+ * @throws {UseSenseError} If the API key is invalid or configuration is malformed.
85
+ */
86
+ static async initialize(config) {
87
+ if (!config.apiKey) {
88
+ throw {
89
+ code: 'invalid_config',
90
+ message: 'apiKey is required'
91
+ };
92
+ }
93
+ return UseSenseModule.initialize({
94
+ apiKey: config.apiKey,
95
+ environment: config.environment ?? 'sandbox',
96
+ organizationId: config.organizationId,
97
+ sessionType: config.sessionType ?? 'enrollment',
98
+ identityId: config.identityId,
99
+ challengePolicy: config.challengePolicy ?? 'standard',
100
+ enableAudio: config.enableAudio ?? false,
101
+ timeout: config.timeout ?? 60000,
102
+ metadata: config.metadata
103
+ });
104
+ }
105
+
106
+ /**
107
+ * Start a verification session. Presents a full-screen native camera UI.
108
+ *
109
+ * The returned promise resolves when the session completes and the modal
110
+ * dismisses, or rejects if an error occurs.
111
+ *
112
+ * @param options - Optional overrides for session type and identity ID.
113
+ * @returns The verification result (for UI feedback only).
114
+ * @throws {UseSenseError} On failure or cancellation.
115
+ */
116
+ static async startSession(options) {
117
+ return UseSenseModule.startSession(options ?? {});
118
+ }
119
+
120
+ /**
121
+ * Cancel an in-progress verification session.
122
+ *
123
+ * The camera UI is dismissed and the `startSession` promise rejects
124
+ * with a `session_cancelled` error.
125
+ */
126
+ static async cancelSession() {
127
+ return UseSenseModule.cancelSession();
128
+ }
129
+
130
+ /**
131
+ * Get the status of a verification session by ID.
132
+ *
133
+ * @param sessionId - The session ID to query.
134
+ * @returns The current status and optional result.
135
+ */
136
+ static async getSessionStatus(sessionId) {
137
+ return UseSenseModule.getSessionStatus(sessionId);
138
+ }
139
+
140
+ /**
141
+ * Subscribe to real-time events during the verification lifecycle.
142
+ *
143
+ * Call this before `startSession()` to receive all events.
144
+ *
145
+ * @param callback - Function invoked for each event.
146
+ * @returns A subscription handle with a `remove()` method.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * const listener = UseSense.addListener((event) => {
151
+ * console.log(event.type);
152
+ * });
153
+ * // Later:
154
+ * listener.remove();
155
+ * ```
156
+ */
157
+ static addListener(callback) {
158
+ UseSenseModule.subscribeToEvents();
159
+ const subscription = eventEmitter.addListener('UseSenseEvent', callback);
160
+ return {
161
+ remove: () => {
162
+ subscription.remove();
163
+ UseSenseModule.unsubscribeFromEvents();
164
+ }
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Get the native SDK version string.
170
+ *
171
+ * @returns Version string in semver format (e.g. `"1.0.0"`).
172
+ */
173
+ static getSdkVersion() {
174
+ return UseSenseModule.getSdkVersion();
175
+ }
176
+ }
177
+ exports.UseSense = UseSense;
178
+ var _default = exports.default = UseSense;
179
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["_reactNative","require","UseSenseModule","NativeModules","Error","eventEmitter","NativeEventEmitter","UseSense","initialize","config","apiKey","code","message","environment","organizationId","sessionType","identityId","challengePolicy","enableAudio","timeout","metadata","startSession","options","cancelSession","getSessionStatus","sessionId","addListener","callback","subscribeToEvents","subscription","remove","unsubscribeFromEvents","getSdkVersion","exports","_default","default"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA,MAAM;EAAEC;AAAe,CAAC,GAAGC,0BAAa;AAExC,IAAI,CAACD,cAAc,EAAE;EACnB,MAAM,IAAIE,KAAK,CACb,mGAAmG,GACjG,oCAAoC,GACpC,wDACJ,CAAC;AACH;;AAEA;;AAEA;;AAGA;;AAGA;;AAGA;;AAGA;;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAsBA;AACA;AACA;AACA;AACA;AACA;;AAwBA;AACA;AACA;;AAUA;AACA;AACA;;AASA;;AAQA;;AAQA;;AAMA;;AAEA,MAAMC,YAAY,GAAG,IAAIC,+BAAkB,CAACJ,cAAc,CAAC;;AAE3D;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAMK,QAAQ,CAAC;EACpB;AACF;AACA;AACA;AACA;AACA;EACE,aAAaC,UAAUA,CAACC,MAAsB,EAAiB;IAC7D,IAAI,CAACA,MAAM,CAACC,MAAM,EAAE;MAClB,MAAM;QAAEC,IAAI,EAAE,gBAAgB;QAAEC,OAAO,EAAE;MAAqB,CAAC;IACjE;IACA,OAAOV,cAAc,CAACM,UAAU,CAAC;MAC/BE,MAAM,EAAED,MAAM,CAACC,MAAM;MACrBG,WAAW,EAAEJ,MAAM,CAACI,WAAW,IAAI,SAAS;MAC5CC,cAAc,EAAEL,MAAM,CAACK,cAAc;MACrCC,WAAW,EAAEN,MAAM,CAACM,WAAW,IAAI,YAAY;MAC/CC,UAAU,EAAEP,MAAM,CAACO,UAAU;MAC7BC,eAAe,EAAER,MAAM,CAACQ,eAAe,IAAI,UAAU;MACrDC,WAAW,EAAET,MAAM,CAACS,WAAW,IAAI,KAAK;MACxCC,OAAO,EAAEV,MAAM,CAACU,OAAO,IAAI,KAAK;MAChCC,QAAQ,EAAEX,MAAM,CAACW;IACnB,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACE,aAAaC,YAAYA,CAACC,OAA6B,EAA2B;IAChF,OAAOpB,cAAc,CAACmB,YAAY,CAACC,OAAO,IAAI,CAAC,CAAC,CAAC;EACnD;;EAEA;AACF;AACA;AACA;AACA;AACA;EACE,aAAaC,aAAaA,CAAA,EAAkB;IAC1C,OAAOrB,cAAc,CAACqB,aAAa,CAAC,CAAC;EACvC;;EAEA;AACF;AACA;AACA;AACA;AACA;EACE,aAAaC,gBAAgBA,CAACC,SAAiB,EAA0B;IACvE,OAAOvB,cAAc,CAACsB,gBAAgB,CAACC,SAAS,CAAC;EACnD;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACE,OAAOC,WAAWA,CAACC,QAAwC,EAAwB;IACjFzB,cAAc,CAAC0B,iBAAiB,CAAC,CAAC;IAClC,MAAMC,YAAY,GAAGxB,YAAY,CAACqB,WAAW,CAAC,eAAe,EAAEC,QAAQ,CAAC;IACxE,OAAO;MACLG,MAAM,EAAEA,CAAA,KAAM;QACZD,YAAY,CAACC,MAAM,CAAC,CAAC;QACrB5B,cAAc,CAAC6B,qBAAqB,CAAC,CAAC;MACxC;IACF,CAAC;EACH;;EAEA;AACF;AACA;AACA;AACA;EACE,OAAOC,aAAaA,CAAA,EAAW;IAC7B,OAAO9B,cAAc,CAAC8B,aAAa,CAAC,CAAC;EACvC;AACF;AAACC,OAAA,CAAA1B,QAAA,GAAAA,QAAA;AAAA,IAAA2B,QAAA,GAAAD,OAAA,CAAAE,OAAA,GAEc5B,QAAQ","ignoreList":[]}