@hcaptcha/react-hcaptcha 1.8.1 → 1.9.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/README.md CHANGED
@@ -138,6 +138,7 @@ return <HCaptcha ref={captchaRef} onLoad={onLoad} sitekey={sitekey} {...props} /
138
138
  |`imghost`|String|No|`-`|See enterprise docs.|
139
139
  |`reportapi`|String|No|`-`|See enterprise docs.|
140
140
  |`sentry`|String|No|`-`|See enterprise docs.|
141
+ | `cleanup` | Boolean | No | `true` | Remove script tag after setup.|
141
142
  |`custom`|Boolean|No|`-`|See enterprise docs.|
142
143
  |`loadAsync`|Boolean|No|`true`|Set if the script should be loaded asynchronously.|
143
144
  |`scriptLocation`|Element|No|`document.head`| Location of where to append the script tag. Make sure to add it to an area that will persist to prevent loading multiple times in the same document view. Note: If `null` is provided, the `document.head` will be used.|
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.breadcrumbMessages = exports.scopeTag = void 0;
7
+ var scopeTag = {
8
+ key: 'source',
9
+ value: '@hCaptcha/react'
10
+ };
11
+ exports.scopeTag = scopeTag;
12
+ var breadcrumbMessages = {
13
+ mounted: 'hCaptcha component mounted',
14
+ expired: 'hCaptcha expired',
15
+ unmounted: 'hCaptcha component unmounted',
16
+ reset: 'hCaptcha reset',
17
+ removed: 'hCaptcha removed'
18
+ };
19
+ exports.breadcrumbMessages = breadcrumbMessages;
@@ -0,0 +1,11 @@
1
+ export var scopeTag = {
2
+ key: 'source',
3
+ value: '@hCaptcha/react'
4
+ };
5
+ export var breadcrumbMessages = {
6
+ mounted: 'hCaptcha component mounted',
7
+ expired: 'hCaptcha expired',
8
+ unmounted: 'hCaptcha component unmounted',
9
+ reset: 'hCaptcha reset',
10
+ removed: 'hCaptcha removed'
11
+ };
package/dist/esm/index.js CHANGED
@@ -1,77 +1,30 @@
1
1
  import _assertThisInitialized from "@babel/runtime/helpers/assertThisInitialized";
2
2
  import _inheritsLoose from "@babel/runtime/helpers/inheritsLoose";
3
3
  import * as React from 'react';
4
- import { generateQuery, getFrame, getMountElement } from './utils.js';
5
- var SCRIPT_ID = 'hcaptcha-api-script-id';
6
- var HCAPTCHA_LOAD_FN_NAME = 'hcaptchaOnLoad'; // Prevent loading API script multiple times
7
-
8
- var scripts = []; // Generate hCaptcha API script
9
-
10
- var mountCaptchaScript = function mountCaptchaScript(params) {
11
- if (params === void 0) {
12
- params = {};
13
- }
14
-
15
- var element = getMountElement(params.scriptLocation);
16
- delete params.scriptLocation;
17
- var frame = getFrame(element);
18
- var script = scripts.find(function (_ref) {
19
- var scope = _ref.scope;
20
- return scope === frame.window;
21
- });
22
-
23
- if (frame.document.getElementById(SCRIPT_ID) && script) {
24
- // API was already requested
25
- return script.promise;
26
- }
27
-
28
- var promise = new Promise(function (resolve, reject) {
29
- // Create global onload callback
30
- frame.window[HCAPTCHA_LOAD_FN_NAME] = resolve;
31
- var domain = params.apihost || "https://js.hcaptcha.com";
32
- delete params.apihost;
33
- var script = frame.document.createElement("script");
34
- script.id = SCRIPT_ID;
35
- script.src = domain + "/1/api.js?render=explicit&onload=" + HCAPTCHA_LOAD_FN_NAME;
36
- script.async = params.loadAsync !== undefined ? params.loadAsync : true;
37
- delete params.loadAsync;
38
-
39
- script.onerror = function (event) {
40
- return reject('script-error');
41
- };
42
-
43
- var query = generateQuery(params);
44
- script.src += query !== "" ? "&" + query : "";
45
- element.appendChild(script);
46
- });
47
- scripts.push({
48
- promise: promise,
49
- scope: frame.window
50
- });
51
- return promise;
52
- };
53
-
4
+ import { hCaptchaLoader, initSentry } from '@hcaptcha/loader';
5
+ import { getFrame, getMountElement } from './utils.js';
6
+ import { breadcrumbMessages, scopeTag } from "./constants";
54
7
  var HCaptcha = /*#__PURE__*/function (_React$Component) {
55
8
  _inheritsLoose(HCaptcha, _React$Component);
56
-
57
9
  function HCaptcha(props) {
58
10
  var _this;
59
-
60
11
  _this = _React$Component.call(this, props) || this;
12
+
61
13
  /**
62
14
  * Internal reference to track hCaptcha API
63
15
  *
64
16
  * Required as window is relative to initialization in application
65
17
  * not where the script and iFrames have been loaded.
66
18
  */
19
+ _this._hcaptcha = undefined;
67
20
 
68
- _this._hcaptcha = undefined; // API Methods
69
-
21
+ // API Methods
70
22
  _this.renderCaptcha = _this.renderCaptcha.bind(_assertThisInitialized(_this));
71
23
  _this.resetCaptcha = _this.resetCaptcha.bind(_assertThisInitialized(_this));
72
24
  _this.removeCaptcha = _this.removeCaptcha.bind(_assertThisInitialized(_this));
73
- _this.isReady = _this.isReady.bind(_assertThisInitialized(_this)); // Event Handlers
25
+ _this.isReady = _this.isReady.bind(_assertThisInitialized(_this));
74
26
 
27
+ // Event Handlers
75
28
  _this.loadCaptcha = _this.loadCaptcha.bind(_assertThisInitialized(_this));
76
29
  _this.handleOnLoad = _this.handleOnLoad.bind(_assertThisInitialized(_this));
77
30
  _this.handleSubmit = _this.handleSubmit.bind(_assertThisInitialized(_this));
@@ -82,6 +35,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
82
35
  _this.handleChallengeExpired = _this.handleChallengeExpired.bind(_assertThisInitialized(_this));
83
36
  _this.ref = /*#__PURE__*/React.createRef();
84
37
  _this.apiScriptRequested = false;
38
+ _this.sentryHub = null;
85
39
  _this.state = {
86
40
  isApiReady: false,
87
41
  isRemoved: false,
@@ -90,23 +44,25 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
90
44
  };
91
45
  return _this;
92
46
  }
93
-
94
47
  var _proto = HCaptcha.prototype;
95
-
96
48
  _proto.componentDidMount = function componentDidMount() {
97
49
  var _this2 = this;
98
-
99
50
  // Once captcha is mounted intialize hCaptcha - hCaptcha
100
51
  var element = getMountElement(this.props.scriptLocation);
101
52
  var frame = getFrame(element);
102
53
  this._hcaptcha = frame.window.hcaptcha || undefined;
103
54
  var isApiReady = typeof this._hcaptcha !== 'undefined';
55
+ this.sentryHub = initSentry(this.props.sentry, scopeTag);
56
+ this.sentryHub.addBreadcrumb({
57
+ category: scopeTag.value,
58
+ message: breadcrumbMessages.mounted
59
+ });
60
+
104
61
  /*
105
62
  * Check if hCaptcha has already been loaded,
106
63
  * If Yes, render the captcha
107
64
  * If No, create script tag and wait to render the captcha
108
65
  */
109
-
110
66
  if (isApiReady) {
111
67
  this.setState({
112
68
  isApiReady: true
@@ -115,85 +71,84 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
115
71
  });
116
72
  return;
117
73
  }
118
-
119
74
  this.loadCaptcha();
120
75
  };
121
-
122
76
  _proto.componentWillUnmount = function componentWillUnmount() {
123
77
  var captchaId = this.state.captchaId;
124
78
  var hcaptcha = this._hcaptcha;
125
-
126
79
  if (!this.isReady()) {
127
80
  return;
128
- } // Reset any stored variables / timers when unmounting
129
-
81
+ }
130
82
 
83
+ // Reset any stored variables / timers when unmounting
131
84
  hcaptcha.reset(captchaId);
132
85
  hcaptcha.remove(captchaId);
86
+ this.sentryHub.addBreadcrumb({
87
+ category: scopeTag.value,
88
+ message: breadcrumbMessages.unmounted
89
+ });
133
90
  };
134
-
135
91
  _proto.shouldComponentUpdate = function shouldComponentUpdate(nextProps, nextState) {
136
92
  // Prevent component re-rendering when these internal state variables are updated
137
93
  if (this.state.isApiReady !== nextState.isApiReady || this.state.isRemoved !== nextState.isRemoved) {
138
94
  return false;
139
95
  }
140
-
141
96
  return true;
142
97
  };
143
-
144
98
  _proto.componentDidUpdate = function componentDidUpdate(prevProps) {
145
99
  var _this3 = this;
146
-
147
100
  // Prop Keys that could change
148
- var keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint']; // See if any props changed during component update
149
-
101
+ var keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint'];
102
+ // See if any props changed during component update
150
103
  var match = keys.every(function (key) {
151
104
  return prevProps[key] === _this3.props[key];
152
- }); // If they have changed, remove current captcha and render a new one
105
+ });
153
106
 
107
+ // If they have changed, remove current captcha and render a new one
154
108
  if (!match) {
155
109
  this.removeCaptcha(function () {
156
110
  _this3.renderCaptcha();
157
111
  });
158
112
  }
159
113
  };
160
-
161
114
  _proto.loadCaptcha = function loadCaptcha() {
162
115
  if (this.apiScriptRequested) {
163
116
  return;
164
117
  }
165
-
166
118
  var _this$props = this.props,
167
- apihost = _this$props.apihost,
168
- assethost = _this$props.assethost,
169
- endpoint = _this$props.endpoint,
170
- host = _this$props.host,
171
- imghost = _this$props.imghost,
172
- hl = _this$props.languageOverride,
173
- reCaptchaCompat = _this$props.reCaptchaCompat,
174
- reportapi = _this$props.reportapi,
175
- sentry = _this$props.sentry,
176
- custom = _this$props.custom,
177
- loadAsync = _this$props.loadAsync,
178
- scriptLocation = _this$props.scriptLocation;
119
+ apihost = _this$props.apihost,
120
+ assethost = _this$props.assethost,
121
+ endpoint = _this$props.endpoint,
122
+ host = _this$props.host,
123
+ imghost = _this$props.imghost,
124
+ hl = _this$props.languageOverride,
125
+ reCaptchaCompat = _this$props.reCaptchaCompat,
126
+ reportapi = _this$props.reportapi,
127
+ sentry = _this$props.sentry,
128
+ custom = _this$props.custom,
129
+ loadAsync = _this$props.loadAsync,
130
+ scriptLocation = _this$props.scriptLocation,
131
+ _this$props$cleanup = _this$props.cleanup,
132
+ cleanup = _this$props$cleanup === void 0 ? true : _this$props$cleanup;
179
133
  var mountParams = {
134
+ render: 'explicit',
180
135
  apihost: apihost,
181
136
  assethost: assethost,
182
137
  endpoint: endpoint,
183
138
  hl: hl,
184
139
  host: host,
185
140
  imghost: imghost,
186
- recaptchacompat: reCaptchaCompat === false ? "off" : null,
141
+ recaptchacompat: reCaptchaCompat === false ? 'off' : null,
187
142
  reportapi: reportapi,
188
143
  sentry: sentry,
189
144
  custom: custom,
190
145
  loadAsync: loadAsync,
191
- scriptLocation: scriptLocation
146
+ scriptLocation: scriptLocation,
147
+ cleanup: cleanup
192
148
  };
193
- mountCaptchaScript(mountParams).then(this.handleOnLoad)["catch"](this.handleError);
149
+ hCaptchaLoader(mountParams).then(this.handleOnLoad, this.handleError)["catch"](this.handleError);
194
150
  this.apiScriptRequested = true;
195
151
  };
196
-
197
152
  _proto.renderCaptcha = function renderCaptcha(onReady) {
198
153
  var isApiReady = this.state.isApiReady;
199
154
  if (!isApiReady) return;
@@ -208,8 +163,8 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
208
163
  hl: this.props.hl || this.props.languageOverride,
209
164
  languageOverride: undefined
210
165
  });
211
- var hcaptcha = this._hcaptcha; //Render hCaptcha widget and provide necessary callbacks - hCaptcha
212
-
166
+ var hcaptcha = this._hcaptcha;
167
+ //Render hCaptcha widget and provide necessary callbacks - hCaptcha
213
168
  var captchaId = hcaptcha.render(this.ref.current, renderParams);
214
169
  this.setState({
215
170
  isRemoved: false,
@@ -218,169 +173,154 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
218
173
  onReady && onReady();
219
174
  });
220
175
  };
221
-
222
176
  _proto.resetCaptcha = function resetCaptcha() {
223
177
  var captchaId = this.state.captchaId;
224
178
  var hcaptcha = this._hcaptcha;
225
-
226
179
  if (!this.isReady()) {
227
180
  return;
228
- } // Reset captcha state, removes stored token and unticks checkbox
229
-
230
-
181
+ }
182
+ // Reset captcha state, removes stored token and unticks checkbox
231
183
  hcaptcha.reset(captchaId);
184
+ this.sentryHub.addBreadcrumb({
185
+ category: scopeTag.value,
186
+ message: breadcrumbMessages.reset
187
+ });
232
188
  };
233
-
234
189
  _proto.removeCaptcha = function removeCaptcha(callback) {
235
190
  var captchaId = this.state.captchaId;
236
191
  var hcaptcha = this._hcaptcha;
237
-
238
192
  if (!this.isReady()) {
239
193
  return;
240
194
  }
241
-
242
195
  this.setState({
243
196
  isRemoved: true
244
197
  }, function () {
245
198
  hcaptcha.remove(captchaId);
246
199
  callback && callback();
247
200
  });
201
+ this.sentryHub.addBreadcrumb({
202
+ category: scopeTag.value,
203
+ message: breadcrumbMessages.removed
204
+ });
248
205
  };
249
-
250
206
  _proto.handleOnLoad = function handleOnLoad() {
251
207
  var _this4 = this;
252
-
253
208
  this.setState({
254
209
  isApiReady: true
255
210
  }, function () {
256
- var element = getMountElement(_this4.props.scriptLocation);
257
- var frame = getFrame(element);
258
- _this4._hcaptcha = frame.window.hcaptcha; // render captcha and wait for captcha id
259
-
260
- _this4.renderCaptcha(function () {
261
- // trigger onLoad if it exists
262
- var onLoad = _this4.props.onLoad;
263
- if (onLoad) onLoad();
264
- });
211
+ try {
212
+ var element = getMountElement(_this4.props.scriptLocation);
213
+ var frame = getFrame(element);
214
+ _this4._hcaptcha = frame.window.hcaptcha;
215
+
216
+ // render captcha and wait for captcha id
217
+ _this4.renderCaptcha(function () {
218
+ // trigger onLoad if it exists
219
+ var onLoad = _this4.props.onLoad;
220
+ if (onLoad) onLoad();
221
+ });
222
+ } catch (error) {
223
+ _this4.sentryHub.captureException(error);
224
+ }
265
225
  });
266
226
  };
267
-
268
227
  _proto.handleSubmit = function handleSubmit(event) {
269
228
  var onVerify = this.props.onVerify;
270
229
  var _this$state = this.state,
271
- isRemoved = _this$state.isRemoved,
272
- captchaId = _this$state.captchaId;
230
+ isRemoved = _this$state.isRemoved,
231
+ captchaId = _this$state.captchaId;
273
232
  var hcaptcha = this._hcaptcha;
274
233
  if (typeof hcaptcha === 'undefined' || isRemoved) return;
275
234
  var token = hcaptcha.getResponse(captchaId); //Get response token from hCaptcha widget
276
-
277
235
  var ekey = hcaptcha.getRespKey(captchaId); //Get current challenge session id from hCaptcha widget
278
-
279
236
  if (onVerify) onVerify(token, ekey); //Dispatch event to verify user response
280
237
  };
281
-
282
238
  _proto.handleExpire = function handleExpire() {
283
239
  var onExpire = this.props.onExpire;
284
240
  var captchaId = this.state.captchaId;
285
241
  var hcaptcha = this._hcaptcha;
286
-
287
242
  if (!this.isReady()) {
288
243
  return;
289
244
  }
290
-
291
245
  hcaptcha.reset(captchaId); // If hCaptcha runs into error, reset captcha - hCaptcha
292
246
 
293
247
  if (onExpire) onExpire();
248
+ this.sentryHub.addBreadcrumb({
249
+ category: scopeTag.value,
250
+ message: breadcrumbMessages.expired
251
+ });
294
252
  };
295
-
296
253
  _proto.handleError = function handleError(event) {
297
254
  var onError = this.props.onError;
298
255
  var captchaId = this.state.captchaId;
299
256
  var hcaptcha = this._hcaptcha;
300
-
301
257
  if (this.isReady()) {
302
258
  // If hCaptcha runs into error, reset captcha - hCaptcha
303
259
  hcaptcha.reset(captchaId);
304
260
  }
305
-
306
261
  if (onError) onError(event);
307
262
  };
308
-
309
263
  _proto.isReady = function isReady() {
310
264
  var _this$state2 = this.state,
311
- isApiReady = _this$state2.isApiReady,
312
- isRemoved = _this$state2.isRemoved;
265
+ isApiReady = _this$state2.isApiReady,
266
+ isRemoved = _this$state2.isRemoved;
313
267
  return isApiReady && !isRemoved;
314
268
  };
315
-
316
269
  _proto.handleOpen = function handleOpen() {
317
270
  if (!this.isReady() || !this.props.onOpen) {
318
271
  return;
319
272
  }
320
-
321
273
  this.props.onOpen();
322
274
  };
323
-
324
275
  _proto.handleClose = function handleClose() {
325
276
  if (!this.isReady() || !this.props.onClose) {
326
277
  return;
327
278
  }
328
-
329
279
  this.props.onClose();
330
280
  };
331
-
332
281
  _proto.handleChallengeExpired = function handleChallengeExpired() {
333
282
  if (!this.isReady() || !this.props.onChalExpired) {
334
283
  return;
335
284
  }
336
-
337
285
  this.props.onChalExpired();
338
286
  };
339
-
340
287
  _proto.execute = function execute(opts) {
341
288
  if (opts === void 0) {
342
289
  opts = null;
343
290
  }
344
-
345
- var captchaId = this.state.captchaId;
346
- var hcaptcha = this._hcaptcha;
347
-
348
- if (!this.isReady()) {
349
- return;
350
- }
351
-
352
- if (opts && typeof opts !== "object") {
353
- opts = null;
291
+ try {
292
+ var captchaId = this.state.captchaId;
293
+ var hcaptcha = this._hcaptcha;
294
+ if (!this.isReady()) {
295
+ return;
296
+ }
297
+ if (opts && typeof opts !== "object") {
298
+ opts = null;
299
+ }
300
+ return hcaptcha.execute(captchaId, opts);
301
+ } catch (error) {
302
+ this.sentryHub.captureException(error);
354
303
  }
355
-
356
- return hcaptcha.execute(captchaId, opts);
357
304
  };
358
-
359
305
  _proto.setData = function setData(data) {
360
306
  var captchaId = this.state.captchaId;
361
307
  var hcaptcha = this._hcaptcha;
362
-
363
308
  if (!this.isReady()) {
364
309
  return;
365
310
  }
366
-
367
311
  if (data && typeof data !== "object") {
368
312
  data = null;
369
313
  }
370
-
371
314
  hcaptcha.setData(captchaId, data);
372
315
  };
373
-
374
316
  _proto.getResponse = function getResponse() {
375
317
  var hcaptcha = this._hcaptcha;
376
318
  return hcaptcha.getResponse(this.state.captchaId);
377
319
  };
378
-
379
320
  _proto.getRespKey = function getRespKey() {
380
321
  var hcaptcha = this._hcaptcha;
381
322
  return hcaptcha.getRespKey(this.state.captchaId);
382
323
  };
383
-
384
324
  _proto.render = function render() {
385
325
  var elementId = this.state.elementId;
386
326
  return /*#__PURE__*/React.createElement("div", {
@@ -388,8 +328,6 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
388
328
  id: elementId
389
329
  });
390
330
  };
391
-
392
331
  return HCaptcha;
393
332
  }(React.Component);
394
-
395
333
  export default HCaptcha;
package/dist/esm/utils.js CHANGED
@@ -1,17 +1,3 @@
1
- function generateQuery(params) {
2
- return Object.entries(params).filter(function (_ref) {
3
- var key = _ref[0],
4
- value = _ref[1];
5
- return value || value === false;
6
- }).map(function (_ref2) {
7
- var key = _ref2[0],
8
- value = _ref2[1];
9
- return encodeURIComponent(key) + "=" + encodeURIComponent(value);
10
- }).join("&");
11
- }
12
-
13
- ;
14
-
15
1
  function getFrame(element) {
16
2
  var doc = element && element.ownerDocument || document;
17
3
  var win = doc.defaultView || doc.parentWindow || window;
@@ -20,9 +6,7 @@ function getFrame(element) {
20
6
  window: win
21
7
  };
22
8
  }
23
-
24
9
  function getMountElement(element) {
25
10
  return element || document.head;
26
11
  }
27
-
28
- export { generateQuery, getFrame, getMountElement };
12
+ export { getFrame, getMountElement };
package/dist/index.js CHANGED
@@ -1,106 +1,47 @@
1
1
  "use strict";
2
2
 
3
3
  var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
4
-
5
4
  var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
6
-
7
5
  Object.defineProperty(exports, "__esModule", {
8
6
  value: true
9
7
  });
10
8
  exports["default"] = void 0;
11
-
12
9
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
13
-
14
10
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
15
-
16
11
  var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
17
-
18
12
  var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
19
-
20
13
  var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
21
-
22
14
  var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
23
-
24
15
  var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
25
-
26
16
  var React = _interopRequireWildcard(require("react"));
27
-
17
+ var _loader = require("@hcaptcha/loader");
28
18
  var _utils = require("./utils.js");
29
-
19
+ var _constants = require("./constants");
30
20
  function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = (0, _getPrototypeOf2["default"])(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = (0, _getPrototypeOf2["default"])(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return (0, _possibleConstructorReturn2["default"])(this, result); }; }
31
-
32
21
  function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
33
-
34
- var SCRIPT_ID = 'hcaptcha-api-script-id';
35
- var HCAPTCHA_LOAD_FN_NAME = 'hcaptchaOnLoad'; // Prevent loading API script multiple times
36
-
37
- var scripts = []; // Generate hCaptcha API script
38
-
39
- var mountCaptchaScript = function mountCaptchaScript() {
40
- var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
41
- var element = (0, _utils.getMountElement)(params.scriptLocation);
42
- delete params.scriptLocation;
43
- var frame = (0, _utils.getFrame)(element);
44
- var script = scripts.find(function (_ref) {
45
- var scope = _ref.scope;
46
- return scope === frame.window;
47
- });
48
-
49
- if (frame.document.getElementById(SCRIPT_ID) && script) {
50
- // API was already requested
51
- return script.promise;
52
- }
53
-
54
- var promise = new Promise(function (resolve, reject) {
55
- // Create global onload callback
56
- frame.window[HCAPTCHA_LOAD_FN_NAME] = resolve;
57
- var domain = params.apihost || "https://js.hcaptcha.com";
58
- delete params.apihost;
59
- var script = frame.document.createElement("script");
60
- script.id = SCRIPT_ID;
61
- script.src = "".concat(domain, "/1/api.js?render=explicit&onload=").concat(HCAPTCHA_LOAD_FN_NAME);
62
- script.async = params.loadAsync !== undefined ? params.loadAsync : true;
63
- delete params.loadAsync;
64
-
65
- script.onerror = function (event) {
66
- return reject('script-error');
67
- };
68
-
69
- var query = (0, _utils.generateQuery)(params);
70
- script.src += query !== "" ? "&".concat(query) : "";
71
- element.appendChild(script);
72
- });
73
- scripts.push({
74
- promise: promise,
75
- scope: frame.window
76
- });
77
- return promise;
78
- };
79
-
80
22
  var HCaptcha = /*#__PURE__*/function (_React$Component) {
81
23
  (0, _inherits2["default"])(HCaptcha, _React$Component);
82
-
83
24
  var _super = _createSuper(HCaptcha);
84
-
85
25
  function HCaptcha(props) {
86
26
  var _this;
87
-
88
27
  (0, _classCallCheck2["default"])(this, HCaptcha);
89
28
  _this = _super.call(this, props);
29
+
90
30
  /**
91
31
  * Internal reference to track hCaptcha API
92
32
  *
93
33
  * Required as window is relative to initialization in application
94
34
  * not where the script and iFrames have been loaded.
95
35
  */
36
+ _this._hcaptcha = undefined;
96
37
 
97
- _this._hcaptcha = undefined; // API Methods
98
-
38
+ // API Methods
99
39
  _this.renderCaptcha = _this.renderCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
100
40
  _this.resetCaptcha = _this.resetCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
101
41
  _this.removeCaptcha = _this.removeCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
102
- _this.isReady = _this.isReady.bind((0, _assertThisInitialized2["default"])(_this)); // Event Handlers
42
+ _this.isReady = _this.isReady.bind((0, _assertThisInitialized2["default"])(_this));
103
43
 
44
+ // Event Handlers
104
45
  _this.loadCaptcha = _this.loadCaptcha.bind((0, _assertThisInitialized2["default"])(_this));
105
46
  _this.handleOnLoad = _this.handleOnLoad.bind((0, _assertThisInitialized2["default"])(_this));
106
47
  _this.handleSubmit = _this.handleSubmit.bind((0, _assertThisInitialized2["default"])(_this));
@@ -111,6 +52,7 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
111
52
  _this.handleChallengeExpired = _this.handleChallengeExpired.bind((0, _assertThisInitialized2["default"])(_this));
112
53
  _this.ref = /*#__PURE__*/React.createRef();
113
54
  _this.apiScriptRequested = false;
55
+ _this.sentryHub = null;
114
56
  _this.state = {
115
57
  isApiReady: false,
116
58
  isRemoved: false,
@@ -119,23 +61,26 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
119
61
  };
120
62
  return _this;
121
63
  }
122
-
123
64
  (0, _createClass2["default"])(HCaptcha, [{
124
65
  key: "componentDidMount",
125
66
  value: function componentDidMount() {
126
67
  var _this2 = this;
127
-
128
68
  // Once captcha is mounted intialize hCaptcha - hCaptcha
129
69
  var element = (0, _utils.getMountElement)(this.props.scriptLocation);
130
70
  var frame = (0, _utils.getFrame)(element);
131
71
  this._hcaptcha = frame.window.hcaptcha || undefined;
132
72
  var isApiReady = typeof this._hcaptcha !== 'undefined';
73
+ this.sentryHub = (0, _loader.initSentry)(this.props.sentry, _constants.scopeTag);
74
+ this.sentryHub.addBreadcrumb({
75
+ category: _constants.scopeTag.value,
76
+ message: _constants.breadcrumbMessages.mounted
77
+ });
78
+
133
79
  /*
134
80
  * Check if hCaptcha has already been loaded,
135
81
  * If Yes, render the captcha
136
82
  * If No, create script tag and wait to render the captcha
137
83
  */
138
-
139
84
  if (isApiReady) {
140
85
  this.setState({
141
86
  isApiReady: true
@@ -144,7 +89,6 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
144
89
  });
145
90
  return;
146
91
  }
147
-
148
92
  this.loadCaptcha();
149
93
  }
150
94
  }, {
@@ -152,14 +96,17 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
152
96
  value: function componentWillUnmount() {
153
97
  var captchaId = this.state.captchaId;
154
98
  var hcaptcha = this._hcaptcha;
155
-
156
99
  if (!this.isReady()) {
157
100
  return;
158
- } // Reset any stored variables / timers when unmounting
159
-
101
+ }
160
102
 
103
+ // Reset any stored variables / timers when unmounting
161
104
  hcaptcha.reset(captchaId);
162
105
  hcaptcha.remove(captchaId);
106
+ this.sentryHub.addBreadcrumb({
107
+ category: _constants.scopeTag.value,
108
+ message: _constants.breadcrumbMessages.unmounted
109
+ });
163
110
  }
164
111
  }, {
165
112
  key: "shouldComponentUpdate",
@@ -168,21 +115,20 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
168
115
  if (this.state.isApiReady !== nextState.isApiReady || this.state.isRemoved !== nextState.isRemoved) {
169
116
  return false;
170
117
  }
171
-
172
118
  return true;
173
119
  }
174
120
  }, {
175
121
  key: "componentDidUpdate",
176
122
  value: function componentDidUpdate(prevProps) {
177
123
  var _this3 = this;
178
-
179
124
  // Prop Keys that could change
180
- var keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint']; // See if any props changed during component update
181
-
125
+ var keys = ['sitekey', 'size', 'theme', 'tabindex', 'languageOverride', 'endpoint'];
126
+ // See if any props changed during component update
182
127
  var match = keys.every(function (key) {
183
128
  return prevProps[key] === _this3.props[key];
184
- }); // If they have changed, remove current captcha and render a new one
129
+ });
185
130
 
131
+ // If they have changed, remove current captcha and render a new one
186
132
  if (!match) {
187
133
  this.removeCaptcha(function () {
188
134
  _this3.renderCaptcha();
@@ -195,35 +141,38 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
195
141
  if (this.apiScriptRequested) {
196
142
  return;
197
143
  }
198
-
199
144
  var _this$props = this.props,
200
- apihost = _this$props.apihost,
201
- assethost = _this$props.assethost,
202
- endpoint = _this$props.endpoint,
203
- host = _this$props.host,
204
- imghost = _this$props.imghost,
205
- hl = _this$props.languageOverride,
206
- reCaptchaCompat = _this$props.reCaptchaCompat,
207
- reportapi = _this$props.reportapi,
208
- sentry = _this$props.sentry,
209
- custom = _this$props.custom,
210
- loadAsync = _this$props.loadAsync,
211
- scriptLocation = _this$props.scriptLocation;
145
+ apihost = _this$props.apihost,
146
+ assethost = _this$props.assethost,
147
+ endpoint = _this$props.endpoint,
148
+ host = _this$props.host,
149
+ imghost = _this$props.imghost,
150
+ hl = _this$props.languageOverride,
151
+ reCaptchaCompat = _this$props.reCaptchaCompat,
152
+ reportapi = _this$props.reportapi,
153
+ sentry = _this$props.sentry,
154
+ custom = _this$props.custom,
155
+ loadAsync = _this$props.loadAsync,
156
+ scriptLocation = _this$props.scriptLocation,
157
+ _this$props$cleanup = _this$props.cleanup,
158
+ cleanup = _this$props$cleanup === void 0 ? true : _this$props$cleanup;
212
159
  var mountParams = {
160
+ render: 'explicit',
213
161
  apihost: apihost,
214
162
  assethost: assethost,
215
163
  endpoint: endpoint,
216
164
  hl: hl,
217
165
  host: host,
218
166
  imghost: imghost,
219
- recaptchacompat: reCaptchaCompat === false ? "off" : null,
167
+ recaptchacompat: reCaptchaCompat === false ? 'off' : null,
220
168
  reportapi: reportapi,
221
169
  sentry: sentry,
222
170
  custom: custom,
223
171
  loadAsync: loadAsync,
224
- scriptLocation: scriptLocation
172
+ scriptLocation: scriptLocation,
173
+ cleanup: cleanup
225
174
  };
226
- mountCaptchaScript(mountParams).then(this.handleOnLoad)["catch"](this.handleError);
175
+ (0, _loader.hCaptchaLoader)(mountParams).then(this.handleOnLoad, this.handleError)["catch"](this.handleError);
227
176
  this.apiScriptRequested = true;
228
177
  }
229
178
  }, {
@@ -242,8 +191,8 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
242
191
  hl: this.props.hl || this.props.languageOverride,
243
192
  languageOverride: undefined
244
193
  });
245
- var hcaptcha = this._hcaptcha; //Render hCaptcha widget and provide necessary callbacks - hCaptcha
246
-
194
+ var hcaptcha = this._hcaptcha;
195
+ //Render hCaptcha widget and provide necessary callbacks - hCaptcha
247
196
  var captchaId = hcaptcha.render(this.ref.current, renderParams);
248
197
  this.setState({
249
198
  isRemoved: false,
@@ -257,48 +206,56 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
257
206
  value: function resetCaptcha() {
258
207
  var captchaId = this.state.captchaId;
259
208
  var hcaptcha = this._hcaptcha;
260
-
261
209
  if (!this.isReady()) {
262
210
  return;
263
- } // Reset captcha state, removes stored token and unticks checkbox
264
-
265
-
211
+ }
212
+ // Reset captcha state, removes stored token and unticks checkbox
266
213
  hcaptcha.reset(captchaId);
214
+ this.sentryHub.addBreadcrumb({
215
+ category: _constants.scopeTag.value,
216
+ message: _constants.breadcrumbMessages.reset
217
+ });
267
218
  }
268
219
  }, {
269
220
  key: "removeCaptcha",
270
221
  value: function removeCaptcha(callback) {
271
222
  var captchaId = this.state.captchaId;
272
223
  var hcaptcha = this._hcaptcha;
273
-
274
224
  if (!this.isReady()) {
275
225
  return;
276
226
  }
277
-
278
227
  this.setState({
279
228
  isRemoved: true
280
229
  }, function () {
281
230
  hcaptcha.remove(captchaId);
282
231
  callback && callback();
283
232
  });
233
+ this.sentryHub.addBreadcrumb({
234
+ category: _constants.scopeTag.value,
235
+ message: _constants.breadcrumbMessages.removed
236
+ });
284
237
  }
285
238
  }, {
286
239
  key: "handleOnLoad",
287
240
  value: function handleOnLoad() {
288
241
  var _this4 = this;
289
-
290
242
  this.setState({
291
243
  isApiReady: true
292
244
  }, function () {
293
- var element = (0, _utils.getMountElement)(_this4.props.scriptLocation);
294
- var frame = (0, _utils.getFrame)(element);
295
- _this4._hcaptcha = frame.window.hcaptcha; // render captcha and wait for captcha id
296
-
297
- _this4.renderCaptcha(function () {
298
- // trigger onLoad if it exists
299
- var onLoad = _this4.props.onLoad;
300
- if (onLoad) onLoad();
301
- });
245
+ try {
246
+ var element = (0, _utils.getMountElement)(_this4.props.scriptLocation);
247
+ var frame = (0, _utils.getFrame)(element);
248
+ _this4._hcaptcha = frame.window.hcaptcha;
249
+
250
+ // render captcha and wait for captcha id
251
+ _this4.renderCaptcha(function () {
252
+ // trigger onLoad if it exists
253
+ var onLoad = _this4.props.onLoad;
254
+ if (onLoad) onLoad();
255
+ });
256
+ } catch (error) {
257
+ _this4.sentryHub.captureException(error);
258
+ }
302
259
  });
303
260
  }
304
261
  }, {
@@ -306,14 +263,12 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
306
263
  value: function handleSubmit(event) {
307
264
  var onVerify = this.props.onVerify;
308
265
  var _this$state = this.state,
309
- isRemoved = _this$state.isRemoved,
310
- captchaId = _this$state.captchaId;
266
+ isRemoved = _this$state.isRemoved,
267
+ captchaId = _this$state.captchaId;
311
268
  var hcaptcha = this._hcaptcha;
312
269
  if (typeof hcaptcha === 'undefined' || isRemoved) return;
313
270
  var token = hcaptcha.getResponse(captchaId); //Get response token from hCaptcha widget
314
-
315
271
  var ekey = hcaptcha.getRespKey(captchaId); //Get current challenge session id from hCaptcha widget
316
-
317
272
  if (onVerify) onVerify(token, ekey); //Dispatch event to verify user response
318
273
  }
319
274
  }, {
@@ -322,14 +277,16 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
322
277
  var onExpire = this.props.onExpire;
323
278
  var captchaId = this.state.captchaId;
324
279
  var hcaptcha = this._hcaptcha;
325
-
326
280
  if (!this.isReady()) {
327
281
  return;
328
282
  }
329
-
330
283
  hcaptcha.reset(captchaId); // If hCaptcha runs into error, reset captcha - hCaptcha
331
284
 
332
285
  if (onExpire) onExpire();
286
+ this.sentryHub.addBreadcrumb({
287
+ category: _constants.scopeTag.value,
288
+ message: _constants.breadcrumbMessages.expired
289
+ });
333
290
  }
334
291
  }, {
335
292
  key: "handleError",
@@ -337,20 +294,18 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
337
294
  var onError = this.props.onError;
338
295
  var captchaId = this.state.captchaId;
339
296
  var hcaptcha = this._hcaptcha;
340
-
341
297
  if (this.isReady()) {
342
298
  // If hCaptcha runs into error, reset captcha - hCaptcha
343
299
  hcaptcha.reset(captchaId);
344
300
  }
345
-
346
301
  if (onError) onError(event);
347
302
  }
348
303
  }, {
349
304
  key: "isReady",
350
305
  value: function isReady() {
351
306
  var _this$state2 = this.state,
352
- isApiReady = _this$state2.isApiReady,
353
- isRemoved = _this$state2.isRemoved;
307
+ isApiReady = _this$state2.isApiReady,
308
+ isRemoved = _this$state2.isRemoved;
354
309
  return isApiReady && !isRemoved;
355
310
  }
356
311
  }, {
@@ -359,7 +314,6 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
359
314
  if (!this.isReady() || !this.props.onOpen) {
360
315
  return;
361
316
  }
362
-
363
317
  this.props.onOpen();
364
318
  }
365
319
  }, {
@@ -368,7 +322,6 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
368
322
  if (!this.isReady() || !this.props.onClose) {
369
323
  return;
370
324
  }
371
-
372
325
  this.props.onClose();
373
326
  }
374
327
  }, {
@@ -377,40 +330,37 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
377
330
  if (!this.isReady() || !this.props.onChalExpired) {
378
331
  return;
379
332
  }
380
-
381
333
  this.props.onChalExpired();
382
334
  }
383
335
  }, {
384
336
  key: "execute",
385
337
  value: function execute() {
386
338
  var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
387
- var captchaId = this.state.captchaId;
388
- var hcaptcha = this._hcaptcha;
389
-
390
- if (!this.isReady()) {
391
- return;
339
+ try {
340
+ var captchaId = this.state.captchaId;
341
+ var hcaptcha = this._hcaptcha;
342
+ if (!this.isReady()) {
343
+ return;
344
+ }
345
+ if (opts && (0, _typeof2["default"])(opts) !== "object") {
346
+ opts = null;
347
+ }
348
+ return hcaptcha.execute(captchaId, opts);
349
+ } catch (error) {
350
+ this.sentryHub.captureException(error);
392
351
  }
393
-
394
- if (opts && (0, _typeof2["default"])(opts) !== "object") {
395
- opts = null;
396
- }
397
-
398
- return hcaptcha.execute(captchaId, opts);
399
352
  }
400
353
  }, {
401
354
  key: "setData",
402
355
  value: function setData(data) {
403
356
  var captchaId = this.state.captchaId;
404
357
  var hcaptcha = this._hcaptcha;
405
-
406
358
  if (!this.isReady()) {
407
359
  return;
408
360
  }
409
-
410
361
  if (data && (0, _typeof2["default"])(data) !== "object") {
411
362
  data = null;
412
363
  }
413
-
414
364
  hcaptcha.setData(captchaId, data);
415
365
  }
416
366
  }, {
@@ -437,7 +387,6 @@ var HCaptcha = /*#__PURE__*/function (_React$Component) {
437
387
  }]);
438
388
  return HCaptcha;
439
389
  }(React.Component);
440
-
441
390
  var _default = HCaptcha;
442
391
  exports["default"] = _default;
443
392
  module.exports = exports.default;
package/dist/utils.js CHANGED
@@ -1,34 +1,10 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
3
  Object.defineProperty(exports, "__esModule", {
6
4
  value: true
7
5
  });
8
- exports.generateQuery = generateQuery;
9
6
  exports.getFrame = getFrame;
10
7
  exports.getMountElement = getMountElement;
11
-
12
- var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
-
14
- function generateQuery(params) {
15
- return Object.entries(params).filter(function (_ref) {
16
- var _ref2 = (0, _slicedToArray2["default"])(_ref, 2),
17
- key = _ref2[0],
18
- value = _ref2[1];
19
-
20
- return value || value === false;
21
- }).map(function (_ref3) {
22
- var _ref4 = (0, _slicedToArray2["default"])(_ref3, 2),
23
- key = _ref4[0],
24
- value = _ref4[1];
25
-
26
- return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(value));
27
- }).join("&");
28
- }
29
-
30
- ;
31
-
32
8
  function getFrame(element) {
33
9
  var doc = element && element.ownerDocument || document;
34
10
  var win = doc.defaultView || doc.parentWindow || window;
@@ -37,7 +13,6 @@ function getFrame(element) {
37
13
  window: win
38
14
  };
39
15
  }
40
-
41
16
  function getMountElement(element) {
42
17
  return element || document.head;
43
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hcaptcha/react-hcaptcha",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "types": "types/index.d.ts",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -58,6 +58,7 @@
58
58
  "webpack-dev-server": "^4.13.1"
59
59
  },
60
60
  "dependencies": {
61
- "@babel/runtime": "^7.17.9"
61
+ "@babel/runtime": "^7.17.9",
62
+ "@hcaptcha/loader": "^1.0.8"
62
63
  }
63
64
  }
@@ -0,0 +1,13 @@
1
+ export const scopeTag = {
2
+ key: 'source',
3
+ value: '@hCaptcha/react'
4
+ }
5
+
6
+ export const breadcrumbMessages = {
7
+ mounted: 'hCaptcha component mounted',
8
+ expired: 'hCaptcha expired',
9
+ unmounted: 'hCaptcha component unmounted',
10
+ reset: 'hCaptcha reset',
11
+ removed: 'hCaptcha removed'
12
+
13
+ }
package/src/index.js CHANGED
@@ -1,51 +1,8 @@
1
1
  import * as React from 'react';
2
- import { generateQuery, getFrame, getMountElement } from './utils.js';
2
+ import { hCaptchaLoader, initSentry } from '@hcaptcha/loader';
3
3
 
4
- const SCRIPT_ID = 'hcaptcha-api-script-id';
5
- const HCAPTCHA_LOAD_FN_NAME = 'hcaptchaOnLoad';
6
-
7
- // Prevent loading API script multiple times
8
- const scripts = [];
9
-
10
- // Generate hCaptcha API script
11
- const mountCaptchaScript = (params = {}) => {
12
- const element = getMountElement(params.scriptLocation);
13
- delete params.scriptLocation;
14
-
15
- const frame = getFrame(element);
16
- const script = scripts.find(({ scope }) => scope === frame.window);
17
-
18
- if (frame.document.getElementById(SCRIPT_ID) && script) {
19
- // API was already requested
20
- return script.promise;
21
- }
22
-
23
- const promise = new Promise((resolve, reject) => {
24
- // Create global onload callback
25
- frame.window[HCAPTCHA_LOAD_FN_NAME] = resolve;
26
-
27
- const domain = params.apihost || "https://js.hcaptcha.com";
28
- delete params.apihost;
29
-
30
- const script = frame.document.createElement("script");
31
- script.id = SCRIPT_ID;
32
- script.src = `${domain}/1/api.js?render=explicit&onload=${HCAPTCHA_LOAD_FN_NAME}`;
33
-
34
- script.async = params.loadAsync !== undefined? params.loadAsync : true;
35
- delete params.loadAsync;
36
-
37
- script.onerror = (event) => reject('script-error');
38
-
39
- const query = generateQuery(params);
40
- script.src += query !== ""? `&${query}` : "";
41
-
42
- element.appendChild(script);
43
- });
44
-
45
- scripts.push({ promise, scope: frame.window });
46
-
47
- return promise;
48
- };
4
+ import { getFrame, getMountElement } from './utils.js';
5
+ import { breadcrumbMessages, scopeTag } from "./constants";
49
6
 
50
7
 
51
8
  class HCaptcha extends React.Component {
@@ -78,6 +35,7 @@ class HCaptcha extends React.Component {
78
35
 
79
36
  this.ref = React.createRef();
80
37
  this.apiScriptRequested = false;
38
+ this.sentryHub = null;
81
39
 
82
40
  this.state = {
83
41
  isApiReady: false,
@@ -94,6 +52,13 @@ class HCaptcha extends React.Component {
94
52
 
95
53
  const isApiReady = typeof this._hcaptcha !== 'undefined';
96
54
 
55
+ this.sentryHub = initSentry(this.props.sentry, scopeTag);
56
+
57
+ this.sentryHub.addBreadcrumb({
58
+ category: scopeTag.value,
59
+ message: breadcrumbMessages.mounted,
60
+ });
61
+
97
62
  /*
98
63
  * Check if hCaptcha has already been loaded,
99
64
  * If Yes, render the captcha
@@ -126,6 +91,11 @@ class HCaptcha extends React.Component {
126
91
  // Reset any stored variables / timers when unmounting
127
92
  hcaptcha.reset(captchaId);
128
93
  hcaptcha.remove(captchaId);
94
+
95
+ this.sentryHub.addBreadcrumb({
96
+ category: scopeTag.value,
97
+ message: breadcrumbMessages.unmounted,
98
+ });
129
99
  }
130
100
 
131
101
  shouldComponentUpdate(nextProps, nextState) {
@@ -168,26 +138,30 @@ class HCaptcha extends React.Component {
168
138
  sentry,
169
139
  custom,
170
140
  loadAsync,
171
- scriptLocation
141
+ scriptLocation,
142
+ cleanup = true,
172
143
  } = this.props;
173
144
  const mountParams = {
145
+ render: 'explicit',
174
146
  apihost,
175
147
  assethost,
176
148
  endpoint,
177
149
  hl,
178
150
  host,
179
151
  imghost,
180
- recaptchacompat: reCaptchaCompat === false? "off" : null,
152
+ recaptchacompat: reCaptchaCompat === false? 'off' : null,
181
153
  reportapi,
182
154
  sentry,
183
155
  custom,
184
156
  loadAsync,
185
157
  scriptLocation,
158
+ cleanup
186
159
  };
187
160
 
188
- mountCaptchaScript(mountParams)
189
- .then(this.handleOnLoad)
190
- .catch(this.handleError);
161
+ hCaptchaLoader(mountParams)
162
+ .then(this.handleOnLoad, this.handleError)
163
+ .catch(this.handleError);
164
+
191
165
  this.apiScriptRequested = true;
192
166
  }
193
167
 
@@ -225,6 +199,11 @@ class HCaptcha extends React.Component {
225
199
  }
226
200
  // Reset captcha state, removes stored token and unticks checkbox
227
201
  hcaptcha.reset(captchaId)
202
+
203
+ this.sentryHub.addBreadcrumb({
204
+ category: scopeTag.value,
205
+ message: breadcrumbMessages.reset,
206
+ });
228
207
  }
229
208
 
230
209
  removeCaptcha(callback) {
@@ -239,21 +218,33 @@ class HCaptcha extends React.Component {
239
218
  hcaptcha.remove(captchaId);
240
219
  callback && callback()
241
220
  });
221
+
222
+
223
+ this.sentryHub.addBreadcrumb({
224
+ category: scopeTag.value,
225
+ message: breadcrumbMessages.removed,
226
+ });
242
227
  }
243
228
 
244
- handleOnLoad () {
229
+ handleOnLoad () {
245
230
  this.setState({ isApiReady: true }, () => {
246
- const element = getMountElement(this.props.scriptLocation);
247
- const frame = getFrame(element);
231
+ try {
232
+ const element = getMountElement(this.props.scriptLocation);
233
+ const frame = getFrame(element);
234
+
235
+ this._hcaptcha = frame.window.hcaptcha;
248
236
 
249
- this._hcaptcha = frame.window.hcaptcha;
250
237
 
251
- // render captcha and wait for captcha id
252
- this.renderCaptcha(() => {
238
+ // render captcha and wait for captcha id
239
+ this.renderCaptcha(() => {
253
240
  // trigger onLoad if it exists
241
+
254
242
  const { onLoad } = this.props;
255
243
  if (onLoad) onLoad();
256
- });
244
+ });
245
+ } catch (error) {
246
+ this.sentryHub.captureException(error);
247
+ }
257
248
  });
258
249
  }
259
250
 
@@ -281,6 +272,11 @@ class HCaptcha extends React.Component {
281
272
  hcaptcha.reset(captchaId) // If hCaptcha runs into error, reset captcha - hCaptcha
282
273
 
283
274
  if (onExpire) onExpire();
275
+
276
+ this.sentryHub.addBreadcrumb({
277
+ category: scopeTag.value,
278
+ message: breadcrumbMessages.expired,
279
+ });
284
280
  }
285
281
 
286
282
  handleError (event) {
@@ -327,18 +323,23 @@ class HCaptcha extends React.Component {
327
323
  }
328
324
 
329
325
  execute (opts = null) {
330
- const { captchaId } = this.state;
331
- const hcaptcha = this._hcaptcha;
326
+ try {
327
+ const { captchaId } = this.state;
328
+ const hcaptcha = this._hcaptcha;
332
329
 
333
- if (!this.isReady()) {
334
- return;
335
- }
336
330
 
337
- if (opts && typeof opts !== "object") {
338
- opts = null;
339
- }
331
+ if (!this.isReady()) {
332
+ return;
333
+ }
340
334
 
341
- return hcaptcha.execute(captchaId, opts);
335
+ if (opts && typeof opts !== "object") {
336
+ opts = null;
337
+ }
338
+
339
+ return hcaptcha.execute(captchaId, opts);
340
+ } catch (error) {
341
+ this.sentryHub.captureException(error);
342
+ }
342
343
  }
343
344
 
344
345
  setData (data) {
package/src/utils.js CHANGED
@@ -1,11 +1,3 @@
1
- function generateQuery(params) {
2
- return Object.entries(params)
3
- .filter(([key, value]) => value || value === false)
4
- .map(([key, value]) => {
5
- return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`
6
- }).join("&");
7
- };
8
-
9
1
  function getFrame(element) {
10
2
  const doc = (element && element.ownerDocument) || document;
11
3
  const win = doc.defaultView || doc.parentWindow || window;
@@ -18,7 +10,6 @@ function getMountElement(element) {
18
10
  }
19
11
 
20
12
  export {
21
- generateQuery,
22
13
  getFrame,
23
14
  getMountElement
24
- };
15
+ };
package/types/index.d.ts CHANGED
@@ -30,6 +30,8 @@ interface HCaptchaProps {
30
30
  reCaptchaCompat?: boolean;
31
31
  loadAsync?: boolean;
32
32
  scriptLocation?: HTMLElement | null;
33
+ sentry?: boolean;
34
+ cleanup?: boolean;
33
35
  }
34
36
 
35
37
  interface ExecuteResponse {