@prosopo/procaptcha-puzzle 2.10.39 → 2.10.40

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.
@@ -1,215 +1,188 @@
1
- import { jsxs, Fragment, jsx } from "@emotion/react/jsx-runtime";
2
- import { useTranslation, loadI18next } from "@prosopo/locale";
3
- import { useProcaptcha, buildUpdateState, Honeypot, Checkbox } from "@prosopo/procaptcha-common";
4
- import { ModeEnum } from "@prosopo/types";
5
- import { lightTheme, darkTheme } from "@prosopo/widget-skeleton";
6
- import { useState, useRef, useEffect, useCallback } from "react";
1
+ import "../_virtual/_rolldown/runtime.js";
7
2
  import { Manager } from "../services/Manager.js";
8
3
  import { PuzzleCanvas } from "./PuzzleCanvas.js";
9
- const PROCAPTCHA_EXECUTE_EVENT = "procaptcha:execute";
10
- const Procaptcha = (props) => {
11
- const { t, ready: isTranslationReady } = useTranslation();
12
- const config = props.config;
13
- const i18n = props.i18n;
14
- const theme = "light" === config.theme ? lightTheme : darkTheme;
15
- const frictionlessState = props.frictionlessState;
16
- const callbacks = props.callbacks || {};
17
- const [state, _updateState] = useProcaptcha(useState, useRef);
18
- const [loading, setLoading] = useState(false);
19
- const [puzzlePhase, setPuzzlePhase] = useState("checkbox");
20
- const [challengeData, setChallengeData] = useState(null);
21
- const [showRetry, setShowRetry] = useState(false);
22
- const updateState = buildUpdateState(state, _updateState);
23
- const hpRef = useRef(null);
24
- const manager = useRef(
25
- Manager(
26
- config,
27
- state,
28
- updateState,
29
- callbacks,
30
- frictionlessState,
31
- () => hpRef.current?.value || void 0
32
- )
33
- );
34
- const lastCoordsRef = useRef(null);
35
- const sessionInvalidatedFiredRef = useRef(false);
36
- useEffect(() => {
37
- if (!config.language) return;
38
- if (i18n) {
39
- if (i18n.language !== config.language) {
40
- void i18n.changeLanguage(config.language);
41
- }
42
- return;
43
- }
44
- void loadI18next(false, config.language);
45
- }, [i18n, config.language]);
46
- useEffect(() => {
47
- if (!props.autoStart) return;
48
- setLoading(true);
49
- setShowRetry(false);
50
- const coords = props.startCoords;
51
- lastCoordsRef.current = coords ?? null;
52
- manager.current.start(coords?.x ?? 0, coords?.y ?? 0).then(
53
- (challenge) => {
54
- if (challenge) {
55
- setChallengeData(challenge);
56
- setPuzzlePhase("dragging");
57
- }
58
- setLoading(false);
59
- },
60
- () => setLoading(false)
61
- );
62
- }, [props.autoStart, props.startCoords]);
63
- useEffect(() => {
64
- if (!state.error) return void 0;
65
- setLoading(false);
66
- setPuzzlePhase("checkbox");
67
- setChallengeData(null);
68
- setShowRetry(false);
69
- if (state.error.key !== "CAPTCHA.NO_SESSION_FOUND") return void 0;
70
- if (props.onSessionInvalidated && !sessionInvalidatedFiredRef.current) {
71
- sessionInvalidatedFiredRef.current = true;
72
- const coords = lastCoordsRef.current;
73
- props.onSessionInvalidated(coords?.x, coords?.y);
74
- return void 0;
75
- }
76
- if (frictionlessState) {
77
- const timer = setTimeout(() => {
78
- frictionlessState.restart();
79
- }, 100);
80
- return () => clearTimeout(timer);
81
- }
82
- return void 0;
83
- }, [state.error, frictionlessState, props.onSessionInvalidated]);
84
- useEffect(() => {
85
- if (config.mode === ModeEnum.invisible) {
86
- const handleExecuteEvent = async () => {
87
- if (loading) {
88
- return;
89
- }
90
- setLoading(true);
91
- setShowRetry(false);
92
- try {
93
- const challenge = await manager.current.start();
94
- if (challenge) {
95
- setChallengeData(challenge);
96
- setPuzzlePhase("dragging");
97
- }
98
- } catch (error) {
99
- callbacks.onError?.(
100
- error instanceof Error ? error : new Error(String(error))
101
- );
102
- } finally {
103
- setLoading(false);
104
- }
105
- };
106
- document.addEventListener(PROCAPTCHA_EXECUTE_EVENT, handleExecuteEvent);
107
- return () => {
108
- document.removeEventListener(
109
- PROCAPTCHA_EXECUTE_EVENT,
110
- handleExecuteEvent
111
- );
112
- };
113
- }
114
- return () => {
115
- };
116
- }, [config.mode, callbacks.onError, loading]);
117
- const handlePuzzleComplete = useCallback(
118
- async (finalX, finalY, puzzleEvents) => {
119
- setPuzzlePhase("submitting");
120
- let verified = false;
121
- try {
122
- verified = await manager.current.submitSolution(
123
- finalX,
124
- finalY,
125
- puzzleEvents
126
- );
127
- } catch (error) {
128
- callbacks.onError?.(
129
- error instanceof Error ? error : new Error(String(error))
130
- );
131
- }
132
- if (verified) {
133
- setPuzzlePhase("checkbox");
134
- setChallengeData(null);
135
- setShowRetry(false);
136
- setLoading(false);
137
- return;
138
- }
139
- setShowRetry(true);
140
- setPuzzlePhase("dragging");
141
- try {
142
- const newChallenge = await manager.current.start();
143
- if (newChallenge) {
144
- setChallengeData(newChallenge);
145
- } else {
146
- setPuzzlePhase("checkbox");
147
- setChallengeData(null);
148
- setShowRetry(false);
149
- }
150
- } catch {
151
- setPuzzlePhase("checkbox");
152
- setChallengeData(null);
153
- setShowRetry(false);
154
- }
155
- setLoading(false);
156
- },
157
- [callbacks.onError]
158
- );
159
- const isInvisible = config.mode === ModeEnum.invisible;
160
- const showPuzzleOverlay = (puzzlePhase === "dragging" || puzzlePhase === "submitting") && challengeData;
161
- return /* @__PURE__ */ jsxs(Fragment, { children: [
162
- frictionlessState?.hp && /* @__PURE__ */ jsx(Honeypot, { ref: hpRef, encodedQuestion: frictionlessState.hp }),
163
- showPuzzleOverlay && /* @__PURE__ */ jsx(
164
- PuzzleCanvas,
165
- {
166
- originX: challengeData.originX,
167
- originY: challengeData.originY,
168
- targetX: challengeData.targetX,
169
- targetY: challengeData.targetY,
170
- onComplete: handlePuzzleComplete,
171
- showRetry,
172
- submitting: puzzlePhase === "submitting"
173
- }
174
- ),
175
- !isInvisible && /* @__PURE__ */ jsx(
176
- Checkbox,
177
- {
178
- checked: state.isHuman,
179
- theme,
180
- onChange: async (event) => {
181
- if (loading) {
182
- return;
183
- }
184
- setLoading(true);
185
- setShowRetry(false);
186
- let x = 0;
187
- let y = 0;
188
- const mouseOrTouchEvent = event.nativeEvent;
189
- if (!mouseOrTouchEvent.isTrusted) {
190
- } else if ("touches" in mouseOrTouchEvent && mouseOrTouchEvent.touches.length > 0 && mouseOrTouchEvent.touches[0]) {
191
- x = mouseOrTouchEvent.touches[0].clientX;
192
- y = mouseOrTouchEvent.touches[0].clientY;
193
- } else if ("clientX" in mouseOrTouchEvent && "clientY" in mouseOrTouchEvent) {
194
- x = mouseOrTouchEvent.clientX;
195
- y = mouseOrTouchEvent.clientY;
196
- }
197
- lastCoordsRef.current = { x, y };
198
- const challenge = await manager.current.start(x, y);
199
- if (challenge) {
200
- setChallengeData(challenge);
201
- setPuzzlePhase("dragging");
202
- }
203
- setLoading(false);
204
- },
205
- labelText: isTranslationReady ? t("WIDGET.I_AM_HUMAN") : "",
206
- error: state.error?.message,
207
- "aria-label": "human checkbox",
208
- loading: loading || puzzlePhase === "submitting"
209
- }
210
- )
211
- ] });
212
- };
213
- export {
214
- Procaptcha as default
4
+ import { loadI18next, useTranslation } from "@prosopo/locale";
5
+ import { Checkbox, Honeypot, buildUpdateState, useProcaptcha } from "@prosopo/procaptcha-common";
6
+ import { ModeEnum } from "@prosopo/types";
7
+ import { darkTheme, lightTheme } from "@prosopo/widget-skeleton";
8
+ import { useCallback, useEffect, useRef, useState } from "react";
9
+ import { Fragment, jsx, jsxs } from "@emotion/react/jsx-runtime";
10
+ //#region src/components/ProcaptchaWidget.tsx
11
+ var PROCAPTCHA_EXECUTE_EVENT = "procaptcha:execute";
12
+ var Procaptcha = (props) => {
13
+ const { t, ready: isTranslationReady } = useTranslation();
14
+ const config = props.config;
15
+ const i18n = props.i18n;
16
+ const theme = "light" === config.theme ? lightTheme : darkTheme;
17
+ const frictionlessState = props.frictionlessState;
18
+ const callbacks = props.callbacks || {};
19
+ const [state, _updateState] = useProcaptcha(useState, useRef);
20
+ const [loading, setLoading] = useState(false);
21
+ const [puzzlePhase, setPuzzlePhase] = useState("checkbox");
22
+ const [challengeData, setChallengeData] = useState(null);
23
+ const [showRetry, setShowRetry] = useState(false);
24
+ const updateState = buildUpdateState(state, _updateState);
25
+ const hpRef = useRef(null);
26
+ const manager = useRef(Manager(config, state, updateState, callbacks, frictionlessState, () => hpRef.current?.value || void 0));
27
+ const lastCoordsRef = useRef(null);
28
+ const sessionInvalidatedFiredRef = useRef(false);
29
+ useEffect(() => {
30
+ if (!config.language) return;
31
+ if (i18n) {
32
+ if (i18n.language !== config.language) i18n.changeLanguage(config.language);
33
+ return;
34
+ }
35
+ loadI18next(false, config.language);
36
+ }, [i18n, config.language]);
37
+ useEffect(() => {
38
+ if (!props.autoStart) return;
39
+ setLoading(true);
40
+ setShowRetry(false);
41
+ const coords = props.startCoords;
42
+ lastCoordsRef.current = coords ?? null;
43
+ manager.current.start(coords?.x ?? 0, coords?.y ?? 0).then((challenge) => {
44
+ if (challenge) {
45
+ setChallengeData(challenge);
46
+ setPuzzlePhase("dragging");
47
+ }
48
+ setLoading(false);
49
+ }, () => setLoading(false));
50
+ }, [props.autoStart, props.startCoords]);
51
+ useEffect(() => {
52
+ if (!state.error) return void 0;
53
+ setLoading(false);
54
+ setPuzzlePhase("checkbox");
55
+ setChallengeData(null);
56
+ setShowRetry(false);
57
+ if (state.error.key !== "CAPTCHA.NO_SESSION_FOUND") return void 0;
58
+ if (props.onSessionInvalidated && !sessionInvalidatedFiredRef.current) {
59
+ sessionInvalidatedFiredRef.current = true;
60
+ const coords = lastCoordsRef.current;
61
+ props.onSessionInvalidated(coords?.x, coords?.y);
62
+ return;
63
+ }
64
+ if (frictionlessState) {
65
+ const timer = setTimeout(() => {
66
+ frictionlessState.restart();
67
+ }, 100);
68
+ return () => clearTimeout(timer);
69
+ }
70
+ }, [
71
+ state.error,
72
+ frictionlessState,
73
+ props.onSessionInvalidated
74
+ ]);
75
+ useEffect(() => {
76
+ if (config.mode === ModeEnum.invisible) {
77
+ const handleExecuteEvent = async () => {
78
+ if (loading) return;
79
+ setLoading(true);
80
+ setShowRetry(false);
81
+ try {
82
+ const challenge = await manager.current.start();
83
+ if (challenge) {
84
+ setChallengeData(challenge);
85
+ setPuzzlePhase("dragging");
86
+ }
87
+ } catch (error) {
88
+ callbacks.onError?.(error instanceof Error ? error : new Error(String(error)));
89
+ } finally {
90
+ setLoading(false);
91
+ }
92
+ };
93
+ document.addEventListener(PROCAPTCHA_EXECUTE_EVENT, handleExecuteEvent);
94
+ return () => {
95
+ document.removeEventListener(PROCAPTCHA_EXECUTE_EVENT, handleExecuteEvent);
96
+ };
97
+ }
98
+ return () => {};
99
+ }, [
100
+ config.mode,
101
+ callbacks.onError,
102
+ loading
103
+ ]);
104
+ const handlePuzzleComplete = useCallback(async (finalX, finalY, puzzleEvents) => {
105
+ setPuzzlePhase("submitting");
106
+ let verified = false;
107
+ try {
108
+ verified = await manager.current.submitSolution(finalX, finalY, puzzleEvents);
109
+ } catch (error) {
110
+ callbacks.onError?.(error instanceof Error ? error : new Error(String(error)));
111
+ }
112
+ if (verified) {
113
+ setPuzzlePhase("checkbox");
114
+ setChallengeData(null);
115
+ setShowRetry(false);
116
+ setLoading(false);
117
+ return;
118
+ }
119
+ setShowRetry(true);
120
+ setPuzzlePhase("dragging");
121
+ try {
122
+ const newChallenge = await manager.current.start();
123
+ if (newChallenge) setChallengeData(newChallenge);
124
+ else {
125
+ setPuzzlePhase("checkbox");
126
+ setChallengeData(null);
127
+ setShowRetry(false);
128
+ }
129
+ } catch {
130
+ setPuzzlePhase("checkbox");
131
+ setChallengeData(null);
132
+ setShowRetry(false);
133
+ }
134
+ setLoading(false);
135
+ }, [callbacks.onError]);
136
+ const isInvisible = config.mode === ModeEnum.invisible;
137
+ const showPuzzleOverlay = (puzzlePhase === "dragging" || puzzlePhase === "submitting") && challengeData;
138
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
139
+ frictionlessState?.hp && /* @__PURE__ */ jsx(Honeypot, {
140
+ ref: hpRef,
141
+ encodedQuestion: frictionlessState.hp
142
+ }),
143
+ showPuzzleOverlay && /* @__PURE__ */ jsx(PuzzleCanvas, {
144
+ originX: challengeData.originX,
145
+ originY: challengeData.originY,
146
+ targetX: challengeData.targetX,
147
+ targetY: challengeData.targetY,
148
+ onComplete: handlePuzzleComplete,
149
+ showRetry,
150
+ submitting: puzzlePhase === "submitting"
151
+ }),
152
+ !isInvisible && /* @__PURE__ */ jsx(Checkbox, {
153
+ checked: state.isHuman,
154
+ theme,
155
+ onChange: async (event) => {
156
+ if (loading) return;
157
+ setLoading(true);
158
+ setShowRetry(false);
159
+ let x = 0;
160
+ let y = 0;
161
+ const mouseOrTouchEvent = event.nativeEvent;
162
+ if (!mouseOrTouchEvent.isTrusted) {} else if ("touches" in mouseOrTouchEvent && mouseOrTouchEvent.touches.length > 0 && mouseOrTouchEvent.touches[0]) {
163
+ x = mouseOrTouchEvent.touches[0].clientX;
164
+ y = mouseOrTouchEvent.touches[0].clientY;
165
+ } else if ("clientX" in mouseOrTouchEvent && "clientY" in mouseOrTouchEvent) {
166
+ x = mouseOrTouchEvent.clientX;
167
+ y = mouseOrTouchEvent.clientY;
168
+ }
169
+ lastCoordsRef.current = {
170
+ x,
171
+ y
172
+ };
173
+ const challenge = await manager.current.start(x, y);
174
+ if (challenge) {
175
+ setChallengeData(challenge);
176
+ setPuzzlePhase("dragging");
177
+ }
178
+ setLoading(false);
179
+ },
180
+ labelText: isTranslationReady ? t("WIDGET.I_AM_HUMAN") : "",
181
+ error: state.error?.message,
182
+ "aria-label": "human checkbox",
183
+ loading: loading || puzzlePhase === "submitting"
184
+ })
185
+ ] });
215
186
  };
187
+ //#endregion
188
+ export { Procaptcha as default };