react-native-otp-auto-verify 0.2.0 → 0.2.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/package.json CHANGED
@@ -1,175 +1,183 @@
1
- {
2
- "name": "react-native-otp-auto-verify",
3
- "version": "0.2.0",
4
- "description": "react-native-otp-auto-verify is a React Native library for automatic OTP verification on Android and iOS. It uses the Google SMS Retriever API to detect OTP codes automatically without requiring the READ_SMS permission. The library is fully Play Store compliant, improves user login experience, supports TurboModule, and works with the React Native New Architecture",
5
- "keywords": [
6
- "react-native",
7
- "react-native-library",
8
- "react-native-otp",
9
- "react-native-otp-auto-verify",
10
- "react-native-sms",
11
- "otp",
12
- "otp-verification",
13
- "otp-auto-verify",
14
- "otp-auto-read",
15
- "sms",
16
- "sms-retriever",
17
- "google-sms-retriever",
18
- "android-otp",
19
- "ios-otp",
20
- "otp-without-permission",
21
- "play-store-compliant",
22
- "react-native-new-architecture",
23
- "turbo-module"
24
- ],
25
- "homepage": "https://github.com/kailas-rathod/react-native-otp-auto-verify?tab=readme-ov-file",
26
- "bugs": {
27
- "url": "https://github.com/kailas-rathod/react-native-otp-auto-verify/issues"
28
- },
29
- "repository": {
30
- "type": "git",
31
- "url": "git+https://github.com/kailas-rathod/react-native-otp-auto-verify.git"
32
- },
33
- "license": "MIT",
34
- "author": "Kailas Rathod (https://github.com/kailas-rathod)",
35
- "type": "commonjs",
36
- "main": "./lib/module/index.js",
37
- "types": "./lib/typescript/src/index.d.ts",
38
- "files": [
39
- "src",
40
- "lib",
41
- "android",
42
- "ios",
43
- "*.podspec",
44
- "react-native.config.js",
45
- "!ios/build",
46
- "!android/build",
47
- "!android/gradle",
48
- "!android/gradlew",
49
- "!android/gradlew.bat",
50
- "!android/local.properties",
51
- "!**/__tests__",
52
- "!**/__fixtures__",
53
- "!**/__mocks__",
54
- "!**/.*"
55
- ],
56
- "scripts": {
57
- "prepare": "bob build",
58
- "typecheck": "tsc",
59
- "lint": "eslint \"**/*.{js,ts,tsx}\"",
60
- "test": "jest",
61
- "check": "yarn typecheck && yarn lint && yarn test",
62
- "release": "release-it --only-version"
63
- },
64
- "devDependencies": {
65
- "@commitlint/config-conventional": "^19.8.1",
66
- "@eslint/compat": "^1.3.2",
67
- "@eslint/eslintrc": "^3.3.4",
68
- "@eslint/js": "^9.35.0",
69
- "@react-native/babel-preset": "0.83.0",
70
- "@react-native-community/eslint-config": "^3.2.0",
71
- "@release-it/conventional-changelog": "^10.0.1",
72
- "@types/jest": "^29.5.14",
73
- "@types/react": "^19.2.0",
74
- "commitlint": "^19.8.1",
75
- "eslint": "^10.0.2",
76
- "eslint-config-prettier": "^10.1.8",
77
- "eslint-plugin-prettier": "^5.5.4",
78
- "eslint-plugin-react-native": "^5.0.0",
79
- "jest": "^30.2.0",
80
- "prettier": "^2.8.8",
81
- "react": "19.2.4",
82
- "react-native": "0.83.0",
83
- "react-native-builder-bob": "0.39.1",
84
- "release-it": "^19.0.4",
85
- "typescript": "^5.9.3"
86
- },
87
- "peerDependencies": {
88
- "react": "*",
89
- "react-native": "*"
90
- },
91
- "publishConfig": {
92
- "registry": "https://registry.npmjs.org/"
93
- },
94
- "eslintConfig": {
95
- "root": true,
96
- "extends": [
97
- "@react-native-community",
98
- "prettier"
99
- ],
100
- "rules": {
101
- "prettier/prettier": [
102
- "error",
103
- {
104
- "quoteProps": "consistent",
105
- "singleQuote": true,
106
- "tabWidth": 2,
107
- "trailingComma": "es5",
108
- "useTabs": false
109
- }
110
- ]
111
- }
112
- },
113
- "eslintIgnore": [
114
- "node_modules/",
115
- "lib/"
116
- ],
117
- "packageManager": "yarn@4.11.0",
118
- "react-native-builder-bob": {
119
- "source": "src",
120
- "output": "lib",
121
- "targets": [
122
- [
123
- "module",
124
- {
125
- "esm": true
126
- }
127
- ],
128
- [
129
- "typescript",
130
- {
131
- "project": "tsconfig.build.json"
132
- }
133
- ]
134
- ]
135
- },
136
- "codegenConfig": {
137
- "name": "OtpAutoVerifySpec",
138
- "type": "modules",
139
- "jsSrcsDir": "src",
140
- "android": {
141
- "javaPackageName": "com.otpautoverify"
142
- }
143
- },
144
- "prettier": {
145
- "quoteProps": "consistent",
146
- "singleQuote": true,
147
- "tabWidth": 2,
148
- "trailingComma": "es5",
149
- "useTabs": false
150
- },
151
- "commitlint": {
152
- "extends": [
153
- "@commitlint/config-conventional"
154
- ]
155
- },
156
- "release-it": {
157
- "git": {
158
- "commitMessage": "chore: release ${version}",
159
- "tagName": "v${version}"
160
- },
161
- "npm": {
162
- "publish": true
163
- },
164
- "github": {
165
- "release": true
166
- },
167
- "plugins": {
168
- "@release-it/conventional-changelog": {
169
- "preset": {
170
- "name": "angular"
171
- }
172
- }
173
- }
174
- }
175
- }
1
+ {
2
+ "name": "react-native-otp-auto-verify",
3
+ "version": "0.2.1",
4
+ "description": "react-native-otp-auto-verify is a React Native library for automatic OTP verification on Android and iOS. It uses the Google SMS Retriever API to detect OTP codes automatically without requiring the READ_SMS permission. The library is fully Play Store compliant, improves user login experience, supports TurboModule, and works with the React Native New Architecture",
5
+ "keywords": [
6
+ "react-native",
7
+ "react-native-library",
8
+ "react-native-otp",
9
+ "react-native-otp-auto-verify",
10
+ "react-native-sms",
11
+ "otp",
12
+ "otp-verification",
13
+ "otp-auto-verify",
14
+ "otp-auto-read",
15
+ "sms",
16
+ "sms-retriever",
17
+ "google-sms-retriever",
18
+ "android-otp",
19
+ "ios-otp",
20
+ "otp-without-permission",
21
+ "play-store-compliant",
22
+ "react-native-new-architecture",
23
+ "turbo-module"
24
+ ],
25
+ "homepage": "https://github.com/kailas-rathod/react-native-otp-auto-verify?tab=readme-ov-file",
26
+ "bugs": {
27
+ "url": "https://github.com/kailas-rathod/react-native-otp-auto-verify/issues"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/kailas-rathod/react-native-otp-auto-verify.git"
32
+ },
33
+ "license": "MIT",
34
+ "author": "Kailas Rathod (https://github.com/kailas-rathod)",
35
+ "type": "commonjs",
36
+ "main": "./lib/module/index.js",
37
+ "types": "./lib/typescript/src/index.d.ts",
38
+ "files": [
39
+ "src",
40
+ "lib",
41
+ "android",
42
+ "ios",
43
+ "*.podspec",
44
+ "react-native.config.js",
45
+ "!ios/build",
46
+ "!android/build",
47
+ "!android/gradle",
48
+ "!android/gradlew",
49
+ "!android/gradlew.bat",
50
+ "!android/local.properties",
51
+ "!**/__tests__",
52
+ "!**/__fixtures__",
53
+ "!**/__mocks__",
54
+ "!**/.*"
55
+ ],
56
+ "scripts": {
57
+ "prepare": "bob build",
58
+ "typecheck": "tsc",
59
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
60
+ "test": "jest",
61
+ "check": "yarn typecheck && yarn lint && yarn test",
62
+ "release": "release-it --only-version"
63
+ },
64
+ "devDependencies": {
65
+ "@babel/eslint-parser": "^7.28.0",
66
+ "@commitlint/config-conventional": "^19.8.1",
67
+ "@eslint/eslintrc": "^3.3.4",
68
+ "@eslint/js": "9.35.0",
69
+ "@react-native-community/eslint-config": "^3.2.0",
70
+ "@react-native-community/eslint-plugin": "^1.1.0",
71
+ "@typescript-eslint/eslint-plugin": "^8.15.0",
72
+ "@typescript-eslint/parser": "^8.15.0",
73
+ "@react-native/babel-preset": "0.83.3",
74
+ "@release-it/conventional-changelog": "^10.0.6",
75
+ "@types/jest": "^29.5.14",
76
+ "@types/react": "^19.2.0",
77
+ "commitlint": "^19.8.1",
78
+ "eslint": "9.35.0",
79
+ "eslint-config-prettier": "^9.1.0",
80
+ "eslint-plugin-eslint-comments": "^3.2.0",
81
+ "eslint-plugin-ft-flow": "^2.0.1",
82
+ "eslint-plugin-jest": "^28.11.0",
83
+ "eslint-plugin-prettier": "^5.5.4",
84
+ "eslint-plugin-react": "^7.37.0",
85
+ "eslint-plugin-react-hooks": "^4.6.0",
86
+ "eslint-plugin-react-native": "^5.0.0",
87
+ "jest": "^30.3.0",
88
+ "prettier": "^3.4.2",
89
+ "react": "19.2.5",
90
+ "react-native": "0.83.3",
91
+ "react-native-builder-bob": "0.39.1",
92
+ "release-it": "^19.0.4",
93
+ "typescript": "^5.9.3"
94
+ },
95
+ "peerDependencies": {
96
+ "react": "*",
97
+ "react-native": "*"
98
+ },
99
+ "publishConfig": {
100
+ "registry": "https://registry.npmjs.org/"
101
+ },
102
+ "eslintConfig": {
103
+ "root": true,
104
+ "extends": [
105
+ "@react-native-community",
106
+ "prettier"
107
+ ],
108
+ "rules": {
109
+ "prettier/prettier": [
110
+ "error",
111
+ {
112
+ "quoteProps": "consistent",
113
+ "singleQuote": true,
114
+ "tabWidth": 2,
115
+ "trailingComma": "es5",
116
+ "useTabs": false
117
+ }
118
+ ]
119
+ }
120
+ },
121
+ "eslintIgnore": [
122
+ "node_modules/",
123
+ "lib/"
124
+ ],
125
+ "packageManager": "yarn@4.11.0",
126
+ "react-native-builder-bob": {
127
+ "source": "src",
128
+ "output": "lib",
129
+ "targets": [
130
+ [
131
+ "module",
132
+ {
133
+ "esm": true
134
+ }
135
+ ],
136
+ [
137
+ "typescript",
138
+ {
139
+ "project": "tsconfig.build.json"
140
+ }
141
+ ]
142
+ ]
143
+ },
144
+ "codegenConfig": {
145
+ "name": "OtpAutoVerifySpec",
146
+ "type": "modules",
147
+ "jsSrcsDir": "src",
148
+ "android": {
149
+ "javaPackageName": "com.otpautoverify"
150
+ }
151
+ },
152
+ "prettier": {
153
+ "quoteProps": "consistent",
154
+ "singleQuote": true,
155
+ "tabWidth": 2,
156
+ "trailingComma": "es5",
157
+ "useTabs": false
158
+ },
159
+ "commitlint": {
160
+ "extends": [
161
+ "@commitlint/config-conventional"
162
+ ]
163
+ },
164
+ "release-it": {
165
+ "git": {
166
+ "commitMessage": "chore: release ${version}",
167
+ "tagName": "v${version}"
168
+ },
169
+ "npm": {
170
+ "publish": true
171
+ },
172
+ "github": {
173
+ "release": true
174
+ },
175
+ "plugins": {
176
+ "@release-it/conventional-changelog": {
177
+ "preset": {
178
+ "name": "angular"
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
@@ -1,25 +1,37 @@
1
- import { NativeModules, TurboModuleRegistry, type TurboModule } from 'react-native';
2
-
3
- export interface OtpAutoVerifySpec extends TurboModule {
4
- getConstants(): { OTP_RECEIVED_EVENT: string };
5
- getHash(): Promise<ReadonlyArray<string>>;
6
- startSmsRetriever(): Promise<boolean>;
7
- addListener(eventName: string): void;
8
- removeListeners(count: number): void;
9
- }
10
-
11
- function getNativeModule(): OtpAutoVerifySpec {
12
- try {
13
- return TurboModuleRegistry.getEnforcing<OtpAutoVerifySpec>('OtpAutoVerify');
14
- } catch {
15
- const legacy = NativeModules.OtpAutoVerify;
16
- if (!legacy) {
17
- throw new Error(
18
- 'OtpAutoVerify native module is not available. Ensure the library is properly linked.'
19
- );
20
- }
21
- return legacy as OtpAutoVerifySpec;
22
- }
23
- }
24
-
25
- export default getNativeModule();
1
+ import {
2
+ NativeModules,
3
+ TurboModuleRegistry,
4
+ type TurboModule,
5
+ } from 'react-native';
6
+
7
+ export interface Spec extends TurboModule {
8
+ getConstants(): { OTP_RECEIVED_EVENT: string };
9
+ getHash(): Promise<ReadonlyArray<string>>;
10
+ startSmsRetriever(): Promise<boolean>;
11
+ addListener(eventName: string): void;
12
+ removeListeners(count: number): void;
13
+ }
14
+
15
+ function loadNativeModule(): Spec {
16
+ try {
17
+ return TurboModuleRegistry.getEnforcing<Spec>('OtpAutoVerify');
18
+ } catch {
19
+ const legacy = NativeModules.OtpAutoVerify;
20
+ if (!legacy) {
21
+ throw new Error(
22
+ 'OtpAutoVerify native module is not available. Ensure the library is properly linked.'
23
+ );
24
+ }
25
+ return legacy as Spec;
26
+ }
27
+ }
28
+
29
+ let cachedModule: Spec | null = null;
30
+
31
+ /** Resolves the native module on first use so importing this package does not throw before APIs run. */
32
+ export function getOtpNativeModule(): Spec {
33
+ if (cachedModule === null) {
34
+ cachedModule = loadNativeModule();
35
+ }
36
+ return cachedModule;
37
+ }
package/src/index.tsx CHANGED
@@ -1,15 +1,9 @@
1
1
  import * as React from 'react';
2
2
  import { NativeEventEmitter, Platform } from 'react-native';
3
- import NativeOtpAutoVerify from './NativeOtpAutoVerify';
3
+ import { getOtpNativeModule } from './NativeOtpAutoVerify';
4
4
 
5
- const eventEmitter =
6
- Platform.OS === 'android' && NativeOtpAutoVerify
7
- ? new NativeEventEmitter(NativeOtpAutoVerify)
8
- : null;
9
-
10
- const OTP_RECEIVED_EVENT =
11
- (NativeOtpAutoVerify.getConstants?.()?.OTP_RECEIVED_EVENT as string) ??
12
- 'otpReceived';
5
+ /** Must match native `OTP_RECEIVED_EVENT` (Android/iOS). */
6
+ export const OTP_RECEIVED_EVENT = 'otpReceived';
13
7
 
14
8
  export const TIMEOUT_MESSAGE = 'Timeout Error.';
15
9
  const DEFAULT_DIGITS = 6;
@@ -19,11 +13,29 @@ const MAX_OTP_DIGITS = 8;
19
13
 
20
14
  export type OtpDigits = 4 | 5 | 6 | 7 | 8;
21
15
 
22
- function getOtpRegex(digits: number): RegExp {
23
- if (digits < MIN_OTP_DIGITS || digits > MAX_OTP_DIGITS) {
24
- return new RegExp(`\\b(\\d{${DEFAULT_DIGITS}})\\b`);
16
+ let androidEventEmitter: NativeEventEmitter | null | undefined;
17
+
18
+ function getAndroidEventEmitter(): NativeEventEmitter | null {
19
+ if (Platform.OS !== 'android') return null;
20
+ if (androidEventEmitter !== undefined) {
21
+ return androidEventEmitter;
22
+ }
23
+ try {
24
+ androidEventEmitter = new NativeEventEmitter(getOtpNativeModule());
25
+ return androidEventEmitter;
26
+ } catch {
27
+ androidEventEmitter = null;
28
+ return null;
25
29
  }
26
- return new RegExp(`\\b(\\d{${digits}})\\b`);
30
+ }
31
+
32
+ function getOtpRegex(digits: number): RegExp {
33
+ const d =
34
+ digits >= MIN_OTP_DIGITS && digits <= MAX_OTP_DIGITS
35
+ ? digits
36
+ : DEFAULT_DIGITS;
37
+ // Prefer non-`\b` boundaries so OTPs still match after ":" and similar (e.g. "OTP:123456").
38
+ return new RegExp(`(^|[^0-9])(\\d{${d}})(?!\\d)`);
27
39
  }
28
40
 
29
41
  export interface UseOtpVerificationOptions {
@@ -64,13 +76,13 @@ export function extractOtp(
64
76
  if (!trimmed) return null;
65
77
  const regex = getOtpRegex(numberOfDigits);
66
78
  const match = trimmed.match(regex);
67
- return match ? match[1]! : null;
79
+ return match ? match[2]! : null;
68
80
  }
69
81
 
70
82
  /** Returns app hash strings for the current app. Android only; iOS returns []. */
71
83
  export async function getHash(): Promise<string[]> {
72
84
  if (Platform.OS !== 'android') return [];
73
- const arr = await NativeOtpAutoVerify.getHash();
85
+ const arr = await getOtpNativeModule().getHash();
74
86
  return Array.from(arr);
75
87
  }
76
88
 
@@ -84,12 +96,17 @@ export async function activateOtpListener(
84
96
  handler: (sms: string, extractedOtp?: string | null) => void,
85
97
  options?: { numberOfDigits?: OtpDigits }
86
98
  ): Promise<OtpListenerSubscription> {
87
- if (Platform.OS !== 'android' || !eventEmitter) {
99
+ if (Platform.OS !== 'android') {
100
+ return NOOP_SUBSCRIPTION;
101
+ }
102
+
103
+ const emitter = getAndroidEventEmitter();
104
+ if (!emitter) {
88
105
  return NOOP_SUBSCRIPTION;
89
106
  }
90
107
 
91
108
  const numberOfDigits = options?.numberOfDigits ?? DEFAULT_DIGITS;
92
- const subscription = eventEmitter.addListener(
109
+ const subscription = emitter.addListener(
93
110
  OTP_RECEIVED_EVENT,
94
111
  (...args: unknown[]) => {
95
112
  const smsText = String(args[0] ?? '');
@@ -97,11 +114,21 @@ export async function activateOtpListener(
97
114
  }
98
115
  );
99
116
 
100
- await NativeOtpAutoVerify.startSmsRetriever();
117
+ try {
118
+ await getOtpNativeModule().startSmsRetriever();
119
+ } catch (err) {
120
+ subscription.remove();
121
+ throw err;
122
+ }
123
+
101
124
  return {
102
125
  remove: () => {
103
126
  subscription.remove();
104
- NativeOtpAutoVerify.removeListeners(0);
127
+ try {
128
+ getOtpNativeModule().removeListeners(0);
129
+ } catch {
130
+ // Native may be unavailable during teardown
131
+ }
105
132
  },
106
133
  };
107
134
  }
@@ -111,8 +138,11 @@ export async function activateOtpListener(
111
138
  * The native module ignores the count parameter and always unregisters the SMS receiver.
112
139
  */
113
140
  export function removeListener(): void {
114
- if (Platform.OS === 'android') {
115
- NativeOtpAutoVerify.removeListeners(0);
141
+ if (Platform.OS !== 'android') return;
142
+ try {
143
+ getOtpNativeModule().removeListeners(0);
144
+ } catch {
145
+ // Native not linked or already torn down
116
146
  }
117
147
  }
118
148
 
@@ -153,7 +183,9 @@ export function useOtpVerification(
153
183
  const hashes = await getHash();
154
184
  setHashCode(hashes[0] ?? '');
155
185
  } catch (err) {
156
- setError(err instanceof Error ? err : new Error(String(err)));
186
+ const wrapped = err instanceof Error ? err : new Error(String(err));
187
+ setError(wrapped);
188
+ return;
157
189
  }
158
190
 
159
191
  const sub = await activateOtpListener(
@@ -193,5 +225,3 @@ export function useOtpVerification(
193
225
  [hashCode, otp, sms, timeoutError, error, startListening, stopListening]
194
226
  );
195
227
  }
196
-
197
- export { OTP_RECEIVED_EVENT };