@ar.io/wayfinder-core 0.0.5-alpha.2 → 0.0.5-alpha.4

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.
Files changed (52) hide show
  1. package/README.md +58 -2
  2. package/dist/classifiers/gql-classifier.d.ts +1 -2
  3. package/dist/classifiers/gql-classifier.d.ts.map +1 -1
  4. package/dist/classifiers/gql-classifier.js +16 -0
  5. package/dist/gateways/network.d.ts +2 -3
  6. package/dist/gateways/network.d.ts.map +1 -1
  7. package/dist/gateways/simple-cache.d.ts +1 -19
  8. package/dist/gateways/simple-cache.d.ts.map +1 -1
  9. package/dist/gateways/simple-cache.js +10 -11
  10. package/dist/gateways/static.d.ts +1 -1
  11. package/dist/gateways/static.d.ts.map +1 -1
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +3 -0
  15. package/dist/logger.d.ts +1 -10
  16. package/dist/logger.d.ts.map +1 -1
  17. package/dist/logger.js +16 -0
  18. package/dist/routing/ping.d.ts +1 -2
  19. package/dist/routing/ping.d.ts.map +1 -1
  20. package/dist/routing/preferred-with-fallback.d.ts +1 -2
  21. package/dist/routing/preferred-with-fallback.d.ts.map +1 -1
  22. package/dist/routing/preferred-with-fallback.js +16 -0
  23. package/dist/routing/random.d.ts +1 -1
  24. package/dist/routing/random.d.ts.map +1 -1
  25. package/dist/routing/round-robin.d.ts +1 -19
  26. package/dist/routing/round-robin.d.ts.map +1 -1
  27. package/dist/routing/round-robin.js +10 -11
  28. package/dist/routing/simple-cache.d.ts +37 -0
  29. package/dist/routing/simple-cache.d.ts.map +1 -0
  30. package/dist/routing/simple-cache.js +72 -0
  31. package/dist/routing/static.d.ts +1 -19
  32. package/dist/routing/static.d.ts.map +1 -1
  33. package/dist/routing/static.js +10 -11
  34. package/dist/telemetry.d.ts +33 -0
  35. package/dist/telemetry.d.ts.map +1 -0
  36. package/dist/telemetry.js +115 -0
  37. package/dist/types.d.ts +170 -0
  38. package/dist/types.d.ts.map +1 -0
  39. package/dist/types.js +17 -0
  40. package/dist/utils/hash.d.ts +1 -2
  41. package/dist/utils/hash.d.ts.map +1 -1
  42. package/dist/verification/data-root-verifier.d.ts +2 -3
  43. package/dist/verification/data-root-verifier.d.ts.map +1 -1
  44. package/dist/verification/hash-verifier.d.ts +2 -3
  45. package/dist/verification/hash-verifier.d.ts.map +1 -1
  46. package/dist/verification/signature-verifier.d.ts +4 -4
  47. package/dist/verification/signature-verifier.d.ts.map +1 -1
  48. package/dist/verification/signature-verifier.js +2 -0
  49. package/dist/wayfinder.d.ts +29 -113
  50. package/dist/wayfinder.d.ts.map +1 -1
  51. package/dist/wayfinder.js +163 -90
  52. package/package.json +6 -1
@@ -1 +1 @@
1
- {"version":3,"file":"wayfinder.d.ts","sourceRoot":"","sources":["../src/wayfinder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wayfinder.d.ts","sourceRoot":"","sources":["../src/wayfinder.ts"],"names":[],"mappings":"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"}
package/dist/wayfinder.js CHANGED
@@ -16,7 +16,9 @@
16
16
  */
17
17
  import { EventEmitter } from 'eventemitter3';
18
18
  import { defaultLogger } from './logger.js';
19
+ import { context, trace } from '@opentelemetry/api';
19
20
  import { FastestPingRoutingStrategy } from './routing/ping.js';
21
+ import { initTelemetry, startRequestSpans } from './telemetry.js';
20
22
  import { sandboxFromId } from './utils/base64.js';
21
23
  import { HashVerificationStrategy } from './verification/hash-verifier.js';
22
24
  // known regexes for wayfinder urls
@@ -68,7 +70,7 @@ export const resolveWayfinderUrl = ({ originalUrl, selectedGateway, logger, }) =
68
70
  return new URL(originalUrl);
69
71
  };
70
72
  export class WayfinderEmitter extends EventEmitter {
71
- constructor({ verification, routing } = {}) {
73
+ constructor({ verification, routing, parentEmitter, } = {}) {
72
74
  super();
73
75
  if (verification) {
74
76
  if (verification.onVerificationSucceeded) {
@@ -92,6 +94,29 @@ export class WayfinderEmitter extends EventEmitter {
92
94
  this.on('routing-succeeded', routing.onRoutingSucceeded);
93
95
  }
94
96
  }
97
+ if (parentEmitter) {
98
+ this.on('routing-started', (event) => {
99
+ parentEmitter.emit('routing-started', event);
100
+ });
101
+ this.on('routing-skipped', (event) => {
102
+ parentEmitter.emit('routing-skipped', event);
103
+ });
104
+ this.on('routing-succeeded', (event) => {
105
+ parentEmitter.emit('routing-succeeded', event);
106
+ });
107
+ this.on('verification-succeeded', (event) => {
108
+ parentEmitter.emit('verification-succeeded', event);
109
+ });
110
+ this.on('verification-failed', (event) => {
111
+ parentEmitter.emit('verification-failed', event);
112
+ });
113
+ this.on('verification-progress', (event) => {
114
+ parentEmitter.emit('verification-progress', event);
115
+ });
116
+ this.on('verification-skipped', (event) => {
117
+ parentEmitter.emit('verification-skipped', event);
118
+ });
119
+ }
95
120
  }
96
121
  }
97
122
  export function tapAndVerifyReadableStream({ originalStream, contentLength, verifyData, txId, emitter, strict = false, }) {
@@ -182,19 +207,35 @@ export function tapAndVerifyReadableStream({ originalStream, contentLength, veri
182
207
  * @param resolveUrl - the function to construct the redirect url for ar:// requests
183
208
  * @returns a wrapped fetch function that supports ar:// protocol and always returns Response
184
209
  */
185
- export const wayfinderFetch = ({ emitter = new WayfinderEmitter(), logger = defaultLogger, gatewaysProvider, verificationSettings, routingSettings, }) => {
210
+ export const wayfinderFetch = ({ logger = defaultLogger, gatewaysProvider, verificationSettings, routingSettings, emitter, tracer, }) => {
186
211
  return async (input, init) => {
212
+ const {
213
+ // allows for overriding the verification and routing settings for a single request
214
+ verificationSettings: requestVerificationSettings, routingSettings: requestRoutingSettings, ...restInit } = init ?? {};
187
215
  const url = input instanceof URL ? input.toString() : input.toString();
216
+ const requestEmitter = new WayfinderEmitter({
217
+ verification: requestVerificationSettings?.events,
218
+ routing: requestRoutingSettings?.events,
219
+ parentEmitter: emitter,
220
+ });
221
+ const { parentSpan } = startRequestSpans({
222
+ originalUrl: url,
223
+ verificationSettings: requestVerificationSettings ?? verificationSettings,
224
+ routingSettings: requestRoutingSettings ?? routingSettings,
225
+ gatewaysProvider,
226
+ emitter: requestEmitter,
227
+ tracer,
228
+ });
188
229
  if (!url.toString().startsWith('ar://')) {
189
230
  logger?.debug('URL is not a wayfinder url, skipping routing', {
190
231
  input,
191
232
  });
192
- emitter?.emit('routing-skipped', {
233
+ requestEmitter.emit('routing-skipped', {
193
234
  originalUrl: JSON.stringify(input),
194
235
  });
195
236
  return fetch(input, init);
196
237
  }
197
- emitter?.emit('routing-started', {
238
+ requestEmitter.emit('routing-started', {
198
239
  originalUrl: input.toString(),
199
240
  });
200
241
  const maxRetries = 3;
@@ -220,7 +261,7 @@ export const wayfinderFetch = ({ emitter = new WayfinderEmitter(), logger = defa
220
261
  selectedGateway,
221
262
  logger,
222
263
  });
223
- emitter?.emit('routing-succeeded', {
264
+ requestEmitter.emit('routing-succeeded', {
224
265
  originalUrl: url,
225
266
  selectedGateway: selectedGateway.toString(),
226
267
  redirectUrl: redirectUrl.toString(),
@@ -229,70 +270,97 @@ export const wayfinderFetch = ({ emitter = new WayfinderEmitter(), logger = defa
229
270
  originalUrl: url,
230
271
  redirectUrl: redirectUrl.toString(),
231
272
  });
273
+ const requestSpan = parentSpan
274
+ ? tracer?.startSpan('wayfinder.fetch', undefined, trace.setSpan(context.active(), parentSpan))
275
+ : undefined;
232
276
  // make the request to the target gateway using the redirect url
233
277
  const response = await fetch(redirectUrl.toString(), {
234
278
  // enforce CORS given we're likely going to a different origin, but always allow the client to override
235
279
  redirect: 'follow',
236
280
  mode: 'cors',
237
- ...init,
281
+ ...restInit,
282
+ });
283
+ // add response attributes to the span
284
+ requestSpan?.setAttribute('response.status', response.status);
285
+ requestSpan?.setAttribute('response.statusText', response.statusText);
286
+ response.headers.forEach((value, key) => {
287
+ requestSpan?.setAttribute(`response.headers.${key}`, value);
238
288
  });
239
289
  logger?.debug(`Successfully routed request to gateway`, {
240
290
  redirectUrl: redirectUrl.toString(),
241
291
  originalUrl: url,
242
292
  });
243
293
  // only verify data if the redirect url is different from the original url
244
- if (redirectUrl.toString() !== url) {
245
- if (verificationSettings.enabled &&
246
- verificationSettings.strategy?.verifyData) {
247
- const headers = response.headers;
248
- // transaction id is either in the response headers or the path of the request as the first parameter
249
- const txId = headers.get('x-arns-resolved-id') ??
250
- redirectUrl.pathname.split('/')[1];
251
- const contentLength = +(headers.get('content-length') ?? 0);
252
- if (!txIdRegex.test(txId)) {
253
- // no transaction id found, skip verification
254
- logger?.debug('No transaction id found, skipping verification', {
255
- redirectUrl: redirectUrl.toString(),
256
- originalUrl: url,
257
- });
258
- emitter?.emit('verification-skipped', {
259
- originalUrl: url,
260
- });
261
- return response;
262
- }
263
- emitter?.emit('identified-transaction-id', {
264
- originalUrl: url,
265
- selectedGateway: redirectUrl.toString(),
266
- txId,
267
- });
268
- // Check if the response has a body
269
- if (response.body) {
270
- const newClientStream = tapAndVerifyReadableStream({
271
- originalStream: response.body,
272
- contentLength,
273
- verifyData: verificationSettings.strategy?.verifyData.bind(verificationSettings.strategy),
274
- txId,
275
- emitter,
276
- strict: verificationSettings.strict,
277
- });
278
- return new Response(newClientStream, {
279
- status: response.status,
280
- statusText: response.statusText,
281
- headers: response.headers,
282
- });
283
- }
284
- else {
285
- // No response body to verify, skip verification
286
- logger?.debug('No response body to verify', {
287
- redirectUrl: redirectUrl.toString(),
288
- originalUrl: url,
289
- txId,
290
- });
291
- return response;
292
- }
293
- }
294
+ if (redirectUrl.toString() === url) {
295
+ logger?.debug('Redirect URL is the same as the original URL, skipping verification', {
296
+ redirectUrl: redirectUrl.toString(),
297
+ originalUrl: url,
298
+ });
299
+ requestEmitter.emit('verification-skipped', {
300
+ originalUrl: url,
301
+ });
302
+ requestSpan?.end();
303
+ return response;
304
+ }
305
+ // if verification is disabled, return the response
306
+ if (!(verificationSettings.enabled &&
307
+ verificationSettings.strategy?.verifyData)) {
308
+ logger?.debug('Verification is disabled or no verification strategy is provided, skipping verification', {
309
+ redirectUrl: redirectUrl.toString(),
310
+ originalUrl: url,
311
+ });
312
+ requestEmitter.emit('verification-skipped', {
313
+ originalUrl: url,
314
+ });
315
+ requestSpan?.end();
316
+ return response;
317
+ }
318
+ // Verify the response
319
+ // TODO: add more verification and response attributes to the span
320
+ const headers = response.headers;
321
+ // transaction id is either in the response headers or the path of the request as the first parameter
322
+ const txId = headers.get('x-arns-resolved-id') ??
323
+ redirectUrl.pathname.split('/')[1];
324
+ const contentLength = +(headers.get('content-length') ?? 0);
325
+ requestSpan?.setAttribute('txId', txId);
326
+ requestSpan?.setAttribute('contentLength', contentLength);
327
+ requestSpan?.end();
328
+ if (!txIdRegex.test(txId)) {
329
+ // no transaction id found, skip verification
330
+ logger?.debug('No transaction id found, skipping verification', {
331
+ redirectUrl: redirectUrl.toString(),
332
+ originalUrl: url,
333
+ });
334
+ requestEmitter.emit('verification-skipped', {
335
+ originalUrl: url,
336
+ });
337
+ return response;
338
+ }
339
+ // Check if the response has a body
340
+ if (response.body) {
341
+ const newClientStream = tapAndVerifyReadableStream({
342
+ originalStream: response.body,
343
+ contentLength,
344
+ verifyData: verificationSettings.strategy?.verifyData.bind(verificationSettings.strategy),
345
+ txId,
346
+ emitter: requestEmitter,
347
+ strict: verificationSettings.strict,
348
+ });
349
+ return new Response(newClientStream, {
350
+ status: response.status,
351
+ statusText: response.statusText,
352
+ headers: response.headers,
353
+ });
354
+ }
355
+ else {
356
+ // No response body to verify, skip verification
357
+ logger?.debug('No response body to verify', {
358
+ redirectUrl: redirectUrl.toString(),
359
+ originalUrl: url,
360
+ txId,
361
+ });
362
+ return response;
294
363
  }
295
- return response;
296
364
  }
297
365
  catch (error) {
298
366
  logger?.debug('Failed to route request', {
@@ -341,6 +409,14 @@ export class Wayfinder {
341
409
  * The verification settings to use when verifying data.
342
410
  */
343
411
  verificationSettings;
412
+ /**
413
+ * Telemetry configuration used for OpenTelemetry tracing
414
+ */
415
+ telemetrySettings;
416
+ /**
417
+ * OpenTelemetry tracer instance
418
+ */
419
+ tracer;
344
420
  /**
345
421
  * A helper function that resolves the redirect url for ar:// requests to a target gateway.
346
422
  *
@@ -392,7 +468,13 @@ export class Wayfinder {
392
468
  */
393
469
  logger;
394
470
  /**
395
- * The event emitter for wayfinder that emits routing and verification events.
471
+ * The event emitter for wayfinder that emits routing and verification events for all requests.
472
+ *
473
+ * This is useful for tracking all requests and their statuses, and is updated for each request.
474
+ *
475
+ * If you prefer request-specific events, you can pass in the events to the `request` function.
476
+ *
477
+ * @example
396
478
  *
397
479
  * const wayfinder = new Wayfinder()
398
480
  *
@@ -419,10 +501,8 @@ export class Wayfinder {
419
501
  * console.log('Verification passed!', event);
420
502
  * },
421
503
  * onVerificationFailed: (event) => {
422
- * console.log('Verification failed!', event);
423
- * },
424
- * onVerificationProgress: (event) => {
425
- * console.log('Verification progress!', event);
504
+ * console.log('Verification failed!', event);
505
+ * },
426
506
  * },
427
507
  * }
428
508
  * routingSettings: {
@@ -451,13 +531,9 @@ export class Wayfinder {
451
531
  * @param options - Wayfinder configuration options
452
532
  */
453
533
  constructor({ logger = defaultLogger, gatewaysProvider, // forcing it to be required to avoid making ar-io-sdk a dependency
454
- verificationSettings, routingSettings, }) {
534
+ verificationSettings, routingSettings, telemetrySettings, }) {
455
535
  this.logger = logger;
456
536
  this.gatewaysProvider = gatewaysProvider;
457
- this.emitter = new WayfinderEmitter({
458
- verification: verificationSettings?.events,
459
- routing: routingSettings?.events,
460
- });
461
537
  // default verification settings
462
538
  this.verificationSettings = {
463
539
  enabled: verificationSettings?.enabled ??
@@ -465,46 +541,39 @@ export class Wayfinder {
465
541
  strategy: new HashVerificationStrategy({
466
542
  trustedGateways: [new URL('https://permagate.io')],
467
543
  }),
468
- events: {
469
- onVerificationFailed: (event) => {
470
- this.logger.error('Verification failed', event);
471
- },
472
- onVerificationSucceeded: (event) => {
473
- this.logger.info('Verification succeeded', event);
474
- },
475
- onVerificationProgress: (event) => {
476
- this.logger.info('Verification progress', event);
477
- },
478
- },
479
544
  // overwrite the default settings with the provided ones
480
545
  ...verificationSettings,
481
546
  };
482
547
  // default routing settings
483
548
  this.routingSettings = {
549
+ events: {},
484
550
  strategy: new FastestPingRoutingStrategy({
485
551
  timeoutMs: 1000,
552
+ maxConcurrency: 5, // 5 concurrent HEAD requests on the requested path
486
553
  logger: defaultLogger,
487
554
  }),
488
- events: {
489
- onRoutingStarted: (event) => {
490
- this.logger.debug('Routing started', event);
491
- },
492
- onRoutingSkipped: (event) => {
493
- this.logger.debug('Routing skipped', event);
494
- },
495
- onRoutingSucceeded: (event) => {
496
- this.logger.debug('Routing succeeded', event);
497
- },
498
- },
499
555
  // overwrite the default settings with the provided ones
500
556
  ...routingSettings,
501
557
  };
558
+ this.emitter = new WayfinderEmitter({
559
+ verification: this.verificationSettings?.events,
560
+ routing: this.routingSettings?.events,
561
+ });
562
+ this.telemetrySettings = {
563
+ enabled: telemetrySettings?.enabled ?? false,
564
+ sampleRate: telemetrySettings?.sampleRate ?? 0.1, // 10% sample rate by default
565
+ apiKey: telemetrySettings?.apiKey ?? 'c8gU8dHlu6V7e5k2Gn9LaG', // intentionally left here - if it gets abused we'll disable it
566
+ exporterUrl: telemetrySettings?.exporterUrl ?? 'https://api.honeycomb.io/v1/traces', // TODO: replace with proxy url and remove api key
567
+ serviceName: telemetrySettings?.serviceName ?? 'wayfinder-core',
568
+ };
569
+ this.tracer = initTelemetry(this.telemetrySettings);
502
570
  this.request = wayfinderFetch({
503
571
  logger: this.logger,
504
572
  emitter: this.emitter,
505
573
  gatewaysProvider: this.gatewaysProvider,
506
574
  routingSettings: this.routingSettings,
507
575
  verificationSettings: this.verificationSettings,
576
+ tracer: this.tracer,
508
577
  });
509
578
  this.resolveUrl = async ({ originalUrl, logger = this.logger }) => {
510
579
  const selectedGateway = await this.routingSettings.strategy.selectGateway({
@@ -516,6 +585,10 @@ export class Wayfinder {
516
585
  logger,
517
586
  });
518
587
  };
519
- this.logger.debug(`Wayfinder initialized with ${this.routingSettings.strategy?.constructor.name} routing strategy`);
588
+ this.logger.debug('Wayfinder initialized', {
589
+ verificationSettings: this.verificationSettings,
590
+ routingSettings: this.routingSettings,
591
+ telemetrySettings: this.telemetrySettings,
592
+ });
520
593
  }
521
594
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ar.io/wayfinder-core",
3
- "version": "0.0.5-alpha.2",
3
+ "version": "0.0.5-alpha.4",
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",
@@ -49,6 +49,11 @@
49
49
  },
50
50
  "dependencies": {
51
51
  "@dha-team/arbundles": "^1.0.3",
52
+ "@opentelemetry/api": "^1.9.0",
53
+ "@opentelemetry/exporter-trace-otlp-http": "^0.202.0",
54
+ "@opentelemetry/sdk-trace-base": "^2.0.1",
55
+ "@opentelemetry/sdk-trace-node": "^2.0.1",
56
+ "@opentelemetry/sdk-trace-web": "^2.0.1",
52
57
  "arweave": "^1.14.0",
53
58
  "eventemitter3": "^5.0.1",
54
59
  "plimit-lit": "^3.0.1",