@internetarchive/recaptcha-manager 0.0.1-alpha.1 → 0.0.1-alpha.2

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.
@@ -0,0 +1,140 @@
1
+ import { LazyLoaderService, } from '@internetarchive/lazy-loader-service';
2
+ export class RecaptchaManager {
3
+ constructor(options) {
4
+ this.isExecuting = false;
5
+ this.containerElement = null;
6
+ this.grecaptchaLibrary = options.grecaptchaLibrary;
7
+ this.siteKey = options.siteKey;
8
+ }
9
+ /**
10
+ * This is a convenience initializer that also lazily loads
11
+ * the recaptcha library from Google.
12
+ *
13
+ * @param options
14
+ * @returns
15
+ */
16
+ static async getRecaptchaManager(siteKey, options) {
17
+ const grecaptchaLibrary = await RecaptchaManager.loadRecaptchaLibrary({
18
+ lazyLoader: options === null || options === void 0 ? void 0 : options.lazyLoader,
19
+ });
20
+ const manager = new RecaptchaManager({
21
+ grecaptchaLibrary,
22
+ siteKey,
23
+ });
24
+ manager.setup({
25
+ tabIndex: options === null || options === void 0 ? void 0 : options.tabIndex,
26
+ theme: options === null || options === void 0 ? void 0 : options.theme,
27
+ type: options === null || options === void 0 ? void 0 : options.type,
28
+ size: options === null || options === void 0 ? void 0 : options.size,
29
+ });
30
+ return manager;
31
+ }
32
+ /**
33
+ * Load the Recaptch library from Google.
34
+ *
35
+ * @param options
36
+ * @returns
37
+ */
38
+ static async loadRecaptchaLibrary(options) {
39
+ var _a;
40
+ const service = (_a = options === null || options === void 0 ? void 0 : options.lazyLoader) !== null && _a !== void 0 ? _a : new LazyLoaderService();
41
+ return new Promise(resolve => {
42
+ window.grecaptchaLoadedCallback = () => {
43
+ setTimeout(() => {
44
+ // remove the callback when we're done with it
45
+ delete window.grecaptchaLoadedCallback;
46
+ }, 10);
47
+ resolve(window.grecaptcha);
48
+ };
49
+ service.loadScript({
50
+ src: 'https://www.google.com/recaptcha/api.js?onload=grecaptchaLoadedCallback&render=explicit',
51
+ });
52
+ });
53
+ }
54
+ /** @inheritdoc */
55
+ execute() {
56
+ if (this.isExecuting) {
57
+ throw new Error('Recaptcha is already executing');
58
+ }
59
+ this.isExecuting = true;
60
+ return new Promise((resolve, reject) => {
61
+ this.executionSuccessBlock = (token) => {
62
+ this.finishExecution();
63
+ resolve(token);
64
+ };
65
+ this.executionExpiredBlock = () => {
66
+ this.finishExecution();
67
+ reject(new Error('expired'));
68
+ };
69
+ this.executionErrorBlock = () => {
70
+ this.finishExecution();
71
+ reject(new Error('error'));
72
+ };
73
+ this.grecaptchaLibrary.execute();
74
+ });
75
+ }
76
+ /** @inheritdoc */
77
+ setup(options) {
78
+ var _a, _b;
79
+ const container = (_a = options === null || options === void 0 ? void 0 : options.container) !== null && _a !== void 0 ? _a : this.createContainer();
80
+ // we've already rendered this container, don't do it again
81
+ if (container === this.containerElement)
82
+ return;
83
+ this.containerElement = container;
84
+ this.grecaptchaLibrary.render(container, {
85
+ callback: this.responseHandler.bind(this),
86
+ 'expired-callback': this.expiredHandler.bind(this),
87
+ 'error-callback': this.errorHandler.bind(this),
88
+ sitekey: this.siteKey,
89
+ tabindex: options === null || options === void 0 ? void 0 : options.tabIndex,
90
+ theme: options === null || options === void 0 ? void 0 : options.theme,
91
+ type: options === null || options === void 0 ? void 0 : options.type,
92
+ size: (_b = options === null || options === void 0 ? void 0 : options.size) !== null && _b !== void 0 ? _b : 'invisible',
93
+ });
94
+ }
95
+ createContainer() {
96
+ const elementId = `recaptchaManager-${this.siteKey}`;
97
+ let element = document.getElementById(elementId);
98
+ if (!element) {
99
+ element = document.createElement('div');
100
+ element.id = elementId;
101
+ element.style.position = 'fixed';
102
+ element.style.top = '50%';
103
+ element.style.left = '50%';
104
+ element.style.outline = '1px solid purple';
105
+ element.style.width = '10px';
106
+ element.style.height = '10px';
107
+ // #recaptcha - TopCollections {
108
+ // position: fixed;
109
+ // top: 50 %;
110
+ // left: 50 %;
111
+ // }
112
+ document.body.insertBefore(element, document.body.firstChild);
113
+ }
114
+ return element;
115
+ }
116
+ finishExecution() {
117
+ this.isExecuting = false;
118
+ this.grecaptchaLibrary.reset();
119
+ }
120
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
121
+ responseHandler(response) {
122
+ if (this.executionSuccessBlock) {
123
+ this.executionSuccessBlock(response);
124
+ this.executionSuccessBlock = undefined;
125
+ }
126
+ }
127
+ expiredHandler() {
128
+ if (this.executionExpiredBlock) {
129
+ this.executionExpiredBlock();
130
+ this.executionExpiredBlock = undefined;
131
+ }
132
+ }
133
+ errorHandler() {
134
+ if (this.executionErrorBlock) {
135
+ this.executionErrorBlock();
136
+ this.executionErrorBlock = undefined;
137
+ }
138
+ }
139
+ }
140
+ //# sourceMappingURL=recaptcha-manager-bak.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recaptcha-manager-bak.js","sourceRoot":"","sources":["../../src/recaptcha-manager-bak.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n LazyLoaderService,\n LazyLoaderServiceInterface,\n} from '@internetarchive/lazy-loader-service';\n\nexport interface RecaptchaManagerInterface {\n /**\n * Executes the recaptcha and returns a token\n *\n * Example:\n *\n * try {\n * const recaptchaResult = await recaptchaManager.execute();\n * console.log('recaptcha token:', recaptchaResult);\n * } catch {\n * console.error('something happened')\n * }\n */\n execute(): Promise<string>;\n\n /**\n * Create the recaptcha widget.\n *\n * If the `container` is not passed in, one will be created.\n *\n * @param options\n */\n setup(options?: {\n container?: HTMLElement;\n tabIndex?: number;\n theme?: ReCaptchaV2.Theme;\n type?: ReCaptchaV2.Type;\n size?: ReCaptchaV2.Size;\n }): void;\n}\n\nexport class RecaptchaManager implements RecaptchaManagerInterface {\n /**\n * This is a convenience initializer that also lazily loads\n * the recaptcha library from Google.\n *\n * @param options\n * @returns\n */\n static async getRecaptchaManager(\n siteKey: string,\n options?: {\n lazyLoader?: LazyLoaderServiceInterface;\n tabIndex?: number;\n theme?: ReCaptchaV2.Theme;\n type?: ReCaptchaV2.Type;\n size?: ReCaptchaV2.Size;\n }\n ): Promise<RecaptchaManagerInterface> {\n const grecaptchaLibrary = await RecaptchaManager.loadRecaptchaLibrary({\n lazyLoader: options?.lazyLoader,\n });\n\n const manager = new RecaptchaManager({\n grecaptchaLibrary,\n siteKey,\n });\n\n manager.setup({\n tabIndex: options?.tabIndex,\n theme: options?.theme,\n type: options?.type,\n size: options?.size,\n });\n\n return manager;\n }\n\n /**\n * Load the Recaptch library from Google.\n *\n * @param options\n * @returns\n */\n static async loadRecaptchaLibrary(options?: {\n lazyLoader?: LazyLoaderServiceInterface;\n }): Promise<ReCaptchaV2.ReCaptcha> {\n const service = options?.lazyLoader ?? new LazyLoaderService();\n\n return new Promise(resolve => {\n (window as any).grecaptchaLoadedCallback = (): void => {\n setTimeout(() => {\n // remove the callback when we're done with it\n delete (window as any).grecaptchaLoadedCallback;\n }, 10);\n resolve(window.grecaptcha);\n };\n\n service.loadScript({\n src: 'https://www.google.com/recaptcha/api.js?onload=grecaptchaLoadedCallback&render=explicit',\n });\n });\n }\n\n private grecaptchaLibrary: ReCaptchaV2.ReCaptcha;\n\n private siteKey: string;\n\n constructor(options: {\n grecaptchaLibrary: ReCaptchaV2.ReCaptcha;\n siteKey: string;\n }) {\n this.grecaptchaLibrary = options.grecaptchaLibrary;\n this.siteKey = options.siteKey;\n }\n\n private executionSuccessBlock?: (token: string) => void;\n\n private executionExpiredBlock?: () => void;\n\n private executionErrorBlock?: () => void;\n\n private isExecuting = false;\n\n private containerElement: HTMLElement | null = null;\n\n /** @inheritdoc */\n execute(): Promise<string> {\n if (this.isExecuting) {\n throw new Error('Recaptcha is already executing');\n }\n\n this.isExecuting = true;\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();\n });\n }\n\n /** @inheritdoc */\n setup(options?: {\n container?: HTMLElement;\n tabIndex?: number;\n theme?: ReCaptchaV2.Theme;\n type?: ReCaptchaV2.Type;\n size?: ReCaptchaV2.Size;\n }): void {\n const container = options?.container ?? this.createContainer();\n\n // we've already rendered this container, don't do it again\n if (container === this.containerElement) return;\n\n this.containerElement = container;\n 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: options?.tabIndex,\n theme: options?.theme,\n type: options?.type,\n size: options?.size ?? 'invisible',\n });\n }\n\n private createContainer(): 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.outline = '1px solid purple';\n element.style.width = '10px';\n element.style.height = '10px';\n // #recaptcha - TopCollections {\n // position: fixed;\n // top: 50 %;\n // left: 50 %;\n // }\n\n document.body.insertBefore(element, document.body.firstChild);\n }\n return element;\n }\n\n private finishExecution(): void {\n this.isExecuting = false;\n this.grecaptchaLibrary.reset();\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private responseHandler(response: any): 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,68 +1,35 @@
1
1
  /// <reference types="grecaptcha" />
2
2
  import { LazyLoaderServiceInterface } from '@internetarchive/lazy-loader-service';
3
+ import { RecaptchaWidget } from './recaptcha-widget';
3
4
  export interface RecaptchaManagerInterface {
4
- execute(): Promise<string>;
5
- setup(container: HTMLElement, tabIndex: number, theme: ReCaptchaV2.Theme, type: ReCaptchaV2.Type): void;
6
- }
7
- export declare class RecaptchaManager implements RecaptchaManagerInterface {
8
- /**
9
- * This is a convenience initializer that also lazily loads
10
- * the recaptcha library from Google.
11
- *
12
- * @param options
13
- * @returns
14
- */
15
- static getRecaptchaManager(options: {
16
- siteKey: string;
17
- }): Promise<RecaptchaManagerInterface>;
18
5
  /**
19
- * Load the Recaptch library from Google.
20
- *
21
- * @param options
22
- * @returns
6
+ * Load a recaptcha widget for a given site key or the default site key.
23
7
  */
24
- static loadRecaptchaLibrary(options?: {
25
- lazyLoader?: LazyLoaderServiceInterface;
26
- }): Promise<ReCaptchaV2.ReCaptcha>;
27
- private grecaptchaLibrary;
28
- private siteKey;
8
+ getRecaptchaWidget(options?: {
9
+ siteKey?: string;
10
+ recaptchaParams?: ReCaptchaV2.Parameters;
11
+ }): Promise<RecaptchaWidget>;
12
+ }
13
+ export declare class RecaptchaManager implements RecaptchaManagerInterface {
14
+ private lazyLoader;
15
+ private defaultSiteKey?;
16
+ private recaptchaCache;
29
17
  constructor(options: {
30
- grecaptchaLibrary: ReCaptchaV2.ReCaptcha;
31
- siteKey: string;
18
+ defaultSiteKey: string;
19
+ lazyLoader?: LazyLoaderServiceInterface;
20
+ grecaptchaLibrary?: ReCaptchaV2.ReCaptcha;
32
21
  });
33
- private executionSuccessBlock?;
34
- private executionExpiredBlock?;
35
- private executionErrorBlock?;
36
- private isExecuting;
22
+ /** @inheritdoc */
23
+ getRecaptchaWidget(options?: {
24
+ siteKey?: string;
25
+ recaptchaParams?: ReCaptchaV2.Parameters;
26
+ }): Promise<RecaptchaWidget>;
37
27
  /**
38
- * Execute Recaptcha and return a Promise containing the response token.
39
- *
40
- * This is an interesting flow.. we call `execute()` here, but have to wait for the
41
- * response and expiration handlers that we bind during the inital `setup` call.
42
- * For consumers, we want to be able to just call `execute()` and wait for a response.
43
- * To allow this, we assign two callbacks:
44
- * - `executionSuccessBlock`
45
- * - `executionExpiredBlock`
46
- *
47
- * We then call those callbacks from inside `responseHandler` and `expiredHandler` to
48
- * either resolve or reject the Promise.
49
- *
50
- * ie:
51
- *
52
- * try {
53
- * const recaptchaResult = await recaptchaManager.execute();
54
- * console.log('recaptcha token:', recaptchaResult);
55
- * } catch {
56
- * console.error('something happened')
57
- * }
28
+ * Load the Recaptch library from Google.
58
29
  *
59
- * @returns {Promise<string>}
60
- * @memberof RecaptchaManager
30
+ * @returns Promise<ReCaptchaV2.ReCaptcha>
61
31
  */
62
- execute(): Promise<string>;
63
- private finishExecution;
64
- setup(container: HTMLElement, tabIndex: number, theme: ReCaptchaV2.Theme, type: ReCaptchaV2.Type): void;
65
- private responseHandler;
66
- private expiredHandler;
67
- private errorHandler;
32
+ private getRecaptchaLibrary;
33
+ /** don't use directly, use `getRecaptchaLibrary()` */
34
+ private grecaptchaLibraryCache?;
68
35
  }
@@ -1,126 +1,53 @@
1
1
  import { LazyLoaderService, } from '@internetarchive/lazy-loader-service';
2
+ import { RecaptchaWidget } from './recaptcha-widget';
2
3
  export class RecaptchaManager {
3
4
  constructor(options) {
4
- this.isExecuting = false;
5
- this.grecaptchaLibrary = options.grecaptchaLibrary;
6
- this.siteKey = options.siteKey;
5
+ var _a;
6
+ this.recaptchaCache = {};
7
+ this.defaultSiteKey = options.defaultSiteKey;
8
+ this.lazyLoader = (_a = options.lazyLoader) !== null && _a !== void 0 ? _a : new LazyLoaderService();
9
+ this.grecaptchaLibraryCache = options.grecaptchaLibrary;
7
10
  }
8
- /**
9
- * This is a convenience initializer that also lazily loads
10
- * the recaptcha library from Google.
11
- *
12
- * @param options
13
- * @returns
14
- */
15
- static async getRecaptchaManager(options) {
16
- const grecaptchaLibrary = await RecaptchaManager.loadRecaptchaLibrary();
17
- return new RecaptchaManager({
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('RecaptchaManager 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,
18
24
  grecaptchaLibrary,
19
- siteKey: options.siteKey,
20
- });
25
+ }, options === null || options === void 0 ? void 0 : options.recaptchaParams);
26
+ this.recaptchaCache[key] = recaptcha;
27
+ return recaptcha;
21
28
  }
22
29
  /**
23
30
  * Load the Recaptch library from Google.
24
31
  *
25
- * @param options
26
- * @returns
32
+ * @returns Promise<ReCaptchaV2.ReCaptcha>
27
33
  */
28
- static async loadRecaptchaLibrary(options) {
29
- var _a;
30
- const service = (_a = options === null || options === void 0 ? void 0 : options.lazyLoader) !== null && _a !== void 0 ? _a : new LazyLoaderService();
34
+ async getRecaptchaLibrary() {
35
+ if (this.grecaptchaLibraryCache) {
36
+ return this.grecaptchaLibraryCache;
37
+ }
31
38
  return new Promise(resolve => {
32
39
  window.grecaptchaLoadedCallback = () => {
33
40
  setTimeout(() => {
34
41
  // remove the callback when we're done with it
35
42
  delete window.grecaptchaLoadedCallback;
36
43
  }, 10);
44
+ this.grecaptchaLibraryCache = window.grecaptcha;
37
45
  resolve(window.grecaptcha);
38
46
  };
39
- service.loadScript({
47
+ this.lazyLoader.loadScript({
40
48
  src: 'https://www.google.com/recaptcha/api.js?onload=grecaptchaLoadedCallback&render=explicit',
41
49
  });
42
50
  });
43
51
  }
44
- /**
45
- * Execute Recaptcha and return a Promise containing the response token.
46
- *
47
- * This is an interesting flow.. we call `execute()` here, but have to wait for the
48
- * response and expiration handlers that we bind during the inital `setup` call.
49
- * For consumers, we want to be able to just call `execute()` and wait for a response.
50
- * To allow this, we assign two callbacks:
51
- * - `executionSuccessBlock`
52
- * - `executionExpiredBlock`
53
- *
54
- * We then call those callbacks from inside `responseHandler` and `expiredHandler` to
55
- * either resolve or reject the Promise.
56
- *
57
- * ie:
58
- *
59
- * try {
60
- * const recaptchaResult = await recaptchaManager.execute();
61
- * console.log('recaptcha token:', recaptchaResult);
62
- * } catch {
63
- * console.error('something happened')
64
- * }
65
- *
66
- * @returns {Promise<string>}
67
- * @memberof RecaptchaManager
68
- */
69
- execute() {
70
- if (this.isExecuting) {
71
- this.finishExecution();
72
- }
73
- this.isExecuting = true;
74
- return new Promise((resolve, reject) => {
75
- this.executionSuccessBlock = (token) => {
76
- this.finishExecution();
77
- resolve(token);
78
- };
79
- this.executionExpiredBlock = () => {
80
- this.finishExecution();
81
- reject(new Error('expired'));
82
- };
83
- this.executionErrorBlock = () => {
84
- this.finishExecution();
85
- reject(new Error('error'));
86
- };
87
- this.grecaptchaLibrary.execute();
88
- });
89
- }
90
- finishExecution() {
91
- this.isExecuting = false;
92
- this.grecaptchaLibrary.reset();
93
- }
94
- setup(container, tabIndex, theme, type) {
95
- this.grecaptchaLibrary.render(container, {
96
- callback: this.responseHandler.bind(this),
97
- 'expired-callback': this.expiredHandler.bind(this),
98
- 'error-callback': this.errorHandler.bind(this),
99
- sitekey: this.siteKey,
100
- tabindex: tabIndex,
101
- theme,
102
- type,
103
- size: 'invisible',
104
- });
105
- }
106
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
- responseHandler(response) {
108
- if (this.executionSuccessBlock) {
109
- this.executionSuccessBlock(response);
110
- this.executionSuccessBlock = undefined;
111
- }
112
- }
113
- expiredHandler() {
114
- if (this.executionExpiredBlock) {
115
- this.executionExpiredBlock();
116
- this.executionExpiredBlock = undefined;
117
- }
118
- }
119
- errorHandler() {
120
- if (this.executionErrorBlock) {
121
- this.executionErrorBlock();
122
- this.executionErrorBlock = undefined;
123
- }
124
- }
125
52
  }
126
53
  //# sourceMappingURL=recaptcha-manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"recaptcha-manager.js","sourceRoot":"","sources":["../../src/recaptcha-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n LazyLoaderService,\n LazyLoaderServiceInterface,\n} from '@internetarchive/lazy-loader-service';\n\nexport interface RecaptchaManagerInterface {\n execute(): Promise<string>;\n setup(\n container: HTMLElement,\n tabIndex: number,\n theme: ReCaptchaV2.Theme,\n type: ReCaptchaV2.Type\n ): void;\n}\n\nexport class RecaptchaManager implements RecaptchaManagerInterface {\n /**\n * This is a convenience initializer that also lazily loads\n * the recaptcha library from Google.\n *\n * @param options\n * @returns\n */\n static async getRecaptchaManager(options: {\n siteKey: string;\n }): Promise<RecaptchaManagerInterface> {\n const grecaptchaLibrary = await RecaptchaManager.loadRecaptchaLibrary();\n return new RecaptchaManager({\n grecaptchaLibrary,\n siteKey: options.siteKey,\n });\n }\n\n /**\n * Load the Recaptch library from Google.\n *\n * @param options\n * @returns\n */\n static async loadRecaptchaLibrary(options?: {\n lazyLoader?: LazyLoaderServiceInterface;\n }): Promise<ReCaptchaV2.ReCaptcha> {\n const service = options?.lazyLoader ?? new LazyLoaderService();\n\n return new Promise(resolve => {\n (window as any).grecaptchaLoadedCallback = (): void => {\n setTimeout(() => {\n // remove the callback when we're done with it\n delete (window as any).grecaptchaLoadedCallback;\n }, 10);\n resolve(window.grecaptcha);\n };\n\n service.loadScript({\n src: 'https://www.google.com/recaptcha/api.js?onload=grecaptchaLoadedCallback&render=explicit',\n });\n });\n }\n\n private grecaptchaLibrary: ReCaptchaV2.ReCaptcha;\n\n private siteKey: string;\n\n constructor(options: {\n grecaptchaLibrary: ReCaptchaV2.ReCaptcha;\n siteKey: string;\n }) {\n this.grecaptchaLibrary = options.grecaptchaLibrary;\n this.siteKey = options.siteKey;\n }\n\n private executionSuccessBlock?: (token: string) => void;\n\n private executionExpiredBlock?: () => void;\n\n private executionErrorBlock?: () => void;\n\n private isExecuting = false;\n\n /**\n * Execute Recaptcha and return a Promise containing the response token.\n *\n * This is an interesting flow.. we call `execute()` here, but have to wait for the\n * response and expiration handlers that we bind during the inital `setup` call.\n * For consumers, we want to be able to just call `execute()` and wait for a response.\n * To allow this, we assign two callbacks:\n * - `executionSuccessBlock`\n * - `executionExpiredBlock`\n *\n * We then call those callbacks from inside `responseHandler` and `expiredHandler` to\n * either resolve or reject the Promise.\n *\n * ie:\n *\n * try {\n * const recaptchaResult = await recaptchaManager.execute();\n * console.log('recaptcha token:', recaptchaResult);\n * } catch {\n * console.error('something happened')\n * }\n *\n * @returns {Promise<string>}\n * @memberof RecaptchaManager\n */\n execute(): Promise<string> {\n if (this.isExecuting) {\n this.finishExecution();\n }\n this.isExecuting = true;\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();\n });\n }\n\n private finishExecution(): void {\n this.isExecuting = false;\n this.grecaptchaLibrary.reset();\n }\n\n setup(\n container: HTMLElement,\n tabIndex: number,\n theme: ReCaptchaV2.Theme,\n type: ReCaptchaV2.Type\n ): void {\n 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: tabIndex,\n theme,\n type,\n size: 'invisible',\n });\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private responseHandler(response: any): 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
+ {"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,EAAE,MAAM,oBAAoB,CAAC;AAYrD,MAAM,OAAO,gBAAgB;IAO3B,YAAY,OAIX;;QANO,mBAAc,GAAoC,EAAE,CAAC;QAO3D,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAC7C,IAAI,CAAC,UAAU,GAAG,MAAA,OAAO,CAAC,UAAU,mCAAI,IAAI,iBAAiB,EAAE,CAAC;QAChE,IAAI,CAAC,sBAAsB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAC1D,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;YACR,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;SACzD;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;YAC/B,OAAO,IAAI,CAAC,sBAAsB,CAAC;SACpC;QACD,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;YAC1B,MAAc,CAAC,wBAAwB,GAAG,GAAS,EAAE;gBACpD,UAAU,CAAC,GAAG,EAAE;oBACd,8CAA8C;oBAC9C,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 } 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<RecaptchaWidget>;\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<RecaptchaWidget> {\n const key = options?.siteKey ?? this.defaultSiteKey;\n if (!key) {\n throw new Error('RecaptchaManager 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 (window as any).grecaptchaLoadedCallback = (): void => {\n setTimeout(() => {\n // remove the callback when we're done with it\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"]}
@@ -0,0 +1,33 @@
1
+ /// <reference types="grecaptcha" />
2
+ /**
3
+ * This encompasses a widget for a given site key.
4
+ */
5
+ export interface RecaptchaWidgetInterface {
6
+ /** execute the recaptcha request and returns a token */
7
+ execute(): Promise<string>;
8
+ }
9
+ /**
10
+ * This encompasses a widget for a given site key.
11
+ */
12
+ export declare class RecaptchaWidget implements RecaptchaWidgetInterface {
13
+ private executionSuccessBlock?;
14
+ private executionExpiredBlock?;
15
+ private executionErrorBlock?;
16
+ private grecaptchaLibrary;
17
+ private siteKey;
18
+ private widgetId;
19
+ private isExecuting;
20
+ constructor(config: {
21
+ siteKey: string;
22
+ grecaptchaLibrary: ReCaptchaV2.ReCaptcha;
23
+ }, recaptchaParams?: ReCaptchaV2.Parameters);
24
+ /** @inheritdoc */
25
+ execute(): Promise<string>;
26
+ private finishExecution;
27
+ /** @inheritdoc */
28
+ private setup;
29
+ private createContainer;
30
+ private responseHandler;
31
+ private expiredHandler;
32
+ private errorHandler;
33
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * This encompasses a widget for a given site key.
3
+ */
4
+ export class RecaptchaWidget {
5
+ constructor(config, recaptchaParams) {
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, recaptchaParams);
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
+ return new Promise((resolve, reject) => {
26
+ this.executionSuccessBlock = (token) => {
27
+ this.finishExecution();
28
+ resolve(token);
29
+ };
30
+ this.executionExpiredBlock = () => {
31
+ this.finishExecution();
32
+ reject(new Error('expired'));
33
+ };
34
+ this.executionErrorBlock = () => {
35
+ this.finishExecution();
36
+ reject(new Error('error'));
37
+ };
38
+ this.grecaptchaLibrary.execute(widgetId);
39
+ });
40
+ }
41
+ finishExecution() {
42
+ this.isExecuting = false;
43
+ const { widgetId } = this;
44
+ if (widgetId === null)
45
+ return;
46
+ this.grecaptchaLibrary.reset(widgetId);
47
+ }
48
+ /** @inheritdoc */
49
+ setup(container, recaptchaParams) {
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: recaptchaParams === null || recaptchaParams === void 0 ? void 0 : recaptchaParams.tabindex,
57
+ theme: recaptchaParams === null || recaptchaParams === void 0 ? void 0 : recaptchaParams.theme,
58
+ type: recaptchaParams === null || recaptchaParams === void 0 ? void 0 : recaptchaParams.type,
59
+ size: (_a = recaptchaParams === null || recaptchaParams === void 0 ? void 0 : recaptchaParams.size) !== null && _a !== void 0 ? _a : 'invisible',
60
+ });
61
+ }
62
+ createContainer() {
63
+ const elementId = `recaptchaManager-${this.siteKey}`;
64
+ let element = document.getElementById(elementId);
65
+ if (!element) {
66
+ element = document.createElement('div');
67
+ element.id = elementId;
68
+ element.style.position = 'fixed';
69
+ element.style.top = '50%';
70
+ element.style.left = '50%';
71
+ document.body.appendChild(element);
72
+ }
73
+ return element;
74
+ }
75
+ responseHandler(response) {
76
+ if (this.executionSuccessBlock) {
77
+ this.executionSuccessBlock(response);
78
+ this.executionSuccessBlock = undefined;
79
+ }
80
+ }
81
+ expiredHandler() {
82
+ if (this.executionExpiredBlock) {
83
+ this.executionExpiredBlock();
84
+ this.executionExpiredBlock = undefined;
85
+ }
86
+ }
87
+ errorHandler() {
88
+ if (this.executionErrorBlock) {
89
+ this.executionErrorBlock();
90
+ this.executionErrorBlock = undefined;
91
+ }
92
+ }
93
+ }
94
+ //# sourceMappingURL=recaptcha-widget.js.map
@@ -0,0 +1 @@
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 * 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 recaptchaParams?: ReCaptchaV2.Parameters\n ) {\n this.siteKey = config.siteKey;\n this.grecaptchaLibrary = config.grecaptchaLibrary;\n\n const container = this.createContainer();\n this.setup(container, recaptchaParams);\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 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 /** @inheritdoc */\n private setup(\n container: HTMLElement,\n recaptchaParams?: 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: recaptchaParams?.tabindex,\n theme: recaptchaParams?.theme,\n type: recaptchaParams?.type,\n size: recaptchaParams?.size ?? 'invisible',\n });\n }\n\n private createContainer(): 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 document.body.appendChild(element);\n }\n return element;\n }\n\n private responseHandler(response: any): 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"]}
@@ -0,0 +1,28 @@
1
+ /// <reference types="grecaptcha" />
2
+ export interface RecaptchaWidgetInterface {
3
+ /** execute the recaptcha request and returns a token */
4
+ execute(): Promise<string>;
5
+ }
6
+ /**
7
+ * This encompasses a widget for a given site key.
8
+ */
9
+ export declare class RecaptchaWidget implements RecaptchaWidgetInterface {
10
+ private executionSuccessBlock?;
11
+ private executionExpiredBlock?;
12
+ private executionErrorBlock?;
13
+ private grecaptchaLibrary;
14
+ private siteKey;
15
+ private widgetId;
16
+ constructor(config: {
17
+ siteKey: string;
18
+ grecaptchaLibrary: ReCaptchaV2.ReCaptcha;
19
+ }, recaptchaParams?: ReCaptchaV2.Parameters);
20
+ /** @inheritdoc */
21
+ execute(): Promise<string>;
22
+ /** @inheritdoc */
23
+ private setup;
24
+ private createContainer;
25
+ private responseHandler;
26
+ private expiredHandler;
27
+ private errorHandler;
28
+ }