@prosopo/load-balancer 2.10.6 → 2.10.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.
- package/.turbo/turbo-build$colon$cjs.log +4 -4
- package/.turbo/turbo-build$colon$tsc.log +11 -11
- package/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +23 -0
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/providers.cjs +31 -0
- package/dist/index.js +2 -1
- package/dist/providers.d.ts +1 -0
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +32 -1
- package/dist/providers.js.map +1 -1
- package/dist/tests/productionProviderList.unit.test.d.ts +2 -0
- package/dist/tests/productionProviderList.unit.test.d.ts.map +1 -0
- package/dist/tests/productionProviderList.unit.test.js +132 -0
- package/dist/tests/productionProviderList.unit.test.js.map +1 -0
- package/dist/tests/providers.unit.test.js +78 -1
- package/dist/tests/providers.unit.test.js.map +1 -1
- package/package.json +2 -2
- package/src/providers.ts +66 -0
- package/src/tests/productionProviderList.unit.test.ts +230 -0
- package/src/tests/providers.unit.test.ts +129 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/load-balancer@2.10.
|
|
2
|
+
> @prosopo/load-balancer@2.10.8 build:cjs
|
|
3
3
|
> NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
|
|
4
4
|
|
|
5
5
|
ViteCommonJSConfig: .
|
|
@@ -29,7 +29,7 @@ transforming...
|
|
|
29
29
|
Build end
|
|
30
30
|
[32m✓[39m 3 modules transformed.
|
|
31
31
|
rendering chunks...
|
|
32
|
-
[2mdist/cjs/[22m[36mindex.cjs [39m[1m[2m0.
|
|
32
|
+
[2mdist/cjs/[22m[36mindex.cjs [39m[1m[2m0.77 kB[22m[1m[22m
|
|
33
33
|
[2mdist/cjs/[22m[36mbalancer.cjs [39m[1m[2m2.21 kB[22m[1m[22m
|
|
34
|
-
[2mdist/cjs/[22m[36mproviders.cjs [39m[1m[
|
|
35
|
-
[32m✓ built in
|
|
34
|
+
[2mdist/cjs/[22m[36mproviders.cjs [39m[1m[2m3.92 kB[22m[1m[22m
|
|
35
|
+
[32m✓ built in 153ms[39m
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/load-balancer@2.10.
|
|
2
|
+
> @prosopo/load-balancer@2.10.8 build:tsc
|
|
3
3
|
> tsc --build --verbose
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
8:56:41 AM - Projects in this build:
|
|
6
6
|
* ../../dev/config/tsconfig.json
|
|
7
7
|
* ../locale/tsconfig.json
|
|
8
8
|
* ../util/tsconfig.json
|
|
@@ -12,21 +12,21 @@
|
|
|
12
12
|
* ../common/tsconfig.json
|
|
13
13
|
* tsconfig.json
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
8:56:41 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
8:56:41 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
8:56:41 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
8:56:41 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
8:56:41 AM - 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'
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
8:56:41 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
8:56:41 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
8:56:41 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
8:56:41 AM - Building project '/home/runner/work/captcha/captcha/packages/load-balancer/tsconfig.json'...
|
|
32
32
|
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/load-balancer@2.10.
|
|
2
|
+
> @prosopo/load-balancer@2.10.8 build
|
|
3
3
|
> npm run build:cross-env -- --mode ${NODE_ENV:-development}
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
> @prosopo/load-balancer@2.10.
|
|
6
|
+
> @prosopo/load-balancer@2.10.8 build:cross-env
|
|
7
7
|
> vite build --config vite.esm.config.ts --mode production
|
|
8
8
|
|
|
9
9
|
ViteEsmConfig: .
|
|
@@ -33,7 +33,7 @@ transforming...
|
|
|
33
33
|
Build end
|
|
34
34
|
[32m✓[39m 3 modules transformed.
|
|
35
35
|
rendering chunks...
|
|
36
|
-
[2mdist/[22m[36mindex.js [39m[1m[2m0.
|
|
36
|
+
[2mdist/[22m[36mindex.js [39m[1m[2m0.50 kB[22m[1m[22m
|
|
37
37
|
[2mdist/[22m[36mbalancer.js [39m[1m[2m1.98 kB[22m[1m[22m
|
|
38
|
-
[2mdist/[22m[36mproviders.js [39m[1m[
|
|
39
|
-
[32m✓ built in
|
|
38
|
+
[2mdist/[22m[36mproviders.js [39m[1m[2m3.70 kB[22m[1m[22m
|
|
39
|
+
[32m✓ built in 177ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# @prosopo/load-balancer
|
|
2
2
|
|
|
3
|
+
## 2.10.8
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
- Updated dependencies [8bde5df]
|
|
7
|
+
- @prosopo/types@4.9.7
|
|
8
|
+
|
|
9
|
+
## 2.10.7
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- b3f351b: fix(procaptcha): random provider re-selection + backoff on error fallback
|
|
13
|
+
|
|
14
|
+
When a provider errored, the widget retried the same DNS-routed endpoint immediately and in a tight loop. A fleet of widgets whose provider was unhealthy could therefore accidentally DDoS the provider fleet — retrying the same (possibly-down) endpoint as fast as the event loop allowed.
|
|
15
|
+
|
|
16
|
+
The error-fallback path now:
|
|
17
|
+
|
|
18
|
+
- **Re-selects a different provider on retry.** The first attempt still hits the DNS-routed endpoint (unchanged happy path, preserves session stickiness). On a retry the widget picks a random provider straight from the provider list (`getRandomProviderFromList`), weighted by provider capacity and excluding the URL that just failed. In development the list holds only the single local provider, so a retry simply re-targets that provider.
|
|
19
|
+
- **Backs off between retries.** `providerRetry` now waits an exponential-backoff-with-full-jitter delay (0.5s → 1s → 2s → 4s …, capped at 10s) before retrying, so a down provider is no longer hammered and a fleet of clients that all errored at once don't reconverge into a thundering herd.
|
|
20
|
+
|
|
21
|
+
Applies to the image, PoW and puzzle managers and the frictionless detection flow. New shared `ProviderSelectRetryContext` type; `BotDetectionFunction` gains an optional retry-context argument.
|
|
22
|
+
- Updated dependencies [b3f351b]
|
|
23
|
+
- Updated dependencies [17bc76e]
|
|
24
|
+
- @prosopo/types@4.9.6
|
|
25
|
+
|
|
3
26
|
## 2.10.6
|
|
4
27
|
### Patch Changes
|
|
5
28
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -6,6 +6,7 @@ exports._resetPinCache = providers._resetPinCache;
|
|
|
6
6
|
exports._resetProviderListCache = providers._resetProviderListCache;
|
|
7
7
|
exports.getProviders = providers.getProviders;
|
|
8
8
|
exports.getRandomActiveProvider = providers.getRandomActiveProvider;
|
|
9
|
+
exports.getRandomProviderFromList = providers.getRandomProviderFromList;
|
|
9
10
|
exports.convertHostedProvider = balancer.convertHostedProvider;
|
|
10
11
|
exports.getLoadBalancerUrl = balancer.getLoadBalancerUrl;
|
|
11
12
|
exports.getProviderHostname = balancer.getProviderHostname;
|
package/dist/cjs/providers.cjs
CHANGED
|
@@ -72,6 +72,36 @@ const getRandomActiveProvider = async (env, ipMode) => {
|
|
|
72
72
|
provider: { url }
|
|
73
73
|
};
|
|
74
74
|
};
|
|
75
|
+
const sameProviderUrl = (a, b) => a.replace(/\/$/, "") === b.replace(/\/$/, "");
|
|
76
|
+
const pickWeightedProvider = (providers, random) => {
|
|
77
|
+
const totalWeight = providers.reduce((sum, p) => sum + p.weight, 0);
|
|
78
|
+
let threshold = random() * totalWeight;
|
|
79
|
+
let chosen;
|
|
80
|
+
for (const provider of providers) {
|
|
81
|
+
chosen = provider;
|
|
82
|
+
threshold -= provider.weight;
|
|
83
|
+
if (threshold < 0) break;
|
|
84
|
+
}
|
|
85
|
+
return chosen;
|
|
86
|
+
};
|
|
87
|
+
const getRandomProviderFromList = async (env, ipMode, excludeUrl, random = Math.random) => {
|
|
88
|
+
const providers = await getProviders(env);
|
|
89
|
+
if (providers.length === 0) {
|
|
90
|
+
return getRandomActiveProvider(env, ipMode);
|
|
91
|
+
}
|
|
92
|
+
const eligible = excludeUrl && providers.length > 1 ? providers.filter(
|
|
93
|
+
(p) => !sameProviderUrl(applyIpModeToUrl(p.url, ipMode), excludeUrl)
|
|
94
|
+
) : providers;
|
|
95
|
+
const pool = eligible.length > 0 ? eligible : providers;
|
|
96
|
+
const chosen = pickWeightedProvider(pool, random);
|
|
97
|
+
if (!chosen) {
|
|
98
|
+
return getRandomActiveProvider(env, ipMode);
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
providerAccount: chosen.address,
|
|
102
|
+
provider: { url: applyIpModeToUrl(chosen.url, ipMode) }
|
|
103
|
+
};
|
|
104
|
+
};
|
|
75
105
|
const _resetPinCache = () => {
|
|
76
106
|
pinPromiseCache.clear();
|
|
77
107
|
};
|
|
@@ -82,3 +112,4 @@ exports._resetPinCache = _resetPinCache;
|
|
|
82
112
|
exports._resetProviderListCache = _resetProviderListCache;
|
|
83
113
|
exports.getProviders = getProviders;
|
|
84
114
|
exports.getRandomActiveProvider = getRandomActiveProvider;
|
|
115
|
+
exports.getRandomProviderFromList = getRandomProviderFromList;
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _resetPinCache, _resetProviderListCache, getProviders, getRandomActiveProvider } from "./providers.js";
|
|
1
|
+
import { _resetPinCache, _resetProviderListCache, getProviders, getRandomActiveProvider, getRandomProviderFromList } from "./providers.js";
|
|
2
2
|
import { convertHostedProvider, getLoadBalancerUrl, getProviderHostname, loadBalancer, stripIpModeLabel } from "./balancer.js";
|
|
3
3
|
export {
|
|
4
4
|
_resetPinCache,
|
|
@@ -8,6 +8,7 @@ export {
|
|
|
8
8
|
getProviderHostname,
|
|
9
9
|
getProviders,
|
|
10
10
|
getRandomActiveProvider,
|
|
11
|
+
getRandomProviderFromList,
|
|
11
12
|
loadBalancer,
|
|
12
13
|
stripIpModeLabel
|
|
13
14
|
};
|
package/dist/providers.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { EnvironmentTypes, RandomProvider } from "@prosopo/types";
|
|
|
2
2
|
import { type HardcodedProvider, type IpMode } from "./balancer.js";
|
|
3
3
|
export declare const getProviders: (env: EnvironmentTypes) => Promise<HardcodedProvider[]>;
|
|
4
4
|
export declare const getRandomActiveProvider: (env: EnvironmentTypes, ipMode?: IpMode) => Promise<RandomProvider>;
|
|
5
|
+
export declare const getRandomProviderFromList: (env: EnvironmentTypes, ipMode?: IpMode, excludeUrl?: string, random?: () => number) => Promise<RandomProvider>;
|
|
5
6
|
export declare const _resetPinCache: () => void;
|
|
6
7
|
export declare const _resetProviderListCache: () => void;
|
|
7
8
|
//# sourceMappingURL=providers.d.ts.map
|
package/dist/providers.d.ts.map
CHANGED
|
@@ -1 +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,EACN,KAAK,iBAAiB,EACtB,KAAK,MAAM,EAEX,MAAM,eAAe,CAAC;AA4GvB,eAAO,MAAM,YAAY,QACnB,gBAAgB,KACnB,OAAO,CAAC,iBAAiB,EAAE,CAY7B,CAAC;AAEF,eAAO,MAAM,uBAAuB,QAC9B,gBAAgB,WACZ,MAAM,KACb,OAAO,CAAC,cAAc,CAMxB,CAAC;AAIF,eAAO,MAAM,cAAc,YAE1B,CAAC;AAIF,eAAO,MAAM,uBAAuB,YAEnC,CAAC"}
|
|
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,EACN,KAAK,iBAAiB,EACtB,KAAK,MAAM,EAEX,MAAM,eAAe,CAAC;AA4GvB,eAAO,MAAM,YAAY,QACnB,gBAAgB,KACnB,OAAO,CAAC,iBAAiB,EAAE,CAY7B,CAAC;AAEF,eAAO,MAAM,uBAAuB,QAC9B,gBAAgB,WACZ,MAAM,KACb,OAAO,CAAC,cAAc,CAMxB,CAAC;AAoCF,eAAO,MAAM,yBAAyB,QAChC,gBAAgB,WACZ,MAAM,eACF,MAAM,WACX,MAAM,MAAM,KAClB,OAAO,CAAC,cAAc,CAyBxB,CAAC;AAIF,eAAO,MAAM,cAAc,YAE1B,CAAC;AAIF,eAAO,MAAM,uBAAuB,YAEnC,CAAC"}
|
package/dist/providers.js
CHANGED
|
@@ -70,6 +70,36 @@ const getRandomActiveProvider = async (env, ipMode) => {
|
|
|
70
70
|
provider: { url }
|
|
71
71
|
};
|
|
72
72
|
};
|
|
73
|
+
const sameProviderUrl = (a, b) => a.replace(/\/$/, "") === b.replace(/\/$/, "");
|
|
74
|
+
const pickWeightedProvider = (providers, random) => {
|
|
75
|
+
const totalWeight = providers.reduce((sum, p) => sum + p.weight, 0);
|
|
76
|
+
let threshold = random() * totalWeight;
|
|
77
|
+
let chosen;
|
|
78
|
+
for (const provider of providers) {
|
|
79
|
+
chosen = provider;
|
|
80
|
+
threshold -= provider.weight;
|
|
81
|
+
if (threshold < 0) break;
|
|
82
|
+
}
|
|
83
|
+
return chosen;
|
|
84
|
+
};
|
|
85
|
+
const getRandomProviderFromList = async (env, ipMode, excludeUrl, random = Math.random) => {
|
|
86
|
+
const providers = await getProviders(env);
|
|
87
|
+
if (providers.length === 0) {
|
|
88
|
+
return getRandomActiveProvider(env, ipMode);
|
|
89
|
+
}
|
|
90
|
+
const eligible = excludeUrl && providers.length > 1 ? providers.filter(
|
|
91
|
+
(p) => !sameProviderUrl(applyIpModeToUrl(p.url, ipMode), excludeUrl)
|
|
92
|
+
) : providers;
|
|
93
|
+
const pool = eligible.length > 0 ? eligible : providers;
|
|
94
|
+
const chosen = pickWeightedProvider(pool, random);
|
|
95
|
+
if (!chosen) {
|
|
96
|
+
return getRandomActiveProvider(env, ipMode);
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
providerAccount: chosen.address,
|
|
100
|
+
provider: { url: applyIpModeToUrl(chosen.url, ipMode) }
|
|
101
|
+
};
|
|
102
|
+
};
|
|
73
103
|
const _resetPinCache = () => {
|
|
74
104
|
pinPromiseCache.clear();
|
|
75
105
|
};
|
|
@@ -80,5 +110,6 @@ export {
|
|
|
80
110
|
_resetPinCache,
|
|
81
111
|
_resetProviderListCache,
|
|
82
112
|
getProviders,
|
|
83
|
-
getRandomActiveProvider
|
|
113
|
+
getRandomActiveProvider,
|
|
114
|
+
getRandomProviderFromList
|
|
84
115
|
};
|
package/dist/providers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"productionProviderList.unit.test.d.ts","sourceRoot":"","sources":["../../src/tests/productionProviderList.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { convertHostedProvider, getProviderHostname, loadBalancer, } from "../balancer.js";
|
|
3
|
+
import { _resetPinCache, _resetProviderListCache, getRandomProviderFromList, } from "../providers.js";
|
|
4
|
+
const DATASET_ID = "0x2e52a1e70f0e0c808972bc04e6fbc8f264b1fbb29c94041fdf01a839b000a40e";
|
|
5
|
+
const ADDRESS = "5FnBurrfqWgSLJFMsojjEP74mLX1vZZ9ASyNXKfA5YXu8FR2";
|
|
6
|
+
const entry = (host) => ({
|
|
7
|
+
url: `https://${host}.prosopo.io`,
|
|
8
|
+
datasetId: DATASET_ID,
|
|
9
|
+
address: ADDRESS,
|
|
10
|
+
});
|
|
11
|
+
const productionJson = {
|
|
12
|
+
ipv4: {
|
|
13
|
+
pronode6: entry("ipv4.pronode6"),
|
|
14
|
+
pronode7: entry("ipv4.pronode7"),
|
|
15
|
+
pronode16: entry("ipv4.pronode16"),
|
|
16
|
+
},
|
|
17
|
+
ipv6: {
|
|
18
|
+
pronode6: entry("ipv6.pronode6"),
|
|
19
|
+
pronode7: entry("ipv6.pronode7"),
|
|
20
|
+
pronode16: entry("ipv6.pronode16"),
|
|
21
|
+
},
|
|
22
|
+
pronode6: entry("pronode6"),
|
|
23
|
+
pronode7: entry("pronode7"),
|
|
24
|
+
pronode16: entry("pronode16"),
|
|
25
|
+
};
|
|
26
|
+
const originalFetch = globalThis.fetch;
|
|
27
|
+
const mockFetchReturning = (payload) => {
|
|
28
|
+
const mocked = vi.fn(async () => new Response(JSON.stringify(payload), { status: 200 }));
|
|
29
|
+
globalThis.fetch = mocked;
|
|
30
|
+
return mocked;
|
|
31
|
+
};
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
_resetPinCache();
|
|
34
|
+
_resetProviderListCache();
|
|
35
|
+
});
|
|
36
|
+
afterEach(() => {
|
|
37
|
+
globalThis.fetch = originalFetch;
|
|
38
|
+
vi.restoreAllMocks();
|
|
39
|
+
});
|
|
40
|
+
describe("convertHostedProvider (production.json shape)", () => {
|
|
41
|
+
it("keeps only the dual-stack pronodes and drops the ipv4/ipv6 sub-lists", () => {
|
|
42
|
+
const providers = convertHostedProvider(productionJson);
|
|
43
|
+
expect(providers.map((p) => p.url)).toEqual([
|
|
44
|
+
"https://pronode16.prosopo.io",
|
|
45
|
+
"https://pronode6.prosopo.io",
|
|
46
|
+
"https://pronode7.prosopo.io",
|
|
47
|
+
]);
|
|
48
|
+
for (const provider of providers) {
|
|
49
|
+
expect(provider.weight).toBe(1);
|
|
50
|
+
expect(provider.datasetId).toBe(DATASET_ID);
|
|
51
|
+
expect(provider.address).toBe(ADDRESS);
|
|
52
|
+
expect(provider.url).not.toMatch(/ipv[46]\./);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
it("returns the ipv4 single-stack sub-list when ipMode=ipv4", () => {
|
|
56
|
+
const providers = convertHostedProvider(productionJson, "ipv4");
|
|
57
|
+
expect(providers.map((p) => p.url)).toEqual([
|
|
58
|
+
"https://ipv4.pronode16.prosopo.io",
|
|
59
|
+
"https://ipv4.pronode6.prosopo.io",
|
|
60
|
+
"https://ipv4.pronode7.prosopo.io",
|
|
61
|
+
]);
|
|
62
|
+
expect(providers.map((p) => getProviderHostname(p))).toEqual([
|
|
63
|
+
"pronode16.prosopo.io",
|
|
64
|
+
"pronode6.prosopo.io",
|
|
65
|
+
"pronode7.prosopo.io",
|
|
66
|
+
]);
|
|
67
|
+
});
|
|
68
|
+
it("returns the ipv6 single-stack sub-list when ipMode=ipv6", () => {
|
|
69
|
+
const providers = convertHostedProvider(productionJson, "ipv6");
|
|
70
|
+
expect(providers.map((p) => p.url)).toEqual([
|
|
71
|
+
"https://ipv6.pronode16.prosopo.io",
|
|
72
|
+
"https://ipv6.pronode6.prosopo.io",
|
|
73
|
+
"https://ipv6.pronode7.prosopo.io",
|
|
74
|
+
]);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe("loadBalancer (production.json shape)", () => {
|
|
78
|
+
it("fetches the production list and returns the dual-stack pronodes", async () => {
|
|
79
|
+
const mocked = mockFetchReturning(productionJson);
|
|
80
|
+
const providers = await loadBalancer("production");
|
|
81
|
+
expect(mocked).toHaveBeenCalledWith("https://provider-list.prosopo.io/", expect.objectContaining({ method: "GET" }));
|
|
82
|
+
expect(providers.map((p) => p.url)).toEqual([
|
|
83
|
+
"https://pronode16.prosopo.io",
|
|
84
|
+
"https://pronode6.prosopo.io",
|
|
85
|
+
"https://pronode7.prosopo.io",
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe("getRandomProviderFromList (production.json end-to-end)", () => {
|
|
90
|
+
const dualStackUrls = [
|
|
91
|
+
"https://pronode16.prosopo.io",
|
|
92
|
+
"https://pronode6.prosopo.io",
|
|
93
|
+
"https://pronode7.prosopo.io",
|
|
94
|
+
];
|
|
95
|
+
it("picks a dual-stack pronode from the fetched production list", async () => {
|
|
96
|
+
mockFetchReturning(productionJson);
|
|
97
|
+
const result = await getRandomProviderFromList("production", undefined, undefined, () => 0);
|
|
98
|
+
expect(result.provider.url).toBe("https://pronode16.prosopo.io");
|
|
99
|
+
expect(result.providerAccount).toBe(ADDRESS);
|
|
100
|
+
});
|
|
101
|
+
it("labels the chosen pronode with ipv4 when ipMode=ipv4", async () => {
|
|
102
|
+
mockFetchReturning(productionJson);
|
|
103
|
+
const result = await getRandomProviderFromList("production", "ipv4", undefined, () => 0);
|
|
104
|
+
expect(result.provider.url).toBe("https://ipv4.pronode16.prosopo.io");
|
|
105
|
+
});
|
|
106
|
+
it("labels the chosen pronode with ipv6 when ipMode=ipv6", async () => {
|
|
107
|
+
mockFetchReturning(productionJson);
|
|
108
|
+
const result = await getRandomProviderFromList("production", "ipv6", undefined, () => 0.99);
|
|
109
|
+
expect(result.provider.url).toBe("https://ipv6.pronode7.prosopo.io");
|
|
110
|
+
});
|
|
111
|
+
it("excludes the pronode that just failed and picks a different one", async () => {
|
|
112
|
+
mockFetchReturning(productionJson);
|
|
113
|
+
const result = await getRandomProviderFromList("production", undefined, "https://pronode16.prosopo.io", () => 0);
|
|
114
|
+
expect(result.provider.url).not.toBe("https://pronode16.prosopo.io");
|
|
115
|
+
expect(dualStackUrls).toContain(result.provider.url);
|
|
116
|
+
expect(result.provider.url).toBe("https://pronode6.prosopo.io");
|
|
117
|
+
});
|
|
118
|
+
it("excludes by ipv4-labelled url, matching what the widget actually used", async () => {
|
|
119
|
+
mockFetchReturning(productionJson);
|
|
120
|
+
const result = await getRandomProviderFromList("production", "ipv4", "https://ipv4.pronode16.prosopo.io", () => 0);
|
|
121
|
+
expect(result.provider.url).not.toBe("https://ipv4.pronode16.prosopo.io");
|
|
122
|
+
expect(result.provider.url).toBe("https://ipv4.pronode6.prosopo.io");
|
|
123
|
+
});
|
|
124
|
+
it("serves the cached list across repeat selections (one fetch)", async () => {
|
|
125
|
+
const mocked = mockFetchReturning(productionJson);
|
|
126
|
+
await getRandomProviderFromList("production", undefined, undefined, () => 0);
|
|
127
|
+
await getRandomProviderFromList("production", "ipv4", undefined, () => 0);
|
|
128
|
+
await getRandomProviderFromList("production", "ipv6", undefined, () => 0);
|
|
129
|
+
expect(mocked).toHaveBeenCalledTimes(1);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
//# sourceMappingURL=productionProviderList.unit.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"productionProviderList.unit.test.js","sourceRoot":"","sources":["../../src/tests/productionProviderList.unit.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { _resetPinCache, _resetProviderListCache, getProviders, getRandomActiveProvider, } from "../providers.js";
|
|
2
|
+
import { _resetPinCache, _resetProviderListCache, getProviders, getRandomActiveProvider, getRandomProviderFromList, } from "../providers.js";
|
|
3
3
|
const loadBalancer = vi.fn();
|
|
4
4
|
vi.mock("../balancer.js", () => ({
|
|
5
5
|
loadBalancer: (...args) => loadBalancer(...args),
|
|
@@ -129,4 +129,81 @@ describe("getProviders", () => {
|
|
|
129
129
|
expect(loadBalancer).toHaveBeenCalledTimes(2);
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
|
+
describe("getRandomProviderFromList", () => {
|
|
133
|
+
const providerList = [
|
|
134
|
+
{
|
|
135
|
+
address: "5A",
|
|
136
|
+
url: "https://provider-a.io",
|
|
137
|
+
datasetId: "0x0",
|
|
138
|
+
weight: 1,
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
address: "5B",
|
|
142
|
+
url: "https://provider-b.io",
|
|
143
|
+
datasetId: "0x0",
|
|
144
|
+
weight: 1,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
address: "5C",
|
|
148
|
+
url: "https://provider-c.io",
|
|
149
|
+
datasetId: "0x0",
|
|
150
|
+
weight: 1,
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
beforeEach(() => {
|
|
154
|
+
_resetProviderListCache();
|
|
155
|
+
_resetPinCache();
|
|
156
|
+
loadBalancer.mockReset();
|
|
157
|
+
});
|
|
158
|
+
it("picks a provider from the list (not the DNS-routed endpoint)", async () => {
|
|
159
|
+
loadBalancer.mockResolvedValue(providerList);
|
|
160
|
+
const result = await getRandomProviderFromList("production", undefined, undefined, () => 0.4);
|
|
161
|
+
expect(result.provider.url).toBe("https://provider-b.io");
|
|
162
|
+
expect(result.providerAccount).toBe("5B");
|
|
163
|
+
});
|
|
164
|
+
it("excludes the provider that just failed when others remain", async () => {
|
|
165
|
+
loadBalancer.mockResolvedValue(providerList);
|
|
166
|
+
const result = await getRandomProviderFromList("production", undefined, "https://provider-a.io", () => 0);
|
|
167
|
+
expect(result.provider.url).not.toBe("https://provider-a.io");
|
|
168
|
+
expect(result.provider.url).toBe("https://provider-b.io");
|
|
169
|
+
});
|
|
170
|
+
it("ignores a trailing slash when matching the excluded url", async () => {
|
|
171
|
+
loadBalancer.mockResolvedValue(providerList);
|
|
172
|
+
const result = await getRandomProviderFromList("production", undefined, "https://provider-a.io/", () => 0);
|
|
173
|
+
expect(result.provider.url).toBe("https://provider-b.io");
|
|
174
|
+
});
|
|
175
|
+
it("still returns the only provider in development even if it is excluded", async () => {
|
|
176
|
+
const single = [
|
|
177
|
+
{
|
|
178
|
+
address: "5Local",
|
|
179
|
+
url: "https://localhost:9229",
|
|
180
|
+
datasetId: "0x0",
|
|
181
|
+
weight: 1,
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
loadBalancer.mockResolvedValue(single);
|
|
185
|
+
const result = await getRandomProviderFromList("development", undefined, "https://localhost:9229", () => 0);
|
|
186
|
+
expect(result.provider.url).toBe("https://localhost:9229");
|
|
187
|
+
});
|
|
188
|
+
it("applies the ipv4 label to the chosen provider url", async () => {
|
|
189
|
+
loadBalancer.mockResolvedValue(providerList);
|
|
190
|
+
const result = await getRandomProviderFromList("production", "ipv4", undefined, () => 0);
|
|
191
|
+
expect(result.provider.url).toBe("https://ipv4.provider-a.io");
|
|
192
|
+
});
|
|
193
|
+
it("weights the pick by provider weight", async () => {
|
|
194
|
+
const weighted = [
|
|
195
|
+
{ address: "5A", url: "https://a.io", datasetId: "0x0", weight: 1 },
|
|
196
|
+
{ address: "5B", url: "https://b.io", datasetId: "0x0", weight: 99 },
|
|
197
|
+
];
|
|
198
|
+
loadBalancer.mockResolvedValue(weighted);
|
|
199
|
+
const result = await getRandomProviderFromList("production", undefined, undefined, () => 0.5);
|
|
200
|
+
expect(result.provider.url).toBe("https://b.io");
|
|
201
|
+
});
|
|
202
|
+
it("falls back to the DNS-routed endpoint when the list is empty", async () => {
|
|
203
|
+
loadBalancer.mockResolvedValue([]);
|
|
204
|
+
const result = await getRandomProviderFromList("development", undefined, undefined, () => 0);
|
|
205
|
+
expect(result.provider.url).toBe("https://localhost:9229");
|
|
206
|
+
expect(result.providerAccount).toBe("dns-routed");
|
|
207
|
+
});
|
|
208
|
+
});
|
|
132
209
|
//# sourceMappingURL=providers.unit.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.unit.test.js","sourceRoot":"","sources":["../../src/tests/providers.unit.test.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;
|
|
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.10.
|
|
3
|
+
"version": "2.10.8",
|
|
4
4
|
"description": "Provider load balancer",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"homepage": "https://github.com/prosopo/captcha#readme",
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@prosopo/common": "3.1.43",
|
|
41
|
-
"@prosopo/types": "4.9.
|
|
41
|
+
"@prosopo/types": "4.9.7",
|
|
42
42
|
"zod": "3.23.8"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
package/src/providers.ts
CHANGED
|
@@ -152,6 +152,72 @@ export const getRandomActiveProvider = async (
|
|
|
152
152
|
};
|
|
153
153
|
};
|
|
154
154
|
|
|
155
|
+
// Compare two provider URLs ignoring a trailing slash so a stored "failed" URL
|
|
156
|
+
// still matches its list entry regardless of formatting.
|
|
157
|
+
const sameProviderUrl = (a: string, b: string): boolean =>
|
|
158
|
+
a.replace(/\/$/, "") === b.replace(/\/$/, "");
|
|
159
|
+
|
|
160
|
+
// Weighted random pick — providers advertise a capacity weight (1-100), so
|
|
161
|
+
// fallback traffic is spread in proportion to capacity rather than uniformly.
|
|
162
|
+
// Returns undefined only for an empty list (callers guard against that).
|
|
163
|
+
const pickWeightedProvider = (
|
|
164
|
+
providers: HardcodedProvider[],
|
|
165
|
+
random: () => number,
|
|
166
|
+
): HardcodedProvider | undefined => {
|
|
167
|
+
const totalWeight = providers.reduce((sum, p) => sum + p.weight, 0);
|
|
168
|
+
let threshold = random() * totalWeight;
|
|
169
|
+
let chosen: HardcodedProvider | undefined;
|
|
170
|
+
for (const provider of providers) {
|
|
171
|
+
chosen = provider;
|
|
172
|
+
threshold -= provider.weight;
|
|
173
|
+
if (threshold < 0) break;
|
|
174
|
+
}
|
|
175
|
+
return chosen;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Pick a random provider directly from the provider list, bypassing the
|
|
180
|
+
* DNS-routed endpoint. This is the error-fallback path: once a provider has
|
|
181
|
+
* errored, retrying it re-hits the same (possibly-down) endpoint, and a fleet
|
|
182
|
+
* of widgets doing that in a tight loop can accidentally DDoS the provider — so
|
|
183
|
+
* instead we spread the retry across the fleet by choosing a random provider
|
|
184
|
+
* from the list. `excludeUrl` (the provider that just failed) is dropped from
|
|
185
|
+
* the pool when other providers remain. In development the list holds only the
|
|
186
|
+
* single local provider, so this naturally degrades to retrying that provider.
|
|
187
|
+
* `random` is injectable so tests can make the pick deterministic.
|
|
188
|
+
*/
|
|
189
|
+
export const getRandomProviderFromList = async (
|
|
190
|
+
env: EnvironmentTypes,
|
|
191
|
+
ipMode?: IpMode,
|
|
192
|
+
excludeUrl?: string,
|
|
193
|
+
random: () => number = Math.random,
|
|
194
|
+
): Promise<RandomProvider> => {
|
|
195
|
+
const providers = await getProviders(env);
|
|
196
|
+
if (providers.length === 0) {
|
|
197
|
+
// Nothing to choose from — fall back to the DNS-routed endpoint.
|
|
198
|
+
return getRandomActiveProvider(env, ipMode);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Only exclude the failed provider when at least one other remains, so we
|
|
202
|
+
// never end up with an empty pool (e.g. a single-provider environment).
|
|
203
|
+
const eligible =
|
|
204
|
+
excludeUrl && providers.length > 1
|
|
205
|
+
? providers.filter(
|
|
206
|
+
(p) => !sameProviderUrl(applyIpModeToUrl(p.url, ipMode), excludeUrl),
|
|
207
|
+
)
|
|
208
|
+
: providers;
|
|
209
|
+
const pool = eligible.length > 0 ? eligible : providers;
|
|
210
|
+
|
|
211
|
+
const chosen = pickWeightedProvider(pool, random);
|
|
212
|
+
if (!chosen) {
|
|
213
|
+
return getRandomActiveProvider(env, ipMode);
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
providerAccount: chosen.address,
|
|
217
|
+
provider: { url: applyIpModeToUrl(chosen.url, ipMode) },
|
|
218
|
+
};
|
|
219
|
+
};
|
|
220
|
+
|
|
155
221
|
// Test-only escape hatch so tests can isolate the healthz cache between
|
|
156
222
|
// cases. Not exported from the package index — internal use only.
|
|
157
223
|
export const _resetPinCache = () => {
|