@ar.io/wayfinder-core 1.3.1 → 1.4.0-alpha.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.
package/README.md CHANGED
@@ -17,21 +17,96 @@ yarn add @ar.io/wayfinder-core
17
17
  ### Basic Usage
18
18
 
19
19
  ```javascript
20
+ import { createWayfinderClient } from '@ar.io/wayfinder-core';
21
+
22
+ // Uses static gateways by default
23
+ const wayfinder = createWayfinderClient();
24
+
25
+ // Use Wayfinder to fetch and verify data using ar:// protocol
26
+ const response = await wayfinder.request('ar://example-name');
27
+ ```
28
+
29
+ ### Using with AR.IO Network
30
+
31
+ ```javascript
32
+ import { createWayfinderClient } from '@ar.io/wayfinder-core';
20
33
  import { ARIO } from '@ar.io/sdk';
21
- import { Wayfinder } from '@ar.io/wayfinder-core';
22
34
 
23
- // create a new Wayfinder instance that uses the top 10 gateways by operator stake from the ARIO Network
24
- const wayfinder = new Wayfinder({
25
- gatewaysProvider: new NetworkGatewaysProvider({
26
- ario: ARIO.mainnet(),
27
- sortBy: 'operatorStake',
28
- sortOrder: 'desc',
29
- limit: 10,
30
- }),
35
+ // Provide ARIO instance to use AR.IO network gateways
36
+ const wayfinder = createWayfinderClient({
37
+ ario: ARIO.mainnet(),
38
+ gatewaySelection: 'highest-performing', // Selection criteria for AR.IO network
31
39
  });
40
+ ```
32
41
 
33
- // use Wayfinder to fetch and verify data using ar:// protocol
34
- const response = await wayfinder.request('ar://example-name');
42
+ ### Static Gateways with Custom Options
43
+
44
+ ```javascript
45
+ import { createWayfinderClient } from '@ar.io/wayfinder-core';
46
+
47
+ // Use custom static gateways
48
+ const wayfinder = createWayfinderClient({
49
+ trustedGateways: ['https://permagate.io', 'https://arweave.net'],
50
+ routing: 'fastest',
51
+ verification: 'hash',
52
+ });
53
+ ```
54
+
55
+ ### Configuration Options
56
+
57
+ `createWayfinderClient` accepts the following options:
58
+
59
+ ```javascript
60
+ const wayfinder = createWayfinderClient({
61
+ // Routing strategy
62
+ routing: 'fastest', // 'random' | 'fastest' | 'round-robin' | 'preferred'
63
+
64
+ // Verification strategy
65
+ verification: 'hash', // 'hash' | 'data-root' | 'remote' | 'disabled' (default: 'disabled')
66
+
67
+ // Gateway selection (only applies when ario instance is provided)
68
+ gatewaySelection: 'highest-performing', // 'highest-performing' | 'longest-tenure' | etc.
69
+
70
+ // Enable caching for routing and gateway providers
71
+ cache: true, // Uses default 5-minute TTL
72
+ // OR specify custom TTL:
73
+ // cache: { ttlSeconds: 3600 }, // 1 hour
74
+
75
+ // List of trusted gateways for verification
76
+ trustedGateways: ['https://arweave.net', 'https://permagate.io'],
77
+ });
78
+
79
+ // Sync version with static gateways (when no ario instance provided)
80
+ const wayfinderSync = createWayfinderClientSync({
81
+ // Same options as above, but uses static gateways by default
82
+ routing: 'random',
83
+ verification: 'disabled',
84
+ trustedGateways: ['https://permagate.io', 'https://arweave.net'],
85
+ });
86
+ ```
87
+
88
+ ### Gateway Selection Options (with AR.IO Network)
89
+
90
+ When using the AR.IO Network provider, you can specify how gateways are selected:
91
+
92
+ ```javascript
93
+ import { createWayfinderClient } from '@ar.io/wayfinder-core';
94
+ import { ARIO } from '@ar.io/sdk';
95
+
96
+ const wayfinder = createWayfinderClient({
97
+ ario: ARIO.mainnet(),
98
+
99
+ // Gateway selection (only works with ARIO instance)
100
+ gatewaySelection: 'highest-performing', // Options:
101
+ // 'highest-performing' - Gateways with best performance metrics
102
+ // 'longest-tenure' - Gateways with longest service history
103
+ // 'highest-staked' - Gateways with most stake
104
+ // 'highest-weight' - Gateways with highest composite weight
105
+ // 'longest-streak' - Gateways with longest uptime streak
106
+
107
+ routing: 'random', // How to select from the filtered gateways
108
+ cache: { ttlSeconds: 600 }, // Cache for 10 minutes
109
+ });
35
110
  ```
36
111
 
37
112
  ## ar:// Protocol
@@ -259,21 +334,34 @@ const strategy = new SimpleCacheRoutingStrategy({
259
334
 
260
335
  #### Preferred gateway with fallback to ping-random
261
336
 
262
- Attempt to use a favorite gateway, but fall back to a ping-checked random choice
263
- if it fails.
337
+ Attempt to use a favorite gateway, but fallback to a fastest pinging strategy using the ARIO Network if it fails.
264
338
 
265
339
  ```ts
266
340
  import {
267
341
  PreferredWithFallbackRoutingStrategy,
268
342
  RandomRoutingStrategy,
269
343
  PingRoutingStrategy,
344
+ NetworkGatewaysProvider,
270
345
  } from "@ar.io/wayfinder-core";
346
+ import { ARIO } from '@ar.io/sdk';
347
+
348
+ // these will be our fallback gateways
349
+ const gatewayProvider = new NetworkGatewaysProvider({
350
+ ario: ARIO.mainnet(),
351
+ sortBy: 'operatorStake',
352
+ limit: 5,
353
+ });
354
+
355
+ // this is our fallback strategy if our preferred gateway fails
356
+ const fastestPingStrategy = new FastestPingRoutingStrategy({
357
+ timeoutMs: 500,
358
+ gatewaysProvider: gatewayProvider,
359
+ });
271
360
 
361
+ // compose the strategies together, the preferred gateway will be used first, and if it fails, the fallback strategy will be used.
272
362
  const strategy = new PreferredWithFallbackRoutingStrategy({
273
363
  preferredGateway: "https://my-gateway.example",
274
- fallbackStrategy: new PingRoutingStrategy({
275
- routingStrategy: new RandomRoutingStrategy(),
276
- }),
364
+ fallbackStrategy: fastestPingStrategy,
277
365
  });
278
366
  ```
279
367
 
@@ -285,13 +373,27 @@ Cycle through gateways sequentially, checking each one’s health before use.
285
373
  import {
286
374
  RoundRobinRoutingStrategy,
287
375
  PingRoutingStrategy,
376
+ NetworkGatewaysProvider,
288
377
  } from "@ar.io/wayfinder-core";
378
+ import { ARIO } from '@ar.io/sdk';
289
379
 
380
+ // use static gateways
290
381
  const strategy = new PingRoutingStrategy({
291
382
  routingStrategy: new RoundRobinRoutingStrategy({
292
383
  gateways: [new URL("https://gw1"), new URL("https://gw2")],
293
384
  }),
294
385
  });
386
+
387
+ // use a dynamic list of gateways from the ARIO Network
388
+ const strategy2 = new PingRoutingStrategy({
389
+ routingStrategy: new RoundRobinRoutingStrategy({
390
+ gatewaysProvider: new NetworkGatewaysProvider({
391
+ ario: ARIO.mainnet(),
392
+ sortBy: 'operatorStake',
393
+ limit: 5,
394
+ }),
395
+ }),
396
+ });
295
397
  ```
296
398
 
297
399
  #### Cache around any composed strategy
@@ -300,10 +402,21 @@ Because `SimpleCacheRoutingStrategy` accepts any `RoutingStrategy`, you can
300
402
  cache more complex compositions too.
301
403
 
302
404
  ```ts
405
+ // use a dynamic list of gateways from the ARIO Network
406
+ const randomStrategy = new RandomRoutingStrategy({
407
+ gatewaysProvider: new NetworkGatewaysProvider({
408
+ ario: ARIO.mainnet(),
409
+ sortBy: 'operatorStake',
410
+ limit: 20,
411
+ }),
412
+ });
413
+
414
+ // wrap the random strategy with a ping strategy
303
415
  const pingRandom = new PingRoutingStrategy({
304
- routingStrategy: new RandomRoutingStrategy(),
416
+ routingStrategy: randomStrategy,
305
417
  });
306
418
 
419
+ // wrap the ping random strategy with a cache strategy, caching the selected gateway for 10 minutes
307
420
  const cachedStrategy = new SimpleCacheRoutingStrategy({
308
421
  routingStrategy: pingRandom,
309
422
  ttlSeconds: 600,
@@ -510,13 +623,77 @@ const response = await wayfinder.request('ar://example-name', {
510
623
  });
511
624
  ```
512
625
 
626
+ ## Installation Notes
627
+
628
+ ### Optional Dependencies
629
+
630
+ The `@ar.io/sdk` package is an optional peer dependency. To use AR.IO network gateways, you must explicitly provide an `ario` instance:
631
+
632
+ **With AR.IO SDK (Recommended):**
633
+ ```bash
634
+ npm install @ar.io/wayfinder-core @ar.io/sdk
635
+ # or
636
+ yarn add @ar.io/wayfinder-core @ar.io/sdk
637
+ ```
638
+ - `createWayfinderClient({ ario: ARIO.mainnet() })` uses AR.IO network gateways
639
+ - Supports intelligent gateway selection criteria
640
+ - Dynamic gateway discovery and updates
641
+
642
+ **Without AR.IO SDK:**
643
+ ```bash
644
+ npm install @ar.io/wayfinder-core
645
+ ```
646
+ - `createWayfinderClient()` falls back to curated static gateways
647
+ - `createWayfinderClientSync()` uses only static gateways
648
+ - Full routing and verification functionality available
649
+ - No network gateway selection options
650
+
651
+ ### Caching
652
+
653
+ Wayfinder supports intelligent caching:
654
+
655
+ - **In browsers**: Uses localStorage for persistent caching across page reloads
656
+ - **In Node.js**: Uses in-memory caching
657
+ - **What's cached**: Gateway lists, routing decisions, and more
658
+ - **Cache configuration**:
659
+ - `cache: true` - Enable with default 5-minute TTL
660
+ - `cache: { ttlSeconds: 3600 }` - Enable with custom TTL (in seconds)
661
+ - `cache: false` - Disable caching (default)
662
+
513
663
  ## Advanced Usage
514
664
 
515
- ### Custom Configuration
665
+ ### Using createWayfinderClient with Custom Providers
666
+
667
+ For advanced use cases, you can provide custom providers and strategies to `createWayfinderClient`:
668
+
669
+ ```javascript
670
+ import { createWayfinderClient, NetworkGatewaysProvider } from '@ar.io/wayfinder-core';
671
+ import { ARIO } from '@ar.io/sdk';
672
+
673
+ const wayfinder = createWayfinderClient({
674
+ // Use custom gateways provider
675
+ gatewaysProvider: new NetworkGatewaysProvider({
676
+ ario: ARIO.mainnet(),
677
+ sortBy: 'operatorStake',
678
+ sortOrder: 'desc',
679
+ limit: 10,
680
+ }),
681
+
682
+ // Override with custom verification strategy
683
+ verification: 'hash',
684
+ trustedGateways: ['https://permagate.io'],
685
+
686
+ // Gateway selection
687
+ gatewaySelection: 'highest-staked',
688
+
689
+ // Enable caching with custom TTL
690
+ cache: { ttlSeconds: 3600 }, // 1 hour
691
+ });
692
+ ```
516
693
 
517
- You can customize the wayfinder instance with different gateways, verification strategies, and routing strategies based on your use case.
694
+ ### Direct Constructor Usage
518
695
 
519
- Example:
696
+ For complete control, you can use the Wayfinder constructor directly. This is useful when you need fine-grained control over the configuration:
520
697
 
521
698
  > _Wayfinder client that caches the top 10 gateways by operator stake from the ARIO Network for 1 hour and uses the fastest pinging routing strategy to select the fastest gateway for requests._
522
699
 
@@ -0,0 +1,89 @@
1
+ /**
2
+ * WayFinder
3
+ * Copyright (C) 2022-2025 Permanent Data Solutions, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { AoARIORead } from '@ar.io/sdk';
18
+ import type { GatewaysProvider, Logger, RoutingStrategy, VerificationStrategy } from './types.js';
19
+ import { Wayfinder } from './wayfinder.js';
20
+ export type RoutingOption = 'random' | 'fastest' | 'round-robin' | 'preferred';
21
+ export type VerificationOption = 'hash' | 'data-root' | 'remote' | 'disabled';
22
+ export type GatewaySelection = 'highest-performing' | 'longest-tenure' | 'highest-staked' | 'highest-weight' | 'longest-streak';
23
+ /**
24
+ * Creates a Wayfinder client synchronously using static gateways
25
+ * Use this when you want to avoid the AR.IO SDK dependency
26
+ */
27
+ export declare function createWayfinderClientSync(options?: CreateWayfinderClientOptions): Wayfinder;
28
+ export interface CreateWayfinderClientOptions {
29
+ /**
30
+ * The ARIO instance to use for network gateways provider
31
+ * If not provided, will fall back to static gateways
32
+ */
33
+ ario?: AoARIORead;
34
+ /**
35
+ * The routing strategy to use
36
+ * @default 'random'
37
+ */
38
+ routing?: RoutingOption;
39
+ /**
40
+ * The verification strategy to use
41
+ * @default 'disabled'
42
+ */
43
+ verification?: VerificationOption;
44
+ /**
45
+ * The gateway selection when using NetworkGatewaysProvider (requires ario instance)
46
+ * Only applies when using AR.IO network - ignored for static gateways
47
+ * @default 'highest-performing'
48
+ */
49
+ gatewaySelection?: GatewaySelection;
50
+ /**
51
+ * The trusted gateways to use
52
+ * @default ['https://arweave.net']
53
+ */
54
+ trustedGateways?: string[];
55
+ /**
56
+ * Cache configuration. Can be:
57
+ * - false/undefined: No caching
58
+ * - true: Enable caching with default TTL (300 seconds)
59
+ * - { ttlSeconds: number }: Enable caching with custom TTL
60
+ * @default false
61
+ */
62
+ cache?: boolean | {
63
+ ttlSeconds: number;
64
+ };
65
+ /**
66
+ * Custom logger implementation
67
+ */
68
+ logger?: Logger;
69
+ /**
70
+ * Custom gateways provider (overrides gatewaySelection)
71
+ */
72
+ gatewaysProvider?: GatewaysProvider;
73
+ /**
74
+ * Custom routing strategy (overrides routing option)
75
+ */
76
+ routingStrategy?: RoutingStrategy;
77
+ /**
78
+ * Custom verification strategy (overrides verification option)
79
+ */
80
+ verificationStrategy?: VerificationStrategy;
81
+ }
82
+ /**
83
+ * Creates a Wayfinder client with the specified configuration
84
+ * Uses static gateways by default. Provide an `ario` instance to use NetworkGatewaysProvider
85
+ */
86
+ export declare function createWayfinderClient(options?: CreateWayfinderClientOptions): Wayfinder;
87
+ export declare const createWayfinder: typeof createWayfinderClient;
88
+ export declare const createWayfinderSync: typeof createWayfinderClientSync;
89
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAS7C,OAAO,KAAK,EACV,gBAAgB,EAChB,MAAM,EACN,eAAe,EACf,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAKpB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,aAAa,GAAG,WAAW,CAAC;AAE/E,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9E,MAAM,MAAM,gBAAgB,GACxB,oBAAoB,GACpB,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,CAAC;AAErB;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,4BAAiC,GACzC,SAAS,CAoLX;AAED,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,CAAC;IAElC;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAEpC;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAE3B;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAEzC;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAEpC;;OAEG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC;;OAEG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,4BAAiC,GACzC,SAAS,CAmLX;AAGD,eAAO,MAAM,eAAe,8BAAwB,CAAC;AACrD,eAAO,MAAM,mBAAmB,kCAA4B,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,353 @@
1
+ /**
2
+ * WayFinder
3
+ * Copyright (C) 2022-2025 Permanent Data Solutions, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { LocalStorageGatewaysProvider } from './gateways/local-storage-cache.js';
18
+ import { NetworkGatewaysProvider } from './gateways/network.js';
19
+ import { SimpleCacheGatewaysProvider } from './gateways/simple-cache.js';
20
+ import { StaticGatewaysProvider } from './gateways/static.js';
21
+ import { FastestPingRoutingStrategy } from './routing/ping.js';
22
+ import { PreferredWithFallbackRoutingStrategy } from './routing/preferred-with-fallback.js';
23
+ import { RandomRoutingStrategy } from './routing/random.js';
24
+ import { SimpleCacheRoutingStrategy } from './routing/simple-cache.js';
25
+ import { isBrowser } from './utils/browser.js';
26
+ import { DataRootVerificationStrategy } from './verification/data-root-verification.js';
27
+ import { HashVerificationStrategy } from './verification/hash-verification.js';
28
+ import { RemoteVerificationStrategy } from './verification/remote-verification.js';
29
+ import { Wayfinder } from './wayfinder.js';
30
+ /**
31
+ * Creates a Wayfinder client synchronously using static gateways
32
+ * Use this when you want to avoid the AR.IO SDK dependency
33
+ */
34
+ export function createWayfinderClientSync(options = {}) {
35
+ const { routing = 'random', verification = 'disabled', trustedGateways = [], cache = false, gatewaySelection, logger, ario, gatewaysProvider: customGatewaysProvider, routingStrategy: customRoutingStrategy, verificationStrategy: customVerificationStrategy, } = options;
36
+ // Parse cache configuration
37
+ const cacheEnabled = !!cache;
38
+ const cacheTTLSeconds = typeof cache === 'object' ? cache.ttlSeconds : 300; // 5 minutes default
39
+ // Set up gateways provider
40
+ let gatewaysProvider;
41
+ if (customGatewaysProvider) {
42
+ gatewaysProvider = customGatewaysProvider;
43
+ }
44
+ else if (ario) {
45
+ // Use NetworkGatewaysProvider if ARIO instance is provided
46
+ let sortBy = 'totalDelegatedStake';
47
+ let sortOrder = 'desc';
48
+ const selection = gatewaySelection || 'highest-performing';
49
+ switch (selection) {
50
+ case 'highest-performing':
51
+ sortBy = 'weights.gatewayPerformanceRatio';
52
+ sortOrder = 'desc';
53
+ break;
54
+ case 'longest-tenure':
55
+ sortBy = 'weights.tenureWeight';
56
+ sortOrder = 'desc';
57
+ break;
58
+ case 'highest-staked':
59
+ sortBy = 'weights.stakeWeight';
60
+ sortOrder = 'desc';
61
+ break;
62
+ case 'highest-weight':
63
+ sortBy = 'weights.normalizedCompositeWeight';
64
+ sortOrder = 'desc';
65
+ break;
66
+ case 'longest-streak':
67
+ sortBy = 'stats.passedConsecutiveEpochs';
68
+ sortOrder = 'desc';
69
+ break;
70
+ default:
71
+ sortBy = 'weights.normalizedCompositeWeight';
72
+ sortOrder = 'desc';
73
+ break;
74
+ }
75
+ gatewaysProvider = new NetworkGatewaysProvider({
76
+ ario,
77
+ sortBy,
78
+ sortOrder,
79
+ limit: 10,
80
+ });
81
+ }
82
+ else {
83
+ // Fall back to static gateways when no ARIO instance is provided
84
+ gatewaysProvider = new StaticGatewaysProvider({
85
+ gateways: trustedGateways.length
86
+ ? trustedGateways
87
+ : [
88
+ 'https://permagate.io',
89
+ 'https://arweave.net',
90
+ 'https://ardrive.net',
91
+ ],
92
+ });
93
+ }
94
+ // Wrap with cache if enabled
95
+ if (cacheEnabled) {
96
+ if (isBrowser()) {
97
+ gatewaysProvider = new LocalStorageGatewaysProvider({
98
+ gatewaysProvider: gatewaysProvider,
99
+ ttlSeconds: cacheTTLSeconds,
100
+ });
101
+ }
102
+ else {
103
+ gatewaysProvider = new SimpleCacheGatewaysProvider({
104
+ gatewaysProvider: gatewaysProvider,
105
+ ttlSeconds: cacheTTLSeconds,
106
+ });
107
+ }
108
+ }
109
+ // Set up routing strategy
110
+ let routingStrategy;
111
+ if (customRoutingStrategy) {
112
+ routingStrategy = customRoutingStrategy;
113
+ }
114
+ else {
115
+ switch (routing) {
116
+ case 'random':
117
+ routingStrategy = new RandomRoutingStrategy();
118
+ break;
119
+ case 'fastest':
120
+ routingStrategy = new FastestPingRoutingStrategy();
121
+ break;
122
+ case 'preferred':
123
+ routingStrategy = new PreferredWithFallbackRoutingStrategy({
124
+ preferredGateway: trustedGateways[0],
125
+ fallbackStrategy: new RandomRoutingStrategy(),
126
+ });
127
+ break;
128
+ default:
129
+ throw new Error(`Unknown routing strategy: ${routing}`);
130
+ }
131
+ // Wrap with cache if enabled
132
+ if (cacheEnabled) {
133
+ // TODO: add browser cache support for routing strategy
134
+ routingStrategy = new SimpleCacheRoutingStrategy({
135
+ routingStrategy,
136
+ ttlSeconds: cacheTTLSeconds,
137
+ });
138
+ }
139
+ }
140
+ // Set up verification strategy
141
+ let verificationStrategy;
142
+ let verificationEnabled = true;
143
+ if (customVerificationStrategy) {
144
+ verificationStrategy = customVerificationStrategy;
145
+ }
146
+ else {
147
+ switch (verification) {
148
+ case 'hash':
149
+ verificationStrategy = new HashVerificationStrategy({
150
+ trustedGateways: trustedGateways.map((url) => new URL(url)),
151
+ });
152
+ break;
153
+ case 'data-root':
154
+ verificationStrategy = new DataRootVerificationStrategy({
155
+ trustedGateways: trustedGateways.map((url) => new URL(url)),
156
+ });
157
+ break;
158
+ case 'remote':
159
+ verificationStrategy = new RemoteVerificationStrategy();
160
+ break;
161
+ case 'disabled':
162
+ verificationEnabled = false;
163
+ verificationStrategy = undefined;
164
+ break;
165
+ default:
166
+ throw new Error(`Unknown verification strategy: ${verification}`);
167
+ }
168
+ }
169
+ // Create Wayfinder options
170
+ const wayfinderOptions = {
171
+ logger,
172
+ gatewaysProvider,
173
+ routingSettings: {
174
+ strategy: routingStrategy,
175
+ },
176
+ };
177
+ // Only add verification settings if not disabled
178
+ if (verificationEnabled && verificationStrategy) {
179
+ wayfinderOptions.verificationSettings = {
180
+ enabled: true,
181
+ strategy: verificationStrategy,
182
+ };
183
+ }
184
+ else {
185
+ wayfinderOptions.verificationSettings = {
186
+ enabled: false,
187
+ };
188
+ }
189
+ return new Wayfinder(wayfinderOptions);
190
+ }
191
+ /**
192
+ * Creates a Wayfinder client with the specified configuration
193
+ * Uses static gateways by default. Provide an `ario` instance to use NetworkGatewaysProvider
194
+ */
195
+ export function createWayfinderClient(options = {}) {
196
+ const { routing = 'random', verification = 'disabled', trustedGateways = [], cache = false, gatewaySelection, logger, ario, gatewaysProvider: customGatewaysProvider, routingStrategy: customRoutingStrategy, verificationStrategy: customVerificationStrategy, } = options;
197
+ // Parse cache configuration
198
+ const cacheEnabled = !!cache;
199
+ const cacheTTLSeconds = typeof cache === 'object' ? cache.ttlSeconds : 300; // 5 minutes default
200
+ // Set up gateways provider
201
+ let gatewaysProvider;
202
+ if (customGatewaysProvider) {
203
+ gatewaysProvider = customGatewaysProvider;
204
+ }
205
+ else if (ario) {
206
+ // Use NetworkGatewaysProvider with dynamically imported ARIO
207
+ let sortBy = 'totalDelegatedStake';
208
+ let sortOrder = 'desc';
209
+ switch (gatewaySelection) {
210
+ case 'highest-performing':
211
+ sortBy = 'weights.gatewayPerformanceRatio';
212
+ sortOrder = 'desc';
213
+ break;
214
+ case 'longest-tenure':
215
+ sortBy = 'weights.tenureWeight';
216
+ sortOrder = 'desc';
217
+ break;
218
+ case 'highest-staked':
219
+ sortBy = 'weights.stakeWeight';
220
+ sortOrder = 'desc';
221
+ break;
222
+ case 'highest-weight':
223
+ sortBy = 'weights.normalizedCompositeWeight';
224
+ sortOrder = 'desc';
225
+ break;
226
+ case 'longest-streak':
227
+ sortBy = 'stats.passedConsecutiveEpochs';
228
+ sortOrder = 'desc';
229
+ break;
230
+ default:
231
+ sortBy = 'weights.normalizedCompositeWeight';
232
+ sortOrder = 'desc';
233
+ break;
234
+ }
235
+ gatewaysProvider = new NetworkGatewaysProvider({
236
+ ario,
237
+ sortBy,
238
+ sortOrder,
239
+ limit: 10,
240
+ });
241
+ }
242
+ else {
243
+ // Fall back to static gateways when no ARIO instance is provided
244
+ gatewaysProvider = new StaticGatewaysProvider({
245
+ gateways: trustedGateways.length
246
+ ? trustedGateways
247
+ : [
248
+ 'https://permagate.io',
249
+ 'https://arweave.net',
250
+ 'https://ardrive.net',
251
+ ],
252
+ });
253
+ }
254
+ // Wrap with cache if enabled
255
+ if (cacheEnabled) {
256
+ if (isBrowser()) {
257
+ gatewaysProvider = new LocalStorageGatewaysProvider({
258
+ gatewaysProvider: gatewaysProvider,
259
+ ttlSeconds: cacheTTLSeconds,
260
+ });
261
+ }
262
+ else {
263
+ gatewaysProvider = new SimpleCacheGatewaysProvider({
264
+ gatewaysProvider: gatewaysProvider,
265
+ ttlSeconds: cacheTTLSeconds,
266
+ });
267
+ }
268
+ }
269
+ // Set up routing strategy
270
+ let routingStrategy;
271
+ if (customRoutingStrategy) {
272
+ routingStrategy = customRoutingStrategy;
273
+ }
274
+ else {
275
+ switch (routing) {
276
+ case 'random':
277
+ routingStrategy = new RandomRoutingStrategy();
278
+ break;
279
+ case 'fastest':
280
+ routingStrategy = new FastestPingRoutingStrategy();
281
+ break;
282
+ case 'preferred':
283
+ routingStrategy = new PreferredWithFallbackRoutingStrategy({
284
+ preferredGateway: trustedGateways[0],
285
+ fallbackStrategy: new RandomRoutingStrategy(),
286
+ });
287
+ break;
288
+ default:
289
+ throw new Error(`Unknown routing strategy: ${routing}`);
290
+ }
291
+ // Wrap with cache if enabled
292
+ if (cacheEnabled) {
293
+ // TODO: add browser cache support for routing strategy
294
+ routingStrategy = new SimpleCacheRoutingStrategy({
295
+ routingStrategy,
296
+ ttlSeconds: cacheTTLSeconds,
297
+ });
298
+ }
299
+ }
300
+ // Set up verification strategy
301
+ let verificationStrategy;
302
+ let verificationEnabled = true;
303
+ if (customVerificationStrategy) {
304
+ verificationStrategy = customVerificationStrategy;
305
+ }
306
+ else {
307
+ switch (verification) {
308
+ case 'hash':
309
+ verificationStrategy = new HashVerificationStrategy({
310
+ trustedGateways: trustedGateways.map((url) => new URL(url)),
311
+ });
312
+ break;
313
+ case 'data-root':
314
+ verificationStrategy = new DataRootVerificationStrategy({
315
+ trustedGateways: trustedGateways.map((url) => new URL(url)),
316
+ });
317
+ break;
318
+ case 'remote':
319
+ verificationStrategy = new RemoteVerificationStrategy();
320
+ break;
321
+ case 'disabled':
322
+ verificationEnabled = false;
323
+ verificationStrategy = undefined;
324
+ break;
325
+ default:
326
+ throw new Error(`Unknown verification strategy: ${verification}`);
327
+ }
328
+ }
329
+ // Create Wayfinder options
330
+ const wayfinderOptions = {
331
+ logger,
332
+ gatewaysProvider,
333
+ routingSettings: {
334
+ strategy: routingStrategy,
335
+ },
336
+ };
337
+ // Only add verification settings if not disabled
338
+ if (verificationEnabled && verificationStrategy) {
339
+ wayfinderOptions.verificationSettings = {
340
+ enabled: true,
341
+ strategy: verificationStrategy,
342
+ };
343
+ }
344
+ else {
345
+ wayfinderOptions.verificationSettings = {
346
+ enabled: false,
347
+ };
348
+ }
349
+ return new Wayfinder(wayfinderOptions);
350
+ }
351
+ // Re-export as aliases
352
+ export const createWayfinder = createWayfinderClient;
353
+ export const createWayfinderSync = createWayfinderClientSync;
@@ -25,7 +25,7 @@ export declare class NetworkGatewaysProvider implements GatewaysProvider {
25
25
  private logger;
26
26
  constructor({ ario, sortBy, sortOrder, limit, filter, logger, }: {
27
27
  ario: AoARIORead;
28
- sortBy?: 'totalDelegatedStake' | 'operatorStake' | 'startTimestamp';
28
+ sortBy?: 'totalDelegatedStake' | 'operatorStake' | 'startTimestamp' | 'weights.gatewayPerformanceRatio' | 'weights.tenureWeight' | 'weights.stakeWeight' | 'weights.compositeWeight' | 'stats.passedConsecutiveEpochs' | 'weights.normalizedCompositeWeight';
29
29
  sortOrder?: 'asc' | 'desc';
30
30
  limit?: number;
31
31
  blocklist?: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/gateways/network.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE5D,qBAAa,uBAAwB,YAAW,gBAAgB;IAC9D,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,MAAM,CAA6D;IAC3E,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,MAAM,CAAS;gBAEX,EACV,IAAI,EACJ,MAAwB,EACxB,SAAkB,EAClB,KAAY,EACZ,MAAqC,EACrC,MAAsB,GACvB,EAAE;QACD,IAAI,EAAE,UAAU,CAAC;QACjB,MAAM,CAAC,EAAE,qBAAqB,GAAG,eAAe,GAAG,gBAAgB,CAAC;QACpE,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC;QACnC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IASK,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;CA0DpC"}
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/gateways/network.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE5D,qBAAa,uBAAwB,YAAW,gBAAgB;IAC9D,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,MAAM,CAS0B;IACxC,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,MAAM,CAAS;gBAEX,EACV,IAAI,EACJ,MAAwB,EACxB,SAAkB,EAClB,KAAY,EACZ,MAAqC,EACrC,MAAsB,GACvB,EAAE;QACD,IAAI,EAAE,UAAU,CAAC;QACjB,MAAM,CAAC,EACH,qBAAqB,GACrB,eAAe,GACf,gBAAgB,GAChB,iCAAiC,GACjC,sBAAsB,GACtB,qBAAqB,GACrB,yBAAyB,GACzB,+BAA+B,GAC/B,mCAAmC,CAAC;QACxC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;QAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC;QACnC,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IASK,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;CA2DpC"}
@@ -31,6 +31,7 @@ export class NetworkGatewaysProvider {
31
31
  cursor,
32
32
  sortBy: this.sortBy,
33
33
  sortOrder: this.sortOrder,
34
+ // TODO: support filters on gateways
34
35
  });
35
36
  gateways.push(...newGateways);
36
37
  cursor = nextCursor;
package/dist/index.d.ts CHANGED
@@ -31,4 +31,5 @@ export * from './verification/signature-verification.js';
31
31
  export * from './verification/remote-verification.js';
32
32
  export * from './emitter.js';
33
33
  export * from './wayfinder.js';
34
+ export * from './client.js';
34
35
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,cAAc,YAAY,CAAC;AAG3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sCAAsC,CAAC;AACrD,cAAc,2BAA2B,CAAC;AAG1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,mCAAmC,CAAC;AAGlD,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AAGtD,cAAc,cAAc,CAAC;AAG7B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,cAAc,YAAY,CAAC;AAG3B,cAAc,qBAAqB,CAAC;AACpC,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,sCAAsC,CAAC;AACrD,cAAc,2BAA2B,CAAC;AAG1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sBAAsB,CAAC;AACrC,cAAc,mCAAmC,CAAC;AAGlD,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,0CAA0C,CAAC;AACzD,cAAc,uCAAuC,CAAC;AAGtD,cAAc,cAAc,CAAC;AAG7B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,aAAa,CAAC"}
package/dist/index.js CHANGED
@@ -37,3 +37,5 @@ export * from './verification/remote-verification.js';
37
37
  export * from './emitter.js';
38
38
  // wayfinder
39
39
  export * from './wayfinder.js';
40
+ // utility functions
41
+ export * from './client.js';
@@ -1,15 +1,17 @@
1
- import type { Logger, RoutingStrategy } from '../types.js';
1
+ import type { GatewaysProvider, Logger, RoutingStrategy } from '../types.js';
2
2
  export declare class FastestPingRoutingStrategy implements RoutingStrategy {
3
3
  private timeoutMs;
4
4
  private logger;
5
5
  private maxConcurrency;
6
- constructor({ timeoutMs, maxConcurrency, logger, }?: {
6
+ private gatewaysProvider?;
7
+ constructor({ timeoutMs, maxConcurrency, logger, gatewaysProvider, }?: {
7
8
  timeoutMs?: number;
8
9
  maxConcurrency?: number;
9
10
  logger?: Logger;
11
+ gatewaysProvider?: GatewaysProvider;
10
12
  });
11
13
  selectGateway({ gateways, path, subdomain, }: {
12
- gateways: URL[];
14
+ gateways?: URL[];
13
15
  path?: string;
14
16
  subdomain?: string;
15
17
  }): Promise<URL>;
@@ -25,11 +27,13 @@ export declare class PingRoutingStrategy implements RoutingStrategy {
25
27
  private logger;
26
28
  private retries;
27
29
  private timeoutMs;
28
- constructor({ routingStrategy, logger, retries, timeoutMs, }: {
30
+ private gatewaysProvider?;
31
+ constructor({ routingStrategy, logger, retries, timeoutMs, gatewaysProvider, }: {
29
32
  routingStrategy: RoutingStrategy;
30
33
  logger?: Logger;
31
34
  retries?: number;
32
35
  timeoutMs?: number;
36
+ gatewaysProvider?: GatewaysProvider;
33
37
  });
34
38
  selectGateway(params: {
35
39
  gateways?: URL[];
@@ -1 +1 @@
1
- {"version":3,"file":"ping.d.ts","sourceRoot":"","sources":["../../src/routing/ping.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE3D,qBAAa,0BAA2B,YAAW,eAAe;IAChE,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,cAAc,CAAS;gBAEnB,EACV,SAAe,EACf,cAAmB,EACnB,MAAsB,GACvB,GAAE;QACD,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,MAAM,CAAC;KACZ;IAMA,aAAa,CAAC,EAClB,QAAQ,EACR,IAAS,EACT,SAAS,GACV,EAAE;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,GAAG,CAAC;CA8EjB;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;gBAEd,EACV,eAAe,EACf,MAAsB,EACtB,OAAW,EACX,SAAgB,GACjB,EAAE;QACD,eAAe,EAAE,eAAe,CAAC;QACjC,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB;IAOK,aAAa,CAAC,MAAM,EAAE;QAC1B,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,GAAG,CAAC;CA4EjB"}
1
+ {"version":3,"file":"ping.d.ts","sourceRoot":"","sources":["../../src/routing/ping.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE7E,qBAAa,0BAA2B,YAAW,eAAe;IAChE,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,gBAAgB,CAAC,CAAmB;gBAEhC,EACV,SAAe,EACf,cAAmB,EACnB,MAAsB,EACtB,gBAAgB,GACjB,GAAE;QACD,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;KAChC;IAOA,aAAa,CAAC,EAClB,QAAQ,EACR,IAAS,EACT,SAAS,GACV,EAAE;QACD,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,GAAG,CAAC;CAoFjB;AAED;;;;;GAKG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAC,CAAmB;gBAEhC,EACV,eAAe,EACf,MAAsB,EACtB,OAAW,EACX,SAAgB,EAChB,gBAAgB,GACjB,EAAE;QACD,eAAe,EAAE,eAAe,CAAC;QACjC,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;KACrC;IAQK,aAAa,CAAC,MAAM,EAAE;QAC1B,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC,GAAG,CAAC;CAkFjB"}
@@ -20,25 +20,29 @@ export class FastestPingRoutingStrategy {
20
20
  timeoutMs;
21
21
  logger;
22
22
  maxConcurrency;
23
- constructor({ timeoutMs = 500, maxConcurrency = 50, logger = defaultLogger, } = {}) {
23
+ gatewaysProvider;
24
+ constructor({ timeoutMs = 500, maxConcurrency = 50, logger = defaultLogger, gatewaysProvider, } = {}) {
24
25
  this.timeoutMs = timeoutMs;
25
26
  this.logger = logger;
26
27
  this.maxConcurrency = maxConcurrency;
28
+ this.gatewaysProvider = gatewaysProvider;
27
29
  }
28
30
  async selectGateway({ gateways, path = '', subdomain, }) {
29
- if (gateways.length === 0) {
31
+ const resolvedGateways = gateways ??
32
+ (this.gatewaysProvider ? await this.gatewaysProvider.getGateways() : []);
33
+ if (resolvedGateways.length === 0) {
30
34
  const error = new Error('No gateways provided');
31
35
  this.logger.error('Failed to select gateway', { error: error.message });
32
36
  throw error;
33
37
  }
34
38
  try {
35
- this.logger.debug(`Pinging ${gateways.length} gateways with timeout ${this.timeoutMs}ms`, {
36
- gateways: gateways.map((g) => g.toString()),
39
+ this.logger.debug(`Pinging ${resolvedGateways.length} gateways with timeout ${this.timeoutMs}ms`, {
40
+ gateways: resolvedGateways.map((g) => g.toString()),
37
41
  timeoutMs: this.timeoutMs,
38
42
  probePath: path,
39
43
  });
40
- const throttle = pLimit(Math.min(this.maxConcurrency, gateways.length));
41
- const pingPromises = gateways.map(async (gateway) => {
44
+ const throttle = pLimit(Math.min(this.maxConcurrency, resolvedGateways.length));
45
+ const pingPromises = resolvedGateways.map(async (gateway) => {
42
46
  return throttle(async () => {
43
47
  const url = new URL(gateway.toString());
44
48
  if (subdomain) {
@@ -79,11 +83,11 @@ export class FastestPingRoutingStrategy {
79
83
  this.logger.error('All gateways failed to respond', {
80
84
  subdomain,
81
85
  path,
82
- gateways: gateways.map((g) => g.toString()),
86
+ gateways: resolvedGateways.map((g) => g.toString()),
83
87
  });
84
88
  throw new Error('All gateways failed to respond', {
85
89
  cause: {
86
- gateways: gateways.map((g) => g.toString()),
90
+ gateways: resolvedGateways.map((g) => g.toString()),
87
91
  path,
88
92
  subdomain,
89
93
  },
@@ -102,21 +106,27 @@ export class PingRoutingStrategy {
102
106
  logger;
103
107
  retries;
104
108
  timeoutMs;
105
- constructor({ routingStrategy, logger = defaultLogger, retries = 5, timeoutMs = 1000, }) {
109
+ gatewaysProvider;
110
+ constructor({ routingStrategy, logger = defaultLogger, retries = 5, timeoutMs = 1000, gatewaysProvider, }) {
106
111
  this.routingStrategy = routingStrategy;
107
112
  this.logger = logger;
108
113
  this.retries = retries;
109
114
  this.timeoutMs = timeoutMs;
115
+ this.gatewaysProvider = gatewaysProvider;
110
116
  }
111
117
  async selectGateway(params) {
112
- const { gateways = [], path, subdomain } = params;
113
- if (gateways.length === 0) {
118
+ const { gateways, path, subdomain } = params;
119
+ const resolvedGateways = gateways ??
120
+ (this.gatewaysProvider ? await this.gatewaysProvider.getGateways() : []);
121
+ if (resolvedGateways.length === 0) {
114
122
  throw new Error('No gateways available');
115
123
  }
124
+ const paramsWithGateways = { ...params, gateways: resolvedGateways };
116
125
  for (let i = 0; i < this.retries; i++) {
117
126
  let selectedGateway = undefined;
118
127
  try {
119
- selectedGateway = await this.routingStrategy.selectGateway(params);
128
+ selectedGateway =
129
+ await this.routingStrategy.selectGateway(paramsWithGateways);
120
130
  const pingUrl = new URL(selectedGateway.toString());
121
131
  if (subdomain) {
122
132
  pingUrl.hostname = `${subdomain}.${pingUrl.hostname}`;
@@ -166,7 +176,7 @@ export class PingRoutingStrategy {
166
176
  }
167
177
  throw new Error('Failed to find working gateway after HEAD checks', {
168
178
  cause: {
169
- gateways: gateways.map((g) => g.toString()),
179
+ gateways: resolvedGateways.map((g) => g.toString()),
170
180
  path,
171
181
  subdomain,
172
182
  retries: this.retries,
@@ -1,23 +1,13 @@
1
- /**
2
- * WayFinder
3
- * Copyright (C) 2022-2025 Permanent Data Solutions, Inc.
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
- import type { RoutingStrategy } from '../types.js';
1
+ import type { GatewaysProvider, Logger, RoutingStrategy } from '../types.js';
18
2
  export declare class RandomRoutingStrategy implements RoutingStrategy {
19
- selectGateway({ gateways, }: {
20
- gateways: URL[];
3
+ private gatewaysProvider?;
4
+ private logger;
5
+ constructor({ gatewaysProvider, logger, }?: {
6
+ gatewaysProvider?: GatewaysProvider;
7
+ logger?: Logger;
8
+ });
9
+ selectGateway({ gateways, }?: {
10
+ gateways?: URL[];
21
11
  }): Promise<URL>;
22
12
  }
23
13
  //# sourceMappingURL=random.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../../src/routing/random.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,qBAAa,qBAAsB,YAAW,eAAe;IACrD,aAAa,CAAC,EAClB,QAAQ,GACT,EAAE;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC;KACjB,GAAG,OAAO,CAAC,GAAG,CAAC;CAMjB"}
1
+ {"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../../src/routing/random.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG7E,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,MAAM,CAAS;gBAEX,EACV,gBAAgB,EAChB,MAAsB,GACvB,GAAE;QACD,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;QACpC,MAAM,CAAC,EAAE,MAAM,CAAC;KACZ;IAKA,aAAa,CAAC,EAClB,QAAQ,GACT,GAAE;QACD,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;KACb,GAAG,OAAO,CAAC,GAAG,CAAC;CAatB"}
@@ -1,9 +1,38 @@
1
+ /**
2
+ * WayFinder
3
+ * Copyright (C) 2022-2025 Permanent Data Solutions, Inc.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { defaultLogger } from '../logger.js';
1
18
  import { randomInt } from '../utils/random.js';
2
19
  export class RandomRoutingStrategy {
3
- async selectGateway({ gateways, }) {
4
- if (gateways.length === 0) {
20
+ gatewaysProvider;
21
+ logger;
22
+ constructor({ gatewaysProvider, logger = defaultLogger, } = {}) {
23
+ this.gatewaysProvider = gatewaysProvider;
24
+ this.logger = logger;
25
+ }
26
+ async selectGateway({ gateways, } = {}) {
27
+ const resolvedGateways = gateways ??
28
+ (this.gatewaysProvider ? await this.gatewaysProvider.getGateways() : []);
29
+ if (resolvedGateways.length === 0) {
30
+ this.logger.error('No gateways available');
5
31
  throw new Error('No gateways available');
6
32
  }
7
- return gateways[randomInt(0, gateways.length)];
33
+ this.logger.debug('Selecting random gateway', {
34
+ gateways: resolvedGateways.map((g) => g.toString()),
35
+ });
36
+ return resolvedGateways[randomInt(0, resolvedGateways.length)];
8
37
  }
9
38
  }
@@ -1,12 +1,14 @@
1
- import type { Logger, RoutingStrategy } from '../types.js';
1
+ import type { GatewaysProvider, Logger, RoutingStrategy } from '../types.js';
2
2
  export declare class RoundRobinRoutingStrategy implements RoutingStrategy {
3
3
  readonly name = "round-robin";
4
4
  private gateways;
5
5
  private currentIndex;
6
6
  private logger;
7
- constructor({ gateways, logger, }: {
8
- gateways: URL[];
7
+ private gatewaysProvider?;
8
+ constructor({ gateways, logger, gatewaysProvider, }?: {
9
+ gateways?: URL[];
9
10
  logger?: Logger;
11
+ gatewaysProvider?: GatewaysProvider;
10
12
  });
11
13
  selectGateway(): Promise<URL>;
12
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"round-robin.d.ts","sourceRoot":"","sources":["../../src/routing/round-robin.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE3D,qBAAa,yBAA0B,YAAW,eAAe;IAC/D,SAAgB,IAAI,iBAAiB;IACrC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAS;gBAEX,EACV,QAAQ,EACR,MAAsB,GACvB,EAAE;QACD,QAAQ,EAAE,GAAG,EAAE,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB;IAMK,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC;CAMpC"}
1
+ {"version":3,"file":"round-robin.d.ts","sourceRoot":"","sources":["../../src/routing/round-robin.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE7E,qBAAa,yBAA0B,YAAW,eAAe;IAC/D,SAAgB,IAAI,iBAAiB;IACrC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAC,CAAmB;gBAEhC,EACV,QAAQ,EACR,MAAsB,EACtB,gBAAgB,GACjB,GAAE;QACD,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC;QACjB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;KAChC;IAcA,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC;CAqBpC"}
@@ -20,14 +20,35 @@ export class RoundRobinRoutingStrategy {
20
20
  gateways;
21
21
  currentIndex;
22
22
  logger;
23
- constructor({ gateways, logger = defaultLogger, }) {
24
- this.gateways = gateways;
23
+ gatewaysProvider;
24
+ constructor({ gateways, logger = defaultLogger, gatewaysProvider, } = {}) {
25
+ if (gateways && gatewaysProvider) {
26
+ throw new Error('Cannot provide both gateways and gatewaysProvider');
27
+ }
28
+ if (!gateways && !gatewaysProvider) {
29
+ throw new Error('Must provide either gateways or gatewaysProvider');
30
+ }
31
+ this.gateways = gateways || [];
25
32
  this.currentIndex = 0;
26
33
  this.logger = logger;
34
+ this.gatewaysProvider = gatewaysProvider;
27
35
  }
28
36
  async selectGateway() {
37
+ // Lazy load gateways from provider if not already loaded
38
+ if (this.gateways.length === 0 && this.gatewaysProvider) {
39
+ this.logger.debug('Loading gateways from provider');
40
+ this.gateways = await this.gatewaysProvider.getGateways();
41
+ this.currentIndex = 0;
42
+ }
43
+ if (this.gateways.length === 0) {
44
+ throw new Error('No gateways available');
45
+ }
29
46
  const gateway = this.gateways[this.currentIndex];
30
- this.logger.info('Selecting gateway', { gateway });
47
+ this.logger.debug('Selecting gateway', {
48
+ gateway: gateway.toString(),
49
+ currentIndex: this.currentIndex,
50
+ totalGateways: this.gateways.length,
51
+ });
31
52
  this.currentIndex = (this.currentIndex + 1) % this.gateways.length;
32
53
  return gateway;
33
54
  }
package/dist/version.d.ts CHANGED
@@ -14,5 +14,5 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- export declare const WAYFINDER_CORE_VERSION = "v1.3.1";
17
+ export declare const WAYFINDER_CORE_VERSION = "v1.4.0-alpha.0";
18
18
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,WAAW,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,mBAAmB,CAAC"}
package/dist/version.js CHANGED
@@ -14,4 +14,4 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
- export const WAYFINDER_CORE_VERSION = 'v1.3.1';
17
+ export const WAYFINDER_CORE_VERSION = 'v1.4.0-alpha.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ar.io/wayfinder-core",
3
- "version": "1.3.1",
3
+ "version": "1.4.0-alpha.0",
4
4
  "description": "WayFinder core library for intelligently routing to optimal AR.IO gateways",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -57,6 +57,11 @@
57
57
  "peerDependencies": {
58
58
  "@ar.io/sdk": ">=3.12.0"
59
59
  },
60
+ "peerDependenciesMeta": {
61
+ "@ar.io/sdk": {
62
+ "optional": true
63
+ }
64
+ },
60
65
  "devDependencies": {
61
66
  "@ar.io/sdk": "^3.13.0",
62
67
  "@types/node": "^24.0.0",