@ar.io/sdk 3.12.0-beta.6 → 3.12.0-beta.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,8 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DataRootVerificationStrategy = exports.convertReadableToDataRoot = void 0;
7
- exports.convertBufferToDataRoot = convertBufferToDataRoot;
6
+ exports.DataRootVerificationStrategy = exports.convertDataStreamToDataRoot = void 0;
8
7
  /**
9
8
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
10
9
  *
@@ -22,39 +21,16 @@ exports.convertBufferToDataRoot = convertBufferToDataRoot;
22
21
  */
23
22
  const arweave_1 = __importDefault(require("arweave"));
24
23
  const merkle_js_1 = require("arweave/node/lib/merkle.js");
25
- const node_stream_1 = require("node:stream");
26
24
  const base64_js_1 = require("../../../../utils/base64.js");
27
- async function convertBufferToDataRoot({ buffer, }) {
28
- const chunks = [];
29
- let cursor = 0;
30
- let offset = 0;
31
- while (offset < buffer.byteLength) {
32
- let chunkSize = Math.min(merkle_js_1.MAX_CHUNK_SIZE, buffer.byteLength - offset);
33
- const remainder = buffer.byteLength - offset - chunkSize;
34
- if (remainder > 0 && remainder < merkle_js_1.MIN_CHUNK_SIZE) {
35
- chunkSize = Math.ceil((buffer.byteLength - offset) / 2);
36
- }
37
- // subarray does not exist on web Buffer type
38
- const slice = buffer.subarray(offset, offset + chunkSize);
39
- const hash = await crypto.subtle.digest('SHA-256', slice);
40
- const hashArray = new Uint8Array(hash);
41
- chunks.push({
42
- dataHash: hashArray,
43
- minByteRange: cursor,
44
- maxByteRange: cursor + chunkSize,
45
- });
46
- cursor += chunkSize;
47
- offset += chunkSize;
48
- }
49
- const leaves = await (0, merkle_js_1.generateLeaves)(chunks);
50
- const result = await (0, merkle_js_1.buildLayers)(leaves);
51
- return Buffer.from(result.id).toString('base64url');
52
- }
53
- const convertReadableToDataRoot = async ({ iterable, }) => {
25
+ const hash_js_1 = require("../../../../utils/hash.js");
26
+ const convertDataStreamToDataRoot = async ({ dataStream, }) => {
54
27
  const chunks = [];
55
28
  let leftover = new Uint8Array(0);
56
29
  let cursor = 0;
57
- for await (const data of iterable) {
30
+ const asyncIterable = (0, hash_js_1.isAsyncIterable)(dataStream)
31
+ ? dataStream
32
+ : (0, hash_js_1.readableStreamToAsyncIterable)(dataStream);
33
+ for await (const data of asyncIterable) {
58
34
  const inputChunk = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
59
35
  const combined = new Uint8Array(leftover.length + inputChunk.length);
60
36
  combined.set(leftover, 0);
@@ -79,6 +55,7 @@ const convertReadableToDataRoot = async ({ iterable, }) => {
79
55
  leftover = combined.slice(startIndex);
80
56
  }
81
57
  if (leftover.length > 0) {
58
+ // TODO: ensure a web friendly crypto hash function is used in web
82
59
  const dataHash = await arweave_1.default.crypto.hash(leftover);
83
60
  chunks.push({
84
61
  dataHash,
@@ -90,27 +67,21 @@ const convertReadableToDataRoot = async ({ iterable, }) => {
90
67
  const root = await (0, merkle_js_1.buildLayers)(leaves);
91
68
  return (0, base64_js_1.toB64Url)(Buffer.from(root.id));
92
69
  };
93
- exports.convertReadableToDataRoot = convertReadableToDataRoot;
70
+ exports.convertDataStreamToDataRoot = convertDataStreamToDataRoot;
94
71
  class DataRootVerificationStrategy {
95
72
  trustedDataRootProvider;
96
73
  constructor({ trustedDataRootProvider, }) {
97
74
  this.trustedDataRootProvider = trustedDataRootProvider;
98
75
  }
99
76
  async verifyData({ data, txId, }) {
100
- const trustedDataRootPromise = this.trustedDataRootProvider.getDataRoot({
101
- txId,
102
- });
103
- let computedDataRoot;
104
- if (Buffer.isBuffer(data)) {
105
- computedDataRoot = await convertBufferToDataRoot({ buffer: data });
106
- }
107
- else if (data instanceof node_stream_1.Readable || data instanceof ReadableStream) {
108
- computedDataRoot = await (0, exports.convertReadableToDataRoot)({ iterable: data });
109
- }
110
- if (computedDataRoot === undefined) {
111
- throw new Error('Data root could not be computed');
112
- }
113
- const trustedDataRoot = await trustedDataRootPromise;
77
+ const [computedDataRoot, trustedDataRoot] = await Promise.all([
78
+ (0, exports.convertDataStreamToDataRoot)({
79
+ dataStream: data,
80
+ }),
81
+ this.trustedDataRootProvider.getDataRoot({
82
+ txId,
83
+ }),
84
+ ]);
114
85
  if (computedDataRoot !== trustedDataRoot) {
115
86
  throw new Error('Data root does not match', {
116
87
  cause: { computedDataRoot, trustedDataRoot },
@@ -1,22 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HashVerificationStrategy = void 0;
4
- /**
5
- * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
6
- *
7
- * Licensed under the Apache License, Version 2.0 (the "License");
8
- * you may not use this file except in compliance with the License.
9
- * You may obtain a copy of the License at
10
- *
11
- * http://www.apache.org/licenses/LICENSE-2.0
12
- *
13
- * Unless required by applicable law or agreed to in writing, software
14
- * distributed under the License is distributed on an "AS IS" BASIS,
15
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- * See the License for the specific language governing permissions and
17
- * limitations under the License.
18
- */
19
- const node_stream_1 = require("node:stream");
20
4
  const hash_js_1 = require("../../../../utils/hash.js");
21
5
  class HashVerificationStrategy {
22
6
  trustedHashProvider;
@@ -24,25 +8,18 @@ class HashVerificationStrategy {
24
8
  this.trustedHashProvider = trustedHashProvider;
25
9
  }
26
10
  async verifyData({ data, txId, }) {
27
- const hashPromise = this.trustedHashProvider.getHash({ txId });
28
- let computedHash;
29
- if (Buffer.isBuffer(data)) {
30
- computedHash = (0, hash_js_1.hashBufferToB64Url)(data);
31
- }
32
- else if (data instanceof node_stream_1.Readable) {
33
- computedHash = await (0, hash_js_1.hashReadableToB64Url)(data);
34
- }
35
- else if (data instanceof ReadableStream) {
36
- computedHash = await (0, hash_js_1.hashReadableStreamToB64Url)(data);
37
- }
11
+ // kick off the hash computation, but don't wait for it until we compute our own hash
12
+ const [computedHash, fetchedHash] = await Promise.all([
13
+ (0, hash_js_1.hashDataStreamToB64Url)(data),
14
+ this.trustedHashProvider.getHash({ txId }),
15
+ ]);
38
16
  // await on the hash promise and compare to get a little concurrency when computing hashes over larger data
39
- const { hash } = await hashPromise;
40
17
  if (computedHash === undefined) {
41
18
  throw new Error('Hash could not be computed');
42
19
  }
43
- if (computedHash !== hash) {
20
+ if (computedHash !== fetchedHash.hash) {
44
21
  throw new Error('Hash does not match', {
45
- cause: { computedHash, trustedHash: hash },
22
+ cause: { computedHash, trustedHash: fetchedHash },
46
23
  });
47
24
  }
48
25
  }
@@ -27,14 +27,14 @@ class TrustedGatewaysHashProvider {
27
27
  const gateways = await this.gatewaysProvider.getGateways();
28
28
  const hashes = await Promise.all(gateways.map(async (gateway) => {
29
29
  const sandbox = (0, wayfinder_js_1.sandboxFromId)(txId);
30
- const urlWithSandbox = `${gateway.protocol}//${sandbox}.${gateway.hostname}/${txId}/`;
30
+ const urlWithSandbox = `${gateway.protocol}//${sandbox}.${gateway.hostname}/${txId}`;
31
31
  let txIdHash;
32
32
  /**
33
33
  * This is a problem because we're not able to verify the hash of the data item if the gateway doesn't have the data in its cache.
34
34
  * We should add the ability to send a HEAD request to trigger a GET request to hydrate the cache on the trusted gateway via a header.
35
35
  * For now, we'll just do a GET request to hydrate the cache if the HEAD request doesn't contain the digest.
36
36
  */
37
- for (const method of ['HEAD', 'GET']) {
37
+ for (const method of ['HEAD', 'GET', 'GET']) {
38
38
  const response = await fetch(urlWithSandbox, {
39
39
  method,
40
40
  redirect: 'follow',
@@ -1,10 +1,7 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.Wayfinder = exports.createWayfinderClient = exports.WayfinderEmitter = exports.resolveWayfinderUrl = exports.txIdRegex = exports.arnsRegex = void 0;
7
- exports.tapAndVerifyStream = tapAndVerifyStream;
4
+ exports.tapAndVerifyReadableStream = tapAndVerifyReadableStream;
8
5
  exports.sandboxFromId = sandboxFromId;
9
6
  /**
10
7
  * Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
@@ -21,8 +18,7 @@ exports.sandboxFromId = sandboxFromId;
21
18
  * See the License for the specific language governing permissions and
22
19
  * limitations under the License.
23
20
  */
24
- const node_events_1 = __importDefault(require("node:events"));
25
- const node_stream_1 = require("node:stream");
21
+ const eventemitter3_1 = require("eventemitter3");
26
22
  const rfc4648_1 = require("rfc4648");
27
23
  const io_js_1 = require("../io.js");
28
24
  const logger_js_1 = require("../logger.js");
@@ -72,22 +68,20 @@ const resolveWayfinderUrl = ({ originalUrl, selectedGateway, logger, }) => {
72
68
  return new URL(rest.length > 0 ? rest.join('/') : '', arnsUrl);
73
69
  }
74
70
  // TODO: support .eth addresses
75
- // TODO: "gasless" routing via DNS TXT records (e.g. ar://gatewaypie.com -> TXT record lookup for TX ID and redirect to that gateway)
71
+ // TODO: "gasless" routing via DNS TXT records
76
72
  }
77
73
  logger?.debug('No wayfinder routing protocol applied', {
78
74
  originalUrl,
79
75
  });
80
- // return the original url if it's not a wayfinder url (allows you to use the wayfinder client with non-wayfinder urls)
76
+ // return the original url if it's not a wayfinder url
81
77
  return new URL(originalUrl);
82
78
  };
83
79
  exports.resolveWayfinderUrl = resolveWayfinderUrl;
84
- class WayfinderEmitter extends node_events_1.default {
85
- constructor({ onVerificationPassed, onVerificationFailed, onVerificationProgress,
86
- // TODO: continue this pattern for all events
87
- } = {}) {
80
+ class WayfinderEmitter extends eventemitter3_1.EventEmitter {
81
+ constructor({ onVerificationSucceeded, onVerificationFailed, onVerificationProgress, } = {}) {
88
82
  super();
89
- if (onVerificationPassed) {
90
- this.on('verification-succeeded', onVerificationPassed);
83
+ if (onVerificationSucceeded) {
84
+ this.on('verification-succeeded', onVerificationSucceeded);
91
85
  }
92
86
  if (onVerificationFailed) {
93
87
  this.on('verification-failed', onVerificationFailed);
@@ -96,84 +90,18 @@ class WayfinderEmitter extends node_events_1.default {
96
90
  this.on('verification-progress', onVerificationProgress);
97
91
  }
98
92
  }
99
- emit(event, payload) {
100
- return super.emit(event, payload);
101
- }
102
- on(event, listener) {
103
- return super.on(event, listener);
104
- }
105
93
  }
106
94
  exports.WayfinderEmitter = WayfinderEmitter;
107
- function tapAndVerifyStream({ originalStream, contentLength, verifyData, txId, emitter, strict = false, }) {
108
- // taps node streams
109
- if (originalStream instanceof node_stream_1.Readable &&
110
- typeof originalStream.pipe === 'function') {
111
- const tappedClientStream = new node_stream_1.PassThrough();
112
- const streamToVerify = new node_stream_1.PassThrough();
113
- // kick off the verification promise, this will be awaited when the original stream ends
114
- const verificationPromise = verifyData({
115
- data: streamToVerify,
116
- txId,
117
- });
118
- let bytesProcessed = 0;
119
- // pipe the original stream to the verifier and the client stream
120
- originalStream.on('data', (chunk) => {
121
- streamToVerify.write(chunk);
122
- tappedClientStream.write(chunk);
123
- bytesProcessed += chunk.length;
124
- // only emit if contentLength is not 0
125
- if (contentLength !== 0) {
126
- emitter?.emit('verification-progress', {
127
- txId,
128
- totalBytes: contentLength,
129
- processedBytes: bytesProcessed,
130
- });
131
- }
132
- });
133
- originalStream.on('end', async () => {
134
- streamToVerify.end(); // triggers verifier completion and completes the verification promise
135
- if (strict) {
136
- // in strict mode, we wait for verification to complete before ending the client stream
137
- try {
138
- await verificationPromise;
139
- emitter?.emit('verification-succeeded', { txId });
140
- tappedClientStream.end();
141
- }
142
- catch (error) {
143
- emitter?.emit('verification-failed', { error, txId });
144
- // In strict mode, destroy the client stream with the error
145
- tappedClientStream.destroy(new Error('Verification failed', { cause: error }));
146
- }
147
- }
148
- else {
149
- // in non-strict mode, we end the client stream immediately and handle verification asynchronously
150
- tappedClientStream.end();
151
- // trigger the verification promise and emit events for the result
152
- verificationPromise
153
- .then(() => {
154
- emitter?.emit('verification-succeeded', { txId });
155
- })
156
- .catch((error) => {
157
- emitter?.emit('verification-failed', { error, txId });
158
- });
159
- }
160
- });
161
- originalStream.on('error', (err) => {
162
- // emit the verification failed event
163
- emitter?.emit('verification-failed', {
164
- error: err,
165
- txId,
166
- });
167
- // destroy both streams and propagate the original stream error
168
- streamToVerify.destroy(err);
169
- tappedClientStream.destroy(err);
170
- });
171
- // send the stream to the verify function and if it errors end the client stream
172
- return tappedClientStream;
173
- }
174
- // taps web readable streams
95
+ function tapAndVerifyReadableStream({ originalStream, contentLength, verifyData, txId, emitter, strict = false, }) {
175
96
  if (originalStream instanceof ReadableStream &&
176
97
  typeof originalStream.tee === 'function') {
98
+ /**
99
+ * NOTE: tee requires the streams both streams to be consumed, so we need to make sure we consume the client branch
100
+ * by the caller. This means when `request` is called, the client stream must be consumed by the caller via await request.text()
101
+ * for verification to complete.
102
+ *
103
+ * It is feasible to make the verification stream not to depend on the client branch being consumed, should the DX not be obvious.
104
+ */
177
105
  const [verifyBranch, clientBranch] = originalStream.tee();
178
106
  // setup our promise to verify the data
179
107
  const verificationPromise = verifyData({
@@ -194,10 +122,8 @@ function tapAndVerifyStream({ originalStream, contentLength, verifyData, txId, e
194
122
  controller.close();
195
123
  }
196
124
  catch (err) {
197
- emitter?.emit('verification-failed', {
198
- txId,
199
- error: err,
200
- });
125
+ // emit the verification failed event
126
+ emitter?.emit('verification-failed', err);
201
127
  // In strict mode, we report the error to the client stream
202
128
  controller.error(new Error('Verification failed', { cause: err }));
203
129
  }
@@ -209,10 +135,7 @@ function tapAndVerifyStream({ originalStream, contentLength, verifyData, txId, e
209
135
  emitter?.emit('verification-succeeded', { txId });
210
136
  })
211
137
  .catch((error) => {
212
- emitter?.emit('verification-failed', {
213
- txId,
214
- error,
215
- });
138
+ emitter?.emit('verification-failed', error);
216
139
  });
217
140
  // in non-strict mode, we close the controller immediately and handle verification asynchronously
218
141
  controller.close();
@@ -240,8 +163,6 @@ function tapAndVerifyStream({ originalStream, contentLength, verifyData, txId, e
240
163
  },
241
164
  }),
242
165
  });
243
- // note: we don't block or throw errors here even in strict mode
244
- // since the stream is already being cancelled by the client
245
166
  },
246
167
  });
247
168
  return clientStreamWithVerification;
@@ -268,146 +189,128 @@ function sandboxFromId(id) {
268
189
  * @returns a wrapped fetch function that supports ar:// protocol and always returns Response
269
190
  */
270
191
  const createWayfinderClient = ({ resolveUrl, verifyData, selectGateway, emitter = new WayfinderEmitter(), logger, strict = false, }) => {
271
- // Create a function that will handle the redirection logic for ar:// URLs
272
- const wayfinderRedirect = async (url, init) => {
273
- // If the url is not a string or URL (e.g., it's a Request object), extract the URL from it
274
- const originalUrl = url instanceof Request ? url.url : url.toString();
275
- // If it's not an ar:// URL, pass it directly to fetch
276
- if (!originalUrl.startsWith('ar://')) {
277
- logger?.debug('Not a wayfinder URL, passing to fetch directly', {
278
- originalUrl,
192
+ return async (url, init) => {
193
+ if (typeof url !== 'string') {
194
+ logger?.debug('URL is not a string, skipping routing', {
195
+ url,
279
196
  });
280
197
  emitter?.emit('routing-skipped', {
281
- originalUrl,
198
+ originalUrl: JSON.stringify(url),
282
199
  });
283
- // we don't do anything special with non-ar:// urls, just pass them through
284
200
  return fetch(url, init);
285
201
  }
286
- // Start the routing process
287
202
  emitter?.emit('routing-started', {
288
- originalUrl,
203
+ originalUrl: url.toString(),
289
204
  });
290
- // Retry logic for gateway selection
291
205
  const maxRetries = 3;
292
206
  const retryDelay = 1000;
293
207
  for (let i = 0; i < maxRetries; i++) {
294
208
  try {
295
209
  // select the target gateway
296
- // TODO: we may want to provide the `path` to select gateway so the HEAD checks in routers check the existence of the actual path/request
297
210
  const selectedGateway = await selectGateway();
298
211
  logger?.debug('Selected gateway', {
299
- originalUrl,
212
+ originalUrl: url,
300
213
  selectedGateway: selectedGateway.toString(),
301
214
  });
302
215
  // route the request to the target gateway
303
216
  const redirectUrl = resolveUrl({
304
- originalUrl,
217
+ originalUrl: url,
305
218
  selectedGateway,
306
219
  logger,
307
220
  });
308
221
  emitter?.emit('routing-succeeded', {
309
- originalUrl,
222
+ originalUrl: url,
310
223
  selectedGateway: selectedGateway.toString(),
311
224
  redirectUrl: redirectUrl.toString(),
312
225
  });
313
226
  logger?.debug(`Redirecting request`, {
314
- originalUrl,
227
+ originalUrl: url,
315
228
  redirectUrl: redirectUrl.toString(),
316
229
  });
317
- // Make the request to the target gateway
230
+ // make the request to the target gateway using the redirect url
318
231
  const response = await fetch(redirectUrl.toString(), {
319
- // follow redirects as gateways use sandboxing on /txId requests
232
+ // enforce CORS given we're likely going to a different origin, but always allow the client to override
320
233
  redirect: 'follow',
321
234
  mode: 'cors',
322
- // allow requestor to override and any additional request configuration
323
235
  ...init,
324
236
  });
325
- // TODO: update any caching we use for the request and gateway response
326
237
  logger?.debug(`Successfully routed request to gateway`, {
327
238
  redirectUrl: redirectUrl.toString(),
328
- originalUrl,
239
+ originalUrl: url.toString(),
329
240
  });
330
- // return the response right away if no redirect was made
331
- if (redirectUrl.toString() === originalUrl) {
332
- return response;
333
- }
334
- // return the response right away if no verification is needed or if there is no body
335
- if (!verifyData) {
336
- return response;
337
- }
338
- // the txId is either in the response headers or the path of the request as the first parameter
339
- const txId = response.headers.get('x-arns-resolved-id') ??
340
- redirectUrl.pathname.split('/')[1];
341
- const contentLength = +(response.headers.get('content-length') ?? 0);
342
- if (!exports.txIdRegex.test(txId)) {
343
- // No transaction ID found, skip verification
344
- logger?.debug('No transaction ID found, skipping verification', {
345
- redirectUrl: redirectUrl.toString(),
346
- originalUrl,
347
- });
348
- emitter?.emit('verification-skipped', {
349
- originalUrl,
350
- });
351
- return response;
352
- }
353
- emitter?.emit('identified-transaction-id', {
354
- originalUrl,
355
- selectedGateway: redirectUrl.toString(),
356
- txId,
357
- });
358
- if (!response.body) {
359
- logger?.debug('No body, skipping verification', {
360
- redirectUrl: redirectUrl.toString(),
361
- originalUrl,
362
- });
363
- emitter?.emit('verification-skipped', {
364
- originalUrl,
365
- });
366
- return response;
241
+ // only verify data if the redirect url is different from the original url
242
+ if (redirectUrl.toString() !== url.toString()) {
243
+ if (verifyData) {
244
+ const headers = response.headers;
245
+ // transaction id is either in the response headers or the path of the request as the first parameter
246
+ const txId = headers.get('x-arns-resolved-id') ??
247
+ redirectUrl.pathname.split('/')[1];
248
+ const contentLength = +(headers.get('content-length') ?? 0);
249
+ if (!exports.txIdRegex.test(txId)) {
250
+ // no transaction id found, skip verification
251
+ logger?.debug('No transaction id found, skipping verification', {
252
+ redirectUrl: redirectUrl.toString(),
253
+ originalUrl: url,
254
+ });
255
+ emitter?.emit('verification-skipped', {
256
+ originalUrl: url,
257
+ });
258
+ return response;
259
+ }
260
+ emitter?.emit('identified-transaction-id', {
261
+ originalUrl: url,
262
+ selectedGateway: redirectUrl.toString(),
263
+ txId,
264
+ });
265
+ // Check if the response has a body
266
+ if (response.body) {
267
+ const newClientStream = tapAndVerifyReadableStream({
268
+ originalStream: response.body,
269
+ contentLength,
270
+ verifyData,
271
+ txId,
272
+ emitter,
273
+ strict,
274
+ });
275
+ return new Response(newClientStream, {
276
+ status: response.status,
277
+ statusText: response.statusText,
278
+ headers: response.headers,
279
+ });
280
+ }
281
+ else {
282
+ // No response body to verify, skip verification
283
+ logger?.debug('No response body to verify', {
284
+ redirectUrl: redirectUrl.toString(),
285
+ originalUrl: url,
286
+ txId,
287
+ });
288
+ return response;
289
+ }
290
+ }
367
291
  }
368
- const verifiedStream = tapAndVerifyStream({
369
- originalStream: response.body,
370
- contentLength,
371
- verifyData,
372
- txId,
373
- emitter,
374
- strict,
375
- });
376
- // wrap the response with the verified stream
377
- return new Response(verifiedStream, {
378
- status: response.status,
379
- statusText: response.statusText,
380
- // TODO: we could add identified transaction id to the headers here, but it would be changing information from the original response
381
- headers: response.headers,
382
- });
292
+ return response;
383
293
  }
384
294
  catch (error) {
385
295
  logger?.debug('Failed to route request', {
386
296
  error: error.message,
387
297
  stack: error.stack,
388
- originalUrl,
298
+ originalUrl: url,
389
299
  attempt: i + 1,
390
300
  maxRetries,
391
301
  });
392
302
  if (i < maxRetries - 1) {
393
303
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
394
304
  }
395
- else {
396
- emitter?.emit('routing-failed', {
397
- originalUrl,
398
- error,
399
- });
400
- }
401
305
  }
402
306
  }
403
307
  throw new Error('Failed to route request after max retries', {
404
308
  cause: {
405
- originalUrl,
309
+ originalUrl: url,
406
310
  maxRetries,
407
311
  },
408
312
  });
409
313
  };
410
- return wayfinderRedirect;
411
314
  };
412
315
  exports.createWayfinderClient = createWayfinderClient;
413
316
  /**
@@ -559,7 +462,7 @@ class Wayfinder {
559
462
  }),
560
463
  }),
561
464
  }), events = {
562
- onVerificationPassed: (event) => {
465
+ onVerificationSucceeded: (event) => {
563
466
  logger.debug('Verification passed!', event);
564
467
  },
565
468
  onVerificationFailed: (event) => {
@@ -568,7 +471,7 @@ class Wayfinder {
568
471
  onVerificationProgress: (event) => {
569
472
  logger.debug('Verification progress!', event);
570
473
  },
571
- }, strict = false, }) {
474
+ }, strict = false, } = {}) {
572
475
  this.routingStrategy = routingStrategy;
573
476
  this.gatewaysProvider = gatewaysProvider;
574
477
  this.emitter = new WayfinderEmitter(events);