@prosopo/load-balancer 2.10.0 → 2.10.2
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 +19 -0
- package/dist/cjs/index.cjs +2 -0
- package/dist/cjs/providers.cjs +17 -0
- package/dist/index.js +3 -1
- package/dist/providers.d.ts +3 -1
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +17 -0
- package/dist/providers.js.map +1 -1
- package/dist/tests/providers.unit.test.js +47 -1
- package/dist/tests/providers.unit.test.js.map +1 -1
- package/package.json +4 -4
- package/src/providers.ts +40 -1
- package/src/tests/providers.unit.test.ts +66 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/load-balancer@2.10.
|
|
2
|
+
> @prosopo/load-balancer@2.10.2 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.
|
|
33
|
-
[2mdist/cjs/[22m[36mproviders.cjs [39m[1m[2m2.17 kB[22m[1m[22m
|
|
32
|
+
[2mdist/cjs/[22m[36mindex.cjs [39m[1m[2m0.70 kB[22m[1m[22m
|
|
34
33
|
[2mdist/cjs/[22m[36mbalancer.cjs [39m[1m[2m2.21 kB[22m[1m[22m
|
|
35
|
-
[
|
|
34
|
+
[2mdist/cjs/[22m[36mproviders.cjs [39m[1m[2m2.76 kB[22m[1m[22m
|
|
35
|
+
[32m✓ built in 157ms[39m
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
|
-
> @prosopo/load-balancer@2.10.
|
|
2
|
+
> @prosopo/load-balancer@2.10.2 build:tsc
|
|
3
3
|
> tsc --build --verbose
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
11:37:31 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
|
+
11:37:31 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
|
+
11:37:31 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
|
+
11:37:31 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
|
+
11:37:31 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
|
+
11:37:31 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
|
+
11:37:31 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/keyring/keyring/types.ts' is older than output '../types/tsconfig.tsbuildinfo'
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
11:37:31 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/error.ts' is older than output '../common/tsconfig.tsbuildinfo'
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
11:37:31 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
11:37:31 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.2 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.2 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.45 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[2m2.
|
|
39
|
-
[32m✓ built in
|
|
38
|
+
[2mdist/[22m[36mproviders.js [39m[1m[2m2.57 kB[22m[1m[22m
|
|
39
|
+
[32m✓ built in 159ms[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @prosopo/load-balancer
|
|
2
2
|
|
|
3
|
+
## 2.10.2
|
|
4
|
+
### Patch Changes
|
|
5
|
+
|
|
6
|
+
- Updated dependencies [8986976]
|
|
7
|
+
- Updated dependencies [970bca2]
|
|
8
|
+
- @prosopo/types@4.9.1
|
|
9
|
+
- @prosopo/common@3.1.42
|
|
10
|
+
|
|
11
|
+
## 2.10.1
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 6a7b122: Allow a client to send a captcha verify request to any pronode: a provider that did not issue the token now forwards the verification to the issuing provider (decoded from the token's providerUrl, SSRF-guarded against the known provider list) and returns its response, mirroring the AWS Lambda verify endpoint. Falls back to local verification when this node is the issuer, the provider list can't be loaded, or the issuer can't be determined.
|
|
15
|
+
- Updated dependencies [dfb0c53]
|
|
16
|
+
- Updated dependencies [11f1e8c]
|
|
17
|
+
- Updated dependencies [b166037]
|
|
18
|
+
- Updated dependencies [1111ff2]
|
|
19
|
+
- @prosopo/common@3.1.41
|
|
20
|
+
- @prosopo/types@4.9.0
|
|
21
|
+
|
|
3
22
|
## 2.10.0
|
|
4
23
|
### Minor Changes
|
|
5
24
|
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
3
3
|
const providers = require("./providers.cjs");
|
|
4
4
|
const balancer = require("./balancer.cjs");
|
|
5
5
|
exports._resetPinCache = providers._resetPinCache;
|
|
6
|
+
exports._resetProviderListCache = providers._resetProviderListCache;
|
|
7
|
+
exports.getProviders = providers.getProviders;
|
|
6
8
|
exports.getRandomActiveProvider = providers.getRandomActiveProvider;
|
|
7
9
|
exports.convertHostedProvider = balancer.convertHostedProvider;
|
|
8
10
|
exports.getLoadBalancerUrl = balancer.getLoadBalancerUrl;
|
package/dist/cjs/providers.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const balancer = require("./balancer.cjs");
|
|
3
4
|
const DNS_ENDPOINT = {
|
|
4
5
|
development: "https://localhost:9229",
|
|
5
6
|
staging: "https://staging.pronode.prosopo.io",
|
|
@@ -53,6 +54,17 @@ const resolvePinnedUrl = async (env, ipMode) => {
|
|
|
53
54
|
pinPromiseCache.set(key, promise);
|
|
54
55
|
return promise;
|
|
55
56
|
};
|
|
57
|
+
const providerListPromiseCache = /* @__PURE__ */ new Map();
|
|
58
|
+
const getProviders = async (env) => {
|
|
59
|
+
const cached = providerListPromiseCache.get(env);
|
|
60
|
+
if (cached) return cached;
|
|
61
|
+
const promise = balancer.loadBalancer(env).catch((err) => {
|
|
62
|
+
providerListPromiseCache.delete(env);
|
|
63
|
+
throw err;
|
|
64
|
+
});
|
|
65
|
+
providerListPromiseCache.set(env, promise);
|
|
66
|
+
return promise;
|
|
67
|
+
};
|
|
56
68
|
const getRandomActiveProvider = async (env, ipMode) => {
|
|
57
69
|
const url = await resolvePinnedUrl(env, ipMode);
|
|
58
70
|
return {
|
|
@@ -63,5 +75,10 @@ const getRandomActiveProvider = async (env, ipMode) => {
|
|
|
63
75
|
const _resetPinCache = () => {
|
|
64
76
|
pinPromiseCache.clear();
|
|
65
77
|
};
|
|
78
|
+
const _resetProviderListCache = () => {
|
|
79
|
+
providerListPromiseCache.clear();
|
|
80
|
+
};
|
|
66
81
|
exports._resetPinCache = _resetPinCache;
|
|
82
|
+
exports._resetProviderListCache = _resetProviderListCache;
|
|
83
|
+
exports.getProviders = getProviders;
|
|
67
84
|
exports.getRandomActiveProvider = getRandomActiveProvider;
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { _resetPinCache, getRandomActiveProvider } from "./providers.js";
|
|
1
|
+
import { _resetPinCache, _resetProviderListCache, getProviders, getRandomActiveProvider } from "./providers.js";
|
|
2
2
|
import { convertHostedProvider, getLoadBalancerUrl, getProviderHostname, loadBalancer, stripIpModeLabel } from "./balancer.js";
|
|
3
3
|
export {
|
|
4
4
|
_resetPinCache,
|
|
5
|
+
_resetProviderListCache,
|
|
5
6
|
convertHostedProvider,
|
|
6
7
|
getLoadBalancerUrl,
|
|
7
8
|
getProviderHostname,
|
|
9
|
+
getProviders,
|
|
8
10
|
getRandomActiveProvider,
|
|
9
11
|
loadBalancer,
|
|
10
12
|
stripIpModeLabel
|
package/dist/providers.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { EnvironmentTypes, RandomProvider } from "@prosopo/types";
|
|
2
|
-
import type
|
|
2
|
+
import { type HardcodedProvider, type IpMode } from "./balancer.js";
|
|
3
|
+
export declare const getProviders: (env: EnvironmentTypes) => Promise<HardcodedProvider[]>;
|
|
3
4
|
export declare const getRandomActiveProvider: (env: EnvironmentTypes, ipMode?: IpMode) => Promise<RandomProvider>;
|
|
4
5
|
export declare const _resetPinCache: () => void;
|
|
6
|
+
export declare const _resetProviderListCache: () => void;
|
|
5
7
|
//# 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,KAAK,
|
|
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"}
|
package/dist/providers.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { loadBalancer } from "./balancer.js";
|
|
1
2
|
const DNS_ENDPOINT = {
|
|
2
3
|
development: "https://localhost:9229",
|
|
3
4
|
staging: "https://staging.pronode.prosopo.io",
|
|
@@ -51,6 +52,17 @@ const resolvePinnedUrl = async (env, ipMode) => {
|
|
|
51
52
|
pinPromiseCache.set(key, promise);
|
|
52
53
|
return promise;
|
|
53
54
|
};
|
|
55
|
+
const providerListPromiseCache = /* @__PURE__ */ new Map();
|
|
56
|
+
const getProviders = async (env) => {
|
|
57
|
+
const cached = providerListPromiseCache.get(env);
|
|
58
|
+
if (cached) return cached;
|
|
59
|
+
const promise = loadBalancer(env).catch((err) => {
|
|
60
|
+
providerListPromiseCache.delete(env);
|
|
61
|
+
throw err;
|
|
62
|
+
});
|
|
63
|
+
providerListPromiseCache.set(env, promise);
|
|
64
|
+
return promise;
|
|
65
|
+
};
|
|
54
66
|
const getRandomActiveProvider = async (env, ipMode) => {
|
|
55
67
|
const url = await resolvePinnedUrl(env, ipMode);
|
|
56
68
|
return {
|
|
@@ -61,7 +73,12 @@ const getRandomActiveProvider = async (env, ipMode) => {
|
|
|
61
73
|
const _resetPinCache = () => {
|
|
62
74
|
pinPromiseCache.clear();
|
|
63
75
|
};
|
|
76
|
+
const _resetProviderListCache = () => {
|
|
77
|
+
providerListPromiseCache.clear();
|
|
78
|
+
};
|
|
64
79
|
export {
|
|
65
80
|
_resetPinCache,
|
|
81
|
+
_resetProviderListCache,
|
|
82
|
+
getProviders,
|
|
66
83
|
getRandomActiveProvider
|
|
67
84
|
};
|
package/dist/providers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { _resetPinCache, getRandomActiveProvider } from "../providers.js";
|
|
2
|
+
import { _resetPinCache, _resetProviderListCache, getProviders, getRandomActiveProvider, } from "../providers.js";
|
|
3
|
+
const loadBalancer = vi.fn();
|
|
4
|
+
vi.mock("../balancer.js", () => ({
|
|
5
|
+
loadBalancer: (...args) => loadBalancer(...args),
|
|
6
|
+
}));
|
|
3
7
|
const originalFetch = globalThis.fetch;
|
|
4
8
|
const mockHealthzFetch = (host, ok = true, status = 200) => {
|
|
5
9
|
const mocked = vi.fn(async () => ({
|
|
@@ -83,4 +87,46 @@ describe("getRandomActiveProvider (single stack ipMode)", () => {
|
|
|
83
87
|
expect(mocked).toHaveBeenCalledTimes(2);
|
|
84
88
|
});
|
|
85
89
|
});
|
|
90
|
+
describe("getProviders", () => {
|
|
91
|
+
beforeEach(() => {
|
|
92
|
+
_resetProviderListCache();
|
|
93
|
+
loadBalancer.mockReset();
|
|
94
|
+
});
|
|
95
|
+
it("loads the provider list once and serves repeat callers from cache", async () => {
|
|
96
|
+
const providers = [
|
|
97
|
+
{ address: "5xyz", url: "https://p1", datasetId: "0x0" },
|
|
98
|
+
];
|
|
99
|
+
loadBalancer.mockResolvedValue(providers);
|
|
100
|
+
const first = await getProviders("production");
|
|
101
|
+
const second = await getProviders("production");
|
|
102
|
+
expect(first).toBe(providers);
|
|
103
|
+
expect(second).toBe(providers);
|
|
104
|
+
expect(loadBalancer).toHaveBeenCalledTimes(1);
|
|
105
|
+
});
|
|
106
|
+
it("dedupes concurrent in-flight loads into a single call", async () => {
|
|
107
|
+
const providers = [
|
|
108
|
+
{ address: "5xyz", url: "https://p1", datasetId: "0x0" },
|
|
109
|
+
];
|
|
110
|
+
loadBalancer.mockResolvedValue(providers);
|
|
111
|
+
const [a, b] = await Promise.all([
|
|
112
|
+
getProviders("production"),
|
|
113
|
+
getProviders("production"),
|
|
114
|
+
]);
|
|
115
|
+
expect(a).toBe(providers);
|
|
116
|
+
expect(b).toBe(providers);
|
|
117
|
+
expect(loadBalancer).toHaveBeenCalledTimes(1);
|
|
118
|
+
});
|
|
119
|
+
it("does not cache failures — a rejected load is retried on the next call", async () => {
|
|
120
|
+
const providers = [
|
|
121
|
+
{ address: "5xyz", url: "https://p1", datasetId: "0x0" },
|
|
122
|
+
];
|
|
123
|
+
loadBalancer
|
|
124
|
+
.mockRejectedValueOnce(new Error("transient"))
|
|
125
|
+
.mockResolvedValueOnce(providers);
|
|
126
|
+
await expect(getProviders("production")).rejects.toThrow("transient");
|
|
127
|
+
const retry = await getProviders("production");
|
|
128
|
+
expect(retry).toBe(providers);
|
|
129
|
+
expect(loadBalancer).toHaveBeenCalledTimes(2);
|
|
130
|
+
});
|
|
131
|
+
});
|
|
86
132
|
//# 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;AACzE,OAAO,
|
|
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.2",
|
|
4
4
|
"description": "Provider load balancer",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/prosopo/captcha#readme",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@prosopo/common": "3.1.
|
|
41
|
-
"@prosopo/types": "4.
|
|
40
|
+
"@prosopo/common": "3.1.42",
|
|
41
|
+
"@prosopo/types": "4.9.1",
|
|
42
42
|
"zod": "3.23.8"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@prosopo/config": "3.3.
|
|
45
|
+
"@prosopo/config": "3.3.4",
|
|
46
46
|
"@types/node": "22.10.2",
|
|
47
47
|
"@vitest/coverage-v8": "3.2.4",
|
|
48
48
|
"concurrently": "9.0.1",
|
package/src/providers.ts
CHANGED
|
@@ -13,7 +13,11 @@
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
|
|
15
15
|
import type { EnvironmentTypes, RandomProvider } from "@prosopo/types";
|
|
16
|
-
import
|
|
16
|
+
import {
|
|
17
|
+
type HardcodedProvider,
|
|
18
|
+
type IpMode,
|
|
19
|
+
loadBalancer,
|
|
20
|
+
} from "./balancer.js";
|
|
17
21
|
|
|
18
22
|
// Base DNS endpoint per env — the `pronode.prosopo.io` family is latency-routed
|
|
19
23
|
// (A/AAAA records across the pronode fleet). Clients hit this URL's `/healthz`
|
|
@@ -108,6 +112,35 @@ const resolvePinnedUrl = async (
|
|
|
108
112
|
return promise;
|
|
109
113
|
};
|
|
110
114
|
|
|
115
|
+
// Cached, in-flight provider-list load per env. The list rarely changes, so a
|
|
116
|
+
// single fetch is shared across callers rather than re-fetching the
|
|
117
|
+
// provider-list JSON on every verify-forward decision.
|
|
118
|
+
const providerListPromiseCache: Map<
|
|
119
|
+
EnvironmentTypes,
|
|
120
|
+
Promise<HardcodedProvider[]>
|
|
121
|
+
> = new Map();
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Returns the full (cached) list of active providers for an environment.
|
|
125
|
+
* Used to look up a provider by url/address, e.g. to find the provider that
|
|
126
|
+
* issued a token before forwarding a verification request to it.
|
|
127
|
+
*/
|
|
128
|
+
export const getProviders = async (
|
|
129
|
+
env: EnvironmentTypes,
|
|
130
|
+
): Promise<HardcodedProvider[]> => {
|
|
131
|
+
const cached = providerListPromiseCache.get(env);
|
|
132
|
+
if (cached) return cached;
|
|
133
|
+
|
|
134
|
+
const promise = loadBalancer(env).catch((err) => {
|
|
135
|
+
// Don't cache failures — a transient fetch error shouldn't poison the
|
|
136
|
+
// cache for the lifetime of the process.
|
|
137
|
+
providerListPromiseCache.delete(env);
|
|
138
|
+
throw err;
|
|
139
|
+
});
|
|
140
|
+
providerListPromiseCache.set(env, promise);
|
|
141
|
+
return promise;
|
|
142
|
+
};
|
|
143
|
+
|
|
111
144
|
export const getRandomActiveProvider = async (
|
|
112
145
|
env: EnvironmentTypes,
|
|
113
146
|
ipMode?: IpMode,
|
|
@@ -124,3 +157,9 @@ export const getRandomActiveProvider = async (
|
|
|
124
157
|
export const _resetPinCache = () => {
|
|
125
158
|
pinPromiseCache.clear();
|
|
126
159
|
};
|
|
160
|
+
|
|
161
|
+
// Test-only escape hatch to isolate the provider-list cache between cases.
|
|
162
|
+
// Not exported from the package index — internal use only.
|
|
163
|
+
export const _resetProviderListCache = () => {
|
|
164
|
+
providerListPromiseCache.clear();
|
|
165
|
+
};
|
|
@@ -13,7 +13,20 @@
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
|
|
15
15
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
_resetPinCache,
|
|
18
|
+
_resetProviderListCache,
|
|
19
|
+
getProviders,
|
|
20
|
+
getRandomActiveProvider,
|
|
21
|
+
} from "../providers.js";
|
|
22
|
+
|
|
23
|
+
// Mock the underlying load balancer so getProviders' caching/failure semantics
|
|
24
|
+
// can be exercised without real network calls. getRandomActiveProvider tests
|
|
25
|
+
// don't use loadBalancer, so this mock leaves them unaffected.
|
|
26
|
+
const loadBalancer = vi.fn();
|
|
27
|
+
vi.mock("../balancer.js", () => ({
|
|
28
|
+
loadBalancer: (...args: unknown[]) => loadBalancer(...args),
|
|
29
|
+
}));
|
|
17
30
|
|
|
18
31
|
const originalFetch = globalThis.fetch;
|
|
19
32
|
|
|
@@ -122,3 +135,55 @@ describe("getRandomActiveProvider (single stack ipMode)", () => {
|
|
|
122
135
|
expect(mocked).toHaveBeenCalledTimes(2);
|
|
123
136
|
});
|
|
124
137
|
});
|
|
138
|
+
|
|
139
|
+
describe("getProviders", () => {
|
|
140
|
+
beforeEach(() => {
|
|
141
|
+
_resetProviderListCache();
|
|
142
|
+
loadBalancer.mockReset();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("loads the provider list once and serves repeat callers from cache", async () => {
|
|
146
|
+
const providers = [
|
|
147
|
+
{ address: "5xyz", url: "https://p1", datasetId: "0x0" },
|
|
148
|
+
];
|
|
149
|
+
loadBalancer.mockResolvedValue(providers);
|
|
150
|
+
|
|
151
|
+
const first = await getProviders("production");
|
|
152
|
+
const second = await getProviders("production");
|
|
153
|
+
|
|
154
|
+
expect(first).toBe(providers);
|
|
155
|
+
expect(second).toBe(providers);
|
|
156
|
+
expect(loadBalancer).toHaveBeenCalledTimes(1);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it("dedupes concurrent in-flight loads into a single call", async () => {
|
|
160
|
+
const providers = [
|
|
161
|
+
{ address: "5xyz", url: "https://p1", datasetId: "0x0" },
|
|
162
|
+
];
|
|
163
|
+
loadBalancer.mockResolvedValue(providers);
|
|
164
|
+
|
|
165
|
+
const [a, b] = await Promise.all([
|
|
166
|
+
getProviders("production"),
|
|
167
|
+
getProviders("production"),
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
expect(a).toBe(providers);
|
|
171
|
+
expect(b).toBe(providers);
|
|
172
|
+
expect(loadBalancer).toHaveBeenCalledTimes(1);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("does not cache failures — a rejected load is retried on the next call", async () => {
|
|
176
|
+
const providers = [
|
|
177
|
+
{ address: "5xyz", url: "https://p1", datasetId: "0x0" },
|
|
178
|
+
];
|
|
179
|
+
loadBalancer
|
|
180
|
+
.mockRejectedValueOnce(new Error("transient"))
|
|
181
|
+
.mockResolvedValueOnce(providers);
|
|
182
|
+
|
|
183
|
+
await expect(getProviders("production")).rejects.toThrow("transient");
|
|
184
|
+
// The failure must have cleared the cache, so the next call retries.
|
|
185
|
+
const retry = await getProviders("production");
|
|
186
|
+
expect(retry).toBe(providers);
|
|
187
|
+
expect(loadBalancer).toHaveBeenCalledTimes(2);
|
|
188
|
+
});
|
|
189
|
+
});
|