@prosopo/procaptcha-pow 2.13.6 → 2.13.8

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.
Files changed (35) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +5 -8
  2. package/.turbo/turbo-build$colon$tsc.log +19 -22
  3. package/.turbo/turbo-build.log +5 -11
  4. package/CHANGELOG.md +59 -0
  5. package/LICENSE +201 -0
  6. package/dist/cjs/components/procaptchaWidget.cjs +2 -1
  7. package/dist/cjs/services/Manager.cjs +16 -1
  8. package/dist/components/procaptchaWidget.d.ts.map +1 -1
  9. package/dist/components/procaptchaWidget.js +3 -2
  10. package/dist/components/procaptchaWidget.js.map +1 -1
  11. package/dist/services/Manager.d.ts +1 -0
  12. package/dist/services/Manager.d.ts.map +1 -1
  13. package/dist/services/Manager.js +16 -1
  14. package/dist/services/Manager.js.map +1 -1
  15. package/dist/tests/manager.unit.test.js +28 -0
  16. package/dist/tests/manager.unit.test.js.map +1 -1
  17. package/dist/tests/procaptchaPow.test-d.js +1 -1
  18. package/dist/tests/procaptchaPow.test-d.js.map +1 -1
  19. package/dist/tests/procaptchaWidget.unit.test.js +42 -2
  20. package/dist/tests/procaptchaWidget.unit.test.js.map +1 -1
  21. package/package.json +73 -72
  22. package/src/components/procaptchaPoW.ts +0 -53
  23. package/src/components/procaptchaWidget.ts +0 -294
  24. package/src/index.ts +0 -15
  25. package/src/services/Manager.ts +0 -462
  26. package/src/tests/domHarness.ts +0 -122
  27. package/src/tests/manager.unit.test.ts +0 -1105
  28. package/src/tests/managerHarness.ts +0 -135
  29. package/src/tests/procaptchaPoW.unit.test.ts +0 -143
  30. package/src/tests/procaptchaPow.test-d.ts +0 -128
  31. package/src/tests/procaptchaWidget.unit.test.ts +0 -531
  32. package/tsconfig.cjs.json +0 -43
  33. package/tsconfig.json +0 -47
  34. package/tsconfig.tsbuildinfo +0 -1
  35. package/tsconfig.types.json +0 -9
@@ -1,294 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import { createTranslator, loadI18next } from "@prosopo/locale";
16
- import {
17
- type CheckboxProps,
18
- type Component,
19
- type HoneypotComponent,
20
- type ProcaptchaStateHandle,
21
- Teardown,
22
- buildUpdateState,
23
- createElement,
24
- createProcaptchaState,
25
- createRenderScheduler,
26
- isEventTrusted,
27
- mountCheckbox,
28
- mountHoneypot,
29
- } from "@prosopo/procaptcha-common";
30
- import {
31
- ModeEnum,
32
- type ProcaptchaProps,
33
- type ProcaptchaState,
34
- } from "@prosopo/types";
35
- import { darkTheme, lightTheme } from "@prosopo/widget-skeleton";
36
- import { Manager } from "../services/Manager.js";
37
-
38
- // Define the same event name as in the bundle for consistency
39
- const PROCAPTCHA_EXECUTE_EVENT = "procaptcha:execute";
40
-
41
- export interface ProcaptchaPowHandle {
42
- destroy(): void;
43
- }
44
-
45
- export const mountProcaptchaPowWidget = (
46
- container: HTMLElement,
47
- props: ProcaptchaProps,
48
- ): ProcaptchaPowHandle => {
49
- const teardown = new Teardown();
50
- const config = props.config;
51
- const i18n = props.i18n;
52
- const frictionlessState = props.frictionlessState; // Set up Session ID and Provider if they exist
53
- const callbacks = props.callbacks || {};
54
- const translator = createTranslator(i18n);
55
- const isInvisible = ModeEnum.invisible === config.mode;
56
-
57
- const store: ProcaptchaStateHandle = createProcaptchaState();
58
- let loading = false;
59
- let lastError: ProcaptchaState["error"] = store.state.error;
60
- // Coordinates of the last `manager.start(x, y)` invocation from this widget
61
- // instance — either the checkbox click, or a `startCoords` handoff from a
62
- // prior `onSessionInvalidated` cycle.
63
- let lastCoords: { x: number; y: number } | null = null;
64
- // One-shot guard: a widget instance only escalates a single NO_SESSION_FOUND
65
- // to `onSessionInvalidated`. If the retry also fails, we fall through to
66
- // the pre-existing `frictionlessState.restart()` path instead of looping.
67
- let sessionInvalidatedFired = false;
68
- // A bound button can be clicked again while the first run is still going,
69
- // and in invisible mode there is no spinner standing in the way of it.
70
- let executeRunning = false;
71
-
72
- let honeypot: HoneypotComponent | undefined;
73
- let checkbox: Component<CheckboxProps> | undefined;
74
-
75
- // get the state update mechanism
76
- const updateState = buildUpdateState(store.state, store.update);
77
-
78
- const manager = Manager(
79
- config,
80
- store.state,
81
- updateState,
82
- callbacks,
83
- frictionlessState,
84
- props.onEscalate,
85
- () => honeypot?.getValue(),
86
- );
87
-
88
- const root = createElement("div");
89
- container.appendChild(root);
90
-
91
- if (frictionlessState?.hp) {
92
- // Invisible mode renders no checkbox, but we still render the honeypot
93
- // so bots that scan the DOM for inputs find a tempting target.
94
- honeypot = mountHoneypot(root, { encodedQuestion: frictionlessState.hp });
95
- }
96
-
97
- const runErrorEffect = () => {
98
- if (store.state.error === lastError) {
99
- return;
100
- }
101
- lastError = store.state.error;
102
- if (!store.state.error) {
103
- return;
104
- }
105
- loading = false;
106
- if ("CAPTCHA.NO_SESSION_FOUND" !== store.state.error.key) {
107
- return;
108
- }
109
- // Suppressed only when something is actually going to re-mint: this is
110
- // an internal recovery signal, not something the user should read, so
111
- // hold the spinner rather than paint a support code that is about to
112
- // stop being true. Clearing it re-enters this effect once, which returns
113
- // at the `!error` guard. With no recovery route the error stands — a
114
- // spinner that never resolves is worse than a message.
115
- const willRecover =
116
- (props.onSessionInvalidated && !sessionInvalidatedFired) ||
117
- undefined !== frictionlessState;
118
- if (willRecover) {
119
- loading = true;
120
- store.update({ error: undefined });
121
- }
122
- if (props.onSessionInvalidated && !sessionInvalidatedFired) {
123
- // Preserve the checkbox coords across the retry so the resumed
124
- // submit still carries the real entry-point telemetry. The
125
- // frictionless wrapper mints a fresh session and re-mounts this
126
- // widget with matching `autoStart` + `startCoords`.
127
- sessionInvalidatedFired = true;
128
- props.onSessionInvalidated(lastCoords?.x, lastCoords?.y);
129
- return;
130
- }
131
- if (frictionlessState) {
132
- // Fallback for widgets not mounted under a recovery-aware parent,
133
- // or a second NO_SESSION_FOUND after we've already retried once.
134
- const timer = setTimeout(() => {
135
- frictionlessState.restart();
136
- }, 100);
137
- teardown.add(() => clearTimeout(timer));
138
- }
139
- };
140
-
141
- const render = () => {
142
- runErrorEffect();
143
- checkbox?.update(checkboxProps());
144
- };
145
-
146
- const scheduler = createRenderScheduler(render);
147
-
148
- const checkboxProps = (): CheckboxProps => ({
149
- checked: store.state.isHuman,
150
- theme: "light" === config.theme ? lightTheme : darkTheme,
151
- labelText: translator.isReady() ? translator.t("WIDGET.I_AM_HUMAN") : "",
152
- error: store.state.error?.message,
153
- loadingText: translator.t("WIDGET.CHECKING", {
154
- defaultValue: "Checking that you are human",
155
- }),
156
- loading,
157
- onChange: async (
158
- event: MouseEvent | KeyboardEvent | TouchEvent,
159
- ): Promise<void> => {
160
- if (loading) {
161
- return;
162
- }
163
- loading = true;
164
- scheduler.schedule();
165
-
166
- // Capture click coordinates. Untrusted events are left at (0, 0) so a
167
- // synthesised click can't seed the solution salt.
168
- let x = 0;
169
- let y = 0;
170
- if (!isEventTrusted(event)) {
171
- // Don't capture coordinates for non-trusted events
172
- } else if ("touches" in event && event.touches.length > 0) {
173
- const touch = event.touches[0];
174
- if (touch) {
175
- x = touch.clientX;
176
- y = touch.clientY;
177
- }
178
- } else if ("clientX" in event && "clientY" in event) {
179
- x = event.clientX;
180
- y = event.clientY;
181
- }
182
-
183
- lastCoords = { x, y };
184
- try {
185
- await manager.start(x, y);
186
- } catch (error) {
187
- // Nothing awaits this handler, so a rejection here would surface as
188
- // an unhandled rejection and the spinner would never clear.
189
- console.error("Error starting PoW verification:", error);
190
- }
191
- loading = false;
192
- scheduler.schedule();
193
- },
194
- });
195
-
196
- if (!isInvisible) {
197
- checkbox = mountCheckbox(root, checkboxProps());
198
- }
199
-
200
- teardown.add(scheduler.cancel);
201
- teardown.add(store.subscribe(scheduler.schedule));
202
- teardown.add(translator.subscribe(scheduler.schedule));
203
-
204
- // A bare execute() reaches every invisible widget via document. A targeted
205
- // execute() is dispatched on this widget's container and works in either
206
- // mode, which is what lets a bound button drive a visible widget.
207
- const handleExecute = () => {
208
- if (executeRunning) {
209
- return;
210
- }
211
- executeRunning = true;
212
- if (!isInvisible) {
213
- loading = true;
214
- scheduler.schedule();
215
- }
216
- const logError = (error: unknown) => {
217
- console.error("Error starting PoW verification:", error);
218
- };
219
- const done = () => {
220
- executeRunning = false;
221
- if (!isInvisible) {
222
- loading = false;
223
- scheduler.schedule();
224
- }
225
- };
226
- try {
227
- void manager.start().catch(logError).finally(done);
228
- } catch (error) {
229
- logError(error);
230
- done();
231
- }
232
- };
233
-
234
- if (props.container) {
235
- teardown.addEventListener(
236
- props.container,
237
- PROCAPTCHA_EXECUTE_EVENT,
238
- handleExecute,
239
- );
240
- }
241
- if (isInvisible) {
242
- teardown.addEventListener(
243
- document,
244
- PROCAPTCHA_EXECUTE_EVENT,
245
- handleExecute,
246
- );
247
- }
248
-
249
- if (config.language) {
250
- if (i18n) {
251
- if (i18n.language !== config.language) {
252
- void i18n.changeLanguage(config.language);
253
- }
254
- } else {
255
- // Direct consumers don't go through WidgetFactory, so pass the language
256
- // into loadI18next — first init boots with the right language (skipping
257
- // browser detection), and subsequent calls reconcile via changeLanguage
258
- // inside loadI18next.
259
- void loadI18next(false, config.language);
260
- }
261
- }
262
-
263
- if (props.autoStart) {
264
- loading = true;
265
- const coords = props.startCoords;
266
- lastCoords = coords ?? null;
267
- scheduler.schedule();
268
- manager
269
- .start(coords?.x ?? 0, coords?.y ?? 0)
270
- // The manager handles its own failures, but anything it doesn't (a
271
- // reset that throws, say) would otherwise escape as an unhandled
272
- // rejection and leave the widget stuck showing a spinner.
273
- .catch((error: unknown) => {
274
- console.error("Error starting PoW verification:", error);
275
- })
276
- .finally(() => {
277
- loading = false;
278
- scheduler.schedule();
279
- });
280
- }
281
-
282
- render();
283
-
284
- return {
285
- destroy: () => {
286
- teardown.run();
287
- checkbox?.destroy();
288
- honeypot?.destroy();
289
- root.parentNode?.removeChild(root);
290
- },
291
- };
292
- };
293
-
294
- export default mountProcaptchaPowWidget;
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
- export * from "./components/procaptchaWidget.js";
15
- export * from "./components/procaptchaPoW.js";