@prosopo/load-balancer 2.9.20 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @prosopo/load-balancer@2.9.20 build:cjs
2
+ > @prosopo/load-balancer@2.10.0 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
  ✓ 3 modules transformed.
31
31
  rendering chunks...
32
- dist/cjs/index.cjs 0.64 kB
33
- dist/cjs/balancer.cjs 1.67 kB
34
- dist/cjs/providers.cjs 2.10 kB
35
- ✓ built in 138ms
32
+ dist/cjs/index.cjs 0.58 kB
33
+ dist/cjs/providers.cjs 2.17 kB
34
+ dist/cjs/balancer.cjs 2.21 kB
35
+ ✓ built in 149ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/load-balancer@2.9.20 build:tsc
2
+ > @prosopo/load-balancer@2.10.0 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 9:53:44 PM - Projects in this build:
5
+ 7:02:14 PM - 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
- 9:53:44 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
+ 7:02:15 PM - 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
- 9:53:44 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
17
+ 7:02:15 PM - 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
- 9:53:44 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
19
+ 7:02:15 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/version.ts' is older than output '../util/tsconfig.tsbuildinfo'
20
20
 
21
- 9:53:44 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
21
+ 7:02:15 PM - 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
- 9:53:44 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'
23
+ 7:02:15 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'
24
24
 
25
- 9:53:44 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
25
+ 7:02:15 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/procaptcha/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
26
26
 
27
- 9:53:45 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/tests/utils/batches.unit.test.ts' is older than output '../common/tsconfig.tsbuildinfo'
27
+ 7:02:15 PM - 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
- 9:53:45 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
29
+ 7:02:15 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
30
30
 
31
- 9:53:45 PM - Building project '/home/runner/work/captcha/captcha/packages/load-balancer/tsconfig.json'...
31
+ 7:02:15 PM - Building project '/home/runner/work/captcha/captcha/packages/load-balancer/tsconfig.json'...
32
32
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/load-balancer@2.9.20 build
2
+ > @prosopo/load-balancer@2.10.0 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/load-balancer@2.9.20 build:cross-env
6
+ > @prosopo/load-balancer@2.10.0 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
  ✓ 3 modules transformed.
35
35
  rendering chunks...
36
- dist/index.js 0.41 kB
37
- dist/balancer.js 1.50 kB
38
- dist/providers.js 1.88 kB
39
- ✓ built in 143ms
36
+ dist/index.js 0.36 kB
37
+ dist/balancer.js 1.98 kB
38
+ dist/providers.js 2.04 kB
39
+ ✓ built in 145ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,82 @@
1
1
  # @prosopo/load-balancer
2
2
 
3
+ ## 2.10.0
4
+ ### Minor Changes
5
+
6
+ - 12cd0a6: Replace client-side weighted-random provider selection with static DNS endpoints.
7
+
8
+ - Removed the `providerSelectEntropy` field from `DetectorResult`, `Session`, the
9
+ Mongoose `SessionRecordSchema` (including its standalone index), and every
10
+ call-site that threaded it through frictionless / image / pow / puzzle flows.
11
+ - Removed `FrictionlessManager.hostVerified` and its decision-machine call site
12
+ — there's nothing to verify when the DNS layer picks the host.
13
+ - `getRandomActiveProvider(env)` now returns the per-environment static DNS
14
+ endpoint (`pronode.prosopo.io` family) instead of fetching the provider list
15
+ and weighted-selecting. The entropy parameter is gone.
16
+ - `getProcaptchaRandomActiveProvider` is now a thin re-export so widget packages
17
+ keep importing from `procaptcha-common`.
18
+ - `FrontendProvider.datasetId` is dropped; `CaptchaRequestBody.datasetId` is
19
+ optional. The server falls back to its own most-recently-uploaded dataset
20
+ (`env.datasetId`, populated from `db.getMostRecentDatasetId()` at startup) —
21
+ clients can't pin a dataset under DNS routing because they don't know which
22
+ pronode they'll hit.
23
+ - Removed dead `setProviderLoader` / `prefetchProviders` / `selectWeightedProvider`
24
+ plumbing from `@prosopo/load-balancer`. The server's cacheFile-based loader
25
+ setup in `startProviderApi` goes with them.
26
+ - `getRandomActiveProvider` now hits `/healthz` on the global hostname once per
27
+ page load, reads the responding pronode's identity from the JSON body, and
28
+ pins subsequent captcha calls to that pronode (`https://pronodeN.prosopo.io`)
29
+ so session creation and submission land on the same backend. Falls back to
30
+ the dual-stack global hostname when `/healthz` is unreachable.
31
+ - `/healthz` now returns `{ ok: true, host: <pronode-identity> }` instead of
32
+ `"OK"` to support the above pinning.
33
+ - CORS preflight is now cached for 24h (`maxAge: 86400`) — previously the
34
+ browser refired an OPTIONS preflight before every captcha call because
35
+ the custom `Prosopo-Site-Key` / `Prosopo-User` headers make the request
36
+ non-simple and the default `maxAge` is 5s.
37
+ - 12cd0a6: Add ipv4-only / ipv6-only provider DNS routing via `data-ipv4` / `data-ipv6`.
38
+
39
+ Dapps that need to pin captcha traffic to a single IP stack can now do so:
40
+
41
+ ```html
42
+ <div class="procaptcha" data-sitekey="..." data-ipv4="true"></div>
43
+ ```
44
+
45
+ What happens under the hood:
46
+
47
+ - The widget reads `data-ipv4` / `data-ipv6` (or the matching `ipv4` / `ipv6`
48
+ booleans on `ProcaptchaRenderOptions` / explicit `render(...)`) and threads
49
+ them through `ProcaptchaConfigSchema`.
50
+ - `pickIpMode(config)` resolves them into an `IpMode` (`"ipv4"` / `"ipv6"` /
51
+ `undefined`); `ipv4` wins if both are set.
52
+ - The frictionless / image / pow / puzzle managers pass the `IpMode` into
53
+ `getProcaptchaRandomActiveProvider`, which calls `/healthz` on the matching
54
+ single-stack global hostname (`ipv4.pronode.prosopo.io` or
55
+ `ipv6.pronode.prosopo.io`) and pins subsequent captcha calls to
56
+ `ipv4.pronodeN.prosopo.io` / `ipv6.pronodeN.prosopo.io`. The dual-stack
57
+ cache and the single-stack caches are kept separate.
58
+ - `convertHostedProvider` now accepts an optional `IpMode` and, when set,
59
+ selects the matching `ipv4` / `ipv6` sub-object from the provider-list JSON.
60
+ Top-level `ipv4` / `ipv6` keys are skipped by default so existing dual-stack
61
+ callers keep working.
62
+ - New helpers in `@prosopo/load-balancer`: `IpMode`, `stripIpModeLabel`,
63
+ `getProviderHostname`.
64
+
65
+ Coordinated with the matching `captcha-private` change that publishes the
66
+ `ipv4` / `ipv6` sub-objects to S3.
67
+
68
+ ### Patch Changes
69
+
70
+ - Updated dependencies [12cd0a6]
71
+ - Updated dependencies [12cd0a6]
72
+ - @prosopo/types@4.8.0
73
+
74
+ ## 2.9.21
75
+ ### Patch Changes
76
+
77
+ - Updated dependencies [bb98af1]
78
+ - @prosopo/types@4.7.4
79
+
3
80
  ## 2.9.20
4
81
  ### Patch Changes
5
82
 
@@ -17,8 +17,11 @@ declare const HardcodedProviderSchema: z.ZodObject<{
17
17
  weight?: number | undefined;
18
18
  }>;
19
19
  export type HardcodedProvider = z.infer<typeof HardcodedProviderSchema>;
20
+ export type IpMode = "ipv4" | "ipv6";
20
21
  type hostedProviders = Record<string, unknown>;
21
- export declare const convertHostedProvider: (provider: hostedProviders) => HardcodedProvider[];
22
+ export declare const convertHostedProvider: (provider: hostedProviders, ipMode?: IpMode) => HardcodedProvider[];
23
+ export declare const stripIpModeLabel: (hostname: string) => string;
24
+ export declare const getProviderHostname: (provider: HardcodedProvider) => string;
22
25
  export declare const getLoadBalancerUrl: (environment: EnvironmentTypes) => string;
23
26
  export declare const loadBalancer: (environment: EnvironmentTypes) => Promise<HardcodedProvider[]>;
24
27
  export {};
@@ -1 +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"}
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,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAQrC,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAK/C,eAAO,MAAM,qBAAqB,aACvB,eAAe,WAChB,MAAM,KACb,iBAAiB,EAUnB,CAAC;AAKF,eAAO,MAAM,gBAAgB,aAAc,MAAM,KAAG,MACjB,CAAC;AAEpC,eAAO,MAAM,mBAAmB,aAAc,iBAAiB,KAAG,MACjB,CAAC;AAElD,eAAO,MAAM,kBAAkB,gBAAiB,gBAAgB,KAAG,MAUlE,CAAC;AAEF,eAAO,MAAM,YAAY,gBACX,gBAAgB,KAC3B,OAAO,CAAC,iBAAiB,EAAE,CAqB7B,CAAC"}
package/dist/balancer.js CHANGED
@@ -9,12 +9,15 @@ const HardcodedProviderSchema = z.object({
9
9
  return Math.max(1, Math.min(100, weight));
10
10
  })
11
11
  });
12
- const convertHostedProvider = (provider) => {
13
- const providers = Object.values(provider).map(
14
- (p) => HardcodedProviderSchema.parse(p)
15
- );
12
+ const IP_MODE_KEYS = ["ipv4", "ipv6"];
13
+ const isProviderRecord = (value) => typeof value === "object" && value !== null;
14
+ const convertHostedProvider = (provider, ipMode) => {
15
+ const source = ipMode && isProviderRecord(provider[ipMode]) ? provider[ipMode] : provider;
16
+ const providers = Object.entries(source).filter(([key]) => !IP_MODE_KEYS.includes(key)).map(([, value]) => HardcodedProviderSchema.parse(value));
16
17
  return providers.sort((a, b) => a.url.localeCompare(b.url));
17
18
  };
19
+ const stripIpModeLabel = (hostname) => hostname.replace(/^ipv[46]\./, "");
20
+ const getProviderHostname = (provider) => stripIpModeLabel(new URL(provider.url).hostname);
18
21
  const getLoadBalancerUrl = (environment) => {
19
22
  if (environment === "production") {
20
23
  return "https://provider-list.prosopo.io/";
@@ -49,5 +52,7 @@ const loadBalancer = async (environment) => {
49
52
  export {
50
53
  convertHostedProvider,
51
54
  getLoadBalancerUrl,
52
- loadBalancer
55
+ getProviderHostname,
56
+ loadBalancer,
57
+ stripIpModeLabel
53
58
  };
@@ -1 +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,wBAAwB;gBAC7B,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"}
1
+ {"version":3,"file":"balancer.js","sourceRoot":"","sources":["../src/balancer.ts"],"names":[],"mappings":"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"}
@@ -11,12 +11,15 @@ const HardcodedProviderSchema = zod.z.object({
11
11
  return Math.max(1, Math.min(100, weight));
12
12
  })
13
13
  });
14
- const convertHostedProvider = (provider) => {
15
- const providers = Object.values(provider).map(
16
- (p) => HardcodedProviderSchema.parse(p)
17
- );
14
+ const IP_MODE_KEYS = ["ipv4", "ipv6"];
15
+ const isProviderRecord = (value) => typeof value === "object" && value !== null;
16
+ const convertHostedProvider = (provider, ipMode) => {
17
+ const source = ipMode && isProviderRecord(provider[ipMode]) ? provider[ipMode] : provider;
18
+ const providers = Object.entries(source).filter(([key]) => !IP_MODE_KEYS.includes(key)).map(([, value]) => HardcodedProviderSchema.parse(value));
18
19
  return providers.sort((a, b) => a.url.localeCompare(b.url));
19
20
  };
21
+ const stripIpModeLabel = (hostname) => hostname.replace(/^ipv[46]\./, "");
22
+ const getProviderHostname = (provider) => stripIpModeLabel(new URL(provider.url).hostname);
20
23
  const getLoadBalancerUrl = (environment) => {
21
24
  if (environment === "production") {
22
25
  return "https://provider-list.prosopo.io/";
@@ -50,4 +53,6 @@ const loadBalancer = async (environment) => {
50
53
  };
51
54
  exports.convertHostedProvider = convertHostedProvider;
52
55
  exports.getLoadBalancerUrl = getLoadBalancerUrl;
56
+ exports.getProviderHostname = getProviderHostname;
53
57
  exports.loadBalancer = loadBalancer;
58
+ exports.stripIpModeLabel = stripIpModeLabel;
@@ -2,11 +2,10 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const providers = require("./providers.cjs");
4
4
  const balancer = require("./balancer.cjs");
5
- exports._resetCache = providers._resetCache;
5
+ exports._resetPinCache = providers._resetPinCache;
6
6
  exports.getRandomActiveProvider = providers.getRandomActiveProvider;
7
- exports.prefetchProviders = providers.prefetchProviders;
8
- exports.selectWeightedProvider = providers.selectWeightedProvider;
9
- exports.setProviderLoader = providers.setProviderLoader;
10
7
  exports.convertHostedProvider = balancer.convertHostedProvider;
11
8
  exports.getLoadBalancerUrl = balancer.getLoadBalancerUrl;
9
+ exports.getProviderHostname = balancer.getProviderHostname;
12
10
  exports.loadBalancer = balancer.loadBalancer;
11
+ exports.stripIpModeLabel = balancer.stripIpModeLabel;
@@ -1,66 +1,67 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- require("./index.cjs");
4
- const balancer = require("./balancer.cjs");
5
- const providerPromiseCache = /* @__PURE__ */ new Map();
6
- let customProviderLoader = null;
7
- function setProviderLoader(loader) {
8
- customProviderLoader = loader;
9
- }
10
- function _resetCache() {
11
- providerPromiseCache.clear();
12
- }
13
- function selectWeightedProvider(providers, entropy) {
14
- if (providers.length === 0) {
15
- throw new Error("No providers available");
16
- }
17
- const totalWeight = providers.reduce((sum, p) => sum + p.weight, 0);
18
- const randomValue = entropy % totalWeight;
19
- let cumulativeWeight = 0;
20
- for (const provider of providers) {
21
- cumulativeWeight += provider.weight;
22
- if (randomValue < cumulativeWeight) {
23
- return provider;
24
- }
3
+ const DNS_ENDPOINT = {
4
+ development: "https://localhost:9229",
5
+ staging: "https://staging.pronode.prosopo.io",
6
+ production: "https://pronode.prosopo.io"
7
+ };
8
+ const withIpModeLabel = (hostname, ipMode) => ipMode ? `${ipMode}.${hostname}` : hostname;
9
+ const applyIpModeToUrl = (url, ipMode) => {
10
+ if (!ipMode) return url;
11
+ try {
12
+ const parsed = new URL(url);
13
+ parsed.hostname = withIpModeLabel(parsed.hostname, ipMode);
14
+ return parsed.toString().replace(/\/$/, "");
15
+ } catch {
16
+ return url;
25
17
  }
26
- const selectedProvider = providers[providers.length - 1];
27
- if (!selectedProvider) {
28
- throw new Error("No providers available");
18
+ };
19
+ const cacheKey = (env, ipMode) => `${env}|${ipMode ?? "dual"}`;
20
+ const pinPromiseCache = /* @__PURE__ */ new Map();
21
+ const fetchPinnedHost = async (baseUrl) => {
22
+ const res = await fetch(`${baseUrl}/healthz`, {
23
+ method: "GET",
24
+ cache: "no-store",
25
+ credentials: "omit"
26
+ });
27
+ if (!res.ok) {
28
+ throw new Error(`healthz responded with ${res.status}`);
29
29
  }
30
- return selectedProvider;
31
- }
32
- const loadProviders = async (env) => {
33
- if (customProviderLoader) {
34
- return customProviderLoader(env);
30
+ const body = await res.json();
31
+ if (typeof body.host !== "string" || body.host.length === 0) {
32
+ throw new Error("healthz response missing host field");
35
33
  }
36
- return balancer.loadBalancer(env);
34
+ return body.host;
37
35
  };
38
- const getProvidersPromise = (env) => {
39
- const existing = providerPromiseCache.get(env);
40
- if (existing) return existing;
41
- const promise = loadProviders(env).catch((err) => {
42
- providerPromiseCache.delete(env);
43
- throw err;
44
- });
45
- providerPromiseCache.set(env, promise);
36
+ const resolveBaseUrl = (env) => DNS_ENDPOINT[env] ?? DNS_ENDPOINT.development;
37
+ const resolvePinnedUrl = async (env, ipMode) => {
38
+ const base = applyIpModeToUrl(resolveBaseUrl(env), ipMode);
39
+ if (env === "development") return base;
40
+ const key = cacheKey(env, ipMode);
41
+ const cached = pinPromiseCache.get(key);
42
+ if (cached) return cached;
43
+ const promise = (async () => {
44
+ try {
45
+ const host = await fetchPinnedHost(base);
46
+ const parsed = new URL(base);
47
+ parsed.hostname = withIpModeLabel(host, ipMode);
48
+ return parsed.toString().replace(/\/$/, "");
49
+ } catch {
50
+ return base;
51
+ }
52
+ })();
53
+ pinPromiseCache.set(key, promise);
46
54
  return promise;
47
55
  };
48
- const prefetchProviders = async (env) => {
49
- await getProvidersPromise(env);
50
- };
51
- const getRandomActiveProvider = async (env, entropy) => {
52
- const providers = await getProvidersPromise(env);
53
- const randomProviderObj = selectWeightedProvider(providers, entropy);
56
+ const getRandomActiveProvider = async (env, ipMode) => {
57
+ const url = await resolvePinnedUrl(env, ipMode);
54
58
  return {
55
- providerAccount: randomProviderObj.address,
56
- provider: {
57
- url: randomProviderObj.url,
58
- datasetId: randomProviderObj.datasetId
59
- }
59
+ providerAccount: "dns-routed",
60
+ provider: { url }
60
61
  };
61
62
  };
62
- exports._resetCache = _resetCache;
63
+ const _resetPinCache = () => {
64
+ pinPromiseCache.clear();
65
+ };
66
+ exports._resetPinCache = _resetPinCache;
63
67
  exports.getRandomActiveProvider = getRandomActiveProvider;
64
- exports.prefetchProviders = prefetchProviders;
65
- exports.selectWeightedProvider = selectWeightedProvider;
66
- exports.setProviderLoader = setProviderLoader;
package/dist/index.js CHANGED
@@ -1,12 +1,11 @@
1
- import { _resetCache, getRandomActiveProvider, prefetchProviders, selectWeightedProvider, setProviderLoader } from "./providers.js";
2
- import { convertHostedProvider, getLoadBalancerUrl, loadBalancer } from "./balancer.js";
1
+ import { _resetPinCache, getRandomActiveProvider } from "./providers.js";
2
+ import { convertHostedProvider, getLoadBalancerUrl, getProviderHostname, loadBalancer, stripIpModeLabel } from "./balancer.js";
3
3
  export {
4
- _resetCache,
4
+ _resetPinCache,
5
5
  convertHostedProvider,
6
6
  getLoadBalancerUrl,
7
+ getProviderHostname,
7
8
  getRandomActiveProvider,
8
9
  loadBalancer,
9
- prefetchProviders,
10
- selectWeightedProvider,
11
- setProviderLoader
10
+ stripIpModeLabel
12
11
  };
@@ -1,8 +1,5 @@
1
1
  import type { EnvironmentTypes, RandomProvider } from "@prosopo/types";
2
- import { type HardcodedProvider } from "./index.js";
3
- export declare function setProviderLoader(loader: (env: EnvironmentTypes) => Promise<HardcodedProvider[]>): void;
4
- export declare function _resetCache(): void;
5
- export declare function selectWeightedProvider(providers: HardcodedProvider[], entropy: number): HardcodedProvider;
6
- export declare const prefetchProviders: (env: EnvironmentTypes) => Promise<void>;
7
- export declare const getRandomActiveProvider: (env: EnvironmentTypes, entropy: number) => Promise<RandomProvider>;
2
+ import type { IpMode } from "./balancer.js";
3
+ export declare const getRandomActiveProvider: (env: EnvironmentTypes, ipMode?: IpMode) => Promise<RandomProvider>;
4
+ export declare const _resetPinCache: () => void;
8
5
  //# sourceMappingURL=providers.d.ts.map
@@ -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,EAAE,KAAK,iBAAiB,EAAgB,MAAM,YAAY,CAAC;AAkBlE,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,OAAO,CAAC,iBAAiB,EAAE,CAAC,GAC7D,IAAI,CAEN;AAED,wBAAgB,WAAW,SAE1B;AAUD,wBAAgB,sBAAsB,CACrC,SAAS,EAAE,iBAAiB,EAAE,EAC9B,OAAO,EAAE,MAAM,GACb,iBAAiB,CAyBnB;AA+BD,eAAO,MAAM,iBAAiB,QACxB,gBAAgB,KACnB,OAAO,CAAC,IAAI,CAEd,CAAC;AAEF,eAAO,MAAM,uBAAuB,QAC9B,gBAAgB,WACZ,MAAM,KACb,OAAO,CAAC,cAAc,CAWxB,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,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AA+F5C,eAAO,MAAM,uBAAuB,QAC9B,gBAAgB,WACZ,MAAM,KACb,OAAO,CAAC,cAAc,CAMxB,CAAC;AAIF,eAAO,MAAM,cAAc,YAE1B,CAAC"}
package/dist/providers.js CHANGED
@@ -1,66 +1,67 @@
1
- import "./index.js";
2
- import { loadBalancer } from "./balancer.js";
3
- const providerPromiseCache = /* @__PURE__ */ new Map();
4
- let customProviderLoader = null;
5
- function setProviderLoader(loader) {
6
- customProviderLoader = loader;
7
- }
8
- function _resetCache() {
9
- providerPromiseCache.clear();
10
- }
11
- function selectWeightedProvider(providers, entropy) {
12
- if (providers.length === 0) {
13
- throw new Error("No providers available");
14
- }
15
- const totalWeight = providers.reduce((sum, p) => sum + p.weight, 0);
16
- const randomValue = entropy % totalWeight;
17
- let cumulativeWeight = 0;
18
- for (const provider of providers) {
19
- cumulativeWeight += provider.weight;
20
- if (randomValue < cumulativeWeight) {
21
- return provider;
22
- }
1
+ const DNS_ENDPOINT = {
2
+ development: "https://localhost:9229",
3
+ staging: "https://staging.pronode.prosopo.io",
4
+ production: "https://pronode.prosopo.io"
5
+ };
6
+ const withIpModeLabel = (hostname, ipMode) => ipMode ? `${ipMode}.${hostname}` : hostname;
7
+ const applyIpModeToUrl = (url, ipMode) => {
8
+ if (!ipMode) return url;
9
+ try {
10
+ const parsed = new URL(url);
11
+ parsed.hostname = withIpModeLabel(parsed.hostname, ipMode);
12
+ return parsed.toString().replace(/\/$/, "");
13
+ } catch {
14
+ return url;
23
15
  }
24
- const selectedProvider = providers[providers.length - 1];
25
- if (!selectedProvider) {
26
- throw new Error("No providers available");
16
+ };
17
+ const cacheKey = (env, ipMode) => `${env}|${ipMode ?? "dual"}`;
18
+ const pinPromiseCache = /* @__PURE__ */ new Map();
19
+ const fetchPinnedHost = async (baseUrl) => {
20
+ const res = await fetch(`${baseUrl}/healthz`, {
21
+ method: "GET",
22
+ cache: "no-store",
23
+ credentials: "omit"
24
+ });
25
+ if (!res.ok) {
26
+ throw new Error(`healthz responded with ${res.status}`);
27
27
  }
28
- return selectedProvider;
29
- }
30
- const loadProviders = async (env) => {
31
- if (customProviderLoader) {
32
- return customProviderLoader(env);
28
+ const body = await res.json();
29
+ if (typeof body.host !== "string" || body.host.length === 0) {
30
+ throw new Error("healthz response missing host field");
33
31
  }
34
- return loadBalancer(env);
32
+ return body.host;
35
33
  };
36
- const getProvidersPromise = (env) => {
37
- const existing = providerPromiseCache.get(env);
38
- if (existing) return existing;
39
- const promise = loadProviders(env).catch((err) => {
40
- providerPromiseCache.delete(env);
41
- throw err;
42
- });
43
- providerPromiseCache.set(env, promise);
34
+ const resolveBaseUrl = (env) => DNS_ENDPOINT[env] ?? DNS_ENDPOINT.development;
35
+ const resolvePinnedUrl = async (env, ipMode) => {
36
+ const base = applyIpModeToUrl(resolveBaseUrl(env), ipMode);
37
+ if (env === "development") return base;
38
+ const key = cacheKey(env, ipMode);
39
+ const cached = pinPromiseCache.get(key);
40
+ if (cached) return cached;
41
+ const promise = (async () => {
42
+ try {
43
+ const host = await fetchPinnedHost(base);
44
+ const parsed = new URL(base);
45
+ parsed.hostname = withIpModeLabel(host, ipMode);
46
+ return parsed.toString().replace(/\/$/, "");
47
+ } catch {
48
+ return base;
49
+ }
50
+ })();
51
+ pinPromiseCache.set(key, promise);
44
52
  return promise;
45
53
  };
46
- const prefetchProviders = async (env) => {
47
- await getProvidersPromise(env);
48
- };
49
- const getRandomActiveProvider = async (env, entropy) => {
50
- const providers = await getProvidersPromise(env);
51
- const randomProviderObj = selectWeightedProvider(providers, entropy);
54
+ const getRandomActiveProvider = async (env, ipMode) => {
55
+ const url = await resolvePinnedUrl(env, ipMode);
52
56
  return {
53
- providerAccount: randomProviderObj.address,
54
- provider: {
55
- url: randomProviderObj.url,
56
- datasetId: randomProviderObj.datasetId
57
- }
57
+ providerAccount: "dns-routed",
58
+ provider: { url }
58
59
  };
59
60
  };
61
+ const _resetPinCache = () => {
62
+ pinPromiseCache.clear();
63
+ };
60
64
  export {
61
- _resetCache,
62
- getRandomActiveProvider,
63
- prefetchProviders,
64
- selectWeightedProvider,
65
- setProviderLoader
65
+ _resetPinCache,
66
+ getRandomActiveProvider
66
67
  };
@@ -1 +1 @@
1
- {"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"AAeA,OAAO,EAA0B,YAAY,EAAE,MAAM,YAAY,CAAC;AAGlE,MAAM,oBAAoB,GAGtB,IAAI,GAAG,EAAE,CAAC;AAGd,IAAI,oBAAoB,GAEd,IAAI,CAAC;AAOf,MAAM,UAAU,iBAAiB,CAChC,MAA+D;IAE/D,oBAAoB,GAAG,MAAM,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,WAAW;IAC1B,oBAAoB,CAAC,KAAK,EAAE,CAAC;AAC9B,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;AAGD,MAAM,aAAa,GAAG,KAAK,EAC1B,GAAqB,EACU,EAAE;IACjC,IAAI,oBAAoB,EAAE,CAAC;QAC1B,OAAO,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;AAC1B,CAAC,CAAC;AAIF,MAAM,mBAAmB,GAAG,CAC3B,GAAqB,EACU,EAAE;IACjC,MAAM,QAAQ,GAAG,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;QAChD,oBAAoB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,GAAG,CAAC;IACX,CAAC,CAAC,CAAC;IACH,oBAAoB,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACvC,OAAO,OAAO,CAAC;AAChB,CAAC,CAAC;AAMF,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EACrC,GAAqB,EACL,EAAE;IAClB,MAAM,mBAAmB,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,EAC3C,GAAqB,EACrB,OAAe,EACW,EAAE;IAC5B,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACjD,MAAM,iBAAiB,GAAG,sBAAsB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAErE,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"}
1
+ {"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.ts"],"names":[],"mappings":"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"}