@ar.io/sdk 3.12.0-beta.4 → 3.12.0-beta.6
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/bundles/web.bundle.min.js +111 -111
- package/lib/cjs/common/wayfinder/verification/trusted.js +26 -10
- package/lib/cjs/common/wayfinder/wayfinder.js +15 -2
- package/lib/cjs/version.js +1 -1
- package/lib/esm/common/wayfinder/verification/trusted.js +26 -10
- package/lib/esm/common/wayfinder/wayfinder.js +14 -2
- package/lib/esm/version.js +1 -1
- package/lib/types/common/wayfinder/wayfinder.d.ts +4 -0
- package/lib/types/version.d.ts +1 -1
- package/package.json +2 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TrustedGatewaysHashProvider = void 0;
|
|
4
|
+
const wayfinder_js_1 = require("../wayfinder.js");
|
|
4
5
|
const arioGatewayHeaders = {
|
|
5
6
|
digest: 'x-ar-io-digest',
|
|
6
7
|
verified: 'x-ar-io-verified',
|
|
@@ -25,17 +26,32 @@ class TrustedGatewaysHashProvider {
|
|
|
25
26
|
const hashResults = [];
|
|
26
27
|
const gateways = await this.gatewaysProvider.getGateways();
|
|
27
28
|
const hashes = await Promise.all(gateways.map(async (gateway) => {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
const sandbox = (0, wayfinder_js_1.sandboxFromId)(txId);
|
|
30
|
+
const urlWithSandbox = `${gateway.protocol}//${sandbox}.${gateway.hostname}/${txId}/`;
|
|
31
|
+
let txIdHash;
|
|
32
|
+
/**
|
|
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
|
+
* 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
|
+
* For now, we'll just do a GET request to hydrate the cache if the HEAD request doesn't contain the digest.
|
|
36
|
+
*/
|
|
37
|
+
for (const method of ['HEAD', 'GET']) {
|
|
38
|
+
const response = await fetch(urlWithSandbox, {
|
|
39
|
+
method,
|
|
40
|
+
redirect: 'follow',
|
|
41
|
+
mode: 'cors',
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok) {
|
|
44
|
+
// skip this gateway if the request failed
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
const fetchedTxIdHash = response.headers.get(arioGatewayHeaders.digest);
|
|
48
|
+
if (fetchedTxIdHash !== null && fetchedTxIdHash !== undefined) {
|
|
49
|
+
txIdHash = fetchedTxIdHash;
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
35
52
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// skip this gateway
|
|
53
|
+
if (txIdHash === undefined) {
|
|
54
|
+
// skip this gateway if we didn't get a hash
|
|
39
55
|
return undefined;
|
|
40
56
|
}
|
|
41
57
|
hashResults.push({
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.Wayfinder = exports.createWayfinderClient = exports.WayfinderEmitter = exports.resolveWayfinderUrl = exports.txIdRegex = exports.arnsRegex = void 0;
|
|
7
7
|
exports.tapAndVerifyStream = tapAndVerifyStream;
|
|
8
|
+
exports.sandboxFromId = sandboxFromId;
|
|
8
9
|
/**
|
|
9
10
|
* Copyright (C) 2022-2024 Permanent Data Solutions, Inc.
|
|
10
11
|
*
|
|
@@ -22,6 +23,7 @@ exports.tapAndVerifyStream = tapAndVerifyStream;
|
|
|
22
23
|
*/
|
|
23
24
|
const node_events_1 = __importDefault(require("node:events"));
|
|
24
25
|
const node_stream_1 = require("node:stream");
|
|
26
|
+
const rfc4648_1 = require("rfc4648");
|
|
25
27
|
const io_js_1 = require("../io.js");
|
|
26
28
|
const logger_js_1 = require("../logger.js");
|
|
27
29
|
const network_js_1 = require("./gateways/network.js");
|
|
@@ -57,7 +59,8 @@ const resolveWayfinderUrl = ({ originalUrl, selectedGateway, logger, }) => {
|
|
|
57
59
|
const [firstPart, ...rest] = path.split('/');
|
|
58
60
|
// TODO: this breaks 43 character named arns names - we should check a a local name cache list before resolving raw transaction ids
|
|
59
61
|
if (exports.txIdRegex.test(firstPart)) {
|
|
60
|
-
|
|
62
|
+
const sandbox = sandboxFromId(firstPart);
|
|
63
|
+
return new URL(`${firstPart}${rest.length > 0 ? '/' + rest.join('/') : ''}`, `${selectedGateway.protocol}//${sandbox}.${selectedGateway.hostname}`);
|
|
61
64
|
}
|
|
62
65
|
if (exports.arnsRegex.test(firstPart)) {
|
|
63
66
|
// TODO: tests to ensure arns names support query params and paths
|
|
@@ -245,6 +248,14 @@ function tapAndVerifyStream({ originalStream, contentLength, verifyData, txId, e
|
|
|
245
248
|
}
|
|
246
249
|
throw new Error('Unsupported body type for cloning');
|
|
247
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Gets the sandbox hash for a given transaction id
|
|
253
|
+
*/
|
|
254
|
+
function sandboxFromId(id) {
|
|
255
|
+
return rfc4648_1.base32
|
|
256
|
+
.stringify(Buffer.from(id, 'base64'), { pad: false })
|
|
257
|
+
.toLowerCase();
|
|
258
|
+
}
|
|
248
259
|
/**
|
|
249
260
|
* Creates a wrapped fetch function that supports ar:// protocol
|
|
250
261
|
*
|
|
@@ -305,9 +316,11 @@ const createWayfinderClient = ({ resolveUrl, verifyData, selectGateway, emitter
|
|
|
305
316
|
});
|
|
306
317
|
// Make the request to the target gateway
|
|
307
318
|
const response = await fetch(redirectUrl.toString(), {
|
|
308
|
-
...init,
|
|
309
319
|
// follow redirects as gateways use sandboxing on /txId requests
|
|
310
320
|
redirect: 'follow',
|
|
321
|
+
mode: 'cors',
|
|
322
|
+
// allow requestor to override and any additional request configuration
|
|
323
|
+
...init,
|
|
311
324
|
});
|
|
312
325
|
// TODO: update any caching we use for the request and gateway response
|
|
313
326
|
logger?.debug(`Successfully routed request to gateway`, {
|
package/lib/cjs/version.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sandboxFromId } from '../wayfinder.js';
|
|
1
2
|
const arioGatewayHeaders = {
|
|
2
3
|
digest: 'x-ar-io-digest',
|
|
3
4
|
verified: 'x-ar-io-verified',
|
|
@@ -22,17 +23,32 @@ export class TrustedGatewaysHashProvider {
|
|
|
22
23
|
const hashResults = [];
|
|
23
24
|
const gateways = await this.gatewaysProvider.getGateways();
|
|
24
25
|
const hashes = await Promise.all(gateways.map(async (gateway) => {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
const sandbox = sandboxFromId(txId);
|
|
27
|
+
const urlWithSandbox = `${gateway.protocol}//${sandbox}.${gateway.hostname}/${txId}/`;
|
|
28
|
+
let txIdHash;
|
|
29
|
+
/**
|
|
30
|
+
* 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.
|
|
31
|
+
* 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.
|
|
32
|
+
* For now, we'll just do a GET request to hydrate the cache if the HEAD request doesn't contain the digest.
|
|
33
|
+
*/
|
|
34
|
+
for (const method of ['HEAD', 'GET']) {
|
|
35
|
+
const response = await fetch(urlWithSandbox, {
|
|
36
|
+
method,
|
|
37
|
+
redirect: 'follow',
|
|
38
|
+
mode: 'cors',
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
// skip this gateway if the request failed
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
const fetchedTxIdHash = response.headers.get(arioGatewayHeaders.digest);
|
|
45
|
+
if (fetchedTxIdHash !== null && fetchedTxIdHash !== undefined) {
|
|
46
|
+
txIdHash = fetchedTxIdHash;
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
32
49
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// skip this gateway
|
|
50
|
+
if (txIdHash === undefined) {
|
|
51
|
+
// skip this gateway if we didn't get a hash
|
|
36
52
|
return undefined;
|
|
37
53
|
}
|
|
38
54
|
hashResults.push({
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import EventEmitter from 'node:events';
|
|
17
17
|
import { PassThrough, Readable } from 'node:stream';
|
|
18
|
+
import { base32 } from 'rfc4648';
|
|
18
19
|
import { ARIO } from '../io.js';
|
|
19
20
|
import { Logger } from '../logger.js';
|
|
20
21
|
import { NetworkGatewaysProvider } from './gateways/network.js';
|
|
@@ -50,7 +51,8 @@ export const resolveWayfinderUrl = ({ originalUrl, selectedGateway, logger, }) =
|
|
|
50
51
|
const [firstPart, ...rest] = path.split('/');
|
|
51
52
|
// TODO: this breaks 43 character named arns names - we should check a a local name cache list before resolving raw transaction ids
|
|
52
53
|
if (txIdRegex.test(firstPart)) {
|
|
53
|
-
|
|
54
|
+
const sandbox = sandboxFromId(firstPart);
|
|
55
|
+
return new URL(`${firstPart}${rest.length > 0 ? '/' + rest.join('/') : ''}`, `${selectedGateway.protocol}//${sandbox}.${selectedGateway.hostname}`);
|
|
54
56
|
}
|
|
55
57
|
if (arnsRegex.test(firstPart)) {
|
|
56
58
|
// TODO: tests to ensure arns names support query params and paths
|
|
@@ -236,6 +238,14 @@ export function tapAndVerifyStream({ originalStream, contentLength, verifyData,
|
|
|
236
238
|
}
|
|
237
239
|
throw new Error('Unsupported body type for cloning');
|
|
238
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Gets the sandbox hash for a given transaction id
|
|
243
|
+
*/
|
|
244
|
+
export function sandboxFromId(id) {
|
|
245
|
+
return base32
|
|
246
|
+
.stringify(Buffer.from(id, 'base64'), { pad: false })
|
|
247
|
+
.toLowerCase();
|
|
248
|
+
}
|
|
239
249
|
/**
|
|
240
250
|
* Creates a wrapped fetch function that supports ar:// protocol
|
|
241
251
|
*
|
|
@@ -296,9 +306,11 @@ export const createWayfinderClient = ({ resolveUrl, verifyData, selectGateway, e
|
|
|
296
306
|
});
|
|
297
307
|
// Make the request to the target gateway
|
|
298
308
|
const response = await fetch(redirectUrl.toString(), {
|
|
299
|
-
...init,
|
|
300
309
|
// follow redirects as gateways use sandboxing on /txId requests
|
|
301
310
|
redirect: 'follow',
|
|
311
|
+
mode: 'cors',
|
|
312
|
+
// allow requestor to override and any additional request configuration
|
|
313
|
+
...init,
|
|
302
314
|
});
|
|
303
315
|
// TODO: update any caching we use for the request and gateway response
|
|
304
316
|
logger?.debug(`Successfully routed request to gateway`, {
|
package/lib/esm/version.js
CHANGED
|
@@ -98,6 +98,10 @@ export declare function tapAndVerifyStream<T extends Readable | ReadableStream>(
|
|
|
98
98
|
emitter?: WayfinderEmitter;
|
|
99
99
|
strict?: boolean;
|
|
100
100
|
}): T extends Readable ? PassThrough : T;
|
|
101
|
+
/**
|
|
102
|
+
* Gets the sandbox hash for a given transaction id
|
|
103
|
+
*/
|
|
104
|
+
export declare function sandboxFromId(id: string): string;
|
|
101
105
|
/**
|
|
102
106
|
* Creates a wrapped fetch function that supports ar:// protocol
|
|
103
107
|
*
|
package/lib/types/version.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ar.io/sdk",
|
|
3
|
-
"version": "3.12.0-beta.
|
|
3
|
+
"version": "3.12.0-beta.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/ar-io/ar-io-sdk.git"
|
|
@@ -136,6 +136,7 @@
|
|
|
136
136
|
"eventemitter3": "^5.0.1",
|
|
137
137
|
"plimit-lit": "^3.0.1",
|
|
138
138
|
"prompts": "^2.4.2",
|
|
139
|
+
"rfc4648": "^1.5.4",
|
|
139
140
|
"winston": "^3.13.0",
|
|
140
141
|
"zod": "^3.23.8"
|
|
141
142
|
},
|