@ardrive/turbo-sdk 1.36.0 → 1.37.0-alpha.2
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 +15 -0
- package/bundles/web.bundle.min.js +172510 -144495
- package/lib/cjs/cli/options.js +6 -0
- package/lib/cjs/cli/utils.js +9 -1
- package/lib/cjs/common/chunked.js +1 -1
- package/lib/cjs/common/factory.js +2 -6
- package/lib/cjs/common/http.js +118 -1
- package/lib/cjs/common/logger.js +50 -59
- package/lib/cjs/common/payment.js +2 -2
- package/lib/cjs/common/signer.js +36 -1
- package/lib/cjs/common/token/ario.js +1 -1
- package/lib/cjs/common/token/arweave.js +1 -1
- package/lib/cjs/common/token/erc20.js +1 -1
- package/lib/cjs/common/token/ethereum.js +1 -1
- package/lib/cjs/common/token/kyve.js +1 -1
- package/lib/cjs/common/token/polygon.js +1 -1
- package/lib/cjs/common/token/solana.js +1 -1
- package/lib/cjs/common/upload.js +25 -3
- package/lib/cjs/common/winstonLogger.js +66 -0
- package/lib/cjs/types.js +9 -1
- package/lib/cjs/utils/axiosClient.js +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/esm/cli/options.js +6 -0
- package/lib/esm/cli/utils.js +10 -2
- package/lib/esm/common/chunked.js +2 -2
- package/lib/esm/common/factory.js +3 -7
- package/lib/esm/common/http.js +117 -1
- package/lib/esm/common/logger.js +48 -57
- package/lib/esm/common/payment.js +3 -3
- package/lib/esm/common/signer.js +36 -2
- package/lib/esm/common/token/ario.js +2 -2
- package/lib/esm/common/token/arweave.js +2 -2
- package/lib/esm/common/token/erc20.js +2 -2
- package/lib/esm/common/token/ethereum.js +2 -2
- package/lib/esm/common/token/kyve.js +2 -2
- package/lib/esm/common/token/polygon.js +2 -2
- package/lib/esm/common/token/solana.js +2 -2
- package/lib/esm/common/upload.js +27 -5
- package/lib/esm/common/winstonLogger.js +62 -0
- package/lib/esm/types.js +7 -0
- package/lib/esm/utils/axiosClient.js +2 -2
- package/lib/esm/version.js +1 -1
- package/lib/types/cli/options.d.ts +17 -0
- package/lib/types/cli/options.d.ts.map +1 -1
- package/lib/types/cli/types.d.ts +1 -0
- package/lib/types/cli/types.d.ts.map +1 -1
- package/lib/types/cli/utils.d.ts.map +1 -1
- package/lib/types/common/chunked.d.ts.map +1 -1
- package/lib/types/common/factory.d.ts +4 -5
- package/lib/types/common/factory.d.ts.map +1 -1
- package/lib/types/common/http.d.ts +10 -2
- package/lib/types/common/http.d.ts.map +1 -1
- package/lib/types/common/logger.d.ts +17 -26
- package/lib/types/common/logger.d.ts.map +1 -1
- package/lib/types/common/payment.d.ts.map +1 -1
- package/lib/types/common/signer.d.ts +3 -0
- package/lib/types/common/signer.d.ts.map +1 -1
- package/lib/types/common/token/ario.d.ts.map +1 -1
- package/lib/types/common/token/arweave.d.ts.map +1 -1
- package/lib/types/common/token/erc20.d.ts.map +1 -1
- package/lib/types/common/token/ethereum.d.ts.map +1 -1
- package/lib/types/common/token/kyve.d.ts.map +1 -1
- package/lib/types/common/token/polygon.d.ts.map +1 -1
- package/lib/types/common/token/solana.d.ts.map +1 -1
- package/lib/types/common/upload.d.ts +2 -1
- package/lib/types/common/upload.d.ts.map +1 -1
- package/lib/types/common/winstonLogger.d.ts +14 -0
- package/lib/types/common/winstonLogger.d.ts.map +1 -0
- package/lib/types/types.d.ts +24 -12
- package/lib/types/types.d.ts.map +1 -1
- package/lib/types/version.d.ts +1 -1
- package/package.json +9 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TurboWinstonLogger = 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 version_js_1 = require("../version.js");
|
|
20
|
+
class TurboWinstonLogger {
|
|
21
|
+
constructor({ level = 'info', } = {}) {
|
|
22
|
+
try {
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
24
|
+
this.winston = require('winston');
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw new Error('Winston is not installed. Install it with: npm install winston');
|
|
28
|
+
}
|
|
29
|
+
this.logger = this.winston.createLogger({
|
|
30
|
+
level: level === 'none' ? 'error' : level,
|
|
31
|
+
silent: level === 'none',
|
|
32
|
+
defaultMeta: {
|
|
33
|
+
name: 'ar-io-sdk',
|
|
34
|
+
version: version_js_1.version,
|
|
35
|
+
},
|
|
36
|
+
format: this.winston.format.combine(this.winston.format.timestamp(), this.winston.format.json()),
|
|
37
|
+
transports: [
|
|
38
|
+
new this.winston.transports.Console({
|
|
39
|
+
format: this.winston.format.combine(this.winston.format.timestamp(), this.winston.format.json()),
|
|
40
|
+
}),
|
|
41
|
+
],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
info(message, ...args) {
|
|
45
|
+
this.logger.info(message, ...args);
|
|
46
|
+
}
|
|
47
|
+
warn(message, ...args) {
|
|
48
|
+
this.logger.warn(message, ...args);
|
|
49
|
+
}
|
|
50
|
+
error(message, ...args) {
|
|
51
|
+
this.logger.error(message, ...args);
|
|
52
|
+
}
|
|
53
|
+
debug(message, ...args) {
|
|
54
|
+
this.logger.debug(message, ...args);
|
|
55
|
+
}
|
|
56
|
+
setLogLevel(level) {
|
|
57
|
+
if (level === 'none') {
|
|
58
|
+
this.logger.silent = true;
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.logger.silent = false;
|
|
62
|
+
this.logger.level = level;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.TurboWinstonLogger = TurboWinstonLogger;
|
package/lib/cjs/types.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
|
|
3
|
+
exports.validChunkingModes = exports.isJWK = exports.isWebUploadFolderParams = exports.isNodeUploadFolderParams = exports.multipartFinalizedStatus = exports.multipartFailedStatus = exports.multipartPendingStatus = exports.X402Funding = exports.OnDemandFunding = exports.ExistingBalanceFunding = exports.supportedEvmSignerTokens = exports.tokenTypes = exports.fiatCurrencyTypes = void 0;
|
|
4
4
|
exports.isCurrency = isCurrency;
|
|
5
5
|
exports.isKyvePrivateKey = isKyvePrivateKey;
|
|
6
6
|
exports.isEthPrivateKey = isEthPrivateKey;
|
|
@@ -62,6 +62,14 @@ class OnDemandFunding {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
exports.OnDemandFunding = OnDemandFunding;
|
|
65
|
+
class X402Funding {
|
|
66
|
+
constructor({ signer, maxMUSDCAmount, }) {
|
|
67
|
+
this.signer = signer;
|
|
68
|
+
this.maxMUSDCAmount =
|
|
69
|
+
maxMUSDCAmount !== undefined ? new bignumber_js_1.BigNumber(maxMUSDCAmount) : undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.X402Funding = X402Funding;
|
|
65
73
|
exports.multipartPendingStatus = [
|
|
66
74
|
'ASSEMBLING',
|
|
67
75
|
'VALIDATING',
|
|
@@ -26,7 +26,7 @@ exports.defaultRequestHeaders = {
|
|
|
26
26
|
'x-turbo-source-version': version_js_1.version,
|
|
27
27
|
'x-turbo-source-identifier': 'turbo-sdk',
|
|
28
28
|
};
|
|
29
|
-
const defaultRetryConfig = (logger = logger_js_1.
|
|
29
|
+
const defaultRetryConfig = (logger = logger_js_1.Logger.default) => ({
|
|
30
30
|
retryDelay: (retryCount) => Math.min(1000 * 2 ** (retryCount - 1), 30 * 1000), // exponential backoff up to 30s
|
|
31
31
|
retries: 5,
|
|
32
32
|
onRetry: (retryCount, error) => {
|
package/lib/cjs/version.js
CHANGED
package/lib/esm/cli/options.js
CHANGED
|
@@ -183,6 +183,11 @@ export const optionMap = {
|
|
|
183
183
|
description: 'Enable on-demand crypto top-ups during upload if balance is insufficient',
|
|
184
184
|
default: false,
|
|
185
185
|
},
|
|
186
|
+
x402: {
|
|
187
|
+
alias: '--x402',
|
|
188
|
+
description: 'Pay for the action using x402 funding (if available). Requires token `base-usdc`.',
|
|
189
|
+
default: false,
|
|
190
|
+
},
|
|
186
191
|
topUpBufferMultiplier: {
|
|
187
192
|
alias: '--top-up-buffer-multiplier <topUpBufferMultiplier>',
|
|
188
193
|
description: 'Multiplier to apply to the estimated top-up amount to avoid underpayment during on-demand top-ups. Defaults to 1.1 (10% buffer).',
|
|
@@ -212,6 +217,7 @@ const onDemandOptions = [
|
|
|
212
217
|
optionMap.onDemand,
|
|
213
218
|
optionMap.topUpBufferMultiplier,
|
|
214
219
|
optionMap.maxCryptoTopUpValue,
|
|
220
|
+
optionMap.x402,
|
|
215
221
|
];
|
|
216
222
|
export const uploadOptions = [
|
|
217
223
|
...walletOptions,
|
package/lib/esm/cli/utils.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import bs58 from 'bs58';
|
|
17
17
|
import { readFileSync, statSync } from 'fs';
|
|
18
|
-
import { ExistingBalanceFunding, OnDemandFunding, TurboFactory, defaultTurboConfiguration, developmentTurboConfiguration, fiatCurrencyTypes, isCurrency, isTokenType, privateKeyFromKyveMnemonic, tokenToBaseMap, } from '../node/index.js';
|
|
18
|
+
import { ExistingBalanceFunding, OnDemandFunding, TurboFactory, X402Funding, defaultTurboConfiguration, developmentTurboConfiguration, fiatCurrencyTypes, isCurrency, isTokenType, privateKeyFromKyveMnemonic, tokenToBaseMap, } from '../node/index.js';
|
|
19
19
|
import { defaultProdAoConfigs, tokenToDevAoConfigMap, tokenToDevGatewayMap, } from '../utils/common.js';
|
|
20
20
|
import { NoWalletProvidedError } from './errors.js';
|
|
21
21
|
export function exitWithErrorLog(error) {
|
|
@@ -246,7 +246,10 @@ export function getTagsFromOptions(options) {
|
|
|
246
246
|
return parseTags(options.tags);
|
|
247
247
|
}
|
|
248
248
|
export function onDemandOptionsFromOptions(options) {
|
|
249
|
-
if (
|
|
249
|
+
if (options.x402 && options.onDemand) {
|
|
250
|
+
throw new Error('Cannot use both --x402 and --on-demand flags');
|
|
251
|
+
}
|
|
252
|
+
if (!options.onDemand && !options.x402) {
|
|
250
253
|
return { fundingMode: new ExistingBalanceFunding() };
|
|
251
254
|
}
|
|
252
255
|
const value = options.maxCryptoTopUpValue;
|
|
@@ -258,6 +261,11 @@ export function onDemandOptionsFromOptions(options) {
|
|
|
258
261
|
const token = tokenFromOptions(options);
|
|
259
262
|
maxTokenAmount = tokenToBaseMap[token](value).toString();
|
|
260
263
|
}
|
|
264
|
+
if (options.x402) {
|
|
265
|
+
return {
|
|
266
|
+
fundingMode: new X402Funding({ maxMUSDCAmount: maxTokenAmount }),
|
|
267
|
+
};
|
|
268
|
+
}
|
|
261
269
|
if (options.topUpBufferMultiplier !== undefined &&
|
|
262
270
|
(isNaN(options.topUpBufferMultiplier) || options.topUpBufferMultiplier < 1)) {
|
|
263
271
|
throw new Error('topUpBufferMultiplier must be a number >= 1');
|
|
@@ -19,7 +19,7 @@ import { multipartFailedStatus, validChunkingModes, } from '../types.js';
|
|
|
19
19
|
import { sleep } from '../utils/common.js';
|
|
20
20
|
import { FailedRequestError } from '../utils/errors.js';
|
|
21
21
|
import { TurboEventEmitter, createStreamWithUploadEvents } from './events.js';
|
|
22
|
-
import {
|
|
22
|
+
import { Logger } from './logger.js';
|
|
23
23
|
const fiveMiB = 5 * 1024 * 1024; // 5 MiB
|
|
24
24
|
const fiveHundredMiB = fiveMiB * 100; // 500 MiB
|
|
25
25
|
export const defaultMaxChunkConcurrency = 5;
|
|
@@ -35,7 +35,7 @@ const chunkingHeader = { 'x-chunking-version': '2' };
|
|
|
35
35
|
* uploading them in parallel, and emitting progress/error events.
|
|
36
36
|
*/
|
|
37
37
|
export class ChunkedUploader {
|
|
38
|
-
constructor({ http, token, maxChunkConcurrency = defaultMaxChunkConcurrency, maxFinalizeMs, chunkByteCount = defaultChunkByteCount, logger =
|
|
38
|
+
constructor({ http, token, maxChunkConcurrency = defaultMaxChunkConcurrency, maxFinalizeMs, chunkByteCount = defaultChunkByteCount, logger = Logger.default, chunkingMode = 'auto', dataItemByteCount, }) {
|
|
39
39
|
this.assertChunkParams({
|
|
40
40
|
chunkByteCount,
|
|
41
41
|
chunkingMode,
|
|
@@ -15,20 +15,16 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { HexInjectedSolanaSigner, InjectedEthereumSigner, SignatureConfig, } from '@dha-team/arbundles';
|
|
17
17
|
import { isEthereumWalletAdapter, isSolanaWalletAdapter, supportedEvmSignerTokens, } from '../types.js';
|
|
18
|
-
import {
|
|
18
|
+
import { Logger } from './logger.js';
|
|
19
19
|
import { TurboAuthenticatedPaymentService, TurboUnauthenticatedPaymentService, } from './payment.js';
|
|
20
20
|
import { defaultTokenMap } from './token/index.js';
|
|
21
21
|
import { TurboAuthenticatedClient, TurboUnauthenticatedClient, } from './turbo.js';
|
|
22
22
|
import { TurboUnauthenticatedUploadService } from './upload.js';
|
|
23
23
|
export class TurboBaseFactory {
|
|
24
|
-
/* @deprecated - use
|
|
24
|
+
/* @deprecated - use Logger directly */
|
|
25
25
|
static setLogLevel(level) {
|
|
26
26
|
this.logger.setLogLevel(level);
|
|
27
27
|
}
|
|
28
|
-
/* @deprecated - use TurboWinstonLogger directly */
|
|
29
|
-
static setLogFormat(format) {
|
|
30
|
-
this.logger.setLogFormat(format);
|
|
31
|
-
}
|
|
32
28
|
static unauthenticated({ paymentServiceConfig = {}, uploadServiceConfig = {}, token, } = {}) {
|
|
33
29
|
token = token === 'pol' ? 'matic' : token;
|
|
34
30
|
token ??= 'arweave'; // default to arweave if token is not provided
|
|
@@ -147,4 +143,4 @@ export class TurboBaseFactory {
|
|
|
147
143
|
throw new Error('Unsupported wallet adapter -- wallet adapter is currently only supported for Solana and Ethereum');
|
|
148
144
|
}
|
|
149
145
|
}
|
|
150
|
-
TurboBaseFactory.logger =
|
|
146
|
+
TurboBaseFactory.logger = Logger.default;
|
package/lib/esm/common/http.js
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { AxiosError, CanceledError } from 'axios';
|
|
17
17
|
import { Readable } from 'node:stream';
|
|
18
|
+
import { wrapFetchWithPayment } from 'x402-fetch';
|
|
18
19
|
import { createAxiosInstance, defaultRetryConfig, } from '../utils/axiosClient.js';
|
|
19
20
|
import { sleep } from '../utils/common.js';
|
|
20
21
|
import { FailedRequestError } from '../utils/errors.js';
|
|
@@ -33,7 +34,46 @@ export class TurboHTTPService {
|
|
|
33
34
|
async get({ endpoint, signal, allowedStatuses = [200, 202], headers, }) {
|
|
34
35
|
return this.retryRequest(() => this.axios.get(endpoint, { headers, signal }), allowedStatuses);
|
|
35
36
|
}
|
|
36
|
-
async post({ endpoint, signal, allowedStatuses = [200, 202], headers, data, }) {
|
|
37
|
+
async post({ endpoint, signal, allowedStatuses = [200, 202], headers, data, x402Options, }) {
|
|
38
|
+
if (x402Options !== undefined) {
|
|
39
|
+
this.logger.debug('Using X402 options for POST request', {
|
|
40
|
+
endpoint,
|
|
41
|
+
x402Options,
|
|
42
|
+
});
|
|
43
|
+
const { body, duplex } = await toX402FetchBody(data);
|
|
44
|
+
try {
|
|
45
|
+
const maxMUSDCAmount = x402Options.maxMUSDCAmount !== undefined
|
|
46
|
+
? BigInt(x402Options.maxMUSDCAmount.toString())
|
|
47
|
+
: undefined;
|
|
48
|
+
const fetchWithPay = wrapFetchWithPayment(fetch, x402Options.signer, maxMUSDCAmount);
|
|
49
|
+
const res = await fetchWithPay(this.axios.defaults.baseURL + '/x402/data-item/signed', {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers,
|
|
52
|
+
body,
|
|
53
|
+
signal,
|
|
54
|
+
...(duplex ? { duplex } : {}), // Use duplex only where streams are working
|
|
55
|
+
});
|
|
56
|
+
if (!allowedStatuses.includes(res.status)) {
|
|
57
|
+
const errorText = await res.text();
|
|
58
|
+
throw new FailedRequestError(
|
|
59
|
+
// Return error message from server if available
|
|
60
|
+
errorText || res.statusText, res.status);
|
|
61
|
+
}
|
|
62
|
+
return res.json();
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
if (error instanceof FailedRequestError) {
|
|
66
|
+
throw error; // rethrow FailedRequestError
|
|
67
|
+
}
|
|
68
|
+
// Handle CanceledError specifically
|
|
69
|
+
if (error.message.includes('The operation was aborted')) {
|
|
70
|
+
throw new CanceledError();
|
|
71
|
+
}
|
|
72
|
+
// Log the error and throw a FailedRequestError
|
|
73
|
+
this.logger.error('Error posting data', { endpoint, error });
|
|
74
|
+
throw new FailedRequestError(error instanceof Error ? error.message : 'Unknown error', error.response?.status);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
37
77
|
// Buffer and Readable → keep Axios (streams work fine there)
|
|
38
78
|
if (!(data instanceof ReadableStream)) {
|
|
39
79
|
if (data instanceof Readable) {
|
|
@@ -137,3 +177,79 @@ async function toFetchBody(data) {
|
|
|
137
177
|
const blob = await new Response(data).blob();
|
|
138
178
|
return { body: blob }; // browser sets length
|
|
139
179
|
}
|
|
180
|
+
const isNode = typeof process !== 'undefined' && !!process.versions?.node;
|
|
181
|
+
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
182
|
+
function isFirefoxOrSafari() {
|
|
183
|
+
if (!isBrowser)
|
|
184
|
+
return false;
|
|
185
|
+
const ua = navigator.userAgent;
|
|
186
|
+
return ua.includes('Firefox') || ua.includes('Safari');
|
|
187
|
+
}
|
|
188
|
+
/** Create a re-usable body for x402 fetch protocol */
|
|
189
|
+
export async function toX402FetchBody(data) {
|
|
190
|
+
//
|
|
191
|
+
// 🔹 NODE: always buffer to a non-stream body (Buffer)
|
|
192
|
+
// so fetchWithPayment can reuse it safely.
|
|
193
|
+
//
|
|
194
|
+
if (isNode) {
|
|
195
|
+
let buf;
|
|
196
|
+
// Web ReadableStream → Buffer
|
|
197
|
+
if (typeof ReadableStream !== 'undefined' &&
|
|
198
|
+
data instanceof ReadableStream) {
|
|
199
|
+
const ab = await new Response(data).arrayBuffer();
|
|
200
|
+
buf = Buffer.from(ab);
|
|
201
|
+
}
|
|
202
|
+
// Node Readable → Buffer
|
|
203
|
+
else if (data instanceof Readable) {
|
|
204
|
+
const chunks = [];
|
|
205
|
+
for await (const chunk of data) {
|
|
206
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
207
|
+
}
|
|
208
|
+
buf = Buffer.concat(chunks.map((c) => Uint8Array.from(c)));
|
|
209
|
+
}
|
|
210
|
+
// Buffer / Uint8Array
|
|
211
|
+
else if (Buffer.isBuffer(data)) {
|
|
212
|
+
buf = data;
|
|
213
|
+
}
|
|
214
|
+
else if (data instanceof Uint8Array) {
|
|
215
|
+
buf = Buffer.from(data);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
throw new Error('Unsupported body type for toFetchBody (Node)');
|
|
219
|
+
}
|
|
220
|
+
// For Buffer body, Node *does not* need duplex.
|
|
221
|
+
return { body: buf };
|
|
222
|
+
}
|
|
223
|
+
//
|
|
224
|
+
// 🔹 BROWSER: keep your previous behavior (streams/Blob), no duplex.
|
|
225
|
+
//
|
|
226
|
+
let body;
|
|
227
|
+
// Already a web ReadableStream
|
|
228
|
+
if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) {
|
|
229
|
+
body = data;
|
|
230
|
+
}
|
|
231
|
+
// Node Readable in browser (rare, but be safe) → Blob
|
|
232
|
+
else if (data instanceof Readable) {
|
|
233
|
+
const chunks = [];
|
|
234
|
+
for await (const chunk of data) {
|
|
235
|
+
const buf = typeof chunk === 'string'
|
|
236
|
+
? new TextEncoder().encode(chunk)
|
|
237
|
+
: new Uint8Array(chunk);
|
|
238
|
+
chunks.push(buf);
|
|
239
|
+
}
|
|
240
|
+
body = new Blob(chunks);
|
|
241
|
+
}
|
|
242
|
+
// Buffer / Uint8Array
|
|
243
|
+
else if (Buffer.isBuffer(data) || data instanceof Uint8Array) {
|
|
244
|
+
body = new Blob([Uint8Array.from(data)]);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
throw new Error('Unsupported body type for toFetchBody (browser)');
|
|
248
|
+
}
|
|
249
|
+
// Firefox / Safari – avoid streaming uploads
|
|
250
|
+
if (isFirefoxOrSafari() && body instanceof ReadableStream) {
|
|
251
|
+
const blob = await new Response(body).blob();
|
|
252
|
+
return { body: blob };
|
|
253
|
+
}
|
|
254
|
+
return { body };
|
|
255
|
+
}
|
package/lib/esm/common/logger.js
CHANGED
|
@@ -13,76 +13,67 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { createLogger, format, transports, } from 'winston';
|
|
17
16
|
import { version } from '../version.js';
|
|
18
|
-
export class
|
|
19
|
-
constructor({ level = 'info',
|
|
20
|
-
this.
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
export class Logger {
|
|
18
|
+
constructor({ level = 'info', } = {}) {
|
|
19
|
+
this.level = 'info';
|
|
20
|
+
this.levels = {
|
|
21
|
+
debug: 0,
|
|
22
|
+
info: 1,
|
|
23
|
+
warn: 2,
|
|
24
|
+
error: 3,
|
|
25
|
+
none: 999,
|
|
26
|
+
};
|
|
27
|
+
this.level = level;
|
|
28
|
+
}
|
|
29
|
+
formatMessage(level, message, ...args) {
|
|
30
|
+
const timestamp = new Date().toISOString();
|
|
31
|
+
const meta = {
|
|
32
|
+
timestamp,
|
|
33
|
+
level,
|
|
34
|
+
message,
|
|
35
|
+
name: 'turbo-sdk',
|
|
36
|
+
version,
|
|
37
|
+
};
|
|
38
|
+
if (args.length > 0) {
|
|
39
|
+
return JSON.stringify({ ...meta, args });
|
|
23
40
|
}
|
|
24
|
-
|
|
25
|
-
|
|
41
|
+
return JSON.stringify(meta);
|
|
42
|
+
}
|
|
43
|
+
log(level, message, ...args) {
|
|
44
|
+
if (this.levels[level] < this.levels[this.level]) {
|
|
45
|
+
return;
|
|
26
46
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
});
|
|
47
|
+
const formattedMessage = this.formatMessage(level, message, ...args);
|
|
48
|
+
switch (level) {
|
|
49
|
+
case 'debug':
|
|
50
|
+
console.debug(formattedMessage);
|
|
51
|
+
break;
|
|
52
|
+
case 'info':
|
|
53
|
+
console.info(formattedMessage);
|
|
54
|
+
break;
|
|
55
|
+
case 'warn':
|
|
56
|
+
console.warn(formattedMessage);
|
|
57
|
+
break;
|
|
58
|
+
case 'error':
|
|
59
|
+
console.error(formattedMessage);
|
|
60
|
+
break;
|
|
42
61
|
}
|
|
43
62
|
}
|
|
44
63
|
info(message, ...args) {
|
|
45
|
-
|
|
46
|
-
return;
|
|
47
|
-
this.logger.info(message, ...args);
|
|
64
|
+
this.log('info', message, ...args);
|
|
48
65
|
}
|
|
49
66
|
warn(message, ...args) {
|
|
50
|
-
|
|
51
|
-
return;
|
|
52
|
-
this.logger.warn(message, ...args);
|
|
67
|
+
this.log('warn', message, ...args);
|
|
53
68
|
}
|
|
54
69
|
error(message, ...args) {
|
|
55
|
-
|
|
56
|
-
return;
|
|
57
|
-
this.logger.error(message, ...args);
|
|
70
|
+
this.log('error', message, ...args);
|
|
58
71
|
}
|
|
59
72
|
debug(message, ...args) {
|
|
60
|
-
|
|
61
|
-
return;
|
|
62
|
-
this.logger.debug(message, ...args);
|
|
73
|
+
this.log('debug', message, ...args);
|
|
63
74
|
}
|
|
64
75
|
setLogLevel(level) {
|
|
65
|
-
this.
|
|
66
|
-
if ('silent' in this.logger) {
|
|
67
|
-
this.logger.silent = level === 'none';
|
|
68
|
-
}
|
|
69
|
-
if ('level' in this.logger) {
|
|
70
|
-
this.logger.level = level;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
setLogFormat(logFormat) {
|
|
74
|
-
if ('format' in this.logger) {
|
|
75
|
-
this.logger.format = getLogFormat(logFormat);
|
|
76
|
-
}
|
|
76
|
+
this.level = level;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
|
|
80
|
-
function getLogFormat(logFormat) {
|
|
81
|
-
return format.combine(format((info) => {
|
|
82
|
-
if (info.stack && info.level !== 'error') {
|
|
83
|
-
delete info.stack;
|
|
84
|
-
}
|
|
85
|
-
return info;
|
|
86
|
-
})(), format.errors({ stack: true }), // Ensure errors show a stack trace
|
|
87
|
-
format.timestamp(), logFormat === 'json' ? format.json() : format.simple());
|
|
88
|
-
}
|
|
79
|
+
Logger.default = new Logger();
|
|
@@ -17,12 +17,12 @@ import { BigNumber } from 'bignumber.js';
|
|
|
17
17
|
import { defaultRetryConfig } from '../utils/axiosClient.js';
|
|
18
18
|
import { isAnyValidUserAddress } from '../utils/common.js';
|
|
19
19
|
import { TurboHTTPService } from './http.js';
|
|
20
|
-
import {
|
|
20
|
+
import { Logger } from './logger.js';
|
|
21
21
|
import { exponentMap, tokenToBaseMap } from './token/index.js';
|
|
22
22
|
export const developmentPaymentServiceURL = 'https://payment.ardrive.dev';
|
|
23
23
|
export const defaultPaymentServiceURL = 'https://payment.ardrive.io';
|
|
24
24
|
export class TurboUnauthenticatedPaymentService {
|
|
25
|
-
constructor({ url = defaultPaymentServiceURL, logger =
|
|
25
|
+
constructor({ url = defaultPaymentServiceURL, logger = Logger.default, retryConfig = defaultRetryConfig(logger), token = 'arweave', }) {
|
|
26
26
|
this.logger = logger;
|
|
27
27
|
this.httpService = new TurboHTTPService({
|
|
28
28
|
url: `${url}/v1`,
|
|
@@ -241,7 +241,7 @@ export class TurboUnauthenticatedPaymentService {
|
|
|
241
241
|
}
|
|
242
242
|
// NOTE: to avoid redundancy, we use inheritance here - but generally prefer composition over inheritance
|
|
243
243
|
export class TurboAuthenticatedPaymentService extends TurboUnauthenticatedPaymentService {
|
|
244
|
-
constructor({ url = defaultPaymentServiceURL, retryConfig, signer, logger =
|
|
244
|
+
constructor({ url = defaultPaymentServiceURL, retryConfig, signer, logger = Logger.default, token = 'arweave', tokenTools, }) {
|
|
245
245
|
super({ url, retryConfig, logger, token });
|
|
246
246
|
this.signer = signer;
|
|
247
247
|
this.tokenTools = tokenTools;
|
package/lib/esm/common/signer.js
CHANGED
|
@@ -25,16 +25,19 @@ import { randomBytes } from 'crypto';
|
|
|
25
25
|
import { Wallet as EthereumWallet, ethers, parseEther } from 'ethers';
|
|
26
26
|
import { computeAddress } from 'ethers';
|
|
27
27
|
import nacl from 'tweetnacl';
|
|
28
|
+
import { createWalletClient, custom, http } from 'viem';
|
|
29
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
30
|
+
import { baseSepolia } from 'viem/chains';
|
|
28
31
|
import { isEthereumWalletAdapter, isSolanaWalletAdapter, } from '../types.js';
|
|
29
32
|
import { fromB64Url, ownerToAddress as ownerToB64Address, toB64Url, } from '../utils/base64.js';
|
|
30
|
-
import {
|
|
33
|
+
import { Logger } from './logger.js';
|
|
31
34
|
import { ethDataFromTurboCreditDestinationAddress } from './token/ethereum.js';
|
|
32
35
|
import { memoProgramId } from './token/solana.js';
|
|
33
36
|
/**
|
|
34
37
|
* Abstract class for signing TurboDataItems.
|
|
35
38
|
*/
|
|
36
39
|
export class TurboDataItemAbstractSigner {
|
|
37
|
-
constructor({ signer, logger =
|
|
40
|
+
constructor({ signer, logger = Logger.default, token, walletAdapter, }) {
|
|
38
41
|
this.logger = logger;
|
|
39
42
|
this.signer = signer;
|
|
40
43
|
this.token = token;
|
|
@@ -148,3 +151,34 @@ export class TurboDataItemAbstractSigner {
|
|
|
148
151
|
return this.signer.sign(dataToSign);
|
|
149
152
|
}
|
|
150
153
|
}
|
|
154
|
+
export async function makeX402Signer(arbundlesSigner) {
|
|
155
|
+
// Node: our SDK uses EthereumSigner with a raw private key
|
|
156
|
+
if (arbundlesSigner instanceof EthereumSigner) {
|
|
157
|
+
return createWalletClient({
|
|
158
|
+
account: privateKeyToAccount(('0x' +
|
|
159
|
+
Buffer.from(arbundlesSigner.key).toString('hex'))),
|
|
160
|
+
chain: baseSepolia,
|
|
161
|
+
transport: http(),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// Browser: use injected wallet + selected account
|
|
165
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
166
|
+
if (typeof window !== 'undefined' && window.ethereum) {
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
168
|
+
const provider = window.ethereum;
|
|
169
|
+
// ask wallet for an account
|
|
170
|
+
const accounts = (await provider.request({
|
|
171
|
+
method: 'eth_requestAccounts',
|
|
172
|
+
}));
|
|
173
|
+
if (accounts === undefined || accounts.length === 0) {
|
|
174
|
+
throw new Error('No accounts returned from wallet');
|
|
175
|
+
}
|
|
176
|
+
const account = accounts[0];
|
|
177
|
+
return createWalletClient({
|
|
178
|
+
account,
|
|
179
|
+
chain: baseSepolia,
|
|
180
|
+
transport: custom(provider),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
throw new Error('Unable to construct x402 signer for x402 options');
|
|
184
|
+
}
|
|
@@ -18,9 +18,9 @@ import { connect, createDataItemSigner } from '@permaweb/aoconnect';
|
|
|
18
18
|
import { BigNumber } from 'bignumber.js';
|
|
19
19
|
import { defaultProdAoConfigs, sleep } from '../../utils/common.js';
|
|
20
20
|
import { version } from '../../version.js';
|
|
21
|
-
import {
|
|
21
|
+
import { Logger } from '../logger.js';
|
|
22
22
|
export class ARIOToken {
|
|
23
|
-
constructor({ cuUrl = defaultProdAoConfigs.ario.cuUrl, logger =
|
|
23
|
+
constructor({ cuUrl = defaultProdAoConfigs.ario.cuUrl, logger = Logger.default, pollingOptions = {
|
|
24
24
|
initialBackoffMs: 500,
|
|
25
25
|
pollingIntervalMs: 0, // no polling for ARIO process
|
|
26
26
|
maxAttempts: 0, // no polling for ARIO process
|
|
@@ -17,13 +17,13 @@ import ArweaveModule from 'arweave';
|
|
|
17
17
|
import { BigNumber } from 'bignumber.js';
|
|
18
18
|
import { sha256B64Url, toB64Url } from '../../utils/base64.js';
|
|
19
19
|
import { sleep } from '../../utils/common.js';
|
|
20
|
-
import {
|
|
20
|
+
import { Logger } from '../logger.js';
|
|
21
21
|
const ArweaveClass =
|
|
22
22
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
23
23
|
// @ts-ignore -- Access the correct class constructor for Arweave
|
|
24
24
|
ArweaveModule.default?.default || ArweaveModule.default || ArweaveModule;
|
|
25
25
|
export class ArweaveToken {
|
|
26
|
-
constructor({ gatewayUrl = 'https://arweave.net', arweave, logger =
|
|
26
|
+
constructor({ gatewayUrl = 'https://arweave.net', arweave, logger = Logger.default, mintU = true, pollingOptions = {
|
|
27
27
|
maxAttempts: 10,
|
|
28
28
|
pollingIntervalMs: 3_000,
|
|
29
29
|
initialBackoffMs: 7_000,
|
|
@@ -17,10 +17,10 @@ import { EthereumSigner } from '@dha-team/arbundles';
|
|
|
17
17
|
import { Wallet as EthereumWallet, JsonRpcProvider, ethers } from 'ethers';
|
|
18
18
|
import { isEthereumWalletAdapter, } from '../../types.js';
|
|
19
19
|
import { defaultProdGatewayUrls } from '../../utils/common.js';
|
|
20
|
-
import {
|
|
20
|
+
import { Logger } from '../logger.js';
|
|
21
21
|
import { EthereumToken, ethDataFromTurboCreditDestinationAddress, } from './ethereum.js';
|
|
22
22
|
export class ERC20Token extends EthereumToken {
|
|
23
|
-
constructor({ tokenContractAddress, logger =
|
|
23
|
+
constructor({ tokenContractAddress, logger = Logger.default, gatewayUrl = defaultProdGatewayUrls.ethereum, pollingOptions, }) {
|
|
24
24
|
super({ logger, gatewayUrl, pollingOptions });
|
|
25
25
|
this.tokenContract = new ethers.Contract(tokenContractAddress, [
|
|
26
26
|
'function decimals() view returns (uint8)',
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { BigNumber } from 'bignumber.js';
|
|
17
17
|
import { ethers, hexlify, toUtf8Bytes } from 'ethers';
|
|
18
18
|
import { defaultProdGatewayUrls } from '../../utils/common.js';
|
|
19
|
-
import {
|
|
19
|
+
import { Logger } from '../logger.js';
|
|
20
20
|
export const weiToTokenAmount = (wei) => wei;
|
|
21
21
|
export const ETHToTokenAmount = (eth) => new BigNumber(eth).times(1e18).valueOf();
|
|
22
22
|
export const defaultEthereumPollingOptions = {
|
|
@@ -25,7 +25,7 @@ export const defaultEthereumPollingOptions = {
|
|
|
25
25
|
pollingIntervalMs: 1_500,
|
|
26
26
|
};
|
|
27
27
|
export class EthereumToken {
|
|
28
|
-
constructor({ logger =
|
|
28
|
+
constructor({ logger = Logger.default, gatewayUrl = defaultProdGatewayUrls.ethereum, pollingOptions = defaultEthereumPollingOptions, } = {}) {
|
|
29
29
|
this.logger = logger;
|
|
30
30
|
this.gatewayUrl = gatewayUrl;
|
|
31
31
|
this.pollingOptions = pollingOptions;
|
|
@@ -23,14 +23,14 @@ import { BigNumber } from 'bignumber.js';
|
|
|
23
23
|
import { createAxiosInstance } from '../../utils/axiosClient.js';
|
|
24
24
|
import { defaultProdGatewayUrls } from '../../utils/common.js';
|
|
25
25
|
import { sleep } from '../../utils/common.js';
|
|
26
|
-
import {
|
|
26
|
+
import { Logger } from '../logger.js';
|
|
27
27
|
function hasKyveTxResponse(response) {
|
|
28
28
|
return response.tx_response !== undefined;
|
|
29
29
|
}
|
|
30
30
|
export const ukyveToTokenAmount = (winston) => winston;
|
|
31
31
|
export const KYVEToTokenAmount = (sol) => new BigNumber(sol).times(1e6).valueOf();
|
|
32
32
|
export class KyveToken {
|
|
33
|
-
constructor({ logger =
|
|
33
|
+
constructor({ logger = Logger.default, gatewayUrl = defaultProdGatewayUrls.kyve, pollingOptions = {
|
|
34
34
|
maxAttempts: 5,
|
|
35
35
|
pollingIntervalMs: 1_000,
|
|
36
36
|
initialBackoffMs: 500,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { defaultProdGatewayUrls } from '../../utils/common.js';
|
|
2
|
-
import {
|
|
2
|
+
import { Logger } from '../logger.js';
|
|
3
3
|
import { ETHToTokenAmount, EthereumToken } from './ethereum.js';
|
|
4
4
|
export const POLToTokenAmount = ETHToTokenAmount;
|
|
5
5
|
export const defaultPolygonPollingOptions = {
|
|
@@ -8,7 +8,7 @@ export const defaultPolygonPollingOptions = {
|
|
|
8
8
|
pollingIntervalMs: 1_000,
|
|
9
9
|
};
|
|
10
10
|
export class PolygonToken extends EthereumToken {
|
|
11
|
-
constructor({ logger =
|
|
11
|
+
constructor({ logger = Logger.default, gatewayUrl = defaultProdGatewayUrls.pol, pollingOptions = defaultPolygonPollingOptions, } = {}) {
|
|
12
12
|
super({ logger, gatewayUrl, pollingOptions });
|
|
13
13
|
}
|
|
14
14
|
}
|