@hbar-kit/mirror 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/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/index.cjs +254 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +202 -0
- package/dist/index.d.ts +202 -0
- package/dist/index.js +246 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
- package/src/client.ts +38 -0
- package/src/index.ts +13 -0
- package/src/json.ts +17 -0
- package/src/normalize.ts +81 -0
- package/src/paginate.ts +23 -0
- package/src/resources/accounts.ts +28 -0
- package/src/resources/balances.ts +19 -0
- package/src/resources/tokens.ts +17 -0
- package/src/resources/transactions.ts +52 -0
- package/src/transport.ts +84 -0
- package/src/types.ts +120 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { HederaTimestamp, NetworkInput } from '@hbar-kit/core';
|
|
2
|
+
|
|
3
|
+
interface TransportOptions {
|
|
4
|
+
fetch?: typeof fetch;
|
|
5
|
+
retryCount?: number;
|
|
6
|
+
retryDelay?: number;
|
|
7
|
+
timeout?: number;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
}
|
|
10
|
+
interface Transport {
|
|
11
|
+
get(path: string): Promise<unknown>;
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
}
|
|
14
|
+
declare function http(baseUrl: string, options?: TransportOptions): Transport;
|
|
15
|
+
|
|
16
|
+
interface RawTransfer {
|
|
17
|
+
account: string;
|
|
18
|
+
amount: bigint;
|
|
19
|
+
is_approval?: boolean;
|
|
20
|
+
}
|
|
21
|
+
interface RawTokenTransfer {
|
|
22
|
+
token_id: string;
|
|
23
|
+
account: string;
|
|
24
|
+
amount: bigint;
|
|
25
|
+
is_approval?: boolean;
|
|
26
|
+
}
|
|
27
|
+
interface RawNftTransfer {
|
|
28
|
+
token_id: string;
|
|
29
|
+
sender_account_id: string | null;
|
|
30
|
+
receiver_account_id: string | null;
|
|
31
|
+
serial_number: bigint;
|
|
32
|
+
is_approval?: boolean;
|
|
33
|
+
}
|
|
34
|
+
interface RawTransaction {
|
|
35
|
+
transaction_id: string;
|
|
36
|
+
consensus_timestamp: string;
|
|
37
|
+
valid_start_timestamp?: string;
|
|
38
|
+
result: string;
|
|
39
|
+
name: string;
|
|
40
|
+
charged_tx_fee: bigint;
|
|
41
|
+
memo_base64: string;
|
|
42
|
+
node?: string;
|
|
43
|
+
nonce: number;
|
|
44
|
+
scheduled: boolean;
|
|
45
|
+
parent_consensus_timestamp: string | null;
|
|
46
|
+
transfers: RawTransfer[];
|
|
47
|
+
token_transfers: RawTokenTransfer[];
|
|
48
|
+
nft_transfers: RawNftTransfer[];
|
|
49
|
+
}
|
|
50
|
+
interface RawListLinks {
|
|
51
|
+
next: string | null;
|
|
52
|
+
}
|
|
53
|
+
interface RawTransactionList {
|
|
54
|
+
transactions: RawTransaction[];
|
|
55
|
+
links: RawListLinks;
|
|
56
|
+
}
|
|
57
|
+
interface RawToken {
|
|
58
|
+
token_id: string;
|
|
59
|
+
decimals: string | number;
|
|
60
|
+
symbol: string;
|
|
61
|
+
name: string;
|
|
62
|
+
type: string;
|
|
63
|
+
total_supply?: string;
|
|
64
|
+
max_supply?: string;
|
|
65
|
+
treasury_account_id?: string;
|
|
66
|
+
}
|
|
67
|
+
interface RawAccount {
|
|
68
|
+
account: string;
|
|
69
|
+
evm_address?: string;
|
|
70
|
+
deleted?: boolean;
|
|
71
|
+
balance: {
|
|
72
|
+
balance: bigint;
|
|
73
|
+
timestamp: string;
|
|
74
|
+
tokens: {
|
|
75
|
+
token_id: string;
|
|
76
|
+
balance: bigint;
|
|
77
|
+
}[];
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
interface RawStatus {
|
|
81
|
+
_status?: {
|
|
82
|
+
messages?: {
|
|
83
|
+
message: string;
|
|
84
|
+
}[];
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
interface Transfer {
|
|
88
|
+
account: string;
|
|
89
|
+
amount: bigint;
|
|
90
|
+
isApproval: boolean;
|
|
91
|
+
}
|
|
92
|
+
interface TokenTransfer {
|
|
93
|
+
tokenId: string;
|
|
94
|
+
account: string;
|
|
95
|
+
amount: bigint;
|
|
96
|
+
isApproval: boolean;
|
|
97
|
+
}
|
|
98
|
+
interface NftTransfer {
|
|
99
|
+
tokenId: string;
|
|
100
|
+
sender: string | null;
|
|
101
|
+
receiver: string | null;
|
|
102
|
+
serial: bigint;
|
|
103
|
+
isApproval: boolean;
|
|
104
|
+
}
|
|
105
|
+
interface Transaction {
|
|
106
|
+
transactionId: string;
|
|
107
|
+
consensusTimestamp: HederaTimestamp;
|
|
108
|
+
validStartTimestamp?: HederaTimestamp | undefined;
|
|
109
|
+
result: string;
|
|
110
|
+
name: string;
|
|
111
|
+
chargedTxFee: bigint;
|
|
112
|
+
memo: string;
|
|
113
|
+
nonce: number;
|
|
114
|
+
scheduled: boolean;
|
|
115
|
+
parentConsensusTimestamp: string | null;
|
|
116
|
+
transfers: Transfer[];
|
|
117
|
+
tokenTransfers: TokenTransfer[];
|
|
118
|
+
nftTransfers: NftTransfer[];
|
|
119
|
+
raw: RawTransaction;
|
|
120
|
+
}
|
|
121
|
+
interface Token {
|
|
122
|
+
tokenId: string;
|
|
123
|
+
decimals: number;
|
|
124
|
+
symbol: string;
|
|
125
|
+
name: string;
|
|
126
|
+
type: string;
|
|
127
|
+
totalSupply?: bigint | undefined;
|
|
128
|
+
maxSupply?: bigint | undefined;
|
|
129
|
+
treasuryAccountId?: string | undefined;
|
|
130
|
+
raw: RawToken;
|
|
131
|
+
}
|
|
132
|
+
interface AccountBalance {
|
|
133
|
+
accountId: string;
|
|
134
|
+
balance: bigint;
|
|
135
|
+
tokens: {
|
|
136
|
+
tokenId: string;
|
|
137
|
+
balance: bigint;
|
|
138
|
+
}[];
|
|
139
|
+
raw: RawAccount;
|
|
140
|
+
}
|
|
141
|
+
interface Page<T> {
|
|
142
|
+
items: T[];
|
|
143
|
+
next: string | null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
interface FindTransactionsParams {
|
|
147
|
+
accountId?: string;
|
|
148
|
+
transactionType?: string;
|
|
149
|
+
result?: "success" | "fail";
|
|
150
|
+
order?: "asc" | "desc";
|
|
151
|
+
limit?: number;
|
|
152
|
+
after?: Date | string;
|
|
153
|
+
before?: Date | string;
|
|
154
|
+
}
|
|
155
|
+
interface TransactionsResource {
|
|
156
|
+
find(params?: FindTransactionsParams): Promise<Page<Transaction>>;
|
|
157
|
+
get(transactionId: string): Promise<Transaction[]>;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface AccountsResource {
|
|
161
|
+
getBalance(accountId: string): Promise<AccountBalance>;
|
|
162
|
+
isAssociated(accountId: string, tokenId: string): Promise<boolean>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
interface TokensResource {
|
|
166
|
+
get(tokenId: string): Promise<Token>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
interface BalancesResource {
|
|
170
|
+
get(accountId: string): Promise<{
|
|
171
|
+
accountId: string;
|
|
172
|
+
balance: bigint;
|
|
173
|
+
timestamp: string;
|
|
174
|
+
}>;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
interface MirrorClientConfig extends NetworkInput, TransportOptions {
|
|
178
|
+
transport?: Transport;
|
|
179
|
+
}
|
|
180
|
+
interface MirrorClient {
|
|
181
|
+
baseUrl: string;
|
|
182
|
+
transport: Transport;
|
|
183
|
+
transactions: TransactionsResource;
|
|
184
|
+
accounts: AccountsResource;
|
|
185
|
+
tokens: TokensResource;
|
|
186
|
+
balances: BalancesResource;
|
|
187
|
+
}
|
|
188
|
+
declare function createMirrorClient(config: MirrorClientConfig): MirrorClient;
|
|
189
|
+
|
|
190
|
+
interface PaginateOptions {
|
|
191
|
+
maxPages?: number;
|
|
192
|
+
}
|
|
193
|
+
/** Async-iterate a list endpoint by following links.next verbatim until null. */
|
|
194
|
+
declare function paginate<T>(transport: Transport, firstPath: string, select: (page: unknown) => T[], options?: PaginateOptions): AsyncGenerator<T, void, unknown>;
|
|
195
|
+
|
|
196
|
+
declare function normalizeTransaction(raw: RawTransaction): Transaction;
|
|
197
|
+
declare function normalizeToken(raw: RawToken): Token;
|
|
198
|
+
declare function normalizeAccountBalance(raw: RawAccount): AccountBalance;
|
|
199
|
+
/** Detect Mirror Node not-found: the _status envelope, or an empty transactions array. */
|
|
200
|
+
declare function isNotFound(body: unknown): boolean;
|
|
201
|
+
|
|
202
|
+
export { type AccountBalance, type FindTransactionsParams, type MirrorClient, type MirrorClientConfig, type NftTransfer, type Page, type RawAccount, type RawListLinks, type RawNftTransfer, type RawStatus, type RawToken, type RawTokenTransfer, type RawTransaction, type RawTransactionList, type RawTransfer, type Token, type TokenTransfer, type Transaction, type Transfer, type Transport, type TransportOptions, createMirrorClient, http, isNotFound, normalizeAccountBalance, normalizeToken, normalizeTransaction, paginate };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { parseTimestamp, resolveNetwork, RateLimitError, MirrorHttpError, TimeoutError, NetworkError, assertEntityId, txIdToMirror, tsFilter } from '@hbar-kit/core';
|
|
2
|
+
|
|
3
|
+
// src/client.ts
|
|
4
|
+
|
|
5
|
+
// src/json.ts
|
|
6
|
+
var BIGINT_KEYS = ["amount", "balance", "charged_tx_fee", "serial_number"];
|
|
7
|
+
function parseJsonWithBigInt(text) {
|
|
8
|
+
const keyAlternation = BIGINT_KEYS.join("|");
|
|
9
|
+
const re = new RegExp(`("(?:${keyAlternation})"\\s*:\\s*)(-?\\d+)`, "g");
|
|
10
|
+
const marked = text.replace(re, (_m, prefix, num) => `${prefix}" bi:${num}"`);
|
|
11
|
+
return JSON.parse(marked, (_k, value) => {
|
|
12
|
+
if (typeof value === "string" && value.startsWith(" bi:")) return BigInt(value.slice(4));
|
|
13
|
+
return value;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/transport.ts
|
|
18
|
+
var RETRYABLE = /* @__PURE__ */ new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
19
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
20
|
+
var jitter = (ms) => ms * (0.5 + Math.random());
|
|
21
|
+
function http(baseUrl, options = {}) {
|
|
22
|
+
const doFetch = options.fetch ?? globalThis.fetch;
|
|
23
|
+
const retryCount = options.retryCount ?? 3;
|
|
24
|
+
const retryDelay = options.retryDelay ?? 150;
|
|
25
|
+
const timeout = options.timeout ?? 1e4;
|
|
26
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
27
|
+
async function get(path) {
|
|
28
|
+
const url = path.startsWith("http") ? path : base + path;
|
|
29
|
+
let attempt = 0;
|
|
30
|
+
let lastErr;
|
|
31
|
+
while (attempt <= retryCount) {
|
|
32
|
+
const controller = new AbortController();
|
|
33
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
34
|
+
try {
|
|
35
|
+
const response = await doFetch(url, {
|
|
36
|
+
signal: controller.signal,
|
|
37
|
+
headers: { accept: "application/json", ...options.headers }
|
|
38
|
+
});
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
if (response.ok) {
|
|
41
|
+
const text = await response.text();
|
|
42
|
+
return text ? parseJsonWithBigInt(text) : null;
|
|
43
|
+
}
|
|
44
|
+
if (RETRYABLE.has(response.status) && attempt < retryCount) {
|
|
45
|
+
const retryAfter = Number(response.headers.get("retry-after"));
|
|
46
|
+
const wait = Number.isFinite(retryAfter) && retryAfter > 0 ? retryAfter * 1e3 : jitter(retryDelay * 2 ** attempt);
|
|
47
|
+
await sleep(wait);
|
|
48
|
+
attempt++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const body = await response.text().catch(() => "");
|
|
52
|
+
if (response.status === 429) {
|
|
53
|
+
const ra = Number(response.headers.get("retry-after"));
|
|
54
|
+
throw new RateLimitError(
|
|
55
|
+
`Mirror Node rate limited (429)`,
|
|
56
|
+
Number.isFinite(ra) ? { details: body, retryAfter: ra } : { details: body }
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
throw new MirrorHttpError(`Mirror Node HTTP ${response.status}`, response.status, {
|
|
60
|
+
details: body
|
|
61
|
+
});
|
|
62
|
+
} catch (err) {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
if (err instanceof MirrorHttpError) throw err;
|
|
65
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
66
|
+
throw new TimeoutError(`Mirror Node request timed out after ${timeout}ms`, { cause: err });
|
|
67
|
+
}
|
|
68
|
+
lastErr = err;
|
|
69
|
+
if (attempt < retryCount) {
|
|
70
|
+
await sleep(jitter(retryDelay * 2 ** attempt));
|
|
71
|
+
attempt++;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
throw new NetworkError(`Mirror Node request failed: ${String(err)}`, { cause: err });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
throw new NetworkError(`Mirror Node request failed`, { cause: lastErr });
|
|
78
|
+
}
|
|
79
|
+
return { get, baseUrl: base };
|
|
80
|
+
}
|
|
81
|
+
var decodeMemo = (b64) => b64 ? Buffer.from(b64, "base64").toString("utf8") : "";
|
|
82
|
+
function normalizeTransaction(raw) {
|
|
83
|
+
return {
|
|
84
|
+
transactionId: raw.transaction_id,
|
|
85
|
+
consensusTimestamp: parseTimestamp(raw.consensus_timestamp),
|
|
86
|
+
validStartTimestamp: raw.valid_start_timestamp ? parseTimestamp(raw.valid_start_timestamp) : void 0,
|
|
87
|
+
result: raw.result,
|
|
88
|
+
name: raw.name,
|
|
89
|
+
chargedTxFee: raw.charged_tx_fee,
|
|
90
|
+
memo: decodeMemo(raw.memo_base64),
|
|
91
|
+
nonce: raw.nonce,
|
|
92
|
+
scheduled: raw.scheduled,
|
|
93
|
+
parentConsensusTimestamp: raw.parent_consensus_timestamp,
|
|
94
|
+
transfers: (raw.transfers ?? []).map((t) => ({
|
|
95
|
+
account: t.account,
|
|
96
|
+
amount: t.amount,
|
|
97
|
+
isApproval: t.is_approval ?? false
|
|
98
|
+
})),
|
|
99
|
+
tokenTransfers: (raw.token_transfers ?? []).map((t) => ({
|
|
100
|
+
tokenId: t.token_id,
|
|
101
|
+
account: t.account,
|
|
102
|
+
amount: t.amount,
|
|
103
|
+
isApproval: t.is_approval ?? false
|
|
104
|
+
})),
|
|
105
|
+
nftTransfers: (raw.nft_transfers ?? []).map((t) => ({
|
|
106
|
+
tokenId: t.token_id,
|
|
107
|
+
sender: t.sender_account_id,
|
|
108
|
+
receiver: t.receiver_account_id,
|
|
109
|
+
serial: t.serial_number,
|
|
110
|
+
isApproval: t.is_approval ?? false
|
|
111
|
+
})),
|
|
112
|
+
raw
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function normalizeToken(raw) {
|
|
116
|
+
return {
|
|
117
|
+
tokenId: raw.token_id,
|
|
118
|
+
decimals: Number(raw.decimals),
|
|
119
|
+
symbol: raw.symbol,
|
|
120
|
+
name: raw.name,
|
|
121
|
+
type: raw.type,
|
|
122
|
+
totalSupply: raw.total_supply != null ? BigInt(raw.total_supply) : void 0,
|
|
123
|
+
maxSupply: raw.max_supply != null ? BigInt(raw.max_supply) : void 0,
|
|
124
|
+
treasuryAccountId: raw.treasury_account_id,
|
|
125
|
+
raw
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function normalizeAccountBalance(raw) {
|
|
129
|
+
return {
|
|
130
|
+
accountId: raw.account,
|
|
131
|
+
balance: raw.balance.balance,
|
|
132
|
+
tokens: (raw.balance.tokens ?? []).map((t) => ({ tokenId: t.token_id, balance: t.balance })),
|
|
133
|
+
raw
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function isNotFound(body) {
|
|
137
|
+
if (!body || typeof body !== "object") return false;
|
|
138
|
+
const status = body._status;
|
|
139
|
+
if (status?.messages?.some((m) => /not found/i.test(m.message))) return true;
|
|
140
|
+
const txs = body.transactions;
|
|
141
|
+
if (Array.isArray(txs) && txs.length === 0) return true;
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// src/resources/transactions.ts
|
|
146
|
+
function buildQuery(params) {
|
|
147
|
+
const q = new URLSearchParams();
|
|
148
|
+
if (params.accountId) q.set("account.id", params.accountId);
|
|
149
|
+
if (params.transactionType) q.set("transactiontype", params.transactionType);
|
|
150
|
+
if (params.result) q.set("result", params.result);
|
|
151
|
+
if (params.order) q.set("order", params.order);
|
|
152
|
+
q.set("limit", String(Math.min(params.limit ?? 25, 100)));
|
|
153
|
+
if (params.after) q.append("timestamp", tsFilter("gte", params.after));
|
|
154
|
+
if (params.before) q.append("timestamp", tsFilter("lt", params.before));
|
|
155
|
+
return q.toString();
|
|
156
|
+
}
|
|
157
|
+
function createTransactionsResource(transport) {
|
|
158
|
+
return {
|
|
159
|
+
async find(params = {}) {
|
|
160
|
+
const body = await transport.get(
|
|
161
|
+
`/api/v1/transactions?${buildQuery(params)}`
|
|
162
|
+
);
|
|
163
|
+
return {
|
|
164
|
+
items: (body.transactions ?? []).map(normalizeTransaction),
|
|
165
|
+
next: body.links?.next ?? null
|
|
166
|
+
};
|
|
167
|
+
},
|
|
168
|
+
async get(transactionId) {
|
|
169
|
+
const id = txIdToMirror(transactionId);
|
|
170
|
+
const body = await transport.get(`/api/v1/transactions/${id}`);
|
|
171
|
+
return (body.transactions ?? []).map(normalizeTransaction);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function createAccountsResource(transport) {
|
|
176
|
+
return {
|
|
177
|
+
async getBalance(accountId) {
|
|
178
|
+
assertEntityId(accountId);
|
|
179
|
+
return normalizeAccountBalance(
|
|
180
|
+
await transport.get(`/api/v1/accounts/${accountId}`)
|
|
181
|
+
);
|
|
182
|
+
},
|
|
183
|
+
async isAssociated(accountId, tokenId) {
|
|
184
|
+
assertEntityId(accountId);
|
|
185
|
+
assertEntityId(tokenId);
|
|
186
|
+
const body = await transport.get(
|
|
187
|
+
`/api/v1/accounts/${accountId}/tokens?token.id=${tokenId}`
|
|
188
|
+
);
|
|
189
|
+
return Array.isArray(body.tokens) && body.tokens.length > 0;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function createTokensResource(transport) {
|
|
194
|
+
return {
|
|
195
|
+
async get(tokenId) {
|
|
196
|
+
assertEntityId(tokenId);
|
|
197
|
+
return normalizeToken(await transport.get(`/api/v1/tokens/${tokenId}`));
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function createBalancesResource(transport) {
|
|
202
|
+
return {
|
|
203
|
+
async get(accountId) {
|
|
204
|
+
assertEntityId(accountId);
|
|
205
|
+
const body = await transport.get(`/api/v1/balances?account.id=${accountId}&limit=1`);
|
|
206
|
+
return { accountId, balance: body.balances?.[0]?.balance ?? 0n, timestamp: body.timestamp };
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// src/client.ts
|
|
212
|
+
function createMirrorClient(config) {
|
|
213
|
+
const { baseUrl } = resolveNetwork(config);
|
|
214
|
+
const transportOptions = {};
|
|
215
|
+
if (config.fetch !== void 0) transportOptions.fetch = config.fetch;
|
|
216
|
+
if (config.retryCount !== void 0) transportOptions.retryCount = config.retryCount;
|
|
217
|
+
if (config.retryDelay !== void 0) transportOptions.retryDelay = config.retryDelay;
|
|
218
|
+
if (config.timeout !== void 0) transportOptions.timeout = config.timeout;
|
|
219
|
+
if (config.headers !== void 0) transportOptions.headers = config.headers;
|
|
220
|
+
const transport = config.transport ?? http(baseUrl, transportOptions);
|
|
221
|
+
return {
|
|
222
|
+
baseUrl: transport.baseUrl,
|
|
223
|
+
transport,
|
|
224
|
+
transactions: createTransactionsResource(transport),
|
|
225
|
+
accounts: createAccountsResource(transport),
|
|
226
|
+
tokens: createTokensResource(transport),
|
|
227
|
+
balances: createBalancesResource(transport)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/paginate.ts
|
|
232
|
+
async function* paginate(transport, firstPath, select, options = {}) {
|
|
233
|
+
const maxPages = options.maxPages ?? 1e3;
|
|
234
|
+
let path = firstPath;
|
|
235
|
+
let pages = 0;
|
|
236
|
+
while (path && pages < maxPages) {
|
|
237
|
+
const page = await transport.get(path);
|
|
238
|
+
for (const item of select(page)) yield item;
|
|
239
|
+
path = page.links?.next ?? null;
|
|
240
|
+
pages++;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export { createMirrorClient, http, isNotFound, normalizeAccountBalance, normalizeToken, normalizeTransaction, paginate };
|
|
245
|
+
//# sourceMappingURL=index.js.map
|
|
246
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/json.ts","../src/transport.ts","../src/normalize.ts","../src/resources/transactions.ts","../src/resources/accounts.ts","../src/resources/tokens.ts","../src/resources/balances.ts","../src/client.ts","../src/paginate.ts"],"names":["assertEntityId"],"mappings":";;;;;AACA,IAAM,WAAA,GAAc,CAAC,QAAA,EAAU,SAAA,EAAW,kBAAkB,eAAe,CAAA;AAOpE,SAAS,oBAAoB,IAAA,EAAuB;AACzD,EAAA,MAAM,cAAA,GAAiB,WAAA,CAAY,IAAA,CAAK,GAAG,CAAA;AAC3C,EAAA,MAAM,KAAK,IAAI,MAAA,CAAO,CAAA,KAAA,EAAQ,cAAc,wBAAwB,GAAG,CAAA;AACvE,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,EAAA,EAAI,CAAC,EAAA,EAAI,MAAA,EAAgB,GAAA,KAAgB,CAAA,EAAG,MAAM,CAAA,KAAA,EAAQ,GAAG,CAAA,CAAA,CAAG,CAAA;AAC5F,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,CAAC,IAAI,KAAA,KAAU;AACvC,IAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,CAAM,UAAA,CAAW,MAAM,CAAA,EAAG,OAAO,MAAA,CAAO,KAAA,CAAM,KAAA,CAAM,CAAC,CAAC,CAAA;AACvF,IAAA,OAAO,KAAA;AAAA,EACT,CAAC,CAAA;AACH;;;ACDA,IAAM,SAAA,mBAAY,IAAI,GAAA,CAAI,CAAC,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAG,CAAC,CAAA;AAC7D,IAAM,KAAA,GAAQ,CAAC,EAAA,KAAe,IAAI,OAAA,CAAQ,CAAC,CAAA,KAAM,UAAA,CAAW,CAAA,EAAG,EAAE,CAAC,CAAA;AAClE,IAAM,SAAS,CAAC,EAAA,KAAe,EAAA,IAAM,GAAA,GAAM,KAAK,MAAA,EAAO,CAAA;AAEhD,SAAS,IAAA,CAAK,OAAA,EAAiB,OAAA,GAA4B,EAAC,EAAc;AAC/E,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AAC5C,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,CAAA;AACzC,EAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,GAAA;AACzC,EAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,IAAW,GAAA;AACnC,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AAEvC,EAAA,eAAe,IAAI,IAAA,EAAgC;AACjD,IAAA,MAAM,MAAM,IAAA,CAAK,UAAA,CAAW,MAAM,CAAA,GAAI,OAAO,IAAA,GAAO,IAAA;AACpD,IAAA,IAAI,OAAA,GAAU,CAAA;AACd,IAAA,IAAI,OAAA;AACJ,IAAA,OAAO,WAAW,UAAA,EAAY;AAC5B,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,QAAQ,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,OAAO,CAAA;AAC1D,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,OAAA,CAAQ,GAAA,EAAK;AAAA,UAClC,QAAQ,UAAA,CAAW,MAAA;AAAA,UACnB,SAAS,EAAE,MAAA,EAAQ,kBAAA,EAAoB,GAAG,QAAQ,OAAA;AAAQ,SAC3D,CAAA;AACD,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,IAAI,SAAS,EAAA,EAAI;AACf,UAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,UAAA,OAAO,IAAA,GAAO,mBAAA,CAAoB,IAAI,CAAA,GAAI,IAAA;AAAA,QAC5C;AACA,QAAA,IAAI,UAAU,GAAA,CAAI,QAAA,CAAS,MAAM,CAAA,IAAK,UAAU,UAAA,EAAY;AAC1D,UAAA,MAAM,aAAa,MAAA,CAAO,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAC,CAAA;AAC7D,UAAA,MAAM,IAAA,GACJ,MAAA,CAAO,QAAA,CAAS,UAAU,CAAA,IAAK,UAAA,GAAa,CAAA,GACxC,UAAA,GAAa,GAAA,GACb,MAAA,CAAO,UAAA,GAAa,CAAA,IAAK,OAAO,CAAA;AACtC,UAAA,MAAM,MAAM,IAAI,CAAA;AAChB,UAAA,OAAA,EAAA;AACA,UAAA;AAAA,QACF;AACA,QAAA,MAAM,OAAO,MAAM,QAAA,CAAS,MAAK,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AACjD,QAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,UAAA,MAAM,KAAK,MAAA,CAAO,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAC,CAAA;AACrD,UAAA,MAAM,IAAI,cAAA;AAAA,YACR,CAAA,8BAAA,CAAA;AAAA,YACA,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,GAAI,EAAE,OAAA,EAAS,IAAA,EAAM,UAAA,EAAY,EAAA,EAAG,GAAI,EAAE,OAAA,EAAS,IAAA;AAAK,WAC5E;AAAA,QACF;AACA,QAAA,MAAM,IAAI,eAAA,CAAgB,CAAA,iBAAA,EAAoB,SAAS,MAAM,CAAA,CAAA,EAAI,SAAS,MAAA,EAAQ;AAAA,UAChF,OAAA,EAAS;AAAA,SACV,CAAA;AAAA,MACH,SAAS,GAAA,EAAK;AACZ,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,IAAI,GAAA,YAAe,iBAAiB,MAAM,GAAA;AAC1C,QAAA,IAAI,GAAA,YAAe,YAAA,IAAgB,GAAA,CAAI,IAAA,KAAS,YAAA,EAAc;AAC5D,UAAA,MAAM,IAAI,aAAa,CAAA,oCAAA,EAAuC,OAAO,MAAM,EAAE,KAAA,EAAO,KAAK,CAAA;AAAA,QAC3F;AACA,QAAA,OAAA,GAAU,GAAA;AACV,QAAA,IAAI,UAAU,UAAA,EAAY;AACxB,UAAA,MAAM,KAAA,CAAM,MAAA,CAAO,UAAA,GAAa,CAAA,IAAK,OAAO,CAAC,CAAA;AAC7C,UAAA,OAAA,EAAA;AACA,UAAA;AAAA,QACF;AACA,QAAA,MAAM,IAAI,YAAA,CAAa,CAAA,4BAAA,EAA+B,MAAA,CAAO,GAAG,CAAC,CAAA,CAAA,EAAI,EAAE,KAAA,EAAO,GAAA,EAAK,CAAA;AAAA,MACrF;AAAA,IACF;AACA,IAAA,MAAM,IAAI,YAAA,CAAa,CAAA,0BAAA,CAAA,EAA8B,EAAE,KAAA,EAAO,SAAS,CAAA;AAAA,EACzE;AAEA,EAAA,OAAO,EAAE,GAAA,EAAK,OAAA,EAAS,IAAA,EAAK;AAC9B;ACxEA,IAAM,UAAA,GAAa,CAAC,GAAA,KAAyB,GAAA,GAAM,MAAA,CAAO,IAAA,CAAK,GAAA,EAAK,QAAQ,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,GAAI,EAAA;AAE1F,SAAS,qBAAqB,GAAA,EAAkC;AACrE,EAAA,OAAO;AAAA,IACL,eAAe,GAAA,CAAI,cAAA;AAAA,IACnB,kBAAA,EAAoB,cAAA,CAAe,GAAA,CAAI,mBAAmB,CAAA;AAAA,IAC1D,qBAAqB,GAAA,CAAI,qBAAA,GACrB,cAAA,CAAe,GAAA,CAAI,qBAAqB,CAAA,GACxC,MAAA;AAAA,IACJ,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,MAAM,GAAA,CAAI,IAAA;AAAA,IACV,cAAc,GAAA,CAAI,cAAA;AAAA,IAClB,IAAA,EAAM,UAAA,CAAW,GAAA,CAAI,WAAW,CAAA;AAAA,IAChC,OAAO,GAAA,CAAI,KAAA;AAAA,IACX,WAAW,GAAA,CAAI,SAAA;AAAA,IACf,0BAA0B,GAAA,CAAI,0BAAA;AAAA,IAC9B,YAAY,GAAA,CAAI,SAAA,IAAa,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MAC3C,SAAS,CAAA,CAAE,OAAA;AAAA,MACX,QAAQ,CAAA,CAAE,MAAA;AAAA,MACV,UAAA,EAAY,EAAE,WAAA,IAAe;AAAA,KAC/B,CAAE,CAAA;AAAA,IACF,iBAAiB,GAAA,CAAI,eAAA,IAAmB,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MACtD,SAAS,CAAA,CAAE,QAAA;AAAA,MACX,SAAS,CAAA,CAAE,OAAA;AAAA,MACX,QAAQ,CAAA,CAAE,MAAA;AAAA,MACV,UAAA,EAAY,EAAE,WAAA,IAAe;AAAA,KAC/B,CAAE,CAAA;AAAA,IACF,eAAe,GAAA,CAAI,aAAA,IAAiB,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MAClD,SAAS,CAAA,CAAE,QAAA;AAAA,MACX,QAAQ,CAAA,CAAE,iBAAA;AAAA,MACV,UAAU,CAAA,CAAE,mBAAA;AAAA,MACZ,QAAQ,CAAA,CAAE,aAAA;AAAA,MACV,UAAA,EAAY,EAAE,WAAA,IAAe;AAAA,KAC/B,CAAE,CAAA;AAAA,IACF;AAAA,GACF;AACF;AAEO,SAAS,eAAe,GAAA,EAAsB;AACnD,EAAA,OAAO;AAAA,IACL,SAAS,GAAA,CAAI,QAAA;AAAA,IACb,QAAA,EAAU,MAAA,CAAO,GAAA,CAAI,QAAQ,CAAA;AAAA,IAC7B,QAAQ,GAAA,CAAI,MAAA;AAAA,IACZ,MAAM,GAAA,CAAI,IAAA;AAAA,IACV,MAAM,GAAA,CAAI,IAAA;AAAA,IACV,aAAa,GAAA,CAAI,YAAA,IAAgB,OAAO,MAAA,CAAO,GAAA,CAAI,YAAY,CAAA,GAAI,MAAA;AAAA,IACnE,WAAW,GAAA,CAAI,UAAA,IAAc,OAAO,MAAA,CAAO,GAAA,CAAI,UAAU,CAAA,GAAI,MAAA;AAAA,IAC7D,mBAAmB,GAAA,CAAI,mBAAA;AAAA,IACvB;AAAA,GACF;AACF;AAEO,SAAS,wBAAwB,GAAA,EAAiC;AACvE,EAAA,OAAO;AAAA,IACL,WAAW,GAAA,CAAI,OAAA;AAAA,IACf,OAAA,EAAS,IAAI,OAAA,CAAQ,OAAA;AAAA,IACrB,SAAS,GAAA,CAAI,OAAA,CAAQ,MAAA,IAAU,IAAI,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,SAAS,CAAA,CAAE,QAAA,EAAU,OAAA,EAAS,CAAA,CAAE,SAAQ,CAAE,CAAA;AAAA,IAC3F;AAAA,GACF;AACF;AAGO,SAAS,WAAW,IAAA,EAAwB;AACjD,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,KAAA;AAC9C,EAAA,MAAM,SAAU,IAAA,CAAmB,OAAA;AACnC,EAAA,IAAI,MAAA,EAAQ,QAAA,EAAU,IAAA,CAAK,CAAC,CAAA,KAAM,YAAA,CAAa,IAAA,CAAK,CAAA,CAAE,OAAO,CAAC,CAAA,EAAG,OAAO,IAAA;AACxE,EAAA,MAAM,MAAO,IAAA,CAAsC,YAAA;AACnD,EAAA,IAAI,MAAM,OAAA,CAAQ,GAAG,KAAK,GAAA,CAAI,MAAA,KAAW,GAAG,OAAO,IAAA;AACnD,EAAA,OAAO,KAAA;AACT;;;AC5DA,SAAS,WAAW,MAAA,EAAwC;AAC1D,EAAA,MAAM,CAAA,GAAI,IAAI,eAAA,EAAgB;AAC9B,EAAA,IAAI,OAAO,SAAA,EAAW,CAAA,CAAE,GAAA,CAAI,YAAA,EAAc,OAAO,SAAS,CAAA;AAC1D,EAAA,IAAI,OAAO,eAAA,EAAiB,CAAA,CAAE,GAAA,CAAI,iBAAA,EAAmB,OAAO,eAAe,CAAA;AAC3E,EAAA,IAAI,OAAO,MAAA,EAAQ,CAAA,CAAE,GAAA,CAAI,QAAA,EAAU,OAAO,MAAM,CAAA;AAChD,EAAA,IAAI,OAAO,KAAA,EAAO,CAAA,CAAE,GAAA,CAAI,OAAA,EAAS,OAAO,KAAK,CAAA;AAC7C,EAAA,CAAA,CAAE,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,IAAA,CAAK,GAAA,CAAI,OAAO,KAAA,IAAS,EAAA,EAAI,GAAG,CAAC,CAAC,CAAA;AACxD,EAAA,IAAI,MAAA,CAAO,OAAO,CAAA,CAAE,MAAA,CAAO,aAAa,QAAA,CAAS,KAAA,EAAO,MAAA,CAAO,KAAK,CAAC,CAAA;AACrE,EAAA,IAAI,MAAA,CAAO,QAAQ,CAAA,CAAE,MAAA,CAAO,aAAa,QAAA,CAAS,IAAA,EAAM,MAAA,CAAO,MAAM,CAAC,CAAA;AACtE,EAAA,OAAO,EAAE,QAAA,EAAS;AACpB;AAEO,SAAS,2BAA2B,SAAA,EAA4C;AACrF,EAAA,OAAO;AAAA,IACL,MAAM,IAAA,CAAK,MAAA,GAAS,EAAC,EAAG;AACtB,MAAA,MAAM,IAAA,GAAQ,MAAM,SAAA,CAAU,GAAA;AAAA,QAC5B,CAAA,qBAAA,EAAwB,UAAA,CAAW,MAAM,CAAC,CAAA;AAAA,OAC5C;AACA,MAAA,OAAO;AAAA,QACL,QAAQ,IAAA,CAAK,YAAA,IAAgB,EAAC,EAAG,IAAI,oBAAoB,CAAA;AAAA,QACzD,IAAA,EAAM,IAAA,CAAK,KAAA,EAAO,IAAA,IAAQ;AAAA,OAC5B;AAAA,IACF,CAAA;AAAA,IACA,MAAM,IAAI,aAAA,EAAe;AACvB,MAAA,MAAM,EAAA,GAAK,aAAa,aAAa,CAAA;AACrC,MAAA,MAAM,OAAQ,MAAM,SAAA,CAAU,GAAA,CAAI,CAAA,qBAAA,EAAwB,EAAE,CAAA,CAAE,CAAA;AAG9D,MAAA,OAAA,CAAQ,IAAA,CAAK,YAAA,IAAgB,EAAC,EAAG,IAAI,oBAAoB,CAAA;AAAA,IAC3D;AAAA,GACF;AACF;ACzCO,SAAS,uBAAuB,SAAA,EAAwC;AAC7E,EAAA,OAAO;AAAA,IACL,MAAM,WAAW,SAAA,EAAW;AAC1B,MAAA,cAAA,CAAe,SAAS,CAAA;AACxB,MAAA,OAAO,uBAAA;AAAA,QACJ,MAAM,SAAA,CAAU,GAAA,CAAI,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAE;AAAA,OACtD;AAAA,IACF,CAAA;AAAA,IACA,MAAM,YAAA,CAAa,SAAA,EAAW,OAAA,EAAS;AACrC,MAAA,cAAA,CAAe,SAAS,CAAA;AACxB,MAAA,cAAA,CAAe,OAAO,CAAA;AACtB,MAAA,MAAM,IAAA,GAAQ,MAAM,SAAA,CAAU,GAAA;AAAA,QAC5B,CAAA,iBAAA,EAAoB,SAAS,CAAA,iBAAA,EAAoB,OAAO,CAAA;AAAA,OAC1D;AACA,MAAA,OAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA,IAAK,IAAA,CAAK,OAAO,MAAA,GAAS,CAAA;AAAA,IAC5D;AAAA,GACF;AACF;AClBO,SAAS,qBAAqB,SAAA,EAAsC;AACzE,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,OAAA,EAAS;AACjB,MAAAA,eAAe,OAAO,CAAA;AACtB,MAAA,OAAO,eAAgB,MAAM,SAAA,CAAU,IAAI,CAAA,eAAA,EAAkB,OAAO,EAAE,CAAc,CAAA;AAAA,IACtF;AAAA,GACF;AACF;ACTO,SAAS,uBAAuB,SAAA,EAAwC;AAC7E,EAAA,OAAO;AAAA,IACL,MAAM,IAAI,SAAA,EAAW;AACnB,MAAAA,eAAe,SAAS,CAAA;AACxB,MAAA,MAAM,OAAQ,MAAM,SAAA,CAAU,GAAA,CAAI,CAAA,4BAAA,EAA+B,SAAS,CAAA,QAAA,CAAU,CAAA;AAIpF,MAAA,OAAO,EAAE,SAAA,EAAW,OAAA,EAAS,IAAA,CAAK,QAAA,GAAW,CAAC,CAAA,EAAG,OAAA,IAAW,EAAA,EAAI,SAAA,EAAW,IAAA,CAAK,SAAA,EAAU;AAAA,IAC5F;AAAA,GACF;AACF;;;ACEO,SAAS,mBAAmB,MAAA,EAA0C;AAC3E,EAAA,MAAM,EAAE,OAAA,EAAQ,GAAI,cAAA,CAAe,MAAM,CAAA;AACzC,EAAA,MAAM,mBAAqC,EAAC;AAC5C,EAAA,IAAI,MAAA,CAAO,KAAA,KAAU,MAAA,EAAW,gBAAA,CAAiB,QAAQ,MAAA,CAAO,KAAA;AAChE,EAAA,IAAI,MAAA,CAAO,UAAA,KAAe,MAAA,EAAW,gBAAA,CAAiB,aAAa,MAAA,CAAO,UAAA;AAC1E,EAAA,IAAI,MAAA,CAAO,UAAA,KAAe,MAAA,EAAW,gBAAA,CAAiB,aAAa,MAAA,CAAO,UAAA;AAC1E,EAAA,IAAI,MAAA,CAAO,OAAA,KAAY,MAAA,EAAW,gBAAA,CAAiB,UAAU,MAAA,CAAO,OAAA;AACpE,EAAA,IAAI,MAAA,CAAO,OAAA,KAAY,MAAA,EAAW,gBAAA,CAAiB,UAAU,MAAA,CAAO,OAAA;AACpE,EAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,IAAa,IAAA,CAAK,SAAS,gBAAgB,CAAA;AACpE,EAAA,OAAO;AAAA,IACL,SAAS,SAAA,CAAU,OAAA;AAAA,IACnB,SAAA;AAAA,IACA,YAAA,EAAc,2BAA2B,SAAS,CAAA;AAAA,IAClD,QAAA,EAAU,uBAAuB,SAAS,CAAA;AAAA,IAC1C,MAAA,EAAQ,qBAAqB,SAAS,CAAA;AAAA,IACtC,QAAA,EAAU,uBAAuB,SAAS;AAAA,GAC5C;AACF;;;AC9BA,gBAAuB,SACrB,SAAA,EACA,SAAA,EACA,MAAA,EACA,OAAA,GAA2B,EAAC,EACM;AAClC,EAAA,MAAM,QAAA,GAAW,QAAQ,QAAA,IAAY,GAAA;AACrC,EAAA,IAAI,IAAA,GAAsB,SAAA;AAC1B,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,OAAO,IAAA,IAAQ,QAAQ,QAAA,EAAU;AAC/B,IAAA,MAAM,IAAA,GAAgB,MAAM,SAAA,CAAU,GAAA,CAAI,IAAI,CAAA;AAC9C,IAAA,KAAA,MAAW,IAAA,IAAQ,MAAA,CAAO,IAAI,CAAA,EAAG,MAAM,IAAA;AACvC,IAAA,IAAA,GAAQ,IAAA,CAA6C,OAAO,IAAA,IAAQ,IAAA;AACpE,IAAA,KAAA,EAAA;AAAA,EACF;AACF","file":"index.js","sourcesContent":["// Fields whose integer values may exceed Number.MAX_SAFE_INTEGER and must be bigint.\nconst BIGINT_KEYS = [\"amount\", \"balance\", \"charged_tx_fee\", \"serial_number\"]\n\n/**\n * Parse Mirror Node JSON, coercing known integer money fields to bigint losslessly.\n * Wraps `\"key\": <integer>` in a sentinel string BEFORE JSON.parse so large integers\n * never pass through a lossy JS number, then revives them as bigint.\n */\nexport function parseJsonWithBigInt(text: string): unknown {\n const keyAlternation = BIGINT_KEYS.join(\"|\")\n const re = new RegExp(`(\"(?:${keyAlternation})\"\\\\s*:\\\\s*)(-?\\\\d+)`, \"g\")\n const marked = text.replace(re, (_m, prefix: string, num: string) => `${prefix}\" bi:${num}\"`)\n return JSON.parse(marked, (_k, value) => {\n if (typeof value === \"string\" && value.startsWith(\" bi:\")) return BigInt(value.slice(4))\n return value\n })\n}\n","import { MirrorHttpError, RateLimitError, TimeoutError, NetworkError } from \"@hbar-kit/core\"\nimport { parseJsonWithBigInt } from \"./json.js\"\n\nexport interface TransportOptions {\n fetch?: typeof fetch\n retryCount?: number\n retryDelay?: number\n timeout?: number\n headers?: Record<string, string>\n}\nexport interface Transport {\n get(path: string): Promise<unknown>\n baseUrl: string\n}\n\nconst RETRYABLE = new Set([408, 425, 429, 500, 502, 503, 504])\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))\nconst jitter = (ms: number) => ms * (0.5 + Math.random())\n\nexport function http(baseUrl: string, options: TransportOptions = {}): Transport {\n const doFetch = options.fetch ?? globalThis.fetch\n const retryCount = options.retryCount ?? 3\n const retryDelay = options.retryDelay ?? 150\n const timeout = options.timeout ?? 10_000\n const base = baseUrl.replace(/\\/+$/, \"\")\n\n async function get(path: string): Promise<unknown> {\n const url = path.startsWith(\"http\") ? path : base + path\n let attempt = 0\n let lastErr: unknown\n while (attempt <= retryCount) {\n const controller = new AbortController()\n const timer = setTimeout(() => controller.abort(), timeout)\n try {\n const response = await doFetch(url, {\n signal: controller.signal,\n headers: { accept: \"application/json\", ...options.headers },\n })\n clearTimeout(timer)\n if (response.ok) {\n const text = await response.text()\n return text ? parseJsonWithBigInt(text) : null\n }\n if (RETRYABLE.has(response.status) && attempt < retryCount) {\n const retryAfter = Number(response.headers.get(\"retry-after\"))\n const wait =\n Number.isFinite(retryAfter) && retryAfter > 0\n ? retryAfter * 1000\n : jitter(retryDelay * 2 ** attempt)\n await sleep(wait)\n attempt++\n continue\n }\n const body = await response.text().catch(() => \"\")\n if (response.status === 429) {\n const ra = Number(response.headers.get(\"retry-after\"))\n throw new RateLimitError(\n `Mirror Node rate limited (429)`,\n Number.isFinite(ra) ? { details: body, retryAfter: ra } : { details: body },\n )\n }\n throw new MirrorHttpError(`Mirror Node HTTP ${response.status}`, response.status, {\n details: body,\n })\n } catch (err) {\n clearTimeout(timer)\n if (err instanceof MirrorHttpError) throw err\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw new TimeoutError(`Mirror Node request timed out after ${timeout}ms`, { cause: err })\n }\n lastErr = err\n if (attempt < retryCount) {\n await sleep(jitter(retryDelay * 2 ** attempt))\n attempt++\n continue\n }\n throw new NetworkError(`Mirror Node request failed: ${String(err)}`, { cause: err })\n }\n }\n throw new NetworkError(`Mirror Node request failed`, { cause: lastErr })\n }\n\n return { get, baseUrl: base }\n}\n","import { parseTimestamp } from \"@hbar-kit/core\"\nimport type {\n RawTransaction,\n Transaction,\n RawToken,\n Token,\n RawAccount,\n AccountBalance,\n RawStatus,\n} from \"./types.js\"\n\nconst decodeMemo = (b64: string): string => (b64 ? Buffer.from(b64, \"base64\").toString(\"utf8\") : \"\")\n\nexport function normalizeTransaction(raw: RawTransaction): Transaction {\n return {\n transactionId: raw.transaction_id,\n consensusTimestamp: parseTimestamp(raw.consensus_timestamp),\n validStartTimestamp: raw.valid_start_timestamp\n ? parseTimestamp(raw.valid_start_timestamp)\n : undefined,\n result: raw.result,\n name: raw.name,\n chargedTxFee: raw.charged_tx_fee,\n memo: decodeMemo(raw.memo_base64),\n nonce: raw.nonce,\n scheduled: raw.scheduled,\n parentConsensusTimestamp: raw.parent_consensus_timestamp,\n transfers: (raw.transfers ?? []).map((t) => ({\n account: t.account,\n amount: t.amount,\n isApproval: t.is_approval ?? false,\n })),\n tokenTransfers: (raw.token_transfers ?? []).map((t) => ({\n tokenId: t.token_id,\n account: t.account,\n amount: t.amount,\n isApproval: t.is_approval ?? false,\n })),\n nftTransfers: (raw.nft_transfers ?? []).map((t) => ({\n tokenId: t.token_id,\n sender: t.sender_account_id,\n receiver: t.receiver_account_id,\n serial: t.serial_number,\n isApproval: t.is_approval ?? false,\n })),\n raw,\n }\n}\n\nexport function normalizeToken(raw: RawToken): Token {\n return {\n tokenId: raw.token_id,\n decimals: Number(raw.decimals),\n symbol: raw.symbol,\n name: raw.name,\n type: raw.type,\n totalSupply: raw.total_supply != null ? BigInt(raw.total_supply) : undefined,\n maxSupply: raw.max_supply != null ? BigInt(raw.max_supply) : undefined,\n treasuryAccountId: raw.treasury_account_id,\n raw,\n }\n}\n\nexport function normalizeAccountBalance(raw: RawAccount): AccountBalance {\n return {\n accountId: raw.account,\n balance: raw.balance.balance,\n tokens: (raw.balance.tokens ?? []).map((t) => ({ tokenId: t.token_id, balance: t.balance })),\n raw,\n }\n}\n\n/** Detect Mirror Node not-found: the _status envelope, or an empty transactions array. */\nexport function isNotFound(body: unknown): boolean {\n if (!body || typeof body !== \"object\") return false\n const status = (body as RawStatus)._status\n if (status?.messages?.some((m) => /not found/i.test(m.message))) return true\n const txs = (body as { transactions?: unknown[] }).transactions\n if (Array.isArray(txs) && txs.length === 0) return true\n return false\n}\n","import { tsFilter, txIdToMirror } from \"@hbar-kit/core\"\nimport type { Transport } from \"../transport.js\"\nimport { normalizeTransaction } from \"../normalize.js\"\nimport type { Page, RawTransaction, RawTransactionList, Transaction } from \"../types.js\"\n\nexport interface FindTransactionsParams {\n accountId?: string\n transactionType?: string\n result?: \"success\" | \"fail\"\n order?: \"asc\" | \"desc\"\n limit?: number\n after?: Date | string\n before?: Date | string\n}\n\nexport interface TransactionsResource {\n find(params?: FindTransactionsParams): Promise<Page<Transaction>>\n get(transactionId: string): Promise<Transaction[]>\n}\n\nfunction buildQuery(params: FindTransactionsParams): string {\n const q = new URLSearchParams()\n if (params.accountId) q.set(\"account.id\", params.accountId)\n if (params.transactionType) q.set(\"transactiontype\", params.transactionType)\n if (params.result) q.set(\"result\", params.result)\n if (params.order) q.set(\"order\", params.order)\n q.set(\"limit\", String(Math.min(params.limit ?? 25, 100)))\n if (params.after) q.append(\"timestamp\", tsFilter(\"gte\", params.after))\n if (params.before) q.append(\"timestamp\", tsFilter(\"lt\", params.before))\n return q.toString()\n}\n\nexport function createTransactionsResource(transport: Transport): TransactionsResource {\n return {\n async find(params = {}) {\n const body = (await transport.get(\n `/api/v1/transactions?${buildQuery(params)}`,\n )) as RawTransactionList\n return {\n items: (body.transactions ?? []).map(normalizeTransaction),\n next: body.links?.next ?? null,\n }\n },\n async get(transactionId) {\n const id = txIdToMirror(transactionId)\n const body = (await transport.get(`/api/v1/transactions/${id}`)) as {\n transactions?: RawTransaction[]\n }\n return (body.transactions ?? []).map(normalizeTransaction)\n },\n }\n}\n","import { assertEntityId } from \"@hbar-kit/core\"\nimport type { Transport } from \"../transport.js\"\nimport { normalizeAccountBalance } from \"../normalize.js\"\nimport type { AccountBalance, RawAccount } from \"../types.js\"\n\nexport interface AccountsResource {\n getBalance(accountId: string): Promise<AccountBalance>\n isAssociated(accountId: string, tokenId: string): Promise<boolean>\n}\n\nexport function createAccountsResource(transport: Transport): AccountsResource {\n return {\n async getBalance(accountId) {\n assertEntityId(accountId)\n return normalizeAccountBalance(\n (await transport.get(`/api/v1/accounts/${accountId}`)) as RawAccount,\n )\n },\n async isAssociated(accountId, tokenId) {\n assertEntityId(accountId)\n assertEntityId(tokenId)\n const body = (await transport.get(\n `/api/v1/accounts/${accountId}/tokens?token.id=${tokenId}`,\n )) as { tokens?: unknown[] }\n return Array.isArray(body.tokens) && body.tokens.length > 0\n },\n }\n}\n","import { assertEntityId } from \"@hbar-kit/core\"\nimport type { Transport } from \"../transport.js\"\nimport { normalizeToken } from \"../normalize.js\"\nimport type { RawToken, Token } from \"../types.js\"\n\nexport interface TokensResource {\n get(tokenId: string): Promise<Token>\n}\n\nexport function createTokensResource(transport: Transport): TokensResource {\n return {\n async get(tokenId) {\n assertEntityId(tokenId)\n return normalizeToken((await transport.get(`/api/v1/tokens/${tokenId}`)) as RawToken)\n },\n }\n}\n","import { assertEntityId } from \"@hbar-kit/core\"\nimport type { Transport } from \"../transport.js\"\n\nexport interface BalancesResource {\n get(accountId: string): Promise<{ accountId: string; balance: bigint; timestamp: string }>\n}\n\nexport function createBalancesResource(transport: Transport): BalancesResource {\n return {\n async get(accountId) {\n assertEntityId(accountId)\n const body = (await transport.get(`/api/v1/balances?account.id=${accountId}&limit=1`)) as {\n timestamp: string\n balances: { account: string; balance: bigint }[]\n }\n return { accountId, balance: body.balances?.[0]?.balance ?? 0n, timestamp: body.timestamp }\n },\n }\n}\n","import { resolveNetwork, type NetworkInput } from \"@hbar-kit/core\"\nimport { http, type TransportOptions, type Transport } from \"./transport.js\"\nimport { createTransactionsResource, type TransactionsResource } from \"./resources/transactions.js\"\nimport { createAccountsResource, type AccountsResource } from \"./resources/accounts.js\"\nimport { createTokensResource, type TokensResource } from \"./resources/tokens.js\"\nimport { createBalancesResource, type BalancesResource } from \"./resources/balances.js\"\n\nexport interface MirrorClientConfig extends NetworkInput, TransportOptions {\n transport?: Transport\n}\n\nexport interface MirrorClient {\n baseUrl: string\n transport: Transport\n transactions: TransactionsResource\n accounts: AccountsResource\n tokens: TokensResource\n balances: BalancesResource\n}\n\nexport function createMirrorClient(config: MirrorClientConfig): MirrorClient {\n const { baseUrl } = resolveNetwork(config)\n const transportOptions: TransportOptions = {}\n if (config.fetch !== undefined) transportOptions.fetch = config.fetch\n if (config.retryCount !== undefined) transportOptions.retryCount = config.retryCount\n if (config.retryDelay !== undefined) transportOptions.retryDelay = config.retryDelay\n if (config.timeout !== undefined) transportOptions.timeout = config.timeout\n if (config.headers !== undefined) transportOptions.headers = config.headers\n const transport = config.transport ?? http(baseUrl, transportOptions)\n return {\n baseUrl: transport.baseUrl,\n transport,\n transactions: createTransactionsResource(transport),\n accounts: createAccountsResource(transport),\n tokens: createTokensResource(transport),\n balances: createBalancesResource(transport),\n }\n}\n","import type { Transport } from \"./transport.js\"\n\nexport interface PaginateOptions {\n maxPages?: number\n}\n\n/** Async-iterate a list endpoint by following links.next verbatim until null. */\nexport async function* paginate<T>(\n transport: Transport,\n firstPath: string,\n select: (page: unknown) => T[],\n options: PaginateOptions = {},\n): AsyncGenerator<T, void, unknown> {\n const maxPages = options.maxPages ?? 1000\n let path: string | null = firstPath\n let pages = 0\n while (path && pages < maxPages) {\n const page: unknown = await transport.get(path)\n for (const item of select(page)) yield item\n path = (page as { links?: { next: string | null } }).links?.next ?? null\n pages++\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hbar-kit/mirror",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Typed Hedera Mirror Node REST client.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://devwhodevs.github.io/hbar-kit/",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/devwhodevs/hbar-kit.git",
|
|
10
|
+
"directory": "packages/mirror"
|
|
11
|
+
},
|
|
12
|
+
"bugs": "https://github.com/devwhodevs/hbar-kit/issues",
|
|
13
|
+
"keywords": [
|
|
14
|
+
"hedera",
|
|
15
|
+
"hbar",
|
|
16
|
+
"hts",
|
|
17
|
+
"mirror-node",
|
|
18
|
+
"rest",
|
|
19
|
+
"typescript"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"!src/**/*.test.ts"
|
|
27
|
+
],
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"require": {
|
|
35
|
+
"types": "./dist/index.d.cts",
|
|
36
|
+
"default": "./dist/index.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
40
|
+
},
|
|
41
|
+
"main": "./dist/index.cjs",
|
|
42
|
+
"module": "./dist/index.js",
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@hbar-kit/core": "0.1.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"typescript": "^5.6.0",
|
|
49
|
+
"vitest": "^2.1.0"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"typecheck": "tsc --noEmit",
|
|
58
|
+
"lint": "eslint src",
|
|
59
|
+
"check:publish": "publint --strict && attw --pack ."
|
|
60
|
+
}
|
|
61
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { resolveNetwork, type NetworkInput } from "@hbar-kit/core"
|
|
2
|
+
import { http, type TransportOptions, type Transport } from "./transport.js"
|
|
3
|
+
import { createTransactionsResource, type TransactionsResource } from "./resources/transactions.js"
|
|
4
|
+
import { createAccountsResource, type AccountsResource } from "./resources/accounts.js"
|
|
5
|
+
import { createTokensResource, type TokensResource } from "./resources/tokens.js"
|
|
6
|
+
import { createBalancesResource, type BalancesResource } from "./resources/balances.js"
|
|
7
|
+
|
|
8
|
+
export interface MirrorClientConfig extends NetworkInput, TransportOptions {
|
|
9
|
+
transport?: Transport
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface MirrorClient {
|
|
13
|
+
baseUrl: string
|
|
14
|
+
transport: Transport
|
|
15
|
+
transactions: TransactionsResource
|
|
16
|
+
accounts: AccountsResource
|
|
17
|
+
tokens: TokensResource
|
|
18
|
+
balances: BalancesResource
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createMirrorClient(config: MirrorClientConfig): MirrorClient {
|
|
22
|
+
const { baseUrl } = resolveNetwork(config)
|
|
23
|
+
const transportOptions: TransportOptions = {}
|
|
24
|
+
if (config.fetch !== undefined) transportOptions.fetch = config.fetch
|
|
25
|
+
if (config.retryCount !== undefined) transportOptions.retryCount = config.retryCount
|
|
26
|
+
if (config.retryDelay !== undefined) transportOptions.retryDelay = config.retryDelay
|
|
27
|
+
if (config.timeout !== undefined) transportOptions.timeout = config.timeout
|
|
28
|
+
if (config.headers !== undefined) transportOptions.headers = config.headers
|
|
29
|
+
const transport = config.transport ?? http(baseUrl, transportOptions)
|
|
30
|
+
return {
|
|
31
|
+
baseUrl: transport.baseUrl,
|
|
32
|
+
transport,
|
|
33
|
+
transactions: createTransactionsResource(transport),
|
|
34
|
+
accounts: createAccountsResource(transport),
|
|
35
|
+
tokens: createTokensResource(transport),
|
|
36
|
+
balances: createBalancesResource(transport),
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { createMirrorClient } from "./client.js"
|
|
2
|
+
export type { MirrorClient, MirrorClientConfig } from "./client.js"
|
|
3
|
+
export { http } from "./transport.js"
|
|
4
|
+
export type { Transport, TransportOptions } from "./transport.js"
|
|
5
|
+
export { paginate } from "./paginate.js"
|
|
6
|
+
export {
|
|
7
|
+
normalizeTransaction,
|
|
8
|
+
normalizeToken,
|
|
9
|
+
normalizeAccountBalance,
|
|
10
|
+
isNotFound,
|
|
11
|
+
} from "./normalize.js"
|
|
12
|
+
export type { FindTransactionsParams } from "./resources/transactions.js"
|
|
13
|
+
export * from "./types.js"
|
package/src/json.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Fields whose integer values may exceed Number.MAX_SAFE_INTEGER and must be bigint.
|
|
2
|
+
const BIGINT_KEYS = ["amount", "balance", "charged_tx_fee", "serial_number"]
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Parse Mirror Node JSON, coercing known integer money fields to bigint losslessly.
|
|
6
|
+
* Wraps `"key": <integer>` in a sentinel string BEFORE JSON.parse so large integers
|
|
7
|
+
* never pass through a lossy JS number, then revives them as bigint.
|
|
8
|
+
*/
|
|
9
|
+
export function parseJsonWithBigInt(text: string): unknown {
|
|
10
|
+
const keyAlternation = BIGINT_KEYS.join("|")
|
|
11
|
+
const re = new RegExp(`("(?:${keyAlternation})"\\s*:\\s*)(-?\\d+)`, "g")
|
|
12
|
+
const marked = text.replace(re, (_m, prefix: string, num: string) => `${prefix}" bi:${num}"`)
|
|
13
|
+
return JSON.parse(marked, (_k, value) => {
|
|
14
|
+
if (typeof value === "string" && value.startsWith(" bi:")) return BigInt(value.slice(4))
|
|
15
|
+
return value
|
|
16
|
+
})
|
|
17
|
+
}
|