@hawcx/react-native-sdk 1.0.8 → 1.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.
Files changed (134) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/HawcxReactNative.podspec +2 -2
  3. package/README.md +327 -109
  4. package/android/build.gradle +2 -2
  5. package/android/src/main/java/com/hawcx/reactnative/HawcxEventDispatcher.kt +4 -0
  6. package/android/src/main/java/com/hawcx/reactnative/HawcxReactNativeModule.kt +324 -1
  7. package/android/src/main/java/com/hawcx/reactnative/v6/HawcxV6Bridge.kt +402 -0
  8. package/ios/Frameworks/HawcxFramework.xcframework/Info.plist +5 -5
  9. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/HawcxFramework +0 -0
  10. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Info.plist +0 -0
  11. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.abi.json +22145 -2
  12. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.private.swiftinterface +628 -0
  13. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  14. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftinterface +628 -0
  15. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/HawcxFramework +0 -0
  16. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Info.plist +0 -0
  17. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.abi.json +22145 -2
  18. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +628 -0
  19. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  20. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftinterface +628 -0
  21. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json +22145 -2
  22. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +628 -0
  23. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  24. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +628 -0
  25. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/_CodeSignature/CodeResources +21 -21
  26. package/ios/HawcxReactNative.m +56 -0
  27. package/ios/HawcxReactNative.swift +380 -1
  28. package/ios/HawcxV6BridgeSupport.swift +468 -0
  29. package/lib/commonjs/index.js +326 -3
  30. package/lib/commonjs/index.js.map +1 -1
  31. package/lib/commonjs/v6Normalization.js +325 -0
  32. package/lib/commonjs/v6Normalization.js.map +1 -0
  33. package/lib/commonjs/v6State.js +186 -0
  34. package/lib/commonjs/v6State.js.map +1 -0
  35. package/lib/commonjs/v6Types.js +2 -0
  36. package/lib/commonjs/v6Types.js.map +1 -0
  37. package/lib/commonjs/v6WebLogin.js +101 -0
  38. package/lib/commonjs/v6WebLogin.js.map +1 -0
  39. package/lib/module/index.js +287 -1
  40. package/lib/module/index.js.map +1 -1
  41. package/lib/module/v6Normalization.js +318 -0
  42. package/lib/module/v6Normalization.js.map +1 -0
  43. package/lib/module/v6State.js +173 -0
  44. package/lib/module/v6State.js.map +1 -0
  45. package/lib/module/v6Types.js +2 -0
  46. package/lib/module/v6Types.js.map +1 -0
  47. package/lib/module/v6WebLogin.js +92 -0
  48. package/lib/module/v6WebLogin.js.map +1 -0
  49. package/lib/typescript/index.d.ts +83 -0
  50. package/lib/typescript/index.d.ts.map +1 -1
  51. package/lib/typescript/v6Normalization.d.ts +3 -0
  52. package/lib/typescript/v6Normalization.d.ts.map +1 -0
  53. package/lib/typescript/v6State.d.ts +13 -0
  54. package/lib/typescript/v6State.d.ts.map +1 -0
  55. package/lib/typescript/v6Types.d.ts +157 -0
  56. package/lib/typescript/v6Types.d.ts.map +1 -0
  57. package/lib/typescript/v6WebLogin.d.ts +32 -0
  58. package/lib/typescript/v6WebLogin.d.ts.map +1 -0
  59. package/package.json +21 -9
  60. package/src/index.ts +477 -0
  61. package/src/v6Normalization.ts +356 -0
  62. package/src/v6State.ts +238 -0
  63. package/src/v6Types.ts +194 -0
  64. package/src/v6WebLogin.ts +154 -0
  65. package/android/.settings/org.eclipse.buildship.core.prefs +0 -2
  66. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  67. package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
  68. package/android/gradlew +0 -185
  69. package/android/gradlew.bat +0 -89
  70. package/android/libs/hawcx-5.1.4.aar +0 -0
  71. package/docs/RELEASE.md +0 -129
  72. package/example/README.md +0 -59
  73. package/example/android/app/build.gradle +0 -126
  74. package/example/android/app/debug.keystore +0 -0
  75. package/example/android/app/proguard-rules.pro +0 -10
  76. package/example/android/app/src/debug/AndroidManifest.xml +0 -9
  77. package/example/android/app/src/main/AndroidManifest.xml +0 -27
  78. package/example/android/app/src/main/java/com/hawcx/example/MainActivity.kt +0 -22
  79. package/example/android/app/src/main/java/com/hawcx/example/MainApplication.kt +0 -45
  80. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -36
  81. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  82. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  83. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  84. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  85. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  86. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  87. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  88. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  89. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  90. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  91. package/example/android/app/src/main/res/values/strings.xml +0 -3
  92. package/example/android/app/src/main/res/values/styles.xml +0 -9
  93. package/example/android/build.gradle +0 -35
  94. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  95. package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
  96. package/example/android/gradle.properties +0 -41
  97. package/example/android/gradlew +0 -249
  98. package/example/android/gradlew.bat +0 -92
  99. package/example/android/local.properties +0 -2
  100. package/example/android/settings.gradle +0 -38
  101. package/example/app.json +0 -4
  102. package/example/babel.config.js +0 -3
  103. package/example/e2e/README.md +0 -17
  104. package/example/e2e/hawcx-login.yaml +0 -14
  105. package/example/index.js +0 -5
  106. package/example/ios/.xcode.env +0 -11
  107. package/example/ios/HawcxExampleApp/AppDelegate.h +0 -6
  108. package/example/ios/HawcxExampleApp/AppDelegate.mm +0 -31
  109. package/example/ios/HawcxExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json +0 -53
  110. package/example/ios/HawcxExampleApp/Images.xcassets/Contents.json +0 -6
  111. package/example/ios/HawcxExampleApp/Info.plist +0 -55
  112. package/example/ios/HawcxExampleApp/LaunchScreen.storyboard +0 -47
  113. package/example/ios/HawcxExampleApp/PrivacyInfo.xcprivacy +0 -37
  114. package/example/ios/HawcxExampleApp/main.m +0 -10
  115. package/example/ios/HawcxExampleApp.xcodeproj/project.pbxproj +0 -704
  116. package/example/ios/HawcxExampleApp.xcodeproj/project.xcworkspace/xcuserdata/agambhullar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  117. package/example/ios/HawcxExampleApp.xcodeproj/xcshareddata/xcschemes/HawcxExampleApp.xcscheme +0 -90
  118. package/example/ios/HawcxExampleApp.xcodeproj/xcuserdata/agambhullar.xcuserdatad/xcschemes/xcschememanagement.plist +0 -16
  119. package/example/ios/HawcxExampleApp.xcworkspace/contents.xcworkspacedata +0 -10
  120. package/example/ios/HawcxExampleAppTests/HawcxExampleAppTests.m +0 -66
  121. package/example/ios/HawcxExampleAppTests/Info.plist +0 -24
  122. package/example/ios/Podfile +0 -79
  123. package/example/ios/Podfile.lock +0 -1290
  124. package/example/metro.config.js +0 -16
  125. package/example/package-lock.json +0 -13220
  126. package/example/package.json +0 -30
  127. package/example/src/App.tsx +0 -755
  128. package/example/src/hawcx.config.ts +0 -25
  129. package/example/tsconfig.json +0 -8
  130. package/ios/Frameworks/.keep +0 -0
  131. package/lib/typescript/__tests__/index.test.d.ts +0 -2
  132. package/lib/typescript/__tests__/index.test.d.ts.map +0 -1
  133. package/react_mobile_sdk_plan.md +0 -242
  134. package/src/__tests__/index.test.ts +0 -206
@@ -0,0 +1,402 @@
1
+ package com.hawcx.reactnative.v6
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.ReadableMap
5
+ import com.facebook.react.bridge.WritableArray
6
+ import com.facebook.react.bridge.WritableMap
7
+ import com.hawcx.internal.HawcxSDK
8
+ import com.hawcx.protocol.v1.HawcxV1ErrorDetails
9
+ import com.hawcx.protocol.v1.HawcxV1FieldError
10
+ import com.hawcx.protocol.v1.HawcxV1FlowUpdate
11
+ import com.hawcx.protocol.v1.HawcxV1FlowType
12
+ import com.hawcx.protocol.v1.HawcxV1Method
13
+ import com.hawcx.protocol.v1.HawcxV1OAuthCallbackParser
14
+ import com.hawcx.protocol.v1.HawcxV1PromptContext
15
+ import com.hawcx.protocol.v1.HawcxV1QrApprovalOutcome
16
+ import com.hawcx.protocol.v1.HawcxV1QrPayloadParser
17
+ import com.hawcx.protocol.v1.HawcxV1RiskInfo
18
+ import com.hawcx.protocol.v1.HawcxV1RiskLocation
19
+ import com.hawcx.protocol.v1.HawcxV1SDK
20
+ import com.hawcx.protocol.v1.HawcxV1StepInfo
21
+ import com.hawcx.protocol.v1.HawcxV1UserPrompt
22
+ import com.hawcx.reactnative.HawcxEventDispatcher
23
+ import com.hawcx.reactnative.HawcxReactNativeLogger
24
+
25
+ internal data class HawcxV6StartOptions(
26
+ val identifier: String,
27
+ val flowType: HawcxV1FlowType = HawcxV1FlowType.SIGNIN,
28
+ val startToken: String? = null,
29
+ val inviteCode: String? = null,
30
+ val codeChallenge: String? = null
31
+ ) {
32
+ companion object {
33
+ fun from(options: ReadableMap): HawcxV6StartOptions {
34
+ val identifier = options.getString("identifier")?.trim().orEmpty()
35
+ require(identifier.isNotEmpty()) { "identifier is required" }
36
+
37
+ val flowType = when (options.getString("flowType")?.trim().orEmpty().ifBlank { "signin" }) {
38
+ "signin" -> HawcxV1FlowType.SIGNIN
39
+ "signup" -> HawcxV1FlowType.SIGNUP
40
+ "account_manage" -> HawcxV1FlowType.ACCOUNT_MANAGE
41
+ else -> throw IllegalArgumentException(
42
+ "flowType must be one of signin, signup, or account_manage"
43
+ )
44
+ }
45
+
46
+ return HawcxV6StartOptions(
47
+ identifier = identifier,
48
+ flowType = flowType,
49
+ startToken = options.getOptionalString("startToken"),
50
+ inviteCode = options.getOptionalString("inviteCode"),
51
+ codeChallenge = options.getOptionalString("codeChallenge")
52
+ )
53
+ }
54
+ }
55
+ }
56
+
57
+ internal data class HawcxV6InitializeOptions(
58
+ val relyingParty: String? = null,
59
+ val autoPollApprovals: Boolean = true
60
+ ) {
61
+ companion object {
62
+ fun from(config: ReadableMap): HawcxV6InitializeOptions {
63
+ val relyingParty = if (config.hasKey("relyingParty") && !config.isNull("relyingParty")) {
64
+ config.getString("relyingParty")?.trim().takeUnless { it.isNullOrEmpty() }
65
+ } else {
66
+ null
67
+ }
68
+ val autoPollApprovals = if (config.hasKey("autoPollApprovals") && !config.isNull("autoPollApprovals")) {
69
+ config.getBoolean("autoPollApprovals")
70
+ } else {
71
+ true
72
+ }
73
+ return HawcxV6InitializeOptions(
74
+ relyingParty = relyingParty,
75
+ autoPollApprovals = autoPollApprovals
76
+ )
77
+ }
78
+ }
79
+ }
80
+
81
+ internal class HawcxV6Bridge(
82
+ private val eventDispatcher: HawcxEventDispatcher
83
+ ) {
84
+ private var sdk: HawcxV1SDK? = null
85
+
86
+ fun configure(legacySdk: HawcxSDK, configId: String, options: HawcxV6InitializeOptions) {
87
+ dispose(resetFlow = true)
88
+
89
+ val sdk = legacySdk.buildProtocolV1Sdk(
90
+ configId = configId,
91
+ relyingParty = options.relyingParty,
92
+ autoPollApprovals = options.autoPollApprovals
93
+ )
94
+
95
+ sdk.flow.onUpdate = { update ->
96
+ val event = HawcxV6FlowEventCodec.encode(update)
97
+ eventDispatcher.emitV6FlowEvent(event.type, event.payload)
98
+ }
99
+
100
+ this.sdk = sdk
101
+ }
102
+
103
+ fun start(options: HawcxV6StartOptions) {
104
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
105
+ sdk.start(
106
+ flowType = options.flowType,
107
+ identifier = options.identifier,
108
+ startToken = options.startToken,
109
+ inviteCode = options.inviteCode,
110
+ codeChallenge = options.codeChallenge
111
+ )
112
+ }
113
+
114
+ fun selectMethod(methodId: String) {
115
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
116
+ sdk.flow.selectMethod(methodId)
117
+ }
118
+
119
+ fun submitCode(code: String) {
120
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
121
+ sdk.flow.submitCode(code)
122
+ }
123
+
124
+ fun submitTotp(code: String) {
125
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
126
+ sdk.flow.submitTotp(code)
127
+ }
128
+
129
+ fun submitPhone(phone: String) {
130
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
131
+ sdk.flow.submitPhone(phone)
132
+ }
133
+
134
+ fun resend(): Boolean {
135
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
136
+ return sdk.flow.resend()
137
+ }
138
+
139
+ fun poll() {
140
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
141
+ sdk.flow.poll()
142
+ }
143
+
144
+ fun cancel() {
145
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
146
+ sdk.flow.cancel()
147
+ }
148
+
149
+ fun reset() {
150
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
151
+ sdk.reset()
152
+ }
153
+
154
+ fun approveQr(
155
+ rawPayload: String,
156
+ identifier: String,
157
+ rememberDevice: Boolean,
158
+ completion: (Result<HawcxV6QrApprovalResult>) -> Unit
159
+ ) {
160
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
161
+ val payload = HawcxV1QrPayloadParser.parse(rawPayload.trim())
162
+ if (payload == null) {
163
+ completion(Result.failure(IllegalArgumentException("rawPayload must be a valid Hawcx QR payload")))
164
+ return
165
+ }
166
+
167
+ val trimmedIdentifier = identifier.trim()
168
+ if (trimmedIdentifier.isEmpty()) {
169
+ completion(Result.failure(IllegalArgumentException("identifier is required")))
170
+ return
171
+ }
172
+
173
+ val service = sdk.createQrApprovalService()
174
+ if (service == null) {
175
+ completion(Result.failure(IllegalStateException("QR approval is unavailable for the current V6 bridge configuration")))
176
+ return
177
+ }
178
+
179
+ service.approve(
180
+ payload = payload,
181
+ identifier = trimmedIdentifier,
182
+ rememberDevice = rememberDevice
183
+ ) { result ->
184
+ completion(result.mapCatching { outcome ->
185
+ when (outcome) {
186
+ HawcxV1QrApprovalOutcome.Approved -> HawcxV6QrApprovalResult(
187
+ outcome = "approved",
188
+ payloadType = payload.type.wireValue
189
+ )
190
+
191
+ is HawcxV1QrApprovalOutcome.Bound -> HawcxV6QrApprovalResult(
192
+ outcome = "bound",
193
+ payloadType = payload.type.wireValue,
194
+ userId = outcome.userid
195
+ )
196
+ }
197
+ })
198
+ }
199
+ }
200
+
201
+ fun handleRedirectUrl(url: String) {
202
+ val sdk = requireNotNull(sdk) { "initialize must be called before using V6 bridge methods" }
203
+ val callback = HawcxV1OAuthCallbackParser.parse(url)
204
+ ?: throw IllegalArgumentException("url must be a valid V6 OAuth callback URL")
205
+ sdk.flow.oauthCallback(callback.code, callback.state)
206
+ }
207
+
208
+ fun dispose(resetFlow: Boolean) {
209
+ val current = sdk ?: return
210
+ current.flow.onUpdate = null
211
+ if (resetFlow) {
212
+ runCatching { current.reset() }
213
+ .onFailure { error ->
214
+ HawcxReactNativeLogger.w("Failed to reset V6 flow during bridge disposal", error)
215
+ }
216
+ }
217
+ sdk = null
218
+ }
219
+
220
+ }
221
+
222
+ internal data class HawcxV6QrApprovalResult(
223
+ val outcome: String,
224
+ val payloadType: String,
225
+ val userId: String? = null
226
+ )
227
+
228
+ private data class HawcxV6EventEnvelope(
229
+ val type: String,
230
+ val payload: WritableMap? = null
231
+ )
232
+
233
+ private object HawcxV6FlowEventCodec {
234
+ fun encode(update: HawcxV1FlowUpdate): HawcxV6EventEnvelope {
235
+ return when (update) {
236
+ is HawcxV1FlowUpdate.Idle -> HawcxV6EventEnvelope(type = "idle")
237
+ is HawcxV1FlowUpdate.Loading -> HawcxV6EventEnvelope(
238
+ type = "loading",
239
+ payload = Arguments.createMap().apply {
240
+ update.session?.takeIf { it.isNotBlank() }?.let { putString("session", it) }
241
+ }
242
+ )
243
+
244
+ is HawcxV1FlowUpdate.Prompt -> HawcxV6EventEnvelope(
245
+ type = "prompt",
246
+ payload = encodePromptPayload(update.context, update.prompt)
247
+ )
248
+
249
+ is HawcxV1FlowUpdate.Completed -> HawcxV6EventEnvelope(
250
+ type = "completed",
251
+ payload = Arguments.createMap().apply {
252
+ putString("session", update.session)
253
+ putString("authCode", update.authCode)
254
+ putString("expiresAt", update.expiresAt)
255
+ update.codeVerifier?.let { putString("codeVerifier", it) }
256
+ putString("traceId", update.meta.traceId)
257
+ }
258
+ )
259
+
260
+ is HawcxV1FlowUpdate.Error -> HawcxV6EventEnvelope(
261
+ type = "error",
262
+ payload = Arguments.createMap().apply {
263
+ update.session?.let { putString("session", it) }
264
+ putString("code", update.code)
265
+ update.action?.let { putString("action", it.wireValue) }
266
+ putString("message", update.message)
267
+ putBoolean("retryable", update.retryable)
268
+ update.meta?.traceId?.let { putString("traceId", it) }
269
+ update.details?.let { putMap("details", encodeErrorDetails(it)) }
270
+ }
271
+ )
272
+ }
273
+ }
274
+
275
+ private fun encodePromptPayload(
276
+ context: HawcxV1PromptContext,
277
+ prompt: HawcxV1UserPrompt
278
+ ): WritableMap {
279
+ return Arguments.createMap().apply {
280
+ putString("session", context.session)
281
+ putString("traceId", context.meta.traceId)
282
+ putString("expiresAt", context.meta.expiresAt)
283
+ context.stepInfo?.let { putMap("step", encodeStepInfo(it)) }
284
+ context.risk?.let { putMap("risk", encodeRiskInfo(it)) }
285
+ context.codeChannel?.takeIf { it.isNotBlank() }?.let { putString("codeChannel", it) }
286
+ putMap("prompt", encodePrompt(prompt))
287
+ }
288
+ }
289
+
290
+ private fun encodePrompt(prompt: HawcxV1UserPrompt): WritableMap {
291
+ return Arguments.createMap().apply {
292
+ when (prompt) {
293
+ is HawcxV1UserPrompt.SelectMethod -> {
294
+ putString("type", "select_method")
295
+ putArray("methods", prompt.methods.toWritableArray { encodeMethod(it) })
296
+ prompt.phase?.takeIf { it.isNotBlank() }?.let { putString("phase", it) }
297
+ }
298
+ is HawcxV1UserPrompt.EnterCode -> {
299
+ putString("type", "enter_code")
300
+ putString("destination", prompt.destination)
301
+ prompt.codeLength?.let { putInt("codeLength", it) }
302
+ prompt.codeFormat?.takeIf { it.isNotBlank() }?.let { putString("codeFormat", it) }
303
+ prompt.codeExpiresAt?.takeIf { it.isNotBlank() }?.let { putString("codeExpiresAt", it) }
304
+ prompt.resendAt?.takeIf { it.isNotBlank() }?.let { putString("resendAt", it) }
305
+ }
306
+ HawcxV1UserPrompt.EnterTotp -> {
307
+ putString("type", "enter_totp")
308
+ }
309
+ is HawcxV1UserPrompt.SetupTotp -> {
310
+ putString("type", "setup_totp")
311
+ putString("secret", prompt.secret)
312
+ putString("otpauthUrl", prompt.otpauthUrl)
313
+ prompt.period?.let { putInt("period", it) }
314
+ }
315
+ is HawcxV1UserPrompt.SetupSms -> {
316
+ putString("type", "setup_sms")
317
+ prompt.existingPhone?.takeIf { it.isNotBlank() }?.let { putString("existingPhone", it) }
318
+ }
319
+ is HawcxV1UserPrompt.Redirect -> {
320
+ putString("type", "redirect")
321
+ putString("url", prompt.url)
322
+ prompt.returnScheme?.takeIf { it.isNotBlank() }?.let { putString("returnScheme", it) }
323
+ }
324
+ is HawcxV1UserPrompt.AwaitApproval -> {
325
+ putString("type", "await_approval")
326
+ prompt.qrData?.takeIf { it.isNotBlank() }?.let { putString("qrData", it) }
327
+ putString("expiresAt", prompt.expiresAt)
328
+ putInt("pollInterval", prompt.pollInterval)
329
+ }
330
+ }
331
+ }
332
+ }
333
+
334
+ private fun encodeMethod(method: HawcxV1Method): WritableMap {
335
+ return Arguments.createMap().apply {
336
+ putString("id", method.id)
337
+ putString("label", method.label)
338
+ method.icon?.takeIf { it.isNotBlank() }?.let { putString("icon", it) }
339
+ }
340
+ }
341
+
342
+ private fun encodeStepInfo(stepInfo: HawcxV1StepInfo): WritableMap {
343
+ return Arguments.createMap().apply {
344
+ putString("id", stepInfo.id)
345
+ stepInfo.label?.takeIf { it.isNotBlank() }?.let { putString("label", it) }
346
+ }
347
+ }
348
+
349
+ private fun encodeRiskInfo(risk: HawcxV1RiskInfo): WritableMap {
350
+ return Arguments.createMap().apply {
351
+ putBoolean("detected", risk.detected)
352
+ putArray("reasons", risk.reasons.toStringArray())
353
+ risk.message?.takeIf { it.isNotBlank() }?.let { putString("message", it) }
354
+ risk.location?.let { putMap("location", encodeRiskLocation(it)) }
355
+ risk.riskScore?.let { putDouble("riskScore", it) }
356
+ }
357
+ }
358
+
359
+ private fun encodeRiskLocation(location: HawcxV1RiskLocation): WritableMap {
360
+ return Arguments.createMap().apply {
361
+ location.city?.takeIf { it.isNotBlank() }?.let { putString("city", it) }
362
+ location.country?.takeIf { it.isNotBlank() }?.let { putString("country", it) }
363
+ }
364
+ }
365
+
366
+ private fun encodeErrorDetails(details: HawcxV1ErrorDetails): WritableMap {
367
+ return Arguments.createMap().apply {
368
+ details.retryAfterSeconds?.let { putInt("retryAfterSeconds", it) }
369
+ details.retryAt?.takeIf { it.isNotBlank() }?.let { putString("retryAt", it) }
370
+ details.attemptsRemaining?.let { putInt("attemptsRemaining", it) }
371
+ details.errors?.takeIf { it.isNotEmpty() }?.let {
372
+ putArray("errors", it.toWritableArray(::encodeFieldError))
373
+ }
374
+ }
375
+ }
376
+
377
+ private fun encodeFieldError(error: HawcxV1FieldError): WritableMap {
378
+ return Arguments.createMap().apply {
379
+ putString("field", error.field)
380
+ putString("message", error.message)
381
+ }
382
+ }
383
+
384
+ private fun <T> List<T>.toWritableArray(transform: (T) -> WritableMap): WritableArray {
385
+ return Arguments.createArray().apply {
386
+ forEach { pushMap(transform(it)) }
387
+ }
388
+ }
389
+
390
+ private fun List<String>.toStringArray(): WritableArray {
391
+ return Arguments.createArray().apply {
392
+ this@toStringArray.forEach { pushString(it) }
393
+ }
394
+ }
395
+ }
396
+
397
+ private fun ReadableMap.getOptionalString(key: String): String? {
398
+ if (!hasKey(key) || isNull(key)) {
399
+ return null
400
+ }
401
+ return getString(key)?.trim()?.takeUnless { it.isEmpty() }
402
+ }
@@ -8,32 +8,32 @@
8
8
  <key>BinaryPath</key>
9
9
  <string>HawcxFramework.framework/HawcxFramework</string>
10
10
  <key>LibraryIdentifier</key>
11
- <string>ios-arm64_x86_64-simulator</string>
11
+ <string>ios-arm64</string>
12
12
  <key>LibraryPath</key>
13
13
  <string>HawcxFramework.framework</string>
14
14
  <key>SupportedArchitectures</key>
15
15
  <array>
16
16
  <string>arm64</string>
17
- <string>x86_64</string>
18
17
  </array>
19
18
  <key>SupportedPlatform</key>
20
19
  <string>ios</string>
21
- <key>SupportedPlatformVariant</key>
22
- <string>simulator</string>
23
20
  </dict>
24
21
  <dict>
25
22
  <key>BinaryPath</key>
26
23
  <string>HawcxFramework.framework/HawcxFramework</string>
27
24
  <key>LibraryIdentifier</key>
28
- <string>ios-arm64</string>
25
+ <string>ios-arm64_x86_64-simulator</string>
29
26
  <key>LibraryPath</key>
30
27
  <string>HawcxFramework.framework</string>
31
28
  <key>SupportedArchitectures</key>
32
29
  <array>
33
30
  <string>arm64</string>
31
+ <string>x86_64</string>
34
32
  </array>
35
33
  <key>SupportedPlatform</key>
36
34
  <string>ios</string>
35
+ <key>SupportedPlatformVariant</key>
36
+ <string>simulator</string>
37
37
  </dict>
38
38
  </array>
39
39
  <key>CFBundlePackageType</key>