@upcoming/bee-js 12.0.0 → 12.2.3

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/dist/cjs/bee.js CHANGED
@@ -319,7 +319,7 @@ class Bee {
319
319
  const fileData = await (0, file_1.fileArrayBuffer)(data);
320
320
  const fileName = name ?? data.name;
321
321
  const contentType = data.type;
322
- const fileOptions = { contentType, ...options };
322
+ const fileOptions = { ...options, contentType };
323
323
  return bzz.uploadFile(this.getRequestOptionsForCall(requestOptions), fileData, postageBatchId, fileName, fileOptions);
324
324
  }
325
325
  else {
@@ -1629,10 +1629,16 @@ class Bee {
1629
1629
  ? currentAmount * multiplier
1630
1630
  : (currentAmount + additionalAmount) * multiplier;
1631
1631
  const amountDelta = targetAmount - currentAmount;
1632
- const transactionId = await this.topUpBatch(batch.batchID, amountDelta, requestOptions);
1632
+ let transactionId;
1633
+ if (amountDelta > 0n) {
1634
+ transactionId = await this.topUpBatch(batch.batchID, amountDelta, requestOptions);
1635
+ }
1633
1636
  if (depthDelta > 0) {
1634
1637
  return this.diluteBatch(batch.batchID, depth, requestOptions);
1635
1638
  }
1639
+ if (!transactionId) {
1640
+ throw new Error('Nothing to extend, both size and duration are already sufficient');
1641
+ }
1636
1642
  return transactionId;
1637
1643
  }
1638
1644
  /**
@@ -1760,16 +1766,15 @@ class Bee {
1760
1766
  /**
1761
1767
  * Calculates the `amount` and expected duration extension for topping up a postage batch with a given BZZ value.
1762
1768
  *
1763
- * @param postageBatchId
1769
+ * @param depth Depth of the postage batch to top up.
1764
1770
  * @param bzz The amount of BZZ to spend on the top-up.
1765
1771
  * @param requestOptions Options for making requests, such as timeouts, custom HTTP agents, headers, etc.
1766
1772
  * @returns An object with `amount` (to pass to {@link topUpBatch}) and `duration` (the expected TTL extension).
1767
1773
  */
1768
- async calculateTopUpForBzz(postageBatchId, bzz, requestOptions) {
1769
- const batch = await this.getPostageBatch(postageBatchId, requestOptions);
1774
+ async calculateTopUpForBzz(depth, bzz, requestOptions) {
1770
1775
  const chainState = await this.getChainState(requestOptions);
1771
1776
  const blockTime = this.network === 'gnosis' ? 5 : 15;
1772
- const amount = bzz.toPLURBigInt() / 2n ** BigInt(batch.depth);
1777
+ const amount = bzz.toPLURBigInt() / 2n ** BigInt(depth);
1773
1778
  const duration = (0, stamps_1.getStampDuration)(amount, chainState.currentPrice, blockTime);
1774
1779
  return { amount, duration };
1775
1780
  }
package/dist/cjs/index.js CHANGED
@@ -36,11 +36,9 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.Stamper = exports.BeeDev = exports.Bee = exports.Size = exports.Utils = exports.Duration = exports.Bytes = exports.SUPPORTED_BEE_VERSION_EXACT = exports.SUPPORTED_BEE_VERSION = exports.MantarayNode = exports.MerkleTree = void 0;
39
+ exports.Stamper = exports.Bee = exports.Size = exports.Utils = exports.Duration = exports.Bytes = exports.SUPPORTED_BEE_VERSION_EXACT = exports.SUPPORTED_BEE_VERSION = exports.MantarayNode = exports.MerkleTree = void 0;
40
40
  const bee_1 = require("./bee");
41
41
  Object.defineProperty(exports, "Bee", { enumerable: true, get: function () { return bee_1.Bee; } });
42
- const bee_dev_1 = require("./bee-dev");
43
- Object.defineProperty(exports, "BeeDev", { enumerable: true, get: function () { return bee_dev_1.BeeDev; } });
44
42
  const stamper_1 = require("./stamper/stamper");
45
43
  Object.defineProperty(exports, "Stamper", { enumerable: true, get: function () { return stamper_1.Stamper; } });
46
44
  var cafe_utility_1 = require("cafe-utility");
@@ -60,7 +60,7 @@ async function fetchLatestFeedUpdate(requestOptions, owner, topic, options) {
60
60
  const response = await (0, http_1.http)(requestOptions, {
61
61
  responseType: 'arraybuffer',
62
62
  url: `${feedEndpoint}/${owner}/${topic}`,
63
- params: options,
63
+ params: { ...options },
64
64
  });
65
65
  return {
66
66
  payload: new bytes_1.Bytes(response.data),
@@ -12,6 +12,7 @@ const http_1 = require("../utils/http");
12
12
  async function checkConnection(requestOptions) {
13
13
  await (0, http_1.http)(requestOptions, {
14
14
  url: '',
15
+ responseType: 'text',
15
16
  });
16
17
  }
17
18
  async function isGateway(requestOptions) {
@@ -5,29 +5,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DEFAULT_HTTP_CONFIG = void 0;
7
7
  exports.http = http;
8
- const axios_1 = __importDefault(require("axios"));
8
+ exports.toBeeResponse = toBeeResponse;
9
9
  const cafe_utility_1 = require("cafe-utility");
10
10
  const debug_1 = __importDefault(require("debug"));
11
11
  const index_1 = require("../index");
12
12
  const debug = (0, debug_1.default)('bee-js:http');
13
- const { AxiosError } = axios_1.default;
14
13
  const MAX_FAILED_ATTEMPTS = 100000;
15
14
  const DELAY_FAST = 200;
16
15
  const DELAY_SLOW = 1000;
17
- const DELAY_THRESHOLD = cafe_utility_1.Dates.minutes(1) / DELAY_FAST;
18
- const ABORT_ERROR_MESSAGE = 'Request aborted';
16
+ const FAST_RETRY_COUNT = 300;
19
17
  exports.DEFAULT_HTTP_CONFIG = {
20
18
  headers: {
21
19
  accept: 'application/json, text/plain, */*',
22
20
  },
23
- maxBodyLength: Infinity,
24
- maxContentLength: Infinity,
25
21
  };
26
- function throwIfAborted(signal, config, responseData, responseStatus) {
27
- if (signal?.aborted) {
28
- throw new index_1.BeeResponseError(config.method || 'get', config.url || '<unknown>', ABORT_ERROR_MESSAGE, responseData, responseStatus, 'ERR_CANCELED');
29
- }
30
- }
31
22
  /**
32
23
  * Main function to make HTTP requests.
33
24
  * @param options User defined settings
@@ -37,65 +28,114 @@ async function http(options, config) {
37
28
  const requestConfig = cafe_utility_1.Objects.deepMerge3(exports.DEFAULT_HTTP_CONFIG, config, options);
38
29
  if (options.signal) {
39
30
  requestConfig.signal = options.signal;
40
- throwIfAborted(options.signal, config);
41
31
  }
42
- maybeReplaceBodyBuffers(requestConfig);
32
+ attachBody(requestConfig);
43
33
  if (requestConfig.params) {
44
- const keys = Object.keys(requestConfig.params);
45
- for (const key of keys) {
46
- const value = requestConfig.params[key];
47
- if (value === undefined) {
48
- delete requestConfig.params[key];
49
- }
34
+ for (const k of Object.keys(requestConfig.params)) {
35
+ if (requestConfig.params[k] === undefined)
36
+ delete requestConfig.params[k];
50
37
  }
51
38
  }
39
+ const url = buildUrl(requestConfig);
40
+ const method = (requestConfig.method || 'GET').toUpperCase();
41
+ requestConfig.method = method;
42
+ options.onRequest?.({
43
+ method,
44
+ url,
45
+ headers: { ...requestConfig.headers },
46
+ params: requestConfig.params,
47
+ });
52
48
  let failedAttempts = 0;
53
49
  while (failedAttempts < MAX_FAILED_ATTEMPTS) {
54
- throwIfAborted(options.signal, config);
55
50
  try {
56
- debug(`${requestConfig.method || 'get'} ${cafe_utility_1.Strings.joinUrl([
57
- requestConfig.baseURL,
58
- requestConfig.url,
59
- ])}`, { headers: { ...requestConfig.headers }, params: requestConfig.params });
60
- maybeRunOnRequestHook(options, requestConfig);
61
- const response = await (0, axios_1.default)(requestConfig);
62
- return response;
51
+ debug(`${method} ${url}`, { headers: requestConfig.headers, params: requestConfig.params });
52
+ const res = await fetch(url, requestConfig);
53
+ if (!res.ok)
54
+ await throwHttpError(method, url, res, requestConfig.responseType);
55
+ return toBeeResponse(res, requestConfig.responseType);
63
56
  }
64
57
  catch (e) {
65
- if (e instanceof AxiosError) {
66
- if (e.code === 'ERR_CANCELED') {
67
- throwIfAborted({ aborted: true }, config, e.response?.data, e.response?.status);
68
- }
69
- if (e.code === 'ECONNABORTED' && options.endlesslyRetry) {
70
- failedAttempts++;
71
- await cafe_utility_1.System.sleepMillis(failedAttempts < DELAY_THRESHOLD ? DELAY_FAST : DELAY_SLOW);
72
- }
73
- else {
74
- throw new index_1.BeeResponseError(config.method || 'get', config.url || '<unknown>', e.message, e.response?.data, e.response?.status, e.response?.statusText);
75
- }
58
+ if (e instanceof index_1.BeeResponseError)
59
+ throw e;
60
+ const err = e;
61
+ if (err.name === 'TimeoutError' && options.endlesslyRetry) {
62
+ failedAttempts++;
63
+ await cafe_utility_1.System.sleepMillis(failedAttempts < FAST_RETRY_COUNT ? DELAY_FAST : DELAY_SLOW);
76
64
  }
77
65
  else {
78
- throw e;
66
+ throw toBeeError(err, method, url);
79
67
  }
80
68
  }
81
69
  }
82
70
  throw Error('Max number of failed attempts reached');
83
71
  }
84
- function maybeRunOnRequestHook(options, requestConfig) {
85
- if (options.onRequest) {
86
- options.onRequest({
87
- method: requestConfig.method || 'GET',
88
- url: cafe_utility_1.Strings.joinUrl([requestConfig.baseURL, requestConfig.url]),
89
- headers: { ...requestConfig.headers },
90
- params: requestConfig.params,
91
- });
72
+ function attachBody(config) {
73
+ if (config.data === undefined)
74
+ return;
75
+ const data = config.data;
76
+ const isJsonShape = (data !== null && typeof data === 'object' && data.constructor === Object) || Array.isArray(data);
77
+ if (isJsonShape) {
78
+ config.body = JSON.stringify(data);
79
+ config.headers = { 'content-type': 'application/json', ...config.headers };
80
+ }
81
+ else {
82
+ config.body = data;
83
+ config.duplex = 'half';
84
+ const ct = data instanceof Blob && data.type ? data.type : 'application/octet-stream';
85
+ config.headers = { 'content-type': ct, ...config.headers };
92
86
  }
93
87
  }
94
- function maybeReplaceBodyBuffers(config) {
95
- if (config.data && config.data instanceof Uint8Array) {
96
- config.data = config.data.buffer.slice(config.data.byteOffset, config.data.byteOffset + config.data.byteLength);
88
+ async function throwHttpError(method, url, res, responseType) {
89
+ const errBody = await toBeeResponse(res, responseType).catch(() => ({ data: undefined }));
90
+ const bodyMsg = typeof errBody.data === 'string' ? errBody.data : JSON.stringify(errBody.data);
91
+ const message = bodyMsg && bodyMsg !== 'undefined' ? `${res.statusText}: ${bodyMsg}` : res.statusText;
92
+ throw new index_1.BeeResponseError(method, url, message, errBody.data, res.status, res.statusText);
93
+ }
94
+ function toBeeError(err, method, url) {
95
+ if (err.name === 'AbortError') {
96
+ return new index_1.BeeResponseError(method, url, 'Request aborted', undefined, undefined, 'ERR_CANCELED');
97
97
  }
98
- if (config.data && typeof Buffer !== 'undefined' && Buffer.isBuffer(config.data)) {
99
- config.data = config.data.buffer.slice(config.data.byteOffset, config.data.byteOffset + config.data.byteLength);
98
+ const cause = err.cause;
99
+ const message = cause?.message ? `${err.message}: ${cause.message}` : err.message;
100
+ return new index_1.BeeResponseError(method, url, message);
101
+ }
102
+ async function toBeeResponse(res, responseType = 'json') {
103
+ let data;
104
+ switch (responseType) {
105
+ case 'arraybuffer':
106
+ data = await res.arrayBuffer();
107
+ break;
108
+ case 'text':
109
+ data = await res.text();
110
+ break;
111
+ case 'blob':
112
+ data = await res.blob();
113
+ break;
114
+ case 'stream':
115
+ data = res.body;
116
+ break;
117
+ case 'json':
118
+ default: {
119
+ const text = await res.text();
120
+ data = text ? JSON.parse(text) : null;
121
+ }
122
+ }
123
+ return {
124
+ data: data,
125
+ status: res.status,
126
+ statusText: res.statusText,
127
+ headers: Object.fromEntries(res.headers.entries()),
128
+ raw: res,
129
+ };
130
+ }
131
+ function buildUrl(config) {
132
+ let url = cafe_utility_1.Strings.joinUrl([config.baseURL ?? '', config.url ?? '']);
133
+ if (config.params) {
134
+ const qs = new URLSearchParams(Object.entries(config.params)
135
+ .filter(([, v]) => v !== undefined)
136
+ .map(([k, v]) => [k, String(v)])).toString();
137
+ if (qs)
138
+ url += `?${qs}`;
100
139
  }
140
+ return url;
101
141
  }
@@ -15,10 +15,17 @@ class TarStream {
15
15
  });
16
16
  }
17
17
  beginFile(path, size) {
18
- if (path.length > 100) {
19
- throw new Error(`File name too long: ${path}`);
18
+ const { name, prefix, longLink } = splitPath(path);
19
+ if (longLink) {
20
+ const encoder = new TextEncoder();
21
+ const pathData = encoder.encode(path + '\0');
22
+ this.pieces.push(createLongLinkHeader(pathData.length));
23
+ this.pieces.push(pathData);
24
+ const padding = pathData.length % 512 === 0 ? 0 : 512 - (pathData.length % 512);
25
+ if (padding > 0)
26
+ this.pieces.push(new Uint8Array(padding));
20
27
  }
21
- const header = createHeader(path, size);
28
+ const header = createHeader(name, prefix, size);
22
29
  this.pieces.push(header);
23
30
  this.currentFileSize = 0;
24
31
  }
@@ -37,7 +44,46 @@ class TarStream {
37
44
  }
38
45
  }
39
46
  exports.TarStream = TarStream;
40
- function createHeader(path, size) {
47
+ function splitPath(path) {
48
+ if (path.length <= 100)
49
+ return { name: path, prefix: '', longLink: false };
50
+ for (let i = path.length - 1; i >= 0; i--) {
51
+ if (path[i] === '/') {
52
+ const name = path.substring(i + 1);
53
+ const prefix = path.substring(0, i);
54
+ if (name.length <= 100 && prefix.length <= 155)
55
+ return { name, prefix, longLink: false };
56
+ }
57
+ }
58
+ // Filename itself is > 100 chars or path > 255 chars — use GNU LongLink
59
+ const lastSlash = path.lastIndexOf('/');
60
+ const truncatedName = (lastSlash >= 0 ? path.substring(lastSlash + 1) : path).slice(0, 100);
61
+ return { name: truncatedName, prefix: '', longLink: true };
62
+ }
63
+ function createLongLinkHeader(size) {
64
+ const encoder = new TextEncoder();
65
+ function writeToBuffer(str, offset, length) {
66
+ const bytes = encoder.encode(str);
67
+ header.set(bytes.slice(0, length), offset);
68
+ }
69
+ const header = new Uint8Array(512);
70
+ header.fill(0);
71
+ writeToBuffer('././@LongLink', 0, 100);
72
+ writeToBuffer('0000644\0', 100, 8);
73
+ writeToBuffer('0000000\0', 108, 8);
74
+ writeToBuffer('0000000\0', 116, 8);
75
+ writeToBuffer(size.toString(8).padStart(11, '0') + '\0', 124, 12);
76
+ writeToBuffer('00000000000\0', 136, 12);
77
+ writeToBuffer(' ', 148, 8);
78
+ writeToBuffer('L', 156, 1);
79
+ writeToBuffer('ustar\0\0', 257, 8);
80
+ let checksum = 0;
81
+ for (let i = 0; i < 512; i++)
82
+ checksum += header[i];
83
+ writeToBuffer(checksum.toString(8).padStart(6, '0') + '\0 ', 148, 8);
84
+ return header;
85
+ }
86
+ function createHeader(name, prefix, size) {
41
87
  const encoder = new TextEncoder();
42
88
  function writeToBuffer(str, offset, length) {
43
89
  const bytes = encoder.encode(str);
@@ -47,7 +93,7 @@ function createHeader(path, size) {
47
93
  const header = new Uint8Array(512);
48
94
  header.fill(0, 0, 512);
49
95
  // File name, truncated to 100 characters if necessary
50
- writeToBuffer(path.slice(0, 100).padEnd(100, '\0'), 0, 100);
96
+ writeToBuffer(name.slice(0, 100).padEnd(100, '\0'), 0, 100);
51
97
  // File mode (octal) and null-terminated
52
98
  writeToBuffer('0000777\0', 100, 8);
53
99
  // UID and GID (octal) and null-terminated
@@ -64,11 +110,13 @@ function createHeader(path, size) {
64
110
  writeToBuffer('0', 156, 1);
65
111
  // USTAR magic and version
66
112
  writeToBuffer('ustar\0\0', 257, 8);
113
+ // UStar prefix field (offset 345, 155 bytes) — used when path > 100 but fits split
114
+ if (prefix)
115
+ writeToBuffer(prefix.slice(0, 155).padEnd(155, '\0'), 345, 155);
67
116
  // Calculate checksum
68
117
  let checksum = 0;
69
- for (let i = 0; i < 512; i++) {
118
+ for (let i = 0; i < 512; i++)
70
119
  checksum += header[i];
71
- }
72
120
  writeToBuffer(checksum.toString(8).padStart(6, '0') + '\0 ', 148, 8);
73
121
  return header;
74
122
  }
@@ -8,10 +8,16 @@ class TarStream {
8
8
  this.currentFileSize = 0;
9
9
  }
10
10
  beginFile(path, size) {
11
- if (path.length > 100) {
12
- throw new Error(`File name too long: ${path}`);
11
+ const { name, prefix, longLink } = splitPath(path);
12
+ if (longLink) {
13
+ const pathData = Buffer.from(path + '\0');
14
+ this.output.write(createLongLinkHeader(pathData.length));
15
+ this.output.write(pathData);
16
+ const padding = pathData.length % 512 === 0 ? 0 : 512 - (pathData.length % 512);
17
+ if (padding > 0)
18
+ this.output.write(Buffer.alloc(padding, 0));
13
19
  }
14
- const header = createHeader(path, size);
20
+ const header = createHeader(name, prefix, size);
15
21
  this.output.write(header);
16
22
  this.currentFileSize = 0;
17
23
  }
@@ -44,11 +50,44 @@ class TarStream {
44
50
  }
45
51
  }
46
52
  exports.TarStream = TarStream;
47
- function createHeader(path, size) {
53
+ function splitPath(path) {
54
+ if (path.length <= 100)
55
+ return { name: path, prefix: '', longLink: false };
56
+ for (let i = path.length - 1; i >= 0; i--) {
57
+ if (path[i] === '/') {
58
+ const name = path.substring(i + 1);
59
+ const prefix = path.substring(0, i);
60
+ if (name.length <= 100 && prefix.length <= 155)
61
+ return { name, prefix, longLink: false };
62
+ }
63
+ }
64
+ // Filename itself is > 100 chars or path > 255 chars — use GNU LongLink
65
+ const lastSlash = path.lastIndexOf('/');
66
+ const truncatedName = (lastSlash >= 0 ? path.substring(lastSlash + 1) : path).slice(0, 100);
67
+ return { name: truncatedName, prefix: '', longLink: true };
68
+ }
69
+ function createLongLinkHeader(size) {
70
+ const header = Buffer.alloc(512, 0);
71
+ header.write('././@LongLink', 0, 100);
72
+ header.write('0000644\0', 100, 8);
73
+ header.write('0000000\0', 108, 8);
74
+ header.write('0000000\0', 116, 8);
75
+ header.write(size.toString(8).padStart(11, '0') + '\0', 124, 12);
76
+ header.write('00000000000\0', 136, 12);
77
+ header.write(' ', 148, 8);
78
+ header.write('L', 156, 1);
79
+ header.write('ustar\0\0', 257, 8);
80
+ let checksum = 0;
81
+ for (let i = 0; i < 512; i++)
82
+ checksum += header[i];
83
+ header.write(checksum.toString(8).padStart(6, '0') + '\0 ', 148, 8);
84
+ return header;
85
+ }
86
+ function createHeader(name, prefix, size) {
48
87
  // Initialize header with zeros
49
88
  const header = Buffer.alloc(512, 0);
50
89
  // File name, truncated to 100 characters if necessary
51
- header.write(path.slice(0, 100).padEnd(100, '\0'), 0, 100);
90
+ header.write(name.slice(0, 100).padEnd(100, '\0'), 0, 100);
52
91
  // File mode (octal) and null-terminated
53
92
  header.write('0000777\0', 100, 8);
54
93
  // UID and GID (octal) and null-terminated
@@ -65,11 +104,13 @@ function createHeader(path, size) {
65
104
  header.write('0', 156, 1);
66
105
  // USTAR magic and version
67
106
  header.write('ustar\0\0', 257, 8);
107
+ // UStar prefix field (offset 345, 155 bytes) — used when path > 100 but fits split
108
+ if (prefix)
109
+ header.write(prefix.slice(0, 155).padEnd(155, '\0'), 345, 155);
68
110
  // Calculate checksum
69
111
  let checksum = 0;
70
- for (let i = 0; i < 512; i++) {
112
+ for (let i = 0; i < 512; i++)
71
113
  checksum += header[i];
72
- }
73
114
  header.write(checksum.toString(8).padStart(6, '0') + '\0 ', 148, 8);
74
115
  return header;
75
116
  }
@@ -12,6 +12,9 @@ class BZZ {
12
12
  static fromPLUR(plur) {
13
13
  return new BZZ(new cafe_utility_1.FixedPointNumber(plur, BZZ.DIGITS));
14
14
  }
15
+ static fromFloat(float) {
16
+ return new BZZ(cafe_utility_1.FixedPointNumber.fromFloat(float, BZZ.DIGITS));
17
+ }
15
18
  toPLURString() {
16
19
  return this.state.toString();
17
20
  }
@@ -21,6 +24,9 @@ class BZZ {
21
24
  toDecimalString() {
22
25
  return this.state.toDecimalString();
23
26
  }
27
+ toFloat() {
28
+ return this.state.toFloat();
29
+ }
24
30
  toSignificantDigits(digits) {
25
31
  return this.toDecimalString().slice(0, this.toDecimalString().indexOf('.') + digits + 1);
26
32
  }
@@ -82,6 +88,9 @@ class DAI {
82
88
  static fromWei(wei) {
83
89
  return new DAI(new cafe_utility_1.FixedPointNumber(wei, DAI.DIGITS));
84
90
  }
91
+ static fromFloat(float) {
92
+ return new DAI(cafe_utility_1.FixedPointNumber.fromFloat(float, DAI.DIGITS));
93
+ }
85
94
  toWeiString() {
86
95
  return this.state.toString();
87
96
  }
@@ -94,6 +103,9 @@ class DAI {
94
103
  toSignificantDigits(digits) {
95
104
  return this.toDecimalString().slice(0, this.toDecimalString().indexOf('.') + digits + 1);
96
105
  }
106
+ toFloat() {
107
+ return this.state.toFloat();
108
+ }
97
109
  /**
98
110
  * Does not mutate the current DAI instance.
99
111
  *