@ohmpathorn/block-node-client 0.1.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.
- package/README.md +322 -0
- package/dist/client.d.ts +81 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +251 -0
- package/dist/client.js.map +1 -0
- package/dist/decoder.d.ts +27 -0
- package/dist/decoder.d.ts.map +1 -0
- package/dist/decoder.js +606 -0
- package/dist/decoder.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/proto-manager.d.ts +22 -0
- package/dist/proto-manager.d.ts.map +1 -0
- package/dist/proto-manager.js +207 -0
- package/dist/proto-manager.js.map +1 -0
- package/dist/types.d.ts +192 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +25 -0
- package/dist/types.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
# @ohmpathorn/block-node-client
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@ohmpathorn/block-node-client)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
|
|
7
|
+
JavaScript/TypeScript SDK for the [Hedera Block Node](https://github.com/hiero-ledger/hiero-block-node) gRPC API.
|
|
8
|
+
|
|
9
|
+
Wraps all four Block Node RPCs with a clean async/callback interface, full TypeScript types, and built-in protobuf decoding — so you never have to touch raw bytes.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @ohmpathorn/block-node-client
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { BlockNodeClient } from "@ohmpathorn/block-node-client";
|
|
23
|
+
|
|
24
|
+
const client = new BlockNodeClient({
|
|
25
|
+
endpoint: "s01.test.blk.ams.lat.ope.eng.hashgraph.io:40840",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Check node health
|
|
29
|
+
const status = await client.serverStatus();
|
|
30
|
+
console.log("Latest block:", status.lastAvailableBlock);
|
|
31
|
+
|
|
32
|
+
// Subscribe to live blocks
|
|
33
|
+
const handle = client.subscribeBlockStream(
|
|
34
|
+
{ startBlockNumber: status.lastAvailableBlock - 10n },
|
|
35
|
+
{
|
|
36
|
+
onStatus: (code, name) => console.log("Stream:", name),
|
|
37
|
+
onBlock: (num, items) => console.log(`Block ${num} — ${items.length} items`),
|
|
38
|
+
onError: (err) => console.error(err.message),
|
|
39
|
+
onEnd: () => client.close(),
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Cancel after 60 seconds
|
|
44
|
+
setTimeout(() => handle.cancel(), 60_000);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Testnet endpoints
|
|
50
|
+
|
|
51
|
+
All endpoints use port **40840**.
|
|
52
|
+
|
|
53
|
+
| Region | Endpoint |
|
|
54
|
+
|--------|----------|
|
|
55
|
+
| Amsterdam | `s01.test.blk.ams.lat.ope.eng.hashgraph.io:40840` |
|
|
56
|
+
| Singapore | `s01.test.blk.sgp.lat.ope.eng.hashgraph.io:40840` |
|
|
57
|
+
| Chicago | `s01.test.blk.chi.lat.ope.eng.hashgraph.io:40840` |
|
|
58
|
+
| Tier 2 | `lfh01.testnet.blocknode.hashgraph-devops.com:40840` |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## API reference
|
|
63
|
+
|
|
64
|
+
### `new BlockNodeClient(options)`
|
|
65
|
+
|
|
66
|
+
| Option | Type | Default | Description |
|
|
67
|
+
|--------|------|---------|-------------|
|
|
68
|
+
| `endpoint` | `string` | required | `host:port` of the block node |
|
|
69
|
+
| `tls` | `"tls" \| "insecure"` | `"tls"` | TLS or plaintext |
|
|
70
|
+
| `timeout` | `number` | `10000` | Unary call timeout in ms |
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### `client.serverStatus()` → `Promise<ServerStatusResponse>`
|
|
75
|
+
|
|
76
|
+
Lightweight health check. Returns the available block range and node state. Always call this first to validate the range before subscribing.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const status = await client.serverStatus();
|
|
80
|
+
console.log(status.firstAvailableBlock); // bigint
|
|
81
|
+
console.log(status.lastAvailableBlock); // bigint
|
|
82
|
+
console.log(status.onlyLatestState); // boolean
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### `client.serverStatusDetail()` → `Promise<ServerStatusDetailResponse>`
|
|
88
|
+
|
|
89
|
+
Full capability report: software versions, all available block ranges, and the network address book.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const detail = await client.serverStatusDetail();
|
|
93
|
+
|
|
94
|
+
const v = detail.versionInformation?.blockNodeVersion;
|
|
95
|
+
console.log(`Block node: ${v?.major}.${v?.minor}.${v?.patch}`);
|
|
96
|
+
|
|
97
|
+
detail.availableRanges.forEach(r =>
|
|
98
|
+
console.log(`${r.rangeStart} → ${r.rangeEnd}`)
|
|
99
|
+
);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### `client.getBlock(request)` → `Promise<GetBlockResponse>`
|
|
105
|
+
|
|
106
|
+
Fetch a single block by number or the latest block.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// By number
|
|
110
|
+
const result = await client.getBlock({ blockNumber: 38764703n });
|
|
111
|
+
|
|
112
|
+
// Latest
|
|
113
|
+
const latest = await client.getBlock({ retrieveLatest: true });
|
|
114
|
+
|
|
115
|
+
console.log(result.statusName); // "SUCCESS"
|
|
116
|
+
console.log(result.block?.length); // number of BlockItems
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### `client.subscribeBlockStream(options, callbacks)` → `StreamHandle`
|
|
122
|
+
|
|
123
|
+
Server-streaming RPC. Buffers `BlockItemSet` messages and fires `onBlock` once per complete block (after `end_of_block` is received).
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
const handle = client.subscribeBlockStream(
|
|
127
|
+
{
|
|
128
|
+
startBlockNumber: 38764000n,
|
|
129
|
+
endBlockNumber: 0n, // 0 = live open-ended stream
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
onStatus: (code, name) => console.log("Stream status:", name),
|
|
133
|
+
onBlock: (blockNumber, items) => {
|
|
134
|
+
items.filter(i => i.kind === "event_transaction").forEach(item => {
|
|
135
|
+
console.log(item.payload.type); // "CRYPTO_TRANSFER"
|
|
136
|
+
console.log(item.payload.transactionId?.toString()); // "0.0.3569@..."
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
onError: (err) => console.error(err.message),
|
|
140
|
+
onEnd: () => console.log("Stream ended"),
|
|
141
|
+
},
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
handle.cancel(); // stop at any time
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
> **Note:** `endBlockNumber: 0` is automatically converted to `uint64_max` for a live stream. Pass an explicit number for a bounded historical range.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
### `client.close()`
|
|
152
|
+
|
|
153
|
+
Releases gRPC connections and cleans up temp files. Always call when done.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Block items
|
|
158
|
+
|
|
159
|
+
Each block is an ordered list of `BlockItem` objects. Every item has:
|
|
160
|
+
|
|
161
|
+
| Property | Type | Description |
|
|
162
|
+
|----------|------|-------------|
|
|
163
|
+
| `kind` | `BlockItemKind` | Which oneof field is set |
|
|
164
|
+
| `payload` | typed per `kind` | Decoded message |
|
|
165
|
+
| `raw` | `Buffer` | Original bytes |
|
|
166
|
+
|
|
167
|
+
### Item kinds
|
|
168
|
+
|
|
169
|
+
| Kind | Description | Payload type |
|
|
170
|
+
|------|-------------|--------------|
|
|
171
|
+
| `block_header` | Block number, hash algorithm, SW versions | `BlockHeader` |
|
|
172
|
+
| `event_header` | Gossip event metadata | `EventHeader` |
|
|
173
|
+
| `round_header` | Consensus round number | `RoundHeader` |
|
|
174
|
+
| `event_transaction` | Transaction type, ID, memo, raw bytes | `EventTransaction` |
|
|
175
|
+
| `transaction_result` | Status, fee, payer, transfers, tx hash | `TransactionResult` |
|
|
176
|
+
| `state_changes` | Which state tables changed | `StateChanges` |
|
|
177
|
+
| `filtered_item_hash` | Hash placeholder for filtered items | `FilteredItemHash` |
|
|
178
|
+
| `block_proof` | TSS block signature + Merkle proof | `BlockProof` |
|
|
179
|
+
| `record_file` | Historical block (pre-HIP-1056) with embedded transactions | `RecordFile` |
|
|
180
|
+
| `address_book_proof` | Network address book hashes for record-file blocks | `AddressBookProof` |
|
|
181
|
+
|
|
182
|
+
### Example: switch on kind
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
items.forEach(item => {
|
|
186
|
+
switch (item.kind) {
|
|
187
|
+
case "block_header":
|
|
188
|
+
console.log("Block #", item.payload.number);
|
|
189
|
+
break;
|
|
190
|
+
|
|
191
|
+
case "event_transaction":
|
|
192
|
+
console.log(item.payload.type); // "CRYPTO_TRANSFER"
|
|
193
|
+
console.log(item.payload.transactionId?.toString()); // "0.0.3@1753912345.000000001"
|
|
194
|
+
console.log(item.payload.memo);
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case "transaction_result":
|
|
198
|
+
console.log(item.payload.statusName); // "SUCCESS"
|
|
199
|
+
console.log(item.payload.transactionFee); // bigint tinybars
|
|
200
|
+
item.payload.transfers.forEach(t =>
|
|
201
|
+
console.log(` ${t.accountId?.accountNum}: ${t.amount}`)
|
|
202
|
+
);
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case "record_file":
|
|
206
|
+
// Historical block format — transactions are nested inside
|
|
207
|
+
item.payload.transactions.forEach(tx =>
|
|
208
|
+
console.log(tx.type, tx.transactionId?.toString())
|
|
209
|
+
);
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Transaction IDs
|
|
218
|
+
|
|
219
|
+
Hedera transaction IDs follow the format `shard.realm.account@seconds.nanos`:
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const txId = item.payload.transactionId;
|
|
223
|
+
console.log(txId.toString());
|
|
224
|
+
// "0.0.3569@1753912345.123456789"
|
|
225
|
+
|
|
226
|
+
console.log(txId.accountId);
|
|
227
|
+
// { shardNum: 0n, realmNum: 0n, accountNum: 3569n }
|
|
228
|
+
|
|
229
|
+
console.log(txId.transactionValidStart.toDate());
|
|
230
|
+
// JavaScript Date
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Transaction types
|
|
236
|
+
|
|
237
|
+
All 55 Hedera transaction types are decoded automatically. You can also import the lookup table directly:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
import { TX_TYPES } from "@ohmpathorn/block-node-client";
|
|
241
|
+
|
|
242
|
+
TX_TYPES[14] // "CRYPTO_TRANSFER"
|
|
243
|
+
TX_TYPES[27] // "CONSENSUS_SUBMIT_MESSAGE"
|
|
244
|
+
TX_TYPES[50] // "ETHEREUM_TRANSACTION"
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Status codes
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
import { SubscribeStreamCode, BlockResponseCode } from "@ohmpathorn/block-node-client";
|
|
253
|
+
|
|
254
|
+
// Stream subscription status (first message)
|
|
255
|
+
SubscribeStreamCode.SUCCESS // 1
|
|
256
|
+
SubscribeStreamCode.INVALID_START_BLOCK_NUMBER // 4
|
|
257
|
+
SubscribeStreamCode.INVALID_END_BLOCK_NUMBER // 5 — caused by sending end_block = 0
|
|
258
|
+
SubscribeStreamCode.NOT_AVAILABLE // 6 — block not on this node
|
|
259
|
+
|
|
260
|
+
// getBlock response status
|
|
261
|
+
BlockResponseCode.SUCCESS // 1
|
|
262
|
+
BlockResponseCode.NOT_FOUND // 4
|
|
263
|
+
BlockResponseCode.NOT_AVAILABLE // 5
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## TypeScript
|
|
269
|
+
|
|
270
|
+
Full types ship with the package. Import what you need:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
import {
|
|
274
|
+
BlockNodeClient,
|
|
275
|
+
BlockNodeClientOptions,
|
|
276
|
+
ServerStatusResponse,
|
|
277
|
+
ServerStatusDetailResponse,
|
|
278
|
+
BlockItem,
|
|
279
|
+
BlockItemKind,
|
|
280
|
+
BlockHeader,
|
|
281
|
+
EventTransaction,
|
|
282
|
+
TransactionResult,
|
|
283
|
+
RecordFile,
|
|
284
|
+
AddressBookProof,
|
|
285
|
+
StreamHandle,
|
|
286
|
+
SubscribeStreamCode,
|
|
287
|
+
BlockResponseCode,
|
|
288
|
+
} from "@ohmpathorn/block-node-client";
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Examples
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# Basic usage — all four APIs
|
|
297
|
+
node examples/basic.js
|
|
298
|
+
|
|
299
|
+
# Live transaction monitor
|
|
300
|
+
node examples/transaction-monitor.js
|
|
301
|
+
|
|
302
|
+
# Multi-endpoint failover
|
|
303
|
+
node examples/multi-endpoint.js
|
|
304
|
+
|
|
305
|
+
# Plaintext instead of TLS
|
|
306
|
+
USE_INSECURE=1 node examples/basic.js
|
|
307
|
+
|
|
308
|
+
# Different endpoint
|
|
309
|
+
ENDPOINT=s01.test.blk.sgp.lat.ope.eng.hashgraph.io:40840 node examples/basic.js
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## How it works
|
|
315
|
+
|
|
316
|
+
The SDK writes the necessary protobuf definitions to a temp directory at startup and loads them via `@grpc/proto-loader`. Inner `BlockItem` payloads are decoded manually using a hand-rolled `ProtoReader` rather than generated stubs — this makes the SDK resilient to schema evolution and unknown fields.
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## License
|
|
321
|
+
|
|
322
|
+
[Apache 2.0](LICENSE)
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BlockNodeClientOptions, ServerStatusResponse, ServerStatusDetailResponse, BlockRequestSpecifier, GetBlockResponse, BlockStreamOptions, BlockStreamCallbacks, StreamHandle } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* BlockNodeClient — the main entry point for the Hedera Block Node SDK.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { BlockNodeClient } from "@ohmpathorn/block-node-client";
|
|
8
|
+
*
|
|
9
|
+
* const client = new BlockNodeClient({
|
|
10
|
+
* endpoint: "s01.test.blk.ams.lat.ope.eng.hashgraph.io:40840",
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* const status = await client.serverStatus();
|
|
14
|
+
* console.log(status.lastAvailableBlock);
|
|
15
|
+
*
|
|
16
|
+
* client.close();
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare class BlockNodeClient {
|
|
20
|
+
private readonly options;
|
|
21
|
+
private proto;
|
|
22
|
+
private timeout;
|
|
23
|
+
constructor(options: BlockNodeClientOptions);
|
|
24
|
+
/**
|
|
25
|
+
* Lightweight health check.
|
|
26
|
+
* Returns the first/last available block and whether the node is running.
|
|
27
|
+
* Always call this before subscribing to validate the block range.
|
|
28
|
+
*/
|
|
29
|
+
serverStatus(): Promise<ServerStatusResponse>;
|
|
30
|
+
/**
|
|
31
|
+
* Full capability report: versions, available block ranges, TSS data,
|
|
32
|
+
* and the current network address book.
|
|
33
|
+
*/
|
|
34
|
+
serverStatusDetail(): Promise<ServerStatusDetailResponse>;
|
|
35
|
+
/**
|
|
36
|
+
* Fetch a single block by number or retrieve the latest block.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const result = await client.getBlock({ blockNumber: 38764703n });
|
|
41
|
+
* const latest = await client.getBlock({ retrieveLatest: true });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
getBlock(request: BlockRequestSpecifier): Promise<GetBlockResponse>;
|
|
45
|
+
/**
|
|
46
|
+
* Subscribe to a live or historical block stream.
|
|
47
|
+
*
|
|
48
|
+
* The server sends `block_items` batches followed by `end_of_block` for each
|
|
49
|
+
* block. This method accumulates items per block and calls `onBlock` once the
|
|
50
|
+
* block is complete.
|
|
51
|
+
*
|
|
52
|
+
* Passing `endBlockNumber: 0` (or omitting it) subscribes to a live stream.
|
|
53
|
+
*
|
|
54
|
+
* @returns A `StreamHandle` with a `cancel()` method.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const status = await client.serverStatus();
|
|
59
|
+
*
|
|
60
|
+
* const handle = client.subscribeBlockStream(
|
|
61
|
+
* { startBlockNumber: status.lastAvailableBlock - 10n },
|
|
62
|
+
* {
|
|
63
|
+
* onStatus: (code, name) => console.log("Stream status:", name),
|
|
64
|
+
* onBlock: (num, items) => console.log(`Block ${num}: ${items.length} items`),
|
|
65
|
+
* onError: (err) => console.error("Stream error:", err.message),
|
|
66
|
+
* onEnd: () => console.log("Stream ended"),
|
|
67
|
+
* },
|
|
68
|
+
* );
|
|
69
|
+
*
|
|
70
|
+
* // Cancel after 30 seconds
|
|
71
|
+
* setTimeout(() => handle.cancel(), 30_000);
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
subscribeBlockStream(options: BlockStreamOptions, callbacks: BlockStreamCallbacks): StreamHandle;
|
|
75
|
+
/**
|
|
76
|
+
* Release gRPC resources and clean up temp proto files.
|
|
77
|
+
* Call when you're done with the client.
|
|
78
|
+
*/
|
|
79
|
+
close(): void;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,gBAAgB,EAEhB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EAMb,MAAM,SAAS,CAAC;AAcjB;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAe;IAId,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,OAAO,CAAS;gBAEK,OAAO,EAAE,sBAAsB;IAU5D;;;;OAIG;IACH,YAAY,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAqB7C;;;OAGG;IACH,kBAAkB,IAAI,OAAO,CAAC,0BAA0B,CAAC;IA2CzD;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAqCnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,oBAAoB,CAClB,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,oBAAoB,GAC9B,YAAY;IA0Df;;;OAGG;IACH,KAAK,IAAI,IAAI;CAGd"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.BlockNodeClient = void 0;
|
|
37
|
+
const grpc = __importStar(require("@grpc/grpc-js"));
|
|
38
|
+
const proto_manager_1 = require("./proto-manager");
|
|
39
|
+
const decoder_1 = require("./decoder");
|
|
40
|
+
const types_1 = require("./types");
|
|
41
|
+
const UINT64_MAX = "18446744073709551615";
|
|
42
|
+
const SUBSCRIBE_CODE_NAMES = {
|
|
43
|
+
0: "UNKNOWN", 1: "SUCCESS", 2: "INVALID_REQUEST", 3: "ERROR",
|
|
44
|
+
4: "INVALID_START_BLOCK_NUMBER", 5: "INVALID_END_BLOCK_NUMBER", 6: "NOT_AVAILABLE",
|
|
45
|
+
};
|
|
46
|
+
const BLOCK_CODE_NAMES = {
|
|
47
|
+
"0": "UNKNOWN", "1": "SUCCESS", "2": "INVALID_REQUEST",
|
|
48
|
+
"3": "ERROR", "4": "NOT_FOUND", "5": "NOT_AVAILABLE",
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* BlockNodeClient — the main entry point for the Hedera Block Node SDK.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* import { BlockNodeClient } from "@ohmpathorn/block-node-client";
|
|
56
|
+
*
|
|
57
|
+
* const client = new BlockNodeClient({
|
|
58
|
+
* endpoint: "s01.test.blk.ams.lat.ope.eng.hashgraph.io:40840",
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* const status = await client.serverStatus();
|
|
62
|
+
* console.log(status.lastAvailableBlock);
|
|
63
|
+
*
|
|
64
|
+
* client.close();
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
class BlockNodeClient {
|
|
68
|
+
constructor(options) {
|
|
69
|
+
this.options = options;
|
|
70
|
+
this.proto = new proto_manager_1.ProtoManager(options.endpoint, options.tls ?? "tls");
|
|
71
|
+
this.timeout = options.timeout ?? 10000;
|
|
72
|
+
}
|
|
73
|
+
// ── 1. serverStatus ──────────────────────────────────────────────────────────
|
|
74
|
+
/**
|
|
75
|
+
* Lightweight health check.
|
|
76
|
+
* Returns the first/last available block and whether the node is running.
|
|
77
|
+
* Always call this before subscribing to validate the block range.
|
|
78
|
+
*/
|
|
79
|
+
serverStatus() {
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
const deadline = new Date(Date.now() + this.timeout);
|
|
82
|
+
this.proto.nodeClient.serverStatus({}, { deadline }, (err, res) => {
|
|
83
|
+
if (err)
|
|
84
|
+
return reject(err);
|
|
85
|
+
resolve({
|
|
86
|
+
firstAvailableBlock: BigInt(res.first_available_block ?? "0"),
|
|
87
|
+
lastAvailableBlock: BigInt(res.last_available_block ?? "0"),
|
|
88
|
+
nextExpectedBlock: BigInt(res.next_expected_block ?? "0"),
|
|
89
|
+
onlyLatestState: Boolean(res.only_latest_state),
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// ── 2. serverStatusDetail ────────────────────────────────────────────────────
|
|
95
|
+
/**
|
|
96
|
+
* Full capability report: versions, available block ranges, TSS data,
|
|
97
|
+
* and the current network address book.
|
|
98
|
+
*/
|
|
99
|
+
serverStatusDetail() {
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
const deadline = new Date(Date.now() + this.timeout);
|
|
102
|
+
this.proto.nodeClient.serverStatusDetail({}, { deadline }, (err, res) => {
|
|
103
|
+
if (err)
|
|
104
|
+
return reject(err);
|
|
105
|
+
const mapRange = (r) => ({
|
|
106
|
+
rangeStart: BigInt(r.range_start ?? "0"),
|
|
107
|
+
rangeEnd: BigInt(r.range_end ?? "0"),
|
|
108
|
+
});
|
|
109
|
+
const mapSemver = (v) => v ? { major: Number(v.major), minor: Number(v.minor), patch: Number(v.patch) } : undefined;
|
|
110
|
+
let versionInformation;
|
|
111
|
+
if (res.version_information) {
|
|
112
|
+
const vi = res.version_information;
|
|
113
|
+
versionInformation = {
|
|
114
|
+
streamProtoVersion: mapSemver(vi.stream_proto_version),
|
|
115
|
+
blockNodeVersion: mapSemver(vi.block_node_version),
|
|
116
|
+
installedPluginVersions: (vi.installed_plugin_versions ?? []).map((p) => ({
|
|
117
|
+
pluginId: p.plugin_id ?? "",
|
|
118
|
+
pluginSoftwareVersion: mapSemver(p.plugin_software_version),
|
|
119
|
+
pluginFeatureNames: p.plugin_feature_names ?? [],
|
|
120
|
+
})),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
resolve({
|
|
124
|
+
versionInformation,
|
|
125
|
+
availableRanges: (res.available_ranges ?? []).map(mapRange),
|
|
126
|
+
storedRanges: (res.stored_ranges ?? []).map(mapRange),
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// ── 3. getBlock ──────────────────────────────────────────────────────────────
|
|
132
|
+
/**
|
|
133
|
+
* Fetch a single block by number or retrieve the latest block.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* const result = await client.getBlock({ blockNumber: 38764703n });
|
|
138
|
+
* const latest = await client.getBlock({ retrieveLatest: true });
|
|
139
|
+
* ```
|
|
140
|
+
*/
|
|
141
|
+
getBlock(request) {
|
|
142
|
+
return new Promise((resolve, reject) => {
|
|
143
|
+
const deadline = new Date(Date.now() + this.timeout);
|
|
144
|
+
const grpcRequest = "blockNumber" in request
|
|
145
|
+
? { block_number: request.blockNumber.toString() }
|
|
146
|
+
: { retrieve_latest: true };
|
|
147
|
+
this.proto.blockClient.getBlock(grpcRequest, { deadline }, (err, res) => {
|
|
148
|
+
if (err)
|
|
149
|
+
return reject(err);
|
|
150
|
+
// Status may come back as enum string or number
|
|
151
|
+
const statusRaw = res.status ?? "0";
|
|
152
|
+
const statusNum = typeof statusRaw === "string" && isNaN(Number(statusRaw))
|
|
153
|
+
? Object.values(types_1.BlockResponseCode).indexOf(statusRaw)
|
|
154
|
+
: Number(statusRaw);
|
|
155
|
+
const statusName = BLOCK_CODE_NAMES[String(statusNum)] ?? `CODE_${statusNum}`;
|
|
156
|
+
let block;
|
|
157
|
+
if (res.block && res.block.items) {
|
|
158
|
+
block = res.block.items.map(raw => (0, decoder_1.decodeBlockItem)(Buffer.from(raw)));
|
|
159
|
+
}
|
|
160
|
+
resolve({ status: statusNum, statusName, block });
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// ── 4. subscribeBlockStream ──────────────────────────────────────────────────
|
|
165
|
+
/**
|
|
166
|
+
* Subscribe to a live or historical block stream.
|
|
167
|
+
*
|
|
168
|
+
* The server sends `block_items` batches followed by `end_of_block` for each
|
|
169
|
+
* block. This method accumulates items per block and calls `onBlock` once the
|
|
170
|
+
* block is complete.
|
|
171
|
+
*
|
|
172
|
+
* Passing `endBlockNumber: 0` (or omitting it) subscribes to a live stream.
|
|
173
|
+
*
|
|
174
|
+
* @returns A `StreamHandle` with a `cancel()` method.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* const status = await client.serverStatus();
|
|
179
|
+
*
|
|
180
|
+
* const handle = client.subscribeBlockStream(
|
|
181
|
+
* { startBlockNumber: status.lastAvailableBlock - 10n },
|
|
182
|
+
* {
|
|
183
|
+
* onStatus: (code, name) => console.log("Stream status:", name),
|
|
184
|
+
* onBlock: (num, items) => console.log(`Block ${num}: ${items.length} items`),
|
|
185
|
+
* onError: (err) => console.error("Stream error:", err.message),
|
|
186
|
+
* onEnd: () => console.log("Stream ended"),
|
|
187
|
+
* },
|
|
188
|
+
* );
|
|
189
|
+
*
|
|
190
|
+
* // Cancel after 30 seconds
|
|
191
|
+
* setTimeout(() => handle.cancel(), 30_000);
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
subscribeBlockStream(options, callbacks) {
|
|
195
|
+
const start = BigInt(options.startBlockNumber);
|
|
196
|
+
const endRaw = options.endBlockNumber ?? 0;
|
|
197
|
+
// Sending 0 causes INVALID_END_BLOCK_NUMBER — convert to uint64_max for live streams
|
|
198
|
+
const end = BigInt(endRaw) === 0n ? UINT64_MAX : BigInt(endRaw).toString();
|
|
199
|
+
const stream = this.proto.streamClient.subscribeBlockStream({
|
|
200
|
+
start_block_number: start.toString(),
|
|
201
|
+
end_block_number: end,
|
|
202
|
+
});
|
|
203
|
+
let pending = [];
|
|
204
|
+
stream.on("data", (response) => {
|
|
205
|
+
// ── Status (first message) ─────────────────────────────────────────────
|
|
206
|
+
if (response.response === "status") {
|
|
207
|
+
const code = parseInt(response.status, 10);
|
|
208
|
+
const name = SUBSCRIBE_CODE_NAMES[code] ?? `CODE_${code}`;
|
|
209
|
+
callbacks.onStatus?.(code, name);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
// ── BlockItemSet (one or more items per message) ───────────────────────
|
|
213
|
+
if (response.response === "block_items") {
|
|
214
|
+
const rawItems = response.block_items?.block_items ?? [];
|
|
215
|
+
rawItems.forEach(raw => pending.push(Buffer.from(raw)));
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
// ── end_of_block (block complete) ──────────────────────────────────────
|
|
219
|
+
if (response.response === "end_of_block") {
|
|
220
|
+
const blockNumber = BigInt(response.end_of_block?.block_number ?? "0");
|
|
221
|
+
const items = pending.map(raw => (0, decoder_1.decodeBlockItem)(raw));
|
|
222
|
+
pending = [];
|
|
223
|
+
callbacks.onBlock?.(blockNumber, items);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
stream.on("error", (err) => {
|
|
228
|
+
if (err.code === grpc.status.CANCELLED) {
|
|
229
|
+
callbacks.onEnd?.();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
callbacks.onError?.(err);
|
|
233
|
+
});
|
|
234
|
+
stream.on("end", () => {
|
|
235
|
+
callbacks.onEnd?.();
|
|
236
|
+
});
|
|
237
|
+
return {
|
|
238
|
+
cancel: () => stream.cancel(),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
// ── Lifecycle ────────────────────────────────────────────────────────────────
|
|
242
|
+
/**
|
|
243
|
+
* Release gRPC resources and clean up temp proto files.
|
|
244
|
+
* Call when you're done with the client.
|
|
245
|
+
*/
|
|
246
|
+
close() {
|
|
247
|
+
this.proto.cleanup();
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
exports.BlockNodeClient = BlockNodeClient;
|
|
251
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,oDAAsC;AACtC,mDAA+C;AAC/C,uCAA4C;AAC5C,mCAeiB;AAEjB,MAAM,UAAU,GAAG,sBAAsB,CAAC;AAE1C,MAAM,oBAAoB,GAA2B;IACnD,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,OAAO;IAC5D,CAAC,EAAE,4BAA4B,EAAE,CAAC,EAAE,0BAA0B,EAAE,CAAC,EAAE,eAAe;CACnF,CAAC;AAEF,MAAM,gBAAgB,GAA2B;IAC/C,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,iBAAiB;IACtD,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,eAAe;CACrD,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAa,eAAe;IAI1B,YAA6B,OAA+B;QAA/B,YAAO,GAAP,OAAO,CAAwB;QAC1D,IAAI,CAAC,KAAK,GAAG,IAAI,4BAAY,CAC3B,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,GAAG,IAAI,KAAK,CACrB,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,KAAM,CAAC;IAC3C,CAAC;IAED,gFAAgF;IAEhF;;;;OAIG;IACH,YAAY;QACV,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YACpD,IAAI,CAAC,KAAK,CAAC,UAAkB,CAAC,YAAY,CACzC,EAAE,EACF,EAAE,QAAQ,EAAE,EACZ,CAAC,GAA6B,EAAE,GAAQ,EAAE,EAAE;gBAC1C,IAAI,GAAG;oBAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC5B,OAAO,CAAC;oBACN,mBAAmB,EAAE,MAAM,CAAC,GAAG,CAAC,qBAAqB,IAAI,GAAG,CAAC;oBAC7D,kBAAkB,EAAG,MAAM,CAAC,GAAG,CAAC,oBAAoB,IAAK,GAAG,CAAC;oBAC7D,iBAAiB,EAAI,MAAM,CAAC,GAAG,CAAC,mBAAmB,IAAM,GAAG,CAAC;oBAC7D,eAAe,EAAM,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;iBACpD,CAAC,CAAC;YACL,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gFAAgF;IAEhF;;;OAGG;IACH,kBAAkB;QAChB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YACpD,IAAI,CAAC,KAAK,CAAC,UAAkB,CAAC,kBAAkB,CAC/C,EAAE,EACF,EAAE,QAAQ,EAAE,EACZ,CAAC,GAA6B,EAAE,GAAQ,EAAE,EAAE;gBAC1C,IAAI,GAAG;oBAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;gBAE5B,MAAM,QAAQ,GAAG,CAAC,CAAM,EAAc,EAAE,CAAC,CAAC;oBACxC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC,WAAW,IAAI,GAAG,CAAC;oBACxC,QAAQ,EAAI,MAAM,CAAC,CAAC,CAAC,SAAS,IAAM,GAAG,CAAC;iBACzC,CAAC,CAAC;gBAEH,MAAM,SAAS,GAAG,CAAC,CAAM,EAA+B,EAAE,CACxD,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;gBAE7F,IAAI,kBAAiD,CAAC;gBACtD,IAAI,GAAG,CAAC,mBAAmB,EAAE,CAAC;oBAC5B,MAAM,EAAE,GAAG,GAAG,CAAC,mBAAmB,CAAC;oBACnC,kBAAkB,GAAG;wBACnB,kBAAkB,EAAE,SAAS,CAAC,EAAE,CAAC,oBAAoB,CAAC;wBACtD,gBAAgB,EAAI,SAAS,CAAC,EAAE,CAAC,kBAAkB,CAAC;wBACpD,uBAAuB,EAAE,CAAC,EAAE,CAAC,yBAAyB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;4BAC7E,QAAQ,EAAE,CAAC,CAAC,SAAS,IAAI,EAAE;4BAC3B,qBAAqB,EAAE,SAAS,CAAC,CAAC,CAAC,uBAAuB,CAAC;4BAC3D,kBAAkB,EAAE,CAAC,CAAC,oBAAoB,IAAI,EAAE;yBACjD,CAAC,CAAC;qBACJ,CAAC;gBACJ,CAAC;gBAED,OAAO,CAAC;oBACN,kBAAkB;oBAClB,eAAe,EAAE,CAAC,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;oBAC3D,YAAY,EAAK,CAAC,GAAG,CAAC,aAAa,IAAO,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC;iBAC5D,CAAC,CAAC;YACL,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gFAAgF;IAEhF;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAA8B;QACrC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;YACrD,MAAM,WAAW,GACf,aAAa,IAAI,OAAO;gBACtB,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,WAAW,CAAC,QAAQ,EAAE,EAAE;gBAClD,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;YAE/B,IAAI,CAAC,KAAK,CAAC,WAAmB,CAAC,QAAQ,CACtC,WAAW,EACX,EAAE,QAAQ,EAAE,EACZ,CAAC,GAA6B,EAAE,GAAQ,EAAE,EAAE;gBAC1C,IAAI,GAAG;oBAAE,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;gBAE5B,gDAAgD;gBAChD,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC;gBACpC,MAAM,SAAS,GACb,OAAO,SAAS,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;oBACvD,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,yBAAiB,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;oBACrD,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;gBACxB,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,QAAQ,SAAS,EAAE,CAAC;gBAE9E,IAAI,KAA8B,CAAC;gBACnC,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;oBACjC,KAAK,GAAI,GAAG,CAAC,KAAK,CAAC,KAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAC9C,IAAA,yBAAe,EAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAClC,CAAC;gBACJ,CAAC;gBAED,OAAO,CAAC,EAAE,MAAM,EAAE,SAA8B,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;YACzE,CAAC,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAED,gFAAgF;IAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,oBAAoB,CAClB,OAA2B,EAC3B,SAA+B;QAE/B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC;QAC3C,qFAAqF;QACrF,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAC;QAE3E,MAAM,MAAM,GAAI,IAAI,CAAC,KAAK,CAAC,YAAoB,CAAC,oBAAoB,CAAC;YACnE,kBAAkB,EAAE,KAAK,CAAC,QAAQ,EAAE;YACpC,gBAAgB,EAAI,GAAG;SACxB,CAAC,CAAC;QAEH,IAAI,OAAO,GAAa,EAAE,CAAC;QAE3B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,QAAa,EAAE,EAAE;YAClC,0EAA0E;YAC1E,IAAI,QAAQ,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBAC3C,MAAM,IAAI,GAAG,oBAAoB,CAAC,IAAI,CAAC,IAAI,QAAQ,IAAI,EAAE,CAAC;gBAC1D,SAAS,CAAC,QAAQ,EAAE,CAAC,IAA2B,EAAE,IAAI,CAAC,CAAC;gBACxD,OAAO;YACT,CAAC;YAED,0EAA0E;YAC1E,IAAI,QAAQ,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;gBACxC,MAAM,QAAQ,GAAa,QAAQ,CAAC,WAAW,EAAE,WAAW,IAAI,EAAE,CAAC;gBACnE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACxD,OAAO;YACT,CAAC;YAED,0EAA0E;YAC1E,IAAI,QAAQ,CAAC,QAAQ,KAAK,cAAc,EAAE,CAAC;gBACzC,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,IAAI,GAAG,CAAC,CAAC;gBACvE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAA,yBAAe,EAAC,GAAG,CAAC,CAAC,CAAC;gBACvD,OAAO,GAAG,EAAE,CAAC;gBACb,SAAS,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;gBACxC,OAAO;YACT,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAsB,EAAE,EAAE;YAC5C,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACvC,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;gBACpB,OAAO;YACT,CAAC;YACD,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE;YACpB,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,MAAM,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE;SAC9B,CAAC;IACJ,CAAC;IAED,gFAAgF;IAEhF;;;OAGG;IACH,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IACvB,CAAC;CACF;AAtOD,0CAsOC"}
|