ngx-recaptcha-v3 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/CHANGELOG.md +0 -0
- package/README.md +275 -0
- package/package.json +83 -0
package/CHANGELOG.md
ADDED
|
File without changes
|
package/README.md
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# ngx-recaptcha-v3
|
|
2
|
+
|
|
3
|
+
A production-grade, enterprise-ready, tree-shakable Angular library for integrating Google reCAPTCHA v2 (Checkbox & Invisible), v3, and Enterprise. Fully compatible with Angular 12 through 22, SSR‑safe (Universal), and native Angular Signals‑ready.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Key Features
|
|
8
|
+
|
|
9
|
+
- **Full Angular Compatibility**: Out‑of‑the‑box support for Angular 12 to 22.
|
|
10
|
+
- **First‑Class Signals Support**: Decoupled `ngx-recaptcha-v3/signals` entry point for modern reactive applications (Angular 16+).
|
|
11
|
+
- **SSR & Hydration Safe**: Strictly guards DOM operations on the server side using `isPlatformBrowser`.
|
|
12
|
+
- **Zoneless Support**: Executes heavy execution logic outside `Zone.js` context for high performance.
|
|
13
|
+
- **Dynamic Re‑Render**: V2 widget automatically re‑renders when inputs (theme, size, tabIndex, siteKey) change! No manual cleanup required!
|
|
14
|
+
- **Tree‑Shakable Secondary Entry Points**: Only import what you use (`core`, `v2`, `v3`, `enterprise`, `signals`, `forms`, `testing`).
|
|
15
|
+
- **ControlValueAccessor (CVA)**: Native support for Reactive Forms and Template‑driven Forms.
|
|
16
|
+
- **Region & Domain Flex**: Easily switch between `google.com` and `recaptcha.net` (for restricted regions).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Folder & Entrypoint Architecture
|
|
21
|
+
|
|
22
|
+
To avoid breaking compilation in legacy Angular environments (versions 12–15) that do not support Signals, the codebase utilizes isolated secondary entry points:
|
|
23
|
+
|
|
24
|
+
- `ngx-recaptcha-v3/core`: Script loading lifecycle, dynamic configuration tokens.
|
|
25
|
+
- `ngx-recaptcha-v3/v2`: V2 Checkbox and Invisible component.
|
|
26
|
+
- `ngx-recaptcha-v3/v3`: Score‑based execution service.
|
|
27
|
+
- `ngx-recaptcha-v3/enterprise`: Score‑based Enterprise service.
|
|
28
|
+
- `ngx-recaptcha-v3/forms`: Angular Forms ControlValueAccessor directive.
|
|
29
|
+
- `ngx-recaptcha-v3/signals`: Reactive signals wrapper (Angular 16+ only).
|
|
30
|
+
- `ngx-recaptcha-v3/testing`: Mock services and providers for Jest/Karma test isolation.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install ngx-recaptcha-v3
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Configure your global settings during the application bootstrap phase:
|
|
41
|
+
|
|
42
|
+
### Standard Module Bootstrapping (Angular 12+)
|
|
43
|
+
```typescript
|
|
44
|
+
import { NgModule } from '@angular/core';
|
|
45
|
+
import { BrowserModule } from '@angular/platform-browser';
|
|
46
|
+
import { RECAPTCHA_CONFIG, RecaptchaConfig } from 'ngx-recaptcha-v3/core';
|
|
47
|
+
import { AppComponent } from './app.component';
|
|
48
|
+
|
|
49
|
+
const recaptchaConfig: RecaptchaConfig = {
|
|
50
|
+
v2SiteKey: 'YOUR_V2_SITE_KEY',
|
|
51
|
+
v3SiteKey: 'YOUR_V3_SITE_KEY',
|
|
52
|
+
recaptchaDomain: 'google.com'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
@NgModule({
|
|
56
|
+
declarations: [AppComponent],
|
|
57
|
+
imports: [BrowserModule],
|
|
58
|
+
providers: [
|
|
59
|
+
{ provide: RECAPTCHA_CONFIG, useValue: recaptchaConfig }
|
|
60
|
+
],
|
|
61
|
+
bootstrap: [AppComponent]
|
|
62
|
+
})
|
|
63
|
+
export class AppModule {}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Standalone Bootstrapping (Angular 14+)
|
|
67
|
+
```typescript
|
|
68
|
+
import { bootstrapApplication } from '@angular/platform-browser';
|
|
69
|
+
import { RECAPTCHA_CONFIG, RecaptchaConfig } from 'ngx-recaptcha-v3/core';
|
|
70
|
+
import { AppComponent } from './app/app.component';
|
|
71
|
+
|
|
72
|
+
bootstrapApplication(AppComponent, {
|
|
73
|
+
providers: [
|
|
74
|
+
{
|
|
75
|
+
provide: RECAPTCHA_CONFIG,
|
|
76
|
+
useValue: {
|
|
77
|
+
v2SiteKey: 'YOUR_V2_SITE_KEY',
|
|
78
|
+
v3SiteKey: 'YOUR_V3_SITE_KEY',
|
|
79
|
+
useEnterprise: false
|
|
80
|
+
} as RecaptchaConfig
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Usage Examples
|
|
89
|
+
|
|
90
|
+
### 1. reCAPTCHA v2 Checkbox with Reactive Forms
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { Component, signal } from '@angular/core';
|
|
94
|
+
import { FormGroup, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
|
|
95
|
+
import { RecaptchaV2Component } from 'ngx-recaptcha-v3/v2';
|
|
96
|
+
import { RecaptchaValueAccessorDirective } from 'ngx-recaptcha-v3/forms';
|
|
97
|
+
|
|
98
|
+
@Component({
|
|
99
|
+
selector: 'app-login',
|
|
100
|
+
template: `
|
|
101
|
+
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
|
|
102
|
+
<input formControlName="username" type="text" placeholder="Username" />
|
|
103
|
+
|
|
104
|
+
<!-- Toggle theme to test dynamic re-render! -->
|
|
105
|
+
<div style="margin: 1rem 0;">
|
|
106
|
+
<label style="margin-right: 1rem;">
|
|
107
|
+
<input type="radio" [value]="'light'" (change)="setTheme('light')" [checked]="theme() === 'light'"> Light
|
|
108
|
+
</label>
|
|
109
|
+
<label>
|
|
110
|
+
<input type="radio" [value]="'dark'" (change)="setTheme('dark')" [checked]="theme() === 'dark'"> Dark
|
|
111
|
+
</label>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<!-- CVA Directive Automatically Binds value to FormGroup -->
|
|
115
|
+
<ngx-recaptcha-v3
|
|
116
|
+
[siteKey]="'YOUR_V2_SITE_KEY'"
|
|
117
|
+
[theme]="theme()"
|
|
118
|
+
formControlName="recaptcha">
|
|
119
|
+
</ngx-recaptcha-v3>
|
|
120
|
+
|
|
121
|
+
<button [disabled]="loginForm.invalid" type="submit">Login</button>
|
|
122
|
+
</form>
|
|
123
|
+
`,
|
|
124
|
+
standalone: true,
|
|
125
|
+
imports: [ReactiveFormsModule, RecaptchaV2Component, RecaptchaValueAccessorDirective]
|
|
126
|
+
})
|
|
127
|
+
export class LoginComponent {
|
|
128
|
+
theme = signal<'light' | 'dark'>('light');
|
|
129
|
+
|
|
130
|
+
loginForm = new FormGroup({
|
|
131
|
+
username: new FormControl('', Validators.required),
|
|
132
|
+
recaptcha: new FormControl(null, Validators.required)
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
setTheme(newTheme: 'light' | 'dark'): void {
|
|
136
|
+
this.theme.set(newTheme);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
onSubmit() {
|
|
140
|
+
console.log(this.loginForm.value);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Dynamic Re‑Render Feature**: `RecaptchaV2Component` now supports automatic re‑rendering when any of its inputs (siteKey, theme, size, tabIndex) change! This means you can dynamically switch between light/dark themes, or normal/compact/invisible sizes at runtime without any manual cleanup!
|
|
146
|
+
|
|
147
|
+
### 2. reCAPTCHA v3 Execution Service (RxJS & Promise)
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { Component, inject } from '@angular/core';
|
|
151
|
+
import { RecaptchaV3Service } from 'ngx-recaptcha-v3/v3';
|
|
152
|
+
|
|
153
|
+
@Component({
|
|
154
|
+
selector: 'app-payment',
|
|
155
|
+
template: `
|
|
156
|
+
<div style="display: flex; gap: 1rem;">
|
|
157
|
+
<button (click)="processPaymentRxjs()">Pay Now (RxJS)</button>
|
|
158
|
+
<button (click)="processPaymentAsync()">Pay Now (Async/Await)</button>
|
|
159
|
+
</div>
|
|
160
|
+
`,
|
|
161
|
+
standalone: true
|
|
162
|
+
})
|
|
163
|
+
export class PaymentComponent {
|
|
164
|
+
private recaptchaV3 = inject(RecaptchaV3Service);
|
|
165
|
+
|
|
166
|
+
processPaymentRxjs(): void {
|
|
167
|
+
this.recaptchaV3.execute('checkout').subscribe({
|
|
168
|
+
next: (token) => {
|
|
169
|
+
// Send token to backend API for verification assessment
|
|
170
|
+
console.log('Action token resolved (RxJS):', token);
|
|
171
|
+
},
|
|
172
|
+
error: (err) => {
|
|
173
|
+
console.error('Resolution error (RxJS):', err);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async processPaymentAsync(): Promise<void> {
|
|
179
|
+
try {
|
|
180
|
+
const token = await this.recaptchaV3.executeAsync('checkout');
|
|
181
|
+
console.log('Action token resolved (Async/Await):', token);
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.error('Resolution error (Async/Await):', err);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### 3. Reactive Signals Integration (Angular 16+)
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
import { Component, effect, inject } from '@angular/core';
|
|
193
|
+
import { RecaptchaSignalService } from 'ngx-recaptcha-v3/signals';
|
|
194
|
+
import { RecaptchaV2Component } from 'ngx-recaptcha-v3/v2';
|
|
195
|
+
|
|
196
|
+
@Component({
|
|
197
|
+
selector: 'app-signals-page',
|
|
198
|
+
template: `
|
|
199
|
+
<ngx-recaptcha-v3
|
|
200
|
+
[siteKey]="'YOUR_V2_SITE_KEY'"
|
|
201
|
+
(resolved)="recaptchaSignals.setToken($event)"
|
|
202
|
+
(error)="recaptchaSignals.setError('Load Error occurred')">
|
|
203
|
+
</ngx-recaptcha-v3>
|
|
204
|
+
|
|
205
|
+
@if (recaptchaSignals.verified()) {
|
|
206
|
+
<p>Verification successful! Token: {{ recaptchaSignals.token() }}</p>
|
|
207
|
+
}
|
|
208
|
+
`,
|
|
209
|
+
standalone: true,
|
|
210
|
+
imports: [RecaptchaV2Component]
|
|
211
|
+
})
|
|
212
|
+
export class SignalsPageComponent {
|
|
213
|
+
recaptchaSignals = inject(RecaptchaSignalService);
|
|
214
|
+
|
|
215
|
+
constructor() {
|
|
216
|
+
// Intercept state reactively
|
|
217
|
+
effect(() => {
|
|
218
|
+
if (this.recaptchaSignals.verified()) {
|
|
219
|
+
console.log('User verified with token:', this.recaptchaSignals.token());
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## SSR (Server‑Side Rendering) Support
|
|
229
|
+
The library contains native safety guards. When rendering on a server environment, the script loading will gracefully bypass DOM manipulation and execute empty mock resolutions. This ensures standard universal/SSR pipelines do not trigger `window` or `document` reference execution errors.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Unit Testing Setup
|
|
234
|
+
|
|
235
|
+
The library provides testing mocks under the `/testing` sub‑entrypoint.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
import { TestBed } from '@angular/core/testing';
|
|
239
|
+
import { RecaptchaV3Service } from 'ngx-recaptcha-v3/v3';
|
|
240
|
+
import { RecaptchaMockV3Service } from 'ngx-recaptcha-v3/testing';
|
|
241
|
+
|
|
242
|
+
describe('MyService', () => {
|
|
243
|
+
beforeEach(() => {
|
|
244
|
+
TestBed.configureTestingModule({
|
|
245
|
+
providers: [
|
|
246
|
+
{ provide: RecaptchaV3Service, useClass: RecaptchaMockV3Service }
|
|
247
|
+
]
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Your unit tests...
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Migration Guide (From `ng-recaptcha`)
|
|
258
|
+
|
|
259
|
+
If migrating from legacy packages like `ng-recaptcha`:
|
|
260
|
+
|
|
261
|
+
1. Remove old configurations and modules from your app imports.
|
|
262
|
+
2. Update imports from `ng-recaptcha` to `ngx-recaptcha-v3/v2`, `ngx-recaptcha-v3/v3`, or `ngx-recaptcha-v3/core`.
|
|
263
|
+
3. Provide settings using the unified configuration injection token: `RECAPTCHA_CONFIG`.
|
|
264
|
+
4. Replace event listeners `(resolved)` with signals using `RecaptchaSignalService` if using Angular 16+.
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## FAQ & Troubleshooting
|
|
269
|
+
|
|
270
|
+
### Why is my reCAPTCHA script not loading?
|
|
271
|
+
- Double‑check that you provided `v2SiteKey` or `v3SiteKey` inside the `RECAPTCHA_CONFIG` InjectionToken.
|
|
272
|
+
- Verify that your network has direct access to `google.com` or switch config settings to use `recaptchaDomain: 'recaptcha.net'`.
|
|
273
|
+
|
|
274
|
+
### Is it compatible with Angular Zoneless mode?
|
|
275
|
+
Yes. The library is built with zoneless capability, utilizing custom element zones via `NgZone.runOutsideAngular` to handle script callbacks without triggering change detection cycles redundantly.
|
package/package.json
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ngx-recaptcha-v3",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A modern, standalone Google reCAPTCHA library for Angular supporting v2, v3, enterprise, reactive forms, and signals. Fully compatible with Angular versions 12–22",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/Tapdiq49/ngx-recaptcha-v3"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/Tapdiq49/ngx-recaptcha-v3/blob/main/README.md",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"angular",
|
|
12
|
+
"recaptcha",
|
|
13
|
+
"google-recaptcha",
|
|
14
|
+
"ng-recaptcha",
|
|
15
|
+
"captcha",
|
|
16
|
+
"recaptcha-v2",
|
|
17
|
+
"recaptcha-v3",
|
|
18
|
+
"recaptcha-enterprise",
|
|
19
|
+
"angular-forms",
|
|
20
|
+
"control-value-accessor",
|
|
21
|
+
"angular-signals"
|
|
22
|
+
],
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@angular/common": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
25
|
+
"@angular/core": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0",
|
|
26
|
+
"@angular/forms": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0 || ^22.0.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependenciesMeta": {
|
|
29
|
+
"@angular/forms": {
|
|
30
|
+
"optional": true
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"tslib": "^2.3.0"
|
|
35
|
+
},
|
|
36
|
+
"author": "",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"files": [
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md"
|
|
41
|
+
],
|
|
42
|
+
"module": "fesm2022/ngx-recaptcha-v3.mjs",
|
|
43
|
+
"typings": "types/ngx-recaptcha-v3.d.ts",
|
|
44
|
+
"exports": {
|
|
45
|
+
"./package.json": {
|
|
46
|
+
"default": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
".": {
|
|
49
|
+
"types": "./types/ngx-recaptcha-v3.d.ts",
|
|
50
|
+
"default": "./fesm2022/ngx-recaptcha-v3.mjs"
|
|
51
|
+
},
|
|
52
|
+
"./core": {
|
|
53
|
+
"types": "./types/ngx-recaptcha-v3-core.d.ts",
|
|
54
|
+
"default": "./fesm2022/ngx-recaptcha-v3-core.mjs"
|
|
55
|
+
},
|
|
56
|
+
"./enterprise": {
|
|
57
|
+
"types": "./types/ngx-recaptcha-v3-enterprise.d.ts",
|
|
58
|
+
"default": "./fesm2022/ngx-recaptcha-v3-enterprise.mjs"
|
|
59
|
+
},
|
|
60
|
+
"./forms": {
|
|
61
|
+
"types": "./types/ngx-recaptcha-v3-forms.d.ts",
|
|
62
|
+
"default": "./fesm2022/ngx-recaptcha-v3-forms.mjs"
|
|
63
|
+
},
|
|
64
|
+
"./signals": {
|
|
65
|
+
"types": "./types/ngx-recaptcha-v3-signals.d.ts",
|
|
66
|
+
"default": "./fesm2022/ngx-recaptcha-v3-signals.mjs"
|
|
67
|
+
},
|
|
68
|
+
"./testing": {
|
|
69
|
+
"types": "./types/ngx-recaptcha-v3-testing.d.ts",
|
|
70
|
+
"default": "./fesm2022/ngx-recaptcha-v3-testing.mjs"
|
|
71
|
+
},
|
|
72
|
+
"./v2": {
|
|
73
|
+
"types": "./types/ngx-recaptcha-v3-v2.d.ts",
|
|
74
|
+
"default": "./fesm2022/ngx-recaptcha-v3-v2.mjs"
|
|
75
|
+
},
|
|
76
|
+
"./v3": {
|
|
77
|
+
"types": "./types/ngx-recaptcha-v3-v3.d.ts",
|
|
78
|
+
"default": "./fesm2022/ngx-recaptcha-v3-v3.mjs"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"sideEffects": false,
|
|
82
|
+
"type": "module"
|
|
83
|
+
}
|