@captchafox/react 1.5.1 → 1.7.0

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/dist/index.cjs CHANGED
@@ -52,42 +52,70 @@ var __async = (__this, __arguments, generator) => {
52
52
  var src_exports = {};
53
53
  __export(src_exports, {
54
54
  CAPTCHA_RESPONSE_KEY: () => CAPTCHA_RESPONSE_KEY,
55
- CaptchaFox: () => CaptchaFox
55
+ CaptchaFox: () => CaptchaFox,
56
+ RetryError: () => RetryError,
57
+ TimeoutError: () => TimeoutError
56
58
  });
57
59
  module.exports = __toCommonJS(src_exports);
58
60
 
59
61
  // ../internal/dist/index.mjs
60
- var resolveFn;
61
- var rejectFn;
62
- var mountInstance = new Promise((resolve, reject) => {
63
- resolveFn = resolve;
64
- rejectFn = reject;
62
+ var RetryError = class extends Error {
63
+ };
64
+ var TimeoutError = class extends Error {
65
+ };
66
+ var backoff = (retryCount) => Math.exp(retryCount) * 150;
67
+ var withRetry = (_0, ..._1) => __async(void 0, [_0, ..._1], function* (callback, { attempts = 4 } = {}) {
68
+ var _a;
69
+ let error;
70
+ for (let i = 0; i < attempts; i++) {
71
+ try {
72
+ return yield callback();
73
+ } catch (err) {
74
+ error = err;
75
+ yield new Promise((r) => setTimeout(r, backoff(i)));
76
+ }
77
+ }
78
+ throw new RetryError((_a = error == null ? void 0 : error.message) != null ? _a : "Exhausted all retries");
65
79
  });
80
+ var mountInstance;
66
81
  var LOAD_FUNC_KEY = "captchaFoxOnLoad";
67
82
  var SCRIPT_SRC = `https://cdn.captchafox.com/api.js?render=explicit&onload=${LOAD_FUNC_KEY}`;
68
- function loadCaptchaScript() {
83
+ function loadScript() {
69
84
  return __async(this, arguments, function* ({ nonce } = {}) {
70
- if (document.querySelector(`script[src="${SCRIPT_SRC}"]`))
85
+ if (mountInstance && document.querySelector(`script[src="${SCRIPT_SRC}"]`)) {
71
86
  return mountInstance;
72
- window[LOAD_FUNC_KEY] = resolveFn;
73
- const script = document.createElement("script");
74
- script.src = SCRIPT_SRC;
75
- script.async = true;
76
- script.defer = true;
77
- script.onerror = rejectFn;
78
- if (nonce) {
79
- script.nonce = nonce;
80
87
  }
81
- document.body.appendChild(script);
88
+ mountInstance = new Promise((resolve, reject) => {
89
+ window[LOAD_FUNC_KEY] = resolve;
90
+ const script = document.createElement("script");
91
+ script.src = SCRIPT_SRC;
92
+ script.async = true;
93
+ script.defer = true;
94
+ script.onerror = (e) => {
95
+ script.remove();
96
+ mountInstance = void 0;
97
+ reject(e);
98
+ };
99
+ if (nonce) {
100
+ script.nonce = nonce;
101
+ }
102
+ document.body.appendChild(script);
103
+ });
82
104
  return mountInstance;
83
105
  });
84
106
  }
107
+ function loadCaptchaScript() {
108
+ return __async(this, arguments, function* (props = {}) {
109
+ return withRetry(() => loadScript(props));
110
+ });
111
+ }
85
112
  var isApiReady = () => typeof (window == null ? void 0 : window.captchafox) !== "undefined";
86
113
 
87
114
  // src/CaptchaFox.tsx
88
115
  var import_react = __toESM(require("react"), 1);
89
116
  var CaptchaFox = (0, import_react.forwardRef)(
90
117
  ({
118
+ executeTimeoutSeconds = 30,
91
119
  sitekey,
92
120
  lang,
93
121
  mode,
@@ -104,6 +132,8 @@ var CaptchaFox = (0, import_react.forwardRef)(
104
132
  const [containerRef, setContainerRef] = (0, import_react.useState)();
105
133
  const [widgetId, setWidgetId] = (0, import_react.useState)();
106
134
  const firstRendered = (0, import_react.useRef)(false);
135
+ const onReady = (0, import_react.useRef)();
136
+ const executeTimeout = (0, import_react.useRef)();
107
137
  (0, import_react.useImperativeHandle)(
108
138
  ref,
109
139
  () => {
@@ -132,7 +162,7 @@ var CaptchaFox = (0, import_react.forwardRef)(
132
162
  },
133
163
  execute: () => {
134
164
  if (!isApiReady() || !widgetId) {
135
- return Promise.reject("[CaptchaFox] Widget has not been loaded");
165
+ return waitAndExecute();
136
166
  }
137
167
  return window.captchafox.execute(widgetId);
138
168
  }
@@ -145,27 +175,11 @@ var CaptchaFox = (0, import_react.forwardRef)(
145
175
  onLoad == null ? void 0 : onLoad();
146
176
  }
147
177
  }, [widgetId]);
148
- const renderCaptcha = () => __async(void 0, null, function* () {
149
- var _a, _b, _c;
150
- (_a = window.captchafox) == null ? void 0 : _a.remove(widgetId);
151
- if (!containerRef || ((_b = containerRef == null ? void 0 : containerRef.children) == null ? void 0 : _b.length) === 1)
152
- return;
153
- const newWidgetId = yield (_c = window.captchafox) == null ? void 0 : _c.render(containerRef, {
154
- lang,
155
- sitekey,
156
- mode,
157
- theme,
158
- i18n,
159
- onError,
160
- onFail,
161
- onClose,
162
- onVerify
163
- });
164
- setWidgetId(newWidgetId);
165
- });
166
178
  (0, import_react.useEffect)(() => {
167
- if (!containerRef)
168
- return;
179
+ return () => clearTimeout(executeTimeout.current);
180
+ }, []);
181
+ (0, import_react.useEffect)(() => {
182
+ if (!containerRef) return;
169
183
  if (firstRendered.current) {
170
184
  if (isApiReady()) {
171
185
  renderCaptcha();
@@ -182,6 +196,41 @@ var CaptchaFox = (0, import_react.forwardRef)(
182
196
  });
183
197
  }
184
198
  }, [containerRef, sitekey, lang, mode]);
199
+ const waitAndExecute = () => {
200
+ return new Promise((resolve, reject) => {
201
+ executeTimeout.current = setTimeout(() => {
202
+ reject(new TimeoutError("Execute timed out"));
203
+ }, executeTimeoutSeconds * 1e3);
204
+ onReady.current = (id) => {
205
+ clearTimeout(executeTimeout.current);
206
+ window.captchafox.execute(id).then(resolve).catch(reject);
207
+ };
208
+ });
209
+ };
210
+ const renderCaptcha = () => __async(void 0, null, function* () {
211
+ var _a, _b, _c;
212
+ (_a = window.captchafox) == null ? void 0 : _a.remove(widgetId);
213
+ if (!containerRef || ((_b = containerRef == null ? void 0 : containerRef.children) == null ? void 0 : _b.length) === 1) return;
214
+ const newWidgetId = yield (_c = window.captchafox) == null ? void 0 : _c.render(containerRef, {
215
+ lang,
216
+ sitekey,
217
+ mode,
218
+ theme,
219
+ i18n,
220
+ onError,
221
+ onFail,
222
+ onClose,
223
+ onVerify
224
+ });
225
+ if (!newWidgetId) {
226
+ return;
227
+ }
228
+ setWidgetId(newWidgetId);
229
+ if (onReady.current) {
230
+ onReady.current(newWidgetId);
231
+ onReady.current = void 0;
232
+ }
233
+ });
185
234
  return /* @__PURE__ */ import_react.default.createElement("div", { ref: setContainerRef, id: widgetId, className });
186
235
  }
187
236
  );
@@ -192,5 +241,7 @@ var CAPTCHA_RESPONSE_KEY = "cf-captcha-response";
192
241
  // Annotate the CommonJS export names for ESM import in node:
193
242
  0 && (module.exports = {
194
243
  CAPTCHA_RESPONSE_KEY,
195
- CaptchaFox
244
+ CaptchaFox,
245
+ RetryError,
246
+ TimeoutError
196
247
  });
package/dist/index.d.cts CHANGED
@@ -1,14 +1,20 @@
1
1
  import { WidgetApi, WidgetOptions } from '@captchafox/types';
2
2
  import React from 'react';
3
3
 
4
+ declare class RetryError extends Error {
5
+ }
6
+ declare class TimeoutError extends Error {
7
+ }
8
+
4
9
  type CaptchaFoxInstance = Omit<WidgetApi, 'render'>;
5
10
  declare const CaptchaFox: React.ForwardRefExoticComponent<WidgetOptions & {
6
11
  /** Called after the widget has been loaded */
7
12
  onLoad?: (() => void) | undefined;
8
13
  className?: string | undefined;
9
14
  nonce?: string | undefined;
15
+ executeTimeoutSeconds?: number | undefined;
10
16
  } & React.RefAttributes<CaptchaFoxInstance>>;
11
17
 
12
18
  declare const CAPTCHA_RESPONSE_KEY = "cf-captcha-response";
13
19
 
14
- export { CAPTCHA_RESPONSE_KEY, CaptchaFox, CaptchaFoxInstance };
20
+ export { CAPTCHA_RESPONSE_KEY, CaptchaFox, type CaptchaFoxInstance, RetryError, TimeoutError };
package/dist/index.d.ts CHANGED
@@ -1,14 +1,20 @@
1
1
  import { WidgetApi, WidgetOptions } from '@captchafox/types';
2
2
  import React from 'react';
3
3
 
4
+ declare class RetryError extends Error {
5
+ }
6
+ declare class TimeoutError extends Error {
7
+ }
8
+
4
9
  type CaptchaFoxInstance = Omit<WidgetApi, 'render'>;
5
10
  declare const CaptchaFox: React.ForwardRefExoticComponent<WidgetOptions & {
6
11
  /** Called after the widget has been loaded */
7
12
  onLoad?: (() => void) | undefined;
8
13
  className?: string | undefined;
9
14
  nonce?: string | undefined;
15
+ executeTimeoutSeconds?: number | undefined;
10
16
  } & React.RefAttributes<CaptchaFoxInstance>>;
11
17
 
12
18
  declare const CAPTCHA_RESPONSE_KEY = "cf-captcha-response";
13
19
 
14
- export { CAPTCHA_RESPONSE_KEY, CaptchaFox, CaptchaFoxInstance };
20
+ export { CAPTCHA_RESPONSE_KEY, CaptchaFox, type CaptchaFoxInstance, RetryError, TimeoutError };
package/dist/index.js CHANGED
@@ -21,37 +21,63 @@ var __async = (__this, __arguments, generator) => {
21
21
  };
22
22
 
23
23
  // ../internal/dist/index.mjs
24
- var resolveFn;
25
- var rejectFn;
26
- var mountInstance = new Promise((resolve, reject) => {
27
- resolveFn = resolve;
28
- rejectFn = reject;
24
+ var RetryError = class extends Error {
25
+ };
26
+ var TimeoutError = class extends Error {
27
+ };
28
+ var backoff = (retryCount) => Math.exp(retryCount) * 150;
29
+ var withRetry = (_0, ..._1) => __async(void 0, [_0, ..._1], function* (callback, { attempts = 4 } = {}) {
30
+ var _a;
31
+ let error;
32
+ for (let i = 0; i < attempts; i++) {
33
+ try {
34
+ return yield callback();
35
+ } catch (err) {
36
+ error = err;
37
+ yield new Promise((r) => setTimeout(r, backoff(i)));
38
+ }
39
+ }
40
+ throw new RetryError((_a = error == null ? void 0 : error.message) != null ? _a : "Exhausted all retries");
29
41
  });
42
+ var mountInstance;
30
43
  var LOAD_FUNC_KEY = "captchaFoxOnLoad";
31
44
  var SCRIPT_SRC = `https://cdn.captchafox.com/api.js?render=explicit&onload=${LOAD_FUNC_KEY}`;
32
- function loadCaptchaScript() {
45
+ function loadScript() {
33
46
  return __async(this, arguments, function* ({ nonce } = {}) {
34
- if (document.querySelector(`script[src="${SCRIPT_SRC}"]`))
47
+ if (mountInstance && document.querySelector(`script[src="${SCRIPT_SRC}"]`)) {
35
48
  return mountInstance;
36
- window[LOAD_FUNC_KEY] = resolveFn;
37
- const script = document.createElement("script");
38
- script.src = SCRIPT_SRC;
39
- script.async = true;
40
- script.defer = true;
41
- script.onerror = rejectFn;
42
- if (nonce) {
43
- script.nonce = nonce;
44
49
  }
45
- document.body.appendChild(script);
50
+ mountInstance = new Promise((resolve, reject) => {
51
+ window[LOAD_FUNC_KEY] = resolve;
52
+ const script = document.createElement("script");
53
+ script.src = SCRIPT_SRC;
54
+ script.async = true;
55
+ script.defer = true;
56
+ script.onerror = (e) => {
57
+ script.remove();
58
+ mountInstance = void 0;
59
+ reject(e);
60
+ };
61
+ if (nonce) {
62
+ script.nonce = nonce;
63
+ }
64
+ document.body.appendChild(script);
65
+ });
46
66
  return mountInstance;
47
67
  });
48
68
  }
69
+ function loadCaptchaScript() {
70
+ return __async(this, arguments, function* (props = {}) {
71
+ return withRetry(() => loadScript(props));
72
+ });
73
+ }
49
74
  var isApiReady = () => typeof (window == null ? void 0 : window.captchafox) !== "undefined";
50
75
 
51
76
  // src/CaptchaFox.tsx
52
77
  import React, { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
53
78
  var CaptchaFox = forwardRef(
54
79
  ({
80
+ executeTimeoutSeconds = 30,
55
81
  sitekey,
56
82
  lang,
57
83
  mode,
@@ -68,6 +94,8 @@ var CaptchaFox = forwardRef(
68
94
  const [containerRef, setContainerRef] = useState();
69
95
  const [widgetId, setWidgetId] = useState();
70
96
  const firstRendered = useRef(false);
97
+ const onReady = useRef();
98
+ const executeTimeout = useRef();
71
99
  useImperativeHandle(
72
100
  ref,
73
101
  () => {
@@ -96,7 +124,7 @@ var CaptchaFox = forwardRef(
96
124
  },
97
125
  execute: () => {
98
126
  if (!isApiReady() || !widgetId) {
99
- return Promise.reject("[CaptchaFox] Widget has not been loaded");
127
+ return waitAndExecute();
100
128
  }
101
129
  return window.captchafox.execute(widgetId);
102
130
  }
@@ -109,27 +137,11 @@ var CaptchaFox = forwardRef(
109
137
  onLoad == null ? void 0 : onLoad();
110
138
  }
111
139
  }, [widgetId]);
112
- const renderCaptcha = () => __async(void 0, null, function* () {
113
- var _a, _b, _c;
114
- (_a = window.captchafox) == null ? void 0 : _a.remove(widgetId);
115
- if (!containerRef || ((_b = containerRef == null ? void 0 : containerRef.children) == null ? void 0 : _b.length) === 1)
116
- return;
117
- const newWidgetId = yield (_c = window.captchafox) == null ? void 0 : _c.render(containerRef, {
118
- lang,
119
- sitekey,
120
- mode,
121
- theme,
122
- i18n,
123
- onError,
124
- onFail,
125
- onClose,
126
- onVerify
127
- });
128
- setWidgetId(newWidgetId);
129
- });
130
140
  useEffect(() => {
131
- if (!containerRef)
132
- return;
141
+ return () => clearTimeout(executeTimeout.current);
142
+ }, []);
143
+ useEffect(() => {
144
+ if (!containerRef) return;
133
145
  if (firstRendered.current) {
134
146
  if (isApiReady()) {
135
147
  renderCaptcha();
@@ -146,6 +158,41 @@ var CaptchaFox = forwardRef(
146
158
  });
147
159
  }
148
160
  }, [containerRef, sitekey, lang, mode]);
161
+ const waitAndExecute = () => {
162
+ return new Promise((resolve, reject) => {
163
+ executeTimeout.current = setTimeout(() => {
164
+ reject(new TimeoutError("Execute timed out"));
165
+ }, executeTimeoutSeconds * 1e3);
166
+ onReady.current = (id) => {
167
+ clearTimeout(executeTimeout.current);
168
+ window.captchafox.execute(id).then(resolve).catch(reject);
169
+ };
170
+ });
171
+ };
172
+ const renderCaptcha = () => __async(void 0, null, function* () {
173
+ var _a, _b, _c;
174
+ (_a = window.captchafox) == null ? void 0 : _a.remove(widgetId);
175
+ if (!containerRef || ((_b = containerRef == null ? void 0 : containerRef.children) == null ? void 0 : _b.length) === 1) return;
176
+ const newWidgetId = yield (_c = window.captchafox) == null ? void 0 : _c.render(containerRef, {
177
+ lang,
178
+ sitekey,
179
+ mode,
180
+ theme,
181
+ i18n,
182
+ onError,
183
+ onFail,
184
+ onClose,
185
+ onVerify
186
+ });
187
+ if (!newWidgetId) {
188
+ return;
189
+ }
190
+ setWidgetId(newWidgetId);
191
+ if (onReady.current) {
192
+ onReady.current(newWidgetId);
193
+ onReady.current = void 0;
194
+ }
195
+ });
149
196
  return /* @__PURE__ */ React.createElement("div", { ref: setContainerRef, id: widgetId, className });
150
197
  }
151
198
  );
@@ -155,5 +202,7 @@ CaptchaFox.displayName = "CaptchaFox";
155
202
  var CAPTCHA_RESPONSE_KEY = "cf-captcha-response";
156
203
  export {
157
204
  CAPTCHA_RESPONSE_KEY,
158
- CaptchaFox
205
+ CaptchaFox,
206
+ RetryError,
207
+ TimeoutError
159
208
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@captchafox/react",
3
- "version": "1.5.1",
3
+ "version": "1.7.0",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -50,7 +50,7 @@
50
50
  "jest": "^29.7.0",
51
51
  "jest-environment-jsdom": "^29.7.0",
52
52
  "react": "^18.2.0",
53
- "tsup": "^7.2.0",
53
+ "tsup": "^8.3.5",
54
54
  "ts-jest": "^29.1.1",
55
55
  "typescript": "^5.0.2"
56
56
  },