@adobe-commerce/recaptcha 1.2.2 → 1.3.0-alpha-20260828142254
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 +175 -65
- package/README.md +63 -31
- package/eslint.config.js +3 -1
- package/package.json +1 -1
- package/src/configs/index.ts +3 -3
- package/src/configs/message.config.ts +4 -4
- package/src/configs/recaptchaBadgeSelector.config.ts +4 -4
- package/src/configs/typeForms.config.ts +28 -4
- package/src/graphql/recaptchaConfig.graphql.ts +5 -7
- package/src/index.ts +41 -27
- package/src/lib/_checkRecaptchaBadge.ts +3 -3
- package/src/lib/_convertKeysToCamelCase.ts +11 -8
- package/src/lib/_extendConfig.ts +8 -6
- package/src/lib/_storageConfig.ts +5 -5
- package/src/lib/index.ts +3 -3
- package/src/services/recaptcha.service.ts +7 -10
- package/src/types/recaptcha.types.ts +8 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @adobe-commerce/recaptcha
|
|
2
2
|
|
|
3
|
+
## 1.3.0-alpha-20260828142254
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 896c6c9: reCAPTCHA form types are now configurable via feature flag.
|
|
8
|
+
COMPANY_CREATE is no longer in the default form-type map. B2B storefronts pass
|
|
9
|
+
`{ b2bEnabled: true }` to `setConfig()` to include it in the configuration
|
|
10
|
+
query.
|
|
11
|
+
|
|
3
12
|
## 1.2.2
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
|
@@ -10,54 +19,89 @@
|
|
|
10
19
|
|
|
11
20
|
### Patch Changes
|
|
12
21
|
|
|
13
|
-
- 24d7ffb: Add `COMPANY_CREATE` to the default reCAPTCHA form map for B2B
|
|
22
|
+
- 24d7ffb: Add `COMPANY_CREATE` to the default reCAPTCHA form map for B2B
|
|
23
|
+
company registration (`createCompany` badge id).
|
|
14
24
|
|
|
15
|
-
Global `setConfig()` now batches `recaptchaFormConfig` for company create
|
|
25
|
+
Global `setConfig()` now batches `recaptchaFormConfig` for company create
|
|
26
|
+
alongside other storefront forms.
|
|
16
27
|
|
|
17
28
|
- 9cd299b: Upgrade TypeScript to 6.0 and ESLint to 9 (flat config)
|
|
18
29
|
|
|
19
30
|
## What changed
|
|
20
31
|
|
|
21
32
|
### TypeScript 4.7 → 6.0
|
|
22
|
-
- `tsconfig-base.json` updated with correct TS 6 defaults. Two new-default
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
- `
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
33
|
+
- `tsconfig-base.json` updated with correct TS 6 defaults. Two new-default
|
|
34
|
+
opt-outs are deferred as tech debt: `exactOptionalPropertyTypes` and
|
|
35
|
+
`verbatimModuleSyntax`.
|
|
36
|
+
- `moduleResolution` changed from `"nodenext"` to `"bundler"` across all
|
|
37
|
+
packages — the correct pairing for `module: "esnext"` in a Vite monorepo.
|
|
38
|
+
Only `packages/elsie` keeps `"NodeNext"` (paired with `module: "NodeNext"`
|
|
39
|
+
for its dual CJS/ESM output).
|
|
40
|
+
- `baseUrl` removed from all tsconfigs (deprecated in TS 6; `paths` resolves
|
|
41
|
+
relative to the tsconfig file directly).
|
|
42
|
+
- `rootDir` added explicitly to `build-tools`, `event-bus`, `fetch-graphql`,
|
|
43
|
+
and `recaptcha` tsconfigs (implicit `rootDir` deprecated in TS 6).
|
|
44
|
+
- `types: []` set in `tsconfig-base.json` to prevent ambient test types from
|
|
45
|
+
leaking into declaration output. Each package's own `tsconfig.json` declares
|
|
46
|
+
its `types` explicitly (`jest`, `node`, `vite/client`, etc.).
|
|
47
|
+
- `babel-plugin-tsconfig-paths` removed — it was a no-op in every package that
|
|
48
|
+
listed it.
|
|
49
|
+
- `noUncheckedIndexedAccess` enabled — violations were few enough to fix in
|
|
50
|
+
source.
|
|
29
51
|
|
|
30
52
|
### ESLint 8 → 9 (flat config)
|
|
31
|
-
- `@typescript-eslint/parser` and `@typescript-eslint/eslint-plugin` (v5)
|
|
53
|
+
- `@typescript-eslint/parser` and `@typescript-eslint/eslint-plugin` (v5)
|
|
54
|
+
removed; replaced by the unified `typescript-eslint` v8 package.
|
|
32
55
|
- `eslint-config-preact` bumped to `^2.0.0` (ESLint 9 support).
|
|
33
56
|
- `eslint-config-prettier` bumped to `^10.0.0`.
|
|
34
|
-
- `eslint-plugin-mdx` bumped to `^3.8.1`. **ESLint 10 is not supported** —
|
|
57
|
+
- `eslint-plugin-mdx` bumped to `^3.8.1`. **ESLint 10 is not supported** —
|
|
58
|
+
`eslint-plugin-mdx` vendors an internal ESLint API removed in v10; ESLint 9
|
|
59
|
+
is pinned until a fix is released.
|
|
35
60
|
- `globals` added (`^15.0.0`) for `languageOptions.globals` in flat config.
|
|
36
|
-
- Shared config (`packages/elsie/config/eslint.mjs`) rewritten as a
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
61
|
+
- Shared config (`packages/elsie/config/eslint.mjs`) rewritten as a
|
|
62
|
+
flat-config array export.
|
|
63
|
+
- Per-package `.eslintrc.js` files deleted; replaced with `eslint.config.js`
|
|
64
|
+
(ESM flat config).
|
|
65
|
+
- Several `typescript-eslint` v8 rules disabled to preserve prior behavior:
|
|
66
|
+
`no-explicit-any`, `ban-ts-comment`, `no-unused-expressions`,
|
|
67
|
+
`no-unsafe-function-type`, `no-require-imports`, `no-empty-object-type`.
|
|
68
|
+
Tracked as tech debt.
|
|
69
|
+
- `reportUnusedDisableDirectives` disabled to avoid sweeping pre-existing
|
|
70
|
+
inline disable comments.
|
|
40
71
|
|
|
41
72
|
### `vite-plugin-dts` removed; replaced with custom `dtsPlugin`
|
|
42
73
|
|
|
43
|
-
`vite-plugin-dts@3.9.1` declared a `typescript <5.0` peer range and its v5
|
|
74
|
+
`vite-plugin-dts@3.9.1` declared a `typescript <5.0` peer range and its v5
|
|
75
|
+
successor was incompatible with this monorepo's layout (cross-package sources,
|
|
76
|
+
workspace symlinks, multi-`outDir`). It was removed and replaced with a thin
|
|
77
|
+
custom Vite plugin at `packages/elsie/config/plugins/dts.mjs` (`dtsPlugin`)
|
|
78
|
+
that runs `tsc` directly. The resulting `dist/` layout is identical to what
|
|
79
|
+
v3.9 produced (147 `.d.ts` files, 12 top-level entry shims).
|
|
44
80
|
|
|
45
81
|
### Module format standardization
|
|
46
82
|
|
|
47
83
|
All tooling config files now follow a consistent format:
|
|
48
84
|
|
|
49
|
-
- **ESLint / Jest / Vite configs** — ESM (`.js` in `"type":"module"` packages;
|
|
50
|
-
|
|
85
|
+
- **ESLint / Jest / Vite configs** — ESM (`.js` in `"type":"module"` packages;
|
|
86
|
+
`.mjs` in elsie).
|
|
87
|
+
- **Prettier** — JSON only (`@adobe-commerce/elsie/config/prettier.json`
|
|
88
|
+
referenced via each package's `"prettier"` key). `prettier.config.*` files
|
|
89
|
+
deleted from all packages.
|
|
51
90
|
- **elsie CLI** (`bin/**`) — intentionally stays CommonJS.
|
|
52
91
|
|
|
53
92
|
See `architecture/decisions/009-module-format.md` for the full convention.
|
|
54
93
|
|
|
55
94
|
### `.elsie.js` → `.elsie.cjs`
|
|
56
95
|
|
|
57
|
-
The consumer project config file is renamed from `.elsie.js` to `.elsie.cjs`.
|
|
96
|
+
The consumer project config file is renamed from `.elsie.js` to `.elsie.cjs`.
|
|
97
|
+
With `"type":"module"` now required in consumer packages, a plain `.js` file
|
|
98
|
+
is treated as ES module — making `module.exports` a SyntaxError and making
|
|
99
|
+
`require()` in the elsie CLI fail with `ERR_REQUIRE_ESM`. The `.cjs` extension
|
|
100
|
+
forces CommonJS regardless of the package's `"type"` field.
|
|
58
101
|
|
|
59
102
|
- `elsie generate config` now writes `.elsie.cjs`.
|
|
60
|
-
- The CLI (`bin/lib/config.js`) and `config/vite.mjs` both prefer `.elsie.cjs`
|
|
103
|
+
- The CLI (`bin/lib/config.js`) and `config/vite.mjs` both prefer `.elsie.cjs`
|
|
104
|
+
and fall back to `.elsie.js` for packages not yet migrated.
|
|
61
105
|
- `.elsie.cjs` added to `.npmignore` in all consumer packages.
|
|
62
106
|
|
|
63
107
|
## Consumer migration
|
|
@@ -65,12 +109,16 @@
|
|
|
65
109
|
See `docs/elsie-v2-migration.md` for the full step-by-step guide. Key actions:
|
|
66
110
|
|
|
67
111
|
1. Add `"type": "module"` to `package.json`.
|
|
68
|
-
2. Replace `.eslintrc.js` with `eslint.config.js` (ESM flat config importing
|
|
112
|
+
2. Replace `.eslintrc.js` with `eslint.config.js` (ESM flat config importing
|
|
113
|
+
from `@adobe-commerce/elsie/config/eslint.mjs`).
|
|
69
114
|
3. Rename `.elsie.js` → `.elsie.cjs` (keep `module.exports` content as-is).
|
|
70
|
-
4. Add `"prettier": "@adobe-commerce/elsie/config/prettier.json"` to
|
|
71
|
-
|
|
115
|
+
4. Add `"prettier": "@adobe-commerce/elsie/config/prettier.json"` to
|
|
116
|
+
`package.json`; delete `prettier.config.js`.
|
|
117
|
+
5. Update `tsconfig.json`: remove `baseUrl`, add explicit `rootDir` and
|
|
118
|
+
`types`.
|
|
72
119
|
6. Add `tsconfig.build.json` for declaration emit (required by `dtsPlugin`).
|
|
73
|
-
7. Convert `.elsie.js` imports in `.storybook/main.js` and
|
|
120
|
+
7. Convert `.elsie.js` imports in `.storybook/main.js` and
|
|
121
|
+
`storybook-stories.js` to reference `.elsie.cjs`.
|
|
74
122
|
|
|
75
123
|
- Updated dependencies [9cd299b]
|
|
76
124
|
- @adobe-commerce/fetch-graphql@1.3.1
|
|
@@ -79,54 +127,89 @@
|
|
|
79
127
|
|
|
80
128
|
### Patch Changes
|
|
81
129
|
|
|
82
|
-
- 24d7ffb: Add `COMPANY_CREATE` to the default reCAPTCHA form map for B2B
|
|
130
|
+
- 24d7ffb: Add `COMPANY_CREATE` to the default reCAPTCHA form map for B2B
|
|
131
|
+
company registration (`createCompany` badge id).
|
|
83
132
|
|
|
84
|
-
Global `setConfig()` now batches `recaptchaFormConfig` for company create
|
|
133
|
+
Global `setConfig()` now batches `recaptchaFormConfig` for company create
|
|
134
|
+
alongside other storefront forms.
|
|
85
135
|
|
|
86
136
|
- 9cd299b: Upgrade TypeScript to 6.0 and ESLint to 9 (flat config)
|
|
87
137
|
|
|
88
138
|
## What changed
|
|
89
139
|
|
|
90
140
|
### TypeScript 4.7 → 6.0
|
|
91
|
-
- `tsconfig-base.json` updated with correct TS 6 defaults. Two new-default
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
- `
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
141
|
+
- `tsconfig-base.json` updated with correct TS 6 defaults. Two new-default
|
|
142
|
+
opt-outs are deferred as tech debt: `exactOptionalPropertyTypes` and
|
|
143
|
+
`verbatimModuleSyntax`.
|
|
144
|
+
- `moduleResolution` changed from `"nodenext"` to `"bundler"` across all
|
|
145
|
+
packages — the correct pairing for `module: "esnext"` in a Vite monorepo.
|
|
146
|
+
Only `packages/elsie` keeps `"NodeNext"` (paired with `module: "NodeNext"`
|
|
147
|
+
for its dual CJS/ESM output).
|
|
148
|
+
- `baseUrl` removed from all tsconfigs (deprecated in TS 6; `paths` resolves
|
|
149
|
+
relative to the tsconfig file directly).
|
|
150
|
+
- `rootDir` added explicitly to `build-tools`, `event-bus`, `fetch-graphql`,
|
|
151
|
+
and `recaptcha` tsconfigs (implicit `rootDir` deprecated in TS 6).
|
|
152
|
+
- `types: []` set in `tsconfig-base.json` to prevent ambient test types from
|
|
153
|
+
leaking into declaration output. Each package's own `tsconfig.json` declares
|
|
154
|
+
its `types` explicitly (`jest`, `node`, `vite/client`, etc.).
|
|
155
|
+
- `babel-plugin-tsconfig-paths` removed — it was a no-op in every package that
|
|
156
|
+
listed it.
|
|
157
|
+
- `noUncheckedIndexedAccess` enabled — violations were few enough to fix in
|
|
158
|
+
source.
|
|
98
159
|
|
|
99
160
|
### ESLint 8 → 9 (flat config)
|
|
100
|
-
- `@typescript-eslint/parser` and `@typescript-eslint/eslint-plugin` (v5)
|
|
161
|
+
- `@typescript-eslint/parser` and `@typescript-eslint/eslint-plugin` (v5)
|
|
162
|
+
removed; replaced by the unified `typescript-eslint` v8 package.
|
|
101
163
|
- `eslint-config-preact` bumped to `^2.0.0` (ESLint 9 support).
|
|
102
164
|
- `eslint-config-prettier` bumped to `^10.0.0`.
|
|
103
|
-
- `eslint-plugin-mdx` bumped to `^3.8.1`. **ESLint 10 is not supported** —
|
|
165
|
+
- `eslint-plugin-mdx` bumped to `^3.8.1`. **ESLint 10 is not supported** —
|
|
166
|
+
`eslint-plugin-mdx` vendors an internal ESLint API removed in v10; ESLint 9
|
|
167
|
+
is pinned until a fix is released.
|
|
104
168
|
- `globals` added (`^15.0.0`) for `languageOptions.globals` in flat config.
|
|
105
|
-
- Shared config (`packages/elsie/config/eslint.mjs`) rewritten as a
|
|
106
|
-
|
|
107
|
-
-
|
|
108
|
-
|
|
169
|
+
- Shared config (`packages/elsie/config/eslint.mjs`) rewritten as a
|
|
170
|
+
flat-config array export.
|
|
171
|
+
- Per-package `.eslintrc.js` files deleted; replaced with `eslint.config.js`
|
|
172
|
+
(ESM flat config).
|
|
173
|
+
- Several `typescript-eslint` v8 rules disabled to preserve prior behavior:
|
|
174
|
+
`no-explicit-any`, `ban-ts-comment`, `no-unused-expressions`,
|
|
175
|
+
`no-unsafe-function-type`, `no-require-imports`, `no-empty-object-type`.
|
|
176
|
+
Tracked as tech debt.
|
|
177
|
+
- `reportUnusedDisableDirectives` disabled to avoid sweeping pre-existing
|
|
178
|
+
inline disable comments.
|
|
109
179
|
|
|
110
180
|
### `vite-plugin-dts` removed; replaced with custom `dtsPlugin`
|
|
111
181
|
|
|
112
|
-
`vite-plugin-dts@3.9.1` declared a `typescript <5.0` peer range and its v5
|
|
182
|
+
`vite-plugin-dts@3.9.1` declared a `typescript <5.0` peer range and its v5
|
|
183
|
+
successor was incompatible with this monorepo's layout (cross-package sources,
|
|
184
|
+
workspace symlinks, multi-`outDir`). It was removed and replaced with a thin
|
|
185
|
+
custom Vite plugin at `packages/elsie/config/plugins/dts.mjs` (`dtsPlugin`)
|
|
186
|
+
that runs `tsc` directly. The resulting `dist/` layout is identical to what
|
|
187
|
+
v3.9 produced (147 `.d.ts` files, 12 top-level entry shims).
|
|
113
188
|
|
|
114
189
|
### Module format standardization
|
|
115
190
|
|
|
116
191
|
All tooling config files now follow a consistent format:
|
|
117
192
|
|
|
118
|
-
- **ESLint / Jest / Vite configs** — ESM (`.js` in `"type":"module"` packages;
|
|
119
|
-
|
|
193
|
+
- **ESLint / Jest / Vite configs** — ESM (`.js` in `"type":"module"` packages;
|
|
194
|
+
`.mjs` in elsie).
|
|
195
|
+
- **Prettier** — JSON only (`@adobe-commerce/elsie/config/prettier.json`
|
|
196
|
+
referenced via each package's `"prettier"` key). `prettier.config.*` files
|
|
197
|
+
deleted from all packages.
|
|
120
198
|
- **elsie CLI** (`bin/**`) — intentionally stays CommonJS.
|
|
121
199
|
|
|
122
200
|
See `architecture/decisions/009-module-format.md` for the full convention.
|
|
123
201
|
|
|
124
202
|
### `.elsie.js` → `.elsie.cjs`
|
|
125
203
|
|
|
126
|
-
The consumer project config file is renamed from `.elsie.js` to `.elsie.cjs`.
|
|
204
|
+
The consumer project config file is renamed from `.elsie.js` to `.elsie.cjs`.
|
|
205
|
+
With `"type":"module"` now required in consumer packages, a plain `.js` file
|
|
206
|
+
is treated as ES module — making `module.exports` a SyntaxError and making
|
|
207
|
+
`require()` in the elsie CLI fail with `ERR_REQUIRE_ESM`. The `.cjs` extension
|
|
208
|
+
forces CommonJS regardless of the package's `"type"` field.
|
|
127
209
|
|
|
128
210
|
- `elsie generate config` now writes `.elsie.cjs`.
|
|
129
|
-
- The CLI (`bin/lib/config.js`) and `config/vite.mjs` both prefer `.elsie.cjs`
|
|
211
|
+
- The CLI (`bin/lib/config.js`) and `config/vite.mjs` both prefer `.elsie.cjs`
|
|
212
|
+
and fall back to `.elsie.js` for packages not yet migrated.
|
|
130
213
|
- `.elsie.cjs` added to `.npmignore` in all consumer packages.
|
|
131
214
|
|
|
132
215
|
## Consumer migration
|
|
@@ -134,12 +217,16 @@
|
|
|
134
217
|
See `docs/elsie-v2-migration.md` for the full step-by-step guide. Key actions:
|
|
135
218
|
|
|
136
219
|
1. Add `"type": "module"` to `package.json`.
|
|
137
|
-
2. Replace `.eslintrc.js` with `eslint.config.js` (ESM flat config importing
|
|
220
|
+
2. Replace `.eslintrc.js` with `eslint.config.js` (ESM flat config importing
|
|
221
|
+
from `@adobe-commerce/elsie/config/eslint.mjs`).
|
|
138
222
|
3. Rename `.elsie.js` → `.elsie.cjs` (keep `module.exports` content as-is).
|
|
139
|
-
4. Add `"prettier": "@adobe-commerce/elsie/config/prettier.json"` to
|
|
140
|
-
|
|
223
|
+
4. Add `"prettier": "@adobe-commerce/elsie/config/prettier.json"` to
|
|
224
|
+
`package.json`; delete `prettier.config.js`.
|
|
225
|
+
5. Update `tsconfig.json`: remove `baseUrl`, add explicit `rootDir` and
|
|
226
|
+
`types`.
|
|
141
227
|
6. Add `tsconfig.build.json` for declaration emit (required by `dtsPlugin`).
|
|
142
|
-
7. Convert `.elsie.js` imports in `.storybook/main.js` and
|
|
228
|
+
7. Convert `.elsie.js` imports in `.storybook/main.js` and
|
|
229
|
+
`storybook-stories.js` to reference `.elsie.cjs`.
|
|
143
230
|
|
|
144
231
|
- Updated dependencies [9cd299b]
|
|
145
232
|
- @adobe-commerce/fetch-graphql@1.3.1-beta.0
|
|
@@ -150,7 +237,10 @@
|
|
|
150
237
|
|
|
151
238
|
- af62897: Update minimum Node.js requirement to 22 LTS
|
|
152
239
|
|
|
153
|
-
Packages are now built with Node.js 22. `elsie` requires `>=22`; browser-only
|
|
240
|
+
Packages are now built with Node.js 22. `elsie` requires `>=22`; browser-only
|
|
241
|
+
packages (`fetch-graphql`, `event-bus`, `recaptcha`, `storefront-design`,
|
|
242
|
+
`build-tools`) do not declare an `engines` field as they do not run in
|
|
243
|
+
Node.js.
|
|
154
244
|
|
|
155
245
|
### Patch Changes
|
|
156
246
|
|
|
@@ -163,7 +253,10 @@
|
|
|
163
253
|
|
|
164
254
|
- af62897: Update minimum Node.js requirement to 22 LTS
|
|
165
255
|
|
|
166
|
-
Packages are now built with Node.js 22. `elsie` requires `>=22`; browser-only
|
|
256
|
+
Packages are now built with Node.js 22. `elsie` requires `>=22`; browser-only
|
|
257
|
+
packages (`fetch-graphql`, `event-bus`, `recaptcha`, `storefront-design`,
|
|
258
|
+
`build-tools`) do not declare an `engines` field as they do not run in
|
|
259
|
+
Node.js.
|
|
167
260
|
|
|
168
261
|
### Patch Changes
|
|
169
262
|
|
|
@@ -176,7 +269,10 @@
|
|
|
176
269
|
|
|
177
270
|
- f55a79c: Migrate to Node.js 24 LTS
|
|
178
271
|
|
|
179
|
-
Minimum required Node.js version is now 24. Updated `engines.node` from
|
|
272
|
+
Minimum required Node.js version is now 24. Updated `engines.node` from
|
|
273
|
+
`>=16`/`>=18` to `>=24` across all packages. Regenerated lockfile under
|
|
274
|
+
Node 24. Updated CI workflows to use `storefront-workflows@v6` with Node 24
|
|
275
|
+
support.
|
|
180
276
|
|
|
181
277
|
### Patch Changes
|
|
182
278
|
|
|
@@ -189,14 +285,21 @@
|
|
|
189
285
|
|
|
190
286
|
- d054150: ### Summary
|
|
191
287
|
|
|
192
|
-
- Replaces the `recaptchaV3Config` GraphQL query with `recaptchaFormConfig`,
|
|
193
|
-
|
|
194
|
-
-
|
|
195
|
-
|
|
288
|
+
- Replaces the `recaptchaV3Config` GraphQL query with `recaptchaFormConfig`,
|
|
289
|
+
which supports both reCAPTCHA v3 and Enterprise
|
|
290
|
+
- Queries all form types in a single request using GraphQL aliases and
|
|
291
|
+
normalizes the per-form response into the existing config shape
|
|
292
|
+
- Loads `enterprise.js` or `api.js` based on the detected `re_captcha_type`
|
|
293
|
+
from the backend
|
|
294
|
+
- Uses `grecaptcha.enterprise.*` namespace for token generation and badge
|
|
295
|
+
rendering when Enterprise is configured
|
|
196
296
|
- Skips forms with empty `website_key` (enabled but not usable)
|
|
197
|
-
- Warns and disables reCAPTCHA entirely if mixed types (v3 + Enterprise) are
|
|
198
|
-
|
|
199
|
-
-
|
|
297
|
+
- Warns and disables reCAPTCHA entirely if mixed types (v3 + Enterprise) are
|
|
298
|
+
detected across forms
|
|
299
|
+
- Adds `[ReCaptcha]`-prefixed debug logging throughout the lifecycle (gated
|
|
300
|
+
behind `enableLogger`)
|
|
301
|
+
- Renames all `V3`-suffixed types to generic names (`ReCaptchaModel`,
|
|
302
|
+
`ReCaptchaResponse`, etc.)
|
|
200
303
|
- Updates README with Enterprise support documentation
|
|
201
304
|
|
|
202
305
|
## 1.1.0-beta.0
|
|
@@ -205,12 +308,19 @@
|
|
|
205
308
|
|
|
206
309
|
- d054150: ### Summary
|
|
207
310
|
|
|
208
|
-
- Replaces the `recaptchaV3Config` GraphQL query with `recaptchaFormConfig`,
|
|
209
|
-
|
|
210
|
-
-
|
|
211
|
-
|
|
311
|
+
- Replaces the `recaptchaV3Config` GraphQL query with `recaptchaFormConfig`,
|
|
312
|
+
which supports both reCAPTCHA v3 and Enterprise
|
|
313
|
+
- Queries all form types in a single request using GraphQL aliases and
|
|
314
|
+
normalizes the per-form response into the existing config shape
|
|
315
|
+
- Loads `enterprise.js` or `api.js` based on the detected `re_captcha_type`
|
|
316
|
+
from the backend
|
|
317
|
+
- Uses `grecaptcha.enterprise.*` namespace for token generation and badge
|
|
318
|
+
rendering when Enterprise is configured
|
|
212
319
|
- Skips forms with empty `website_key` (enabled but not usable)
|
|
213
|
-
- Warns and disables reCAPTCHA entirely if mixed types (v3 + Enterprise) are
|
|
214
|
-
|
|
215
|
-
-
|
|
320
|
+
- Warns and disables reCAPTCHA entirely if mixed types (v3 + Enterprise) are
|
|
321
|
+
detected across forms
|
|
322
|
+
- Adds `[ReCaptcha]`-prefixed debug logging throughout the lifecycle (gated
|
|
323
|
+
behind `enableLogger`)
|
|
324
|
+
- Renames all `V3`-suffixed types to generic names (`ReCaptchaModel`,
|
|
325
|
+
`ReCaptchaResponse`, etc.)
|
|
216
326
|
- Updates README with Enterprise support documentation
|
package/README.md
CHANGED
|
@@ -2,9 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
This module integrates Google reCAPTCHA into Adobe Commerce storefronts. It
|
|
5
|
+
This module integrates Google reCAPTCHA into Adobe Commerce storefronts. It
|
|
6
|
+
supports both **reCAPTCHA v3** and **reCAPTCHA Enterprise**, providing methods
|
|
7
|
+
for configuring, initializing, and verifying reCAPTCHA on protected forms.
|
|
6
8
|
|
|
7
|
-
The module queries the `recaptchaFormConfig` GraphQL endpoint to fetch per-form
|
|
9
|
+
The module queries the `recaptchaFormConfig` GraphQL endpoint to fetch per-form
|
|
10
|
+
configuration, automatically detecting which reCAPTCHA type (v3 or Enterprise)
|
|
11
|
+
is configured and loading the appropriate script.
|
|
8
12
|
|
|
9
13
|
## Installation
|
|
10
14
|
|
|
@@ -15,7 +19,13 @@ npm i @adobe-commerce/recaptcha
|
|
|
15
19
|
## Quick Start
|
|
16
20
|
|
|
17
21
|
```typescript
|
|
18
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
setEndpoint,
|
|
24
|
+
setConfig,
|
|
25
|
+
initReCaptcha,
|
|
26
|
+
verifyReCaptcha,
|
|
27
|
+
enableLogger,
|
|
28
|
+
} from '@adobe-commerce/recaptcha';
|
|
19
29
|
|
|
20
30
|
// 1. Set the backend GraphQL endpoint
|
|
21
31
|
setEndpoint('https://your-store.com/graphql');
|
|
@@ -36,41 +46,50 @@ const token = await verifyReCaptcha();
|
|
|
36
46
|
|
|
37
47
|
Sets the GraphQL endpoint URL used to fetch reCAPTCHA configuration.
|
|
38
48
|
|
|
39
|
-
### `setConfig(configList: PropsFormTypes[])`
|
|
49
|
+
### `setConfig(configList: PropsFormTypes[], options?: { b2bEnabled?: boolean })`
|
|
40
50
|
|
|
41
|
-
Fetches reCAPTCHA configuration from the backend via `recaptchaFormConfig` and
|
|
51
|
+
Fetches reCAPTCHA configuration from the backend via `recaptchaFormConfig` and
|
|
52
|
+
stores it in session storage.
|
|
42
53
|
|
|
43
|
-
- Queries all
|
|
54
|
+
- Queries all default form types and determines which are enabled
|
|
55
|
+
- When `options.b2bEnabled` is `true`, includes `COMPANY_CREATE` in the query —
|
|
56
|
+
this form type is **not** included by default
|
|
44
57
|
- Normalizes the response into a unified config shape
|
|
45
58
|
- Detects the reCAPTCHA type (v3 or Enterprise) from the `re_captcha_type` field
|
|
46
59
|
- Skips forms with empty `website_key`
|
|
47
|
-
- Warns and disables reCAPTCHA if mixed types (both v3 and Enterprise) are
|
|
60
|
+
- Warns and disables reCAPTCHA if mixed types (both v3 and Enterprise) are
|
|
61
|
+
detected across forms
|
|
48
62
|
|
|
49
63
|
```typescript
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
// B2C storefront (default)
|
|
65
|
+
await setConfig([{ badgeId: 'generateCustomerToken' }]);
|
|
66
|
+
|
|
67
|
+
// B2B storefront
|
|
68
|
+
await setConfig([], { b2bEnabled: true });
|
|
54
69
|
```
|
|
55
70
|
|
|
56
71
|
### `initReCaptcha(lazyLoadTimeout?: number)`
|
|
57
72
|
|
|
58
|
-
Initializes reCAPTCHA by loading the appropriate Google script and rendering
|
|
73
|
+
Initializes reCAPTCHA by loading the appropriate Google script and rendering
|
|
74
|
+
badges.
|
|
59
75
|
|
|
60
76
|
- Loads `enterprise.js` for Enterprise or `api.js` for v3
|
|
61
|
-
- For inline badge position, renders invisible widgets into DOM elements
|
|
77
|
+
- For inline badge position, renders invisible widgets into DOM elements
|
|
78
|
+
matching each form's `badgeId`
|
|
62
79
|
- Default lazy load timeout: 3000ms
|
|
63
80
|
|
|
64
81
|
### `verifyReCaptcha(): Promise<string | undefined>`
|
|
65
82
|
|
|
66
83
|
Generates and returns a reCAPTCHA token for form submission.
|
|
67
84
|
|
|
68
|
-
- Uses `grecaptcha.enterprise.execute()` for Enterprise or
|
|
85
|
+
- Uses `grecaptcha.enterprise.execute()` for Enterprise or
|
|
86
|
+
`grecaptcha.execute()` for v3
|
|
69
87
|
- Returns `undefined` if reCAPTCHA is not enabled or not configured
|
|
70
88
|
|
|
71
89
|
### `enableLogger(enabled: boolean)`
|
|
72
90
|
|
|
73
|
-
Enables or disables debug logging. When enabled, logs are prefixed with
|
|
91
|
+
Enables or disables debug logging. When enabled, logs are prefixed with
|
|
92
|
+
`[ReCaptcha]` and include:
|
|
74
93
|
|
|
75
94
|
- Config fetch and normalization details
|
|
76
95
|
- Which reCAPTCHA type is detected and which forms are enabled
|
|
@@ -79,28 +98,41 @@ Enables or disables debug logging. When enabled, logs are prefixed with `[ReCapt
|
|
|
79
98
|
- Token generation attempts
|
|
80
99
|
- Warnings for skipped forms (missing keys) or mixed type configurations
|
|
81
100
|
|
|
82
|
-
##
|
|
101
|
+
## Default Form Types
|
|
102
|
+
|
|
103
|
+
These form types are queried by default (no additional configuration needed):
|
|
104
|
+
|
|
105
|
+
| Form Type | Badge ID |
|
|
106
|
+
| -------------------------- | ---------------------------- |
|
|
107
|
+
| `PLACE_ORDER` | `placeOrder` |
|
|
108
|
+
| `CONTACT` | `contactUs` |
|
|
109
|
+
| `CUSTOMER_LOGIN` | `generateCustomerToken` |
|
|
110
|
+
| `CUSTOMER_FORGOT_PASSWORD` | `requestPasswordResetEmail` |
|
|
111
|
+
| `CUSTOMER_CREATE` | `createCustomerV2` |
|
|
112
|
+
| `CUSTOMER_EDIT` | `updateCustomerV2` |
|
|
113
|
+
| `NEWSLETTER` | `subscribeEmailToNewsletter` |
|
|
114
|
+
| `PRODUCT_REVIEW` | `createProductReview` |
|
|
115
|
+
| `SENDFRIEND` | `SENDFRIEND` |
|
|
116
|
+
| `BRAINTREE` | `BRAINTREE` |
|
|
83
117
|
|
|
84
|
-
|
|
118
|
+
## B2B Form Types
|
|
119
|
+
|
|
120
|
+
B2B storefronts should pass `{ b2bEnabled: true }` to include `COMPANY_CREATE`
|
|
121
|
+
in the reCAPTCHA query:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
await setConfig([], { b2bEnabled: true });
|
|
125
|
+
```
|
|
85
126
|
|
|
86
|
-
|
|
87
|
-
|---|---|
|
|
88
|
-
| `PLACE_ORDER` | `placeOrder` |
|
|
89
|
-
| `CONTACT` | `contactUs` |
|
|
90
|
-
| `CUSTOMER_LOGIN` | `generateCustomerToken` |
|
|
91
|
-
| `CUSTOMER_FORGOT_PASSWORD` | `requestPasswordResetEmail` |
|
|
92
|
-
| `CUSTOMER_CREATE` | `createCustomerV2` |
|
|
93
|
-
| `CUSTOMER_EDIT` | `updateCustomerV2` |
|
|
94
|
-
| `NEWSLETTER` | `subscribeEmailToNewsletter` |
|
|
95
|
-
| `PRODUCT_REVIEW` | `createProductReview` |
|
|
96
|
-
| `SENDFRIEND` | `SENDFRIEND` |
|
|
97
|
-
| `BRAINTREE` | `BRAINTREE` |
|
|
127
|
+
This requires the backend to expose `COMPANY_CREATE` in `ReCaptchaFormEnum`.
|
|
98
128
|
|
|
99
129
|
## Mixed Type Configuration
|
|
100
130
|
|
|
101
|
-
If the Commerce Admin has some forms configured with reCAPTCHA v3 and others
|
|
131
|
+
If the Commerce Admin has some forms configured with reCAPTCHA v3 and others
|
|
132
|
+
with Enterprise, the module will:
|
|
102
133
|
|
|
103
134
|
1. Log a warning with instructions to update the Commerce Admin configuration
|
|
104
135
|
2. Disable reCAPTCHA entirely (returns no config, no script loaded)
|
|
105
136
|
|
|
106
|
-
All forms must use the same reCAPTCHA type. Configure this in the Commerce Admin
|
|
137
|
+
All forms must use the same reCAPTCHA type. Configure this in the Commerce Admin
|
|
138
|
+
under **Stores > Configuration > Security > Google reCAPTCHA Storefront**.
|
package/eslint.config.js
CHANGED
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
|
-
import base, {
|
|
10
|
+
import base, {
|
|
11
|
+
sourceImportRestrictions,
|
|
12
|
+
} from '@adobe-commerce/elsie/config/eslint.mjs';
|
|
11
13
|
|
|
12
14
|
export default [...base, ...sourceImportRestrictions];
|
package/package.json
CHANGED
package/src/configs/index.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
export * from './message.config';
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
export const recaptchaMessage = {
|
|
@@ -16,5 +16,5 @@ export const recaptchaMessage = {
|
|
|
16
16
|
mixedTypesWarning:
|
|
17
17
|
'Multiple reCAPTCHA types detected across forms (both v3 and Enterprise). ' +
|
|
18
18
|
'This configuration is not supported — all forms must use the same reCAPTCHA type. ' +
|
|
19
|
-
'Please update your configuration in Commerce Admin '
|
|
19
|
+
'Please update your configuration in Commerce Admin ',
|
|
20
20
|
};
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
|
-
export const recaptchaBadgeSelector = '.grecaptcha-badge iframe';
|
|
10
|
+
export const recaptchaBadgeSelector = '.grecaptcha-badge iframe';
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* All known reCAPTCHA form types and their associated GraphQL mutation names.
|
|
12
|
+
* Keys = form type identifiers (matching ReCaptchaFormEnum on the backend).
|
|
13
|
+
* Values = mutation names used for badge mapping.
|
|
14
|
+
*/
|
|
15
|
+
export const RECAPTCHA_FORMS = {
|
|
11
16
|
PLACE_ORDER: 'placeOrder',
|
|
12
17
|
CONTACT: 'contactUs',
|
|
13
18
|
CUSTOMER_LOGIN: 'generateCustomerToken',
|
|
@@ -18,4 +23,23 @@ export const typeDefaultForm: Record<string, string> = {
|
|
|
18
23
|
PRODUCT_REVIEW: 'createProductReview',
|
|
19
24
|
SENDFRIEND: 'SENDFRIEND',
|
|
20
25
|
BRAINTREE: 'BRAINTREE',
|
|
26
|
+
COMPANY_CREATE: 'createCompany',
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Default form-type → mutation mapping.
|
|
31
|
+
* Only base (non-B2B) forms live here. B2B forms are included conditionally
|
|
32
|
+
* via the `b2bEnabled` option in `setConfig()`.
|
|
33
|
+
*/
|
|
34
|
+
export const typeDefaultForm: Record<string, string> = {
|
|
35
|
+
PLACE_ORDER: RECAPTCHA_FORMS.PLACE_ORDER,
|
|
36
|
+
CONTACT: RECAPTCHA_FORMS.CONTACT,
|
|
37
|
+
CUSTOMER_LOGIN: RECAPTCHA_FORMS.CUSTOMER_LOGIN,
|
|
38
|
+
CUSTOMER_FORGOT_PASSWORD: RECAPTCHA_FORMS.CUSTOMER_FORGOT_PASSWORD,
|
|
39
|
+
CUSTOMER_CREATE: RECAPTCHA_FORMS.CUSTOMER_CREATE,
|
|
40
|
+
CUSTOMER_EDIT: RECAPTCHA_FORMS.CUSTOMER_EDIT,
|
|
41
|
+
NEWSLETTER: RECAPTCHA_FORMS.NEWSLETTER,
|
|
42
|
+
PRODUCT_REVIEW: RECAPTCHA_FORMS.PRODUCT_REVIEW,
|
|
43
|
+
SENDFRIEND: RECAPTCHA_FORMS.SENDFRIEND,
|
|
44
|
+
BRAINTREE: RECAPTCHA_FORMS.BRAINTREE,
|
|
21
45
|
};
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
const RECAPTCHA_FORM_CONFIG_FIELDS = `
|
|
10
10
|
is_enabled
|
|
@@ -19,14 +19,12 @@ const RECAPTCHA_FORM_CONFIG_FIELDS = `
|
|
|
19
19
|
theme
|
|
20
20
|
}`;
|
|
21
21
|
|
|
22
|
-
export const buildRecaptchaFormConfigQuery = (
|
|
23
|
-
formTypes: string[]
|
|
24
|
-
): string => {
|
|
22
|
+
export const buildRecaptchaFormConfigQuery = (formTypes: string[]): string => {
|
|
25
23
|
const queries = formTypes
|
|
26
24
|
.map(
|
|
27
25
|
(formType) =>
|
|
28
26
|
` ${formType}: recaptchaFormConfig(formType: ${formType}) {${RECAPTCHA_FORM_CONFIG_FIELDS}
|
|
29
|
-
}
|
|
27
|
+
}`,
|
|
30
28
|
)
|
|
31
29
|
.join('\n');
|
|
32
30
|
|
package/src/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
recaptchaMessage,
|
|
18
18
|
recaptchaBadgeSelector,
|
|
19
19
|
typeDefaultForm,
|
|
20
|
+
RECAPTCHA_FORMS,
|
|
20
21
|
} from './configs';
|
|
21
22
|
import {
|
|
22
23
|
extendConfig,
|
|
@@ -36,13 +37,15 @@ import { FetchGraphQL } from '@adobe-commerce/fetch-graphql';
|
|
|
36
37
|
export const recaptchaFetchApi = new FetchGraphQL().getMethods();
|
|
37
38
|
|
|
38
39
|
const RECAPTCHA_V3_SCRIPT_URL = 'https://www.google.com/recaptcha/api.js';
|
|
39
|
-
const RECAPTCHA_ENTERPRISE_SCRIPT_URL =
|
|
40
|
+
const RECAPTCHA_ENTERPRISE_SCRIPT_URL =
|
|
41
|
+
'https://www.google.com/recaptcha/enterprise.js';
|
|
40
42
|
|
|
41
43
|
const LOG_PREFIX = '[ReCaptcha]';
|
|
42
44
|
|
|
43
45
|
export class RecaptchaModule {
|
|
44
46
|
_enableReCAPTCHA: boolean = false;
|
|
45
|
-
_recaptchaBackendEndpoint: string =
|
|
47
|
+
_recaptchaBackendEndpoint: string =
|
|
48
|
+
recaptchaFetchApi.getConfig()?.endpoint || '';
|
|
46
49
|
_recaptchaScriptUrl: string = RECAPTCHA_V3_SCRIPT_URL;
|
|
47
50
|
_configStorageKey: string = 'recaptchaConfig';
|
|
48
51
|
_logger: boolean = false;
|
|
@@ -53,7 +56,7 @@ export class RecaptchaModule {
|
|
|
53
56
|
|
|
54
57
|
_normalizeFormConfigResponse(
|
|
55
58
|
response: any,
|
|
56
|
-
formTypes: string[]
|
|
59
|
+
formTypes: string[],
|
|
57
60
|
): ReCaptchaResponse | undefined {
|
|
58
61
|
const data = response?.data;
|
|
59
62
|
if (!data) return undefined;
|
|
@@ -81,9 +84,7 @@ export class RecaptchaModule {
|
|
|
81
84
|
|
|
82
85
|
// Skip forms with empty website_key — enabled but not usable
|
|
83
86
|
if (!config.website_key) {
|
|
84
|
-
this._log(
|
|
85
|
-
`Skipping ${formType}: enabled but missing website_key`
|
|
86
|
-
);
|
|
87
|
+
this._log(`Skipping ${formType}: enabled but missing website_key`);
|
|
87
88
|
continue;
|
|
88
89
|
}
|
|
89
90
|
|
|
@@ -99,7 +100,7 @@ export class RecaptchaModule {
|
|
|
99
100
|
console.warn(
|
|
100
101
|
LOG_PREFIX,
|
|
101
102
|
recaptchaMessage.mixedTypesWarning,
|
|
102
|
-
`\nDetected: ${[...detectedTypes].join(', ')}
|
|
103
|
+
`\nDetected: ${[...detectedTypes].join(', ')}`,
|
|
103
104
|
);
|
|
104
105
|
return undefined;
|
|
105
106
|
}
|
|
@@ -115,7 +116,7 @@ export class RecaptchaModule {
|
|
|
115
116
|
|
|
116
117
|
this._log(
|
|
117
118
|
`Using reCAPTCHA ${isEnterprise ? 'Enterprise' : 'v3'}`,
|
|
118
|
-
`| Enabled forms: ${enabledForms.join(', ')}
|
|
119
|
+
`| Enabled forms: ${enabledForms.join(', ')}`,
|
|
119
120
|
);
|
|
120
121
|
|
|
121
122
|
return {
|
|
@@ -137,7 +138,7 @@ export class RecaptchaModule {
|
|
|
137
138
|
|
|
138
139
|
async _updateBadgePosition(
|
|
139
140
|
badgeId: string,
|
|
140
|
-
config: ReCaptchaModel
|
|
141
|
+
config: ReCaptchaModel,
|
|
141
142
|
): Promise<void | null> {
|
|
142
143
|
if (!config) return;
|
|
143
144
|
|
|
@@ -147,7 +148,7 @@ export class RecaptchaModule {
|
|
|
147
148
|
badgeId,
|
|
148
149
|
config,
|
|
149
150
|
this._logger,
|
|
150
|
-
config.recaptchaType === 'enterprise'
|
|
151
|
+
config.recaptchaType === 'enterprise',
|
|
151
152
|
);
|
|
152
153
|
} else {
|
|
153
154
|
const isBadgeLoaded = await checkRecaptchaBadge();
|
|
@@ -155,7 +156,7 @@ export class RecaptchaModule {
|
|
|
155
156
|
if (!isBadgeLoaded) return;
|
|
156
157
|
|
|
157
158
|
const recaptchaBadge = document.querySelector(
|
|
158
|
-
recaptchaBadgeSelector
|
|
159
|
+
recaptchaBadgeSelector,
|
|
159
160
|
) as HTMLIFrameElement;
|
|
160
161
|
|
|
161
162
|
const shouldUpdateSrc =
|
|
@@ -167,7 +168,7 @@ export class RecaptchaModule {
|
|
|
167
168
|
if (shouldUpdateSrc) {
|
|
168
169
|
recaptchaBadge.setAttribute(
|
|
169
170
|
'src',
|
|
170
|
-
`${recaptchaBadge.src}&theme=${config.theme}
|
|
171
|
+
`${recaptchaBadge.src}&theme=${config.theme}`,
|
|
171
172
|
);
|
|
172
173
|
}
|
|
173
174
|
}
|
|
@@ -203,9 +204,16 @@ export class RecaptchaModule {
|
|
|
203
204
|
}
|
|
204
205
|
}
|
|
205
206
|
|
|
206
|
-
async _fetchStoreConfig(
|
|
207
|
+
async _fetchStoreConfig(options?: {
|
|
208
|
+
b2bEnabled?: boolean;
|
|
209
|
+
}): Promise<ReCaptchaResponse | undefined> {
|
|
207
210
|
try {
|
|
208
|
-
const formTypes = Object.keys(
|
|
211
|
+
const formTypes = Object.keys({
|
|
212
|
+
...typeDefaultForm,
|
|
213
|
+
...(options?.b2bEnabled
|
|
214
|
+
? { COMPANY_CREATE: RECAPTCHA_FORMS.COMPANY_CREATE }
|
|
215
|
+
: {}),
|
|
216
|
+
});
|
|
209
217
|
const query = buildRecaptchaFormConfigQuery(formTypes);
|
|
210
218
|
|
|
211
219
|
this._log(`Fetching config for forms: ${formTypes.join(', ')}`);
|
|
@@ -248,9 +256,12 @@ export class RecaptchaModule {
|
|
|
248
256
|
recaptchaFetchApi.setEndpoint(url);
|
|
249
257
|
}
|
|
250
258
|
|
|
251
|
-
async setConfig(
|
|
259
|
+
async setConfig(
|
|
260
|
+
configList: PropsFormTypes[],
|
|
261
|
+
options?: { b2bEnabled?: boolean },
|
|
262
|
+
) {
|
|
252
263
|
try {
|
|
253
|
-
const config = await this._fetchStoreConfig();
|
|
264
|
+
const config = await this._fetchStoreConfig(options);
|
|
254
265
|
|
|
255
266
|
if (!config?.data?.recaptchaConfig) {
|
|
256
267
|
this._log('No reCAPTCHA config returned, clearing storage');
|
|
@@ -259,7 +270,7 @@ export class RecaptchaModule {
|
|
|
259
270
|
}
|
|
260
271
|
|
|
261
272
|
const transformConfig: ReCaptchaModel = convertKeysToCamelCase(
|
|
262
|
-
config?.data?.recaptchaConfig
|
|
273
|
+
config?.data?.recaptchaConfig,
|
|
263
274
|
);
|
|
264
275
|
|
|
265
276
|
const extendedRecaptchaConfig = extendConfig(transformConfig, configList);
|
|
@@ -274,7 +285,7 @@ export class RecaptchaModule {
|
|
|
274
285
|
setConfigStorage(
|
|
275
286
|
this._configStorageKey,
|
|
276
287
|
extendedRecaptchaConfig,
|
|
277
|
-
this._logger
|
|
288
|
+
this._logger,
|
|
278
289
|
);
|
|
279
290
|
}
|
|
280
291
|
} catch (error) {
|
|
@@ -293,12 +304,14 @@ export class RecaptchaModule {
|
|
|
293
304
|
const config = await this._loadConfig();
|
|
294
305
|
|
|
295
306
|
if (!config?.forms || !config.isEnabled) {
|
|
296
|
-
this._log(
|
|
307
|
+
this._log(
|
|
308
|
+
'reCAPTCHA disabled or no forms configured, skipping init',
|
|
309
|
+
);
|
|
297
310
|
return;
|
|
298
311
|
}
|
|
299
312
|
|
|
300
313
|
this._log(
|
|
301
|
-
`Initializing reCAPTCHA (type: ${config.recaptchaType ?? 'v3'}, badge: ${config.badgePosition})
|
|
314
|
+
`Initializing reCAPTCHA (type: ${config.recaptchaType ?? 'v3'}, badge: ${config.badgePosition})`,
|
|
302
315
|
);
|
|
303
316
|
|
|
304
317
|
await this._addRecaptchaScript();
|
|
@@ -306,8 +319,8 @@ export class RecaptchaModule {
|
|
|
306
319
|
if (config.badgePosition === 'inline') {
|
|
307
320
|
await Promise.all(
|
|
308
321
|
(config.forms as PropsFormTypes[]).map((element) =>
|
|
309
|
-
this._updateBadgePosition(element.badgeId, config)
|
|
310
|
-
)
|
|
322
|
+
this._updateBadgePosition(element.badgeId, config),
|
|
323
|
+
),
|
|
311
324
|
);
|
|
312
325
|
} else {
|
|
313
326
|
await this._updateBadgePosition('', config);
|
|
@@ -325,17 +338,17 @@ export class RecaptchaModule {
|
|
|
325
338
|
const config = await this._loadConfig();
|
|
326
339
|
|
|
327
340
|
if (!config?.forms || !config.websiteKey || !config.isEnabled) {
|
|
328
|
-
this._log(
|
|
341
|
+
this._log(
|
|
342
|
+
'verifyReCaptcha skipped: reCAPTCHA not enabled or missing config',
|
|
343
|
+
);
|
|
329
344
|
return undefined;
|
|
330
345
|
}
|
|
331
346
|
|
|
332
|
-
this._log(
|
|
333
|
-
`Requesting token (type: ${config.recaptchaType ?? 'v3'})`
|
|
334
|
-
);
|
|
347
|
+
this._log(`Requesting token (type: ${config.recaptchaType ?? 'v3'})`);
|
|
335
348
|
|
|
336
349
|
const token = await getRecaptchaToken(
|
|
337
350
|
config.websiteKey,
|
|
338
|
-
config.recaptchaType === 'enterprise'
|
|
351
|
+
config.recaptchaType === 'enterprise',
|
|
339
352
|
);
|
|
340
353
|
|
|
341
354
|
this._log('Token obtained successfully');
|
|
@@ -367,3 +380,4 @@ const { initReCaptcha, verifyReCaptcha, setEndpoint, setConfig, enableLogger } =
|
|
|
367
380
|
recaptcha.getMethods();
|
|
368
381
|
|
|
369
382
|
export { setEndpoint, setConfig, initReCaptcha, verifyReCaptcha, enableLogger };
|
|
383
|
+
export { RECAPTCHA_FORMS };
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { recaptchaBadgeSelector } from '../configs';
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
export const convertKeysToCamelCase = (obj: {
|
|
@@ -14,9 +14,12 @@ export const convertKeysToCamelCase = (obj: {
|
|
|
14
14
|
return key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
return Object.keys(obj).reduce(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
return Object.keys(obj).reduce(
|
|
18
|
+
(result, key) => {
|
|
19
|
+
const newKey = camelCaseKey(key);
|
|
20
|
+
result[newKey] = obj[key];
|
|
21
|
+
return result;
|
|
22
|
+
},
|
|
23
|
+
{} as { [key: string]: any },
|
|
24
|
+
);
|
|
22
25
|
};
|
package/src/lib/_extendConfig.ts
CHANGED
|
@@ -2,24 +2,26 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { ReCaptchaModel } from '../types/recaptcha.types';
|
|
11
|
-
import { typeDefaultForm } from '../configs/typeForms.config';
|
|
11
|
+
import { typeDefaultForm, RECAPTCHA_FORMS } from '../configs/typeForms.config';
|
|
12
12
|
|
|
13
13
|
export const extendConfig = (
|
|
14
14
|
config: ReCaptchaModel,
|
|
15
|
-
modifyParams: any[]
|
|
15
|
+
modifyParams: any[],
|
|
16
16
|
): ReCaptchaModel | undefined => {
|
|
17
17
|
if (config && config.forms) {
|
|
18
18
|
const modifyForm = config.forms.concat(modifyParams).map((el) => {
|
|
19
19
|
if (typeof el !== 'string') return { ...el, enabledBadgePlace: false };
|
|
20
20
|
|
|
21
21
|
return {
|
|
22
|
-
badgeId:
|
|
22
|
+
badgeId:
|
|
23
|
+
typeDefaultForm[el] ??
|
|
24
|
+
RECAPTCHA_FORMS[el as keyof typeof RECAPTCHA_FORMS],
|
|
23
25
|
enabledBadgePlace: false,
|
|
24
26
|
};
|
|
25
27
|
});
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { recaptchaMessage } from '../configs';
|
|
@@ -13,7 +13,7 @@ import { ReCaptchaModel } from '../types/recaptcha.types';
|
|
|
13
13
|
const getConfigStorage = async (
|
|
14
14
|
storageKey: string,
|
|
15
15
|
retries = 1,
|
|
16
|
-
delay = 1000
|
|
16
|
+
delay = 1000,
|
|
17
17
|
): Promise<ReCaptchaModel | null> => {
|
|
18
18
|
const storedConfig = sessionStorage.getItem(storageKey);
|
|
19
19
|
|
|
@@ -31,7 +31,7 @@ const getConfigStorage = async (
|
|
|
31
31
|
const setConfigStorage = (
|
|
32
32
|
storageKey: string,
|
|
33
33
|
config: ReCaptchaModel,
|
|
34
|
-
logger: boolean
|
|
34
|
+
logger: boolean,
|
|
35
35
|
) => {
|
|
36
36
|
if (!storageKey || !config.websiteKey) return null;
|
|
37
37
|
|
package/src/lib/index.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
export * from './_extendConfig';
|
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
import { recaptchaMessage } from '../configs';
|
|
11
|
-
import {
|
|
12
|
-
MutationObserverInit,
|
|
13
|
-
ReCaptchaModel,
|
|
14
|
-
} from '../types/recaptcha.types';
|
|
11
|
+
import { MutationObserverInit, ReCaptchaModel } from '../types/recaptcha.types';
|
|
15
12
|
|
|
16
13
|
declare global {
|
|
17
14
|
interface Window {
|
|
@@ -23,7 +20,7 @@ const { failedExecutionRecaptcha } = recaptchaMessage;
|
|
|
23
20
|
|
|
24
21
|
export const getRecaptchaToken = async (
|
|
25
22
|
websiteKey: string,
|
|
26
|
-
isEnterprise = false
|
|
23
|
+
isEnterprise = false,
|
|
27
24
|
): Promise<string> => {
|
|
28
25
|
if (!window.grecaptcha) {
|
|
29
26
|
return Promise.reject(failedExecutionRecaptcha);
|
|
@@ -67,7 +64,7 @@ export const verifyReCaptchaLoad = async (
|
|
|
67
64
|
badgeId: string,
|
|
68
65
|
config: ReCaptchaModel,
|
|
69
66
|
logger: boolean,
|
|
70
|
-
isEnterprise = false
|
|
67
|
+
isEnterprise = false,
|
|
71
68
|
): Promise<void> => {
|
|
72
69
|
if (!window.grecaptcha) {
|
|
73
70
|
await waitForReCaptcha();
|
|
@@ -83,7 +80,7 @@ export const verifyReCaptchaLoad = async (
|
|
|
83
80
|
// Handle the case when multiple instances of the drop-in container rendered on the same page
|
|
84
81
|
|
|
85
82
|
badgeContainers.forEach(
|
|
86
|
-
(element) => (element.id = `${element.id}_${Math.random().toString(36)}`) // NOSONAR
|
|
83
|
+
(element) => (element.id = `${element.id}_${Math.random().toString(36)}`), // NOSONAR
|
|
87
84
|
);
|
|
88
85
|
|
|
89
86
|
badgeContainers.forEach((element) => {
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Copyright 2024 Adobe
|
|
3
3
|
* All Rights Reserved.
|
|
4
4
|
*
|
|
5
|
-
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
-
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
-
* accompanying it.
|
|
5
|
+
* NOTICE: Adobe permits you to use, modify, and distribute this
|
|
6
|
+
* file in accordance with the terms of the Adobe license agreement
|
|
7
|
+
* accompanying it.
|
|
8
8
|
*******************************************************************/
|
|
9
9
|
|
|
10
10
|
export interface ReCaptchaInitProps {
|
|
@@ -33,7 +33,11 @@ export interface ReCaptchaModifyProps extends ReCaptchaInitProps {
|
|
|
33
33
|
export interface ReCaptchaResponse {
|
|
34
34
|
data?: {
|
|
35
35
|
recaptchaConfig?: ReCaptchaProps | ReCaptchaModifyProps;
|
|
36
|
-
[key: string]:
|
|
36
|
+
[key: string]:
|
|
37
|
+
| ReCaptchaFormConfigResult
|
|
38
|
+
| ReCaptchaProps
|
|
39
|
+
| ReCaptchaModifyProps
|
|
40
|
+
| undefined;
|
|
37
41
|
};
|
|
38
42
|
errors?: { message: string }[];
|
|
39
43
|
}
|