@hawcx/react-native-sdk 1.0.7 → 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 (132) hide show
  1. package/CHANGELOG.md +3 -6
  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/ios-arm64/HawcxFramework.framework/HawcxFramework +0 -0
  9. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Info.plist +0 -0
  10. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.abi.json +22143 -0
  11. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.private.swiftinterface +649 -21
  12. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  13. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios.swiftinterface +649 -21
  14. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/HawcxFramework +0 -0
  15. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Info.plist +0 -0
  16. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.abi.json +22143 -0
  17. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +649 -21
  18. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  19. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/arm64-apple-ios-simulator.swiftinterface +649 -21
  20. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.abi.json +22143 -0
  21. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +649 -21
  22. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  23. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/Modules/HawcxFramework.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +649 -21
  24. package/ios/Frameworks/HawcxFramework.xcframework/ios-arm64_x86_64-simulator/HawcxFramework.framework/_CodeSignature/CodeResources +21 -21
  25. package/ios/HawcxReactNative.m +56 -0
  26. package/ios/HawcxReactNative.swift +380 -1
  27. package/ios/HawcxV6BridgeSupport.swift +468 -0
  28. package/lib/commonjs/index.js +326 -3
  29. package/lib/commonjs/index.js.map +1 -1
  30. package/lib/commonjs/v6Normalization.js +325 -0
  31. package/lib/commonjs/v6Normalization.js.map +1 -0
  32. package/lib/commonjs/v6State.js +186 -0
  33. package/lib/commonjs/v6State.js.map +1 -0
  34. package/lib/commonjs/v6Types.js +2 -0
  35. package/lib/commonjs/v6Types.js.map +1 -0
  36. package/lib/commonjs/v6WebLogin.js +101 -0
  37. package/lib/commonjs/v6WebLogin.js.map +1 -0
  38. package/lib/module/index.js +287 -1
  39. package/lib/module/index.js.map +1 -1
  40. package/lib/module/v6Normalization.js +318 -0
  41. package/lib/module/v6Normalization.js.map +1 -0
  42. package/lib/module/v6State.js +173 -0
  43. package/lib/module/v6State.js.map +1 -0
  44. package/lib/module/v6Types.js +2 -0
  45. package/lib/module/v6Types.js.map +1 -0
  46. package/lib/module/v6WebLogin.js +92 -0
  47. package/lib/module/v6WebLogin.js.map +1 -0
  48. package/lib/typescript/index.d.ts +83 -0
  49. package/lib/typescript/index.d.ts.map +1 -1
  50. package/lib/typescript/v6Normalization.d.ts +3 -0
  51. package/lib/typescript/v6Normalization.d.ts.map +1 -0
  52. package/lib/typescript/v6State.d.ts +13 -0
  53. package/lib/typescript/v6State.d.ts.map +1 -0
  54. package/lib/typescript/v6Types.d.ts +157 -0
  55. package/lib/typescript/v6Types.d.ts.map +1 -0
  56. package/lib/typescript/v6WebLogin.d.ts +32 -0
  57. package/lib/typescript/v6WebLogin.d.ts.map +1 -0
  58. package/package.json +21 -9
  59. package/src/index.ts +477 -0
  60. package/src/v6Normalization.ts +356 -0
  61. package/src/v6State.ts +238 -0
  62. package/src/v6Types.ts +194 -0
  63. package/src/v6WebLogin.ts +154 -0
  64. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  65. package/android/gradle/wrapper/gradle-wrapper.properties +0 -6
  66. package/android/gradlew +0 -185
  67. package/android/gradlew.bat +0 -89
  68. package/android/libs/hawcx-5.1.2.aar +0 -0
  69. package/docs/RELEASE.md +0 -129
  70. package/example/README.md +0 -59
  71. package/example/android/app/build.gradle +0 -126
  72. package/example/android/app/debug.keystore +0 -0
  73. package/example/android/app/proguard-rules.pro +0 -10
  74. package/example/android/app/src/debug/AndroidManifest.xml +0 -9
  75. package/example/android/app/src/main/AndroidManifest.xml +0 -27
  76. package/example/android/app/src/main/java/com/hawcx/example/MainActivity.kt +0 -22
  77. package/example/android/app/src/main/java/com/hawcx/example/MainApplication.kt +0 -45
  78. package/example/android/app/src/main/res/drawable/rn_edit_text_material.xml +0 -36
  79. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  80. package/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png +0 -0
  81. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  82. package/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png +0 -0
  83. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  84. package/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  85. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  86. package/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  87. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  88. package/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  89. package/example/android/app/src/main/res/values/strings.xml +0 -3
  90. package/example/android/app/src/main/res/values/styles.xml +0 -9
  91. package/example/android/build.gradle +0 -35
  92. package/example/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  93. package/example/android/gradle/wrapper/gradle-wrapper.properties +0 -7
  94. package/example/android/gradle.properties +0 -41
  95. package/example/android/gradlew +0 -249
  96. package/example/android/gradlew.bat +0 -92
  97. package/example/android/local.properties +0 -2
  98. package/example/android/settings.gradle +0 -38
  99. package/example/app.json +0 -4
  100. package/example/babel.config.js +0 -3
  101. package/example/e2e/README.md +0 -17
  102. package/example/e2e/hawcx-login.yaml +0 -14
  103. package/example/index.js +0 -5
  104. package/example/ios/.xcode.env +0 -11
  105. package/example/ios/HawcxExampleApp/AppDelegate.h +0 -6
  106. package/example/ios/HawcxExampleApp/AppDelegate.mm +0 -31
  107. package/example/ios/HawcxExampleApp/Images.xcassets/AppIcon.appiconset/Contents.json +0 -53
  108. package/example/ios/HawcxExampleApp/Images.xcassets/Contents.json +0 -6
  109. package/example/ios/HawcxExampleApp/Info.plist +0 -55
  110. package/example/ios/HawcxExampleApp/LaunchScreen.storyboard +0 -47
  111. package/example/ios/HawcxExampleApp/PrivacyInfo.xcprivacy +0 -37
  112. package/example/ios/HawcxExampleApp/main.m +0 -10
  113. package/example/ios/HawcxExampleApp.xcodeproj/project.pbxproj +0 -704
  114. package/example/ios/HawcxExampleApp.xcodeproj/project.xcworkspace/xcuserdata/agambhullar.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
  115. package/example/ios/HawcxExampleApp.xcodeproj/xcshareddata/xcschemes/HawcxExampleApp.xcscheme +0 -90
  116. package/example/ios/HawcxExampleApp.xcodeproj/xcuserdata/agambhullar.xcuserdatad/xcschemes/xcschememanagement.plist +0 -16
  117. package/example/ios/HawcxExampleApp.xcworkspace/contents.xcworkspacedata +0 -10
  118. package/example/ios/HawcxExampleAppTests/HawcxExampleAppTests.m +0 -66
  119. package/example/ios/HawcxExampleAppTests/Info.plist +0 -24
  120. package/example/ios/Podfile +0 -79
  121. package/example/ios/Podfile.lock +0 -1290
  122. package/example/metro.config.js +0 -16
  123. package/example/package-lock.json +0 -13220
  124. package/example/package.json +0 -30
  125. package/example/src/App.tsx +0 -755
  126. package/example/src/hawcx.config.ts +0 -25
  127. package/example/tsconfig.json +0 -8
  128. package/ios/Frameworks/.keep +0 -0
  129. package/lib/typescript/__tests__/index.test.d.ts +0 -2
  130. package/lib/typescript/__tests__/index.test.d.ts.map +0 -1
  131. package/react_mobile_sdk_plan.md +0 -242
  132. package/src/__tests__/index.test.ts +0 -206
@@ -0,0 +1,356 @@
1
+ import type {
2
+ HawcxV6AwaitApprovalPrompt,
3
+ HawcxV6CompletedPayload,
4
+ HawcxV6EnterCodePrompt,
5
+ HawcxV6ErrorDetails,
6
+ HawcxV6ErrorPayload,
7
+ HawcxV6FlowEvent,
8
+ HawcxV6Method,
9
+ HawcxV6PromptBase,
10
+ HawcxV6PromptPayload,
11
+ HawcxV6RedirectPrompt,
12
+ HawcxV6RiskInfo,
13
+ HawcxV6SelectMethodPrompt,
14
+ HawcxV6SetupSmsPrompt,
15
+ HawcxV6SetupTotpPrompt,
16
+ HawcxV6StepInfo,
17
+ } from './v6Types';
18
+
19
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
20
+ typeof value === 'object' && value !== null && !Array.isArray(value);
21
+
22
+ const asString = (value: unknown): string | undefined => {
23
+ if (typeof value !== 'string') {
24
+ return undefined;
25
+ }
26
+ const trimmed = value.trim();
27
+ return trimmed.length > 0 ? trimmed : undefined;
28
+ };
29
+
30
+ const asNumber = (value: unknown): number | undefined =>
31
+ typeof value === 'number' && Number.isFinite(value) ? value : undefined;
32
+
33
+ const asBoolean = (value: unknown): boolean | undefined =>
34
+ typeof value === 'boolean' ? value : undefined;
35
+
36
+ const asRecordArray = (value: unknown): Record<string, unknown>[] =>
37
+ Array.isArray(value) ? value.filter(isRecord) : [];
38
+
39
+ const normalizeStep = (value: unknown): HawcxV6StepInfo | undefined => {
40
+ if (!isRecord(value)) {
41
+ return undefined;
42
+ }
43
+ const id = asString(value.id);
44
+ if (!id) {
45
+ return undefined;
46
+ }
47
+ return {
48
+ id,
49
+ label: asString(value.label),
50
+ };
51
+ };
52
+
53
+ const normalizeMethod = (value: unknown): HawcxV6Method | undefined => {
54
+ if (!isRecord(value)) {
55
+ return undefined;
56
+ }
57
+ const id = asString(value.id);
58
+ const label = asString(value.label);
59
+ if (!id || !label) {
60
+ return undefined;
61
+ }
62
+ return {
63
+ id,
64
+ label,
65
+ icon: asString(value.icon),
66
+ };
67
+ };
68
+
69
+ const normalizeRisk = (value: unknown): HawcxV6RiskInfo | undefined => {
70
+ if (!isRecord(value)) {
71
+ return undefined;
72
+ }
73
+ const detected = asBoolean(value.detected);
74
+ if (detected === undefined) {
75
+ return undefined;
76
+ }
77
+ const reasons = Array.isArray(value.reasons)
78
+ ? value.reasons.map(asString).filter((item): item is string => Boolean(item))
79
+ : [];
80
+ const location = isRecord(value.location)
81
+ ? {
82
+ city: asString(value.location.city),
83
+ country: asString(value.location.country),
84
+ }
85
+ : undefined;
86
+ return {
87
+ detected,
88
+ reasons,
89
+ message: asString(value.message),
90
+ location:
91
+ location && (location.city !== undefined || location.country !== undefined)
92
+ ? location
93
+ : undefined,
94
+ riskScore: asNumber(value.riskScore),
95
+ };
96
+ };
97
+
98
+ const normalizeErrorDetails = (value: unknown): HawcxV6ErrorDetails | undefined => {
99
+ if (!isRecord(value)) {
100
+ return undefined;
101
+ }
102
+ const errors = asRecordArray(value.errors)
103
+ .map((entry) => {
104
+ const field = asString(entry.field);
105
+ const message = asString(entry.message);
106
+ if (!field || !message) {
107
+ return undefined;
108
+ }
109
+ return { field, message };
110
+ })
111
+ .filter((entry): entry is NonNullable<typeof entry> => Boolean(entry));
112
+
113
+ const details: HawcxV6ErrorDetails = {
114
+ retryAfterSeconds: asNumber(value.retryAfterSeconds),
115
+ retryAt: asString(value.retryAt),
116
+ attemptsRemaining: asNumber(value.attemptsRemaining),
117
+ errors: errors.length > 0 ? errors : undefined,
118
+ };
119
+
120
+ if (
121
+ details.retryAfterSeconds === undefined &&
122
+ details.retryAt === undefined &&
123
+ details.attemptsRemaining === undefined &&
124
+ details.errors === undefined
125
+ ) {
126
+ return undefined;
127
+ }
128
+
129
+ return details;
130
+ };
131
+
132
+ const normalizePromptBase = (value: Record<string, unknown>): HawcxV6PromptBase | null => {
133
+ const session = asString(value.session);
134
+ const traceId = asString(value.traceId);
135
+ const expiresAt = asString(value.expiresAt);
136
+ if (!session || !traceId || !expiresAt) {
137
+ return null;
138
+ }
139
+
140
+ const step =
141
+ normalizeStep(value.step) ??
142
+ normalizeStep({
143
+ id: value.stepId,
144
+ label: value.stepLabel,
145
+ });
146
+
147
+ return {
148
+ session,
149
+ traceId,
150
+ expiresAt,
151
+ step,
152
+ risk: normalizeRisk(value.risk),
153
+ codeChannel: asString(value.codeChannel),
154
+ };
155
+ };
156
+
157
+ const normalizePromptPayload = (value: unknown): HawcxV6PromptPayload | null => {
158
+ if (!isRecord(value)) {
159
+ return null;
160
+ }
161
+
162
+ const base = normalizePromptBase(value);
163
+ if (!base) {
164
+ return null;
165
+ }
166
+
167
+ const rawPrompt = isRecord(value.prompt) ? value.prompt : undefined;
168
+ const promptType = asString(rawPrompt?.type) ?? asString(value.promptType);
169
+ if (!promptType) {
170
+ return null;
171
+ }
172
+
173
+ switch (promptType) {
174
+ case 'select_method': {
175
+ const methodsSource = rawPrompt?.methods ?? value.methods;
176
+ const methods = Array.isArray(methodsSource)
177
+ ? methodsSource.map(normalizeMethod).filter((item): item is HawcxV6Method => Boolean(item))
178
+ : [];
179
+ const payload: HawcxV6SelectMethodPrompt = {
180
+ ...base,
181
+ prompt: {
182
+ type: 'select_method',
183
+ methods,
184
+ phase: asString(rawPrompt?.phase ?? value.phase),
185
+ },
186
+ };
187
+ return payload;
188
+ }
189
+ case 'enter_code': {
190
+ const destination = asString(rawPrompt?.destination ?? value.destination);
191
+ if (!destination) {
192
+ return null;
193
+ }
194
+ const payload: HawcxV6EnterCodePrompt = {
195
+ ...base,
196
+ prompt: {
197
+ type: 'enter_code',
198
+ destination,
199
+ codeLength: asNumber(rawPrompt?.codeLength ?? value.codeLength),
200
+ codeFormat: asString(rawPrompt?.codeFormat ?? value.codeFormat),
201
+ codeExpiresAt: asString(rawPrompt?.codeExpiresAt ?? value.codeExpiresAt),
202
+ resendAt: asString(rawPrompt?.resendAt ?? value.resendAt),
203
+ },
204
+ };
205
+ return payload;
206
+ }
207
+ case 'enter_totp':
208
+ return {
209
+ ...base,
210
+ prompt: { type: 'enter_totp' },
211
+ };
212
+ case 'setup_totp': {
213
+ const secret = asString(rawPrompt?.secret ?? value.secret);
214
+ const otpauthUrl = asString(rawPrompt?.otpauthUrl ?? value.otpauthUrl);
215
+ if (!secret || !otpauthUrl) {
216
+ return null;
217
+ }
218
+ const payload: HawcxV6SetupTotpPrompt = {
219
+ ...base,
220
+ prompt: {
221
+ type: 'setup_totp',
222
+ secret,
223
+ otpauthUrl,
224
+ period: asNumber(rawPrompt?.period ?? value.period),
225
+ },
226
+ };
227
+ return payload;
228
+ }
229
+ case 'setup_sms': {
230
+ const payload: HawcxV6SetupSmsPrompt = {
231
+ ...base,
232
+ prompt: {
233
+ type: 'setup_sms',
234
+ existingPhone: asString(rawPrompt?.existingPhone ?? value.existingPhone),
235
+ },
236
+ };
237
+ return payload;
238
+ }
239
+ case 'redirect': {
240
+ const url = asString(rawPrompt?.url ?? value.url);
241
+ if (!url) {
242
+ return null;
243
+ }
244
+ const payload: HawcxV6RedirectPrompt = {
245
+ ...base,
246
+ prompt: {
247
+ type: 'redirect',
248
+ url,
249
+ returnScheme: asString(rawPrompt?.returnScheme ?? value.returnScheme),
250
+ },
251
+ };
252
+ return payload;
253
+ }
254
+ case 'await_approval': {
255
+ const promptExpiresAt = asString(
256
+ rawPrompt?.expiresAt ?? value.promptExpiresAt ?? value.awaitExpiresAt,
257
+ );
258
+ const pollInterval = asNumber(rawPrompt?.pollInterval ?? value.pollInterval);
259
+ if (!promptExpiresAt || pollInterval === undefined) {
260
+ return null;
261
+ }
262
+ const payload: HawcxV6AwaitApprovalPrompt = {
263
+ ...base,
264
+ prompt: {
265
+ type: 'await_approval',
266
+ qrData: asString(rawPrompt?.qrData ?? value.qrData),
267
+ expiresAt: promptExpiresAt,
268
+ pollInterval,
269
+ },
270
+ };
271
+ return payload;
272
+ }
273
+ default:
274
+ return null;
275
+ }
276
+ };
277
+
278
+ const normalizeCompletedPayload = (value: unknown): HawcxV6CompletedPayload | null => {
279
+ if (!isRecord(value)) {
280
+ return null;
281
+ }
282
+ const session = asString(value.session);
283
+ const authCode = asString(value.authCode);
284
+ const expiresAt = asString(value.expiresAt);
285
+ const traceId = asString(value.traceId);
286
+ if (!session || !authCode || !expiresAt || !traceId) {
287
+ return null;
288
+ }
289
+ return {
290
+ session,
291
+ authCode,
292
+ expiresAt,
293
+ codeVerifier: asString(value.codeVerifier),
294
+ traceId,
295
+ };
296
+ };
297
+
298
+ const normalizeErrorPayload = (value: unknown): HawcxV6ErrorPayload | null => {
299
+ if (!isRecord(value)) {
300
+ return null;
301
+ }
302
+ const code = asString(value.code);
303
+ const message = asString(value.message);
304
+ const retryable = asBoolean(value.retryable);
305
+ if (!code || !message || retryable === undefined) {
306
+ return null;
307
+ }
308
+ return {
309
+ session: asString(value.session),
310
+ code,
311
+ action: asString(value.action),
312
+ message,
313
+ retryable,
314
+ details: normalizeErrorDetails(value.details),
315
+ traceId: asString(value.traceId),
316
+ };
317
+ };
318
+
319
+ export const normalizeV6FlowEvent = (value: unknown): HawcxV6FlowEvent | null => {
320
+ if (!isRecord(value)) {
321
+ return null;
322
+ }
323
+
324
+ const type = asString(value.type);
325
+ if (!type) {
326
+ return null;
327
+ }
328
+
329
+ switch (type) {
330
+ case 'idle':
331
+ return { type: 'idle' };
332
+ case 'loading': {
333
+ const payload = isRecord(value.payload) ? value.payload : {};
334
+ return {
335
+ type: 'loading',
336
+ payload: {
337
+ session: asString(payload.session),
338
+ },
339
+ };
340
+ }
341
+ case 'prompt': {
342
+ const payload = normalizePromptPayload(value.payload);
343
+ return payload ? { type: 'prompt', payload } : null;
344
+ }
345
+ case 'completed': {
346
+ const payload = normalizeCompletedPayload(value.payload);
347
+ return payload ? { type: 'completed', payload } : null;
348
+ }
349
+ case 'error': {
350
+ const payload = normalizeErrorPayload(value.payload);
351
+ return payload ? { type: 'error', payload } : null;
352
+ }
353
+ default:
354
+ return null;
355
+ }
356
+ };
package/src/v6State.ts ADDED
@@ -0,0 +1,238 @@
1
+ import type {
2
+ HawcxV6AuthState,
3
+ HawcxV6CompletedPayload,
4
+ HawcxV6ErrorPayload,
5
+ HawcxV6FlowEvent,
6
+ HawcxV6FlowType,
7
+ HawcxV6PromptPayload,
8
+ HawcxV6ResendAvailability,
9
+ } from './v6Types';
10
+
11
+ const DEFAULT_FLOW_TYPE: HawcxV6FlowType = 'signin';
12
+
13
+ const EMPTY_RESEND_STATE: HawcxV6ResendAvailability = {
14
+ canResend: false,
15
+ };
16
+
17
+ const isFiniteTimestamp = (value: string | undefined): number | null => {
18
+ if (!value) {
19
+ return null;
20
+ }
21
+ const timestamp = Date.parse(value);
22
+ return Number.isFinite(timestamp) ? timestamp : null;
23
+ };
24
+
25
+ const isEnterCodePrompt = (
26
+ prompt: HawcxV6PromptPayload | undefined,
27
+ ): prompt is Extract<HawcxV6PromptPayload, { prompt: { type: 'enter_code' } }> =>
28
+ prompt?.prompt.type === 'enter_code';
29
+
30
+ export const normalizeHawcxV6FlowType = (value?: HawcxV6FlowType): HawcxV6FlowType => {
31
+ switch (value) {
32
+ case 'signup':
33
+ case 'account_manage':
34
+ return value;
35
+ case 'signin':
36
+ default:
37
+ return DEFAULT_FLOW_TYPE;
38
+ }
39
+ };
40
+
41
+ export const getHawcxV6ResendAvailability = (
42
+ prompt?: HawcxV6PromptPayload,
43
+ nowMs: number = Date.now(),
44
+ ): HawcxV6ResendAvailability => {
45
+ if (!isEnterCodePrompt(prompt)) {
46
+ return EMPTY_RESEND_STATE;
47
+ }
48
+
49
+ const resendAt = prompt.prompt.resendAt;
50
+ const resendTimestamp = isFiniteTimestamp(resendAt);
51
+
52
+ if (resendTimestamp === null) {
53
+ return {
54
+ canResend: true,
55
+ resendAt,
56
+ secondsUntilResend: 0,
57
+ };
58
+ }
59
+
60
+ const diffMs = resendTimestamp - nowMs;
61
+ if (diffMs <= 0) {
62
+ return {
63
+ canResend: true,
64
+ resendAt,
65
+ secondsUntilResend: 0,
66
+ };
67
+ }
68
+
69
+ return {
70
+ canResend: false,
71
+ resendAt,
72
+ secondsUntilResend: Math.ceil(diffMs / 1000),
73
+ };
74
+ };
75
+
76
+ export const canHawcxV6Resend = (
77
+ promptOrState?: HawcxV6PromptPayload | HawcxV6AuthState,
78
+ nowMs: number = Date.now(),
79
+ ): boolean => {
80
+ if (!promptOrState) {
81
+ return false;
82
+ }
83
+
84
+ const prompt = 'status' in promptOrState ? promptOrState.prompt : promptOrState;
85
+
86
+ return getHawcxV6ResendAvailability(prompt, nowMs).canResend;
87
+ };
88
+
89
+ export const createInitialHawcxV6AuthState = (flowType?: HawcxV6FlowType): HawcxV6AuthState => ({
90
+ status: 'identifier',
91
+ flowType: normalizeHawcxV6FlowType(flowType),
92
+ resend: EMPTY_RESEND_STATE,
93
+ isLoading: false,
94
+ requiresRedirect: false,
95
+ awaitingApproval: false,
96
+ });
97
+
98
+ export const createIdentifierHawcxV6AuthState = ({
99
+ previous,
100
+ identifier,
101
+ flowType,
102
+ }: {
103
+ previous?: HawcxV6AuthState;
104
+ identifier?: string;
105
+ flowType?: HawcxV6FlowType;
106
+ } = {}): HawcxV6AuthState => ({
107
+ status: 'identifier',
108
+ flowType: normalizeHawcxV6FlowType(flowType ?? previous?.flowType),
109
+ identifier,
110
+ resend: EMPTY_RESEND_STATE,
111
+ isLoading: false,
112
+ requiresRedirect: false,
113
+ awaitingApproval: false,
114
+ });
115
+
116
+ const reducePromptState = (
117
+ prompt: HawcxV6PromptPayload,
118
+ previous: HawcxV6AuthState,
119
+ nowMs: number,
120
+ ): HawcxV6AuthState => {
121
+ const resend = getHawcxV6ResendAvailability(prompt, nowMs);
122
+
123
+ return {
124
+ status: prompt.prompt.type,
125
+ flowType: previous.flowType,
126
+ identifier: previous.identifier,
127
+ session: prompt.session,
128
+ traceId: prompt.traceId,
129
+ expiresAt: prompt.expiresAt,
130
+ step: prompt.step,
131
+ risk: prompt.risk,
132
+ codeChannel: prompt.codeChannel,
133
+ prompt,
134
+ completed: undefined,
135
+ error: undefined,
136
+ resend,
137
+ isLoading: false,
138
+ requiresRedirect: prompt.prompt.type === 'redirect',
139
+ awaitingApproval: prompt.prompt.type === 'await_approval',
140
+ };
141
+ };
142
+
143
+ const reduceCompletedState = (
144
+ completed: HawcxV6CompletedPayload,
145
+ previous: HawcxV6AuthState,
146
+ ): HawcxV6AuthState => ({
147
+ status: 'completed',
148
+ flowType: previous.flowType,
149
+ identifier: previous.identifier,
150
+ session: completed.session,
151
+ traceId: completed.traceId,
152
+ expiresAt: completed.expiresAt,
153
+ prompt: undefined,
154
+ completed,
155
+ error: undefined,
156
+ resend: EMPTY_RESEND_STATE,
157
+ isLoading: false,
158
+ requiresRedirect: false,
159
+ awaitingApproval: false,
160
+ });
161
+
162
+ const reduceErrorState = (
163
+ error: HawcxV6ErrorPayload,
164
+ previous: HawcxV6AuthState,
165
+ ): HawcxV6AuthState => ({
166
+ status: 'error',
167
+ flowType: previous.flowType,
168
+ identifier: previous.identifier,
169
+ session: error.session ?? previous.session,
170
+ traceId: error.traceId ?? previous.traceId,
171
+ expiresAt: previous.expiresAt,
172
+ step: previous.step,
173
+ risk: previous.risk,
174
+ codeChannel: previous.codeChannel,
175
+ prompt: previous.prompt,
176
+ completed: undefined,
177
+ error,
178
+ resend: previous.resend,
179
+ isLoading: false,
180
+ requiresRedirect: false,
181
+ awaitingApproval: false,
182
+ });
183
+
184
+ export const reduceHawcxV6FlowEvent = (
185
+ event: HawcxV6FlowEvent,
186
+ previous: HawcxV6AuthState,
187
+ nowMs: number = Date.now(),
188
+ ): HawcxV6AuthState => {
189
+ switch (event.type) {
190
+ case 'idle':
191
+ return createIdentifierHawcxV6AuthState({
192
+ previous,
193
+ identifier: previous.identifier,
194
+ });
195
+ case 'loading':
196
+ return {
197
+ ...previous,
198
+ status: 'loading',
199
+ session: event.payload.session ?? previous.session,
200
+ isLoading: true,
201
+ requiresRedirect: false,
202
+ awaitingApproval: false,
203
+ error: undefined,
204
+ completed: undefined,
205
+ };
206
+ case 'prompt':
207
+ return reducePromptState(event.payload, previous, nowMs);
208
+ case 'completed':
209
+ return reduceCompletedState(event.payload, previous);
210
+ case 'error':
211
+ return reduceErrorState(event.payload, previous);
212
+ default:
213
+ return previous;
214
+ }
215
+ };
216
+
217
+ export const refreshHawcxV6AuthState = (
218
+ state: HawcxV6AuthState,
219
+ nowMs: number = Date.now(),
220
+ ): HawcxV6AuthState => {
221
+ if (!state.prompt) {
222
+ return state;
223
+ }
224
+
225
+ const resend = getHawcxV6ResendAvailability(state.prompt, nowMs);
226
+ if (
227
+ resend.canResend === state.resend.canResend &&
228
+ resend.resendAt === state.resend.resendAt &&
229
+ resend.secondsUntilResend === state.resend.secondsUntilResend
230
+ ) {
231
+ return state;
232
+ }
233
+
234
+ return {
235
+ ...state,
236
+ resend,
237
+ };
238
+ };