@prosopo/procaptcha-pow 2.13.7 → 2.13.8

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,135 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import type {
16
- Account,
17
- FrictionlessState,
18
- GetPowCaptchaResponse,
19
- PowCaptchaSolutionResponse,
20
- ProcaptchaCallbacks,
21
- ProcaptchaClientConfigInput,
22
- ProcaptchaState,
23
- RandomProvider,
24
- } from "@prosopo/types";
25
- import { vi } from "vitest";
26
-
27
- /**
28
- * Shared fixtures for the manager suite. Kept out of the test file so the mock
29
- * factories, which vitest hoists above every import, can build their canned
30
- * responses from the same shapes the assertions use.
31
- */
32
-
33
- export const PROVIDER_URL = "https://provider.one";
34
- export const OTHER_PROVIDER_URL = "https://provider.two";
35
- export const USER_ADDRESS = "user-address";
36
- export const SITE_KEY = "site-key";
37
-
38
- export const config = (
39
- overrides: Partial<ProcaptchaClientConfigInput> = {},
40
- ): ProcaptchaClientConfigInput => ({
41
- account: { address: SITE_KEY },
42
- defaultEnvironment: "production",
43
- ...overrides,
44
- });
45
-
46
- export const state = (
47
- overrides: Partial<ProcaptchaState> = {},
48
- ): ProcaptchaState => ({
49
- isHuman: false,
50
- index: 0,
51
- solutions: [],
52
- captchaApi: undefined,
53
- challenge: undefined,
54
- showModal: false,
55
- loading: false,
56
- account: undefined,
57
- dappAccount: undefined,
58
- submission: undefined,
59
- timeout: undefined,
60
- successfullChallengeTimeout: undefined,
61
- sendData: false,
62
- attemptCount: 0,
63
- error: undefined,
64
- sessionId: undefined,
65
- ...overrides,
66
- });
67
-
68
- export type SignRaw = NonNullable<
69
- NonNullable<Account["extension"]>["signer"]["signRaw"]
70
- >;
71
-
72
- /**
73
- * An account carrying just enough of an injected extension to sign: the rest of
74
- * the interface is never touched by the manager, but the type demands it.
75
- */
76
- export const account = (signRaw?: SignRaw): Account => ({
77
- account: { address: USER_ADDRESS },
78
- extension: {
79
- name: "test-extension",
80
- version: "0.0.0",
81
- accounts: {
82
- get: async () => [{ address: USER_ADDRESS }],
83
- subscribe: () => () => undefined,
84
- },
85
- signer: signRaw ? { signRaw } : {},
86
- },
87
- });
88
-
89
- export const accountWithoutExtension = (): Account => ({
90
- account: { address: USER_ADDRESS },
91
- });
92
-
93
- export const randomProvider = (url: string = PROVIDER_URL): RandomProvider => ({
94
- providerAccount: "provider-account",
95
- provider: { url },
96
- });
97
-
98
- export const challengeResponse = (
99
- overrides: Partial<GetPowCaptchaResponse> = {},
100
- ): GetPowCaptchaResponse => ({
101
- challenge: "0x1___0xdeadbeef___1700000000000",
102
- difficulty: 2,
103
- timestamp: "1700000000000",
104
- signature: { provider: { challenge: "0xprovider-challenge" } },
105
- status: "ok",
106
- ...overrides,
107
- });
108
-
109
- export const solutionResponse = (
110
- overrides: Partial<PowCaptchaSolutionResponse> = {},
111
- ): PowCaptchaSolutionResponse => ({
112
- verified: true,
113
- status: "ok",
114
- ...overrides,
115
- });
116
-
117
- export const callbacks = (
118
- overrides: Partial<ProcaptchaCallbacks> = {},
119
- ): ProcaptchaCallbacks => ({ ...overrides });
120
-
121
- /**
122
- * The signer every fixture account uses, so a test can assert on what the
123
- * manager asked the extension to sign without rebuilding the frictionless
124
- * state it was handed.
125
- */
126
- export const signRawMock = vi.fn<SignRaw>();
127
-
128
- export const frictionless = (
129
- overrides: Partial<FrictionlessState> = {},
130
- ): FrictionlessState => ({
131
- provider: randomProvider(),
132
- userAccount: account(signRawMock),
133
- restart: () => undefined,
134
- ...overrides,
135
- });
@@ -1,143 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import type { Ti18n } from "@prosopo/locale";
16
- import type { ProcaptchaProps } from "@prosopo/types";
17
- import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
18
- import type { ProcaptchaPowHandle } from "../components/procaptchaWidget.js";
19
- import * as entrypoint from "../index.js";
20
- import { type Mounted, mount, settle } from "./domHarness.js";
21
- import { config } from "./managerHarness.js";
22
-
23
- /**
24
- * The outer entry point exists only to lazy-load the widget and hand it the
25
- * props it was given — which it once did by naming them one at a time, quietly
26
- * dropping `onEscalate` and `autoStart`.
27
- */
28
- const mocks = vi.hoisted(() => ({
29
- received: [] as ProcaptchaProps[],
30
- destroyed: { count: 0 },
31
- }));
32
-
33
- vi.mock("../components/procaptchaWidget.js", () => ({
34
- mountProcaptchaPowWidget: (
35
- container: HTMLElement,
36
- props: ProcaptchaProps,
37
- ): ProcaptchaPowHandle => {
38
- mocks.received.push(props);
39
- const element = document.createElement("div");
40
- element.setAttribute("data-testid", "widget");
41
- container.appendChild(element);
42
- return {
43
- destroy: () => {
44
- element.remove();
45
- mocks.destroyed.count += 1;
46
- },
47
- };
48
- },
49
- }));
50
-
51
- let mounted: Mounted;
52
- let widget: ProcaptchaPowHandle | undefined;
53
-
54
- const props = (overrides: Partial<ProcaptchaProps> = {}): ProcaptchaProps => ({
55
- config: config(),
56
- callbacks: {},
57
- i18n: undefined as unknown as Ti18n,
58
- ...overrides,
59
- });
60
-
61
- const render = async (widgetProps: ProcaptchaProps): Promise<void> => {
62
- widget = entrypoint.mountProcaptchaPow(mounted.container, widgetProps);
63
- await settle();
64
- };
65
-
66
- beforeEach(() => {
67
- mocks.received.length = 0;
68
- mocks.destroyed.count = 0;
69
- widget = undefined;
70
- mounted = mount();
71
- });
72
-
73
- afterEach(() => {
74
- widget?.destroy();
75
- widget = undefined;
76
- mounted.unmount();
77
- });
78
-
79
- describe("mountProcaptchaPow", () => {
80
- test("renders the widget once it has loaded", async () => {
81
- await render(props());
82
- expect(
83
- mounted.container.querySelector('[data-testid="widget"]'),
84
- ).not.toBeNull();
85
- });
86
-
87
- test("passes on the props the widget needs but never names", async () => {
88
- // Enumerating props here dropped onEscalate and autoStart, so the manager
89
- // closed over an undefined escalation handler and the PoW checkbox span
90
- // forever after an escalated solve.
91
- const onEscalate = vi.fn();
92
- const onSessionInvalidated = vi.fn();
93
- await render(
94
- props({
95
- onEscalate,
96
- autoStart: true,
97
- startCoords: { x: 1, y: 2 },
98
- onSessionInvalidated,
99
- }),
100
- );
101
- expect(mocks.received[0]).toMatchObject({
102
- onEscalate,
103
- autoStart: true,
104
- startCoords: { x: 1, y: 2 },
105
- onSessionInvalidated,
106
- });
107
- });
108
-
109
- test("passes on the props it does name, too", async () => {
110
- const callbacks = { onHuman: vi.fn<(token: string) => void>() };
111
- await render(props({ callbacks }));
112
- expect(mocks.received[0]?.callbacks).toBe(callbacks);
113
- expect(mocks.received[0]?.config).toEqual(config());
114
- });
115
-
116
- test("tears the loaded widget down", async () => {
117
- await render(props());
118
- widget?.destroy();
119
- widget = undefined;
120
- expect(mocks.destroyed.count).toBe(1);
121
- expect(
122
- mounted.container.querySelector('[data-testid="widget"]'),
123
- ).toBeNull();
124
- });
125
-
126
- test("cancels a mount that is still loading", async () => {
127
- // Otherwise a widget torn down mid-load reappears as an orphan that
128
- // nothing holds a handle to.
129
- widget = entrypoint.mountProcaptchaPow(mounted.container, props());
130
- widget.destroy();
131
- widget = undefined;
132
- await settle();
133
- expect(mocks.received).toHaveLength(0);
134
- });
135
- });
136
-
137
- describe("the package entrypoint", () => {
138
- test("exports the mount functions a consumer needs", () => {
139
- expect(typeof entrypoint.mountProcaptchaPow).toBe("function");
140
- expect(typeof entrypoint.loadProcaptchaPow).toBe("function");
141
- expect(typeof entrypoint.mountProcaptchaPowWidget).toBe("function");
142
- });
143
- });
@@ -1,128 +0,0 @@
1
- // Copyright 2021-2026 Prosopo (UK) Ltd.
2
- //
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
-
15
- import type { Ti18n } from "@prosopo/locale";
16
- import type {
17
- FrictionlessState,
18
- ProcaptchaCallbacks,
19
- ProcaptchaClientConfigInput,
20
- ProcaptchaEscalationHandler,
21
- ProcaptchaProps,
22
- ProcaptchaState,
23
- ProcaptchaStateUpdateFn,
24
- } from "@prosopo/types";
25
- import { assertType, describe, expectTypeOf, test } from "vitest";
26
- import type * as entrypoint from "../index.js";
27
- import { mountProcaptchaPow } from "../index.js";
28
- import { Manager } from "../services/Manager.js";
29
- import { config, frictionless, state } from "./managerHarness.js";
30
-
31
- /** The widget only ever reads `language`/`changeLanguage` off this. */
32
- const i18n = (): Ti18n => undefined as unknown as Ti18n;
33
-
34
- describe("the package entrypoint's types", () => {
35
- test("mountProcaptchaPow takes a host element and the shared widget props", () => {
36
- expectTypeOf(mountProcaptchaPow).parameters.toEqualTypeOf<
37
- [HTMLElement, ProcaptchaProps]
38
- >();
39
- expectTypeOf(mountProcaptchaPow).returns.toExtend<{
40
- destroy: () => void;
41
- }>();
42
- });
43
-
44
- test("the entrypoint exposes the lazy wrapper and the widget itself", () => {
45
- // The lazy wrapper is what works without a code-splitting bundler; the
46
- // direct mount is what ProcaptchaFrictionless imports once it has already
47
- // paid for the dynamic import of this package.
48
- expectTypeOf<keyof typeof entrypoint>().toEqualTypeOf<
49
- "mountProcaptchaPow" | "loadProcaptchaPow" | "mountProcaptchaPowWidget"
50
- >();
51
- });
52
-
53
- test("config, callbacks and i18n are all required", () => {
54
- // @ts-expect-error - a widget with no config has no provider to talk to.
55
- assertType<ProcaptchaProps>({ callbacks: {}, i18n: i18n() });
56
- // @ts-expect-error - callbacks decide what a solve reports back.
57
- assertType<ProcaptchaProps>({ config: config(), i18n: i18n() });
58
- assertType<ProcaptchaProps>({
59
- config: config(),
60
- callbacks: {},
61
- i18n: i18n(),
62
- });
63
- });
64
- });
65
-
66
- describe("Manager's types", () => {
67
- const updateState: ProcaptchaStateUpdateFn = () => undefined;
68
- const callbacks: ProcaptchaCallbacks = {};
69
-
70
- test("only the first four arguments are required", () => {
71
- assertType<ReturnType<typeof Manager>>(
72
- Manager(config(), state(), updateState, callbacks),
73
- );
74
- // @ts-expect-error - callbacks decide what a solve reports back.
75
- Manager(config(), state(), updateState);
76
- });
77
-
78
- test("the optional arguments keep their positions", () => {
79
- expectTypeOf(Manager)
80
- .parameter(4)
81
- .toEqualTypeOf<FrictionlessState | undefined>();
82
- expectTypeOf(Manager)
83
- .parameter(5)
84
- .toEqualTypeOf<ProcaptchaEscalationHandler | undefined>();
85
- expectTypeOf(Manager)
86
- .parameter(6)
87
- .toEqualTypeOf<(() => string | undefined) | undefined>();
88
- });
89
-
90
- test("it exposes exactly start, resetState and dispose", () => {
91
- expectTypeOf<keyof ReturnType<typeof Manager>>().toEqualTypeOf<
92
- "start" | "resetState" | "dispose"
93
- >();
94
- expectTypeOf<ReturnType<typeof Manager>["start"]>().toEqualTypeOf<
95
- (x?: number, y?: number) => Promise<void>
96
- >();
97
- // resetState takes the frictionless restart callback the widget hands it
98
- // on an invalidated session, and nothing else.
99
- expectTypeOf<
100
- Parameters<ReturnType<typeof Manager>["resetState"]>
101
- >().toEqualTypeOf<[frictionlessRestart?: (() => void) | undefined]>();
102
- expectTypeOf<
103
- ReturnType<ReturnType<typeof Manager>["resetState"]>
104
- >().toEqualTypeOf<void>();
105
- });
106
-
107
- test("start's coordinates are optional numbers", () => {
108
- const manager: ReturnType<typeof Manager> = Manager(
109
- config(),
110
- state(),
111
- updateState,
112
- callbacks,
113
- frictionless(),
114
- );
115
- expectTypeOf(manager.start()).toEqualTypeOf<Promise<void>>();
116
- expectTypeOf(manager.start(1, 2)).toEqualTypeOf<Promise<void>>();
117
- // @ts-expect-error - coordinates come off a DOM event, never as strings.
118
- manager.start("1", "2");
119
- });
120
- });
121
-
122
- describe("the harness fixtures match the shared types", () => {
123
- test("they build the real shapes, not lookalikes", () => {
124
- expectTypeOf(config()).toEqualTypeOf<ProcaptchaClientConfigInput>();
125
- expectTypeOf(state()).toEqualTypeOf<ProcaptchaState>();
126
- expectTypeOf(frictionless()).toEqualTypeOf<FrictionlessState>();
127
- });
128
- });