@did-btcr2/bitcoin 0.3.3 → 0.4.0

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.
Files changed (104) hide show
  1. package/dist/cjs/bitcoin.js +82 -83
  2. package/dist/cjs/bitcoin.js.map +1 -1
  3. package/dist/cjs/client/http.js +9 -0
  4. package/dist/cjs/client/http.js.map +1 -0
  5. package/dist/cjs/client/rest/address.js +20 -28
  6. package/dist/cjs/client/rest/address.js.map +1 -1
  7. package/dist/cjs/client/rest/block.js +23 -22
  8. package/dist/cjs/client/rest/block.js.map +1 -1
  9. package/dist/cjs/client/rest/index.js +30 -83
  10. package/dist/cjs/client/rest/index.js.map +1 -1
  11. package/dist/cjs/client/rest/protocol.js +129 -0
  12. package/dist/cjs/client/rest/protocol.js.map +1 -0
  13. package/dist/cjs/client/rest/transaction.js +19 -21
  14. package/dist/cjs/client/rest/transaction.js.map +1 -1
  15. package/dist/cjs/client/rpc/index.js +77 -338
  16. package/dist/cjs/client/rpc/index.js.map +1 -1
  17. package/dist/cjs/client/rpc/interface.js +1 -6
  18. package/dist/cjs/client/rpc/interface.js.map +1 -1
  19. package/dist/cjs/client/rpc/json-rpc.js +40 -63
  20. package/dist/cjs/client/rpc/json-rpc.js.map +1 -1
  21. package/dist/cjs/client/rpc/protocol.js +134 -0
  22. package/dist/cjs/client/rpc/protocol.js.map +1 -0
  23. package/dist/cjs/constants.js +16 -5
  24. package/dist/cjs/constants.js.map +1 -1
  25. package/dist/cjs/errors.js.map +1 -1
  26. package/dist/cjs/index.js +21 -13
  27. package/dist/cjs/index.js.map +1 -1
  28. package/dist/cjs/types.js +5 -55
  29. package/dist/cjs/types.js.map +1 -1
  30. package/dist/esm/bitcoin.js +82 -83
  31. package/dist/esm/bitcoin.js.map +1 -1
  32. package/dist/esm/client/http.js +9 -0
  33. package/dist/esm/client/http.js.map +1 -0
  34. package/dist/esm/client/rest/address.js +20 -28
  35. package/dist/esm/client/rest/address.js.map +1 -1
  36. package/dist/esm/client/rest/block.js +23 -22
  37. package/dist/esm/client/rest/block.js.map +1 -1
  38. package/dist/esm/client/rest/index.js +30 -83
  39. package/dist/esm/client/rest/index.js.map +1 -1
  40. package/dist/esm/client/rest/protocol.js +129 -0
  41. package/dist/esm/client/rest/protocol.js.map +1 -0
  42. package/dist/esm/client/rest/transaction.js +19 -21
  43. package/dist/esm/client/rest/transaction.js.map +1 -1
  44. package/dist/esm/client/rpc/index.js +77 -338
  45. package/dist/esm/client/rpc/index.js.map +1 -1
  46. package/dist/esm/client/rpc/interface.js +1 -6
  47. package/dist/esm/client/rpc/interface.js.map +1 -1
  48. package/dist/esm/client/rpc/json-rpc.js +40 -63
  49. package/dist/esm/client/rpc/json-rpc.js.map +1 -1
  50. package/dist/esm/client/rpc/protocol.js +134 -0
  51. package/dist/esm/client/rpc/protocol.js.map +1 -0
  52. package/dist/esm/constants.js +16 -5
  53. package/dist/esm/constants.js.map +1 -1
  54. package/dist/esm/errors.js.map +1 -1
  55. package/dist/esm/index.js +21 -13
  56. package/dist/esm/index.js.map +1 -1
  57. package/dist/esm/types.js +5 -55
  58. package/dist/esm/types.js.map +1 -1
  59. package/dist/types/bitcoin.d.ts +66 -45
  60. package/dist/types/bitcoin.d.ts.map +1 -1
  61. package/dist/types/client/http.d.ts +21 -0
  62. package/dist/types/client/http.d.ts.map +1 -0
  63. package/dist/types/client/rest/address.d.ts +16 -19
  64. package/dist/types/client/rest/address.d.ts.map +1 -1
  65. package/dist/types/client/rest/block.d.ts +24 -16
  66. package/dist/types/client/rest/block.d.ts.map +1 -1
  67. package/dist/types/client/rest/index.d.ts +20 -51
  68. package/dist/types/client/rest/index.d.ts.map +1 -1
  69. package/dist/types/client/rest/protocol.d.ts +58 -0
  70. package/dist/types/client/rest/protocol.d.ts.map +1 -0
  71. package/dist/types/client/rest/transaction.d.ts +16 -17
  72. package/dist/types/client/rest/transaction.d.ts.map +1 -1
  73. package/dist/types/client/rpc/index.d.ts +105 -253
  74. package/dist/types/client/rpc/index.d.ts.map +1 -1
  75. package/dist/types/client/rpc/interface.d.ts +1 -4
  76. package/dist/types/client/rpc/interface.d.ts.map +1 -1
  77. package/dist/types/client/rpc/json-rpc.d.ts +20 -18
  78. package/dist/types/client/rpc/json-rpc.d.ts.map +1 -1
  79. package/dist/types/client/rpc/protocol.d.ts +82 -0
  80. package/dist/types/client/rpc/protocol.d.ts.map +1 -0
  81. package/dist/types/constants.d.ts +16 -5
  82. package/dist/types/constants.d.ts.map +1 -1
  83. package/dist/types/errors.d.ts +4 -3
  84. package/dist/types/errors.d.ts.map +1 -1
  85. package/dist/types/index.d.ts +20 -13
  86. package/dist/types/index.d.ts.map +1 -1
  87. package/dist/types/types.d.ts +98 -696
  88. package/dist/types/types.d.ts.map +1 -1
  89. package/package.json +3 -3
  90. package/src/bitcoin.ts +104 -117
  91. package/src/client/http.ts +28 -0
  92. package/src/client/rest/address.ts +24 -30
  93. package/src/client/rest/block.ts +29 -26
  94. package/src/client/rest/index.ts +37 -93
  95. package/src/client/rest/protocol.ts +153 -0
  96. package/src/client/rest/transaction.ts +23 -23
  97. package/src/client/rpc/index.ts +142 -380
  98. package/src/client/rpc/interface.ts +42 -51
  99. package/src/client/rpc/json-rpc.ts +53 -60
  100. package/src/client/rpc/protocol.ts +160 -0
  101. package/src/constants.ts +17 -5
  102. package/src/errors.ts +10 -3
  103. package/src/index.ts +81 -13
  104. package/src/types.ts +332 -973
@@ -4,102 +4,101 @@ import { BitcoinCoreRpcClient } from './client/rpc/index.js';
4
4
  import { getNetwork } from './network.js';
5
5
  import { DEFAULT_BITCOIN_NETWORK_CONFIG } from './constants.js';
6
6
  /**
7
- * The BitcoinNetworkConnection class contains relevant data and methods for interacting with different Bitcoin network.
8
- * @name BitcoinNetworkConnection
9
- * @type {BitcoinNetworkConnection}
7
+ * Represents a connection to a single Bitcoin network.
8
+ * Holds the REST and optional RPC clients for that network.
9
+ *
10
+ * The underlying clients use a sans-I/O protocol layer that separates
11
+ * request construction from HTTP execution. By default, requests are
12
+ * executed via the global `fetch` function. Supply a custom
13
+ * {@link HttpExecutor} to use any HTTP client.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // Quick setup with defaults (uses fetch)
18
+ * const btc = BitcoinConnection.forNetwork('regtest');
19
+ *
20
+ * // With a custom HTTP executor
21
+ * const btc = BitcoinConnection.forNetwork('testnet4', {
22
+ * rest: { host: 'https://my-mempool/api' },
23
+ * executor: myCustomExecutor,
24
+ * });
25
+ *
26
+ * // Direct usage
27
+ * const tx = await btc.rest.transaction.get(txid);
28
+ * const block = await btc.rpc?.getBlock({ height: 100 });
29
+ *
30
+ * // Sans-I/O: build requests without performing I/O
31
+ * const req = btc.rest.protocol.getTx(txid);
32
+ * const res = await myHttpClient.execute(req);
33
+ * ```
10
34
  */
11
- export class BitcoinNetworkConnection {
12
- network;
13
- bitcoin;
14
- testnet3;
15
- testnet4;
16
- signet;
17
- mutinynet;
18
- regtest;
19
- /**
20
- * Creates an instance of the Bitcoin class.
21
- * @param {BitcoinNetworkConfigMap} configs Optional configuration object for the Bitcoin client. If not provided, it will
22
- * be loaded from the BITCOIN_CLIENT_CONFIG environment variables.
23
- * @throws {MethodError} If no configs is passed and BITCOIN_NETWORK_CONFIG is missing or invalid.
24
- */
25
- constructor(configs) {
26
- const BITCOIN_NETWORK_CONFIG = process.env.BITCOIN_NETWORK_CONFIG ?? JSON.stringify(configs ?? DEFAULT_BITCOIN_NETWORK_CONFIG);
27
- if (!BITCOIN_NETWORK_CONFIG) {
28
- throw new MethodError('No BITCOIN_NETWORK_CONFIG available: must pass `configs` to constructor or set `BITCOIN_NETWORK_CONFIG` in env', 'MISSING_BITCOIN_NETWORK_CONFIG', { BITCOIN_NETWORK_CONFIG });
29
- }
30
- // Check if BITCOIN_NETWORK_CONFIG is parsable JSON string
31
- if (!JSON.parsable(BITCOIN_NETWORK_CONFIG)) {
32
- throw new MethodError('Parsing failed: malformed BITCOIN_NETWORK_CONFIG', 'MISSING_MALFORMED_BITCOIN_NETWORK_CONFIG', { BITCOIN_NETWORK_CONFIG });
33
- }
34
- // Parse the BITCOIN_NETWORK_CONFIG
35
- const networkConfigs = JSON.parse(BITCOIN_NETWORK_CONFIG);
36
- // Set a list of available networks
37
- const networks = ['bitcoin', 'testnet3', 'testnet4', 'signet', 'mutinynet', 'regtest'];
38
- // Iterate over the networks and create the client connections
39
- for (const network of networks) {
40
- const networkConfig = (configs?.[network] ?? networkConfigs[network]);
41
- if (networkConfig) {
42
- this[network] = {
43
- name: network,
44
- config: networkConfig,
45
- rpc: new BitcoinCoreRpcClient(networkConfig.rpc),
46
- rest: new BitcoinRestClient(networkConfig.rest),
47
- data: getNetwork(network),
48
- };
49
- }
50
- }
51
- // Load and check the ACTIVE_NETWORK variable
52
- const ACTIVE_NETWORK = (process.env.ACTIVE_NETWORK?.toLowerCase() ?? 'regtest');
53
- if (!ACTIVE_NETWORK) {
54
- throw new MethodError('Missing ACTIVE_NETWORK environment variable', 'MISSING_ACTIVE_NETWORK', { ACTIVE_NETWORK });
55
- }
56
- if (!this[ACTIVE_NETWORK]) {
57
- throw new MethodError(`No configuration found for ACTIVE_NETWORK='${ACTIVE_NETWORK}'`, 'MISSING_CONFIG_FOR_NETWORK');
58
- }
59
- this.network = this[ACTIVE_NETWORK];
60
- }
61
- /**
62
- * Get the Bitcoin network configuration for a specific network.
63
- * @param {keyof AvailableNetworks} network - The Bitcoin network (e.g., 'bitcoin', 'testnet3', 'signet', 'regtest').
64
- * @returns {Bitcoin} The Bitcoin object.
65
- */
66
- getNetworkConnection(network) {
67
- const availableNetwork = network;
68
- if (!this[availableNetwork]) {
69
- throw new MethodError(`No configuration found for network='${availableNetwork}'`, 'MISSING_CONFIG_FOR_NETWORK');
70
- }
71
- return this;
35
+ export class BitcoinConnection {
36
+ /** The network this connection targets. */
37
+ name;
38
+ /** REST client (Esplora API). */
39
+ rest;
40
+ /** RPC client (Bitcoin Core). May be undefined if not configured. */
41
+ rpc;
42
+ /** bitcoinjs-lib network data (for address derivation, PSBT signing, etc.). */
43
+ data;
44
+ constructor(options) {
45
+ this.name = options.network;
46
+ this.rest = new BitcoinRestClient(options.rest, options.executor);
47
+ this.rpc = options.rpc ? new BitcoinCoreRpcClient(options.rpc, options.executor) : undefined;
48
+ this.data = getNetwork(options.network);
72
49
  }
73
50
  /**
74
- * Sets the active Bitcoin network.
75
- * @param {keyof AvailableNetworks} active - The Bitcoin network to set as active (e.g., 'bitcoin', 'testnet3', 'signet', 'regtest').
76
- * @throws {MethodError} If no configuration is found for the specified network.
51
+ * Create a connection for a single network with optional REST/RPC endpoint overrides.
52
+ * Merges overrides on top of defaults from DEFAULT_BITCOIN_NETWORK_CONFIG.
53
+ * Does not read environment variables.
54
+ *
55
+ * @param network The network name (e.g., 'regtest', 'testnet4', 'bitcoin').
56
+ * @param overrides Optional endpoint and executor overrides.
57
+ * @returns A BitcoinConnection for the requested network.
77
58
  */
78
- setActiveNetwork(active) {
79
- const availableNetwork = active;
80
- if (!this[availableNetwork]) {
81
- throw new MethodError(`No configuration found for network='${availableNetwork}'`, 'MISSING_CONFIG_FOR_NETWORK');
59
+ static forNetwork(network, overrides) {
60
+ const defaults = DEFAULT_BITCOIN_NETWORK_CONFIG[network];
61
+ if (!defaults) {
62
+ throw new MethodError(`Unknown network '${network}'. Available: bitcoin, testnet3, testnet4, signet, mutinynet, regtest`, 'UNKNOWN_NETWORK', { network });
82
63
  }
83
- this.network = this[availableNetwork];
64
+ const restCfg = { ...defaults.rest, ...overrides?.rest };
65
+ const hasRpc = defaults.rpc !== undefined || overrides?.rpc !== undefined;
66
+ const rpcCfg = hasRpc
67
+ ? { ...defaults.rpc, ...overrides?.rpc }
68
+ : undefined;
69
+ return new BitcoinConnection({
70
+ network,
71
+ rest: restCfg,
72
+ rpc: rpcCfg,
73
+ executor: overrides?.executor,
74
+ });
84
75
  }
85
76
  /**
86
- * Converts Bitcoin (BTC) to satoshis (SAT).
87
- * @param {number} btc - The amount in BTC.
88
- * @returns {number} The amount in SAT.
77
+ * Converts Bitcoin (BTC) to satoshis.
78
+ * Uses string-based arithmetic to avoid floating-point precision errors.
79
+ * @throws {RangeError} If the value has more than 8 decimal places.
89
80
  */
90
81
  static btcToSats(btc) {
91
- return Math.round(btc * 1e8);
82
+ const str = btc.toFixed(8);
83
+ const [whole, frac] = str.split('.');
84
+ // Verify no precision beyond 8 decimals was lost
85
+ if (Math.abs(btc - Number(str)) > Number.EPSILON) {
86
+ throw new RangeError(`BTC value ${btc} exceeds 8 decimal places of precision`);
87
+ }
88
+ return Number(whole) * 1e8 + Number(frac);
92
89
  }
93
- ;
94
90
  /**
95
- * Converts satoshis (SAT) to Bitcoin (BTC).
96
- * @param {number} sats - The amount in SAT.
97
- * @returns {number} The amount in BTC.
91
+ * Converts satoshis to Bitcoin (BTC).
92
+ * Uses string-based arithmetic to avoid floating-point precision errors.
93
+ * @param sats Must be a non-negative integer.
98
94
  */
99
95
  static satsToBtc(sats) {
100
- return sats / 1e8;
96
+ const negative = sats < 0;
97
+ const abs = Math.abs(sats);
98
+ const whole = Math.floor(abs / 1e8);
99
+ const frac = abs % 1e8;
100
+ const result = Number(`${whole}.${frac.toString().padStart(8, '0')}`);
101
+ return negative ? -result : result;
101
102
  }
102
- ;
103
103
  }
104
- export const bitcoin = new BitcoinNetworkConnection();
105
104
  //# sourceMappingURL=bitcoin.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bitcoin.js","sourceRoot":"","sources":["../../src/bitcoin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,OAAO,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAchE;;;;GAIG;AACH,MAAM,OAAO,wBAAwB;IAC5B,OAAO,CAAuB;IAC9B,OAAO,CAAwB;IAC/B,QAAQ,CAAwB;IAChC,QAAQ,CAAwB;IAChC,MAAM,CAAwB;IAC9B,SAAS,CAAwB;IACjC,OAAO,CAAwB;IAEtC;;;;;OAKG;IACH,YAAY,OAAiC;QAC3C,MAAM,sBAAsB,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,8BAA8B,CAAC,CAAC;QAE/H,IAAG,CAAC,sBAAsB,EAAE,CAAC;YAC3B,MAAM,IAAI,WAAW,CACnB,gHAAgH,EAChH,gCAAgC,EAChC,EAAE,sBAAsB,EAAE,CAC3B,CAAC;QACJ,CAAC;QAED,0DAA0D;QAC1D,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,WAAW,CACnB,kDAAkD,EAClD,0CAA0C,EAC1C,EAAE,sBAAsB,EAAE,CAC3B,CAAC;QACJ,CAAC;QAED,mCAAmC;QACnC,MAAM,cAAc,GAAwC,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAE/F,mCAAmC;QACnC,MAAM,QAAQ,GAAgC,CAAC,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;QAEpH,8DAA8D;QAC9D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,aAAa,GAAwB,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,IAAI,cAAc,CAAC,OAAO,CAAC,CAAwB,CAAC;YAClH,IAAI,aAAa,EAAE,CAAC;gBAClB,IAAI,CAAC,OAAO,CAAC,GAAG;oBACd,IAAI,EAAK,OAAO;oBAChB,MAAM,EAAG,aAAa;oBACtB,GAAG,EAAM,IAAI,oBAAoB,CAAC,aAAa,CAAC,GAAG,CAAC;oBACpD,IAAI,EAAK,IAAI,iBAAiB,CAAC,aAAa,CAAC,IAAI,CAAC;oBAClD,IAAI,EAAK,UAAU,CAAC,OAAO,CAAC;iBAC7B,CAAC;YACJ,CAAC;QACH,CAAC;QAED,6CAA6C;QAC7C,MAAM,cAAc,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,WAAW,EAAE,IAAI,SAAS,CAA4B,CAAC;QAC3G,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,WAAW,CAAC,6CAA6C,EAAE,wBAAwB,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;QACrH,CAAC;QAGD,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,WAAW,CACnB,8CAA8C,cAAc,GAAG,EAC/D,4BAA4B,CAC7B,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACI,oBAAoB,CAAC,OAAe;QACzC,MAAM,gBAAgB,GAAG,OAAkC,CAAC;QAC5D,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,WAAW,CACnB,uCAAuC,gBAAgB,GAAG,EAC1D,4BAA4B,CAC7B,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;OAIG;IACI,gBAAgB,CAAC,MAAc;QACpC,MAAM,gBAAgB,GAAG,MAAiC,CAAC;QAC3D,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,WAAW,CACnB,uCAAuC,gBAAgB,GAAG,EAC1D,4BAA4B,CAC7B,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAyB,CAAC;IAChE,CAAC;IAED;;;;OAIG;IACI,MAAM,CAAC,SAAS,CAAE,GAAW;QAClC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;IAC/B,CAAC;IAAA,CAAC;IAEF;;;;OAIG;IACI,MAAM,CAAC,SAAS,CAAE,IAAY;QACnC,OAAO,IAAI,GAAG,GAAG,CAAC;IACpB,CAAC;IAAA,CAAC;CACH;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,wBAAwB,EAAE,CAAC"}
1
+ {"version":3,"file":"bitcoin.js","sourceRoot":"","sources":["../../src/bitcoin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Default executor backed by the global `fetch` function.
3
+ */
4
+ export const defaultHttpExecutor = (req) => fetch(req.url, {
5
+ method: req.method,
6
+ headers: req.headers,
7
+ body: req.body,
8
+ });
9
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/client/http.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAiB,CAAC,GAAgB,EAAqB,EAAE,CACvF,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;IACb,MAAM,EAAI,GAAG,CAAC,MAAM;IACpB,OAAO,EAAG,GAAG,CAAC,OAAO;IACrB,IAAI,EAAM,GAAG,CAAC,IAAI;CACnB,CAAC,CAAC"}
@@ -1,28 +1,27 @@
1
1
  /**
2
- * Implements a strongly-typed BitcoinRest to connect to remote bitcoin node via REST API for address-related operations.
3
- * @class BitcoinAddress
4
- * @type {BitcoinAddress}
2
+ * Address-related Esplora REST API operations.
5
3
  */
6
4
  export class BitcoinAddress {
7
- api;
8
- constructor(api) {
9
- this.api = api;
5
+ protocol;
6
+ exec;
7
+ constructor(protocol, exec) {
8
+ this.protocol = protocol;
9
+ this.exec = exec;
10
10
  }
11
11
  /**
12
12
  * Get transaction history for the specified address/scripthash, sorted with newest first.
13
13
  * Returns up to 50 mempool transactions plus the first 25 confirmed transactions.
14
14
  * See {@link https://github.com/blockstream/esplora/blob/master/API.md#get-addressaddresstxs | Esplora GET /address/:address/txs } for details.
15
15
  * @param {string} addressOrScripthash The address or scripthash to check.
16
- * @returns {Promise<Array<RawTransactionRest>>} A promise resolving to an array of {@link RawTransactionRest} objects.
16
+ * @returns {Promise<Array<RawTransactionRest>>} Transaction history.
17
17
  */
18
18
  async getTxs(addressOrScripthash) {
19
- return await this.api({ path: `/address/${addressOrScripthash}/txs` });
19
+ return await this.exec(this.protocol.getAddressTxs(addressOrScripthash));
20
20
  }
21
21
  /**
22
- * Calls getAddressTxs and checks if any funds come back.
23
- * Toggle if those funds are confirmed.
22
+ * Checks if an address has any confirmed funded transactions.
24
23
  * @param {string} addressOrScripthash The address or scripthash to check.
25
- * @returns {Promise<boolean>} True if the address has any funds, false otherwise.
24
+ * @returns {Promise<boolean>} True if the address has confirmed funds.
26
25
  */
27
26
  async isFundedAddress(addressOrScripthash) {
28
27
  const txs = await this.getConfirmedTxs(addressOrScripthash);
@@ -33,44 +32,37 @@ export class BitcoinAddress {
33
32
  * Get unconfirmed transaction history for the specified address/scripthash.
34
33
  * Returns up to 50 transactions (no paging).
35
34
  * @param {string} addressOrScripthash The address or scripthash to check.
36
- * @returns {Promise<Array<RawTransactionRest>>} A promise resolving to an array of {@link RawTransactionRest} objects.
35
+ * @returns {Promise<Array<RawTransactionRest>>} Unconfirmed transactions.
37
36
  */
38
37
  async getTxsMempool(addressOrScripthash) {
39
- return await this.api({ path: `/address/${addressOrScripthash}/txs/mempool` });
38
+ return await this.exec(this.protocol.getAddressTxsMempool(addressOrScripthash));
40
39
  }
41
40
  /**
42
41
  * Get information about an address/scripthash.
43
- * Available fields: address/scripthash, chain_stats and mempool_stats.
44
- * {chain,mempool}_stats each contain an object with tx_count, funded_txo_count, funded_txo_sum, spent_txo_count and spent_txo_sum.
45
- * Elements-based chains don't have the {funded,spent}_txo_sum fields.
46
42
  * @param {string} addressOrScripthash The address or scripthash to check.
47
- * @returns {Promise<AddressInfo>} A promise resolving to an {@link AddressInfo} object.
43
+ * @returns {Promise<AddressInfo>} Address information.
48
44
  */
49
45
  async getInfo(addressOrScripthash) {
50
- return await this.api({ path: `/address/${addressOrScripthash}` });
46
+ return await this.exec(this.protocol.getAddressInfo(addressOrScripthash));
51
47
  }
52
48
  /**
53
49
  * Get confirmed transaction history for the specified address/scripthash, sorted with newest first.
54
- * Returns 25 transactions per page. More can be requested by specifying the last txid seen by the previous query.
50
+ * Returns 25 transactions per page.
55
51
  * @param {string} addressOrScripthash The address or scripthash to check.
56
- * @param lastSeenTxId The last transaction id seen by the previous query (optional).
57
- * @returns {Promise<Array<RawTransactionRest>>} A promise resolving to an array of {@link RawTransactionRest} objects.
52
+ * @param {string} [lastSeenTxId] The last transaction id seen by the previous query for pagination.
53
+ * @returns {Promise<Array<RawTransactionRest>>} Confirmed transactions.
58
54
  */
59
55
  async getConfirmedTxs(addressOrScripthash, lastSeenTxId) {
60
- return await this.api({
61
- path: lastSeenTxId
62
- ? `/address/${addressOrScripthash}/txs/chain/${lastSeenTxId}`
63
- : `/address/${addressOrScripthash}/txs/chain`
64
- });
56
+ return await this.exec(this.protocol.getAddressTxsChain(addressOrScripthash, lastSeenTxId));
65
57
  }
66
58
  /**
67
59
  * Get the list of unspent transaction outputs associated with the address/scripthash.
68
60
  * See {@link https://github.com/Blockstream/esplora/blob/master/API.md#get-addressaddressutxo | Esplora GET /address/:address/utxo } for details.
69
61
  * @param {string} addressOrScripthash The address or scripthash to check.
70
- * @returns {Promise<Array<RawTransactionRest>>} A promise resolving to an array of {@link RawTransactionRest} objects.
62
+ * @returns {Promise<Array<AddressUtxo>>} Unspent transaction outputs.
71
63
  */
72
64
  async getUtxos(addressOrScripthash) {
73
- return await this.api({ path: `/address/${addressOrScripthash}/utxo` });
65
+ return await this.exec(this.protocol.getAddressUtxos(addressOrScripthash));
74
66
  }
75
67
  }
76
68
  //# sourceMappingURL=address.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"address.js","sourceRoot":"","sources":["../../../../src/client/rest/address.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,OAAO,cAAc;IACjB,GAAG,CAA8C;IAEzD,YAAY,GAAgD;QAC1D,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,MAAM,CAAC,mBAA2B;QAC7C,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,YAAY,mBAAmB,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,eAAe,CAAC,mBAA2B;QACtD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAsB,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9E,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,aAAa,CAAC,mBAA2B;QACpD,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,YAAY,mBAAmB,cAAc,EAAE,CAAC,CAAC;IACjF,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,OAAO,CAAC,mBAA2B;QAC9C,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,YAAY,mBAAmB,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,eAAe,CAAC,mBAA2B,EAAE,YAAqB;QAC7E,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC;YACpB,IAAI,EAAG,YAAY;gBACjB,CAAC,CAAC,YAAY,mBAAmB,cAAc,YAAY,EAAE;gBAC7D,CAAC,CAAC,YAAY,mBAAmB,YAAY;SAChD,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,QAAQ,CAAC,mBAA2B;QAC/C,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,YAAY,mBAAmB,OAAO,EAAE,CAAC,CAAC;IAC1E,CAAC;CACF"}
1
+ {"version":3,"file":"address.js","sourceRoot":"","sources":["../../../../src/client/rest/address.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,OAAO,cAAc;IACR,QAAQ,CAAkB;IAC1B,IAAI,CAAqC;IAE1D,YAAY,QAAyB,EAAE,IAAwC;QAC7E,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,MAAM,CAAC,mBAA2B;QAC7C,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC3E,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,eAAe,CAAC,mBAA2B;QACtD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC;QAC5D,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAsB,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QAC9E,OAAO,CAAC,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,aAAa,CAAC,mBAA2B;QACpD,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAClF,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,OAAO,CAAC,mBAA2B;QAC9C,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC5E,CAAC;IAED;;;;;;OAMG;IACI,KAAK,CAAC,eAAe,CAAC,mBAA2B,EAAE,YAAqB;QAC7E,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,mBAAmB,EAAE,YAAY,CAAC,CAAC,CAAC;IAC9F,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,QAAQ,CAAC,mBAA2B;QAC/C,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,mBAAmB,CAAC,CAAC,CAAC;IAC7E,CAAC;CACF"}
@@ -1,50 +1,51 @@
1
1
  import { BitcoinRestError } from '../../errors.js';
2
2
  /**
3
- * Implements a strongly-typed BitcoinRest to connect to remote bitcoin node via REST API for block-related operations.
4
- * @class BitcoinBlock
5
- * @type {BitcoinBlock}
3
+ * Block-related Esplora REST API operations.
4
+ *
5
+ * Note: The Esplora API always returns the same block format regardless
6
+ * of any "verbosity" setting (unlike Bitcoin Core RPC). Use the RPC
7
+ * client if you need verbosity-controlled block responses.
6
8
  */
7
9
  export class BitcoinBlock {
8
- api;
9
- constructor(api) {
10
- this.api = api;
10
+ protocol;
11
+ exec;
12
+ constructor(protocol, exec) {
13
+ this.protocol = protocol;
14
+ this.exec = exec;
11
15
  }
12
16
  /**
13
- * Returns the blockheight of the most-work fully-validated chain. The genesis block has height 0.
14
- * @returns {Blockheight} The number of the blockheight with the most-work of the fully-validated chain.
17
+ * Returns the blockheight of the most-work fully-validated chain.
18
+ * @returns {Promise<number>} The current block height.
15
19
  */
16
20
  async count() {
17
- return await this.api({ path: '/blocks/tip/height' });
21
+ return await this.exec(this.protocol.getBlockTipHeight());
18
22
  }
19
23
  /**
20
- * Returns the block data associated with a `blockhash` of a valid block.
21
- * @param {GetBlockParams} params See {@link GetBlockParams} for details.
22
- * @param {?string} params.blockhash The blockhash of the block to query.
23
- * @param {?number} params.height The block height of the block to query.
24
- * @returns {BlockResponse} A promise resolving to a {@link BlockResponse} formatted depending on `verbosity` level.
25
- * @throws {BitcoinRpcError} If neither `blockhash` nor `height` is provided.
24
+ * Returns the Esplora block data for a given blockhash or height.
25
+ * @param {object} params The block hash or height.
26
+ * @param {string} [params.blockhash] The blockhash of the block to query.
27
+ * @param {number} [params.height] The block height of the block to query.
28
+ * @returns {Promise<EsploraBlock | undefined>} The block data.
29
+ * @throws {BitcoinRestError} If neither blockhash nor height is provided.
26
30
  */
27
31
  async get({ blockhash, height }) {
28
- // Check if blockhash or height is provided, if neither throw an error
29
32
  if (!blockhash && height === undefined) {
30
- throw new BitcoinRestError('INVALID_PARAMS_GET_BLOCK: blockhash or height required', { blockhash, height });
33
+ throw new BitcoinRestError('blockhash or height required', { blockhash, height });
31
34
  }
32
- // If height is provided, get the blockhash
33
35
  blockhash ??= await this.getHash(height);
34
36
  if (!blockhash || typeof blockhash !== 'string') {
35
37
  return undefined;
36
38
  }
37
- // Get the block data
38
- return await this.api({ path: `/block/${blockhash}` });
39
+ return await this.exec(this.protocol.getBlock(blockhash));
39
40
  }
40
41
  /**
41
42
  * Get the block hash for a given block height.
42
43
  * See {@link https://github.com/blockstream/esplora/blob/master/API.md#get-block-heightheight | Esplora GET /block-height/:height } for details.
43
44
  * @param {number} height The block height (required).
44
- * @returns {Promise<string>} The hash of the block currently at height..
45
+ * @returns {Promise<string>} The hash of the block at the given height.
45
46
  */
46
47
  async getHash(height) {
47
- return await this.api({ path: `/block-height/${height}` });
48
+ return await this.exec(this.protocol.getBlockHeight(height));
48
49
  }
49
50
  }
50
51
  //# sourceMappingURL=block.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"block.js","sourceRoot":"","sources":["../../../../src/client/rest/block.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD;;;;GAIG;AACH,MAAM,OAAO,YAAY;IACf,GAAG,CAA8C;IAEzD,YAAY,GAAgD;QAC1D,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,KAAK;QAChB,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,oBAAoB,EAAE,CAAC,CAAC;IACxD,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,MAAM,EAAkB;QACpD,sEAAsE;QACtE,IAAG,CAAC,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACtC,MAAM,IAAI,gBAAgB,CAAC,wDAAwD,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9G,CAAC;QAED,2CAA2C;QAC3C,SAAS,KAAK,MAAM,IAAI,CAAC,OAAO,CAAC,MAAO,CAAC,CAAC;QAC1C,IAAG,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAC/C,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,qBAAqB;QACrB,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,SAAS,EAAE,EAAE,CAAY,CAAC;IACpE,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,OAAO,CAAC,MAAc;QACjC,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,iBAAiB,MAAM,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;CACF"}
1
+ {"version":3,"file":"block.js","sourceRoot":"","sources":["../../../../src/client/rest/block.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAKnD;;;;;;GAMG;AACH,MAAM,OAAO,YAAY;IACN,QAAQ,CAAkB;IAC1B,IAAI,CAAqC;IAE1D,YAAY,QAAyB,EAAE,IAAwC;QAC7E,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,KAAK;QAChB,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAC5D,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,MAAM,EAA2C;QAC7E,IAAI,CAAC,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACvC,MAAM,IAAI,gBAAgB,CAAC,8BAA8B,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QACpF,CAAC;QAED,SAAS,KAAK,MAAM,IAAI,CAAC,OAAO,CAAC,MAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAChD,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAiB,CAAC;IAC5E,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,OAAO,CAAC,MAAc;QACjC,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,CAAC;CACF"}
@@ -1,107 +1,54 @@
1
1
  import { MethodError } from '@did-btcr2/common';
2
- import { DEFAULT_BITCOIN_NETWORK_CONFIG } from '../../constants.js';
3
2
  import { BitcoinAddress } from './address.js';
4
3
  import { BitcoinBlock } from './block.js';
5
4
  import { BitcoinTransaction } from './transaction.js';
6
- import { RestClientConfig } from '../../types.js';
5
+ import { EsploraProtocol } from './protocol.js';
6
+ import { defaultHttpExecutor } from '../http.js';
7
7
  /**
8
- * Implements a strongly-typed BitcoinRestClient to connect to remote bitcoin node via REST API.
9
- * @class BitcoinRestClient
10
- * @type {BitcoinRestClient}
8
+ * Esplora REST API client for Bitcoin.
9
+ *
10
+ * Wraps the sans-I/O {@link EsploraProtocol} with an {@link HttpExecutor}
11
+ * for convenience. Users who want full control over I/O can access the
12
+ * protocol layer directly via the {@link protocol} property.
11
13
  */
12
14
  export class BitcoinRestClient {
13
- /**
14
- * The encapsulated {@link RestClientConfig} object.
15
- * @private
16
- * @type {RestClientConfig}
17
- */
18
15
  _config;
19
16
  /**
20
- * The api calls related to bitcoin transactions.
21
- * @type {BitcoinTransaction}
17
+ * The sans-I/O protocol layer. Use this to build {@link HttpRequest}
18
+ * descriptors without performing any I/O.
22
19
  */
20
+ protocol;
21
+ executor;
22
+ /** Transaction-related API calls. */
23
23
  transaction;
24
- /**
25
- * The api calls related to bitcoin blocks.
26
- * @type {BitcoinBlock}
27
- */
24
+ /** Block-related API calls. */
28
25
  block;
29
- /**
30
- * The api calls related to bitcoin addresses.
31
- * @type {BitcoinAddress}
32
- */
26
+ /** Address-related API calls. */
33
27
  address;
34
- /**
35
- * The API call method that can be used to make requests to the REST API.
36
- * @returns {Promise<any>} A promise resolving to the response data.
37
- */
38
- api = this.call;
39
- constructor(config) {
40
- this._config = new RestClientConfig(config);
41
- this.api = this.call.bind(this);
42
- this.transaction = new BitcoinTransaction(this.api);
43
- this.block = new BitcoinBlock(this.api);
44
- this.address = new BitcoinAddress(this.api);
28
+ constructor(config, executor) {
29
+ this._config = config;
30
+ this.protocol = new EsploraProtocol(config);
31
+ this.executor = executor ?? defaultHttpExecutor;
32
+ const exec = this.executeRequest.bind(this);
33
+ this.transaction = new BitcoinTransaction(this.protocol, exec);
34
+ this.block = new BitcoinBlock(this.protocol, exec);
35
+ this.address = new BitcoinAddress(this.protocol, exec);
45
36
  }
46
- /**
47
- * Get the configuration object.
48
- * @private
49
- */
50
37
  get config() {
51
- const config = this._config;
52
- return config;
38
+ return this._config;
53
39
  }
54
40
  /**
55
- * Static method connects to a bitcoin node running a esplora REST API.
56
- *
57
- * @param {RestClientConfig} config The configuration object for the client (optional).
58
- * @returns {BitcoinRestClient} A new {@link BitcoinRestClient} instance.
59
- * @example
60
- * ```
61
- * const rest = BitcoinRestClient.connect();
62
- * ```
41
+ * Execute an {@link HttpRequest} built by the protocol layer,
42
+ * parse the response, and throw on HTTP errors.
63
43
  */
64
- static connect(config) {
65
- return new BitcoinRestClient(config ?? DEFAULT_BITCOIN_NETWORK_CONFIG.regtest.rest);
66
- }
67
- /**
68
- * Make a REST API call to the configured Bitcoin node.
69
- * @private
70
- * @param {RestApiCallParams} params The parameters for the API call. See {@link RestApiCallParams} for details.
71
- * @param {string} [params.path] The path to the API endpoint (required).
72
- * @param {string} [params.url] The full URL to the API endpoint (optional).
73
- * @param {string} [params.method] The HTTP method to use (default is 'GET').
74
- * @param {any} [params.body] The body of the request (optional).
75
- * @param {any} [params.headers] Additional headers to include in the request (optional).
76
- * @returns {Promise<any>} A promise resolving to the response data.
77
- */
78
- async call({ path, url, method, body, headers }) {
79
- // Construct the URL if not provided
80
- url ??= `${this.config.host.replaceEnd('/')}${path}`;
81
- // Set the method to GET if not provided
82
- method ??= 'GET';
83
- // Construct the request options
84
- const requestInit = {
85
- method,
86
- headers: headers ?? {
87
- 'Content-Type': 'application/json',
88
- ...this.config.headers,
89
- }
90
- };
91
- // If the method is POST or PUT, add the body to the request
92
- if (body) {
93
- requestInit.body = JSON.is(body) ? JSON.stringify(body) : body;
94
- requestInit.method = 'POST';
95
- }
96
- // Make the request
97
- const response = await fetch(url, requestInit);
98
- // Check if the response is a text/plain response
99
- const data = (response.headers.get('Content-Type') ?? 'json') === 'text/plain'
44
+ async executeRequest(request) {
45
+ const response = await this.executor(request);
46
+ const contentType = response.headers.get('Content-Type') ?? '';
47
+ const data = contentType.includes('text/plain')
100
48
  ? await response.text()
101
49
  : await response.json();
102
- // Check if the response is ok (status in the range 200-299)
103
50
  if (!response.ok) {
104
- throw new MethodError(`Request to ${url} failed: ${response.status} - ${response.statusText}`, 'FAILED_HTTP_REQUEST', { data, response });
51
+ throw new MethodError(`Request to ${request.url} failed: ${response.status} - ${response.statusText}`, 'FAILED_HTTP_REQUEST', { data });
105
52
  }
106
53
  return data;
107
54
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/client/rest/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/client/rest/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAA6B,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;;;GAMG;AACH,MAAM,OAAO,iBAAiB;IACpB,OAAO,CAAa;IAE5B;;;OAGG;IACM,QAAQ,CAAkB;IAElB,QAAQ,CAAe;IAExC,qCAAqC;IAC9B,WAAW,CAAqB;IAEvC,+BAA+B;IACxB,KAAK,CAAe;IAE3B,iCAAiC;IAC1B,OAAO,CAAiB;IAE/B,YAAY,MAAkB,EAAE,QAAuB;QACrD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,GAAG,QAAQ,IAAI,mBAAmB,CAAC;QAEhD,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,WAAW,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC/D,IAAI,CAAC,KAAK,GAAG,IAAI,YAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;IAED,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,cAAc,CAAC,OAAoB;QAC/C,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE9C,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,WAAW,CAAC,QAAQ,CAAC,YAAY,CAAC;YAC7C,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE;YACvB,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAE1B,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,WAAW,CACnB,cAAc,OAAO,CAAC,GAAG,YAAY,QAAQ,CAAC,MAAM,MAAM,QAAQ,CAAC,UAAU,EAAE,EAC/E,qBAAqB,EACrB,EAAE,IAAI,EAAE,CACT,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}