@prosopo/load-balancer 2.8.12 → 2.8.17

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.
@@ -0,0 +1,33 @@
1
+
2
+ > @prosopo/load-balancer@2.8.17 build:cjs
3
+ > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
+
5
+ ViteCommonJSConfig: .
6
+ {
7
+ tsConfigPaths: [
8
+ '/home/runner/work/captcha/captcha/packages/common/tsconfig.json',
9
+ '/home/runner/work/captcha/captcha/packages/locale/tsconfig.json',
10
+ '/home/runner/work/captcha/captcha/packages/types/tsconfig.json',
11
+ '/home/runner/work/captcha/captcha/packages/util/tsconfig.json',
12
+ '/home/runner/work/captcha/captcha/packages/util-crypto/tsconfig.json'
13
+ ]
14
+ }
15
+ {
16
+ externals: [
17
+ '@prosopo/common',
18
+ '@prosopo/locale',
19
+ '@prosopo/types',
20
+ '@prosopo/util',
21
+ '@prosopo/util-crypto'
22
+ ]
23
+ }
24
+ vite v6.4.1 building SSR bundle for production...
25
+ Bundle build started
26
+ transforming...
27
+ Build end
28
+ ✓ 3 modules transformed.
29
+ rendering chunks...
30
+ dist/cjs/index.cjs 0.53 kB
31
+ dist/cjs/providers.cjs 1.39 kB
32
+ dist/cjs/balancer.cjs 1.67 kB
33
+ ✓ built in 187ms
@@ -0,0 +1,29 @@
1
+
2
+ > @prosopo/load-balancer@2.8.17 build:tsc
3
+ > tsc --build --verbose
4
+
5
+ 4:56:53 PM - Projects in this build:
6
+ * ../../dev/config/tsconfig.json
7
+ * ../locale/tsconfig.json
8
+ * ../util/tsconfig.json
9
+ * ../util-crypto/tsconfig.json
10
+ * ../types/tsconfig.json
11
+ * ../common/tsconfig.json
12
+ * tsconfig.json
13
+
14
+ 4:56:53 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/dependencies.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
15
+
16
+ 4:56:53 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
17
+
18
+ 4:56:53 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/verifyRecency.ts' is older than output '../util/tsconfig.tsbuildinfo'
19
+
20
+ 4:56:53 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'
21
+
22
+ 4:56:53 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/procaptcha/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
23
+
24
+ 4:56:53 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/logger.ts' is older than output '../common/tsconfig.tsbuildinfo'
25
+
26
+ 4:56:53 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
27
+
28
+ 4:56:53 PM - Building project '/home/runner/work/captcha/captcha/packages/load-balancer/tsconfig.json'...
29
+
@@ -0,0 +1,37 @@
1
+
2
+ > @prosopo/load-balancer@2.8.17 build
3
+ > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
+
5
+
6
+ > @prosopo/load-balancer@2.8.17 build:cross-env
7
+ > vite build --config vite.esm.config.ts --mode production
8
+
9
+ ViteEsmConfig: .
10
+ {
11
+ tsConfigPaths: [
12
+ '/home/runner/work/captcha/captcha/packages/common/tsconfig.json',
13
+ '/home/runner/work/captcha/captcha/packages/locale/tsconfig.json',
14
+ '/home/runner/work/captcha/captcha/packages/types/tsconfig.json',
15
+ '/home/runner/work/captcha/captcha/packages/util/tsconfig.json',
16
+ '/home/runner/work/captcha/captcha/packages/util-crypto/tsconfig.json'
17
+ ]
18
+ }
19
+ {
20
+ externals: [
21
+ '@prosopo/common',
22
+ '@prosopo/locale',
23
+ '@prosopo/types',
24
+ '@prosopo/util',
25
+ '@prosopo/util-crypto'
26
+ ]
27
+ }
28
+ vite v6.4.1 building SSR bundle for production...
29
+ Bundle build started
30
+ transforming...
31
+ Build end
32
+ ✓ 3 modules transformed.
33
+ rendering chunks...
34
+ dist/index.js 0.33 kB
35
+ dist/providers.js 1.22 kB
36
+ dist/balancer.js 1.50 kB
37
+ ✓ built in 150ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # @prosopo/load-balancer
2
2
 
3
+ ## 2.8.17
4
+ ### Patch Changes
5
+
6
+ - 0a38892: feat/cross-os-testing
7
+ - a8faa9a: bump license year
8
+ - 3acc333: Release 3.3.0
9
+ - Updated dependencies [3acc333]
10
+ - Updated dependencies [0a38892]
11
+ - Updated dependencies [1ee3d80]
12
+ - Updated dependencies [a8faa9a]
13
+ - Updated dependencies [7543d17]
14
+ - Updated dependencies [3acc333]
15
+ - @prosopo/types@3.8.0
16
+ - @prosopo/common@3.1.28
17
+
18
+ ## 2.8.16
19
+ ### Patch Changes
20
+
21
+ - Updated dependencies [141e462]
22
+ - @prosopo/types@3.7.2
23
+
24
+ ## 2.8.15
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies [345b25b]
28
+ - @prosopo/types@3.7.1
29
+
30
+ ## 2.8.14
31
+ ### Patch Changes
32
+
33
+ - Updated dependencies [ce70a2b]
34
+ - Updated dependencies [c2b940f]
35
+ - Updated dependencies [f6b5094]
36
+ - @prosopo/types@3.7.0
37
+ - @prosopo/common@3.1.27
38
+
39
+ ## 2.8.13
40
+ ### Patch Changes
41
+
42
+ - 7d5eb3f: bump
43
+ - Updated dependencies [7d5eb3f]
44
+ - @prosopo/common@3.1.26
45
+ - @prosopo/types@3.6.4
46
+
3
47
  ## 2.8.12
4
48
  ### Patch Changes
5
49
 
@@ -0,0 +1,25 @@
1
+ import type { EnvironmentTypes } from "@prosopo/types";
2
+ import { z } from "zod";
3
+ declare const HardcodedProviderSchema: z.ZodObject<{
4
+ address: z.ZodString;
5
+ url: z.ZodString;
6
+ datasetId: z.ZodString;
7
+ weight: z.ZodEffects<z.ZodDefault<z.ZodOptional<z.ZodNumber>>, number, number | undefined>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ address: string;
10
+ url: string;
11
+ datasetId: string;
12
+ weight: number;
13
+ }, {
14
+ address: string;
15
+ url: string;
16
+ datasetId: string;
17
+ weight?: number | undefined;
18
+ }>;
19
+ export type HardcodedProvider = z.infer<typeof HardcodedProviderSchema>;
20
+ type hostedProviders = Record<string, unknown>;
21
+ export declare const convertHostedProvider: (provider: hostedProviders) => HardcodedProvider[];
22
+ export declare const getLoadBalancerUrl: (environment: EnvironmentTypes) => string;
23
+ export declare const loadBalancer: (environment: EnvironmentTypes) => Promise<HardcodedProvider[]>;
24
+ export {};
25
+ //# sourceMappingURL=balancer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"balancer.d.ts","sourceRoot":"","sources":["../src/balancer.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,uBAAuB;;;;;;;;;;;;;;;EAa3B,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/C,eAAO,MAAM,qBAAqB,aACvB,eAAe,KACvB,iBAAiB,EAKnB,CAAC;AAEF,eAAO,MAAM,kBAAkB,gBAAiB,gBAAgB,KAAG,MAUlE,CAAC;AAEF,eAAO,MAAM,YAAY,gBACX,gBAAgB,KAC3B,OAAO,CAAC,iBAAiB,EAAE,CAqB7B,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"balancer.js","sourceRoot":"","sources":["../src/balancer.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;IACf,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;IACrB,MAAM,EAAE,CAAC;SACP,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,OAAO,CAAC,CAAC,CAAC;SACV,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE;QAElB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3C,CAAC,CAAC;CACH,CAAC,CAAC;AAMH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACpC,QAAyB,EACH,EAAE;IACxB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACnD,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC,CAChC,CAAC;IACF,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7D,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,WAA6B,EAAU,EAAE;IAC3E,IAAI,WAAW,KAAK,YAAY,EAAE,CAAC;QAClC,OAAO,mCAAmC,CAAC;IAC5C,CAAC;IACD,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,+CAA+C,CAAC;IACxD,CAAC;IACD,MAAM,IAAI,eAAe,CAAC,4BAA4B,EAAE;QACvD,OAAO,EAAE,EAAE,WAAW,EAAE;KACxB,CAAC,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAChC,WAA6B,EACE,EAAE;IACjC,IAAI,WAAW,KAAK,aAAa,EAAE,CAAC;QACnC,OAAO;YACN;gBACC,OAAO,EAAE,kDAAkD;gBAC3D,GAAG,EAAE,uBAAuB;gBAC5B,SAAS,EACR,oEAAoE;gBACrE,MAAM,EAAE,CAAC;aACT;SACD,CAAC;IACH,CAAC;IAED,MAAM,SAAS,GAAoB,MAAM,KAAK,CAC7C,kBAAkB,CAAC,WAAW,CAAC,EAC/B;QACC,MAAM,EAAE,KAAK;QACb,IAAI,EAAE,MAAM;KACZ,CACD,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5B,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAC;AACzC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export * from "./providers.js";
2
+ export * from "./balancer.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { EnvironmentTypes, RandomProvider } from "@prosopo/types";
2
+ import { type HardcodedProvider } from "./index.js";
3
+ export declare function _resetCache(): void;
4
+ export declare function selectWeightedProvider(providers: HardcodedProvider[], entropy: number): HardcodedProvider;
5
+ export declare const getRandomActiveProvider: (env: EnvironmentTypes, entropy: number) => Promise<RandomProvider>;
6
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACvE,OAAO,EAAE,KAAK,iBAAiB,EAAgB,MAAM,YAAY,CAAC;AAIlE,wBAAgB,WAAW,SAE1B;AAUD,wBAAgB,sBAAsB,CACrC,SAAS,EAAE,iBAAiB,EAAE,EAC9B,OAAO,EAAE,MAAM,GACb,iBAAiB,CAyBnB;AAED,eAAO,MAAM,uBAAuB,QAC9B,gBAAgB,WACZ,MAAM,KACb,OAAO,CAAC,cAAc,CAexB,CAAC"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAeA,OAAO,EAA0B,YAAY,EAAE,MAAM,YAAY,CAAC;AAElE,IAAI,eAAe,GAAwB,EAAE,CAAC;AAE9C,MAAM,UAAU,WAAW;IAC1B,eAAe,GAAG,EAAE,CAAC;AACtB,CAAC;AAUD,MAAM,UAAU,sBAAsB,CACrC,SAA8B,EAC9B,OAAe;IAEf,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAGpE,MAAM,WAAW,GAAG,OAAO,GAAG,WAAW,CAAC;IAG1C,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QAClC,gBAAgB,IAAI,QAAQ,CAAC,MAAM,CAAC;QACpC,IAAI,WAAW,GAAG,gBAAgB,EAAE,CAAC;YACpC,OAAO,QAAQ,CAAC;QACjB,CAAC;IACF,CAAC;IAGD,MAAM,gBAAgB,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,gBAAgB,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,EAC3C,GAAqB,EACrB,OAAe,EACW,EAAE;IAC5B,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAElC,eAAe,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAE3E,OAAO;QACN,eAAe,EAAE,iBAAiB,CAAC,OAAO;QAC1C,QAAQ,EAAE;YACT,GAAG,EAAE,iBAAiB,CAAC,GAAG;YAC1B,SAAS,EAAE,iBAAiB,CAAC,SAAS;SACtC;KACD,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=providers.unit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/providers.unit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,224 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { loadBalancer } from "../index.js";
3
+ import { getRandomActiveProvider, selectWeightedProvider, } from "../providers.js";
4
+ import { _resetCache } from "../providers.js";
5
+ vi.mock("../index.js", () => ({
6
+ loadBalancer: vi.fn(),
7
+ }));
8
+ describe("selectWeightedProvider", () => {
9
+ it("selects provider based on weight distribution", () => {
10
+ const providers = [
11
+ {
12
+ address: "address1",
13
+ url: "url1",
14
+ datasetId: "dataset1",
15
+ weight: 1,
16
+ },
17
+ {
18
+ address: "address2",
19
+ url: "url2",
20
+ datasetId: "dataset2",
21
+ weight: 3,
22
+ },
23
+ {
24
+ address: "address3",
25
+ url: "url3",
26
+ datasetId: "dataset3",
27
+ weight: 1,
28
+ },
29
+ ];
30
+ expect(selectWeightedProvider(providers, 0).address).toBe("address1");
31
+ expect(selectWeightedProvider(providers, 1).address).toBe("address2");
32
+ expect(selectWeightedProvider(providers, 2).address).toBe("address2");
33
+ expect(selectWeightedProvider(providers, 3).address).toBe("address2");
34
+ expect(selectWeightedProvider(providers, 4).address).toBe("address3");
35
+ expect(selectWeightedProvider(providers, 5).address).toBe("address1");
36
+ expect(selectWeightedProvider(providers, 6).address).toBe("address2");
37
+ });
38
+ it("handles equal weights correctly", () => {
39
+ const providers = [
40
+ {
41
+ address: "address1",
42
+ url: "url1",
43
+ datasetId: "dataset1",
44
+ weight: 1,
45
+ },
46
+ {
47
+ address: "address2",
48
+ url: "url2",
49
+ datasetId: "dataset2",
50
+ weight: 1,
51
+ },
52
+ ];
53
+ expect(selectWeightedProvider(providers, 0).address).toBe("address1");
54
+ expect(selectWeightedProvider(providers, 1).address).toBe("address2");
55
+ expect(selectWeightedProvider(providers, 2).address).toBe("address1");
56
+ expect(selectWeightedProvider(providers, 3).address).toBe("address2");
57
+ });
58
+ it("handles single provider", () => {
59
+ const providers = [
60
+ {
61
+ address: "address1",
62
+ url: "url1",
63
+ datasetId: "dataset1",
64
+ weight: 10,
65
+ },
66
+ ];
67
+ expect(selectWeightedProvider(providers, 0).address).toBe("address1");
68
+ expect(selectWeightedProvider(providers, 5).address).toBe("address1");
69
+ expect(selectWeightedProvider(providers, 100).address).toBe("address1");
70
+ });
71
+ it("throws error for empty provider list", () => {
72
+ expect(() => selectWeightedProvider([], 0)).toThrow("No providers available");
73
+ });
74
+ it("heavily weighted provider is selected more often", () => {
75
+ const providers = [
76
+ {
77
+ address: "address1",
78
+ url: "url1",
79
+ datasetId: "dataset1",
80
+ weight: 1,
81
+ },
82
+ {
83
+ address: "address2",
84
+ url: "url2",
85
+ datasetId: "dataset2",
86
+ weight: 99,
87
+ },
88
+ ];
89
+ const selections = { address1: 0, address2: 0 };
90
+ for (let i = 0; i < 100; i++) {
91
+ const selected = selectWeightedProvider(providers, i);
92
+ if (selected.address === "address1") {
93
+ selections.address1++;
94
+ }
95
+ else {
96
+ selections.address2++;
97
+ }
98
+ }
99
+ expect(selections.address1).toBe(1);
100
+ expect(selections.address2).toBe(99);
101
+ });
102
+ it("handles maximum weight value (100)", () => {
103
+ const providers = [
104
+ {
105
+ address: "address1",
106
+ url: "url1",
107
+ datasetId: "dataset1",
108
+ weight: 100,
109
+ },
110
+ {
111
+ address: "address2",
112
+ url: "url2",
113
+ datasetId: "dataset2",
114
+ weight: 100,
115
+ },
116
+ ];
117
+ const selections = { address1: 0, address2: 0 };
118
+ for (let i = 0; i < 200; i++) {
119
+ const selected = selectWeightedProvider(providers, i);
120
+ if (selected.address === "address1") {
121
+ selections.address1++;
122
+ }
123
+ else {
124
+ selections.address2++;
125
+ }
126
+ }
127
+ expect(selections.address1).toBe(100);
128
+ expect(selections.address2).toBe(100);
129
+ });
130
+ it("correctly handles providers without values for weight", () => {
131
+ const providers = [
132
+ {
133
+ address: "address1",
134
+ url: "url1",
135
+ datasetId: "dataset1",
136
+ },
137
+ {
138
+ address: "address2",
139
+ url: "url2",
140
+ datasetId: "dataset2",
141
+ weight: 3,
142
+ },
143
+ ];
144
+ const providersWithDefaults = [
145
+ { ...providers[0], weight: 1 },
146
+ providers[1],
147
+ ];
148
+ const selections = { address1: 0, address2: 0 };
149
+ for (let i = 0; i < 100; i++) {
150
+ const selected = selectWeightedProvider(providersWithDefaults, i);
151
+ if (selected.address === "address1") {
152
+ selections.address1++;
153
+ }
154
+ else {
155
+ selections.address2++;
156
+ }
157
+ }
158
+ expect(selections.address1).toBe(25);
159
+ expect(selections.address2).toBe(75);
160
+ });
161
+ });
162
+ describe("getRandomActiveProvider", () => {
163
+ beforeEach(() => {
164
+ _resetCache();
165
+ vi.clearAllMocks();
166
+ vi.resetAllMocks();
167
+ vi.resetModules();
168
+ });
169
+ it("returns a random provider when providers list is populated", async () => {
170
+ const mockProviders = [
171
+ { address: "address1", url: "url1", datasetId: "dataset1", weight: 1 },
172
+ { address: "address2", url: "url2", datasetId: "dataset2", weight: 1 },
173
+ ];
174
+ loadBalancer.mockResolvedValue(mockProviders);
175
+ const result = await getRandomActiveProvider("development", 1);
176
+ expect(result.providerAccount).toBe("address2");
177
+ expect(result.provider.url).toBe("url2");
178
+ expect(result.provider.datasetId).toBe("dataset2");
179
+ });
180
+ it("loads providers only once when called multiple times", async () => {
181
+ const mockProviders = [
182
+ { address: "address1", url: "url1", datasetId: "dataset1", weight: 1 },
183
+ ];
184
+ loadBalancer.mockResolvedValue(mockProviders);
185
+ await getRandomActiveProvider("development", 123);
186
+ await getRandomActiveProvider("development", 456);
187
+ expect(loadBalancer).toHaveBeenCalledTimes(1);
188
+ });
189
+ it("handles empty providers list gracefully", async () => {
190
+ loadBalancer.mockResolvedValue([]);
191
+ await expect(getRandomActiveProvider("development", 123)).rejects.toThrow();
192
+ });
193
+ it("respects provider weights when selecting", async () => {
194
+ const mockProviders = [
195
+ { address: "address1", url: "url1", datasetId: "dataset1", weight: 1 },
196
+ { address: "address2", url: "url2", datasetId: "dataset2", weight: 3 },
197
+ ];
198
+ loadBalancer.mockResolvedValue(mockProviders);
199
+ const result0 = await getRandomActiveProvider("development", 0);
200
+ expect(result0.providerAccount).toBe("address1");
201
+ _resetCache();
202
+ const result1 = await getRandomActiveProvider("development", 1);
203
+ expect(result1.providerAccount).toBe("address2");
204
+ _resetCache();
205
+ const result2 = await getRandomActiveProvider("development", 2);
206
+ expect(result2.providerAccount).toBe("address2");
207
+ _resetCache();
208
+ const result3 = await getRandomActiveProvider("development", 3);
209
+ expect(result3.providerAccount).toBe("address2");
210
+ });
211
+ it("handles providers with missing weight field (defaults to 1)", async () => {
212
+ const mockProviders = [
213
+ { address: "address1", url: "url1", datasetId: "dataset1", weight: 1 },
214
+ { address: "address2", url: "url2", datasetId: "dataset2", weight: 1 },
215
+ ];
216
+ loadBalancer.mockResolvedValue(mockProviders);
217
+ const result0 = await getRandomActiveProvider("development", 0);
218
+ expect(result0.providerAccount).toBe("address1");
219
+ _resetCache();
220
+ const result1 = await getRandomActiveProvider("development", 1);
221
+ expect(result1.providerAccount).toBe("address2");
222
+ });
223
+ });
224
+ //# sourceMappingURL=providers.unit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.unit.test.js","sourceRoot":"","sources":["../../src/tests/providers.unit.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/load-balancer",
3
- "version": "2.8.12",
3
+ "version": "2.8.17",
4
4
  "description": "Provider load balancer",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -18,7 +18,8 @@
18
18
  },
19
19
  "scripts": {
20
20
  "clean": "del-cli --verbose dist tsconfig.tsbuildinfo",
21
- "build": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.esm.config.ts --mode $NODE_ENV",
21
+ "build": "npm run build:cross-env -- --mode ${NODE_ENV:-development}",
22
+ "build:cross-env": "vite build --config vite.esm.config.ts",
22
23
  "build:tsc": "tsc --build --verbose",
23
24
  "build:cjs": "NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV",
24
25
  "test": "NODE_ENV=${NODE_ENV:-test}; npx vitest run --config ./vite.test.config.ts --mode $NODE_ENV",
@@ -35,12 +36,12 @@
35
36
  },
36
37
  "homepage": "https://github.com/prosopo/captcha#readme",
37
38
  "dependencies": {
38
- "@prosopo/common": "3.1.25",
39
- "@prosopo/types": "3.6.3",
39
+ "@prosopo/common": "3.1.28",
40
+ "@prosopo/types": "3.8.0",
40
41
  "zod": "3.23.8"
41
42
  },
42
43
  "devDependencies": {
43
- "@prosopo/config": "3.1.25",
44
+ "@prosopo/config": "3.3.0",
44
45
  "@types/node": "22.10.2",
45
46
  "@vitest/coverage-v8": "3.2.4",
46
47
  "concurrently": "9.0.1",
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- // Copyright 2021-2025 Prosopo (UK) Ltd.
2
+ // Copyright 2021-2026 Prosopo (UK) Ltd.
3
3
  //
4
4
  // Licensed under the Apache License, Version 2.0 (the "License");
5
5
  // you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- // Copyright 2021-2025 Prosopo (UK) Ltd.
1
+ // Copyright 2021-2026 Prosopo (UK) Ltd.
2
2
  //
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- // Copyright 2021-2025 Prosopo (UK) Ltd.
1
+ // Copyright 2021-2026 Prosopo (UK) Ltd.
2
2
  //
3
3
  // Licensed under the Apache License, Version 2.0 (the "License");
4
4
  // you may not use this file except in compliance with the License.