@dynamic-labs-sdk/react-native-captcha 0.0.0 → 1.27.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/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @dynamic-labs-sdk/react-native-captcha
2
+
3
+ Captcha challenge widgets for React Native apps using Dynamic.
4
+
5
+ Provides self-contained `HCaptchaChallenge` and `TurnstileChallenge` components that render their provider's widget in a `react-native-webview` and return the captcha token over a postMessage bridge. Pass the token to `@dynamic-labs-sdk/client`'s `setCaptchaToken` before sign-in methods that require captcha.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pnpm add @dynamic-labs-sdk/react-native-captcha
11
+ ```
12
+
13
+ Requires `@dynamic-labs-sdk/client` and `react-native-webview`.
14
+
15
+ ## Usage
16
+
17
+ Read the active provider and site key from `getCaptchaSettings`, then render the matching challenge component.
18
+
19
+ ```tsx
20
+ import { useState } from 'react';
21
+ import { View, Button } from 'react-native';
22
+ import { CaptchaProviderEnum, getCaptchaSettings, setCaptchaToken } from '@dynamic-labs-sdk/client';
23
+ import { HCaptchaChallenge, TurnstileChallenge } from '@dynamic-labs-sdk/react-native-captcha';
24
+
25
+ function SignInScreen() {
26
+ const [captchaReady, setCaptchaReady] = useState(false);
27
+ const settings = getCaptchaSettings();
28
+ const siteKey = settings?.siteKey;
29
+
30
+ const handleSignIn = async () => {
31
+ // sendEmailOTP, auth.wallet.connect, etc.
32
+ };
33
+
34
+ if (!settings || !siteKey) {
35
+ return <Button title="Sign in" onPress={handleSignIn} />;
36
+ }
37
+
38
+ const handleToken = (token: string) => {
39
+ setCaptchaToken({ captchaToken: token });
40
+ setCaptchaReady(true);
41
+ };
42
+
43
+ const Challenge =
44
+ settings.provider === CaptchaProviderEnum.Hcaptcha
45
+ ? HCaptchaChallenge
46
+ : TurnstileChallenge;
47
+
48
+ return (
49
+ <View>
50
+ <Challenge
51
+ baseUrl="https://your-whitelisted-origin.com"
52
+ siteKey={siteKey}
53
+ onToken={handleToken}
54
+ onError={(error) => console.error(error.message)}
55
+ />
56
+ <Button title="Sign in" disabled={!captchaReady} onPress={handleSignIn} />
57
+ </View>
58
+ );
59
+ }
60
+ ```
61
+
62
+ The `baseUrl` prop is the origin the widget document is served under. hCaptcha and Turnstile site keys only render on whitelisted hostnames, so set it to a domain you have added to your provider's site configuration.
63
+
64
+ ## Components
65
+
66
+ | Component | Provider |
67
+ | --------- | -------- |
68
+ | `HCaptchaChallenge` | hCaptcha |
69
+ | `TurnstileChallenge` | Cloudflare Turnstile |
70
+
71
+ Both components accept `baseUrl`, `siteKey`, `onToken`, `onError`, and an optional `style` prop. `onToken` fires once when the user solves the challenge; `onError` fires once on challenge error, token expiry, or WebView load failure. The returned errors are typed (`HCaptchaError` / `TurnstileError`) so you can branch on `error instanceof HCaptchaError` or inspect `error.message`.
72
+
73
+ Use the `style` prop to override the default height, border radius, or other container styles to match your app's design.
74
+
75
+ ## Retrying a challenge
76
+
77
+ `onToken` and `onError` each fire at most once per mounted widget. If you need to let the user retry after a challenge error or token expiry, unmount and remount the component, for example by incrementing a React `key`:
78
+
79
+ ```tsx
80
+ const [attemptKey, setAttemptKey] = useState(0);
81
+
82
+ <Challenge
83
+ key={attemptKey}
84
+ baseUrl="https://your-whitelisted-origin.com"
85
+ siteKey={siteKey}
86
+ onToken={handleToken}
87
+ onError={(error) => {
88
+ console.error(error.message);
89
+ setAttemptKey((k) => k + 1);
90
+ }}
91
+ />
92
+ ```
@@ -0,0 +1,86 @@
1
+ import type { FC } from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ import { type LogLevel } from '@dynamic-labs-sdk/client/core';
4
+ import { HCaptchaError } from '../../errors/HCaptchaError';
5
+ /**
6
+ * `testID` e2e drivers use to locate the hCaptcha widget on screen.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <View testID={HCAPTCHA_CHALLENGE_TEST_ID} />
11
+ * ```
12
+ * @see HCaptchaChallenge
13
+ * @see HCaptchaChallengeProps
14
+ */
15
+ export declare const HCAPTCHA_CHALLENGE_TEST_ID = "hcaptcha-challenge";
16
+ /**
17
+ * Props for the {@link HCaptchaChallenge} component.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * const props: HCaptchaChallengeProps = {
22
+ * baseUrl: 'https://your-whitelisted-origin.com',
23
+ * onError: console.error,
24
+ * onToken: setToken,
25
+ * siteKey: '10000000-ffff-ffff-ffff-000000000001',
26
+ * logLevel: 'warn',
27
+ * };
28
+ * ```
29
+ * @see HCaptchaChallenge
30
+ * @see HCaptchaError
31
+ */
32
+ export type HCaptchaChallengeProps = {
33
+ /**
34
+ * Origin the widget document is served under. hCaptcha refuses to render on
35
+ * origin-less documents, and real (non-test) site keys only render on hosts
36
+ * whitelisted for the key — pass an origin your site key accepts.
37
+ */
38
+ baseUrl: string;
39
+ /**
40
+ * Optional minimum log level for the component's internal logger. Defaults
41
+ * to `'warn'` when omitted.
42
+ */
43
+ logLevel?: LogLevel;
44
+ /**
45
+ * Called when the widget cannot produce a token: the WebView fails to load,
46
+ * hCaptcha reports a challenge error, or the token expires unclaimed —
47
+ * so callers awaiting `onToken` fail immediately instead of hanging until
48
+ * their own timeout.
49
+ */
50
+ onError: (error: HCaptchaError) => void;
51
+ /** Called with the token hCaptcha issues when the widget is solved. */
52
+ onToken: (token: string) => void;
53
+ /**
54
+ * The environment's hCaptcha site key, from the SDK's `getCaptchaSettings`.
55
+ */
56
+ siteKey: string;
57
+ /**
58
+ * Optional style for the outer container. Use this to set width/height,
59
+ * margins, border radius, or background color to match your app's UI.
60
+ */
61
+ style?: StyleProp<ViewStyle>;
62
+ };
63
+ /**
64
+ * Renders the hCaptcha checkbox widget in an inline WebView and reports the
65
+ * resulting captcha token over the postMessage bridge. The caller hands the
66
+ * token to the SDK's `setCaptchaToken`. Exactly one of `onToken`/`onError`
67
+ * fires, exactly once — the first terminal event (token, challenge error,
68
+ * expiry, or WebView load failure) wins.
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * <HCaptchaChallenge
73
+ * baseUrl="https://your-whitelisted-origin.com"
74
+ * siteKey="10000000-ffff-ffff-ffff-000000000001"
75
+ * onToken={(token) => setCaptchaToken({ captchaToken: token })}
76
+ * onError={(error) => console.error(error.message)}
77
+ * style={{ height: 260 }}
78
+ * logLevel="warn"
79
+ * />
80
+ * ```
81
+ * @returns A React Native view that hosts the captcha WebView.
82
+ * @see HCaptchaChallengeProps
83
+ * @see HCaptchaError
84
+ */
85
+ export declare const HCaptchaChallenge: FC<HCaptchaChallengeProps>;
86
+ //# sourceMappingURL=HCaptchaChallenge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HCaptchaChallenge.d.ts","sourceRoot":"","sources":["../../../src/components/HCaptchaChallenge/HCaptchaChallenge.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,+BAA+B,CAAC;AAG5E,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,uBAAuB,CAAC;AAmB/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;;;OAKG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACxC,uEAAuE;IACvE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC;AAkDF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,iBAAiB,EAAE,EAAE,CAAC,sBAAsB,CAyFxD,CAAC"}
@@ -0,0 +1,86 @@
1
+ import type { FC } from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ import { type LogLevel } from '@dynamic-labs-sdk/client/core';
4
+ import { TurnstileError } from '../../errors/TurnstileError';
5
+ /**
6
+ * `testID` e2e drivers use to locate the Turnstile widget on screen.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * <View testID={TURNSTILE_CHALLENGE_TEST_ID} />
11
+ * ```
12
+ * @see TurnstileChallenge
13
+ * @see TurnstileChallengeProps
14
+ */
15
+ export declare const TURNSTILE_CHALLENGE_TEST_ID = "turnstile-challenge";
16
+ /**
17
+ * Props for the {@link TurnstileChallenge} component.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * const props: TurnstileChallengeProps = {
22
+ * baseUrl: 'https://your-whitelisted-origin.com',
23
+ * onError: console.error,
24
+ * onToken: setToken,
25
+ * siteKey: '1x00000000000000000000AA',
26
+ * logLevel: 'warn',
27
+ * };
28
+ * ```
29
+ * @see TurnstileChallenge
30
+ * @see TurnstileError
31
+ */
32
+ export type TurnstileChallengeProps = {
33
+ /**
34
+ * Origin the widget document is served under. Turnstile refuses to render on
35
+ * origin-less documents, and real (non-test) site keys only render on hosts
36
+ * whitelisted for the key — pass an origin your site key accepts.
37
+ */
38
+ baseUrl: string;
39
+ /**
40
+ * Optional minimum log level for the component's internal logger. Defaults
41
+ * to `'warn'` when omitted.
42
+ */
43
+ logLevel?: LogLevel;
44
+ /**
45
+ * Called when the widget cannot produce a token: the WebView fails to load,
46
+ * Turnstile reports a challenge error, or the token expires unclaimed —
47
+ * so callers awaiting `onToken` fail immediately instead of hanging until
48
+ * their own timeout.
49
+ */
50
+ onError: (error: TurnstileError) => void;
51
+ /** Called with the token Turnstile issues when the widget is solved. */
52
+ onToken: (token: string) => void;
53
+ /**
54
+ * The environment's Turnstile site key, from the SDK's `getCaptchaSettings`.
55
+ */
56
+ siteKey: string;
57
+ /**
58
+ * Optional style for the outer container. Use this to set width/height,
59
+ * margins, border radius, or background color to match your app's UI.
60
+ */
61
+ style?: StyleProp<ViewStyle>;
62
+ };
63
+ /**
64
+ * Renders the Cloudflare Turnstile widget in an inline WebView and reports the
65
+ * resulting captcha token over the postMessage bridge. The caller hands the
66
+ * token to the SDK's `setCaptchaToken`. Exactly one of `onToken`/`onError`
67
+ * fires, exactly once — the first terminal event (token, challenge error,
68
+ * expiry, or WebView load failure) wins.
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * <TurnstileChallenge
73
+ * baseUrl="https://your-whitelisted-origin.com"
74
+ * siteKey="1x00000000000000000000AA"
75
+ * onToken={(token) => setCaptchaToken({ captchaToken: token })}
76
+ * onError={(error) => console.error(error.message)}
77
+ * style={{ height: 120 }}
78
+ * logLevel="warn"
79
+ * />
80
+ * ```
81
+ * @returns A React Native view that hosts the captcha WebView.
82
+ * @see TurnstileChallengeProps
83
+ * @see TurnstileError
84
+ */
85
+ export declare const TurnstileChallenge: FC<TurnstileChallengeProps>;
86
+ //# sourceMappingURL=TurnstileChallenge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TurnstileChallenge.d.ts","sourceRoot":"","sources":["../../../src/components/TurnstileChallenge/TurnstileChallenge.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzD,OAAO,EAAE,KAAK,QAAQ,EAAgB,MAAM,+BAA+B,CAAC;AAG5E,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,wBAAwB,CAAC;AAmBjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB;;;;;OAKG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,wEAAwE;IACxE,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC;AAkDF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,kBAAkB,EAAE,EAAE,CAAC,uBAAuB,CA2F1D,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { BaseError } from '@dynamic-labs-sdk/client';
2
+ /**
3
+ * Thrown when the embedded hCaptcha challenge fails to resolve — for example
4
+ * the widget reports an error, the token expires, or the user closes the
5
+ * challenge without completing it.
6
+ *
7
+ * The {@link HCaptchaChallenge} component surfaces this typed error through its
8
+ * `onError` callback so consumers can distinguish a failed captcha from other
9
+ * failures and prompt the user to retry rather than treating it as a fatal
10
+ * error.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <HCaptchaChallenge
15
+ * onError={(error) => {
16
+ * if (error instanceof HCaptchaError) {
17
+ * promptRetry(error.message);
18
+ * }
19
+ * }}
20
+ * />
21
+ * ```
22
+ * @see HCaptchaChallenge
23
+ * @see HCaptchaChallengeProps
24
+ */
25
+ export declare class HCaptchaError extends BaseError {
26
+ constructor({ description }: {
27
+ description: string | undefined;
28
+ });
29
+ }
30
+ //# sourceMappingURL=HCaptchaError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HCaptchaError.d.ts","sourceRoot":"","sources":["../../src/errors/HCaptchaError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAc,SAAQ,SAAS;gBAC9B,EAAE,WAAW,EAAE,EAAE;QAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;CASjE"}
@@ -0,0 +1,30 @@
1
+ import { BaseError } from '@dynamic-labs-sdk/client';
2
+ /**
3
+ * Thrown when the embedded Cloudflare Turnstile challenge fails to resolve —
4
+ * for example the widget reports an error, the token expires, or the user
5
+ * closes the challenge without completing it.
6
+ *
7
+ * The {@link TurnstileChallenge} component surfaces this typed error through
8
+ * its `onError` callback so consumers can distinguish a failed captcha from
9
+ * other failures and prompt the user to retry rather than treating it as a
10
+ * fatal error.
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * <TurnstileChallenge
15
+ * onError={(error) => {
16
+ * if (error instanceof TurnstileError) {
17
+ * promptRetry(error.message);
18
+ * }
19
+ * }}
20
+ * />
21
+ * ```
22
+ * @see TurnstileChallenge
23
+ * @see TurnstileChallengeProps
24
+ */
25
+ export declare class TurnstileError extends BaseError {
26
+ constructor({ description }: {
27
+ description: string | undefined;
28
+ });
29
+ }
30
+ //# sourceMappingURL=TurnstileError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TurnstileError.d.ts","sourceRoot":"","sources":["../../src/errors/TurnstileError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,cAAe,SAAQ,SAAS;gBAC/B,EAAE,WAAW,EAAE,EAAE;QAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;CASjE"}
@@ -0,0 +1,7 @@
1
+ export { HCAPTCHA_CHALLENGE_TEST_ID, HCaptchaChallenge, } from '../components/HCaptchaChallenge/HCaptchaChallenge';
2
+ export type { HCaptchaChallengeProps } from '../components/HCaptchaChallenge/HCaptchaChallenge';
3
+ export { HCaptchaError } from '../errors/HCaptchaError';
4
+ export { TURNSTILE_CHALLENGE_TEST_ID, TurnstileChallenge, } from '../components/TurnstileChallenge/TurnstileChallenge';
5
+ export type { TurnstileChallengeProps } from '../components/TurnstileChallenge/TurnstileChallenge';
6
+ export { TurnstileError } from '../errors/TurnstileError';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/exports/index.ts"],"names":[],"mappings":"AASA,OAAO,EACL,0BAA0B,EAC1B,iBAAiB,GAClB,MAAM,mDAAmD,CAAC;AAC3D,YAAY,EAAE,sBAAsB,EAAE,MAAM,mDAAmD,CAAC;AAChG,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,EACL,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,qDAAqD,CAAC;AAC7D,YAAY,EAAE,uBAAuB,EAAE,MAAM,qDAAqD,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC"}