@sekyro/react-native 0.0.1-stage → 0.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/LICENSE +29 -0
- package/README.md +76 -2
- package/SekyroReactNative.podspec +22 -0
- package/android/build.gradle +38 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/ai/sekyro/reactnative/ConfigMapper.kt +73 -0
- package/android/src/main/java/ai/sekyro/reactnative/SekyroReactNativeModule.kt +107 -0
- package/android/src/main/java/ai/sekyro/reactnative/SekyroReactNativePackage.kt +26 -0
- package/app.plugin.js +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +99 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +1 -0
- package/dist/package.json +1 -0
- package/ios/RCTSekyroReactNative.h +5 -0
- package/ios/RCTSekyroReactNative.mm +57 -0
- package/ios/SekyroRNBridge.swift +108 -0
- package/package.json +97 -6
- package/plugin/android.js +24 -0
- package/plugin/index.js +76 -0
- package/plugin/ios.js +61 -0
- package/sbom.cdx.json +153 -0
- package/scripts/sekyro_pods.rb +27 -0
- package/src/specs/NativeSekyroReactNative.ts +46 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @sekyro/react-native
|
|
2
|
+
|
|
3
|
+
## 0.1.0-rc.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e9dd971: Flow model 2026-10-23 (SDK spec §3.7): the native Android and iOS drop-ins render the three-section flow (contact → identity → live photo) with consent as one line on the first screen, the phone-or-email choice, the identity summary before the live photo, the document chip as its own method, and one camera session for the live photo with the ICAO portrait. Step events report the new `SekyroStep` values `'contact_choice'`, `'identity_summary'` (never the details shown) and `'portrait'` (with the server's `icao_*` reason as `retryReason`); `'consent'` is no longer emitted (consent is one line on the first screen). **Migration (TypeScript):** code that switches exhaustively over `SekyroStep` must add the three new branches.
|
|
8
|
+
- 088fa55: National eID (SDK spec §3.2; Houwiyeti in Mauritania): the native Android and iOS drop-ins now run the eID screens, and step events report them as the new `SekyroStep` value `'eid'` (with the server's `eid_*` reason as `retryReason`). **Migration (TypeScript):** code that switches exhaustively over `SekyroStep` must add an `'eid'` branch.
|
|
9
|
+
- f52408e: First release candidate of the React Native SDK: `startSekyro()` and `<SekyroFlow>` over the native Android (Maven Central) and iOS (`sekyro-ios-dist`) SDKs 0.1.0-rc.2, step events, theming and string overrides, Expo config plugin. New Architecture only (React Native ≥ 0.76).
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 45f639f: Licence: the SDKs are now under the Sekyro AI SDK License of Ibtikar Technologies, Inc. (https://sekyro.ai/en/legal/sdk-license, also in French and Arabic). `package.json#license` is `SEE LICENSE IN LICENSE`, and each package's `LICENSE` summarises the licence and links to it. `@sekyro/web` now also ships `THIRD-PARTY-NOTICES.txt` with the MIT notice of the bundled scanic document detector and the MIT/NCSA notice of the Emscripten glue in its OCR worker.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Sekyro AI SDK License (proprietary)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ibtikar Technologies, Inc. All rights reserved.
|
|
4
|
+
|
|
5
|
+
This SDK is proprietary software of Ibtikar Technologies, Inc., licensed
|
|
6
|
+
only under the Sekyro AI SDK License:
|
|
7
|
+
https://sekyro.ai/en/legal/sdk-license
|
|
8
|
+
(in French: https://sekyro.ai/fr/legal/sdk-license,
|
|
9
|
+
in Arabic: https://sekyro.ai/ar/legal/sdk-license).
|
|
10
|
+
|
|
11
|
+
In short, and subject to the full license:
|
|
12
|
+
|
|
13
|
+
- You may use the SDK only with a Sekyro AI account, a customer agreement
|
|
14
|
+
with Ibtikar Technologies, Inc. and valid API keys, to integrate the
|
|
15
|
+
Sekyro AI services into your own applications. You may distribute it to
|
|
16
|
+
your end users only as embedded in those applications.
|
|
17
|
+
- You may not use it with another identity verification provider, modify it
|
|
18
|
+
beyond the configuration and theming it exposes, reverse engineer,
|
|
19
|
+
decompile or disassemble it (except where the law expressly allows it),
|
|
20
|
+
circumvent its security, attestation, license checks or usage metering,
|
|
21
|
+
or distribute or sublicense it on its own.
|
|
22
|
+
- Open-source components keep their own licenses. See the third-party
|
|
23
|
+
notices shipped with this SDK.
|
|
24
|
+
- The SDK is provided "as is", without warranty, except as your customer
|
|
25
|
+
agreement provides. The license is governed by the laws of the State of
|
|
26
|
+
Delaware, USA. If this license and your customer agreement conflict,
|
|
27
|
+
the customer agreement prevails.
|
|
28
|
+
|
|
29
|
+
Questions: contact@sekyro.ai
|
package/README.md
CHANGED
|
@@ -1,3 +1,77 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @sekyro/react-native
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Sekyro AI eKYC for React Native: a thin TurboModule over the binary Sekyro Android and iOS SDKs.
|
|
4
|
+
The native drop-in runs the whole flow (consent, document, NFC chip, liveness) full screen and
|
|
5
|
+
returns a display-only result. Read the decision on your server (webhooks or `sessions.get`).
|
|
6
|
+
|
|
7
|
+
Requirements: React Native 0.76 or later with the New Architecture, Android minSdk 24, iOS 15.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @sekyro/react-native
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### iOS (Podfile)
|
|
16
|
+
|
|
17
|
+
The pod depends on `SekyroKit`, which is not on CocoaPods trunk. Declare its source once in the
|
|
18
|
+
app target, after `use_native_modules!`:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
require Pod::Executable.execute_command('node', ['-p',
|
|
22
|
+
'require.resolve("@sekyro/react-native/package.json", {paths: [process.argv[1]]})', __dir__]
|
|
23
|
+
).strip.sub(/package\.json$/, 'scripts/sekyro_pods.rb')
|
|
24
|
+
|
|
25
|
+
target 'App' do
|
|
26
|
+
config = use_native_modules!
|
|
27
|
+
sekyro_kit_pod!
|
|
28
|
+
# ...
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Add `NSCameraUsageDescription` and, for chip reading, `NFCReaderUsageDescription`, the ISO 7816
|
|
33
|
+
identifiers `A0000002471001` and `A0000002472001`, and the NFC `TAG` entitlement.
|
|
34
|
+
|
|
35
|
+
### Android (Gradle)
|
|
36
|
+
|
|
37
|
+
The native core comes from Maven Central (`ai.sekyro:sekyro-bom` + `ai.sekyro:sekyro-ui`), so
|
|
38
|
+
`mavenCentral()` must be in your repositories. Set `minSdkVersion` to 24 or more. The core's
|
|
39
|
+
manifest merges the camera and NFC permissions and `SekyroActivity`; nothing else to add.
|
|
40
|
+
|
|
41
|
+
### Expo
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
{ "plugins": [["@sekyro/react-native", { "nfc": true, "appAttest": "production" }]] }
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Then `npx expo prebuild`. Options: `nfc` (default `true`), `appAttest`
|
|
48
|
+
(`'production'` | `'development'` | `false`), `cameraPermission`, `nfcPermission`.
|
|
49
|
+
|
|
50
|
+
## Use
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { startSekyro } from '@sekyro/react-native';
|
|
54
|
+
|
|
55
|
+
const result = await startSekyro({
|
|
56
|
+
clientToken, // from your backend: sekyro.sessions.create()
|
|
57
|
+
locale: 'fr',
|
|
58
|
+
theme: { colors: { brand: '#0F68B1' }, radius: 12 },
|
|
59
|
+
onEvent: (e) => console.log(e.type), // step telemetry, no personal data
|
|
60
|
+
});
|
|
61
|
+
// { status: 'finished', sessionId, displayStatus } | { status: 'cancelled' } | { status: 'failed', code }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or declaratively: `<SekyroFlow clientToken={token} onResult={setResult} />` (renders nothing).
|
|
65
|
+
|
|
66
|
+
Never log the `clientToken`; it is a one-time bearer credential.
|
|
67
|
+
|
|
68
|
+
While a flow runs, the wrapper owns the native SDK's process-wide event listener
|
|
69
|
+
(`Sekyro.setEventListener` on Android, the drop-in's `onEvent` on iOS) and clears it when the
|
|
70
|
+
flow ends, so a listener your app set natively is replaced for that time. A flow interrupted
|
|
71
|
+
because the React instance was torn down resolves `{ status: 'failed', code: 'flow_interrupted' }`.
|
|
72
|
+
|
|
73
|
+
Full guide: [React Native guide](https://docs.sekyro.ai/en/mobile/react-native).
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
Proprietary: the [Sekyro AI SDK License](https://sekyro.ai/en/legal/sdk-license). See [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @sekyro/react-native — iOS glue (TurboModule over the binary SekyroKit.xcframework).
|
|
2
|
+
require 'json'
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'SekyroReactNative'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.homepage = 'https://sekyro.ai'
|
|
10
|
+
s.license = { :type => 'Proprietary', :file => 'LICENSE' }
|
|
11
|
+
s.authors = 'Ibtikar Technologies, Inc.'
|
|
12
|
+
s.platforms = { :ios => '15.1' }
|
|
13
|
+
s.source = { :http => "https://registry.npmjs.org/@sekyro/react-native/-/react-native-#{package['version']}.tgz" }
|
|
14
|
+
s.source_files = 'ios/**/*.{h,mm,swift}'
|
|
15
|
+
# The TurboModule header imports the C++ codegen header: keep it out of the module umbrella that Swift imports.
|
|
16
|
+
s.private_header_files = 'ios/RCTSekyroReactNative.h'
|
|
17
|
+
s.swift_version = '5.9'
|
|
18
|
+
# The binary core, pinned by package.json#sekyro.ios. WHERE it comes from is declared in the app's Podfile
|
|
19
|
+
# by `sekyro_kit_pod!` (scripts/sekyro_pods.rb): the public sekyro-ios-dist podspec, or a local dist tree.
|
|
20
|
+
s.dependency 'SekyroKit', package['sekyro']['ios']
|
|
21
|
+
install_modules_dependencies(s)
|
|
22
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// @sekyro/react-native — Android glue (TurboModule over the binary ai.sekyro:sekyro-ui drop-in).
|
|
2
|
+
// The Sekyro SDK itself is a Maven dependency pinned by package.json#sekyro.android: no AAR ships in npm.
|
|
3
|
+
import groovy.json.JsonSlurper
|
|
4
|
+
|
|
5
|
+
def pkg = new JsonSlurper().parse(file("../package.json"))
|
|
6
|
+
def safeExtGet = { prop, fallback -> rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback }
|
|
7
|
+
|
|
8
|
+
// Development only: resolve ai.sekyro from ~/.m2 after `./gradlew publishToMavenLocal` in sekyro-mobile/android.
|
|
9
|
+
// The app resolves its runtime classpath with ITS repositories, so the content-filtered repo is added to every project.
|
|
10
|
+
// `-Psekyro.version=<v>` overrides the pin (a local publish is versioned VERSION_NAME, e.g. 0.1.0-SNAPSHOT).
|
|
11
|
+
def sekyroLocal = rootProject.findProperty("sekyro.mavenLocal") == "true"
|
|
12
|
+
def sekyroVersion = rootProject.findProperty("sekyro.version") ?: pkg.sekyro.android
|
|
13
|
+
if (sekyroLocal) {
|
|
14
|
+
rootProject.allprojects { repositories { mavenLocal { content { includeGroup "ai.sekyro" } } } }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
apply plugin: "com.android.library"
|
|
18
|
+
apply plugin: "org.jetbrains.kotlin.android"
|
|
19
|
+
apply plugin: "com.facebook.react"
|
|
20
|
+
|
|
21
|
+
android {
|
|
22
|
+
namespace "ai.sekyro.reactnative"
|
|
23
|
+
compileSdk safeExtGet("compileSdkVersion", 36)
|
|
24
|
+
defaultConfig { minSdk Math.max(24, safeExtGet("minSdkVersion", 24) as int) }
|
|
25
|
+
compileOptions { sourceCompatibility JavaVersion.VERSION_17; targetCompatibility JavaVersion.VERSION_17 }
|
|
26
|
+
testOptions { unitTests.includeAndroidResources = true }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
kotlin { jvmToolchain(17) }
|
|
30
|
+
|
|
31
|
+
dependencies {
|
|
32
|
+
implementation "com.facebook.react:react-android"
|
|
33
|
+
implementation platform("ai.sekyro:sekyro-bom:${sekyroVersion}")
|
|
34
|
+
implementation "ai.sekyro:sekyro-ui"
|
|
35
|
+
testImplementation "junit:junit:4.13.2"
|
|
36
|
+
testImplementation "org.robolectric:robolectric:4.15.1"
|
|
37
|
+
testImplementation "androidx.test:core:1.6.1"
|
|
38
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
package ai.sekyro.reactnative
|
|
2
|
+
|
|
3
|
+
import ai.sekyro.SekyroConfig
|
|
4
|
+
import ai.sekyro.SekyroLocale
|
|
5
|
+
import ai.sekyro.SekyroResult
|
|
6
|
+
import ai.sekyro.SekyroTheme
|
|
7
|
+
import ai.sekyro.core.DisplayStatus
|
|
8
|
+
import com.facebook.react.bridge.Arguments
|
|
9
|
+
import com.facebook.react.bridge.ReadableMap
|
|
10
|
+
import com.facebook.react.bridge.ReadableType
|
|
11
|
+
import com.facebook.react.bridge.WritableMap
|
|
12
|
+
|
|
13
|
+
/** JS wire ↔ Sekyro types. The JS layer validated the config; this re-checks the shape and never logs it. */
|
|
14
|
+
internal object ConfigMapper {
|
|
15
|
+
private val HEX = Regex("^#[0-9a-fA-F]{6}$")
|
|
16
|
+
|
|
17
|
+
private fun ReadableMap.str(key: String): String? =
|
|
18
|
+
if (hasKey(key) && !isNull(key) && getType(key) == ReadableType.String) getString(key) else null
|
|
19
|
+
|
|
20
|
+
private fun color(map: ReadableMap?, key: String): Int? {
|
|
21
|
+
val hex = map?.str(key) ?: return null
|
|
22
|
+
require(HEX.matches(hex)) { "invalid_config" }
|
|
23
|
+
return (0xFF000000 or hex.substring(1).toLong(16)).toInt()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fun toConfig(m: ReadableMap): SekyroConfig {
|
|
27
|
+
val token = m.str("clientToken").orEmpty()
|
|
28
|
+
require(token.isNotEmpty()) { "invalid_config" }
|
|
29
|
+
val colors = if (m.hasKey("colors") && !m.isNull("colors")) m.getMap("colors") else null
|
|
30
|
+
val radius = if (m.hasKey("radius") && !m.isNull("radius")) m.getDouble("radius") else null
|
|
31
|
+
require(radius == null || radius in 0.0..32.0) { "invalid_config" }
|
|
32
|
+
val darkMode = m.str("darkMode")
|
|
33
|
+
val hasTheme = colors != null || radius != null || darkMode != null
|
|
34
|
+
val theme = if (!hasTheme) null else SekyroTheme(
|
|
35
|
+
brand = color(colors, "brand"), brandFg = color(colors, "brandFg"),
|
|
36
|
+
radiusDp = radius?.toInt(),
|
|
37
|
+
darkMode = when (darkMode) {
|
|
38
|
+
"light" -> SekyroTheme.DarkMode.LIGHT; "dark" -> SekyroTheme.DarkMode.DARK; else -> SekyroTheme.DarkMode.SYSTEM
|
|
39
|
+
},
|
|
40
|
+
bg = color(colors, "bg"), surface = color(colors, "surface"), fg = color(colors, "fg"),
|
|
41
|
+
fgMuted = color(colors, "fgMuted"), danger = color(colors, "danger"),
|
|
42
|
+
success = color(colors, "success"), warning = color(colors, "warning"),
|
|
43
|
+
).also { color(colors, "border") } // `border` (validated) and `fontFamily`: not themable on Android in 0.1 (README).
|
|
44
|
+
val strings = if (m.hasKey("strings") && !m.isNull("strings")) {
|
|
45
|
+
m.getMap("strings")?.toHashMap().orEmpty().mapNotNull { (k, v) -> (v as? String)?.let { k to it } }.toMap()
|
|
46
|
+
} else emptyMap()
|
|
47
|
+
return SekyroConfig(
|
|
48
|
+
clientToken = token,
|
|
49
|
+
apiOrigin = m.str("apiOrigin"),
|
|
50
|
+
locale = when (m.str("locale")) {
|
|
51
|
+
"ar" -> SekyroLocale.AR; "fr" -> SekyroLocale.FR; "en" -> SekyroLocale.EN; else -> null
|
|
52
|
+
},
|
|
53
|
+
theme = theme,
|
|
54
|
+
strings = strings,
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** [into] is injectable so JVM tests can use a JavaOnlyMap (Arguments.createMap() needs the RN native libs). */
|
|
59
|
+
fun toWire(r: SekyroResult, into: WritableMap = Arguments.createMap()): WritableMap = into.apply {
|
|
60
|
+
when (r) {
|
|
61
|
+
is SekyroResult.Finished -> {
|
|
62
|
+
putString("status", "finished"); putString("sessionId", r.sessionId)
|
|
63
|
+
// EXPIRED stays on the wire; the JS layer maps it to { status: 'failed', code: 'session_expired' } (iOS parity).
|
|
64
|
+
putString("displayStatus", when (r.displayStatus) {
|
|
65
|
+
DisplayStatus.APPROVED -> "approved"; DisplayStatus.REVIEW_PENDING -> "review_pending"
|
|
66
|
+
DisplayStatus.REJECTED -> "rejected"; DisplayStatus.EXPIRED -> "expired"
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
SekyroResult.Cancelled -> putString("status", "cancelled")
|
|
70
|
+
is SekyroResult.Failed -> { putString("status", "failed"); putString("code", r.code) }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
package ai.sekyro.reactnative
|
|
2
|
+
|
|
3
|
+
import ai.sekyro.Sekyro
|
|
4
|
+
import ai.sekyro.SekyroContract
|
|
5
|
+
import ai.sekyro.SekyroEvent
|
|
6
|
+
import ai.sekyro.SekyroResult
|
|
7
|
+
import ai.sekyro.core.SekyroVersion
|
|
8
|
+
import android.app.Activity
|
|
9
|
+
import android.content.Intent
|
|
10
|
+
import com.facebook.react.bridge.Arguments
|
|
11
|
+
import com.facebook.react.bridge.BaseActivityEventListener
|
|
12
|
+
import com.facebook.react.bridge.Promise
|
|
13
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
14
|
+
import com.facebook.react.bridge.ReadableMap
|
|
15
|
+
import com.facebook.react.bridge.UiThreadUtil
|
|
16
|
+
import com.facebook.react.bridge.WritableMap
|
|
17
|
+
import java.util.concurrent.atomic.AtomicBoolean
|
|
18
|
+
import java.util.concurrent.atomic.AtomicInteger
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* `SekyroReactNative` TurboModule: presents the native drop-in (SekyroActivity) and forwards its step events.
|
|
22
|
+
* Never logs the config (the client token is a bearer credential).
|
|
23
|
+
*
|
|
24
|
+
* The result comes back through React Native's activity-event routing (`startActivityForResult` +
|
|
25
|
+
* [BaseActivityEventListener]), not an activity-result registry callback: when the RN host activity is
|
|
26
|
+
* recreated while SekyroActivity is on top (configuration change, "Don't keep activities"), the recreated
|
|
27
|
+
* `ReactActivity` still forwards `onActivityResult` to this module, so the promise always settles and the
|
|
28
|
+
* module never stays locked. If the React instance itself is torn down mid-flow, [invalidate] settles it as
|
|
29
|
+
* `failed / flow_interrupted`.
|
|
30
|
+
*/
|
|
31
|
+
class SekyroReactNativeModule(context: ReactApplicationContext) : NativeSekyroReactNativeSpec(context) {
|
|
32
|
+
private val running = AtomicBoolean(false)
|
|
33
|
+
private val seq = AtomicInteger(0)
|
|
34
|
+
@Volatile private var pending: Promise? = null
|
|
35
|
+
|
|
36
|
+
private val activityListener = object : BaseActivityEventListener() {
|
|
37
|
+
override fun onActivityResult(activity: Activity, requestCode: Int, resultCode: Int, data: Intent?) {
|
|
38
|
+
if (requestCode != REQUEST_CODE) return
|
|
39
|
+
settle(SekyroContract().parseResult(resultCode, data))
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
init {
|
|
44
|
+
context.addActivityEventListener(activityListener)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
override fun getName(): String = NAME
|
|
48
|
+
|
|
49
|
+
override fun nativeVersion(): String = "android ${SekyroVersion.name}"
|
|
50
|
+
|
|
51
|
+
override fun start(config: ReadableMap, promise: Promise) {
|
|
52
|
+
val parsed = try { ConfigMapper.toConfig(config) } catch (_: IllegalArgumentException) {
|
|
53
|
+
promise.reject("invalid_config", "invalid config"); return
|
|
54
|
+
}
|
|
55
|
+
val activity = reactApplicationContext.currentActivity
|
|
56
|
+
?: run { promise.reject("no_activity", "no foreground activity"); return }
|
|
57
|
+
if (!running.compareAndSet(false, true)) { promise.reject("flow_in_progress", "a Sekyro flow is already running"); return }
|
|
58
|
+
UiThreadUtil.runOnUiThread {
|
|
59
|
+
try {
|
|
60
|
+
if (activity.isFinishing || activity.isDestroyed) throw IllegalStateException("activity is finishing")
|
|
61
|
+
pending = promise
|
|
62
|
+
Sekyro.setEventListener { event -> emitOnEvent(toWire(event)) }
|
|
63
|
+
activity.startActivityForResult(SekyroContract().createIntent(activity, parsed), REQUEST_CODE)
|
|
64
|
+
} catch (_: Exception) {
|
|
65
|
+
// e.g. the activity is finishing, or the drop-in activity cannot be resolved.
|
|
66
|
+
pending = null
|
|
67
|
+
Sekyro.setEventListener(null)
|
|
68
|
+
running.set(false)
|
|
69
|
+
promise.reject("no_activity", "the foreground activity cannot launch the flow")
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Settle the running flow once (a duplicate or late result is ignored). */
|
|
75
|
+
private fun settle(result: SekyroResult) {
|
|
76
|
+
val p = pending ?: return
|
|
77
|
+
pending = null
|
|
78
|
+
Sekyro.setEventListener(null)
|
|
79
|
+
running.set(false)
|
|
80
|
+
p.resolve(ConfigMapper.toWire(result))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private fun toWire(e: SekyroEvent): WritableMap = Arguments.createMap().apply {
|
|
84
|
+
putInt("seq", seq.incrementAndGet())
|
|
85
|
+
when (e) {
|
|
86
|
+
is SekyroEvent.Step -> {
|
|
87
|
+
putString("type", "step"); putString("step", e.step.wire)
|
|
88
|
+
e.retryReason?.let { putString("retryReason", it) }
|
|
89
|
+
}
|
|
90
|
+
SekyroEvent.WaitingForNetwork -> putString("type", "waiting_for_network")
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
override fun invalidate() {
|
|
95
|
+
reactApplicationContext.removeActivityEventListener(activityListener)
|
|
96
|
+
// The React instance is going away with a flow still open: settle it rather than leak it.
|
|
97
|
+
settle(SekyroResult.Failed("flow_interrupted"))
|
|
98
|
+
Sekyro.setEventListener(null)
|
|
99
|
+
super.invalidate()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
companion object {
|
|
103
|
+
const val NAME = "SekyroReactNative"
|
|
104
|
+
/** Activity request code of the drop-in (any 16-bit value unique to this module). */
|
|
105
|
+
private const val REQUEST_CODE = 0x5E4B
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
package ai.sekyro.reactnative
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.BaseReactPackage
|
|
4
|
+
import com.facebook.react.bridge.NativeModule
|
|
5
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
6
|
+
import com.facebook.react.module.model.ReactModuleInfo
|
|
7
|
+
import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
8
|
+
|
|
9
|
+
/** Autolinked by the React Native CLI (New Architecture: a TurboModule). */
|
|
10
|
+
class SekyroReactNativePackage : BaseReactPackage() {
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? =
|
|
12
|
+
if (name == SekyroReactNativeModule.NAME) SekyroReactNativeModule(reactContext) else null
|
|
13
|
+
|
|
14
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider = ReactModuleInfoProvider {
|
|
15
|
+
mapOf(
|
|
16
|
+
SekyroReactNativeModule.NAME to ReactModuleInfo(
|
|
17
|
+
SekyroReactNativeModule.NAME,
|
|
18
|
+
SekyroReactNativeModule::class.java.name,
|
|
19
|
+
false, // canOverrideExistingModule
|
|
20
|
+
false, // needsEagerInit
|
|
21
|
+
false, // isCxxModule
|
|
22
|
+
true, // isTurboModule
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./plugin');
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var u=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var h=Object.prototype.hasOwnProperty;var C=(e,r)=>{for(var t in r)u(e,t,{get:r[t],enumerable:!0})},O=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let i of w(r))!h.call(e,i)&&i!==t&&u(e,i,{get:()=>r[i],enumerable:!(n=N(r,i))||n.enumerable});return e};var x=e=>O(u({},"__esModule",{value:!0}),e);var A={};C(A,{SDK_VERSION:()=>R,SekyroError:()=>l,SekyroFlow:()=>E,nativeVersion:()=>_,startSekyro:()=>f});module.exports=x(A);var S=require("react-native"),d=S.TurboModuleRegistry.get("SekyroReactNative");var l=class extends Error{code;constructor(r,t=r){super(t),this.name="SekyroError",this.code=r}};var b=500,T=new Set(["ar","fr","en"]),F=["brand","brandFg","bg","surface","fg","fgMuted","border","danger","success","warning"],I=new Set(["clientToken","apiOrigin","locale","theme","strings","onEvent"]),D=/^#[0-9a-fA-F]{6}$/,V=/^http:\/\/(localhost|127\.0\.0\.1|10\.0\.2\.2)(:\d+)?$/,a=e=>{throw new l("invalid_config",`invalid config: ${e}`)};function k(e){(typeof e!="object"||e===null)&&a("config");for(let n of Object.keys(e))I.has(n)||a(`unknown key "${n.slice(0,32)}"`);(typeof e.clientToken!="string"||e.clientToken.length===0)&&a("clientToken");let r={clientToken:e.clientToken};e.apiOrigin!==void 0&&(typeof e.apiOrigin=="string"&&(/^https:\/\/[a-z0-9.-]+(:\d+)?$/i.test(e.apiOrigin)||__DEV__&&V.test(e.apiOrigin))||a("apiOrigin"),r.apiOrigin=e.apiOrigin),e.locale!==void 0&&(T.has(e.locale)||a("locale"),r.locale=e.locale);let t=e.theme;if(t){if(t.colors){let n={};for(let[i,s]of Object.entries(t.colors))s!==void 0&&((!F.includes(i)||typeof s!="string"||!D.test(s))&&a(`theme.colors.${i.slice(0,32)}`),n[i]=s);r.colors=n}t.radius!==void 0&&((typeof t.radius!="number"||!Number.isFinite(t.radius)||t.radius<0||t.radius>32)&&a("theme.radius"),r.radius=t.radius),t.fontFamily!==void 0&&((typeof t.fontFamily!="string"||!/^[\w .-]{1,64}$/.test(t.fontFamily))&&a("theme.fontFamily"),r.fontFamily=t.fontFamily),t.darkMode!==void 0&&(["system","light","dark"].includes(t.darkMode)||a("theme.darkMode"),r.darkMode=t.darkMode)}if(e.strings){e.locale||a("strings need locale");let n=e.strings[e.locale]??{},i={},s=Object.entries(n).filter(([,o])=>o!==void 0);s.length>b&&a("strings (too many overrides)");for(let[o,y]of s)/^[a-zA-Z0-9_.]{1,96}$/.test(o)||a("strings key"),(typeof y!="string"||y.length>2e3)&&a(`strings.${o}`),i[o]=y;r.strings=i}return r}var M=new Set(["method","phone_otp","email_otp","registry","document","nfc","liveness","processing","result","update_required","eid","contact_choice","identity_summary","portrait"]);function g(e){if(e.type==="waiting_for_network")return{type:"waiting_for_network"};if(e.type!=="step"||!M.has(e.step))return null;let r=e.step,t=typeof e.retryReason=="string"&&/^[a-z0-9_]{1,64}$/.test(e.retryReason)?e.retryReason:void 0;return t?{type:"step",step:r,retryReason:t}:{type:"step",step:r}}var $=new Set(["approved","review_pending","rejected"]);function m(e){if(e.status==="cancelled")return{status:"cancelled"};if(e.status==="failed")return{status:"failed",code:e.code||"unknown_error"};if(e.status==="finished"&&e.sessionId){if(e.displayStatus==="expired")return{status:"failed",code:"session_expired"};if($.has(e.displayStatus))return{status:"finished",sessionId:e.sessionId,displayStatus:e.displayStatus}}return{status:"failed",code:"unknown_error"}}function v(){if(!d)throw new l("unsupported_platform","the SekyroReactNative native module is not linked (New Architecture app, pod install / Gradle sync)");return d}var c=!1,j=new Set(["no_activity","unsupported_platform","flow_in_progress","invalid_config"]);async function f(e){let r=k(e),t=v();if(c)throw new l("flow_in_progress");c=!0;let n=e.onEvent,i=n?t.onEvent(s=>{let o=g(s);o&&n(o)}):void 0;try{return m(await t.start(r))}catch(s){let o=s?.code;if(typeof o=="string"&&j.has(o))throw new l(o);return{status:"failed",code:"unknown_error"}}finally{i?.remove(),c=!1}}function _(){return v().nativeVersion()}var p=require("react");function E(e){let r=(0,p.useRef)(null),t=(0,p.useRef)(e);return t.current=e,(0,p.useEffect)(()=>{if(r.current===e.clientToken)return;r.current=e.clientToken;let{onResult:n,onEvent:i,...s}=t.current;f({...s,onEvent:o=>t.current.onEvent?.(o)}).then(o=>t.current.onResult(o),o=>t.current.onResult({status:"failed",code:o instanceof l?o.code:"unknown_error"}))},[e.clientToken]),null}var R="0.1.0-rc.1";0&&(module.exports={SDK_VERSION,SekyroError,SekyroFlow,nativeVersion,startSekyro});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** UI language of the native flow. @public */
|
|
2
|
+
type SekyroLocale = 'ar' | 'fr' | 'en';
|
|
3
|
+
/** Themeable colour roles, shared by the Android and iOS cores. @public */
|
|
4
|
+
type SekyroColorRole = 'brand' | 'brandFg' | 'bg' | 'surface' | 'fg' | 'fgMuted' | 'border' | 'danger' | 'success' | 'warning';
|
|
5
|
+
/** Runtime theme applied over the native defaults. @public */
|
|
6
|
+
interface SekyroTheme {
|
|
7
|
+
/** `'#RRGGBB'` per role. */
|
|
8
|
+
colors?: Partial<Record<SekyroColorRole, string>>;
|
|
9
|
+
/** Corner radius in dp / pt, 0–32. */
|
|
10
|
+
radius?: number;
|
|
11
|
+
/** iOS only in 0.1 (Android ignores it). */
|
|
12
|
+
fontFamily?: string;
|
|
13
|
+
/** Colour scheme; `'system'` follows the device. */
|
|
14
|
+
darkMode?: 'system' | 'light' | 'dark';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Screen of the native flow, as reported by step events (`eid`: the national eID sign-in, SDK spec §3.2).
|
|
18
|
+
* Flow model 2026-10-23: `contact_choice` (phone or email), `identity_summary` (check the details read — the event never
|
|
19
|
+
* carries them), `portrait` (the ICAO portrait on its own). `consent` is never emitted (consent is one line on the first
|
|
20
|
+
* screen); it stays in the union only because the public API may only grow. @public
|
|
21
|
+
*/
|
|
22
|
+
type SekyroStep = 'consent' | 'method' | 'phone_otp' | 'email_otp' | 'registry' | 'document' | 'nfc' | 'liveness' | 'processing' | 'result' | 'update_required' | 'eid' | 'contact_choice' | 'identity_summary' | 'portrait';
|
|
23
|
+
/** Telemetry only; carries no personal data. @public */
|
|
24
|
+
type SekyroEvent = {
|
|
25
|
+
type: 'step';
|
|
26
|
+
step: SekyroStep;
|
|
27
|
+
retryReason?: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: 'waiting_for_network';
|
|
30
|
+
};
|
|
31
|
+
/** Options for {@link startSekyro}. @public */
|
|
32
|
+
interface SekyroConfig {
|
|
33
|
+
/** One-time session token from your backend (`sessions.create`). Never logged. */
|
|
34
|
+
clientToken: string;
|
|
35
|
+
/** e.g. `'https://api-staging.sekyro.ai'`; defaults to production. */
|
|
36
|
+
apiOrigin?: string;
|
|
37
|
+
/** UI language of the flow; required when `strings` is set. */
|
|
38
|
+
locale?: SekyroLocale;
|
|
39
|
+
/** Colours, radius, font and colour scheme. */
|
|
40
|
+
theme?: SekyroTheme;
|
|
41
|
+
/** Catalog-key overrides per locale; only `strings[locale]` is used, so `locale` is required. */
|
|
42
|
+
strings?: Partial<Record<SekyroLocale, Record<string, string>>>;
|
|
43
|
+
/** Step telemetry of this flow only (no personal data). */
|
|
44
|
+
onEvent?: (event: SekyroEvent) => void;
|
|
45
|
+
}
|
|
46
|
+
/** What the end user was shown; not the decision. @public */
|
|
47
|
+
type SekyroDisplayStatus = 'approved' | 'review_pending' | 'rejected';
|
|
48
|
+
/** Display-only outcome; read the decision server-side (webhooks / `sessions.get`). @public */
|
|
49
|
+
type SekyroResult = {
|
|
50
|
+
status: 'finished';
|
|
51
|
+
sessionId: string;
|
|
52
|
+
displayStatus: SekyroDisplayStatus;
|
|
53
|
+
} | {
|
|
54
|
+
status: 'cancelled';
|
|
55
|
+
} | {
|
|
56
|
+
status: 'failed';
|
|
57
|
+
code: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Present the Sekyro drop-in full screen. Resolves a display-only result; read the decision
|
|
62
|
+
* server-side. Rejects only with a {@link SekyroError} (programmer errors).
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
declare function startSekyro(config: SekyroConfig): Promise<SekyroResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Version of the bundled native core: `'android 0.1.0-rc.2'` / `'ios 0.1.0-rc.2'`.
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
declare function nativeVersion(): string;
|
|
71
|
+
|
|
72
|
+
/** Props of {@link SekyroFlow}: the config plus result and event callbacks. @public */
|
|
73
|
+
type SekyroFlowProps = Omit<SekyroConfig, 'onEvent'> & {
|
|
74
|
+
onResult: (result: SekyroResult) => void;
|
|
75
|
+
onEvent?: (event: SekyroEvent) => void;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Declarative drop-in: presents the native flow full screen on mount and renders nothing.
|
|
79
|
+
* Mount it once per session; to restart, remount with a new `clientToken`.
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
declare function SekyroFlow(props: SekyroFlowProps): null;
|
|
83
|
+
|
|
84
|
+
/** Programmer errors only. Flow outcomes resolve as a {@link SekyroResult}. @public */
|
|
85
|
+
type SekyroErrorCode = 'invalid_config' | 'flow_in_progress' | 'no_activity' | 'unsupported_platform';
|
|
86
|
+
/** Thrown (rejected) by {@link startSekyro} on a programmer error. @public */
|
|
87
|
+
declare class SekyroError extends Error {
|
|
88
|
+
/** Machine-readable cause. */
|
|
89
|
+
readonly code: SekyroErrorCode;
|
|
90
|
+
constructor(code: SekyroErrorCode, message?: string);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Version of this npm package.
|
|
95
|
+
* @public
|
|
96
|
+
*/
|
|
97
|
+
declare const SDK_VERSION: string;
|
|
98
|
+
|
|
99
|
+
export { SDK_VERSION, type SekyroColorRole, type SekyroConfig, type SekyroDisplayStatus, SekyroError, type SekyroErrorCode, type SekyroEvent, SekyroFlow, type SekyroFlowProps, type SekyroLocale, type SekyroResult, type SekyroStep, type SekyroTheme, nativeVersion, startSekyro };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/** UI language of the native flow. @public */
|
|
2
|
+
type SekyroLocale = 'ar' | 'fr' | 'en';
|
|
3
|
+
/** Themeable colour roles, shared by the Android and iOS cores. @public */
|
|
4
|
+
type SekyroColorRole = 'brand' | 'brandFg' | 'bg' | 'surface' | 'fg' | 'fgMuted' | 'border' | 'danger' | 'success' | 'warning';
|
|
5
|
+
/** Runtime theme applied over the native defaults. @public */
|
|
6
|
+
interface SekyroTheme {
|
|
7
|
+
/** `'#RRGGBB'` per role. */
|
|
8
|
+
colors?: Partial<Record<SekyroColorRole, string>>;
|
|
9
|
+
/** Corner radius in dp / pt, 0–32. */
|
|
10
|
+
radius?: number;
|
|
11
|
+
/** iOS only in 0.1 (Android ignores it). */
|
|
12
|
+
fontFamily?: string;
|
|
13
|
+
/** Colour scheme; `'system'` follows the device. */
|
|
14
|
+
darkMode?: 'system' | 'light' | 'dark';
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Screen of the native flow, as reported by step events (`eid`: the national eID sign-in, SDK spec §3.2).
|
|
18
|
+
* Flow model 2026-10-23: `contact_choice` (phone or email), `identity_summary` (check the details read — the event never
|
|
19
|
+
* carries them), `portrait` (the ICAO portrait on its own). `consent` is never emitted (consent is one line on the first
|
|
20
|
+
* screen); it stays in the union only because the public API may only grow. @public
|
|
21
|
+
*/
|
|
22
|
+
type SekyroStep = 'consent' | 'method' | 'phone_otp' | 'email_otp' | 'registry' | 'document' | 'nfc' | 'liveness' | 'processing' | 'result' | 'update_required' | 'eid' | 'contact_choice' | 'identity_summary' | 'portrait';
|
|
23
|
+
/** Telemetry only; carries no personal data. @public */
|
|
24
|
+
type SekyroEvent = {
|
|
25
|
+
type: 'step';
|
|
26
|
+
step: SekyroStep;
|
|
27
|
+
retryReason?: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: 'waiting_for_network';
|
|
30
|
+
};
|
|
31
|
+
/** Options for {@link startSekyro}. @public */
|
|
32
|
+
interface SekyroConfig {
|
|
33
|
+
/** One-time session token from your backend (`sessions.create`). Never logged. */
|
|
34
|
+
clientToken: string;
|
|
35
|
+
/** e.g. `'https://api-staging.sekyro.ai'`; defaults to production. */
|
|
36
|
+
apiOrigin?: string;
|
|
37
|
+
/** UI language of the flow; required when `strings` is set. */
|
|
38
|
+
locale?: SekyroLocale;
|
|
39
|
+
/** Colours, radius, font and colour scheme. */
|
|
40
|
+
theme?: SekyroTheme;
|
|
41
|
+
/** Catalog-key overrides per locale; only `strings[locale]` is used, so `locale` is required. */
|
|
42
|
+
strings?: Partial<Record<SekyroLocale, Record<string, string>>>;
|
|
43
|
+
/** Step telemetry of this flow only (no personal data). */
|
|
44
|
+
onEvent?: (event: SekyroEvent) => void;
|
|
45
|
+
}
|
|
46
|
+
/** What the end user was shown; not the decision. @public */
|
|
47
|
+
type SekyroDisplayStatus = 'approved' | 'review_pending' | 'rejected';
|
|
48
|
+
/** Display-only outcome; read the decision server-side (webhooks / `sessions.get`). @public */
|
|
49
|
+
type SekyroResult = {
|
|
50
|
+
status: 'finished';
|
|
51
|
+
sessionId: string;
|
|
52
|
+
displayStatus: SekyroDisplayStatus;
|
|
53
|
+
} | {
|
|
54
|
+
status: 'cancelled';
|
|
55
|
+
} | {
|
|
56
|
+
status: 'failed';
|
|
57
|
+
code: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Present the Sekyro drop-in full screen. Resolves a display-only result; read the decision
|
|
62
|
+
* server-side. Rejects only with a {@link SekyroError} (programmer errors).
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
declare function startSekyro(config: SekyroConfig): Promise<SekyroResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Version of the bundled native core: `'android 0.1.0-rc.2'` / `'ios 0.1.0-rc.2'`.
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
declare function nativeVersion(): string;
|
|
71
|
+
|
|
72
|
+
/** Props of {@link SekyroFlow}: the config plus result and event callbacks. @public */
|
|
73
|
+
type SekyroFlowProps = Omit<SekyroConfig, 'onEvent'> & {
|
|
74
|
+
onResult: (result: SekyroResult) => void;
|
|
75
|
+
onEvent?: (event: SekyroEvent) => void;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Declarative drop-in: presents the native flow full screen on mount and renders nothing.
|
|
79
|
+
* Mount it once per session; to restart, remount with a new `clientToken`.
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
declare function SekyroFlow(props: SekyroFlowProps): null;
|
|
83
|
+
|
|
84
|
+
/** Programmer errors only. Flow outcomes resolve as a {@link SekyroResult}. @public */
|
|
85
|
+
type SekyroErrorCode = 'invalid_config' | 'flow_in_progress' | 'no_activity' | 'unsupported_platform';
|
|
86
|
+
/** Thrown (rejected) by {@link startSekyro} on a programmer error. @public */
|
|
87
|
+
declare class SekyroError extends Error {
|
|
88
|
+
/** Machine-readable cause. */
|
|
89
|
+
readonly code: SekyroErrorCode;
|
|
90
|
+
constructor(code: SekyroErrorCode, message?: string);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Version of this npm package.
|
|
95
|
+
* @public
|
|
96
|
+
*/
|
|
97
|
+
declare const SDK_VERSION: string;
|
|
98
|
+
|
|
99
|
+
export { SDK_VERSION, type SekyroColorRole, type SekyroConfig, type SekyroDisplayStatus, SekyroError, type SekyroErrorCode, type SekyroEvent, SekyroFlow, type SekyroFlowProps, type SekyroLocale, type SekyroResult, type SekyroStep, type SekyroTheme, nativeVersion, startSekyro };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{TurboModuleRegistry as m}from"react-native";var f=m.get("SekyroReactNative");var a=class extends Error{code;constructor(r,t=r){super(t),this.name="SekyroError",this.code=r}};var v=500,_=new Set(["ar","fr","en"]),E=["brand","brandFg","bg","surface","fg","fgMuted","border","danger","success","warning"],R=new Set(["clientToken","apiOrigin","locale","theme","strings","onEvent"]),N=/^#[0-9a-fA-F]{6}$/,w=/^http:\/\/(localhost|127\.0\.0\.1|10\.0\.2\.2)(:\d+)?$/,i=e=>{throw new a("invalid_config",`invalid config: ${e}`)};function d(e){(typeof e!="object"||e===null)&&i("config");for(let s of Object.keys(e))R.has(s)||i(`unknown key "${s.slice(0,32)}"`);(typeof e.clientToken!="string"||e.clientToken.length===0)&&i("clientToken");let r={clientToken:e.clientToken};e.apiOrigin!==void 0&&(typeof e.apiOrigin=="string"&&(/^https:\/\/[a-z0-9.-]+(:\d+)?$/i.test(e.apiOrigin)||__DEV__&&w.test(e.apiOrigin))||i("apiOrigin"),r.apiOrigin=e.apiOrigin),e.locale!==void 0&&(_.has(e.locale)||i("locale"),r.locale=e.locale);let t=e.theme;if(t){if(t.colors){let s={};for(let[l,n]of Object.entries(t.colors))n!==void 0&&((!E.includes(l)||typeof n!="string"||!N.test(n))&&i(`theme.colors.${l.slice(0,32)}`),s[l]=n);r.colors=s}t.radius!==void 0&&((typeof t.radius!="number"||!Number.isFinite(t.radius)||t.radius<0||t.radius>32)&&i("theme.radius"),r.radius=t.radius),t.fontFamily!==void 0&&((typeof t.fontFamily!="string"||!/^[\w .-]{1,64}$/.test(t.fontFamily))&&i("theme.fontFamily"),r.fontFamily=t.fontFamily),t.darkMode!==void 0&&(["system","light","dark"].includes(t.darkMode)||i("theme.darkMode"),r.darkMode=t.darkMode)}if(e.strings){e.locale||i("strings need locale");let s=e.strings[e.locale]??{},l={},n=Object.entries(s).filter(([,o])=>o!==void 0);n.length>v&&i("strings (too many overrides)");for(let[o,p]of n)/^[a-zA-Z0-9_.]{1,96}$/.test(o)||i("strings key"),(typeof p!="string"||p.length>2e3)&&i(`strings.${o}`),l[o]=p;r.strings=l}return r}var h=new Set(["method","phone_otp","email_otp","registry","document","nfc","liveness","processing","result","update_required","eid","contact_choice","identity_summary","portrait"]);function c(e){if(e.type==="waiting_for_network")return{type:"waiting_for_network"};if(e.type!=="step"||!h.has(e.step))return null;let r=e.step,t=typeof e.retryReason=="string"&&/^[a-z0-9_]{1,64}$/.test(e.retryReason)?e.retryReason:void 0;return t?{type:"step",step:r,retryReason:t}:{type:"step",step:r}}var C=new Set(["approved","review_pending","rejected"]);function S(e){if(e.status==="cancelled")return{status:"cancelled"};if(e.status==="failed")return{status:"failed",code:e.code||"unknown_error"};if(e.status==="finished"&&e.sessionId){if(e.displayStatus==="expired")return{status:"failed",code:"session_expired"};if(C.has(e.displayStatus))return{status:"finished",sessionId:e.sessionId,displayStatus:e.displayStatus}}return{status:"failed",code:"unknown_error"}}function k(){if(!f)throw new a("unsupported_platform","the SekyroReactNative native module is not linked (New Architecture app, pod install / Gradle sync)");return f}var y=!1,O=new Set(["no_activity","unsupported_platform","flow_in_progress","invalid_config"]);async function u(e){let r=d(e),t=k();if(y)throw new a("flow_in_progress");y=!0;let s=e.onEvent,l=s?t.onEvent(n=>{let o=c(n);o&&s(o)}):void 0;try{return S(await t.start(r))}catch(n){let o=n?.code;if(typeof o=="string"&&O.has(o))throw new a(o);return{status:"failed",code:"unknown_error"}}finally{l?.remove(),y=!1}}function x(){return k().nativeVersion()}import{useEffect as b,useRef as g}from"react";function T(e){let r=g(null),t=g(e);return t.current=e,b(()=>{if(r.current===e.clientToken)return;r.current=e.clientToken;let{onResult:s,onEvent:l,...n}=t.current;u({...n,onEvent:o=>t.current.onEvent?.(o)}).then(o=>t.current.onResult(o),o=>t.current.onResult({status:"failed",code:o instanceof a?o.code:"unknown_error"}))},[e.clientToken]),null}var F="0.1.0-rc.1";export{F as SDK_VERSION,a as SekyroError,T as SekyroFlow,x as nativeVersion,u as startSekyro};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"module"}
|