@prosopo/procaptcha-frictionless 2.12.24 → 2.13.1

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.
Files changed (39) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +13 -14
  2. package/.turbo/turbo-build$colon$tsc.log +24 -27
  3. package/.turbo/turbo-build.log +14 -15
  4. package/CHANGELOG.md +41 -0
  5. package/dist/cjs/customDetectBot.cjs +48 -9
  6. package/dist/cjs/detectorLoader.cjs +18 -2
  7. package/dist/cjs/detectorPrefetch.cjs +65 -0
  8. package/dist/cjs/index.cjs +4 -0
  9. package/dist/customDetectBot.d.ts.map +1 -1
  10. package/dist/customDetectBot.js +49 -10
  11. package/dist/customDetectBot.js.map +1 -1
  12. package/dist/detectorLoader.d.ts +30 -3
  13. package/dist/detectorLoader.d.ts.map +1 -1
  14. package/dist/detectorLoader.js +18 -2
  15. package/dist/detectorLoader.js.map +1 -1
  16. package/dist/detectorPrefetch.d.ts +12 -0
  17. package/dist/detectorPrefetch.d.ts.map +1 -0
  18. package/dist/detectorPrefetch.js +63 -0
  19. package/dist/detectorPrefetch.js.map +1 -0
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +2 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/tests/customDetectBotSimd.test.js +10 -1
  25. package/dist/tests/customDetectBotSimd.test.js.map +1 -1
  26. package/dist/tests/detectorPrefetch.test.d.ts +2 -0
  27. package/dist/tests/detectorPrefetch.test.d.ts.map +1 -0
  28. package/dist/tests/detectorPrefetch.test.js +71 -0
  29. package/dist/tests/detectorPrefetch.test.js.map +1 -0
  30. package/package.json +9 -10
  31. package/src/customDetectBot.ts +154 -59
  32. package/src/detectorLoader.ts +65 -3
  33. package/src/detectorPrefetch.ts +115 -0
  34. package/src/index.ts +1 -0
  35. package/src/tests/customDetectBotSimd.test.ts +11 -1
  36. package/src/tests/detectorPrefetch.test.ts +114 -0
  37. package/tsconfig.cjs.json +0 -3
  38. package/tsconfig.json +0 -3
  39. package/tsconfig.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"detectorLoader.js","sourceRoot":"","sources":["../src/detectorLoader.ts"],"names":[],"mappings":"AAgBA,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,IAA2B,EAAE,CAC/D,CAAC,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC"}
1
+ {"version":3,"file":"detectorLoader.js","sourceRoot":"","sources":["../src/detectorLoader.ts"],"names":[],"mappings":"AAkEA,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,EAC5C,MAAc,EACU,EAAE;IAC1B,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,CAAC,MAAM,MAAM,CAAoB,GAAG,CAAC,CAEhD,CAAC;QACF,OAAO,GAAG,CAAC,OAAO,CAAC;IACpB,CAAC;YAAS,CAAC;QACV,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;AACF,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { getProcaptchaRandomActiveProvider } from "@prosopo/procaptcha-common";
2
+ import type { AssignDetectorBundleResponse, EnvironmentTypes, RandomProvider } from "@prosopo/types";
3
+ type IpModeParam = Parameters<typeof getProcaptchaRandomActiveProvider>[1];
4
+ export interface PrefetchedDetector {
5
+ provider: RandomProvider;
6
+ assigned: AssignDetectorBundleResponse;
7
+ }
8
+ export declare const prefetchDetector: (environment: EnvironmentTypes, ipMode: IpModeParam, siteKey: string) => void;
9
+ export declare const takePrefetchedDetector: (environment: EnvironmentTypes, ipMode: IpModeParam, siteKey: string) => Promise<PrefetchedDetector> | undefined;
10
+ export declare const clearPrefetchedDetectors: () => void;
11
+ export {};
12
+ //# sourceMappingURL=detectorPrefetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectorPrefetch.d.ts","sourceRoot":"","sources":["../src/detectorPrefetch.ts"],"names":[],"mappings":"AAoCA,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AAC/E,OAAO,KAAK,EACX,4BAA4B,EAC5B,gBAAgB,EAChB,cAAc,EACd,MAAM,gBAAgB,CAAC;AAMxB,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,iCAAiC,CAAC,CAAC,CAAC,CAAC,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,cAAc,CAAC;IACzB,QAAQ,EAAE,4BAA4B,CAAC;CACvC;AAmBD,eAAO,MAAM,gBAAgB,gBACf,gBAAgB,UACrB,WAAW,WACV,MAAM,KACb,IAmBF,CAAC;AAMF,eAAO,MAAM,sBAAsB,gBACrB,gBAAgB,UACrB,WAAW,WACV,MAAM,KACb,OAAO,CAAC,kBAAkB,CAAC,GAAG,SAKhC,CAAC;AAGF,eAAO,MAAM,wBAAwB,QAAO,IAE3C,CAAC"}
@@ -0,0 +1,63 @@
1
+ import { getProcaptchaRandomActiveProvider } from "@prosopo/procaptcha-common";
2
+ import { ProviderApi } from "@prosopo/api";
3
+ //#region src/detectorPrefetch.ts
4
+ /**
5
+ * Detector bundle prefetch.
6
+ *
7
+ * Since the detector moved into the provider-served pool, the frictionless flow
8
+ * cannot start until `/detector/assign` has returned. That request is issued by
9
+ * `customDetectBot`, which only runs once React has mounted the widget — and
10
+ * that mount sits behind the bundle's dynamic-import chain. Measured on a
11
+ * staging demo page the assign request did not leave the browser until 1513 ms,
12
+ * of which ~700 ms was purely waiting for chunks to arrive in sequence.
13
+ *
14
+ * Nothing in the request depends on React, i18n or the widget config: it needs
15
+ * the site key (a DOM attribute, readable immediately), the environment (a
16
+ * build-time constant) and the IP-mode flags (DOM attributes). So the entry
17
+ * point kicks it off as soon as it has read those, and `customDetectBot` picks
18
+ * up the in-flight promise instead of starting its own.
19
+ *
20
+ * The cache is deliberately single-use. A provider pin is only valid for the
21
+ * attempt it was made for — on a retry the previous pronode is the one that
22
+ * just failed — so a consumed entry is dropped and the retry re-resolves.
23
+ */
24
+ var inFlight = /* @__PURE__ */ new Map();
25
+ var keyOf = (environment, ipMode, siteKey) => `${environment}|${ipMode ?? "auto"}|${siteKey}`;
26
+ /**
27
+ * Start resolving a provider and assigning a detector bundle. Safe to call more
28
+ * than once for the same key — subsequent calls join the in-flight request.
29
+ *
30
+ * Never rejects to the caller: a failed prefetch is indistinguishable from
31
+ * never having prefetched, and `customDetectBot` already handles assign failure
32
+ * by falling back to PoW. Returning a rejected promise here would surface as an
33
+ * unhandled rejection in the host page.
34
+ */
35
+ var prefetchDetector = (environment, ipMode, siteKey) => {
36
+ const key = keyOf(environment, ipMode, siteKey);
37
+ if (inFlight.has(key)) return;
38
+ const promise = (async () => {
39
+ const provider = await getProcaptchaRandomActiveProvider(environment, ipMode);
40
+ return {
41
+ provider,
42
+ assigned: await new ProviderApi(provider.provider.url, siteKey).assignDetectorBundle(siteKey)
43
+ };
44
+ })();
45
+ promise.catch(() => void 0);
46
+ inFlight.set(key, promise);
47
+ };
48
+ /**
49
+ * Claim a prefetched assignment, if one was started for this exact key. The
50
+ * entry is removed, so a retry does not reuse a pin that may have just failed.
51
+ */
52
+ var takePrefetchedDetector = (environment, ipMode, siteKey) => {
53
+ const key = keyOf(environment, ipMode, siteKey);
54
+ const promise = inFlight.get(key);
55
+ if (promise) inFlight.delete(key);
56
+ return promise;
57
+ };
58
+ /** Test seam — drops any in-flight prefetches. */
59
+ var clearPrefetchedDetectors = () => {
60
+ inFlight.clear();
61
+ };
62
+ //#endregion
63
+ export { clearPrefetchedDetectors, prefetchDetector, takePrefetchedDetector };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectorPrefetch.js","sourceRoot":"","sources":["../src/detectorPrefetch.ts"],"names":[],"mappings":"AAmCA,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AAkB/E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuC,CAAC;AAEhE,MAAM,KAAK,GAAG,CACb,WAA6B,EAC7B,MAAmB,EACnB,OAAe,EACN,EAAE,CAAC,GAAG,WAAW,IAAI,MAAM,IAAI,MAAM,IAAI,OAAO,EAAE,CAAC;AAW7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,WAA6B,EAC7B,MAAmB,EACnB,OAAe,EACR,EAAE;IACT,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO;IAE9B,MAAM,OAAO,GAAG,CAAC,KAAK,IAAiC,EAAE;QACxD,MAAM,QAAQ,GAAG,MAAM,iCAAiC,CACvD,WAAW,EACX,MAAM,CACN,CAAC;QACF,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACpE,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACjE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IAC/B,CAAC,CAAC,EAAE,CAAC;IAKL,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC/B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC,CAAC;AAMF,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACrC,WAA6B,EAC7B,MAAmB,EACnB,OAAe,EAC2B,EAAE;IAC5C,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAChD,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,OAAO;QAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAGF,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAS,EAAE;IAClD,QAAQ,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from "./ProcaptchaFrictionless.js";
2
+ export * from "./detectorPrefetch.js";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  import "./_virtual/_rolldown/runtime.js";
2
+ import { clearPrefetchedDetectors, prefetchDetector, takePrefetchedDetector } from "./detectorPrefetch.js";
2
3
  import { ProcaptchaFrictionless } from "./ProcaptchaFrictionless.js";
3
- export { ProcaptchaFrictionless };
4
+ export { ProcaptchaFrictionless, clearPrefetchedDetectors, prefetchDetector, takePrefetchedDetector };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,uBAAuB,CAAC"}
@@ -2,12 +2,14 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
2
2
  const mocks = vi.hoisted(() => ({
3
3
  getFrictionlessCaptcha: vi.fn(),
4
4
  getProcaptchaRandomActiveProvider: vi.fn(),
5
+ assignDetectorBundle: vi.fn(),
5
6
  detect: vi.fn(),
6
7
  }));
7
8
  vi.mock("@prosopo/api", () => ({
8
9
  ProviderApi: vi.fn(function () {
9
10
  return {
10
11
  getFrictionlessCaptcha: mocks.getFrictionlessCaptcha,
12
+ assignDetectorBundle: mocks.assignDetectorBundle,
11
13
  };
12
14
  }),
13
15
  }));
@@ -25,7 +27,7 @@ vi.mock("@prosopo/procaptcha-common", () => ({
25
27
  pickIpMode: vi.fn(() => undefined),
26
28
  }));
27
29
  vi.mock("../detectorLoader.js", () => ({
28
- DetectorLoader: vi.fn(async () => mocks.detect),
30
+ DetectorLoaderFromScript: vi.fn(async () => mocks.detect),
29
31
  }));
30
32
  import customDetectBot from "../customDetectBot.js";
31
33
  const baseConfig = {
@@ -51,11 +53,18 @@ const captchaResponse = {
51
53
  beforeEach(() => {
52
54
  mocks.getFrictionlessCaptcha.mockReset();
53
55
  mocks.getProcaptchaRandomActiveProvider.mockReset();
56
+ mocks.assignDetectorBundle.mockReset();
54
57
  mocks.detect.mockReset();
55
58
  mocks.getProcaptchaRandomActiveProvider.mockResolvedValue({
56
59
  providerAccount: "dns-routed",
57
60
  provider: { url: "https://provider.test" },
58
61
  });
62
+ mocks.assignDetectorBundle.mockResolvedValue({
63
+ useProviderBundle: true,
64
+ detectorSessionId: "det-1",
65
+ detectorScript: "SELF_CONTAINED_ESM",
66
+ status: "ok",
67
+ });
59
68
  mocks.getFrictionlessCaptcha.mockResolvedValue(captchaResponse);
60
69
  });
61
70
  describe("customDetectBot SIMD deferral", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"customDetectBotSimd.test.js","sourceRoot":"","sources":["../../src/tests/customDetectBotSimd.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"customDetectBotSimd.test.js","sourceRoot":"","sources":["../../src/tests/customDetectBotSimd.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=detectorPrefetch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectorPrefetch.test.d.ts","sourceRoot":"","sources":["../../src/tests/detectorPrefetch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,71 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+ const assignDetectorBundle = vi.fn();
3
+ const getProcaptchaRandomActiveProvider = vi.fn();
4
+ vi.mock("@prosopo/api", () => ({
5
+ ProviderApi: class {
6
+ constructor() {
7
+ this.assignDetectorBundle = assignDetectorBundle;
8
+ }
9
+ },
10
+ }));
11
+ vi.mock("@prosopo/procaptcha-common", () => ({
12
+ getProcaptchaRandomActiveProvider: (...args) => getProcaptchaRandomActiveProvider(...args),
13
+ }));
14
+ const { prefetchDetector, takePrefetchedDetector, clearPrefetchedDetectors } = await import("../detectorPrefetch.js");
15
+ const ENV = "staging";
16
+ const SITE_KEY = "5CcNvLUdiXFpzKDMjThGLSK9rhWHA1H4EF3zrgkpkjAdqmuP";
17
+ const provider = { provider: { url: "https://pronode.example" } };
18
+ afterEach(() => {
19
+ clearPrefetchedDetectors();
20
+ vi.clearAllMocks();
21
+ });
22
+ describe("detectorPrefetch", () => {
23
+ it("returns undefined when nothing was prefetched", () => {
24
+ expect(takePrefetchedDetector(ENV, undefined, SITE_KEY)).toBeUndefined();
25
+ });
26
+ it("resolves the provider and assigns a bundle", async () => {
27
+ getProcaptchaRandomActiveProvider.mockResolvedValue(provider);
28
+ assignDetectorBundle.mockResolvedValue({ useProviderBundle: true });
29
+ prefetchDetector(ENV, undefined, SITE_KEY);
30
+ const claimed = takePrefetchedDetector(ENV, undefined, SITE_KEY);
31
+ expect(claimed).toBeDefined();
32
+ const result = await claimed;
33
+ expect(result).toStrictEqual({
34
+ provider,
35
+ assigned: { useProviderBundle: true },
36
+ });
37
+ expect(assignDetectorBundle).toHaveBeenCalledWith(SITE_KEY);
38
+ });
39
+ it("is single-use, so a retry does not reuse a stale provider pin", async () => {
40
+ getProcaptchaRandomActiveProvider.mockResolvedValue(provider);
41
+ assignDetectorBundle.mockResolvedValue({ useProviderBundle: true });
42
+ prefetchDetector(ENV, undefined, SITE_KEY);
43
+ const first = takePrefetchedDetector(ENV, undefined, SITE_KEY);
44
+ await first;
45
+ expect(takePrefetchedDetector(ENV, undefined, SITE_KEY)).toBeUndefined();
46
+ });
47
+ it("does not start a second request for the same key", () => {
48
+ getProcaptchaRandomActiveProvider.mockResolvedValue(provider);
49
+ assignDetectorBundle.mockResolvedValue({ useProviderBundle: true });
50
+ prefetchDetector(ENV, undefined, SITE_KEY);
51
+ prefetchDetector(ENV, undefined, SITE_KEY);
52
+ expect(getProcaptchaRandomActiveProvider).toHaveBeenCalledTimes(1);
53
+ });
54
+ it("keys on site key and ip mode, so a different widget does not claim it", () => {
55
+ getProcaptchaRandomActiveProvider.mockResolvedValue(provider);
56
+ assignDetectorBundle.mockResolvedValue({ useProviderBundle: true });
57
+ prefetchDetector(ENV, "ipv4", SITE_KEY);
58
+ expect(takePrefetchedDetector(ENV, "ipv6", SITE_KEY)).toBeUndefined();
59
+ expect(takePrefetchedDetector(ENV, undefined, SITE_KEY)).toBeUndefined();
60
+ expect(takePrefetchedDetector(ENV, "ipv4", "other-key")).toBeUndefined();
61
+ expect(takePrefetchedDetector(ENV, "ipv4", SITE_KEY)).toBeDefined();
62
+ });
63
+ it("surfaces failure to the claimant without an unhandled rejection", async () => {
64
+ getProcaptchaRandomActiveProvider.mockRejectedValue(new Error("no providers"));
65
+ prefetchDetector(ENV, undefined, SITE_KEY);
66
+ const claimed = takePrefetchedDetector(ENV, undefined, SITE_KEY);
67
+ expect(claimed).toBeDefined();
68
+ await expect(claimed).rejects.toThrow("no providers");
69
+ });
70
+ });
71
+ //# sourceMappingURL=detectorPrefetch.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detectorPrefetch.test.js","sourceRoot":"","sources":["../../src/tests/detectorPrefetch.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/procaptcha-frictionless",
3
- "version": "2.12.24",
3
+ "version": "2.13.1",
4
4
  "author": "PROSOPO LIMITED <info@prosopo.io>",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -30,21 +30,20 @@
30
30
  },
31
31
  "browserslist": ["> 0.5%, last 2 versions, not dead"],
32
32
  "dependencies": {
33
- "@prosopo/api": "3.5.21",
33
+ "@prosopo/api": "4.0.0",
34
34
  "@prosopo/common": "3.1.48",
35
- "@prosopo/detector": "3.5.14",
36
35
  "@prosopo/locale": "3.2.8",
37
- "@prosopo/procaptcha-common": "2.11.19",
38
- "@prosopo/procaptcha-pow": "2.10.26",
39
- "@prosopo/procaptcha-puzzle": "2.10.42",
40
- "@prosopo/procaptcha-react": "2.9.99",
41
- "@prosopo/types": "4.10.0",
42
- "@prosopo/widget-skeleton": "2.8.4",
36
+ "@prosopo/procaptcha-common": "2.11.20",
37
+ "@prosopo/procaptcha-pow": "2.10.27",
38
+ "@prosopo/procaptcha-puzzle": "2.10.43",
39
+ "@prosopo/procaptcha-react": "2.9.100",
40
+ "@prosopo/types": "5.0.0",
41
+ "@prosopo/widget-skeleton": "2.8.5",
43
42
  "dotenv": "16.4.5",
44
43
  "react": "18.3.1"
45
44
  },
46
45
  "devDependencies": {
47
- "@prosopo/config": "3.3.5",
46
+ "@prosopo/config": "3.3.6",
48
47
  "@types/node": "22.10.2",
49
48
  "@vitest/coverage-v8": "4.1.10",
50
49
  "concurrently": "9.0.1",
@@ -25,7 +25,20 @@ import type {
25
25
  ProviderSelectRetryContext,
26
26
  } from "@prosopo/types";
27
27
  import type { BotDetectionFunctionResult } from "@prosopo/types";
28
- import { DetectorLoader } from "./detectorLoader.js";
28
+ import {
29
+ DetectorLoaderFromScript,
30
+ type DetectorType,
31
+ } from "./detectorLoader.js";
32
+ import {
33
+ type PrefetchedDetector,
34
+ takePrefetchedDetector,
35
+ } from "./detectorPrefetch.js";
36
+
37
+ // Upper bound on the detector-bundle assignment + load probe. The detector
38
+ // lives ONLY in the provider-served pool bundles, so if the provider is
39
+ // slow/unreachable we abandon the probe and signal `detectorUnavailable`, and
40
+ // the provider serves a PoW challenge instead of running detection.
41
+ const ASSIGN_TIMEOUT_MS = 2000;
29
42
 
30
43
  // The page(s) the widget is rendered on, reduced to origin + path.
31
44
  // Deliberately built from `origin` + `pathname` (never `href`) so the query
@@ -129,80 +142,160 @@ const customDetectBot: BotDetectionFunction = async (
129
142
  restartFn: () => void,
130
143
  retryContext?: ProviderSelectRetryContext,
131
144
  ): Promise<BotDetectionFunctionResult> => {
132
- // Kick off the provider-pin resolution (which hits `/healthz` on the
133
- // load-balancer DNS endpoint) in parallel with the module loaders and
134
- // the whole `detect()` flow. `getProcaptchaRandomActiveProvider` is
135
- // memoised via `pinPromiseCache` keyed on `(env, ipMode)`, so the
136
- // in-flight promise is reused when catcher's internal provider selector
137
- // and the awaited `getProcaptchaRandomActiveProvider(...)` below reach
138
- // for it — no duplicate healthz request. Before this ordering change
139
- // the healthz round-trip was fully serial with `detect()`, adding
140
- // ~100–200ms to the frictionless critical path on cold DNS/TLS.
141
- //
142
- // We warm both cache keys because the catcher-internal selector call
143
- // (line ~145 below) passes no ipMode, while the awaited call further
144
- // down passes `pickIpMode(config)`. Same key when the dapp opts into
145
- // neither ipv4 nor ipv6 (dual stack — the common case); different
146
- // keys when the dapp is pinned to a single stack. Fire-and-forget on
147
- // both — the real synchronisation happens at the awaits below.
148
- const ipMode = pickIpMode(config);
149
- void getProcaptchaRandomActiveProvider(config.defaultEnvironment).catch(
150
- () => undefined,
151
- );
152
- if (ipMode !== undefined) {
153
- void getProcaptchaRandomActiveProvider(
154
- config.defaultEnvironment,
155
- ipMode,
156
- retryContext,
157
- ).catch(() => undefined);
158
- }
159
-
160
- const [ExtClass, detect] = await Promise.all([
161
- ExtensionLoader(config.web2),
162
- DetectorLoader(),
163
- ]);
164
- const ext = new ExtClass();
165
-
166
- // The detector bundle still expects the legacy 5-arg signature
167
- // `(env, randomProviderSelectorFn, container, restart, accountGenerator)`.
168
- // Until the bundle is rebuilt without the provider selector, hand it a
169
- // noop selector that resolves to the static DNS endpoint — the detector
170
- // no longer uses the returned RandomProvider for routing.
171
- const detectionResult = await detect(
172
- config.defaultEnvironment,
173
- async () => getProcaptchaRandomActiveProvider(config.defaultEnvironment),
174
- container,
175
- restartFn,
176
- () => ext.getAccount(config),
177
- );
178
-
179
- const userAccount = detectionResult.userAccount;
180
-
181
145
  if (!config.account.address) {
182
146
  throw new ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
183
147
  }
184
148
 
149
+ const ipMode = pickIpMode(config);
150
+
151
+ // Start the extension/account module load now rather than after the bundle
152
+ // assignment. It depends on nothing above, so it overlaps the provider-pin
153
+ // `/healthz` round-trip and the assign request for free; it is awaited below
154
+ // at the point `ExtClass` is first needed.
155
+ //
156
+ // This is what survives of main's parallel-pin optimisation. That change
157
+ // warmed BOTH `pinPromiseCache` keys — with and without ipMode — because the
158
+ // catcher-internal provider selector called with no ipMode while the awaited
159
+ // call passed one. The 3-arg detector has no provider selector at all, so
160
+ // only the `(env, ipMode)` key is ever used now and warming the bare-env key
161
+ // would just issue a healthz request nothing consumes. Nor can the pin be
162
+ // moved off the critical path the way it was before: the detector now comes
163
+ // FROM the provider, so resolving the provider is a hard prerequisite of
164
+ // having anything to run, not something that can proceed alongside it.
165
+ const extClassPromise = ExtensionLoader(config.web2);
166
+
185
167
  // On the first attempt this resolves the static DNS endpoint for this env —
186
168
  // the DNS layer load-balances across the pronode fleet. On a retry
187
169
  // (`retryContext.attempt > 1`) the previously used pronode errored, so we
188
170
  // pick a random provider straight from the list instead of re-pinning the
189
171
  // same one. `pickIpMode(config)` honours the dapp's data-ipv4 / data-ipv6
190
172
  // preference so frictionless and the subsequent captcha hops stay on the
191
- // same stack.
192
- //
193
- // This await usually hits the in-flight/resolved promise warmed at the
194
- // top of this function see the prefetch call there.
195
- const provider = await getProcaptchaRandomActiveProvider(
196
- config.defaultEnvironment,
197
- ipMode,
198
- retryContext,
199
- );
173
+ // same stack. Resolved up front — before detection rather than alongside it —
174
+ // because the detector bundle is served BY this provider.
175
+ // The bundle entry starts provider resolution + assign as soon as it has read
176
+ // the site key off the DOM, which is well before React has mounted this
177
+ // widget. Claim that in-flight work if it exists rather than repeating it.
178
+ // Only valid on a first attempt: a retry is retrying *because* the pinned
179
+ // pronode failed, so it must re-resolve.
180
+ const isFirstAttempt = !retryContext || retryContext.attempt <= 1;
181
+ const prefetched = isFirstAttempt
182
+ ? takePrefetchedDetector(
183
+ config.defaultEnvironment,
184
+ ipMode,
185
+ config.account.address,
186
+ )
187
+ : undefined;
188
+
189
+ // A prefetch that failed must not fail the flow — it is an optimisation, and
190
+ // the normal path below handles provider selection and assign failure
191
+ // already. So swallow it and re-resolve.
192
+ let prefetchedResult: PrefetchedDetector | undefined;
193
+ if (prefetched) {
194
+ try {
195
+ prefetchedResult = await prefetched;
196
+ } catch {
197
+ prefetchedResult = undefined;
198
+ }
199
+ }
200
+
201
+ const provider =
202
+ prefetchedResult?.provider ??
203
+ (await getProcaptchaRandomActiveProvider(
204
+ config.defaultEnvironment,
205
+ ipMode,
206
+ retryContext,
207
+ ));
200
208
 
201
209
  const providerApi = new ProviderApi(
202
210
  provider.provider.url,
203
211
  config.account.address,
204
212
  );
205
213
 
214
+ // Ask the provider for a per-session detector bundle. The detector lives ONLY
215
+ // in the provider-served pool: when the provider has a populated pool it
216
+ // returns the obfuscated detector (each with its own keys + inner cipher)
217
+ // plus a detectorSessionId. When it cannot (no pool, network, decode, or
218
+ // timeout) we have NO detector to run — there is no bundled fallback — so we
219
+ // signal `detectorUnavailable` and the provider serves a PoW challenge.
220
+ let detectorSessionId: string | undefined;
221
+ let providerDetect: DetectorType | undefined;
222
+ try {
223
+ // Reuse the prefetched assignment when the entry already fetched one for
224
+ // this provider; otherwise issue it now.
225
+ const assigned =
226
+ prefetchedResult?.assigned ??
227
+ (await withTimeout(
228
+ providerApi.assignDetectorBundle(config.account.address),
229
+ ASSIGN_TIMEOUT_MS,
230
+ ));
231
+ if (assigned.useProviderBundle && assigned.detectorScript) {
232
+ detectorSessionId = assigned.detectorSessionId;
233
+ providerDetect = await withTimeout(
234
+ DetectorLoaderFromScript(assigned.detectorScript),
235
+ ASSIGN_TIMEOUT_MS,
236
+ );
237
+ }
238
+ } catch {
239
+ // No detector available — fall through to the PoW request below.
240
+ }
241
+
242
+ const ExtClass = await extClassPromise;
243
+ const ext = new ExtClass();
244
+
245
+ // No provider detector ⇒ no detection is possible. Request a PoW challenge
246
+ // directly: send no token and the detectorUnavailable flag so the provider
247
+ // serves PoW rather than attempting to score an absent payload.
248
+ if (providerDetect === undefined) {
249
+ const userAccount = await ext.getAccount(config);
250
+ const { currentUrl: fallbackUrl, iframeUrl: fallbackIframeUrl } =
251
+ getCurrentPageUrls();
252
+ const powCaptcha = await withTimeout(
253
+ providerApi.getFrictionlessCaptcha(
254
+ undefined,
255
+ undefined,
256
+ config.account.address,
257
+ userAccount.account.address,
258
+ config.mode,
259
+ undefined,
260
+ undefined,
261
+ true,
262
+ fallbackUrl,
263
+ fallbackIframeUrl,
264
+ ),
265
+ 10000,
266
+ );
267
+ if (powCaptcha.dns_url) {
268
+ try {
269
+ void fetch(powCaptcha.dns_url, {
270
+ method: "GET",
271
+ mode: "no-cors",
272
+ credentials: "omit",
273
+ keepalive: true,
274
+ cache: "no-store",
275
+ }).catch(() => undefined);
276
+ } catch {
277
+ /* swallow */
278
+ }
279
+ }
280
+ return {
281
+ captchaType: powCaptcha.captchaType,
282
+ sessionId: powCaptcha.sessionId,
283
+ provider: provider,
284
+ status: powCaptcha.status,
285
+ userAccount: userAccount,
286
+ error: powCaptcha.error,
287
+ hp: powCaptcha.hp,
288
+ };
289
+ }
290
+
291
+ const detect: DetectorType = providerDetect;
292
+
293
+ const detectionResult = await detect(container, restartFn, () =>
294
+ ext.getAccount(config),
295
+ );
296
+
297
+ const userAccount = detectionResult.userAccount;
298
+
206
299
  // SIMD readings deliberately omitted from the frictionless hop. The WASM
207
300
  // benchmark is a CPU-bound loop that contends with BotScoreWorker if it
208
301
  // runs during detection; deferring it until after the POST is in flight
@@ -217,6 +310,8 @@ const customDetectBot: BotDetectionFunction = async (
217
310
  userAccount.account.address,
218
311
  config.mode,
219
312
  undefined,
313
+ detectorSessionId,
314
+ undefined,
220
315
  currentUrl,
221
316
  iframeUrl,
222
317
  );
@@ -12,7 +12,69 @@
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
 
15
- type DetectorType = typeof import("@prosopo/detector").default;
15
+ import type {
16
+ Account,
17
+ BehavioralData,
18
+ ClickEventPoint,
19
+ MouseMovementPoint,
20
+ PackedBehavioralData,
21
+ TouchEventPoint,
22
+ } from "@prosopo/types";
16
23
 
17
- export const DetectorLoader = async (): Promise<DetectorType> =>
18
- (await import("@prosopo/detector")).default;
24
+ // The detector is NOT bundled into the widget — it lives only in the
25
+ // provider-served pool bundles, loaded at runtime via a blob URL below. So the
26
+ // signature is declared locally here (mirroring `@prosopo/detector`'s default
27
+ // export) from shared @prosopo/types primitives, rather than importing the
28
+ // detector package as a build-time type dependency.
29
+ export type DetectorType = (
30
+ container: HTMLElement | undefined,
31
+ restart: () => void,
32
+ accountGenerator: () => Promise<Account>,
33
+ ) => Promise<{
34
+ token: string;
35
+ shadowDomCleanup: () => void;
36
+ encryptHeadHash: string;
37
+ mouseTracker?: {
38
+ start: () => void;
39
+ stop: () => void;
40
+ getData: () => MouseMovementPoint[];
41
+ clear: () => void;
42
+ };
43
+ touchTracker?: {
44
+ start: () => void;
45
+ stop: () => void;
46
+ getData: () => TouchEventPoint[];
47
+ clear: () => void;
48
+ };
49
+ clickTracker?: {
50
+ start: () => void;
51
+ stop: () => void;
52
+ getData: () => ClickEventPoint[];
53
+ clear: () => void;
54
+ };
55
+ hasTouchSupport?: string;
56
+ encryptBehavioralData?: (data: string) => Promise<string>;
57
+ packBehavioralData?: (behavioralData: BehavioralData) => PackedBehavioralData;
58
+ getSimdReadings?: (timeoutMs?: number) => Promise<string | undefined>;
59
+ userAccount: Account;
60
+ }>;
61
+
62
+ /**
63
+ * Loads a detector from a provider-served obfuscated ESM string (the per-session
64
+ * pool bundle). Evaluated via a blob URL — the served module is self-contained
65
+ * (no imports), so dynamic `import()` of the blob yields its default export.
66
+ */
67
+ export const DetectorLoaderFromScript = async (
68
+ script: string,
69
+ ): Promise<DetectorType> => {
70
+ const blob = new Blob([script], { type: "text/javascript" });
71
+ const url = URL.createObjectURL(blob);
72
+ try {
73
+ const mod = (await import(/* @vite-ignore */ url)) as {
74
+ default: DetectorType;
75
+ };
76
+ return mod.default;
77
+ } finally {
78
+ URL.revokeObjectURL(url);
79
+ }
80
+ };