@prosopo/procaptcha-bundle 4.1.33 → 4.1.35

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/procaptcha-bundle@4.1.33 build:cjs
2
+ > @prosopo/procaptcha-bundle@4.1.35 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -64,11 +64,11 @@ Build end
64
64
  rendering chunks...
65
65
  dist/cjs/util/timeout.cjs 0.61 kB
66
66
  dist/cjs/util/widgetThemeResolver.cjs 0.62 kB
67
- dist/cjs/util/language.cjs 0.67 kB
68
67
  dist/cjs/util/config.cjs 0.76 kB
69
68
  dist/cjs/util/captcha/components/bundleCaptcha.cjs 0.83 kB
69
+ dist/cjs/util/language.cjs 0.89 kB
70
70
  dist/cjs/util/configCreator.cjs 0.93 kB
71
71
  dist/cjs/util/captcha/captchaRenderer.cjs 2.21 kB
72
- dist/cjs/util/widgetFactory.cjs 2.82 kB
72
+ dist/cjs/util/widgetFactory.cjs 3.08 kB
73
73
  dist/cjs/index.cjs 5.79 kB
74
- ✓ built in 211ms
74
+ ✓ built in 147ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/procaptcha-bundle@4.1.33 build:tsc
2
+ > @prosopo/procaptcha-bundle@4.1.35 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 10:19:12 PM - Projects in this build:
5
+ 4:35:58 PM - Projects in this build:
6
6
  * ../../dev/config/tsconfig.json
7
7
  * ../util/tsconfig.json
8
8
  * ../logger/tsconfig.json
@@ -28,53 +28,53 @@
28
28
  * ../procaptcha-frictionless/tsconfig.json
29
29
  * tsconfig.json
30
30
 
31
- 10:19:12 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
31
+ 4:35:58 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/index.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
32
32
 
33
- 10:19:12 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
33
+ 4:35:58 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
34
34
 
35
- 10:19:12 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
35
+ 4:35:58 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
36
36
 
37
- 10:19:13 PM - Project '../dotenv/tsconfig.json' is up to date because newest input '../dotenv/src/env.ts' is older than output '../dotenv/tsconfig.tsbuildinfo'
37
+ 4:35:58 PM - Project '../dotenv/tsconfig.json' is up to date because newest input '../dotenv/src/env.ts' is older than output '../dotenv/tsconfig.tsbuildinfo'
38
38
 
39
- 10:19:13 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
39
+ 4:35:58 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
40
40
 
41
- 10:19:13 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
41
+ 4:35:58 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
42
42
 
43
- 10:19:13 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
43
+ 4:35:58 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/keyring/keyring/types.ts' is older than output '../types/tsconfig.tsbuildinfo'
44
44
 
45
- 10:19:13 PM - Project '../detector/tsconfig.json' is up to date because newest input '../detector/src/index.js' is older than output '../detector/tsconfig.tsbuildinfo'
45
+ 4:35:58 PM - Project '../detector/tsconfig.json' is up to date because newest input '../detector/src/index.js' is older than output '../detector/tsconfig.tsbuildinfo'
46
46
 
47
- 10:19:13 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
47
+ 4:35:58 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
48
48
 
49
- 10:19:13 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/utils/batches.ts' is older than output '../common/tsconfig.tsbuildinfo'
49
+ 4:35:58 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
50
50
 
51
- 10:19:13 PM - Project '../fingerprintjs/tsconfig.json' is up to date because newest input '../fingerprintjs/src/utils/browser.ts' is older than output '../fingerprintjs/tsconfig.tsbuildinfo'
51
+ 4:35:58 PM - Project '../fingerprintjs/tsconfig.json' is up to date because newest input '../fingerprintjs/src/utils/browser.ts' is older than output '../fingerprintjs/tsconfig.tsbuildinfo'
52
52
 
53
- 10:19:13 PM - Project '../fingerprint/tsconfig.json' is up to date because newest input '../fingerprint/src/index.ts' is older than output '../fingerprint/tsconfig.tsbuildinfo'
53
+ 4:35:58 PM - Project '../fingerprint/tsconfig.json' is up to date because newest input '../fingerprint/src/index.ts' is older than output '../fingerprint/tsconfig.tsbuildinfo'
54
54
 
55
- 10:19:13 PM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/balancer.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
55
+ 4:35:58 PM - Project '../load-balancer/tsconfig.json' is up to date because newest input '../load-balancer/src/balancer.ts' is older than output '../load-balancer/tsconfig.tsbuildinfo'
56
56
 
57
- 10:19:13 PM - Project '../widget-skeleton/tsconfig.json' is up to date because newest input '../widget-skeleton/src/webComponent/createWidget.ts' is older than output '../widget-skeleton/tsconfig.tsbuildinfo'
57
+ 4:35:58 PM - Project '../widget-skeleton/tsconfig.json' is up to date because newest input '../widget-skeleton/src/webComponent/createWidget.ts' is older than output '../widget-skeleton/tsconfig.tsbuildinfo'
58
58
 
59
- 10:19:13 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
59
+ 4:35:58 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
60
60
 
61
- 10:19:13 PM - Project '../account/tsconfig.json' is up to date because newest input '../account/src/index.ts' is older than output '../account/tsconfig.tsbuildinfo'
61
+ 4:35:58 PM - Project '../account/tsconfig.json' is up to date because newest input '../account/src/custom.d.ts' is older than output '../account/tsconfig.tsbuildinfo'
62
62
 
63
- 10:19:13 PM - Project '../procaptcha-common/tsconfig.json' is up to date because newest input '../procaptcha-common/src/tests/defaultEvents.test.ts' is older than output '../procaptcha-common/tsconfig.tsbuildinfo'
63
+ 4:35:58 PM - Project '../procaptcha-common/tsconfig.json' is up to date because newest input '../procaptcha-common/src/reactComponents/Honeypot.tsx' is older than output '../procaptcha-common/tsconfig.tsbuildinfo'
64
64
 
65
- 10:19:13 PM - Project '../procaptcha-pow/tsconfig.json' is up to date because newest input '../procaptcha-pow/src/index.ts' is older than output '../procaptcha-pow/tsconfig.tsbuildinfo'
65
+ 4:35:58 PM - Project '../procaptcha-pow/tsconfig.json' is up to date because newest input '../procaptcha-pow/src/index.ts' is older than output '../procaptcha-pow/tsconfig.tsbuildinfo'
66
66
 
67
- 10:19:13 PM - Project '../procaptcha-puzzle/tsconfig.json' is up to date because newest input '../procaptcha-puzzle/src/index.ts' is older than output '../procaptcha-puzzle/tsconfig.tsbuildinfo'
67
+ 4:35:58 PM - Project '../procaptcha-puzzle/tsconfig.json' is up to date because newest input '../procaptcha-puzzle/src/index.ts' is older than output '../procaptcha-puzzle/tsconfig.tsbuildinfo'
68
68
 
69
- 10:19:13 PM - Project '../datasets/tsconfig.json' is up to date because newest input '../datasets/src/tests/mocks/data/captchas.ts' is older than output '../datasets/tsconfig.tsbuildinfo'
69
+ 4:35:58 PM - Project '../datasets/tsconfig.json' is up to date because newest input '../datasets/src/tests/mocks/data/captchas.ts' is older than output '../datasets/tsconfig.tsbuildinfo'
70
70
 
71
- 10:19:13 PM - Project '../procaptcha/tsconfig.json' is up to date because newest input '../procaptcha/src/index.ts' is older than output '../procaptcha/tsconfig.tsbuildinfo'
71
+ 4:35:58 PM - Project '../procaptcha/tsconfig.json' is up to date because newest input '../procaptcha/src/modules/Manager.ts' is older than output '../procaptcha/tsconfig.tsbuildinfo'
72
72
 
73
- 10:19:13 PM - Project '../procaptcha-react/tsconfig.json' is up to date because newest input '../procaptcha-react/src/index.ts' is older than output '../procaptcha-react/tsconfig.tsbuildinfo'
73
+ 4:35:58 PM - Project '../procaptcha-react/tsconfig.json' is up to date because newest input '../procaptcha-react/src/index.ts' is older than output '../procaptcha-react/tsconfig.tsbuildinfo'
74
74
 
75
- 10:19:13 PM - Project '../procaptcha-frictionless/tsconfig.json' is up to date because newest input '../procaptcha-frictionless/src/customDetectBot.ts' is older than output '../procaptcha-frictionless/tsconfig.tsbuildinfo'
75
+ 4:35:58 PM - Project '../procaptcha-frictionless/tsconfig.json' is up to date because newest input '../procaptcha-frictionless/src/tests/customDetectBotSimd.test.ts' is older than output '../procaptcha-frictionless/tsconfig.tsbuildinfo'
76
76
 
77
- 10:19:13 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
77
+ 4:35:58 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
78
78
 
79
- 10:19:13 PM - Building project '/home/runner/work/captcha/captcha/packages/procaptcha-bundle/tsconfig.json'...
79
+ 4:35:58 PM - Building project '/home/runner/work/captcha/captcha/packages/procaptcha-bundle/tsconfig.json'...
80
80
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/procaptcha-bundle@4.1.33 build
2
+ > @prosopo/procaptcha-bundle@4.1.35 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/procaptcha-bundle@4.1.33 build:cross-env
6
+ > @prosopo/procaptcha-bundle@4.1.35 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -67,11 +67,11 @@ Build end
67
67
  rendering chunks...
68
68
  dist/util/timeout.js 0.50 kB
69
69
  dist/util/widgetThemeResolver.js 0.51 kB
70
- dist/util/language.js 0.57 kB
71
70
  dist/util/config.js 0.63 kB
72
71
  dist/util/captcha/components/bundleCaptcha.js 0.66 kB
72
+ dist/util/language.js 0.77 kB
73
73
  dist/util/configCreator.js 0.86 kB
74
74
  dist/util/captcha/captchaRenderer.js 2.04 kB
75
- dist/util/widgetFactory.js 2.65 kB
75
+ dist/util/widgetFactory.js 2.90 kB
76
76
  dist/index.js 5.55 kB
77
- ✓ built in 275ms
77
+ ✓ built in 216ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # @prosopo/procaptcha-bundle
2
2
 
3
+ ## 4.1.35
4
+ ### Patch Changes
5
+
6
+ - 7272dee: perf(procaptcha-bundle): force `@prosopo/fingerprint` + `@prosopo/fingerprintjs` into a single shared chunk (~220ms main-thread win)
7
+
8
+ Chrome timeline traces on a heavy page (pimeyes.com) showed **two nearly-identical ~220ms main-thread tasks** dominated by `getContext` / `getParameter` / `getImageData` — Canvas + WebGL fingerprint sources running twice per widget session. Different obfuscated function graphs in each task (`vr → jl → mr` vs `ge → kn → U`) and different chunk URLs (`index-Bty406ZI.js` vs `index-Cewgc2Jv.js`), while `@prosopo/fingerprint/index.ts` deliberately memoises via a module-local `componentsCache` — proof that Vite was inlining `@prosopo/fingerprint` into multiple downstream chunks (the widget bundle for `account.createAccount` → `getFingerprint`, and `procaptcha-pow` for `Manager` → `getFingerprintProof`), giving each chunk its own module instance and its own private cache.
9
+
10
+ `manualChunks` now routes `packages/fingerprint/dist` + `packages/fingerprintjs/dist` into one shared chunk (per-build opaque name, same pattern as `honeypotChunkName` — no `fingerprint` in the emitted URL). One physical chunk = one module instance = one populated `componentsCache` = one execution of the expensive Canvas + WebGL sources per widget session. Both `getFingerprint` and `getFingerprintProof` now hit the cache on the second call within a session, which was the original design intent.
11
+
12
+ Measured on staging widget injected into pimeyes.com:
13
+
14
+ - Before: 2 × ~220ms main-thread long tasks, ~2000 `getContext` self-samples on main
15
+ - After: 0 `getContext` self-samples on main, fingerprint dropped out of the main-thread long-task list entirely
16
+
17
+ No API change. Opaque chunk name means the emitted URL doesn't leak what's inside (won't help static URL-blocklist scrapers).
18
+ - b500d56: fix(widget): enforce single language across widget, kill browser/config race
19
+
20
+ `WidgetFactory.getCaptchaRenderer()` booted the i18n singleton with the
21
+ browser-detected language before the site-owner `renderOptions.language` /
22
+ `data-language` had been resolved, and each widget then called
23
+ `i18n.changeLanguage(config.language)` from a post-mount effect. Any child
24
+ component that read `useTranslation()` between first render and the async
25
+ `changeLanguage` resolution rendered in the browser language, then re-rendered
26
+ in the site-owner language — the multi-language flash customers reported.
27
+
28
+ Resolve the site-owner language in `WidgetFactory.createWidget()` before the
29
+ lazy renderer load and thread it into `loadI18next(false, lng)`, so the
30
+ singleton boots (or reconciles via `changeLanguage` + await) with the correct
31
+ language before React mounts. Site-owner language wins; falls back to browser
32
+ detection only when no `language` / `data-language` is set.
33
+ - Updated dependencies [b500d56]
34
+ - @prosopo/locale@3.2.7
35
+ - @prosopo/procaptcha-frictionless@2.12.16
36
+ - @prosopo/types@4.9.9
37
+ - @prosopo/procaptcha-common@2.11.13
38
+
39
+ ## 4.1.34
40
+ ### Patch Changes
41
+
42
+ - Updated dependencies [d0f3a52]
43
+ - @prosopo/procaptcha-frictionless@2.12.15
44
+
3
45
  ## 4.1.33
4
46
  ### Patch Changes
5
47
 
@@ -2,10 +2,17 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const locale = require("@prosopo/locale");
4
4
  const setLanguage = (renderOptions, element, config) => {
5
+ const resolved = resolveLanguage(renderOptions, element);
6
+ if (resolved) {
7
+ config.language = resolved;
8
+ }
9
+ };
10
+ const resolveLanguage = (renderOptions, element) => {
5
11
  const languageAttribute = renderOptions?.language || element.getAttribute("data-language");
6
- if (languageAttribute) {
7
- config.language = validateLanguage(languageAttribute);
12
+ if (!languageAttribute) {
13
+ return void 0;
8
14
  }
15
+ return validateLanguage(languageAttribute);
9
16
  };
10
17
  const validateLanguage = (languageAttribute) => {
11
18
  try {
@@ -15,4 +22,5 @@ const validateLanguage = (languageAttribute) => {
15
22
  return locale.LanguageSchema.parse("en");
16
23
  }
17
24
  };
25
+ exports.resolveLanguage = resolveLanguage;
18
26
  exports.setLanguage = setLanguage;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const locale = require("@prosopo/locale");
4
4
  const procaptchaCommon = require("@prosopo/procaptcha-common");
5
5
  const widgetSkeleton = require("@prosopo/widget-skeleton");
6
+ const language = require("./language.cjs");
6
7
  class WidgetFactory {
7
8
  constructor(widgetThemeResolver) {
8
9
  this.widgetThemeResolver = widgetThemeResolver;
@@ -52,7 +53,8 @@ class WidgetFactory {
52
53
  widgetInteractiveArea = widgetResult.widgetInteractiveArea;
53
54
  widgetContainer = widgetResult.webComponent;
54
55
  }
55
- const captchaRenderer = await this.getCaptchaRenderer();
56
+ const language$1 = language.resolveLanguage(renderOptions, container);
57
+ const captchaRenderer = await this.getCaptchaRenderer(language$1);
56
58
  const captchaRoot = captchaRenderer.renderCaptcha(
57
59
  {
58
60
  identifierPrefix: "procaptcha-",
@@ -70,9 +72,11 @@ class WidgetFactory {
70
72
  );
71
73
  return captchaRoot;
72
74
  }
73
- async getCaptchaRenderer() {
75
+ async getCaptchaRenderer(language2) {
74
76
  if (this._i18n === null) {
75
- this._i18n = await locale.loadI18next(false);
77
+ this._i18n = await locale.loadI18next(false, language2);
78
+ } else if (language2 && this._i18n.language !== language2) {
79
+ await this._i18n.changeLanguage(language2);
76
80
  }
77
81
  if (this.captchaRenderer === null) {
78
82
  this.captchaRenderer = await this.createCaptchaRenderer();
@@ -1,3 +1,4 @@
1
1
  import type { ProcaptchaClientConfigInput, ProcaptchaRenderOptions } from "@prosopo/types";
2
2
  export declare const setLanguage: (renderOptions: ProcaptchaRenderOptions | undefined, element: Element, config: ProcaptchaClientConfigInput) => void;
3
+ export declare const resolveLanguage: (renderOptions: ProcaptchaRenderOptions | undefined, element: Element) => string | undefined;
3
4
  //# sourceMappingURL=language.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"language.d.ts","sourceRoot":"","sources":["../../src/util/language.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACX,2BAA2B,EAC3B,uBAAuB,EACvB,MAAM,gBAAgB,CAAC;AAcxB,eAAO,MAAM,WAAW,kBACR,uBAAuB,GAAG,SAAS,WACzC,OAAO,UACR,2BAA2B,SAQnC,CAAC"}
1
+ {"version":3,"file":"language.d.ts","sourceRoot":"","sources":["../../src/util/language.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACX,2BAA2B,EAC3B,uBAAuB,EACvB,MAAM,gBAAgB,CAAC;AAcxB,eAAO,MAAM,WAAW,kBACR,uBAAuB,GAAG,SAAS,WACzC,OAAO,UACR,2BAA2B,SAOnC,CAAC;AAMF,eAAO,MAAM,eAAe,kBACZ,uBAAuB,GAAG,SAAS,WACzC,OAAO,KACd,MAAM,GAAG,SASX,CAAC"}
@@ -1,9 +1,16 @@
1
1
  import { LanguageSchema } from "@prosopo/locale";
2
2
  const setLanguage = (renderOptions, element, config) => {
3
+ const resolved = resolveLanguage(renderOptions, element);
4
+ if (resolved) {
5
+ config.language = resolved;
6
+ }
7
+ };
8
+ const resolveLanguage = (renderOptions, element) => {
3
9
  const languageAttribute = renderOptions?.language || element.getAttribute("data-language");
4
- if (languageAttribute) {
5
- config.language = validateLanguage(languageAttribute);
10
+ if (!languageAttribute) {
11
+ return void 0;
6
12
  }
13
+ return validateLanguage(languageAttribute);
7
14
  };
8
15
  const validateLanguage = (languageAttribute) => {
9
16
  try {
@@ -14,5 +21,6 @@ const validateLanguage = (languageAttribute) => {
14
21
  }
15
22
  };
16
23
  export {
24
+ resolveLanguage,
17
25
  setLanguage
18
26
  };
@@ -1 +1 @@
1
- {"version":3,"file":"language.js","sourceRoot":"","sources":["../../src/util/language.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,cAAc,EAAkB,MAAM,iBAAiB,CAAC;AAkBjE,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,aAAkD,EAClD,OAAgB,EAChB,MAAmC,EAClC,EAAE;IACH,MAAM,iBAAiB,GACtB,aAAa,EAAE,QAAQ,IAAI,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;IAElE,IAAI,iBAAiB,EAAE,CAAC;QACvB,MAAM,CAAC,QAAQ,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;IACvD,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,iBAA4C,EAAE,EAAE;IACzE,IAAI,CAAC;QACJ,OAAO,cAAc,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,+BAA+B,iBAAiB,EAAE,CAAC,CAAC;QAClE,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;AACF,CAAC,CAAC"}
1
+ {"version":3,"file":"language.js","sourceRoot":"","sources":["../../src/util/language.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,cAAc,EAAkB,MAAM,iBAAiB,CAAC;AAkBjE,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,aAAkD,EAClD,OAAgB,EAChB,MAAmC,EAClC,EAAE;IACH,MAAM,QAAQ,GAAG,eAAe,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAEzD,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC5B,CAAC;AACF,CAAC,CAAC;AAMF,MAAM,CAAC,MAAM,eAAe,GAAG,CAC9B,aAAkD,EAClD,OAAgB,EACK,EAAE;IACvB,MAAM,iBAAiB,GACtB,aAAa,EAAE,QAAQ,IAAI,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;IAElE,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,OAAO,gBAAgB,CAAC,iBAAiB,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,iBAA4C,EAAE,EAAE;IACzE,IAAI,CAAC;QACJ,OAAO,cAAc,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,+BAA+B,iBAAiB,EAAE,CAAC,CAAC;QAClE,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;AACF,CAAC,CAAC"}
@@ -11,7 +11,7 @@ declare class WidgetFactory {
11
11
  get i18n(): Ti18n;
12
12
  createWidgets(containers: Element[], renderOptions: ProcaptchaRenderOptions, isWeb2?: boolean, invisible?: boolean): Promise<Root[]>;
13
13
  createWidget(container: Element, renderOptions: ProcaptchaRenderOptions, callbacks: Callbacks, isWeb2?: boolean, invisible?: boolean): Promise<Root>;
14
- protected getCaptchaRenderer(): Promise<CaptchaRenderer>;
14
+ protected getCaptchaRenderer(language?: string): Promise<CaptchaRenderer>;
15
15
  protected createCaptchaRenderer(): Promise<CaptchaRenderer>;
16
16
  }
17
17
  export { WidgetFactory };
@@ -1 +1 @@
1
- {"version":3,"file":"widgetFactory.d.ts","sourceRoot":"","sources":["../../src/util/widgetFactory.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAK7C,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAMzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,cAAM,aAAa;IAKjB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAJrC,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,KAAK,CAAsB;gBAGjB,mBAAmB,EAAE,mBAAmB;IAG1D,IAAI,IAAI,IAAI,KAAK,CAKhB;IAEY,aAAa,CACzB,UAAU,EAAE,OAAO,EAAE,EACrB,aAAa,EAAE,uBAAuB,EACtC,MAAM,UAAO,EACb,SAAS,UAAQ,GACf,OAAO,CAAC,IAAI,EAAE,CAAC;IAgBL,YAAY,CACxB,SAAS,EAAE,OAAO,EAClB,aAAa,EAAE,uBAAuB,EACtC,SAAS,EAAE,SAAS,EACpB,MAAM,UAAO,EACb,SAAS,UAAQ,GACf,OAAO,CAAC,IAAI,CAAC;cAoDA,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC;cAY9C,qBAAqB,IAAI,OAAO,CAAC,eAAe,CAAC;CAMjE;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"widgetFactory.d.ts","sourceRoot":"","sources":["../../src/util/widgetFactory.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAK7C,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAMzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,cAAM,aAAa;IAKjB,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAJrC,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,KAAK,CAAsB;gBAGjB,mBAAmB,EAAE,mBAAmB;IAG1D,IAAI,IAAI,IAAI,KAAK,CAKhB;IAEY,aAAa,CACzB,UAAU,EAAE,OAAO,EAAE,EACrB,aAAa,EAAE,uBAAuB,EACtC,MAAM,UAAO,EACb,SAAS,UAAQ,GACf,OAAO,CAAC,IAAI,EAAE,CAAC;IAgBL,YAAY,CACxB,SAAS,EAAE,OAAO,EAClB,aAAa,EAAE,uBAAuB,EACtC,SAAS,EAAE,SAAS,EACpB,MAAM,UAAO,EACb,SAAS,UAAQ,GACf,OAAO,CAAC,IAAI,CAAC;cA2DA,kBAAkB,CACjC,QAAQ,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,eAAe,CAAC;cAiBX,qBAAqB,IAAI,OAAO,CAAC,eAAe,CAAC;CAMjE;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { loadI18next } from "@prosopo/locale";
2
2
  import { getDefaultCallbacks, setUserCallbacks } from "@prosopo/procaptcha-common";
3
3
  import { lightTheme, darkTheme, createWidgetSkeleton } from "@prosopo/widget-skeleton";
4
+ import { resolveLanguage } from "./language.js";
4
5
  class WidgetFactory {
5
6
  constructor(widgetThemeResolver) {
6
7
  this.widgetThemeResolver = widgetThemeResolver;
@@ -50,7 +51,8 @@ class WidgetFactory {
50
51
  widgetInteractiveArea = widgetResult.widgetInteractiveArea;
51
52
  widgetContainer = widgetResult.webComponent;
52
53
  }
53
- const captchaRenderer = await this.getCaptchaRenderer();
54
+ const language = resolveLanguage(renderOptions, container);
55
+ const captchaRenderer = await this.getCaptchaRenderer(language);
54
56
  const captchaRoot = captchaRenderer.renderCaptcha(
55
57
  {
56
58
  identifierPrefix: "procaptcha-",
@@ -68,9 +70,11 @@ class WidgetFactory {
68
70
  );
69
71
  return captchaRoot;
70
72
  }
71
- async getCaptchaRenderer() {
73
+ async getCaptchaRenderer(language) {
72
74
  if (this._i18n === null) {
73
- this._i18n = await loadI18next(false);
75
+ this._i18n = await loadI18next(false, language);
76
+ } else if (language && this._i18n.language !== language) {
77
+ await this._i18n.changeLanguage(language);
74
78
  }
75
79
  if (this.captchaRenderer === null) {
76
80
  this.captchaRenderer = await this.createCaptchaRenderer();
@@ -1 +1 @@
1
- {"version":3,"file":"widgetFactory.js","sourceRoot":"","sources":["../../src/util/widgetFactory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"widgetFactory.js","sourceRoot":"","sources":["../../src/util/widgetFactory.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/procaptcha-bundle",
3
- "version": "4.1.33",
3
+ "version": "4.1.35",
4
4
  "author": "PROSOPO LIMITED <info@prosopo.io>",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -40,10 +40,10 @@
40
40
  "browserslist": ["> 0.5%, last 2 versions, not dead"],
41
41
  "dependencies": {
42
42
  "@prosopo/dotenv": "3.0.49",
43
- "@prosopo/locale": "3.2.6",
44
- "@prosopo/procaptcha-common": "2.11.12",
45
- "@prosopo/procaptcha-frictionless": "2.12.14",
46
- "@prosopo/types": "4.9.8",
43
+ "@prosopo/locale": "3.2.7",
44
+ "@prosopo/procaptcha-common": "2.11.13",
45
+ "@prosopo/procaptcha-frictionless": "2.12.16",
46
+ "@prosopo/types": "4.9.9",
47
47
  "@prosopo/util": "3.3.4",
48
48
  "@prosopo/widget-skeleton": "2.8.4",
49
49
  "dotenv": "16.4.5",
@@ -35,12 +35,29 @@ export const setLanguage = (
35
35
  element: Element,
36
36
  config: ProcaptchaClientConfigInput,
37
37
  ) => {
38
+ const resolved = resolveLanguage(renderOptions, element);
39
+
40
+ if (resolved) {
41
+ config.language = resolved;
42
+ }
43
+ };
44
+
45
+ // Resolves the site-owner language before an i18n singleton has been created,
46
+ // so the widget can boot i18n with the correct language on first init instead
47
+ // of having to run a post-mount changeLanguage() effect. Returning `undefined`
48
+ // preserves the existing "fall back to browser detection" behaviour.
49
+ export const resolveLanguage = (
50
+ renderOptions: ProcaptchaRenderOptions | undefined,
51
+ element: Element,
52
+ ): string | undefined => {
38
53
  const languageAttribute =
39
54
  renderOptions?.language || element.getAttribute("data-language");
40
55
 
41
- if (languageAttribute) {
42
- config.language = validateLanguage(languageAttribute);
56
+ if (!languageAttribute) {
57
+ return undefined;
43
58
  }
59
+
60
+ return validateLanguage(languageAttribute);
44
61
  };
45
62
 
46
63
  const validateLanguage = (languageAttribute: string | typeof Languages) => {
@@ -26,6 +26,7 @@ import {
26
26
  } from "@prosopo/widget-skeleton";
27
27
  import type { Root } from "react-dom/client";
28
28
  import type { CaptchaRenderer } from "./captcha/captchaRenderer.js";
29
+ import { resolveLanguage } from "./language.js";
29
30
  import type { WidgetThemeResolver } from "./widgetThemeResolver.js";
30
31
 
31
32
  class WidgetFactory {
@@ -99,9 +100,16 @@ class WidgetFactory {
99
100
  widgetContainer = widgetResult.webComponent;
100
101
  }
101
102
 
103
+ // Resolve the site-owner language BEFORE lazy-loading the renderer so
104
+ // i18n can boot (or reconcile) with the correct language on first init,
105
+ // rather than starting in the browser-detected language and swapping to
106
+ // the site-owner language via a post-mount effect (which caused mixed-
107
+ // language flashes visible to end users).
108
+ const language = resolveLanguage(renderOptions, container);
109
+
102
110
  // all the captcha-rendering logic is lazy-loaded, to avoid react & zod delay the initial widget creation.
103
111
 
104
- const captchaRenderer = await this.getCaptchaRenderer();
112
+ const captchaRenderer = await this.getCaptchaRenderer(language);
105
113
 
106
114
  const captchaRoot = captchaRenderer.renderCaptcha(
107
115
  {
@@ -122,9 +130,16 @@ class WidgetFactory {
122
130
  return captchaRoot;
123
131
  }
124
132
 
125
- protected async getCaptchaRenderer(): Promise<CaptchaRenderer> {
133
+ protected async getCaptchaRenderer(
134
+ language?: string,
135
+ ): Promise<CaptchaRenderer> {
126
136
  if (this._i18n === null) {
127
- this._i18n = await loadI18next(false);
137
+ this._i18n = await loadI18next(false, language);
138
+ } else if (language && this._i18n.language !== language) {
139
+ // A subsequent widget on the same page requested a different
140
+ // language than the cached singleton — reconcile before rendering
141
+ // so all widgets on the page share one consistent language state.
142
+ await this._i18n.changeLanguage(language);
128
143
  }
129
144
 
130
145
  if (this.captchaRenderer === null) {