@prosopo/procaptcha-common 2.9.23 → 2.10.18

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 (54) hide show
  1. package/.turbo/turbo-build$colon$cjs.log +22 -15
  2. package/.turbo/turbo-build$colon$tsc.log +22 -16
  3. package/.turbo/turbo-build.log +22 -16
  4. package/CHANGELOG.md +355 -0
  5. package/dist/cjs/elements/window.cjs +7 -0
  6. package/dist/cjs/index.cjs +5 -0
  7. package/dist/cjs/reactComponents/Checkbox.cjs +11 -7
  8. package/dist/cjs/reactComponents/Honeypot.cjs +67 -0
  9. package/dist/cjs/reactComponents/TestModeBanner.cjs +47 -0
  10. package/dist/elements/window.d.ts +1 -0
  11. package/dist/elements/window.d.ts.map +1 -1
  12. package/dist/elements/window.js +8 -1
  13. package/dist/elements/window.js.map +1 -1
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +6 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/reactComponents/Checkbox.d.ts.map +1 -1
  19. package/dist/reactComponents/Checkbox.js +11 -7
  20. package/dist/reactComponents/Checkbox.js.map +1 -1
  21. package/dist/reactComponents/Honeypot.d.ts +6 -0
  22. package/dist/reactComponents/Honeypot.d.ts.map +1 -0
  23. package/dist/reactComponents/Honeypot.js +67 -0
  24. package/dist/reactComponents/Honeypot.js.map +1 -0
  25. package/dist/reactComponents/TestModeBanner.d.ts +7 -0
  26. package/dist/reactComponents/TestModeBanner.d.ts.map +1 -0
  27. package/dist/reactComponents/TestModeBanner.js +47 -0
  28. package/dist/reactComponents/TestModeBanner.js.map +1 -0
  29. package/dist/tests/window.test.js +22 -1
  30. package/dist/tests/window.test.js.map +1 -1
  31. package/package.json +11 -8
  32. package/src/callbacks/defaultCallbacks.ts +197 -0
  33. package/src/callbacks/defaultEvents.ts +21 -0
  34. package/src/elements/form.ts +41 -0
  35. package/src/elements/window.ts +39 -0
  36. package/src/extensionLoader.ts +17 -0
  37. package/src/index.ts +24 -0
  38. package/src/providers.ts +49 -0
  39. package/src/reactComponents/Checkbox.tsx +203 -0
  40. package/src/reactComponents/Honeypot.tsx +133 -0
  41. package/src/reactComponents/Reload.tsx +99 -0
  42. package/src/reactComponents/TestModeBanner.tsx +75 -0
  43. package/src/state/builder.ts +137 -0
  44. package/src/tests/defaultCallbacks.test.ts +372 -0
  45. package/src/tests/defaultEvents.test.ts +80 -0
  46. package/src/tests/extensionLoader.test.ts +41 -0
  47. package/src/tests/form.test.ts +154 -0
  48. package/src/tests/providers.test.ts +175 -0
  49. package/src/tests/state-builder.test.ts +264 -0
  50. package/src/tests/window.test.ts +137 -0
  51. package/tsconfig.cjs.json +32 -0
  52. package/tsconfig.json +33 -0
  53. package/tsconfig.tsbuildinfo +1 -0
  54. package/tsconfig.types.json +9 -0
@@ -0,0 +1,175 @@
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 { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
16
+ import {
17
+ getProcaptchaRandomActiveProvider,
18
+ providerRetry,
19
+ } from "../providers.js";
20
+
21
+ // Mock the load-balancer module
22
+ vi.mock("@prosopo/load-balancer", () => ({
23
+ getRandomActiveProvider: vi.fn(),
24
+ }));
25
+
26
+ describe("providers", () => {
27
+ describe("getProcaptchaRandomActiveProvider", () => {
28
+ // biome-ignore lint/suspicious/noExplicitAny: Store original crypto function
29
+ let originalGetRandomValues: any;
30
+
31
+ beforeEach(() => {
32
+ originalGetRandomValues = global.window.crypto.getRandomValues.bind(
33
+ global.window.crypto,
34
+ );
35
+ });
36
+
37
+ afterEach(() => {
38
+ global.window.crypto.getRandomValues = originalGetRandomValues;
39
+ });
40
+
41
+ it("should generate random values and call getRandomActiveProvider", async () => {
42
+ // Mock window.crypto.getRandomValues
43
+ const mockRandomValues = new Uint8Array([
44
+ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
45
+ ]);
46
+ const mockGetRandomValues = vi.fn(() => mockRandomValues);
47
+ // biome-ignore lint/suspicious/noExplicitAny: Mock crypto API
48
+ global.window.crypto.getRandomValues = mockGetRandomValues as any;
49
+
50
+ // Mock the getRandomActiveProvider import
51
+ const { getRandomActiveProvider } = await import(
52
+ "@prosopo/load-balancer"
53
+ );
54
+ vi.mocked(getRandomActiveProvider).mockResolvedValue({
55
+ providerUrl: "https://test-provider.com",
56
+ // biome-ignore lint/suspicious/noExplicitAny: Mock return type
57
+ } as any);
58
+
59
+ const result = await getProcaptchaRandomActiveProvider("development");
60
+
61
+ expect(mockGetRandomValues).toHaveBeenCalledWith(expect.any(Uint8Array));
62
+ expect(getRandomActiveProvider).toHaveBeenCalledWith("development", 550); // sum of mockRandomValues
63
+ expect(result).toEqual({ providerUrl: "https://test-provider.com" });
64
+ });
65
+
66
+ it("should use different random values on each call", async () => {
67
+ let callCount = 0;
68
+ const mockGetRandomValues = vi.fn((arr: Uint8Array) => {
69
+ callCount++;
70
+ arr.fill(callCount);
71
+ return arr;
72
+ });
73
+ // biome-ignore lint/suspicious/noExplicitAny: Mock crypto API
74
+ global.window.crypto.getRandomValues = mockGetRandomValues as any;
75
+
76
+ const { getRandomActiveProvider } = await import(
77
+ "@prosopo/load-balancer"
78
+ );
79
+ // biome-ignore lint/suspicious/noExplicitAny: Mock return type
80
+ vi.mocked(getRandomActiveProvider).mockResolvedValue({} as any);
81
+
82
+ await getProcaptchaRandomActiveProvider("development");
83
+ await getProcaptchaRandomActiveProvider("development");
84
+
85
+ expect(mockGetRandomValues).toHaveBeenCalledTimes(2);
86
+ });
87
+ });
88
+
89
+ describe("providerRetry", () => {
90
+ it("should successfully execute currentFn without retrying", async () => {
91
+ const currentFn = vi.fn().mockResolvedValue(undefined);
92
+ const retryFn = vi.fn();
93
+ const stateReset = vi.fn();
94
+
95
+ await providerRetry(currentFn, retryFn, stateReset, 0, 3);
96
+
97
+ expect(currentFn).toHaveBeenCalledTimes(1);
98
+ expect(retryFn).not.toHaveBeenCalled();
99
+ expect(stateReset).not.toHaveBeenCalled();
100
+ });
101
+
102
+ it("should call retryFn when currentFn fails and attempts are below max", async () => {
103
+ const error = new Error("Provider failed");
104
+ const currentFn = vi.fn().mockRejectedValue(error);
105
+ const retryFn = vi.fn().mockResolvedValue(undefined);
106
+ const stateReset = vi.fn();
107
+
108
+ await providerRetry(currentFn, retryFn, stateReset, 1, 3);
109
+
110
+ expect(currentFn).toHaveBeenCalledTimes(1);
111
+ expect(stateReset).toHaveBeenCalledTimes(1);
112
+ expect(retryFn).toHaveBeenCalledTimes(1);
113
+ });
114
+
115
+ it("should call stateReset and not retry when max retries reached", async () => {
116
+ const error = new Error("Provider failed");
117
+ const currentFn = vi.fn().mockRejectedValue(error);
118
+ const retryFn = vi.fn();
119
+ const stateReset = vi.fn();
120
+ const consoleErrorSpy = vi
121
+ .spyOn(console, "error")
122
+ .mockImplementation(() => {});
123
+
124
+ await providerRetry(currentFn, retryFn, stateReset, 3, 3);
125
+
126
+ expect(currentFn).toHaveBeenCalledTimes(1);
127
+ expect(stateReset).toHaveBeenCalledTimes(1);
128
+ expect(retryFn).not.toHaveBeenCalled();
129
+ expect(consoleErrorSpy).toHaveBeenCalledWith(error);
130
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
131
+ "Max retries (3 of 3) reached, aborting",
132
+ );
133
+
134
+ consoleErrorSpy.mockRestore();
135
+ });
136
+
137
+ it("should call stateReset and not retry when attempts exceed max", async () => {
138
+ const error = new Error("Provider failed");
139
+ const currentFn = vi.fn().mockRejectedValue(error);
140
+ const retryFn = vi.fn();
141
+ const stateReset = vi.fn();
142
+ const consoleErrorSpy = vi
143
+ .spyOn(console, "error")
144
+ .mockImplementation(() => {});
145
+
146
+ await providerRetry(currentFn, retryFn, stateReset, 5, 3);
147
+
148
+ expect(currentFn).toHaveBeenCalledTimes(1);
149
+ expect(stateReset).toHaveBeenCalledTimes(1);
150
+ expect(retryFn).not.toHaveBeenCalled();
151
+
152
+ consoleErrorSpy.mockRestore();
153
+ });
154
+
155
+ it("should reset state even when retryFn fails", async () => {
156
+ const error = new Error("Provider failed");
157
+ const currentFn = vi.fn().mockRejectedValue(error);
158
+ const retryFn = vi.fn().mockRejectedValue(new Error("Retry failed"));
159
+ const stateReset = vi.fn();
160
+ const consoleErrorSpy = vi
161
+ .spyOn(console, "error")
162
+ .mockImplementation(() => {});
163
+
164
+ await expect(
165
+ providerRetry(currentFn, retryFn, stateReset, 1, 3),
166
+ ).rejects.toThrow("Retry failed");
167
+
168
+ expect(currentFn).toHaveBeenCalledTimes(1);
169
+ expect(stateReset).toHaveBeenCalledTimes(1);
170
+ expect(retryFn).toHaveBeenCalledTimes(1);
171
+
172
+ consoleErrorSpy.mockRestore();
173
+ });
174
+ });
175
+ });
@@ -0,0 +1,264 @@
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 { ProcaptchaState } from "@prosopo/types";
16
+ import { describe, expect, it, vi } from "vitest";
17
+ import { buildUpdateState, useProcaptcha } from "../state/builder.js";
18
+
19
+ describe("state/builder", () => {
20
+ describe("buildUpdateState", () => {
21
+ it("should mutate state and call onStateUpdate with partial state", () => {
22
+ const state: ProcaptchaState = {
23
+ isHuman: false,
24
+ index: 0,
25
+ solutions: [],
26
+ captchaApi: undefined,
27
+ showModal: false,
28
+ challenge: undefined,
29
+ loading: false,
30
+ account: undefined,
31
+ dappAccount: undefined,
32
+ submission: undefined,
33
+ timeout: undefined,
34
+ successfullChallengeTimeout: undefined,
35
+ sendData: false,
36
+ attemptCount: 0,
37
+ error: undefined,
38
+ sessionId: undefined,
39
+ };
40
+
41
+ const onStateUpdate = vi.fn();
42
+ const updateState = buildUpdateState(state, onStateUpdate);
43
+
44
+ const partialState = { isHuman: true, attemptCount: 1 };
45
+ updateState(partialState);
46
+
47
+ expect(state.isHuman).toBe(true);
48
+ expect(state.attemptCount).toBe(1);
49
+ expect(onStateUpdate).toHaveBeenCalledWith(partialState);
50
+ });
51
+
52
+ it("should handle multiple property updates in order", () => {
53
+ const state: ProcaptchaState = {
54
+ isHuman: false,
55
+ index: 0,
56
+ solutions: [],
57
+ captchaApi: undefined,
58
+ showModal: false,
59
+ challenge: undefined,
60
+ loading: false,
61
+ account: undefined,
62
+ dappAccount: undefined,
63
+ submission: undefined,
64
+ timeout: undefined,
65
+ successfullChallengeTimeout: undefined,
66
+ sendData: false,
67
+ attemptCount: 0,
68
+ error: undefined,
69
+ sessionId: undefined,
70
+ };
71
+
72
+ const onStateUpdate = vi.fn();
73
+ const updateState = buildUpdateState(state, onStateUpdate);
74
+
75
+ updateState({ loading: true, index: 5, showModal: true });
76
+
77
+ expect(state.loading).toBe(true);
78
+ expect(state.index).toBe(5);
79
+ expect(state.showModal).toBe(true);
80
+ });
81
+
82
+ it("should update error state correctly", () => {
83
+ const state: ProcaptchaState = {
84
+ isHuman: false,
85
+ index: 0,
86
+ solutions: [],
87
+ captchaApi: undefined,
88
+ showModal: false,
89
+ challenge: undefined,
90
+ loading: false,
91
+ account: undefined,
92
+ dappAccount: undefined,
93
+ submission: undefined,
94
+ timeout: undefined,
95
+ successfullChallengeTimeout: undefined,
96
+ sendData: false,
97
+ attemptCount: 0,
98
+ error: undefined,
99
+ sessionId: undefined,
100
+ };
101
+
102
+ const onStateUpdate = vi.fn();
103
+ const updateState = buildUpdateState(state, onStateUpdate);
104
+
105
+ const errorObj = { message: "Test error", key: "test" };
106
+ updateState({ error: errorObj });
107
+
108
+ expect(state.error).toEqual(errorObj);
109
+ expect(onStateUpdate).toHaveBeenCalledWith({ error: errorObj });
110
+ });
111
+ });
112
+
113
+ describe("useProcaptcha", () => {
114
+ it("should initialize with default state values", () => {
115
+ const useState = vi.fn((defaultValue) => [defaultValue, vi.fn()]);
116
+ const useRef = vi.fn((defaultValue) => ({ current: defaultValue }));
117
+
118
+ // biome-ignore lint/suspicious/noExplicitAny: Mock useState/useRef functions
119
+ const [state, updateFn] = useProcaptcha(useState as any, useRef as any);
120
+
121
+ expect(state.isHuman).toBe(false);
122
+ expect(state.index).toBe(0);
123
+ expect(state.solutions).toEqual([]);
124
+ expect(state.showModal).toBe(false);
125
+ expect(state.loading).toBe(false);
126
+ expect(state.sendData).toBe(false);
127
+ expect(state.attemptCount).toBe(0);
128
+ expect(state.account).toBeUndefined();
129
+ expect(state.captchaApi).toBeUndefined();
130
+ expect(state.challenge).toBeUndefined();
131
+ expect(state.error).toBeUndefined();
132
+ expect(typeof updateFn).toBe("function");
133
+ });
134
+
135
+ it("should call appropriate setters when updating state", () => {
136
+ // biome-ignore lint/suspicious/noExplicitAny: Mock setter functions
137
+ const setters: Record<string, any> = {};
138
+
139
+ const useState = vi.fn((defaultValue) => {
140
+ // Create a unique key based on the type and value to track different state variables
141
+ const key =
142
+ typeof defaultValue === "boolean"
143
+ ? `boolean-${defaultValue}`
144
+ : typeof defaultValue === "number"
145
+ ? `number-${defaultValue}`
146
+ : Array.isArray(defaultValue)
147
+ ? "array"
148
+ : "other";
149
+
150
+ if (!setters[key]) {
151
+ setters[key] = vi.fn();
152
+ }
153
+ return [defaultValue, setters[key]];
154
+ });
155
+
156
+ const useRef = vi.fn((defaultValue) => ({ current: defaultValue }));
157
+
158
+ // biome-ignore lint/suspicious/noExplicitAny: Mock useState/useRef functions
159
+ const [state, updateFn] = useProcaptcha(useState as any, useRef as any);
160
+
161
+ updateFn({
162
+ isHuman: true,
163
+ index: 5,
164
+ loading: true,
165
+ attemptCount: 3,
166
+ });
167
+
168
+ // Check that setters were called
169
+ // isHuman setter (boolean-false)
170
+ expect(setters["boolean-false"]).toHaveBeenCalledWith(true);
171
+ // index and attemptCount setters (both number-0)
172
+ expect(setters["number-0"]).toHaveBeenCalledWith(5);
173
+ expect(setters["number-0"]).toHaveBeenCalledWith(3);
174
+ });
175
+
176
+ it("should handle solutions array correctly with slice", () => {
177
+ const setSolutions = vi.fn();
178
+ // biome-ignore lint/suspicious/noExplicitAny: Mock solutions array
179
+ let solutionsValue: any[] = [];
180
+
181
+ const useState = vi.fn((defaultValue) => {
182
+ if (Array.isArray(defaultValue)) {
183
+ return [
184
+ solutionsValue,
185
+ // biome-ignore lint/suspicious/noExplicitAny: Mock setter function
186
+ (newValue: any) => {
187
+ solutionsValue = newValue;
188
+ setSolutions(newValue);
189
+ },
190
+ ];
191
+ }
192
+ return [defaultValue, vi.fn()];
193
+ });
194
+
195
+ const useRef = vi.fn((defaultValue) => ({ current: defaultValue }));
196
+
197
+ // biome-ignore lint/suspicious/noExplicitAny: Mock useState/useRef functions
198
+ const [state, updateFn] = useProcaptcha(useState as any, useRef as any);
199
+
200
+ const newSolutions: [string, number, number][][] = [[["test", 1, 2]]];
201
+ updateFn({ solutions: newSolutions });
202
+
203
+ expect(setSolutions).toHaveBeenCalled();
204
+ // Verify that slice was called by checking that we got a new array reference
205
+ const callArg = setSolutions.mock.calls[0]?.[0];
206
+ expect(callArg).toBeDefined();
207
+ expect(callArg).not.toBe(newSolutions);
208
+ expect(callArg).toEqual(newSolutions);
209
+ });
210
+
211
+ it("should not call setters for undefined values in partial update", () => {
212
+ const setIsHuman = vi.fn();
213
+ const setIndex = vi.fn();
214
+
215
+ // biome-ignore lint/suspicious/noExplicitAny: Mock setter tracking
216
+ let isHumanSetter: any;
217
+ // biome-ignore lint/suspicious/noExplicitAny: Mock setter tracking
218
+ let indexSetter: any;
219
+
220
+ const useState = vi.fn((defaultValue) => {
221
+ if (defaultValue === false) {
222
+ isHumanSetter = setIsHuman;
223
+ return [false, setIsHuman];
224
+ }
225
+ if (defaultValue === 0) {
226
+ indexSetter = setIndex;
227
+ return [0, setIndex];
228
+ }
229
+ return [defaultValue, vi.fn()];
230
+ });
231
+
232
+ const useRef = vi.fn((defaultValue) => ({ current: defaultValue }));
233
+
234
+ // biome-ignore lint/suspicious/noExplicitAny: Mock useState/useRef functions
235
+ const [state, updateFn] = useProcaptcha(useState as any, useRef as any);
236
+
237
+ updateFn({ isHuman: true });
238
+
239
+ expect(setIsHuman).toHaveBeenCalledWith(true);
240
+ expect(setIndex).not.toHaveBeenCalled();
241
+ });
242
+
243
+ it("should handle error state update", () => {
244
+ const setError = vi.fn();
245
+
246
+ const useState = vi.fn((defaultValue) => {
247
+ if (defaultValue === undefined && setError.mock.calls.length === 0) {
248
+ return [undefined, setError];
249
+ }
250
+ return [defaultValue, vi.fn()];
251
+ });
252
+
253
+ const useRef = vi.fn((defaultValue) => ({ current: defaultValue }));
254
+
255
+ // biome-ignore lint/suspicious/noExplicitAny: Mock useState/useRef functions
256
+ const [state, updateFn] = useProcaptcha(useState as any, useRef as any);
257
+
258
+ const errorObj = { message: "Test error", key: "testKey" };
259
+ updateFn({ error: errorObj });
260
+
261
+ expect(setError).toHaveBeenCalledWith(errorObj);
262
+ });
263
+ });
264
+ });
@@ -0,0 +1,137 @@
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 { afterEach, describe, expect, it } from "vitest";
16
+ import {
17
+ getWindowCallback,
18
+ isSecureBrowserContext,
19
+ } from "../elements/window.js";
20
+
21
+ describe("elements/window", () => {
22
+ describe("getWindowCallback", () => {
23
+ const originalWindow = global.window;
24
+
25
+ afterEach(() => {
26
+ // Clean up window object
27
+ const keys = Object.keys(window);
28
+ for (const key of keys) {
29
+ if (key.startsWith("test")) {
30
+ // biome-ignore lint/suspicious/noExplicitAny: Dynamic property cleanup
31
+ delete (window as any)[key];
32
+ }
33
+ }
34
+ });
35
+
36
+ it("should retrieve a function from window object", () => {
37
+ const testFn = () => "test";
38
+ // biome-ignore lint/suspicious/noExplicitAny: Test setup
39
+ (window as any).testCallback = testFn;
40
+
41
+ const result = getWindowCallback("testCallback");
42
+ expect(result).toBe(testFn);
43
+ });
44
+
45
+ it("should handle window. prefix in callback name", () => {
46
+ const testFn = () => "test";
47
+ // biome-ignore lint/suspicious/noExplicitAny: Test setup
48
+ (window as any).testCallback = testFn;
49
+
50
+ const result = getWindowCallback("window.testCallback");
51
+ expect(result).toBe(testFn);
52
+ });
53
+
54
+ it("should throw error if callback is not a function", () => {
55
+ // biome-ignore lint/suspicious/noExplicitAny: Test setup
56
+ (window as any).testCallback = "not a function";
57
+
58
+ expect(() => getWindowCallback("testCallback")).toThrow(
59
+ "Callback testCallback is not defined on the window object",
60
+ );
61
+ });
62
+
63
+ it("should throw error if callback does not exist", () => {
64
+ expect(() => getWindowCallback("nonExistentCallback")).toThrow(
65
+ "Callback nonExistentCallback is not defined on the window object",
66
+ );
67
+ });
68
+
69
+ it("should handle callback that is undefined", () => {
70
+ // biome-ignore lint/suspicious/noExplicitAny: Test setup
71
+ (window as any).testCallback = undefined;
72
+
73
+ expect(() => getWindowCallback("testCallback")).toThrow(
74
+ "Callback testCallback is not defined on the window object",
75
+ );
76
+ });
77
+
78
+ it("should handle callback that is null", () => {
79
+ // biome-ignore lint/suspicious/noExplicitAny: Test setup
80
+ (window as any).testCallback = null;
81
+
82
+ expect(() => getWindowCallback("testCallback")).toThrow(
83
+ "Callback testCallback is not defined on the window object",
84
+ );
85
+ });
86
+
87
+ it("should work with arrow functions", () => {
88
+ const arrowFn = (x: number) => x * 2;
89
+ // biome-ignore lint/suspicious/noExplicitAny: Test setup
90
+ (window as any).testArrowCallback = arrowFn;
91
+
92
+ const result = getWindowCallback("testArrowCallback");
93
+ expect(result).toBe(arrowFn);
94
+ expect(result(5)).toBe(10);
95
+ });
96
+
97
+ it("should work with regular functions", () => {
98
+ function regularFn(x: number) {
99
+ return x * 2;
100
+ }
101
+ // biome-ignore lint/suspicious/noExplicitAny: Test setup
102
+ (window as any).testRegularCallback = regularFn;
103
+
104
+ const result = getWindowCallback("testRegularCallback");
105
+ expect(result).toBe(regularFn);
106
+ expect(result(5)).toBe(10);
107
+ });
108
+ });
109
+
110
+ describe("isSecureBrowserContext", () => {
111
+ const originalIsSecureContext = window.isSecureContext;
112
+
113
+ const setIsSecureContext = (value: boolean): void => {
114
+ Object.defineProperty(window, "isSecureContext", {
115
+ value,
116
+ configurable: true,
117
+ writable: true,
118
+ });
119
+ };
120
+
121
+ afterEach(() => {
122
+ setIsSecureContext(originalIsSecureContext);
123
+ });
124
+
125
+ it("should return true in a secure context (HTTPS/localhost)", () => {
126
+ setIsSecureContext(true);
127
+
128
+ expect(isSecureBrowserContext()).toBe(true);
129
+ });
130
+
131
+ it("should return false in an insecure context (plain HTTP)", () => {
132
+ setIsSecureContext(false);
133
+
134
+ expect(isSecureBrowserContext()).toBe(false);
135
+ });
136
+ });
137
+ });
@@ -0,0 +1,32 @@
1
+ {
2
+ "extends": "../../tsconfig.cjs.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist/cjs",
6
+ "lib": ["es6", "dom"],
7
+ "jsxImportSource": "@emotion/react"
8
+ },
9
+ "include": [
10
+ "./src/**/*.ts",
11
+ "./src/**/*.json",
12
+ "./src/**/*.d.ts",
13
+ "./src/**/*.tsx"
14
+ ],
15
+ "references": [
16
+ {
17
+ "path": "../../dev/config/tsconfig.cjs.json"
18
+ },
19
+ {
20
+ "path": "../load-balancer/tsconfig.cjs.json"
21
+ },
22
+ {
23
+ "path": "../types/tsconfig.cjs.json"
24
+ },
25
+ {
26
+ "path": "../widget-skeleton/tsconfig.cjs.json"
27
+ },
28
+ {
29
+ "path": "../account/tsconfig.cjs.json"
30
+ }
31
+ ]
32
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "extends": "../../tsconfig.esm.json",
3
+ "compilerOptions": {
4
+ "rootDir": "./src",
5
+ "outDir": "./dist",
6
+ "lib": ["es6", "dom"],
7
+ "jsxImportSource": "@emotion/react"
8
+ },
9
+ "include": [
10
+ "src",
11
+ "src/**/*.json",
12
+ "src/**/*.ts",
13
+ "src/**/*.tsx",
14
+ "src/**/*.d.ts"
15
+ ],
16
+ "references": [
17
+ {
18
+ "path": "../../dev/config/tsconfig.json"
19
+ },
20
+ {
21
+ "path": "../load-balancer"
22
+ },
23
+ {
24
+ "path": "../types"
25
+ },
26
+ {
27
+ "path": "../widget-skeleton"
28
+ },
29
+ {
30
+ "path": "../account"
31
+ }
32
+ ]
33
+ }