@captchafox/react 1.0.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 +93 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +163 -0
- package/dist/index.mjs +136 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# @captchafox/react
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@captchafox/react)
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the library using your prefered package manager
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @captchafox/react
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
yarn add @captchafox/react
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pnpm add @captchafox/react
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { CaptchaFox } from '@captchafox/react'
|
|
25
|
+
|
|
26
|
+
function Example() {
|
|
27
|
+
return <CaptchaFox sitekey="sk_11111111000000001111111100000000" />
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Props
|
|
32
|
+
|
|
33
|
+
| **Prop** | **Type** | **Description** | **Required** |
|
|
34
|
+
| -------- | ----------------------- | ------------------------------------------------------------------------------- | ------------ |
|
|
35
|
+
| sitekey | `string` | The sitekey for the widget | ✅ |
|
|
36
|
+
| lng | `string` | The language the widget should display. Defaults to automatically detecting it. | |
|
|
37
|
+
| mode | `inline\|popup\|hidden` | The mode the widget should be displayed in | |
|
|
38
|
+
| onVerify | `function` | Called with the response token after successful verification | |
|
|
39
|
+
| onFail | `function` | Called after unsuccessful verification | |
|
|
40
|
+
| onError | `function` | Called if an error occured | |
|
|
41
|
+
| onExpire | `function` | Called if the challenge expired | |
|
|
42
|
+
| onClose | `function` | Called if the challenge was closed | |
|
|
43
|
+
|
|
44
|
+
### Using the verification callback
|
|
45
|
+
|
|
46
|
+
```jsx
|
|
47
|
+
import { CaptchaFox, CAPTCHA_RESPONSE_KEY } from '@captchafox/react'
|
|
48
|
+
|
|
49
|
+
function Example() {
|
|
50
|
+
const handleVerify = (token: string) => {
|
|
51
|
+
// do something with the token here (e.g. submit the form)
|
|
52
|
+
const formData = {
|
|
53
|
+
// your form data
|
|
54
|
+
[CAPTCHA_RESPONSE_KEY]: token
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<CaptchaFox
|
|
60
|
+
sitekey="sk_11111111000000001111111100000000"
|
|
61
|
+
onVerify={handleVerify}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Interacting with the instance
|
|
68
|
+
|
|
69
|
+
```jsx
|
|
70
|
+
import { useRef } from 'react'
|
|
71
|
+
import { CaptchaFox, CaptchaFoxInstance } from '@captchafox/react'
|
|
72
|
+
|
|
73
|
+
function Example() {
|
|
74
|
+
const captchaRef = useRef<CaptchaFoxInstance | null>(null);
|
|
75
|
+
|
|
76
|
+
const triggerAction = async () => {
|
|
77
|
+
// execute the captcha
|
|
78
|
+
try {
|
|
79
|
+
const token = await captchaRef.current?.execute()
|
|
80
|
+
} catch {
|
|
81
|
+
// unsuccessful verification
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<CaptchaFox
|
|
87
|
+
sitekey="sk_11111111000000001111111100000000"
|
|
88
|
+
ref={captchaRef}
|
|
89
|
+
/>
|
|
90
|
+
<button onClick={triggerAction}>Action</button>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { WidgetApi, WidgetOptions } from '@captchafox/types';
|
|
3
|
+
|
|
4
|
+
type CaptchaFoxInstance = Omit<WidgetApi, 'render'>;
|
|
5
|
+
declare const CaptchaFox: react.ForwardRefExoticComponent<WidgetOptions & {
|
|
6
|
+
/** Called after the widget has been loaded */
|
|
7
|
+
onLoad?: (() => void) | undefined;
|
|
8
|
+
className?: string | undefined;
|
|
9
|
+
} & react.RefAttributes<CaptchaFoxInstance>>;
|
|
10
|
+
|
|
11
|
+
declare const CAPTCHA_RESPONSE_KEY = "cf-captcha-response";
|
|
12
|
+
|
|
13
|
+
export { CAPTCHA_RESPONSE_KEY, CaptchaFox, CaptchaFoxInstance };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __async = (__this, __arguments, generator) => {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
var fulfilled = (value) => {
|
|
22
|
+
try {
|
|
23
|
+
step(generator.next(value));
|
|
24
|
+
} catch (e) {
|
|
25
|
+
reject(e);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
var rejected = (value) => {
|
|
29
|
+
try {
|
|
30
|
+
step(generator.throw(value));
|
|
31
|
+
} catch (e) {
|
|
32
|
+
reject(e);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
36
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/index.tsx
|
|
41
|
+
var src_exports = {};
|
|
42
|
+
__export(src_exports, {
|
|
43
|
+
CAPTCHA_RESPONSE_KEY: () => CAPTCHA_RESPONSE_KEY,
|
|
44
|
+
CaptchaFox: () => CaptchaFox
|
|
45
|
+
});
|
|
46
|
+
module.exports = __toCommonJS(src_exports);
|
|
47
|
+
|
|
48
|
+
// ../internal/dist/index.mjs
|
|
49
|
+
var resolveFn;
|
|
50
|
+
var rejectFn;
|
|
51
|
+
var mountInstance = new Promise((resolve, reject) => {
|
|
52
|
+
resolveFn = resolve;
|
|
53
|
+
rejectFn = reject;
|
|
54
|
+
});
|
|
55
|
+
var LOAD_FUNC_KEY = "captchaFoxOnLoad";
|
|
56
|
+
var SCRIPT_SRC = `https://cdn.captchafox.com/api.js?render=explicit&onload=${LOAD_FUNC_KEY}`;
|
|
57
|
+
function loadCaptchaScript() {
|
|
58
|
+
return __async(this, null, function* () {
|
|
59
|
+
if (document.querySelector(`script[src="${SCRIPT_SRC}"]`))
|
|
60
|
+
return mountInstance;
|
|
61
|
+
window[LOAD_FUNC_KEY] = resolveFn;
|
|
62
|
+
const script = document.createElement("script");
|
|
63
|
+
script.src = SCRIPT_SRC;
|
|
64
|
+
script.async = true;
|
|
65
|
+
script.defer = true;
|
|
66
|
+
script.type = "module";
|
|
67
|
+
script.onerror = rejectFn;
|
|
68
|
+
document.body.appendChild(script);
|
|
69
|
+
return mountInstance;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
var isApiReady = () => typeof (window == null ? void 0 : window.captchafox) !== "undefined";
|
|
73
|
+
|
|
74
|
+
// src/CaptchaFox.tsx
|
|
75
|
+
var import_react = require("react");
|
|
76
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
77
|
+
var CaptchaFox = (0, import_react.forwardRef)(
|
|
78
|
+
({ sitekey, lng, mode, className, onError, onVerify, onLoad, onFail, onClose }, ref) => {
|
|
79
|
+
const containerRef = (0, import_react.createRef)();
|
|
80
|
+
const [widgetId, setWidgetId] = (0, import_react.useState)();
|
|
81
|
+
const firstRendered = (0, import_react.useRef)(false);
|
|
82
|
+
(0, import_react.useImperativeHandle)(
|
|
83
|
+
ref,
|
|
84
|
+
() => {
|
|
85
|
+
return {
|
|
86
|
+
getResponse() {
|
|
87
|
+
if (!isApiReady() || !widgetId) {
|
|
88
|
+
console.warn("[CaptchaFox] Widget has not been loaded");
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
return window.captchafox.getResponse(widgetId);
|
|
92
|
+
},
|
|
93
|
+
reset() {
|
|
94
|
+
if (!isApiReady() || !widgetId) {
|
|
95
|
+
console.warn("[CaptchaFox] Widget has not been loaded");
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
window.captchafox.reset(widgetId);
|
|
99
|
+
},
|
|
100
|
+
remove() {
|
|
101
|
+
if (!isApiReady() || !widgetId) {
|
|
102
|
+
console.warn("[CaptchaFox] Widget has not been loaded");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
setWidgetId("");
|
|
106
|
+
window.captchafox.remove(widgetId);
|
|
107
|
+
},
|
|
108
|
+
execute: () => {
|
|
109
|
+
if (!isApiReady() || !widgetId) {
|
|
110
|
+
return Promise.reject("[CaptchaFox] Widget has not been loaded");
|
|
111
|
+
}
|
|
112
|
+
return window.captchafox.execute(widgetId);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
[widgetId]
|
|
117
|
+
);
|
|
118
|
+
const renderCaptcha = () => __async(void 0, null, function* () {
|
|
119
|
+
var _a, _b, _c, _d;
|
|
120
|
+
(_a = window.captchafox) == null ? void 0 : _a.remove(widgetId);
|
|
121
|
+
if (!containerRef.current || ((_c = (_b = containerRef.current) == null ? void 0 : _b.children) == null ? void 0 : _c.length) === 1)
|
|
122
|
+
return;
|
|
123
|
+
const newWidgetId = yield (_d = window.captchafox) == null ? void 0 : _d.render(containerRef.current, {
|
|
124
|
+
lng,
|
|
125
|
+
sitekey,
|
|
126
|
+
mode,
|
|
127
|
+
onError,
|
|
128
|
+
onFail,
|
|
129
|
+
onClose,
|
|
130
|
+
onVerify
|
|
131
|
+
});
|
|
132
|
+
setWidgetId(newWidgetId);
|
|
133
|
+
onLoad == null ? void 0 : onLoad();
|
|
134
|
+
});
|
|
135
|
+
(0, import_react.useEffect)(() => {
|
|
136
|
+
if (firstRendered.current) {
|
|
137
|
+
if (isApiReady()) {
|
|
138
|
+
renderCaptcha();
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
loadCaptchaScript().then(() => __async(void 0, null, function* () {
|
|
142
|
+
if (isApiReady()) {
|
|
143
|
+
firstRendered.current = true;
|
|
144
|
+
yield renderCaptcha();
|
|
145
|
+
}
|
|
146
|
+
})).catch((err) => {
|
|
147
|
+
onError == null ? void 0 : onError(err);
|
|
148
|
+
console.error("[CaptchaFox] Could not load script:", err);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}, [sitekey, lng, mode]);
|
|
152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: containerRef, id: widgetId, className });
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
CaptchaFox.displayName = "CaptchaFox";
|
|
156
|
+
|
|
157
|
+
// src/index.tsx
|
|
158
|
+
var CAPTCHA_RESPONSE_KEY = "cf-captcha-response";
|
|
159
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
160
|
+
0 && (module.exports = {
|
|
161
|
+
CAPTCHA_RESPONSE_KEY,
|
|
162
|
+
CaptchaFox
|
|
163
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
var __async = (__this, __arguments, generator) => {
|
|
2
|
+
return new Promise((resolve, reject) => {
|
|
3
|
+
var fulfilled = (value) => {
|
|
4
|
+
try {
|
|
5
|
+
step(generator.next(value));
|
|
6
|
+
} catch (e) {
|
|
7
|
+
reject(e);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var rejected = (value) => {
|
|
11
|
+
try {
|
|
12
|
+
step(generator.throw(value));
|
|
13
|
+
} catch (e) {
|
|
14
|
+
reject(e);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
18
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// ../internal/dist/index.mjs
|
|
23
|
+
var resolveFn;
|
|
24
|
+
var rejectFn;
|
|
25
|
+
var mountInstance = new Promise((resolve, reject) => {
|
|
26
|
+
resolveFn = resolve;
|
|
27
|
+
rejectFn = reject;
|
|
28
|
+
});
|
|
29
|
+
var LOAD_FUNC_KEY = "captchaFoxOnLoad";
|
|
30
|
+
var SCRIPT_SRC = `https://cdn.captchafox.com/api.js?render=explicit&onload=${LOAD_FUNC_KEY}`;
|
|
31
|
+
function loadCaptchaScript() {
|
|
32
|
+
return __async(this, null, function* () {
|
|
33
|
+
if (document.querySelector(`script[src="${SCRIPT_SRC}"]`))
|
|
34
|
+
return mountInstance;
|
|
35
|
+
window[LOAD_FUNC_KEY] = resolveFn;
|
|
36
|
+
const script = document.createElement("script");
|
|
37
|
+
script.src = SCRIPT_SRC;
|
|
38
|
+
script.async = true;
|
|
39
|
+
script.defer = true;
|
|
40
|
+
script.type = "module";
|
|
41
|
+
script.onerror = rejectFn;
|
|
42
|
+
document.body.appendChild(script);
|
|
43
|
+
return mountInstance;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
var isApiReady = () => typeof (window == null ? void 0 : window.captchafox) !== "undefined";
|
|
47
|
+
|
|
48
|
+
// src/CaptchaFox.tsx
|
|
49
|
+
import { createRef, forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
|
|
50
|
+
import { jsx } from "react/jsx-runtime";
|
|
51
|
+
var CaptchaFox = forwardRef(
|
|
52
|
+
({ sitekey, lng, mode, className, onError, onVerify, onLoad, onFail, onClose }, ref) => {
|
|
53
|
+
const containerRef = createRef();
|
|
54
|
+
const [widgetId, setWidgetId] = useState();
|
|
55
|
+
const firstRendered = useRef(false);
|
|
56
|
+
useImperativeHandle(
|
|
57
|
+
ref,
|
|
58
|
+
() => {
|
|
59
|
+
return {
|
|
60
|
+
getResponse() {
|
|
61
|
+
if (!isApiReady() || !widgetId) {
|
|
62
|
+
console.warn("[CaptchaFox] Widget has not been loaded");
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
return window.captchafox.getResponse(widgetId);
|
|
66
|
+
},
|
|
67
|
+
reset() {
|
|
68
|
+
if (!isApiReady() || !widgetId) {
|
|
69
|
+
console.warn("[CaptchaFox] Widget has not been loaded");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
window.captchafox.reset(widgetId);
|
|
73
|
+
},
|
|
74
|
+
remove() {
|
|
75
|
+
if (!isApiReady() || !widgetId) {
|
|
76
|
+
console.warn("[CaptchaFox] Widget has not been loaded");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
setWidgetId("");
|
|
80
|
+
window.captchafox.remove(widgetId);
|
|
81
|
+
},
|
|
82
|
+
execute: () => {
|
|
83
|
+
if (!isApiReady() || !widgetId) {
|
|
84
|
+
return Promise.reject("[CaptchaFox] Widget has not been loaded");
|
|
85
|
+
}
|
|
86
|
+
return window.captchafox.execute(widgetId);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
},
|
|
90
|
+
[widgetId]
|
|
91
|
+
);
|
|
92
|
+
const renderCaptcha = () => __async(void 0, null, function* () {
|
|
93
|
+
var _a, _b, _c, _d;
|
|
94
|
+
(_a = window.captchafox) == null ? void 0 : _a.remove(widgetId);
|
|
95
|
+
if (!containerRef.current || ((_c = (_b = containerRef.current) == null ? void 0 : _b.children) == null ? void 0 : _c.length) === 1)
|
|
96
|
+
return;
|
|
97
|
+
const newWidgetId = yield (_d = window.captchafox) == null ? void 0 : _d.render(containerRef.current, {
|
|
98
|
+
lng,
|
|
99
|
+
sitekey,
|
|
100
|
+
mode,
|
|
101
|
+
onError,
|
|
102
|
+
onFail,
|
|
103
|
+
onClose,
|
|
104
|
+
onVerify
|
|
105
|
+
});
|
|
106
|
+
setWidgetId(newWidgetId);
|
|
107
|
+
onLoad == null ? void 0 : onLoad();
|
|
108
|
+
});
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (firstRendered.current) {
|
|
111
|
+
if (isApiReady()) {
|
|
112
|
+
renderCaptcha();
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
loadCaptchaScript().then(() => __async(void 0, null, function* () {
|
|
116
|
+
if (isApiReady()) {
|
|
117
|
+
firstRendered.current = true;
|
|
118
|
+
yield renderCaptcha();
|
|
119
|
+
}
|
|
120
|
+
})).catch((err) => {
|
|
121
|
+
onError == null ? void 0 : onError(err);
|
|
122
|
+
console.error("[CaptchaFox] Could not load script:", err);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}, [sitekey, lng, mode]);
|
|
126
|
+
return /* @__PURE__ */ jsx("div", { ref: containerRef, id: widgetId, className });
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
CaptchaFox.displayName = "CaptchaFox";
|
|
130
|
+
|
|
131
|
+
// src/index.tsx
|
|
132
|
+
var CAPTCHA_RESPONSE_KEY = "cf-captcha-response";
|
|
133
|
+
export {
|
|
134
|
+
CAPTCHA_RESPONSE_KEY,
|
|
135
|
+
CaptchaFox
|
|
136
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@captchafox/react",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"module": "./dist/index.mjs",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/**"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup src/index.tsx --format esm,cjs --dts --external react",
|
|
14
|
+
"dev": "tsup src/index.tsx --format esm,cjs --watch --dts --external react",
|
|
15
|
+
"lint": "eslint \"src/**/*.ts*\"",
|
|
16
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
|
17
|
+
"test": "jest --coverage",
|
|
18
|
+
"test:watch": "jest --watch"
|
|
19
|
+
},
|
|
20
|
+
"peerDependencies": {
|
|
21
|
+
"react": ">=16.8.0",
|
|
22
|
+
"react-dom": ">=16.8.0"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@captchafox/types": "*"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@captchafox/internal": "*",
|
|
29
|
+
"@testing-library/react": "^14.0.0",
|
|
30
|
+
"@testing-library/user-event": "^14.4.3",
|
|
31
|
+
"@testing-library/jest-dom": "^5.16.5",
|
|
32
|
+
"@types/jest": "^29.5.0",
|
|
33
|
+
"@types/react": "^18.0.9",
|
|
34
|
+
"@types/react-dom": "^18.0.4",
|
|
35
|
+
"eslint": "^8.15.0",
|
|
36
|
+
"eslint-config-captchafox": "*",
|
|
37
|
+
"jest": "^29.5.0",
|
|
38
|
+
"jest-environment-jsdom": "^29.5.0",
|
|
39
|
+
"react": "^18.1.0",
|
|
40
|
+
"tsup": "^6.6.3",
|
|
41
|
+
"ts-jest": "^29.0.5",
|
|
42
|
+
"typescript": "^4.9.5"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/CaptchaFox/javascript-integrations/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/CaptchaFox/javascript-integrations#readme"
|
|
48
|
+
}
|