@ar.io/sdk 3.22.0-alpha.1 → 3.22.0-alpha.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.
@@ -448,11 +448,7 @@ const utils_js_1 = require("./utils.js");
448
448
  (0, utils_js_1.makeCommand)({
449
449
  name: 'get-ants-for-address',
450
450
  description: 'Get the list of ANTs owned by an address according to the ANT registry',
451
- options: [
452
- options_js_1.optionMap.address,
453
- options_js_1.optionMap.antRegistryProcessId,
454
- options_js_1.optionMap.hyperbeamUrl,
455
- ],
451
+ options: [options_js_1.optionMap.address, options_js_1.optionMap.antRegistryProcessId],
456
452
  action: readCommands_js_1.listAntsForAddress,
457
453
  });
458
454
  // # ANTS
@@ -339,6 +339,7 @@ exports.globalOptions = [
339
339
  exports.optionMap.debug,
340
340
  exports.optionMap.arioProcessId,
341
341
  exports.optionMap.cuUrl,
342
+ exports.optionMap.hyperbeamUrl,
342
343
  ];
343
344
  exports.writeActionOptions = [exports.optionMap.skipConfirmation, exports.optionMap.tags];
344
345
  exports.arnsPurchaseOptions = [
@@ -181,6 +181,7 @@ function aoProcessFromOptions(options) {
181
181
  function readARIOFromOptions(options) {
182
182
  setLoggerIfDebug(options);
183
183
  return index_js_1.ARIO.init({
184
+ hyperbeamUrl: options.hyperbeamUrl,
184
185
  process: aoProcessFromOptions({
185
186
  cuUrl: 'https://cu.ardrive.io', // default to ardrive cu for ARIO process
186
187
  ...options,
@@ -227,6 +228,7 @@ function writeARIOFromOptions(options) {
227
228
  process: aoProcessFromOptions(options),
228
229
  signer,
229
230
  paymentUrl: options.paymentUrl,
231
+ hyperbeamUrl: options.hyperbeamUrl,
230
232
  }),
231
233
  signerAddress,
232
234
  };
@@ -487,6 +489,7 @@ function ANTProcessFromOptions(options) {
487
489
  function readANTFromOptions(options) {
488
490
  return index_js_1.ANT.init({
489
491
  process: ANTProcessFromOptions(options),
492
+ hyperbeamUrl: options.hyperbeamUrl,
490
493
  });
491
494
  }
492
495
  function writeANTFromOptions(options, signer) {
@@ -494,6 +497,7 @@ function writeANTFromOptions(options, signer) {
494
497
  return index_js_1.ANT.init({
495
498
  process: ANTProcessFromOptions(options),
496
499
  signer,
500
+ hyperbeamUrl: options.hyperbeamUrl,
497
501
  });
498
502
  }
499
503
  function booleanFromOptions(options, key) {
@@ -67,6 +67,7 @@ class ANT {
67
67
  : false;
68
68
  const ario = index_js_2.ARIO.init({
69
69
  process: new index_js_2.AOProcess({ processId: arioProcessId, ao }),
70
+ hyperbeamUrl,
70
71
  });
71
72
  const getAllAffiliatedNames = async () => {
72
73
  let cursor = undefined;
@@ -147,6 +148,7 @@ class ANT {
147
148
  logger,
148
149
  antRegistryId,
149
150
  onSigningProgress,
151
+ hyperbeamUrl,
150
152
  });
151
153
  // we could parallelize this, but then signing progress would be harder to track
152
154
  const reassignedNames = {};
@@ -471,7 +473,10 @@ class AoANTReadable {
471
473
  const currentModuleId = await this.getModuleId({ graphqlUrl, retries });
472
474
  // Get all versions from the ANT registry
473
475
  const antVersions = ant_versions_js_1.ANTVersions.init({
474
- processId: antRegistryId,
476
+ process: new index_js_2.AOProcess({
477
+ processId: antRegistryId,
478
+ ao: this.process.ao,
479
+ }),
475
480
  });
476
481
  const versions = await antVersions.getANTVersions();
477
482
  // Find the version that matches our module ID
@@ -510,7 +515,10 @@ class AoANTReadable {
510
515
  });
511
516
  // Get all versions from the ANT registry
512
517
  const antVersions = ant_versions_js_1.ANTVersions.init({
513
- processId: antRegistryId,
518
+ process: new index_js_2.AOProcess({
519
+ processId: antRegistryId,
520
+ ao: this.process.ao,
521
+ }),
514
522
  });
515
523
  const latestVersion = await antVersions.getLatestANTVersion();
516
524
  return currentVersion === latestVersion.version;
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HB = void 0;
4
+ const logger_js_1 = require("../logger.js");
5
+ class HB {
6
+ url;
7
+ processId;
8
+ isHyperBeamCompatible;
9
+ checkHyperBeamPromise;
10
+ logger;
11
+ hbTimeoutMs;
12
+ constructor(config) {
13
+ this.url = config.url;
14
+ this.processId = config.processId;
15
+ this.logger = config.logger ?? logger_js_1.Logger.default;
16
+ this.hbTimeoutMs = config.hbTimeoutMs ?? 5000;
17
+ this.isHyperBeamCompatible = undefined;
18
+ this.checkHyperBeamPromise = this.checkHyperBeamCompatibility();
19
+ }
20
+ /**
21
+ * fetches the meta data for the process
22
+ *
23
+ * @returns The meta data for the process
24
+ *
25
+ * @example
26
+ * const hyperbeam = new Hyperbeam({ url: 'https://hyperbeam.ario.permaweb.services', processId: 'qNvAoz0TgcH7DMg8BCVn8jF32QH5L6T29VjHxhHqqGE' });
27
+ * const meta = await hyperbeam.meta();
28
+ * console.log(meta);
29
+ */
30
+ async meta() {
31
+ const url = new URL(`${this.url}/${this.processId}~process@1.0/meta`);
32
+ return this.fetchHyperbeamPath({
33
+ path: url.toString(),
34
+ });
35
+ }
36
+ /**
37
+ * calls the process device /now function, which evaluates the current process state pulling new messages
38
+ * to get the latest state
39
+ *
40
+ * @param path - The path to the hb state
41
+ * @param json - Whether to return the result as JSON, defaults to true
42
+ * @returns The result of the compute operation
43
+ *
44
+ * @example
45
+ * const hyperbeam = new Hyperbeam({ url: 'https://hyperbeam.ario.permaweb.services', processId: 'qNvAoz0TgcH7DMg8BCVn8jF32QH5L6T29VjHxhHqqGE' });
46
+ * const result = await hyperbeam.now({ path: 'balances/QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ' });
47
+ * console.log(result);
48
+ */
49
+ async now({ path, json = false, }) {
50
+ return this.fetchHyperbeamPath({
51
+ path: `${this.url}/${this.processId}~process@1.0/now/${path}`,
52
+ json,
53
+ });
54
+ }
55
+ /**
56
+ * calls the process device /compute function, which uses the currently evaluated state in the node
57
+ *
58
+ * @param path - The path to the compute resource
59
+ * @param json - Whether to return the result as JSON, defaults to true
60
+ * @returns The result of the compute operation
61
+ *
62
+ * @example
63
+ * const hyperbeam = new Hyperbeam({ url: 'https://hyperbeam.ario.permaweb.services', processId: 'qNvAoz0TgcH7DMg8BCVn8jF32QH5L6T29VjHxhHqqGE' });
64
+ * const result = await hyperbeam.compute({ path: 'balances/QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ' });
65
+ * console.log(result);
66
+ */
67
+ async compute({ path, json = false, }) {
68
+ return this.fetchHyperbeamPath({
69
+ path: `${this.url}/${this.processId}~process@1.0/compute/${path}`,
70
+ json,
71
+ });
72
+ }
73
+ /**
74
+ * Checks if the process is HyperBeam compatible and caches the result.
75
+ *
76
+ * @returns {Promise<boolean>} True if the process is HyperBeam compatible, false otherwise.
77
+ */
78
+ async checkHyperBeamCompatibility({ minSlot, } = {}) {
79
+ // refetch if min slot is provided
80
+ if (minSlot !== undefined) {
81
+ this.isHyperBeamCompatible = undefined;
82
+ this.checkHyperBeamPromise = undefined;
83
+ }
84
+ if (this.checkHyperBeamPromise !== undefined) {
85
+ return this.checkHyperBeamPromise;
86
+ }
87
+ if (this.isHyperBeamCompatible !== undefined) {
88
+ return Promise.resolve(this.isHyperBeamCompatible);
89
+ }
90
+ const result = fetch(
91
+ // use /now to force a refresh of the cache state, then compute when calling it for keys
92
+ `${this.url.toString()}/${this.processId}~process@1.0/now`, {
93
+ method: 'HEAD',
94
+ signal: AbortSignal.timeout(this.hbTimeoutMs),
95
+ })
96
+ .then(async (res) => {
97
+ if (res.ok) {
98
+ if (minSlot !== undefined) {
99
+ const slotRes = await this.compute({
100
+ path: 'at-slot',
101
+ json: false,
102
+ });
103
+ const slot = Number(slotRes);
104
+ if (slot < minSlot) {
105
+ return false;
106
+ }
107
+ }
108
+ this.isHyperBeamCompatible = true;
109
+ return true;
110
+ }
111
+ this.isHyperBeamCompatible = false;
112
+ return false;
113
+ })
114
+ .catch((error) => {
115
+ this.logger.debug('Failed to check HyperBeam compatibility', {
116
+ cause: error,
117
+ });
118
+ this.isHyperBeamCompatible = false;
119
+ return false;
120
+ });
121
+ this.checkHyperBeamPromise = result;
122
+ return result;
123
+ }
124
+ async fetchHyperbeamPath({ path, json = true, }) {
125
+ try {
126
+ const url = new URL(path);
127
+ if (json) {
128
+ this.logger.debug('Fetching path as JSON', { path });
129
+ /**
130
+ * This is the (current) way to access data as json
131
+ * the old way is /~json@1.0/serialize path
132
+ */
133
+ url.searchParams.set('require-codec', 'application/json');
134
+ url.searchParams.set('accept-bundle', 'true');
135
+ const res = await fetch(url);
136
+ if (!res.ok) {
137
+ throw new Error(`Failed to fetch path as JSON: ${res.statusText}`);
138
+ }
139
+ const jsonResult = await res
140
+ .json()
141
+ .then((json) => json)
142
+ .catch((error) => {
143
+ this.logger.error('Failed to parse JSON', {
144
+ cause: error,
145
+ });
146
+ throw new Error(`Received response but failed to parse JSON: ${error.message}`);
147
+ });
148
+ if (typeof jsonResult !== 'object' ||
149
+ jsonResult === null ||
150
+ !('body' in jsonResult)) {
151
+ throw new Error('Response body missing in JSON response');
152
+ }
153
+ return jsonResult.body;
154
+ }
155
+ else {
156
+ this.logger.debug('Fetching path as text', { path });
157
+ const res = await fetch(url);
158
+ if (!res.ok) {
159
+ throw new Error(`Failed to fetch path: ${res.statusText}`);
160
+ }
161
+ const body = await res.text();
162
+ return body;
163
+ }
164
+ }
165
+ catch (error) {
166
+ this.logger.error('Failed to fetch path as JSON', {
167
+ cause: error,
168
+ });
169
+ throw error;
170
+ }
171
+ }
172
+ }
173
+ exports.HB = HB;
@@ -27,6 +27,7 @@ const arweave_js_2 = require("./arweave.js");
27
27
  const ao_process_js_1 = require("./contracts/ao-process.js");
28
28
  const error_js_1 = require("./error.js");
29
29
  const faucet_js_1 = require("./faucet.js");
30
+ const hb_js_1 = require("./hyperbeam/hb.js");
30
31
  const logger_js_1 = require("./logger.js");
31
32
  const turbo_js_1 = require("./turbo.js");
32
33
  class ARIO {
@@ -104,9 +105,9 @@ class ARIOReadable {
104
105
  hyperbeamUrl;
105
106
  paymentProvider; // TODO: this could be an array/map of payment providers
106
107
  logger = logger_js_1.Logger.default;
108
+ hb;
107
109
  constructor(config) {
108
110
  this.arweave = config?.arweave ?? arweave_js_2.defaultArweave;
109
- this.hyperbeamUrl = config?.hyperbeamUrl;
110
111
  if (config === undefined || Object.keys(config).length === 0) {
111
112
  this.process = new ao_process_js_1.AOProcess({
112
113
  processId: constants_js_1.ARIO_MAINNET_PROCESS_ID,
@@ -123,6 +124,19 @@ class ARIOReadable {
123
124
  else {
124
125
  throw new error_js_1.InvalidContractConfigurationError();
125
126
  }
127
+ // only use hyperbeam if the client has provided a hyperbeamUrl
128
+ // this will avoid overwhelming the HyperBeam node with requests
129
+ // as we shift using HyperBEAM for all ANT operations
130
+ if (config?.hyperbeamUrl !== undefined) {
131
+ this.hyperbeamUrl = config.hyperbeamUrl;
132
+ this.hb = new hb_js_1.HB({
133
+ url: this.hyperbeamUrl,
134
+ processId: this.process.processId,
135
+ });
136
+ this.logger.debug(`Using HyperBEAM node for process ${this.process.processId}`, {
137
+ hyperbeamUrl: this.hyperbeamUrl,
138
+ });
139
+ }
126
140
  this.paymentProvider = turbo_js_1.TurboArNSPaymentFactory.init({
127
141
  paymentUrl: config?.paymentUrl,
128
142
  });
@@ -222,6 +236,24 @@ class ARIOReadable {
222
236
  });
223
237
  }
224
238
  async getBalance({ address }) {
239
+ if (this.hb && (await this.hb.checkHyperBeamCompatibility())) {
240
+ this.logger.debug('Getting balance from HyperBEAM', { address });
241
+ const res = await this.hb
242
+ .compute({
243
+ path: `balances/${address}`,
244
+ })
245
+ .then((res) => Number(res))
246
+ .catch((error) => {
247
+ this.logger.error('Failed to get balance from HyperBEAM', {
248
+ cause: error,
249
+ });
250
+ return null;
251
+ });
252
+ if (res !== null)
253
+ return res;
254
+ // else fall through to CU read
255
+ this.logger.info('Failed to get balance from HyperBEAM, failing over to to CU read', { address });
256
+ }
225
257
  return this.process.read({
226
258
  tags: [
227
259
  { name: 'Action', value: 'Balance' },
@@ -649,6 +681,7 @@ class ARIOReadable {
649
681
  ao: this.process.ao,
650
682
  processId: nameData.processId,
651
683
  }),
684
+ hyperbeamUrl: this.hyperbeamUrl,
652
685
  });
653
686
  const [owner, antRecord] = await Promise.all([
654
687
  ant.getOwner(),
@@ -1286,6 +1319,7 @@ class ARIOWriteable extends ARIOReadable {
1286
1319
  ao: this.process.ao,
1287
1320
  }),
1288
1321
  signer: this.signer,
1322
+ hyperbeamUrl: this.hyperbeamUrl,
1289
1323
  });
1290
1324
  // approve the primary name request with the ant
1291
1325
  const approveResult = await antClient.approvePrimaryNameRequest({
@@ -39,7 +39,7 @@ async function spawnANT({ signer, module, ao = (0, aoconnect_1.connect)({
39
39
  MODE: 'legacy',
40
40
  }), scheduler = constants_js_1.DEFAULT_SCHEDULER_ID, state, tags = [], antRegistryId = constants_js_1.ANT_REGISTRY_ID, logger = index_js_1.Logger.default, authority = constants_js_1.AO_AUTHORITY, onSigningProgress = (name, payload) => {
41
41
  logger.debug('Signing progress', { name, payload });
42
- }, }) {
42
+ }, hyperbeamUrl, }) {
43
43
  if (state) {
44
44
  (0, schema_js_1.parseSchemaResult)(ant_js_1.SpawnANTStateSchema, state);
45
45
  }
@@ -194,7 +194,12 @@ async function spawnANT({ signer, module, ao = (0, aoconnect_1.connect)({
194
194
  // check the ACL for the owner
195
195
  const antRegistry = ant_registry_js_1.ANTRegistry.init({
196
196
  signer,
197
- processId: antRegistryId,
197
+ process: new index_js_1.AOProcess({
198
+ processId: antRegistryId,
199
+ ao,
200
+ logger,
201
+ }),
202
+ hyperbeamUrl,
198
203
  });
199
204
  let attempts = 0;
200
205
  const maxAttempts = 5;
@@ -225,7 +230,7 @@ async function spawnANT({ signer, module, ao = (0, aoconnect_1.connect)({
225
230
  }
226
231
  async function forkANT({ signer, antProcessId, logger = index_js_1.Logger.default, ao, moduleId, antRegistryId = constants_js_1.ANT_REGISTRY_ID, onSigningProgress = (name, payload) => {
227
232
  logger.debug('Forking ANT', { name, payload });
228
- }, }) {
233
+ }, hyperbeamUrl, }) {
229
234
  // get the state of the current ANT and use it to spawn a new ANT
230
235
  const ant = index_js_1.ANT.init({
231
236
  process: new index_js_1.AOProcess({
@@ -233,6 +238,7 @@ async function forkANT({ signer, antProcessId, logger = index_js_1.Logger.defaul
233
238
  ao,
234
239
  logger,
235
240
  }),
241
+ hyperbeamUrl,
236
242
  });
237
243
  const state = await ant.getState();
238
244
  if (state === undefined) {
@@ -256,6 +262,7 @@ async function forkANT({ signer, antProcessId, logger = index_js_1.Logger.defaul
256
262
  balances: state.Balances,
257
263
  logo: state.Logo,
258
264
  },
265
+ hyperbeamUrl,
259
266
  });
260
267
  return forkedProcessId;
261
268
  }
@@ -17,4 +17,4 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.version = void 0;
19
19
  // AUTOMATICALLY GENERATED FILE - DO NOT TOUCH
20
- exports.version = '3.22.0-alpha.1';
20
+ exports.version = '3.22.0-alpha.3';
@@ -446,11 +446,7 @@ makeCommand({
446
446
  makeCommand({
447
447
  name: 'get-ants-for-address',
448
448
  description: 'Get the list of ANTs owned by an address according to the ANT registry',
449
- options: [
450
- optionMap.address,
451
- optionMap.antRegistryProcessId,
452
- optionMap.hyperbeamUrl,
453
- ],
449
+ options: [optionMap.address, optionMap.antRegistryProcessId],
454
450
  action: listAntsForAddress,
455
451
  });
456
452
  // # ANTS
@@ -336,6 +336,7 @@ export const globalOptions = [
336
336
  optionMap.debug,
337
337
  optionMap.arioProcessId,
338
338
  optionMap.cuUrl,
339
+ optionMap.hyperbeamUrl,
339
340
  ];
340
341
  export const writeActionOptions = [optionMap.skipConfirmation, optionMap.tags];
341
342
  export const arnsPurchaseOptions = [
@@ -127,6 +127,7 @@ function aoProcessFromOptions(options) {
127
127
  export function readARIOFromOptions(options) {
128
128
  setLoggerIfDebug(options);
129
129
  return ARIO.init({
130
+ hyperbeamUrl: options.hyperbeamUrl,
130
131
  process: aoProcessFromOptions({
131
132
  cuUrl: 'https://cu.ardrive.io', // default to ardrive cu for ARIO process
132
133
  ...options,
@@ -173,6 +174,7 @@ export function writeARIOFromOptions(options) {
173
174
  process: aoProcessFromOptions(options),
174
175
  signer,
175
176
  paymentUrl: options.paymentUrl,
177
+ hyperbeamUrl: options.hyperbeamUrl,
176
178
  }),
177
179
  signerAddress,
178
180
  };
@@ -433,6 +435,7 @@ function ANTProcessFromOptions(options) {
433
435
  export function readANTFromOptions(options) {
434
436
  return ANT.init({
435
437
  process: ANTProcessFromOptions(options),
438
+ hyperbeamUrl: options.hyperbeamUrl,
436
439
  });
437
440
  }
438
441
  export function writeANTFromOptions(options, signer) {
@@ -440,6 +443,7 @@ export function writeANTFromOptions(options, signer) {
440
443
  return ANT.init({
441
444
  process: ANTProcessFromOptions(options),
442
445
  signer,
446
+ hyperbeamUrl: options.hyperbeamUrl,
443
447
  });
444
448
  }
445
449
  export function booleanFromOptions(options, key) {
@@ -64,6 +64,7 @@ export class ANT {
64
64
  : false;
65
65
  const ario = ARIO.init({
66
66
  process: new AOProcess({ processId: arioProcessId, ao }),
67
+ hyperbeamUrl,
67
68
  });
68
69
  const getAllAffiliatedNames = async () => {
69
70
  let cursor = undefined;
@@ -144,6 +145,7 @@ export class ANT {
144
145
  logger,
145
146
  antRegistryId,
146
147
  onSigningProgress,
148
+ hyperbeamUrl,
147
149
  });
148
150
  // we could parallelize this, but then signing progress would be harder to track
149
151
  const reassignedNames = {};
@@ -467,7 +469,10 @@ export class AoANTReadable {
467
469
  const currentModuleId = await this.getModuleId({ graphqlUrl, retries });
468
470
  // Get all versions from the ANT registry
469
471
  const antVersions = ANTVersions.init({
470
- processId: antRegistryId,
472
+ process: new AOProcess({
473
+ processId: antRegistryId,
474
+ ao: this.process.ao,
475
+ }),
471
476
  });
472
477
  const versions = await antVersions.getANTVersions();
473
478
  // Find the version that matches our module ID
@@ -506,7 +511,10 @@ export class AoANTReadable {
506
511
  });
507
512
  // Get all versions from the ANT registry
508
513
  const antVersions = ANTVersions.init({
509
- processId: antRegistryId,
514
+ process: new AOProcess({
515
+ processId: antRegistryId,
516
+ ao: this.process.ao,
517
+ }),
510
518
  });
511
519
  const latestVersion = await antVersions.getLatestANTVersion();
512
520
  return currentVersion === latestVersion.version;
@@ -0,0 +1,169 @@
1
+ import { Logger } from '../logger.js';
2
+ export class HB {
3
+ url;
4
+ processId;
5
+ isHyperBeamCompatible;
6
+ checkHyperBeamPromise;
7
+ logger;
8
+ hbTimeoutMs;
9
+ constructor(config) {
10
+ this.url = config.url;
11
+ this.processId = config.processId;
12
+ this.logger = config.logger ?? Logger.default;
13
+ this.hbTimeoutMs = config.hbTimeoutMs ?? 5000;
14
+ this.isHyperBeamCompatible = undefined;
15
+ this.checkHyperBeamPromise = this.checkHyperBeamCompatibility();
16
+ }
17
+ /**
18
+ * fetches the meta data for the process
19
+ *
20
+ * @returns The meta data for the process
21
+ *
22
+ * @example
23
+ * const hyperbeam = new Hyperbeam({ url: 'https://hyperbeam.ario.permaweb.services', processId: 'qNvAoz0TgcH7DMg8BCVn8jF32QH5L6T29VjHxhHqqGE' });
24
+ * const meta = await hyperbeam.meta();
25
+ * console.log(meta);
26
+ */
27
+ async meta() {
28
+ const url = new URL(`${this.url}/${this.processId}~process@1.0/meta`);
29
+ return this.fetchHyperbeamPath({
30
+ path: url.toString(),
31
+ });
32
+ }
33
+ /**
34
+ * calls the process device /now function, which evaluates the current process state pulling new messages
35
+ * to get the latest state
36
+ *
37
+ * @param path - The path to the hb state
38
+ * @param json - Whether to return the result as JSON, defaults to true
39
+ * @returns The result of the compute operation
40
+ *
41
+ * @example
42
+ * const hyperbeam = new Hyperbeam({ url: 'https://hyperbeam.ario.permaweb.services', processId: 'qNvAoz0TgcH7DMg8BCVn8jF32QH5L6T29VjHxhHqqGE' });
43
+ * const result = await hyperbeam.now({ path: 'balances/QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ' });
44
+ * console.log(result);
45
+ */
46
+ async now({ path, json = false, }) {
47
+ return this.fetchHyperbeamPath({
48
+ path: `${this.url}/${this.processId}~process@1.0/now/${path}`,
49
+ json,
50
+ });
51
+ }
52
+ /**
53
+ * calls the process device /compute function, which uses the currently evaluated state in the node
54
+ *
55
+ * @param path - The path to the compute resource
56
+ * @param json - Whether to return the result as JSON, defaults to true
57
+ * @returns The result of the compute operation
58
+ *
59
+ * @example
60
+ * const hyperbeam = new Hyperbeam({ url: 'https://hyperbeam.ario.permaweb.services', processId: 'qNvAoz0TgcH7DMg8BCVn8jF32QH5L6T29VjHxhHqqGE' });
61
+ * const result = await hyperbeam.compute({ path: 'balances/QGWqtJdLLgm2ehFWiiPzMaoFLD50CnGuzZIPEdoDRGQ' });
62
+ * console.log(result);
63
+ */
64
+ async compute({ path, json = false, }) {
65
+ return this.fetchHyperbeamPath({
66
+ path: `${this.url}/${this.processId}~process@1.0/compute/${path}`,
67
+ json,
68
+ });
69
+ }
70
+ /**
71
+ * Checks if the process is HyperBeam compatible and caches the result.
72
+ *
73
+ * @returns {Promise<boolean>} True if the process is HyperBeam compatible, false otherwise.
74
+ */
75
+ async checkHyperBeamCompatibility({ minSlot, } = {}) {
76
+ // refetch if min slot is provided
77
+ if (minSlot !== undefined) {
78
+ this.isHyperBeamCompatible = undefined;
79
+ this.checkHyperBeamPromise = undefined;
80
+ }
81
+ if (this.checkHyperBeamPromise !== undefined) {
82
+ return this.checkHyperBeamPromise;
83
+ }
84
+ if (this.isHyperBeamCompatible !== undefined) {
85
+ return Promise.resolve(this.isHyperBeamCompatible);
86
+ }
87
+ const result = fetch(
88
+ // use /now to force a refresh of the cache state, then compute when calling it for keys
89
+ `${this.url.toString()}/${this.processId}~process@1.0/now`, {
90
+ method: 'HEAD',
91
+ signal: AbortSignal.timeout(this.hbTimeoutMs),
92
+ })
93
+ .then(async (res) => {
94
+ if (res.ok) {
95
+ if (minSlot !== undefined) {
96
+ const slotRes = await this.compute({
97
+ path: 'at-slot',
98
+ json: false,
99
+ });
100
+ const slot = Number(slotRes);
101
+ if (slot < minSlot) {
102
+ return false;
103
+ }
104
+ }
105
+ this.isHyperBeamCompatible = true;
106
+ return true;
107
+ }
108
+ this.isHyperBeamCompatible = false;
109
+ return false;
110
+ })
111
+ .catch((error) => {
112
+ this.logger.debug('Failed to check HyperBeam compatibility', {
113
+ cause: error,
114
+ });
115
+ this.isHyperBeamCompatible = false;
116
+ return false;
117
+ });
118
+ this.checkHyperBeamPromise = result;
119
+ return result;
120
+ }
121
+ async fetchHyperbeamPath({ path, json = true, }) {
122
+ try {
123
+ const url = new URL(path);
124
+ if (json) {
125
+ this.logger.debug('Fetching path as JSON', { path });
126
+ /**
127
+ * This is the (current) way to access data as json
128
+ * the old way is /~json@1.0/serialize path
129
+ */
130
+ url.searchParams.set('require-codec', 'application/json');
131
+ url.searchParams.set('accept-bundle', 'true');
132
+ const res = await fetch(url);
133
+ if (!res.ok) {
134
+ throw new Error(`Failed to fetch path as JSON: ${res.statusText}`);
135
+ }
136
+ const jsonResult = await res
137
+ .json()
138
+ .then((json) => json)
139
+ .catch((error) => {
140
+ this.logger.error('Failed to parse JSON', {
141
+ cause: error,
142
+ });
143
+ throw new Error(`Received response but failed to parse JSON: ${error.message}`);
144
+ });
145
+ if (typeof jsonResult !== 'object' ||
146
+ jsonResult === null ||
147
+ !('body' in jsonResult)) {
148
+ throw new Error('Response body missing in JSON response');
149
+ }
150
+ return jsonResult.body;
151
+ }
152
+ else {
153
+ this.logger.debug('Fetching path as text', { path });
154
+ const res = await fetch(url);
155
+ if (!res.ok) {
156
+ throw new Error(`Failed to fetch path: ${res.statusText}`);
157
+ }
158
+ const body = await res.text();
159
+ return body;
160
+ }
161
+ }
162
+ catch (error) {
163
+ this.logger.error('Failed to fetch path as JSON', {
164
+ cause: error,
165
+ });
166
+ throw error;
167
+ }
168
+ }
169
+ }