@sekyro/react-native 0.0.0-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.
@@ -0,0 +1,57 @@
1
+ // @sekyro/react-native — iOS TurboModule: rebuilds a plain dictionary from the codegen struct and hands it to
2
+ // SekyroRNBridge (Swift). Never logs the config: the client token is a bearer credential.
3
+ #import "RCTSekyroReactNative.h"
4
+ #if __has_include(<SekyroReactNative/SekyroReactNative-Swift.h>)
5
+ #import <SekyroReactNative/SekyroReactNative-Swift.h>
6
+ #else
7
+ #import "SekyroReactNative-Swift.h"
8
+ #endif
9
+
10
+ @implementation RCTSekyroReactNative
11
+
12
+ + (NSString *)moduleName { return @"SekyroReactNative"; }
13
+
14
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
15
+ return std::make_shared<facebook::react::NativeSekyroReactNativeSpecJSI>(params);
16
+ }
17
+
18
+ - (NSString *)nativeVersion { return [SekyroRNBridge nativeVersion]; }
19
+
20
+ - (void)start:(JS::NativeSekyroReactNative::NativeConfig &)config
21
+ resolve:(RCTPromiseResolveBlock)resolve
22
+ reject:(RCTPromiseRejectBlock)reject {
23
+ // The struct is only valid during this call: copy everything out first.
24
+ NSMutableDictionary *d = [NSMutableDictionary new];
25
+ if (config.clientToken()) d[@"clientToken"] = config.clientToken();
26
+ if (config.apiOrigin()) d[@"apiOrigin"] = config.apiOrigin();
27
+ if (config.locale()) d[@"locale"] = config.locale();
28
+ if (config.fontFamily()) d[@"fontFamily"] = config.fontFamily();
29
+ if (config.darkMode()) d[@"darkMode"] = config.darkMode();
30
+ if (config.radius().has_value()) d[@"radius"] = @(config.radius().value());
31
+ id strings = config.strings();
32
+ if ([strings isKindOfClass:[NSDictionary class]]) d[@"strings"] = strings;
33
+ if (config.colors().has_value()) {
34
+ auto c = config.colors().value();
35
+ NSMutableDictionary *colors = [NSMutableDictionary new];
36
+ if (c.brand()) colors[@"brand"] = c.brand();
37
+ if (c.brandFg()) colors[@"brandFg"] = c.brandFg();
38
+ if (c.bg()) colors[@"bg"] = c.bg();
39
+ if (c.surface()) colors[@"surface"] = c.surface();
40
+ if (c.fg()) colors[@"fg"] = c.fg();
41
+ if (c.fgMuted()) colors[@"fgMuted"] = c.fgMuted();
42
+ if (c.border()) colors[@"border"] = c.border();
43
+ if (c.danger()) colors[@"danger"] = c.danger();
44
+ if (c.success()) colors[@"success"] = c.success();
45
+ if (c.warning()) colors[@"warning"] = c.warning();
46
+ d[@"colors"] = colors;
47
+ }
48
+ __weak RCTSekyroReactNative *weakSelf = self;
49
+ dispatch_async(dispatch_get_main_queue(), ^{
50
+ NSString *error = [SekyroRNBridge start:d
51
+ onEvent:^(NSDictionary *event) { [weakSelf emitOnEvent:event]; }
52
+ completion:^(NSDictionary *result) { resolve(result); }];
53
+ if (error) reject(error, error, nil);
54
+ });
55
+ }
56
+
57
+ @end
@@ -0,0 +1,108 @@
1
+ // @sekyro/react-native — Swift side of the iOS TurboModule (RCTSekyroReactNative.mm calls it).
2
+ import Foundation
3
+ import SekyroKit
4
+ import UIKit
5
+
6
+ @objc(SekyroRNBridge)
7
+ public final class SekyroRNBridge: NSObject {
8
+ @MainActor private static var running = false
9
+ @MainActor private static var seq: Int32 = 0
10
+
11
+ @objc public static func nativeVersion() -> String { "ios \(Sekyro.sdkVersion)" }
12
+
13
+ struct InvalidConfig: Error {}
14
+
15
+ /// The JS layer validated the config; this re-checks the shape. Never logs it (the token is a bearer credential).
16
+ static func parse(_ d: NSDictionary) throws -> SekyroConfig {
17
+ guard let token = d["clientToken"] as? String, !token.isEmpty else { throw InvalidConfig() }
18
+ var origin: URL?
19
+ if let raw = d["apiOrigin"] as? String {
20
+ guard let url = URL(string: raw), url.scheme == "https" || url.host == "localhost" || url.host == "127.0.0.1" else { throw InvalidConfig() }
21
+ origin = url
22
+ }
23
+ let preferred = Locale.preferredLanguages.lazy.map { String($0.prefix(2)) }.first { ["ar", "fr", "en"].contains($0) }
24
+ let locale = SekyroLocale(rawValue: (d["locale"] as? String) ?? preferred ?? "fr") ?? .fr
25
+ let colors = d["colors"] as? [String: Any] ?? [:]
26
+ func color(_ key: String) throws -> SekyroColor? {
27
+ guard let value = colors[key] else { return nil }
28
+ guard let hex = value as? String, let c = SekyroColor(hex: hex) else { throw InvalidConfig() }
29
+ return c
30
+ }
31
+ let mode: SekyroDarkMode
32
+ switch d["darkMode"] as? String {
33
+ case "light": mode = .light
34
+ case "dark": mode = .dark
35
+ default: mode = .system
36
+ }
37
+ var radius: CGFloat?
38
+ if let r = d["radius"] as? NSNumber {
39
+ guard (0...32).contains(r.doubleValue) else { throw InvalidConfig() }
40
+ radius = CGFloat(truncating: r)
41
+ }
42
+ let strings = (d["strings"] as? [String: Any] ?? [:]).compactMapValues { $0 as? String }
43
+ let theme = SekyroTheme(
44
+ brand: try color("brand"), brandFg: try color("brandFg"), background: try color("bg"), surface: try color("surface"),
45
+ foreground: try color("fg"), foregroundMuted: try color("fgMuted"), border: try color("border"),
46
+ danger: try color("danger"), success: try color("success"), warning: try color("warning"),
47
+ cornerRadius: radius, fontFamily: d["fontFamily"] as? String, darkMode: mode, strings: strings)
48
+ return SekyroConfig(clientToken: token, apiOrigin: origin, locale: locale, theme: theme)
49
+ }
50
+
51
+ static func wire(_ result: SekyroResult) -> NSDictionary {
52
+ switch result {
53
+ case .finished(let sessionId, let status):
54
+ return ["status": "finished", "sessionId": sessionId, "displayStatus": status.rawValue]
55
+ case .cancelled:
56
+ return ["status": "cancelled"]
57
+ case .failed(let code):
58
+ return ["status": "failed", "code": code]
59
+ }
60
+ }
61
+
62
+ /// Presents the drop-in. Returns nil when started, else an error code (`invalid_config`, `no_activity`, `flow_in_progress`).
63
+ @MainActor @objc public static func start(_ config: NSDictionary,
64
+ onEvent: @escaping (NSDictionary) -> Void,
65
+ completion: @escaping (NSDictionary) -> Void) -> String? {
66
+ guard let parsed = try? parse(config) else { return "invalid_config" }
67
+ guard !running else { return "flow_in_progress" }
68
+ // UIKit only logs (and never calls back) when the presenter is off-window, mid-transition or
69
+ // already presenting — refuse up front so the promise can never stay pending.
70
+ guard let presenter = topViewController(), canPresent(from: presenter) else { return "no_activity" }
71
+ running = true
72
+ Sekyro.start(from: presenter, config: parsed, onEvent: { event in
73
+ seq &+= 1
74
+ switch event {
75
+ case .step(let step, let reason):
76
+ var e: [String: Any] = ["type": "step", "step": step.rawValue, "seq": NSNumber(value: seq)]
77
+ if let reason { e["retryReason"] = reason }
78
+ onEvent(e as NSDictionary)
79
+ case .waitingForNetwork:
80
+ onEvent(["type": "waiting_for_network", "seq": NSNumber(value: seq)])
81
+ }
82
+ }, completion: { result in
83
+ running = false
84
+ completion(wire(result))
85
+ })
86
+ return nil
87
+ }
88
+
89
+ @MainActor private static func topViewController() -> UIViewController? {
90
+ let scenes = UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }
91
+ let foreground = scenes.filter { $0.activationState == .foregroundActive }
92
+ // The key window across every foreground scene first, then any foreground window, then any scene.
93
+ let window = foreground.lazy.flatMap { $0.windows }.first { $0.isKeyWindow }
94
+ ?? foreground.lazy.flatMap { $0.windows }.first
95
+ ?? scenes.first?.windows.first
96
+ var top = window?.rootViewController
97
+ while let presented = top?.presentedViewController, !presented.isBeingDismissed { top = presented }
98
+ return top
99
+ }
100
+
101
+ /// True when `presenter` can present right now (in a window, not transitioning, not presenting).
102
+ @MainActor static func canPresent(from presenter: UIViewController) -> Bool {
103
+ presenter.viewIfLoaded?.window != nil
104
+ && !presenter.isBeingPresented
105
+ && !presenter.isBeingDismissed
106
+ && presenter.presentedViewController == nil
107
+ }
108
+ }
package/package.json CHANGED
@@ -1,6 +1,105 @@
1
1
  {
2
2
  "name": "@sekyro/react-native",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0-rc.1",
4
+ "private": false,
5
+ "description": "Sekyro AI eKYC for React Native — glue over the binary Android and iOS SDKs (New Architecture)",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/IBTIKAR-Technologies/ibtikar-identity.git",
10
+ "directory": "sekyro-mobile/react-native"
11
+ },
12
+ "keywords": [
13
+ "sekyro",
14
+ "kyc",
15
+ "ekyc",
16
+ "identity-verification",
17
+ "nfc",
18
+ "react-native",
19
+ "turbomodule",
20
+ "mauritania"
21
+ ],
22
+ "engines": {
23
+ "node": ">=20"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "main": "./dist/index.cjs",
29
+ "module": "./dist/index.js",
30
+ "react-native": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "import": {
35
+ "types": "./dist/index.d.ts",
36
+ "default": "./dist/index.js"
37
+ },
38
+ "require": {
39
+ "types": "./dist/index.d.cts",
40
+ "default": "./dist/index.cjs"
41
+ }
42
+ },
43
+ "./app.plugin.js": "./app.plugin.js",
44
+ "./package.json": "./package.json"
45
+ },
46
+ "files": [
47
+ "dist",
48
+ "src/specs",
49
+ "android/build.gradle",
50
+ "android/src/main",
51
+ "ios",
52
+ "SekyroReactNative.podspec",
53
+ "scripts/sekyro_pods.rb",
54
+ "app.plugin.js",
55
+ "plugin",
56
+ "README.md",
57
+ "CHANGELOG.md",
58
+ "LICENSE",
59
+ "sbom.cdx.json"
60
+ ],
61
+ "sideEffects": false,
62
+ "scripts": {
63
+ "typecheck": "tsc -p tsconfig.json --noEmit",
64
+ "test": "jest --maxWorkers=2",
65
+ "build": "tsup && node -e \"const f=require('fs');f.renameSync('dist/index.d.ts','dist/index.d.cts');f.renameSync('dist/index.d.mts','dist/index.d.ts');f.writeFileSync('dist/package.json','{\\\"type\\\":\\\"module\\\"}\\n')\"",
66
+ "api:report": "api-extractor run --local",
67
+ "api:check": "api-extractor run"
68
+ },
69
+ "peerDependencies": {
70
+ "react": ">=18.3",
71
+ "react-native": ">=0.76"
72
+ },
73
+ "devDependencies": {
74
+ "@expo/config-plugins": "54.0.2",
75
+ "@microsoft/api-extractor": "7.52.10",
76
+ "@react-native/babel-preset": "0.84.1",
77
+ "@testing-library/react-native": "13.3.3",
78
+ "@types/jest": "29.5.14",
79
+ "@types/react": "19.2.2",
80
+ "jest": "29.7.0",
81
+ "react": "19.2.3",
82
+ "react-native": "0.84.1",
83
+ "react-test-renderer": "19.2.3",
84
+ "ts-jest": "29.4.4",
85
+ "tsup": "8.5.0",
86
+ "typescript": "5.9.3"
87
+ },
88
+ "codegenConfig": {
89
+ "name": "RNSekyroReactNativeSpec",
90
+ "type": "modules",
91
+ "jsSrcsDir": "src/specs",
92
+ "android": {
93
+ "javaPackageName": "ai.sekyro.reactnative"
94
+ },
95
+ "ios": {
96
+ "modulesProvider": {
97
+ "SekyroReactNative": "RCTSekyroReactNative"
98
+ }
99
+ }
100
+ },
101
+ "sekyro": {
102
+ "android": "0.1.0-rc.2",
103
+ "ios": "0.1.0-rc.2"
104
+ }
105
+ }
@@ -0,0 +1,24 @@
1
+ // Android needs no manifest edits: the sekyro-ui AAR manifest merges CAMERA, NFC, the optional
2
+ // features and SekyroActivity. The only requirement checked here is the core's minSdk.
3
+ const MIN_SDK = 24;
4
+
5
+ /** @param {Record<string, any>} config @returns {number | undefined} */
6
+ function buildPropertiesMinSdk(config) {
7
+ for (const entry of config.plugins ?? []) {
8
+ if (Array.isArray(entry) && entry[0] === 'expo-build-properties') {
9
+ const value = entry[1]?.android?.minSdkVersion;
10
+ if (value !== undefined) return Number(value);
11
+ }
12
+ }
13
+ return undefined;
14
+ }
15
+
16
+ /** @param {Record<string, any>} config */
17
+ function assertMinSdk(config) {
18
+ const minSdk = buildPropertiesMinSdk(config);
19
+ if (minSdk !== undefined && !(minSdk >= MIN_SDK)) {
20
+ throw new Error(`@sekyro/react-native needs Android minSdkVersion >= ${MIN_SDK} (expo-build-properties sets ${minSdk})`);
21
+ }
22
+ }
23
+
24
+ module.exports = { assertMinSdk, MIN_SDK };
@@ -0,0 +1,76 @@
1
+ // Expo config plugin: plugins: [['@sekyro/react-native', { nfc: true, appAttest: 'production' }]]
2
+
3
+ /** `expo/config-plugins` (Expo's documented re-export, resolvable from the app) before `@expo/config-plugins`. */
4
+ function loadConfigPlugins() {
5
+ try {
6
+ return require('expo/config-plugins');
7
+ } catch {
8
+ return require('@expo/config-plugins');
9
+ }
10
+ }
11
+ const {
12
+ withInfoPlist,
13
+ withEntitlementsPlist,
14
+ withDangerousMod,
15
+ withAndroidManifest,
16
+ createRunOncePlugin,
17
+ } = loadConfigPlugins();
18
+ const fs = require('node:fs');
19
+ const path = require('node:path');
20
+ const ios = require('./ios');
21
+ const { assertMinSdk } = require('./android');
22
+ const pkg = require('../package.json');
23
+
24
+ const KNOWN = new Set(['nfc', 'appAttest', 'cameraPermission', 'nfcPermission']);
25
+
26
+ function validate(options) {
27
+ if (typeof options !== 'object' || options === null || Array.isArray(options)) {
28
+ throw new Error('@sekyro/react-native plugin: options must be an object');
29
+ }
30
+ for (const k of Object.keys(options)) {
31
+ if (!KNOWN.has(k)) throw new Error(`@sekyro/react-native plugin: unknown option "${k}"`);
32
+ }
33
+ if (options.nfc !== undefined && typeof options.nfc !== 'boolean') {
34
+ throw new Error('@sekyro/react-native plugin: nfc must be a boolean');
35
+ }
36
+ if (options.appAttest !== undefined && !['production', 'development', false].includes(options.appAttest)) {
37
+ throw new Error("@sekyro/react-native plugin: appAttest must be 'production', 'development' or false");
38
+ }
39
+ for (const k of ['cameraPermission', 'nfcPermission']) {
40
+ if (options[k] !== undefined && (typeof options[k] !== 'string' || options[k].length === 0)) {
41
+ throw new Error(`@sekyro/react-native plugin: ${k} must be a non-empty string`);
42
+ }
43
+ }
44
+ }
45
+
46
+ /**
47
+ * @param {import('@expo/config-plugins').ExportedConfig} config
48
+ * @param {import('./ios').SekyroPluginOptions} [options]
49
+ */
50
+ function withSekyro(config, options = {}) {
51
+ validate(options ?? {});
52
+ const opts = options ?? {};
53
+ config = withInfoPlist(config, (c) => {
54
+ c.modResults = ios.withSekyroInfoPlist(c.modResults, opts);
55
+ return c;
56
+ });
57
+ config = withEntitlementsPlist(config, (c) => {
58
+ c.modResults = ios.withSekyroEntitlements(c.modResults, opts);
59
+ return c;
60
+ });
61
+ config = withDangerousMod(config, [
62
+ 'ios',
63
+ (c) => {
64
+ const file = path.join(c.modRequest.platformProjectRoot, 'Podfile');
65
+ fs.writeFileSync(file, ios.addSekyroPod(fs.readFileSync(file, 'utf8')));
66
+ return c;
67
+ },
68
+ ]);
69
+ config = withAndroidManifest(config, (c) => {
70
+ assertMinSdk(c);
71
+ return c;
72
+ });
73
+ return config;
74
+ }
75
+
76
+ module.exports = createRunOncePlugin(withSekyro, pkg.name, pkg.version);
package/plugin/ios.js ADDED
@@ -0,0 +1,61 @@
1
+ // Pure helpers for the Expo config plugin (iOS). No Expo imports, so they are unit-testable.
2
+ const AIDS = ['A0000002471001', 'A0000002472001']; // ICAO eMRTD LDS1 application + its alternate (Plan 3 sample)
3
+ const AID_KEY = 'com.apple.developer.nfc.readersession.iso7816.select-identifiers';
4
+ const NFC_FORMATS_KEY = 'com.apple.developer.nfc.readersession.formats';
5
+ const APP_ATTEST_KEY = 'com.apple.developer.devicecheck.appattest-environment';
6
+
7
+ /**
8
+ * @typedef {object} SekyroPluginOptions
9
+ * @property {boolean} [nfc] Read the document chip (default true).
10
+ * @property {'production' | 'development' | false} [appAttest] App Attest environment (default 'production').
11
+ * @property {string} [cameraPermission] NSCameraUsageDescription when the app has none.
12
+ * @property {string} [nfcPermission] NFCReaderUsageDescription when the app has none.
13
+ */
14
+
15
+ /**
16
+ * @param {Record<string, any>} plist
17
+ * @param {SekyroPluginOptions} [options]
18
+ * @returns {Record<string, any>}
19
+ */
20
+ function withSekyroInfoPlist(plist, { nfc = true, cameraPermission, nfcPermission } = {}) {
21
+ const out = { ...plist };
22
+ out.NSCameraUsageDescription ??=
23
+ cameraPermission ?? 'The camera is used to capture your face and your identity document for verification.';
24
+ if (nfc) {
25
+ out.NFCReaderUsageDescription ??= nfcPermission ?? 'NFC is used to read the chip of your passport or identity card.';
26
+ out[AID_KEY] = [...new Set([...(out[AID_KEY] ?? []), ...AIDS])];
27
+ }
28
+ return out;
29
+ }
30
+
31
+ /**
32
+ * @param {Record<string, any>} ent
33
+ * @param {SekyroPluginOptions} [options]
34
+ * @returns {Record<string, any>}
35
+ */
36
+ function withSekyroEntitlements(ent, { nfc = true, appAttest = 'production' } = {}) {
37
+ const out = { ...ent };
38
+ if (nfc) out[NFC_FORMATS_KEY] = [...new Set([...(out[NFC_FORMATS_KEY] ?? []), 'TAG'])];
39
+ if (appAttest) out[APP_ATTEST_KEY] = appAttest;
40
+ return out;
41
+ }
42
+
43
+ const REQUIRE = `require Pod::Executable.execute_command('node', ['-p', 'require.resolve("@sekyro/react-native/package.json", {paths: [process.argv[1]]})', __dir__]).strip.sub(/package\\.json$/, 'scripts/sekyro_pods.rb')`;
44
+
45
+ /**
46
+ * @param {string} podfile
47
+ * @returns {string}
48
+ */
49
+ function addSekyroPod(podfile) {
50
+ if (podfile.includes('sekyro_kit_pod!')) return podfile;
51
+ const anchor = /(config = use_native_modules!.*\n)/;
52
+ if (!anchor.test(podfile)) {
53
+ throw new Error(
54
+ '@sekyro/react-native plugin: the Podfile has no `config = use_native_modules!` line — add `sekyro_kit_pod!` to your target by hand',
55
+ );
56
+ }
57
+ const withPod = podfile.replace(anchor, `$1 sekyro_kit_pod!\n`);
58
+ return withPod.includes(REQUIRE) ? withPod : `${REQUIRE}\n${withPod}`;
59
+ }
60
+
61
+ module.exports = { withSekyroInfoPlist, withSekyroEntitlements, addSekyroPod, AIDS };
package/sbom.cdx.json ADDED
@@ -0,0 +1,153 @@
1
+ {
2
+ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
3
+ "bomFormat": "CycloneDX",
4
+ "specVersion": "1.6",
5
+ "version": 1,
6
+ "metadata": {
7
+ "tools": {
8
+ "components": [
9
+ {
10
+ "type": "application",
11
+ "name": "npm",
12
+ "version": "11.12.1"
13
+ },
14
+ {
15
+ "type": "library",
16
+ "name": "cyclonedx-library",
17
+ "group": "@cyclonedx",
18
+ "version": "10.3.0",
19
+ "author": "Jan Kowalleck",
20
+ "description": "Core functionality of CycloneDX for JavaScript (Node.js or WebBrowser).",
21
+ "licenses": [
22
+ {
23
+ "license": {
24
+ "id": "Apache-2.0"
25
+ }
26
+ }
27
+ ],
28
+ "externalReferences": [
29
+ {
30
+ "url": "https://github.com/CycloneDX/cyclonedx-javascript-library/issues",
31
+ "type": "issue-tracker",
32
+ "comment": "as detected from PackageJson property \"bugs.url\""
33
+ },
34
+ {
35
+ "url": "git+https://github.com/CycloneDX/cyclonedx-javascript-library.git",
36
+ "type": "vcs",
37
+ "comment": "as detected from PackageJson property \"repository.url\""
38
+ },
39
+ {
40
+ "url": "https://github.com/CycloneDX/cyclonedx-javascript-library#readme",
41
+ "type": "website",
42
+ "comment": "as detected from PackageJson property \"homepage\""
43
+ }
44
+ ],
45
+ "properties": [
46
+ {
47
+ "name": "cdx:npm:package:constraint:engine:node",
48
+ "value": ">=20.18.0"
49
+ }
50
+ ]
51
+ },
52
+ {
53
+ "type": "application",
54
+ "name": "cyclonedx-npm",
55
+ "group": "@cyclonedx",
56
+ "version": "6.0.1",
57
+ "author": "Jan Kowalleck",
58
+ "description": "Create CycloneDX Software Bill of Materials (SBOM) from NPM projects.",
59
+ "licenses": [
60
+ {
61
+ "license": {
62
+ "id": "Apache-2.0"
63
+ }
64
+ }
65
+ ],
66
+ "externalReferences": [
67
+ {
68
+ "url": "https://github.com/CycloneDX/cyclonedx-node-npm/issues",
69
+ "type": "issue-tracker",
70
+ "comment": "as detected from PackageJson property \"bugs.url\""
71
+ },
72
+ {
73
+ "url": "git+https://github.com/CycloneDX/cyclonedx-node-npm.git",
74
+ "type": "vcs",
75
+ "comment": "as detected from PackageJson property \"repository.url\""
76
+ },
77
+ {
78
+ "url": "https://github.com/CycloneDX/cyclonedx-node-npm#readme",
79
+ "type": "website",
80
+ "comment": "as detected from PackageJson property \"homepage\""
81
+ }
82
+ ],
83
+ "properties": [
84
+ {
85
+ "name": "cdx:npm:package:constraint:engine:node",
86
+ "value": ">=20.18.0"
87
+ },
88
+ {
89
+ "name": "cdx:npm:package:constraint:engine:npm",
90
+ "value": ">=9"
91
+ }
92
+ ]
93
+ }
94
+ ]
95
+ },
96
+ "component": {
97
+ "type": "application",
98
+ "name": "react-native",
99
+ "group": "@sekyro",
100
+ "version": "0.1.0-rc.1",
101
+ "bom-ref": "@sekyro/react-native@0.1.0-rc.1",
102
+ "description": "Sekyro AI eKYC for React Native — glue over the binary Android and iOS SDKs (New Architecture)",
103
+ "licenses": [
104
+ {
105
+ "license": {
106
+ "name": "SEE LICENSE IN LICENSE",
107
+ "acknowledgement": "declared"
108
+ }
109
+ }
110
+ ],
111
+ "purl": "pkg:npm/%40sekyro/react-native@0.1.0-rc.1?vcs_url=git%2Bhttps%3A%2F%2Fgithub.com%2FIBTIKAR-Technologies%2Fibtikar-identity.git%23sekyro-mobile%2Freact-native",
112
+ "externalReferences": [
113
+ {
114
+ "url": "https://github.com/IBTIKAR-Technologies/ibtikar-identity/issues",
115
+ "type": "issue-tracker",
116
+ "comment": "as detected from PackageJson property \"bugs.url\""
117
+ },
118
+ {
119
+ "url": "git+https://github.com/IBTIKAR-Technologies/ibtikar-identity.git#sekyro-mobile/react-native",
120
+ "type": "vcs",
121
+ "comment": "as detected from PackageJson property \"repository.url\" and \"repository.directory\""
122
+ },
123
+ {
124
+ "url": "https://github.com/IBTIKAR-Technologies/ibtikar-identity#readme",
125
+ "type": "website",
126
+ "comment": "as detected from PackageJson property \"homepage\""
127
+ }
128
+ ],
129
+ "properties": [
130
+ {
131
+ "name": "cdx:npm:package:constraint:engine:node",
132
+ "value": ">=20"
133
+ },
134
+ {
135
+ "name": "cdx:npm:package:path",
136
+ "value": ""
137
+ }
138
+ ]
139
+ },
140
+ "properties": [
141
+ {
142
+ "name": "cdx:reproducible",
143
+ "value": "true"
144
+ }
145
+ ]
146
+ },
147
+ "components": [],
148
+ "dependencies": [
149
+ {
150
+ "ref": "@sekyro/react-native@0.1.0-rc.1"
151
+ }
152
+ ]
153
+ }
@@ -0,0 +1,27 @@
1
+ # @sekyro/react-native — declares where the binary SekyroKit pod comes from. In the app's Podfile:
2
+ #
3
+ # require Pod::Executable.execute_command('node', ['-p',
4
+ # 'require.resolve("@sekyro/react-native/package.json", {paths: [process.argv[1]]})', __dir__]).strip.sub(/package\.json$/, 'scripts/sekyro_pods.rb')
5
+ #
6
+ # target 'YourApp' do
7
+ # config = use_native_modules!
8
+ # sekyro_kit_pod!
9
+ # ...
10
+ #
11
+ # Default: the podspec of the public sekyro-ios-dist repo at the pinned tag (it downloads the signed
12
+ # XCFramework zip from assets.sekyro.ai and checks its sha256). Development: SEKYRO_KIT_PATH=<dir> uses a
13
+ # `make-dist.sh <pin> --local` tree instead.
14
+ require 'json'
15
+
16
+ def sekyro_kit_pod!
17
+ pkg = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
18
+ version = pkg.fetch('sekyro').fetch('ios')
19
+ local = ENV['SEKYRO_KIT_PATH']
20
+ if local && !local.strip.empty?
21
+ Pod::UI.puts "[Sekyro] SekyroKit #{version} from the local dist #{local} (development only)".yellow
22
+ pod 'SekyroKit', :path => local
23
+ else
24
+ # :podspec, not :git — a :git pod is built from the repo checkout, and the binary is not in the repo.
25
+ pod 'SekyroKit', :podspec => "https://raw.githubusercontent.com/IBTIKAR-Technologies/sekyro-ios-dist/#{version}/SekyroKit.podspec"
26
+ end
27
+ end
@@ -0,0 +1,46 @@
1
+ // Shipped as source on purpose: RN codegen reads it when the app builds. Types only, no logic.
2
+ import type { TurboModule } from 'react-native';
3
+ import { TurboModuleRegistry } from 'react-native';
4
+ // Deep type import: the codegen of RN 0.76–0.84 resolves these names (CodegenTypes is re-exported
5
+ // from 'react-native' only from 0.80). For this package's own tsc, types/codegen-types.d.ts
6
+ // supplies the TypeScript declarations that newer RN releases no longer ship at this path.
7
+ import type { Double, EventEmitter, Int32, UnsafeObject } from 'react-native/Libraries/Types/CodegenTypes';
8
+
9
+ export type NativeColors = {
10
+ brand?: string;
11
+ brandFg?: string;
12
+ bg?: string;
13
+ surface?: string;
14
+ fg?: string;
15
+ fgMuted?: string;
16
+ border?: string;
17
+ danger?: string;
18
+ success?: string;
19
+ warning?: string;
20
+ };
21
+
22
+ export type NativeConfig = {
23
+ clientToken: string;
24
+ apiOrigin?: string;
25
+ locale?: string;
26
+ colors?: NativeColors;
27
+ radius?: Double;
28
+ fontFamily?: string;
29
+ darkMode?: string;
30
+ /** Flat catalog-key → text map for the active locale. */
31
+ strings?: UnsafeObject;
32
+ };
33
+
34
+ export type NativeResult = { status: string; sessionId?: string; displayStatus?: string; code?: string };
35
+
36
+ export type NativeEvent = { type: string; step?: string; retryReason?: string; seq: Int32 };
37
+
38
+ export interface Spec extends TurboModule {
39
+ start(config: NativeConfig): Promise<NativeResult>;
40
+ nativeVersion(): string;
41
+ readonly onEvent: EventEmitter<NativeEvent>;
42
+ }
43
+
44
+ // `get`, not `getEnforcing`: importing the package must not throw in an app without the native
45
+ // module (Expo Go, web, a missing pod install); `startSekyro` then rejects `unsupported_platform`.
46
+ export default TurboModuleRegistry.get<Spec>('SekyroReactNative');