@dynamic-labs-sdk/react-native-captcha 0.0.0 → 1.27.2
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 +92 -0
- package/dist/components/HCaptchaChallenge/HCaptchaChallenge.d.ts +86 -0
- package/dist/components/HCaptchaChallenge/HCaptchaChallenge.d.ts.map +1 -0
- package/dist/components/TurnstileChallenge/TurnstileChallenge.d.ts +86 -0
- package/dist/components/TurnstileChallenge/TurnstileChallenge.d.ts.map +1 -0
- package/dist/errors/HCaptchaError.d.ts +30 -0
- package/dist/errors/HCaptchaError.d.ts.map +1 -0
- package/dist/errors/TurnstileError.d.ts +30 -0
- package/dist/errors/TurnstileError.d.ts.map +1 -0
- package/dist/exports/index.d.ts +7 -0
- package/dist/exports/index.d.ts.map +1 -0
- package/dist/index.cjs +434 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +400 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/package.json +45 -1
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { assertPackageVersion } from "@dynamic-labs-sdk/assert-package-version";
|
|
2
|
+
import { useMemo, useRef } from "react";
|
|
3
|
+
import { StyleSheet, View } from "react-native";
|
|
4
|
+
import WebView from "react-native-webview";
|
|
5
|
+
import { createLogger } from "@dynamic-labs-sdk/client/core";
|
|
6
|
+
import * as z from "zod/mini";
|
|
7
|
+
import { BaseError } from "@dynamic-labs-sdk/client";
|
|
8
|
+
import { jsx } from "react/jsx-runtime";
|
|
9
|
+
|
|
10
|
+
//#region package.json
|
|
11
|
+
var name = "@dynamic-labs-sdk/react-native-captcha";
|
|
12
|
+
var version = "1.27.2";
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region src/errors/HCaptchaError.ts
|
|
16
|
+
/**
|
|
17
|
+
* Thrown when the embedded hCaptcha challenge fails to resolve — for example
|
|
18
|
+
* the widget reports an error, the token expires, or the user closes the
|
|
19
|
+
* challenge without completing it.
|
|
20
|
+
*
|
|
21
|
+
* The {@link HCaptchaChallenge} component surfaces this typed error through its
|
|
22
|
+
* `onError` callback so consumers can distinguish a failed captcha from other
|
|
23
|
+
* failures and prompt the user to retry rather than treating it as a fatal
|
|
24
|
+
* error.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* <HCaptchaChallenge
|
|
29
|
+
* onError={(error) => {
|
|
30
|
+
* if (error instanceof HCaptchaError) {
|
|
31
|
+
* promptRetry(error.message);
|
|
32
|
+
* }
|
|
33
|
+
* }}
|
|
34
|
+
* />
|
|
35
|
+
* ```
|
|
36
|
+
* @see HCaptchaChallenge
|
|
37
|
+
* @see HCaptchaChallengeProps
|
|
38
|
+
*/
|
|
39
|
+
var HCaptchaError = class extends BaseError {
|
|
40
|
+
constructor({ description }) {
|
|
41
|
+
super({
|
|
42
|
+
cause: null,
|
|
43
|
+
code: "hcaptcha_failed",
|
|
44
|
+
docsUrl: null,
|
|
45
|
+
name: "HCaptchaError",
|
|
46
|
+
shortMessage: `hCaptcha failed: ${description ?? "unknown error"}`
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/components/HCaptchaChallenge/HCaptchaChallenge.tsx
|
|
53
|
+
/**
|
|
54
|
+
* `testID` e2e drivers use to locate the hCaptcha widget on screen.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* <View testID={HCAPTCHA_CHALLENGE_TEST_ID} />
|
|
59
|
+
* ```
|
|
60
|
+
* @see HCaptchaChallenge
|
|
61
|
+
* @see HCaptchaChallengeProps
|
|
62
|
+
*/
|
|
63
|
+
const HCAPTCHA_CHALLENGE_TEST_ID = "hcaptcha-challenge";
|
|
64
|
+
const bridgeMessageSchema$1 = z.union([z.object({
|
|
65
|
+
message: z.optional(z.string()),
|
|
66
|
+
type: z.enum(["error"])
|
|
67
|
+
}), z.object({
|
|
68
|
+
token: z.string(),
|
|
69
|
+
type: z.enum(["token"])
|
|
70
|
+
})]);
|
|
71
|
+
const sanitizeSiteKey$1 = (siteKey) => siteKey.replace(/[^\w-]/g, "");
|
|
72
|
+
const buildHCaptchaHtml = (siteKey) => `
|
|
73
|
+
<!DOCTYPE html>
|
|
74
|
+
<html>
|
|
75
|
+
<head>
|
|
76
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
77
|
+
<!-- No Subresource Integrity: hCaptcha serves a mutable loader script and
|
|
78
|
+
does not support SRI pinning; a pinned hash would break on their next
|
|
79
|
+
deploy. The script loads from hCaptcha's own origin over HTTPS. -->
|
|
80
|
+
<script src="https://js.hcaptcha.com/1/api.js" async defer><\/script>
|
|
81
|
+
</head>
|
|
82
|
+
<body style="margin:0;padding:0;display:flex;justify-content:center;align-items:center;min-height:100vh;">
|
|
83
|
+
<div
|
|
84
|
+
class="h-captcha"
|
|
85
|
+
data-sitekey="${siteKey}"
|
|
86
|
+
data-callback="onSuccess"
|
|
87
|
+
data-error-callback="onChallengeError"
|
|
88
|
+
data-expired-callback="onExpired"
|
|
89
|
+
></div>
|
|
90
|
+
<script>
|
|
91
|
+
const post = (payload) => {
|
|
92
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(payload));
|
|
93
|
+
};
|
|
94
|
+
// hCaptcha resolves the data-*-callback attributes by name off the global
|
|
95
|
+
// scope, so these handlers must be assigned onto window — a bare const
|
|
96
|
+
// binding would not be reachable and the callbacks would never fire.
|
|
97
|
+
window.onSuccess = (token) => {
|
|
98
|
+
post({ type: 'token', token: token });
|
|
99
|
+
};
|
|
100
|
+
window.onChallengeError = (error) => {
|
|
101
|
+
// Cap the hCaptcha-supplied error before forwarding it: it only ends up
|
|
102
|
+
// in an HCaptchaError message, so bound its length rather than surface an
|
|
103
|
+
// arbitrarily long string from the widget.
|
|
104
|
+
const errorMessage = String(error).substring(0, 200);
|
|
105
|
+
post({ type: 'error', message: 'challenge error: ' + errorMessage });
|
|
106
|
+
};
|
|
107
|
+
window.onExpired = () => {
|
|
108
|
+
post({ type: 'error', message: 'token expired before it was claimed' });
|
|
109
|
+
};
|
|
110
|
+
<\/script>
|
|
111
|
+
</body>
|
|
112
|
+
</html>
|
|
113
|
+
`;
|
|
114
|
+
/**
|
|
115
|
+
* Renders the hCaptcha checkbox widget in an inline WebView and reports the
|
|
116
|
+
* resulting captcha token over the postMessage bridge. The caller hands the
|
|
117
|
+
* token to the SDK's `setCaptchaToken`. Exactly one of `onToken`/`onError`
|
|
118
|
+
* fires, exactly once — the first terminal event (token, challenge error,
|
|
119
|
+
* expiry, or WebView load failure) wins.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```tsx
|
|
123
|
+
* <HCaptchaChallenge
|
|
124
|
+
* baseUrl="https://your-whitelisted-origin.com"
|
|
125
|
+
* siteKey="10000000-ffff-ffff-ffff-000000000001"
|
|
126
|
+
* onToken={(token) => setCaptchaToken({ captchaToken: token })}
|
|
127
|
+
* onError={(error) => console.error(error.message)}
|
|
128
|
+
* style={{ height: 260 }}
|
|
129
|
+
* logLevel="warn"
|
|
130
|
+
* />
|
|
131
|
+
* ```
|
|
132
|
+
* @returns A React Native view that hosts the captcha WebView.
|
|
133
|
+
* @see HCaptchaChallengeProps
|
|
134
|
+
* @see HCaptchaError
|
|
135
|
+
*/
|
|
136
|
+
const HCaptchaChallenge = ({ baseUrl, logLevel, onError, onToken, siteKey, style }) => {
|
|
137
|
+
const settledRef = useRef(false);
|
|
138
|
+
const logger = useMemo(() => createLogger({ level: logLevel ?? "warn" }), [logLevel]);
|
|
139
|
+
const settleWith = (dispatch) => {
|
|
140
|
+
if (settledRef.current) return;
|
|
141
|
+
settledRef.current = true;
|
|
142
|
+
dispatch();
|
|
143
|
+
};
|
|
144
|
+
const handleMessage = (event) => {
|
|
145
|
+
let data;
|
|
146
|
+
try {
|
|
147
|
+
data = JSON.parse(event.nativeEvent.data);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
logger.debug("Malformed hCaptcha bridge message", {
|
|
150
|
+
error: error instanceof Error ? error.message : String(error),
|
|
151
|
+
rawData: String(event.nativeEvent.data).slice(0, 200)
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const parsed = bridgeMessageSchema$1.safeParse(data);
|
|
156
|
+
if (!parsed.success) {
|
|
157
|
+
logger.debug("Malformed hCaptcha bridge message", {
|
|
158
|
+
error: parsed.error.message,
|
|
159
|
+
rawData: String(event.nativeEvent.data).slice(0, 200)
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (parsed.data.type === "token") {
|
|
164
|
+
const token = parsed.data.token;
|
|
165
|
+
settleWith(() => onToken(token));
|
|
166
|
+
} else {
|
|
167
|
+
const message = parsed.data.message;
|
|
168
|
+
settleWith(() => onError(new HCaptchaError({ description: message })));
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
const handleLoadError = (event) => {
|
|
172
|
+
settleWith(() => onError(new HCaptchaError({ description: `WebView failed to load: ${event.nativeEvent.description ?? "unknown error"}` })));
|
|
173
|
+
};
|
|
174
|
+
return /* @__PURE__ */ jsx(View, {
|
|
175
|
+
style: [styles$1.container, style],
|
|
176
|
+
testID: HCAPTCHA_CHALLENGE_TEST_ID,
|
|
177
|
+
children: /* @__PURE__ */ jsx(WebView, {
|
|
178
|
+
domStorageEnabled: true,
|
|
179
|
+
javaScriptEnabled: true,
|
|
180
|
+
thirdPartyCookiesEnabled: true,
|
|
181
|
+
onError: handleLoadError,
|
|
182
|
+
onHttpError: handleLoadError,
|
|
183
|
+
onMessage: handleMessage,
|
|
184
|
+
originWhitelist: ["https://*"],
|
|
185
|
+
source: {
|
|
186
|
+
baseUrl,
|
|
187
|
+
html: buildHCaptchaHtml(sanitizeSiteKey$1(siteKey))
|
|
188
|
+
},
|
|
189
|
+
style: styles$1.webView
|
|
190
|
+
})
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
const styles$1 = StyleSheet.create({
|
|
194
|
+
container: {
|
|
195
|
+
height: 260,
|
|
196
|
+
overflow: "hidden"
|
|
197
|
+
},
|
|
198
|
+
webView: {
|
|
199
|
+
backgroundColor: "transparent",
|
|
200
|
+
flex: 1
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/errors/TurnstileError.ts
|
|
206
|
+
/**
|
|
207
|
+
* Thrown when the embedded Cloudflare Turnstile challenge fails to resolve —
|
|
208
|
+
* for example the widget reports an error, the token expires, or the user
|
|
209
|
+
* closes the challenge without completing it.
|
|
210
|
+
*
|
|
211
|
+
* The {@link TurnstileChallenge} component surfaces this typed error through
|
|
212
|
+
* its `onError` callback so consumers can distinguish a failed captcha from
|
|
213
|
+
* other failures and prompt the user to retry rather than treating it as a
|
|
214
|
+
* fatal error.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```tsx
|
|
218
|
+
* <TurnstileChallenge
|
|
219
|
+
* onError={(error) => {
|
|
220
|
+
* if (error instanceof TurnstileError) {
|
|
221
|
+
* promptRetry(error.message);
|
|
222
|
+
* }
|
|
223
|
+
* }}
|
|
224
|
+
* />
|
|
225
|
+
* ```
|
|
226
|
+
* @see TurnstileChallenge
|
|
227
|
+
* @see TurnstileChallengeProps
|
|
228
|
+
*/
|
|
229
|
+
var TurnstileError = class extends BaseError {
|
|
230
|
+
constructor({ description }) {
|
|
231
|
+
super({
|
|
232
|
+
cause: null,
|
|
233
|
+
code: "turnstile_failed",
|
|
234
|
+
docsUrl: null,
|
|
235
|
+
name: "TurnstileError",
|
|
236
|
+
shortMessage: `Turnstile failed: ${description ?? "unknown error"}`
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
//#endregion
|
|
242
|
+
//#region src/components/TurnstileChallenge/TurnstileChallenge.tsx
|
|
243
|
+
/**
|
|
244
|
+
* `testID` e2e drivers use to locate the Turnstile widget on screen.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```tsx
|
|
248
|
+
* <View testID={TURNSTILE_CHALLENGE_TEST_ID} />
|
|
249
|
+
* ```
|
|
250
|
+
* @see TurnstileChallenge
|
|
251
|
+
* @see TurnstileChallengeProps
|
|
252
|
+
*/
|
|
253
|
+
const TURNSTILE_CHALLENGE_TEST_ID = "turnstile-challenge";
|
|
254
|
+
const bridgeMessageSchema = z.union([z.object({
|
|
255
|
+
message: z.optional(z.string()),
|
|
256
|
+
type: z.enum(["error"])
|
|
257
|
+
}), z.object({
|
|
258
|
+
token: z.string(),
|
|
259
|
+
type: z.enum(["token"])
|
|
260
|
+
})]);
|
|
261
|
+
const sanitizeSiteKey = (siteKey) => siteKey.replace(/[^\w-]/g, "");
|
|
262
|
+
const buildTurnstileHtml = (siteKey) => `
|
|
263
|
+
<!DOCTYPE html>
|
|
264
|
+
<html>
|
|
265
|
+
<head>
|
|
266
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
267
|
+
<!-- No Subresource Integrity: Cloudflare serves a mutable loader script and
|
|
268
|
+
does not support SRI pinning; a pinned hash would break on their next
|
|
269
|
+
deploy. The script loads from Cloudflare's own origin over HTTPS. -->
|
|
270
|
+
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer><\/script>
|
|
271
|
+
</head>
|
|
272
|
+
<body style="margin:0;padding:0;display:flex;justify-content:center;align-items:center;min-height:100vh;">
|
|
273
|
+
<div
|
|
274
|
+
class="cf-turnstile"
|
|
275
|
+
data-sitekey="${siteKey}"
|
|
276
|
+
data-callback="onSuccess"
|
|
277
|
+
data-error-callback="onError"
|
|
278
|
+
data-expired-callback="onExpired"
|
|
279
|
+
></div>
|
|
280
|
+
<script>
|
|
281
|
+
const post = (payload) => {
|
|
282
|
+
window.ReactNativeWebView.postMessage(JSON.stringify(payload));
|
|
283
|
+
};
|
|
284
|
+
// Turnstile resolves the data-*-callback attributes by name off the global
|
|
285
|
+
// scope, so these handlers must be assigned onto window — a bare const
|
|
286
|
+
// binding would not be reachable and the callbacks would never fire.
|
|
287
|
+
window.onSuccess = (token) => {
|
|
288
|
+
post({ type: 'token', token: token });
|
|
289
|
+
};
|
|
290
|
+
window.onError = (error) => {
|
|
291
|
+
// Cap the Turnstile-supplied error before forwarding it: it only ends up
|
|
292
|
+
// in a TurnstileError message, so bound its length rather than surface an
|
|
293
|
+
// arbitrarily long string from the widget.
|
|
294
|
+
const errorMessage = String(error).substring(0, 200);
|
|
295
|
+
post({ type: 'error', message: 'challenge error: ' + errorMessage });
|
|
296
|
+
};
|
|
297
|
+
window.onExpired = () => {
|
|
298
|
+
post({ type: 'error', message: 'token expired before it was claimed' });
|
|
299
|
+
};
|
|
300
|
+
<\/script>
|
|
301
|
+
</body>
|
|
302
|
+
</html>
|
|
303
|
+
`;
|
|
304
|
+
/**
|
|
305
|
+
* Renders the Cloudflare Turnstile widget in an inline WebView and reports the
|
|
306
|
+
* resulting captcha token over the postMessage bridge. The caller hands the
|
|
307
|
+
* token to the SDK's `setCaptchaToken`. Exactly one of `onToken`/`onError`
|
|
308
|
+
* fires, exactly once — the first terminal event (token, challenge error,
|
|
309
|
+
* expiry, or WebView load failure) wins.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```tsx
|
|
313
|
+
* <TurnstileChallenge
|
|
314
|
+
* baseUrl="https://your-whitelisted-origin.com"
|
|
315
|
+
* siteKey="1x00000000000000000000AA"
|
|
316
|
+
* onToken={(token) => setCaptchaToken({ captchaToken: token })}
|
|
317
|
+
* onError={(error) => console.error(error.message)}
|
|
318
|
+
* style={{ height: 120 }}
|
|
319
|
+
* logLevel="warn"
|
|
320
|
+
* />
|
|
321
|
+
* ```
|
|
322
|
+
* @returns A React Native view that hosts the captcha WebView.
|
|
323
|
+
* @see TurnstileChallengeProps
|
|
324
|
+
* @see TurnstileError
|
|
325
|
+
*/
|
|
326
|
+
const TurnstileChallenge = ({ baseUrl, logLevel, onError, onToken, siteKey, style }) => {
|
|
327
|
+
const settledRef = useRef(false);
|
|
328
|
+
const logger = useMemo(() => createLogger({ level: logLevel ?? "warn" }), [logLevel]);
|
|
329
|
+
const settleWith = (dispatch) => {
|
|
330
|
+
if (settledRef.current) return;
|
|
331
|
+
settledRef.current = true;
|
|
332
|
+
dispatch();
|
|
333
|
+
};
|
|
334
|
+
const handleMessage = (event) => {
|
|
335
|
+
let data;
|
|
336
|
+
try {
|
|
337
|
+
data = JSON.parse(event.nativeEvent.data);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
logger.debug("Malformed Turnstile bridge message", {
|
|
340
|
+
error: error instanceof Error ? error.message : String(error),
|
|
341
|
+
rawData: String(event.nativeEvent.data).slice(0, 200)
|
|
342
|
+
});
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const parsed = bridgeMessageSchema.safeParse(data);
|
|
346
|
+
if (!parsed.success) {
|
|
347
|
+
logger.debug("Malformed Turnstile bridge message", {
|
|
348
|
+
error: parsed.error.message,
|
|
349
|
+
rawData: String(event.nativeEvent.data).slice(0, 200)
|
|
350
|
+
});
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (parsed.data.type === "token") {
|
|
354
|
+
const token = parsed.data.token;
|
|
355
|
+
settleWith(() => onToken(token));
|
|
356
|
+
} else {
|
|
357
|
+
const message = parsed.data.message;
|
|
358
|
+
settleWith(() => onError(new TurnstileError({ description: message })));
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
const handleLoadError = (event) => {
|
|
362
|
+
settleWith(() => onError(new TurnstileError({ description: `WebView failed to load: ${event.nativeEvent.description ?? "unknown error"}` })));
|
|
363
|
+
};
|
|
364
|
+
return /* @__PURE__ */ jsx(View, {
|
|
365
|
+
style: [styles.container, style],
|
|
366
|
+
testID: TURNSTILE_CHALLENGE_TEST_ID,
|
|
367
|
+
children: /* @__PURE__ */ jsx(WebView, {
|
|
368
|
+
domStorageEnabled: true,
|
|
369
|
+
javaScriptEnabled: true,
|
|
370
|
+
thirdPartyCookiesEnabled: true,
|
|
371
|
+
onError: handleLoadError,
|
|
372
|
+
onHttpError: handleLoadError,
|
|
373
|
+
onMessage: handleMessage,
|
|
374
|
+
originWhitelist: ["https://*", "about:srcdoc"],
|
|
375
|
+
source: {
|
|
376
|
+
baseUrl,
|
|
377
|
+
html: buildTurnstileHtml(sanitizeSiteKey(siteKey))
|
|
378
|
+
},
|
|
379
|
+
style: styles.webView
|
|
380
|
+
})
|
|
381
|
+
});
|
|
382
|
+
};
|
|
383
|
+
const styles = StyleSheet.create({
|
|
384
|
+
container: {
|
|
385
|
+
height: 120,
|
|
386
|
+
overflow: "hidden"
|
|
387
|
+
},
|
|
388
|
+
webView: {
|
|
389
|
+
backgroundColor: "transparent",
|
|
390
|
+
flex: 1
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
//#endregion
|
|
395
|
+
//#region src/exports/index.ts
|
|
396
|
+
assertPackageVersion(name, version);
|
|
397
|
+
|
|
398
|
+
//#endregion
|
|
399
|
+
export { HCAPTCHA_CHALLENGE_TEST_ID, HCaptchaChallenge, HCaptchaError, TURNSTILE_CHALLENGE_TEST_ID, TurnstileChallenge, TurnstileError };
|
|
400
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","names":["bridgeMessageSchema","sanitizeSiteKey","HCaptchaChallenge: FC<HCaptchaChallengeProps>","data: unknown","styles","TurnstileChallenge: FC<TurnstileChallengeProps>","data: unknown","packageName","packageVersion"],"sources":["../package.json","../src/errors/HCaptchaError.ts","../src/components/HCaptchaChallenge/HCaptchaChallenge.tsx","../src/errors/TurnstileError.ts","../src/components/TurnstileChallenge/TurnstileChallenge.tsx","../src/exports/index.ts"],"sourcesContent":["","import { BaseError } from '@dynamic-labs-sdk/client';\n\n/**\n * Thrown when the embedded hCaptcha challenge fails to resolve — for example\n * the widget reports an error, the token expires, or the user closes the\n * challenge without completing it.\n *\n * The {@link HCaptchaChallenge} component surfaces this typed error through its\n * `onError` callback so consumers can distinguish a failed captcha from other\n * failures and prompt the user to retry rather than treating it as a fatal\n * error.\n *\n * @example\n * ```tsx\n * <HCaptchaChallenge\n * onError={(error) => {\n * if (error instanceof HCaptchaError) {\n * promptRetry(error.message);\n * }\n * }}\n * />\n * ```\n * @see HCaptchaChallenge\n * @see HCaptchaChallengeProps\n */\nexport class HCaptchaError extends BaseError {\n constructor({ description }: { description: string | undefined }) {\n super({\n cause: null,\n code: 'hcaptcha_failed',\n docsUrl: null,\n name: 'HCaptchaError',\n shortMessage: `hCaptcha failed: ${description ?? 'unknown error'}`,\n });\n }\n}\n","'use client';\n\nimport type { FC } from 'react';\nimport { useMemo, useRef } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { StyleSheet, View } from 'react-native';\nimport type { WebViewMessageEvent } from 'react-native-webview';\nimport WebView from 'react-native-webview';\nimport { type LogLevel, createLogger } from '@dynamic-labs-sdk/client/core';\nimport * as z from 'zod/mini';\n\nimport { HCaptchaError } from '../../errors/HCaptchaError';\n\n/**\n * `testID` e2e drivers use to locate the hCaptcha widget on screen.\n *\n * @example\n * ```tsx\n * <View testID={HCAPTCHA_CHALLENGE_TEST_ID} />\n * ```\n * @see HCaptchaChallenge\n * @see HCaptchaChallengeProps\n */\nexport const HCAPTCHA_CHALLENGE_TEST_ID = 'hcaptcha-challenge';\n\n// react-native-webview only exports the message event type from its root;\n// for error events this is the minimal shape both onError and onHttpError\n// deliver.\ntype WebViewErrorEvent = { nativeEvent: { description?: string } };\n\n// Payload the widget document posts over the bridge (see buildHCaptchaHtml).\nconst bridgeMessageSchema = z.union([\n z.object({\n message: z.optional(z.string()),\n type: z.enum(['error']),\n }),\n z.object({\n token: z.string(),\n type: z.enum(['token']),\n }),\n]);\n\n/**\n * Props for the {@link HCaptchaChallenge} component.\n *\n * @example\n * ```tsx\n * const props: HCaptchaChallengeProps = {\n * baseUrl: 'https://your-whitelisted-origin.com',\n * onError: console.error,\n * onToken: setToken,\n * siteKey: '10000000-ffff-ffff-ffff-000000000001',\n * logLevel: 'warn',\n * };\n * ```\n * @see HCaptchaChallenge\n * @see HCaptchaError\n */\nexport type HCaptchaChallengeProps = {\n /**\n * Origin the widget document is served under. hCaptcha refuses to render on\n * origin-less documents, and real (non-test) site keys only render on hosts\n * whitelisted for the key — pass an origin your site key accepts.\n */\n baseUrl: string;\n /**\n * Optional minimum log level for the component's internal logger. Defaults\n * to `'warn'` when omitted.\n */\n logLevel?: LogLevel;\n /**\n * Called when the widget cannot produce a token: the WebView fails to load,\n * hCaptcha reports a challenge error, or the token expires unclaimed —\n * so callers awaiting `onToken` fail immediately instead of hanging until\n * their own timeout.\n */\n onError: (error: HCaptchaError) => void;\n /** Called with the token hCaptcha issues when the widget is solved. */\n onToken: (token: string) => void;\n /**\n * The environment's hCaptcha site key, from the SDK's `getCaptchaSettings`.\n */\n siteKey: string;\n /**\n * Optional style for the outer container. Use this to set width/height,\n * margins, border radius, or background color to match your app's UI.\n */\n style?: StyleProp<ViewStyle>;\n};\n\n// The site key is server-controlled, but it lands inside an HTML attribute —\n// keep only the charset real hCaptcha site keys use so a misconfigured value\n// cannot break out of the markup.\nconst sanitizeSiteKey = (siteKey: string) => siteKey.replace(/[^\\w-]/g, '');\n\nconst buildHCaptchaHtml = (siteKey: string) => `\n<!DOCTYPE html>\n<html>\n<head>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <!-- No Subresource Integrity: hCaptcha serves a mutable loader script and\n does not support SRI pinning; a pinned hash would break on their next\n deploy. The script loads from hCaptcha's own origin over HTTPS. -->\n <script src=\"https://js.hcaptcha.com/1/api.js\" async defer></script>\n</head>\n<body style=\"margin:0;padding:0;display:flex;justify-content:center;align-items:center;min-height:100vh;\">\n <div\n class=\"h-captcha\"\n data-sitekey=\"${siteKey}\"\n data-callback=\"onSuccess\"\n data-error-callback=\"onChallengeError\"\n data-expired-callback=\"onExpired\"\n ></div>\n <script>\n const post = (payload) => {\n window.ReactNativeWebView.postMessage(JSON.stringify(payload));\n };\n // hCaptcha resolves the data-*-callback attributes by name off the global\n // scope, so these handlers must be assigned onto window — a bare const\n // binding would not be reachable and the callbacks would never fire.\n window.onSuccess = (token) => {\n post({ type: 'token', token: token });\n };\n window.onChallengeError = (error) => {\n // Cap the hCaptcha-supplied error before forwarding it: it only ends up\n // in an HCaptchaError message, so bound its length rather than surface an\n // arbitrarily long string from the widget.\n const errorMessage = String(error).substring(0, 200);\n post({ type: 'error', message: 'challenge error: ' + errorMessage });\n };\n window.onExpired = () => {\n post({ type: 'error', message: 'token expired before it was claimed' });\n };\n </script>\n</body>\n</html>\n`;\n\n/**\n * Renders the hCaptcha checkbox widget in an inline WebView and reports the\n * resulting captcha token over the postMessage bridge. The caller hands the\n * token to the SDK's `setCaptchaToken`. Exactly one of `onToken`/`onError`\n * fires, exactly once — the first terminal event (token, challenge error,\n * expiry, or WebView load failure) wins.\n *\n * @example\n * ```tsx\n * <HCaptchaChallenge\n * baseUrl=\"https://your-whitelisted-origin.com\"\n * siteKey=\"10000000-ffff-ffff-ffff-000000000001\"\n * onToken={(token) => setCaptchaToken({ captchaToken: token })}\n * onError={(error) => console.error(error.message)}\n * style={{ height: 260 }}\n * logLevel=\"warn\"\n * />\n * ```\n * @returns A React Native view that hosts the captcha WebView.\n * @see HCaptchaChallengeProps\n * @see HCaptchaError\n */\nexport const HCaptchaChallenge: FC<HCaptchaChallengeProps> = ({\n baseUrl,\n logLevel,\n onError,\n onToken,\n siteKey,\n style,\n}) => {\n // First terminal event wins: hCaptcha only issues one token per solve, but\n // guard against duplicate bridge messages and double error events (onError\n // and onHttpError can both fire for one failure) so the callbacks keep\n // their called-once contract.\n const settledRef = useRef(false);\n\n const logger = useMemo(\n () => createLogger({ level: logLevel ?? 'warn' }),\n [logLevel]\n );\n\n // eslint-disable-next-line custom-rules/require-single-object-param -- (dispatch) is the entire input of this local helper\n const settleWith = (dispatch: () => void) => {\n if (settledRef.current) {\n return;\n }\n\n settledRef.current = true;\n dispatch();\n };\n\n const handleMessage = (event: WebViewMessageEvent) => {\n let data: unknown;\n try {\n data = JSON.parse(event.nativeEvent.data);\n } catch (error) {\n logger.debug('Malformed hCaptcha bridge message', {\n error: error instanceof Error ? error.message : String(error),\n rawData: String(event.nativeEvent.data).slice(0, 200),\n });\n return;\n }\n\n const parsed = bridgeMessageSchema.safeParse(data);\n if (!parsed.success) {\n logger.debug('Malformed hCaptcha bridge message', {\n error: parsed.error.message,\n rawData: String(event.nativeEvent.data).slice(0, 200),\n });\n return;\n }\n\n if (parsed.data.type === 'token') {\n const token = parsed.data.token;\n settleWith(() => onToken(token));\n } else {\n const message = parsed.data.message;\n settleWith(() => onError(new HCaptchaError({ description: message })));\n }\n };\n\n const handleLoadError = (event: WebViewErrorEvent) => {\n settleWith(() =>\n onError(\n new HCaptchaError({\n description: `WebView failed to load: ${event.nativeEvent.description ?? 'unknown error'}`,\n })\n )\n );\n };\n\n return (\n <View style={[styles.container, style]} testID={HCAPTCHA_CHALLENGE_TEST_ID}>\n <WebView\n domStorageEnabled\n javaScriptEnabled\n thirdPartyCookiesEnabled\n onError={handleLoadError}\n onHttpError={handleLoadError}\n onMessage={handleMessage}\n // https-only: hCaptcha's iframes may come from several hosts, but a\n // custom-scheme navigation escaping the WebView is never legitimate.\n originWhitelist={['https://*']}\n source={{\n baseUrl,\n html: buildHCaptchaHtml(sanitizeSiteKey(siteKey)),\n }}\n style={styles.webView}\n />\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n // Tall enough for the checkbox widget plus the verification animation;\n // the challenge popup never appears with the always-pass test site key.\n height: 260,\n overflow: 'hidden',\n },\n webView: {\n backgroundColor: 'transparent',\n flex: 1,\n },\n});\n","import { BaseError } from '@dynamic-labs-sdk/client';\n\n/**\n * Thrown when the embedded Cloudflare Turnstile challenge fails to resolve —\n * for example the widget reports an error, the token expires, or the user\n * closes the challenge without completing it.\n *\n * The {@link TurnstileChallenge} component surfaces this typed error through\n * its `onError` callback so consumers can distinguish a failed captcha from\n * other failures and prompt the user to retry rather than treating it as a\n * fatal error.\n *\n * @example\n * ```tsx\n * <TurnstileChallenge\n * onError={(error) => {\n * if (error instanceof TurnstileError) {\n * promptRetry(error.message);\n * }\n * }}\n * />\n * ```\n * @see TurnstileChallenge\n * @see TurnstileChallengeProps\n */\nexport class TurnstileError extends BaseError {\n constructor({ description }: { description: string | undefined }) {\n super({\n cause: null,\n code: 'turnstile_failed',\n docsUrl: null,\n name: 'TurnstileError',\n shortMessage: `Turnstile failed: ${description ?? 'unknown error'}`,\n });\n }\n}\n","'use client';\n\nimport type { FC } from 'react';\nimport { useMemo, useRef } from 'react';\nimport type { StyleProp, ViewStyle } from 'react-native';\nimport { StyleSheet, View } from 'react-native';\nimport type { WebViewMessageEvent } from 'react-native-webview';\nimport WebView from 'react-native-webview';\nimport { type LogLevel, createLogger } from '@dynamic-labs-sdk/client/core';\nimport * as z from 'zod/mini';\n\nimport { TurnstileError } from '../../errors/TurnstileError';\n\n/**\n * `testID` e2e drivers use to locate the Turnstile widget on screen.\n *\n * @example\n * ```tsx\n * <View testID={TURNSTILE_CHALLENGE_TEST_ID} />\n * ```\n * @see TurnstileChallenge\n * @see TurnstileChallengeProps\n */\nexport const TURNSTILE_CHALLENGE_TEST_ID = 'turnstile-challenge';\n\n// react-native-webview only exports the message event type from its root;\n// for error events this is the minimal shape both onError and onHttpError\n// deliver.\ntype WebViewErrorEvent = { nativeEvent: { description?: string } };\n\n// Payload the widget document posts over the bridge (see buildTurnstileHtml).\nconst bridgeMessageSchema = z.union([\n z.object({\n message: z.optional(z.string()),\n type: z.enum(['error']),\n }),\n z.object({\n token: z.string(),\n type: z.enum(['token']),\n }),\n]);\n\n/**\n * Props for the {@link TurnstileChallenge} component.\n *\n * @example\n * ```tsx\n * const props: TurnstileChallengeProps = {\n * baseUrl: 'https://your-whitelisted-origin.com',\n * onError: console.error,\n * onToken: setToken,\n * siteKey: '1x00000000000000000000AA',\n * logLevel: 'warn',\n * };\n * ```\n * @see TurnstileChallenge\n * @see TurnstileError\n */\nexport type TurnstileChallengeProps = {\n /**\n * Origin the widget document is served under. Turnstile refuses to render on\n * origin-less documents, and real (non-test) site keys only render on hosts\n * whitelisted for the key — pass an origin your site key accepts.\n */\n baseUrl: string;\n /**\n * Optional minimum log level for the component's internal logger. Defaults\n * to `'warn'` when omitted.\n */\n logLevel?: LogLevel;\n /**\n * Called when the widget cannot produce a token: the WebView fails to load,\n * Turnstile reports a challenge error, or the token expires unclaimed —\n * so callers awaiting `onToken` fail immediately instead of hanging until\n * their own timeout.\n */\n onError: (error: TurnstileError) => void;\n /** Called with the token Turnstile issues when the widget is solved. */\n onToken: (token: string) => void;\n /**\n * The environment's Turnstile site key, from the SDK's `getCaptchaSettings`.\n */\n siteKey: string;\n /**\n * Optional style for the outer container. Use this to set width/height,\n * margins, border radius, or background color to match your app's UI.\n */\n style?: StyleProp<ViewStyle>;\n};\n\n// The site key is server-controlled, but it lands inside an HTML attribute —\n// keep only the charset real Turnstile site keys use so a misconfigured value\n// cannot break out of the markup.\nconst sanitizeSiteKey = (siteKey: string) => siteKey.replace(/[^\\w-]/g, '');\n\nconst buildTurnstileHtml = (siteKey: string) => `\n<!DOCTYPE html>\n<html>\n<head>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <!-- No Subresource Integrity: Cloudflare serves a mutable loader script and\n does not support SRI pinning; a pinned hash would break on their next\n deploy. The script loads from Cloudflare's own origin over HTTPS. -->\n <script src=\"https://challenges.cloudflare.com/turnstile/v0/api.js\" async defer></script>\n</head>\n<body style=\"margin:0;padding:0;display:flex;justify-content:center;align-items:center;min-height:100vh;\">\n <div\n class=\"cf-turnstile\"\n data-sitekey=\"${siteKey}\"\n data-callback=\"onSuccess\"\n data-error-callback=\"onError\"\n data-expired-callback=\"onExpired\"\n ></div>\n <script>\n const post = (payload) => {\n window.ReactNativeWebView.postMessage(JSON.stringify(payload));\n };\n // Turnstile resolves the data-*-callback attributes by name off the global\n // scope, so these handlers must be assigned onto window — a bare const\n // binding would not be reachable and the callbacks would never fire.\n window.onSuccess = (token) => {\n post({ type: 'token', token: token });\n };\n window.onError = (error) => {\n // Cap the Turnstile-supplied error before forwarding it: it only ends up\n // in a TurnstileError message, so bound its length rather than surface an\n // arbitrarily long string from the widget.\n const errorMessage = String(error).substring(0, 200);\n post({ type: 'error', message: 'challenge error: ' + errorMessage });\n };\n window.onExpired = () => {\n post({ type: 'error', message: 'token expired before it was claimed' });\n };\n </script>\n</body>\n</html>\n`;\n\n/**\n * Renders the Cloudflare Turnstile widget in an inline WebView and reports the\n * resulting captcha token over the postMessage bridge. The caller hands the\n * token to the SDK's `setCaptchaToken`. Exactly one of `onToken`/`onError`\n * fires, exactly once — the first terminal event (token, challenge error,\n * expiry, or WebView load failure) wins.\n *\n * @example\n * ```tsx\n * <TurnstileChallenge\n * baseUrl=\"https://your-whitelisted-origin.com\"\n * siteKey=\"1x00000000000000000000AA\"\n * onToken={(token) => setCaptchaToken({ captchaToken: token })}\n * onError={(error) => console.error(error.message)}\n * style={{ height: 120 }}\n * logLevel=\"warn\"\n * />\n * ```\n * @returns A React Native view that hosts the captcha WebView.\n * @see TurnstileChallengeProps\n * @see TurnstileError\n */\nexport const TurnstileChallenge: FC<TurnstileChallengeProps> = ({\n baseUrl,\n logLevel,\n onError,\n onToken,\n siteKey,\n style,\n}) => {\n // First terminal event wins: Turnstile only issues one token per solve, but\n // guard against duplicate bridge messages and double error events (onError\n // and onHttpError can both fire for one failure) so the callbacks keep\n // their called-once contract.\n const settledRef = useRef(false);\n\n const logger = useMemo(\n () => createLogger({ level: logLevel ?? 'warn' }),\n [logLevel]\n );\n\n // eslint-disable-next-line custom-rules/require-single-object-param -- (dispatch) is the entire input of this local helper\n const settleWith = (dispatch: () => void) => {\n if (settledRef.current) {\n return;\n }\n\n settledRef.current = true;\n dispatch();\n };\n\n const handleMessage = (event: WebViewMessageEvent) => {\n let data: unknown;\n try {\n data = JSON.parse(event.nativeEvent.data);\n } catch (error) {\n logger.debug('Malformed Turnstile bridge message', {\n error: error instanceof Error ? error.message : String(error),\n rawData: String(event.nativeEvent.data).slice(0, 200),\n });\n return;\n }\n\n const parsed = bridgeMessageSchema.safeParse(data);\n if (!parsed.success) {\n logger.debug('Malformed Turnstile bridge message', {\n error: parsed.error.message,\n rawData: String(event.nativeEvent.data).slice(0, 200),\n });\n return;\n }\n\n if (parsed.data.type === 'token') {\n const token = parsed.data.token;\n settleWith(() => onToken(token));\n } else {\n const message = parsed.data.message;\n settleWith(() => onError(new TurnstileError({ description: message })));\n }\n };\n\n const handleLoadError = (event: WebViewErrorEvent) => {\n settleWith(() =>\n onError(\n new TurnstileError({\n description: `WebView failed to load: ${event.nativeEvent.description ?? 'unknown error'}`,\n })\n )\n );\n };\n\n return (\n <View style={[styles.container, style]} testID={TURNSTILE_CHALLENGE_TEST_ID}>\n <WebView\n domStorageEnabled\n javaScriptEnabled\n thirdPartyCookiesEnabled\n onError={handleLoadError}\n onHttpError={handleLoadError}\n onMessage={handleMessage}\n // Turnstile renders challenge frames over HTTPS, but its iOS WebView\n // placeholder iframes use about:blank/about:srcdoc (the WebView already\n // allows about:blank by default, so we only need to add about:srcdoc).\n // Keep the whitelist https-only to block cleartext or custom-scheme nav.\n originWhitelist={['https://*', 'about:srcdoc']}\n source={{\n baseUrl,\n html: buildTurnstileHtml(sanitizeSiteKey(siteKey)),\n }}\n style={styles.webView}\n />\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n // Tall enough for the widget plus the verification animation.\n height: 120,\n overflow: 'hidden',\n },\n webView: {\n backgroundColor: 'transparent',\n flex: 1,\n },\n});\n","import { assertPackageVersion } from '@dynamic-labs-sdk/assert-package-version';\n\nimport {\n name as packageName,\n version as packageVersion,\n} from '../../package.json';\n\nassertPackageVersion(packageName, packageVersion);\n\nexport {\n HCAPTCHA_CHALLENGE_TEST_ID,\n HCaptchaChallenge,\n} from '../components/HCaptchaChallenge/HCaptchaChallenge';\nexport type { HCaptchaChallengeProps } from '../components/HCaptchaChallenge/HCaptchaChallenge';\nexport { HCaptchaError } from '../errors/HCaptchaError';\n\nexport {\n TURNSTILE_CHALLENGE_TEST_ID,\n TurnstileChallenge,\n} from '../components/TurnstileChallenge/TurnstileChallenge';\nexport type { TurnstileChallengeProps } from '../components/TurnstileChallenge/TurnstileChallenge';\nexport { TurnstileError } from '../errors/TurnstileError';\n"],"mappings":"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"}
|