@internetarchive/recaptcha-manager 0.1.2-alpha.1 → 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/.editorconfig +29 -29
- package/.github/workflows/ci.yml +27 -26
- package/.github/workflows/gh-pages-main.yml +39 -0
- package/.github/workflows/pr-preview.yml +63 -0
- package/.husky/pre-commit +1 -4
- package/.prettierignore +1 -0
- package/LICENSE +661 -661
- package/README.md +63 -85
- package/coverage/.gitignore +3 -0
- package/coverage/.placeholder +0 -0
- package/demo/app-root.ts +93 -93
- package/demo/index.html +21 -21
- package/dist/demo/app-root.d.ts +14 -14
- package/dist/demo/app-root.js +85 -85
- package/dist/demo/app-root.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/src/recaptcha-manager.d.ts +34 -35
- package/dist/src/recaptcha-manager.js +54 -52
- package/dist/src/recaptcha-manager.js.map +1 -1
- package/dist/src/recaptcha-widget.d.ts +38 -39
- package/dist/src/recaptcha-widget.js +95 -95
- package/dist/src/recaptcha-widget.js.map +1 -1
- package/dist/test/mock-grecaptcha.d.ts +23 -24
- package/dist/test/mock-grecaptcha.js +59 -61
- package/dist/test/mock-grecaptcha.js.map +1 -1
- package/dist/test/mock-lazy-loader.d.ts +15 -15
- package/dist/test/mock-lazy-loader.js +17 -16
- package/dist/test/mock-lazy-loader.js.map +1 -1
- package/dist/test/recaptcha-manager.test.d.ts +1 -1
- package/dist/test/recaptcha-manager.test.js +137 -137
- package/dist/test/recaptcha-manager.test.js.map +1 -1
- package/dist/vite.config.d.ts +2 -0
- package/dist/vite.config.js +25 -0
- package/dist/vite.config.js.map +1 -0
- package/eslint.config.mjs +53 -0
- package/index.ts +10 -10
- package/package.json +76 -98
- package/renovate.json +6 -0
- package/src/recaptcha-manager.ts +89 -87
- package/src/recaptcha-widget.ts +148 -148
- package/ssl/server.crt +22 -0
- package/ssl/server.key +28 -0
- package/test/mock-grecaptcha.ts +85 -87
- package/test/mock-lazy-loader.ts +39 -38
- package/test/recaptcha-manager.test.ts +151 -151
- package/tsconfig.json +31 -20
- package/vite.config.ts +25 -0
- package/web-dev-server.config.mjs +32 -28
- package/web-test-runner.config.mjs +41 -41
|
@@ -1,35 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
1
|
+
import { LazyLoaderServiceInterface } from '@internetarchive/lazy-loader-service';
|
|
2
|
+
import { RecaptchaWidgetInterface } from './recaptcha-widget';
|
|
3
|
+
export interface RecaptchaManagerInterface {
|
|
4
|
+
/**
|
|
5
|
+
* Load a recaptcha widget for a given site key or the default site key.
|
|
6
|
+
*/
|
|
7
|
+
getRecaptchaWidget(options?: {
|
|
8
|
+
siteKey?: string;
|
|
9
|
+
recaptchaParams?: ReCaptchaV2.Parameters;
|
|
10
|
+
}): Promise<RecaptchaWidgetInterface>;
|
|
11
|
+
}
|
|
12
|
+
export declare class RecaptchaManager implements RecaptchaManagerInterface {
|
|
13
|
+
private lazyLoader;
|
|
14
|
+
private defaultSiteKey?;
|
|
15
|
+
private recaptchaCache;
|
|
16
|
+
constructor(options?: {
|
|
17
|
+
defaultSiteKey?: string;
|
|
18
|
+
lazyLoader?: LazyLoaderServiceInterface;
|
|
19
|
+
grecaptchaLibrary?: ReCaptchaV2.ReCaptcha;
|
|
20
|
+
});
|
|
21
|
+
/** @inheritdoc */
|
|
22
|
+
getRecaptchaWidget(options?: {
|
|
23
|
+
siteKey?: string;
|
|
24
|
+
recaptchaParams?: ReCaptchaV2.Parameters;
|
|
25
|
+
}): Promise<RecaptchaWidgetInterface>;
|
|
26
|
+
/**
|
|
27
|
+
* Load the Recaptch library from Google.
|
|
28
|
+
*
|
|
29
|
+
* @returns Promise<ReCaptchaV2.ReCaptcha>
|
|
30
|
+
*/
|
|
31
|
+
private getRecaptchaLibrary;
|
|
32
|
+
/** don't use directly, use `getRecaptchaLibrary()` */
|
|
33
|
+
private grecaptchaLibraryCache?;
|
|
34
|
+
}
|
|
@@ -1,53 +1,55 @@
|
|
|
1
|
-
import { LazyLoaderService, } from '@internetarchive/lazy-loader-service';
|
|
2
|
-
import { RecaptchaWidget } from './recaptcha-widget
|
|
3
|
-
export class RecaptchaManager {
|
|
4
|
-
constructor(options) {
|
|
5
|
-
var _a;
|
|
6
|
-
this.recaptchaCache = {};
|
|
7
|
-
this.defaultSiteKey = options === null || options === void 0 ? void 0 : options.defaultSiteKey;
|
|
8
|
-
this.lazyLoader = (_a = options === null || options === void 0 ? void 0 : options.lazyLoader) !== null && _a !== void 0 ? _a : new LazyLoaderService();
|
|
9
|
-
this.grecaptchaLibraryCache = options === null || options === void 0 ? void 0 : options.grecaptchaLibrary;
|
|
10
|
-
}
|
|
11
|
-
/** @inheritdoc */
|
|
12
|
-
async getRecaptchaWidget(options) {
|
|
13
|
-
var _a;
|
|
14
|
-
const key = (_a = options === null || options === void 0 ? void 0 : options.siteKey) !== null && _a !== void 0 ? _a : this.defaultSiteKey;
|
|
15
|
-
if (!key) {
|
|
16
|
-
throw new Error('The reCaptcha widget requires a site key');
|
|
17
|
-
}
|
|
18
|
-
const cached = this.recaptchaCache[key];
|
|
19
|
-
if (cached)
|
|
20
|
-
return cached;
|
|
21
|
-
const grecaptchaLibrary = await this.getRecaptchaLibrary();
|
|
22
|
-
const recaptcha = new RecaptchaWidget({
|
|
23
|
-
siteKey: key,
|
|
24
|
-
grecaptchaLibrary,
|
|
25
|
-
}, options === null || options === void 0 ? void 0 : options.recaptchaParams);
|
|
26
|
-
this.recaptchaCache[key] = recaptcha;
|
|
27
|
-
return recaptcha;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Load the Recaptch library from Google.
|
|
31
|
-
*
|
|
32
|
-
* @returns Promise<ReCaptchaV2.ReCaptcha>
|
|
33
|
-
*/
|
|
34
|
-
async getRecaptchaLibrary() {
|
|
35
|
-
if (this.grecaptchaLibraryCache) {
|
|
36
|
-
return this.grecaptchaLibraryCache;
|
|
37
|
-
}
|
|
38
|
-
return new Promise(resolve => {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
1
|
+
import { LazyLoaderService, } from '@internetarchive/lazy-loader-service';
|
|
2
|
+
import { RecaptchaWidget } from './recaptcha-widget';
|
|
3
|
+
export class RecaptchaManager {
|
|
4
|
+
constructor(options) {
|
|
5
|
+
var _a;
|
|
6
|
+
this.recaptchaCache = {};
|
|
7
|
+
this.defaultSiteKey = options === null || options === void 0 ? void 0 : options.defaultSiteKey;
|
|
8
|
+
this.lazyLoader = (_a = options === null || options === void 0 ? void 0 : options.lazyLoader) !== null && _a !== void 0 ? _a : new LazyLoaderService();
|
|
9
|
+
this.grecaptchaLibraryCache = options === null || options === void 0 ? void 0 : options.grecaptchaLibrary;
|
|
10
|
+
}
|
|
11
|
+
/** @inheritdoc */
|
|
12
|
+
async getRecaptchaWidget(options) {
|
|
13
|
+
var _a;
|
|
14
|
+
const key = (_a = options === null || options === void 0 ? void 0 : options.siteKey) !== null && _a !== void 0 ? _a : this.defaultSiteKey;
|
|
15
|
+
if (!key) {
|
|
16
|
+
throw new Error('The reCaptcha widget requires a site key');
|
|
17
|
+
}
|
|
18
|
+
const cached = this.recaptchaCache[key];
|
|
19
|
+
if (cached)
|
|
20
|
+
return cached;
|
|
21
|
+
const grecaptchaLibrary = await this.getRecaptchaLibrary();
|
|
22
|
+
const recaptcha = new RecaptchaWidget({
|
|
23
|
+
siteKey: key,
|
|
24
|
+
grecaptchaLibrary,
|
|
25
|
+
}, options === null || options === void 0 ? void 0 : options.recaptchaParams);
|
|
26
|
+
this.recaptchaCache[key] = recaptcha;
|
|
27
|
+
return recaptcha;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Load the Recaptch library from Google.
|
|
31
|
+
*
|
|
32
|
+
* @returns Promise<ReCaptchaV2.ReCaptcha>
|
|
33
|
+
*/
|
|
34
|
+
async getRecaptchaLibrary() {
|
|
35
|
+
if (this.grecaptchaLibraryCache) {
|
|
36
|
+
return this.grecaptchaLibraryCache;
|
|
37
|
+
}
|
|
38
|
+
return new Promise(resolve => {
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
40
|
+
window.grecaptchaLoadedCallback = () => {
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
// remove the callback when we're done with it
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
+
delete window.grecaptchaLoadedCallback;
|
|
45
|
+
}, 10);
|
|
46
|
+
this.grecaptchaLibraryCache = window.grecaptcha;
|
|
47
|
+
resolve(window.grecaptcha);
|
|
48
|
+
};
|
|
49
|
+
this.lazyLoader.loadScript({
|
|
50
|
+
src: 'https://www.google.com/recaptcha/api.js?onload=grecaptchaLoadedCallback&render=explicit',
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
53
55
|
//# sourceMappingURL=recaptcha-manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recaptcha-manager.js","sourceRoot":"","sources":["../../src/recaptcha-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,eAAe,EAA4B,MAAM,
|
|
1
|
+
{"version":3,"file":"recaptcha-manager.js","sourceRoot":"","sources":["../../src/recaptcha-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,GAElB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,eAAe,EAA4B,MAAM,oBAAoB,CAAC;AAY/E,MAAM,OAAO,gBAAgB;IAO3B,YAAY,OAIX;;QANO,mBAAc,GAAoC,EAAE,CAAC;QAO3D,IAAI,CAAC,cAAc,GAAG,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,cAAc,CAAC;QAC9C,IAAI,CAAC,UAAU,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,UAAU,mCAAI,IAAI,iBAAiB,EAAE,CAAC;QACjE,IAAI,CAAC,sBAAsB,GAAG,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,iBAAiB,CAAC;IAC3D,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,kBAAkB,CAAC,OAGxB;;QACC,MAAM,GAAG,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,mCAAI,IAAI,CAAC,cAAc,CAAC;QACpD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;QAC9D,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;QACxC,IAAI,MAAM;YAAE,OAAO,MAAM,CAAC;QAE1B,MAAM,iBAAiB,GAAG,MAAM,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC3D,MAAM,SAAS,GAAG,IAAI,eAAe,CACnC;YACE,OAAO,EAAE,GAAG;YACZ,iBAAiB;SAClB,EACD,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,eAAe,CACzB,CAAC;QAEF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;QACrC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,mBAAmB;QAC/B,IAAI,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,sBAAsB,CAAC;QACrC,CAAC;QACD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;YAC3B,8DAA8D;YAC7D,MAAc,CAAC,wBAAwB,GAAG,GAAS,EAAE;gBACpD,UAAU,CAAC,GAAG,EAAE;oBACd,8CAA8C;oBAC9C,8DAA8D;oBAC9D,OAAQ,MAAc,CAAC,wBAAwB,CAAC;gBAClD,CAAC,EAAE,EAAE,CAAC,CAAC;gBACP,IAAI,CAAC,sBAAsB,GAAG,MAAM,CAAC,UAAU,CAAC;gBAChD,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC7B,CAAC,CAAC;YAEF,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;gBACzB,GAAG,EAAE,yFAAyF;aAC/F,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CAIF","sourcesContent":["import {\n LazyLoaderService,\n LazyLoaderServiceInterface,\n} from '@internetarchive/lazy-loader-service';\nimport { RecaptchaWidget, RecaptchaWidgetInterface } from './recaptcha-widget';\n\nexport interface RecaptchaManagerInterface {\n /**\n * Load a recaptcha widget for a given site key or the default site key.\n */\n getRecaptchaWidget(options?: {\n siteKey?: string;\n recaptchaParams?: ReCaptchaV2.Parameters;\n }): Promise<RecaptchaWidgetInterface>;\n}\n\nexport class RecaptchaManager implements RecaptchaManagerInterface {\n private lazyLoader: LazyLoaderServiceInterface;\n\n private defaultSiteKey?: string;\n\n private recaptchaCache: Record<string, RecaptchaWidget> = {};\n\n constructor(options?: {\n defaultSiteKey?: string;\n lazyLoader?: LazyLoaderServiceInterface;\n grecaptchaLibrary?: ReCaptchaV2.ReCaptcha; // allows dependency injection or will be lazy loaded\n }) {\n this.defaultSiteKey = options?.defaultSiteKey;\n this.lazyLoader = options?.lazyLoader ?? new LazyLoaderService();\n this.grecaptchaLibraryCache = options?.grecaptchaLibrary;\n }\n\n /** @inheritdoc */\n async getRecaptchaWidget(options?: {\n siteKey?: string;\n recaptchaParams?: ReCaptchaV2.Parameters;\n }): Promise<RecaptchaWidgetInterface> {\n const key = options?.siteKey ?? this.defaultSiteKey;\n if (!key) {\n throw new Error('The reCaptcha widget requires a site key');\n }\n\n const cached = this.recaptchaCache[key];\n if (cached) return cached;\n\n const grecaptchaLibrary = await this.getRecaptchaLibrary();\n const recaptcha = new RecaptchaWidget(\n {\n siteKey: key,\n grecaptchaLibrary,\n },\n options?.recaptchaParams,\n );\n\n this.recaptchaCache[key] = recaptcha;\n return recaptcha;\n }\n\n /**\n * Load the Recaptch library from Google.\n *\n * @returns Promise<ReCaptchaV2.ReCaptcha>\n */\n private async getRecaptchaLibrary(): Promise<ReCaptchaV2.ReCaptcha> {\n if (this.grecaptchaLibraryCache) {\n return this.grecaptchaLibraryCache;\n }\n return new Promise(resolve => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (window as any).grecaptchaLoadedCallback = (): void => {\n setTimeout(() => {\n // remove the callback when we're done with it\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n delete (window as any).grecaptchaLoadedCallback;\n }, 10);\n this.grecaptchaLibraryCache = window.grecaptcha;\n resolve(window.grecaptcha);\n };\n\n this.lazyLoader.loadScript({\n src: 'https://www.google.com/recaptcha/api.js?onload=grecaptchaLoadedCallback&render=explicit',\n });\n });\n }\n\n /** don't use directly, use `getRecaptchaLibrary()` */\n private grecaptchaLibraryCache?: ReCaptchaV2.ReCaptcha;\n}\n"]}
|
|
@@ -1,39 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
private
|
|
21
|
-
private
|
|
22
|
-
private
|
|
23
|
-
private
|
|
24
|
-
private
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
private
|
|
34
|
-
private
|
|
35
|
-
private
|
|
36
|
-
private
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* This encompasses a widget for a given site key.
|
|
3
|
+
*/
|
|
4
|
+
export interface RecaptchaWidgetInterface {
|
|
5
|
+
/** execute the recaptcha request and returns a token */
|
|
6
|
+
execute(): Promise<string>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The combines parameters from the Recaptcha config and adds the zIndex, which
|
|
10
|
+
* allows us to control the z-index of the recaptcha widget.
|
|
11
|
+
*/
|
|
12
|
+
export type RecaptchaWidgetConfig = Pick<ReCaptchaV2.Parameters, 'tabindex' | 'theme' | 'type' | 'size' | 'badge'> & {
|
|
13
|
+
zIndex?: number;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* This encompasses a widget for a given site key.
|
|
17
|
+
*/
|
|
18
|
+
export declare class RecaptchaWidget implements RecaptchaWidgetInterface {
|
|
19
|
+
private executionSuccessBlock?;
|
|
20
|
+
private executionExpiredBlock?;
|
|
21
|
+
private executionErrorBlock?;
|
|
22
|
+
private grecaptchaLibrary;
|
|
23
|
+
private siteKey;
|
|
24
|
+
private widgetId;
|
|
25
|
+
private isExecuting;
|
|
26
|
+
constructor(config: {
|
|
27
|
+
siteKey: string;
|
|
28
|
+
grecaptchaLibrary: ReCaptchaV2.ReCaptcha;
|
|
29
|
+
}, widgetConfig?: RecaptchaWidgetConfig);
|
|
30
|
+
/** @inheritdoc */
|
|
31
|
+
execute(): Promise<string>;
|
|
32
|
+
private finishExecution;
|
|
33
|
+
private setup;
|
|
34
|
+
private createContainer;
|
|
35
|
+
private responseHandler;
|
|
36
|
+
private expiredHandler;
|
|
37
|
+
private errorHandler;
|
|
38
|
+
}
|
|
@@ -1,96 +1,96 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This encompasses a widget for a given site key.
|
|
3
|
-
*/
|
|
4
|
-
export class RecaptchaWidget {
|
|
5
|
-
constructor(config, widgetConfig) {
|
|
6
|
-
this.widgetId = null;
|
|
7
|
-
this.isExecuting = false;
|
|
8
|
-
this.siteKey = config.siteKey;
|
|
9
|
-
this.grecaptchaLibrary = config.grecaptchaLibrary;
|
|
10
|
-
const container = this.createContainer();
|
|
11
|
-
this.setup(container, widgetConfig);
|
|
12
|
-
}
|
|
13
|
-
/** @inheritdoc */
|
|
14
|
-
async execute() {
|
|
15
|
-
const { widgetId } = this;
|
|
16
|
-
if (widgetId === null) {
|
|
17
|
-
throw new Error('Recaptcha is not setup');
|
|
18
|
-
}
|
|
19
|
-
if (this.isExecuting) {
|
|
20
|
-
// there is no way to know when users have dismissed a previous recaptcha
|
|
21
|
-
// so if we're still in the middle of execution when we call execute again,
|
|
22
|
-
// just reset it and execute it again
|
|
23
|
-
this.finishExecution();
|
|
24
|
-
}
|
|
25
|
-
this.isExecuting = true;
|
|
26
|
-
return new Promise((resolve, reject) => {
|
|
27
|
-
this.executionSuccessBlock = (token) => {
|
|
28
|
-
this.finishExecution();
|
|
29
|
-
resolve(token);
|
|
30
|
-
};
|
|
31
|
-
this.executionExpiredBlock = () => {
|
|
32
|
-
this.finishExecution();
|
|
33
|
-
reject(new Error('expired'));
|
|
34
|
-
};
|
|
35
|
-
this.executionErrorBlock = () => {
|
|
36
|
-
this.finishExecution();
|
|
37
|
-
reject(new Error('error'));
|
|
38
|
-
};
|
|
39
|
-
this.grecaptchaLibrary.execute(widgetId);
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
finishExecution() {
|
|
43
|
-
this.isExecuting = false;
|
|
44
|
-
const { widgetId } = this;
|
|
45
|
-
if (widgetId === null)
|
|
46
|
-
return;
|
|
47
|
-
this.grecaptchaLibrary.reset(widgetId);
|
|
48
|
-
}
|
|
49
|
-
setup(container, widgetConfig) {
|
|
50
|
-
var _a;
|
|
51
|
-
this.widgetId = this.grecaptchaLibrary.render(container, {
|
|
52
|
-
callback: this.responseHandler.bind(this),
|
|
53
|
-
'expired-callback': this.expiredHandler.bind(this),
|
|
54
|
-
'error-callback': this.errorHandler.bind(this),
|
|
55
|
-
sitekey: this.siteKey,
|
|
56
|
-
tabindex: widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.tabindex,
|
|
57
|
-
theme: widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.theme,
|
|
58
|
-
type: widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.type,
|
|
59
|
-
size: (_a = widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.size) !== null && _a !== void 0 ? _a : 'invisible',
|
|
60
|
-
badge: widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.badge,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
createContainer(zIndex) {
|
|
64
|
-
const elementId = `recaptchaManager-${this.siteKey}`;
|
|
65
|
-
let element = document.getElementById(elementId);
|
|
66
|
-
if (!element) {
|
|
67
|
-
element = document.createElement('div');
|
|
68
|
-
element.id = elementId;
|
|
69
|
-
element.style.position = 'fixed';
|
|
70
|
-
element.style.top = '50%';
|
|
71
|
-
element.style.left = '50%';
|
|
72
|
-
element.style.zIndex = zIndex ? `${zIndex}` : '10';
|
|
73
|
-
document.body.appendChild(element);
|
|
74
|
-
}
|
|
75
|
-
return element;
|
|
76
|
-
}
|
|
77
|
-
responseHandler(response) {
|
|
78
|
-
if (this.executionSuccessBlock) {
|
|
79
|
-
this.executionSuccessBlock(response);
|
|
80
|
-
this.executionSuccessBlock = undefined;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
expiredHandler() {
|
|
84
|
-
if (this.executionExpiredBlock) {
|
|
85
|
-
this.executionExpiredBlock();
|
|
86
|
-
this.executionExpiredBlock = undefined;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
errorHandler() {
|
|
90
|
-
if (this.executionErrorBlock) {
|
|
91
|
-
this.executionErrorBlock();
|
|
92
|
-
this.executionErrorBlock = undefined;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* This encompasses a widget for a given site key.
|
|
3
|
+
*/
|
|
4
|
+
export class RecaptchaWidget {
|
|
5
|
+
constructor(config, widgetConfig) {
|
|
6
|
+
this.widgetId = null;
|
|
7
|
+
this.isExecuting = false;
|
|
8
|
+
this.siteKey = config.siteKey;
|
|
9
|
+
this.grecaptchaLibrary = config.grecaptchaLibrary;
|
|
10
|
+
const container = this.createContainer();
|
|
11
|
+
this.setup(container, widgetConfig);
|
|
12
|
+
}
|
|
13
|
+
/** @inheritdoc */
|
|
14
|
+
async execute() {
|
|
15
|
+
const { widgetId } = this;
|
|
16
|
+
if (widgetId === null) {
|
|
17
|
+
throw new Error('Recaptcha is not setup');
|
|
18
|
+
}
|
|
19
|
+
if (this.isExecuting) {
|
|
20
|
+
// there is no way to know when users have dismissed a previous recaptcha
|
|
21
|
+
// so if we're still in the middle of execution when we call execute again,
|
|
22
|
+
// just reset it and execute it again
|
|
23
|
+
this.finishExecution();
|
|
24
|
+
}
|
|
25
|
+
this.isExecuting = true;
|
|
26
|
+
return new Promise((resolve, reject) => {
|
|
27
|
+
this.executionSuccessBlock = (token) => {
|
|
28
|
+
this.finishExecution();
|
|
29
|
+
resolve(token);
|
|
30
|
+
};
|
|
31
|
+
this.executionExpiredBlock = () => {
|
|
32
|
+
this.finishExecution();
|
|
33
|
+
reject(new Error('expired'));
|
|
34
|
+
};
|
|
35
|
+
this.executionErrorBlock = () => {
|
|
36
|
+
this.finishExecution();
|
|
37
|
+
reject(new Error('error'));
|
|
38
|
+
};
|
|
39
|
+
this.grecaptchaLibrary.execute(widgetId);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
finishExecution() {
|
|
43
|
+
this.isExecuting = false;
|
|
44
|
+
const { widgetId } = this;
|
|
45
|
+
if (widgetId === null)
|
|
46
|
+
return;
|
|
47
|
+
this.grecaptchaLibrary.reset(widgetId);
|
|
48
|
+
}
|
|
49
|
+
setup(container, widgetConfig) {
|
|
50
|
+
var _a;
|
|
51
|
+
this.widgetId = this.grecaptchaLibrary.render(container, {
|
|
52
|
+
callback: this.responseHandler.bind(this),
|
|
53
|
+
'expired-callback': this.expiredHandler.bind(this),
|
|
54
|
+
'error-callback': this.errorHandler.bind(this),
|
|
55
|
+
sitekey: this.siteKey,
|
|
56
|
+
tabindex: widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.tabindex,
|
|
57
|
+
theme: widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.theme,
|
|
58
|
+
type: widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.type,
|
|
59
|
+
size: (_a = widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.size) !== null && _a !== void 0 ? _a : 'invisible',
|
|
60
|
+
badge: widgetConfig === null || widgetConfig === void 0 ? void 0 : widgetConfig.badge,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
createContainer(zIndex) {
|
|
64
|
+
const elementId = `recaptchaManager-${this.siteKey}`;
|
|
65
|
+
let element = document.getElementById(elementId);
|
|
66
|
+
if (!element) {
|
|
67
|
+
element = document.createElement('div');
|
|
68
|
+
element.id = elementId;
|
|
69
|
+
element.style.position = 'fixed';
|
|
70
|
+
element.style.top = '50%';
|
|
71
|
+
element.style.left = '50%';
|
|
72
|
+
element.style.zIndex = zIndex ? `${zIndex}` : '10';
|
|
73
|
+
document.body.appendChild(element);
|
|
74
|
+
}
|
|
75
|
+
return element;
|
|
76
|
+
}
|
|
77
|
+
responseHandler(response) {
|
|
78
|
+
if (this.executionSuccessBlock) {
|
|
79
|
+
this.executionSuccessBlock(response);
|
|
80
|
+
this.executionSuccessBlock = undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
expiredHandler() {
|
|
84
|
+
if (this.executionExpiredBlock) {
|
|
85
|
+
this.executionExpiredBlock();
|
|
86
|
+
this.executionExpiredBlock = undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
errorHandler() {
|
|
90
|
+
if (this.executionErrorBlock) {
|
|
91
|
+
this.executionErrorBlock();
|
|
92
|
+
this.executionErrorBlock = undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
96
|
//# sourceMappingURL=recaptcha-widget.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recaptcha-widget.js","sourceRoot":"","sources":["../../src/recaptcha-widget.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,MAAM,OAAO,eAAe;IAe1B,YACE,MAGC,EACD,YAAoC;QAT9B,aAAQ,GAAkB,IAAI,CAAC;QAE/B,gBAAW,GAAG,KAAK,CAAC;QAS1B,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC9B,IAAI,CAAC,iBAAiB,GAAG,MAAM,CAAC,iBAAiB,CAAC;QAElD,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACzC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACtC,CAAC;IAED,kBAAkB;IAClB,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;QAE1B,IAAI,QAAQ,KAAK,IAAI,EAAE;
|
|
1
|
+
{"version":3,"file":"recaptcha-widget.js","sourceRoot":"","sources":["../../src/recaptcha-widget.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * This encompasses a widget for a given site key.\n */\nexport interface RecaptchaWidgetInterface {\n /** execute the recaptcha request and returns a token */\n execute(): Promise<string>;\n}\n\n/**\n * The combines parameters from the Recaptcha config and adds the zIndex, which\n * allows us to control the z-index of the recaptcha widget.\n */\nexport type RecaptchaWidgetConfig = Pick<\n ReCaptchaV2.Parameters,\n 'tabindex' | 'theme' | 'type' | 'size' | 'badge'\n> & {\n zIndex?: number;\n};\n\n/**\n * This encompasses a widget for a given site key.\n */\nexport class RecaptchaWidget implements RecaptchaWidgetInterface {\n private executionSuccessBlock?: (token: string) => void;\n\n private executionExpiredBlock?: () => void;\n\n private executionErrorBlock?: () => void;\n\n private grecaptchaLibrary: ReCaptchaV2.ReCaptcha;\n\n private siteKey: string;\n\n private widgetId: number | null = null;\n\n private isExecuting = false;\n\n constructor(\n config: {\n siteKey: string;\n grecaptchaLibrary: ReCaptchaV2.ReCaptcha;\n },\n widgetConfig?: RecaptchaWidgetConfig,\n ) {\n this.siteKey = config.siteKey;\n this.grecaptchaLibrary = config.grecaptchaLibrary;\n\n const container = this.createContainer();\n this.setup(container, widgetConfig);\n }\n\n /** @inheritdoc */\n async execute(): Promise<string> {\n const { widgetId } = this;\n\n if (widgetId === null) {\n throw new Error('Recaptcha is not setup');\n }\n\n if (this.isExecuting) {\n // there is no way to know when users have dismissed a previous recaptcha\n // so if we're still in the middle of execution when we call execute again,\n // just reset it and execute it again\n this.finishExecution();\n }\n\n this.isExecuting = true;\n\n return new Promise((resolve, reject) => {\n this.executionSuccessBlock = (token: string): void => {\n this.finishExecution();\n resolve(token);\n };\n\n this.executionExpiredBlock = (): void => {\n this.finishExecution();\n reject(new Error('expired'));\n };\n\n this.executionErrorBlock = (): void => {\n this.finishExecution();\n reject(new Error('error'));\n };\n\n this.grecaptchaLibrary.execute(widgetId);\n });\n }\n\n private finishExecution() {\n this.isExecuting = false;\n const { widgetId } = this;\n if (widgetId === null) return;\n this.grecaptchaLibrary.reset(widgetId);\n }\n\n private setup(\n container: HTMLElement,\n widgetConfig?: ReCaptchaV2.Parameters,\n ): void {\n this.widgetId = this.grecaptchaLibrary.render(container, {\n callback: this.responseHandler.bind(this),\n 'expired-callback': this.expiredHandler.bind(this),\n 'error-callback': this.errorHandler.bind(this),\n sitekey: this.siteKey,\n tabindex: widgetConfig?.tabindex,\n theme: widgetConfig?.theme,\n type: widgetConfig?.type,\n size: widgetConfig?.size ?? 'invisible',\n badge: widgetConfig?.badge,\n });\n }\n\n private createContainer(zIndex?: number): HTMLElement {\n const elementId = `recaptchaManager-${this.siteKey}`;\n let element: HTMLElement | null = document.getElementById(elementId);\n if (!element) {\n element = document.createElement('div');\n element.id = elementId;\n element.style.position = 'fixed';\n element.style.top = '50%';\n element.style.left = '50%';\n element.style.zIndex = zIndex ? `${zIndex}` : '10';\n document.body.appendChild(element);\n }\n return element;\n }\n\n private responseHandler(response: string): void {\n if (this.executionSuccessBlock) {\n this.executionSuccessBlock(response);\n this.executionSuccessBlock = undefined;\n }\n }\n\n private expiredHandler(): void {\n if (this.executionExpiredBlock) {\n this.executionExpiredBlock();\n this.executionExpiredBlock = undefined;\n }\n }\n\n private errorHandler(): void {\n if (this.executionErrorBlock) {\n this.executionErrorBlock();\n this.executionErrorBlock = undefined;\n }\n }\n}\n"]}
|
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
export declare
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
callback?: (
|
|
12
|
-
'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
1
|
+
export type MockGrecaptchaMode = 'success' | 'expired' | 'error';
|
|
2
|
+
export declare class MockGrecaptcha implements ReCaptchaV2.ReCaptcha {
|
|
3
|
+
response: string;
|
|
4
|
+
renderCalled: boolean;
|
|
5
|
+
executeCalled: boolean;
|
|
6
|
+
resetCallCount: number;
|
|
7
|
+
getResponseCalled: boolean;
|
|
8
|
+
mode: MockGrecaptchaMode;
|
|
9
|
+
addDelay: boolean;
|
|
10
|
+
callback?: (response: string) => void;
|
|
11
|
+
'expired-callback'?: () => void;
|
|
12
|
+
'error-callback'?: () => void;
|
|
13
|
+
render(container: string | HTMLElement, parameters?: ReCaptchaV2.Parameters | undefined, inherit?: boolean | undefined): number;
|
|
14
|
+
reset(opt_widget_id?: number | undefined): void;
|
|
15
|
+
getResponse(opt_widget_id?: number | undefined): string;
|
|
16
|
+
execute(opt_widget_id?: number): void;
|
|
17
|
+
ready(callback: () => void): void;
|
|
18
|
+
private callCallback;
|
|
19
|
+
constructor(options: {
|
|
20
|
+
mode: MockGrecaptchaMode;
|
|
21
|
+
addDelay?: boolean;
|
|
22
|
+
});
|
|
23
|
+
}
|